laplace-api 3.0.0 → 3.1.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
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { Client } from "./client";
|
|
2
|
+
import { Region } from "./collections";
|
|
3
|
+
import { AssetClass, AssetType } from "./stocks";
|
|
4
|
+
|
|
5
|
+
export enum BrokerSort {
|
|
6
|
+
NetBuy = "netBuy",
|
|
7
|
+
NetSell = "netSell",
|
|
8
|
+
Volume = "volume",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Broker {
|
|
12
|
+
id: number;
|
|
13
|
+
symbol: string;
|
|
14
|
+
name: string;
|
|
15
|
+
longName: string;
|
|
16
|
+
logo: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BrokerStock {
|
|
20
|
+
symbol: string;
|
|
21
|
+
name: string;
|
|
22
|
+
id: string;
|
|
23
|
+
assetType: AssetType;
|
|
24
|
+
assetClass: AssetClass;
|
|
25
|
+
region: Region;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface BaseBrokerStats {
|
|
29
|
+
totalBuyAmount: number;
|
|
30
|
+
totalSellAmount: number;
|
|
31
|
+
netAmount: number;
|
|
32
|
+
totalBuyVolume: number;
|
|
33
|
+
totalSellVolume: number;
|
|
34
|
+
totalVolume: number;
|
|
35
|
+
totalAmount: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BrokerStats extends BaseBrokerStats {
|
|
39
|
+
broker: Broker;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MarketBrokersResponse {
|
|
43
|
+
recordCount: number;
|
|
44
|
+
totalStats: BaseBrokerStats;
|
|
45
|
+
items: BrokerStats[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TopBrokersResponse {
|
|
49
|
+
topStats: BaseBrokerStats;
|
|
50
|
+
restStats: BaseBrokerStats;
|
|
51
|
+
topItems: BrokerStats[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface StockBrokerStats extends BaseBrokerStats {
|
|
55
|
+
averageCost: number;
|
|
56
|
+
broker: Broker;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface StockOverallStats extends BaseBrokerStats {
|
|
60
|
+
averageCost: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface StockBrokersResponse {
|
|
64
|
+
recordCount: number;
|
|
65
|
+
totalStats: StockOverallStats;
|
|
66
|
+
items: StockBrokerStats[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface TopStockBrokersResponse {
|
|
70
|
+
topStats: StockOverallStats;
|
|
71
|
+
restStats: StockOverallStats;
|
|
72
|
+
topItems: StockBrokerStats[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface BrokerStockStats extends BaseBrokerStats {
|
|
76
|
+
stock: BrokerStock;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface TopStocksForBrokerResponse {
|
|
80
|
+
topStats: BaseBrokerStats;
|
|
81
|
+
restStats: BaseBrokerStats;
|
|
82
|
+
topItems: BrokerStockStats[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class BrokerClient extends Client {
|
|
86
|
+
private static readonly BASE = "/api/v1/brokers";
|
|
87
|
+
|
|
88
|
+
async getMarketBrokers(
|
|
89
|
+
region: Region,
|
|
90
|
+
fromDate: string,
|
|
91
|
+
toDate: string,
|
|
92
|
+
sortBy: BrokerSort,
|
|
93
|
+
page: number = 0,
|
|
94
|
+
size: number = 10
|
|
95
|
+
): Promise<MarketBrokersResponse> {
|
|
96
|
+
return this.sendRequest<MarketBrokersResponse>({
|
|
97
|
+
method: "GET",
|
|
98
|
+
url: BrokerClient.BASE + "/market",
|
|
99
|
+
params: {
|
|
100
|
+
region,
|
|
101
|
+
fromDate,
|
|
102
|
+
toDate,
|
|
103
|
+
sortBy,
|
|
104
|
+
page,
|
|
105
|
+
size,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getTopMarketBrokers(
|
|
111
|
+
region: Region,
|
|
112
|
+
fromDate: string,
|
|
113
|
+
toDate: string,
|
|
114
|
+
sortBy: BrokerSort,
|
|
115
|
+
top: number = 5
|
|
116
|
+
): Promise<TopBrokersResponse> {
|
|
117
|
+
return this.sendRequest<TopBrokersResponse>({
|
|
118
|
+
method: "GET",
|
|
119
|
+
url: BrokerClient.BASE + "/market/top",
|
|
120
|
+
params: {
|
|
121
|
+
region,
|
|
122
|
+
fromDate,
|
|
123
|
+
toDate,
|
|
124
|
+
sortBy,
|
|
125
|
+
top,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async getStockBrokers(
|
|
131
|
+
region: Region,
|
|
132
|
+
fromDate: string,
|
|
133
|
+
toDate: string,
|
|
134
|
+
sortBy: BrokerSort,
|
|
135
|
+
symbol: string,
|
|
136
|
+
page: number = 0,
|
|
137
|
+
size: number = 10
|
|
138
|
+
): Promise<StockBrokersResponse> {
|
|
139
|
+
return this.sendRequest<StockBrokersResponse>({
|
|
140
|
+
method: "GET",
|
|
141
|
+
url: BrokerClient.BASE + "/stock",
|
|
142
|
+
params: {
|
|
143
|
+
region,
|
|
144
|
+
fromDate,
|
|
145
|
+
toDate,
|
|
146
|
+
sortBy,
|
|
147
|
+
page,
|
|
148
|
+
size,
|
|
149
|
+
symbol,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getTopStockBrokers(
|
|
155
|
+
region: Region,
|
|
156
|
+
fromDate: string,
|
|
157
|
+
toDate: string,
|
|
158
|
+
sortBy: BrokerSort,
|
|
159
|
+
symbol: string,
|
|
160
|
+
top: number = 5
|
|
161
|
+
): Promise<TopStockBrokersResponse> {
|
|
162
|
+
return this.sendRequest<TopStockBrokersResponse>({
|
|
163
|
+
method: "GET",
|
|
164
|
+
url: BrokerClient.BASE + "/stock/top",
|
|
165
|
+
params: {
|
|
166
|
+
region,
|
|
167
|
+
fromDate,
|
|
168
|
+
toDate,
|
|
169
|
+
sortBy,
|
|
170
|
+
top,
|
|
171
|
+
symbol,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getTopStocksForBroker(
|
|
177
|
+
region: Region,
|
|
178
|
+
fromDate: string,
|
|
179
|
+
toDate: string,
|
|
180
|
+
sortBy: BrokerSort,
|
|
181
|
+
brokerSymbol: string,
|
|
182
|
+
top: number = 5
|
|
183
|
+
): Promise<TopStocksForBrokerResponse> {
|
|
184
|
+
return this.sendRequest<TopStocksForBrokerResponse>({
|
|
185
|
+
method: "GET",
|
|
186
|
+
url: BrokerClient.BASE + "/top",
|
|
187
|
+
params: {
|
|
188
|
+
region,
|
|
189
|
+
fromDate,
|
|
190
|
+
toDate,
|
|
191
|
+
sortBy,
|
|
192
|
+
top,
|
|
193
|
+
brokerSymbol,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -131,7 +131,7 @@ export interface HistoricalFinancialSheets {
|
|
|
131
131
|
|
|
132
132
|
export interface HistoricalFinancialSheet {
|
|
133
133
|
period: string;
|
|
134
|
-
|
|
134
|
+
items: HistoricalFinancialSheetRow[];
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export interface HistoricalFinancialSheetRow {
|
|
@@ -107,6 +107,9 @@ export class LivePriceWebSocketClient {
|
|
|
107
107
|
private wsUrl: string | null = null;
|
|
108
108
|
private readonly options: Required<WebSocketOptions>;
|
|
109
109
|
private connectPromise: Promise<void> | null = null;
|
|
110
|
+
private lastMessageTimestamp: number = 0;
|
|
111
|
+
private inactivityCheckInterval: NodeJS.Timeout | null = null;
|
|
112
|
+
private readonly INACTIVITY_TIMEOUT = 15000;
|
|
110
113
|
|
|
111
114
|
constructor(options: WebSocketOptions = {}) {
|
|
112
115
|
this.options = {
|
|
@@ -119,6 +122,32 @@ export class LivePriceWebSocketClient {
|
|
|
119
122
|
};
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
private startInactivityInterval() {
|
|
126
|
+
this.lastMessageTimestamp = Date.now();
|
|
127
|
+
if (this.inactivityCheckInterval) {
|
|
128
|
+
clearInterval(this.inactivityCheckInterval);
|
|
129
|
+
this.inactivityCheckInterval = null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.inactivityCheckInterval = setInterval(async ()=> {
|
|
133
|
+
if (Date.now() - this.lastMessageTimestamp >= this.INACTIVITY_TIMEOUT) {
|
|
134
|
+
this.stopInactivityInterval();
|
|
135
|
+
try {
|
|
136
|
+
this.attemptReconnect();
|
|
137
|
+
} catch(error) {
|
|
138
|
+
this.log(`Failed to reconnect: ${error}`, "error");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, this.INACTIVITY_TIMEOUT)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private stopInactivityInterval() {
|
|
145
|
+
if (this.inactivityCheckInterval) {
|
|
146
|
+
clearInterval(this.inactivityCheckInterval);
|
|
147
|
+
this.inactivityCheckInterval = null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
122
151
|
private log(message: string, level: "info" | "error" | "warn" = "info") {
|
|
123
152
|
if (!this.options.enableLogging) return;
|
|
124
153
|
|
|
@@ -183,6 +212,7 @@ export class LivePriceWebSocketClient {
|
|
|
183
212
|
this.ws.onopen = () => {
|
|
184
213
|
this.reconnectAttempts = 0;
|
|
185
214
|
this.log("WebSocket connected");
|
|
215
|
+
this.startInactivityInterval();
|
|
186
216
|
resolve();
|
|
187
217
|
};
|
|
188
218
|
|
|
@@ -198,6 +228,8 @@ export class LivePriceWebSocketClient {
|
|
|
198
228
|
this.ws.onclose = () => {
|
|
199
229
|
this.isClosed = true;
|
|
200
230
|
this.log("WebSocket closed");
|
|
231
|
+
|
|
232
|
+
this.stopInactivityInterval();
|
|
201
233
|
if (this.closedReason !== WebSocketCloseReason.NORMAL_CLOSURE) {
|
|
202
234
|
try {
|
|
203
235
|
this.attemptReconnect();
|
|
@@ -225,6 +257,7 @@ export class LivePriceWebSocketClient {
|
|
|
225
257
|
};
|
|
226
258
|
|
|
227
259
|
this.ws.onmessage = (event) => {
|
|
260
|
+
this.lastMessageTimestamp = Date.now();
|
|
228
261
|
try {
|
|
229
262
|
const rawData = JSON.parse(event.data.toString());
|
|
230
263
|
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { Logger } from "winston";
|
|
2
|
+
import { Region } from "../client/collections";
|
|
3
|
+
import { LaplaceConfiguration } from "../utilities/configuration";
|
|
4
|
+
import "./client_test_suite";
|
|
5
|
+
import {
|
|
6
|
+
BaseBrokerStats,
|
|
7
|
+
BrokerClient,
|
|
8
|
+
BrokerSort,
|
|
9
|
+
BrokerStats,
|
|
10
|
+
BrokerStockStats,
|
|
11
|
+
StockBrokerStats,
|
|
12
|
+
StockOverallStats,
|
|
13
|
+
} from "../client/broker";
|
|
14
|
+
|
|
15
|
+
describe("BrokerClient", () => {
|
|
16
|
+
let brokerClient: BrokerClient;
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
const config = (global as any).testSuite.config as LaplaceConfiguration;
|
|
20
|
+
|
|
21
|
+
const logger: Logger = {
|
|
22
|
+
info: jest.fn(),
|
|
23
|
+
error: jest.fn(),
|
|
24
|
+
warn: jest.fn(),
|
|
25
|
+
debug: jest.fn(),
|
|
26
|
+
} as unknown as Logger;
|
|
27
|
+
|
|
28
|
+
brokerClient = new BrokerClient(config, logger);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const region = Region.Tr;
|
|
32
|
+
const fromDate = "2025-05-20";
|
|
33
|
+
const toDate = "2025-05-28";
|
|
34
|
+
|
|
35
|
+
test("getMarketBrokers returns valid and fully typed data", async () => {
|
|
36
|
+
const response = await brokerClient.getMarketBrokers(
|
|
37
|
+
Region.Tr,
|
|
38
|
+
"2025-05-27",
|
|
39
|
+
"2025-05-28",
|
|
40
|
+
BrokerSort.Volume,
|
|
41
|
+
0,
|
|
42
|
+
5
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(response).toBeDefined();
|
|
46
|
+
expect(typeof response.recordCount).toBe("number");
|
|
47
|
+
|
|
48
|
+
const stats = response.totalStats;
|
|
49
|
+
expect(stats).toMatchObject<BaseBrokerStats>({
|
|
50
|
+
totalBuyAmount: expect.any(Number),
|
|
51
|
+
totalSellAmount: expect.any(Number),
|
|
52
|
+
netAmount: expect.any(Number),
|
|
53
|
+
totalBuyVolume: expect.any(Number),
|
|
54
|
+
totalSellVolume: expect.any(Number),
|
|
55
|
+
totalVolume: expect.any(Number),
|
|
56
|
+
totalAmount: expect.any(Number),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(Array.isArray(response.items)).toBe(true);
|
|
60
|
+
expect(response.items.length).toBeGreaterThan(0);
|
|
61
|
+
|
|
62
|
+
for (const item of response.items) {
|
|
63
|
+
expect(item).toMatchObject<BrokerStats>({
|
|
64
|
+
totalBuyAmount: expect.any(Number),
|
|
65
|
+
totalSellAmount: expect.any(Number),
|
|
66
|
+
netAmount: expect.any(Number),
|
|
67
|
+
totalBuyVolume: expect.any(Number),
|
|
68
|
+
totalSellVolume: expect.any(Number),
|
|
69
|
+
totalVolume: expect.any(Number),
|
|
70
|
+
totalAmount: expect.any(Number),
|
|
71
|
+
broker: {
|
|
72
|
+
id: expect.any(Number),
|
|
73
|
+
symbol: expect.any(String),
|
|
74
|
+
name: expect.any(String),
|
|
75
|
+
longName: expect.any(String),
|
|
76
|
+
logo: expect.any(String),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("getTopMarketBrokers returns fully typed top and rest stats", async () => {
|
|
83
|
+
const response = await brokerClient.getTopMarketBrokers(
|
|
84
|
+
region,
|
|
85
|
+
fromDate,
|
|
86
|
+
toDate,
|
|
87
|
+
BrokerSort.Volume
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(response).toBeDefined();
|
|
91
|
+
|
|
92
|
+
const statsList = [response.topStats, response.restStats];
|
|
93
|
+
for (const stats of statsList) {
|
|
94
|
+
expect(stats).toMatchObject<BaseBrokerStats>({
|
|
95
|
+
totalBuyAmount: expect.any(Number),
|
|
96
|
+
totalSellAmount: expect.any(Number),
|
|
97
|
+
netAmount: expect.any(Number),
|
|
98
|
+
totalBuyVolume: expect.any(Number),
|
|
99
|
+
totalSellVolume: expect.any(Number),
|
|
100
|
+
totalVolume: expect.any(Number),
|
|
101
|
+
totalAmount: expect.any(Number),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
expect(Array.isArray(response.topItems)).toBe(true);
|
|
106
|
+
expect(response.topItems.length).toBeGreaterThan(0);
|
|
107
|
+
|
|
108
|
+
for (const item of response.topItems) {
|
|
109
|
+
expect(item).toMatchObject<BrokerStats>({
|
|
110
|
+
totalBuyAmount: expect.any(Number),
|
|
111
|
+
totalSellAmount: expect.any(Number),
|
|
112
|
+
netAmount: expect.any(Number),
|
|
113
|
+
totalBuyVolume: expect.any(Number),
|
|
114
|
+
totalSellVolume: expect.any(Number),
|
|
115
|
+
totalVolume: expect.any(Number),
|
|
116
|
+
totalAmount: expect.any(Number),
|
|
117
|
+
broker: {
|
|
118
|
+
id: expect.any(Number),
|
|
119
|
+
symbol: expect.any(String),
|
|
120
|
+
name: expect.any(String),
|
|
121
|
+
longName: expect.any(String),
|
|
122
|
+
logo: expect.any(String),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("getStockBrokers returns full broker stats with averageCost", async () => {
|
|
129
|
+
const response = await brokerClient.getStockBrokers(
|
|
130
|
+
region,
|
|
131
|
+
fromDate,
|
|
132
|
+
toDate,
|
|
133
|
+
BrokerSort.Volume,
|
|
134
|
+
"TUPRS",
|
|
135
|
+
0,
|
|
136
|
+
5
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
expect(response).toBeDefined();
|
|
140
|
+
expect(typeof response.recordCount).toBe("number");
|
|
141
|
+
|
|
142
|
+
expect(response.totalStats).toMatchObject<StockOverallStats>({
|
|
143
|
+
totalBuyAmount: expect.any(Number),
|
|
144
|
+
totalSellAmount: expect.any(Number),
|
|
145
|
+
netAmount: expect.any(Number),
|
|
146
|
+
totalBuyVolume: expect.any(Number),
|
|
147
|
+
totalSellVolume: expect.any(Number),
|
|
148
|
+
totalVolume: expect.any(Number),
|
|
149
|
+
totalAmount: expect.any(Number),
|
|
150
|
+
averageCost: expect.any(Number),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
for (const item of response.items) {
|
|
154
|
+
expect(item).toMatchObject<StockBrokerStats>({
|
|
155
|
+
averageCost: expect.any(Number),
|
|
156
|
+
totalBuyAmount: expect.any(Number),
|
|
157
|
+
totalSellAmount: expect.any(Number),
|
|
158
|
+
netAmount: expect.any(Number),
|
|
159
|
+
totalBuyVolume: expect.any(Number),
|
|
160
|
+
totalSellVolume: expect.any(Number),
|
|
161
|
+
totalVolume: expect.any(Number),
|
|
162
|
+
totalAmount: expect.any(Number),
|
|
163
|
+
broker: {
|
|
164
|
+
id: expect.any(Number),
|
|
165
|
+
symbol: expect.any(String),
|
|
166
|
+
name: expect.any(String),
|
|
167
|
+
longName: expect.any(String),
|
|
168
|
+
logo: expect.any(String),
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("getTopStockBrokers returns fully typed top & rest stats with averageCost", async () => {
|
|
175
|
+
const response = await brokerClient.getTopStockBrokers(
|
|
176
|
+
region,
|
|
177
|
+
fromDate,
|
|
178
|
+
toDate,
|
|
179
|
+
BrokerSort.Volume,
|
|
180
|
+
"TUPRS"
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
expect(response).toBeDefined();
|
|
184
|
+
|
|
185
|
+
for (const stats of [response.topStats, response.restStats]) {
|
|
186
|
+
expect(stats).toMatchObject<StockOverallStats>({
|
|
187
|
+
totalBuyAmount: expect.any(Number),
|
|
188
|
+
totalSellAmount: expect.any(Number),
|
|
189
|
+
netAmount: expect.any(Number),
|
|
190
|
+
totalBuyVolume: expect.any(Number),
|
|
191
|
+
totalSellVolume: expect.any(Number),
|
|
192
|
+
totalVolume: expect.any(Number),
|
|
193
|
+
totalAmount: expect.any(Number),
|
|
194
|
+
averageCost: expect.any(Number),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (const item of response.topItems) {
|
|
199
|
+
expect(item).toMatchObject<StockBrokerStats>({
|
|
200
|
+
averageCost: expect.any(Number),
|
|
201
|
+
totalBuyAmount: expect.any(Number),
|
|
202
|
+
totalSellAmount: expect.any(Number),
|
|
203
|
+
netAmount: expect.any(Number),
|
|
204
|
+
totalBuyVolume: expect.any(Number),
|
|
205
|
+
totalSellVolume: expect.any(Number),
|
|
206
|
+
totalVolume: expect.any(Number),
|
|
207
|
+
totalAmount: expect.any(Number),
|
|
208
|
+
broker: {
|
|
209
|
+
id: expect.any(Number),
|
|
210
|
+
symbol: expect.any(String),
|
|
211
|
+
name: expect.any(String),
|
|
212
|
+
longName: expect.any(String),
|
|
213
|
+
logo: expect.any(String),
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("getTopBrokersForBroker returns top brokers without averageCost", async () => {
|
|
220
|
+
const response = await brokerClient.getTopStocksForBroker(
|
|
221
|
+
region,
|
|
222
|
+
fromDate,
|
|
223
|
+
toDate,
|
|
224
|
+
BrokerSort.Volume,
|
|
225
|
+
"BIYKR"
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
expect(response).toBeDefined();
|
|
229
|
+
|
|
230
|
+
for (const stats of [response.topStats, response.restStats]) {
|
|
231
|
+
expect(stats).toMatchObject<BaseBrokerStats>({
|
|
232
|
+
totalBuyAmount: expect.any(Number),
|
|
233
|
+
totalSellAmount: expect.any(Number),
|
|
234
|
+
netAmount: expect.any(Number),
|
|
235
|
+
totalBuyVolume: expect.any(Number),
|
|
236
|
+
totalSellVolume: expect.any(Number),
|
|
237
|
+
totalVolume: expect.any(Number),
|
|
238
|
+
totalAmount: expect.any(Number),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
for (const item of response.topItems) {
|
|
243
|
+
expect(item).toMatchObject<BrokerStockStats>({
|
|
244
|
+
totalBuyAmount: expect.any(Number),
|
|
245
|
+
totalSellAmount: expect.any(Number),
|
|
246
|
+
netAmount: expect.any(Number),
|
|
247
|
+
totalBuyVolume: expect.any(Number),
|
|
248
|
+
totalSellVolume: expect.any(Number),
|
|
249
|
+
totalVolume: expect.any(Number),
|
|
250
|
+
totalAmount: expect.any(Number),
|
|
251
|
+
stock: {
|
|
252
|
+
id: expect.any(String),
|
|
253
|
+
symbol: expect.any(String),
|
|
254
|
+
name: expect.any(String),
|
|
255
|
+
assetType: expect.any(String),
|
|
256
|
+
assetClass: expect.any(String),
|
|
257
|
+
region: expect.any(String),
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
});
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
FinancialSheetType,
|
|
8
8
|
FinancialSheetPeriod,
|
|
9
9
|
Currency,
|
|
10
|
+
HistoricalFinancialSheetRow,
|
|
10
11
|
} from "../client/financial_ratios";
|
|
11
12
|
import { Region, Locale } from "../client/collections";
|
|
12
13
|
import "./client_test_suite";
|
|
@@ -74,6 +75,35 @@ describe('FinancialRatios', () => {
|
|
|
74
75
|
Currency.TRY,
|
|
75
76
|
Region.Tr
|
|
76
77
|
);
|
|
78
|
+
|
|
79
|
+
expect(resp).toBeDefined();
|
|
80
|
+
expect(resp).not.toBeNull();
|
|
77
81
|
expect(resp).not.toBeEmpty();
|
|
82
|
+
|
|
83
|
+
expect(resp.sheets).toBeDefined();
|
|
84
|
+
expect(Array.isArray(resp.sheets)).toBe(true);
|
|
85
|
+
expect(resp.sheets.length).toBeGreaterThan(0);
|
|
86
|
+
|
|
87
|
+
const firstSheet = resp.sheets[0];
|
|
88
|
+
expect(firstSheet).toBeDefined();
|
|
89
|
+
|
|
90
|
+
expect(firstSheet.period).toBeDefined();
|
|
91
|
+
expect(typeof firstSheet.period).toBe("string")
|
|
92
|
+
|
|
93
|
+
expect(firstSheet.items).toBeDefined();
|
|
94
|
+
expect(Array.isArray(firstSheet.items)).toBe(true);
|
|
95
|
+
expect(firstSheet.items.length).toBeGreaterThan(0);
|
|
96
|
+
|
|
97
|
+
const firstRow = firstSheet.items[0];
|
|
98
|
+
expect(firstRow).toBeDefined();
|
|
99
|
+
|
|
100
|
+
expect(firstRow).toMatchObject<HistoricalFinancialSheetRow>({
|
|
101
|
+
description: expect.any(String),
|
|
102
|
+
value: expect.any(Number),
|
|
103
|
+
lineCodeId: expect.any(Number),
|
|
104
|
+
indentLevel: expect.any(Number),
|
|
105
|
+
firstAncestorLineCodeId: expect.any(Number),
|
|
106
|
+
sectionLineCodeId: expect.any(Number),
|
|
107
|
+
});
|
|
78
108
|
});
|
|
79
|
-
});
|
|
109
|
+
});
|
package/src/utilities/test.env
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
BASE_URL=
|
|
2
|
-
API_KEY=
|
|
1
|
+
BASE_URL=https://api.finfree.app
|
|
2
|
+
API_KEY=api-6fa6cefb-16df-4a19-8351-54f83c6bbe2f
|