laplace-api 3.1.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 +79 -99
- package/src/client/capital_increase.ts +17 -22
- package/src/client/collections.ts +61 -40
- package/src/client/financial_fundamentals.ts +40 -35
- package/src/client/financial_ratios.ts +168 -128
- package/src/client/funds.ts +139 -0
- package/src/client/key-insights.ts +17 -0
- package/src/client/live-price-web-socket.ts +84 -11
- package/src/client/live-price.ts +210 -58
- package/src/client/politician.ts +75 -0
- package/src/client/stocks.ts +85 -2
- package/src/test/broker.test.ts +581 -170
- package/src/test/capital_increase.test.ts +266 -15
- package/src/test/collections.test.ts +460 -17
- package/src/test/custom_theme.test.ts +256 -65
- package/src/test/financial_fundamentals.test.ts +301 -45
- package/src/test/financial_ratios.test.ts +376 -75
- package/src/test/funds.test.ts +317 -0
- package/src/test/helpers.ts +58 -0
- package/src/test/key-insight.test.ts +110 -0
- package/src/test/live-price.test.ts +427 -67
- package/src/test/politician.test.ts +253 -0
- package/src/test/readme.test.ts +483 -0
- package/src/test/search.test.ts +308 -23
- package/src/test/stocks.test.ts +800 -70
- package/src/utilities/configuration.ts +23 -10
- package/src/utilities/test.env +2 -2
|
@@ -1,13 +1,82 @@
|
|
|
1
1
|
import { Logger } from 'winston';
|
|
2
2
|
import { LaplaceConfiguration } from '../utilities/configuration';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
FinancialFundamentalsClient,
|
|
5
|
+
TopMoverDirection,
|
|
6
|
+
StockDividend,
|
|
7
|
+
StockStats,
|
|
8
|
+
TopMover
|
|
9
|
+
} from '../client/financial_fundamentals';
|
|
6
10
|
import './client_test_suite';
|
|
7
|
-
import {
|
|
11
|
+
import { Region } from '../client/collections';
|
|
12
|
+
import { AssetType, AssetClass } from '../client/stocks';
|
|
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
|
+
];
|
|
8
78
|
|
|
9
79
|
describe('FinancialFundamentals', () => {
|
|
10
|
-
let client: Client;
|
|
11
80
|
let stockClient: FinancialFundamentalsClient;
|
|
12
81
|
|
|
13
82
|
beforeAll(() => {
|
|
@@ -22,16 +91,29 @@ describe('FinancialFundamentals', () => {
|
|
|
22
91
|
stockClient = new FinancialFundamentalsClient(config, logger);
|
|
23
92
|
});
|
|
24
93
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
94
|
+
describe("Integration Tests", () => {
|
|
95
|
+
test("GetStockDividend", async () => {
|
|
96
|
+
const resp = await stockClient.getStockDividends("TUPRS", Region.Tr);
|
|
97
|
+
expect(resp).not.toBeEmpty();
|
|
98
|
+
|
|
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
|
+
});
|
|
29
112
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
113
|
+
test('GetStockStats', async () => {
|
|
114
|
+
const resp = await stockClient.getStockStats(['TUPRS'], Region.Tr);
|
|
115
|
+
expect(resp).not.toBeEmpty();
|
|
116
|
+
expect(resp.length).toBe(1);
|
|
35
117
|
|
|
36
118
|
var currentStockStats = resp[0];
|
|
37
119
|
expect(currentStockStats).not.toBeEmpty();
|
|
@@ -42,7 +124,7 @@ describe('FinancialFundamentals', () => {
|
|
|
42
124
|
expect(currentStockStats.pbRatio).not.toBe(0.0);
|
|
43
125
|
expect(currentStockStats.yearLow).toBeGreaterThan(0.0);
|
|
44
126
|
expect(currentStockStats.yearHigh).toBeGreaterThan(0.0);
|
|
45
|
-
expect(typeof currentStockStats.weeklyReturn).toBe('number')
|
|
127
|
+
expect(typeof currentStockStats.weeklyReturn).toBe('number');
|
|
46
128
|
expect(typeof currentStockStats.monthlyReturn).toBe('number');
|
|
47
129
|
expect(typeof currentStockStats['3MonthReturn']).toBe('number');
|
|
48
130
|
expect(typeof currentStockStats.ytdReturn).toBe('number');
|
|
@@ -57,44 +139,218 @@ describe('FinancialFundamentals', () => {
|
|
|
57
139
|
expect(currentStockStats.lowerPriceLimit).toBeGreaterThan(0.0);
|
|
58
140
|
expect(currentStockStats.dayOpen).toBeGreaterThan(0.0);
|
|
59
141
|
expect(typeof currentStockStats.eps).toBe('number');
|
|
60
|
-
|
|
142
|
+
});
|
|
61
143
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
async function testTopMovers(direction: TopMoverDirection, shouldBePositive: boolean) {
|
|
68
|
-
const result = await stockClient.getTopMovers(region, page, pageSize, direction, AssetType.Stock);
|
|
144
|
+
describe('GetTopMovers', () => {
|
|
145
|
+
const region = Region.Tr;
|
|
146
|
+
const page = 0;
|
|
147
|
+
const pageSize = 20;
|
|
69
148
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
}
|
|
72
175
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
expect(mover).toHaveProperty('change');
|
|
76
|
-
expect(typeof mover.symbol).toBe('string');
|
|
77
|
-
expect(typeof mover.change).toBe('number');
|
|
176
|
+
test('should return gainers data', async () => {
|
|
177
|
+
await testTopMovers(TopMoverDirection.Gainers, true);
|
|
78
178
|
});
|
|
79
179
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
83
|
-
|
|
180
|
+
test('should return losers data', async () => {
|
|
181
|
+
await testTopMovers(TopMoverDirection.Losers, false);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
});
|
|
84
185
|
|
|
85
|
-
|
|
186
|
+
describe("Mock Tests", () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
jest.clearAllMocks();
|
|
189
|
+
});
|
|
86
190
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
191
|
+
describe("getStockDividends", () => {
|
|
192
|
+
test("should return dividends with mock data", async () => {
|
|
193
|
+
jest.spyOn(stockClient, 'getStockDividends').mockResolvedValue(mockDividendsResponse);
|
|
194
|
+
|
|
195
|
+
const resp = await stockClient.getStockDividends("TUPRS", Region.Tr);
|
|
196
|
+
|
|
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);
|
|
208
|
+
|
|
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
|
+
});
|
|
94
218
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
});
|
|
98
354
|
});
|
|
99
355
|
});
|
|
100
356
|
});
|