laplace-api 4.0.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +37 -0
- package/.github/workflows/test.yml +25 -0
- package/README.md +461 -2
- package/package.json +1 -1
- package/src/client/broker.ts +9 -7
- package/src/client/capital_increase.ts +7 -12
- package/src/client/collections.ts +57 -28
- package/src/client/financial_fundamentals.ts +2 -5
- package/src/client/financial_ratios.ts +114 -95
- package/src/client/live-price-web-socket.ts +24 -9
- package/src/client/live-price.ts +228 -46
- package/src/client/politician.ts +75 -0
- package/src/client/stocks.ts +73 -0
- package/src/test/broker.test.ts +583 -148
- package/src/test/capital_increase.test.ts +186 -39
- package/src/test/collections.test.ts +445 -60
- package/src/test/custom_theme.test.ts +242 -60
- package/src/test/financial_fundamentals.test.ts +297 -56
- package/src/test/financial_ratios.test.ts +363 -92
- package/src/test/funds.test.ts +275 -68
- package/src/test/key-insight.test.ts +81 -19
- package/src/test/live-price.test.ts +425 -64
- package/src/test/politician.test.ts +253 -0
- package/src/test/readme.test.ts +481 -0
- package/src/test/search.test.ts +301 -65
- package/src/test/stocks.test.ts +764 -152
- package/src/utilities/configuration.ts +23 -10
- package/src/utilities/test.env +2 -2
package/src/client/live-price.ts
CHANGED
|
@@ -1,31 +1,73 @@
|
|
|
1
1
|
import { Client } from "./client";
|
|
2
2
|
import { Region } from "./collections";
|
|
3
|
-
import { v4 as uuidv4 } from
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
4
|
import { LivePriceFeed } from "./live-price-web-socket";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
url: string;
|
|
8
|
-
}
|
|
6
|
+
export type MessageType = "pr" | "state_change" | "heartbeat" | "ob";
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
// Stream Message Wrapper - v2 formatter wraps all messages in this structure
|
|
9
|
+
export interface StreamMessage<T> {
|
|
10
|
+
t: MessageType;
|
|
11
|
+
d: T;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
export interface
|
|
16
|
-
s: string;
|
|
14
|
+
export interface BISTStockPriceData {
|
|
15
|
+
s: string; // Symbol
|
|
17
16
|
ch: number; // DailyPercentChange
|
|
18
17
|
p: number; // ClosePrice
|
|
18
|
+
d: number; // Date
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface USStockPriceData {
|
|
22
|
+
s: string; // Symbol
|
|
23
|
+
p: number; // Price
|
|
24
|
+
d: number; // Date
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type BISTStockStreamData = StreamMessage<BISTStockPriceData>;
|
|
28
|
+
export type USStockStreamData = StreamMessage<USStockPriceData>;
|
|
29
|
+
|
|
30
|
+
export enum OrderbookLevelSide {
|
|
31
|
+
Bid = "bid",
|
|
32
|
+
Ask = "ask"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface OrderbookLevel {
|
|
36
|
+
level: number;
|
|
37
|
+
vol: number;
|
|
38
|
+
orders: number;
|
|
39
|
+
p: number;
|
|
40
|
+
side: OrderbookLevelSide;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface OrderbookDeletedLevel {
|
|
44
|
+
level: number;
|
|
45
|
+
side: OrderbookLevelSide;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface OrderbookLiveData {
|
|
49
|
+
updated?: OrderbookLevel[];
|
|
50
|
+
deleted?: OrderbookDeletedLevel[];
|
|
51
|
+
symbol: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export enum PriceDataType {
|
|
55
|
+
Live = "live",
|
|
56
|
+
Delayed = "delayed",
|
|
57
|
+
Orderbook = "orderbook",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface WebSocketUrlResponse {
|
|
61
|
+
url: string;
|
|
19
62
|
}
|
|
20
63
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
ap: number; // AskPrice
|
|
64
|
+
interface WebSocketUrlParams {
|
|
65
|
+
externalUserId: string;
|
|
66
|
+
feeds: LivePriceFeed[];
|
|
25
67
|
}
|
|
26
68
|
|
|
27
69
|
export enum AccessorType {
|
|
28
|
-
User = "user"
|
|
70
|
+
User = "user",
|
|
29
71
|
}
|
|
30
72
|
|
|
31
73
|
interface UpdateUserDetailsParams {
|
|
@@ -35,26 +77,188 @@ interface UpdateUserDetailsParams {
|
|
|
35
77
|
address?: string;
|
|
36
78
|
city?: string;
|
|
37
79
|
countryCode?: string;
|
|
38
|
-
accessorType?: AccessorType;
|
|
80
|
+
accessorType?: AccessorType;
|
|
39
81
|
active: boolean;
|
|
40
82
|
}
|
|
41
83
|
|
|
42
|
-
|
|
84
|
+
export interface ILivePriceClient<T> {
|
|
85
|
+
close(): void;
|
|
86
|
+
receive(): AsyncIterable<T>;
|
|
87
|
+
subscribe(symbols: string[]): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class LivePriceClientImpl<T> implements ILivePriceClient<T> {
|
|
91
|
+
private client: Client;
|
|
92
|
+
private region: Region;
|
|
93
|
+
private dataType: PriceDataType;
|
|
94
|
+
private symbols: string[] = [];
|
|
95
|
+
private closed = false;
|
|
96
|
+
private currentStream: AsyncIterable<T> | null = null;
|
|
97
|
+
private cancelFn: (() => void) | null = null;
|
|
98
|
+
|
|
99
|
+
constructor(client: Client, region: Region, dataType: PriceDataType) {
|
|
100
|
+
this.client = client;
|
|
101
|
+
this.region = region;
|
|
102
|
+
this.dataType = dataType;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
close(): void {
|
|
106
|
+
if (this.closed) return;
|
|
107
|
+
|
|
108
|
+
this.closed = true;
|
|
109
|
+
if (this.cancelFn) {
|
|
110
|
+
this.cancelFn();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
receive(): AsyncIterable<T> {
|
|
115
|
+
if (!this.currentStream) {
|
|
116
|
+
throw new Error("Not subscribed. Call subscribe() first.");
|
|
117
|
+
}
|
|
118
|
+
return this.currentStream;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async subscribe(symbols: string[]): Promise<void> {
|
|
122
|
+
// Cancel existing connection
|
|
123
|
+
if (this.cancelFn) {
|
|
124
|
+
this.cancelFn();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const streamId = uuidv4();
|
|
128
|
+
let url: string;
|
|
129
|
+
|
|
130
|
+
switch (this.dataType) {
|
|
131
|
+
case PriceDataType.Live:
|
|
132
|
+
url = `${
|
|
133
|
+
this.client["baseUrl"]
|
|
134
|
+
}/api/v2/stock/price/live?filter=${symbols.join(",")}®ion=${
|
|
135
|
+
this.region
|
|
136
|
+
}&stream=${streamId}`;
|
|
137
|
+
break;
|
|
138
|
+
case PriceDataType.Delayed:
|
|
139
|
+
url = `${
|
|
140
|
+
this.client["baseUrl"]
|
|
141
|
+
}/api/v1/stock/price/delayed?filter=${symbols.join(",")}®ion=${
|
|
142
|
+
this.region
|
|
143
|
+
}&stream=${streamId}`;
|
|
144
|
+
break;
|
|
145
|
+
case PriceDataType.Orderbook:
|
|
146
|
+
url = `${
|
|
147
|
+
this.client["baseUrl"]
|
|
148
|
+
}/api/v1/stock/orderbook/live?filter=${symbols.join(",")}®ion=${
|
|
149
|
+
this.region
|
|
150
|
+
}&stream=${streamId}`;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const { events, cancel } = this.client.sendSSERequest<T>(url);
|
|
155
|
+
|
|
156
|
+
this.currentStream = events;
|
|
157
|
+
this.cancelFn = cancel;
|
|
158
|
+
this.symbols = symbols;
|
|
159
|
+
this.closed = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getLivePrice<T>(
|
|
164
|
+
client: Client,
|
|
165
|
+
symbols: string[],
|
|
166
|
+
region: Region
|
|
167
|
+
): ILivePriceClient<T> {
|
|
168
|
+
if (!client) {
|
|
169
|
+
throw new Error("Client cannot be null");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const livePriceClient = new LivePriceClientImpl<T>(client, region, PriceDataType.Live);
|
|
173
|
+
livePriceClient.subscribe(symbols).catch((error) => {
|
|
174
|
+
console.error("Failed to initialize live price client", error);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return livePriceClient;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function getDelayedPrice<T>(
|
|
43
181
|
client: Client,
|
|
44
182
|
symbols: string[],
|
|
45
183
|
region: Region,
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
184
|
+
): ILivePriceClient<T> {
|
|
185
|
+
if (!client) {
|
|
186
|
+
throw new Error("Client cannot be null");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const livePriceClient = new LivePriceClientImpl<T>(client, region, PriceDataType.Delayed);
|
|
190
|
+
livePriceClient.subscribe(symbols).catch((error) => {
|
|
191
|
+
console.error("Failed to initialize live price client", error);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
return livePriceClient;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function getOrderbook<T>(
|
|
198
|
+
client: Client,
|
|
199
|
+
symbols: string[],
|
|
200
|
+
region: Region,
|
|
201
|
+
): ILivePriceClient<T> {
|
|
202
|
+
if (!client) {
|
|
203
|
+
throw new Error("Client cannot be null");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const orderbookClient = new LivePriceClientImpl<T>(client, region, PriceDataType.Orderbook);
|
|
207
|
+
orderbookClient.subscribe(symbols).catch((error) => {
|
|
208
|
+
console.error("Failed to initialize orderbook client", error);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return orderbookClient;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function getLivePriceForBIST(
|
|
215
|
+
client: Client,
|
|
216
|
+
symbols: string[]
|
|
217
|
+
): ILivePriceClient<BISTStockStreamData> {
|
|
218
|
+
return getLivePrice<BISTStockStreamData>(client, symbols, Region.Tr);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function getLivePriceForUS(
|
|
222
|
+
client: Client,
|
|
223
|
+
symbols: string[]
|
|
224
|
+
): ILivePriceClient<USStockStreamData> {
|
|
225
|
+
return getLivePrice<USStockStreamData>(client, symbols, Region.Us);
|
|
226
|
+
}
|
|
52
227
|
|
|
53
|
-
|
|
228
|
+
export function getDelayedPriceForBIST(
|
|
229
|
+
client: Client,
|
|
230
|
+
symbols: string[]
|
|
231
|
+
): ILivePriceClient<BISTStockStreamData> {
|
|
232
|
+
return getDelayedPrice<BISTStockStreamData>(client, symbols, Region.Tr);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function getOrderbookForBIST(
|
|
236
|
+
client: Client,
|
|
237
|
+
symbols: string[]
|
|
238
|
+
): ILivePriceClient<OrderbookLiveData> {
|
|
239
|
+
return getOrderbook<OrderbookLiveData>(client, symbols, Region.Tr);
|
|
54
240
|
}
|
|
55
241
|
|
|
56
242
|
export class LivePriceClient extends Client {
|
|
57
|
-
|
|
243
|
+
getLivePriceForBIST(symbols: string[]): ILivePriceClient<BISTStockStreamData> {
|
|
244
|
+
return getLivePriceForBIST(this, symbols);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
getLivePriceForUS(symbols: string[]): ILivePriceClient<USStockStreamData> {
|
|
248
|
+
return getLivePriceForUS(this, symbols);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
getDelayedPriceForBIST(
|
|
252
|
+
symbols: string[],
|
|
253
|
+
): ILivePriceClient<BISTStockStreamData> {
|
|
254
|
+
return getDelayedPriceForBIST(this, symbols);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getOrderbookForBIST(symbols: string[]): ILivePriceClient<OrderbookLiveData> {
|
|
258
|
+
return getOrderbookForBIST(this, symbols);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async getClientWebsocketUrl(
|
|
58
262
|
externalUserId: string,
|
|
59
263
|
feeds: LivePriceFeed[]
|
|
60
264
|
): Promise<string> {
|
|
@@ -83,26 +287,4 @@ export class LivePriceClient extends Client {
|
|
|
83
287
|
data: params,
|
|
84
288
|
});
|
|
85
289
|
}
|
|
86
|
-
|
|
87
|
-
getLivePriceForBIST(
|
|
88
|
-
symbols: string[],
|
|
89
|
-
region: Region,
|
|
90
|
-
streamId?: string,
|
|
91
|
-
): {
|
|
92
|
-
events: AsyncIterable<BISTStockLiveData>,
|
|
93
|
-
cancel: () => void
|
|
94
|
-
} {
|
|
95
|
-
return getSSELivePrice<BISTStockLiveData>(this, symbols, region, streamId);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
getLivePriceForUS(
|
|
99
|
-
symbols: string[],
|
|
100
|
-
region: Region,
|
|
101
|
-
streamId?: string,
|
|
102
|
-
): {
|
|
103
|
-
events: AsyncIterable<USStockLiveData>,
|
|
104
|
-
cancel: () => void
|
|
105
|
-
} {
|
|
106
|
-
return getSSELivePrice<USStockLiveData>(this, symbols, region, streamId);
|
|
107
|
-
}
|
|
108
290
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Client } from "./client";
|
|
2
|
+
|
|
3
|
+
export interface Politician {
|
|
4
|
+
id: number;
|
|
5
|
+
politicianName: string;
|
|
6
|
+
totalHoldings: number;
|
|
7
|
+
lastUpdated: Date;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Holding {
|
|
11
|
+
politicianName: string;
|
|
12
|
+
symbol: string;
|
|
13
|
+
company: string;
|
|
14
|
+
holding: string;
|
|
15
|
+
allocation: string;
|
|
16
|
+
lastUpdated: Date;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface HoldingShort {
|
|
20
|
+
symbol: string;
|
|
21
|
+
company: string;
|
|
22
|
+
holding: string;
|
|
23
|
+
allocation: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface TopHolding {
|
|
27
|
+
symbol: string;
|
|
28
|
+
company: string;
|
|
29
|
+
politicians: TopHoldingPolitician[];
|
|
30
|
+
count: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TopHoldingPolitician {
|
|
34
|
+
name: string;
|
|
35
|
+
holding: string;
|
|
36
|
+
allocation: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PoliticianDetail {
|
|
40
|
+
id: number;
|
|
41
|
+
name: string;
|
|
42
|
+
holdings: HoldingShort[];
|
|
43
|
+
totalHoldings: number;
|
|
44
|
+
lastUpdated: Date;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export class PoliticianClient extends Client {
|
|
48
|
+
async getAllPolitician(): Promise<Politician[]> {
|
|
49
|
+
return await this.sendRequest<Politician[]>({
|
|
50
|
+
method: 'GET',
|
|
51
|
+
url: `/api/v1/politician`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getPoliticianHoldingBySymbol(symbol: string): Promise<Holding[]> {
|
|
56
|
+
return await this.sendRequest<Holding[]>({
|
|
57
|
+
method: 'GET',
|
|
58
|
+
url: `/api/v1/holding/${symbol}`
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async getAllTopHoldings(): Promise<TopHolding[]> {
|
|
63
|
+
return await this.sendRequest<TopHolding[]>({
|
|
64
|
+
method: 'GET',
|
|
65
|
+
url: `/api/v1/top-holding`
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async getPoliticianDetail(id: number): Promise<PoliticianDetail> {
|
|
70
|
+
return await this.sendRequest<PoliticianDetail>({
|
|
71
|
+
method: 'GET',
|
|
72
|
+
url: `/api/v1/politician/${id}`
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/client/stocks.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PaginatedResponse } from './capital_increase';
|
|
1
2
|
import { Client } from './client';
|
|
2
3
|
import { Region, Locale } from './collections';
|
|
3
4
|
import { LaplaceHTTPError } from './errors';
|
|
@@ -122,6 +123,32 @@ export interface TickSizeRule {
|
|
|
122
123
|
tickSize: number;
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
export interface EarningsTranscriptListItem {
|
|
127
|
+
symbol: string;
|
|
128
|
+
year: number;
|
|
129
|
+
quarter: number;
|
|
130
|
+
date: string;
|
|
131
|
+
fiscal_year: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface EarningsTranscriptWithSummary {
|
|
135
|
+
symbol: string;
|
|
136
|
+
year: number;
|
|
137
|
+
quarter: number;
|
|
138
|
+
date: string;
|
|
139
|
+
content: string;
|
|
140
|
+
summary?: string;
|
|
141
|
+
has_summary: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface MarketState {
|
|
145
|
+
id: number;
|
|
146
|
+
marketSymbol?: string | null;
|
|
147
|
+
state: string;
|
|
148
|
+
lastTimestamp: string;
|
|
149
|
+
stockSymbol?: string | null;
|
|
150
|
+
}
|
|
151
|
+
|
|
125
152
|
export class StockClient extends Client {
|
|
126
153
|
async getAllStocks(region: Region, page: number|null = null, pageSize: number|null = null): Promise<Stock[]> {
|
|
127
154
|
return this.sendRequest<Stock[]>({
|
|
@@ -205,4 +232,50 @@ export class StockClient extends Client {
|
|
|
205
232
|
params: { symbol, region },
|
|
206
233
|
});
|
|
207
234
|
}
|
|
235
|
+
|
|
236
|
+
async getEarningsTranscripts(symbol: string, region: Region): Promise<EarningsTranscriptListItem[]> {
|
|
237
|
+
return this.sendRequest<EarningsTranscriptListItem[]>({
|
|
238
|
+
method: 'GET',
|
|
239
|
+
url: '/api/v1/earnings/transcripts',
|
|
240
|
+
params: { symbol, region },
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async getEarningsTranscript(symbol: string, year: number, quarter: number): Promise<EarningsTranscriptWithSummary> {
|
|
245
|
+
return this.sendRequest<EarningsTranscriptWithSummary>({
|
|
246
|
+
method: 'GET',
|
|
247
|
+
url: '/api/v1/earnings/transcript',
|
|
248
|
+
params: { symbol, year, quarter },
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async getStockStateAll(page: number, size: number, region: Region): Promise<PaginatedResponse<MarketState>> {
|
|
253
|
+
return this.sendRequest<PaginatedResponse<MarketState>>({
|
|
254
|
+
method: 'GET',
|
|
255
|
+
url: '/api/v1/state/stock/all',
|
|
256
|
+
params: { page, size, region },
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async getStockState(symbol: string): Promise<MarketState> {
|
|
261
|
+
return this.sendRequest<MarketState>({
|
|
262
|
+
method: 'GET',
|
|
263
|
+
url: `/api/v1/state/stock/${symbol}`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async getStateAll(page: number, size: number, region: Region): Promise<PaginatedResponse<MarketState>> {
|
|
268
|
+
return this.sendRequest<PaginatedResponse<MarketState>>({
|
|
269
|
+
method: 'GET',
|
|
270
|
+
url: '/api/v1/state/all',
|
|
271
|
+
params: { page, size, region },
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async getState(symbol: string): Promise<MarketState> {
|
|
276
|
+
return this.sendRequest<MarketState>({
|
|
277
|
+
method: 'GET',
|
|
278
|
+
url: `/api/v1/state/${symbol}`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
208
281
|
}
|