@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.js CHANGED
@@ -144,8 +144,7 @@ var init_icons = __esm({
144
144
  fillRule: "evenodd",
145
145
  clipRule: "evenodd",
146
146
  d: "M5.5 14.5V5.5H7.5V14.5H5.5Z",
147
- fill: "white",
148
- fillOpacity: "0.12"
147
+ className: "oui-fill-base-contrast-12"
149
148
  }
150
149
  ),
151
150
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -202,8 +201,7 @@ var init_icons = __esm({
202
201
  fillRule: "evenodd",
203
202
  clipRule: "evenodd",
204
203
  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",
205
- fill: "white",
206
- fillOpacity: "0.12"
204
+ className: "oui-fill-base-contrast-12"
207
205
  }
208
206
  )
209
207
  ] })
@@ -239,8 +237,7 @@ var init_icons = __esm({
239
237
  {
240
238
  id: "Vector",
241
239
  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",
242
- fill: "white",
243
- fillOpacity: "0.2"
240
+ className: "oui-fill-base-contrast-20"
244
241
  }
245
242
  ) })
246
243
  }
@@ -502,7 +499,7 @@ var init_timeInterval = __esm({
502
499
  "oui-cursor-pointer oui-px-2",
503
500
  "hover:oui-text-base-contrast-80",
504
501
  "oui-break-normal oui-whitespace-nowrap",
505
- props.interval === item.value && "oui-text-base-contrast-80 oui-bg-white/[.06] oui-rounded"
502
+ props.interval === item.value && "oui-text-base-contrast-80 oui-bg-base-contrast-6 oui-rounded"
506
503
  ),
507
504
  id: item.value,
508
505
  onClick: () => props.changeInterval(item.value),
@@ -571,7 +568,7 @@ var init_timeInterval = __esm({
571
568
  className: ui.cn(
572
569
  "oui-px-2",
573
570
  "oui-break-normal oui-whitespace-nowrap",
574
- props.interval === item.value && "oui-text-base-contrast-80 oui-bg-white/[.06] oui-rounded"
571
+ props.interval === item.value && "oui-text-base-contrast-80 oui-bg-base-contrast-6 oui-rounded"
575
572
  ),
576
573
  onClick: () => props.changeInterval(item.value),
577
574
  children: item.label
@@ -894,38 +891,104 @@ var init_displayControl = __esm({
894
891
  init_displayControl_mobile();
895
892
  }
896
893
  });
894
+ var cssVar2Hex = (color) => {
895
+ const [r, g, b] = color.trim().split(/\s+/).map(Number);
896
+ const toHex = (n) => Math.min(255, Math.max(0, n)).toString(16).padStart(2, "0");
897
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
898
+ };
899
+ var useCssVariables = (theme) => {
900
+ const [cssVariables, setCssVariables] = React3.useState({});
901
+ const themeAttribute = ui.useThemeAttribute();
902
+ React3.useEffect(() => {
903
+ const rootStyle = getComputedStyle(document.documentElement);
904
+ const base9 = rootStyle.getPropertyValue("--oui-color-base-9").trim();
905
+ const primaryVar = rootStyle.getPropertyValue("--oui-color-primary").trim();
906
+ const warningLightVar = rootStyle.getPropertyValue("--oui-color-warning-light").trim();
907
+ setCssVariables({
908
+ chartBG: cssVar2Hex(base9),
909
+ primary: cssVar2Hex(primaryVar),
910
+ /** For liquidation line; same as Position list Liq. Price (--oui-color-warning-light). */
911
+ warningLight: warningLightVar ? cssVar2Hex(warningLightVar) : ""
912
+ // upColor: cssVar2Hex(profitColor),
913
+ // downColor: cssVar2Hex(lossColor),
914
+ });
915
+ }, [theme, themeAttribute]);
916
+ return cssVariables;
917
+ };
897
918
 
898
919
  // src/utils/chart.util.ts
899
- var upColor = "#008676";
900
- var downColor = "#D92D6B";
901
- var chartBG = "#131519";
902
- var pnlUpColor = "#00B49E";
903
- var pnlDownColor = "#FF447C";
904
- var pnlZoreColor = "#333948";
905
- var textColor = "#FFFFFF";
906
- var qtyTextColor = "#F4F7F9";
907
920
  var font = "regular 11px Manrope";
908
921
  var defaultColorConfig = {
909
- upColor,
910
- downColor,
911
- chartBG,
912
- pnlUpColor,
913
- pnlDownColor,
914
- pnlZoreColor,
915
- textColor,
916
- qtyTextColor,
917
- font,
918
- volumeUpColor: "#0C3E3A",
919
- volumeDownColor: "#5A1E36",
920
- closeIcon: "rgba(255, 255, 255, 0.8)"
922
+ dark: {
923
+ chartBG: "#131519",
924
+ upColor: "#008676",
925
+ downColor: "#D92D6B",
926
+ pnlUpColor: "#00B49E",
927
+ pnlDownColor: "#FF447C",
928
+ pnlZoreColor: "#333948",
929
+ textColor: "#FFFFFF",
930
+ qtyTextColor: "#F4F7F9",
931
+ font,
932
+ volumeUpColor: "#0C3E3A",
933
+ volumeDownColor: "#5A1E36",
934
+ closeIcon: "rgba(255, 255, 255, 0.8)"
935
+ },
936
+ light: {
937
+ chartBG: "#ffffff",
938
+ upColor: "#2ebd85",
939
+ downColor: "#F6465D",
940
+ pnlUpColor: "#2ebd85",
941
+ pnlDownColor: "#F6465D",
942
+ pnlZoreColor: "#333948",
943
+ textColor: "#000000",
944
+ qtyTextColor: "#000000",
945
+ font,
946
+ volumeUpColor: "#2ebd85",
947
+ volumeDownColor: "#F6465D",
948
+ closeIcon: "rgba(0, 0, 0, 0.8)"
949
+ }
921
950
  };
922
- var getOveriides = (colorConfig, isMobile) => {
951
+ var getColorConfig = ({
952
+ theme,
953
+ cssVariables,
954
+ customerColorConfig
955
+ }) => {
956
+ const defaultCconfig = defaultColorConfig[theme] || defaultColorConfig.dark;
957
+ const chartBG = customerColorConfig?.chartBG || cssVariables.chartBG || defaultCconfig.chartBG;
958
+ const liqLineColor = customerColorConfig?.liqLineColor ?? cssVariables.warningLight;
959
+ return {
960
+ ...defaultCconfig,
961
+ ...customerColorConfig,
962
+ chartBG,
963
+ liqLineColor
964
+ // upColor,
965
+ // downColor,
966
+ };
967
+ };
968
+ var defaultOverrides = {
969
+ dark: {
970
+ "paneProperties.separatorColor": "#2B2833",
971
+ "paneProperties.vertGridProperties.color": "#26232F",
972
+ "paneProperties.horzGridProperties.color": "#26232F",
973
+ "scalesProperties.textColor": "#97969B"
974
+ },
975
+ light: {
976
+ // "paneProperties.separatorColor": "#E5E5E5",
977
+ // "paneProperties.vertGridProperties.color": "#E5E5E5",
978
+ // "paneProperties.horzGridProperties.color": "#E5E5E5",
979
+ // "scalesProperties.textColor": "#97969B",
980
+ "scalesProperties.crosshairLabelBgColorLight": "#E5E5E5"
981
+ }
982
+ };
983
+ var getOveriides = ({
984
+ theme,
985
+ colorConfig,
986
+ isMobile
987
+ }) => {
923
988
  const overrides = {
924
989
  "paneProperties.background": colorConfig.chartBG,
925
- // "paneProperties.background": "#ffff00",
926
990
  // "mainSeriesProperties.style": 1,
927
991
  "paneProperties.backgroundType": "solid",
928
- // "paneProperties.background": "#151822",
929
992
  "mainSeriesProperties.candleStyle.upColor": colorConfig.upColor,
930
993
  "mainSeriesProperties.candleStyle.downColor": colorConfig.downColor,
931
994
  "mainSeriesProperties.candleStyle.borderColor": colorConfig.upColor,
@@ -933,13 +996,10 @@ var getOveriides = (colorConfig, isMobile) => {
933
996
  "mainSeriesProperties.candleStyle.borderDownColor": colorConfig.downColor,
934
997
  "mainSeriesProperties.candleStyle.wickUpColor": colorConfig.upColor,
935
998
  "mainSeriesProperties.candleStyle.wickDownColor": colorConfig.downColor,
936
- "paneProperties.separatorColor": "#2B2833",
937
- "paneProperties.vertGridProperties.color": "#26232F",
938
- "paneProperties.horzGridProperties.color": "#26232F",
939
999
  "scalesProperties.fontSize": isMobile ? 8 : 12,
940
- "scalesProperties.textColor": "#97969B",
941
1000
  "paneProperties.legendProperties.showSeriesTitle": isMobile ? false : true,
942
- "mainSeriesProperties.statusViewStyle.symbolTextSource": "ticker"
1001
+ "mainSeriesProperties.statusViewStyle.symbolTextSource": "ticker",
1002
+ ...defaultOverrides[theme]
943
1003
  };
944
1004
  const studiesOverrides = {
945
1005
  "volume.volume.color.0": colorConfig.volumeDownColor,
@@ -953,6 +1013,26 @@ var getOveriides = (colorConfig, isMobile) => {
953
1013
  var EXCHANGE = "Orderly";
954
1014
  var withoutExchangePrefix = (symbol) => symbol.includes(":") ? symbol.split(":")[1] : symbol;
955
1015
  var withExchangePrefix = (symbol) => symbol.startsWith(`${EXCHANGE}:`) ? symbol : `${EXCHANGE}:${symbol}`;
1016
+ function stringifyWithSortedKeys(obj) {
1017
+ const keys = Object.keys(obj).sort();
1018
+ const sorted = {};
1019
+ for (const k of keys) {
1020
+ sorted[k] = obj[k];
1021
+ }
1022
+ return JSON.stringify(sorted);
1023
+ }
1024
+ function simpleHash(str) {
1025
+ let h = 5381;
1026
+ for (let i = 0; i < str.length; i++) {
1027
+ h = (h << 5) + h + str.charCodeAt(i);
1028
+ h = h >>> 0;
1029
+ }
1030
+ return Math.abs(h).toString(36);
1031
+ }
1032
+ function getOverridesConfigHash(theme, overrides, studiesOverrides) {
1033
+ const payload = `${theme}${stringifyWithSortedKeys(overrides)}${stringifyWithSortedKeys(studiesOverrides)}`;
1034
+ return simpleHash(payload);
1035
+ }
956
1036
  var getBrokerAdapter = (host, broker) => {
957
1037
  let _symbolInfo;
958
1038
  const getOrderCombinationType = (orderType) => {
@@ -2274,6 +2354,8 @@ var EST_TPSL_PNL_DECIMAL = 2;
2274
2354
  var textDash = "--";
2275
2355
  var BracketAlgoType = ["BRACKET" /* BRACKET */, "STOP_BRACKET" /* STOP_BRACKET */];
2276
2356
  var TpslAlgoType = ["POSITIONAL_TP_SL" /* POSITIONAL_TP_SL */, "TP_SL" /* TP_SL */];
2357
+ var isTpOrder = (order) => order.root_algo_order_id !== order.algo_order_id && order.algo_type === "TAKE_PROFIT" /* TAKE_PROFIT */;
2358
+ var isSlOrder = (order) => order.root_algo_order_id !== order.algo_order_id && order.algo_type === "STOP_LOSS" /* STOP_LOSS */;
2277
2359
  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 */);
2278
2360
  var isBracketAlgoType = (algoType) => !!algoType && BracketAlgoType.includes(algoType);
2279
2361
  var isPositionTpsl = (order) => order.type === "CLOSE_POSITION" /* CLOSE_POSITION */;
@@ -2464,6 +2546,8 @@ function useSendOrder(symbol) {
2464
2546
  sendMarketOrder
2465
2547
  };
2466
2548
  }
2549
+
2550
+ // src/tradingviewAdapter/hooks/useBroker.ts
2467
2551
  var createBrokerMethod = (method) => {
2468
2552
  return (params) => method(params);
2469
2553
  };
@@ -2519,6 +2603,9 @@ var useBroker = ({
2519
2603
  React3.useEffect(() => {
2520
2604
  broker.current.cancelOrder = cancelOrder;
2521
2605
  }, [cancelOrder]);
2606
+ React3.useEffect(() => {
2607
+ broker.current.colorConfig = colorConfig;
2608
+ }, [colorConfig]);
2522
2609
  return broker.current;
2523
2610
  };
2524
2611
  var useBroker_default = useBroker;
@@ -2657,8 +2744,91 @@ var ExecutionService = class _ExecutionService {
2657
2744
  this.unsubscribeIntervalChange();
2658
2745
  }
2659
2746
  };
2747
+ var LiquidationLineService = class {
2748
+ constructor(instance, broker) {
2749
+ this.line = null;
2750
+ /** Tracks whether the current line source is estimated liq. price (vs position liq. price). */
2751
+ this.isUsingEstimatedPrice = false;
2752
+ this.instance = instance;
2753
+ this.broker = broker;
2754
+ }
2755
+ /**
2756
+ * Render or update the liquidation line. Call when position or estimated liq. price changes.
2757
+ */
2758
+ renderLiquidationLine(params) {
2759
+ const { positionLiqPrice, estimatedLiqPrice } = params;
2760
+ const hasValidEst = estimatedLiqPrice != null && Number.isFinite(estimatedLiqPrice);
2761
+ const hasValidPosition = positionLiqPrice != null && Number.isFinite(positionLiqPrice);
2762
+ if (hasValidEst) {
2763
+ this.isUsingEstimatedPrice = true;
2764
+ this.setLinePrice(estimatedLiqPrice);
2765
+ this.line?.setLineStyle(1);
2766
+ return;
2767
+ }
2768
+ if (hasValidPosition) {
2769
+ this.isUsingEstimatedPrice = false;
2770
+ this.setLinePrice(positionLiqPrice);
2771
+ this.line?.setLineStyle(0);
2772
+ return;
2773
+ }
2774
+ this.removeLine();
2775
+ }
2776
+ /** Remove the line (e.g. on destroy). */
2777
+ remove() {
2778
+ this.removeLine();
2779
+ }
2780
+ getOrCreateLine() {
2781
+ if (this.line != null) {
2782
+ return this.line;
2783
+ }
2784
+ try {
2785
+ const activeChart = this.instance.activeChart();
2786
+ if (!activeChart) return null;
2787
+ const orderLine = activeChart.createOrderLine();
2788
+ if (!orderLine) return null;
2789
+ const colorConfig = this.broker.colorConfig;
2790
+ const lineColor = colorConfig.liqLineColor ?? colorConfig.textColor;
2791
+ if (!lineColor) return null;
2792
+ this.line = orderLine.setCancellable(false).setLineLength(100).setEditable(false).setExtendLeft(true).setQuantity("").setLineStyle(1).setLineColor(lineColor).setBodyBorderColor(lineColor);
2793
+ this.updateLineLabel();
2794
+ if (colorConfig.chartBG) {
2795
+ this.line = this.line.setBodyBackgroundColor(colorConfig.chartBG).setQuantityBackgroundColor(colorConfig.chartBG);
2796
+ }
2797
+ if (colorConfig.textColor) {
2798
+ this.line = this.line.setBodyTextColor(colorConfig.textColor);
2799
+ }
2800
+ if (colorConfig.font) {
2801
+ this.line = this.line.setBodyFont(colorConfig.font).setQuantityFont(colorConfig.font);
2802
+ }
2803
+ return this.line;
2804
+ } catch {
2805
+ return null;
2806
+ }
2807
+ }
2808
+ setLinePrice(price) {
2809
+ const line = this.getOrCreateLine();
2810
+ if (line) {
2811
+ line.setPrice(price);
2812
+ this.updateLineLabel();
2813
+ }
2814
+ }
2815
+ removeLine() {
2816
+ if (this.line) {
2817
+ this.line.remove();
2818
+ this.line = null;
2819
+ }
2820
+ }
2821
+ /** Ensure tooltip/text reflect whether we are showing estimated or position liq. price. */
2822
+ updateLineLabel() {
2823
+ if (!this.line) return;
2824
+ const key = this.isUsingEstimatedPrice ? "orderEntry.estLiqPrice" : "positions.column.liqPrice";
2825
+ const label = i18n.i18n.t(key);
2826
+ this.line.setTooltip(label).setText(label);
2827
+ }
2828
+ };
2660
2829
 
2661
2830
  // src/tradingviewAdapter/renderer/order.util.ts
2831
+ var CHART_QTY_DECIMAL = 4;
2662
2832
  var getOrderId = (order) => {
2663
2833
  if (order === null || order === void 0) {
2664
2834
  return void 0;
@@ -2690,6 +2860,22 @@ var TpslCalService = class {
2690
2860
  const { estPnl } = getTpslEstPnl(tpslOrder, position);
2691
2861
  return formatPnl(estPnl);
2692
2862
  }
2863
+ /**
2864
+ * Returns the quantity for a TP/SL order for label display.
2865
+ * Position TP/SL: |position.balance|; partial TP/SL: |order.quantity - executed|.
2866
+ * @returns quantity number or undefined when positions unavailable
2867
+ */
2868
+ getTpslQuantity(tpslOrder) {
2869
+ if (this.positions === null) {
2870
+ return void 0;
2871
+ }
2872
+ const position = this.positions[0];
2873
+ if (!position) {
2874
+ return void 0;
2875
+ }
2876
+ const { quantity } = getTpslEstPnl(tpslOrder, position);
2877
+ return quantity;
2878
+ }
2693
2879
  prepareTpslPnlMap(newPendingOrders) {
2694
2880
  const changed = [];
2695
2881
  newPendingOrders.forEach((order) => {
@@ -2840,12 +3026,18 @@ var _OrderLineService = class _OrderLineService {
2840
3026
  }
2841
3027
  return pendingOrder.trigger_price || pendingOrder.price;
2842
3028
  }
3029
+ /**
3030
+ * Builds TP/SL label body: type + PnL (e.g. "Take profit | 123.45 USDC").
3031
+ * Falls back to "--" when PnL unavailable.
3032
+ */
2843
3033
  getTPSLTextWithTpsl(text, pendingOrder) {
2844
3034
  const orderId = getOrderId(pendingOrder);
2845
3035
  if (!orderId) {
2846
3036
  return text;
2847
3037
  }
2848
- return text;
3038
+ const pnlStr = this.tpslCalService.getFormattedEstPnl(pendingOrder);
3039
+ const displayPnl = pnlStr ? pnlStr : formatPnl(void 0);
3040
+ return `${text} | ${displayPnl} USDC`;
2849
3041
  }
2850
3042
  getTPSLText(pendingOrder) {
2851
3043
  const tpslTypeText = getTpslTag(
@@ -2857,17 +3049,26 @@ var _OrderLineService = class _OrderLineService {
2857
3049
  }
2858
3050
  return null;
2859
3051
  }
3052
+ /**
3053
+ * Returns quantity string for order line. For TP/SL: "qty (percent%)".
3054
+ * Uses baseDp from broker.getSymbolInfo; falls back to CHART_QTY_DECIMAL.
3055
+ */
2860
3056
  getOrderQuantity(pendingOrder) {
2861
3057
  if (pendingOrder.algo_order_id) {
2862
- if (isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder)) {
2863
- return "100%";
2864
- }
2865
- if (isActivatedQuantityTpsl(pendingOrder)) {
2866
- const qty = new utils.Decimal(pendingOrder.quantity).minus(
2867
- pendingOrder.executed ?? 0
2868
- );
2869
- const per = qty.div(new utils.Decimal(pendingOrder.position_qty)).mul(100).todp(2).toNumber();
2870
- return `${Math.min(Math.abs(per), 100).toString()}%`;
3058
+ const isTpsl = isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder) || isActivatedQuantityTpsl(pendingOrder);
3059
+ if (isTpsl) {
3060
+ const percentStr = isActivatedPositionTpsl(pendingOrder) || isPositionTpsl(pendingOrder) ? "100%" : (() => {
3061
+ const qty2 = new utils.Decimal(pendingOrder.quantity).minus(
3062
+ pendingOrder.executed ?? 0
3063
+ );
3064
+ const per = qty2.div(new utils.Decimal(pendingOrder.position_qty)).mul(100).todp(0).toNumber();
3065
+ return `${Math.min(Math.abs(per), 100).toString()}%`;
3066
+ })();
3067
+ const qty = this.tpslCalService.getTpslQuantity(pendingOrder);
3068
+ const symbolInfo = this.broker.getSymbolInfo?.(pendingOrder.symbol);
3069
+ const baseDp = symbolInfo?.baseTick != null ? utils.getPrecisionByNumber(symbolInfo.baseTick) : CHART_QTY_DECIMAL;
3070
+ const qtyStr = qty != null ? utils.commify(new utils.Decimal(qty).todp(baseDp).toString()) : textDash;
3071
+ return `${qtyStr} (${percentStr})`;
2871
3072
  }
2872
3073
  }
2873
3074
  return utils.commify(new utils.Decimal(pendingOrder.quantity).toString());
@@ -2883,13 +3084,22 @@ var _OrderLineService = class _OrderLineService {
2883
3084
  if (!orderLine) {
2884
3085
  return null;
2885
3086
  }
2886
- const color = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.upColor : colorConfig.downColor;
2887
- pendingOrder.side === "BUY" /* BUY */ ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
3087
+ let color = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.upColor : colorConfig.downColor;
3088
+ let borderColor = pendingOrder.side === "BUY" /* BUY */ ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
3089
+ if (isTpslOrder(pendingOrder)) {
3090
+ if (isTpOrder(pendingOrder)) {
3091
+ color = colorConfig.upColor;
3092
+ borderColor = colorConfig.pnlUpColor;
3093
+ } else if (isSlOrder(pendingOrder)) {
3094
+ color = colorConfig.downColor;
3095
+ borderColor = colorConfig.pnlDownColor;
3096
+ }
3097
+ }
2888
3098
  const price = _OrderLineService.getOrderPrice(pendingOrder);
2889
3099
  const lineLength = 100;
2890
3100
  const quantity = this.getOrderQuantity(pendingOrder);
2891
- const textColor2 = colorConfig.textColor;
2892
- orderLine.setText(text).setCancelButtonIconColor(colorConfig.closeIcon).setCancelButtonBorderColor(color).setBodyTextColor(textColor2).setBodyBorderColor(color).setQuantityBorderColor(color).setQuantityTextColor(color).setLineColor(color).setLineLength(lineLength).setQuantity(quantity ?? "").setPrice(price);
3101
+ const textColor = colorConfig.textColor;
3102
+ orderLine.setText(text).setCancelButtonIconColor(colorConfig.closeIcon).setCancelButtonBorderColor(color).setBodyTextColor(textColor).setBodyBorderColor(borderColor).setQuantityBorderColor(borderColor).setQuantityTextColor(color).setLineColor(color).setLineLength(lineLength).setQuantity(quantity ?? "").setPrice(price);
2893
3103
  if (this.broker.mode !== 3 /* MOBILE */) {
2894
3104
  orderLine.onCancel(null, () => this.broker.cancelOrder(pendingOrder));
2895
3105
  this.applyEditOnMove(orderLine, pendingOrder);
@@ -2963,7 +3173,21 @@ var PositionLineService = class _PositionLineService {
2963
3173
  this.lastPositions = positions;
2964
3174
  }
2965
3175
  getBasePositionLine() {
2966
- return this.instance.activeChart().createPositionLine().setTooltip(i18n.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);
3176
+ const colorConfig = this.broker.colorConfig;
3177
+ let line = this.instance.activeChart().createPositionLine().setTooltip(i18n.i18n.t("positions.closePosition")).setLineLength(100).setLineStyle(1);
3178
+ if (colorConfig.chartBG) {
3179
+ line = line.setQuantityBackgroundColor(colorConfig.chartBG).setCloseButtonBackgroundColor(colorConfig.chartBG);
3180
+ }
3181
+ if (colorConfig.textColor) {
3182
+ line = line.setBodyTextColor(colorConfig.textColor);
3183
+ }
3184
+ if (colorConfig.qtyTextColor) {
3185
+ line = line.setQuantityTextColor(colorConfig.qtyTextColor);
3186
+ }
3187
+ if (colorConfig.font) {
3188
+ line = line.setBodyFont(colorConfig.font).setQuantityFont(colorConfig.font);
3189
+ }
3190
+ return line;
2967
3191
  }
2968
3192
  static getPositionQuantity(balance) {
2969
3193
  return utils.commify(new utils.Decimal(balance).todp(4, utils.Decimal.ROUND_DOWN).toString());
@@ -2987,25 +3211,35 @@ var PositionLineService = class _PositionLineService {
2987
3211
  }
2988
3212
  drawPositionLine(position, idx) {
2989
3213
  const colorConfig = this.broker.colorConfig;
2990
- const isPositiveUnrealPnl = position.unrealPnl >= 0;
2991
3214
  const isPositiveBalance = position.balance >= 0;
2992
- let pnlColor = colorConfig.pnlZoreColor;
2993
3215
  const pnlDecimal = new utils.Decimal(position.unrealPnl);
3216
+ let pnlColor;
2994
3217
  if (pnlDecimal.greaterThan(0)) {
2995
3218
  pnlColor = colorConfig.upColor;
2996
3219
  } else if (pnlDecimal.lessThan(0)) {
2997
3220
  pnlColor = colorConfig.downColor;
3221
+ } else {
3222
+ pnlColor = colorConfig.pnlZoreColor;
2998
3223
  }
2999
- isPositiveUnrealPnl ? colorConfig.pnlUpColor : colorConfig.pnlDownColor;
3000
3224
  const sideColor = isPositiveBalance ? colorConfig.upColor : colorConfig.downColor;
3001
3225
  const price = new utils.Decimal(position.open).toNumber();
3002
3226
  this.positionLines[idx] = this.positionLines[idx] ?? this.getBasePositionLine();
3003
- 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(
3227
+ let line = this.positionLines[idx].setQuantity(_PositionLineService.getPositionQuantity(position.balance)).setPrice(price).setText(
3004
3228
  _PositionLineService.getPositionPnL(
3005
3229
  position.unrealPnl,
3006
3230
  position.unrealPnlDecimal
3007
3231
  )
3008
3232
  );
3233
+ if (colorConfig.closeIcon) {
3234
+ line = line.setCloseButtonIconColor(colorConfig.closeIcon);
3235
+ }
3236
+ if (sideColor) {
3237
+ line = line.setCloseButtonBorderColor(sideColor).setQuantityTextColor(sideColor).setLineColor(sideColor).setQuantityBorderColor(sideColor);
3238
+ }
3239
+ if (pnlColor) {
3240
+ line = line.setBodyBackgroundColor(pnlColor).setBodyBorderColor(pnlColor);
3241
+ }
3242
+ this.positionLines[idx] = line;
3009
3243
  if (this.broker.mode !== 3 /* MOBILE */) {
3010
3244
  this.positionLines[idx].onClose(null, () => {
3011
3245
  this.broker.closePosition(position);
@@ -3198,7 +3432,7 @@ var TPSLService = class {
3198
3432
  disableUndo: true,
3199
3433
  zOrder: "top",
3200
3434
  overrides: {
3201
- linecolor: "rgba(255,255,255, 0.2)",
3435
+ linecolor: "rgba(var(--oui-color-base-foreground)/0.2)",
3202
3436
  linewidth: 1,
3203
3437
  rightEnd: 1,
3204
3438
  leftEnd: 1
@@ -3303,6 +3537,7 @@ var Renderer = class {
3303
3537
  this.orderLineService = new OrderLineService(instance, broker);
3304
3538
  this.executionService = new ExecutionService(instance, broker);
3305
3539
  this.tpslService = new TPSLService(instance, broker);
3540
+ this.liquidationLineService = new LiquidationLineService(instance, broker);
3306
3541
  }
3307
3542
  async renderPositions(positions) {
3308
3543
  await this.chartReady();
@@ -3311,6 +3546,10 @@ var Renderer = class {
3311
3546
  this.orderLineService.updatePositions(positions);
3312
3547
  this.tpslService.updatePositions(positions);
3313
3548
  }
3549
+ /** Update the single liquidation price line (position + optional estimated from Order Entry). */
3550
+ renderLiquidationLine(params) {
3551
+ this.liquidationLineService.renderLiquidationLine(params);
3552
+ }
3314
3553
  async renderPendingOrders(pendingOrders) {
3315
3554
  await this.chartReady();
3316
3555
  this.orderLineService.renderPendingOrders(pendingOrders);
@@ -3323,6 +3562,7 @@ var Renderer = class {
3323
3562
  remove() {
3324
3563
  this.orderLineService.removeAll();
3325
3564
  this.positionLineService.removePositions();
3565
+ this.liquidationLineService.remove();
3326
3566
  this.executionService.destroy();
3327
3567
  }
3328
3568
  onDataLoaded() {
@@ -3373,6 +3613,10 @@ function useCreateRenderer(symbol, displayControlSetting) {
3373
3613
  status: types.OrderStatus.FILLED,
3374
3614
  size: 500
3375
3615
  });
3616
+ const ee = hooks.useEventEmitter();
3617
+ const [estimatedLiqPrice, setEstimatedLiqPrice] = React3.useState(
3618
+ null
3619
+ );
3376
3620
  const createRenderer = React3.useRef(
3377
3621
  (instance, host, broker, container) => {
3378
3622
  if (rendererRef.current) {
@@ -3405,11 +3649,41 @@ function useCreateRenderer(symbol, displayControlSetting) {
3405
3649
  unrealPnl: item.unrealized_pnl ?? 0,
3406
3650
  interest: 0,
3407
3651
  unrealPnlDecimal: 2,
3408
- basePriceDecimal: 4
3652
+ basePriceDecimal: 4,
3653
+ est_liq_price: item.est_liq_price ?? null
3409
3654
  };
3410
3655
  });
3411
3656
  renderer?.renderPositions(positionList);
3412
3657
  }, [renderer, positions, symbol, displayControlSetting, state]);
3658
+ React3.useEffect(() => {
3659
+ const handler = (payload) => {
3660
+ if (payload.symbol === symbol) {
3661
+ setEstimatedLiqPrice(
3662
+ payload.isUserActive !== false ? payload.estLiqPrice : null
3663
+ );
3664
+ }
3665
+ };
3666
+ ee.on(types.ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, handler);
3667
+ return () => {
3668
+ ee.off(types.ORDER_ENTRY_EST_LIQ_PRICE_CHANGE, handler);
3669
+ };
3670
+ }, [ee, symbol]);
3671
+ React3.useEffect(() => {
3672
+ if (!renderer || !displayControlSetting?.position) return;
3673
+ const symbolPosition = (positions ?? []).find((p) => p.symbol === symbol);
3674
+ const positionLiqPrice = symbolPosition != null ? symbolPosition.est_liq_price ?? null : null;
3675
+ const effectiveEstimatedLiqPrice = estimatedLiqPrice != null && Number.isFinite(estimatedLiqPrice) ? estimatedLiqPrice : null;
3676
+ renderer.renderLiquidationLine({
3677
+ positionLiqPrice,
3678
+ estimatedLiqPrice: effectiveEstimatedLiqPrice
3679
+ });
3680
+ }, [
3681
+ renderer,
3682
+ positions,
3683
+ symbol,
3684
+ estimatedLiqPrice,
3685
+ displayControlSetting?.position
3686
+ ]);
3413
3687
  React3.useEffect(() => {
3414
3688
  if (!displayControlSetting || !displayControlSetting.buySell) {
3415
3689
  renderer?.renderFilledOrders([], 6);
@@ -3894,6 +4168,7 @@ var Widget = class {
3894
4168
  locale: options.locale,
3895
4169
  theme: options.theme,
3896
4170
  loading_screen: options.loadingScreen,
4171
+ toolbar_bg: options.toolbarBg,
3897
4172
  overrides: options.overrides,
3898
4173
  container: options.container,
3899
4174
  favorites: {
@@ -3968,10 +4243,8 @@ var Widget = class {
3968
4243
  };
3969
4244
 
3970
4245
  // src/components/tradingview.script.ts
3971
- var CHART_KEY = "SDK_Tradingview";
3972
- var MOBILE_CHART_KEY = "SDK_Moblie_Tradingview";
3973
- var getChartKey = (isMobile) => {
3974
- return isMobile ? MOBILE_CHART_KEY : CHART_KEY;
4246
+ var getChartLocalStorageKey = (suffix, isMobile) => {
4247
+ return isMobile ? `orderly_tradingview_mobile_${suffix}` : `orderly_tradingview_${suffix}`;
3975
4248
  };
3976
4249
  var defaultLocale = (localeCode) => {
3977
4250
  return localeCode === "id" ? "id_ID" : localeCode === "tc" ? "zh_TW" : localeCode;
@@ -3984,7 +4257,6 @@ function useTradingviewScript(props) {
3984
4257
  overrides: customerOverrides,
3985
4258
  studiesOverrides: customerStudiesOverrides,
3986
4259
  symbol,
3987
- theme,
3988
4260
  loadingScreen: customerLoadingScreen,
3989
4261
  mode,
3990
4262
  colorConfig: customerColorConfig,
@@ -3995,6 +4267,10 @@ function useTradingviewScript(props) {
3995
4267
  customIndicatorsGetter
3996
4268
  } = props;
3997
4269
  const localeCode = i18n.useLocaleCode();
4270
+ const { isMobile } = ui.useScreen();
4271
+ const { currentTheme } = ui.useOrderlyTheme();
4272
+ const theme = props.theme ?? currentTheme?.mode ?? "dark";
4273
+ const cssVariables = useCssVariables(theme);
3998
4274
  const chart = React3.useRef(null);
3999
4275
  const apiBaseUrl = hooks.useConfig("apiBaseUrl");
4000
4276
  const { state: accountState } = hooks.useAccount();
@@ -4049,19 +4325,13 @@ function useTradingviewScript(props) {
4049
4325
  }
4050
4326
  return lastUsedLineType;
4051
4327
  });
4052
- const isMobile = hooks.useMediaQuery(types.MEDIA_TABLET);
4053
- const colorConfig = React3.useMemo(
4054
- () => Object.assign({}, defaultColorConfig, customerColorConfig ?? {}),
4055
- [customerColorConfig]
4056
- );
4057
- const loadingScreen = React3.useMemo(() => {
4058
- if (typeof customerLoadingScreen === "object") {
4059
- return customerLoadingScreen;
4060
- }
4061
- return {
4062
- backgroundColor: chartBG
4063
- };
4064
- }, [customerLoadingScreen]);
4328
+ const colorConfig = React3.useMemo(() => {
4329
+ return getColorConfig({
4330
+ theme,
4331
+ customerColorConfig,
4332
+ cssVariables
4333
+ });
4334
+ }, [theme, customerColorConfig, cssVariables]);
4065
4335
  const ws = hooks.useWS();
4066
4336
  const [chartingLibrarySciprtReady, setChartingLibrarySciprtReady] = React3.useState(false);
4067
4337
  const closePositionConfirmCallback = (data) => {
@@ -4176,6 +4446,42 @@ function useTradingviewScript(props) {
4176
4446
  chartRef.current.appendChild(script);
4177
4447
  }
4178
4448
  }, [chartRef, chartingLibrarySciprtReady, tradingViewScriptSrc]);
4449
+ const { overrides, studiesOverrides, configHash, toolbarBg, loadingScreen } = React3.useMemo(() => {
4450
+ const defaultOverrides2 = getOveriides({
4451
+ theme,
4452
+ colorConfig,
4453
+ isMobile
4454
+ });
4455
+ const overrides2 = { ...defaultOverrides2.overrides, ...customerOverrides };
4456
+ const studiesOverrides2 = {
4457
+ ...defaultOverrides2.studiesOverrides,
4458
+ ...customerStudiesOverrides
4459
+ };
4460
+ const configHash2 = getOverridesConfigHash(
4461
+ theme,
4462
+ overrides2,
4463
+ studiesOverrides2
4464
+ );
4465
+ const toolbarBg2 = cssVariables.chartBG;
4466
+ const loadingScreen2 = customerLoadingScreen ?? {
4467
+ backgroundColor: colorConfig.chartBG,
4468
+ foregroundColor: cssVariables.primary
4469
+ };
4470
+ return {
4471
+ overrides: overrides2,
4472
+ studiesOverrides: studiesOverrides2,
4473
+ configHash: configHash2,
4474
+ toolbarBg: toolbarBg2,
4475
+ loadingScreen: loadingScreen2
4476
+ };
4477
+ }, [
4478
+ theme,
4479
+ colorConfig,
4480
+ isMobile,
4481
+ customerOverrides,
4482
+ customerStudiesOverrides,
4483
+ customerLoadingScreen
4484
+ ]);
4179
4485
  React3.useEffect(() => {
4180
4486
  if (!symbol) {
4181
4487
  return;
@@ -4183,13 +4489,6 @@ function useTradingviewScript(props) {
4183
4489
  if (!chartingLibrarySciprtReady || !tradingViewScriptSrc) {
4184
4490
  return;
4185
4491
  }
4186
- const defaultOverrides = getOveriides(colorConfig, isMobile);
4187
- const overrides = customerOverrides ? Object.assign({}, defaultOverrides.overrides, customerOverrides) : defaultOverrides.overrides;
4188
- const studiesOverrides = customerStudiesOverrides ? Object.assign(
4189
- {},
4190
- defaultOverrides.studiesOverrides,
4191
- customerStudiesOverrides
4192
- ) : defaultOverrides.studiesOverrides;
4193
4492
  if (chartRef.current) {
4194
4493
  const options = {
4195
4494
  // fullscreen: fullscreen ?? false,
@@ -4202,8 +4501,9 @@ function useTradingviewScript(props) {
4202
4501
  libraryPath,
4203
4502
  customCssUrl: tradingViewCustomCssUrl,
4204
4503
  interval: interval ?? "1",
4205
- theme: theme ?? "dark",
4206
- loadingScreen: loadingScreen ?? {},
4504
+ theme,
4505
+ loadingScreen,
4506
+ toolbarBg,
4207
4507
  overrides,
4208
4508
  studiesOverrides,
4209
4509
  datafeed: new Datafeed(apiBaseUrl, ws),
@@ -4221,7 +4521,7 @@ function useTradingviewScript(props) {
4221
4521
  };
4222
4522
  const chartProps = {
4223
4523
  options,
4224
- chartKey: getChartKey(isMobile),
4524
+ chartKey: getChartLocalStorageKey(`${configHash}`, isMobile),
4225
4525
  mode,
4226
4526
  onClick: () => {
4227
4527
  },
@@ -4234,16 +4534,20 @@ function useTradingviewScript(props) {
4234
4534
  chart.current?.remove();
4235
4535
  };
4236
4536
  }, [
4237
- chartingLibrarySciprtReady,
4238
4537
  isMobile,
4239
4538
  mode,
4240
4539
  chart,
4241
4540
  chartRef,
4242
4541
  chartingLibrarySciprtReady,
4243
4542
  tradingViewScriptSrc,
4244
- colorConfig,
4245
4543
  locale,
4246
4544
  localeCode,
4545
+ theme,
4546
+ overrides,
4547
+ studiesOverrides,
4548
+ configHash,
4549
+ loadingScreen,
4550
+ toolbarBg,
4247
4551
  customIndicatorsGetter
4248
4552
  ]);
4249
4553
  React3.useEffect(() => {
@@ -4432,7 +4736,10 @@ var TradingviewUI = React3.forwardRef((props, ref) => {
4432
4736
  "div",
4433
4737
  {
4434
4738
  ref,
4435
- className: ui.cn("oui-relative oui-size-full", props.classNames?.root),
4739
+ className: ui.cn(
4740
+ "oui-tradingview-root oui-relative oui-size-full",
4741
+ props.classNames?.root
4742
+ ),
4436
4743
  children: !tradingViewScriptSrc ? /* @__PURE__ */ jsxRuntime.jsx(NoTradingview, {}) : /* @__PURE__ */ jsxRuntime.jsxs(
4437
4744
  "div",
4438
4745
  {