@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.js CHANGED
@@ -1016,21 +1016,32 @@ function getErrorMessage(error) {
1016
1016
  }
1017
1017
 
1018
1018
  // src/tradingviewAdapter/datafeed/history-provider.ts
1019
- var HistoryProvider = class {
1019
+ var HISTORY_PATH = "tv/history";
1020
+ var KLINE_HISTORY_PATH = "v1/tv/kline_history";
1021
+ var _HistoryProvider = class _HistoryProvider {
1020
1022
  constructor(datafeedUrl, requester, limitedServerResponse) {
1023
+ this._klinePreference = /* @__PURE__ */ new Map();
1021
1024
  this._datafeedUrl = datafeedUrl;
1022
1025
  this._requester = requester;
1023
1026
  this._limitedServerResponse = limitedServerResponse;
1024
1027
  }
1025
- getBars(symbolInfo, resolution, periodParams) {
1028
+ /**
1029
+ * Build request parameters for history API calls
1030
+ * @param symbolInfo - Symbol information
1031
+ * @param resolution - Resolution string
1032
+ * @param periodParams - Period parameters with optional countback
1033
+ * @returns Request parameters object
1034
+ */
1035
+ _buildRequestParams(symbolInfo, resolution, periodParams) {
1026
1036
  const requestParams = {
1027
1037
  symbol: symbolInfo.ticker || "",
1028
1038
  resolution,
1029
1039
  from: periodParams.from,
1030
1040
  to: periodParams.to
1031
1041
  };
1042
+ const countBack = Math.min(periodParams.countBack ?? 0, 1e3);
1032
1043
  if (periodParams.countBack !== void 0) {
1033
- requestParams.countback = periodParams.countBack;
1044
+ requestParams.countback = countBack;
1034
1045
  }
1035
1046
  if (symbolInfo.currency_code !== void 0) {
1036
1047
  requestParams.currencyCode = symbolInfo.currency_code;
@@ -1038,28 +1049,127 @@ var HistoryProvider = class {
1038
1049
  if (symbolInfo.unit_id !== void 0) {
1039
1050
  requestParams.unitId = symbolInfo.unit_id;
1040
1051
  }
1052
+ return requestParams;
1053
+ }
1054
+ getBars(symbolInfo, resolution, periodParams) {
1055
+ const requestParams = this._buildRequestParams(
1056
+ symbolInfo,
1057
+ resolution,
1058
+ periodParams
1059
+ );
1060
+ const preferenceKey = this._getPreferenceKey(symbolInfo, resolution);
1061
+ const prefersKline = this._klinePreference.get(preferenceKey) === true;
1062
+ const countBack = Math.min(periodParams.countBack ?? 0, 1e3);
1041
1063
  return new Promise(
1042
1064
  async (resolve, reject) => {
1043
1065
  try {
1044
- const initialResponse = await this._requester.sendRequest(
1045
- this._datafeedUrl,
1046
- "history",
1047
- requestParams
1048
- );
1049
- const result = this._processHistoryResponse(initialResponse);
1050
- if (this._limitedServerResponse) {
1051
- await this._processTruncatedResponse(result, requestParams);
1066
+ let result;
1067
+ let usedHistoryResult = false;
1068
+ if (prefersKline) {
1069
+ result = await this._requestKlineHistory(
1070
+ this._buildKlineParams(requestParams, countBack)
1071
+ );
1072
+ } else {
1073
+ const initialResponse = await this._requestHistory(requestParams);
1074
+ result = this._processHistoryResponse(initialResponse);
1075
+ usedHistoryResult = true;
1076
+ const needsFallback = this._shouldFallbackToKline(
1077
+ initialResponse,
1078
+ countBack
1079
+ );
1080
+ if (needsFallback) {
1081
+ const klineResult = await this._tryKlineFallback(
1082
+ requestParams,
1083
+ countBack
1084
+ );
1085
+ if (klineResult !== null) {
1086
+ result = klineResult;
1087
+ usedHistoryResult = false;
1088
+ this._klinePreference.set(preferenceKey, true);
1089
+ } else {
1090
+ this._klinePreference.set(preferenceKey, false);
1091
+ }
1092
+ } else {
1093
+ this._klinePreference.set(preferenceKey, false);
1094
+ }
1095
+ }
1096
+ if (usedHistoryResult && this._limitedServerResponse) {
1097
+ await this._processTruncatedResponse(result, { ...requestParams });
1052
1098
  }
1053
1099
  resolve(result);
1054
1100
  } catch (e) {
1055
- if (e instanceof Error || typeof e === "string") {
1056
- const reasonString = getErrorMessage(e);
1057
- reject(reasonString);
1058
- }
1101
+ const error = e instanceof Error ? e : typeof e === "string" ? e : void 0;
1102
+ const reasonString = getErrorMessage(error);
1103
+ reject(reasonString || "Error");
1059
1104
  }
1060
1105
  }
1061
1106
  );
1062
1107
  }
1108
+ /**
1109
+ * Request kline history using KLINE_HISTORY_PATH endpoint
1110
+ * Handles rate limiting (429 errors) by waiting and retrying
1111
+ * @param requestParams - Request parameters
1112
+ * @param retryCount - Current retry attempt (internal use)
1113
+ * @returns Processed history response
1114
+ */
1115
+ async _requestKlineHistory(requestParams, retryCount = 0) {
1116
+ const maxRetries = 5;
1117
+ const baseRetryDelay = 2500;
1118
+ const maxRetryDelay = 1e4;
1119
+ const params = {
1120
+ ...requestParams,
1121
+ resolution: this._mapToKlineHistoryResolution(
1122
+ requestParams.resolution
1123
+ )
1124
+ };
1125
+ const urlPath = this._buildUrlWithParams(KLINE_HISTORY_PATH, params);
1126
+ const options = { credentials: "same-origin" };
1127
+ const requesterWithHeaders = this._requester;
1128
+ if (requesterWithHeaders._headers !== void 0) {
1129
+ options.headers = requesterWithHeaders._headers;
1130
+ }
1131
+ let response;
1132
+ try {
1133
+ response = await fetch(`${this._datafeedUrl}/${urlPath}`, options);
1134
+ } catch (error) {
1135
+ throw error;
1136
+ }
1137
+ if (response.status === 429) {
1138
+ if (retryCount >= maxRetries) {
1139
+ throw new Error(
1140
+ `Rate limit exceeded: Maximum retry attempts (${maxRetries}) reached`
1141
+ );
1142
+ }
1143
+ const retryDelay = Math.min(
1144
+ baseRetryDelay * Math.pow(2, retryCount),
1145
+ maxRetryDelay
1146
+ );
1147
+ const retryAfter = response.headers.get("Retry-After");
1148
+ const delay = retryAfter ? parseInt(retryAfter, 10) * 1e3 : retryDelay;
1149
+ await new Promise((resolve) => setTimeout(resolve, delay));
1150
+ return this._requestKlineHistory(requestParams, retryCount + 1);
1151
+ }
1152
+ if (!response.ok) {
1153
+ const errorData = await response.json().catch(() => ({
1154
+ message: response.statusText
1155
+ }));
1156
+ throw new Error(
1157
+ errorData.message || errorData.errmsg || response.statusText
1158
+ );
1159
+ }
1160
+ let data;
1161
+ try {
1162
+ data = await response.json();
1163
+ } catch {
1164
+ throw new Error("Failed to parse response JSON");
1165
+ }
1166
+ return this._processHistoryResponse(data);
1167
+ }
1168
+ /**
1169
+ * Process truncated response by making follow-up requests if needed
1170
+ * @param result - Current result with bars
1171
+ * @param requestParams - Request parameters
1172
+ */
1063
1173
  async _processTruncatedResponse(result, requestParams) {
1064
1174
  let lastResultLength = result.bars.length;
1065
1175
  try {
@@ -1076,7 +1186,7 @@ var HistoryProvider = class {
1076
1186
  }
1077
1187
  const followupResponse = await this._requester.sendRequest(
1078
1188
  this._datafeedUrl,
1079
- "history",
1189
+ HISTORY_PATH,
1080
1190
  requestParams
1081
1191
  );
1082
1192
  const followupResult = this._processHistoryResponse(followupResponse);
@@ -1143,7 +1253,106 @@ var HistoryProvider = class {
1143
1253
  meta
1144
1254
  };
1145
1255
  }
1256
+ /**
1257
+ * Maps TradingView resolution format to Kline History API resolution format
1258
+ * @param resolution - TradingView resolution string (e.g., "1", "60", "1D")
1259
+ * @returns Kline History API resolution string (e.g., "1m", "1h", "1d")
1260
+ */
1261
+ _mapToKlineHistoryResolution(resolution) {
1262
+ return _HistoryProvider._RESOLUTION_MAP.get(resolution) ?? resolution;
1263
+ }
1264
+ _buildUrlWithParams(path, params) {
1265
+ if (!params || Object.keys(params).length === 0) {
1266
+ return path;
1267
+ }
1268
+ const searchParams = new URLSearchParams();
1269
+ Object.keys(params).forEach((key) => {
1270
+ const value = params[key];
1271
+ if (Array.isArray(value)) {
1272
+ value.forEach((item) => searchParams.append(key, item));
1273
+ } else {
1274
+ searchParams.append(key, value.toString());
1275
+ }
1276
+ });
1277
+ const queryString = searchParams.toString();
1278
+ return queryString ? `${path}?${queryString}` : path;
1279
+ }
1280
+ _getPreferenceKey(symbolInfo, resolution) {
1281
+ return `${symbolInfo.ticker ?? ""}|${resolution}`;
1282
+ }
1283
+ _requestHistory(requestParams) {
1284
+ return this._requester.sendRequest(
1285
+ this._datafeedUrl,
1286
+ HISTORY_PATH,
1287
+ requestParams
1288
+ );
1289
+ }
1290
+ _buildKlineParams(requestParams, countBack) {
1291
+ const params = {
1292
+ ...requestParams
1293
+ };
1294
+ delete params.countback;
1295
+ if (countBack > 0) {
1296
+ params.limit = countBack;
1297
+ } else {
1298
+ delete params.limit;
1299
+ }
1300
+ return params;
1301
+ }
1302
+ async _tryKlineFallback(requestParams, countBack) {
1303
+ try {
1304
+ const result = await this._requestKlineHistory(
1305
+ this._buildKlineParams(requestParams, countBack)
1306
+ );
1307
+ return result.bars.length > 0 ? result : null;
1308
+ } catch {
1309
+ return null;
1310
+ }
1311
+ }
1312
+ _shouldFallbackToKline(response, expectedCount) {
1313
+ if (response.s !== "ok") {
1314
+ return false;
1315
+ }
1316
+ const barsCount = response.t.length;
1317
+ if (expectedCount > 0 && barsCount < expectedCount) {
1318
+ return true;
1319
+ }
1320
+ if (this._limitedServerResponse && this._limitedServerResponse.maxResponseLength > 0 && barsCount >= this._limitedServerResponse.maxResponseLength) {
1321
+ return true;
1322
+ }
1323
+ return false;
1324
+ }
1146
1325
  };
1326
+ /**
1327
+ * Static mapping table for resolution conversion
1328
+ * Maps TradingView resolution format to Kline History API resolution format
1329
+ * Key: TradingView resolution, Value: Kline History API resolution
1330
+ */
1331
+ _HistoryProvider._RESOLUTION_MAP = /* @__PURE__ */ new Map([
1332
+ ["1", "1m"],
1333
+ // 1 minute
1334
+ ["3", "3m"],
1335
+ // 3 minutes
1336
+ ["5", "5m"],
1337
+ // 5 minutes
1338
+ ["15", "15m"],
1339
+ // 15 minutes
1340
+ ["30", "30m"],
1341
+ // 30 minutes
1342
+ ["60", "1h"],
1343
+ // 1 hour
1344
+ ["240", "4h"],
1345
+ // 4 hours
1346
+ ["720", "12h"],
1347
+ // 12 hours
1348
+ ["1D", "1d"],
1349
+ // 1 day
1350
+ ["1W", "1w"],
1351
+ // 1 week
1352
+ ["1M", "1mon"]
1353
+ // 1 month (mapped to 1m)
1354
+ ]);
1355
+ var HistoryProvider = _HistoryProvider;
1147
1356
 
1148
1357
  // src/tradingviewAdapter/datafeed/requester.ts
1149
1358
  var Requester = class {
@@ -1168,7 +1377,15 @@ var Requester = class {
1168
1377
  if (this._headers !== void 0) {
1169
1378
  options.headers = this._headers;
1170
1379
  }
1171
- return fetch(`${datafeedUrl}/${urlPath}`, options).then((response) => response.text()).then((responseTest) => JSON.parse(responseTest));
1380
+ return fetch(`${datafeedUrl}/${urlPath}`, options).then((response) => response.json()).then((data) => {
1381
+ if (typeof data.success === "undefined") {
1382
+ return data;
1383
+ }
1384
+ if (!data.success) {
1385
+ throw new Error(data.message);
1386
+ }
1387
+ return data;
1388
+ });
1172
1389
  }
1173
1390
  };
1174
1391
 
@@ -1267,9 +1484,13 @@ var SymbolsStorage = class {
1267
1484
  _requestExchangeData(exchange) {
1268
1485
  return new Promise(
1269
1486
  (resolve, reject) => {
1270
- this._requester.sendRequest(this._datafeedUrl, "symbol_info", {
1271
- group: exchange
1272
- }).then((response) => {
1487
+ this._requester.sendRequest(
1488
+ this._datafeedUrl,
1489
+ "tv/symbol_info",
1490
+ {
1491
+ group: exchange
1492
+ }
1493
+ ).then((response) => {
1273
1494
  try {
1274
1495
  this._onExchangeDataReceived(exchange, response);
1275
1496
  } catch (error) {
@@ -1362,11 +1583,13 @@ var SymbolsStorage = class {
1362
1583
  extractField(data, "intraday-multipliers", symbolIndex, true),
1363
1584
  ["1", "5", "15", "30", "60"]
1364
1585
  ),
1365
- has_weekly_and_monthly: extractField(
1366
- data,
1367
- "has-weekly-and-monthly",
1368
- symbolIndex
1369
- ),
1586
+ // daily_multipliers:["1"],
1587
+ // has_weekly_and_monthly: extractField(
1588
+ // data,
1589
+ // "has-weekly-and-monthly",
1590
+ // symbolIndex,
1591
+ // ),
1592
+ has_weekly_and_monthly: true,
1370
1593
  has_empty_bars: extractField(data, "has-empty-bars", symbolIndex),
1371
1594
  volume_precision: definedValueOrDefault(
1372
1595
  extractField(data, "volume-precision", symbolIndex),
@@ -1400,6 +1623,7 @@ var AbstractDatafeed = class {
1400
1623
  constructor(datafeedURL) {
1401
1624
  this._configuration = defaultConfiguration();
1402
1625
  this._symbolsStorage = null;
1626
+ this._historyCursor = null;
1403
1627
  this._datafeedURL = datafeedURL;
1404
1628
  this._requester = new Requester();
1405
1629
  this._historyProvider = new HistoryProvider(datafeedURL, this._requester);
@@ -1413,9 +1637,52 @@ var AbstractDatafeed = class {
1413
1637
  );
1414
1638
  }
1415
1639
  getBars(symbolInfo, resolution, periodParams, onResult, onError) {
1416
- this._historyProvider.getBars(symbolInfo, resolution, periodParams).then((result) => {
1417
- onResult(result.bars, result.meta);
1418
- }).catch(onError);
1640
+ const { to, firstDataRequest } = periodParams;
1641
+ if (firstDataRequest || this._historyCursor === null) {
1642
+ this._historyCursor = to;
1643
+ }
1644
+ const resolutionInSeconds = this._resolutionToSeconds(resolution);
1645
+ const cursor = this._historyCursor ?? to;
1646
+ const maxBarsPerRequest = 1e3;
1647
+ const shouldUseOriginalRange = resolution.toLowerCase?.() === "1m" && periodParams.from !== void 0;
1648
+ const barsToLoad = shouldUseOriginalRange ? periodParams.countBack ?? maxBarsPerRequest : maxBarsPerRequest;
1649
+ const requestTo = shouldUseOriginalRange ? periodParams.to ?? to : cursor;
1650
+ const requestFrom = shouldUseOriginalRange ? periodParams.from : Math.max(requestTo - resolutionInSeconds * barsToLoad, 0);
1651
+ this._historyProvider.getBars(symbolInfo, resolution, {
1652
+ ...periodParams,
1653
+ countBack: barsToLoad,
1654
+ from: requestFrom,
1655
+ to: requestTo
1656
+ }).then(
1657
+ (result) => {
1658
+ if (result.bars.length > 0) {
1659
+ this._historyCursor = Math.floor(result.bars[0].time / 1e3) - 1;
1660
+ }
1661
+ onResult(result.bars, result.meta);
1662
+ },
1663
+ (error) => {
1664
+ onError(error);
1665
+ }
1666
+ ).catch((error) => {
1667
+ onError(error);
1668
+ });
1669
+ }
1670
+ _resolutionToSeconds(resolution) {
1671
+ switch (resolution) {
1672
+ case "1D":
1673
+ return 86400;
1674
+ case "3D":
1675
+ return 86400 * 3;
1676
+ case "1W":
1677
+ return 86400 * 7;
1678
+ case "1M":
1679
+ return 86400 * 30;
1680
+ }
1681
+ const res = parseInt(resolution);
1682
+ if (!isNaN(res)) {
1683
+ return res * 60;
1684
+ }
1685
+ return 60;
1419
1686
  }
1420
1687
  onReady(callback) {
1421
1688
  this._configurationReadyPromise.then(() => {
@@ -1451,7 +1718,7 @@ var AbstractDatafeed = class {
1451
1718
  getServerTime() {
1452
1719
  }
1453
1720
  _requestConfiguration() {
1454
- return this._send("config").catch(
1721
+ return this._send("tv/config").catch(
1455
1722
  (reason) => {
1456
1723
  logMessage(
1457
1724
  `Datafeed: Cannot get datafeed configuration - use default, error=${getErrorMessage(
@@ -1734,7 +2001,7 @@ var getAutoIncrementId = /* @__PURE__ */ (() => {
1734
2001
  })();
1735
2002
  var Datafeed = class extends AbstractDatafeed {
1736
2003
  constructor(apiUrl, ws) {
1737
- const datafeedURL = `${apiUrl}/tv`;
2004
+ const datafeedURL = `${apiUrl}`;
1738
2005
  super(datafeedURL);
1739
2006
  this.bbosMap = /* @__PURE__ */ new Map();
1740
2007
  this.tickersMap = /* @__PURE__ */ new Map();
@@ -3396,6 +3663,11 @@ var Widget = class {
3396
3663
  } catch (e) {
3397
3664
  }
3398
3665
  }
3666
+ setResolution(resolution) {
3667
+ if (this._instance) {
3668
+ this._instance.activeChart().setResolution(resolution);
3669
+ }
3670
+ }
3399
3671
  executeActionById(actionId) {
3400
3672
  try {
3401
3673
  this._instance?.onChartReady(() => {
@@ -3480,7 +3752,7 @@ var Widget = class {
3480
3752
  "30",
3481
3753
  "60",
3482
3754
  "240",
3483
- "1D",
3755
+ "1d",
3484
3756
  "1W",
3485
3757
  "1M"
3486
3758
  ],
@@ -3690,7 +3962,7 @@ function useTradingviewScript(props) {
3690
3962
  }
3691
3963
  localStorage.setItem(TradingViewSDKLocalstorageKey.interval, newInterval);
3692
3964
  setInterval(newInterval);
3693
- chart.current?.setSymbol(symbol ?? "", newInterval);
3965
+ chart.current?.setResolution(newInterval);
3694
3966
  };
3695
3967
  const changeLineType = (newLineType) => {
3696
3968
  if (!chart.current) {