@orderly.network/ui-tradingview 2.10.1 → 2.10.2

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,10 +1,10 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React3, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react';
3
3
  import { useTranslation, Trans, useLocaleCode, i18n } from '@orderly.network/i18n';
4
- import { cn, Flex, Divider, Box, toast, modal, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent, Text, Switch } from '@orderly.network/ui';
4
+ import { cn, Flex, Divider, Box, useScreen, useOrderlyTheme, useThemeAttribute, toast, modal, DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuPortal, DropdownMenuContent, Text, Switch } from '@orderly.network/ui';
5
5
  import { useMediaQuery, useConfig, useAccount, useSymbolsInfo, useLocalStorage, useOrderEntry_deprecated, useWS, usePositionStream, useOrderStream, useEventEmitter } from '@orderly.network/hooks';
6
- import { MEDIA_TABLET, OrderSide, TradingviewFullscreenKey, OrderType, AccountStatusEnum, OrderStatus } from '@orderly.network/types';
7
- import { Decimal, commify, getTrailingStopPrice } from '@orderly.network/utils';
6
+ import { MEDIA_TABLET, OrderSide, TradingviewFullscreenKey, OrderType, AccountStatusEnum, OrderStatus, ORDER_ENTRY_EST_LIQ_PRICE_CHANGE } from '@orderly.network/types';
7
+ import { Decimal, commify, getTrailingStopPrice, getPrecisionByNumber } from '@orderly.network/utils';
8
8
  import { startOfSecond, startOfMinute, startOfHour, startOfDay, startOfMonth, startOfYear, startOfWeek } from 'date-fns';
9
9
 
10
10
  var __defProp = Object.defineProperty;
@@ -138,8 +138,7 @@ var init_icons = __esm({
138
138
  fillRule: "evenodd",
139
139
  clipRule: "evenodd",
140
140
  d: "M5.5 14.5V5.5H7.5V14.5H5.5Z",
141
- fill: "white",
142
- fillOpacity: "0.12"
141
+ className: "oui-fill-base-contrast-12"
143
142
  }
144
143
  ),
145
144
  /* @__PURE__ */ jsx(
@@ -196,8 +195,7 @@ var init_icons = __esm({
196
195
  fillRule: "evenodd",
197
196
  clipRule: "evenodd",
198
197
  d: "M2.7608 17.7562C2.84602 17.899 3.00106 18 3.19016 18H16.362C17.4665 18 18.362 17.1046 18.362 16V3.94663C18.362 3.86618 18.3453 3.79397 18.3162 3.73108L13.495 12.4968C13.0776 13.2557 12.1061 13.5044 11.3754 13.0394L7.19881 10.3816L2.9774 17.558C2.92465 17.6477 2.84826 17.7148 2.7608 17.7562Z",
199
- fill: "white",
200
- fillOpacity: "0.12"
198
+ className: "oui-fill-base-contrast-12"
201
199
  }
202
200
  )
203
201
  ] })
@@ -233,8 +231,7 @@ var init_icons = __esm({
233
231
  {
234
232
  id: "Vector",
235
233
  d: "M6.00684 0.999023C3.24544 0.999023 1.00684 3.23752 1.00684 5.99902C1.00684 8.76051 3.24544 10.999 6.00684 10.999C8.76834 10.999 11.0068 8.76051 11.0068 5.99902C11.0068 3.23752 8.76834 0.999023 6.00684 0.999023ZM6.00684 1.99902C8.21584 1.99902 10.0068 3.79002 10.0068 5.99902C10.0068 8.20801 8.21584 9.99901 6.00684 9.99901C3.79769 9.99901 2.00684 8.20801 2.00684 5.99902C2.00684 3.79002 3.79769 1.99902 6.00684 1.99902Z",
236
- fill: "white",
237
- fillOpacity: "0.2"
234
+ className: "oui-fill-base-contrast-20"
238
235
  }
239
236
  ) })
240
237
  }
@@ -496,7 +493,7 @@ var init_timeInterval = __esm({
496
493
  "oui-cursor-pointer oui-px-2",
497
494
  "hover:oui-text-base-contrast-80",
498
495
  "oui-break-normal oui-whitespace-nowrap",
499
- props.interval === item.value && "oui-text-base-contrast-80 oui-bg-white/[.06] oui-rounded"
496
+ props.interval === item.value && "oui-text-base-contrast-80 oui-bg-base-contrast-6 oui-rounded"
500
497
  ),
501
498
  id: item.value,
502
499
  onClick: () => props.changeInterval(item.value),
@@ -565,7 +562,7 @@ var init_timeInterval = __esm({
565
562
  className: cn(
566
563
  "oui-px-2",
567
564
  "oui-break-normal oui-whitespace-nowrap",
568
- props.interval === item.value && "oui-text-base-contrast-80 oui-bg-white/[.06] oui-rounded"
565
+ props.interval === item.value && "oui-text-base-contrast-80 oui-bg-base-contrast-6 oui-rounded"
569
566
  ),
570
567
  onClick: () => props.changeInterval(item.value),
571
568
  children: item.label
@@ -888,38 +885,104 @@ var init_displayControl = __esm({
888
885
  init_displayControl_mobile();
889
886
  }
890
887
  });
888
+ var cssVar2Hex = (color) => {
889
+ const [r, g, b] = color.trim().split(/\s+/).map(Number);
890
+ const toHex = (n) => Math.min(255, Math.max(0, n)).toString(16).padStart(2, "0");
891
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
892
+ };
893
+ var useCssVariables = (theme) => {
894
+ const [cssVariables, setCssVariables] = useState({});
895
+ const themeAttribute = useThemeAttribute();
896
+ useEffect(() => {
897
+ const rootStyle = getComputedStyle(document.documentElement);
898
+ const base9 = rootStyle.getPropertyValue("--oui-color-base-9").trim();
899
+ const primaryVar = rootStyle.getPropertyValue("--oui-color-primary").trim();
900
+ const warningLightVar = rootStyle.getPropertyValue("--oui-color-warning-light").trim();
901
+ setCssVariables({
902
+ chartBG: cssVar2Hex(base9),
903
+ primary: cssVar2Hex(primaryVar),
904
+ /** For liquidation line; same as Position list Liq. Price (--oui-color-warning-light). */
905
+ warningLight: warningLightVar ? cssVar2Hex(warningLightVar) : ""
906
+ // upColor: cssVar2Hex(profitColor),
907
+ // downColor: cssVar2Hex(lossColor),
908
+ });
909
+ }, [theme, themeAttribute]);
910
+ return cssVariables;
911
+ };
891
912
 
892
913
  // src/utils/chart.util.ts
893
- var upColor = "#008676";
894
- var downColor = "#D92D6B";
895
- var chartBG = "#131519";
896
- var pnlUpColor = "#00B49E";
897
- var pnlDownColor = "#FF447C";
898
- var pnlZoreColor = "#333948";
899
- var textColor = "#FFFFFF";
900
- var qtyTextColor = "#F4F7F9";
901
914
  var font = "regular 11px Manrope";
902
915
  var defaultColorConfig = {
903
- upColor,
904
- downColor,
905
- chartBG,
906
- pnlUpColor,
907
- pnlDownColor,
908
- pnlZoreColor,
909
- textColor,
910
- qtyTextColor,
911
- font,
912
- volumeUpColor: "#0C3E3A",
913
- volumeDownColor: "#5A1E36",
914
- closeIcon: "rgba(255, 255, 255, 0.8)"
916
+ dark: {
917
+ chartBG: "#131519",
918
+ upColor: "#008676",
919
+ downColor: "#D92D6B",
920
+ pnlUpColor: "#00B49E",
921
+ pnlDownColor: "#FF447C",
922
+ pnlZoreColor: "#333948",
923
+ textColor: "#FFFFFF",
924
+ qtyTextColor: "#F4F7F9",
925
+ font,
926
+ volumeUpColor: "#0C3E3A",
927
+ volumeDownColor: "#5A1E36",
928
+ closeIcon: "rgba(255, 255, 255, 0.8)"
929
+ },
930
+ light: {
931
+ chartBG: "#ffffff",
932
+ upColor: "#2ebd85",
933
+ downColor: "#F6465D",
934
+ pnlUpColor: "#2ebd85",
935
+ pnlDownColor: "#F6465D",
936
+ pnlZoreColor: "#333948",
937
+ textColor: "#000000",
938
+ qtyTextColor: "#000000",
939
+ font,
940
+ volumeUpColor: "#2ebd85",
941
+ volumeDownColor: "#F6465D",
942
+ closeIcon: "rgba(0, 0, 0, 0.8)"
943
+ }
915
944
  };
916
- var getOveriides = (colorConfig, isMobile) => {
945
+ var getColorConfig = ({
946
+ theme,
947
+ cssVariables,
948
+ customerColorConfig
949
+ }) => {
950
+ const defaultCconfig = defaultColorConfig[theme] || defaultColorConfig.dark;
951
+ const chartBG = customerColorConfig?.chartBG || cssVariables.chartBG || defaultCconfig.chartBG;
952
+ const liqLineColor = customerColorConfig?.liqLineColor ?? cssVariables.warningLight;
953
+ return {
954
+ ...defaultCconfig,
955
+ ...customerColorConfig,
956
+ chartBG,
957
+ liqLineColor
958
+ // upColor,
959
+ // downColor,
960
+ };
961
+ };
962
+ var defaultOverrides = {
963
+ dark: {
964
+ "paneProperties.separatorColor": "#2B2833",
965
+ "paneProperties.vertGridProperties.color": "#26232F",
966
+ "paneProperties.horzGridProperties.color": "#26232F",
967
+ "scalesProperties.textColor": "#97969B"
968
+ },
969
+ light: {
970
+ // "paneProperties.separatorColor": "#E5E5E5",
971
+ // "paneProperties.vertGridProperties.color": "#E5E5E5",
972
+ // "paneProperties.horzGridProperties.color": "#E5E5E5",
973
+ // "scalesProperties.textColor": "#97969B",
974
+ "scalesProperties.crosshairLabelBgColorLight": "#E5E5E5"
975
+ }
976
+ };
977
+ var getOveriides = ({
978
+ theme,
979
+ colorConfig,
980
+ isMobile
981
+ }) => {
917
982
  const overrides = {
918
983
  "paneProperties.background": colorConfig.chartBG,
919
- // "paneProperties.background": "#ffff00",
920
984
  // "mainSeriesProperties.style": 1,
921
985
  "paneProperties.backgroundType": "solid",
922
- // "paneProperties.background": "#151822",
923
986
  "mainSeriesProperties.candleStyle.upColor": colorConfig.upColor,
924
987
  "mainSeriesProperties.candleStyle.downColor": colorConfig.downColor,
925
988
  "mainSeriesProperties.candleStyle.borderColor": colorConfig.upColor,
@@ -927,13 +990,10 @@ var getOveriides = (colorConfig, isMobile) => {
927
990
  "mainSeriesProperties.candleStyle.borderDownColor": colorConfig.downColor,
928
991
  "mainSeriesProperties.candleStyle.wickUpColor": colorConfig.upColor,
929
992
  "mainSeriesProperties.candleStyle.wickDownColor": colorConfig.downColor,
930
- "paneProperties.separatorColor": "#2B2833",
931
- "paneProperties.vertGridProperties.color": "#26232F",
932
- "paneProperties.horzGridProperties.color": "#26232F",
933
993
  "scalesProperties.fontSize": isMobile ? 8 : 12,
934
- "scalesProperties.textColor": "#97969B",
935
994
  "paneProperties.legendProperties.showSeriesTitle": isMobile ? false : true,
936
- "mainSeriesProperties.statusViewStyle.symbolTextSource": "ticker"
995
+ "mainSeriesProperties.statusViewStyle.symbolTextSource": "ticker",
996
+ ...defaultOverrides[theme]
937
997
  };
938
998
  const studiesOverrides = {
939
999
  "volume.volume.color.0": colorConfig.volumeDownColor,
@@ -947,6 +1007,26 @@ var getOveriides = (colorConfig, isMobile) => {
947
1007
  var EXCHANGE = "Orderly";
948
1008
  var withoutExchangePrefix = (symbol) => symbol.includes(":") ? symbol.split(":")[1] : symbol;
949
1009
  var withExchangePrefix = (symbol) => symbol.startsWith(`${EXCHANGE}:`) ? symbol : `${EXCHANGE}:${symbol}`;
1010
+ function stringifyWithSortedKeys(obj) {
1011
+ const keys = Object.keys(obj).sort();
1012
+ const sorted = {};
1013
+ for (const k of keys) {
1014
+ sorted[k] = obj[k];
1015
+ }
1016
+ return JSON.stringify(sorted);
1017
+ }
1018
+ function simpleHash(str) {
1019
+ let h = 5381;
1020
+ for (let i = 0; i < str.length; i++) {
1021
+ h = (h << 5) + h + str.charCodeAt(i);
1022
+ h = h >>> 0;
1023
+ }
1024
+ return Math.abs(h).toString(36);
1025
+ }
1026
+ function getOverridesConfigHash(theme, overrides, studiesOverrides) {
1027
+ const payload = `${theme}${stringifyWithSortedKeys(overrides)}${stringifyWithSortedKeys(studiesOverrides)}`;
1028
+ return simpleHash(payload);
1029
+ }
950
1030
  var getBrokerAdapter = (host, broker) => {
951
1031
  let _symbolInfo;
952
1032
  const getOrderCombinationType = (orderType) => {
@@ -2268,6 +2348,8 @@ var EST_TPSL_PNL_DECIMAL = 2;
2268
2348
  var textDash = "--";
2269
2349
  var BracketAlgoType = ["BRACKET" /* BRACKET */, "STOP_BRACKET" /* STOP_BRACKET */];
2270
2350
  var TpslAlgoType = ["POSITIONAL_TP_SL" /* POSITIONAL_TP_SL */, "TP_SL" /* TP_SL */];
2351
+ var isTpOrder = (order) => order.root_algo_order_id !== order.algo_order_id && order.algo_type === "TAKE_PROFIT" /* TAKE_PROFIT */;
2352
+ var isSlOrder = (order) => order.root_algo_order_id !== order.algo_order_id && order.algo_type === "STOP_LOSS" /* STOP_LOSS */;
2271
2353
  var isTpslOrder = (order) => order.root_algo_order_id !== order.algo_order_id && (order.algo_type === "TAKE_PROFIT" /* TAKE_PROFIT */ || order.algo_type === "STOP_LOSS" /* STOP_LOSS */);
2272
2354
  var isBracketAlgoType = (algoType) => !!algoType && BracketAlgoType.includes(algoType);
2273
2355
  var isPositionTpsl = (order) => order.type === "CLOSE_POSITION" /* CLOSE_POSITION */;
@@ -2458,6 +2540,8 @@ function useSendOrder(symbol) {
2458
2540
  sendMarketOrder
2459
2541
  };
2460
2542
  }
2543
+
2544
+ // src/tradingviewAdapter/hooks/useBroker.ts
2461
2545
  var createBrokerMethod = (method) => {
2462
2546
  return (params) => method(params);
2463
2547
  };
@@ -2513,6 +2597,9 @@ var useBroker = ({
2513
2597
  useEffect(() => {
2514
2598
  broker.current.cancelOrder = cancelOrder;
2515
2599
  }, [cancelOrder]);
2600
+ useEffect(() => {
2601
+ broker.current.colorConfig = colorConfig;
2602
+ }, [colorConfig]);
2516
2603
  return broker.current;
2517
2604
  };
2518
2605
  var useBroker_default = useBroker;
@@ -2651,8 +2738,91 @@ var ExecutionService = class _ExecutionService {
2651
2738
  this.unsubscribeIntervalChange();
2652
2739
  }
2653
2740
  };
2741
+ var LiquidationLineService = class {
2742
+ constructor(instance, broker) {
2743
+ this.line = null;
2744
+ /** Tracks whether the current line source is estimated liq. price (vs position liq. price). */
2745
+ this.isUsingEstimatedPrice = false;
2746
+ this.instance = instance;
2747
+ this.broker = broker;
2748
+ }
2749
+ /**
2750
+ * Render or update the liquidation line. Call when position or estimated liq. price changes.
2751
+ */
2752
+ renderLiquidationLine(params) {
2753
+ const { positionLiqPrice, estimatedLiqPrice } = params;
2754
+ const hasValidEst = estimatedLiqPrice != null && Number.isFinite(estimatedLiqPrice);
2755
+ const hasValidPosition = positionLiqPrice != null && Number.isFinite(positionLiqPrice);
2756
+ if (hasValidEst) {
2757
+ this.isUsingEstimatedPrice = true;
2758
+ this.setLinePrice(estimatedLiqPrice);
2759
+ this.line?.setLineStyle(1);
2760
+ return;
2761
+ }
2762
+ if (hasValidPosition) {
2763
+ this.isUsingEstimatedPrice = false;
2764
+ this.setLinePrice(positionLiqPrice);
2765
+ this.line?.setLineStyle(0);
2766
+ return;
2767
+ }
2768
+ this.removeLine();
2769
+ }
2770
+ /** Remove the line (e.g. on destroy). */
2771
+ remove() {
2772
+ this.removeLine();
2773
+ }
2774
+ getOrCreateLine() {
2775
+ if (this.line != null) {
2776
+ return this.line;
2777
+ }
2778
+ try {
2779
+ const activeChart = this.instance.activeChart();
2780
+ if (!activeChart) return null;
2781
+ const orderLine = activeChart.createOrderLine();
2782
+ if (!orderLine) return null;
2783
+ const colorConfig = this.broker.colorConfig;
2784
+ const lineColor = colorConfig.liqLineColor ?? colorConfig.textColor;
2785
+ if (!lineColor) return null;
2786
+ this.line = orderLine.setCancellable(false).setLineLength(100).setEditable(false).setExtendLeft(true).setQuantity("").setLineStyle(1).setLineColor(lineColor).setBodyBorderColor(lineColor);
2787
+ this.updateLineLabel();
2788
+ if (colorConfig.chartBG) {
2789
+ this.line = this.line.setBodyBackgroundColor(colorConfig.chartBG).setQuantityBackgroundColor(colorConfig.chartBG);
2790
+ }
2791
+ if (colorConfig.textColor) {
2792
+ this.line = this.line.setBodyTextColor(colorConfig.textColor);
2793
+ }
2794
+ if (colorConfig.font) {
2795
+ this.line = this.line.setBodyFont(colorConfig.font).setQuantityFont(colorConfig.font);
2796
+ }
2797
+ return this.line;
2798
+ } catch {
2799
+ return null;
2800
+ }
2801
+ }
2802
+ setLinePrice(price) {
2803
+ const line = this.getOrCreateLine();
2804
+ if (line) {
2805
+ line.setPrice(price);
2806
+ this.updateLineLabel();
2807
+ }
2808
+ }
2809
+ removeLine() {
2810
+ if (this.line) {
2811
+ this.line.remove();
2812
+ this.line = null;
2813
+ }
2814
+ }
2815
+ /** Ensure tooltip/text reflect whether we are showing estimated or position liq. price. */
2816
+ updateLineLabel() {
2817
+ if (!this.line) return;
2818
+ const key = this.isUsingEstimatedPrice ? "orderEntry.estLiqPrice" : "positions.column.liqPrice";
2819
+ const label = i18n.t(key);
2820
+ this.line.setTooltip(label).setText(label);
2821
+ }
2822
+ };
2654
2823
 
2655
2824
  // src/tradingviewAdapter/renderer/order.util.ts
2825
+ var CHART_QTY_DECIMAL = 4;
2656
2826
  var getOrderId = (order) => {
2657
2827
  if (order === null || order === void 0) {
2658
2828
  return void 0;
@@ -2684,6 +2854,22 @@ var TpslCalService = class {
2684
2854
  const { estPnl } = getTpslEstPnl(tpslOrder, position);
2685
2855
  return formatPnl(estPnl);
2686
2856
  }
2857
+ /**
2858
+ * Returns the quantity for a TP/SL order for label display.
2859
+ * Position TP/SL: |position.balance|; partial TP/SL: |order.quantity - executed|.
2860
+ * @returns quantity number or undefined when positions unavailable
2861
+ */
2862
+ getTpslQuantity(tpslOrder) {
2863
+ if (this.positions === null) {
2864
+ return void 0;
2865
+ }
2866
+ const position = this.positions[0];
2867
+ if (!position) {
2868
+ return void 0;
2869
+ }
2870
+ const { quantity } = getTpslEstPnl(tpslOrder, position);
2871
+ return quantity;
2872
+ }
2687
2873
  prepareTpslPnlMap(newPendingOrders) {
2688
2874
  const changed = [];
2689
2875
  newPendingOrders.forEach((order) => {
@@ -2834,12 +3020,18 @@ var _OrderLineService = class _OrderLineService {
2834
3020
  }
2835
3021
  return pendingOrder.trigger_price || pendingOrder.price;
2836
3022
  }
3023
+ /**
3024
+ * Builds TP/SL label body: type + PnL (e.g. "Take profit | 123.45 USDC").
3025
+ * Falls back to "--" when PnL unavailable.
3026
+ */
2837
3027
  getTPSLTextWithTpsl(text, pendingOrder) {
2838
3028
  const orderId = getOrderId(pendingOrder);
2839
3029
  if (!orderId) {
2840
3030
  return text;
2841
3031
  }
2842
- return text;
3032
+ const pnlStr = this.tpslCalService.getFormattedEstPnl(pendingOrder);
3033
+ const displayPnl = pnlStr ? pnlStr : formatPnl(void 0);
3034
+ return `${text} | ${displayPnl} USDC`;
2843
3035
  }
2844
3036
  getTPSLText(pendingOrder) {
2845
3037
  const tpslTypeText = getTpslTag(
@@ -2851,17 +3043,26 @@ var _OrderLineService = class _OrderLineService {
2851
3043
  }
2852
3044
  return null;
2853
3045
  }
3046
+ /**
3047
+ * Returns quantity string for order line. For TP/SL: "qty (percent%)".
3048
+ * Uses baseDp from broker.getSymbolInfo; falls back to CHART_QTY_DECIMAL.
3049
+ */
2854
3050
  getOrderQuantity(pendingOrder) {
2855
3051
  if (pendingOrder.algo_order_id) {
2856
- if (isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder)) {
2857
- return "100%";
2858
- }
2859
- if (isActivatedQuantityTpsl(pendingOrder)) {
2860
- const qty = new Decimal(pendingOrder.quantity).minus(
2861
- pendingOrder.executed ?? 0
2862
- );
2863
- const per = qty.div(new Decimal(pendingOrder.position_qty)).mul(100).todp(2).toNumber();
2864
- return `${Math.min(Math.abs(per), 100).toString()}%`;
3052
+ const isTpsl = isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder) || isActivatedQuantityTpsl(pendingOrder);
3053
+ if (isTpsl) {
3054
+ const percentStr = isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder) ? "100%" : (() => {
3055
+ const qty2 = new Decimal(pendingOrder.quantity).minus(
3056
+ pendingOrder.executed ?? 0
3057
+ );
3058
+ const per = qty2.div(new Decimal(pendingOrder.position_qty)).mul(100).todp(0).toNumber();
3059
+ return `${Math.min(Math.abs(per), 100).toString()}%`;
3060
+ })();
3061
+ const qty = this.tpslCalService.getTpslQuantity(pendingOrder);
3062
+ const symbolInfo = this.broker.getSymbolInfo?.(pendingOrder.symbol);
3063
+ const baseDp = symbolInfo?.baseTick != null ? getPrecisionByNumber(symbolInfo.baseTick) : CHART_QTY_DECIMAL;
3064
+ const qtyStr = qty != null ? commify(new Decimal(qty).todp(baseDp).toString()) : textDash;
3065
+ return `${qtyStr} (${percentStr})`;
2865
3066
  }
2866
3067
  }
2867
3068
  return commify(new Decimal(pendingOrder.quantity).toString());
@@ -2877,13 +3078,22 @@ var _OrderLineService = class _OrderLineService {
2877
3078
  if (!orderLine) {
2878
3079
  return null;
2879
3080
  }
2880
- const color = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.upColor : colorConfig.downColor;
2881
- pendingOrder.side === "BUY" /* BUY */ ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
3081
+ let color = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.upColor : colorConfig.downColor;
3082
+ let borderColor = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
3083
+ if (isTpslOrder(pendingOrder)) {
3084
+ if (isTpOrder(pendingOrder)) {
3085
+ color = colorConfig.upColor;
3086
+ borderColor = colorConfig.pnlUpColor;
3087
+ } else if (isSlOrder(pendingOrder)) {
3088
+ color = colorConfig.downColor;
3089
+ borderColor = colorConfig.pnlDownColor;
3090
+ }
3091
+ }
2882
3092
  const price = _OrderLineService.getOrderPrice(pendingOrder);
2883
3093
  const lineLength = 100;
2884
3094
  const quantity = this.getOrderQuantity(pendingOrder);
2885
- const textColor2 = colorConfig.textColor;
2886
- orderLine.setText(text).setCancelButtonIconColor(colorConfig.closeIcon).setCancelButtonBorderColor(color).setBodyTextColor(textColor2).setBodyBorderColor(color).setQuantityBorderColor(color).setQuantityTextColor(color).setLineColor(color).setLineLength(lineLength).setQuantity(quantity ?? "").setPrice(price);
3095
+ const textColor = colorConfig.textColor;
3096
+ orderLine.setText(text).setCancelButtonIconColor(colorConfig.closeIcon).setCancelButtonBorderColor(color).setBodyTextColor(textColor).setBodyBorderColor(borderColor).setQuantityBorderColor(borderColor).setQuantityTextColor(color).setLineColor(color).setLineLength(lineLength).setQuantity(quantity ?? "").setPrice(price);
2887
3097
  if (this.broker.mode !== 3 /* MOBILE */) {
2888
3098
  orderLine.onCancel(null, () => this.broker.cancelOrder(pendingOrder));
2889
3099
  this.applyEditOnMove(orderLine, pendingOrder);
@@ -2957,7 +3167,21 @@ var PositionLineService = class _PositionLineService {
2957
3167
  this.lastPositions = positions;
2958
3168
  }
2959
3169
  getBasePositionLine() {
2960
- return this.instance.activeChart().createPositionLine().setTooltip(i18n.t("positions.closePosition")).setQuantityBackgroundColor(this.broker.colorConfig.chartBG).setCloseButtonBackgroundColor(this.broker.colorConfig.chartBG).setBodyTextColor(this.broker.colorConfig.textColor).setQuantityTextColor(this.broker.colorConfig.qtyTextColor).setBodyFont(this.broker.colorConfig.font).setQuantityFont(this.broker.colorConfig.font).setLineLength(100).setLineStyle(1);
3170
+ const colorConfig = this.broker.colorConfig;
3171
+ let line = this.instance.activeChart().createPositionLine().setTooltip(i18n.t("positions.closePosition")).setLineLength(100).setLineStyle(1);
3172
+ if (colorConfig.chartBG) {
3173
+ line = line.setQuantityBackgroundColor(colorConfig.chartBG).setCloseButtonBackgroundColor(colorConfig.chartBG);
3174
+ }
3175
+ if (colorConfig.textColor) {
3176
+ line = line.setBodyTextColor(colorConfig.textColor);
3177
+ }
3178
+ if (colorConfig.qtyTextColor) {
3179
+ line = line.setQuantityTextColor(colorConfig.qtyTextColor);
3180
+ }
3181
+ if (colorConfig.font) {
3182
+ line = line.setBodyFont(colorConfig.font).setQuantityFont(colorConfig.font);
3183
+ }
3184
+ return line;
2961
3185
  }
2962
3186
  static getPositionQuantity(balance) {
2963
3187
  return commify(new Decimal(balance).todp(4, Decimal.ROUND_DOWN).toString());
@@ -2981,25 +3205,35 @@ var PositionLineService = class _PositionLineService {
2981
3205
  }
2982
3206
  drawPositionLine(position, idx) {
2983
3207
  const colorConfig = this.broker.colorConfig;
2984
- const isPositiveUnrealPnl = position.unrealPnl >= 0;
2985
3208
  const isPositiveBalance = position.balance >= 0;
2986
- let pnlColor = colorConfig.pnlZoreColor;
2987
3209
  const pnlDecimal = new Decimal(position.unrealPnl);
3210
+ let pnlColor;
2988
3211
  if (pnlDecimal.greaterThan(0)) {
2989
3212
  pnlColor = colorConfig.upColor;
2990
3213
  } else if (pnlDecimal.lessThan(0)) {
2991
3214
  pnlColor = colorConfig.downColor;
3215
+ } else {
3216
+ pnlColor = colorConfig.pnlZoreColor;
2992
3217
  }
2993
- isPositiveUnrealPnl ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
2994
3218
  const sideColor = isPositiveBalance ? colorConfig.upColor : colorConfig.downColor;
2995
3219
  const price = new Decimal(position.open).toNumber();
2996
3220
  this.positionLines[idx] = this.positionLines[idx] ?? this.getBasePositionLine();
2997
- this.positionLines[idx].setQuantity(_PositionLineService.getPositionQuantity(position.balance)).setPrice(price).setCloseButtonIconColor(colorConfig.closeIcon).setCloseButtonBorderColor(sideColor).setBodyBackgroundColor(pnlColor).setQuantityTextColor(sideColor).setBodyBorderColor(pnlColor).setLineColor(sideColor).setQuantityBorderColor(sideColor).setText(
3221
+ let line = this.positionLines[idx].setQuantity(_PositionLineService.getPositionQuantity(position.balance)).setPrice(price).setText(
2998
3222
  _PositionLineService.getPositionPnL(
2999
3223
  position.unrealPnl,
3000
3224
  position.unrealPnlDecimal
3001
3225
  )
3002
3226
  );
3227
+ if (colorConfig.closeIcon) {
3228
+ line = line.setCloseButtonIconColor(colorConfig.closeIcon);
3229
+ }
3230
+ if (sideColor) {
3231
+ line = line.setCloseButtonBorderColor(sideColor).setQuantityTextColor(sideColor).setLineColor(sideColor).setQuantityBorderColor(sideColor);
3232
+ }
3233
+ if (pnlColor) {
3234
+ line = line.setBodyBackgroundColor(pnlColor).setBodyBorderColor(pnlColor);
3235
+ }
3236
+ this.positionLines[idx] = line;
3003
3237
  if (this.broker.mode !== 3 /* MOBILE */) {
3004
3238
  this.positionLines[idx].onClose(null, () => {
3005
3239
  this.broker.closePosition(position);
@@ -3192,7 +3426,7 @@ var TPSLService = class {
3192
3426
  disableUndo: true,
3193
3427
  zOrder: "top",
3194
3428
  overrides: {
3195
- linecolor: "rgba(255,255,255, 0.2)",
3429
+ linecolor: "rgba(var(--oui-color-base-foreground)/0.2)",
3196
3430
  linewidth: 1,
3197
3431
  rightEnd: 1,
3198
3432
  leftEnd: 1
@@ -3297,6 +3531,7 @@ var Renderer = class {
3297
3531
  this.orderLineService = new OrderLineService(instance, broker);
3298
3532
  this.executionService = new ExecutionService(instance, broker);
3299
3533
  this.tpslService = new TPSLService(instance, broker);
3534
+ this.liquidationLineService = new LiquidationLineService(instance, broker);
3300
3535
  }
3301
3536
  async renderPositions(positions) {
3302
3537
  await this.chartReady();
@@ -3305,6 +3540,10 @@ var Renderer = class {
3305
3540
  this.orderLineService.updatePositions(positions);
3306
3541
  this.tpslService.updatePositions(positions);
3307
3542
  }
3543
+ /** Update the single liquidation price line (position + optional estimated from Order Entry). */
3544
+ renderLiquidationLine(params) {
3545
+ this.liquidationLineService.renderLiquidationLine(params);
3546
+ }
3308
3547
  async renderPendingOrders(pendingOrders) {
3309
3548
  await this.chartReady();
3310
3549
  this.orderLineService.renderPendingOrders(pendingOrders);
@@ -3317,6 +3556,7 @@ var Renderer = class {
3317
3556
  remove() {
3318
3557
  this.orderLineService.removeAll();
3319
3558
  this.positionLineService.removePositions();
3559
+ this.liquidationLineService.remove();
3320
3560
  this.executionService.destroy();
3321
3561
  }
3322
3562
  onDataLoaded() {
@@ -3367,6 +3607,10 @@ function useCreateRenderer(symbol, displayControlSetting) {
3367
3607
  status: OrderStatus.FILLED,
3368
3608
  size: 500
3369
3609
  });
3610
+ const ee = useEventEmitter();
3611
+ const [estimatedLiqPrice, setEstimatedLiqPrice] = useState(
3612
+ null
3613
+ );
3370
3614
  const createRenderer = useRef(
3371
3615
  (instance, host, broker, container) => {
3372
3616
  if (rendererRef.current) {
@@ -3399,11 +3643,41 @@ function useCreateRenderer(symbol, displayControlSetting) {
3399
3643
  unrealPnl: item.unrealized_pnl ?? 0,
3400
3644
  interest: 0,
3401
3645
  unrealPnlDecimal: 2,
3402
- basePriceDecimal: 4
3646
+ basePriceDecimal: 4,
3647
+ est_liq_price: item.est_liq_price ?? null
3403
3648
  };
3404
3649
  });
3405
3650
  renderer?.renderPositions(positionList);
3406
3651
  }, [renderer, positions, symbol, displayControlSetting, state]);
3652
+ useEffect(() => {
3653
+ const handler = (payload) => {
3654
+ if (payload.symbol === symbol) {
3655
+ setEstimatedLiqPrice(
3656
+ payload.isUserActive !== false ? payload.estLiqPrice : null
3657
+ );
3658
+ }
3659
+ };
3660
+ ee.on(ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, handler);
3661
+ return () => {
3662
+ ee.off(ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, handler);
3663
+ };
3664
+ }, [ee, symbol]);
3665
+ useEffect(() => {
3666
+ if (!renderer || !displayControlSetting?.position) return;
3667
+ const symbolPosition = (positions ?? []).find((p) => p.symbol === symbol);
3668
+ const positionLiqPrice = symbolPosition != null ? symbolPosition.est_liq_price ?? null : null;
3669
+ const effectiveEstimatedLiqPrice = estimatedLiqPrice != null && Number.isFinite(estimatedLiqPrice) ? estimatedLiqPrice : null;
3670
+ renderer.renderLiquidationLine({
3671
+ positionLiqPrice,
3672
+ estimatedLiqPrice: effectiveEstimatedLiqPrice
3673
+ });
3674
+ }, [
3675
+ renderer,
3676
+ positions,
3677
+ symbol,
3678
+ estimatedLiqPrice,
3679
+ displayControlSetting?.position
3680
+ ]);
3407
3681
  useEffect(() => {
3408
3682
  if (!displayControlSetting || !displayControlSetting.buySell) {
3409
3683
  renderer?.renderFilledOrders([], 6);
@@ -3888,6 +4162,7 @@ var Widget = class {
3888
4162
  locale: options.locale,
3889
4163
  theme: options.theme,
3890
4164
  loading_screen: options.loadingScreen,
4165
+ toolbar_bg: options.toolbarBg,
3891
4166
  overrides: options.overrides,
3892
4167
  container: options.container,
3893
4168
  favorites: {
@@ -3962,10 +4237,8 @@ var Widget = class {
3962
4237
  };
3963
4238
 
3964
4239
  // src/components/tradingview.script.ts
3965
- var CHART_KEY = "SDK_Tradingview";
3966
- var MOBILE_CHART_KEY = "SDK_Moblie_Tradingview";
3967
- var getChartKey = (isMobile) => {
3968
- return isMobile ? MOBILE_CHART_KEY : CHART_KEY;
4240
+ var getChartLocalStorageKey = (suffix, isMobile) => {
4241
+ return isMobile ? `orderly_tradingview_mobile_${suffix}` : `orderly_tradingview_${suffix}`;
3969
4242
  };
3970
4243
  var defaultLocale = (localeCode) => {
3971
4244
  return localeCode === "id" ? "id_ID" : localeCode === "tc" ? "zh_TW" : localeCode;
@@ -3978,7 +4251,6 @@ function useTradingviewScript(props) {
3978
4251
  overrides: customerOverrides,
3979
4252
  studiesOverrides: customerStudiesOverrides,
3980
4253
  symbol,
3981
- theme,
3982
4254
  loadingScreen: customerLoadingScreen,
3983
4255
  mode,
3984
4256
  colorConfig: customerColorConfig,
@@ -3989,6 +4261,10 @@ function useTradingviewScript(props) {
3989
4261
  customIndicatorsGetter
3990
4262
  } = props;
3991
4263
  const localeCode = useLocaleCode();
4264
+ const { isMobile } = useScreen();
4265
+ const { currentTheme } = useOrderlyTheme();
4266
+ const theme = props.theme ?? currentTheme?.mode ?? "dark";
4267
+ const cssVariables = useCssVariables(theme);
3992
4268
  const chart = useRef(null);
3993
4269
  const apiBaseUrl = useConfig("apiBaseUrl");
3994
4270
  const { state: accountState } = useAccount();
@@ -4043,19 +4319,13 @@ function useTradingviewScript(props) {
4043
4319
  }
4044
4320
  return lastUsedLineType;
4045
4321
  });
4046
- const isMobile = useMediaQuery(MEDIA_TABLET);
4047
- const colorConfig = useMemo(
4048
- () => Object.assign({}, defaultColorConfig, customerColorConfig ?? {}),
4049
- [customerColorConfig]
4050
- );
4051
- const loadingScreen = useMemo(() => {
4052
- if (typeof customerLoadingScreen === "object") {
4053
- return customerLoadingScreen;
4054
- }
4055
- return {
4056
- backgroundColor: chartBG
4057
- };
4058
- }, [customerLoadingScreen]);
4322
+ const colorConfig = useMemo(() => {
4323
+ return getColorConfig({
4324
+ theme,
4325
+ customerColorConfig,
4326
+ cssVariables
4327
+ });
4328
+ }, [theme, customerColorConfig, cssVariables]);
4059
4329
  const ws = useWS();
4060
4330
  const [chartingLibrarySciprtReady, setChartingLibrarySciprtReady] = useState(false);
4061
4331
  const closePositionConfirmCallback = (data) => {
@@ -4170,6 +4440,42 @@ function useTradingviewScript(props) {
4170
4440
  chartRef.current.appendChild(script);
4171
4441
  }
4172
4442
  }, [chartRef, chartingLibrarySciprtReady, tradingViewScriptSrc]);
4443
+ const { overrides, studiesOverrides, configHash, toolbarBg, loadingScreen } = useMemo(() => {
4444
+ const defaultOverrides2 = getOveriides({
4445
+ theme,
4446
+ colorConfig,
4447
+ isMobile
4448
+ });
4449
+ const overrides2 = { ...defaultOverrides2.overrides, ...customerOverrides };
4450
+ const studiesOverrides2 = {
4451
+ ...defaultOverrides2.studiesOverrides,
4452
+ ...customerStudiesOverrides
4453
+ };
4454
+ const configHash2 = getOverridesConfigHash(
4455
+ theme,
4456
+ overrides2,
4457
+ studiesOverrides2
4458
+ );
4459
+ const toolbarBg2 = cssVariables.chartBG;
4460
+ const loadingScreen2 = customerLoadingScreen ?? {
4461
+ backgroundColor: colorConfig.chartBG,
4462
+ foregroundColor: cssVariables.primary
4463
+ };
4464
+ return {
4465
+ overrides: overrides2,
4466
+ studiesOverrides: studiesOverrides2,
4467
+ configHash: configHash2,
4468
+ toolbarBg: toolbarBg2,
4469
+ loadingScreen: loadingScreen2
4470
+ };
4471
+ }, [
4472
+ theme,
4473
+ colorConfig,
4474
+ isMobile,
4475
+ customerOverrides,
4476
+ customerStudiesOverrides,
4477
+ customerLoadingScreen
4478
+ ]);
4173
4479
  useEffect(() => {
4174
4480
  if (!symbol) {
4175
4481
  return;
@@ -4177,13 +4483,6 @@ function useTradingviewScript(props) {
4177
4483
  if (!chartingLibrarySciprtReady || !tradingViewScriptSrc) {
4178
4484
  return;
4179
4485
  }
4180
- const defaultOverrides = getOveriides(colorConfig, isMobile);
4181
- const overrides = customerOverrides ? Object.assign({}, defaultOverrides.overrides, customerOverrides) : defaultOverrides.overrides;
4182
- const studiesOverrides = customerStudiesOverrides ? Object.assign(
4183
- {},
4184
- defaultOverrides.studiesOverrides,
4185
- customerStudiesOverrides
4186
- ) : defaultOverrides.studiesOverrides;
4187
4486
  if (chartRef.current) {
4188
4487
  const options = {
4189
4488
  // fullscreen: fullscreen ?? false,
@@ -4196,8 +4495,9 @@ function useTradingviewScript(props) {
4196
4495
  libraryPath,
4197
4496
  customCssUrl: tradingViewCustomCssUrl,
4198
4497
  interval: interval ?? "1",
4199
- theme: theme ?? "dark",
4200
- loadingScreen: loadingScreen ?? {},
4498
+ theme,
4499
+ loadingScreen,
4500
+ toolbarBg,
4201
4501
  overrides,
4202
4502
  studiesOverrides,
4203
4503
  datafeed: new Datafeed(apiBaseUrl, ws),
@@ -4215,7 +4515,7 @@ function useTradingviewScript(props) {
4215
4515
  };
4216
4516
  const chartProps = {
4217
4517
  options,
4218
- chartKey: getChartKey(isMobile),
4518
+ chartKey: getChartLocalStorageKey(`${configHash}`, isMobile),
4219
4519
  mode,
4220
4520
  onClick: () => {
4221
4521
  },
@@ -4228,16 +4528,20 @@ function useTradingviewScript(props) {
4228
4528
  chart.current?.remove();
4229
4529
  };
4230
4530
  }, [
4231
- chartingLibrarySciprtReady,
4232
4531
  isMobile,
4233
4532
  mode,
4234
4533
  chart,
4235
4534
  chartRef,
4236
4535
  chartingLibrarySciprtReady,
4237
4536
  tradingViewScriptSrc,
4238
- colorConfig,
4239
4537
  locale,
4240
4538
  localeCode,
4539
+ theme,
4540
+ overrides,
4541
+ studiesOverrides,
4542
+ configHash,
4543
+ loadingScreen,
4544
+ toolbarBg,
4241
4545
  customIndicatorsGetter
4242
4546
  ]);
4243
4547
  useEffect(() => {
@@ -4426,7 +4730,10 @@ var TradingviewUI = forwardRef((props, ref) => {
4426
4730
  "div",
4427
4731
  {
4428
4732
  ref,
4429
- className: cn("oui-relative oui-size-full", props.classNames?.root),
4733
+ className: cn(
4734
+ "oui-tradingview-root oui-relative oui-size-full",
4735
+ props.classNames?.root
4736
+ ),
4430
4737
  children: !tradingViewScriptSrc ? /* @__PURE__ */ jsx(NoTradingview, {}) : /* @__PURE__ */ jsxs(
4431
4738
  "div",
4432
4739
  {