laplace-api 4.4.0 → 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/stocks.ts +7 -5
- package/src/test/stocks.test.ts +35 -27
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/stocks.ts
CHANGED
|
@@ -298,12 +298,14 @@ export class StockClient extends Client {
|
|
|
298
298
|
if (request.resolution) params.resolution = request.resolution;
|
|
299
299
|
if (request.indicators) params.indicators = request.indicators;
|
|
300
300
|
if (request.chartType != null) params.chartType = request.chartType;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
method:
|
|
304
|
-
url:
|
|
301
|
+
|
|
302
|
+
const data = await this.sendRequest<ArrayBuffer>({
|
|
303
|
+
method: "GET",
|
|
304
|
+
url: "/api/v1/stock/chart",
|
|
305
305
|
params,
|
|
306
|
-
responseType:
|
|
306
|
+
responseType: "arraybuffer",
|
|
307
307
|
});
|
|
308
|
+
|
|
309
|
+
return new Blob([data], { type: "image/png" });
|
|
308
310
|
}
|
|
309
311
|
}
|
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,7 +174,7 @@ 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"
|
|
@@ -389,7 +389,7 @@ describe("Stocks Client", () => {
|
|
|
389
389
|
|
|
390
390
|
describe("getEarningsTranscripts", () => {
|
|
391
391
|
test("should return earnings transcript list", async () => {
|
|
392
|
-
const resp = await client.getEarningsTranscripts("
|
|
392
|
+
const resp = await client.getEarningsTranscripts("AAPL", Region.Us);
|
|
393
393
|
|
|
394
394
|
expect(Array.isArray(resp)).toBe(true);
|
|
395
395
|
|
|
@@ -406,7 +406,7 @@ describe("Stocks Client", () => {
|
|
|
406
406
|
|
|
407
407
|
describe("getEarningsTranscript", () => {
|
|
408
408
|
test("should return earnings transcript detail", async () => {
|
|
409
|
-
const resp = await client.getEarningsTranscript("
|
|
409
|
+
const resp = await client.getEarningsTranscript("AAPL", 2023, 4);
|
|
410
410
|
|
|
411
411
|
expect(resp).toBeDefined();
|
|
412
412
|
expect(typeof resp.symbol).toBe("string");
|
|
@@ -476,7 +476,7 @@ describe("Stocks Client", () => {
|
|
|
476
476
|
|
|
477
477
|
describe("getState", () => {
|
|
478
478
|
test("should return single market state", async () => {
|
|
479
|
-
const resp = await client.getState("
|
|
479
|
+
const resp = await client.getState("BIST");
|
|
480
480
|
|
|
481
481
|
expect(resp).toBeDefined();
|
|
482
482
|
expect(typeof resp.id).toBe("number");
|
|
@@ -495,11 +495,10 @@ describe("Stocks Client", () => {
|
|
|
495
495
|
symbol: "TUPRS",
|
|
496
496
|
region: Region.Tr,
|
|
497
497
|
});
|
|
498
|
-
|
|
499
498
|
expect(resp).toBeDefined();
|
|
500
499
|
expect(resp).toBeInstanceOf(Blob);
|
|
501
500
|
expect(resp.size).toBeGreaterThan(0);
|
|
502
|
-
});
|
|
501
|
+
}, 10000);
|
|
503
502
|
});
|
|
504
503
|
});
|
|
505
504
|
|
|
@@ -693,12 +692,12 @@ describe("Stocks Client", () => {
|
|
|
693
692
|
test("should return earnings transcript list with mock data", async () => {
|
|
694
693
|
jest.spyOn(client, 'getEarningsTranscripts').mockResolvedValue(mockEarningsTranscriptList);
|
|
695
694
|
|
|
696
|
-
const resp = await client.getEarningsTranscripts("
|
|
695
|
+
const resp = await client.getEarningsTranscripts("AAPL", Region.Us);
|
|
697
696
|
|
|
698
697
|
expect(resp).toHaveLength(2);
|
|
699
698
|
|
|
700
699
|
const firstTranscript = resp[0];
|
|
701
|
-
expect(firstTranscript.symbol).toBe("
|
|
700
|
+
expect(firstTranscript.symbol).toBe("AAPL");
|
|
702
701
|
expect(firstTranscript.year).toBe(2024);
|
|
703
702
|
expect(firstTranscript.quarter).toBe(1);
|
|
704
703
|
expect(firstTranscript.date).toBe("2024-05-15");
|
|
@@ -708,14 +707,18 @@ describe("Stocks Client", () => {
|
|
|
708
707
|
expect(secondTranscript.year).toBe(2023);
|
|
709
708
|
expect(secondTranscript.quarter).toBe(4);
|
|
710
709
|
|
|
711
|
-
expect(client.getEarningsTranscripts).toHaveBeenCalledWith(
|
|
710
|
+
expect(client.getEarningsTranscripts).toHaveBeenCalledWith(
|
|
711
|
+
"AAPL",
|
|
712
|
+
Region.Us
|
|
713
|
+
);
|
|
712
714
|
});
|
|
713
715
|
|
|
714
716
|
test("should handle API errors for earnings transcripts", async () => {
|
|
715
717
|
jest.spyOn(client, 'getEarningsTranscripts').mockRejectedValue(new Error("Transcripts not found"));
|
|
716
718
|
|
|
717
|
-
await expect(
|
|
718
|
-
.
|
|
719
|
+
await expect(
|
|
720
|
+
client.getEarningsTranscripts("INVALID", Region.Us)
|
|
721
|
+
).rejects.toThrow("Transcripts not found");
|
|
719
722
|
});
|
|
720
723
|
});
|
|
721
724
|
|
|
@@ -723,9 +726,9 @@ describe("Stocks Client", () => {
|
|
|
723
726
|
test("should return earnings transcript detail with mock data", async () => {
|
|
724
727
|
jest.spyOn(client, 'getEarningsTranscript').mockResolvedValue(mockEarningsTranscriptDetail);
|
|
725
728
|
|
|
726
|
-
const resp = await client.getEarningsTranscript("
|
|
729
|
+
const resp = await client.getEarningsTranscript("AAPL", 2024, 1);
|
|
727
730
|
|
|
728
|
-
expect(resp.symbol).toBe("
|
|
731
|
+
expect(resp.symbol).toBe("AAPL");
|
|
729
732
|
expect(resp.year).toBe(2024);
|
|
730
733
|
expect(resp.quarter).toBe(1);
|
|
731
734
|
expect(resp.date).toBe("2024-05-15");
|
|
@@ -733,14 +736,19 @@ describe("Stocks Client", () => {
|
|
|
733
736
|
expect(resp.summary).toBe("Strong Q1 performance with 15% revenue growth");
|
|
734
737
|
expect(resp.has_summary).toBe(true);
|
|
735
738
|
|
|
736
|
-
expect(client.getEarningsTranscript).toHaveBeenCalledWith(
|
|
739
|
+
expect(client.getEarningsTranscript).toHaveBeenCalledWith(
|
|
740
|
+
"AAPL",
|
|
741
|
+
2024,
|
|
742
|
+
1
|
|
743
|
+
);
|
|
737
744
|
});
|
|
738
745
|
|
|
739
746
|
test("should handle API errors for earnings transcript detail", async () => {
|
|
740
747
|
jest.spyOn(client, 'getEarningsTranscript').mockRejectedValue(new Error("Transcript not found"));
|
|
741
748
|
|
|
742
|
-
await expect(
|
|
743
|
-
.
|
|
749
|
+
await expect(
|
|
750
|
+
client.getEarningsTranscript("AAPL", 2020, 1)
|
|
751
|
+
).rejects.toThrow("Transcript not found");
|
|
744
752
|
});
|
|
745
753
|
});
|
|
746
754
|
|
|
@@ -755,7 +763,7 @@ describe("Stocks Client", () => {
|
|
|
755
763
|
|
|
756
764
|
const firstState = resp.items[0];
|
|
757
765
|
expect(firstState.id).toBe(1);
|
|
758
|
-
expect(firstState.marketSymbol).toBe("
|
|
766
|
+
expect(firstState.marketSymbol).toBe("BIST");
|
|
759
767
|
expect(firstState.state).toBe("OPEN");
|
|
760
768
|
expect(firstState.stockSymbol).toBe("TUPRS");
|
|
761
769
|
|
|
@@ -777,7 +785,7 @@ describe("Stocks Client", () => {
|
|
|
777
785
|
const resp = await client.getStockState("TUPRS");
|
|
778
786
|
|
|
779
787
|
expect(resp.id).toBe(1);
|
|
780
|
-
expect(resp.marketSymbol).toBe("
|
|
788
|
+
expect(resp.marketSymbol).toBe("BIST");
|
|
781
789
|
expect(resp.state).toBe("OPEN");
|
|
782
790
|
expect(resp.lastTimestamp).toBe("2024-03-14T10:00:00Z");
|
|
783
791
|
expect(resp.stockSymbol).toBe("TUPRS");
|
|
@@ -821,14 +829,14 @@ describe("Stocks Client", () => {
|
|
|
821
829
|
test("should return single market state with mock data", async () => {
|
|
822
830
|
jest.spyOn(client, 'getState').mockResolvedValue(mockSingleMarketState);
|
|
823
831
|
|
|
824
|
-
const resp = await client.getState("
|
|
832
|
+
const resp = await client.getState("BIST");
|
|
825
833
|
|
|
826
834
|
expect(resp.id).toBe(1);
|
|
827
|
-
expect(resp.marketSymbol).toBe("
|
|
835
|
+
expect(resp.marketSymbol).toBe("BIST");
|
|
828
836
|
expect(resp.state).toBe("OPEN");
|
|
829
837
|
expect(resp.lastTimestamp).toBe("2024-03-14T10:00:00Z");
|
|
830
838
|
|
|
831
|
-
expect(client.getState).toHaveBeenCalledWith("
|
|
839
|
+
expect(client.getState).toHaveBeenCalledWith("BIST");
|
|
832
840
|
});
|
|
833
841
|
|
|
834
842
|
test("should handle API errors for single state", async () => {
|