@orderly.network/ui-tradingview 2.8.3 → 2.8.4-alpha.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/dist/index.mjs CHANGED
@@ -1010,21 +1010,32 @@ function getErrorMessage(error) {
1010
1010
  }
1011
1011
 
1012
1012
  // src/tradingviewAdapter/datafeed/history-provider.ts
1013
- var HistoryProvider = class {
1013
+ var HISTORY_PATH = "tv/history";
1014
+ var KLINE_HISTORY_PATH = "v1/tv/kline_history";
1015
+ var _HistoryProvider = class _HistoryProvider {
1014
1016
  constructor(datafeedUrl, requester, limitedServerResponse) {
1017
+ this._klinePreference = /* @__PURE__ */ new Map();
1015
1018
  this._datafeedUrl = datafeedUrl;
1016
1019
  this._requester = requester;
1017
1020
  this._limitedServerResponse = limitedServerResponse;
1018
1021
  }
1019
- getBars(symbolInfo, resolution, periodParams) {
1022
+ /**
1023
+ * Build request parameters for history API calls
1024
+ * @param symbolInfo - Symbol information
1025
+ * @param resolution - Resolution string
1026
+ * @param periodParams - Period parameters with optional countback
1027
+ * @returns Request parameters object
1028
+ */
1029
+ _buildRequestParams(symbolInfo, resolution, periodParams) {
1020
1030
  const requestParams = {
1021
1031
  symbol: symbolInfo.ticker || "",
1022
1032
  resolution,
1023
1033
  from: periodParams.from,
1024
1034
  to: periodParams.to
1025
1035
  };
1036
+ const countBack = Math.min(periodParams.countBack ?? 0, 1e3);
1026
1037
  if (periodParams.countBack !== void 0) {
1027
- requestParams.countback = periodParams.countBack;
1038
+ requestParams.countback = countBack;
1028
1039
  }
1029
1040
  if (symbolInfo.currency_code !== void 0) {
1030
1041
  requestParams.currencyCode = symbolInfo.currency_code;
@@ -1032,28 +1043,127 @@ var HistoryProvider = class {
1032
1043
  if (symbolInfo.unit_id !== void 0) {
1033
1044
  requestParams.unitId = symbolInfo.unit_id;
1034
1045
  }
1046
+ return requestParams;
1047
+ }
1048
+ getBars(symbolInfo, resolution, periodParams) {
1049
+ const requestParams = this._buildRequestParams(
1050
+ symbolInfo,
1051
+ resolution,
1052
+ periodParams
1053
+ );
1054
+ const preferenceKey = this._getPreferenceKey(symbolInfo, resolution);
1055
+ const prefersKline = this._klinePreference.get(preferenceKey) === true;
1056
+ const countBack = Math.min(periodParams.countBack ?? 0, 1e3);
1035
1057
  return new Promise(
1036
1058
  async (resolve, reject) => {
1037
1059
  try {
1038
- const initialResponse = await this._requester.sendRequest(
1039
- this._datafeedUrl,
1040
- "history",
1041
- requestParams
1042
- );
1043
- const result = this._processHistoryResponse(initialResponse);
1044
- if (this._limitedServerResponse) {
1045
- await this._processTruncatedResponse(result, requestParams);
1060
+ let result;
1061
+ let usedHistoryResult = false;
1062
+ if (prefersKline) {
1063
+ result = await this._requestKlineHistory(
1064
+ this._buildKlineParams(requestParams, countBack)
1065
+ );
1066
+ } else {
1067
+ const initialResponse = await this._requestHistory(requestParams);
1068
+ result = this._processHistoryResponse(initialResponse);
1069
+ usedHistoryResult = true;
1070
+ const needsFallback = this._shouldFallbackToKline(
1071
+ initialResponse,
1072
+ countBack
1073
+ );
1074
+ if (needsFallback) {
1075
+ const klineResult = await this._tryKlineFallback(
1076
+ requestParams,
1077
+ countBack
1078
+ );
1079
+ if (klineResult !== null) {
1080
+ result = klineResult;
1081
+ usedHistoryResult = false;
1082
+ this._klinePreference.set(preferenceKey, true);
1083
+ } else {
1084
+ this._klinePreference.set(preferenceKey, false);
1085
+ }
1086
+ } else {
1087
+ this._klinePreference.set(preferenceKey, false);
1088
+ }
1089
+ }
1090
+ if (usedHistoryResult && this._limitedServerResponse) {
1091
+ await this._processTruncatedResponse(result, { ...requestParams });
1046
1092
  }
1047
1093
  resolve(result);
1048
1094
  } catch (e) {
1049
- if (e instanceof Error || typeof e === "string") {
1050
- const reasonString = getErrorMessage(e);
1051
- reject(reasonString);
1052
- }
1095
+ const error = e instanceof Error ? e : typeof e === "string" ? e : void 0;
1096
+ const reasonString = getErrorMessage(error);
1097
+ reject(reasonString || "Error");
1053
1098
  }
1054
1099
  }
1055
1100
  );
1056
1101
  }
1102
+ /**
1103
+ * Request kline history using KLINE_HISTORY_PATH endpoint
1104
+ * Handles rate limiting (429 errors) by waiting and retrying
1105
+ * @param requestParams - Request parameters
1106
+ * @param retryCount - Current retry attempt (internal use)
1107
+ * @returns Processed history response
1108
+ */
1109
+ async _requestKlineHistory(requestParams, retryCount = 0) {
1110
+ const maxRetries = 5;
1111
+ const baseRetryDelay = 2500;
1112
+ const maxRetryDelay = 1e4;
1113
+ const params = {
1114
+ ...requestParams,
1115
+ resolution: this._mapToKlineHistoryResolution(
1116
+ requestParams.resolution
1117
+ )
1118
+ };
1119
+ const urlPath = this._buildUrlWithParams(KLINE_HISTORY_PATH, params);
1120
+ const options = { credentials: "same-origin" };
1121
+ const requesterWithHeaders = this._requester;
1122
+ if (requesterWithHeaders._headers !== void 0) {
1123
+ options.headers = requesterWithHeaders._headers;
1124
+ }
1125
+ let response;
1126
+ try {
1127
+ response = await fetch(`${this._datafeedUrl}/${urlPath}`, options);
1128
+ } catch (error) {
1129
+ throw error;
1130
+ }
1131
+ if (response.status === 429) {
1132
+ if (retryCount >= maxRetries) {
1133
+ throw new Error(
1134
+ `Rate limit exceeded: Maximum retry attempts (${maxRetries}) reached`
1135
+ );
1136
+ }
1137
+ const retryDelay = Math.min(
1138
+ baseRetryDelay * Math.pow(2, retryCount),
1139
+ maxRetryDelay
1140
+ );
1141
+ const retryAfter = response.headers.get("Retry-After");
1142
+ const delay = retryAfter ? parseInt(retryAfter, 10) * 1e3 : retryDelay;
1143
+ await new Promise((resolve) => setTimeout(resolve, delay));
1144
+ return this._requestKlineHistory(requestParams, retryCount + 1);
1145
+ }
1146
+ if (!response.ok) {
1147
+ const errorData = await response.json().catch(() => ({
1148
+ message: response.statusText
1149
+ }));
1150
+ throw new Error(
1151
+ errorData.message || errorData.errmsg || response.statusText
1152
+ );
1153
+ }
1154
+ let data;
1155
+ try {
1156
+ data = await response.json();
1157
+ } catch {
1158
+ throw new Error("Failed to parse response JSON");
1159
+ }
1160
+ return this._processHistoryResponse(data);
1161
+ }
1162
+ /**
1163
+ * Process truncated response by making follow-up requests if needed
1164
+ * @param result - Current result with bars
1165
+ * @param requestParams - Request parameters
1166
+ */
1057
1167
  async _processTruncatedResponse(result, requestParams) {
1058
1168
  let lastResultLength = result.bars.length;
1059
1169
  try {
@@ -1070,7 +1180,7 @@ var HistoryProvider = class {
1070
1180
  }
1071
1181
  const followupResponse = await this._requester.sendRequest(
1072
1182
  this._datafeedUrl,
1073
- "history",
1183
+ HISTORY_PATH,
1074
1184
  requestParams
1075
1185
  );
1076
1186
  const followupResult = this._processHistoryResponse(followupResponse);
@@ -1137,7 +1247,106 @@ var HistoryProvider = class {
1137
1247
  meta
1138
1248
  };
1139
1249
  }
1250
+ /**
1251
+ * Maps TradingView resolution format to Kline History API resolution format
1252
+ * @param resolution - TradingView resolution string (e.g., "1", "60", "1D")
1253
+ * @returns Kline History API resolution string (e.g., "1m", "1h", "1d")
1254
+ */
1255
+ _mapToKlineHistoryResolution(resolution) {
1256
+ return _HistoryProvider._RESOLUTION_MAP.get(resolution) ?? resolution;
1257
+ }
1258
+ _buildUrlWithParams(path, params) {
1259
+ if (!params || Object.keys(params).length === 0) {
1260
+ return path;
1261
+ }
1262
+ const searchParams = new URLSearchParams();
1263
+ Object.keys(params).forEach((key) => {
1264
+ const value = params[key];
1265
+ if (Array.isArray(value)) {
1266
+ value.forEach((item) => searchParams.append(key, item));
1267
+ } else {
1268
+ searchParams.append(key, value.toString());
1269
+ }
1270
+ });
1271
+ const queryString = searchParams.toString();
1272
+ return queryString ? `${path}?${queryString}` : path;
1273
+ }
1274
+ _getPreferenceKey(symbolInfo, resolution) {
1275
+ return `${symbolInfo.ticker ?? ""}|${resolution}`;
1276
+ }
1277
+ _requestHistory(requestParams) {
1278
+ return this._requester.sendRequest(
1279
+ this._datafeedUrl,
1280
+ HISTORY_PATH,
1281
+ requestParams
1282
+ );
1283
+ }
1284
+ _buildKlineParams(requestParams, countBack) {
1285
+ const params = {
1286
+ ...requestParams
1287
+ };
1288
+ delete params.countback;
1289
+ if (countBack > 0) {
1290
+ params.limit = countBack;
1291
+ } else {
1292
+ delete params.limit;
1293
+ }
1294
+ return params;
1295
+ }
1296
+ async _tryKlineFallback(requestParams, countBack) {
1297
+ try {
1298
+ const result = await this._requestKlineHistory(
1299
+ this._buildKlineParams(requestParams, countBack)
1300
+ );
1301
+ return result.bars.length > 0 ? result : null;
1302
+ } catch {
1303
+ return null;
1304
+ }
1305
+ }
1306
+ _shouldFallbackToKline(response, expectedCount) {
1307
+ if (response.s !== "ok") {
1308
+ return false;
1309
+ }
1310
+ const barsCount = response.t.length;
1311
+ if (expectedCount > 0 && barsCount < expectedCount) {
1312
+ return true;
1313
+ }
1314
+ if (this._limitedServerResponse && this._limitedServerResponse.maxResponseLength > 0 && barsCount >= this._limitedServerResponse.maxResponseLength) {
1315
+ return true;
1316
+ }
1317
+ return false;
1318
+ }
1140
1319
  };
1320
+ /**
1321
+ * Static mapping table for resolution conversion
1322
+ * Maps TradingView resolution format to Kline History API resolution format
1323
+ * Key: TradingView resolution, Value: Kline History API resolution
1324
+ */
1325
+ _HistoryProvider._RESOLUTION_MAP = /* @__PURE__ */ new Map([
1326
+ ["1", "1m"],
1327
+ // 1 minute
1328
+ ["3", "3m"],
1329
+ // 3 minutes
1330
+ ["5", "5m"],
1331
+ // 5 minutes
1332
+ ["15", "15m"],
1333
+ // 15 minutes
1334
+ ["30", "30m"],
1335
+ // 30 minutes
1336
+ ["60", "1h"],
1337
+ // 1 hour
1338
+ ["240", "4h"],
1339
+ // 4 hours
1340
+ ["720", "12h"],
1341
+ // 12 hours
1342
+ ["1D", "1d"],
1343
+ // 1 day
1344
+ ["1W", "1w"],
1345
+ // 1 week
1346
+ ["1M", "1mon"]
1347
+ // 1 month (mapped to 1m)
1348
+ ]);
1349
+ var HistoryProvider = _HistoryProvider;
1141
1350
 
1142
1351
  // src/tradingviewAdapter/datafeed/requester.ts
1143
1352
  var Requester = class {
@@ -1162,7 +1371,15 @@ var Requester = class {
1162
1371
  if (this._headers !== void 0) {
1163
1372
  options.headers = this._headers;
1164
1373
  }
1165
- return fetch(`${datafeedUrl}/${urlPath}`, options).then((response) => response.text()).then((responseTest) => JSON.parse(responseTest));
1374
+ return fetch(`${datafeedUrl}/${urlPath}`, options).then((response) => response.json()).then((data) => {
1375
+ if (typeof data.success === "undefined") {
1376
+ return data;
1377
+ }
1378
+ if (!data.success) {
1379
+ throw new Error(data.message);
1380
+ }
1381
+ return data;
1382
+ });
1166
1383
  }
1167
1384
  };
1168
1385
 
@@ -1261,9 +1478,13 @@ var SymbolsStorage = class {
1261
1478
  _requestExchangeData(exchange) {
1262
1479
  return new Promise(
1263
1480
  (resolve, reject) => {
1264
- this._requester.sendRequest(this._datafeedUrl, "symbol_info", {
1265
- group: exchange
1266
- }).then((response) => {
1481
+ this._requester.sendRequest(
1482
+ this._datafeedUrl,
1483
+ "tv/symbol_info",
1484
+ {
1485
+ group: exchange
1486
+ }
1487
+ ).then((response) => {
1267
1488
  try {
1268
1489
  this._onExchangeDataReceived(exchange, response);
1269
1490
  } catch (error) {
@@ -1356,11 +1577,13 @@ var SymbolsStorage = class {
1356
1577
  extractField(data, "intraday-multipliers", symbolIndex, true),
1357
1578
  ["1", "5", "15", "30", "60"]
1358
1579
  ),
1359
- has_weekly_and_monthly: extractField(
1360
- data,
1361
- "has-weekly-and-monthly",
1362
- symbolIndex
1363
- ),
1580
+ // daily_multipliers:["1"],
1581
+ // has_weekly_and_monthly: extractField(
1582
+ // data,
1583
+ // "has-weekly-and-monthly",
1584
+ // symbolIndex,
1585
+ // ),
1586
+ has_weekly_and_monthly: true,
1364
1587
  has_empty_bars: extractField(data, "has-empty-bars", symbolIndex),
1365
1588
  volume_precision: definedValueOrDefault(
1366
1589
  extractField(data, "volume-precision", symbolIndex),
@@ -1394,6 +1617,7 @@ var AbstractDatafeed = class {
1394
1617
  constructor(datafeedURL) {
1395
1618
  this._configuration = defaultConfiguration();
1396
1619
  this._symbolsStorage = null;
1620
+ this._historyCursor = null;
1397
1621
  this._datafeedURL = datafeedURL;
1398
1622
  this._requester = new Requester();
1399
1623
  this._historyProvider = new HistoryProvider(datafeedURL, this._requester);
@@ -1407,9 +1631,52 @@ var AbstractDatafeed = class {
1407
1631
  );
1408
1632
  }
1409
1633
  getBars(symbolInfo, resolution, periodParams, onResult, onError) {
1410
- this._historyProvider.getBars(symbolInfo, resolution, periodParams).then((result) => {
1411
- onResult(result.bars, result.meta);
1412
- }).catch(onError);
1634
+ const { to, firstDataRequest } = periodParams;
1635
+ if (firstDataRequest || this._historyCursor === null) {
1636
+ this._historyCursor = to;
1637
+ }
1638
+ const resolutionInSeconds = this._resolutionToSeconds(resolution);
1639
+ const cursor = this._historyCursor ?? to;
1640
+ const maxBarsPerRequest = 1e3;
1641
+ const shouldUseOriginalRange = resolution.toLowerCase?.() === "1m" && periodParams.from !== void 0;
1642
+ const barsToLoad = shouldUseOriginalRange ? periodParams.countBack ?? maxBarsPerRequest : maxBarsPerRequest;
1643
+ const requestTo = shouldUseOriginalRange ? periodParams.to ?? to : cursor;
1644
+ const requestFrom = shouldUseOriginalRange ? periodParams.from : Math.max(requestTo - resolutionInSeconds * barsToLoad, 0);
1645
+ this._historyProvider.getBars(symbolInfo, resolution, {
1646
+ ...periodParams,
1647
+ countBack: barsToLoad,
1648
+ from: requestFrom,
1649
+ to: requestTo
1650
+ }).then(
1651
+ (result) => {
1652
+ if (result.bars.length > 0) {
1653
+ this._historyCursor = Math.floor(result.bars[0].time / 1e3) - 1;
1654
+ }
1655
+ onResult(result.bars, result.meta);
1656
+ },
1657
+ (error) => {
1658
+ onError(error);
1659
+ }
1660
+ ).catch((error) => {
1661
+ onError(error);
1662
+ });
1663
+ }
1664
+ _resolutionToSeconds(resolution) {
1665
+ switch (resolution) {
1666
+ case "1D":
1667
+ return 86400;
1668
+ case "3D":
1669
+ return 86400 * 3;
1670
+ case "1W":
1671
+ return 86400 * 7;
1672
+ case "1M":
1673
+ return 86400 * 30;
1674
+ }
1675
+ const res = parseInt(resolution);
1676
+ if (!isNaN(res)) {
1677
+ return res * 60;
1678
+ }
1679
+ return 60;
1413
1680
  }
1414
1681
  onReady(callback) {
1415
1682
  this._configurationReadyPromise.then(() => {
@@ -1445,7 +1712,7 @@ var AbstractDatafeed = class {
1445
1712
  getServerTime() {
1446
1713
  }
1447
1714
  _requestConfiguration() {
1448
- return this._send("config").catch(
1715
+ return this._send("tv/config").catch(
1449
1716
  (reason) => {
1450
1717
  logMessage(
1451
1718
  `Datafeed: Cannot get datafeed configuration - use default, error=${getErrorMessage(
@@ -1728,7 +1995,7 @@ var getAutoIncrementId = /* @__PURE__ */ (() => {
1728
1995
  })();
1729
1996
  var Datafeed = class extends AbstractDatafeed {
1730
1997
  constructor(apiUrl, ws) {
1731
- const datafeedURL = `${apiUrl}/tv`;
1998
+ const datafeedURL = `${apiUrl}`;
1732
1999
  super(datafeedURL);
1733
2000
  this.bbosMap = /* @__PURE__ */ new Map();
1734
2001
  this.tickersMap = /* @__PURE__ */ new Map();
@@ -3390,6 +3657,11 @@ var Widget = class {
3390
3657
  } catch (e) {
3391
3658
  }
3392
3659
  }
3660
+ setResolution(resolution) {
3661
+ if (this._instance) {
3662
+ this._instance.activeChart().setResolution(resolution);
3663
+ }
3664
+ }
3393
3665
  executeActionById(actionId) {
3394
3666
  try {
3395
3667
  this._instance?.onChartReady(() => {
@@ -3474,7 +3746,7 @@ var Widget = class {
3474
3746
  "30",
3475
3747
  "60",
3476
3748
  "240",
3477
- "1D",
3749
+ "1d",
3478
3750
  "1W",
3479
3751
  "1M"
3480
3752
  ],
@@ -3684,7 +3956,7 @@ function useTradingviewScript(props) {
3684
3956
  }
3685
3957
  localStorage.setItem(TradingViewSDKLocalstorageKey.interval, newInterval);
3686
3958
  setInterval(newInterval);
3687
- chart.current?.setSymbol(symbol ?? "", newInterval);
3959
+ chart.current?.setResolution(newInterval);
3688
3960
  };
3689
3961
  const changeLineType = (newLineType) => {
3690
3962
  if (!chart.current) {