laplace-api 4.0.0 → 4.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/.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 +84 -11
- package/src/client/live-price.ts +204 -77
- 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 +483 -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
|
@@ -1,10 +1,81 @@
|
|
|
1
1
|
import { Logger } from 'winston';
|
|
2
2
|
import { LaplaceConfiguration } from '../utilities/configuration';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
FinancialFundamentalsClient,
|
|
5
|
+
TopMoverDirection,
|
|
6
|
+
StockDividend,
|
|
7
|
+
StockStats,
|
|
8
|
+
TopMover
|
|
9
|
+
} from '../client/financial_fundamentals';
|
|
4
10
|
import './client_test_suite';
|
|
5
11
|
import { Region } from '../client/collections';
|
|
6
12
|
import { AssetType, AssetClass } from '../client/stocks';
|
|
7
13
|
|
|
14
|
+
const mockDividendsResponse: StockDividend[] = [
|
|
15
|
+
{
|
|
16
|
+
date: "2024-03-14T10:00:00Z",
|
|
17
|
+
netAmount: 8.75,
|
|
18
|
+
netRatio: 0.0875,
|
|
19
|
+
grossAmount: 10.0,
|
|
20
|
+
grossRatio: 0.10,
|
|
21
|
+
priceThen: 425.5,
|
|
22
|
+
stoppageRatio: 0.15,
|
|
23
|
+
stoppageAmount: 1.25
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
date: "2023-03-15T10:00:00Z",
|
|
27
|
+
netAmount: 7.0,
|
|
28
|
+
netRatio: 0.07,
|
|
29
|
+
grossAmount: 8.0,
|
|
30
|
+
grossRatio: 0.08,
|
|
31
|
+
priceThen: 380.0,
|
|
32
|
+
stoppageRatio: 0.15,
|
|
33
|
+
stoppageAmount: 1.0
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const mockStockStatsResponse: StockStats[] = [
|
|
38
|
+
{
|
|
39
|
+
symbol: "TUPRS",
|
|
40
|
+
previousClose: 425.5,
|
|
41
|
+
marketCap: 106375000000,
|
|
42
|
+
peRatio: 5.8,
|
|
43
|
+
pbRatio: 2.1,
|
|
44
|
+
yearLow: 320.5,
|
|
45
|
+
yearHigh: 450.2,
|
|
46
|
+
weeklyReturn: 0.025,
|
|
47
|
+
monthlyReturn: 0.058,
|
|
48
|
+
"3MonthReturn": 0.125,
|
|
49
|
+
ytdReturn: 0.15,
|
|
50
|
+
yearlyReturn: 0.45,
|
|
51
|
+
"3YearReturn": 1.25,
|
|
52
|
+
"5YearReturn": 2.85,
|
|
53
|
+
latestPrice: 428.5,
|
|
54
|
+
dailyChange: 0.007,
|
|
55
|
+
dayLow: 424.0,
|
|
56
|
+
dayHigh: 429.5,
|
|
57
|
+
upperPriceLimit: 468.05,
|
|
58
|
+
lowerPriceLimit: 382.95,
|
|
59
|
+
dayOpen: 426.0,
|
|
60
|
+
eps: 73.45
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const mockTopMoversResponse: TopMover[] = [
|
|
65
|
+
{
|
|
66
|
+
symbol: "TUPRS",
|
|
67
|
+
change: 5.8,
|
|
68
|
+
assetClass: AssetClass.Equity,
|
|
69
|
+
assetType: AssetType.Stock
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
symbol: "SASA",
|
|
73
|
+
change: 4.2,
|
|
74
|
+
assetClass: AssetClass.Equity,
|
|
75
|
+
assetType: AssetType.Stock
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
8
79
|
describe('FinancialFundamentals', () => {
|
|
9
80
|
let stockClient: FinancialFundamentalsClient;
|
|
10
81
|
|
|
@@ -20,29 +91,29 @@ describe('FinancialFundamentals', () => {
|
|
|
20
91
|
stockClient = new FinancialFundamentalsClient(config, logger);
|
|
21
92
|
});
|
|
22
93
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const firstDividend = resp[0];
|
|
28
|
-
expect(typeof firstDividend.date).toBe("string");
|
|
29
|
-
expect(() => new Date(firstDividend.date)).not.toThrow();
|
|
30
|
-
expect(new Date(firstDividend.date).getTime()).not.toBeNaN();
|
|
94
|
+
describe("Integration Tests", () => {
|
|
95
|
+
test("GetStockDividend", async () => {
|
|
96
|
+
const resp = await stockClient.getStockDividends("TUPRS", Region.Tr);
|
|
97
|
+
expect(resp).not.toBeEmpty();
|
|
31
98
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
99
|
+
const firstDividend = resp[0];
|
|
100
|
+
expect(typeof firstDividend.date).toBe("string");
|
|
101
|
+
expect(() => new Date(firstDividend.date)).not.toThrow();
|
|
102
|
+
expect(new Date(firstDividend.date).getTime()).not.toBeNaN();
|
|
103
|
+
|
|
104
|
+
expect(typeof firstDividend.netAmount).toBe("number");
|
|
105
|
+
expect(typeof firstDividend.netRatio).toBe("number");
|
|
106
|
+
expect(typeof firstDividend.grossAmount).toBe("number");
|
|
107
|
+
expect(typeof firstDividend.grossRatio).toBe("number");
|
|
108
|
+
expect(typeof firstDividend.priceThen).toBe("number");
|
|
109
|
+
expect(typeof firstDividend.stoppageRatio).toBe("number");
|
|
110
|
+
expect(typeof firstDividend.stoppageAmount).toBe("number");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('GetStockStats', async () => {
|
|
114
|
+
const resp = await stockClient.getStockStats(['TUPRS'], Region.Tr);
|
|
115
|
+
expect(resp).not.toBeEmpty();
|
|
116
|
+
expect(resp.length).toBe(1);
|
|
46
117
|
|
|
47
118
|
var currentStockStats = resp[0];
|
|
48
119
|
expect(currentStockStats).not.toBeEmpty();
|
|
@@ -53,7 +124,7 @@ describe('FinancialFundamentals', () => {
|
|
|
53
124
|
expect(currentStockStats.pbRatio).not.toBe(0.0);
|
|
54
125
|
expect(currentStockStats.yearLow).toBeGreaterThan(0.0);
|
|
55
126
|
expect(currentStockStats.yearHigh).toBeGreaterThan(0.0);
|
|
56
|
-
expect(typeof currentStockStats.weeklyReturn).toBe('number')
|
|
127
|
+
expect(typeof currentStockStats.weeklyReturn).toBe('number');
|
|
57
128
|
expect(typeof currentStockStats.monthlyReturn).toBe('number');
|
|
58
129
|
expect(typeof currentStockStats['3MonthReturn']).toBe('number');
|
|
59
130
|
expect(typeof currentStockStats.ytdReturn).toBe('number');
|
|
@@ -68,48 +139,218 @@ describe('FinancialFundamentals', () => {
|
|
|
68
139
|
expect(currentStockStats.lowerPriceLimit).toBeGreaterThan(0.0);
|
|
69
140
|
expect(currentStockStats.dayOpen).toBeGreaterThan(0.0);
|
|
70
141
|
expect(typeof currentStockStats.eps).toBe('number');
|
|
71
|
-
|
|
142
|
+
});
|
|
72
143
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
async function testTopMovers(direction: TopMoverDirection, shouldBePositive: boolean) {
|
|
79
|
-
const result = await stockClient.getTopMovers(region, page, pageSize, direction, AssetType.Stock, AssetClass.Equity);
|
|
144
|
+
describe('GetTopMovers', () => {
|
|
145
|
+
const region = Region.Tr;
|
|
146
|
+
const page = 0;
|
|
147
|
+
const pageSize = 20;
|
|
80
148
|
|
|
81
|
-
|
|
82
|
-
|
|
149
|
+
async function testTopMovers(direction: TopMoverDirection, shouldBePositive: boolean) {
|
|
150
|
+
const result = await stockClient.getTopMovers(region, page, pageSize, direction, AssetType.Stock, AssetClass.Equity);
|
|
151
|
+
|
|
152
|
+
expect(Array.isArray(result)).toBe(true);
|
|
153
|
+
expect(result.length).toBeGreaterThan(0);
|
|
154
|
+
|
|
155
|
+
result.forEach(mover => {
|
|
156
|
+
expect(mover).toHaveProperty('symbol');
|
|
157
|
+
expect(mover).toHaveProperty('change');
|
|
158
|
+
expect(typeof mover.symbol).toBe('string');
|
|
159
|
+
expect(typeof mover.change).toBe('number');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const directionCheck = result.every(mover =>
|
|
163
|
+
shouldBePositive ? mover.change > 0 : mover.change < 0
|
|
164
|
+
);
|
|
165
|
+
expect(directionCheck).toBe(true);
|
|
166
|
+
|
|
167
|
+
const assetTypeCheck = result.every(mover => mover.assetType === AssetType.Stock)
|
|
168
|
+
expect(assetTypeCheck).toBe(true);
|
|
169
|
+
|
|
170
|
+
const assetClassCheck = result.every(mover => mover.assetClass === AssetClass.Equity)
|
|
171
|
+
expect(assetClassCheck).toBe(true);
|
|
172
|
+
|
|
173
|
+
expect(result.length).toBeLessThanOrEqual(pageSize);
|
|
174
|
+
}
|
|
83
175
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(mover).toHaveProperty('change');
|
|
87
|
-
expect(typeof mover.symbol).toBe('string');
|
|
88
|
-
expect(typeof mover.change).toBe('number');
|
|
176
|
+
test('should return gainers data', async () => {
|
|
177
|
+
await testTopMovers(TopMoverDirection.Gainers, true);
|
|
89
178
|
});
|
|
90
179
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
);
|
|
94
|
-
|
|
180
|
+
test('should return losers data', async () => {
|
|
181
|
+
await testTopMovers(TopMoverDirection.Losers, false);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("Mock Tests", () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
jest.clearAllMocks();
|
|
189
|
+
});
|
|
95
190
|
|
|
96
|
-
|
|
191
|
+
describe("getStockDividends", () => {
|
|
192
|
+
test("should return dividends with mock data", async () => {
|
|
193
|
+
jest.spyOn(stockClient, 'getStockDividends').mockResolvedValue(mockDividendsResponse);
|
|
97
194
|
|
|
98
|
-
|
|
195
|
+
const resp = await stockClient.getStockDividends("TUPRS", Region.Tr);
|
|
99
196
|
|
|
100
|
-
|
|
197
|
+
expect(resp).toHaveLength(2);
|
|
198
|
+
|
|
199
|
+
const firstDividend = resp[0];
|
|
200
|
+
expect(firstDividend.date).toBe("2024-03-14T10:00:00Z");
|
|
201
|
+
expect(firstDividend.netAmount).toBe(8.75);
|
|
202
|
+
expect(firstDividend.netRatio).toBe(0.0875);
|
|
203
|
+
expect(firstDividend.grossAmount).toBe(10.0);
|
|
204
|
+
expect(firstDividend.grossRatio).toBe(0.10);
|
|
205
|
+
expect(firstDividend.priceThen).toBe(425.5);
|
|
206
|
+
expect(firstDividend.stoppageRatio).toBe(0.15);
|
|
207
|
+
expect(firstDividend.stoppageAmount).toBe(1.25);
|
|
101
208
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
209
|
+
expect(stockClient.getStockDividends).toHaveBeenCalledWith("TUPRS", Region.Tr);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("should handle empty dividends", async () => {
|
|
213
|
+
jest.spyOn(stockClient, 'getStockDividends').mockResolvedValue([]);
|
|
214
|
+
|
|
215
|
+
const resp = await stockClient.getStockDividends("NO_DIVIDEND_STOCK", Region.Tr);
|
|
216
|
+
expect(resp).toHaveLength(0);
|
|
217
|
+
});
|
|
109
218
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
219
|
+
|
|
220
|
+
describe("getStockStats", () => {
|
|
221
|
+
test("should return stock stats with mock data", async () => {
|
|
222
|
+
jest.spyOn(stockClient, 'getStockStats').mockResolvedValue(mockStockStatsResponse);
|
|
223
|
+
|
|
224
|
+
const resp = await stockClient.getStockStats(["TUPRS"], Region.Tr);
|
|
225
|
+
|
|
226
|
+
expect(resp).toHaveLength(1);
|
|
227
|
+
|
|
228
|
+
const stats = resp[0];
|
|
229
|
+
expect(stats.symbol).toBe("TUPRS");
|
|
230
|
+
expect(stats.previousClose).toBe(425.5);
|
|
231
|
+
expect(stats.marketCap).toBe(106375000000);
|
|
232
|
+
expect(stats.peRatio).toBe(5.8);
|
|
233
|
+
expect(stats.pbRatio).toBe(2.1);
|
|
234
|
+
expect(stats.yearLow).toBe(320.5);
|
|
235
|
+
expect(stats.yearHigh).toBe(450.2);
|
|
236
|
+
expect(stats.weeklyReturn).toBe(0.025);
|
|
237
|
+
expect(stats.monthlyReturn).toBe(0.058);
|
|
238
|
+
expect(stats["3MonthReturn"]).toBe(0.125);
|
|
239
|
+
expect(stats.ytdReturn).toBe(0.15);
|
|
240
|
+
expect(stats.yearlyReturn).toBe(0.45);
|
|
241
|
+
expect(stats["3YearReturn"]).toBe(1.25);
|
|
242
|
+
expect(stats["5YearReturn"]).toBe(2.85);
|
|
243
|
+
expect(stats.latestPrice).toBe(428.5);
|
|
244
|
+
expect(stats.dailyChange).toBe(0.007);
|
|
245
|
+
expect(stats.dayLow).toBe(424.0);
|
|
246
|
+
expect(stats.dayHigh).toBe(429.5);
|
|
247
|
+
expect(stats.upperPriceLimit).toBe(468.05);
|
|
248
|
+
expect(stats.lowerPriceLimit).toBe(382.95);
|
|
249
|
+
expect(stats.dayOpen).toBe(426.0);
|
|
250
|
+
expect(stats.eps).toBe(73.45);
|
|
251
|
+
|
|
252
|
+
expect(stockClient.getStockStats).toHaveBeenCalledWith(["TUPRS"], Region.Tr);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("should handle multiple symbols", async () => {
|
|
256
|
+
const multipleStatsResponse = [
|
|
257
|
+
mockStockStatsResponse[0],
|
|
258
|
+
{ ...mockStockStatsResponse[0], symbol: "SASA", marketCap: 52000000000 }
|
|
259
|
+
];
|
|
260
|
+
jest.spyOn(stockClient, 'getStockStats').mockResolvedValue(multipleStatsResponse);
|
|
261
|
+
|
|
262
|
+
const resp = await stockClient.getStockStats(["TUPRS", "SASA"], Region.Tr);
|
|
263
|
+
expect(resp).toHaveLength(2);
|
|
264
|
+
expect(resp[0].symbol).toBe("TUPRS");
|
|
265
|
+
expect(resp[1].symbol).toBe("SASA");
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("getTopMovers", () => {
|
|
270
|
+
test("should return gainers with mock data", async () => {
|
|
271
|
+
jest.spyOn(stockClient, 'getTopMovers').mockResolvedValue(mockTopMoversResponse);
|
|
272
|
+
|
|
273
|
+
const resp = await stockClient.getTopMovers(
|
|
274
|
+
Region.Tr,
|
|
275
|
+
0,
|
|
276
|
+
10,
|
|
277
|
+
TopMoverDirection.Gainers,
|
|
278
|
+
AssetType.Stock,
|
|
279
|
+
AssetClass.Equity
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
expect(resp).toHaveLength(2);
|
|
283
|
+
|
|
284
|
+
const firstMover = resp[0];
|
|
285
|
+
expect(firstMover.symbol).toBe("TUPRS");
|
|
286
|
+
expect(firstMover.change).toBe(5.8);
|
|
287
|
+
expect(firstMover.assetClass).toBe(AssetClass.Equity);
|
|
288
|
+
expect(firstMover.assetType).toBe(AssetType.Stock);
|
|
289
|
+
|
|
290
|
+
expect(resp.every(mover => mover.change > 0)).toBe(true);
|
|
291
|
+
|
|
292
|
+
expect(stockClient.getTopMovers).toHaveBeenCalledWith(
|
|
293
|
+
Region.Tr,
|
|
294
|
+
0,
|
|
295
|
+
10,
|
|
296
|
+
TopMoverDirection.Gainers,
|
|
297
|
+
AssetType.Stock,
|
|
298
|
+
AssetClass.Equity
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("should return losers with mock data", async () => {
|
|
303
|
+
const losersResponse = mockTopMoversResponse.map(mover => ({
|
|
304
|
+
...mover,
|
|
305
|
+
change: -Math.abs(mover.change)
|
|
306
|
+
}));
|
|
307
|
+
jest.spyOn(stockClient, 'getTopMovers').mockResolvedValue(losersResponse);
|
|
308
|
+
|
|
309
|
+
const resp = await stockClient.getTopMovers(
|
|
310
|
+
Region.Tr,
|
|
311
|
+
0,
|
|
312
|
+
10,
|
|
313
|
+
TopMoverDirection.Losers,
|
|
314
|
+
AssetType.Stock,
|
|
315
|
+
AssetClass.Equity
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect(resp.every(mover => mover.change < 0)).toBe(true);
|
|
319
|
+
|
|
320
|
+
expect(stockClient.getTopMovers).toHaveBeenCalledWith(
|
|
321
|
+
Region.Tr,
|
|
322
|
+
0,
|
|
323
|
+
10,
|
|
324
|
+
TopMoverDirection.Losers,
|
|
325
|
+
AssetType.Stock,
|
|
326
|
+
AssetClass.Equity
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("should handle pagination", async () => {
|
|
331
|
+
jest.spyOn(stockClient, 'getTopMovers').mockResolvedValue([mockTopMoversResponse[0]]);
|
|
332
|
+
|
|
333
|
+
const resp = await stockClient.getTopMovers(
|
|
334
|
+
Region.Tr,
|
|
335
|
+
1,
|
|
336
|
+
1,
|
|
337
|
+
TopMoverDirection.Gainers,
|
|
338
|
+
AssetType.Stock,
|
|
339
|
+
AssetClass.Equity
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
expect(resp).toHaveLength(1);
|
|
343
|
+
expect(resp[0].symbol).toBe("TUPRS");
|
|
344
|
+
|
|
345
|
+
expect(stockClient.getTopMovers).toHaveBeenCalledWith(
|
|
346
|
+
Region.Tr,
|
|
347
|
+
1,
|
|
348
|
+
1,
|
|
349
|
+
TopMoverDirection.Gainers,
|
|
350
|
+
AssetType.Stock,
|
|
351
|
+
AssetClass.Equity
|
|
352
|
+
);
|
|
353
|
+
});
|
|
113
354
|
});
|
|
114
355
|
});
|
|
115
356
|
});
|