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,5 +1,5 @@
|
|
|
1
1
|
import { Client } from './client';
|
|
2
|
-
import { Stock } from './stocks';
|
|
2
|
+
import { HistoricalPricePeriod, PriceDataPoint, Stock } from './stocks';
|
|
3
3
|
|
|
4
4
|
export enum CollectionType {
|
|
5
5
|
Sector = 'sector',
|
|
@@ -42,52 +42,81 @@ export interface CollectionDetail extends Collection {
|
|
|
42
42
|
stocks: Stock[];
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export interface CollectionPriceGraph {
|
|
46
|
+
previous_close: number;
|
|
47
|
+
graph: PriceDataPoint[];
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
export class CollectionClient extends Client {
|
|
46
|
-
|
|
51
|
+
async getAllSectors(region: Region, locale: Locale): Promise<Collection[]> {
|
|
47
52
|
return this.sendRequest<Collection[]>({
|
|
48
53
|
method: 'GET',
|
|
49
|
-
url: `/api/v1
|
|
54
|
+
url: `/api/v1/sector`,
|
|
50
55
|
params: { region, locale },
|
|
51
56
|
});
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return this.sendRequest<
|
|
58
|
+
|
|
59
|
+
async getAllIndustries(region: Region, locale: Locale): Promise<Collection[]> {
|
|
60
|
+
return this.sendRequest<Collection[]>({
|
|
56
61
|
method: 'GET',
|
|
57
|
-
url: `/api/v1
|
|
62
|
+
url: `/api/v1/industry`,
|
|
58
63
|
params: { region, locale },
|
|
59
64
|
});
|
|
60
65
|
}
|
|
61
|
-
|
|
62
|
-
async getAllSectors(region: Region, locale: Locale): Promise<Collection[]> {
|
|
63
|
-
return this.getAllCollectionsPrivate(CollectionType.Sector, region, locale);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async getAllIndustries(region: Region, locale: Locale): Promise<Collection[]> {
|
|
67
|
-
return this.getAllCollectionsPrivate(CollectionType.Industry, region, locale);
|
|
68
|
-
}
|
|
69
|
-
|
|
66
|
+
|
|
70
67
|
async getAllThemes(region: Region, locale: Locale): Promise<Collection[]> {
|
|
71
|
-
return this.
|
|
68
|
+
return this.sendRequest<Collection[]>({
|
|
69
|
+
method: 'GET',
|
|
70
|
+
url: `/api/v1/theme`,
|
|
71
|
+
params: { region, locale },
|
|
72
|
+
});
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
async getAllCollections(region: Region, locale: Locale): Promise<Collection[]> {
|
|
75
|
-
return this.
|
|
76
|
+
return this.sendRequest<Collection[]>({
|
|
77
|
+
method: 'GET',
|
|
78
|
+
url: `/api/v1/collection`,
|
|
79
|
+
params: { region, locale },
|
|
80
|
+
});
|
|
76
81
|
}
|
|
77
|
-
|
|
82
|
+
|
|
78
83
|
async getSectorDetail(id: string, region: Region, locale: Locale): Promise<CollectionDetail> {
|
|
79
|
-
return this.
|
|
84
|
+
return this.sendRequest<CollectionDetail>({
|
|
85
|
+
method: 'GET',
|
|
86
|
+
url: `/api/v1/sector/${id}`,
|
|
87
|
+
params: { region, locale },
|
|
88
|
+
});
|
|
80
89
|
}
|
|
81
|
-
|
|
90
|
+
|
|
82
91
|
async getIndustryDetail(id: string, region: Region, locale: Locale): Promise<CollectionDetail> {
|
|
83
|
-
return this.
|
|
92
|
+
return this.sendRequest<CollectionDetail>({
|
|
93
|
+
method: 'GET',
|
|
94
|
+
url: `/api/v1/industry/${id}`,
|
|
95
|
+
params: { region, locale },
|
|
96
|
+
});
|
|
84
97
|
}
|
|
85
|
-
|
|
98
|
+
|
|
86
99
|
async getThemeDetail(id: string, region: Region, locale: Locale): Promise<CollectionDetail> {
|
|
87
|
-
return this.
|
|
100
|
+
return this.sendRequest<CollectionDetail>({
|
|
101
|
+
method: 'GET',
|
|
102
|
+
url: `/api/v1/theme/${id}`,
|
|
103
|
+
params: { region, locale },
|
|
104
|
+
});
|
|
88
105
|
}
|
|
89
|
-
|
|
106
|
+
|
|
90
107
|
async getCollectionDetail(id: string, region: Region, locale: Locale): Promise<CollectionDetail> {
|
|
91
|
-
return this.
|
|
108
|
+
return this.sendRequest<CollectionDetail>({
|
|
109
|
+
method: 'GET',
|
|
110
|
+
url: `/api/v1/collection/${id}`,
|
|
111
|
+
params: { region, locale },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async getAggregateGraph(period: HistoricalPricePeriod, sectorId: string, industryId: string, collectionId: string, region: Region): Promise<CollectionPriceGraph> {
|
|
116
|
+
return this.sendRequest<CollectionPriceGraph>({
|
|
117
|
+
method: 'GET',
|
|
118
|
+
url: `/api/v1/aggregate/graph`,
|
|
119
|
+
params: { period, sectorId, industryId, collectionId, region },
|
|
120
|
+
});
|
|
92
121
|
}
|
|
93
|
-
}
|
|
122
|
+
}
|
|
@@ -75,13 +75,10 @@ export class FinancialFundamentalsClient extends Client {
|
|
|
75
75
|
symbol: string,
|
|
76
76
|
region: Region
|
|
77
77
|
): Promise<StockDividend[]> {
|
|
78
|
-
const url = new URL(`${this["baseUrl"]}/api/v2/stock/dividends`);
|
|
79
|
-
url.searchParams.append("symbol", symbol);
|
|
80
|
-
url.searchParams.append("region", region);
|
|
81
|
-
|
|
82
78
|
return this.sendRequest<StockDividend[]>({
|
|
83
79
|
method: 'GET',
|
|
84
|
-
url:
|
|
80
|
+
url: "/api/v2/stock/dividends",
|
|
81
|
+
params: { symbol, region }
|
|
85
82
|
});
|
|
86
83
|
}
|
|
87
84
|
|
|
@@ -3,7 +3,7 @@ import { Region, Locale } from "./collections";
|
|
|
3
3
|
|
|
4
4
|
export enum RatioComparisonPeerType {
|
|
5
5
|
Industry = "industry",
|
|
6
|
-
Sector = "sector"
|
|
6
|
+
Sector = "sector",
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface StockPeerFinancialRatioComparison {
|
|
@@ -43,79 +43,79 @@ export enum HistoricalRatiosFormat {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export enum HistoricalRatiosKey {
|
|
46
|
-
Revenue =
|
|
47
|
-
EBITDA =
|
|
48
|
-
NetProfit =
|
|
49
|
-
GrossMargin =
|
|
50
|
-
NetMargin =
|
|
51
|
-
ReturnOnAssets =
|
|
52
|
-
ReturnOnEquity =
|
|
53
|
-
ReturnOnCapitalEmployed =
|
|
54
|
-
ReturnOnInvestedCapital =
|
|
55
|
-
PriceToEarningsRatio =
|
|
56
|
-
PriceToEarnings =
|
|
57
|
-
PriceToBookRatio =
|
|
58
|
-
EnterpriseValueToEBITDA =
|
|
59
|
-
EnterpriseValueToInvestedCapital =
|
|
60
|
-
InterestCoverage =
|
|
61
|
-
QuickRatio =
|
|
62
|
-
LeverageRatio =
|
|
63
|
-
DebtToEquity =
|
|
64
|
-
RevenueGrowth =
|
|
65
|
-
EBITDAGrowth =
|
|
66
|
-
NetProfitGrowth =
|
|
67
|
-
FreeCashFlowGrowth =
|
|
68
|
-
CashConversionCycle =
|
|
69
|
-
DaysSalesOutstanding =
|
|
70
|
-
DaysPayable =
|
|
71
|
-
DaysInventory =
|
|
72
|
-
|
|
73
|
-
EBITDAMargin =
|
|
74
|
-
InventoryTurnover =
|
|
75
|
-
DepositGrowth =
|
|
76
|
-
NetInterestMargin =
|
|
77
|
-
CompensationGrowth =
|
|
78
|
-
PremiumPerCompensation =
|
|
79
|
-
EnterpriseValueToOperatingCashFlow =
|
|
80
|
-
EarningsBeforeTax =
|
|
81
|
-
CapitalExpenditure =
|
|
82
|
-
FinancialInvestments =
|
|
83
|
-
RealtimeEarningsPerShare =
|
|
84
|
-
RealtimeMarketValue =
|
|
85
|
-
RealtimePriceToBookRatio =
|
|
86
|
-
RealtimePriceToEarningsRatio =
|
|
87
|
-
CurrentRatio =
|
|
88
|
-
AssetTurnover =
|
|
89
|
-
TotalOperationalExpense =
|
|
90
|
-
TotalOperationalExpenseToGrossProfit =
|
|
91
|
-
CashAndCashEquivalents =
|
|
92
|
-
CashToAssets =
|
|
93
|
-
CapexToNetProfit =
|
|
94
|
-
RealtimeEnterpriseValueToEBITDA =
|
|
95
|
-
ReceivablesTurnover =
|
|
96
|
-
EarningsPerShare =
|
|
97
|
-
CreditToAssetRatio =
|
|
98
|
-
CreditToDepositRatio =
|
|
99
|
-
TechnicalProfitGrowth =
|
|
100
|
-
NetEarnedPremiumGrowth =
|
|
101
|
-
EBITGrowth =
|
|
102
|
-
CashReturnOnInvestedCapital =
|
|
103
|
-
MarketCapitalization =
|
|
104
|
-
ShortTermToLongTermObligations =
|
|
105
|
-
RetainedEarnings =
|
|
106
|
-
ThreeYearCAGRFreeCashFlow =
|
|
107
|
-
LongTermLoansToPeriodicProfitRatio =
|
|
108
|
-
LongTermLoans =
|
|
109
|
-
CommercialReceivablesToTotalCurrentAssets =
|
|
110
|
-
StockGrowth =
|
|
111
|
-
FiveYearRetainedEarningsChange =
|
|
112
|
-
ThreeYearCAGRRetainedEarnings =
|
|
113
|
-
PotentialOperatingCashFlow =
|
|
114
|
-
FreeCashFlowToEnterpriseValue =
|
|
115
|
-
DebtToDeposit =
|
|
116
|
-
NetDebt =
|
|
117
|
-
PaidCapital =
|
|
118
|
-
FinancialExpensesToEBIT =
|
|
46
|
+
Revenue = "satislar",
|
|
47
|
+
EBITDA = "ebitda",
|
|
48
|
+
NetProfit = "net_kar",
|
|
49
|
+
GrossMargin = "gross-margin",
|
|
50
|
+
NetMargin = "net-margin",
|
|
51
|
+
ReturnOnAssets = "roa",
|
|
52
|
+
ReturnOnEquity = "roe",
|
|
53
|
+
ReturnOnCapitalEmployed = "roce",
|
|
54
|
+
ReturnOnInvestedCapital = "roic",
|
|
55
|
+
PriceToEarningsRatio = "pe-ratio",
|
|
56
|
+
PriceToEarnings = "poe",
|
|
57
|
+
PriceToBookRatio = "pb-ratio",
|
|
58
|
+
EnterpriseValueToEBITDA = "ev-to-ebitda",
|
|
59
|
+
EnterpriseValueToInvestedCapital = "evic",
|
|
60
|
+
InterestCoverage = "interestCoverage",
|
|
61
|
+
QuickRatio = "quick-ratio",
|
|
62
|
+
LeverageRatio = "leverage-ratio",
|
|
63
|
+
DebtToEquity = "debt-to-equity",
|
|
64
|
+
RevenueGrowth = "satis_buyumesi",
|
|
65
|
+
EBITDAGrowth = "favok_buyumesi",
|
|
66
|
+
NetProfitGrowth = "net_kar_buyumesi",
|
|
67
|
+
FreeCashFlowGrowth = "serbest_nakit_akisi_buyumesi",
|
|
68
|
+
CashConversionCycle = "cash-conversion-cycle",
|
|
69
|
+
DaysSalesOutstanding = "days-sales-outstanding",
|
|
70
|
+
DaysPayable = "days-payable",
|
|
71
|
+
DaysInventory = "days-inventory",
|
|
72
|
+
|
|
73
|
+
EBITDAMargin = "favok_marji",
|
|
74
|
+
InventoryTurnover = "inventory-turnover",
|
|
75
|
+
DepositGrowth = "mevduat_buyumesi",
|
|
76
|
+
NetInterestMargin = "net_faiz_marji",
|
|
77
|
+
CompensationGrowth = "gerceklesen_tazminatlar_buyumesi",
|
|
78
|
+
PremiumPerCompensation = "prim_basina_tazminat_orani",
|
|
79
|
+
EnterpriseValueToOperatingCashFlow = "evOcf",
|
|
80
|
+
EarningsBeforeTax = "ebt",
|
|
81
|
+
CapitalExpenditure = "capex",
|
|
82
|
+
FinancialInvestments = "financial_investments",
|
|
83
|
+
RealtimeEarningsPerShare = "realtime_eps-basic",
|
|
84
|
+
RealtimeMarketValue = "realtime_piyasa_degeri",
|
|
85
|
+
RealtimePriceToBookRatio = "realtime_pb-ratio",
|
|
86
|
+
RealtimePriceToEarningsRatio = "realtime_pe-ratio",
|
|
87
|
+
CurrentRatio = "current-ratio",
|
|
88
|
+
AssetTurnover = "asset-turnover",
|
|
89
|
+
TotalOperationalExpense = "total_operational_expense",
|
|
90
|
+
TotalOperationalExpenseToGrossProfit = "total_operational_expense_gross_profit_ratio",
|
|
91
|
+
CashAndCashEquivalents = "cash_and_cash_equivalents",
|
|
92
|
+
CashToAssets = "cash_to_assets",
|
|
93
|
+
CapexToNetProfit = "capex_to_net_profit",
|
|
94
|
+
RealtimeEnterpriseValueToEBITDA = "realtime_ev-to-ebitda",
|
|
95
|
+
ReceivablesTurnover = "alacak_devir_hizi",
|
|
96
|
+
EarningsPerShare = "eps-basic",
|
|
97
|
+
CreditToAssetRatio = "kredi_aktif_orani",
|
|
98
|
+
CreditToDepositRatio = "kredi_mevduat_orani",
|
|
99
|
+
TechnicalProfitGrowth = "teknik_kar_buyumesi",
|
|
100
|
+
NetEarnedPremiumGrowth = "net_kazanilan_prim_buyumesi",
|
|
101
|
+
EBITGrowth = "ebitGrowth",
|
|
102
|
+
CashReturnOnInvestedCapital = "croic",
|
|
103
|
+
MarketCapitalization = "piyasa_degeri",
|
|
104
|
+
ShortTermToLongTermObligations = "short_term_obligations_long_term_obligations",
|
|
105
|
+
RetainedEarnings = "retained_earnings",
|
|
106
|
+
ThreeYearCAGRFreeCashFlow = "three_year_cagr_free_cash_flow",
|
|
107
|
+
LongTermLoansToPeriodicProfitRatio = "long_term_loans_period_profit_ratio",
|
|
108
|
+
LongTermLoans = "long_term_loans",
|
|
109
|
+
CommercialReceivablesToTotalCurrentAssets = "commercial_receivables_total_current_assets",
|
|
110
|
+
StockGrowth = "stock_growth",
|
|
111
|
+
FiveYearRetainedEarningsChange = "five_year_retained_earnings_change",
|
|
112
|
+
ThreeYearCAGRRetainedEarnings = "three_year_cagr_retained_earnings",
|
|
113
|
+
PotentialOperatingCashFlow = "pocf",
|
|
114
|
+
FreeCashFlowToEnterpriseValue = "fcfEv",
|
|
115
|
+
DebtToDeposit = "dd",
|
|
116
|
+
NetDebt = "net_borc",
|
|
117
|
+
PaidCapital = "odenmis_sermaye",
|
|
118
|
+
FinancialExpensesToEBIT = "financial_expenses_ebit_ratio",
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
export interface StockHistoricalRatiosDescription {
|
|
@@ -148,21 +148,21 @@ export interface HistoricalFinancialSheetRow {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
export enum FinancialSheetType {
|
|
151
|
-
IncomeStatement =
|
|
152
|
-
BalanceSheet =
|
|
153
|
-
CashFlow =
|
|
151
|
+
IncomeStatement = "incomeStatement",
|
|
152
|
+
BalanceSheet = "balanceSheet",
|
|
153
|
+
CashFlow = "cashFlowStatement",
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
export enum FinancialSheetPeriod {
|
|
157
|
-
Annual =
|
|
158
|
-
Quarterly =
|
|
159
|
-
Cumulative =
|
|
157
|
+
Annual = "annual",
|
|
158
|
+
Quarterly = "quarterly",
|
|
159
|
+
Cumulative = "cumulative",
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
export enum Currency {
|
|
163
|
-
USD =
|
|
164
|
-
TRY =
|
|
165
|
-
EUR =
|
|
163
|
+
USD = "USD",
|
|
164
|
+
TRY = "TRY",
|
|
165
|
+
EUR = "EUR",
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
export interface FinancialSheetDate {
|
|
@@ -203,7 +203,7 @@ export class FinancialClient extends Client {
|
|
|
203
203
|
url.searchParams.append("slugs", keys.join(","));
|
|
204
204
|
|
|
205
205
|
return this.sendRequest<StockHistoricalRatios[]>({
|
|
206
|
-
method:
|
|
206
|
+
method: "GET",
|
|
207
207
|
url: url.toString(),
|
|
208
208
|
});
|
|
209
209
|
}
|
|
@@ -219,7 +219,7 @@ export class FinancialClient extends Client {
|
|
|
219
219
|
url.searchParams.append("region", region);
|
|
220
220
|
|
|
221
221
|
return this.sendRequest<StockHistoricalRatiosDescription[]>({
|
|
222
|
-
method:
|
|
222
|
+
method: "GET",
|
|
223
223
|
url: url.toString(),
|
|
224
224
|
});
|
|
225
225
|
}
|
|
@@ -233,18 +233,37 @@ export class FinancialClient extends Client {
|
|
|
233
233
|
currency: Currency,
|
|
234
234
|
region: Region
|
|
235
235
|
): Promise<HistoricalFinancialSheets> {
|
|
236
|
-
const url = new URL(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
url.searchParams.append(
|
|
240
|
-
url.searchParams.append(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
const url = new URL(
|
|
237
|
+
`${this["baseUrl"]}/api/v3/stock/historical-financial-sheets`
|
|
238
|
+
);
|
|
239
|
+
url.searchParams.append("symbol", symbol);
|
|
240
|
+
url.searchParams.append(
|
|
241
|
+
"from",
|
|
242
|
+
`${from.year.toString().padStart(4, "0")}-${from.month
|
|
243
|
+
.toString()
|
|
244
|
+
.padStart(2, "0")}-${from.day.toString().padStart(2, "0")}`
|
|
245
|
+
);
|
|
246
|
+
url.searchParams.append(
|
|
247
|
+
"to",
|
|
248
|
+
`${to.year.toString().padStart(4, "0")}-${to.month
|
|
249
|
+
.toString()
|
|
250
|
+
.padStart(2, "0")}-${to.day.toString().padStart(2, "0")}`
|
|
251
|
+
);
|
|
252
|
+
url.searchParams.append("sheetType", sheetType);
|
|
253
|
+
url.searchParams.append("periodType", period);
|
|
254
|
+
url.searchParams.append("currency", currency);
|
|
255
|
+
url.searchParams.append("region", region);
|
|
256
|
+
|
|
257
|
+
if (
|
|
258
|
+
sheetType === FinancialSheetType.BalanceSheet &&
|
|
259
|
+
period !== FinancialSheetPeriod.Cumulative
|
|
260
|
+
) {
|
|
261
|
+
throw new Error("balance sheet is only available for cumulative period");
|
|
262
|
+
}
|
|
244
263
|
|
|
245
264
|
return this.sendRequest<HistoricalFinancialSheets>({
|
|
246
|
-
method:
|
|
265
|
+
method: "GET",
|
|
247
266
|
url: url.toString(),
|
|
248
267
|
});
|
|
249
268
|
}
|
|
250
|
-
}
|
|
269
|
+
}
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
import { Logger } from "winston";
|
|
2
|
+
import { LaplaceConfiguration } from "../utilities/configuration";
|
|
3
|
+
import { Client } from "./client";
|
|
4
|
+
|
|
5
|
+
interface WebSocketUrlResponse {
|
|
6
|
+
url: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface WebSocketUrlParams {
|
|
10
|
+
externalUserId: string;
|
|
11
|
+
feeds: LivePriceFeed[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export enum AccessorType {
|
|
15
|
+
User = "user",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface UpdateUserDetailsParams {
|
|
19
|
+
externalUserID: string;
|
|
20
|
+
firstName?: string;
|
|
21
|
+
lastName?: string;
|
|
22
|
+
address?: string;
|
|
23
|
+
city?: string;
|
|
24
|
+
countryCode?: string;
|
|
25
|
+
accessorType?: AccessorType;
|
|
26
|
+
active: boolean;
|
|
27
|
+
}
|
|
1
28
|
|
|
2
29
|
interface RawBISTStockLiveData {
|
|
3
30
|
_id: number;
|
|
@@ -88,7 +115,7 @@ export class WebSocketError extends Error {
|
|
|
88
115
|
}
|
|
89
116
|
}
|
|
90
117
|
|
|
91
|
-
export class LivePriceWebSocketClient {
|
|
118
|
+
export class LivePriceWebSocketClient extends Client {
|
|
92
119
|
private ws: WebSocket | null = null;
|
|
93
120
|
private subscriptionCounter = 0;
|
|
94
121
|
private subscriptions = new Map<
|
|
@@ -99,7 +126,10 @@ export class LivePriceWebSocketClient {
|
|
|
99
126
|
feed: LivePriceFeed;
|
|
100
127
|
}
|
|
101
128
|
>();
|
|
102
|
-
private symbolLastData = new Map<
|
|
129
|
+
private symbolLastData = new Map<
|
|
130
|
+
string,
|
|
131
|
+
BISTStockLiveData | USStockLiveData
|
|
132
|
+
>();
|
|
103
133
|
private reconnectAttempts = 0;
|
|
104
134
|
private reconnectTimeout: NodeJS.Timeout | null = null;
|
|
105
135
|
private isClosed: boolean = false;
|
|
@@ -110,8 +140,19 @@ export class LivePriceWebSocketClient {
|
|
|
110
140
|
private lastMessageTimestamp: number = 0;
|
|
111
141
|
private inactivityCheckInterval: NodeJS.Timeout | null = null;
|
|
112
142
|
private readonly INACTIVITY_TIMEOUT = 15000;
|
|
143
|
+
private externalUserId: string | null = null;
|
|
144
|
+
private feeds: LivePriceFeed[] | null = null;
|
|
113
145
|
|
|
114
|
-
constructor(
|
|
146
|
+
constructor(
|
|
147
|
+
feeds: LivePriceFeed[],
|
|
148
|
+
externalUserId: string,
|
|
149
|
+
cfg: LaplaceConfiguration,
|
|
150
|
+
logger: Logger,
|
|
151
|
+
options: WebSocketOptions = {}
|
|
152
|
+
) {
|
|
153
|
+
super(cfg, logger);
|
|
154
|
+
this.feeds = feeds;
|
|
155
|
+
this.externalUserId = externalUserId;
|
|
115
156
|
this.options = {
|
|
116
157
|
enableLogging: true,
|
|
117
158
|
logLevel: LogLevel.Error,
|
|
@@ -128,17 +169,17 @@ export class LivePriceWebSocketClient {
|
|
|
128
169
|
clearInterval(this.inactivityCheckInterval);
|
|
129
170
|
this.inactivityCheckInterval = null;
|
|
130
171
|
}
|
|
131
|
-
|
|
132
|
-
this.inactivityCheckInterval = setInterval(async ()=> {
|
|
172
|
+
|
|
173
|
+
this.inactivityCheckInterval = setInterval(async () => {
|
|
133
174
|
if (Date.now() - this.lastMessageTimestamp >= this.INACTIVITY_TIMEOUT) {
|
|
134
175
|
this.stopInactivityInterval();
|
|
135
176
|
try {
|
|
136
177
|
this.attemptReconnect();
|
|
137
|
-
} catch(error) {
|
|
178
|
+
} catch (error) {
|
|
138
179
|
this.log(`Failed to reconnect: ${error}`, "error");
|
|
139
180
|
}
|
|
140
181
|
}
|
|
141
|
-
}, this.INACTIVITY_TIMEOUT)
|
|
182
|
+
}, this.INACTIVITY_TIMEOUT);
|
|
142
183
|
}
|
|
143
184
|
|
|
144
185
|
private stopInactivityInterval() {
|
|
@@ -175,12 +216,43 @@ export class LivePriceWebSocketClient {
|
|
|
175
216
|
}
|
|
176
217
|
}
|
|
177
218
|
|
|
178
|
-
async
|
|
219
|
+
async updateUserDetails(params: UpdateUserDetailsParams): Promise<void> {
|
|
220
|
+
const url = new URL(`${this["baseUrl"]}/api/v1/ws/user`);
|
|
221
|
+
|
|
222
|
+
await this.sendRequest<void>({
|
|
223
|
+
method: "PUT",
|
|
224
|
+
url: url.toString(),
|
|
225
|
+
data: params,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async getWebSocketUrl(): Promise<string> {
|
|
230
|
+
const url = new URL(`${this["baseUrl"]}/api/v2/ws/url`);
|
|
231
|
+
|
|
232
|
+
const params: WebSocketUrlParams = {
|
|
233
|
+
externalUserId: this.externalUserId!,
|
|
234
|
+
feeds: this.feeds!,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const response = await this.sendRequest<WebSocketUrlResponse>({
|
|
238
|
+
method: "POST",
|
|
239
|
+
url: url.toString(),
|
|
240
|
+
data: params,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return response.url;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async connect(url?: string): Promise<WebSocket> {
|
|
179
247
|
this.log("Connecting to WebSocket...");
|
|
180
|
-
this.
|
|
248
|
+
if (!this.externalUserId || !this.feeds) {
|
|
249
|
+
throw new Error("External user ID and feeds are required");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.wsUrl = url || (await this.getWebSocketUrl());
|
|
181
253
|
|
|
182
254
|
if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
|
|
183
|
-
this.ws = new WebSocket(
|
|
255
|
+
this.ws = new WebSocket(this.wsUrl);
|
|
184
256
|
this.connectPromise = this.setupWebSocket();
|
|
185
257
|
|
|
186
258
|
await this.connectPromise;
|
|
@@ -419,7 +491,8 @@ export class LivePriceWebSocketClient {
|
|
|
419
491
|
if (symbolHandlers.length === 1) {
|
|
420
492
|
symbolsToAdd.push(symbol);
|
|
421
493
|
} else if (symbolHandlers.length > 1) {
|
|
422
|
-
const lastData: BISTStockLiveData | USStockLiveData | undefined =
|
|
494
|
+
const lastData: BISTStockLiveData | USStockLiveData | undefined =
|
|
495
|
+
this.symbolLastData.get(symbol);
|
|
423
496
|
if (lastData) {
|
|
424
497
|
typedHandler(lastData);
|
|
425
498
|
}
|