@orderly.network/hooks 2.10.2-alpha.0 → 3.0.0-beta.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,10 +1,11 @@
1
1
  import { get, WS, mutate as mutate$1 } from '@orderly.network/net';
2
+ import React, { createContext, useContext, useCallback, useState, useEffect, useMemo, useRef, useId, useLayoutEffect } from 'react';
2
3
  import useSWR5__default, { mutate } from 'swr';
3
4
  import * as useSWR5 from 'swr';
4
5
  export { useSWR5 as swr };
5
6
  export { unstable_serialize, default as useSWR, useSWRConfig } from 'swr';
6
- import { TesnetTokenFallback, ArbitrumSepoliaTokenInfo, SolanaDevnetTokenInfo, OrderType, OrderSide, SDKError, TrackerEventName, AccountStatusEnum, AlgoOrderType, AlgoOrderRootType, OrderStatus, ArbitrumSepoliaChainInfo, SolanaDevnetChainInfo, EMPTY_LIST, EMPTY_OBJECT, isNativeTokenChecker, nativeTokenAddress, ChainKey, chainsInfoMap, ARBITRUM_TESTNET_CHAINID, ARBITRUM_MAINNET_CHAINID, ChainNamespace, MaxUint256, DEPOSIT_FEE_RATE, ETHEREUM_MAINNET_CHAINID, LedgerWalletKey, SOLANA_TESTNET_CHAINID, MONAD_TESTNET_CHAINID, ABSTRACT_TESTNET_CHAINID, BSC_TESTNET_CHAINID, SolanaChains, PositionType, DistributionType, TriggerPriceType } from '@orderly.network/types';
7
- import React, { createContext, useContext, useCallback, useState, useEffect, useMemo, useRef, useId, useLayoutEffect } from 'react';
7
+ import { TesnetTokenFallback, ArbitrumSepoliaTokenInfo, SolanaDevnetTokenInfo, OrderType, OrderSide, MarginMode, SDKError, TrackerEventName, AccountStatusEnum, AlgoOrderType, AlgoOrderRootType, OrderStatus, ArbitrumSepoliaChainInfo, SolanaDevnetChainInfo, EMPTY_LIST, EMPTY_OBJECT, isNativeTokenChecker, nativeTokenAddress, ChainKey, chainsInfoMap, ARBITRUM_TESTNET_CHAINID, ARBITRUM_MAINNET_CHAINID, ChainNamespace, MaxUint256, DEPOSIT_FEE_RATE, ETHEREUM_MAINNET_CHAINID, LedgerWalletKey, SOLANA_TESTNET_CHAINID, MONAD_TESTNET_CHAINID, ABSTRACT_TESTNET_CHAINID, BSC_TESTNET_CHAINID, SolanaChains, PositionType, DistributionType, TriggerPriceType } from '@orderly.network/types';
8
+ import { usePluginScope } from '@orderly.network/plugin-core';
8
9
  import { zero, windowGuard, getTimestamp, getGlobalObject, Decimal, timeConvertString, isTestnet, getPrecisionByNumber, getBBOType, camelCaseToUnderscoreCase, commify, todpIfNeed, getTPSLDirection } from '@orderly.network/utils';
9
10
  import useSWRMutation from 'swr/mutation';
10
11
  import useConstant from 'use-constant';
@@ -38,9 +39,9 @@ var __export = (target, all) => {
38
39
  // src/version.ts
39
40
  if (typeof window !== "undefined") {
40
41
  window.__ORDERLY_VERSION__ = window.__ORDERLY_VERSION__ || {};
41
- window.__ORDERLY_VERSION__["@orderly.network/hooks"] = "2.10.2-alpha.0";
42
+ window.__ORDERLY_VERSION__["@orderly.network/hooks"] = "3.0.0-beta.0";
42
43
  }
43
- var version_default = "2.10.2-alpha.0";
44
+ var version_default = "3.0.0-beta.0";
44
45
  var fetcher = (url, init2 = {}, queryOptions) => get(url, init2, queryOptions?.formatter);
45
46
  var noCacheConfig = {
46
47
  dedupingInterval: 0,
@@ -71,21 +72,26 @@ function useConfig(key, defaultValue) {
71
72
  }
72
73
  return configStore;
73
74
  }
74
-
75
- // src/useQuery.ts
75
+ var PLUGIN_ID_HEADER = "X-Orderly-Plugin-Id";
76
76
  var useQuery = (query, options) => {
77
77
  const apiBaseUrl = useConfig("apiBaseUrl");
78
+ const pluginScope = usePluginScope();
78
79
  const { formatter, ...swrOptions } = options || {};
79
80
  if (typeof apiBaseUrl === "undefined") {
80
81
  throw new SDKError("please add OrderlyConfigProvider to your app");
81
82
  }
82
- return useSWR5__default(
83
- query,
84
- (url, init2) => fetcher(url.startsWith("http") ? url : `${apiBaseUrl}${url}`, init2, {
85
- formatter
86
- }),
87
- swrOptions
83
+ const fetcherFn = useCallback(
84
+ (url, init2) => {
85
+ const fullUrl = url.startsWith("http") ? url : `${apiBaseUrl}${url}`;
86
+ const headers = new Headers(init2?.headers);
87
+ if (pluginScope?.pluginId) {
88
+ headers.set(PLUGIN_ID_HEADER, pluginScope.pluginId);
89
+ }
90
+ return fetcher(fullUrl, { ...init2, headers }, { formatter });
91
+ },
92
+ [apiBaseUrl, pluginScope?.pluginId, formatter]
88
93
  );
94
+ return useSWR5__default(query, fetcherFn, swrOptions);
89
95
  };
90
96
  var timestampOffsetPromise = null;
91
97
  var timestampOffsetReady = false;
@@ -1084,12 +1090,12 @@ var findTPSLOrderPriceFromOrder = (order) => {
1084
1090
  sl_order_price
1085
1091
  };
1086
1092
  };
1087
- var findPositionTPSLFromOrders = (orders, symbol) => {
1093
+ var findPositionTPSLFromOrders = (orders, symbol, marginMode = MarginMode.CROSS) => {
1088
1094
  const fullPositionOrder = orders?.find((order) => {
1089
- return order.symbol === symbol && order.algo_type === AlgoOrderRootType.POSITIONAL_TP_SL && (order.root_algo_status === OrderStatus.NEW || order.root_algo_status === OrderStatus.REPLACED || order.root_algo_status === OrderStatus.PARTIAL_FILLED);
1095
+ return order.symbol === symbol && order.algo_type === AlgoOrderRootType.POSITIONAL_TP_SL && order.margin_mode === marginMode && (order.root_algo_status === OrderStatus.NEW || order.root_algo_status === OrderStatus.REPLACED || order.root_algo_status === OrderStatus.PARTIAL_FILLED);
1090
1096
  });
1091
1097
  const partialPositionOrders = orders?.filter((order) => {
1092
- return order.symbol === symbol && order.algo_type === AlgoOrderRootType.TP_SL && (order.root_algo_status === OrderStatus.NEW || order.root_algo_status === OrderStatus.REPLACED || order.root_algo_status === OrderStatus.PARTIAL_FILLED);
1098
+ return order.symbol === symbol && order.margin_mode === marginMode && order.algo_type === AlgoOrderRootType.TP_SL && (order.root_algo_status === OrderStatus.NEW || order.root_algo_status === OrderStatus.REPLACED || order.root_algo_status === OrderStatus.PARTIAL_FILLED);
1093
1099
  }).sort((a, b) => {
1094
1100
  return b.created_time - a.created_time;
1095
1101
  });
@@ -2114,9 +2120,11 @@ var useAppStore = create()(
2114
2120
  totalCollateral: zero,
2115
2121
  totalValue: null,
2116
2122
  freeCollateral: zero,
2123
+ freeCollateralUSDCOnly: zero,
2117
2124
  availableBalance: 0,
2118
2125
  unsettledPnL: 0,
2119
- totalUnrealizedROI: 0
2126
+ totalUnrealizedROI: 0,
2127
+ usdcHolding: 0
2120
2128
  },
2121
2129
  appState: {
2122
2130
  positionsLoading: false,
@@ -2137,9 +2145,11 @@ var useAppStore = create()(
2137
2145
  totalCollateral: zero,
2138
2146
  totalValue: null,
2139
2147
  freeCollateral: zero,
2148
+ freeCollateralUSDCOnly: zero,
2140
2149
  availableBalance: 0,
2141
2150
  unsettledPnL: 0,
2142
- totalUnrealizedROI: 0
2151
+ totalUnrealizedROI: 0,
2152
+ usdcHolding: 0
2143
2153
  };
2144
2154
  }, false);
2145
2155
  },
@@ -3935,25 +3945,27 @@ var OrderbookService = class _OrderbookService {
3935
3945
  };
3936
3946
  var orderBookService = OrderbookService.getInstance();
3937
3947
  var orderbook_service_default = orderBookService;
3948
+ var useMarkPriceStore = create((set, get3) => ({
3949
+ markPrices: {},
3950
+ // orderBook: {},
3951
+ // ask_bid: [],
3952
+ actions: {
3953
+ updateMarkPrice: (markPrice) => {
3954
+ set({
3955
+ markPrices: markPrice
3956
+ });
3957
+ },
3958
+ getMarkPriceBySymbol: (symbol) => {
3959
+ return get3().markPrices[symbol];
3960
+ }
3961
+ }
3962
+ }));
3963
+ var useMarkPriceBySymbol = (symbol) => useMarkPriceStore((state) => state.actions.getMarkPriceBySymbol(symbol));
3964
+ var useMarkPriceActions = () => useMarkPriceStore((state) => state.actions);
3965
+
3966
+ // src/orderly/useMarkPrice.ts
3938
3967
  var useMarkPrice = (symbol) => {
3939
- const ws = useWS();
3940
- const [price, setPrice] = useState(0);
3941
- const symbolRef = useRef(symbol);
3942
- symbolRef.current = symbol;
3943
- useEffect(() => {
3944
- const unsubscribe = ws.subscribe(`${symbol}@markprice`, {
3945
- onMessage: (message) => {
3946
- if (message.symbol !== symbolRef.current) {
3947
- unsubscribe?.();
3948
- return;
3949
- }
3950
- setPrice(message.price);
3951
- }
3952
- });
3953
- return () => {
3954
- unsubscribe?.();
3955
- };
3956
- }, [symbol]);
3968
+ const price = useMarkPriceBySymbol(symbol);
3957
3969
  return { data: price };
3958
3970
  };
3959
3971
  var useIndexPrice = (symbol) => {
@@ -3976,6 +3988,12 @@ var useIndexPrice = (symbol) => {
3976
3988
  };
3977
3989
  });
3978
3990
  };
3991
+
3992
+ // src/orderly/useMarkPricesStream.ts
3993
+ var useMarkPricesStream = () => {
3994
+ const data = useMarkPriceStore((state) => state.markPrices);
3995
+ return { data };
3996
+ };
3979
3997
  var useOpenInterest = (symbol) => {
3980
3998
  const ws = useWS();
3981
3999
  const symbolRef = useRef(symbol);
@@ -3995,29 +4013,6 @@ var useOpenInterest = (symbol) => {
3995
4013
  };
3996
4014
  });
3997
4015
  };
3998
- var useMarkPriceStore = create((set, get3) => ({
3999
- markPrices: {},
4000
- // orderBook: {},
4001
- // ask_bid: [],
4002
- actions: {
4003
- updateMarkPrice: (markPrice) => {
4004
- set({
4005
- markPrices: markPrice
4006
- });
4007
- },
4008
- getMarkPriceBySymbol: (symbol) => {
4009
- return get3().markPrices[symbol];
4010
- }
4011
- }
4012
- }));
4013
- var useMarkPriceBySymbol = (symbol) => useMarkPriceStore((state) => state.actions.getMarkPriceBySymbol(symbol));
4014
- var useMarkPriceActions = () => useMarkPriceStore((state) => state.actions);
4015
-
4016
- // src/orderly/useMarkPricesStream.ts
4017
- var useMarkPricesStream = () => {
4018
- const data = useMarkPriceStore((state) => state.markPrices);
4019
- return { data };
4020
- };
4021
4016
 
4022
4017
  // src/orderly/useTickerStream.ts
4023
4018
  var useTickerStream = (symbol) => {
@@ -4710,6 +4705,94 @@ var useLeverage = () => {
4710
4705
  maxLeverage: memoizedMaxLeverage
4711
4706
  };
4712
4707
  };
4708
+ var buildKey = (symbol, marginMode) => `${symbol}_${marginMode ?? MarginMode.CROSS}`;
4709
+ var useSymbolLeverageMap = () => {
4710
+ const { data, error, isLoading, mutate: mutate6 } = usePrivateQuery("/v1/client/leverages", {
4711
+ revalidateOnFocus: false,
4712
+ revalidateOnReconnect: false,
4713
+ errorRetryCount: 1
4714
+ });
4715
+ const leverages = useMemo(() => {
4716
+ if (!data || !Array.isArray(data)) return {};
4717
+ const map = {};
4718
+ for (const item of data) {
4719
+ const key = buildKey(item.symbol, item.margin_mode);
4720
+ map[key] = item.leverage;
4721
+ }
4722
+ return map;
4723
+ }, [data]);
4724
+ const getSymbolLeverage = (symbol, marginMode) => {
4725
+ if (!symbol) return void 0;
4726
+ const key = buildKey(symbol, marginMode);
4727
+ return leverages[key];
4728
+ };
4729
+ return {
4730
+ leverages,
4731
+ getSymbolLeverage,
4732
+ isLoading,
4733
+ error,
4734
+ refresh: mutate6
4735
+ };
4736
+ };
4737
+ var useMarginModes = () => {
4738
+ const { data, error, isLoading, mutate: mutate6 } = usePrivateQuery("/v1/client/margin_modes", {
4739
+ revalidateOnFocus: false,
4740
+ revalidateOnMount: true
4741
+ });
4742
+ const [setMarginModeInternal, { isMutating }] = useMutation(
4743
+ "/v1/client/margin_mode",
4744
+ "POST"
4745
+ );
4746
+ const marginModes = useMemo(() => {
4747
+ if (!data || !Array.isArray(data)) return {};
4748
+ const map = {};
4749
+ for (const item of data) {
4750
+ map[item.symbol] = item.default_margin_mode;
4751
+ }
4752
+ return map;
4753
+ }, [data]);
4754
+ const setMarginMode = (payload) => setMarginModeInternal(payload);
4755
+ const updateMarginMode = useCallback(
4756
+ async (payload) => {
4757
+ const result = await setMarginMode(payload);
4758
+ if (result.success) {
4759
+ await mutate6();
4760
+ return result;
4761
+ }
4762
+ throw new Error(result.message ?? "Failed to update margin mode");
4763
+ },
4764
+ [setMarginMode, mutate6]
4765
+ );
4766
+ return {
4767
+ marginModes,
4768
+ isLoading,
4769
+ error,
4770
+ refresh: mutate6,
4771
+ setMarginMode,
4772
+ updateMarginMode,
4773
+ isMutating
4774
+ };
4775
+ };
4776
+ var useMarginModeBySymbol = (symbol, fallback = MarginMode.CROSS) => {
4777
+ const { marginModes, isLoading, error, refresh, updateMarginMode } = useMarginModes();
4778
+ const marginMode = fallback === null ? marginModes[symbol] : marginModes[symbol] ?? fallback;
4779
+ const update = useCallback(
4780
+ async (mode) => {
4781
+ return updateMarginMode({
4782
+ symbol_list: [symbol],
4783
+ default_margin_mode: mode
4784
+ });
4785
+ },
4786
+ [symbol, updateMarginMode]
4787
+ );
4788
+ return {
4789
+ marginMode,
4790
+ isLoading,
4791
+ error,
4792
+ refresh,
4793
+ update
4794
+ };
4795
+ };
4713
4796
 
4714
4797
  // src/orderly/useOdosQuote.ts
4715
4798
  var useOdosQuote = () => {
@@ -5067,10 +5150,13 @@ var CalculatorService = class {
5067
5150
  // this.pendingCalc = [];
5068
5151
  // }
5069
5152
  async handleCalcQueue(context) {
5070
- const first = this.calcQueue.shift();
5071
- if (first) {
5153
+ const batchCollector = /* @__PURE__ */ new Map();
5154
+ let currentContext = context;
5155
+ while (this.calcQueue.length > 0) {
5156
+ const first = this.calcQueue.shift();
5157
+ if (!first) break;
5072
5158
  const { scope, data, options } = first;
5073
- const ctx = context || CalculatorContext.create(scope, data);
5159
+ const ctx = currentContext || CalculatorContext.create(scope, data);
5074
5160
  const calculators = this.calculators.get(scope);
5075
5161
  if (Array.isArray(calculators) && calculators.length) {
5076
5162
  try {
@@ -5078,13 +5164,46 @@ var CalculatorService = class {
5078
5164
  } catch (e) {
5079
5165
  }
5080
5166
  if (!options?.skipUpdate) {
5081
- this.scheduler.update(scope, calculators, ctx.outputToValue());
5167
+ this.collectUpdates(
5168
+ batchCollector,
5169
+ scope,
5170
+ calculators,
5171
+ ctx.outputToValue()
5172
+ );
5082
5173
  }
5083
5174
  }
5084
- if (this.calcQueue.length) {
5085
- this.handleCalcQueue(ctx);
5175
+ currentContext = ctx;
5176
+ }
5177
+ await this.commitBatchUpdates(batchCollector);
5178
+ this.ctx = currentContext;
5179
+ }
5180
+ collectUpdates(collector, scope, calculators, data) {
5181
+ if (!collector.has(scope)) {
5182
+ collector.set(scope, /* @__PURE__ */ new Map());
5183
+ }
5184
+ const scopeCollector = collector.get(scope);
5185
+ for (const calculator of calculators) {
5186
+ const item = data[calculator.name];
5187
+ if (item !== void 0 && item !== null) {
5188
+ scopeCollector.set(calculator.name, item);
5189
+ }
5190
+ }
5191
+ }
5192
+ async commitBatchUpdates(collector) {
5193
+ if (collector.size === 0) return;
5194
+ for (const [scope, updateMap] of collector.entries()) {
5195
+ const calculators = this.calculators.get(scope);
5196
+ if (!Array.isArray(calculators)) continue;
5197
+ const batchData = {};
5198
+ for (const [calculatorName, data] of updateMap.entries()) {
5199
+ batchData[calculatorName] = data;
5200
+ }
5201
+ try {
5202
+ this.scheduler.update(scope, calculators, batchData);
5203
+ } catch (e) {
5086
5204
  }
5087
5205
  }
5206
+ collector.clear();
5088
5207
  }
5089
5208
  stop() {
5090
5209
  this.calcQueue = [];
@@ -5115,6 +5234,18 @@ var cancelIdleCallbackPolyfill = (id) => {
5115
5234
  var safeRequestIdleCallback = typeof window !== "undefined" && window.requestIdleCallback ? window.requestIdleCallback.bind(window) : requestIdleCallbackPolyfill;
5116
5235
  typeof window !== "undefined" && window.cancelIdleCallback ? window.cancelIdleCallback.bind(window) : cancelIdleCallbackPolyfill;
5117
5236
  var ShardingScheduler = class {
5237
+ constructor() {
5238
+ /**
5239
+ * Maximum continuous execution time per frame (in milliseconds)
5240
+ * Prevents blocking the main thread for too long
5241
+ */
5242
+ this.MAX_CONTINUOUS_MS = 5;
5243
+ /**
5244
+ * Minimum remaining idle time threshold (in milliseconds)
5245
+ * Ensures the browser has enough idle time for other tasks
5246
+ */
5247
+ this.MIN_IDLE_REMAINING = 2;
5248
+ }
5118
5249
  // run(calculators: Calculator[]) {}
5119
5250
  calc(scope, calculators, data, ctx) {
5120
5251
  return new Promise((resolve, reject) => {
@@ -5155,28 +5286,27 @@ var ShardingScheduler = class {
5155
5286
  computation(data, processor, onComplete) {
5156
5287
  let index = 0;
5157
5288
  const results = [];
5158
- const estimatedShardSize = Math.min(data.length, 2);
5159
- function processNextShard(deadline) {
5160
- let shardSize = estimatedShardSize;
5161
- while (index + shardSize <= data.length && deadline.timeRemaining() > 0) {
5162
- const shard = data.slice(index, index + shardSize);
5163
- const result = processor(shard);
5164
- results.push(result);
5165
- index += shardSize;
5166
- if (deadline.timeRemaining() < 1) {
5167
- shardSize = Math.max(1, Math.floor(shardSize / 2));
5168
- } else {
5169
- shardSize = Math.min(data.length - index, shardSize * 2);
5289
+ const MAX_CONTINUOUS_MS = this.MAX_CONTINUOUS_MS;
5290
+ const MIN_IDLE_REMAINING = this.MIN_IDLE_REMAINING;
5291
+ const processNextShard = (deadline) => {
5292
+ const frameStart = performance.now();
5293
+ while (index < data.length) {
5294
+ const elapsed = performance.now() - frameStart;
5295
+ const remaining = deadline.timeRemaining();
5296
+ if (elapsed > MAX_CONTINUOUS_MS || remaining < MIN_IDLE_REMAINING) {
5297
+ safeRequestIdleCallback(processNextShard, {
5298
+ timeout: 1e3
5299
+ });
5300
+ return;
5170
5301
  }
5302
+ const result = processor([data[index]]);
5303
+ if (result && result.length > 0) {
5304
+ results.push(...result);
5305
+ }
5306
+ index++;
5171
5307
  }
5172
- if (index < data.length) {
5173
- safeRequestIdleCallback(processNextShard, {
5174
- timeout: 1e3
5175
- });
5176
- } else {
5177
- onComplete(results.flat());
5178
- }
5179
- }
5308
+ onComplete(results);
5309
+ };
5180
5310
  safeRequestIdleCallback(processNextShard, {
5181
5311
  timeout: 1e3
5182
5312
  });
@@ -5389,13 +5519,14 @@ var PositionCalculator = class extends BaseCalculator {
5389
5519
  const unsettlementPnL = unsettlementPnL_total.toNumber();
5390
5520
  let totalUnrealizedROI = 0, totalUnrealizedROI_index = 0;
5391
5521
  if (portfolio) {
5392
- const { totalValue, totalCollateral } = portfolio;
5522
+ const { totalValue, totalCollateral: crossMarginCollateral } = portfolio;
5393
5523
  rows = rows.map((item) => {
5394
5524
  const info = symbolsInfo[item.symbol];
5525
+ const totalCollateral = item.margin_mode === MarginMode.ISOLATED ? new Decimal(item.margin ?? 0).add(item.unsettlement_pnl ?? 0).toNumber() : crossMarginCollateral.toNumber();
5395
5526
  const est_liq_price = positions.liqPrice({
5396
5527
  symbol: item.symbol,
5397
5528
  markPrice: item.mark_price,
5398
- totalCollateral: totalCollateral.toNumber(),
5529
+ totalCollateral,
5399
5530
  positionQty: item.position_qty,
5400
5531
  positions: rows,
5401
5532
  MMR: item.mmr,
@@ -5554,7 +5685,9 @@ var PortfolioCalculator = class extends BaseCalculator {
5554
5685
  return {
5555
5686
  ...item,
5556
5687
  holding: data.holding[item.token].holding,
5557
- frozen: data.holding[item.token].frozen
5688
+ frozen: data.holding[item.token].frozen,
5689
+ isolatedMargin: data.holding[item.token].isolatedMargin,
5690
+ isolatedOrderFrozen: data.holding[item.token].isolatedOrderFrozen
5558
5691
  };
5559
5692
  }
5560
5693
  return item;
@@ -5590,7 +5723,14 @@ var PortfolioCalculator = class extends BaseCalculator {
5590
5723
  if (!holding || !positions3 || !Array.isArray(positions3.rows) || !markPrices || !indexPrices || !accountInfo) {
5591
5724
  return null;
5592
5725
  }
5593
- const unsettledPnL = pathOr(0, ["total_unsettled_pnl"])(positions3);
5726
+ const totallCrossUnsettledPnL = positions3.rows.reduce(
5727
+ (sum, pos) => pos.margin_mode === MarginMode.ISOLATED ? sum : sum + (pos.unsettled_pnl ?? 0),
5728
+ 0
5729
+ );
5730
+ const totalUnsettlementPnL = positions3.rows.reduce(
5731
+ (sum, pos) => sum + (pos.unsettled_pnl ?? 0),
5732
+ 0
5733
+ );
5594
5734
  const unrealizedPnL = pathOr(0, ["total_unreal_pnl"])(positions3);
5595
5735
  const [USDC_holding, nonUSDC] = parseHolding(
5596
5736
  holding,
@@ -5601,28 +5741,51 @@ var PortfolioCalculator = class extends BaseCalculator {
5601
5741
  const totalCollateral = account.totalCollateral({
5602
5742
  USDCHolding: USDC_holding,
5603
5743
  nonUSDCHolding: nonUSDC,
5604
- unsettlementPnL: unsettledPnL
5744
+ unsettlementPnL: totallCrossUnsettledPnL,
5745
+ usdcBalancePendingShortQty: usdc?.pending_short ?? 0,
5746
+ usdcBalanceIsolatedOrderFrozen: usdc?.isolated_order_frozen ?? 0
5605
5747
  });
5748
+ const sumIsolatedMargin = positions3.rows.reduce((acc, curr) => {
5749
+ if (curr.margin_mode !== MarginMode.ISOLATED) {
5750
+ return acc;
5751
+ }
5752
+ return acc.add(curr.margin ?? 0);
5753
+ }, zero);
5606
5754
  const totalValue = account.totalValue({
5607
- totalUnsettlementPnL: unsettledPnL,
5755
+ totalUnsettlementPnL,
5608
5756
  USDCHolding: USDC_holding,
5609
- nonUSDCHolding: nonUSDC
5757
+ nonUSDCHolding: nonUSDC,
5758
+ totalIsolatedPositionMargin: sumIsolatedMargin.toNumber()
5610
5759
  });
5611
5760
  const totalUnrealizedROI = account.totalUnrealizedROI({
5612
5761
  totalUnrealizedPnL: unrealizedPnL,
5613
5762
  totalValue: totalValue.toNumber()
5614
5763
  });
5764
+ const maxLeverageBySymbol = positions3.rows.reduce(
5765
+ (acc, position) => {
5766
+ if (position.margin_mode !== MarginMode.ISOLATED && position.leverage && !acc[position.symbol]) {
5767
+ acc[position.symbol] = position.leverage;
5768
+ }
5769
+ return acc;
5770
+ },
5771
+ {}
5772
+ );
5615
5773
  const totalInitialMarginWithOrders = account.totalInitialMarginWithQty({
5616
5774
  positions: positions3.rows,
5775
+ orders: [],
5617
5776
  markPrices,
5618
5777
  IMR_Factors: accountInfo.imr_factor,
5619
- maxLeverage: accountInfo.max_leverage,
5778
+ maxLeverageBySymbol,
5620
5779
  symbolInfo: createGetter({ ...symbolsInfo })
5621
5780
  });
5622
5781
  const freeCollateral = account.freeCollateral({
5623
5782
  totalCollateral,
5624
5783
  totalInitialMarginWithOrders
5625
5784
  });
5785
+ const freeCollateralUSDCOnly = account.freeCollateralUSDCOnly({
5786
+ freeCollateral,
5787
+ nonUSDCHolding: nonUSDC
5788
+ });
5626
5789
  const availableBalance = account.availableBalance({
5627
5790
  USDCHolding: usdc?.holding ?? 0,
5628
5791
  unsettlementPnL: positions3.total_unsettled_pnl ?? 0
@@ -5633,8 +5796,10 @@ var PortfolioCalculator = class extends BaseCalculator {
5633
5796
  totalUnrealizedROI,
5634
5797
  freeCollateral,
5635
5798
  availableBalance,
5636
- unsettledPnL,
5637
- holding
5799
+ unsettledPnL: totalUnsettlementPnL,
5800
+ holding,
5801
+ usdcHolding: USDC_holding,
5802
+ freeCollateralUSDCOnly
5638
5803
  };
5639
5804
  }
5640
5805
  update(data, scope) {
@@ -5643,10 +5808,12 @@ var PortfolioCalculator = class extends BaseCalculator {
5643
5808
  totalCollateral: data.totalCollateral,
5644
5809
  totalValue: data.totalValue,
5645
5810
  freeCollateral: data.freeCollateral,
5811
+ freeCollateralUSDCOnly: data.freeCollateralUSDCOnly,
5646
5812
  availableBalance: data.availableBalance,
5647
5813
  totalUnrealizedROI: data.totalUnrealizedROI,
5648
5814
  unsettledPnL: data.unsettledPnL,
5649
- holding: Array.isArray(data.holding) ? data.holding : []
5815
+ holding: Array.isArray(data.holding) ? data.holding : [],
5816
+ usdcHolding: typeof data.usdcHolding === "number" ? data.usdcHolding : data.usdcHolding.toNumber()
5650
5817
  });
5651
5818
  }
5652
5819
  }
@@ -5777,7 +5944,7 @@ var usePositionStream = (symbol = "all", options) => {
5777
5944
  }
5778
5945
  if (Array.isArray(tpslOrders) && tpslOrders.length) {
5779
5946
  rows = rows.map((item) => {
5780
- const { fullPositionOrder, partialPositionOrders } = findPositionTPSLFromOrders(tpslOrders, item.symbol);
5947
+ const { fullPositionOrder, partialPositionOrders } = findPositionTPSLFromOrders(tpslOrders, item.symbol, item.margin_mode);
5781
5948
  const full_tp_sl = fullPositionOrder ? findTPSLFromOrder(fullPositionOrder) : void 0;
5782
5949
  const partialPossitionOrder = partialPositionOrders && partialPositionOrders.length ? partialPositionOrders[0] : void 0;
5783
5950
  const partial_tp_sl = partialPossitionOrder ? findTPSLFromOrder(partialPossitionOrder) : void 0;
@@ -5909,15 +6076,21 @@ var useOrderStream = (params, options) => {
5909
6076
  }
5910
6077
  };
5911
6078
  }, [normalOrderKeyFn, options?.keeplive]);
5912
- const normalOrdersResponse = usePrivateInfiniteQuery(normalOrderKeyFn, {
5913
- initialSize: 1,
5914
- formatter: (data) => data,
5915
- revalidateOnFocus: false
5916
- });
5917
- const algoOrdersResponse = usePrivateInfiniteQuery(algoOrderKeyFn, {
5918
- formatter: (data) => data,
5919
- revalidateOnFocus: false
5920
- });
6079
+ const normalOrdersResponse = usePrivateInfiniteQuery(
6080
+ normalOrderKeyFn,
6081
+ {
6082
+ initialSize: 1,
6083
+ formatter: (data) => data,
6084
+ revalidateOnFocus: false
6085
+ }
6086
+ );
6087
+ const algoOrdersResponse = usePrivateInfiniteQuery(
6088
+ algoOrderKeyFn,
6089
+ {
6090
+ formatter: (data) => data,
6091
+ revalidateOnFocus: false
6092
+ }
6093
+ );
5921
6094
  const flattenOrders = useMemo(() => {
5922
6095
  if (!normalOrdersResponse.data || !algoOrdersResponse.data && !sourceTypeAll) {
5923
6096
  return null;
@@ -6035,7 +6208,9 @@ var useOrderStream = (params, options) => {
6035
6208
  // trailing stop order fields
6036
6209
  activated_price: order.activated_price,
6037
6210
  callback_value: order.callback_value,
6038
- callback_rate: order.callback_rate
6211
+ callback_rate: order.callback_rate,
6212
+ // Include margin_mode if present
6213
+ ...order.margin_mode && { margin_mode: order.margin_mode }
6039
6214
  });
6040
6215
  default:
6041
6216
  return doUpdateOrder({ ...order, order_id: orderId });
@@ -6230,7 +6405,14 @@ function formatPortfolio(inputs) {
6230
6405
  if (!holding || !positions3 || !Array.isArray(positions3.rows) || !markPrices || !indexPrices || !accountInfo || symbolsInfo?.isNil) {
6231
6406
  return null;
6232
6407
  }
6233
- const unsettledPnL = pathOr(0, ["total_unsettled_pnl"])(positions3);
6408
+ const totallCrossUnsettledPnL = positions3.rows.reduce(
6409
+ (sum, pos) => pos.margin_mode === MarginMode.ISOLATED ? sum : sum + (pos.unsettled_pnl ?? 0),
6410
+ 0
6411
+ );
6412
+ const totalUnsettlementPnL = positions3.rows.reduce(
6413
+ (sum, pos) => sum + (pos.unsettled_pnl ?? 0),
6414
+ 0
6415
+ );
6234
6416
  const unrealizedPnL = pathOr(0, ["total_unreal_pnl"])(positions3);
6235
6417
  const [USDC_holding, nonUSDC] = parseHolding(
6236
6418
  holding,
@@ -6241,29 +6423,51 @@ function formatPortfolio(inputs) {
6241
6423
  const totalCollateral = account.totalCollateral({
6242
6424
  USDCHolding: USDC_holding,
6243
6425
  nonUSDCHolding: nonUSDC,
6244
- unsettlementPnL: unsettledPnL
6426
+ unsettlementPnL: totallCrossUnsettledPnL,
6427
+ usdcBalancePendingShortQty: usdc?.pending_short ?? 0,
6428
+ usdcBalanceIsolatedOrderFrozen: usdc?.isolated_order_frozen ?? 0
6245
6429
  });
6430
+ const sumIsolatedMargin = positions3.rows.reduce((acc, curr) => {
6431
+ if (curr.margin_mode !== MarginMode.ISOLATED) {
6432
+ return acc;
6433
+ }
6434
+ return acc.add(curr.margin ?? 0);
6435
+ }, zero);
6246
6436
  const totalValue = account.totalValue({
6247
- totalUnsettlementPnL: unsettledPnL,
6437
+ totalUnsettlementPnL,
6248
6438
  USDCHolding: USDC_holding,
6249
- nonUSDCHolding: nonUSDC
6439
+ nonUSDCHolding: nonUSDC,
6440
+ totalIsolatedPositionMargin: sumIsolatedMargin.toNumber()
6250
6441
  });
6251
6442
  const totalUnrealizedROI = account.totalUnrealizedROI({
6252
6443
  totalUnrealizedPnL: unrealizedPnL,
6253
6444
  totalValue: totalValue.toNumber()
6254
6445
  });
6446
+ const maxLeverageBySymbol = positions3.rows.reduce(
6447
+ (acc, position) => {
6448
+ if (position.margin_mode !== MarginMode.ISOLATED && position.leverage && !acc[position.symbol]) {
6449
+ acc[position.symbol] = position.leverage;
6450
+ }
6451
+ return acc;
6452
+ },
6453
+ {}
6454
+ );
6255
6455
  const totalInitialMarginWithOrders = account.totalInitialMarginWithQty({
6256
6456
  positions: positions3.rows,
6457
+ orders: [],
6257
6458
  markPrices,
6258
6459
  IMR_Factors: accountInfo.imr_factor,
6259
- // Not used
6260
- maxLeverage: accountInfo.max_leverage,
6261
- symbolInfo: symbolsInfo
6460
+ maxLeverageBySymbol,
6461
+ symbolInfo: createGetter({ ...symbolsInfo })
6262
6462
  });
6263
6463
  const freeCollateral = account.freeCollateral({
6264
6464
  totalCollateral,
6265
6465
  totalInitialMarginWithOrders
6266
6466
  });
6467
+ const freeCollateralUSDCOnly = account.freeCollateralUSDCOnly({
6468
+ freeCollateral,
6469
+ nonUSDCHolding: nonUSDC
6470
+ });
6267
6471
  const availableBalance = account.availableBalance({
6268
6472
  USDCHolding: usdc?.holding ?? 0,
6269
6473
  unsettlementPnL: positions3.total_unsettled_pnl ?? 0
@@ -6274,8 +6478,9 @@ function formatPortfolio(inputs) {
6274
6478
  totalUnrealizedROI,
6275
6479
  freeCollateral,
6276
6480
  availableBalance,
6277
- unsettledPnL,
6278
- holding
6481
+ unsettledPnL: totalUnsettlementPnL,
6482
+ holding,
6483
+ freeCollateralUSDCOnly
6279
6484
  };
6280
6485
  }
6281
6486
  function formatPositions(data, accountInfo, symbolsInfo, fundingRates) {
@@ -6795,7 +7000,9 @@ var useSubAccountAlgoOrderStream = (params, options) => {
6795
7000
  // trailing stop order fields
6796
7001
  activated_price: order.activated_price,
6797
7002
  callback_value: order.callback_value,
6798
- callback_rate: order.callback_rate
7003
+ callback_rate: order.callback_rate,
7004
+ // include margin_mode if present (align with useOrderStream)
7005
+ ...order.margin_mode && { margin_mode: order.margin_mode }
6799
7006
  });
6800
7007
  default:
6801
7008
  return doUpdateOrder({ ...order, order_id: orderId });
@@ -6982,120 +7189,122 @@ var useCollateral = (options = { dp: 6 }) => {
6982
7189
  totalCollateral,
6983
7190
  totalValue,
6984
7191
  freeCollateral,
7192
+ freeCollateralUSDCOnly,
6985
7193
  availableBalance,
6986
7194
  unsettledPnL,
6987
- holding
7195
+ holding,
7196
+ usdcHolding
6988
7197
  } = useAppStore((state) => state.portfolio);
6989
7198
  const accountInfo = useAppStore((state) => state.accountInfo);
6990
7199
  return {
6991
7200
  totalCollateral: totalCollateral.toDecimalPlaces(dp).toNumber(),
6992
7201
  freeCollateral: freeCollateral.toDecimalPlaces(dp).toNumber(),
7202
+ freeCollateralUSDCOnly: freeCollateralUSDCOnly.toDecimalPlaces(dp).toNumber(),
6993
7203
  totalValue: totalValue?.toDecimalPlaces(dp).toNumber() ?? null,
6994
7204
  availableBalance,
6995
7205
  unsettledPnL,
6996
7206
  accountInfo,
6997
- holding
7207
+ holding,
7208
+ usdcHolding
6998
7209
  // @hidden
6999
7210
  // positions: positionsPath(positions),
7000
7211
  };
7001
7212
  };
7002
- var useLeverageBySymbol = (symbol) => {
7003
- const { state } = useAccount();
7004
- const ws = useWS();
7005
- const { data } = usePrivateQuery(
7006
- symbol ? `/v1/client/leverage?symbol=${symbol}` : null,
7007
- {
7008
- revalidateOnFocus: false
7009
- }
7010
- );
7011
- useEffect(() => {
7012
- if (!state.accountId || !symbol) return;
7013
- const unsubscribe = ws.privateSubscribe("account", {
7014
- onMessage: (data2) => {
7015
- const res = data2?.accountDetail?.symbolLeverage || {};
7016
- if (res.symbol === symbol) {
7017
- const key = [`/v1/client/leverage?symbol=${symbol}`, state.accountId];
7018
- mutate(
7019
- key,
7020
- (prevData) => {
7021
- return {
7022
- ...prevData,
7023
- leverage: res.leverage
7024
- };
7025
- },
7026
- {
7027
- revalidate: false
7028
- }
7029
- );
7030
- }
7031
- }
7032
- });
7033
- return () => unsubscribe?.();
7034
- }, [symbol, state.accountId]);
7035
- return data?.leverage;
7213
+
7214
+ // src/orderly/useLeverageBySymbol.ts
7215
+ var useLeverageBySymbol = (symbol, marginMode) => {
7216
+ const { getSymbolLeverage } = useSymbolLeverageMap();
7217
+ return getSymbolLeverage(symbol, marginMode);
7036
7218
  };
7037
7219
 
7038
7220
  // src/orderly/useMaxQty.ts
7039
- var useMaxQty = (symbol, side, reduceOnly = false) => {
7221
+ function useMaxQty(symbol, side, reduceOnlyOrOptions, marginMode) {
7222
+ const reduceOnly = typeof reduceOnlyOrOptions === "object" && reduceOnlyOrOptions !== null ? reduceOnlyOrOptions.reduceOnly ?? false : reduceOnlyOrOptions ?? false;
7223
+ const finalMarginMode = typeof reduceOnlyOrOptions === "object" && reduceOnlyOrOptions !== null ? reduceOnlyOrOptions.marginMode ?? MarginMode.CROSS : marginMode ?? MarginMode.CROSS;
7040
7224
  const positions3 = usePositions();
7041
7225
  const accountInfo = useAccountInfo();
7042
7226
  const symbolInfo = useSymbolsInfo();
7043
- const { totalCollateral } = useCollateral();
7227
+ const { totalCollateral, freeCollateralUSDCOnly } = useCollateral();
7044
7228
  const { data: markPrices } = useMarkPricesStream();
7045
- const symbolLeverage = useLeverageBySymbol(symbol);
7229
+ const symbolLeverage = useLeverageBySymbol(symbol, finalMarginMode);
7046
7230
  const maxQty = useMemo(() => {
7047
7231
  if (!symbol) return 0;
7048
- const positionQty = account.getQtyFromPositions(
7049
- positions3 === null ? [] : positions3,
7050
- symbol
7232
+ if (!markPrices || !markPrices[symbol] || !accountInfo || !positions3) {
7233
+ return 0;
7234
+ }
7235
+ const positionsArray = (positions3 === null ? [] : positions3).filter(
7236
+ (position) => position.margin_mode === finalMarginMode
7051
7237
  );
7238
+ const positionQty = account.getQtyFromPositions(positionsArray, symbol);
7052
7239
  if (reduceOnly) {
7053
- if (positionQty > 0) {
7054
- if (side === OrderSide.BUY) {
7055
- return 0;
7056
- } else {
7057
- return Math.abs(positionQty);
7058
- }
7059
- }
7060
- if (positionQty < 0) {
7061
- if (side === OrderSide.BUY) {
7062
- return Math.abs(positionQty);
7063
- } else {
7064
- return 0;
7065
- }
7066
- }
7067
- return 0;
7240
+ if (positionQty === 0) return 0;
7241
+ return side === OrderSide.BUY ? positionQty < 0 ? Math.abs(positionQty) : 0 : positionQty > 0 ? positionQty : 0;
7068
7242
  }
7069
- if (!markPrices || !markPrices[symbol] || !accountInfo || !positions3)
7070
- return 0;
7071
7243
  const getSymbolInfo = symbolInfo[symbol];
7072
- const currentSymbolPosition = positions3.find(
7073
- (item) => item.symbol === symbol
7074
- );
7244
+ let currentSymbolPosition;
7245
+ const otherPositions = [];
7246
+ for (const position of positionsArray) {
7247
+ if (position.symbol === symbol) {
7248
+ currentSymbolPosition = position;
7249
+ } else {
7250
+ otherPositions.push(position);
7251
+ }
7252
+ }
7253
+ const markPrice = markPrices[symbol];
7254
+ const baseIMR = getSymbolInfo("base_imr") ?? 0;
7255
+ const IMR_Factor = accountInfo.imr_factor[symbol] ?? 0;
7256
+ const leverage = symbolLeverage || currentSymbolPosition?.leverage || 1;
7075
7257
  const buyOrdersQty = currentSymbolPosition?.pending_long_qty ?? 0;
7076
7258
  const sellOrdersQty = currentSymbolPosition?.pending_short_qty ?? 0;
7077
- const otherPositions = !Array.isArray(positions3) ? [] : positions3.filter((item) => item.symbol !== symbol);
7259
+ if (finalMarginMode === MarginMode.ISOLATED) {
7260
+ const availableBalance = freeCollateralUSDCOnly;
7261
+ const pendingLongOrders = buyOrdersQty > 0 ? [{ referencePrice: markPrice, quantity: buyOrdersQty }] : [];
7262
+ const pendingSellOrders = sellOrdersQty > 0 ? [{ referencePrice: markPrice, quantity: sellOrdersQty }] : [];
7263
+ const markPriceDecimal = new Decimal(markPrice);
7264
+ const leverageDecimal = new Decimal(leverage);
7265
+ const isoOrderFrozenLong = buyOrdersQty > 0 ? markPriceDecimal.mul(buyOrdersQty).div(leverageDecimal).toNumber() : 0;
7266
+ const isoOrderFrozenShort = sellOrdersQty > 0 ? markPriceDecimal.mul(sellOrdersQty).div(leverageDecimal).toNumber() : 0;
7267
+ const symbolMaxNotional = accountInfo.max_notional?.[symbol] ?? positions.maxPositionNotional({
7268
+ leverage,
7269
+ IMRFactor: IMR_Factor
7270
+ });
7271
+ const currentOrderReferencePrice = typeof reduceOnlyOrOptions === "object" && reduceOnlyOrOptions !== null && typeof reduceOnlyOrOptions.currentOrderReferencePrice === "number" && reduceOnlyOrOptions.currentOrderReferencePrice > 0 ? reduceOnlyOrOptions.currentOrderReferencePrice : markPrice;
7272
+ return account.maxQtyForIsolatedMargin({
7273
+ symbol,
7274
+ orderSide: side,
7275
+ currentOrderReferencePrice,
7276
+ availableBalance,
7277
+ leverage,
7278
+ baseIMR,
7279
+ IMR_Factor,
7280
+ markPrice,
7281
+ positionQty,
7282
+ pendingLongOrders,
7283
+ pendingSellOrders,
7284
+ isoOrderFrozenLong,
7285
+ isoOrderFrozenShort,
7286
+ symbolMaxNotional
7287
+ });
7288
+ }
7078
7289
  const otherIMs = account.otherIMs({
7079
7290
  positions: otherPositions,
7080
7291
  symbolInfo,
7081
7292
  markPrices,
7082
- IMR_Factors: accountInfo.imr_factor,
7083
- // Not used
7084
- maxLeverage: accountInfo.max_leverage
7293
+ IMR_Factors: accountInfo.imr_factor
7085
7294
  });
7086
7295
  return account.maxQty(side, {
7087
- markPrice: markPrices[symbol],
7296
+ markPrice,
7088
7297
  symbol,
7089
7298
  baseMaxQty: getSymbolInfo("base_max"),
7090
7299
  totalCollateral,
7091
- maxLeverage: symbolLeverage || currentSymbolPosition?.leverage || 1,
7300
+ maxLeverage: leverage,
7092
7301
  takerFeeRate: accountInfo.futures_taker_fee_rate,
7093
- baseIMR: getSymbolInfo("base_imr"),
7302
+ baseIMR,
7094
7303
  otherIMs,
7095
7304
  positionQty,
7096
7305
  buyOrdersQty,
7097
7306
  sellOrdersQty,
7098
- IMR_Factor: accountInfo.imr_factor[symbol]
7307
+ IMR_Factor
7099
7308
  });
7100
7309
  }, [
7101
7310
  symbol,
@@ -7105,10 +7314,12 @@ var useMaxQty = (symbol, side, reduceOnly = false) => {
7105
7314
  accountInfo,
7106
7315
  symbolInfo,
7107
7316
  side,
7108
- totalCollateral
7317
+ totalCollateral,
7318
+ finalMarginMode,
7319
+ symbolLeverage
7109
7320
  ]);
7110
7321
  return Math.max(maxQty, 0);
7111
- };
7322
+ }
7112
7323
  var useMarginRatio = () => {
7113
7324
  const positions3 = usePositionStore((state2) => state2.positions.all);
7114
7325
  const { rows, notional } = positions3;
@@ -8414,56 +8625,94 @@ var usePrivateDataObserver = (options) => {
8414
8625
  }, [state.accountId, subOrder]);
8415
8626
  useEffect(() => {
8416
8627
  if (!state.accountId) return;
8417
- const key = ["/v1/positions", state.accountId];
8628
+ const positionsKey = ["/v1/positions", state.accountId];
8629
+ const leveragesKey = ["/v1/client/leverages", state.accountId];
8418
8630
  const unsubscribe = ws.privateSubscribe("account", {
8419
8631
  onMessage: (data) => {
8420
- const { symbol, leverage } = data?.accountDetail?.symbolLeverage || {};
8421
- if (symbol && leverage) {
8422
- mutate(
8423
- key,
8424
- (prevPositions) => {
8425
- if (prevPositions?.rows?.length) {
8426
- return {
8427
- ...prevPositions,
8428
- rows: prevPositions.rows.map((row) => {
8429
- return row.symbol === symbol ? { ...row, leverage } : row;
8430
- })
8431
- };
8432
- }
8433
- return prevPositions;
8434
- },
8435
- {
8436
- revalidate: false
8437
- }
8438
- );
8632
+ const { symbol, leverage, marginMode } = data?.accountDetail?.symbolLeverage || {};
8633
+ if (!symbol || leverage === void 0) {
8634
+ return;
8439
8635
  }
8440
- }
8441
- });
8442
- return () => unsubscribe?.();
8443
- }, [state.accountId]);
8444
- useEffect(() => {
8445
- if (!state.accountId) {
8446
- return;
8447
- }
8448
- const key = ["/v1/positions", state.accountId];
8449
- const unsubscribe = ws.privateSubscribe("position", {
8450
- onMessage: (data) => {
8451
- const { positions: nextPositions } = data;
8452
8636
  mutate(
8453
- key,
8637
+ positionsKey,
8454
8638
  (prevPositions) => {
8455
- if (!!prevPositions) {
8456
- const newPositions = {
8639
+ if (prevPositions?.rows?.length) {
8640
+ return {
8457
8641
  ...prevPositions,
8458
8642
  rows: prevPositions.rows.map((row) => {
8459
- const itemIndex = nextPositions.findIndex(
8460
- (item) => item.symbol === row.symbol
8461
- );
8462
- if (itemIndex >= 0) {
8463
- const itemArr = nextPositions.splice(itemIndex, 1);
8464
- const item = itemArr[0];
8465
- if (item.averageOpenPrice === 0 && item.positionQty !== 0) {
8466
- return row;
8643
+ return row.symbol === symbol && row.margin_mode === marginMode ? { ...row, leverage } : row;
8644
+ })
8645
+ };
8646
+ }
8647
+ return prevPositions;
8648
+ },
8649
+ {
8650
+ revalidate: false
8651
+ }
8652
+ );
8653
+ mutate(
8654
+ leveragesKey,
8655
+ (prev) => {
8656
+ if (!prev) {
8657
+ return [
8658
+ {
8659
+ symbol,
8660
+ leverage,
8661
+ margin_mode: marginMode
8662
+ }
8663
+ ];
8664
+ }
8665
+ const index = prev.findIndex(
8666
+ (item) => item.symbol === symbol && (item.margin_mode ?? MarginMode.CROSS) === (marginMode ?? MarginMode.CROSS)
8667
+ );
8668
+ if (index === -1) {
8669
+ return [
8670
+ ...prev,
8671
+ {
8672
+ symbol,
8673
+ leverage,
8674
+ margin_mode: marginMode
8675
+ }
8676
+ ];
8677
+ }
8678
+ const next = [...prev];
8679
+ next[index] = {
8680
+ ...next[index],
8681
+ leverage
8682
+ };
8683
+ return next;
8684
+ },
8685
+ {
8686
+ revalidate: false
8687
+ }
8688
+ );
8689
+ }
8690
+ });
8691
+ return () => unsubscribe?.();
8692
+ }, [state.accountId]);
8693
+ useEffect(() => {
8694
+ if (!state.accountId) {
8695
+ return;
8696
+ }
8697
+ const key = ["/v1/positions", state.accountId];
8698
+ const unsubscribe = ws.privateSubscribe("position", {
8699
+ onMessage: (data) => {
8700
+ const { positions: nextPositions } = data;
8701
+ mutate(
8702
+ key,
8703
+ (prevPositions) => {
8704
+ if (!!prevPositions) {
8705
+ const newPositions = {
8706
+ ...prevPositions,
8707
+ rows: prevPositions.rows.map((row) => {
8708
+ const itemIndex = nextPositions.findIndex(
8709
+ (item) => item.symbol === row.symbol && item.marginMode === row.margin_mode
8710
+ );
8711
+ if (itemIndex >= 0) {
8712
+ const itemArr = nextPositions.splice(itemIndex, 1);
8713
+ const item = itemArr[0];
8714
+ if (item.averageOpenPrice === 0 && item.positionQty !== 0) {
8715
+ return row;
8467
8716
  }
8468
8717
  return object2underscore(item);
8469
8718
  }
@@ -8640,6 +8889,61 @@ var OrderValidation = class {
8640
8889
 
8641
8890
  // src/services/orderCreator/baseCreator.ts
8642
8891
  var BaseOrderCreator = class {
8892
+ /**
8893
+ * Template method for order creation
8894
+ * Defines the algorithm structure with hooks for subclasses
8895
+ */
8896
+ create(values, config) {
8897
+ this.beforeCreate(values, config);
8898
+ const order = this.buildOrder(values, config);
8899
+ return this.afterCreate(order, config);
8900
+ }
8901
+ /**
8902
+ * Template method for order validation
8903
+ * Defines the algorithm structure with hooks for subclasses
8904
+ */
8905
+ validate(values, config) {
8906
+ this.beforeValidate(values, config);
8907
+ const errors = this.runValidations(values, config);
8908
+ return Promise.resolve(this.afterValidate(errors, values, config));
8909
+ }
8910
+ /**
8911
+ * Hook method called before order creation
8912
+ * Subclasses can override to perform pre-creation setup
8913
+ * @param values - Order values
8914
+ * @param config - Configuration
8915
+ */
8916
+ beforeCreate(values, config) {
8917
+ }
8918
+ /**
8919
+ * Hook method called after order creation
8920
+ * Subclasses can override to perform post-creation processing
8921
+ * @param order - The created order
8922
+ * @param config - Configuration
8923
+ * @returns The final order (possibly modified)
8924
+ */
8925
+ afterCreate(order, config) {
8926
+ return order;
8927
+ }
8928
+ /**
8929
+ * Hook method called before validation
8930
+ * Subclasses can override to perform pre-validation setup
8931
+ * @param values - Order values to validate
8932
+ * @param config - Configuration
8933
+ */
8934
+ beforeValidate(values, config) {
8935
+ }
8936
+ /**
8937
+ * Hook method called after validation
8938
+ * Subclasses can override to perform post-validation processing
8939
+ * @param errors - Validation errors found
8940
+ * @param values - Original order values
8941
+ * @param config - Configuration
8942
+ * @returns Final validation result (possibly modified)
8943
+ */
8944
+ afterValidate(errors, values, config) {
8945
+ return errors;
8946
+ }
8643
8947
  baseOrder(data) {
8644
8948
  const order = {
8645
8949
  symbol: data.symbol,
@@ -8647,7 +8951,8 @@ var BaseOrderCreator = class {
8647
8951
  side: data.side,
8648
8952
  reduce_only: data.reduce_only,
8649
8953
  order_quantity: data.order_quantity,
8650
- total: data.total
8954
+ total: data.total,
8955
+ margin_mode: data.margin_mode || MarginMode.CROSS
8651
8956
  // slippage: data.slippage,
8652
8957
  };
8653
8958
  if (data.order_type === OrderType.MARKET && !!data.slippage) {
@@ -8666,10 +8971,21 @@ var BaseOrderCreator = class {
8666
8971
  child_orders: [bracketOrder]
8667
8972
  };
8668
8973
  }
8974
+ /**
8975
+ * Base validation method that can be called by subclasses
8976
+ * Validates common order properties like quantity and min notional
8977
+ * @param values - Order values to validate
8978
+ * @param configs - Configuration
8979
+ * @returns Validation result (synchronous, not a Promise)
8980
+ */
8669
8981
  baseValidate(values, configs) {
8670
8982
  const errors = {};
8671
8983
  const { maxQty, symbol, markPrice } = configs;
8672
- let { order_quantity, total, order_price, reduce_only, order_type } = values;
8984
+ let order_quantity = values.order_quantity;
8985
+ const total = values.total;
8986
+ const order_price = values.order_price;
8987
+ const reduce_only = values.reduce_only;
8988
+ const order_type = values.order_type;
8673
8989
  const { min_notional, base_tick, quote_dp, quote_tick, base_dp } = symbol || {};
8674
8990
  if (!order_quantity) {
8675
8991
  if (total && order_price) {
@@ -8714,7 +9030,7 @@ var BaseOrderCreator = class {
8714
9030
  };
8715
9031
  }
8716
9032
  this.validateBracketOrder(values, configs, errors);
8717
- return Promise.resolve(errors);
9033
+ return errors;
8718
9034
  }
8719
9035
  totalToQuantity(order, config) {
8720
9036
  if (!order.order_quantity && order.total && order.order_price) {
@@ -8863,7 +9179,11 @@ var BBOOrderCreator = class extends BaseOrderCreator {
8863
9179
  super(...arguments);
8864
9180
  this.orderType = OrderType.LIMIT;
8865
9181
  }
8866
- create(values) {
9182
+ /**
9183
+ * Builds the BBO order
9184
+ * Implements template method hook
9185
+ */
9186
+ buildOrder(values) {
8867
9187
  const order = {
8868
9188
  ...this.baseOrder(values),
8869
9189
  level: values.level
@@ -8876,15 +9196,18 @@ var BBOOrderCreator = class extends BaseOrderCreator {
8876
9196
  "reduce_only",
8877
9197
  "side",
8878
9198
  "order_type",
9199
+ "margin_mode",
8879
9200
  "level"
8880
9201
  ],
8881
9202
  order
8882
9203
  );
8883
9204
  }
8884
- async validate(values, configs) {
8885
- return this.baseValidate(values, configs).then((errors) => {
8886
- return errors;
8887
- });
9205
+ /**
9206
+ * Runs base validations
9207
+ * Implements template method hook
9208
+ */
9209
+ runValidations(values, configs) {
9210
+ return this.baseValidate(values, configs);
8888
9211
  }
8889
9212
  };
8890
9213
  function getOrderPrice(order, askAndBid) {
@@ -8911,6 +9234,24 @@ function getOrderPrice(order, askAndBid) {
8911
9234
  }
8912
9235
  }
8913
9236
  }
9237
+ order.getOrderReferencePrice;
9238
+ function getOrderReferencePriceFromOrder(order$1, askAndBid) {
9239
+ if (!askAndBid || askAndBid.length < 2) return null;
9240
+ if (!order$1.order_type || !order$1.side) {
9241
+ return null;
9242
+ }
9243
+ return order.getOrderReferencePrice(
9244
+ {
9245
+ orderType: order$1.order_type,
9246
+ orderTypeExt: order$1.order_type_ext,
9247
+ side: order$1.side,
9248
+ limitPrice: order$1.order_price ? Number(order$1.order_price) : void 0,
9249
+ triggerPrice: order$1.trigger_price ? Number(order$1.trigger_price) : void 0
9250
+ },
9251
+ askAndBid[0],
9252
+ askAndBid[1]
9253
+ );
9254
+ }
8914
9255
  function getPriceRange(inputs) {
8915
9256
  const { basePrice, side, symbolInfo } = inputs;
8916
9257
  const { price_range, price_scope, quote_min, quote_max } = symbolInfo;
@@ -8924,97 +9265,170 @@ function getPriceRange(inputs) {
8924
9265
  min: minPriceNumber,
8925
9266
  max: scopePriceNumber
8926
9267
  };
8927
- const minPrice3 = Math.max(quote_min, priceRange?.min);
8928
- const maxPrice3 = Math.min(quote_max, priceRange?.max);
9268
+ const minPrice = Math.max(quote_min, priceRange?.min);
9269
+ const maxPrice = Math.min(quote_max, priceRange?.max);
8929
9270
  return {
8930
- minPrice: minPrice3,
8931
- maxPrice: maxPrice3
9271
+ minPrice,
9272
+ maxPrice
8932
9273
  };
8933
9274
  }
8934
9275
 
8935
- // src/services/orderCreator/baseBracketOrderCreator.ts
9276
+ // src/services/orderCreator/validators/TPSLValidationStrategy.ts
8936
9277
  var formatPrice = (price, quote_dp) => {
8937
9278
  return new Decimal(price).toDecimalPlaces(quote_dp).toNumber();
8938
9279
  };
8939
- async function bracketOrderValidator(values, config) {
8940
- const result = /* @__PURE__ */ Object.create(null);
8941
- await Promise.resolve();
8942
- const {
8943
- // tp_enable,
8944
- // sl_enable,
8945
- tp_trigger_price,
8946
- tp_order_price,
8947
- tp_order_type,
8948
- sl_trigger_price,
8949
- sl_order_price,
8950
- sl_order_type,
8951
- side
8952
- } = values;
8953
- const qty = Number(values.quantity);
8954
- const maxQty = config.maxQty;
8955
- const type = values.order_type;
8956
- const { quote_max, quote_min, quote_dp } = config.symbol ?? {};
8957
- const mark_price = type === OrderType.MARKET ? config.markPrice : values.order_price ? Number(values.order_price) : void 0;
8958
- const tpslSide = side === OrderSide.BUY ? OrderSide.SELL : OrderSide.BUY;
8959
- if (!isNaN(qty) && qty > maxQty) {
8960
- result.quantity = OrderValidation.max("quantity", config.maxQty);
8961
- }
8962
- if (Number(tp_trigger_price) < 0) {
8963
- result.tp_trigger_price = OrderValidation.min("tp_trigger_price", 0);
8964
- }
8965
- if (Number(sl_trigger_price) < 0) {
8966
- result.sl_trigger_price = OrderValidation.min("sl_trigger_price", 0);
8967
- }
8968
- if (tp_order_type === OrderType.LIMIT && !tp_order_price) {
8969
- result.tp_order_price = OrderValidation.required("tp_order_price");
8970
- }
8971
- if (sl_order_type === OrderType.LIMIT && !sl_order_price) {
8972
- result.sl_order_price = OrderValidation.required("sl_order_price");
8973
- }
8974
- if (side === OrderSide.BUY && mark_price) {
8975
- if (!!sl_trigger_price && Number(sl_trigger_price) < quote_min) {
8976
- result.sl_trigger_price = OrderValidation.min(
8977
- "sl_trigger_price",
8978
- formatPrice(quote_min, quote_dp)
9280
+ var TPSLValidationStrategy = class {
9281
+ /**
9282
+ * Validates TP/SL order values
9283
+ * @param values - TP/SL order values including trigger prices, order prices, etc.
9284
+ * @param config - Configuration with symbol info and mark price
9285
+ * @returns Validation result object with any errors found
9286
+ */
9287
+ validate(values, config) {
9288
+ const result = /* @__PURE__ */ Object.create(null);
9289
+ const {
9290
+ tp_trigger_price,
9291
+ tp_order_price,
9292
+ tp_order_type,
9293
+ sl_trigger_price,
9294
+ sl_order_price,
9295
+ sl_order_type,
9296
+ side,
9297
+ quantity,
9298
+ order_type,
9299
+ order_price
9300
+ } = values;
9301
+ const qty = Number(quantity);
9302
+ const maxQty = config.maxQty;
9303
+ const { quote_max, quote_min, quote_dp, base_min } = config.symbol ?? {};
9304
+ const mark_price = order_type === OrderType.MARKET || order_type == null ? config.markPrice : order_price ? Number(order_price) : void 0;
9305
+ const tpslSide = side === OrderSide.BUY ? OrderSide.SELL : OrderSide.BUY;
9306
+ if (!isNaN(qty) && qty > maxQty) {
9307
+ result.quantity = OrderValidation.max("quantity", config.maxQty);
9308
+ }
9309
+ if (!isNaN(qty) && qty < (base_min ?? 0)) {
9310
+ result.quantity = OrderValidation.min("quantity", base_min ?? 0);
9311
+ }
9312
+ if (tp_trigger_price !== void 0 && tp_trigger_price !== "" && tp_trigger_price !== null && Number(tp_trigger_price) < 0) {
9313
+ result.tp_trigger_price = OrderValidation.min("tp_trigger_price", 0);
9314
+ }
9315
+ if (sl_trigger_price !== void 0 && sl_trigger_price !== "" && sl_trigger_price !== null && Number(sl_trigger_price) < 0) {
9316
+ result.sl_trigger_price = OrderValidation.min("sl_trigger_price", 0);
9317
+ }
9318
+ if (tp_order_type === OrderType.LIMIT && !tp_order_price) {
9319
+ result.tp_order_price = OrderValidation.required("tp_order_price");
9320
+ }
9321
+ if (sl_order_type === OrderType.LIMIT && !sl_order_price) {
9322
+ result.sl_order_price = OrderValidation.required("sl_order_price");
9323
+ }
9324
+ if (side === OrderSide.BUY && mark_price) {
9325
+ this.validateBuySide(
9326
+ {
9327
+ tp_trigger_price,
9328
+ tp_order_price,
9329
+ sl_trigger_price,
9330
+ sl_order_price
9331
+ },
9332
+ {
9333
+ mark_price,
9334
+ quote_min: quote_min ?? 0,
9335
+ quote_max: quote_max ?? 0,
9336
+ quote_dp: quote_dp ?? 0,
9337
+ tpslSide,
9338
+ symbol: config.symbol
9339
+ },
9340
+ result
8979
9341
  );
8980
- }
8981
- if (!!sl_trigger_price && Number(sl_trigger_price) > mark_price) {
8982
- result.sl_trigger_price = OrderValidation.max(
8983
- "sl_trigger_price",
8984
- formatPrice(mark_price, quote_dp)
9342
+ } else if (side === OrderSide.SELL && mark_price) {
9343
+ this.validateSellSide(
9344
+ {
9345
+ tp_trigger_price,
9346
+ tp_order_price,
9347
+ sl_trigger_price,
9348
+ sl_order_price
9349
+ },
9350
+ {
9351
+ mark_price,
9352
+ quote_min: quote_min ?? 0,
9353
+ quote_max: quote_max ?? 0,
9354
+ quote_dp: quote_dp ?? 0,
9355
+ tpslSide,
9356
+ symbol: config.symbol
9357
+ },
9358
+ result
8985
9359
  );
8986
9360
  }
8987
- if (!!tp_trigger_price && Number(tp_trigger_price) <= mark_price) {
8988
- result.tp_trigger_price = OrderValidation.min(
8989
- "tp_trigger_price",
8990
- formatPrice(mark_price, quote_dp)
8991
- );
9361
+ return Object.keys(result).length > 0 ? result : null;
9362
+ }
9363
+ /**
9364
+ * Validates TP/SL for BUY orders
9365
+ * For BUY orders:
9366
+ * - SL trigger price must be < mark price
9367
+ * - TP trigger price must be > mark price
9368
+ */
9369
+ validateBuySide(prices, config, result) {
9370
+ const {
9371
+ tp_trigger_price,
9372
+ tp_order_price,
9373
+ sl_trigger_price,
9374
+ sl_order_price
9375
+ } = prices;
9376
+ const { mark_price, quote_min, quote_max, quote_dp, tpslSide, symbol } = config;
9377
+ if (sl_trigger_price !== void 0 && sl_trigger_price !== "" && sl_trigger_price !== null) {
9378
+ const slTrigger = Number(sl_trigger_price);
9379
+ if (!isNaN(slTrigger)) {
9380
+ if (quote_min > 0 && slTrigger < quote_min) {
9381
+ result.sl_trigger_price = OrderValidation.min(
9382
+ "sl_trigger_price",
9383
+ formatPrice(quote_min, quote_dp)
9384
+ );
9385
+ }
9386
+ if (slTrigger >= mark_price) {
9387
+ result.sl_trigger_price = OrderValidation.max(
9388
+ "sl_trigger_price",
9389
+ formatPrice(mark_price, quote_dp)
9390
+ );
9391
+ }
9392
+ }
8992
9393
  }
8993
- if (!!tp_trigger_price && Number(tp_trigger_price) > quote_max) {
8994
- result.tp_trigger_price = OrderValidation.max(
8995
- "tp_trigger_price",
8996
- formatPrice(quote_max, quote_dp)
8997
- );
9394
+ if (tp_trigger_price !== void 0 && tp_trigger_price !== "" && tp_trigger_price !== null) {
9395
+ const tpTrigger = Number(tp_trigger_price);
9396
+ if (!isNaN(tpTrigger)) {
9397
+ if (tpTrigger <= mark_price) {
9398
+ result.tp_trigger_price = OrderValidation.min(
9399
+ "tp_trigger_price",
9400
+ formatPrice(mark_price, quote_dp)
9401
+ );
9402
+ }
9403
+ if (quote_max > 0 && tpTrigger > quote_max) {
9404
+ result.tp_trigger_price = OrderValidation.max(
9405
+ "tp_trigger_price",
9406
+ formatPrice(quote_max, quote_dp)
9407
+ );
9408
+ }
9409
+ }
8998
9410
  }
8999
9411
  if (sl_trigger_price && sl_order_price) {
9000
9412
  const priceRange = getPriceRange({
9001
9413
  side: tpslSide,
9002
9414
  basePrice: Number(sl_trigger_price),
9003
- symbolInfo: config.symbol
9415
+ symbolInfo: symbol
9004
9416
  });
9005
- if (Number(sl_order_price) < priceRange.minPrice) {
9417
+ const slOrderPrice = Number(sl_order_price);
9418
+ const slTrigger = Number(sl_trigger_price);
9419
+ if (slOrderPrice < priceRange.minPrice) {
9006
9420
  result.sl_order_price = OrderValidation.min(
9007
9421
  "sl_order_price",
9008
9422
  formatPrice(priceRange.minPrice, quote_dp)
9009
9423
  );
9010
9424
  }
9011
- if (Number(sl_order_price) > priceRange.maxPrice) {
9425
+ if (slOrderPrice > priceRange.maxPrice) {
9012
9426
  result.sl_order_price = OrderValidation.max(
9013
9427
  "sl_order_price",
9014
9428
  formatPrice(priceRange.maxPrice, quote_dp)
9015
9429
  );
9016
9430
  }
9017
- if (Number(sl_trigger_price) < Number(sl_order_price)) {
9431
+ if (slTrigger < slOrderPrice) {
9018
9432
  result.sl_trigger_price = OrderValidation.priceErrorMax("sl_trigger_price");
9019
9433
  }
9020
9434
  }
@@ -9022,69 +9436,96 @@ async function bracketOrderValidator(values, config) {
9022
9436
  const priceRange = getPriceRange({
9023
9437
  side: tpslSide,
9024
9438
  basePrice: Number(tp_trigger_price),
9025
- symbolInfo: config.symbol
9439
+ symbolInfo: symbol
9026
9440
  });
9027
- if (Number(tp_order_price) > priceRange.maxPrice) {
9441
+ const tpOrderPrice = Number(tp_order_price);
9442
+ const tpTrigger = Number(tp_trigger_price);
9443
+ if (tpOrderPrice > priceRange.maxPrice) {
9028
9444
  result.tp_order_price = OrderValidation.max(
9029
9445
  "tp_order_price",
9030
9446
  formatPrice(priceRange.maxPrice, quote_dp)
9031
9447
  );
9032
9448
  }
9033
- if (Number(tp_order_price) < priceRange.minPrice) {
9449
+ if (tpOrderPrice < priceRange.minPrice) {
9034
9450
  result.tp_order_price = OrderValidation.min(
9035
9451
  "tp_order_price",
9036
9452
  formatPrice(priceRange.minPrice, quote_dp)
9037
9453
  );
9038
9454
  }
9039
- if (Number(tp_trigger_price) > Number(tp_order_price)) {
9455
+ if (tpTrigger > tpOrderPrice) {
9040
9456
  result.tp_trigger_price = OrderValidation.priceErrorMax("tp_trigger_price");
9041
9457
  }
9042
9458
  }
9043
9459
  }
9044
- if (side === OrderSide.SELL && mark_price) {
9045
- if (!!sl_trigger_price && Number(sl_trigger_price) > quote_max) {
9046
- result.sl_trigger_price = OrderValidation.max(
9047
- "sl_trigger_price",
9048
- formatPrice(quote_max, quote_dp)
9049
- );
9050
- }
9051
- if (!!sl_trigger_price && Number(sl_trigger_price) < mark_price) {
9052
- result.sl_trigger_price = OrderValidation.min(
9053
- "sl_trigger_price",
9054
- formatPrice(mark_price, quote_dp)
9055
- );
9056
- }
9057
- if (!!tp_trigger_price && Number(tp_trigger_price) >= mark_price) {
9058
- result.tp_trigger_price = OrderValidation.max(
9059
- "tp_trigger_price",
9060
- formatPrice(mark_price, quote_dp)
9061
- );
9460
+ /**
9461
+ * Validates TP/SL for SELL orders
9462
+ * For SELL orders:
9463
+ * - SL trigger price must be > mark price
9464
+ * - TP trigger price must be < mark price
9465
+ */
9466
+ validateSellSide(prices, config, result) {
9467
+ const {
9468
+ tp_trigger_price,
9469
+ tp_order_price,
9470
+ sl_trigger_price,
9471
+ sl_order_price
9472
+ } = prices;
9473
+ const { mark_price, quote_min, quote_max, quote_dp, tpslSide, symbol } = config;
9474
+ if (sl_trigger_price !== void 0 && sl_trigger_price !== "" && sl_trigger_price !== null) {
9475
+ const slTrigger = Number(sl_trigger_price);
9476
+ if (!isNaN(slTrigger)) {
9477
+ if (quote_max > 0 && slTrigger > quote_max) {
9478
+ result.sl_trigger_price = OrderValidation.max(
9479
+ "sl_trigger_price",
9480
+ formatPrice(quote_max, quote_dp)
9481
+ );
9482
+ }
9483
+ if (slTrigger <= mark_price) {
9484
+ result.sl_trigger_price = OrderValidation.min(
9485
+ "sl_trigger_price",
9486
+ formatPrice(mark_price, quote_dp)
9487
+ );
9488
+ }
9489
+ }
9062
9490
  }
9063
- if (!!tp_trigger_price && Number(tp_trigger_price) < quote_min) {
9064
- result.tp_trigger_price = OrderValidation.min(
9065
- "tp_trigger_price",
9066
- formatPrice(quote_min, quote_dp)
9067
- );
9491
+ if (tp_trigger_price !== void 0 && tp_trigger_price !== "" && tp_trigger_price !== null) {
9492
+ const tpTrigger = Number(tp_trigger_price);
9493
+ if (!isNaN(tpTrigger)) {
9494
+ if (tpTrigger >= mark_price) {
9495
+ result.tp_trigger_price = OrderValidation.max(
9496
+ "tp_trigger_price",
9497
+ formatPrice(mark_price, quote_dp)
9498
+ );
9499
+ }
9500
+ if (quote_min > 0 && tpTrigger < quote_min) {
9501
+ result.tp_trigger_price = OrderValidation.min(
9502
+ "tp_trigger_price",
9503
+ formatPrice(quote_min, quote_dp)
9504
+ );
9505
+ }
9506
+ }
9068
9507
  }
9069
9508
  if (sl_trigger_price && sl_order_price) {
9070
9509
  const priceRange = getPriceRange({
9071
9510
  side: tpslSide,
9072
9511
  basePrice: Number(sl_trigger_price),
9073
- symbolInfo: config.symbol
9512
+ symbolInfo: symbol
9074
9513
  });
9075
- if (Number(sl_order_price) < priceRange.minPrice) {
9514
+ const slOrderPrice = Number(sl_order_price);
9515
+ const slTrigger = Number(sl_trigger_price);
9516
+ if (slOrderPrice < priceRange.minPrice) {
9076
9517
  result.sl_order_price = OrderValidation.min(
9077
9518
  "sl_order_price",
9078
9519
  formatPrice(priceRange.minPrice, quote_dp)
9079
9520
  );
9080
9521
  }
9081
- if (Number(sl_order_price) > priceRange.maxPrice) {
9522
+ if (slOrderPrice > priceRange.maxPrice) {
9082
9523
  result.sl_order_price = OrderValidation.max(
9083
9524
  "sl_order_price",
9084
9525
  formatPrice(priceRange.maxPrice, quote_dp)
9085
9526
  );
9086
9527
  }
9087
- if (Number(sl_trigger_price) > Number(sl_order_price)) {
9528
+ if (slTrigger > slOrderPrice) {
9088
9529
  result.sl_trigger_price = OrderValidation.priceErrorMin("sl_trigger_price");
9089
9530
  }
9090
9531
  }
@@ -9092,37 +9533,294 @@ async function bracketOrderValidator(values, config) {
9092
9533
  const priceRange = getPriceRange({
9093
9534
  side: tpslSide,
9094
9535
  basePrice: Number(tp_trigger_price),
9095
- symbolInfo: config.symbol
9536
+ symbolInfo: symbol
9096
9537
  });
9097
- if (Number(tp_order_price) < priceRange.minPrice) {
9538
+ const tpOrderPrice = Number(tp_order_price);
9539
+ const tpTrigger = Number(tp_trigger_price);
9540
+ if (tpOrderPrice < priceRange.minPrice) {
9098
9541
  result.tp_order_price = OrderValidation.min(
9099
9542
  "tp_order_price",
9100
9543
  formatPrice(priceRange.minPrice, quote_dp)
9101
9544
  );
9102
9545
  }
9103
- if (Number(tp_order_price) > priceRange.maxPrice) {
9546
+ if (tpOrderPrice > priceRange.maxPrice) {
9104
9547
  result.tp_order_price = OrderValidation.max(
9105
9548
  "tp_order_price",
9106
9549
  formatPrice(priceRange.maxPrice, quote_dp)
9107
9550
  );
9108
9551
  }
9109
- if (Number(tp_trigger_price) < Number(tp_order_price)) {
9552
+ if (tpTrigger < tpOrderPrice) {
9110
9553
  result.tp_trigger_price = OrderValidation.priceErrorMax("tp_trigger_price");
9111
9554
  }
9112
9555
  }
9113
9556
  }
9114
- return Object.keys(result).length > 0 ? result : null;
9557
+ };
9558
+
9559
+ // src/services/orderCreator/baseBracketOrderCreator.ts
9560
+ async function bracketOrderValidator(values, config) {
9561
+ const strategy = new TPSLValidationStrategy();
9562
+ return strategy.validate(values, config);
9115
9563
  }
9116
- var { maxPrice, minPrice, scopePrice } = order;
9564
+
9565
+ // src/services/orderCreator/validators/BaseValidator.ts
9566
+ var BaseValidator = class {
9567
+ /**
9568
+ * Sets the next validator in the chain
9569
+ * @param validator - The next validator to call
9570
+ * @returns The next validator for method chaining
9571
+ */
9572
+ setNext(validator) {
9573
+ this.next = validator;
9574
+ return validator;
9575
+ }
9576
+ /**
9577
+ * Validates values and passes to next validator in chain
9578
+ * @param values - The values to validate
9579
+ * @param config - Configuration for validation
9580
+ * @param errors - Accumulated validation errors
9581
+ * @returns Updated validation result
9582
+ */
9583
+ validate(values, config, errors) {
9584
+ const result = this.doValidate(values, config);
9585
+ if (result) {
9586
+ errors[this.getFieldName()] = result;
9587
+ }
9588
+ return this.next?.validate(values, config, errors) ?? errors;
9589
+ }
9590
+ };
9591
+ var PriceValidationStrategy = class {
9592
+ /**
9593
+ * Validates order price against symbol constraints and price range
9594
+ * @param values - Object containing order_price, side, and order_type
9595
+ * @param config - Configuration with symbol info and mark price
9596
+ * @returns Validation error if price is invalid, undefined otherwise
9597
+ */
9598
+ validate(values, config) {
9599
+ const { order_price, side } = values;
9600
+ if (!order_price) {
9601
+ return OrderValidation.required("order_price");
9602
+ }
9603
+ const price = new Decimal(order_price);
9604
+ const { symbol } = config;
9605
+ const { quote_max, quote_min, quote_dp, price_range, price_scope } = symbol;
9606
+ const maxPriceNumber = order.maxPrice(config.markPrice, price_range);
9607
+ const minPriceNumber = order.minPrice(config.markPrice, price_range);
9608
+ const scopePriceNumber = order.scopePrice(
9609
+ config.markPrice,
9610
+ price_scope,
9611
+ side
9612
+ );
9613
+ const priceRange = side === OrderSide.BUY ? {
9614
+ min: scopePriceNumber,
9615
+ max: maxPriceNumber
9616
+ } : {
9617
+ min: minPriceNumber,
9618
+ max: scopePriceNumber
9619
+ };
9620
+ if (price.gt(quote_max)) {
9621
+ return OrderValidation.max("order_price", quote_max);
9622
+ }
9623
+ if (price.lt(quote_min)) {
9624
+ return OrderValidation.min("order_price", quote_min);
9625
+ }
9626
+ if (price.gt(priceRange.max)) {
9627
+ return OrderValidation.max(
9628
+ "order_price",
9629
+ new Decimal(priceRange.max).todp(quote_dp).toString()
9630
+ );
9631
+ }
9632
+ if (price.lt(priceRange.min)) {
9633
+ return OrderValidation.min(
9634
+ "order_price",
9635
+ new Decimal(priceRange.min).todp(quote_dp).toString()
9636
+ );
9637
+ }
9638
+ return void 0;
9639
+ }
9640
+ };
9641
+ var TriggerPriceValidationStrategy = class {
9642
+ /**
9643
+ * Validates trigger price against symbol constraints
9644
+ * @param values - Object containing trigger_price
9645
+ * @param config - Configuration with symbol info
9646
+ * @returns Validation error if trigger price is invalid, undefined otherwise
9647
+ */
9648
+ validate(values, config) {
9649
+ const { trigger_price } = values;
9650
+ const { symbol } = config;
9651
+ const { quote_max, quote_min } = symbol;
9652
+ if (!trigger_price) {
9653
+ return OrderValidation.required("trigger_price");
9654
+ }
9655
+ const triggerPrice = Number(trigger_price);
9656
+ if (triggerPrice > quote_max) {
9657
+ return OrderValidation.max("trigger_price", quote_max);
9658
+ }
9659
+ if (triggerPrice < quote_min || triggerPrice === 0) {
9660
+ return OrderValidation.min("trigger_price", quote_min);
9661
+ }
9662
+ return void 0;
9663
+ }
9664
+ };
9665
+
9666
+ // src/services/orderCreator/validators/PriceValidator.ts
9667
+ var PriceValidator = class extends BaseValidator {
9668
+ constructor() {
9669
+ super();
9670
+ this.strategy = new PriceValidationStrategy();
9671
+ }
9672
+ doValidate(values, config) {
9673
+ return this.strategy.validate(
9674
+ {
9675
+ order_price: values.order_price,
9676
+ side: values.side,
9677
+ order_type: values.order_type
9678
+ },
9679
+ config
9680
+ );
9681
+ }
9682
+ getFieldName() {
9683
+ return "order_price";
9684
+ }
9685
+ };
9686
+ var QuantityValidationStrategy = class {
9687
+ /**
9688
+ * Validates order quantity against symbol constraints
9689
+ * Also handles conversion from total to quantity if needed
9690
+ * @param values - Object containing order_quantity, total, and order_price
9691
+ * @param config - Configuration with symbol info and max quantity
9692
+ * @returns Validation error if quantity is invalid, undefined otherwise
9693
+ */
9694
+ validate(values, config) {
9695
+ let { order_quantity, total, order_price } = values;
9696
+ const { maxQty, symbol } = config;
9697
+ const { base_min, base_dp, quote_dp } = symbol;
9698
+ if (!order_quantity && total && order_price) {
9699
+ const totalNumber = new Decimal(total);
9700
+ const qty2 = totalNumber.dividedBy(order_price).toFixed(quote_dp);
9701
+ order_quantity = qty2;
9702
+ }
9703
+ if (!order_quantity) {
9704
+ return OrderValidation.required("order_quantity");
9705
+ }
9706
+ const qty = new Decimal(order_quantity);
9707
+ if (qty.lt(base_min)) {
9708
+ return OrderValidation.min(
9709
+ "order_quantity",
9710
+ new Decimal(base_min).todp(base_dp).toString()
9711
+ );
9712
+ }
9713
+ if (qty.gt(maxQty)) {
9714
+ return OrderValidation.max(
9715
+ "order_quantity",
9716
+ new Decimal(maxQty).todp(base_dp).toString()
9717
+ );
9718
+ }
9719
+ return void 0;
9720
+ }
9721
+ };
9722
+
9723
+ // src/services/orderCreator/validators/QuantityValidator.ts
9724
+ var QuantityValidator = class extends BaseValidator {
9725
+ constructor() {
9726
+ super();
9727
+ this.strategy = new QuantityValidationStrategy();
9728
+ }
9729
+ doValidate(values, config) {
9730
+ return this.strategy.validate(
9731
+ {
9732
+ order_quantity: values.order_quantity,
9733
+ total: values.total,
9734
+ order_price: values.order_price
9735
+ },
9736
+ config
9737
+ );
9738
+ }
9739
+ getFieldName() {
9740
+ return "order_quantity";
9741
+ }
9742
+ };
9743
+
9744
+ // src/services/orderCreator/validators/ValidationChain.ts
9745
+ var ValidationChain = class {
9746
+ constructor() {
9747
+ this.validators = [];
9748
+ }
9749
+ /**
9750
+ * Adds a validator to the chain
9751
+ * @param validator - The validator to add
9752
+ * @returns This chain instance for method chaining
9753
+ */
9754
+ addValidator(validator) {
9755
+ this.validators.push(validator);
9756
+ if (!this.head) {
9757
+ this.head = validator;
9758
+ } else {
9759
+ let current = this.head;
9760
+ while (current && current.next) {
9761
+ current = current.next;
9762
+ }
9763
+ if (current) {
9764
+ current.setNext(validator);
9765
+ }
9766
+ }
9767
+ return this;
9768
+ }
9769
+ /**
9770
+ * Adds multiple validators to the chain
9771
+ * @param validators - Array of validators to add
9772
+ * @returns This chain instance for method chaining
9773
+ */
9774
+ addValidators(validators) {
9775
+ validators.forEach((validator) => this.addValidator(validator));
9776
+ return this;
9777
+ }
9778
+ /**
9779
+ * Validates values using all validators in the chain
9780
+ * @param values - The values to validate
9781
+ * @param config - Configuration for validation
9782
+ * @returns Validation result with all errors found
9783
+ */
9784
+ validate(values, config) {
9785
+ const errors = {};
9786
+ if (this.head) {
9787
+ return this.head.validate(values, config, errors);
9788
+ }
9789
+ return errors;
9790
+ }
9791
+ /**
9792
+ * Clears all validators from the chain
9793
+ */
9794
+ clear() {
9795
+ this.validators = [];
9796
+ this.head = void 0;
9797
+ }
9798
+ /**
9799
+ * Gets the number of validators in the chain
9800
+ * @returns The number of validators
9801
+ */
9802
+ get length() {
9803
+ return this.validators.length;
9804
+ }
9805
+ };
9806
+
9807
+ // src/services/orderCreator/limitOrderCreator.ts
9117
9808
  var LimitOrderCreator = class extends BaseOrderCreator {
9118
9809
  constructor() {
9119
9810
  super(...arguments);
9811
+ // private priceValidationStrategy = new PriceValidationStrategy();
9812
+ this.validationChain = new ValidationChain().addValidator(new QuantityValidator()).addValidator(new PriceValidator());
9120
9813
  this.orderType = OrderType.LIMIT;
9121
9814
  }
9122
- create(values, config) {
9815
+ /**
9816
+ * Builds the limit order
9817
+ * Implements template method hook
9818
+ */
9819
+ buildOrder(values, config) {
9820
+ const orderlyValues = values;
9123
9821
  const order = {
9124
- ...this.baseOrder(values),
9125
- order_price: values.order_price
9822
+ ...this.baseOrder(orderlyValues),
9823
+ order_price: orderlyValues.order_price
9126
9824
  };
9127
9825
  this.totalToQuantity(order, config);
9128
9826
  return pick(
@@ -9131,61 +9829,26 @@ var LimitOrderCreator = class extends BaseOrderCreator {
9131
9829
  "order_price",
9132
9830
  "order_quantity",
9133
9831
  "visible_quantity",
9134
- "reduce_only",
9135
- "side",
9136
- "order_type",
9137
- "algo_type",
9138
- "child_orders"
9139
- ],
9140
- order
9141
- );
9142
- }
9143
- validate(values, config) {
9144
- return this.baseValidate(values, config).then((errors) => {
9145
- const { order_price, side } = values;
9146
- if (!order_price) {
9147
- errors.order_price = OrderValidation.required("order_price");
9148
- } else {
9149
- const price = new Decimal(order_price);
9150
- const { symbol } = config;
9151
- const { price_range, price_scope, quote_max, quote_min } = symbol;
9152
- const maxPriceNumber = maxPrice(config.markPrice, price_range);
9153
- const minPriceNumber = minPrice(config.markPrice, price_range);
9154
- const scopePriceNumber = scopePrice(
9155
- config.markPrice,
9156
- price_scope,
9157
- side
9158
- );
9159
- const priceRange = side === "BUY" ? {
9160
- min: scopePriceNumber,
9161
- max: maxPriceNumber
9162
- } : {
9163
- min: minPriceNumber,
9164
- max: scopePriceNumber
9165
- };
9166
- if (price.gt(quote_max)) {
9167
- errors.order_price = OrderValidation.max("order_price", quote_max);
9168
- } else {
9169
- if (price.gt(priceRange?.max)) {
9170
- errors.order_price = OrderValidation.max(
9171
- "order_price",
9172
- new Decimal(priceRange.max).todp(symbol.quote_dp).toString()
9173
- );
9174
- }
9175
- }
9176
- if (price.lt(quote_min)) {
9177
- errors.order_price = OrderValidation.min("order_price", quote_min);
9178
- } else {
9179
- if (price.lt(priceRange?.min)) {
9180
- errors.order_price = OrderValidation.min(
9181
- "order_price",
9182
- new Decimal(priceRange.min).todp(symbol.quote_dp).toString()
9183
- );
9184
- }
9185
- }
9186
- }
9187
- return errors;
9188
- });
9832
+ "reduce_only",
9833
+ "side",
9834
+ "order_type",
9835
+ "margin_mode",
9836
+ "algo_type",
9837
+ "child_orders"
9838
+ ],
9839
+ order
9840
+ );
9841
+ }
9842
+ /**
9843
+ * Runs validations using validation chain
9844
+ * Implements template method hook
9845
+ */
9846
+ runValidations(values, config) {
9847
+ const orderlyValues = values;
9848
+ const errors = this.baseValidate(orderlyValues, config);
9849
+ const chainErrors = this.validationChain.validate(orderlyValues, config);
9850
+ Object.assign(errors, chainErrors);
9851
+ return errors;
9189
9852
  }
9190
9853
  };
9191
9854
 
@@ -9208,34 +9871,81 @@ var BracketLimitOrderCreator = class extends LimitOrderCreator {
9208
9871
  }
9209
9872
  };
9210
9873
 
9874
+ // src/services/orderCreator/validators/SlippageValidationStrategy.ts
9875
+ var SlippageValidationStrategy = class {
9876
+ /**
9877
+ * Validates slippage against estimated slippage
9878
+ * @param values - Object containing slippage value
9879
+ * @param config - Configuration with estimated slippage
9880
+ * @returns Validation error if slippage exceeds estimated, undefined otherwise
9881
+ */
9882
+ validate(values, config) {
9883
+ const slippage = Number(values.slippage);
9884
+ const estSlippage = Number.isNaN(config.estSlippage) ? 0 : Number(config.estSlippage) * 100;
9885
+ if (!isNaN(slippage) && estSlippage > slippage) {
9886
+ return {
9887
+ type: "max",
9888
+ message: "Estimated slippage exceeds your maximum allowed slippage.",
9889
+ value: estSlippage
9890
+ };
9891
+ }
9892
+ return void 0;
9893
+ }
9894
+ };
9895
+
9896
+ // src/services/orderCreator/validators/SlippageValidator.ts
9897
+ var SlippageValidator = class extends BaseValidator {
9898
+ constructor() {
9899
+ super();
9900
+ this.strategy = new SlippageValidationStrategy();
9901
+ }
9902
+ doValidate(values, config) {
9903
+ return this.strategy.validate(
9904
+ {
9905
+ slippage: values.slippage
9906
+ },
9907
+ config
9908
+ );
9909
+ }
9910
+ getFieldName() {
9911
+ return "slippage";
9912
+ }
9913
+ };
9914
+
9211
9915
  // src/services/orderCreator/marketOrderCreator.ts
9212
9916
  var MarketOrderCreator = class extends BaseOrderCreator {
9213
- create(values) {
9214
- const data = this.baseOrder(values);
9215
- delete data["order_price"];
9216
- delete data["total"];
9217
- delete data["trigger_price"];
9218
- delete data["isStopOrder"];
9219
- return {
9220
- ...data
9221
- };
9917
+ constructor() {
9918
+ super(...arguments);
9919
+ this.slippageValidationStrategy = new SlippageValidationStrategy();
9920
+ this.validationChain = new ValidationChain().addValidator(new QuantityValidator()).addValidator(new SlippageValidator());
9921
+ this.orderType = OrderType.MARKET;
9222
9922
  }
9223
- validate(values, configs) {
9224
- return this.baseValidate(values, configs).then((result) => {
9225
- const slippage = Number(values.slippage);
9226
- const estSlippage = Number.isNaN(configs.estSlippage) ? 0 : Number(configs.estSlippage) * 100;
9227
- if (!isNaN(slippage) && estSlippage > slippage) {
9228
- return {
9229
- ...result,
9230
- slippage: {
9231
- type: "max",
9232
- message: "Estimated slippage exceeds your maximum allowed slippage.",
9233
- value: estSlippage
9234
- }
9235
- };
9236
- }
9237
- return result;
9238
- });
9923
+ /**
9924
+ * Builds the market order
9925
+ * Implements template method hook
9926
+ */
9927
+ buildOrder(values, config) {
9928
+ const orderlyValues = values;
9929
+ const data = this.baseOrder(orderlyValues);
9930
+ const result = { ...data };
9931
+ delete result.order_price;
9932
+ delete result.total;
9933
+ delete result.trigger_price;
9934
+ if ("isStopOrder" in result) {
9935
+ delete result.isStopOrder;
9936
+ }
9937
+ return result;
9938
+ }
9939
+ /**
9940
+ * Runs validations using validation chain
9941
+ * Implements template method hook
9942
+ */
9943
+ runValidations(values, configs) {
9944
+ const orderlyValues = values;
9945
+ const result = this.baseValidate(orderlyValues, configs);
9946
+ const chainErrors = this.validationChain.validate(orderlyValues, configs);
9947
+ Object.assign(result, chainErrors);
9948
+ return result;
9239
9949
  }
9240
9950
  };
9241
9951
 
@@ -9267,14 +9977,26 @@ var FOKOrderCreator = class extends LimitOrderCreator {
9267
9977
 
9268
9978
  // src/services/orderCreator/generalCreator.ts
9269
9979
  var GeneralOrderCreator = class extends BaseOrderCreator {
9270
- create(data) {
9980
+ constructor() {
9981
+ super(...arguments);
9982
+ this.orderType = void 0;
9983
+ }
9984
+ /**
9985
+ * Builds the general order
9986
+ * Implements template method hook
9987
+ */
9988
+ buildOrder(data) {
9271
9989
  return {
9272
9990
  ...this.baseOrder(data),
9273
9991
  order_price: data.order_price,
9274
9992
  order_quantity: data.order_quantity
9275
9993
  };
9276
9994
  }
9277
- validate(values, configs) {
9995
+ /**
9996
+ * Runs base validations
9997
+ * Implements template method hook
9998
+ */
9999
+ runValidations(values, configs) {
9278
10000
  return super.baseValidate(values, configs);
9279
10001
  }
9280
10002
  };
@@ -9383,7 +10105,8 @@ function calcScaledOrderBatchBody(order, symbolInfo) {
9383
10105
  distribution_type,
9384
10106
  skew,
9385
10107
  reduce_only,
9386
- visible_quantity
10108
+ visible_quantity,
10109
+ margin_mode
9387
10110
  } = order;
9388
10111
  const prices = calcScaledOrderPrices({
9389
10112
  start_price,
@@ -9409,6 +10132,8 @@ function calcScaledOrderBatchBody(order, symbolInfo) {
9409
10132
  order_quantity: qtys[index],
9410
10133
  order_price: price,
9411
10134
  reduce_only,
10135
+ margin_mode: margin_mode || MarginMode.CROSS,
10136
+ // Default to CROSS if not provided for backward compatibility
9412
10137
  // it will be used for identify the scaled order from ws
9413
10138
  client_order_id: `scaled_${index}_${now}`
9414
10139
  };
@@ -9512,7 +10237,11 @@ var ScaledOrderCreator = class extends BaseOrderCreator {
9512
10237
  super(...arguments);
9513
10238
  this.orderType = OrderType.SCALED;
9514
10239
  }
9515
- create(values, config) {
10240
+ /**
10241
+ * Builds the scaled order
10242
+ * Implements template method hook
10243
+ */
10244
+ buildOrder(values, config) {
9516
10245
  const orders = calcScaledOrderBatchBody(values, config.symbol);
9517
10246
  const { total_orders, distribution_type, skew } = values;
9518
10247
  const order = {
@@ -9530,6 +10259,7 @@ var ScaledOrderCreator = class extends BaseOrderCreator {
9530
10259
  "reduce_only",
9531
10260
  "side",
9532
10261
  "order_type",
10262
+ "margin_mode",
9533
10263
  "orders",
9534
10264
  "total_orders",
9535
10265
  "distribution_type",
@@ -9538,7 +10268,11 @@ var ScaledOrderCreator = class extends BaseOrderCreator {
9538
10268
  order
9539
10269
  );
9540
10270
  }
9541
- async validate(values, config) {
10271
+ /**
10272
+ * Runs validations for scaled order
10273
+ * Implements template method hook
10274
+ */
10275
+ runValidations(values, config) {
9542
10276
  const { maxQty, askAndBid, markPrice, symbol } = config;
9543
10277
  const { base_dp, quote_dp } = config.symbol;
9544
10278
  const { order_quantity, total, total_orders, distribution_type, skew } = values;
@@ -9607,22 +10341,22 @@ function validatePrice(values, config) {
9607
10341
  if (!config.markPrice) {
9608
10342
  return errors;
9609
10343
  }
9610
- const { minPrice: minPrice3, maxPrice: maxPrice3 } = getPriceRange({
10344
+ const { minPrice, maxPrice } = getPriceRange({
9611
10345
  side: values.side,
9612
10346
  basePrice: config.markPrice,
9613
10347
  symbolInfo: config.symbol
9614
10348
  });
9615
10349
  const comparePrice = (key, value) => {
9616
10350
  const price = new Decimal(value || 0);
9617
- if (price.lt(minPrice3)) {
10351
+ if (price.lt(minPrice)) {
9618
10352
  errors[key] = OrderValidation.min(
9619
10353
  key,
9620
- new Decimal(minPrice3).todp(quote_dp).toString()
10354
+ new Decimal(minPrice).todp(quote_dp).toString()
9621
10355
  );
9622
- } else if (price.gt(maxPrice3)) {
10356
+ } else if (price.gt(maxPrice)) {
9623
10357
  errors[key] = OrderValidation.max(
9624
10358
  key,
9625
- new Decimal(maxPrice3).todp(quote_dp).toString()
10359
+ new Decimal(maxPrice).todp(quote_dp).toString()
9626
10360
  );
9627
10361
  }
9628
10362
  };
@@ -9630,13 +10364,40 @@ function validatePrice(values, config) {
9630
10364
  comparePrice("end_price", end_price);
9631
10365
  return errors;
9632
10366
  }
9633
- var { maxPrice: maxPrice2, minPrice: minPrice2, scopePrice: scopePrice2 } = order;
10367
+
10368
+ // src/services/orderCreator/validators/TriggerPriceValidator.ts
10369
+ var TriggerPriceValidator = class extends BaseValidator {
10370
+ constructor() {
10371
+ super();
10372
+ this.strategy = new TriggerPriceValidationStrategy();
10373
+ }
10374
+ doValidate(values, config) {
10375
+ return this.strategy.validate(
10376
+ {
10377
+ trigger_price: values.trigger_price
10378
+ },
10379
+ config
10380
+ );
10381
+ }
10382
+ getFieldName() {
10383
+ return "trigger_price";
10384
+ }
10385
+ };
10386
+
10387
+ // src/services/orderCreator/stopLimitOrderCreator.ts
9634
10388
  var StopLimitOrderCreator = class extends BaseOrderCreator {
9635
10389
  constructor() {
9636
10390
  super(...arguments);
10391
+ this.priceValidationStrategy = new PriceValidationStrategy();
10392
+ this.triggerPriceValidationStrategy = new TriggerPriceValidationStrategy();
10393
+ this.validationChain = new ValidationChain().addValidator(new QuantityValidator()).addValidator(new TriggerPriceValidator()).addValidator(new PriceValidator());
9637
10394
  this.orderType = OrderType.STOP_LIMIT;
9638
10395
  }
9639
- create(values, config) {
10396
+ /**
10397
+ * Builds the stop-limit order
10398
+ * Implements template method hook
10399
+ */
10400
+ buildOrder(values, config) {
9640
10401
  this.totalToQuantity(values, config);
9641
10402
  const order = {
9642
10403
  ...this.baseOrder(values),
@@ -9658,84 +10419,82 @@ var StopLimitOrderCreator = class extends BaseOrderCreator {
9658
10419
  "trigger_price_type",
9659
10420
  "side",
9660
10421
  "reduce_only",
10422
+ "margin_mode",
9661
10423
  "visible_quantity"
9662
10424
  ],
9663
10425
  order
9664
10426
  );
9665
10427
  }
9666
- validate(values, config) {
9667
- return this.baseValidate(values, config).then((errors) => {
9668
- const { order_price, trigger_price, side } = values;
9669
- const { symbol } = config;
9670
- const { price_range, price_scope, quote_max, quote_min } = symbol;
9671
- if (!order_price) {
9672
- errors.order_price = OrderValidation.required("order_price");
9673
- }
9674
- if (!trigger_price) {
9675
- errors.trigger_price = OrderValidation.required("trigger_price");
9676
- } else {
9677
- if (trigger_price > quote_max) {
9678
- errors.trigger_price = OrderValidation.max(
9679
- "trigger_price",
9680
- quote_max
9681
- );
9682
- } else if (trigger_price < quote_min || trigger_price == 0) {
9683
- errors.trigger_price = OrderValidation.min(
9684
- "trigger_price",
9685
- quote_min
9686
- );
9687
- } else if (order_price) {
9688
- const price = new Decimal(order_price);
9689
- const maxPriceNumber = maxPrice2(trigger_price, price_range);
9690
- const minPriceNumber = minPrice2(trigger_price, price_range);
9691
- const scropePriceNumbere = scopePrice2(
9692
- trigger_price,
9693
- price_scope,
9694
- side
9695
- );
9696
- const priceRange = side === "BUY" ? {
9697
- min: scropePriceNumbere,
9698
- max: maxPriceNumber
9699
- } : {
9700
- min: minPriceNumber,
9701
- max: scropePriceNumbere
9702
- };
9703
- if (price.gt(quote_max)) {
9704
- errors.order_price = OrderValidation.max("order_price", quote_max);
9705
- } else {
9706
- if (price.gt(priceRange?.max)) {
9707
- errors.order_price = OrderValidation.max(
9708
- "order_price",
9709
- new Decimal(priceRange.max).todp(symbol.quote_dp).toString()
9710
- );
9711
- }
9712
- }
9713
- if (price.lt(quote_min)) {
9714
- errors.order_price = OrderValidation.min("order_price", quote_min);
9715
- } else {
9716
- if (price.lt(priceRange?.min)) {
9717
- errors.order_price = OrderValidation.min(
9718
- "order_price",
9719
- new Decimal(priceRange.min).todp(symbol.quote_dp).toString()
9720
- );
9721
- }
9722
- }
9723
- }
10428
+ /**
10429
+ * Runs validations using validation chain
10430
+ * Implements template method hook
10431
+ */
10432
+ runValidations(values, config) {
10433
+ const orderlyValues = values;
10434
+ const errors = this.baseValidate(orderlyValues, config);
10435
+ const triggerPrice = values.trigger_price;
10436
+ if (!triggerPrice || triggerPrice === 0) {
10437
+ errors.trigger_price = OrderValidation.required("trigger_price");
10438
+ } else {
10439
+ const triggerError = this.triggerPriceValidationStrategy.validate(
10440
+ { trigger_price: triggerPrice },
10441
+ config
10442
+ );
10443
+ if (triggerError) {
10444
+ errors.trigger_price = triggerError;
10445
+ }
10446
+ }
10447
+ const valuesWithFields = values;
10448
+ const orderPrice = valuesWithFields.order_price ?? valuesWithFields.price;
10449
+ const triggerPriceForPriceValidation = triggerPrice;
10450
+ const side = valuesWithFields.side ?? orderlyValues.side;
10451
+ if (!orderPrice) {
10452
+ errors.order_price = OrderValidation.required("order_price");
10453
+ } else if (orderPrice && triggerPriceForPriceValidation && side) {
10454
+ const modifiedConfig = {
10455
+ ...config,
10456
+ markPrice: Number(triggerPriceForPriceValidation)
10457
+ };
10458
+ const priceError = this.priceValidationStrategy.validate(
10459
+ {
10460
+ order_price: orderPrice,
10461
+ side,
10462
+ order_type: OrderType.LIMIT
10463
+ },
10464
+ modifiedConfig
10465
+ );
10466
+ if (priceError) {
10467
+ errors.order_price = priceError;
9724
10468
  }
9725
- return errors;
9726
- });
10469
+ } else if (orderPrice && triggerPriceForPriceValidation) {
10470
+ const { quote_max, quote_min } = config.symbol;
10471
+ const price = Number(orderPrice);
10472
+ if (quote_max && price > quote_max) {
10473
+ errors.order_price = OrderValidation.max("order_price", quote_max);
10474
+ } else if (quote_min && price < quote_min) {
10475
+ errors.order_price = OrderValidation.min("order_price", quote_min);
10476
+ }
10477
+ }
10478
+ return errors;
9727
10479
  }
9728
10480
  };
9729
10481
  var StopMarketOrderCreator = class extends BaseOrderCreator {
9730
- create(values) {
10482
+ constructor() {
10483
+ super(...arguments);
10484
+ this.triggerPriceValidationStrategy = new TriggerPriceValidationStrategy();
10485
+ this.validationChain = new ValidationChain().addValidator(new QuantityValidator()).addValidator(new TriggerPriceValidator());
10486
+ }
10487
+ /**
10488
+ * Builds the stop-market order
10489
+ * Implements template method hook
10490
+ */
10491
+ buildOrder(values) {
9731
10492
  const order = {
9732
10493
  ...this.baseOrder(values),
9733
- // order_price: values.order_price,
9734
10494
  trigger_price: values.trigger_price,
9735
10495
  algo_type: AlgoOrderRootType.STOP,
9736
10496
  type: OrderType.MARKET,
9737
10497
  quantity: values["order_quantity"],
9738
- // price: values["order_price"],
9739
10498
  trigger_price_type: TriggerPriceType.MARK_PRICE
9740
10499
  };
9741
10500
  return pick(
@@ -9745,225 +10504,40 @@ var StopMarketOrderCreator = class extends BaseOrderCreator {
9745
10504
  "algo_type",
9746
10505
  "type",
9747
10506
  "quantity",
9748
- // "price",
9749
10507
  "trigger_price_type",
9750
10508
  "side",
9751
10509
  "reduce_only",
10510
+ "margin_mode",
9752
10511
  "visible_quantity"
9753
10512
  ],
9754
10513
  order
9755
10514
  );
9756
10515
  }
9757
- validate(values, config) {
9758
- return this.baseValidate(values, config).then((errors) => {
9759
- const { trigger_price } = values;
9760
- const { symbol } = config;
9761
- const { quote_max, quote_min } = symbol;
9762
- if (!trigger_price) {
9763
- errors.trigger_price = OrderValidation.required("trigger_price");
9764
- } else if (trigger_price > quote_max) {
9765
- errors.trigger_price = OrderValidation.max("trigger_price", quote_max);
9766
- } else if (trigger_price < quote_min || trigger_price == 0) {
9767
- errors.trigger_price = OrderValidation.min("trigger_price", quote_min);
9768
- }
9769
- return errors;
9770
- });
10516
+ /**
10517
+ * Runs validations using validation chain
10518
+ * Implements template method hook
10519
+ */
10520
+ runValidations(values, config) {
10521
+ const errors = this.baseValidate(values, config);
10522
+ const chainErrors = this.validationChain.validate(values, config);
10523
+ Object.assign(errors, chainErrors);
10524
+ return errors;
9771
10525
  }
9772
10526
  };
9773
- var formatPrice2 = (price, quote_dp) => {
9774
- return new Decimal(price).toDecimalPlaces(quote_dp).toNumber();
9775
- };
10527
+
10528
+ // src/services/orderCreator/baseAlgoCreator.ts
9776
10529
  var BaseAlgoOrderCreator = class {
10530
+ constructor() {
10531
+ this.tpslValidationStrategy = new TPSLValidationStrategy();
10532
+ }
9777
10533
  /**
9778
- * base validate
10534
+ * Validates TP/SL order using TPSLValidationStrategy
10535
+ * Consolidates validation logic from baseBracketOrderCreator
9779
10536
  */
9780
10537
  validate(values, config) {
9781
- const result = /* @__PURE__ */ Object.create(null);
9782
- return Promise.resolve().then(() => {
9783
- const {
9784
- tp_trigger_price,
9785
- sl_trigger_price,
9786
- side,
9787
- // tp_enable,
9788
- // sl_enable,
9789
- tp_order_type,
9790
- sl_order_type,
9791
- tp_order_price,
9792
- sl_order_price
9793
- } = values;
9794
- const qty = Number(values.quantity);
9795
- const maxQty = config.maxQty;
9796
- const orderType = values.order_type;
9797
- const {
9798
- quote_max,
9799
- quote_min,
9800
- price_scope,
9801
- quote_dp,
9802
- base_min,
9803
- min_notional
9804
- } = config.symbol ?? {};
9805
- if (!isNaN(qty) && qty > maxQty) {
9806
- result.quantity = OrderValidation.max("quantity", config.maxQty);
9807
- }
9808
- if (!isNaN(qty) && qty < base_min) {
9809
- result.quantity = OrderValidation.min("quantity", base_min);
9810
- }
9811
- if (Number(tp_trigger_price) < 0) {
9812
- result.tp_trigger_price = OrderValidation.min("tp_trigger_price", 0);
9813
- }
9814
- if (Number(sl_trigger_price) < 0) {
9815
- result.sl_trigger_price = OrderValidation.min("sl_trigger_price", 0);
9816
- }
9817
- if (tp_order_type === OrderType.LIMIT && !tp_order_price) {
9818
- result.tp_order_price = OrderValidation.required("tp_order_price");
9819
- }
9820
- if (sl_order_type === OrderType.LIMIT && !sl_order_price) {
9821
- result.sl_order_price = OrderValidation.required("sl_order_price");
9822
- }
9823
- const mark_price = orderType === OrderType.MARKET || orderType == null ? config.markPrice : values.order_price ? Number(values.order_price) : void 0;
9824
- const tpslSide = side === OrderSide.BUY ? OrderSide.SELL : OrderSide.BUY;
9825
- if (side === OrderSide.BUY && mark_price) {
9826
- if (!!sl_trigger_price && Number(sl_trigger_price) < quote_min) {
9827
- result.sl_trigger_price = OrderValidation.min(
9828
- "sl_trigger_price",
9829
- formatPrice2(quote_min, quote_dp)
9830
- );
9831
- }
9832
- if (!!sl_trigger_price && Number(sl_trigger_price) >= mark_price) {
9833
- result.sl_trigger_price = OrderValidation.max(
9834
- "sl_trigger_price",
9835
- formatPrice2(mark_price, quote_dp)
9836
- );
9837
- }
9838
- if (!!tp_trigger_price && Number(tp_trigger_price) <= mark_price) {
9839
- result.tp_trigger_price = OrderValidation.min(
9840
- "tp_trigger_price",
9841
- formatPrice2(mark_price, quote_dp)
9842
- );
9843
- }
9844
- if (!!tp_trigger_price && Number(tp_trigger_price) > quote_max) {
9845
- result.tp_trigger_price = OrderValidation.max(
9846
- "tp_trigger_price",
9847
- formatPrice2(quote_max, quote_dp)
9848
- );
9849
- }
9850
- if (sl_trigger_price && sl_order_price) {
9851
- const slOrderPriceRange = getPriceRange({
9852
- side: tpslSide,
9853
- basePrice: Number(sl_trigger_price),
9854
- symbolInfo: config.symbol
9855
- });
9856
- if (Number(sl_order_price) < slOrderPriceRange.minPrice) {
9857
- result.sl_order_price = OrderValidation.min(
9858
- "sl_order_price",
9859
- formatPrice2(slOrderPriceRange.minPrice, quote_dp)
9860
- );
9861
- }
9862
- if (Number(sl_order_price) > slOrderPriceRange.maxPrice) {
9863
- result.sl_order_price = OrderValidation.max(
9864
- "sl_order_price",
9865
- formatPrice2(slOrderPriceRange.maxPrice, quote_dp)
9866
- );
9867
- }
9868
- if (Number(sl_trigger_price) < Number(sl_order_price)) {
9869
- result.sl_trigger_price = OrderValidation.priceErrorMax("sl_trigger_price");
9870
- }
9871
- }
9872
- if (tp_trigger_price && tp_order_price) {
9873
- const tpOrderPriceRange = getPriceRange({
9874
- side: tpslSide,
9875
- basePrice: Number(tp_trigger_price),
9876
- symbolInfo: config.symbol
9877
- });
9878
- if (Number(tp_order_price) > tpOrderPriceRange.maxPrice) {
9879
- result.tp_order_price = OrderValidation.max(
9880
- "tp_order_price",
9881
- formatPrice2(tpOrderPriceRange.maxPrice, quote_dp)
9882
- );
9883
- }
9884
- if (Number(tp_order_price) < tpOrderPriceRange.minPrice) {
9885
- result.tp_order_price = OrderValidation.min(
9886
- "tp_order_price",
9887
- formatPrice2(tpOrderPriceRange.minPrice, quote_dp)
9888
- );
9889
- }
9890
- if (Number(tp_trigger_price) > Number(tp_order_price)) {
9891
- result.tp_trigger_price = OrderValidation.priceErrorMin("tp_trigger_price");
9892
- }
9893
- }
9894
- }
9895
- if (side === OrderSide.SELL && mark_price) {
9896
- if (!!sl_trigger_price && Number(sl_trigger_price) > quote_max) {
9897
- result.sl_trigger_price = OrderValidation.max(
9898
- "sl_trigger_price",
9899
- formatPrice2(quote_max, quote_dp)
9900
- );
9901
- }
9902
- if (!!sl_trigger_price && Number(sl_trigger_price) <= mark_price) {
9903
- result.sl_trigger_price = OrderValidation.min(
9904
- "sl_trigger_price",
9905
- formatPrice2(mark_price, quote_dp)
9906
- );
9907
- }
9908
- if (!!tp_trigger_price && Number(tp_trigger_price) >= mark_price) {
9909
- result.tp_trigger_price = OrderValidation.max(
9910
- "tp_trigger_price",
9911
- formatPrice2(mark_price, quote_dp)
9912
- );
9913
- }
9914
- if (!!tp_trigger_price && Number(tp_trigger_price) < quote_min) {
9915
- result.tp_trigger_price = OrderValidation.min(
9916
- "tp_trigger_price",
9917
- formatPrice2(quote_min, quote_dp)
9918
- );
9919
- }
9920
- if (sl_trigger_price && sl_order_price) {
9921
- const slOrderPriceRange = getPriceRange({
9922
- side: tpslSide,
9923
- basePrice: Number(sl_trigger_price),
9924
- symbolInfo: config.symbol
9925
- });
9926
- if (Number(sl_order_price) < slOrderPriceRange.minPrice) {
9927
- result.sl_order_price = OrderValidation.min(
9928
- "sl_order_price",
9929
- formatPrice2(slOrderPriceRange.minPrice, quote_dp)
9930
- );
9931
- }
9932
- if (Number(sl_order_price) > slOrderPriceRange.maxPrice) {
9933
- result.sl_order_price = OrderValidation.max(
9934
- "sl_order_price",
9935
- formatPrice2(slOrderPriceRange.maxPrice, quote_dp)
9936
- );
9937
- }
9938
- if (Number(sl_trigger_price) > Number(sl_order_price)) {
9939
- result.sl_trigger_price = OrderValidation.priceErrorMin("sl_trigger_price");
9940
- }
9941
- }
9942
- if (tp_trigger_price && tp_order_price) {
9943
- const tpOrderPriceRange = getPriceRange({
9944
- side: tpslSide,
9945
- basePrice: Number(tp_trigger_price),
9946
- symbolInfo: config.symbol
9947
- });
9948
- if (Number(tp_order_price) < tpOrderPriceRange.minPrice) {
9949
- result.tp_order_price = OrderValidation.min(
9950
- "tp_order_price",
9951
- formatPrice2(tpOrderPriceRange.minPrice, quote_dp)
9952
- );
9953
- }
9954
- if (Number(tp_order_price) > tpOrderPriceRange.maxPrice) {
9955
- result.tp_order_price = OrderValidation.max(
9956
- "tp_order_price",
9957
- formatPrice2(tpOrderPriceRange.maxPrice, quote_dp)
9958
- );
9959
- }
9960
- if (Number(tp_trigger_price) < Number(tp_order_price)) {
9961
- result.tp_trigger_price = OrderValidation.priceErrorMax("tp_trigger_price");
9962
- }
9963
- }
9964
- }
9965
- return Object.keys(result).length > 0 ? result : null;
9966
- });
10538
+ return Promise.resolve(
10539
+ this.tpslValidationStrategy.validate(values, config)
10540
+ );
9967
10541
  }
9968
10542
  };
9969
10543
 
@@ -10016,7 +10590,9 @@ var TPSLOrderCreator = class extends BaseAlgoOrderCreator {
10016
10590
  reduce_only: true,
10017
10591
  quantity: values.quantity,
10018
10592
  symbol: values.symbol,
10019
- child_orders
10593
+ child_orders,
10594
+ // Include margin_mode for isolated/cross margin support; default CROSS per order entry pattern
10595
+ margin_mode: values.margin_mode || MarginMode.CROSS
10020
10596
  };
10021
10597
  }
10022
10598
  crateUpdateOrder(values, oldValue, config) {
@@ -10118,7 +10694,9 @@ var TPSLPositionOrderCreator = class extends BaseAlgoOrderCreator {
10118
10694
  trigger_price_type: TriggerPriceType.MARK_PRICE,
10119
10695
  // reduce_only: true,
10120
10696
  symbol: values.symbol,
10121
- child_orders
10697
+ child_orders,
10698
+ // Include margin_mode for isolated/cross margin support; default CROSS per order entry pattern
10699
+ margin_mode: values.margin_mode || MarginMode.CROSS
10122
10700
  };
10123
10701
  }
10124
10702
  crateUpdateOrder(values, oldValue, config) {
@@ -10155,7 +10733,11 @@ var TrailingStopOrderCreator = class extends BaseOrderCreator {
10155
10733
  super(...arguments);
10156
10734
  this.orderType = OrderType.TRAILING_STOP;
10157
10735
  }
10158
- create(values, config) {
10736
+ /**
10737
+ * Builds the trailing stop order
10738
+ * Implements template method hook
10739
+ */
10740
+ buildOrder(values, config) {
10159
10741
  const { order_quantity, activated_price, callback_value, callback_rate } = values;
10160
10742
  const order = {
10161
10743
  ...this.baseOrder(values),
@@ -10176,6 +10758,7 @@ var TrailingStopOrderCreator = class extends BaseOrderCreator {
10176
10758
  "quantity",
10177
10759
  "side",
10178
10760
  "reduce_only",
10761
+ "margin_mode",
10179
10762
  "visible_quantity",
10180
10763
  "activated_price",
10181
10764
  "callback_value",
@@ -10184,28 +10767,34 @@ var TrailingStopOrderCreator = class extends BaseOrderCreator {
10184
10767
  order
10185
10768
  );
10186
10769
  }
10187
- async validate(values, config) {
10770
+ /**
10771
+ * Runs validations for trailing stop order
10772
+ * Implements template method hook
10773
+ */
10774
+ runValidations(values, config) {
10188
10775
  const { markPrice, symbol } = config;
10189
10776
  const { quote_dp } = config.symbol;
10190
10777
  const { side, activated_price, callback_value, callback_rate } = values;
10191
- const errors = await this.baseValidate(values, config);
10778
+ const errors = this.baseValidate(values, config);
10192
10779
  if (activated_price) {
10193
- const { minPrice: minPrice3, maxPrice: maxPrice3 } = getPriceRange2({
10780
+ const { minPrice, maxPrice } = getPriceRange({
10194
10781
  side,
10195
- markPrice,
10782
+ basePrice: markPrice,
10196
10783
  symbolInfo: symbol
10197
10784
  });
10198
- const activatedPrice = new Decimal(activated_price);
10199
- if (activatedPrice.lt(minPrice3) || activatedPrice.equals(0)) {
10200
- errors.activated_price = OrderValidation.min(
10201
- "activated_price",
10202
- new Decimal(minPrice3).todp(quote_dp).toString()
10203
- );
10204
- } else if (activatedPrice.gt(maxPrice3)) {
10205
- errors.activated_price = OrderValidation.max(
10206
- "activated_price",
10207
- new Decimal(maxPrice3).todp(quote_dp).toString()
10208
- );
10785
+ if (!isNaN(minPrice) && !isNaN(maxPrice)) {
10786
+ const activatedPrice = new Decimal(activated_price);
10787
+ if (activatedPrice.lt(minPrice) || activatedPrice.equals(0)) {
10788
+ errors.activated_price = OrderValidation.min(
10789
+ "activated_price",
10790
+ new Decimal(minPrice).todp(quote_dp).toString()
10791
+ );
10792
+ } else if (activatedPrice.gt(maxPrice)) {
10793
+ errors.activated_price = OrderValidation.max(
10794
+ "activated_price",
10795
+ new Decimal(maxPrice).todp(quote_dp).toString()
10796
+ );
10797
+ }
10209
10798
  }
10210
10799
  }
10211
10800
  if (!callback_value && !callback_rate) {
@@ -10237,23 +10826,6 @@ var TrailingStopOrderCreator = class extends BaseOrderCreator {
10237
10826
  return errors;
10238
10827
  }
10239
10828
  };
10240
- function getPriceRange2(inputs) {
10241
- const { markPrice, side, symbolInfo } = inputs;
10242
- const { quote_min, quote_max } = symbolInfo;
10243
- const priceRange = side === OrderSide.BUY ? {
10244
- min: quote_min,
10245
- max: markPrice
10246
- } : {
10247
- min: markPrice,
10248
- max: quote_max
10249
- };
10250
- const minPrice3 = Math.max(quote_min, priceRange?.min);
10251
- const maxPrice3 = Math.min(quote_max, priceRange?.max);
10252
- return {
10253
- minPrice: minPrice3,
10254
- maxPrice: maxPrice3
10255
- };
10256
- }
10257
10829
 
10258
10830
  // src/services/orderCreator/factory.ts
10259
10831
  var OrderFactory = class {
@@ -10376,27 +10948,63 @@ var calcEstLiqPrice = (order$1, askAndBid, inputs) => {
10376
10948
  markPrice,
10377
10949
  totalCollateral,
10378
10950
  futures_taker_fee_rate,
10379
- positions: positions3
10951
+ positions: positions3,
10952
+ // leverage,
10953
+ sumUnitaryFunding
10380
10954
  } = inputs;
10381
10955
  const orderFee = order.orderFee({
10382
10956
  qty: quantity,
10383
10957
  price,
10384
10958
  futuresTakeFeeRate: Number(futures_taker_fee_rate) / 1e4
10385
10959
  });
10386
- const liqPrice = order.estLiqPrice({
10387
- markPrice,
10388
- baseIMR: symbolInfo.base_imr,
10389
- baseMMR: symbolInfo.base_mmr,
10390
- totalCollateral,
10391
- positions: positions3 == null ? [] : positions3,
10392
- IMR_Factor: imr_factor,
10393
- orderFee,
10394
- newOrder: {
10395
- qty: quantity,
10396
- price,
10397
- symbol
10960
+ let liqPrice = 0;
10961
+ if (order$1.margin_mode === MarginMode.CROSS) {
10962
+ liqPrice = order.estLiqPrice({
10963
+ markPrice,
10964
+ baseIMR: symbolInfo.base_imr,
10965
+ baseMMR: symbolInfo.base_mmr,
10966
+ totalCollateral,
10967
+ positions: positions3 == null ? [] : positions3,
10968
+ IMR_Factor: imr_factor,
10969
+ orderFee,
10970
+ newOrder: {
10971
+ qty: quantity,
10972
+ price,
10973
+ symbol
10974
+ }
10975
+ });
10976
+ } else {
10977
+ let isolatedPositionMargin = 0, costPosition = 0, positionQty = 0, lastSumUnitaryFunding = 0, leverage = 1;
10978
+ if (positions3) {
10979
+ const position = positions3.find(
10980
+ (p) => p.symbol === symbol && p.margin_mode === MarginMode.ISOLATED
10981
+ );
10982
+ if (position) {
10983
+ isolatedPositionMargin = position.margin ?? 0;
10984
+ costPosition = position.cost_position ?? 0;
10985
+ positionQty = position.position_qty ?? 0;
10986
+ lastSumUnitaryFunding = position.last_sum_unitary_funding ?? 0;
10987
+ leverage = position.leverage ? Number(position.leverage) : 1;
10988
+ }
10398
10989
  }
10399
- });
10990
+ liqPrice = order.estLiqPriceIsolated({
10991
+ isolatedPositionMargin,
10992
+ costPosition,
10993
+ positionQty,
10994
+ sumUnitaryFunding,
10995
+ lastSumUnitaryFunding,
10996
+ markPrice,
10997
+ baseIMR: symbolInfo.base_imr,
10998
+ baseMMR: symbolInfo.base_mmr,
10999
+ IMR_Factor: imr_factor,
11000
+ leverage,
11001
+ newOrder: {
11002
+ symbol,
11003
+ qty: quantity,
11004
+ price
11005
+ }
11006
+ });
11007
+ }
10400
11008
  if (liqPrice <= 0) return null;
10401
11009
  return liqPrice;
10402
11010
  };
@@ -10454,7 +11062,9 @@ var useTaskProfitAndStopLossInternal = (position, options) => {
10454
11062
  // sl_enable: isEditing
10455
11063
  // ? checkIsEnableTpSL(options?.defaultOrder).sl_enable
10456
11064
  // : options?.tpslEnable?.sl_enable,
10457
- position_type: options?.positionType
11065
+ position_type: options?.positionType,
11066
+ // Use defaultOrder.margin_mode when editing; otherwise position.margin_mode; default CROSS for backward compatibility
11067
+ margin_mode: options?.defaultOrder?.margin_mode ?? position?.margin_mode ?? MarginMode.CROSS
10458
11068
  });
10459
11069
  const symbolInfo = useSymbolsInfo()[position.symbol]();
10460
11070
  const { data: markPrice } = useMarkPrice(order.symbol);
@@ -10757,11 +11367,53 @@ var useMaxLeverage = (symbol) => {
10757
11367
  };
10758
11368
  var useSymbolLeverage = (symbol) => {
10759
11369
  const symbolInfo = useSymbolInfo(symbol);
10760
- const [update, { isMutating }] = useMutation("/v1/client/leverage");
11370
+ const { state } = useAccount();
11371
+ const [updateMutation, { isMutating }] = useMutation("/v1/client/leverage");
10761
11372
  const maxLeverage = useMemo(() => {
10762
11373
  const baseIMR = symbolInfo?.("base_imr");
10763
11374
  return baseIMR ? 1 / baseIMR : 1;
10764
11375
  }, [symbolInfo]);
11376
+ const update = async (data) => {
11377
+ const result = await updateMutation(data);
11378
+ if (result?.success && data.symbol && state.accountId) {
11379
+ const key = ["/v1/client/leverages", state.accountId];
11380
+ mutate(
11381
+ key,
11382
+ (prev) => {
11383
+ if (!prev) {
11384
+ return [
11385
+ {
11386
+ symbol: data.symbol,
11387
+ leverage: data.leverage,
11388
+ margin_mode: data.margin_mode
11389
+ }
11390
+ ];
11391
+ }
11392
+ const index = prev.findIndex(
11393
+ (item) => item.symbol === data.symbol && (item.margin_mode ?? MarginMode.CROSS) === (data.margin_mode ?? MarginMode.CROSS)
11394
+ );
11395
+ if (index === -1) {
11396
+ return [
11397
+ ...prev,
11398
+ {
11399
+ symbol: data.symbol,
11400
+ leverage: data.leverage,
11401
+ margin_mode: data.margin_mode
11402
+ }
11403
+ ];
11404
+ }
11405
+ const next = [...prev];
11406
+ next[index] = {
11407
+ ...next[index],
11408
+ leverage: data.leverage
11409
+ };
11410
+ return next;
11411
+ },
11412
+ { revalidate: false }
11413
+ );
11414
+ }
11415
+ return result;
11416
+ };
10765
11417
  return {
10766
11418
  maxLeverage,
10767
11419
  update,
@@ -11498,8 +12150,8 @@ function useOrderEntry(symbolOrOrder, sideOrOptions, reduceOnly, options) {
11498
12150
  const keys = Object.keys(current);
11499
12151
  for (let i = 0; i < keys.length; i++) {
11500
12152
  const k = keys[i];
11501
- let preveValue = prev[k];
11502
- let currentValue = current[k];
12153
+ const preveValue = prev[k];
12154
+ const currentValue = current[k];
11503
12155
  if (typeof preveValue === "undefined" && typeof currentValue === "undefined")
11504
12156
  continue;
11505
12157
  if (preveValue !== currentValue) {
@@ -11512,7 +12164,12 @@ function useOrderEntry(symbolOrOrder, sideOrOptions, reduceOnly, options) {
11512
12164
  if (!key) return null;
11513
12165
  return { key, value, preValue };
11514
12166
  };
11515
- const maxQty = useMaxQty(symbol, sideValue, isReduceOnly);
12167
+ const { marginMode: symbolMarginMode } = useMarginModeBySymbol(symbol);
12168
+ const marginMode = typeof symbolOrOrder === "object" && symbolOrOrder.margin_mode ? symbolOrOrder.margin_mode : symbolMarginMode;
12169
+ const maxQty = useMaxQty(symbol, sideValue, {
12170
+ reduceOnly: isReduceOnly,
12171
+ marginMode: marginMode ?? MarginMode.CROSS
12172
+ });
11516
12173
  const parseString2Number = (order, key, dp) => {
11517
12174
  if (typeof order[key] !== "string") return;
11518
12175
  if (order[key] && order[key].startsWith(".")) {
@@ -12063,7 +12720,7 @@ var DataPaint = class extends BasePaint {
12063
12720
  );
12064
12721
  const { position, fontSize = 14 } = layout;
12065
12722
  let left = this._ratio(position.left);
12066
- let top = layout.position.top + offsetTop + this.transformTop;
12723
+ const top = layout.position.top + offsetTop + this.transformTop;
12067
12724
  let prevElementBoundingBox = {};
12068
12725
  if (typeof options.data?.position.side !== "undefined") {
12069
12726
  prevElementBoundingBox = this._drawText(options.data.position.side, {
@@ -12094,6 +12751,28 @@ var DataPaint = class extends BasePaint {
12094
12751
  fontFamily: options.fontFamily
12095
12752
  });
12096
12753
  }
12754
+ const marginMode = options.data?.position.marginMode;
12755
+ if (marginMode) {
12756
+ left += (prevElementBoundingBox.width ?? 0) + this._ratio(7);
12757
+ if (prevElementBoundingBox.width) {
12758
+ prevElementBoundingBox = this._drawText("|", {
12759
+ color: "rgba(255,255,255,0.2)",
12760
+ left,
12761
+ top: this._ratio(top),
12762
+ fontSize: this._ratio(fontSize),
12763
+ fontFamily: options.fontFamily
12764
+ });
12765
+ }
12766
+ left += (prevElementBoundingBox.width ?? 0) + this._ratio(7);
12767
+ const marginModeText = marginMode.charAt(0).toUpperCase() + marginMode.slice(1);
12768
+ prevElementBoundingBox = this._drawText(marginModeText, {
12769
+ color: layout.color,
12770
+ left,
12771
+ top: this._ratio(top),
12772
+ fontSize: this._ratio(fontSize),
12773
+ fontFamily: options.fontFamily
12774
+ });
12775
+ }
12097
12776
  if (typeof options.data?.position.leverage !== "undefined") {
12098
12777
  left += (prevElementBoundingBox.width ?? 0) + this._ratio(7);
12099
12778
  if (prevElementBoundingBox.width) {
@@ -12174,8 +12853,8 @@ var DataPaint = class extends BasePaint {
12174
12853
  (options.data?.position.informations.length ?? 0) === 2;
12175
12854
  const col = informations.length > 4 ? 3 : 2;
12176
12855
  informations.forEach((info, index) => {
12177
- let left = position.left + index % col * this.positionInfoCellWidth;
12178
- let top = position.top + Math.floor(index / col) * 38 + this.transformTop;
12856
+ const left = position.left + index % col * this.positionInfoCellWidth;
12857
+ const top = position.top + Math.floor(index / col) * 38 + this.transformTop;
12179
12858
  this._drawText(info.title, {
12180
12859
  left: this._ratio(left),
12181
12860
  top: this._ratio(top),
@@ -17717,95 +18396,111 @@ var initialOrderState = {
17717
18396
  trigger_price: "",
17718
18397
  tp_trigger_price: "",
17719
18398
  sl_trigger_price: "",
17720
- tp_order_type: OrderType.MARKET,
17721
- tp_pnl: "",
17722
- sl_pnl: "",
17723
- tp_offset_percentage: "",
17724
- sl_offset_percentage: "",
17725
- tp_offset: "",
17726
- sl_offset: "",
17727
- sl_order_type: OrderType.MARKET,
17728
18399
  total: "",
17729
- // scaled order
17730
- start_price: "",
17731
- end_price: "",
17732
- totalOrders: "",
17733
- distribution_type: "",
17734
- skew: "",
17735
- // symbol: "",
17736
- // trailing stop order
17737
- activated_price: "",
17738
- callback_value: "",
17739
- callback_rate: ""
18400
+ symbol: ""
17740
18401
  };
17741
- var useOrderStore = (initialOrder) => {
17742
- const [entry, setEntry] = useState(initialOrder);
17743
- const [estLeverage, setEstLeverage] = useState(null);
17744
- const [estLiquidationPrice, setEstLiquidationPrice] = useState(
17745
- null
17746
- );
17747
- const [errors, setErrors] = useState({});
17748
- const updateOrder = (order) => {
17749
- setEntry(
17750
- // if use {...draft, ...order} will cause extra updated when object it not changed
17751
- produce((draft) => {
17752
- Object.assign(draft, order);
17753
- })
17754
- // (prev) => ({ ...prev, ...order })
17755
- );
17756
- };
17757
- const updateOrderByKey = (key, value) => {
17758
- setEntry(
17759
- produce((draft) => {
17760
- draft[key] = value;
17761
- })
17762
- );
17763
- };
17764
- const restoreOrder = (order) => {
17765
- setEntry(produce((draft) => order));
17766
- };
17767
- const updateOrderComputed = (data) => {
17768
- setEstLeverage(data.estLeverage);
17769
- setEstLiquidationPrice(data.estLiquidationPrice);
17770
- };
17771
- const resetOrder = (order) => {
17772
- setEntry(
17773
- produce((draft) => ({
17774
- ...draft,
17775
- ...order ?? initialOrderState
17776
- }))
17777
- );
17778
- };
17779
- const hasTP_SL = () => {
17780
- return typeof entry.tp_trigger_price !== "undefined" || typeof entry.sl_trigger_price !== "undefined";
17781
- };
17782
- return {
17783
- entry,
17784
- estLeverage,
17785
- estLiquidationPrice,
17786
- errors,
18402
+ var useOrderStore = create()(
18403
+ immer((set, get3) => ({
18404
+ entry: {
18405
+ side: OrderSide.BUY,
18406
+ order_type: OrderType.LIMIT,
18407
+ ...initialOrderState
18408
+ },
18409
+ estLeverage: null,
18410
+ estLiquidationPrice: null,
18411
+ errors: {},
17787
18412
  actions: {
17788
- updateOrder,
17789
- updateOrderByKey,
17790
- restoreOrder,
17791
- updateOrderComputed,
17792
- resetOrder,
17793
- hasTP_SL
18413
+ initOrder: (symbol, options) => {
18414
+ set((state) => {
18415
+ state.entry = {
18416
+ ...initialOrderState,
18417
+ symbol,
18418
+ side: options?.side ?? OrderSide.BUY,
18419
+ order_type: options?.order_type ?? OrderType.LIMIT,
18420
+ margin_mode: options?.margin_mode ?? MarginMode.CROSS
18421
+ };
18422
+ state.estLeverage = null;
18423
+ state.estLiquidationPrice = null;
18424
+ state.errors = {};
18425
+ });
18426
+ },
18427
+ hasTP_SL: () => {
18428
+ const order = get3().entry;
18429
+ return typeof order.tp_trigger_price !== "undefined" || typeof order.sl_trigger_price !== "undefined";
18430
+ },
18431
+ updateOrderComputed: (data) => {
18432
+ set(
18433
+ (state) => {
18434
+ state.estLeverage = data.estLeverage;
18435
+ state.estLiquidationPrice = data.estLiquidationPrice;
18436
+ },
18437
+ false
18438
+ // "updateOrderComputed"
18439
+ );
18440
+ },
18441
+ updateOrder: (order) => {
18442
+ set(
18443
+ (state) => {
18444
+ state.entry = {
18445
+ ...state.entry,
18446
+ ...order
18447
+ };
18448
+ },
18449
+ false
18450
+ // "updateOrder"
18451
+ );
18452
+ },
18453
+ updateOrderByKey: (key, value) => {
18454
+ set(
18455
+ (state) => {
18456
+ state.entry[key] = value;
18457
+ },
18458
+ false
18459
+ // "updateOrderByKey"
18460
+ );
18461
+ },
18462
+ restoreOrder: (order) => {
18463
+ set(
18464
+ (state) => {
18465
+ state.entry = order;
18466
+ },
18467
+ false
18468
+ // "restoreOrder"
18469
+ );
18470
+ },
18471
+ resetOrder: (_order) => {
18472
+ set(
18473
+ (state) => {
18474
+ state.entry.order_price = "";
18475
+ state.entry.order_quantity = "";
18476
+ state.entry.trigger_price = "";
18477
+ state.entry.total = "";
18478
+ state.entry.tp_trigger_price = "";
18479
+ state.entry.tp_pnl = "";
18480
+ state.entry.tp_offset = "";
18481
+ state.entry.tp_offset_percentage = "";
18482
+ state.entry.sl_trigger_price = "";
18483
+ state.entry.sl_pnl = "";
18484
+ state.entry.sl_offset = "";
18485
+ state.entry.sl_offset_percentage = "";
18486
+ },
18487
+ true
18488
+ // "resetOrder"
18489
+ );
18490
+ }
17794
18491
  }
17795
- };
17796
- };
17797
-
17798
- // src/next/useOrderEntry/useOrderEntry.internal.ts
18492
+ }))
18493
+ );
17799
18494
  var useOrderEntryNextInternal = (symbol, options = {}) => {
17800
18495
  const { symbolInfo, symbolLeverage } = options;
17801
- const initialOrder = {
17802
- side: OrderSide.BUY,
17803
- order_type: OrderType.LIMIT,
17804
- order_price: "",
17805
- symbol,
17806
- ...options.initialOrder
17807
- };
17808
- const { actions: orderEntryActions, entry: orderEntity } = useOrderStore(initialOrder);
18496
+ const orderEntity = useOrderStore((state) => state.entry);
18497
+ const orderEntryActions = useOrderStore((state) => state.actions);
18498
+ useEffect(() => {
18499
+ orderEntryActions.initOrder(symbol, options.initialOrder);
18500
+ if (options.initialOrder) {
18501
+ orderEntryActions.updateOrder(options.initialOrder);
18502
+ }
18503
+ }, [symbol]);
17809
18504
  const calculate2 = useCallback(
17810
18505
  (values, fieldName, value, markPrice, config) => {
17811
18506
  const fieldHandler = getCalculateHandler(fieldName);
@@ -17819,17 +18514,15 @@ var useOrderEntryNextInternal = (symbol, options = {}) => {
17819
18514
  },
17820
18515
  []
17821
18516
  );
17822
- useEffect(() => {
17823
- orderEntryActions.updateOrderByKey("symbol", symbol);
17824
- }, [orderEntryActions, symbol]);
17825
18517
  const setValue = (key, value, additional) => {
17826
18518
  if (!symbolInfo) {
17827
18519
  orderEntryActions.updateOrderByKey(key, value);
17828
18520
  return;
17829
18521
  }
18522
+ const currentEntry = useOrderStore.getState().entry;
17830
18523
  const { markPrice } = additional ?? { markPrice: 0 };
17831
18524
  let newValues = calculate2(
17832
- { ...orderEntity },
18525
+ { ...currentEntry },
17833
18526
  key,
17834
18527
  value,
17835
18528
  markPrice,
@@ -17910,7 +18603,8 @@ var useOrderEntryNextInternal = (symbol, options = {}) => {
17910
18603
  orderEntryActions.updateOrder(values);
17911
18604
  return;
17912
18605
  }
17913
- let newValues = { ...orderEntity };
18606
+ const currentEntry = useOrderStore.getState().entry;
18607
+ let newValues = { ...currentEntry };
17914
18608
  Object.keys(values).forEach((key) => {
17915
18609
  newValues = calculate2(
17916
18610
  newValues,
@@ -17926,10 +18620,11 @@ var useOrderEntryNextInternal = (symbol, options = {}) => {
17926
18620
  const onMarkPriceUpdated = useCallback(
17927
18621
  (markPrice, baseOn = []) => {
17928
18622
  if (!options.symbolInfo) return;
17929
- let newValues = { ...orderEntity };
18623
+ const currentEntry = useOrderStore.getState().entry;
18624
+ let newValues = { ...currentEntry };
17930
18625
  if (baseOn.length === 0) {
17931
18626
  newValues = calculate2(
17932
- { ...orderEntity },
18627
+ { ...currentEntry },
17933
18628
  "order_price",
17934
18629
  markPrice,
17935
18630
  markPrice,
@@ -17940,7 +18635,7 @@ var useOrderEntryNextInternal = (symbol, options = {}) => {
17940
18635
  newValues = calculate2(
17941
18636
  { ...newValues },
17942
18637
  key,
17943
- orderEntity[key],
18638
+ currentEntry[key],
17944
18639
  markPrice,
17945
18640
  options.symbolInfo
17946
18641
  );
@@ -17961,7 +18656,7 @@ var useOrderEntryNextInternal = (symbol, options = {}) => {
17961
18656
  }
17962
18657
  orderEntryActions.updateOrder(newValues);
17963
18658
  },
17964
- [calculate2, options.symbolInfo, orderEntity, orderEntryActions]
18659
+ [calculate2, options.symbolInfo, orderEntryActions]
17965
18660
  );
17966
18661
  const validate = (order, creator, options2) => {
17967
18662
  return creator?.validate(order, {
@@ -18010,6 +18705,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18010
18705
  const lastChangedField = useRef();
18011
18706
  const lastOrderTypeExt = useRef();
18012
18707
  const lastLevel = useRef();
18708
+ const fundingRates = useFundingRatesStore();
18013
18709
  const calculateTPSL_baseOn = useRef({
18014
18710
  tp: "",
18015
18711
  sl: ""
@@ -18018,7 +18714,9 @@ var useOrderEntry2 = (symbol, options = {}) => {
18018
18714
  const symbolConfig = useSymbolsInfo();
18019
18715
  const accountInfo = useAccountInfo();
18020
18716
  const positions3 = usePositions();
18021
- const symbolLeverage = useLeverageBySymbol(symbol);
18717
+ const entry = useOrderStore((s) => s.entry);
18718
+ const effectiveMarginMode = options?.initialOrder?.margin_mode ?? entry?.margin_mode ?? MarginMode.CROSS;
18719
+ const symbolLeverage = useLeverageBySymbol(symbol, effectiveMarginMode);
18022
18720
  const symbolInfo = symbolConfig[symbol]();
18023
18721
  const markPrice = actions.getMarkPriceBySymbol(symbol);
18024
18722
  const { orderMetadata } = useOrderlyContext();
@@ -18040,12 +18738,39 @@ var useOrderEntry2 = (symbol, options = {}) => {
18040
18738
  const [doCreateOrder, { isMutating }] = useMutation(
18041
18739
  getCreateOrderUrl(formattedOrder)
18042
18740
  );
18043
- const maxQtyValue = useMaxQty(
18044
- symbol,
18045
- formattedOrder.side,
18046
- formattedOrder.reduce_only
18047
- );
18741
+ const bestAskBid = askAndBid.current?.[0] || [];
18742
+ const referencePriceFromOrder = bestAskBid.length >= 2 && formattedOrder.order_type && formattedOrder.side ? getOrderReferencePriceFromOrder(formattedOrder, bestAskBid) : null;
18743
+ const maxBuyQtyValue = useMaxQty(symbol, OrderSide.BUY, {
18744
+ reduceOnly: formattedOrder.reduce_only,
18745
+ marginMode: effectiveMarginMode,
18746
+ currentOrderReferencePrice: referencePriceFromOrder && referencePriceFromOrder > 0 ? referencePriceFromOrder : void 0
18747
+ });
18748
+ const maxSellQtyValue = useMaxQty(symbol, OrderSide.SELL, {
18749
+ reduceOnly: formattedOrder.reduce_only,
18750
+ marginMode: effectiveMarginMode,
18751
+ currentOrderReferencePrice: referencePriceFromOrder && referencePriceFromOrder > 0 ? referencePriceFromOrder : void 0
18752
+ });
18753
+ const maxQtyValue = formattedOrder.side === OrderSide.BUY ? maxBuyQtyValue : maxSellQtyValue;
18048
18754
  const maxQty = options.maxQty ?? maxQtyValue;
18755
+ const maxQtys = useMemo(
18756
+ () => ({
18757
+ maxBuy: formattedOrder.side === OrderSide.BUY ? (
18758
+ // @ts-ignore
18759
+ options.maxQty ?? maxBuyQtyValue
18760
+ ) : maxBuyQtyValue,
18761
+ maxSell: formattedOrder.side === OrderSide.SELL ? (
18762
+ // @ts-ignore
18763
+ options.maxQty ?? maxSellQtyValue
18764
+ ) : maxSellQtyValue
18765
+ }),
18766
+ [
18767
+ formattedOrder.side,
18768
+ maxBuyQtyValue,
18769
+ maxSellQtyValue,
18770
+ // @ts-ignore
18771
+ options.maxQty
18772
+ ]
18773
+ );
18049
18774
  const updateOrderPrice = () => {
18050
18775
  const order_type = formattedOrder.order_type;
18051
18776
  const order_type_ext = formattedOrder.order_type_ext ?? lastOrderTypeExt.current;
@@ -18134,6 +18859,9 @@ var useOrderEntry2 = (symbol, options = {}) => {
18134
18859
  };
18135
18860
  }, []);
18136
18861
  useEffect(() => {
18862
+ if (formattedOrder.symbol !== symbol) {
18863
+ return;
18864
+ }
18137
18865
  if ((formattedOrder.order_type === OrderType.MARKET || formattedOrder.order_type === OrderType.STOP_MARKET) && markPrice) {
18138
18866
  const baseOn = /* @__PURE__ */ new Set();
18139
18867
  if (lastChangedField.current) {
@@ -18147,7 +18875,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18147
18875
  }
18148
18876
  orderEntryActions.onMarkPriceChange(markPrice, Array.from(baseOn));
18149
18877
  }
18150
- }, [markPrice, formattedOrder.order_type]);
18878
+ }, [markPrice, formattedOrder.order_type, formattedOrder.symbol, symbol]);
18151
18879
  const prepareData = useCallback(() => {
18152
18880
  return {
18153
18881
  markPrice: actions.getMarkPriceBySymbol(symbol),
@@ -18256,7 +18984,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18256
18984
  }
18257
18985
  );
18258
18986
  };
18259
- const { freeCollateral, totalCollateral } = useCollateral();
18987
+ const { freeCollateral, freeCollateralUSDCOnly, totalCollateral } = useCollateral();
18260
18988
  const currentPosition = useMemo(() => {
18261
18989
  const rows = positions3 ?? [];
18262
18990
  const p = Array.isArray(rows) ? rows.find(
@@ -18269,6 +18997,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18269
18997
  if (!markPrice2 || !accountInfo || !symbolInfo) {
18270
18998
  return null;
18271
18999
  }
19000
+ const sumUnitaryFunding = fundingRates[symbol]?.sum_unitary_funding ?? 0;
18272
19001
  const estLiqPrice2 = calcEstLiqPrice(formattedOrder, askAndBid.current[0], {
18273
19002
  markPrice: markPrice2,
18274
19003
  totalCollateral,
@@ -18276,7 +19005,8 @@ var useOrderEntry2 = (symbol, options = {}) => {
18276
19005
  imr_factor: accountInfo.imr_factor[symbol],
18277
19006
  symbol,
18278
19007
  positions: positions3,
18279
- symbolInfo
19008
+ symbolInfo,
19009
+ sumUnitaryFunding
18280
19010
  });
18281
19011
  return estLiqPrice2;
18282
19012
  }, [
@@ -18286,7 +19016,8 @@ var useOrderEntry2 = (symbol, options = {}) => {
18286
19016
  totalCollateral,
18287
19017
  symbol,
18288
19018
  maxQty,
18289
- symbolInfo
19019
+ symbolInfo,
19020
+ fundingRates
18290
19021
  ]);
18291
19022
  const estLiqPriceDistance = useMemo(() => {
18292
19023
  if (!estLiqPrice) {
@@ -18409,6 +19140,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18409
19140
  resetMetaState,
18410
19141
  formattedOrder,
18411
19142
  maxQty,
19143
+ maxQtys,
18412
19144
  estLiqPrice,
18413
19145
  estLiqPriceDistance,
18414
19146
  currentPosition,
@@ -18421,7 +19153,7 @@ var useOrderEntry2 = (symbol, options = {}) => {
18421
19153
  validator: validateOrder,
18422
19154
  validate: validateOrder
18423
19155
  },
18424
- freeCollateral,
19156
+ freeCollateral: effectiveMarginMode === MarginMode.ISOLATED ? freeCollateralUSDCOnly : freeCollateral,
18425
19157
  setValue: useMemoizedFn(setValue),
18426
19158
  setValues: useMemoizedFn(setValues),
18427
19159
  symbolInfo: symbolInfo || EMPTY_OBJECT,
@@ -18431,93 +19163,6 @@ var useOrderEntry2 = (symbol, options = {}) => {
18431
19163
  symbolLeverage
18432
19164
  };
18433
19165
  };
18434
- var initialOrderState2 = {
18435
- order_price: "",
18436
- order_quantity: "",
18437
- trigger_price: "",
18438
- tp_trigger_price: "",
18439
- sl_trigger_price: "",
18440
- total: "",
18441
- symbol: ""
18442
- };
18443
- var useOrderStore2 = create()(
18444
- immer((set, get3) => ({
18445
- entry: {
18446
- side: OrderSide.BUY,
18447
- order_type: OrderType.LIMIT,
18448
- ...initialOrderState2
18449
- },
18450
- estLeverage: null,
18451
- estLiquidationPrice: null,
18452
- errors: {},
18453
- actions: {
18454
- hasTP_SL: () => {
18455
- const order = get3().entry;
18456
- return typeof order.tp_trigger_price !== "undefined" || typeof order.sl_trigger_price !== "undefined";
18457
- },
18458
- updateOrderComputed: (data) => {
18459
- set(
18460
- (state) => {
18461
- state.estLeverage = data.estLeverage;
18462
- state.estLiquidationPrice = data.estLiquidationPrice;
18463
- },
18464
- false
18465
- // "updateOrderComputed"
18466
- );
18467
- },
18468
- updateOrder: (order) => {
18469
- set(
18470
- (state) => {
18471
- state.entry = {
18472
- ...state.entry,
18473
- ...order
18474
- };
18475
- },
18476
- false
18477
- // "updateOrder"
18478
- );
18479
- },
18480
- updateOrderByKey: (key, value) => {
18481
- set(
18482
- (state) => {
18483
- state.entry[key] = value;
18484
- },
18485
- false
18486
- // "updateOrderByKey"
18487
- );
18488
- },
18489
- restoreOrder: (order) => {
18490
- set(
18491
- (state) => {
18492
- state.entry = order;
18493
- },
18494
- false
18495
- // "restoreOrder"
18496
- );
18497
- },
18498
- resetOrder: (order) => {
18499
- set(
18500
- (state) => {
18501
- state.entry.order_price = "";
18502
- state.entry.order_quantity = "";
18503
- state.entry.trigger_price = "";
18504
- state.entry.total = "";
18505
- state.entry.tp_trigger_price = "";
18506
- state.entry.tp_pnl = "";
18507
- state.entry.tp_offset = "";
18508
- state.entry.tp_offset_percentage = "";
18509
- state.entry.sl_trigger_price = "";
18510
- state.entry.sl_pnl = "";
18511
- state.entry.sl_offset = "";
18512
- state.entry.sl_offset_percentage = "";
18513
- },
18514
- true
18515
- // "resetOrder"
18516
- );
18517
- }
18518
- }
18519
- }))
18520
- );
18521
19166
  var useOrderEntity = (order, options) => {
18522
19167
  const { symbol } = order;
18523
19168
  const { maxQty } = options || {};
@@ -18675,13 +19320,15 @@ var usePositionClose = (options) => {
18675
19320
  symbol,
18676
19321
  order_type: type,
18677
19322
  side,
18678
- reduce_only: true
19323
+ reduce_only: true,
19324
+ // Use position's margin_mode or default to CROSS for backward compatibility
19325
+ margin_mode: position.margin_mode || MarginMode.CROSS
18679
19326
  };
18680
19327
  if (type === OrderType.LIMIT) {
18681
19328
  data.order_price = price;
18682
19329
  }
18683
19330
  return data;
18684
- }, [symbol, price, type, quantity]);
19331
+ }, [symbol, price, type, quantity, position.margin_mode]);
18685
19332
  const maxQty = useMemo(() => {
18686
19333
  if (!position) {
18687
19334
  return 0;
@@ -18883,9 +19530,11 @@ var useTpslPriceChecker = (params) => {
18883
19530
  prevResultRef.current = currentResult;
18884
19531
  return currentResult;
18885
19532
  };
18886
- var useEstLiqPriceBySymbol = (symbol) => {
19533
+ var useEstLiqPriceBySymbol = (symbol, marginMode) => {
18887
19534
  const [data] = usePositionStream(symbol);
18888
- const position = data?.rows?.find((row) => row.symbol === symbol);
19535
+ const position = data?.rows?.find(
19536
+ (row) => row.symbol === symbol && row.margin_mode === marginMode
19537
+ );
18889
19538
  return useMemo(() => {
18890
19539
  return position?.est_liq_price ?? void 0;
18891
19540
  }, [position]);
@@ -18906,7 +19555,63 @@ var useGetEstLiqPrice = (props) => {
18906
19555
  return estLiqPrice;
18907
19556
  }, [estLiqPrice, markPrice, side]);
18908
19557
  };
19558
+ var useFeatureFlag = (key) => {
19559
+ const { data: publicFlags, isLoading: publicLoading } = useQuery("/v1/public/feature_flags", {});
19560
+ const publicFlag = useMemo(() => {
19561
+ if (!publicFlags || publicLoading) {
19562
+ return void 0;
19563
+ }
19564
+ return publicFlags.find((flag) => flag.key === key);
19565
+ }, [publicFlags, publicLoading, key]);
19566
+ const shouldQueryPrivate = useMemo(() => {
19567
+ return publicFlag !== void 0;
19568
+ }, [publicFlag]);
19569
+ const { data: privateFlags, isLoading: privateLoading } = usePrivateQuery(shouldQueryPrivate ? "/v1/feature_flags" : null, {});
19570
+ const privateFlag = useMemo(() => {
19571
+ if (!shouldQueryPrivate || !privateFlags || privateLoading) {
19572
+ return void 0;
19573
+ }
19574
+ return privateFlags.find((flag) => flag.key === key);
19575
+ }, [shouldQueryPrivate, privateFlags, privateLoading, key]);
19576
+ return useMemo(() => {
19577
+ if (publicLoading || shouldQueryPrivate && privateLoading) {
19578
+ return {
19579
+ enabled: false,
19580
+ data: void 0
19581
+ };
19582
+ }
19583
+ if (publicFlag === void 0) {
19584
+ return {
19585
+ enabled: true,
19586
+ data: void 0
19587
+ };
19588
+ }
19589
+ if (privateFlag === void 0) {
19590
+ return {
19591
+ enabled: false,
19592
+ data: void 0
19593
+ };
19594
+ }
19595
+ return {
19596
+ enabled: true,
19597
+ data: privateFlag
19598
+ };
19599
+ }, [
19600
+ publicFlag,
19601
+ privateFlag,
19602
+ publicLoading,
19603
+ shouldQueryPrivate,
19604
+ privateLoading,
19605
+ key
19606
+ ]);
19607
+ };
19608
+
19609
+ // src/feature-flag/flagKeys.ts
19610
+ var FlagKeys = /* @__PURE__ */ ((FlagKeys2) => {
19611
+ FlagKeys2["IsolatedMargin"] = "isolated-margin";
19612
+ return FlagKeys2;
19613
+ })(FlagKeys || {});
18909
19614
 
18910
- export { DefaultLayoutConfig, DistributionId, ENVType2 as ENVType, ERROR_MSG_CODES, EpochStatus, ExtendedConfigStore, MaintenanceStatus, MarketsStorageKey, MarketsType, ORDERLY_ORDERBOOK_DEPTH_KEY, OrderlyConfigProvider, OrderlyContext, OrderlyProvider, StatusProvider, TWType, WalletConnectorContext, WsNetworkStatus, checkNotional, cleanStringStyle, fetcher, findPositionTPSLFromOrders, findTPSLFromOrder, findTPSLOrderPriceFromOrder, getMinNotional, getPriceKey, indexedDBManager, initializeAppDatabase, isCurrentlyClosed, isCurrentlyTrading, noCacheConfig, parseJSON, persistIndexedDB, resetTimestampOffsetState, timestampWaitingMiddleware, useAccount, useAccountInfo2 as useAccountInfo, useAccountInstance, useAccountRewardsHistory, useAllBrokers, useApiKeyManager, useAppStore, useAssetsHistory, useAudioPlayer, useBalanceSubscription, useBalanceTopic, useBoolean, useChain, useChainInfo, useChains, useCheckReferralCode, useCollateral, useCommission, useComputedLTV, useConfig, useConvert, useCurEpochEstimate, useDaily, useDeposit, useDistribution, useDistributionHistory, useEpochInfo, useEstLiqPriceBySymbol, useEventEmitter, useFeeState, useFundingDetails, useFundingFeeHistory, useFundingRate, useFundingRateBySymbol, useFundingRateHistory, useFundingRates, useFundingRatesStore, useGetClaimed, useGetEnv, useGetEstLiqPrice, useGetReferralCode, useGetRwaSymbolCloseTimeInterval, useGetRwaSymbolInfo, useGetRwaSymbolOpenStatus, useGetRwaSymbolOpenTimeInterval, useHoldingStream, useIndexPrice, useIndexPricesStream, useInfiniteQuery, useInitRwaSymbolsRuntime, useInternalTransfer, useKeyStore, useLazyQuery, useLeverage, useLeverageBySymbol, useLocalStorage, useMainTokenStore, useMainnetChainsStore, useMaintenanceStatus, useMarginRatio, useMarkPrice, useMarkPriceBySymbol, useMarkPricesStream, useMarket, useMarketList, useMarketMap, useMarketTradeStream, useMarkets, useMarketsStore, useMarketsStream, useMaxLeverage, useMaxQty, useMaxWithdrawal, useMediaQuery, useMemoizedFn, useMutation, useNetworkInfo, useOdosQuote, useOrderEntity, useOrderEntry2 as useOrderEntry, useOrderEntry as useOrderEntry_deprecated, useOrderStore2 as useOrderStore, useOrderStream, useOrderbookStream, useOrderlyContext, usePortfolio, usePositionActions, usePositionClose, usePositionStream, usePoster, usePreLoadData, usePrivateDataObserver, usePrivateInfiniteQuery, usePrivateQuery, useQuery, useRefereeHistory, useRefereeInfo, useRefereeRebateSummary, useReferralInfo, useReferralRebateSummary, useRestrictedInfo, useRwaSymbolsInfo, useRwaSymbolsInfoStore, useSessionStorage, useSettleSubscription, useSimpleDI, useStatisticsDaily, useStorageChain, useStorageLedgerAddress, useSubAccountAlgoOrderStream, useSubAccountDataObserver, useSubAccountMaxWithdrawal, useSubAccountMutation, useSubAccountQuery, useSubAccountWS, useSwapSupportStore, useSymbolInfo, useSymbolLeverage, useSymbolPriceRange, useSymbolsInfo, useSymbolsInfoStore, useTPSLOrder, useTestTokenStore, useTestnetChainsStore, useTickerStream, useTokenInfo, useTokensInfo, useTpslPriceChecker, useTrack, useTrackingInstance, useTradingRewardsStatus, useTransfer, useTransferHistory, useUpdatedRef, useUserStatistics, useVaultsHistory, useWS, useWalletConnector, useWalletRewardsHistory, useWalletSubscription, useWalletTopic, useWithdraw, useWsStatus, utils_exports as utils, version_default as version };
19615
+ export { DefaultLayoutConfig, DistributionId, ENVType2 as ENVType, ERROR_MSG_CODES, EpochStatus, ExtendedConfigStore, FlagKeys, MaintenanceStatus, MarketsStorageKey, MarketsType, ORDERLY_ORDERBOOK_DEPTH_KEY, OrderlyConfigProvider, OrderlyContext, OrderlyProvider, StatusProvider, TWType, WalletConnectorContext, WsNetworkStatus, checkNotional, cleanStringStyle, fetcher, findPositionTPSLFromOrders, findTPSLFromOrder, findTPSLOrderPriceFromOrder, getMinNotional, getPriceKey, indexedDBManager, initializeAppDatabase, isCurrentlyClosed, isCurrentlyTrading, noCacheConfig, parseJSON, persistIndexedDB, resetTimestampOffsetState, timestampWaitingMiddleware, useAccount, useAccountInfo2 as useAccountInfo, useAccountInstance, useAccountRewardsHistory, useAllBrokers, useApiKeyManager, useAppStore, useAssetsHistory, useAudioPlayer, useBalanceSubscription, useBalanceTopic, useBoolean, useChain, useChainInfo, useChains, useCheckReferralCode, useCollateral, useCommission, useComputedLTV, useConfig, useConvert, useCurEpochEstimate, useDaily, useDeposit, useDistribution, useDistributionHistory, useEpochInfo, useEstLiqPriceBySymbol, useEventEmitter, useFeatureFlag, useFeeState, useFundingDetails, useFundingFeeHistory, useFundingRate, useFundingRateBySymbol, useFundingRateHistory, useFundingRates, useFundingRatesStore, useGetClaimed, useGetEnv, useGetEstLiqPrice, useGetReferralCode, useGetRwaSymbolCloseTimeInterval, useGetRwaSymbolInfo, useGetRwaSymbolOpenStatus, useGetRwaSymbolOpenTimeInterval, useHoldingStream, useIndexPrice, useIndexPricesStream, useInfiniteQuery, useInitRwaSymbolsRuntime, useInternalTransfer, useKeyStore, useLazyQuery, useLeverage, useLeverageBySymbol, useLocalStorage, useMainTokenStore, useMainnetChainsStore, useMaintenanceStatus, useMarginModeBySymbol, useMarginModes, useMarginRatio, useMarkPrice, useMarkPriceBySymbol, useMarkPricesStream, useMarket, useMarketList, useMarketMap, useMarketTradeStream, useMarkets, useMarketsStore, useMarketsStream, useMaxLeverage, useMaxQty, useMaxWithdrawal, useMediaQuery, useMemoizedFn, useMutation, useNetworkInfo, useOdosQuote, useOrderEntity, useOrderEntry2 as useOrderEntry, useOrderEntry as useOrderEntry_deprecated, useOrderStore, useOrderStream, useOrderbookStream, useOrderlyContext, usePortfolio, usePositionActions, usePositionClose, usePositionStream, usePositions, usePoster, usePreLoadData, usePrivateDataObserver, usePrivateInfiniteQuery, usePrivateQuery, useQuery, useRefereeHistory, useRefereeInfo, useRefereeRebateSummary, useReferralInfo, useReferralRebateSummary, useRestrictedInfo, useRwaSymbolsInfo, useRwaSymbolsInfoStore, useSessionStorage, useSettleSubscription, useSimpleDI, useStatisticsDaily, useStorageChain, useStorageLedgerAddress, useSubAccountAlgoOrderStream, useSubAccountDataObserver, useSubAccountMaxWithdrawal, useSubAccountMutation, useSubAccountQuery, useSubAccountWS, useSwapSupportStore, useSymbolInfo, useSymbolLeverage, useSymbolLeverageMap, useSymbolPriceRange, useSymbolsInfo, useSymbolsInfoStore, useTPSLOrder, useTestTokenStore, useTestnetChainsStore, useTickerStream, useTokenInfo, useTokensInfo, useTpslPriceChecker, useTrack, useTrackingInstance, useTradingRewardsStatus, useTransfer, useTransferHistory, useUpdatedRef, useUserStatistics, useVaultsHistory, useWS, useWalletConnector, useWalletRewardsHistory, useWalletSubscription, useWalletTopic, useWithdraw, useWsStatus, utils_exports as utils, version_default as version };
18911
19616
  //# sourceMappingURL=index.mjs.map
18912
19617
  //# sourceMappingURL=index.mjs.map