laplace-api 4.3.3 → 4.5.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/package.json +1 -1
- package/src/client/live-price-web-socket.ts +17 -20
- package/src/client/live-price.ts +67 -0
- package/src/client/stocks.ts +30 -0
- package/src/test/live-price.test.ts +58 -1
- package/src/test/stocks.test.ts +72 -25
package/package.json
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { OrderbookLiveData } from "./live-price";
|
|
2
|
+
|
|
1
3
|
interface RawBISTStockLiveData {
|
|
2
4
|
_id: number;
|
|
3
5
|
symbol: string;
|
|
@@ -33,14 +35,15 @@ export enum LivePriceFeed {
|
|
|
33
35
|
LiveUs = "live_price_us",
|
|
34
36
|
DelayedBist = "delayed_price_tr",
|
|
35
37
|
DelayedUs = "delayed_price_us",
|
|
36
|
-
|
|
38
|
+
DepthBist = "depth_tr",
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
type StockLiveDataType<T extends LivePriceFeed> = T extends
|
|
40
42
|
| LivePriceFeed.LiveBist
|
|
41
43
|
| LivePriceFeed.DelayedBist
|
|
42
|
-
?
|
|
43
|
-
|
|
44
|
+
? BISTStockLiveData
|
|
45
|
+
: T extends LivePriceFeed.DepthBist
|
|
46
|
+
? OrderbookLiveData
|
|
44
47
|
: USStockLiveData;
|
|
45
48
|
|
|
46
49
|
export enum LogLevel {
|
|
@@ -94,14 +97,10 @@ export class LivePriceWebSocketClient {
|
|
|
94
97
|
number,
|
|
95
98
|
{
|
|
96
99
|
symbols: string[];
|
|
97
|
-
handler: (data: BISTStockLiveData | USStockLiveData) => void;
|
|
100
|
+
handler: (data: BISTStockLiveData | USStockLiveData | OrderbookLiveData) => void;
|
|
98
101
|
feed: LivePriceFeed;
|
|
99
102
|
}
|
|
100
103
|
>();
|
|
101
|
-
private symbolLastData = new Map<
|
|
102
|
-
string,
|
|
103
|
-
BISTStockLiveData | USStockLiveData
|
|
104
|
-
>();
|
|
105
104
|
private reconnectAttempts = 0;
|
|
106
105
|
private reconnectTimeout: NodeJS.Timeout | null = null;
|
|
107
106
|
private isClosed: boolean = false;
|
|
@@ -276,13 +275,11 @@ export class LivePriceWebSocketClient {
|
|
|
276
275
|
WebSocketErrorType.MESSAGE_PARSE_ERROR
|
|
277
276
|
);
|
|
278
277
|
}
|
|
279
|
-
let priceData: BISTStockLiveData | USStockLiveData;
|
|
278
|
+
let priceData: BISTStockLiveData | USStockLiveData | OrderbookLiveData;
|
|
280
279
|
|
|
281
280
|
if (
|
|
282
281
|
feed === LivePriceFeed.DelayedBist ||
|
|
283
282
|
feed === LivePriceFeed.LiveBist
|
|
284
|
-
// ||
|
|
285
|
-
// feed === LivePriceFeed.DepthBist
|
|
286
283
|
) {
|
|
287
284
|
const message = messageData as RawBISTStockLiveData;
|
|
288
285
|
priceData = {
|
|
@@ -293,6 +290,13 @@ export class LivePriceWebSocketClient {
|
|
|
293
290
|
timestamp: message?.d,
|
|
294
291
|
percentChange: message?.c,
|
|
295
292
|
} as BISTStockLiveData;
|
|
293
|
+
} else if (feed === LivePriceFeed.DepthBist) {
|
|
294
|
+
const message = messageData as OrderbookLiveData;
|
|
295
|
+
priceData = {
|
|
296
|
+
updated: message.updated,
|
|
297
|
+
deleted: message.deleted,
|
|
298
|
+
symbol: message?.symbol,
|
|
299
|
+
} as OrderbookLiveData;
|
|
296
300
|
} else {
|
|
297
301
|
const message = messageData as RawUSStockLiveData;
|
|
298
302
|
priceData = {
|
|
@@ -302,7 +306,6 @@ export class LivePriceWebSocketClient {
|
|
|
302
306
|
} as USStockLiveData;
|
|
303
307
|
}
|
|
304
308
|
if (priceData.symbol) {
|
|
305
|
-
this.symbolLastData.set(priceData.symbol, priceData);
|
|
306
309
|
const handlers = this.getHandlersForSymbol(
|
|
307
310
|
priceData.symbol,
|
|
308
311
|
feed
|
|
@@ -409,7 +412,7 @@ export class LivePriceWebSocketClient {
|
|
|
409
412
|
const subscriptionId = this.subscriptionCounter++;
|
|
410
413
|
let symbolsToAdd: string[] = [];
|
|
411
414
|
|
|
412
|
-
const typedHandler = (data: BISTStockLiveData | USStockLiveData) => {
|
|
415
|
+
const typedHandler = (data: BISTStockLiveData | USStockLiveData | OrderbookLiveData) => {
|
|
413
416
|
handler(data as StockLiveDataType<F>);
|
|
414
417
|
};
|
|
415
418
|
|
|
@@ -423,12 +426,6 @@ export class LivePriceWebSocketClient {
|
|
|
423
426
|
const symbolHandlers = this.getHandlersForSymbol(symbol, feed);
|
|
424
427
|
if (symbolHandlers.length === 1) {
|
|
425
428
|
symbolsToAdd.push(symbol);
|
|
426
|
-
} else if (symbolHandlers.length > 1) {
|
|
427
|
-
const lastData: BISTStockLiveData | USStockLiveData | undefined =
|
|
428
|
-
this.symbolLastData.get(symbol);
|
|
429
|
-
if (lastData) {
|
|
430
|
-
typedHandler(lastData);
|
|
431
|
-
}
|
|
432
429
|
}
|
|
433
430
|
}
|
|
434
431
|
this.addSymbols(symbolsToAdd, feed);
|
|
@@ -445,7 +442,7 @@ export class LivePriceWebSocketClient {
|
|
|
445
442
|
private getHandlersForSymbol(
|
|
446
443
|
symbol: string,
|
|
447
444
|
feed: LivePriceFeed
|
|
448
|
-
): ((data: BISTStockLiveData | USStockLiveData) => void)[] {
|
|
445
|
+
): ((data: BISTStockLiveData | USStockLiveData | OrderbookLiveData) => void)[] {
|
|
449
446
|
return Array.from(this.subscriptions.values())
|
|
450
447
|
.filter((s) => s.symbols.includes(symbol) && s.feed === feed)
|
|
451
448
|
.map((s) => s.handler);
|
package/src/client/live-price.ts
CHANGED
|
@@ -26,6 +26,7 @@ export interface USStockPriceData {
|
|
|
26
26
|
|
|
27
27
|
export type BISTStockStreamData = StreamMessage<BISTStockPriceData>;
|
|
28
28
|
export type USStockStreamData = StreamMessage<USStockPriceData>;
|
|
29
|
+
export type BISTBidAskStreamData = StreamMessage<BISTBidAskData>;
|
|
29
30
|
|
|
30
31
|
export enum OrderbookLevelSide {
|
|
31
32
|
Bid = "bid",
|
|
@@ -40,6 +41,13 @@ export interface OrderbookLevel {
|
|
|
40
41
|
side: OrderbookLevelSide;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
export interface BISTBidAskData {
|
|
45
|
+
d: string;
|
|
46
|
+
s: string;
|
|
47
|
+
ask: number;
|
|
48
|
+
bid: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
43
51
|
export interface OrderbookDeletedLevel {
|
|
44
52
|
level: number;
|
|
45
53
|
side: OrderbookLevelSide;
|
|
@@ -55,6 +63,7 @@ export enum PriceDataType {
|
|
|
55
63
|
Live = "live",
|
|
56
64
|
Delayed = "delayed",
|
|
57
65
|
Orderbook = "orderbook",
|
|
66
|
+
Bids = "bids",
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
interface WebSocketUrlResponse {
|
|
@@ -87,6 +96,13 @@ interface UpdateUserDetailsParams {
|
|
|
87
96
|
active: boolean;
|
|
88
97
|
}
|
|
89
98
|
|
|
99
|
+
export interface SendWebsocketEventRequest {
|
|
100
|
+
externalUserID?: string;
|
|
101
|
+
event: Record<string, any>;
|
|
102
|
+
transient?: boolean;
|
|
103
|
+
broadCastToAll?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
export interface ILivePriceClient<T> {
|
|
91
107
|
close(): void;
|
|
92
108
|
receive(): AsyncIterable<T>;
|
|
@@ -155,6 +171,13 @@ class LivePriceClientImpl<T> implements ILivePriceClient<T> {
|
|
|
155
171
|
this.region
|
|
156
172
|
}&stream=${streamId}`;
|
|
157
173
|
break;
|
|
174
|
+
case PriceDataType.Bids:
|
|
175
|
+
url = `${
|
|
176
|
+
this.client["baseUrl"]
|
|
177
|
+
}/api/v1/stock/price/bids?filter=${symbols.join(",")}®ion=${
|
|
178
|
+
this.region
|
|
179
|
+
}&stream=${streamId}`;
|
|
180
|
+
break;
|
|
158
181
|
}
|
|
159
182
|
|
|
160
183
|
const { events, cancel } = this.client.sendSSERequest<T>(url);
|
|
@@ -217,6 +240,27 @@ function getOrderbook<T>(
|
|
|
217
240
|
return orderbookClient;
|
|
218
241
|
}
|
|
219
242
|
|
|
243
|
+
function getBidAsk<T>(
|
|
244
|
+
client: Client,
|
|
245
|
+
symbols: string[],
|
|
246
|
+
region: Region
|
|
247
|
+
): ILivePriceClient<T> {
|
|
248
|
+
if (!client) {
|
|
249
|
+
throw new Error("Client cannot be null");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const bidAskClient = new LivePriceClientImpl<T>(
|
|
253
|
+
client,
|
|
254
|
+
region,
|
|
255
|
+
PriceDataType.Bids
|
|
256
|
+
);
|
|
257
|
+
bidAskClient.subscribe(symbols).catch((error) => {
|
|
258
|
+
console.error("Failed to initialize bist bid ask client", error);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return bidAskClient;
|
|
262
|
+
}
|
|
263
|
+
|
|
220
264
|
export function getLivePriceForBIST(
|
|
221
265
|
client: Client,
|
|
222
266
|
symbols: string[]
|
|
@@ -245,6 +289,13 @@ export function getOrderbookForBIST(
|
|
|
245
289
|
return getOrderbook<OrderbookLiveData>(client, symbols, Region.Tr);
|
|
246
290
|
}
|
|
247
291
|
|
|
292
|
+
export function getBidAskForBIST(
|
|
293
|
+
client: Client,
|
|
294
|
+
symbols: string[]
|
|
295
|
+
): ILivePriceClient<BISTBidAskStreamData> {
|
|
296
|
+
return getBidAsk<BISTBidAskStreamData>(client, symbols, Region.Tr);
|
|
297
|
+
}
|
|
298
|
+
|
|
248
299
|
export class LivePriceClient extends Client {
|
|
249
300
|
getLivePriceForBIST(symbols: string[]): ILivePriceClient<BISTStockStreamData> {
|
|
250
301
|
return getLivePriceForBIST(this, symbols);
|
|
@@ -264,6 +315,10 @@ export class LivePriceClient extends Client {
|
|
|
264
315
|
return getOrderbookForBIST(this, symbols);
|
|
265
316
|
}
|
|
266
317
|
|
|
318
|
+
getBidAskForBIST(symbols: string[]): ILivePriceClient<BISTBidAskStreamData> {
|
|
319
|
+
return getBidAskForBIST(this, symbols);
|
|
320
|
+
}
|
|
321
|
+
|
|
267
322
|
async getClientWebsocketUrl(
|
|
268
323
|
externalUserId: string,
|
|
269
324
|
feeds: LivePriceFeed[]
|
|
@@ -301,4 +356,16 @@ export class LivePriceClient extends Client {
|
|
|
301
356
|
|
|
302
357
|
return response;
|
|
303
358
|
}
|
|
359
|
+
|
|
360
|
+
async sendWebsocketEvent(
|
|
361
|
+
request: SendWebsocketEventRequest
|
|
362
|
+
): Promise<void> {
|
|
363
|
+
const url = new URL(`${this["baseUrl"]}/api/v1/ws/event`);
|
|
364
|
+
|
|
365
|
+
await this.sendRequest<void>({
|
|
366
|
+
method: "POST",
|
|
367
|
+
url: url.toString(),
|
|
368
|
+
data: request,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
304
371
|
}
|
package/src/client/stocks.ts
CHANGED
|
@@ -149,6 +149,15 @@ export interface MarketState {
|
|
|
149
149
|
stockSymbol?: string | null;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
export interface GenerateChartImageRequest {
|
|
153
|
+
symbol: string;
|
|
154
|
+
period?: HistoricalPricePeriod;
|
|
155
|
+
region: Region;
|
|
156
|
+
resolution?: HistoricalPriceInterval;
|
|
157
|
+
indicators?: string[];
|
|
158
|
+
chartType?: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
152
161
|
export class StockClient extends Client {
|
|
153
162
|
async getAllStocks(region: Region, page: number|null = null, pageSize: number|null = null): Promise<Stock[]> {
|
|
154
163
|
return this.sendRequest<Stock[]>({
|
|
@@ -278,4 +287,25 @@ export class StockClient extends Client {
|
|
|
278
287
|
url: `/api/v1/state/${symbol}`,
|
|
279
288
|
});
|
|
280
289
|
}
|
|
290
|
+
|
|
291
|
+
async getStockChartImage(request: GenerateChartImageRequest): Promise<Blob> {
|
|
292
|
+
const params: Partial<GenerateChartImageRequest> = {
|
|
293
|
+
symbol: request.symbol,
|
|
294
|
+
region: request.region,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
if (request.period) params.period = request.period;
|
|
298
|
+
if (request.resolution) params.resolution = request.resolution;
|
|
299
|
+
if (request.indicators) params.indicators = request.indicators;
|
|
300
|
+
if (request.chartType != null) params.chartType = request.chartType;
|
|
301
|
+
|
|
302
|
+
const data = await this.sendRequest<ArrayBuffer>({
|
|
303
|
+
method: "GET",
|
|
304
|
+
url: "/api/v1/stock/chart",
|
|
305
|
+
params,
|
|
306
|
+
responseType: "arraybuffer",
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
return new Blob([data], { type: "image/png" });
|
|
310
|
+
}
|
|
281
311
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Logger } from "winston";
|
|
2
2
|
import { LaplaceConfiguration } from "../utilities/configuration";
|
|
3
3
|
import "./client_test_suite";
|
|
4
|
-
import { BISTStockStreamData, LivePriceClient, OrderbookLiveData } from "../client/live-price";
|
|
4
|
+
import { BISTBidAskStreamData, BISTStockStreamData, LivePriceClient, OrderbookLiveData } from "../client/live-price";
|
|
5
5
|
|
|
6
6
|
describe("LivePrice", () => {
|
|
7
7
|
let client: LivePriceClient;
|
|
@@ -474,4 +474,61 @@ describe("LivePrice", () => {
|
|
|
474
474
|
TEST_CONSTANTS.JEST_TIMEOUT
|
|
475
475
|
);
|
|
476
476
|
});
|
|
477
|
+
|
|
478
|
+
describe("GetBidAskForBIST", () => {
|
|
479
|
+
it(
|
|
480
|
+
"should receive BIST bid/ask data",
|
|
481
|
+
async () => {
|
|
482
|
+
const symbols = ["AKBNK"];
|
|
483
|
+
let receivedData: BISTBidAskStreamData | null = null;
|
|
484
|
+
let receivedError: Error | null = null;
|
|
485
|
+
|
|
486
|
+
const lc = client.getBidAskForBIST(symbols);
|
|
487
|
+
activeConnections.push(lc);
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
const receiveChan = lc.receive();
|
|
491
|
+
|
|
492
|
+
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
493
|
+
const timeout = setTimeout(
|
|
494
|
+
() => reject(new Error("Timeout waiting for bid/ask data")),
|
|
495
|
+
TEST_CONSTANTS.MAIN_TIMEOUT
|
|
496
|
+
);
|
|
497
|
+
activeTimeouts.push(timeout);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
const dataPromise = (async () => {
|
|
501
|
+
try {
|
|
502
|
+
for await (const data of receiveChan) {
|
|
503
|
+
receivedData = data;
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
} catch (error) {
|
|
507
|
+
console.log("Error in bid/ask data stream:", error);
|
|
508
|
+
}
|
|
509
|
+
})();
|
|
510
|
+
|
|
511
|
+
await Promise.race([dataPromise, timeoutPromise]);
|
|
512
|
+
|
|
513
|
+
if (receivedData != null) {
|
|
514
|
+
const tempReceivedData = (receivedData as BISTBidAskStreamData).d;
|
|
515
|
+
console.log("Received BIST bid/ask data:", tempReceivedData);
|
|
516
|
+
expect(tempReceivedData.s).toBeDefined();
|
|
517
|
+
expect(typeof tempReceivedData.s).toBe("string");
|
|
518
|
+
expect(typeof tempReceivedData.d).toBe("string");
|
|
519
|
+
expect(typeof tempReceivedData.ask).toBe("number");
|
|
520
|
+
expect(typeof tempReceivedData.bid).toBe("number");
|
|
521
|
+
} else {
|
|
522
|
+
console.log("Timeout waiting for BIST bid/ask data");
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
receivedError = error as Error;
|
|
526
|
+
console.log("Received bid/ask error:", receivedError.message);
|
|
527
|
+
} finally {
|
|
528
|
+
lc.close();
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
TEST_CONSTANTS.JEST_TIMEOUT
|
|
532
|
+
);
|
|
533
|
+
});
|
|
477
534
|
});
|
package/src/test/stocks.test.ts
CHANGED
|
@@ -125,14 +125,14 @@ const mockTickRulesResponse = {
|
|
|
125
125
|
|
|
126
126
|
const mockEarningsTranscriptList: EarningsTranscriptListItem[] = [
|
|
127
127
|
{
|
|
128
|
-
symbol: "
|
|
128
|
+
symbol: "AAPL",
|
|
129
129
|
year: 2024,
|
|
130
130
|
quarter: 1,
|
|
131
131
|
date: "2024-05-15",
|
|
132
|
-
fiscal_year: 2024
|
|
132
|
+
fiscal_year: 2024,
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
|
-
symbol: "
|
|
135
|
+
symbol: "AAPL",
|
|
136
136
|
year: 2023,
|
|
137
137
|
quarter: 4,
|
|
138
138
|
date: "2024-02-20",
|
|
@@ -141,7 +141,7 @@ const mockEarningsTranscriptList: EarningsTranscriptListItem[] = [
|
|
|
141
141
|
];
|
|
142
142
|
|
|
143
143
|
const mockEarningsTranscriptDetail: EarningsTranscriptWithSummary = {
|
|
144
|
-
symbol: "
|
|
144
|
+
symbol: "AAPL",
|
|
145
145
|
year: 2024,
|
|
146
146
|
quarter: 1,
|
|
147
147
|
date: "2024-05-15",
|
|
@@ -153,14 +153,14 @@ const mockEarningsTranscriptDetail: EarningsTranscriptWithSummary = {
|
|
|
153
153
|
const mockMarketStates: MarketState[] = [
|
|
154
154
|
{
|
|
155
155
|
id: 1,
|
|
156
|
-
marketSymbol: "
|
|
156
|
+
marketSymbol: "BIST",
|
|
157
157
|
state: "OPEN",
|
|
158
158
|
lastTimestamp: "2024-03-14T10:00:00Z",
|
|
159
159
|
stockSymbol: "TUPRS"
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
id: 2,
|
|
163
|
-
marketSymbol: "
|
|
163
|
+
marketSymbol: "BIST",
|
|
164
164
|
state: "CLOSED",
|
|
165
165
|
lastTimestamp: "2024-03-14T18:00:00Z",
|
|
166
166
|
stockSymbol: "GARAN"
|
|
@@ -174,12 +174,14 @@ const mockPaginatedMarketStates: PaginatedResponse<MarketState> = {
|
|
|
174
174
|
|
|
175
175
|
const mockSingleMarketState: MarketState = {
|
|
176
176
|
id: 1,
|
|
177
|
-
marketSymbol: "
|
|
177
|
+
marketSymbol: "BIST",
|
|
178
178
|
state: "OPEN",
|
|
179
179
|
lastTimestamp: "2024-03-14T10:00:00Z",
|
|
180
180
|
stockSymbol: "TUPRS"
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
+
const mockChartImageBlob = new Blob(['mock chart image data'], { type: 'image/png' });
|
|
184
|
+
|
|
183
185
|
describe("Stocks Client", () => {
|
|
184
186
|
let client: StockClient;
|
|
185
187
|
|
|
@@ -387,7 +389,7 @@ describe("Stocks Client", () => {
|
|
|
387
389
|
|
|
388
390
|
describe("getEarningsTranscripts", () => {
|
|
389
391
|
test("should return earnings transcript list", async () => {
|
|
390
|
-
const resp = await client.getEarningsTranscripts("
|
|
392
|
+
const resp = await client.getEarningsTranscripts("AAPL", Region.Us);
|
|
391
393
|
|
|
392
394
|
expect(Array.isArray(resp)).toBe(true);
|
|
393
395
|
|
|
@@ -404,7 +406,7 @@ describe("Stocks Client", () => {
|
|
|
404
406
|
|
|
405
407
|
describe("getEarningsTranscript", () => {
|
|
406
408
|
test("should return earnings transcript detail", async () => {
|
|
407
|
-
const resp = await client.getEarningsTranscript("
|
|
409
|
+
const resp = await client.getEarningsTranscript("AAPL", 2023, 4);
|
|
408
410
|
|
|
409
411
|
expect(resp).toBeDefined();
|
|
410
412
|
expect(typeof resp.symbol).toBe("string");
|
|
@@ -474,7 +476,7 @@ describe("Stocks Client", () => {
|
|
|
474
476
|
|
|
475
477
|
describe("getState", () => {
|
|
476
478
|
test("should return single market state", async () => {
|
|
477
|
-
const resp = await client.getState("
|
|
479
|
+
const resp = await client.getState("BIST");
|
|
478
480
|
|
|
479
481
|
expect(resp).toBeDefined();
|
|
480
482
|
expect(typeof resp.id).toBe("number");
|
|
@@ -486,6 +488,18 @@ describe("Stocks Client", () => {
|
|
|
486
488
|
}
|
|
487
489
|
});
|
|
488
490
|
});
|
|
491
|
+
|
|
492
|
+
describe("getStockChartImage", () => {
|
|
493
|
+
test("should return chart image blob", async () => {
|
|
494
|
+
const resp = await client.getStockChartImage({
|
|
495
|
+
symbol: "TUPRS",
|
|
496
|
+
region: Region.Tr,
|
|
497
|
+
});
|
|
498
|
+
expect(resp).toBeDefined();
|
|
499
|
+
expect(resp).toBeInstanceOf(Blob);
|
|
500
|
+
expect(resp.size).toBeGreaterThan(0);
|
|
501
|
+
}, 10000);
|
|
502
|
+
});
|
|
489
503
|
});
|
|
490
504
|
|
|
491
505
|
describe("Mock Tests", () => {
|
|
@@ -678,12 +692,12 @@ describe("Stocks Client", () => {
|
|
|
678
692
|
test("should return earnings transcript list with mock data", async () => {
|
|
679
693
|
jest.spyOn(client, 'getEarningsTranscripts').mockResolvedValue(mockEarningsTranscriptList);
|
|
680
694
|
|
|
681
|
-
const resp = await client.getEarningsTranscripts("
|
|
695
|
+
const resp = await client.getEarningsTranscripts("AAPL", Region.Us);
|
|
682
696
|
|
|
683
697
|
expect(resp).toHaveLength(2);
|
|
684
698
|
|
|
685
699
|
const firstTranscript = resp[0];
|
|
686
|
-
expect(firstTranscript.symbol).toBe("
|
|
700
|
+
expect(firstTranscript.symbol).toBe("AAPL");
|
|
687
701
|
expect(firstTranscript.year).toBe(2024);
|
|
688
702
|
expect(firstTranscript.quarter).toBe(1);
|
|
689
703
|
expect(firstTranscript.date).toBe("2024-05-15");
|
|
@@ -693,14 +707,18 @@ describe("Stocks Client", () => {
|
|
|
693
707
|
expect(secondTranscript.year).toBe(2023);
|
|
694
708
|
expect(secondTranscript.quarter).toBe(4);
|
|
695
709
|
|
|
696
|
-
expect(client.getEarningsTranscripts).toHaveBeenCalledWith(
|
|
710
|
+
expect(client.getEarningsTranscripts).toHaveBeenCalledWith(
|
|
711
|
+
"AAPL",
|
|
712
|
+
Region.Us
|
|
713
|
+
);
|
|
697
714
|
});
|
|
698
715
|
|
|
699
716
|
test("should handle API errors for earnings transcripts", async () => {
|
|
700
717
|
jest.spyOn(client, 'getEarningsTranscripts').mockRejectedValue(new Error("Transcripts not found"));
|
|
701
718
|
|
|
702
|
-
await expect(
|
|
703
|
-
.
|
|
719
|
+
await expect(
|
|
720
|
+
client.getEarningsTranscripts("INVALID", Region.Us)
|
|
721
|
+
).rejects.toThrow("Transcripts not found");
|
|
704
722
|
});
|
|
705
723
|
});
|
|
706
724
|
|
|
@@ -708,9 +726,9 @@ describe("Stocks Client", () => {
|
|
|
708
726
|
test("should return earnings transcript detail with mock data", async () => {
|
|
709
727
|
jest.spyOn(client, 'getEarningsTranscript').mockResolvedValue(mockEarningsTranscriptDetail);
|
|
710
728
|
|
|
711
|
-
const resp = await client.getEarningsTranscript("
|
|
729
|
+
const resp = await client.getEarningsTranscript("AAPL", 2024, 1);
|
|
712
730
|
|
|
713
|
-
expect(resp.symbol).toBe("
|
|
731
|
+
expect(resp.symbol).toBe("AAPL");
|
|
714
732
|
expect(resp.year).toBe(2024);
|
|
715
733
|
expect(resp.quarter).toBe(1);
|
|
716
734
|
expect(resp.date).toBe("2024-05-15");
|
|
@@ -718,14 +736,19 @@ describe("Stocks Client", () => {
|
|
|
718
736
|
expect(resp.summary).toBe("Strong Q1 performance with 15% revenue growth");
|
|
719
737
|
expect(resp.has_summary).toBe(true);
|
|
720
738
|
|
|
721
|
-
expect(client.getEarningsTranscript).toHaveBeenCalledWith(
|
|
739
|
+
expect(client.getEarningsTranscript).toHaveBeenCalledWith(
|
|
740
|
+
"AAPL",
|
|
741
|
+
2024,
|
|
742
|
+
1
|
|
743
|
+
);
|
|
722
744
|
});
|
|
723
745
|
|
|
724
746
|
test("should handle API errors for earnings transcript detail", async () => {
|
|
725
747
|
jest.spyOn(client, 'getEarningsTranscript').mockRejectedValue(new Error("Transcript not found"));
|
|
726
748
|
|
|
727
|
-
await expect(
|
|
728
|
-
.
|
|
749
|
+
await expect(
|
|
750
|
+
client.getEarningsTranscript("AAPL", 2020, 1)
|
|
751
|
+
).rejects.toThrow("Transcript not found");
|
|
729
752
|
});
|
|
730
753
|
});
|
|
731
754
|
|
|
@@ -740,7 +763,7 @@ describe("Stocks Client", () => {
|
|
|
740
763
|
|
|
741
764
|
const firstState = resp.items[0];
|
|
742
765
|
expect(firstState.id).toBe(1);
|
|
743
|
-
expect(firstState.marketSymbol).toBe("
|
|
766
|
+
expect(firstState.marketSymbol).toBe("BIST");
|
|
744
767
|
expect(firstState.state).toBe("OPEN");
|
|
745
768
|
expect(firstState.stockSymbol).toBe("TUPRS");
|
|
746
769
|
|
|
@@ -762,7 +785,7 @@ describe("Stocks Client", () => {
|
|
|
762
785
|
const resp = await client.getStockState("TUPRS");
|
|
763
786
|
|
|
764
787
|
expect(resp.id).toBe(1);
|
|
765
|
-
expect(resp.marketSymbol).toBe("
|
|
788
|
+
expect(resp.marketSymbol).toBe("BIST");
|
|
766
789
|
expect(resp.state).toBe("OPEN");
|
|
767
790
|
expect(resp.lastTimestamp).toBe("2024-03-14T10:00:00Z");
|
|
768
791
|
expect(resp.stockSymbol).toBe("TUPRS");
|
|
@@ -806,14 +829,14 @@ describe("Stocks Client", () => {
|
|
|
806
829
|
test("should return single market state with mock data", async () => {
|
|
807
830
|
jest.spyOn(client, 'getState').mockResolvedValue(mockSingleMarketState);
|
|
808
831
|
|
|
809
|
-
const resp = await client.getState("
|
|
832
|
+
const resp = await client.getState("BIST");
|
|
810
833
|
|
|
811
834
|
expect(resp.id).toBe(1);
|
|
812
|
-
expect(resp.marketSymbol).toBe("
|
|
835
|
+
expect(resp.marketSymbol).toBe("BIST");
|
|
813
836
|
expect(resp.state).toBe("OPEN");
|
|
814
837
|
expect(resp.lastTimestamp).toBe("2024-03-14T10:00:00Z");
|
|
815
838
|
|
|
816
|
-
expect(client.getState).toHaveBeenCalledWith("
|
|
839
|
+
expect(client.getState).toHaveBeenCalledWith("BIST");
|
|
817
840
|
});
|
|
818
841
|
|
|
819
842
|
test("should handle API errors for single state", async () => {
|
|
@@ -823,5 +846,29 @@ describe("Stocks Client", () => {
|
|
|
823
846
|
.rejects.toThrow("Market state not found");
|
|
824
847
|
});
|
|
825
848
|
});
|
|
849
|
+
|
|
850
|
+
describe("getStockChartImage", () => {
|
|
851
|
+
test("should return chart image with mock data", async () => {
|
|
852
|
+
jest.spyOn(client, 'getStockChartImage').mockResolvedValue(mockChartImageBlob);
|
|
853
|
+
|
|
854
|
+
const resp = await client.getStockChartImage({
|
|
855
|
+
symbol: "TUPRS",
|
|
856
|
+
region: Region.Tr,
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
expect(resp).toBeDefined();
|
|
860
|
+
expect(resp).toBeInstanceOf(Blob);
|
|
861
|
+
expect(resp.type).toBe('image/png');
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test("should handle API errors for chart image", async () => {
|
|
865
|
+
jest.spyOn(client, 'getStockChartImage').mockRejectedValue(new Error("Failed to generate chart"));
|
|
866
|
+
|
|
867
|
+
await expect(client.getStockChartImage({
|
|
868
|
+
symbol: "INVALID",
|
|
869
|
+
region: Region.Tr
|
|
870
|
+
})).rejects.toThrow("Failed to generate chart");
|
|
871
|
+
});
|
|
872
|
+
});
|
|
826
873
|
});
|
|
827
874
|
});
|