@orderly.network/ui-tradingview 3.1.2-alpha.1 → 3.1.3-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
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import React3, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
2
+ import React3, { forwardRef, useRef, useState, useEffect, useCallback, useMemo } from 'react';
3
3
  import { useTranslation, Trans, useLocaleCode, i18n } from '@orderly.network/i18n';
4
4
  import { injectable, useScreen, cn, Flex, Divider, useDocumentDirection, useOrderlyTheme, Text, Switch, Box, useThemeAttribute, toast, modal, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent } from '@orderly.network/ui';
5
5
  import { useConfig, useAccount, useSymbolsInfo, useLocalStorage, useOrderEntry_deprecated, useMarginModeBySymbol, useWS, usePositionStream, useOrderStream, useEventEmitter } from '@orderly.network/hooks';
@@ -1165,11 +1165,13 @@ function getErrorMessage(error) {
1165
1165
  var HISTORY_PATH = "tv/history";
1166
1166
  var KLINE_HISTORY_PATH = "v1/tv/kline_history";
1167
1167
  var _HistoryProvider = class _HistoryProvider {
1168
- constructor(datafeedUrl, requester, limitedServerResponse) {
1168
+ constructor(datafeedUrl, requester, limitedServerResponse, getSymbolCreatedTime) {
1169
1169
  this._klinePreference = /* @__PURE__ */ new Map();
1170
+ this._createTimeBoundaryCutoff = /* @__PURE__ */ new Map();
1170
1171
  this._datafeedUrl = datafeedUrl;
1171
1172
  this._requester = requester;
1172
1173
  this._limitedServerResponse = limitedServerResponse;
1174
+ this._getSymbolCreatedTime = getSymbolCreatedTime;
1173
1175
  }
1174
1176
  /**
1175
1177
  * Build request parameters for history API calls
@@ -1211,18 +1213,32 @@ var _HistoryProvider = class _HistoryProvider {
1211
1213
  try {
1212
1214
  let result;
1213
1215
  let usedHistoryResult = false;
1216
+ if (this._shouldSkipRequestAtCreateTimeBoundary(
1217
+ symbolInfo,
1218
+ resolution,
1219
+ requestParams
1220
+ )) {
1221
+ resolve(this._createNoDataResult());
1222
+ return;
1223
+ }
1214
1224
  if (prefersKline) {
1215
- result = await this._requestKlineHistory(
1216
- this._buildKlineParams(requestParams, countBack)
1225
+ const klineParams = this._buildBoundedKlineParams(
1226
+ symbolInfo,
1227
+ resolution,
1228
+ requestParams,
1229
+ countBack
1217
1230
  );
1231
+ result = klineParams === null ? this._createNoDataResult() : await this._requestKlineHistory(klineParams.params);
1218
1232
  } else {
1219
1233
  const initialResponse = await this._requestHistory(requestParams);
1220
1234
  result = this._processHistoryResponse(initialResponse);
1221
1235
  usedHistoryResult = true;
1222
- const needsFallback = this._shouldFallbackToKline(
1223
- initialResponse,
1224
- countBack
1236
+ const reachedCreateTimeBoundary = this._markCreateTimeBoundaryIfReached(
1237
+ symbolInfo,
1238
+ resolution,
1239
+ result.bars
1225
1240
  );
1241
+ const needsFallback = !reachedCreateTimeBoundary && this._shouldFallbackToKline(initialResponse, countBack);
1226
1242
  if (needsFallback) {
1227
1243
  const earliestTime = this._getEarliestTime(result.bars);
1228
1244
  if (earliestTime !== null) {
@@ -1230,29 +1246,33 @@ var _HistoryProvider = class _HistoryProvider {
1230
1246
  0,
1231
1247
  countBack - result.bars.length
1232
1248
  );
1233
- const mergedResult = await this._tryKlineFallbackWithMerge(
1249
+ const fallbackOutcome = await this._tryKlineFallbackWithMerge(
1250
+ symbolInfo,
1251
+ resolution,
1234
1252
  requestParams,
1235
1253
  remainingCountBack,
1236
1254
  earliestTime,
1237
1255
  result
1238
1256
  );
1239
- if (mergedResult !== null) {
1240
- result = mergedResult;
1257
+ if (fallbackOutcome.status === "success") {
1258
+ result = fallbackOutcome.result;
1241
1259
  usedHistoryResult = false;
1242
1260
  this._klinePreference.set(preferenceKey, true);
1243
- } else {
1261
+ } else if (fallbackOutcome.status === "failed") {
1244
1262
  this._klinePreference.set(preferenceKey, false);
1245
1263
  }
1246
1264
  } else {
1247
- const klineResult = await this._tryKlineFallback(
1265
+ const fallbackOutcome = await this._tryKlineFallback(
1266
+ symbolInfo,
1267
+ resolution,
1248
1268
  requestParams,
1249
1269
  countBack
1250
1270
  );
1251
- if (klineResult !== null) {
1252
- result = klineResult;
1271
+ if (fallbackOutcome.status === "success") {
1272
+ result = fallbackOutcome.result;
1253
1273
  usedHistoryResult = false;
1254
1274
  this._klinePreference.set(preferenceKey, true);
1255
- } else {
1275
+ } else if (fallbackOutcome.status === "failed") {
1256
1276
  this._klinePreference.set(preferenceKey, false);
1257
1277
  }
1258
1278
  }
@@ -1260,6 +1280,11 @@ var _HistoryProvider = class _HistoryProvider {
1260
1280
  this._klinePreference.set(preferenceKey, false);
1261
1281
  }
1262
1282
  }
1283
+ this._markCreateTimeBoundaryIfReached(
1284
+ symbolInfo,
1285
+ resolution,
1286
+ result.bars
1287
+ );
1263
1288
  if (usedHistoryResult && this._limitedServerResponse) {
1264
1289
  await this._processTruncatedResponse(result, { ...requestParams });
1265
1290
  }
@@ -1469,6 +1494,13 @@ var _HistoryProvider = class _HistoryProvider {
1469
1494
  }
1470
1495
  return params;
1471
1496
  }
1497
+ _buildBoundedKlineParams(symbolInfo, resolution, requestParams, countBack) {
1498
+ return this._applyCreateTimeBoundaryToKlineParams(
1499
+ symbolInfo,
1500
+ resolution,
1501
+ this._buildKlineParams(requestParams, countBack)
1502
+ );
1503
+ }
1472
1504
  /**
1473
1505
  * Get the earliest time from bars array
1474
1506
  * @param bars - Array of bars
@@ -1490,22 +1522,38 @@ var _HistoryProvider = class _HistoryProvider {
1490
1522
  * @param historyResult - History result to merge with
1491
1523
  * @returns Merged result or null if kline request fails
1492
1524
  */
1493
- async _tryKlineFallbackWithMerge(requestParams, countBack, earliestTime, historyResult) {
1525
+ async _tryKlineFallbackWithMerge(symbolInfo, resolution, requestParams, countBack, earliestTime, historyResult) {
1494
1526
  try {
1527
+ if (this._hasReachedCreateTimeBoundary(symbolInfo, resolution, earliestTime)) {
1528
+ this._markCreateTimeBoundary(symbolInfo, resolution, earliestTime);
1529
+ return { status: "boundary" };
1530
+ }
1495
1531
  const klineParams = {
1496
1532
  ...requestParams,
1497
1533
  from: requestParams.from,
1498
1534
  to: earliestTime
1499
1535
  };
1536
+ const boundedKlineParams = this._buildBoundedKlineParams(
1537
+ symbolInfo,
1538
+ resolution,
1539
+ klineParams,
1540
+ countBack
1541
+ );
1542
+ if (boundedKlineParams === null) {
1543
+ return { status: "boundary" };
1544
+ }
1500
1545
  const klineResult = await this._requestKlineHistory(
1501
- this._buildKlineParams(klineParams, countBack)
1546
+ boundedKlineParams.params
1502
1547
  );
1503
1548
  if (klineResult.bars.length === 0) {
1504
- return null;
1549
+ return { status: "failed" };
1505
1550
  }
1506
- return this._mergeBars(historyResult, klineResult);
1551
+ return {
1552
+ status: "success",
1553
+ result: this._mergeBars(historyResult, klineResult)
1554
+ };
1507
1555
  } catch {
1508
- return null;
1556
+ return { status: "failed" };
1509
1557
  }
1510
1558
  }
1511
1559
  /**
@@ -1569,14 +1617,27 @@ var _HistoryProvider = class _HistoryProvider {
1569
1617
  meta
1570
1618
  };
1571
1619
  }
1572
- async _tryKlineFallback(requestParams, countBack) {
1620
+ async _tryKlineFallback(symbolInfo, resolution, requestParams, countBack) {
1573
1621
  try {
1574
- const result = await this._requestKlineHistory(
1575
- this._buildKlineParams(requestParams, countBack)
1622
+ const klineParams = this._buildBoundedKlineParams(
1623
+ symbolInfo,
1624
+ resolution,
1625
+ requestParams,
1626
+ countBack
1576
1627
  );
1577
- return result.bars.length > 0 ? result : null;
1628
+ if (klineParams === null) {
1629
+ return { status: "boundary" };
1630
+ }
1631
+ const result = await this._requestKlineHistory(klineParams.params);
1632
+ if (result.bars.length === 0) {
1633
+ return { status: "failed" };
1634
+ }
1635
+ return {
1636
+ status: "success",
1637
+ result
1638
+ };
1578
1639
  } catch {
1579
- return null;
1640
+ return { status: "failed" };
1580
1641
  }
1581
1642
  }
1582
1643
  _shouldFallbackToKline(response, expectedCount) {
@@ -1592,6 +1653,119 @@ var _HistoryProvider = class _HistoryProvider {
1592
1653
  }
1593
1654
  return false;
1594
1655
  }
1656
+ _applyCreateTimeBoundaryToKlineParams(symbolInfo, resolution, params) {
1657
+ const createdTimeSeconds = this._getSymbolCreatedTimeSeconds(symbolInfo);
1658
+ if (createdTimeSeconds === null) {
1659
+ return {
1660
+ params,
1661
+ clampedToCreateTime: false
1662
+ };
1663
+ }
1664
+ const to = this._getNumericParam(params, "to");
1665
+ if (to !== null && to <= createdTimeSeconds) {
1666
+ this._markCreateTimeBoundary(symbolInfo, resolution, createdTimeSeconds);
1667
+ return null;
1668
+ }
1669
+ const from = this._getNumericParam(params, "from");
1670
+ if (from !== null && from < createdTimeSeconds) {
1671
+ return {
1672
+ params: {
1673
+ ...params,
1674
+ from: createdTimeSeconds
1675
+ },
1676
+ clampedToCreateTime: true
1677
+ };
1678
+ }
1679
+ return {
1680
+ params,
1681
+ clampedToCreateTime: false
1682
+ };
1683
+ }
1684
+ _shouldSkipRequestAtCreateTimeBoundary(symbolInfo, resolution, params) {
1685
+ const createdTimeSeconds = this._getSymbolCreatedTimeSeconds(symbolInfo);
1686
+ if (createdTimeSeconds === null) {
1687
+ return false;
1688
+ }
1689
+ const to = this._getNumericParam(params, "to");
1690
+ if (to === null) {
1691
+ return false;
1692
+ }
1693
+ if (to <= createdTimeSeconds) {
1694
+ return true;
1695
+ }
1696
+ const cutoff = this._createTimeBoundaryCutoff.get(
1697
+ this._getPreferenceKey(symbolInfo, resolution)
1698
+ );
1699
+ return cutoff !== void 0 && to <= cutoff;
1700
+ }
1701
+ _markCreateTimeBoundaryIfReached(symbolInfo, resolution, bars) {
1702
+ const earliestTime = this._getEarliestTime(bars);
1703
+ if (earliestTime !== null && this._hasReachedCreateTimeBoundary(symbolInfo, resolution, earliestTime)) {
1704
+ this._markCreateTimeBoundary(symbolInfo, resolution, earliestTime);
1705
+ return true;
1706
+ }
1707
+ return false;
1708
+ }
1709
+ _markCreateTimeBoundary(symbolInfo, resolution, cutoffSeconds) {
1710
+ const key = this._getPreferenceKey(symbolInfo, resolution);
1711
+ const currentCutoff = this._createTimeBoundaryCutoff.get(key);
1712
+ if (currentCutoff === void 0 || cutoffSeconds > currentCutoff) {
1713
+ this._createTimeBoundaryCutoff.set(key, cutoffSeconds);
1714
+ }
1715
+ }
1716
+ _hasReachedCreateTimeBoundary(symbolInfo, resolution, earliestTimeSeconds) {
1717
+ const createdTimeSeconds = this._getSymbolCreatedTimeSeconds(symbolInfo);
1718
+ if (createdTimeSeconds === null) {
1719
+ return false;
1720
+ }
1721
+ return earliestTimeSeconds <= createdTimeSeconds + this._resolutionToSeconds(resolution);
1722
+ }
1723
+ _getSymbolCreatedTimeSeconds(symbolInfo) {
1724
+ const symbolInfoWithCreateTime = symbolInfo;
1725
+ const createdTime = symbolInfoWithCreateTime.created_time ?? this._getSymbolCreatedTime?.(symbolInfo);
1726
+ if (typeof createdTime !== "number" || !Number.isFinite(createdTime)) {
1727
+ return null;
1728
+ }
1729
+ return Math.floor(createdTime / 1e3);
1730
+ }
1731
+ _getNumericParam(params, key) {
1732
+ const value = params[key];
1733
+ if (Array.isArray(value)) {
1734
+ return null;
1735
+ }
1736
+ const numberValue = Number(value);
1737
+ return Number.isFinite(numberValue) ? numberValue : null;
1738
+ }
1739
+ _resolutionToSeconds(resolution) {
1740
+ const match = resolution.match(/^(\d*)([DWMY])?$/);
1741
+ if (!match) {
1742
+ return 60;
1743
+ }
1744
+ const amount = match[1] ? parseInt(match[1], 10) : 1;
1745
+ if (!Number.isFinite(amount) || amount <= 0) {
1746
+ return 60;
1747
+ }
1748
+ switch (match[2]) {
1749
+ case "D":
1750
+ return amount * 86400;
1751
+ case "W":
1752
+ return amount * 7 * 86400;
1753
+ case "M":
1754
+ return amount * 30 * 86400;
1755
+ case "Y":
1756
+ return amount * 365 * 86400;
1757
+ default:
1758
+ return amount * 60;
1759
+ }
1760
+ }
1761
+ _createNoDataResult() {
1762
+ return {
1763
+ bars: [],
1764
+ meta: {
1765
+ noData: true
1766
+ }
1767
+ };
1768
+ }
1595
1769
  };
1596
1770
  /**
1597
1771
  * Static mapping table for resolution conversion
@@ -1890,13 +2064,18 @@ function definedValueOrDefault(value, defaultValue) {
1890
2064
 
1891
2065
  // src/tradingviewAdapter/datafeed/abstract-datafeed.ts
1892
2066
  var AbstractDatafeed = class {
1893
- constructor(datafeedURL) {
2067
+ constructor(datafeedURL, options) {
1894
2068
  this._configuration = defaultConfiguration();
1895
2069
  this._symbolsStorage = null;
1896
2070
  this._historyCursor = null;
1897
2071
  this._datafeedURL = datafeedURL;
1898
2072
  this._requester = new Requester();
1899
- this._historyProvider = new HistoryProvider(datafeedURL, this._requester);
2073
+ this._historyProvider = new HistoryProvider(
2074
+ datafeedURL,
2075
+ this._requester,
2076
+ void 0,
2077
+ options?.getSymbolCreatedTime
2078
+ );
1900
2079
  this._configurationReadyPromise = this._requestConfiguration().then(
1901
2080
  (configuration) => {
1902
2081
  if (configuration === null) {
@@ -2270,9 +2449,9 @@ var getAutoIncrementId = /* @__PURE__ */ (() => {
2270
2449
  return () => id++;
2271
2450
  })();
2272
2451
  var Datafeed = class extends AbstractDatafeed {
2273
- constructor(apiUrl, ws) {
2452
+ constructor(apiUrl, ws, options) {
2274
2453
  const datafeedURL = `${apiUrl}`;
2275
- super(datafeedURL);
2454
+ super(datafeedURL, options);
2276
2455
  this.bbosMap = /* @__PURE__ */ new Map();
2277
2456
  this.tickersMap = /* @__PURE__ */ new Map();
2278
2457
  this.eventBus = new MultiBroadcastEventBus();
@@ -4442,14 +4621,40 @@ function useTradingviewScript(props) {
4442
4621
  const theme = props.theme ?? currentTheme?.mode ?? "dark";
4443
4622
  const cssVariables = useCssVariables(theme);
4444
4623
  const chart = useRef(null);
4624
+ const [readyWidget, setReadyWidget] = useState(
4625
+ null
4626
+ );
4445
4627
  const apiBaseUrl = useConfig("apiBaseUrl");
4446
4628
  const { state: accountState } = useAccount();
4447
4629
  const [side, setSide] = useState(OrderSide.SELL);
4448
4630
  const symbolsInfo = useSymbolsInfo();
4631
+ const symbolsInfoRef = useRef(symbolsInfo);
4449
4632
  const [fullscreen, setFullscreen] = useLocalStorage(
4450
4633
  TradingviewFullscreenKey,
4451
4634
  false
4452
4635
  );
4636
+ useEffect(() => {
4637
+ symbolsInfoRef.current = symbolsInfo;
4638
+ }, [symbolsInfo]);
4639
+ const getSymbolCreatedTime = useCallback(
4640
+ (symbolInfo) => {
4641
+ const candidates = [
4642
+ symbolInfo.ticker,
4643
+ symbolInfo.name,
4644
+ ...symbolInfo.base_name ?? []
4645
+ ].filter(
4646
+ (symbolName) => typeof symbolName === "string" && symbolName.length > 0
4647
+ ).map((symbolName) => withoutExchangePrefix(symbolName));
4648
+ for (const candidate of candidates) {
4649
+ const createdTime = symbolsInfoRef.current[candidate]?.("created_time");
4650
+ if (typeof createdTime === "number") {
4651
+ return createdTime;
4652
+ }
4653
+ }
4654
+ return void 0;
4655
+ },
4656
+ []
4657
+ );
4453
4658
  const { onSubmit, submitting } = useOrderEntry_deprecated(
4454
4659
  {
4455
4660
  symbol: symbol ?? "",
@@ -4518,7 +4723,8 @@ function useTradingviewScript(props) {
4518
4723
  symbol,
4519
4724
  order_type: OrderType.MARKET,
4520
4725
  side: side2,
4521
- reduce_only: true
4726
+ reduce_only: true,
4727
+ margin_mode: data.marginMode ?? data.margin_mode ?? marginMode ?? MarginMode.CROSS
4522
4728
  };
4523
4729
  setSide(side2);
4524
4730
  modal.show("MarketCloseConfirmID", {
@@ -4680,7 +4886,7 @@ function useTradingviewScript(props) {
4680
4886
  toolbarBg,
4681
4887
  overrides,
4682
4888
  studiesOverrides,
4683
- datafeed: new Datafeed(apiBaseUrl, ws),
4889
+ datafeed: new Datafeed(apiBaseUrl, ws, { getSymbolCreatedTime }),
4684
4890
  contextMenu: {
4685
4891
  items_processor: async (defaultItems) => {
4686
4892
  return defaultItems;
@@ -4706,6 +4912,7 @@ function useTradingviewScript(props) {
4706
4912
  }
4707
4913
  return () => {
4708
4914
  chart.current?.remove();
4915
+ setReadyWidget(null);
4709
4916
  };
4710
4917
  }, [
4711
4918
  isMobile,
@@ -4723,7 +4930,8 @@ function useTradingviewScript(props) {
4723
4930
  loadingScreen,
4724
4931
  toolbarBg,
4725
4932
  customIndicatorsGetter,
4726
- direction
4933
+ direction,
4934
+ getSymbolCreatedTime
4727
4935
  ]);
4728
4936
  useEffect(() => {
4729
4937
  ws.on(
@@ -4744,6 +4952,7 @@ function useTradingviewScript(props) {
4744
4952
  useEffect(() => {
4745
4953
  if (chart.current && chart.current?.instance) {
4746
4954
  chart.current?.instance?.onChartReady(() => {
4955
+ setReadyWidget(chart.current?.instance ?? null);
4747
4956
  if (isLoggedIn && chart.current?.instance) {
4748
4957
  createRenderer(
4749
4958
  chart.current.instance,
@@ -4784,7 +4993,8 @@ function useTradingviewScript(props) {
4784
4993
  onFullScreenChange,
4785
4994
  classNames,
4786
4995
  direction,
4787
- fullscreen
4996
+ fullscreen,
4997
+ readyWidget
4788
4998
  };
4789
4999
  }
4790
5000
 
@@ -4858,6 +5068,10 @@ var InjectableTradingviewDesktop = injectable(
4858
5068
  TradingviewChart,
4859
5069
  "TradingView.Desktop"
4860
5070
  );
5071
+ var InjectableChartOverlay = injectable(
5072
+ () => null,
5073
+ "Trading.Chart.Overlay"
5074
+ );
4861
5075
  var LazyLineType = React3.lazy(() => Promise.resolve().then(() => (init_lineType(), lineType_exports)));
4862
5076
  var LazyTimeInterval = React3.lazy(
4863
5077
  () => Promise.resolve().then(() => (init_timeInterval(), timeInterval_exports)).then((mod) => ({ default: mod.TimeInterval }))
@@ -5060,7 +5274,16 @@ var TradingviewUI = forwardRef((props, ref) => {
5060
5274
  onFullScreenChange
5061
5275
  }
5062
5276
  ) }),
5063
- /* @__PURE__ */ jsx(InjectableTradingviewDesktop, { ...props })
5277
+ /* @__PURE__ */ jsxs("div", { className: "oui-relative oui-min-h-0 oui-flex-1", children: [
5278
+ /* @__PURE__ */ jsx(InjectableTradingviewDesktop, { ...props }),
5279
+ props.readyWidget && props.symbol && /* @__PURE__ */ jsx("div", { className: "oui-pointer-events-none oui-absolute oui-inset-0 oui-z-[2]", children: /* @__PURE__ */ jsx(
5280
+ InjectableChartOverlay,
5281
+ {
5282
+ widget: props.readyWidget,
5283
+ symbol: props.symbol
5284
+ }
5285
+ ) })
5286
+ ] })
5064
5287
  ]
5065
5288
  }
5066
5289
  )
@@ -5075,6 +5298,6 @@ var TradingviewWidget = forwardRef((props, ref) => {
5075
5298
  // src/index.ts
5076
5299
  init_displayControl();
5077
5300
 
5078
- export { DesktopDisplayControl, DesktopDisplayControlMenuListTarget, InjectableTradingviewDesktop, MobileDisplayControl, TradingviewUI, TradingviewWidget, useTradingviewScript };
5301
+ export { DesktopDisplayControl, DesktopDisplayControlMenuListTarget, InjectableChartOverlay, InjectableTradingviewDesktop, MobileDisplayControl, TradingviewUI, TradingviewWidget, useTradingviewScript };
5079
5302
  //# sourceMappingURL=index.mjs.map
5080
5303
  //# sourceMappingURL=index.mjs.map