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.
@@ -8,79 +8,65 @@ import {
8
8
  Currency,
9
9
  HistoricalFinancialSheetRow,
10
10
  RatioComparisonPeerType,
11
- StockPeerFinancialRatioComparison,
12
- StockHistoricalRatios,
13
11
  HistoricalRatiosFormat,
14
- StockHistoricalRatiosDescription,
15
- HistoricalFinancialSheets
16
12
  } from "../client/financial_ratios";
17
13
  import "./client_test_suite";
18
14
  import { equal } from "assert";
19
15
  import { Locale, Region } from "../client/collections";
20
16
 
21
- const mockRatioComparisonResponse: StockPeerFinancialRatioComparison[] = [
17
+ const mockRatioComparisonResponse = [
22
18
  {
23
- metricName: "Net Kar Marjı",
24
- normalizedValue: 0.85,
25
- data: [
19
+ "metricName": "pricing",
20
+ "normalizedValue": 57.1272139065688,
21
+ "data": [
26
22
  {
27
- slug: "TUPRS",
28
- value: 12.5,
29
- average: 8.2
30
- },
31
- {
32
- slug: "SECTOR_AVERAGE",
33
- value: 7.8,
34
- average: 8.2
23
+ "slug": "F/K",
24
+ "value": 14.9584698641742,
25
+ "average": -26.1316853855662
35
26
  }
36
27
  ]
37
28
  }
38
29
  ];
39
30
 
40
- const mockHistoricalRatiosResponse: StockHistoricalRatios[] = [
31
+ const mockHistoricalRatiosResponse = [
41
32
  {
42
- slug: HistoricalRatiosKey.NetMargin,
43
- finalValue: 12.5,
44
- threeYearGrowth: 15.2,
45
- yearGrowth: 5.8,
46
- finalSectorValue: 7.8,
47
- currency: Currency.TRY,
48
- format: HistoricalRatiosFormat.PERCENTAGE,
49
- name: "Net Kar Marjı",
50
- items: [
51
- {
52
- period: "2024-Q1",
53
- value: 12.5,
54
- sectorMean: 7.8
55
- },
33
+ "items": [
56
34
  {
57
- period: "2023-Q4",
58
- value: 11.8,
59
- sectorMean: 7.5
35
+ "period": "2025-4",
36
+ "value": 0.080499759559051,
37
+ "sectorMean": 0.080499759559051
60
38
  }
61
- ]
39
+ ],
40
+ "finalValue": 0.080499759559051,
41
+ "threeYearGrowth": -0.922611834325054,
42
+ "yearGrowth": -0.527234781985081,
43
+ "finalSectorValue": 0.080499759559051,
44
+ "slug": "roe",
45
+ "currency": "TRY",
46
+ "format": "percentage",
47
+ "name": "ROE"
62
48
  }
63
49
  ];
64
50
 
65
- const mockRatiosDescriptionsResponse: StockHistoricalRatiosDescription[] = [
51
+ const mockRatiosDescriptionsResponse = [
66
52
  {
67
- id: 1,
68
- format: HistoricalRatiosFormat.PERCENTAGE,
69
- currency: Currency.TRY,
70
- slug: HistoricalRatiosKey.NetMargin,
71
- createdAt: "2024-03-14T10:00:00Z",
72
- updatedAt: "2024-03-14T10:00:00Z",
73
- name: "Net Kar Marjı",
74
- description: "Net karın satışlara oranı",
75
- locale: Locale.Tr,
76
- isRealtime: false
53
+ "id": 112,
54
+ "format": "percentage",
55
+ "currency": "TRY",
56
+ "slug": "gross-margin",
57
+ "createdAt": "2025-04-16T07:50:52.245181Z",
58
+ "updatedAt": "2025-04-16T07:50:52.245181Z",
59
+ "name": "Brüt Kâr Marjı",
60
+ "description": "Satışların maliyetinin gelirden çıkarılmasıyla brüt kâr bulunur. Brüt kârın satışlara oranı brüt kâr marjı olarak isimlendirilir.",
61
+ "locale": "tr",
62
+ "isRealtime": false
77
63
  }
78
64
  ];
79
65
 
80
- const mockFinancialSheetsResponse: HistoricalFinancialSheets = {
66
+ const mockFinancialSheetsResponse = {
81
67
  sheets: [
82
68
  {
83
- period: "2024-Q1",
69
+ period: "2024-1",
84
70
  items: [
85
71
  {
86
72
  description: "Satış Gelirleri",
@@ -153,6 +139,7 @@ describe("FinancialRatios", () => {
153
139
  expect(equal(firstRatio.currency, Currency.TRY));
154
140
  expect(typeof firstRatio.format).toBe("string");
155
141
  expect(typeof firstRatio.name).toBe("string");
142
+ expect(typeof firstRatio.slug).toBe("string");
156
143
 
157
144
  expect(firstRatio.items).not.toBeEmpty();
158
145
  const firstItem = firstRatio.items[0];
@@ -241,117 +228,178 @@ describe("FinancialRatios", () => {
241
228
  });
242
229
  });
243
230
 
244
- describe("Mock Tests", () => {
231
+ describe("Mock Tests (Broker-style)", () => {
232
+ let client: FinancialClient;
233
+ let cli: { request: jest.Mock };
234
+
245
235
  beforeEach(() => {
246
- jest.clearAllMocks();
236
+ cli = { request: jest.fn() };
237
+
238
+ const config = (global as any).testSuite.config as LaplaceConfiguration;
239
+ const logger: Logger = {
240
+ info: jest.fn(),
241
+ error: jest.fn(),
242
+ warn: jest.fn(),
243
+ debug: jest.fn(),
244
+ } as unknown as Logger;
245
+
246
+ client = new FinancialClient(config, logger, cli as any);
247
247
  });
248
-
248
+
249
249
  describe("getFinancialRatioComparison", () => {
250
- test("should return ratio comparison with mock data", async () => {
251
- jest.spyOn(financialClient, 'getFinancialRatioComparison').mockResolvedValue(mockRatioComparisonResponse);
250
+ test("calls correct endpoint/params and matches raw response", async () => {
251
+ cli.request.mockResolvedValueOnce({ data: mockRatioComparisonResponse });
252
252
 
253
- const resp = await financialClient.getFinancialRatioComparison(
253
+ const resp = await client.getFinancialRatioComparison(
254
254
  "TUPRS",
255
255
  Region.Tr,
256
256
  RatioComparisonPeerType.Sector
257
257
  );
258
258
 
259
+ expect(cli.request).toHaveBeenCalledTimes(1);
260
+ const call = cli.request.mock.calls[0][0];
261
+
262
+ expect(call.method).toBe("GET");
263
+ expect(call.url).toBe("/api/v2/stock/financial-ratio-comparison");
264
+ expect(call.params).toEqual({ symbol: "TUPRS", region: Region.Tr, peerType: RatioComparisonPeerType.Sector });
265
+
259
266
  expect(resp).toHaveLength(1);
260
-
261
- const comparison = resp[0];
262
- expect(comparison.metricName).toBe("Net Kar Marjı");
263
- expect(comparison.normalizedValue).toBe(0.85);
264
- expect(comparison.data).toHaveLength(2);
265
-
266
- const companyData = comparison.data[0];
267
- expect(companyData.slug).toBe("TUPRS");
268
- expect(companyData.value).toBe(12.5);
269
- expect(companyData.average).toBe(8.2);
270
-
271
- const sectorData = comparison.data[1];
272
- expect(sectorData.slug).toBe("SECTOR_AVERAGE");
273
- expect(sectorData.value).toBe(7.8);
274
- expect(sectorData.average).toBe(8.2);
275
-
276
- expect(financialClient.getFinancialRatioComparison).toHaveBeenCalledWith(
277
- "TUPRS",
278
- Region.Tr,
279
- RatioComparisonPeerType.Sector
280
- );
267
+
268
+ const item = resp[0];
269
+ expect(item.metricName).toBe("pricing");
270
+ expect(item.normalizedValue).toBe(57.1272139065688);
271
+
272
+ expect(item.data).toHaveLength(1);
273
+ expect(item.data[0].slug).toBe("F/K");
274
+ expect(item.data[0].value).toBe(14.9584698641742);
275
+ expect(item.data[0].average).toBe(-26.1316853855662);
276
+ });
277
+
278
+ test("bubbles up request error", async () => {
279
+ cli.request.mockRejectedValueOnce(new Error("Bad request"));
280
+
281
+ await expect(
282
+ client.getFinancialRatioComparison("TUPRS", Region.Tr, RatioComparisonPeerType.Sector)
283
+ ).rejects.toThrow("Bad request");
281
284
  });
282
285
  });
283
-
286
+
284
287
  describe("getHistoricalRatios", () => {
285
- test("should return historical ratios with mock data", async () => {
286
- jest.spyOn(financialClient, 'getHistoricalRatios').mockResolvedValue(mockHistoricalRatiosResponse);
288
+ test("calls correct endpoint/params and matches raw response", async () => {
289
+ cli.request.mockResolvedValueOnce({ data: mockHistoricalRatiosResponse });
287
290
 
288
- const resp = await financialClient.getHistoricalRatios(
291
+ const resp = await client.getHistoricalRatios(
289
292
  "TUPRS",
290
- [HistoricalRatiosKey.NetMargin],
293
+ [HistoricalRatiosKey.ReturnOnEquity],
291
294
  Region.Tr,
292
295
  Locale.Tr
293
296
  );
294
297
 
298
+ expect(cli.request).toHaveBeenCalledTimes(1);
299
+ const call = cli.request.mock.calls[0][0];
300
+
301
+ expect(call.method).toBe("GET");
302
+ expect(call.url).toBe("/api/v2/stock/historical-ratios");
303
+ expect(call.params).toEqual({ symbol: "TUPRS", region: Region.Tr, slugs: "roe", locale: Locale.Tr });
304
+
295
305
  expect(resp).toHaveLength(1);
296
-
297
- const ratio = resp[0];
298
- expect(ratio.slug).toBe(HistoricalRatiosKey.NetMargin);
299
- expect(ratio.finalValue).toBe(12.5);
300
- expect(ratio.threeYearGrowth).toBe(15.2);
301
- expect(ratio.yearGrowth).toBe(5.8);
302
- expect(ratio.finalSectorValue).toBe(7.8);
303
- expect(ratio.currency).toBe(Currency.TRY);
304
- expect(ratio.format).toBe(HistoricalRatiosFormat.PERCENTAGE);
305
- expect(ratio.name).toBe("Net Kar Marjı");
306
-
307
- expect(ratio.items).toHaveLength(2);
308
- const firstItem = ratio.items[0];
309
- expect(firstItem.period).toBe("2024-Q1");
310
- expect(firstItem.value).toBe(12.5);
311
- expect(firstItem.sectorMean).toBe(7.8);
312
-
313
- expect(financialClient.getHistoricalRatios).toHaveBeenCalledWith(
314
- "TUPRS",
315
- [HistoricalRatiosKey.NetMargin],
316
- Region.Tr,
317
- Locale.Tr
318
- );
306
+
307
+ const r = resp[0];
308
+ expect(r.slug).toBe("roe");
309
+ expect(r.finalValue).toBe(0.080499759559051);
310
+ expect(r.threeYearGrowth).toBe(-0.922611834325054);
311
+ expect(r.yearGrowth).toBe(-0.527234781985081);
312
+ expect(r.finalSectorValue).toBe(0.080499759559051);
313
+ expect(r.currency).toBe("TRY");
314
+ expect(r.format).toBe("percentage");
315
+ expect(r.name).toBe("ROE");
316
+
317
+ expect(r.items).toHaveLength(1);
318
+ expect(r.items[0].period).toBe("2025-4");
319
+ expect(r.items[0].value).toBe(0.080499759559051);
320
+ expect(r.items[0].sectorMean).toBe(0.080499759559051);
319
321
  });
320
- });
322
+
323
+ test("omits locale param when locale is not provided", async () => {
324
+ cli.request.mockResolvedValueOnce({ data: mockHistoricalRatiosResponse });
325
+
326
+ await client.getHistoricalRatios("TUPRS", [HistoricalRatiosKey.ReturnOnEquity], Region.Tr);
321
327
 
328
+ const call = cli.request.mock.calls[0][0];
329
+ expect(call.url).toBe("/api/v2/stock/historical-ratios");
330
+ expect(call.params).toEqual({ symbol: "TUPRS", region: Region.Tr, slugs: "roe" });
331
+ expect(call.params.locale).toBeUndefined();
332
+ });
333
+
334
+ test("bubbles up request error", async () => {
335
+ cli.request.mockRejectedValueOnce(new Error("Invalid slugs"));
336
+
337
+ await expect(
338
+ client.getHistoricalRatios("TUPRS", [HistoricalRatiosKey.ReturnOnEquity], Region.Tr, Locale.Tr)
339
+ ).rejects.toThrow("Invalid slugs");
340
+ });
341
+ });
342
+
322
343
  describe("getHistoricalRatiosDescriptions", () => {
323
- test("should return ratio descriptions with mock data", async () => {
324
- jest.spyOn(financialClient, 'getHistoricalRatiosDescriptions').mockResolvedValue(mockRatiosDescriptionsResponse);
344
+ test("calls correct endpoint/params and matches raw response", async () => {
345
+ cli.request.mockResolvedValueOnce({ data: mockRatiosDescriptionsResponse });
325
346
 
326
- const resp = await financialClient.getHistoricalRatiosDescriptions(
327
- Locale.Tr,
328
- Region.Tr
329
- );
347
+ const resp = await client.getHistoricalRatiosDescriptions(Locale.Tr, Region.Tr);
330
348
 
349
+ expect(cli.request).toHaveBeenCalledTimes(1);
350
+ const call = cli.request.mock.calls[0][0];
351
+
352
+ expect(call.method).toBe("GET");
353
+ expect(call.url).toBe("/api/v2/stock/historical-ratios/descriptions");
354
+ expect(call.params).toEqual({ locale: Locale.Tr, region: Region.Tr });
355
+
331
356
  expect(resp).toHaveLength(1);
332
-
333
- const description = resp[0];
334
- expect(description.id).toBe(1);
335
- expect(description.format).toBe(HistoricalRatiosFormat.PERCENTAGE);
336
- expect(description.currency).toBe(Currency.TRY);
337
- expect(description.slug).toBe(HistoricalRatiosKey.NetMargin);
338
- expect(description.name).toBe("Net Kar Marjı");
339
- expect(description.description).toBe("Net karın satışlara oranı");
340
- expect(description.locale).toBe(Locale.Tr);
341
- expect(description.isRealtime).toBe(false);
342
-
343
- expect(financialClient.getHistoricalRatiosDescriptions).toHaveBeenCalledWith(
344
- Locale.Tr,
345
- Region.Tr
357
+
358
+ const d = resp[0];
359
+ expect(d.id).toBe(112);
360
+ expect(d.format).toBe("percentage");
361
+ expect(d.currency).toBe("TRY");
362
+ expect(d.slug).toBe("gross-margin");
363
+ expect(d.createdAt).toBe("2025-04-16T07:50:52.245181Z");
364
+ expect(d.updatedAt).toBe("2025-04-16T07:50:52.245181Z");
365
+ expect(d.name).toBe("Brüt Kâr Marjı");
366
+ expect(d.description).toBe(
367
+ "Satışların maliyetinin gelirden çıkarılmasıyla brüt kâr bulunur. Brüt kârın satışlara oranı brüt kâr marjı olarak isimlendirilir."
346
368
  );
369
+ expect(d.locale).toBe("tr");
370
+ expect(d.isRealtime).toBe(false);
371
+ });
372
+
373
+ test("bubbles up request error", async () => {
374
+ cli.request.mockRejectedValueOnce(new Error("Not found"));
375
+
376
+ await expect(
377
+ client.getHistoricalRatiosDescriptions(Locale.Tr, Region.Tr)
378
+ ).rejects.toThrow("Not found");
347
379
  });
348
380
  });
349
-
381
+
350
382
  describe("getHistoricalFinancialSheets", () => {
351
- test("should return financial sheets with mock data", async () => {
352
- jest.spyOn(financialClient, 'getHistoricalFinancialSheets').mockResolvedValue(mockFinancialSheetsResponse);
383
+ test("throws error locally for balance sheet when period != cumulative (no request)", async () => {
384
+ await expect(
385
+ client.getHistoricalFinancialSheets(
386
+ "TUPRS",
387
+ { year: 2022, month: 1, day: 1 },
388
+ { year: 2025, month: 1, day: 1 },
389
+ FinancialSheetType.BalanceSheet,
390
+ FinancialSheetPeriod.Annual,
391
+ Currency.TRY,
392
+ Region.Tr
393
+ )
394
+ ).rejects.toThrow("balance sheet is only available for cumulative period");
395
+
396
+ expect(cli.request).not.toHaveBeenCalled();
397
+ });
398
+
399
+ test("calls correct endpoint/params and matches raw response", async () => {
400
+ cli.request.mockResolvedValueOnce({ data: mockFinancialSheetsResponse });
353
401
 
354
- const resp = await financialClient.getHistoricalFinancialSheets(
402
+ const resp = await client.getHistoricalFinancialSheets(
355
403
  "TUPRS",
356
404
  { year: 2024, month: 1, day: 1 },
357
405
  { year: 2024, month: 3, day: 31 },
@@ -361,50 +409,54 @@ describe("FinancialRatios", () => {
361
409
  Region.Tr
362
410
  );
363
411
 
412
+ expect(cli.request).toHaveBeenCalledTimes(1);
413
+ const call = cli.request.mock.calls[0][0];
414
+
415
+ expect(call.method).toBe("GET");
416
+ expect(call.url).toBe("/api/v3/stock/historical-financial-sheets");
417
+ expect(call.params).toEqual({
418
+ symbol: "TUPRS",
419
+ from: "2024-01-01",
420
+ to: "2024-03-31",
421
+ sheetType: FinancialSheetType.CashFlow,
422
+ periodType: FinancialSheetPeriod.Quarterly,
423
+ currency: Currency.TRY,
424
+ region: Region.Tr,
425
+ });
426
+
364
427
  expect(resp.sheets).toHaveLength(1);
365
-
366
- const sheet = resp.sheets[0];
367
- expect(sheet.period).toBe("2024-Q1");
368
- expect(sheet.items).toHaveLength(2);
369
-
370
- const revenue = sheet.items[0];
371
- expect(revenue.description).toBe("Satış Gelirleri");
372
- expect(revenue.value).toBe(50000000000);
373
- expect(revenue.lineCodeId).toBe(1);
374
- expect(revenue.indentLevel).toBe(0);
375
-
376
- const cogs = sheet.items[1];
377
- expect(cogs.description).toBe("Satışların Maliyeti");
378
- expect(cogs.value).toBe(-40000000000);
379
- expect(cogs.lineCodeId).toBe(2);
380
- expect(cogs.indentLevel).toBe(0);
381
-
382
- expect(financialClient.getHistoricalFinancialSheets).toHaveBeenCalledWith(
383
- "TUPRS",
384
- { year: 2024, month: 1, day: 1 },
385
- { year: 2024, month: 3, day: 31 },
386
- FinancialSheetType.CashFlow,
387
- FinancialSheetPeriod.Quarterly,
388
- Currency.TRY,
389
- Region.Tr
390
- );
428
+ expect(resp.sheets[0].period).toBe("2024-1");
429
+
430
+ expect(resp.sheets[0].items).toHaveLength(2);
431
+
432
+ const row0 = resp.sheets[0].items[0];
433
+ expect(row0.description).toBe("Satış Gelirleri");
434
+ expect(row0.value).toBe(50000000000);
435
+ expect(row0.lineCodeId).toBe(1);
436
+ expect(row0.indentLevel).toBe(0);
437
+
438
+ const row1 = resp.sheets[0].items[1];
439
+ expect(row1.description).toBe("Satışların Maliyeti");
440
+ expect(row1.value).toBe(-40000000000);
441
+ expect(row1.lineCodeId).toBe(2);
442
+ expect(row1.indentLevel).toBe(0);
391
443
  });
392
-
393
- test("should handle balance sheet period error", async () => {
394
- jest.spyOn(financialClient, 'getHistoricalFinancialSheets').mockRejectedValue(
395
- new Error("Balance sheet is only available for cumulative period")
396
- );
397
-
398
- await expect(financialClient.getHistoricalFinancialSheets(
399
- "TUPRS",
400
- { year: 2024, month: 1, day: 1 },
401
- { year: 2024, month: 3, day: 31 },
402
- FinancialSheetType.BalanceSheet,
403
- FinancialSheetPeriod.Quarterly,
404
- Currency.TRY,
405
- Region.Tr
406
- )).rejects.toThrow("Balance sheet is only available for cumulative period");
444
+
445
+ test("bubbles up request error", async () => {
446
+ cli.request.mockRejectedValueOnce(new Error("Invalid parameters"));
447
+
448
+ await expect(
449
+ client.getHistoricalFinancialSheets(
450
+ "TUPRS",
451
+ { year: 2024, month: 1, day: 1 },
452
+ { year: 2024, month: 3, day: 31 },
453
+ FinancialSheetType.CashFlow,
454
+ FinancialSheetPeriod.Quarterly,
455
+ Currency.TRY,
456
+ Region.Tr
457
+ )
458
+ ).rejects.toThrow("Invalid parameters");
407
459
  });
408
460
  });
409
- });
461
+ });
410
462
  });