laplace-api 4.8.0 → 5.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.
@@ -2,77 +2,59 @@ import { Logger } from 'winston';
2
2
  import { LaplaceConfiguration } from '../utilities/configuration';
3
3
  import {
4
4
  FinancialFundamentalsClient,
5
- TopMoverDirection,
6
- StockDividend,
7
- StockStats,
8
- TopMover
5
+ TopMoverDirection
9
6
  } from '../client/financial_fundamentals';
10
7
  import './client_test_suite';
11
8
  import { Region } from '../client/collections';
12
9
  import { AssetType, AssetClass } from '../client/stocks';
13
10
 
14
- const mockDividendsResponse: StockDividend[] = [
11
+ const mockDividendsResponse = [
15
12
  {
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
13
+ "date": "2019-12-18T06:16:00Z",
14
+ "currency": "TRY",
15
+ "netAmount": 0.00007635,
16
+ "netRatio": 0.000396200399251687,
17
+ "grossAmount": 0.000509,
18
+ "grossRatio": 0.00264133599501125,
19
+ "priceThen": 19.399,
20
+ "stoppageRatio": 0.85,
21
+ "stoppageAmount": 0.00043265
34
22
  }
35
23
  ];
36
24
 
37
- const mockStockStatsResponse: StockStats[] = [
25
+ const mockStockStatsResponse = [
38
26
  {
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
27
+ "previousClose": 307,
28
+ "ytdReturn": 0.339439655172414,
29
+ "yearlyReturn": 2.6855575955102,
30
+ "marketCap": 1425000000000,
31
+ "peRatio": 60.2794409292471,
32
+ "pbRatio": 7.32754994352451,
33
+ "yearLow": 80.56935,
34
+ "yearHigh": 339.25,
35
+ "3YearReturn": 4.65564820832651,
36
+ "5YearReturn": 17.2118318670087,
37
+ "3MonthReturn": 0.682320503304819,
38
+ "monthlyReturn": -0.0792592592592593,
39
+ "weeklyReturn": 0.0375626043405676,
40
+ "symbol": "ASELS",
41
+ "latestPrice": 310.75,
42
+ "dailyChange": 0.012214983713355,
43
+ "dayHigh": 314.75,
44
+ "dayLow": 306,
45
+ "lowerPriceLimit": 276.5,
46
+ "upperPriceLimit": 337.5,
47
+ "dayOpen": 307.25,
48
+ "eps": 4.89223038095817
61
49
  }
62
50
  ];
63
51
 
64
- const mockTopMoversResponse: TopMover[] = [
65
- {
66
- symbol: "TUPRS",
67
- change: 5.8,
68
- assetClass: AssetClass.Equity,
69
- assetType: AssetType.Stock
70
- },
52
+ const mockTopMoversResponse = [
71
53
  {
72
- symbol: "SASA",
73
- change: 4.2,
74
- assetClass: AssetClass.Equity,
75
- assetType: AssetType.Stock
54
+ "symbol": "ISKPL",
55
+ "assetClass": "equity",
56
+ "assetType": "stock",
57
+ "change": 10
76
58
  }
77
59
  ];
78
60
 
@@ -100,7 +82,8 @@ describe('FinancialFundamentals', () => {
100
82
  expect(typeof firstDividend.date).toBe("string");
101
83
  expect(() => new Date(firstDividend.date)).not.toThrow();
102
84
  expect(new Date(firstDividend.date).getTime()).not.toBeNaN();
103
-
85
+
86
+ expect(typeof firstDividend.currency).toBe("string");
104
87
  expect(typeof firstDividend.netAmount).toBe("number");
105
88
  expect(typeof firstDividend.netRatio).toBe("number");
106
89
  expect(typeof firstDividend.grossAmount).toBe("number");
@@ -115,7 +98,7 @@ describe('FinancialFundamentals', () => {
115
98
  expect(resp).not.toBeEmpty();
116
99
  expect(resp.length).toBe(1);
117
100
 
118
- var currentStockStats = resp[0];
101
+ const currentStockStats = resp[0];
119
102
  expect(currentStockStats).not.toBeEmpty();
120
103
  expect(currentStockStats.symbol).toBe('TUPRS');
121
104
  expect(currentStockStats.previousClose).toBeGreaterThan(0.0);
@@ -147,7 +130,7 @@ describe('FinancialFundamentals', () => {
147
130
  const pageSize = 20;
148
131
 
149
132
  async function testTopMovers(direction: TopMoverDirection, shouldBePositive: boolean) {
150
- const result = await stockClient.getTopMovers(region, page, pageSize, direction, AssetType.Stock, AssetClass.Equity);
133
+ const result = await stockClient.getTopMovers(region, pageSize, direction, page, AssetType.Stock, AssetClass.Equity);
151
134
 
152
135
  expect(Array.isArray(result)).toBe(true);
153
136
  expect(result.length).toBeGreaterThan(0);
@@ -184,173 +167,159 @@ describe('FinancialFundamentals', () => {
184
167
  });
185
168
 
186
169
  describe("Mock Tests", () => {
170
+ let client: FinancialFundamentalsClient;
171
+ let cli: { request: jest.Mock };
172
+
187
173
  beforeEach(() => {
188
- jest.clearAllMocks();
174
+ cli = { request: jest.fn() };
175
+
176
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
177
+ const logger: Logger = {
178
+ info: jest.fn(),
179
+ error: jest.fn(),
180
+ warn: jest.fn(),
181
+ debug: jest.fn(),
182
+ } as unknown as Logger;
183
+
184
+ client = new FinancialFundamentalsClient(config, logger, cli as any);
189
185
  });
190
-
186
+
191
187
  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);
188
+ test("calls correct endpoint/params and matches raw response", async () => {
189
+ cli.request.mockResolvedValueOnce({ data: mockDividendsResponse });
190
+
191
+ const resp = await client.getStockDividends("TUPRS", Region.Tr);
192
+
193
+ expect(cli.request).toHaveBeenCalledTimes(1);
194
+ const call = cli.request.mock.calls[0][0];
195
+
196
+ expect(call.method).toBe("GET");
197
+ expect(call.url).toBe("/api/v2/stock/dividends");
198
+ expect(call.params).toEqual({ symbol: "TUPRS", region: Region.Tr });
199
+
200
+ expect(resp).toHaveLength(1);
201
+ const d = resp[0];
202
+
203
+ expect(d.date).toBe("2019-12-18T06:16:00Z");
204
+ expect(d.currency).toBe("TRY");
205
+ expect(d.netAmount).toBe(0.00007635);
206
+ expect(d.netRatio).toBe(0.000396200399251687);
207
+ expect(d.grossAmount).toBe(0.000509);
208
+ expect(d.grossRatio).toBe(0.00264133599501125);
209
+ expect(d.priceThen).toBe(19.399);
210
+ expect(d.stoppageRatio).toBe(0.85);
211
+ expect(d.stoppageAmount).toBe(0.00043265);
210
212
  });
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);
213
+
214
+ test("bubbles up request error", async () => {
215
+ cli.request.mockRejectedValueOnce(new Error("Invalid symbol"));
216
+
217
+ await expect(client.getStockDividends("INVALID", Region.Tr)).rejects.toThrow(
218
+ "Invalid symbol"
219
+ );
217
220
  });
218
221
  });
219
-
222
+
220
223
  describe("getStockStats", () => {
221
- test("should return stock stats with mock data", async () => {
222
- jest.spyOn(stockClient, 'getStockStats').mockResolvedValue(mockStockStatsResponse);
224
+ test("calls correct endpoint/params and matches raw response", async () => {
225
+ cli.request.mockResolvedValueOnce({ data: mockStockStatsResponse });
223
226
 
224
- const resp = await stockClient.getStockStats(["TUPRS"], Region.Tr);
227
+ const resp = await client.getStockStats(["ASELS"], Region.Tr);
225
228
 
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);
229
+ expect(cli.request).toHaveBeenCalledTimes(1);
230
+ const call = cli.request.mock.calls[0][0];
251
231
 
252
- expect(stockClient.getStockStats).toHaveBeenCalledWith(["TUPRS"], Region.Tr);
232
+ expect(call.method).toBe("GET");
233
+ expect(call.url).toBe("/api/v2/stock/stats");
234
+ expect(call.params).toEqual({ symbols: "ASELS", region: Region.Tr });
235
+
236
+ expect(resp).toHaveLength(1);
237
+ const s = resp[0];
238
+
239
+ expect(s.symbol).toBe("ASELS");
240
+
241
+ expect(s.previousClose).toBe(307);
242
+ expect(s.ytdReturn).toBe(0.339439655172414);
243
+ expect(s.yearlyReturn).toBe(2.6855575955102);
244
+
245
+ expect(s.marketCap).toBe(1425000000000);
246
+ expect(s.peRatio).toBe(60.2794409292471);
247
+ expect(s.pbRatio).toBe(7.32754994352451);
248
+
249
+ expect(s.yearLow).toBe(80.56935);
250
+ expect(s.yearHigh).toBe(339.25);
251
+
252
+ expect(s["3YearReturn"]).toBe(4.65564820832651);
253
+ expect(s["5YearReturn"]).toBe(17.2118318670087);
254
+ expect(s["3MonthReturn"]).toBe(0.682320503304819);
255
+
256
+ expect(s.monthlyReturn).toBe(-0.0792592592592593);
257
+ expect(s.weeklyReturn).toBe(0.0375626043405676);
258
+
259
+ expect(s.latestPrice).toBe(310.75);
260
+ expect(s.dailyChange).toBe(0.012214983713355);
261
+
262
+ expect(s.dayHigh).toBe(314.75);
263
+ expect(s.dayLow).toBe(306);
264
+ expect(s.dayOpen).toBe(307.25);
265
+
266
+ expect(s.lowerPriceLimit).toBe(276.5);
267
+ expect(s.upperPriceLimit).toBe(337.5);
268
+
269
+ expect(s.eps).toBe(4.89223038095817);
253
270
  });
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");
271
+
272
+ test("bubbles up request error", async () => {
273
+ cli.request.mockRejectedValueOnce(new Error("Invalid symbols"));
274
+
275
+ await expect(client.getStockStats([""], Region.Tr)).rejects.toThrow("Invalid symbols");
266
276
  });
267
277
  });
268
-
278
+
269
279
  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
- );
280
+ test("calls correct endpoint/params and matches raw response", async () => {
281
+ cli.request.mockResolvedValueOnce({ data: mockTopMoversResponse });
281
282
 
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(
283
+ const resp = await client.getTopMovers(
293
284
  Region.Tr,
294
- 0,
295
285
  10,
296
286
  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
287
  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
288
  AssetType.Stock,
339
289
  AssetClass.Equity
340
290
  );
341
291
 
292
+ expect(cli.request).toHaveBeenCalledTimes(1);
293
+ const call = cli.request.mock.calls[0][0];
294
+
295
+ expect(call.method).toBe("GET");
296
+ expect(call.url).toBe("/api/v2/stock/top-movers");
297
+ expect(call.params).toEqual({
298
+ region: Region.Tr,
299
+ pageSize: 10,
300
+ direction: TopMoverDirection.Gainers,
301
+ page: 0,
302
+ assetType: AssetType.Stock,
303
+ assetClass: AssetClass.Equity,
304
+ });
305
+
342
306
  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
- );
307
+ const m = resp[0];
308
+
309
+ expect(m.symbol).toBe("ISKPL");
310
+ expect(m.change).toBe(10);
311
+ expect(m.assetType).toBe("stock");
312
+ expect(m.assetClass).toBe("equity");
313
+ });
314
+
315
+ test("bubbles up request error", async () => {
316
+ cli.request.mockRejectedValueOnce(new Error("Bad request"));
317
+
318
+ await expect(
319
+ client.getTopMovers(Region.Tr, 10, TopMoverDirection.Gainers)
320
+ ).rejects.toThrow("Bad request");
353
321
  });
354
322
  });
355
323
  });
324
+
356
325
  });