@orderly.network/hooks 2.8.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -61,9 +61,9 @@ var __export = (target, all) => {
61
61
  // src/version.ts
62
62
  if (typeof window !== "undefined") {
63
63
  window.__ORDERLY_VERSION__ = window.__ORDERLY_VERSION__ || {};
64
- window.__ORDERLY_VERSION__["@orderly.network/hooks"] = "2.8.0";
64
+ window.__ORDERLY_VERSION__["@orderly.network/hooks"] = "2.8.1";
65
65
  }
66
- var version_default = "2.8.0";
66
+ var version_default = "2.8.1";
67
67
  var fetcher = (url, init2 = {}, queryOptions) => net.get(url, init2, queryOptions?.formatter);
68
68
  var noCacheConfig = {
69
69
  dedupingInterval: 0,
@@ -1249,7 +1249,7 @@ function checkTPSLOrderTypeIsMarket(key, values) {
1249
1249
  }
1250
1250
  function tpslCalculateHelper(key, inputs, options = {}) {
1251
1251
  const { symbol } = options;
1252
- if (key !== "quantity" && key !== "tp_trigger_price" && key !== "sl_trigger_price" && key !== "tp_pnl" && key !== "sl_pnl" && key !== "tp_offset" && key !== "sl_offset" && key !== "tp_offset_percentage" && key !== "sl_offset_percentage" && key !== "tp_order_price" && key !== "sl_order_price" && key !== "tp_order_type" && key !== "sl_order_type" && key !== "tp_enable" && key !== "sl_enable") {
1252
+ if (key !== "quantity" && key !== "tp_trigger_price" && key !== "sl_trigger_price" && key !== "tp_pnl" && key !== "sl_pnl" && key !== "tp_offset" && key !== "sl_offset" && key !== "tp_offset_percentage" && key !== "sl_offset_percentage" && key !== "tp_order_price" && key !== "sl_order_price" && key !== "tp_order_type" && key !== "sl_order_type") {
1253
1253
  return {
1254
1254
  [key]: inputs.value
1255
1255
  };
@@ -1288,19 +1288,6 @@ function tpslCalculateHelper(key, inputs, options = {}) {
1288
1288
  }
1289
1289
  break;
1290
1290
  }
1291
- case "tp_enable":
1292
- case "sl_enable": {
1293
- return {
1294
- [`${keyPrefix}enable`]: inputs.value,
1295
- [`${keyPrefix}order_type`]: types.OrderType.MARKET,
1296
- [`${keyPrefix}trigger_price`]: "",
1297
- [`${keyPrefix}order_price`]: "",
1298
- [`${keyPrefix}offset`]: "",
1299
- [`${keyPrefix}offset_percentage`]: "",
1300
- [`${keyPrefix}pnl`]: "",
1301
- [`${keyPrefix}ROI`]: ""
1302
- };
1303
- }
1304
1291
  case "tp_pnl":
1305
1292
  case "sl_pnl": {
1306
1293
  pnl = inputs.value;
@@ -1649,7 +1636,6 @@ var getCalculateHandler = (fieldName) => {
1649
1636
  case "total": {
1650
1637
  return totalInputHandle;
1651
1638
  }
1652
- case "tp_enable":
1653
1639
  case "tp_pnl":
1654
1640
  case "sl_pnl":
1655
1641
  case "tp_trigger_price":
@@ -1660,7 +1646,6 @@ var getCalculateHandler = (fieldName) => {
1660
1646
  case "sl_offset_percentage":
1661
1647
  case "tp_order_price":
1662
1648
  case "tp_order_type":
1663
- case "sl_enable":
1664
1649
  case "sl_order_type":
1665
1650
  case "sl_order_price":
1666
1651
  return tpslInputHandle;
@@ -2760,6 +2745,7 @@ var useAppStore = zustand.create()(
2760
2745
  );
2761
2746
  var useAccountInfo2 = () => useAppStore((state) => state.accountInfo);
2762
2747
  var usePortfolio = () => useAppStore((state) => state.portfolio);
2748
+ var useFundingRateBySymbol = (symbol) => useAppStore((state) => state.fundingRates?.[symbol]);
2763
2749
 
2764
2750
  // src/orderly/useRwaSymbolsInfo.ts
2765
2751
  var isCurrentlyTrading = (nextClose, status, currentTime = Date.now()) => {
@@ -2781,10 +2767,16 @@ var computeSymbolState = (rwaSymbol, currentTime) => {
2781
2767
  let closeTimeInterval;
2782
2768
  let openTimeInterval;
2783
2769
  if (next_close && typeof next_close === "number" && next_close > currentTime) {
2784
- closeTimeInterval = Math.max(0, Math.floor((next_close - currentTime) / 1e3));
2770
+ closeTimeInterval = Math.max(
2771
+ 0,
2772
+ Math.floor((next_close - currentTime) / 1e3)
2773
+ );
2785
2774
  }
2786
2775
  if (next_open && typeof next_open === "number" && next_open > currentTime) {
2787
- openTimeInterval = Math.max(0, Math.floor((next_open - currentTime) / 1e3));
2776
+ openTimeInterval = Math.max(
2777
+ 0,
2778
+ Math.floor((next_open - currentTime) / 1e3)
2779
+ );
2788
2780
  }
2789
2781
  return {
2790
2782
  isRwa: true,
@@ -2795,46 +2787,48 @@ var computeSymbolState = (rwaSymbol, currentTime) => {
2795
2787
  openTimeInterval
2796
2788
  };
2797
2789
  };
2798
- var useRwaSymbolsRuntimeStore = zustand.create((set, get3) => ({
2799
- computedStates: {},
2800
- currentTime: Date.now(),
2801
- timerId: void 0,
2802
- startTimer: () => {
2803
- const state = get3();
2804
- if (state.timerId) {
2805
- clearInterval(state.timerId);
2806
- }
2807
- const timerId = setInterval(() => {
2808
- const currentTime = Date.now();
2809
- const rwaSymbolsInfo = useAppStore.getState().rwaSymbolsInfo;
2810
- if (!rwaSymbolsInfo) {
2811
- set({ currentTime });
2812
- return;
2790
+ var useRwaSymbolsRuntimeStore = zustand.create(
2791
+ (set, get3) => ({
2792
+ computedStates: {},
2793
+ currentTime: Date.now(),
2794
+ timerId: void 0,
2795
+ startTimer: () => {
2796
+ const state = get3();
2797
+ if (state.timerId) {
2798
+ clearInterval(state.timerId);
2799
+ }
2800
+ const timerId = setInterval(() => {
2801
+ const currentTime = Date.now();
2802
+ const rwaSymbolsInfo = useAppStore.getState().rwaSymbolsInfo;
2803
+ if (!rwaSymbolsInfo) {
2804
+ set({ currentTime });
2805
+ return;
2806
+ }
2807
+ const computedStates = {};
2808
+ Object.entries(rwaSymbolsInfo).forEach(([symbol, rwaSymbol]) => {
2809
+ computedStates[symbol] = computeSymbolState(rwaSymbol, currentTime);
2810
+ });
2811
+ set({ computedStates, currentTime });
2812
+ }, 1e3);
2813
+ set({ timerId });
2814
+ },
2815
+ stopTimer: () => {
2816
+ const state = get3();
2817
+ if (state.timerId) {
2818
+ clearInterval(state.timerId);
2819
+ set({ timerId: void 0 });
2813
2820
  }
2821
+ },
2822
+ updateComputedStates: (rwaSymbolsInfo) => {
2823
+ const currentTime = get3().currentTime;
2814
2824
  const computedStates = {};
2815
2825
  Object.entries(rwaSymbolsInfo).forEach(([symbol, rwaSymbol]) => {
2816
2826
  computedStates[symbol] = computeSymbolState(rwaSymbol, currentTime);
2817
2827
  });
2818
- set({ computedStates, currentTime });
2819
- }, 1e3);
2820
- set({ timerId });
2821
- },
2822
- stopTimer: () => {
2823
- const state = get3();
2824
- if (state.timerId) {
2825
- clearInterval(state.timerId);
2826
- set({ timerId: void 0 });
2828
+ set({ computedStates });
2827
2829
  }
2828
- },
2829
- updateComputedStates: (rwaSymbolsInfo) => {
2830
- const currentTime = get3().currentTime;
2831
- const computedStates = {};
2832
- Object.entries(rwaSymbolsInfo).forEach(([symbol, rwaSymbol]) => {
2833
- computedStates[symbol] = computeSymbolState(rwaSymbol, currentTime);
2834
- });
2835
- set({ computedStates });
2836
- }
2837
- }));
2830
+ })
2831
+ );
2838
2832
  var useInitRwaSymbolsRuntime = () => {
2839
2833
  const rwaSymbolsInfo = useRwaSymbolsInfoStore();
2840
2834
  const { startTimer, stopTimer, updateComputedStates } = useRwaSymbolsRuntimeStore();
@@ -3310,1914 +3304,1918 @@ var useWSObserver = (calculatorService) => {
3310
3304
  };
3311
3305
  }, []);
3312
3306
  };
3313
- var useApiStatusStore = zustand.create()(
3314
- immer.immer((set) => ({
3315
- apis: {
3316
- positions: {
3317
- loading: false
3318
- }
3319
- },
3320
- actions: {
3321
- updateStatus: (key, status) => {
3322
- set((state) => {
3323
- state.apis[key] = status;
3324
- });
3325
- },
3326
- updateApiLoading: (key, loading) => {
3327
- set((state) => {
3328
- state.apis[key].loading = loading;
3329
- });
3330
- },
3331
- updateApiError: (key, error) => {
3332
- set((state) => {
3333
- state.apis[key] = {
3334
- loading: false,
3335
- error
3336
- };
3337
- });
3338
- }
3339
- }
3340
- }))
3341
- );
3342
- var useApiStatusActions = () => useApiStatusStore((state) => state.actions);
3343
- var useTokensInfoStore = zustand.create(
3344
- (set) => ({
3345
- tokensInfo: [],
3346
- setTokensInfo(data) {
3347
- set({ tokensInfo: data });
3348
- }
3349
- })
3350
- );
3351
- var useTokensInfo = () => {
3352
- const tokensInfo = useTokensInfoStore((state) => state.tokensInfo);
3353
- return tokensInfo;
3354
- };
3355
- var useTokenInfo = (token) => {
3356
- const tokensInfo = useTokensInfo();
3357
- return React2.useMemo(() => {
3358
- return tokensInfo?.find((item) => item.token === token);
3359
- }, [tokensInfo, token]);
3360
- };
3361
3307
 
3362
- // src/orderly/calculator/baseCalculator.ts
3363
- var BaseCalculator = class {
3364
- cache(data) {
3365
- this._cache = data;
3366
- }
3308
+ // src/orderly/orderbook.service.ts
3309
+ var defaultRawOrderBook = {
3310
+ asks: [],
3311
+ bids: [],
3312
+ ts: 0
3367
3313
  };
3368
- var useMarkPriceStore = zustand.create((set, get3) => ({
3369
- markPrices: {},
3370
- // orderBook: {},
3371
- // ask_bid: [],
3372
- actions: {
3373
- updateMarkPrice: (markPrice) => {
3374
- set({
3375
- markPrices: markPrice
3376
- });
3377
- },
3378
- getMarkPriceBySymbol: (symbol) => {
3379
- return get3().markPrices[symbol];
3380
- }
3381
- }
3382
- }));
3383
- var useMarkPriceBySymbol = (symbol) => useMarkPriceStore((state) => state.actions.getMarkPriceBySymbol(symbol));
3384
- var useMarkPriceActions = () => useMarkPriceStore((state) => state.actions);
3385
-
3386
- // src/orderly/calculator/markPrice.ts
3387
- var MarketCalculatorName = "markPriceCalculator";
3388
- var MarkPriceCalculator = class extends BaseCalculator {
3314
+ var OrderbookService = class _OrderbookService {
3389
3315
  constructor() {
3390
- super(...arguments);
3391
- this.name = MarketCalculatorName;
3392
- }
3393
- calc(scope, data, ctx) {
3394
- return data;
3316
+ this.bufferedOrderBookUpdates = {};
3317
+ this.rawOrderBook = {};
3395
3318
  }
3396
- update(data, scope) {
3397
- useMarkPriceStore.getState().actions.updateMarkPrice(data);
3319
+ static getInstance() {
3320
+ if (!this.instance) {
3321
+ this.instance = new _OrderbookService();
3322
+ }
3323
+ return this.instance;
3398
3324
  }
3399
- };
3400
-
3401
- // src/orderly/calculator/calculatorContext.ts
3402
- var CalculatorContext = class _CalculatorContext {
3403
- static get instance() {
3404
- return this._instance;
3325
+ sortBufferedOrderBookUpdates(symbol) {
3326
+ this.bufferedOrderBookUpdates[symbol]?.sort((a, b) => a.ts - b.ts);
3405
3327
  }
3406
- static create(scope, data) {
3407
- if (!this._instance) {
3408
- this._instance = new _CalculatorContext(scope, data);
3328
+ applyUpdateToRawOrderBook(symbol, update) {
3329
+ const rawOrderBook = this.rawOrderBook[symbol];
3330
+ if (!rawOrderBook || rawOrderBook.ts > update.prevTs) {
3331
+ return;
3409
3332
  }
3410
- return this._instance.update(scope, data);
3411
- }
3412
- constructor(scope, data) {
3413
- this.setCtxData();
3414
- this.output = {
3415
- // rows: positions,
3416
- };
3333
+ const askMap = /* @__PURE__ */ new Map();
3334
+ const bidMap = /* @__PURE__ */ new Map();
3335
+ rawOrderBook.asks.forEach((ask) => {
3336
+ askMap.set(ask[0], ask[1]);
3337
+ });
3338
+ rawOrderBook.bids.forEach((bid) => {
3339
+ bidMap.set(bid[0], bid[1]);
3340
+ });
3341
+ update.asks.forEach(
3342
+ (ask) => ask[1] === 0 ? askMap.delete(ask[0]) : askMap.set(ask[0], ask[1])
3343
+ );
3344
+ update.bids.forEach(
3345
+ (bid) => bid[1] === 0 ? bidMap.delete(bid[0]) : bidMap.set(bid[0], bid[1])
3346
+ );
3347
+ rawOrderBook.asks = Array.from(askMap.entries()).sort(
3348
+ (a, b) => a[0] - b[0]
3349
+ );
3350
+ rawOrderBook.bids = Array.from(bidMap.entries()).sort(
3351
+ (a, b) => b[0] - a[0]
3352
+ );
3353
+ rawOrderBook.ts = update.ts;
3417
3354
  }
3418
- update(scope, data) {
3419
- this.setCtxData();
3420
- this.markPrices = scope === "markPrice" /* MARK_PRICE */ ? data : this.output[MarketCalculatorName];
3421
- this.portfolio = this.output["portfolio"] || useAppStore.getState().portfolio;
3422
- return this;
3355
+ applyBufferedUpdatesToRawOrderBooks(symbol) {
3356
+ this.bufferedOrderBookUpdates[symbol]?.forEach((update) => {
3357
+ this.applyUpdateToRawOrderBook(symbol, update);
3358
+ });
3423
3359
  }
3424
- setCtxData() {
3425
- this.accountInfo = useAppStore.getState().accountInfo;
3426
- this.symbolsInfo = useAppStore.getState().symbolsInfo;
3427
- this.fundingRates = useAppStore.getState().fundingRates;
3428
- this.tokensInfo = useTokensInfoStore.getState().tokensInfo;
3360
+ deleteBufferedOrderBookUpdates(symbol) {
3361
+ delete this.bufferedOrderBookUpdates[symbol];
3429
3362
  }
3430
- get(fn) {
3431
- return fn(this.output);
3363
+ commitOrderBook(symbol) {
3364
+ const rawOrderBook = this.rawOrderBook[symbol];
3365
+ if (!rawOrderBook) {
3366
+ return;
3367
+ }
3432
3368
  }
3433
- getCacheValue(name, fallback) {
3434
- return this.output[name] || fallback();
3369
+ pushUpdateToBuffer(symbol, update) {
3370
+ if (this.bufferedOrderBookUpdates[symbol] === void 0) {
3371
+ this.bufferedOrderBookUpdates[symbol] = [];
3372
+ }
3373
+ const buffer = this.bufferedOrderBookUpdates[symbol];
3374
+ if (buffer.length > 0) {
3375
+ const lastUpdate = buffer[buffer.length - 1];
3376
+ if (lastUpdate.ts !== update.prevTs) {
3377
+ this.bufferedOrderBookUpdates[symbol] = [];
3378
+ }
3379
+ }
3380
+ this.bufferedOrderBookUpdates[symbol].push(update);
3435
3381
  }
3436
- clearCache() {
3437
- this.output = {};
3438
- this.accountInfo = void 0;
3439
- this.portfolio = void 0;
3382
+ isValidFullOrderBook(symbol, currentTs) {
3383
+ if ((this.bufferedOrderBookUpdates[symbol]?.length ?? 0) !== 0) {
3384
+ const earliestUpdates = this.bufferedOrderBookUpdates[symbol][0];
3385
+ return earliestUpdates.prevTs <= currentTs;
3386
+ }
3387
+ return true;
3440
3388
  }
3441
- deleteByName(name) {
3442
- delete this.output[name];
3389
+ setFullOrderbook(symbol, rawOrderbook) {
3390
+ const { ts } = rawOrderbook;
3391
+ this.rawOrderBook[symbol] = rawOrderbook;
3392
+ this.sortBufferedOrderBookUpdates(symbol);
3393
+ if (this.isValidFullOrderBook(symbol, ts)) {
3394
+ this.applyBufferedUpdatesToRawOrderBooks(symbol);
3395
+ }
3443
3396
  }
3444
- // get positions(): API.PositionTPSLExt[] {
3445
- // if (this.output.positionCalculator) return this.output.positionCalculator;
3446
- // return usePositionStore.getState().positions;
3447
- // }
3448
- get isReady() {
3449
- return !!this.accountInfo;
3397
+ updateOrderbook(symbol, update, callback) {
3398
+ const { asks, bids, prevTs, ts } = update;
3399
+ const rawOrderBook = this.rawOrderBook[symbol];
3400
+ if (!rawOrderBook) {
3401
+ return;
3402
+ }
3403
+ const currentTs = rawOrderBook.ts;
3404
+ if (currentTs === 0) {
3405
+ this.pushUpdateToBuffer(symbol, { asks, bids, prevTs, ts });
3406
+ return;
3407
+ }
3408
+ if (prevTs !== currentTs) {
3409
+ this.pushUpdateToBuffer(symbol, { asks, bids, prevTs, ts });
3410
+ if (callback) {
3411
+ callback();
3412
+ }
3413
+ return;
3414
+ }
3415
+ this.applyUpdateToRawOrderBook(symbol, update);
3416
+ this.deleteBufferedOrderBookUpdates(symbol);
3450
3417
  }
3451
- saveOutput(name, data) {
3452
- this.output[name] = data;
3418
+ getRawOrderbook(symbol) {
3419
+ return this.rawOrderBook[symbol];
3453
3420
  }
3454
- outputToValue() {
3455
- return this.output;
3421
+ resetOrderBook(symbol) {
3422
+ this.rawOrderBook[symbol] = defaultRawOrderBook;
3456
3423
  }
3457
3424
  };
3458
-
3459
- // src/orderly/calculator/calculatorService.ts
3460
- var CalculatorService = class {
3461
- constructor(scheduler, calculators) {
3462
- this.scheduler = scheduler;
3463
- this.pendingCalc = [];
3464
- this.calcQueue = [];
3465
- /**
3466
- * Reference count for each calculator, used to determine if a calculator is still in use.
3467
- */
3468
- this.referenceCount = /* @__PURE__ */ new Map();
3469
- this.isPaused = false;
3470
- this.calculators = new Map(calculators);
3471
- }
3472
- register(scope, calculator) {
3473
- const ref_count_name = `${scope}_${calculator.name}`;
3474
- const count = this.referenceCount.get(ref_count_name);
3475
- if (typeof count !== "undefined" && count > 0) {
3476
- this.referenceCount.set(ref_count_name, count + 1);
3477
- return;
3425
+ var orderBookService = OrderbookService.getInstance();
3426
+ var orderbook_service_default = orderBookService;
3427
+ var useMarkPrice = (symbol) => {
3428
+ const ws = useWS();
3429
+ const [price, setPrice] = React2.useState(0);
3430
+ React2.useEffect(() => {
3431
+ const unsubscribe = ws.subscribe(`${symbol}@markprice`, {
3432
+ onMessage: (message) => {
3433
+ setPrice(message.price);
3434
+ }
3435
+ });
3436
+ return () => {
3437
+ unsubscribe?.();
3438
+ };
3439
+ }, [symbol]);
3440
+ return { data: price };
3441
+ };
3442
+ var useIndexPrice = (symbol) => {
3443
+ symbol = symbol.replace("PERP", "SPOT");
3444
+ const ws = useWS();
3445
+ return useSWRSubscription__default.default(`${symbol}@indexprice`, (key, { next }) => {
3446
+ const unsubscribe = ws.subscribe(`${symbol}@indexprice`, {
3447
+ onMessage: (message) => {
3448
+ next(null, message.price);
3449
+ }
3450
+ });
3451
+ return () => {
3452
+ unsubscribe?.();
3453
+ };
3454
+ });
3455
+ };
3456
+ var useOpenInterest = (symbol) => {
3457
+ const ws = useWS();
3458
+ return useSWRSubscription__default.default(`${symbol}@openinterest`, (key, { next }) => {
3459
+ const unsubscribe = ws.subscribe(`${symbol}@openinterest`, {
3460
+ onMessage: (message) => {
3461
+ next(null, message.openInterest);
3462
+ }
3463
+ });
3464
+ return () => {
3465
+ unsubscribe?.();
3466
+ };
3467
+ });
3468
+ };
3469
+ var useFutures = () => {
3470
+ const { data, isLoading, error } = useQuery(
3471
+ `/v1/public/futures`,
3472
+ {
3473
+ revalidateOnFocus: false
3478
3474
  }
3479
- const calculators = this.calculators.get(scope);
3480
- if (Array.isArray(calculators)) {
3481
- calculators.push(calculator);
3482
- } else {
3483
- this.calculators.set(scope, [calculator]);
3475
+ );
3476
+ const [sortedData, setSortedData] = React2.useState(data);
3477
+ useWS();
3478
+ React2.useEffect(() => {
3479
+ }, []);
3480
+ React2.useEffect(() => {
3481
+ if (data) {
3482
+ const sortedData2 = data.sort((a, b) => {
3483
+ return 0;
3484
+ });
3485
+ setSortedData(sortedData2);
3484
3486
  }
3485
- this.referenceCount.set(ref_count_name, 1);
3487
+ }, [data]);
3488
+ const sortBy = React2.useCallback((key) => {
3489
+ }, [data]);
3490
+ const filterBy = React2.useCallback((key) => {
3491
+ }, [data]);
3492
+ return {
3493
+ data: sortedData,
3494
+ sortBy,
3495
+ filterBy,
3496
+ isLoading,
3497
+ error
3498
+ };
3499
+ };
3500
+
3501
+ // src/orderly/useTickerStream.ts
3502
+ var useTickerStream = (symbol) => {
3503
+ if (!symbol) {
3504
+ throw new types.SDKError("Symbol is required");
3486
3505
  }
3487
- unregister(scope, calculator) {
3488
- const ref_count_name = `${scope}_${calculator.name}`;
3489
- const count = this.referenceCount.get(ref_count_name);
3490
- if (typeof count !== "undefined" && count > 1) {
3491
- this.referenceCount.set(ref_count_name, count - 1);
3492
- return;
3506
+ const { data: info } = useQuery(
3507
+ `/v1/public/futures/${symbol}`,
3508
+ {
3509
+ revalidateOnFocus: false
3493
3510
  }
3494
- const calculators = this.calculators.get(scope);
3495
- if (Array.isArray(calculators)) {
3496
- const index = calculators.findIndex((c) => c.name === calculator.name);
3497
- if (index > -1) {
3498
- calculators[index].destroy?.();
3499
- calculators.splice(index, 1);
3511
+ );
3512
+ const [ticker, setTicker] = React2.useState();
3513
+ const ws = useWS();
3514
+ React2.useEffect(() => {
3515
+ const unsubscribe = ws.subscribe(
3516
+ // { event: "subscribe", topic: "markprices" },
3517
+ `${symbol}@ticker`,
3518
+ {
3519
+ onMessage: (message) => {
3520
+ if (message.symbol !== symbol)
3521
+ return;
3522
+ setTicker(message);
3523
+ }
3524
+ // onUnsubscribe: () => {
3525
+ // return "markprices";
3526
+ // },
3527
+ // onError: (error: any) => {
3528
+ //
3529
+ // },
3500
3530
  }
3531
+ );
3532
+ return () => {
3533
+ setTicker(void 0);
3534
+ unsubscribe?.();
3535
+ };
3536
+ }, [symbol]);
3537
+ const { data: markPrice } = useMarkPrice(symbol);
3538
+ const { data: indexPrice } = useIndexPrice(symbol);
3539
+ const { data: openInterest } = useOpenInterest(symbol);
3540
+ const { data: futures } = useFutures();
3541
+ const value = React2.useMemo(() => {
3542
+ if (!info)
3543
+ return null;
3544
+ if (!ticker || ticker.symbol !== symbol)
3545
+ return info;
3546
+ const futureIndex = futures?.findIndex(
3547
+ (item) => item.symbol === symbol
3548
+ );
3549
+ let _oi = openInterest;
3550
+ if (!_oi && futureIndex !== -1 && futures) {
3551
+ _oi = futures[futureIndex].open_interest;
3501
3552
  }
3502
- this.referenceCount.delete(ref_count_name);
3503
- }
3504
- async calc(scope, data, options) {
3505
- if (scope !== "position" /* POSITION */) {
3506
- if (!options?.skipWhenOnPause) ;
3553
+ const config = {
3554
+ ...info,
3555
+ mark_price: markPrice,
3556
+ index_price: indexPrice,
3557
+ open_interest: _oi
3558
+ };
3559
+ if (ticker.open !== void 0) {
3560
+ config["24h_open"] = ticker.open;
3507
3561
  }
3508
- const ctx = CalculatorContext.create(scope, data);
3509
- if (!ctx.isReady && options?.skipPending) {
3510
- return;
3562
+ if (ticker.close !== void 0) {
3563
+ config["24h_close"] = ticker.close;
3511
3564
  }
3512
- if (options?.skipWhenOnPause && !this.windowIsVisible)
3513
- return;
3514
- this.calcQueue.push({ scope, data, options });
3515
- await this.handleCalcQueue(ctx);
3516
- this.ctx = ctx;
3517
- }
3518
- // private async handlePendingCalc() {
3519
- // // console.log("[handlePendingCalc]:", this.pendingCalc);
3520
- // if (this.pendingCalc.length === 0) return;
3521
- // this.calcQueue = [...this.pendingCalc, ...this.calcQueue];
3522
- // this.pendingCalc = [];
3523
- // }
3524
- async handleCalcQueue(context) {
3525
- const first = this.calcQueue.shift();
3526
- if (first) {
3527
- const { scope, data, options } = first;
3528
- const ctx = context || CalculatorContext.create(scope, data);
3529
- const calculators = this.calculators.get(scope);
3530
- if (Array.isArray(calculators) && calculators.length) {
3531
- try {
3532
- await this.scheduler.calc(scope, calculators, data, ctx);
3533
- } catch (e) {
3534
- }
3535
- if (!options?.skipUpdate) {
3536
- this.scheduler.update(scope, calculators, ctx.outputToValue());
3537
- }
3538
- }
3539
- if (this.calcQueue.length) {
3540
- this.handleCalcQueue(ctx);
3541
- }
3565
+ if (ticker.high !== void 0) {
3566
+ config["24h_high"] = ticker.high;
3542
3567
  }
3543
- }
3544
- stop() {
3545
- this.calcQueue = [];
3546
- this.ctx?.clearCache();
3547
- }
3548
- get windowIsVisible() {
3549
- if (typeof document === "undefined") {
3550
- return true;
3568
+ if (ticker.low !== void 0) {
3569
+ config["24h_low"] = ticker.low;
3551
3570
  }
3552
- return document.visibilityState === "visible";
3553
- }
3571
+ if (ticker.volume !== void 0) {
3572
+ config["24h_volumn"] = ticker.volume;
3573
+ config["24h_volume"] = ticker.volume;
3574
+ }
3575
+ if (ticker.close !== void 0 && ticker.open !== void 0) {
3576
+ config["change"] = new utils.Decimal(ticker.close).minus(ticker.open).div(ticker.open).toNumber();
3577
+ config["24h_change"] = new utils.Decimal(ticker.close).minus(ticker.open).toNumber();
3578
+ }
3579
+ return config;
3580
+ }, [info, symbol, ticker, futures, openInterest]);
3581
+ return value;
3554
3582
  };
3555
- var CalculatorServiceID = "CalculatorService";
3556
3583
 
3557
- // src/orderly/calculator/shardedScheduler.ts
3558
- var requestIdleCallbackPolyfill = (callback, options) => {
3559
- const startTime = Date.now();
3560
- return setTimeout(() => {
3561
- callback({
3562
- didTimeout: false,
3563
- timeRemaining: () => Math.max(0, 50 - (Date.now() - startTime))
3564
- });
3565
- }, 1);
3584
+ // src/orderly/useOrderbookStream.ts
3585
+ var paddingFn = (len) => Array(len).fill([Number.NaN, Number.NaN, Number.NaN, Number.NaN]);
3586
+ var isNumber = (val) => {
3587
+ return typeof val === "number" && !Number.isNaN(val);
3566
3588
  };
3567
- var cancelIdleCallbackPolyfill = (id) => {
3568
- clearTimeout(id);
3589
+ var getPriceKey = (rawPrice, depth, isAsks) => {
3590
+ return new utils.Decimal(rawPrice).div(depth).toDecimalPlaces(0, isAsks ? utils.Decimal.ROUND_CEIL : utils.Decimal.ROUND_FLOOR).mul(depth).toNumber();
3569
3591
  };
3570
- var safeRequestIdleCallback = typeof window !== "undefined" && window.requestIdleCallback ? window.requestIdleCallback.bind(window) : requestIdleCallbackPolyfill;
3571
- typeof window !== "undefined" && window.cancelIdleCallback ? window.cancelIdleCallback.bind(window) : cancelIdleCallbackPolyfill;
3572
- var ShardingScheduler = class {
3573
- // run(calculators: Calculator[]) {}
3574
- calc(scope, calculators, data, ctx) {
3575
- return new Promise((resolve, reject) => {
3576
- try {
3577
- this.computation(
3578
- calculators,
3579
- (shard) => {
3580
- const results = [];
3581
- for (let index = 0; index < shard.length; index++) {
3582
- const calculator = shard[index];
3583
- const result = calculator.calc(scope, data, ctx);
3584
- if (result) {
3585
- ctx.saveOutput(calculator.name, result);
3586
- results.push(result);
3587
- }
3588
- }
3589
- return results;
3590
- },
3591
- (results) => {
3592
- resolve(results);
3593
- }
3594
- );
3595
- } catch (error) {
3596
- reject(error);
3597
- }
3598
- });
3592
+ var reduceItems = (depth, data, isAsks) => {
3593
+ if (!Array.isArray(data) || data.length === 0) {
3594
+ return [];
3599
3595
  }
3600
- update(scope, calculators, data) {
3601
- for (let index = 0; index < calculators.length; index++) {
3602
- const calculator = calculators[index];
3603
- const item = data[calculator.name];
3604
- if (!!item) {
3605
- calculator.update(item, scope);
3596
+ let newData = [...data];
3597
+ const result = [];
3598
+ if (typeof depth !== "undefined") {
3599
+ const pricesMap = /* @__PURE__ */ new Map();
3600
+ const len = data.length;
3601
+ for (let i = 0; i < len; i++) {
3602
+ const [rawPrice, quantity] = data[i];
3603
+ if (!isNumber(rawPrice) || !isNumber(quantity)) {
3604
+ continue;
3605
+ }
3606
+ const priceKey = getPriceKey(rawPrice, depth, isAsks);
3607
+ const amtByRaw = new utils.Decimal(rawPrice).mul(quantity).toNumber();
3608
+ if (pricesMap.has(priceKey)) {
3609
+ const item = pricesMap.get(priceKey);
3610
+ const sumQty = new utils.Decimal(item[1]).add(quantity).toNumber();
3611
+ const sumAmtByRaw = new utils.Decimal(item[2] ?? 0).add(amtByRaw).toNumber();
3612
+ pricesMap.set(priceKey, [priceKey, sumQty, sumAmtByRaw]);
3613
+ } else {
3614
+ pricesMap.set(priceKey, [priceKey, quantity, amtByRaw]);
3606
3615
  }
3607
3616
  }
3608
- return Promise.resolve();
3617
+ newData = Array.from(pricesMap.values());
3609
3618
  }
3610
- computation(data, processor, onComplete) {
3611
- let index = 0;
3612
- const results = [];
3613
- const estimatedShardSize = Math.min(data.length, 2);
3614
- function processNextShard(deadline) {
3615
- let shardSize = estimatedShardSize;
3616
- while (index + shardSize <= data.length && deadline.timeRemaining() > 0) {
3617
- const shard = data.slice(index, index + shardSize);
3618
- const result = processor(shard);
3619
- results.push(result);
3620
- index += shardSize;
3621
- if (deadline.timeRemaining() < 1) {
3622
- shardSize = Math.max(1, Math.floor(shardSize / 2));
3619
+ for (let i = 0; i < newData.length; i++) {
3620
+ const [price, quantity, sumAmtByRaw] = newData[i];
3621
+ if (!isNumber(price) || !isNumber(quantity)) {
3622
+ continue;
3623
+ }
3624
+ const resLen = result.length;
3625
+ const newQuantity = new utils.Decimal(quantity).add(resLen ? result[resLen - 1][2] : 0).toNumber();
3626
+ const pieceAmount = isNumber(sumAmtByRaw) ? sumAmtByRaw : new utils.Decimal(quantity).mul(price).toNumber();
3627
+ const newAmount = new utils.Decimal(pieceAmount).add(resLen ? result[resLen - 1][3] : 0).toNumber();
3628
+ result.push([price, quantity, newQuantity, newAmount]);
3629
+ }
3630
+ return result;
3631
+ };
3632
+ var reduceOrderbook = (depth, level, padding, data) => {
3633
+ let asks = reduceItems(depth, data.asks, true);
3634
+ let bids = reduceItems(depth, data.bids, false);
3635
+ if (asks.length !== 0 && bids.length !== 0 && asks[0][0] <= bids[0][0]) {
3636
+ if (asks.length === 1) {
3637
+ const [price, qty, newQuantity, newAmount] = asks[0];
3638
+ asks.shift();
3639
+ asks.push([
3640
+ price + (depth === void 0 ? 0 : Number(depth)),
3641
+ qty,
3642
+ newQuantity,
3643
+ newAmount
3644
+ ]);
3645
+ } else {
3646
+ const [bidPrice] = bids[0];
3647
+ while (asks.length > 0) {
3648
+ const [askPrice, askQty, newQuantity, newAmount] = asks[0];
3649
+ if (askPrice <= bidPrice) {
3650
+ asks.shift();
3651
+ for (let i = 0; i < asks.length; i++) {
3652
+ if (i === 0) {
3653
+ const quantity = new utils.Decimal(asks[i][1]).add(askQty);
3654
+ asks[i][1] = quantity.toNumber();
3655
+ asks[i][2] = quantity.toNumber();
3656
+ asks[i][3] = quantity.toDecimalPlaces(0, utils.Decimal.ROUND_CEIL).mul(asks[i][0]).toNumber();
3657
+ } else {
3658
+ asks[i][3] = new utils.Decimal(asks[i][0]).mul(asks[i][1]).add(asks[i - 1][3]).toNumber();
3659
+ }
3660
+ }
3623
3661
  } else {
3624
- shardSize = Math.min(data.length - index, shardSize * 2);
3662
+ break;
3625
3663
  }
3626
3664
  }
3627
- if (index < data.length) {
3628
- safeRequestIdleCallback(processNextShard, {
3629
- timeout: 1e3
3630
- });
3631
- } else {
3632
- onComplete(results.flat());
3633
- }
3634
3665
  }
3635
- safeRequestIdleCallback(processNextShard, {
3636
- timeout: 1e3
3637
- });
3638
3666
  }
3667
+ asks = asks.reverse();
3668
+ if (padding) {
3669
+ asks = asks.length < level ? paddingFn(level - asks.length).concat(asks) : asks;
3670
+ bids = bids.length < level ? bids.concat(paddingFn(level - bids.length)) : bids;
3671
+ }
3672
+ return {
3673
+ asks,
3674
+ bids
3675
+ };
3639
3676
  };
3640
- var POSITION_EMPTY = {
3641
- rows: null,
3642
- margin_ratio: 0,
3643
- initial_margin_ratio: 0,
3644
- maintenance_margin_ratio: 0,
3645
- open_margin_ratio: 0,
3646
- current_margin_ratio_with_orders: 0,
3647
- initial_margin_ratio_with_orders: 0,
3648
- maintenance_margin_ratio_with_orders: 0,
3649
- total_collateral_value: 0,
3650
- free_collateral: 0,
3651
- total_pnl_24_h: 0,
3652
- unrealPnL: 0,
3653
- total_unreal_pnl: 0,
3654
- unsettledPnL: 0,
3655
- total_unsettled_pnl: 0,
3656
- notional: 0,
3657
- unrealPnlROI: 0
3677
+ var ORDERLY_ORDERBOOK_DEPTH_KEY = "orderly_orderbook_depth_key";
3678
+ var INIT_DATA = {
3679
+ asks: [],
3680
+ bids: []
3658
3681
  };
3659
- var usePositionStore = zustand.create()(
3660
- immer.immer((set) => ({
3661
- positions: {
3662
- all: POSITION_EMPTY
3663
- },
3664
- actions: {
3665
- setPositions: (key, positions3) => {
3666
- set((state) => {
3667
- state.positions[key] = positions3;
3668
- });
3669
- },
3670
- closePosition: (symbol) => {
3671
- set((state) => {
3672
- delete state.positions[symbol];
3673
- });
3674
- },
3675
- clearAll: () => {
3676
- set((state) => {
3677
- state.positions = {
3678
- all: POSITION_EMPTY
3679
- };
3680
- });
3681
- }
3682
- }
3683
- }))
3684
- );
3685
- var usePositions = (symbol = "all") => usePositionStore((state) => (state.positions[symbol] ?? POSITION_EMPTY).rows);
3686
- var usePositionActions = () => usePositionStore((state) => state.actions);
3687
-
3688
- // src/orderly/calculator/positions.ts
3689
- var NAME_PREFIX = "positionCalculator";
3690
- var AllPositions = "all";
3691
- var PositionCalculator = class extends BaseCalculator {
3692
- // private id: string;
3693
- constructor(symbol = AllPositions) {
3694
- super();
3695
- this.name = `${NAME_PREFIX}_${symbol}`;
3696
- this.symbol = symbol;
3682
+ var useOrderbookStream = (symbol, initial = INIT_DATA, options) => {
3683
+ if (!symbol) {
3684
+ throw new types.SDKError("Symbol is required");
3697
3685
  }
3698
- calc(scope, data, ctx) {
3699
- if (scope === "markPrice" /* MARK_PRICE */) {
3700
- return this.calcByMarkPrice(data, ctx);
3686
+ const level = options?.level ?? 10;
3687
+ const padding = options?.padding ?? true;
3688
+ const symbolRef = React2.useRef(symbol);
3689
+ symbolRef.current = symbol;
3690
+ const {
3691
+ defaultOrderbookTickSizes: DEFAULT_TICK_SIZES2 = {},
3692
+ defaultOrderbookSymbolDepths: DEFAULT_SYMBOL_DEPTHS2 = {}
3693
+ } = React2.useContext(OrderlyContext);
3694
+ const [data, setData] = React2.useState(initial);
3695
+ const [isLoading, setIsLoading] = React2.useState(true);
3696
+ const config = useSymbolsInfo()[symbol];
3697
+ const [depthObject, setDepthObject] = useLocalStorage(
3698
+ ORDERLY_ORDERBOOK_DEPTH_KEY,
3699
+ {}
3700
+ );
3701
+ const prevMiddlePrice = React2.useRef(0);
3702
+ const tick = config("quote_tick");
3703
+ const depths = React2.useMemo(() => {
3704
+ if (DEFAULT_SYMBOL_DEPTHS2[symbol]) {
3705
+ return DEFAULT_SYMBOL_DEPTHS2[symbol];
3701
3706
  }
3702
- if (scope === "indexPrice" /* INDEX_PRICE */) {
3703
- return this.calcByIndexPrice(data, ctx);
3707
+ if (typeof tick === "undefined") {
3708
+ return [];
3704
3709
  }
3705
- if (scope === "position" /* POSITION */) {
3706
- return this.calcByPosition(
3707
- this.preprocess(data),
3708
- ctx
3709
- );
3710
+ try {
3711
+ const base = new utils.Decimal(tick);
3712
+ return [
3713
+ base.toNumber(),
3714
+ base.mul(10).toNumber(),
3715
+ base.mul(100).toNumber(),
3716
+ base.mul(1e3).toNumber()
3717
+ ];
3718
+ } catch {
3710
3719
  }
3711
- return data;
3712
- }
3713
- update(data, scope) {
3714
- if (!data || !Array.isArray(data.rows))
3720
+ return [tick];
3721
+ }, [symbol, tick]);
3722
+ React2.useEffect(() => {
3723
+ if (depthObject[symbol]) {
3715
3724
  return;
3716
- usePositionStore.getState().actions.setPositions(this.symbol, data);
3717
- if (Array.isArray(data.rows) && useApiStatusStore.getState().apis.positions.loading) {
3718
- useApiStatusStore.getState().actions.updateApiLoading("positions", false);
3719
3725
  }
3720
- }
3721
- calcByMarkPrice(markPrice, ctx) {
3722
- let positions3 = this.getPosition(markPrice, ctx);
3723
- useAppStore.getState().fundingRates;
3724
- if (!positions3 || !Array.isArray(positions3.rows) || !positions3.rows.length)
3725
- return positions3;
3726
- positions3 = {
3727
- ...positions3,
3728
- rows: positions3.rows.map((item) => {
3729
- return {
3730
- ...item,
3731
- mark_price: markPrice[item.symbol] || item.mark_price
3732
- };
3733
- })
3734
- };
3735
- return this.format(positions3, ctx);
3736
- }
3737
- calcByIndexPrice(indexPrice, ctx) {
3738
- let positions3 = this.getPosition(indexPrice, ctx);
3739
- if (!positions3) {
3740
- return positions3;
3741
- }
3742
- if (!Array.isArray(positions3.rows) || !positions3.rows.length)
3743
- return positions3;
3744
- positions3 = {
3745
- ...positions3,
3746
- rows: positions3.rows.map((item) => ({
3747
- ...item,
3748
- index_price: indexPrice[item.symbol] || item.index_price || item.mark_price
3749
- }))
3750
- };
3751
- return this.format(positions3, ctx);
3752
- }
3753
- calcByPosition(positions3, ctx) {
3754
- if (positions3.rows.length === 0)
3755
- return positions3;
3756
- return this.format(positions3, ctx);
3757
- }
3758
- format(data, ctx) {
3759
- const { accountInfo, symbolsInfo, fundingRates, portfolio } = ctx;
3760
- if (!accountInfo || !fundingRates || !symbolsInfo) {
3761
- return data;
3762
- }
3763
- let unrealPnL_total = utils.zero, unrealPnL_total_index = utils.zero, notional_total = utils.zero, unsettlementPnL_total = utils.zero;
3764
- let rows = data.rows.map((item) => {
3765
- const info = symbolsInfo[item.symbol];
3766
- const sum_unitary_funding = fundingRates?.[item.symbol]?.["sum_unitary_funding"] ?? 0;
3767
- const notional = perp.positions.notional(item.position_qty, item.mark_price);
3768
- const unrealPnl = perp.positions.unrealizedPnL({
3769
- qty: item.position_qty,
3770
- openPrice: item?.average_open_price,
3771
- // markPrice: unRealizedPrice,
3772
- markPrice: item.mark_price
3773
- });
3774
- let unrealPnl_index = 0, unrealPnlROI_index = 0;
3775
- const maxLeverage = item.leverage || 1;
3776
- const imr = perp.account.IMR({
3777
- maxLeverage,
3778
- baseIMR: info?.["base_imr"],
3779
- IMR_Factor: accountInfo.imr_factor[item.symbol],
3780
- positionNotional: notional,
3781
- ordersNotional: 0,
3782
- IMR_factor_power: 4 / 5
3783
- });
3784
- const unrealPnlROI = perp.positions.unrealizedPnLROI({
3785
- positionQty: item.position_qty,
3786
- openPrice: item.average_open_price,
3787
- IMR: imr,
3788
- unrealizedPnL: unrealPnl
3789
- });
3790
- if (item.index_price) {
3791
- unrealPnl_index = perp.positions.unrealizedPnL({
3792
- qty: item.position_qty,
3793
- openPrice: item?.average_open_price,
3794
- // markPrice: unRealizedPrice,
3795
- markPrice: item.index_price
3796
- });
3797
- unrealPnlROI_index = perp.positions.unrealizedPnLROI({
3798
- positionQty: item.position_qty,
3799
- openPrice: item.average_open_price,
3800
- IMR: imr,
3801
- unrealizedPnL: unrealPnl_index
3802
- });
3803
- }
3804
- const unsettlementPnL2 = perp.positions.unsettlementPnL({
3805
- positionQty: item.position_qty,
3806
- markPrice: item.mark_price,
3807
- costPosition: item.cost_position,
3808
- sumUnitaryFunding: ramda.propOr(
3809
- 0,
3810
- "sum_unitary_funding",
3811
- fundingRates[item.symbol]
3812
- ),
3813
- lastSumUnitaryFunding: item.last_sum_unitary_funding
3814
- });
3815
- const MMR = perp.positions.MMR({
3816
- baseMMR: info?.["base_mmr"],
3817
- baseIMR: info?.["base_imr"],
3818
- IMRFactor: accountInfo.imr_factor[item.symbol],
3819
- positionNotional: notional,
3820
- IMR_factor_power: 4 / 5
3821
- });
3822
- unrealPnL_total = unrealPnL_total.add(unrealPnl);
3823
- unrealPnL_total_index = unrealPnL_total_index.add(unrealPnl_index);
3824
- notional_total = notional_total.add(notional);
3825
- unsettlementPnL_total = unsettlementPnL_total.add(unsettlementPnL2);
3826
- const fundingFee = new utils.Decimal(sum_unitary_funding).sub(item.last_sum_unitary_funding).mul(item.position_qty).negated().toNumber();
3827
- return {
3828
- ...item,
3829
- fundingFee,
3830
- mm: perp.positions.maintenanceMargin({
3831
- positionQty: item.position_qty,
3832
- markPrice: item.mark_price,
3833
- MMR
3834
- }),
3835
- mmr: MMR,
3836
- notional,
3837
- unsettlement_pnl: unsettlementPnL2,
3838
- unrealized_pnl: unrealPnl,
3839
- unrealized_pnl_index: unrealPnl_index,
3840
- unrealized_pnl_ROI: unrealPnlROI,
3841
- unrealized_pnl_ROI_index: unrealPnlROI_index
3842
- };
3843
- });
3844
- const totalUnrealPnl = unrealPnL_total.toNumber();
3845
- const totalUnrealPnl_index = unrealPnL_total_index.toNumber();
3846
- const unsettlementPnL = unsettlementPnL_total.toNumber();
3847
- let totalUnrealizedROI = 0, totalUnrealizedROI_index = 0;
3848
- if (portfolio) {
3849
- const { totalValue, totalCollateral } = portfolio;
3850
- rows = rows.map((item) => {
3851
- const est_liq_price = perp.positions.liqPrice({
3852
- markPrice: item.mark_price,
3853
- totalCollateral: totalCollateral.toNumber(),
3854
- positionQty: item.position_qty,
3855
- positions: rows,
3856
- MMR: item.mmr
3857
- });
3858
- return {
3859
- ...item,
3860
- est_liq_price
3861
- };
3862
- });
3863
- if (totalValue !== null && !totalValue.eq(utils.zero)) {
3864
- totalUnrealizedROI = perp.account.totalUnrealizedROI({
3865
- totalUnrealizedPnL: totalUnrealPnl,
3866
- totalValue: totalValue.toNumber()
3867
- });
3868
- totalUnrealizedROI_index = perp.account.totalUnrealizedROI({
3869
- totalUnrealizedPnL: totalUnrealPnl_index,
3870
- totalValue: totalValue.toNumber()
3871
- });
3872
- }
3873
- }
3874
- return {
3875
- ...data,
3876
- unrealPnL: totalUnrealPnl,
3877
- total_unreal_pnl: totalUnrealPnl,
3878
- total_unreal_pnl_index: totalUnrealPnl_index,
3879
- notional: notional_total.toNumber(),
3880
- unsettledPnL: unsettlementPnL,
3881
- total_unsettled_pnl: unsettlementPnL,
3882
- unrealPnlROI: totalUnrealizedROI,
3883
- unrealPnlROI_index: totalUnrealizedROI_index,
3884
- rows
3885
- };
3886
- }
3887
- preprocess(data) {
3888
- let rows = data.rows;
3889
- if (this.symbol !== AllPositions && Array.isArray(rows)) {
3890
- rows = rows.filter((item) => item.symbol === this.symbol);
3891
- }
3892
- return {
3893
- ...data,
3894
- rows
3895
- };
3896
- }
3897
- getPosition(_, ctx) {
3898
- const positions3 = ctx.get((output) => output[this.name]) || usePositionStore.getState().positions[this.symbol];
3899
- if (this.symbol === AllPositions) {
3900
- return positions3;
3901
- }
3902
- if (positions3 && Array.isArray(positions3.rows)) {
3903
- return positions3;
3904
- }
3905
- return this.preprocess(this.getAllPositions(ctx));
3906
- }
3907
- destroy() {
3908
- usePositionStore.getState().actions.closePosition(this.symbol);
3909
- CalculatorContext.instance?.deleteByName(this.name);
3910
- }
3911
- getAllPositions(ctx) {
3912
- return ctx.get((output) => output[AllPositions]) || usePositionStore.getState().positions[AllPositions];
3913
- }
3914
- };
3915
- PositionCalculator.logPosition = (symbol = "all") => {
3916
- return usePositionStore.getState().positions[symbol];
3917
- };
3918
- var parseHolding = (holding, indexPrices, tokensInfo) => {
3919
- const nonUSDC = [];
3920
- let USDC_holding = 0;
3921
- holding.forEach((item) => {
3922
- if (item.token === "USDC") {
3923
- USDC_holding = item.holding;
3924
- } else {
3925
- const tokenInfo = tokensInfo.find(({ token }) => token === item.token);
3926
- const {
3927
- base_weight = 0,
3928
- discount_factor = 0,
3929
- user_max_qty = 0
3930
- } = tokenInfo || {};
3931
- const holdingQty = item?.holding ?? 0;
3932
- const indexPrice = indexPrices[`PERP_${item.token}_USDC`] ?? 0;
3933
- const collateralRatio4 = perp.account.collateralRatio({
3934
- baseWeight: base_weight,
3935
- discountFactor: discount_factor,
3936
- collateralQty: holdingQty,
3937
- collateralCap: user_max_qty,
3938
- indexPrice
3939
- });
3940
- nonUSDC.push({
3941
- holding: holdingQty,
3942
- indexPrice,
3943
- collateralCap: user_max_qty,
3944
- collateralRatio: collateralRatio4
3945
- });
3946
- }
3947
- });
3948
- return [USDC_holding, nonUSDC];
3949
- };
3950
- var useIndexPriceStore = zustand.create((set) => ({
3951
- indexPrices: {},
3952
- actions: {
3953
- updateIndexPrice: (indexPrice) => {
3954
- set({
3955
- indexPrices: indexPrice
3956
- });
3957
- }
3958
- }
3959
- }));
3960
-
3961
- // src/orderly/calculator/indexPrice.ts
3962
- var IndexPriceCalculatorName = "indexPriceCalculator";
3963
- var IndexPriceCalculator = class extends BaseCalculator {
3964
- constructor() {
3965
- super(...arguments);
3966
- this.name = IndexPriceCalculatorName;
3967
- }
3968
- calc(scope, data, ctx) {
3969
- return data;
3970
- }
3971
- update(data) {
3972
- if (!data)
3973
- return;
3974
- useIndexPriceStore.getState().actions.updateIndexPrice(data);
3975
- }
3976
- static getValue() {
3977
- return useIndexPriceStore.getState().indexPrices;
3978
- }
3979
- };
3980
-
3981
- // src/orderly/calculator/portfolio.ts
3982
- var PortfolioCalculatorName = "portfolio";
3983
- var PortfolioCalculator = class extends BaseCalculator {
3984
- constructor() {
3985
- super(...arguments);
3986
- this.name = PortfolioCalculatorName;
3987
- }
3988
- calc(scope, data, ctx) {
3989
- let markPrices;
3990
- let indexPrices;
3991
- const portfolio = this.getPortfolio(ctx);
3992
- if (scope === "markPrice" /* MARK_PRICE */) {
3993
- markPrices = data;
3994
- } else {
3995
- markPrices = ctx.get(
3996
- (cache) => cache[MarketCalculatorName]
3997
- );
3998
- }
3999
- if (scope === "indexPrice" /* INDEX_PRICE */) {
4000
- indexPrices = data;
3726
+ if (DEFAULT_TICK_SIZES2[symbol]) {
3727
+ setDepthObject((prev) => ({
3728
+ ...prev,
3729
+ [symbol]: Number(DEFAULT_TICK_SIZES2[symbol])
3730
+ }));
4001
3731
  } else {
4002
- indexPrices = ctx.get(
4003
- (cache) => cache[IndexPriceCalculatorName]
4004
- );
3732
+ setDepthObject((prev) => ({
3733
+ ...prev,
3734
+ [symbol]: tick
3735
+ }));
4005
3736
  }
4006
- const positions3 = ctx.get(
4007
- (output) => output.positionCalculator_all
4008
- );
4009
- let holding = portfolio.holding;
4010
- if (scope === "portfolio" /* PORTFOLIO */ && data.holding && Array.isArray(holding)) {
4011
- if (Array.isArray(data.holding)) {
4012
- holding = data.holding;
4013
- } else {
4014
- holding = holding.map((item) => {
4015
- if (data.holding[item.token]) {
4016
- return {
4017
- ...item,
4018
- holding: data.holding[item.token].holding,
4019
- frozen: data.holding[item.token].frozen
4020
- };
3737
+ }, [depthObject, tick, symbol, DEFAULT_TICK_SIZES2]);
3738
+ const onDepthChange = React2.useCallback(
3739
+ (val) => {
3740
+ setDepthObject((prev) => ({ ...prev, [symbol]: val }));
3741
+ },
3742
+ [symbol]
3743
+ );
3744
+ const ws = useWS();
3745
+ const ticker = useTickerStream(symbol);
3746
+ const eventEmitter = useEventEmitter();
3747
+ React2.useEffect(() => {
3748
+ let needRequestFullOrderbook = true;
3749
+ setIsLoading(true);
3750
+ let fullOrderBookUpdateSub;
3751
+ const orderBookUpdateSub = ws.subscribe(
3752
+ {
3753
+ event: "subscribe",
3754
+ topic: `${symbol}@orderbookupdate`
3755
+ },
3756
+ {
3757
+ formatter: (message) => message,
3758
+ onMessage: (message) => {
3759
+ const { data: wsData, ts } = message;
3760
+ const { symbol: symbol2, asks, bids, prevTs } = wsData;
3761
+ if (symbolRef.current !== symbol2) {
3762
+ orderBookUpdateSub?.();
3763
+ return;
4021
3764
  }
4022
- return item;
4023
- });
3765
+ orderbook_service_default.updateOrderbook(
3766
+ symbol2,
3767
+ { asks, bids, ts, prevTs },
3768
+ () => {
3769
+ needRequestFullOrderbook = true;
3770
+ }
3771
+ );
3772
+ const data2 = orderbook_service_default.getRawOrderbook(symbol2);
3773
+ setData({ bids: data2.bids, asks: data2.asks });
3774
+ }
4024
3775
  }
4025
- }
4026
- const accountInfo = ctx.accountInfo;
4027
- const symbolsInfo = ctx.symbolsInfo;
4028
- const tokensInfo = ctx.tokensInfo;
4029
- return this.format({
4030
- holding,
4031
- positions: positions3,
4032
- markPrices,
4033
- accountInfo,
4034
- symbolsInfo,
4035
- indexPrices,
4036
- tokensInfo: tokensInfo ?? types.EMPTY_LIST
4037
- });
4038
- }
4039
- getPortfolio(ctx) {
4040
- return ctx.get((output) => output[this.name]) || useAppStore.getState().portfolio;
4041
- }
4042
- format(inputs) {
4043
- const {
4044
- holding,
4045
- positions: positions3,
4046
- markPrices,
4047
- indexPrices,
4048
- accountInfo,
4049
- symbolsInfo,
4050
- tokensInfo
4051
- } = inputs;
4052
- if (!holding || !positions3 || !Array.isArray(positions3.rows) || !markPrices || !indexPrices || !accountInfo) {
4053
- return null;
4054
- }
4055
- const unsettledPnL = ramda.pathOr(0, ["total_unsettled_pnl"])(positions3);
4056
- const unrealizedPnL = ramda.pathOr(0, ["total_unreal_pnl"])(positions3);
4057
- const [USDC_holding, nonUSDC] = parseHolding(
4058
- holding,
4059
- indexPrices,
4060
- tokensInfo
4061
3776
  );
4062
- const usdc = holding.find((item) => item.token === "USDC");
4063
- const totalCollateral = perp.account.totalCollateral({
4064
- USDCHolding: USDC_holding,
4065
- nonUSDCHolding: nonUSDC,
4066
- unsettlementPnL: unsettledPnL
4067
- });
4068
- const totalValue = perp.account.totalValue({
4069
- totalUnsettlementPnL: unsettledPnL,
4070
- USDCHolding: USDC_holding,
4071
- nonUSDCHolding: nonUSDC
4072
- });
4073
- const totalUnrealizedROI = perp.account.totalUnrealizedROI({
4074
- totalUnrealizedPnL: unrealizedPnL,
4075
- totalValue: totalValue.toNumber()
4076
- });
4077
- const totalInitialMarginWithOrders = perp.account.totalInitialMarginWithQty({
4078
- positions: positions3.rows,
4079
- markPrices,
4080
- IMR_Factors: accountInfo.imr_factor,
4081
- maxLeverage: accountInfo.max_leverage,
4082
- symbolInfo: createGetter({ ...symbolsInfo })
4083
- });
4084
- const freeCollateral = perp.account.freeCollateral({
4085
- totalCollateral,
4086
- totalInitialMarginWithOrders
4087
- });
4088
- const availableBalance = perp.account.availableBalance({
4089
- USDCHolding: usdc?.holding ?? 0,
4090
- unsettlementPnL: positions3.total_unsettled_pnl ?? 0
4091
- });
4092
- return {
4093
- totalCollateral,
4094
- totalValue,
4095
- totalUnrealizedROI,
4096
- freeCollateral,
4097
- availableBalance,
4098
- unsettledPnL,
4099
- holding
3777
+ if (needRequestFullOrderbook) {
3778
+ setIsLoading(true);
3779
+ fullOrderBookUpdateSub = ws.onceSubscribe(
3780
+ {
3781
+ event: "request",
3782
+ id: `${symbol}@orderbook`,
3783
+ params: {
3784
+ type: "orderbook",
3785
+ symbol
3786
+ }
3787
+ },
3788
+ {
3789
+ formatter: (message) => message,
3790
+ onMessage: (message) => {
3791
+ const { symbol: symbol2, asks, bids, ts } = message.data;
3792
+ if (symbolRef.current !== symbol2) {
3793
+ return;
3794
+ }
3795
+ orderbook_service_default.setFullOrderbook(symbol2, { asks, bids, ts });
3796
+ const data2 = orderbook_service_default.getRawOrderbook(symbol2);
3797
+ setData({ bids: data2.bids, asks: data2.asks });
3798
+ setIsLoading(false);
3799
+ }
3800
+ }
3801
+ );
3802
+ needRequestFullOrderbook = false;
3803
+ }
3804
+ return () => {
3805
+ orderBookUpdateSub?.();
3806
+ fullOrderBookUpdateSub?.();
3807
+ orderbook_service_default.resetOrderBook(symbol);
3808
+ setData(INIT_DATA);
4100
3809
  };
4101
- }
4102
- update(data, scope) {
4103
- if (data) {
4104
- useAppStore.getState().actions.batchUpdateForPortfolio({
4105
- totalCollateral: data.totalCollateral,
4106
- totalValue: data.totalValue,
4107
- freeCollateral: data.freeCollateral,
4108
- availableBalance: data.availableBalance,
4109
- totalUnrealizedROI: data.totalUnrealizedROI,
4110
- unsettledPnL: data.unsettledPnL,
4111
- holding: Array.isArray(data.holding) ? data.holding : []
4112
- });
3810
+ }, [symbol]);
3811
+ const { data: markPrice } = useMarkPrice(symbol);
3812
+ const onItemClick = React2.useCallback((item) => {
3813
+ eventEmitter.emit("orderbook:item:click", item);
3814
+ }, []);
3815
+ const reducedData = reduceOrderbook(depthObject[symbol], level, padding, {
3816
+ asks: data.asks,
3817
+ bids: data.bids
3818
+ });
3819
+ React2.useEffect(() => {
3820
+ eventEmitter.emit("orderbook:update", reducedData);
3821
+ }, [reducedData]);
3822
+ const middlePrice = React2.useMemo(() => {
3823
+ let asksFrist = 0;
3824
+ let bidsFirst = 0;
3825
+ if (data.asks.length > 0) {
3826
+ asksFrist = reducedData.asks?.[reducedData.asks.length - 1]?.[0];
4113
3827
  }
4114
- }
4115
- };
4116
-
4117
- // src/useCalculatorService.ts
4118
- var useCalculatorService = () => {
4119
- const { get: get3 } = useSimpleDI();
4120
- const calculatorService = useConstant__default.default(() => {
4121
- let calculatorService2 = get3(CalculatorServiceID);
4122
- if (!calculatorService2) {
4123
- const positionCalculator = new PositionCalculator();
4124
- const portfolioCalculator = new PortfolioCalculator();
4125
- const markPriceCalculator = new MarkPriceCalculator();
4126
- const indexPriceCalculator = new IndexPriceCalculator();
4127
- calculatorService2 = new CalculatorService(new ShardingScheduler(), [
4128
- [
4129
- "markPrice" /* MARK_PRICE */,
4130
- [
4131
- markPriceCalculator,
4132
- positionCalculator,
4133
- portfolioCalculator,
4134
- positionCalculator
4135
- ]
4136
- ],
4137
- ["position" /* POSITION */, [positionCalculator, portfolioCalculator]],
4138
- ["portfolio" /* PORTFOLIO */, [portfolioCalculator]],
4139
- // indexPrice
4140
- [
4141
- "indexPrice" /* INDEX_PRICE */,
4142
- [indexPriceCalculator, positionCalculator]
4143
- ]
4144
- ]);
4145
- core.SimpleDI.registerByName(CalculatorServiceID, calculatorService2);
3828
+ if (data.bids.length > 0) {
3829
+ bidsFirst = data.bids[0][0];
4146
3830
  }
4147
- return calculatorService2;
4148
- });
4149
- return calculatorService;
4150
- };
4151
-
4152
- // src/orderly/orderbook.service.ts
4153
- var defaultRawOrderBook = {
4154
- asks: [],
4155
- bids: [],
4156
- ts: 0
4157
- };
4158
- var OrderbookService = class _OrderbookService {
4159
- constructor() {
4160
- this.bufferedOrderBookUpdates = {};
4161
- this.rawOrderBook = {};
4162
- }
4163
- static getInstance() {
4164
- if (!this.instance) {
4165
- this.instance = new _OrderbookService();
3831
+ if (!isNumber(asksFrist) || !isNumber(bidsFirst) || !ticker) {
3832
+ return 0;
4166
3833
  }
4167
- return this.instance;
4168
- }
4169
- sortBufferedOrderBookUpdates(symbol) {
4170
- this.bufferedOrderBookUpdates[symbol]?.sort((a, b) => a.ts - b.ts);
4171
- }
4172
- applyUpdateToRawOrderBook(symbol, update) {
4173
- const rawOrderBook = this.rawOrderBook[symbol];
4174
- if (!rawOrderBook || rawOrderBook.ts > update.prevTs) {
4175
- return;
3834
+ return [asksFrist, bidsFirst, ticker["24h_close"]].sort()[1];
3835
+ }, [ticker?.["24h_close"], data]);
3836
+ React2.useEffect(() => {
3837
+ prevMiddlePrice.current = middlePrice;
3838
+ }, [middlePrice]);
3839
+ return [
3840
+ {
3841
+ asks: reducedData.asks.slice(-level),
3842
+ bids: reducedData.bids.slice(0, level),
3843
+ markPrice,
3844
+ middlePrice: [prevMiddlePrice.current, middlePrice]
3845
+ },
3846
+ {
3847
+ onDepthChange,
3848
+ depth: depthObject[symbol],
3849
+ allDepths: depths,
3850
+ isLoading,
3851
+ onItemClick
4176
3852
  }
4177
- const askMap = /* @__PURE__ */ new Map();
4178
- const bidMap = /* @__PURE__ */ new Map();
4179
- rawOrderBook.asks.forEach((ask) => {
4180
- askMap.set(ask[0], ask[1]);
4181
- });
4182
- rawOrderBook.bids.forEach((bid) => {
4183
- bidMap.set(bid[0], bid[1]);
4184
- });
4185
- update.asks.forEach(
4186
- (ask) => ask[1] === 0 ? askMap.delete(ask[0]) : askMap.set(ask[0], ask[1])
4187
- );
4188
- update.bids.forEach(
4189
- (bid) => bid[1] === 0 ? bidMap.delete(bid[0]) : bidMap.set(bid[0], bid[1])
4190
- );
4191
- rawOrderBook.asks = Array.from(askMap.entries()).sort(
4192
- (a, b) => a[0] - b[0]
4193
- );
4194
- rawOrderBook.bids = Array.from(bidMap.entries()).sort(
4195
- (a, b) => b[0] - a[0]
4196
- );
4197
- rawOrderBook.ts = update.ts;
4198
- }
4199
- applyBufferedUpdatesToRawOrderBooks(symbol) {
4200
- this.bufferedOrderBookUpdates[symbol]?.forEach((update) => {
4201
- this.applyUpdateToRawOrderBook(symbol, update);
3853
+ ];
3854
+ };
3855
+ var useSymbolInfo = (symbol) => {
3856
+ const infos = useSymbolsInfo();
3857
+ return React2.useMemo(() => {
3858
+ return !symbol || infos.isNil ? null : infos[symbol];
3859
+ }, [infos, symbol]);
3860
+ };
3861
+ var useFundingRates = () => {
3862
+ const data = useAppStore((state) => state.fundingRates);
3863
+ return createGetter({ ...data });
3864
+ };
3865
+ var useFundingRatesStore = () => {
3866
+ const data = useAppStore((state) => state.fundingRates);
3867
+ return data;
3868
+ };
3869
+
3870
+ // src/orderly/useMarket.ts
3871
+ var DefaultTab = { name: "Popular", id: 1 };
3872
+ var marketsKey = "markets";
3873
+ var useMarket = (type) => {
3874
+ const { configStore } = React2.useContext(OrderlyContext);
3875
+ const symbolsInfo = useSymbolsInfo();
3876
+ const fundingRates = useFundingRates();
3877
+ const { data: futures } = useMarketsStream();
3878
+ const updateStore = (key, data) => {
3879
+ configStore.set(marketsKey, {
3880
+ ...configStore.getOr(marketsKey, {}),
3881
+ [key]: data
4202
3882
  });
4203
- }
4204
- deleteBufferedOrderBookUpdates(symbol) {
4205
- delete this.bufferedOrderBookUpdates[symbol];
4206
- }
4207
- commitOrderBook(symbol) {
4208
- const rawOrderBook = this.rawOrderBook[symbol];
4209
- if (!rawOrderBook) {
4210
- return;
3883
+ };
3884
+ const getStore = (key, defaultValue) => {
3885
+ return configStore.get(marketsKey)[key] || defaultValue;
3886
+ };
3887
+ if (!configStore.get(marketsKey)) {
3888
+ const jsonStr = localStorage.getItem(marketsKey);
3889
+ if (jsonStr) {
3890
+ configStore.set(marketsKey, JSON.parse(jsonStr));
3891
+ } else {
3892
+ configStore.set(marketsKey, {
3893
+ recent: [],
3894
+ favorites: [
3895
+ { name: "PERP_ETH_USDC", tabs: [{ ...DefaultTab }] },
3896
+ { name: "PERP_BTC_USDC", tabs: [{ ...DefaultTab }] }
3897
+ ],
3898
+ favoriteTabs: [{ ...DefaultTab }],
3899
+ lastSelectedFavoriteTab: { ...DefaultTab }
3900
+ });
4211
3901
  }
4212
3902
  }
4213
- pushUpdateToBuffer(symbol, update) {
4214
- if (this.bufferedOrderBookUpdates[symbol] === void 0) {
4215
- this.bufferedOrderBookUpdates[symbol] = [];
4216
- }
4217
- const buffer = this.bufferedOrderBookUpdates[symbol];
4218
- if (buffer.length > 0) {
4219
- const lastUpdate = buffer[buffer.length - 1];
4220
- if (lastUpdate.ts !== update.prevTs) {
4221
- this.bufferedOrderBookUpdates[symbol] = [];
3903
+ const getFavoriteTabs = React2.useMemo(() => {
3904
+ return getStore("favoriteTabs", [{ ...DefaultTab }]);
3905
+ }, []);
3906
+ const getFavorites = React2.useMemo(() => {
3907
+ const curData = getStore("favorites", []);
3908
+ const tabs = getFavoriteTabs;
3909
+ const result = [];
3910
+ const len = curData.length;
3911
+ for (let index = 0; index < len; index++) {
3912
+ const favData = curData[index];
3913
+ const favTabs = favData.tabs.filter(
3914
+ (tab) => tabs.findIndex((item) => tab.id === item.id) !== -1
3915
+ );
3916
+ if (favTabs.length) {
3917
+ result.push({ ...favData, tabs: favTabs });
4222
3918
  }
4223
3919
  }
4224
- this.bufferedOrderBookUpdates[symbol].push(update);
4225
- }
4226
- isValidFullOrderBook(symbol, currentTs) {
4227
- if ((this.bufferedOrderBookUpdates[symbol]?.length ?? 0) !== 0) {
4228
- const earliestUpdates = this.bufferedOrderBookUpdates[symbol][0];
4229
- return earliestUpdates.prevTs <= currentTs;
4230
- }
4231
- return true;
4232
- }
4233
- setFullOrderbook(symbol, rawOrderbook) {
4234
- const { ts } = rawOrderbook;
4235
- this.rawOrderBook[symbol] = rawOrderbook;
4236
- this.sortBufferedOrderBookUpdates(symbol);
4237
- if (this.isValidFullOrderBook(symbol, ts)) {
4238
- this.applyBufferedUpdatesToRawOrderBooks(symbol);
4239
- }
4240
- }
4241
- updateOrderbook(symbol, update, callback) {
4242
- const { asks, bids, prevTs, ts } = update;
4243
- const rawOrderBook = this.rawOrderBook[symbol];
4244
- if (!rawOrderBook) {
4245
- return;
4246
- }
4247
- const currentTs = rawOrderBook.ts;
4248
- if (currentTs === 0) {
4249
- this.pushUpdateToBuffer(symbol, { asks, bids, prevTs, ts });
3920
+ updateStore("favorites", result);
3921
+ return result;
3922
+ }, [configStore]);
3923
+ const [favoriteTabs, setFavoriteTabs] = React2.useState(getFavoriteTabs);
3924
+ const [favorites, setFavorites] = React2.useState(getFavorites);
3925
+ const [recent, setRecent] = React2.useState(
3926
+ getStore("recent", []).filter((e) => e)
3927
+ );
3928
+ const [tabSort, setTabSort] = React2.useState(
3929
+ getStore("tabSort", {})
3930
+ );
3931
+ const updateFavoriteTabs = (tab, operator) => {
3932
+ const saveTabs = (tabs2) => {
3933
+ setFavoriteTabs(tabs2);
3934
+ updateStore("favoriteTabs", tabs2);
3935
+ };
3936
+ if (Array.isArray(tab)) {
3937
+ saveTabs(tab);
4250
3938
  return;
4251
3939
  }
4252
- if (prevTs !== currentTs) {
4253
- this.pushUpdateToBuffer(symbol, { asks, bids, prevTs, ts });
4254
- if (callback) {
4255
- callback();
3940
+ const tabs = [...favoriteTabs];
3941
+ const index = tabs.findIndex((item) => item.id === tab.id);
3942
+ if (operator?.add) {
3943
+ tabs.push(tab);
3944
+ } else if (operator?.update) {
3945
+ if (index !== -1) {
3946
+ tabs[index] = tab;
3947
+ }
3948
+ } else if (operator?.delete) {
3949
+ if (index !== -1) {
3950
+ tabs.splice(index, 1);
4256
3951
  }
4257
- return;
4258
3952
  }
4259
- this.applyUpdateToRawOrderBook(symbol, update);
4260
- this.deleteBufferedOrderBookUpdates(symbol);
4261
- }
4262
- getRawOrderbook(symbol) {
4263
- return this.rawOrderBook[symbol];
4264
- }
4265
- resetOrderBook(symbol) {
4266
- this.rawOrderBook[symbol] = defaultRawOrderBook;
4267
- }
4268
- };
4269
- var orderBookService = OrderbookService.getInstance();
4270
- var orderbook_service_default = orderBookService;
4271
- var useMarkPrice = (symbol) => {
4272
- const ws = useWS();
4273
- const [price, setPrice] = React2.useState(0);
4274
- React2.useEffect(() => {
4275
- const unsubscribe = ws.subscribe(`${symbol}@markprice`, {
4276
- onMessage: (message) => {
4277
- setPrice(message.price);
3953
+ saveTabs(tabs);
3954
+ };
3955
+ const updateFavorites = (favorites2) => {
3956
+ updateStore("favorites", favorites2);
3957
+ setFavorites(favorites2);
3958
+ };
3959
+ const addToHistory = (symbol) => {
3960
+ const curData = [...recent];
3961
+ const index = curData.findIndex((item) => item.name == symbol.symbol);
3962
+ if (index !== -1) {
3963
+ curData.splice(index, 1);
3964
+ }
3965
+ curData.unshift({ name: symbol.symbol });
3966
+ updateStore("recent", curData);
3967
+ setRecent(curData);
3968
+ };
3969
+ const updateSymbolFavoriteState = (symbol, tab, remove = false) => {
3970
+ const curData = [...favorites];
3971
+ const index = curData.findIndex((item) => item.name == symbol.symbol);
3972
+ if (index === -1) {
3973
+ if (Array.isArray(tab)) {
3974
+ if (tab.length > 0) {
3975
+ curData.unshift({ name: symbol.symbol, tabs: tab });
3976
+ }
3977
+ } else {
3978
+ if (!remove) {
3979
+ curData.unshift({ name: symbol.symbol, tabs: [tab] });
3980
+ }
4278
3981
  }
4279
- });
4280
- return () => {
4281
- unsubscribe?.();
4282
- };
4283
- }, [symbol]);
4284
- return { data: price };
4285
- };
4286
- var useIndexPrice = (symbol) => {
4287
- symbol = symbol.replace("PERP", "SPOT");
4288
- const ws = useWS();
4289
- return useSWRSubscription__default.default(`${symbol}@indexprice`, (key, { next }) => {
4290
- const unsubscribe = ws.subscribe(`${symbol}@indexprice`, {
4291
- onMessage: (message) => {
4292
- next(null, message.price);
3982
+ } else {
3983
+ const favorite = curData[index];
3984
+ if (Array.isArray(tab)) {
3985
+ if (tab.length === 0) {
3986
+ curData.splice(index, 1);
3987
+ } else {
3988
+ curData[index] = { ...favorite, tabs: tab };
3989
+ }
3990
+ } else {
3991
+ if (remove) {
3992
+ const tabs = favorite.tabs.filter((item) => item.id != tab.id);
3993
+ if (tabs.length === 0) {
3994
+ curData.splice(index, 1);
3995
+ } else {
3996
+ curData[index] = { ...favorite, tabs };
3997
+ }
3998
+ } else {
3999
+ const tabs = favorite.tabs;
4000
+ tabs.push(tab);
4001
+ curData[index] = { ...favorite, tabs };
4002
+ }
4293
4003
  }
4004
+ }
4005
+ updateStore("favorites", curData);
4006
+ setFavorites(() => curData);
4007
+ };
4008
+ const marketsList = React2.useMemo(() => {
4009
+ const list = futures?.map((item) => {
4010
+ const { open_interest = 0, index_price = 0 } = item;
4011
+ const info = symbolsInfo[item.symbol];
4012
+ const rate = fundingRates[item.symbol];
4013
+ const est_funding_rate = rate("est_funding_rate");
4014
+ const funding_period = info("funding_period");
4015
+ const change = item.change === void 0 ? get24hChange2(item["24h_close"], item["24h_open"]) : item.change;
4016
+ return {
4017
+ ...item,
4018
+ change,
4019
+ "8h_funding": get8hFunding2(est_funding_rate, funding_period),
4020
+ quote_dp: info("quote_dp"),
4021
+ created_time: info("created_time"),
4022
+ openInterest: new utils.Decimal(open_interest || 0).mul(index_price || 0).toNumber()
4023
+ };
4294
4024
  });
4295
- return () => {
4296
- unsubscribe?.();
4297
- };
4298
- });
4299
- };
4300
- var useOpenInterest = (symbol) => {
4301
- const ws = useWS();
4302
- return useSWRSubscription__default.default(`${symbol}@openinterest`, (key, { next }) => {
4303
- const unsubscribe = ws.subscribe(`${symbol}@openinterest`, {
4304
- onMessage: (message) => {
4305
- next(null, message.openInterest);
4025
+ return list || [];
4026
+ }, [symbolsInfo, futures, fundingRates]);
4027
+ const getData = (type2) => {
4028
+ const localData = type2 === 0 /* FAVORITES */ ? [...favorites] : [...recent];
4029
+ const keys = localData.map((item) => item.name);
4030
+ const filter = type2 == 2 /* ALL */ ? marketsList : marketsList?.filter((item) => keys.includes(item.symbol));
4031
+ const favoritesData = [...favorites];
4032
+ const favoriteKeys = favoritesData.map((item) => item.name);
4033
+ if (filter) {
4034
+ for (let index = 0; index < filter.length; index++) {
4035
+ const element = filter[index];
4036
+ const isFavorite = type2 == 0 /* FAVORITES */ ? true : favoriteKeys.includes(element.symbol);
4037
+ const fIndex = favoritesData.findIndex(
4038
+ (item) => item.name === element.symbol
4039
+ );
4040
+ const tabs = fIndex === -1 ? [] : favoritesData[fIndex].tabs;
4041
+ let imr = void 0;
4042
+ if (symbolsInfo) {
4043
+ imr = symbolsInfo?.[element.symbol]("base_imr");
4044
+ }
4045
+ filter[index] = {
4046
+ ...filter[index],
4047
+ isFavorite,
4048
+ tabs,
4049
+ leverage: imr ? 1 / imr : void 0
4050
+ };
4306
4051
  }
4307
- });
4308
- return () => {
4309
- unsubscribe?.();
4052
+ }
4053
+ return filter;
4054
+ };
4055
+ const pinToTop = (symbol) => {
4056
+ const index = favorites.findIndex((item) => item.name === symbol.symbol);
4057
+ if (index !== -1) {
4058
+ const element = favorites[index];
4059
+ const list = [...favorites];
4060
+ list.splice(index, 1);
4061
+ list.unshift(element);
4062
+ updateStore("favorites", list);
4063
+ setFavorites(list);
4064
+ }
4065
+ };
4066
+ const getLastSelFavTab = () => {
4067
+ return getStore("lastSelectedFavoriteTab", { ...DefaultTab });
4068
+ };
4069
+ const updateSelectedFavoriteTab = (tab) => {
4070
+ updateStore("lastSelectedFavoriteTab", tab);
4071
+ };
4072
+ const updateTabsSortState = (tabId, sortKey, sortOrder) => {
4073
+ const map = getStore("tabSort", {});
4074
+ map[tabId] = {
4075
+ sortKey,
4076
+ sortOrder
4310
4077
  };
4311
- });
4312
- };
4313
- var useFutures = () => {
4314
- const { data, isLoading, error } = useQuery(
4315
- `/v1/public/futures`,
4078
+ updateStore("tabSort", map);
4079
+ setTabSort(map);
4080
+ };
4081
+ const markets = getData(type);
4082
+ return [
4083
+ markets || types.EMPTY_LIST,
4316
4084
  {
4317
- revalidateOnFocus: false
4085
+ favoriteTabs,
4086
+ favorites,
4087
+ recent,
4088
+ tabSort,
4089
+ addToHistory,
4090
+ updateFavorites,
4091
+ updateFavoriteTabs,
4092
+ updateSymbolFavoriteState,
4093
+ pinToTop,
4094
+ getLastSelFavTab,
4095
+ updateSelectedFavoriteTab,
4096
+ updateTabsSortState
4318
4097
  }
4319
- );
4320
- const [sortedData, setSortedData] = React2.useState(data);
4321
- useWS();
4322
- React2.useEffect(() => {
4323
- }, []);
4324
- React2.useEffect(() => {
4325
- if (data) {
4326
- const sortedData2 = data.sort((a, b) => {
4327
- return 0;
4098
+ ];
4099
+ };
4100
+ function get8hFunding2(est_funding_rate, funding_period) {
4101
+ let funding8h = 0;
4102
+ if (est_funding_rate === void 0 || est_funding_rate === null) {
4103
+ return null;
4104
+ }
4105
+ if (funding_period) {
4106
+ funding8h = new utils.Decimal(est_funding_rate || 0).mul(funding_period).div(8).toNumber();
4107
+ }
4108
+ return funding8h;
4109
+ }
4110
+ function get24hChange2(close, open) {
4111
+ if (close !== void 0 && open !== void 0) {
4112
+ if (open === 0) {
4113
+ return 0;
4114
+ }
4115
+ return new utils.Decimal(close).minus(open).div(open).toNumber();
4116
+ }
4117
+ }
4118
+ var useMarkPriceStore = zustand.create((set, get3) => ({
4119
+ markPrices: {},
4120
+ // orderBook: {},
4121
+ // ask_bid: [],
4122
+ actions: {
4123
+ updateMarkPrice: (markPrice) => {
4124
+ set({
4125
+ markPrices: markPrice
4126
+ });
4127
+ },
4128
+ getMarkPriceBySymbol: (symbol) => {
4129
+ return get3().markPrices[symbol];
4130
+ }
4131
+ }
4132
+ }));
4133
+ var useMarkPriceBySymbol = (symbol) => useMarkPriceStore((state) => state.actions.getMarkPriceBySymbol(symbol));
4134
+ var useMarkPriceActions = () => useMarkPriceStore((state) => state.actions);
4135
+
4136
+ // src/orderly/useMarkPricesStream.ts
4137
+ var useMarkPricesStream = () => {
4138
+ const data = useMarkPriceStore((state) => state.markPrices);
4139
+ return { data };
4140
+ };
4141
+ var useIndexPriceStore = zustand.create((set) => ({
4142
+ indexPrices: {},
4143
+ actions: {
4144
+ updateIndexPrice: (indexPrice) => {
4145
+ set({
4146
+ indexPrices: indexPrice
4328
4147
  });
4329
- setSortedData(sortedData2);
4330
4148
  }
4331
- }, [data]);
4332
- const sortBy = React2.useCallback((key) => {
4333
- }, [data]);
4334
- const filterBy = React2.useCallback((key) => {
4335
- }, [data]);
4149
+ }
4150
+ }));
4151
+
4152
+ // src/orderly/useIndexPricesStream.ts
4153
+ var useIndexPricesStream = () => {
4154
+ const indexPrices = useIndexPriceStore((state) => state.indexPrices);
4155
+ const getIndexPrice = (token) => {
4156
+ if (token === "USDC") {
4157
+ return 1;
4158
+ }
4159
+ return indexPrices[`PERP_${token}_USDC`] ?? 0;
4160
+ };
4336
4161
  return {
4337
- data: sortedData,
4338
- sortBy,
4339
- filterBy,
4340
- isLoading,
4341
- error
4162
+ data: indexPrices,
4163
+ getIndexPrice: useMemoizedFn(getIndexPrice)
4342
4164
  };
4343
4165
  };
4344
-
4345
- // src/orderly/useTickerStream.ts
4346
- var useTickerStream = (symbol) => {
4347
- if (!symbol) {
4348
- throw new types.SDKError("Symbol is required");
4166
+ var generateLeverageLevers = (max2) => {
4167
+ const min2 = 1;
4168
+ const parts = 5;
4169
+ const step = (max2 - min2) / (parts - 1);
4170
+ const result = [];
4171
+ for (let i = 0; i < parts; i++) {
4172
+ result.push(Math.floor(min2 + step * i));
4349
4173
  }
4350
- const { data: info } = useQuery(
4351
- `/v1/public/futures/${symbol}`,
4174
+ return result;
4175
+ };
4176
+ var useLeverage = () => {
4177
+ const { data, mutate: mutate6 } = usePrivateQuery(
4178
+ "/v1/client/info",
4352
4179
  {
4353
4180
  revalidateOnFocus: false
4354
4181
  }
4355
4182
  );
4356
- const [ticker, setTicker] = React2.useState();
4357
- const ws = useWS();
4358
- React2.useEffect(() => {
4359
- const unsubscribe = ws.subscribe(
4360
- // { event: "subscribe", topic: "markprices" },
4361
- `${symbol}@ticker`,
4362
- {
4363
- onMessage: (message) => {
4364
- if (message.symbol !== symbol)
4365
- return;
4366
- setTicker(message);
4367
- }
4368
- // onUnsubscribe: () => {
4369
- // return "markprices";
4370
- // },
4371
- // onError: (error: any) => {
4372
- //
4373
- // },
4183
+ const [update, { isMutating }] = useMutation("/v1/client/leverage");
4184
+ const { data: leverageConfig, isLoading } = useQuery("/v1/public/leverage", {
4185
+ revalidateOnFocus: false,
4186
+ errorRetryCount: 3
4187
+ // formatter: (data) => data,
4188
+ });
4189
+ const updateLeverage = React2.useCallback(
4190
+ async (data2) => {
4191
+ const res = await update(data2);
4192
+ if (res.success) {
4193
+ return mutate6();
4194
+ } else {
4195
+ throw new Error(res.message);
4374
4196
  }
4375
- );
4376
- return () => {
4377
- setTicker(void 0);
4378
- unsubscribe?.();
4379
- };
4380
- }, [symbol]);
4381
- const { data: markPrice } = useMarkPrice(symbol);
4382
- const { data: indexPrice } = useIndexPrice(symbol);
4383
- const { data: openInterest } = useOpenInterest(symbol);
4384
- const { data: futures } = useFutures();
4385
- const value = React2.useMemo(() => {
4386
- if (!info)
4387
- return null;
4388
- if (!ticker || ticker.symbol !== symbol)
4389
- return info;
4390
- const futureIndex = futures?.findIndex(
4391
- (item) => item.symbol === symbol
4392
- );
4393
- let _oi = openInterest;
4394
- if (!_oi && futureIndex !== -1 && futures) {
4395
- _oi = futures[futureIndex].open_interest;
4396
- }
4397
- const config = {
4398
- ...info,
4399
- mark_price: markPrice,
4400
- index_price: indexPrice,
4401
- open_interest: _oi
4402
- };
4403
- if (ticker.open !== void 0) {
4404
- config["24h_open"] = ticker.open;
4405
- }
4406
- if (ticker.close !== void 0) {
4407
- config["24h_close"] = ticker.close;
4408
- }
4409
- if (ticker.high !== void 0) {
4410
- config["24h_high"] = ticker.high;
4411
- }
4412
- if (ticker.low !== void 0) {
4413
- config["24h_low"] = ticker.low;
4414
- }
4415
- if (ticker.volume !== void 0) {
4416
- config["24h_volumn"] = ticker.volume;
4417
- config["24h_volume"] = ticker.volume;
4197
+ },
4198
+ [update, mutate6]
4199
+ );
4200
+ const memoizedCurLeverage = React2.useMemo(() => {
4201
+ if (data?.max_leverage !== void 0) {
4202
+ return Number(data.max_leverage);
4418
4203
  }
4419
- if (ticker.close !== void 0 && ticker.open !== void 0) {
4420
- config["change"] = new utils.Decimal(ticker.close).minus(ticker.open).div(ticker.open).toNumber();
4421
- config["24h_change"] = new utils.Decimal(ticker.close).minus(ticker.open).toNumber();
4204
+ return 1;
4205
+ }, [data?.max_leverage]);
4206
+ const memoizedMaxLeverage = React2.useMemo(() => {
4207
+ if (leverageConfig?.max_futures_leverage !== void 0) {
4208
+ return Number(leverageConfig.max_futures_leverage);
4422
4209
  }
4423
- return config;
4424
- }, [info, symbol, ticker, futures, openInterest]);
4425
- return value;
4210
+ return 1;
4211
+ }, [leverageConfig?.max_futures_leverage]);
4212
+ const memoizedLeverageLevers = React2.useMemo(() => {
4213
+ return generateLeverageLevers(memoizedMaxLeverage);
4214
+ }, [memoizedMaxLeverage]);
4215
+ return {
4216
+ update: updateLeverage,
4217
+ isLoading: isLoading || isMutating,
4218
+ leverageLevers: memoizedLeverageLevers,
4219
+ curLeverage: memoizedCurLeverage,
4220
+ maxLeverage: memoizedMaxLeverage
4221
+ };
4426
4222
  };
4427
4223
 
4428
- // src/orderly/useOrderbookStream.ts
4429
- var paddingFn = (len) => Array(len).fill([Number.NaN, Number.NaN, Number.NaN, Number.NaN]);
4430
- var isNumber = (val) => {
4431
- return typeof val === "number" && !Number.isNaN(val);
4432
- };
4433
- var getPriceKey = (rawPrice, depth, isAsks) => {
4434
- return new utils.Decimal(rawPrice).div(depth).toDecimalPlaces(0, isAsks ? utils.Decimal.ROUND_CEIL : utils.Decimal.ROUND_FLOOR).mul(depth).toNumber();
4224
+ // src/orderly/useOdosQuote.ts
4225
+ var useOdosQuote = () => {
4226
+ return useMutation(`https://api.odos.xyz/sor/quote/v2`);
4435
4227
  };
4436
- var reduceItems = (depth, data, isAsks) => {
4437
- if (!Array.isArray(data) || data.length === 0) {
4438
- return [];
4439
- }
4440
- let newData = [...data];
4441
- const result = [];
4442
- if (typeof depth !== "undefined") {
4443
- const pricesMap = /* @__PURE__ */ new Map();
4444
- const len = data.length;
4445
- for (let i = 0; i < len; i++) {
4446
- const [rawPrice, quantity] = data[i];
4447
- if (!isNumber(rawPrice) || !isNumber(quantity)) {
4448
- continue;
4449
- }
4450
- const priceKey = getPriceKey(rawPrice, depth, isAsks);
4451
- const amtByRaw = new utils.Decimal(rawPrice).mul(quantity).toNumber();
4452
- if (pricesMap.has(priceKey)) {
4453
- const item = pricesMap.get(priceKey);
4454
- const sumQty = new utils.Decimal(item[1]).add(quantity).toNumber();
4455
- const sumAmtByRaw = new utils.Decimal(item[2] ?? 0).add(amtByRaw).toNumber();
4456
- pricesMap.set(priceKey, [priceKey, sumQty, sumAmtByRaw]);
4457
- } else {
4458
- pricesMap.set(priceKey, [priceKey, quantity, amtByRaw]);
4459
- }
4460
- }
4461
- newData = Array.from(pricesMap.values());
4462
- }
4463
- for (let i = 0; i < newData.length; i++) {
4464
- const [price, quantity, sumAmtByRaw] = newData[i];
4465
- if (!isNumber(price) || !isNumber(quantity)) {
4466
- continue;
4228
+ var useTokensInfoStore = zustand.create(
4229
+ (set) => ({
4230
+ tokensInfo: [],
4231
+ setTokensInfo(data) {
4232
+ set({ tokensInfo: data });
4467
4233
  }
4468
- const resLen = result.length;
4469
- const newQuantity = new utils.Decimal(quantity).add(resLen ? result[resLen - 1][2] : 0).toNumber();
4470
- const pieceAmount = isNumber(sumAmtByRaw) ? sumAmtByRaw : new utils.Decimal(quantity).mul(price).toNumber();
4471
- const newAmount = new utils.Decimal(pieceAmount).add(resLen ? result[resLen - 1][3] : 0).toNumber();
4472
- result.push([price, quantity, newQuantity, newAmount]);
4473
- }
4474
- return result;
4234
+ })
4235
+ );
4236
+ var useTokensInfo = () => {
4237
+ const tokensInfo = useTokensInfoStore((state) => state.tokensInfo);
4238
+ return tokensInfo;
4475
4239
  };
4476
- var reduceOrderbook = (depth, level, padding, data) => {
4477
- let asks = reduceItems(depth, data.asks, true);
4478
- let bids = reduceItems(depth, data.bids, false);
4479
- if (asks.length !== 0 && bids.length !== 0 && asks[0][0] <= bids[0][0]) {
4480
- if (asks.length === 1) {
4481
- const [price, qty, newQuantity, newAmount] = asks[0];
4482
- asks.shift();
4483
- asks.push([
4484
- price + (depth === void 0 ? 0 : Number(depth)),
4485
- qty,
4486
- newQuantity,
4487
- newAmount
4488
- ]);
4489
- } else {
4490
- const [bidPrice] = bids[0];
4491
- while (asks.length > 0) {
4492
- const [askPrice, askQty, newQuantity, newAmount] = asks[0];
4493
- if (askPrice <= bidPrice) {
4494
- asks.shift();
4495
- for (let i = 0; i < asks.length; i++) {
4496
- if (i === 0) {
4497
- const quantity = new utils.Decimal(asks[i][1]).add(askQty);
4498
- asks[i][1] = quantity.toNumber();
4499
- asks[i][2] = quantity.toNumber();
4500
- asks[i][3] = quantity.toDecimalPlaces(0, utils.Decimal.ROUND_CEIL).mul(asks[i][0]).toNumber();
4501
- } else {
4502
- asks[i][3] = new utils.Decimal(asks[i][0]).mul(asks[i][1]).add(asks[i - 1][3]).toNumber();
4503
- }
4504
- }
4505
- } else {
4506
- break;
4507
- }
4508
- }
4240
+ var useTokenInfo = (token) => {
4241
+ const tokensInfo = useTokensInfo();
4242
+ return React2.useMemo(() => {
4243
+ return tokensInfo?.find((item) => item.token === token);
4244
+ }, [tokensInfo, token]);
4245
+ };
4246
+
4247
+ // src/orderly/useComputedLTV.ts
4248
+ var { LTV, collateralRatio } = perp.account;
4249
+ var useComputedLTV = (options = {}) => {
4250
+ const { input, token } = options;
4251
+ const isUSDC = token?.toUpperCase() === "USDC";
4252
+ const tokensInfo = useTokensInfo();
4253
+ const { usdc, data: holdingList = [] } = useHoldingStream();
4254
+ const { getIndexPrice } = useIndexPricesStream();
4255
+ const { unsettledPnL } = useCollateral();
4256
+ const usdcBalance = React2.useMemo(() => {
4257
+ if (isUSDC && input) {
4258
+ return new utils.Decimal(usdc?.holding ?? 0).add(input).toNumber();
4509
4259
  }
4260
+ return usdc?.holding ?? 0;
4261
+ }, [usdc?.holding, input, isUSDC]);
4262
+ const getAdjustedQty = React2.useCallback(
4263
+ (item) => {
4264
+ if (input && item.token === token) {
4265
+ return new utils.Decimal(item?.holding ?? 0).add(input).toNumber();
4266
+ }
4267
+ return item?.holding ?? 0;
4268
+ },
4269
+ [input, token]
4270
+ );
4271
+ const memoizedLTV = React2.useMemo(() => {
4272
+ return LTV({
4273
+ usdcBalance,
4274
+ upnl: unsettledPnL,
4275
+ assets: holdingList.filter((h) => h.token.toUpperCase() !== "USDC").map((item) => {
4276
+ const indexPrice = getIndexPrice(item.token);
4277
+ const findToken = tokensInfo?.find((i) => i.token === item.token);
4278
+ const qty = getAdjustedQty(item);
4279
+ const weight = collateralRatio({
4280
+ baseWeight: findToken?.base_weight ?? 0,
4281
+ discountFactor: findToken?.discount_factor ?? 0,
4282
+ collateralCap: findToken?.user_max_qty ?? qty,
4283
+ collateralQty: qty,
4284
+ indexPrice
4285
+ });
4286
+ return {
4287
+ qty,
4288
+ indexPrice,
4289
+ weight: weight.toNumber()
4290
+ };
4291
+ })
4292
+ });
4293
+ }, [
4294
+ usdcBalance,
4295
+ unsettledPnL,
4296
+ holdingList,
4297
+ tokensInfo,
4298
+ getIndexPrice,
4299
+ getAdjustedQty
4300
+ ]);
4301
+ if (new utils.Decimal(usdcBalance).add(new utils.Decimal(unsettledPnL)).gte(utils.zero)) {
4302
+ return 0;
4510
4303
  }
4511
- asks = asks.reverse();
4512
- if (padding) {
4513
- asks = asks.length < level ? paddingFn(level - asks.length).concat(asks) : asks;
4514
- bids = bids.length < level ? bids.concat(paddingFn(level - bids.length)) : bids;
4515
- }
4516
- return {
4517
- asks,
4518
- bids
4519
- };
4520
- };
4521
- var ORDERLY_ORDERBOOK_DEPTH_KEY = "orderly_orderbook_depth_key";
4522
- var INIT_DATA = {
4523
- asks: [],
4524
- bids: []
4304
+ return new utils.Decimal(memoizedLTV).mul(100).toDecimalPlaces(2, utils.Decimal.ROUND_DOWN).toNumber();
4525
4305
  };
4526
- var useOrderbookStream = (symbol, initial = INIT_DATA, options) => {
4306
+ var useFundingRate = (symbol) => {
4527
4307
  if (!symbol) {
4528
4308
  throw new types.SDKError("Symbol is required");
4529
4309
  }
4530
- const level = options?.level ?? 10;
4531
- const padding = options?.padding ?? true;
4532
- const symbolRef = React2.useRef(symbol);
4533
- symbolRef.current = symbol;
4534
- const {
4535
- defaultOrderbookTickSizes: DEFAULT_TICK_SIZES2 = {},
4536
- defaultOrderbookSymbolDepths: DEFAULT_SYMBOL_DEPTHS2 = {}
4537
- } = React2.useContext(OrderlyContext);
4538
- const [data, setData] = React2.useState(initial);
4539
- const [isLoading, setIsLoading] = React2.useState(true);
4540
- const config = useSymbolsInfo()[symbol];
4541
- const [depthObject, setDepthObject] = useLocalStorage(
4542
- ORDERLY_ORDERBOOK_DEPTH_KEY,
4543
- {}
4544
- );
4545
- const prevMiddlePrice = React2.useRef(0);
4546
- const tick = config("quote_tick");
4547
- const depths = React2.useMemo(() => {
4548
- if (DEFAULT_SYMBOL_DEPTHS2[symbol]) {
4549
- return DEFAULT_SYMBOL_DEPTHS2[symbol];
4550
- }
4551
- if (typeof tick === "undefined") {
4552
- return [];
4553
- }
4554
- try {
4555
- const base = new utils.Decimal(tick);
4556
- return [
4557
- base.toNumber(),
4558
- base.mul(10).toNumber(),
4559
- base.mul(100).toNumber(),
4560
- base.mul(1e3).toNumber()
4561
- ];
4562
- } catch {
4310
+ const [countDown, setCountDown] = React2.useState("00:00:00");
4311
+ const timerRef = React2.useRef(null);
4312
+ const { data, isLoading } = useQuery(
4313
+ `/v1/public/funding_rate/${symbol}`,
4314
+ {
4315
+ fallbackData: {
4316
+ est_funding_rate: 0,
4317
+ next_funing_time: 0
4318
+ }
4563
4319
  }
4564
- return [tick];
4565
- }, [symbol, tick]);
4320
+ );
4566
4321
  React2.useEffect(() => {
4567
- if (depthObject[symbol]) {
4322
+ if (!data || isLoading) {
4568
4323
  return;
4569
4324
  }
4570
- if (DEFAULT_TICK_SIZES2[symbol]) {
4571
- setDepthObject((prev) => ({
4572
- ...prev,
4573
- [symbol]: Number(DEFAULT_TICK_SIZES2[symbol])
4574
- }));
4575
- } else {
4576
- setDepthObject((prev) => ({
4577
- ...prev,
4578
- [symbol]: tick
4579
- }));
4325
+ const { next_funding_time } = data;
4326
+ if (!next_funding_time || next_funding_time <= 0) {
4327
+ return;
4580
4328
  }
4581
- }, [depthObject, tick, symbol, DEFAULT_TICK_SIZES2]);
4582
- const onDepthChange = React2.useCallback(
4583
- (val) => {
4584
- setDepthObject((prev) => ({ ...prev, [symbol]: val }));
4585
- },
4586
- [symbol]
4587
- );
4588
- const ws = useWS();
4589
- const ticker = useTickerStream(symbol);
4590
- const eventEmitter = useEventEmitter();
4591
- React2.useEffect(() => {
4592
- let needRequestFullOrderbook = true;
4593
- setIsLoading(true);
4594
- let fullOrderBookUpdateSub;
4595
- const orderBookUpdateSub = ws.subscribe(
4596
- {
4597
- event: "subscribe",
4598
- topic: `${symbol}@orderbookupdate`
4599
- },
4600
- {
4601
- formatter: (message) => message,
4602
- onMessage: (message) => {
4603
- const { data: wsData, ts } = message;
4604
- const { symbol: symbol2, asks, bids, prevTs } = wsData;
4605
- if (symbolRef.current !== symbol2) {
4606
- orderBookUpdateSub?.();
4607
- return;
4608
- }
4609
- orderbook_service_default.updateOrderbook(
4610
- symbol2,
4611
- { asks, bids, ts, prevTs },
4612
- () => {
4613
- needRequestFullOrderbook = true;
4614
- }
4615
- );
4616
- const data2 = orderbook_service_default.getRawOrderbook(symbol2);
4617
- setData({ bids: data2.bids, asks: data2.asks });
4329
+ timerRef.current = setInterval(() => {
4330
+ const diff = new Date(next_funding_time).getTime() - utils.getTimestamp();
4331
+ if (diff <= 0) {
4332
+ setCountDown("00:00:00");
4333
+ if (timerRef.current) {
4334
+ clearInterval(timerRef.current);
4618
4335
  }
4336
+ return;
4337
+ }
4338
+ const result = utils.timeConvertString(diff);
4339
+ if (result.length === 3) {
4340
+ setCountDown(
4341
+ `${result[0].toString().padStart(2, "0")}:${result[1].toString().padStart(2, "0")}:${result[2].toString().padStart(2, "0")}`
4342
+ );
4343
+ }
4344
+ }, 1e3);
4345
+ return () => {
4346
+ if (timerRef.current) {
4347
+ clearInterval(timerRef.current);
4619
4348
  }
4349
+ };
4350
+ }, [data, isLoading]);
4351
+ const est_funding_rate = React2.useMemo(() => {
4352
+ if (!data) {
4353
+ return;
4354
+ }
4355
+ const { next_funding_time, est_funding_rate: est_funding_rate2 = 0 } = data;
4356
+ if (utils.getTimestamp() > next_funding_time) {
4357
+ return null;
4358
+ }
4359
+ return new utils.Decimal(Number(est_funding_rate2) * 100).toFixed(
4360
+ 4,
4361
+ utils.Decimal.ROUND_DOWN
4620
4362
  );
4621
- if (needRequestFullOrderbook) {
4622
- setIsLoading(true);
4623
- fullOrderBookUpdateSub = ws.onceSubscribe(
4624
- {
4625
- event: "request",
4626
- id: `${symbol}@orderbook`,
4627
- params: {
4628
- type: "orderbook",
4629
- symbol
4630
- }
4363
+ }, [data]);
4364
+ return {
4365
+ ...data,
4366
+ est_funding_rate,
4367
+ countDown
4368
+ };
4369
+ };
4370
+ var useFundingDetails = (symbol) => {
4371
+ if (!symbol) {
4372
+ throw new types.SDKError("symbol is required");
4373
+ }
4374
+ return useQuery(`/v1/public/info/${symbol}`, {
4375
+ errorRetryCount: 3,
4376
+ revalidateOnFocus: false
4377
+ });
4378
+ };
4379
+ var calculatePositiveRate = (periodData, period) => {
4380
+ if (!periodData || !period) {
4381
+ return 0;
4382
+ }
4383
+ const daysMap = {
4384
+ "1d": 1,
4385
+ "3d": 3,
4386
+ "7d": 7,
4387
+ "14d": 14,
4388
+ "30d": 30,
4389
+ "90d": 90
4390
+ };
4391
+ const totalTimes = daysMap[period] * 3;
4392
+ return periodData.positive / totalTimes;
4393
+ };
4394
+ var useFundingRateHistory = () => {
4395
+ const { data: historyData, isLoading } = useQuery(
4396
+ "/v1/public/market_info/funding_history"
4397
+ );
4398
+ const getPositiveRates = React2.useCallback(
4399
+ (data, period) => {
4400
+ if (!data?.length) {
4401
+ return {};
4402
+ }
4403
+ return data.reduce(
4404
+ (acc, item) => {
4405
+ acc[item.symbol] = calculatePositiveRate(
4406
+ item.funding[period],
4407
+ period
4408
+ );
4409
+ return acc;
4631
4410
  },
4632
- {
4633
- formatter: (message) => message,
4634
- onMessage: (message) => {
4635
- const { symbol: symbol2, asks, bids, ts } = message.data;
4636
- if (symbolRef.current !== symbol2) {
4637
- return;
4638
- }
4639
- orderbook_service_default.setFullOrderbook(symbol2, { asks, bids, ts });
4640
- const data2 = orderbook_service_default.getRawOrderbook(symbol2);
4641
- setData({ bids: data2.bids, asks: data2.asks });
4642
- setIsLoading(false);
4643
- }
4644
- }
4411
+ {}
4645
4412
  );
4646
- needRequestFullOrderbook = false;
4647
- }
4648
- return () => {
4649
- orderBookUpdateSub?.();
4650
- fullOrderBookUpdateSub?.();
4651
- orderbook_service_default.resetOrderBook(symbol);
4652
- setData(INIT_DATA);
4653
- };
4654
- }, [symbol]);
4655
- const { data: markPrice } = useMarkPrice(symbol);
4656
- const onItemClick = React2.useCallback((item) => {
4657
- eventEmitter.emit("orderbook:item:click", item);
4658
- }, []);
4659
- const reducedData = reduceOrderbook(depthObject[symbol], level, padding, {
4660
- asks: data.asks,
4661
- bids: data.bids
4662
- });
4663
- React2.useEffect(() => {
4664
- eventEmitter.emit("orderbook:update", reducedData);
4665
- }, [reducedData]);
4666
- const middlePrice = React2.useMemo(() => {
4667
- let asksFrist = 0;
4668
- let bidsFirst = 0;
4669
- if (data.asks.length > 0) {
4670
- asksFrist = reducedData.asks?.[reducedData.asks.length - 1]?.[0];
4671
- }
4672
- if (data.bids.length > 0) {
4673
- bidsFirst = data.bids[0][0];
4674
- }
4675
- if (!isNumber(asksFrist) || !isNumber(bidsFirst) || !ticker) {
4676
- return 0;
4677
- }
4678
- return [asksFrist, bidsFirst, ticker["24h_close"]].sort()[1];
4679
- }, [ticker?.["24h_close"], data]);
4680
- React2.useEffect(() => {
4681
- prevMiddlePrice.current = middlePrice;
4682
- }, [middlePrice]);
4683
- return [
4684
- {
4685
- asks: reducedData.asks.slice(-level),
4686
- bids: reducedData.bids.slice(0, level),
4687
- markPrice,
4688
- middlePrice: [prevMiddlePrice.current, middlePrice]
4689
4413
  },
4690
- {
4691
- onDepthChange,
4692
- depth: depthObject[symbol],
4693
- allDepths: depths,
4414
+ []
4415
+ );
4416
+ return React2.useMemo(() => {
4417
+ return {
4418
+ data: historyData ?? types.EMPTY_LIST,
4694
4419
  isLoading,
4695
- onItemClick
4696
- }
4697
- ];
4420
+ getPositiveRates
4421
+ };
4422
+ }, [historyData, isLoading, getPositiveRates]);
4698
4423
  };
4699
- var useSymbolInfo = (symbol) => {
4700
- const infos = useSymbolsInfo();
4701
- return React2.useMemo(() => {
4702
- return !symbol || infos.isNil ? null : infos[symbol];
4703
- }, [infos, symbol]);
4424
+ var useApiStatusStore = zustand.create()(
4425
+ immer.immer((set) => ({
4426
+ apis: {
4427
+ positions: {
4428
+ loading: false
4429
+ }
4430
+ },
4431
+ actions: {
4432
+ updateStatus: (key, status) => {
4433
+ set((state) => {
4434
+ state.apis[key] = status;
4435
+ });
4436
+ },
4437
+ updateApiLoading: (key, loading) => {
4438
+ set((state) => {
4439
+ state.apis[key].loading = loading;
4440
+ });
4441
+ },
4442
+ updateApiError: (key, error) => {
4443
+ set((state) => {
4444
+ state.apis[key] = {
4445
+ loading: false,
4446
+ error
4447
+ };
4448
+ });
4449
+ }
4450
+ }
4451
+ }))
4452
+ );
4453
+ var useApiStatusActions = () => useApiStatusStore((state) => state.actions);
4454
+
4455
+ // src/orderly/calculator/baseCalculator.ts
4456
+ var BaseCalculator = class {
4457
+ cache(data) {
4458
+ this._cache = data;
4459
+ }
4704
4460
  };
4705
- var useFundingRates = () => {
4706
- const data = useAppStore((state) => state.fundingRates);
4707
- return createGetter({ ...data });
4461
+
4462
+ // src/orderly/calculator/markPrice.ts
4463
+ var MarketCalculatorName = "markPriceCalculator";
4464
+ var MarkPriceCalculator = class extends BaseCalculator {
4465
+ constructor() {
4466
+ super(...arguments);
4467
+ this.name = MarketCalculatorName;
4468
+ }
4469
+ calc(scope, data, ctx) {
4470
+ return data;
4471
+ }
4472
+ update(data, scope) {
4473
+ useMarkPriceStore.getState().actions.updateMarkPrice(data);
4474
+ }
4708
4475
  };
4709
- var useFundingRatesStore = () => {
4710
- const data = useAppStore((state) => state.fundingRates);
4711
- return data;
4476
+
4477
+ // src/orderly/calculator/calculatorContext.ts
4478
+ var CalculatorContext = class _CalculatorContext {
4479
+ static get instance() {
4480
+ return this._instance;
4481
+ }
4482
+ static create(scope, data) {
4483
+ if (!this._instance) {
4484
+ this._instance = new _CalculatorContext(scope, data);
4485
+ }
4486
+ return this._instance.update(scope, data);
4487
+ }
4488
+ constructor(scope, data) {
4489
+ this.setCtxData();
4490
+ this.output = {
4491
+ // rows: positions,
4492
+ };
4493
+ }
4494
+ update(scope, data) {
4495
+ this.setCtxData();
4496
+ this.markPrices = scope === "markPrice" /* MARK_PRICE */ ? data : this.output[MarketCalculatorName];
4497
+ this.portfolio = this.output["portfolio"] || useAppStore.getState().portfolio;
4498
+ return this;
4499
+ }
4500
+ setCtxData() {
4501
+ this.accountInfo = useAppStore.getState().accountInfo;
4502
+ this.symbolsInfo = useAppStore.getState().symbolsInfo;
4503
+ this.fundingRates = useAppStore.getState().fundingRates;
4504
+ this.tokensInfo = useTokensInfoStore.getState().tokensInfo;
4505
+ }
4506
+ get(fn) {
4507
+ return fn(this.output);
4508
+ }
4509
+ getCacheValue(name, fallback) {
4510
+ return this.output[name] || fallback();
4511
+ }
4512
+ clearCache() {
4513
+ this.output = {};
4514
+ this.accountInfo = void 0;
4515
+ this.portfolio = void 0;
4516
+ }
4517
+ deleteByName(name) {
4518
+ delete this.output[name];
4519
+ }
4520
+ // get positions(): API.PositionTPSLExt[] {
4521
+ // if (this.output.positionCalculator) return this.output.positionCalculator;
4522
+ // return usePositionStore.getState().positions;
4523
+ // }
4524
+ get isReady() {
4525
+ return !!this.accountInfo;
4526
+ }
4527
+ saveOutput(name, data) {
4528
+ this.output[name] = data;
4529
+ }
4530
+ outputToValue() {
4531
+ return this.output;
4532
+ }
4712
4533
  };
4713
4534
 
4714
- // src/orderly/useMarket.ts
4715
- var DefaultTab = { name: "Popular", id: 1 };
4716
- var marketsKey = "markets";
4717
- var useMarket = (type) => {
4718
- const { configStore } = React2.useContext(OrderlyContext);
4719
- const symbolsInfo = useSymbolsInfo();
4720
- const fundingRates = useFundingRates();
4721
- const { data: futures } = useMarketsStream();
4722
- const updateStore = (key, data) => {
4723
- configStore.set(marketsKey, {
4724
- ...configStore.getOr(marketsKey, {}),
4725
- [key]: data
4726
- });
4727
- };
4728
- const getStore = (key, defaultValue) => {
4729
- return configStore.get(marketsKey)[key] || defaultValue;
4730
- };
4731
- if (!configStore.get(marketsKey)) {
4732
- const jsonStr = localStorage.getItem(marketsKey);
4733
- if (jsonStr) {
4734
- configStore.set(marketsKey, JSON.parse(jsonStr));
4535
+ // src/orderly/calculator/calculatorService.ts
4536
+ var CalculatorService = class {
4537
+ constructor(scheduler, calculators) {
4538
+ this.scheduler = scheduler;
4539
+ this.pendingCalc = [];
4540
+ this.calcQueue = [];
4541
+ /**
4542
+ * Reference count for each calculator, used to determine if a calculator is still in use.
4543
+ */
4544
+ this.referenceCount = /* @__PURE__ */ new Map();
4545
+ this.isPaused = false;
4546
+ this.calculators = new Map(calculators);
4547
+ }
4548
+ register(scope, calculator) {
4549
+ const ref_count_name = `${scope}_${calculator.name}`;
4550
+ const count = this.referenceCount.get(ref_count_name);
4551
+ if (typeof count !== "undefined" && count > 0) {
4552
+ this.referenceCount.set(ref_count_name, count + 1);
4553
+ return;
4554
+ }
4555
+ const calculators = this.calculators.get(scope);
4556
+ if (Array.isArray(calculators)) {
4557
+ calculators.push(calculator);
4735
4558
  } else {
4736
- configStore.set(marketsKey, {
4737
- recent: [],
4738
- favorites: [
4739
- { name: "PERP_ETH_USDC", tabs: [{ ...DefaultTab }] },
4740
- { name: "PERP_BTC_USDC", tabs: [{ ...DefaultTab }] }
4741
- ],
4742
- favoriteTabs: [{ ...DefaultTab }],
4743
- lastSelectedFavoriteTab: { ...DefaultTab }
4744
- });
4559
+ this.calculators.set(scope, [calculator]);
4560
+ }
4561
+ this.referenceCount.set(ref_count_name, 1);
4562
+ }
4563
+ unregister(scope, calculator) {
4564
+ const ref_count_name = `${scope}_${calculator.name}`;
4565
+ const count = this.referenceCount.get(ref_count_name);
4566
+ if (typeof count !== "undefined" && count > 1) {
4567
+ this.referenceCount.set(ref_count_name, count - 1);
4568
+ return;
4569
+ }
4570
+ const calculators = this.calculators.get(scope);
4571
+ if (Array.isArray(calculators)) {
4572
+ const index = calculators.findIndex((c) => c.name === calculator.name);
4573
+ if (index > -1) {
4574
+ calculators[index].destroy?.();
4575
+ calculators.splice(index, 1);
4576
+ }
4745
4577
  }
4578
+ this.referenceCount.delete(ref_count_name);
4746
4579
  }
4747
- const getFavoriteTabs = React2.useMemo(() => {
4748
- return getStore("favoriteTabs", [{ ...DefaultTab }]);
4749
- }, []);
4750
- const getFavorites = React2.useMemo(() => {
4751
- const curData = getStore("favorites", []);
4752
- const tabs = getFavoriteTabs;
4753
- const result = [];
4754
- const len = curData.length;
4755
- for (let index = 0; index < len; index++) {
4756
- const favData = curData[index];
4757
- const favTabs = favData.tabs.filter(
4758
- (tab) => tabs.findIndex((item) => tab.id === item.id) !== -1
4759
- );
4760
- if (favTabs.length) {
4761
- result.push({ ...favData, tabs: favTabs });
4762
- }
4580
+ async calc(scope, data, options) {
4581
+ if (scope !== "position" /* POSITION */) {
4582
+ if (!options?.skipWhenOnPause) ;
4763
4583
  }
4764
- updateStore("favorites", result);
4765
- return result;
4766
- }, [configStore]);
4767
- const [favoriteTabs, setFavoriteTabs] = React2.useState(getFavoriteTabs);
4768
- const [favorites, setFavorites] = React2.useState(getFavorites);
4769
- const [recent, setRecent] = React2.useState(
4770
- getStore("recent", []).filter((e) => e)
4771
- );
4772
- const [tabSort, setTabSort] = React2.useState(
4773
- getStore("tabSort", {})
4774
- );
4775
- const updateFavoriteTabs = (tab, operator) => {
4776
- const saveTabs = (tabs2) => {
4777
- setFavoriteTabs(tabs2);
4778
- updateStore("favoriteTabs", tabs2);
4779
- };
4780
- if (Array.isArray(tab)) {
4781
- saveTabs(tab);
4584
+ const ctx = CalculatorContext.create(scope, data);
4585
+ if (!ctx.isReady && options?.skipPending) {
4782
4586
  return;
4783
4587
  }
4784
- const tabs = [...favoriteTabs];
4785
- const index = tabs.findIndex((item) => item.id === tab.id);
4786
- if (operator?.add) {
4787
- tabs.push(tab);
4788
- } else if (operator?.update) {
4789
- if (index !== -1) {
4790
- tabs[index] = tab;
4588
+ if (options?.skipWhenOnPause && !this.windowIsVisible)
4589
+ return;
4590
+ this.calcQueue.push({ scope, data, options });
4591
+ await this.handleCalcQueue(ctx);
4592
+ this.ctx = ctx;
4593
+ }
4594
+ // private async handlePendingCalc() {
4595
+ // // console.log("[handlePendingCalc]:", this.pendingCalc);
4596
+ // if (this.pendingCalc.length === 0) return;
4597
+ // this.calcQueue = [...this.pendingCalc, ...this.calcQueue];
4598
+ // this.pendingCalc = [];
4599
+ // }
4600
+ async handleCalcQueue(context) {
4601
+ const first = this.calcQueue.shift();
4602
+ if (first) {
4603
+ const { scope, data, options } = first;
4604
+ const ctx = context || CalculatorContext.create(scope, data);
4605
+ const calculators = this.calculators.get(scope);
4606
+ if (Array.isArray(calculators) && calculators.length) {
4607
+ try {
4608
+ await this.scheduler.calc(scope, calculators, data, ctx);
4609
+ } catch (e) {
4610
+ }
4611
+ if (!options?.skipUpdate) {
4612
+ this.scheduler.update(scope, calculators, ctx.outputToValue());
4613
+ }
4791
4614
  }
4792
- } else if (operator?.delete) {
4793
- if (index !== -1) {
4794
- tabs.splice(index, 1);
4615
+ if (this.calcQueue.length) {
4616
+ this.handleCalcQueue(ctx);
4795
4617
  }
4796
4618
  }
4797
- saveTabs(tabs);
4798
- };
4799
- const updateFavorites = (favorites2) => {
4800
- updateStore("favorites", favorites2);
4801
- setFavorites(favorites2);
4802
- };
4803
- const addToHistory = (symbol) => {
4804
- const curData = [...recent];
4805
- const index = curData.findIndex((item) => item.name == symbol.symbol);
4806
- if (index !== -1) {
4807
- curData.splice(index, 1);
4619
+ }
4620
+ stop() {
4621
+ this.calcQueue = [];
4622
+ this.ctx?.clearCache();
4623
+ }
4624
+ get windowIsVisible() {
4625
+ if (typeof document === "undefined") {
4626
+ return true;
4808
4627
  }
4809
- curData.unshift({ name: symbol.symbol });
4810
- updateStore("recent", curData);
4811
- setRecent(curData);
4812
- };
4813
- const updateSymbolFavoriteState = (symbol, tab, remove = false) => {
4814
- const curData = [...favorites];
4815
- const index = curData.findIndex((item) => item.name == symbol.symbol);
4816
- if (index === -1) {
4817
- if (Array.isArray(tab)) {
4818
- if (tab.length > 0) {
4819
- curData.unshift({ name: symbol.symbol, tabs: tab });
4820
- }
4821
- } else {
4822
- if (!remove) {
4823
- curData.unshift({ name: symbol.symbol, tabs: [tab] });
4824
- }
4628
+ return document.visibilityState === "visible";
4629
+ }
4630
+ };
4631
+ var CalculatorServiceID = "CalculatorService";
4632
+
4633
+ // src/orderly/calculator/shardedScheduler.ts
4634
+ var requestIdleCallbackPolyfill = (callback, options) => {
4635
+ const startTime = Date.now();
4636
+ return setTimeout(() => {
4637
+ callback({
4638
+ didTimeout: false,
4639
+ timeRemaining: () => Math.max(0, 50 - (Date.now() - startTime))
4640
+ });
4641
+ }, 1);
4642
+ };
4643
+ var cancelIdleCallbackPolyfill = (id) => {
4644
+ clearTimeout(id);
4645
+ };
4646
+ var safeRequestIdleCallback = typeof window !== "undefined" && window.requestIdleCallback ? window.requestIdleCallback.bind(window) : requestIdleCallbackPolyfill;
4647
+ typeof window !== "undefined" && window.cancelIdleCallback ? window.cancelIdleCallback.bind(window) : cancelIdleCallbackPolyfill;
4648
+ var ShardingScheduler = class {
4649
+ // run(calculators: Calculator[]) {}
4650
+ calc(scope, calculators, data, ctx) {
4651
+ return new Promise((resolve, reject) => {
4652
+ try {
4653
+ this.computation(
4654
+ calculators,
4655
+ (shard) => {
4656
+ const results = [];
4657
+ for (let index = 0; index < shard.length; index++) {
4658
+ const calculator = shard[index];
4659
+ const result = calculator.calc(scope, data, ctx);
4660
+ if (result) {
4661
+ ctx.saveOutput(calculator.name, result);
4662
+ results.push(result);
4663
+ }
4664
+ }
4665
+ return results;
4666
+ },
4667
+ (results) => {
4668
+ resolve(results);
4669
+ }
4670
+ );
4671
+ } catch (error) {
4672
+ reject(error);
4825
4673
  }
4826
- } else {
4827
- const favorite = curData[index];
4828
- if (Array.isArray(tab)) {
4829
- if (tab.length === 0) {
4830
- curData.splice(index, 1);
4674
+ });
4675
+ }
4676
+ update(scope, calculators, data) {
4677
+ for (let index = 0; index < calculators.length; index++) {
4678
+ const calculator = calculators[index];
4679
+ const item = data[calculator.name];
4680
+ if (!!item) {
4681
+ calculator.update(item, scope);
4682
+ }
4683
+ }
4684
+ return Promise.resolve();
4685
+ }
4686
+ computation(data, processor, onComplete) {
4687
+ let index = 0;
4688
+ const results = [];
4689
+ const estimatedShardSize = Math.min(data.length, 2);
4690
+ function processNextShard(deadline) {
4691
+ let shardSize = estimatedShardSize;
4692
+ while (index + shardSize <= data.length && deadline.timeRemaining() > 0) {
4693
+ const shard = data.slice(index, index + shardSize);
4694
+ const result = processor(shard);
4695
+ results.push(result);
4696
+ index += shardSize;
4697
+ if (deadline.timeRemaining() < 1) {
4698
+ shardSize = Math.max(1, Math.floor(shardSize / 2));
4831
4699
  } else {
4832
- curData[index] = { ...favorite, tabs: tab };
4700
+ shardSize = Math.min(data.length - index, shardSize * 2);
4833
4701
  }
4702
+ }
4703
+ if (index < data.length) {
4704
+ safeRequestIdleCallback(processNextShard, {
4705
+ timeout: 1e3
4706
+ });
4834
4707
  } else {
4835
- if (remove) {
4836
- const tabs = favorite.tabs.filter((item) => item.id != tab.id);
4837
- if (tabs.length === 0) {
4838
- curData.splice(index, 1);
4839
- } else {
4840
- curData[index] = { ...favorite, tabs };
4841
- }
4842
- } else {
4843
- const tabs = favorite.tabs;
4844
- tabs.push(tab);
4845
- curData[index] = { ...favorite, tabs };
4846
- }
4708
+ onComplete(results.flat());
4709
+ }
4710
+ }
4711
+ safeRequestIdleCallback(processNextShard, {
4712
+ timeout: 1e3
4713
+ });
4714
+ }
4715
+ };
4716
+ var POSITION_EMPTY = {
4717
+ rows: null,
4718
+ margin_ratio: 0,
4719
+ initial_margin_ratio: 0,
4720
+ maintenance_margin_ratio: 0,
4721
+ open_margin_ratio: 0,
4722
+ current_margin_ratio_with_orders: 0,
4723
+ initial_margin_ratio_with_orders: 0,
4724
+ maintenance_margin_ratio_with_orders: 0,
4725
+ total_collateral_value: 0,
4726
+ free_collateral: 0,
4727
+ total_pnl_24_h: 0,
4728
+ unrealPnL: 0,
4729
+ total_unreal_pnl: 0,
4730
+ unsettledPnL: 0,
4731
+ total_unsettled_pnl: 0,
4732
+ notional: 0,
4733
+ unrealPnlROI: 0
4734
+ };
4735
+ var usePositionStore = zustand.create()(
4736
+ immer.immer((set) => ({
4737
+ positions: {
4738
+ all: POSITION_EMPTY
4739
+ },
4740
+ actions: {
4741
+ setPositions: (key, positions3) => {
4742
+ set((state) => {
4743
+ state.positions[key] = positions3;
4744
+ });
4745
+ },
4746
+ closePosition: (symbol) => {
4747
+ set((state) => {
4748
+ delete state.positions[symbol];
4749
+ });
4750
+ },
4751
+ clearAll: () => {
4752
+ set((state) => {
4753
+ state.positions = {
4754
+ all: POSITION_EMPTY
4755
+ };
4756
+ });
4847
4757
  }
4848
4758
  }
4849
- updateStore("favorites", curData);
4850
- setFavorites(() => curData);
4851
- };
4852
- const marketsList = React2.useMemo(() => {
4853
- const list = futures?.map((item) => {
4854
- const { open_interest = 0, index_price = 0 } = item;
4759
+ }))
4760
+ );
4761
+ var usePositions = (symbol = "all") => usePositionStore((state) => (state.positions[symbol] ?? POSITION_EMPTY).rows);
4762
+ var usePositionActions = () => usePositionStore((state) => state.actions);
4763
+
4764
+ // src/orderly/calculator/positions.ts
4765
+ var NAME_PREFIX = "positionCalculator";
4766
+ var AllPositions = "all";
4767
+ var PositionCalculator = class extends BaseCalculator {
4768
+ // private id: string;
4769
+ constructor(symbol = AllPositions) {
4770
+ super();
4771
+ this.name = `${NAME_PREFIX}_${symbol}`;
4772
+ this.symbol = symbol;
4773
+ }
4774
+ calc(scope, data, ctx) {
4775
+ if (scope === "markPrice" /* MARK_PRICE */) {
4776
+ return this.calcByMarkPrice(data, ctx);
4777
+ }
4778
+ if (scope === "indexPrice" /* INDEX_PRICE */) {
4779
+ return this.calcByIndexPrice(data, ctx);
4780
+ }
4781
+ if (scope === "position" /* POSITION */) {
4782
+ return this.calcByPosition(
4783
+ this.preprocess(data),
4784
+ ctx
4785
+ );
4786
+ }
4787
+ return data;
4788
+ }
4789
+ update(data, scope) {
4790
+ if (!data || !Array.isArray(data.rows))
4791
+ return;
4792
+ usePositionStore.getState().actions.setPositions(this.symbol, data);
4793
+ if (Array.isArray(data.rows) && useApiStatusStore.getState().apis.positions.loading) {
4794
+ useApiStatusStore.getState().actions.updateApiLoading("positions", false);
4795
+ }
4796
+ }
4797
+ calcByMarkPrice(markPrice, ctx) {
4798
+ let positions3 = this.getPosition(markPrice, ctx);
4799
+ useAppStore.getState().fundingRates;
4800
+ if (!positions3 || !Array.isArray(positions3.rows) || !positions3.rows.length)
4801
+ return positions3;
4802
+ positions3 = {
4803
+ ...positions3,
4804
+ rows: positions3.rows.map((item) => {
4805
+ return {
4806
+ ...item,
4807
+ mark_price: markPrice[item.symbol] || item.mark_price
4808
+ };
4809
+ })
4810
+ };
4811
+ return this.format(positions3, ctx);
4812
+ }
4813
+ calcByIndexPrice(indexPrice, ctx) {
4814
+ let positions3 = this.getPosition(indexPrice, ctx);
4815
+ if (!positions3) {
4816
+ return positions3;
4817
+ }
4818
+ if (!Array.isArray(positions3.rows) || !positions3.rows.length)
4819
+ return positions3;
4820
+ positions3 = {
4821
+ ...positions3,
4822
+ rows: positions3.rows.map((item) => ({
4823
+ ...item,
4824
+ index_price: indexPrice[item.symbol] || item.index_price || item.mark_price
4825
+ }))
4826
+ };
4827
+ return this.format(positions3, ctx);
4828
+ }
4829
+ calcByPosition(positions3, ctx) {
4830
+ if (positions3.rows.length === 0)
4831
+ return positions3;
4832
+ return this.format(positions3, ctx);
4833
+ }
4834
+ format(data, ctx) {
4835
+ const { accountInfo, symbolsInfo, fundingRates, portfolio } = ctx;
4836
+ if (!accountInfo || !fundingRates || !symbolsInfo) {
4837
+ return data;
4838
+ }
4839
+ let unrealPnL_total = utils.zero, unrealPnL_total_index = utils.zero, notional_total = utils.zero, unsettlementPnL_total = utils.zero;
4840
+ let rows = data.rows.map((item) => {
4855
4841
  const info = symbolsInfo[item.symbol];
4856
- const rate = fundingRates[item.symbol];
4857
- const est_funding_rate = rate("est_funding_rate");
4858
- const funding_period = info("funding_period");
4859
- const change = item.change === void 0 ? get24hChange2(item["24h_close"], item["24h_open"]) : item.change;
4842
+ const sum_unitary_funding = fundingRates?.[item.symbol]?.["sum_unitary_funding"] ?? 0;
4843
+ const notional = perp.positions.notional(item.position_qty, item.mark_price);
4844
+ const unrealPnl = perp.positions.unrealizedPnL({
4845
+ qty: item.position_qty,
4846
+ openPrice: item?.average_open_price,
4847
+ // markPrice: unRealizedPrice,
4848
+ markPrice: item.mark_price
4849
+ });
4850
+ let unrealPnl_index = 0, unrealPnlROI_index = 0;
4851
+ const maxLeverage = item.leverage || 1;
4852
+ const imr = perp.account.IMR({
4853
+ maxLeverage,
4854
+ baseIMR: info?.["base_imr"],
4855
+ IMR_Factor: accountInfo.imr_factor[item.symbol],
4856
+ positionNotional: notional,
4857
+ ordersNotional: 0,
4858
+ IMR_factor_power: 4 / 5
4859
+ });
4860
+ const unrealPnlROI = perp.positions.unrealizedPnLROI({
4861
+ positionQty: item.position_qty,
4862
+ openPrice: item.average_open_price,
4863
+ IMR: imr,
4864
+ unrealizedPnL: unrealPnl
4865
+ });
4866
+ if (item.index_price) {
4867
+ unrealPnl_index = perp.positions.unrealizedPnL({
4868
+ qty: item.position_qty,
4869
+ openPrice: item?.average_open_price,
4870
+ // markPrice: unRealizedPrice,
4871
+ markPrice: item.index_price
4872
+ });
4873
+ unrealPnlROI_index = perp.positions.unrealizedPnLROI({
4874
+ positionQty: item.position_qty,
4875
+ openPrice: item.average_open_price,
4876
+ IMR: imr,
4877
+ unrealizedPnL: unrealPnl_index
4878
+ });
4879
+ }
4880
+ const unsettlementPnL2 = perp.positions.unsettlementPnL({
4881
+ positionQty: item.position_qty,
4882
+ markPrice: item.mark_price,
4883
+ costPosition: item.cost_position,
4884
+ sumUnitaryFunding: ramda.propOr(
4885
+ 0,
4886
+ "sum_unitary_funding",
4887
+ fundingRates[item.symbol]
4888
+ ),
4889
+ lastSumUnitaryFunding: item.last_sum_unitary_funding
4890
+ });
4891
+ const MMR = perp.positions.MMR({
4892
+ baseMMR: info?.["base_mmr"],
4893
+ baseIMR: info?.["base_imr"],
4894
+ IMRFactor: accountInfo.imr_factor[item.symbol],
4895
+ positionNotional: notional,
4896
+ IMR_factor_power: 4 / 5
4897
+ });
4898
+ unrealPnL_total = unrealPnL_total.add(unrealPnl);
4899
+ unrealPnL_total_index = unrealPnL_total_index.add(unrealPnl_index);
4900
+ notional_total = notional_total.add(notional);
4901
+ unsettlementPnL_total = unsettlementPnL_total.add(unsettlementPnL2);
4902
+ const fundingFee = new utils.Decimal(sum_unitary_funding).sub(item.last_sum_unitary_funding).mul(item.position_qty).negated().toNumber();
4860
4903
  return {
4861
4904
  ...item,
4862
- change,
4863
- "8h_funding": get8hFunding2(est_funding_rate, funding_period),
4864
- quote_dp: info("quote_dp"),
4865
- created_time: info("created_time"),
4866
- openInterest: new utils.Decimal(open_interest || 0).mul(index_price || 0).toNumber()
4905
+ fundingFee,
4906
+ mm: perp.positions.maintenanceMargin({
4907
+ positionQty: item.position_qty,
4908
+ markPrice: item.mark_price,
4909
+ MMR
4910
+ }),
4911
+ mmr: MMR,
4912
+ notional,
4913
+ unsettlement_pnl: unsettlementPnL2,
4914
+ unrealized_pnl: unrealPnl,
4915
+ unrealized_pnl_index: unrealPnl_index,
4916
+ unrealized_pnl_ROI: unrealPnlROI,
4917
+ unrealized_pnl_ROI_index: unrealPnlROI_index
4867
4918
  };
4868
4919
  });
4869
- return list || [];
4870
- }, [symbolsInfo, futures, fundingRates]);
4871
- const getData = (type2) => {
4872
- const localData = type2 === 0 /* FAVORITES */ ? [...favorites] : [...recent];
4873
- const keys = localData.map((item) => item.name);
4874
- const filter = type2 == 2 /* ALL */ ? marketsList : marketsList?.filter((item) => keys.includes(item.symbol));
4875
- const favoritesData = [...favorites];
4876
- const favoriteKeys = favoritesData.map((item) => item.name);
4877
- if (filter) {
4878
- for (let index = 0; index < filter.length; index++) {
4879
- const element = filter[index];
4880
- const isFavorite = type2 == 0 /* FAVORITES */ ? true : favoriteKeys.includes(element.symbol);
4881
- const fIndex = favoritesData.findIndex(
4882
- (item) => item.name === element.symbol
4883
- );
4884
- const tabs = fIndex === -1 ? [] : favoritesData[fIndex].tabs;
4885
- let imr = void 0;
4886
- if (symbolsInfo) {
4887
- imr = symbolsInfo?.[element.symbol]("base_imr");
4888
- }
4889
- filter[index] = {
4890
- ...filter[index],
4891
- isFavorite,
4892
- tabs,
4893
- leverage: imr ? 1 / imr : void 0
4920
+ const totalUnrealPnl = unrealPnL_total.toNumber();
4921
+ const totalUnrealPnl_index = unrealPnL_total_index.toNumber();
4922
+ const unsettlementPnL = unsettlementPnL_total.toNumber();
4923
+ let totalUnrealizedROI = 0, totalUnrealizedROI_index = 0;
4924
+ if (portfolio) {
4925
+ const { totalValue, totalCollateral } = portfolio;
4926
+ rows = rows.map((item) => {
4927
+ const est_liq_price = perp.positions.liqPrice({
4928
+ markPrice: item.mark_price,
4929
+ totalCollateral: totalCollateral.toNumber(),
4930
+ positionQty: item.position_qty,
4931
+ positions: rows,
4932
+ MMR: item.mmr
4933
+ });
4934
+ return {
4935
+ ...item,
4936
+ est_liq_price
4894
4937
  };
4938
+ });
4939
+ if (totalValue !== null && !totalValue.eq(utils.zero)) {
4940
+ totalUnrealizedROI = perp.account.totalUnrealizedROI({
4941
+ totalUnrealizedPnL: totalUnrealPnl,
4942
+ totalValue: totalValue.toNumber()
4943
+ });
4944
+ totalUnrealizedROI_index = perp.account.totalUnrealizedROI({
4945
+ totalUnrealizedPnL: totalUnrealPnl_index,
4946
+ totalValue: totalValue.toNumber()
4947
+ });
4895
4948
  }
4896
4949
  }
4897
- return filter;
4898
- };
4899
- const pinToTop = (symbol) => {
4900
- const index = favorites.findIndex((item) => item.name === symbol.symbol);
4901
- if (index !== -1) {
4902
- const element = favorites[index];
4903
- const list = [...favorites];
4904
- list.splice(index, 1);
4905
- list.unshift(element);
4906
- updateStore("favorites", list);
4907
- setFavorites(list);
4950
+ return {
4951
+ ...data,
4952
+ unrealPnL: totalUnrealPnl,
4953
+ total_unreal_pnl: totalUnrealPnl,
4954
+ total_unreal_pnl_index: totalUnrealPnl_index,
4955
+ notional: notional_total.toNumber(),
4956
+ unsettledPnL: unsettlementPnL,
4957
+ total_unsettled_pnl: unsettlementPnL,
4958
+ unrealPnlROI: totalUnrealizedROI,
4959
+ unrealPnlROI_index: totalUnrealizedROI_index,
4960
+ rows
4961
+ };
4962
+ }
4963
+ preprocess(data) {
4964
+ let rows = data.rows;
4965
+ if (this.symbol !== AllPositions && Array.isArray(rows)) {
4966
+ rows = rows.filter((item) => item.symbol === this.symbol);
4908
4967
  }
4909
- };
4910
- const getLastSelFavTab = () => {
4911
- return getStore("lastSelectedFavoriteTab", { ...DefaultTab });
4912
- };
4913
- const updateSelectedFavoriteTab = (tab) => {
4914
- updateStore("lastSelectedFavoriteTab", tab);
4915
- };
4916
- const updateTabsSortState = (tabId, sortKey, sortOrder) => {
4917
- const map = getStore("tabSort", {});
4918
- map[tabId] = {
4919
- sortKey,
4920
- sortOrder
4968
+ return {
4969
+ ...data,
4970
+ rows
4921
4971
  };
4922
- updateStore("tabSort", map);
4923
- setTabSort(map);
4924
- };
4925
- const markets = getData(type);
4926
- return [
4927
- markets || types.EMPTY_LIST,
4928
- {
4929
- favoriteTabs,
4930
- favorites,
4931
- recent,
4932
- tabSort,
4933
- addToHistory,
4934
- updateFavorites,
4935
- updateFavoriteTabs,
4936
- updateSymbolFavoriteState,
4937
- pinToTop,
4938
- getLastSelFavTab,
4939
- updateSelectedFavoriteTab,
4940
- updateTabsSortState
4972
+ }
4973
+ getPosition(_, ctx) {
4974
+ const positions3 = ctx.get((output) => output[this.name]) || usePositionStore.getState().positions[this.symbol];
4975
+ if (this.symbol === AllPositions) {
4976
+ return positions3;
4941
4977
  }
4942
- ];
4943
- };
4944
- function get8hFunding2(est_funding_rate, funding_period) {
4945
- let funding8h = 0;
4946
- if (est_funding_rate === void 0 || est_funding_rate === null) {
4947
- return null;
4978
+ if (positions3 && Array.isArray(positions3.rows)) {
4979
+ return positions3;
4980
+ }
4981
+ return this.preprocess(this.getAllPositions(ctx));
4948
4982
  }
4949
- if (funding_period) {
4950
- funding8h = new utils.Decimal(est_funding_rate || 0).mul(funding_period).div(8).toNumber();
4983
+ destroy() {
4984
+ usePositionStore.getState().actions.closePosition(this.symbol);
4985
+ CalculatorContext.instance?.deleteByName(this.name);
4951
4986
  }
4952
- return funding8h;
4953
- }
4954
- function get24hChange2(close, open) {
4955
- if (close !== void 0 && open !== void 0) {
4956
- if (open === 0) {
4957
- return 0;
4958
- }
4959
- return new utils.Decimal(close).minus(open).div(open).toNumber();
4987
+ getAllPositions(ctx) {
4988
+ return ctx.get((output) => output[AllPositions]) || usePositionStore.getState().positions[AllPositions];
4960
4989
  }
4961
- }
4962
-
4963
- // src/orderly/useMarkPricesStream.ts
4964
- var useMarkPricesStream = () => {
4965
- const data = useMarkPriceStore((state) => state.markPrices);
4966
- return { data };
4967
- };
4968
-
4969
- // src/orderly/useIndexPricesStream.ts
4970
- var useIndexPricesStream = () => {
4971
- const indexPrices = useIndexPriceStore((state) => state.indexPrices);
4972
- const getIndexPrice = (token) => {
4973
- if (token === "USDC") {
4974
- return 1;
4975
- }
4976
- return indexPrices[`PERP_${token}_USDC`] ?? 0;
4977
- };
4978
- return {
4979
- data: indexPrices,
4980
- getIndexPrice: useMemoizedFn(getIndexPrice)
4981
- };
4982
4990
  };
4983
- var generateLeverageLevers = (max2) => {
4984
- const min2 = 1;
4985
- const parts = 5;
4986
- const step = (max2 - min2) / (parts - 1);
4987
- const result = [];
4988
- for (let i = 0; i < parts; i++) {
4989
- result.push(Math.floor(min2 + step * i));
4990
- }
4991
- return result;
4991
+ PositionCalculator.logPosition = (symbol = "all") => {
4992
+ return usePositionStore.getState().positions[symbol];
4992
4993
  };
4993
- var useLeverage = () => {
4994
- const { data, mutate: mutate6 } = usePrivateQuery(
4995
- "/v1/client/info",
4996
- {
4997
- revalidateOnFocus: false
4994
+ var parseHolding = (holding, indexPrices, tokensInfo) => {
4995
+ const nonUSDC = [];
4996
+ let USDC_holding = 0;
4997
+ holding.forEach((item) => {
4998
+ if (item.token === "USDC") {
4999
+ USDC_holding = item.holding;
5000
+ } else {
5001
+ const tokenInfo = tokensInfo.find(({ token }) => token === item.token);
5002
+ const {
5003
+ base_weight = 0,
5004
+ discount_factor = 0,
5005
+ user_max_qty = 0
5006
+ } = tokenInfo || {};
5007
+ const holdingQty = item?.holding ?? 0;
5008
+ const indexPrice = indexPrices[`PERP_${item.token}_USDC`] ?? 0;
5009
+ const collateralRatio4 = perp.account.collateralRatio({
5010
+ baseWeight: base_weight,
5011
+ discountFactor: discount_factor,
5012
+ collateralQty: holdingQty,
5013
+ collateralCap: user_max_qty,
5014
+ indexPrice
5015
+ });
5016
+ nonUSDC.push({
5017
+ holding: holdingQty,
5018
+ indexPrice,
5019
+ collateralCap: user_max_qty,
5020
+ collateralRatio: collateralRatio4
5021
+ });
4998
5022
  }
4999
- );
5000
- const [update, { isMutating }] = useMutation("/v1/client/leverage");
5001
- const { data: leverageConfig, isLoading } = useQuery("/v1/public/leverage", {
5002
- revalidateOnFocus: false,
5003
- errorRetryCount: 3
5004
- // formatter: (data) => data,
5005
5023
  });
5006
- const updateLeverage = React2.useCallback(
5007
- async (data2) => {
5008
- const res = await update(data2);
5009
- if (res.success) {
5010
- return mutate6();
5011
- } else {
5012
- throw new Error(res.message);
5013
- }
5014
- },
5015
- [update, mutate6]
5016
- );
5017
- const memoizedCurLeverage = React2.useMemo(() => {
5018
- if (data?.max_leverage !== void 0) {
5019
- return Number(data.max_leverage);
5020
- }
5021
- return 1;
5022
- }, [data?.max_leverage]);
5023
- const memoizedMaxLeverage = React2.useMemo(() => {
5024
- if (leverageConfig?.max_futures_leverage !== void 0) {
5025
- return Number(leverageConfig.max_futures_leverage);
5026
- }
5027
- return 1;
5028
- }, [leverageConfig?.max_futures_leverage]);
5029
- const memoizedLeverageLevers = React2.useMemo(() => {
5030
- return generateLeverageLevers(memoizedMaxLeverage);
5031
- }, [memoizedMaxLeverage]);
5032
- return {
5033
- update: updateLeverage,
5034
- isLoading: isLoading || isMutating,
5035
- leverageLevers: memoizedLeverageLevers,
5036
- curLeverage: memoizedCurLeverage,
5037
- maxLeverage: memoizedMaxLeverage
5038
- };
5024
+ return [USDC_holding, nonUSDC];
5039
5025
  };
5040
5026
 
5041
- // src/orderly/useOdosQuote.ts
5042
- var useOdosQuote = () => {
5043
- return useMutation(`https://api.odos.xyz/sor/quote/v2`);
5044
- };
5045
- var { LTV, collateralRatio } = perp.account;
5046
- var useComputedLTV = (options = {}) => {
5047
- const { input, token } = options;
5048
- const isUSDC = token?.toUpperCase() === "USDC";
5049
- const tokensInfo = useTokensInfo();
5050
- const { usdc, data: holdingList = [] } = useHoldingStream();
5051
- const { getIndexPrice } = useIndexPricesStream();
5052
- const { unsettledPnL } = useCollateral();
5053
- const usdcBalance = React2.useMemo(() => {
5054
- if (isUSDC && input) {
5055
- return new utils.Decimal(usdc?.holding ?? 0).add(input).toNumber();
5056
- }
5057
- return usdc?.holding ?? 0;
5058
- }, [usdc?.holding, input, isUSDC]);
5059
- const getAdjustedQty = React2.useCallback(
5060
- (item) => {
5061
- if (input && item.token === token) {
5062
- return new utils.Decimal(item?.holding ?? 0).add(input).toNumber();
5063
- }
5064
- return item?.holding ?? 0;
5065
- },
5066
- [input, token]
5067
- );
5068
- const memoizedLTV = React2.useMemo(() => {
5069
- return LTV({
5070
- usdcBalance,
5071
- upnl: unsettledPnL,
5072
- assets: holdingList.filter((h) => h.token.toUpperCase() !== "USDC").map((item) => {
5073
- const indexPrice = getIndexPrice(item.token);
5074
- const findToken = tokensInfo?.find((i) => i.token === item.token);
5075
- const qty = getAdjustedQty(item);
5076
- const weight = collateralRatio({
5077
- baseWeight: findToken?.base_weight ?? 0,
5078
- discountFactor: findToken?.discount_factor ?? 0,
5079
- collateralCap: findToken?.user_max_qty ?? qty,
5080
- collateralQty: qty,
5081
- indexPrice
5082
- });
5083
- return {
5084
- qty,
5085
- indexPrice,
5086
- weight: weight.toNumber()
5087
- };
5088
- })
5089
- });
5090
- }, [
5091
- usdcBalance,
5092
- unsettledPnL,
5093
- holdingList,
5094
- tokensInfo,
5095
- getIndexPrice,
5096
- getAdjustedQty
5097
- ]);
5098
- if (new utils.Decimal(usdcBalance).add(new utils.Decimal(unsettledPnL)).gte(utils.zero)) {
5099
- return 0;
5100
- }
5101
- return new utils.Decimal(memoizedLTV).mul(100).toDecimalPlaces(2, utils.Decimal.ROUND_DOWN).toNumber();
5102
- };
5103
- var useFundingRate = (symbol) => {
5104
- if (!symbol) {
5105
- throw new types.SDKError("Symbol is required");
5027
+ // src/orderly/calculator/indexPrice.ts
5028
+ var IndexPriceCalculatorName = "indexPriceCalculator";
5029
+ var IndexPriceCalculator = class extends BaseCalculator {
5030
+ constructor() {
5031
+ super(...arguments);
5032
+ this.name = IndexPriceCalculatorName;
5106
5033
  }
5107
- const [countDown, setCountDown] = React2.useState("00:00:00");
5108
- const timerRef = React2.useRef(null);
5109
- const { data, isLoading } = useQuery(
5110
- `/v1/public/funding_rate/${symbol}`,
5111
- {
5112
- fallbackData: {
5113
- est_funding_rate: 0,
5114
- next_funing_time: 0
5115
- }
5116
- }
5117
- );
5118
- React2.useEffect(() => {
5119
- if (!data || isLoading) {
5034
+ calc(scope, data, ctx) {
5035
+ return data;
5036
+ }
5037
+ update(data) {
5038
+ if (!data)
5120
5039
  return;
5040
+ useIndexPriceStore.getState().actions.updateIndexPrice(data);
5041
+ }
5042
+ static getValue() {
5043
+ return useIndexPriceStore.getState().indexPrices;
5044
+ }
5045
+ };
5046
+
5047
+ // src/orderly/calculator/portfolio.ts
5048
+ var PortfolioCalculatorName = "portfolio";
5049
+ var PortfolioCalculator = class extends BaseCalculator {
5050
+ constructor() {
5051
+ super(...arguments);
5052
+ this.name = PortfolioCalculatorName;
5053
+ }
5054
+ calc(scope, data, ctx) {
5055
+ let markPrices;
5056
+ let indexPrices;
5057
+ const portfolio = this.getPortfolio(ctx);
5058
+ if (scope === "markPrice" /* MARK_PRICE */) {
5059
+ markPrices = data;
5060
+ } else {
5061
+ markPrices = ctx.get(
5062
+ (cache) => cache[MarketCalculatorName]
5063
+ );
5121
5064
  }
5122
- const { next_funding_time } = data;
5123
- if (!next_funding_time || next_funding_time <= 0) {
5124
- return;
5065
+ if (scope === "indexPrice" /* INDEX_PRICE */) {
5066
+ indexPrices = data;
5067
+ } else {
5068
+ indexPrices = ctx.get(
5069
+ (cache) => cache[IndexPriceCalculatorName]
5070
+ );
5125
5071
  }
5126
- timerRef.current = setInterval(() => {
5127
- const diff = new Date(next_funding_time).getTime() - utils.getTimestamp();
5128
- if (diff <= 0) {
5129
- setCountDown("00:00:00");
5130
- if (timerRef.current) {
5131
- clearInterval(timerRef.current);
5132
- }
5133
- return;
5134
- }
5135
- const result = utils.timeConvertString(diff);
5136
- if (result.length === 3) {
5137
- setCountDown(
5138
- `${result[0].toString().padStart(2, "0")}:${result[1].toString().padStart(2, "0")}:${result[2].toString().padStart(2, "0")}`
5139
- );
5140
- }
5141
- }, 1e3);
5142
- return () => {
5143
- if (timerRef.current) {
5144
- clearInterval(timerRef.current);
5072
+ const positions3 = ctx.get(
5073
+ (output) => output.positionCalculator_all
5074
+ );
5075
+ let holding = portfolio.holding;
5076
+ if (scope === "portfolio" /* PORTFOLIO */ && data.holding && Array.isArray(holding)) {
5077
+ if (Array.isArray(data.holding)) {
5078
+ holding = data.holding;
5079
+ } else {
5080
+ holding = holding.map((item) => {
5081
+ if (data.holding[item.token]) {
5082
+ return {
5083
+ ...item,
5084
+ holding: data.holding[item.token].holding,
5085
+ frozen: data.holding[item.token].frozen
5086
+ };
5087
+ }
5088
+ return item;
5089
+ });
5145
5090
  }
5146
- };
5147
- }, [data, isLoading]);
5148
- const est_funding_rate = React2.useMemo(() => {
5149
- if (!data) {
5150
- return;
5151
5091
  }
5152
- const { next_funding_time, est_funding_rate: est_funding_rate2 = 0 } = data;
5153
- if (utils.getTimestamp() > next_funding_time) {
5092
+ const accountInfo = ctx.accountInfo;
5093
+ const symbolsInfo = ctx.symbolsInfo;
5094
+ const tokensInfo = ctx.tokensInfo;
5095
+ return this.format({
5096
+ holding,
5097
+ positions: positions3,
5098
+ markPrices,
5099
+ accountInfo,
5100
+ symbolsInfo,
5101
+ indexPrices,
5102
+ tokensInfo: tokensInfo ?? types.EMPTY_LIST
5103
+ });
5104
+ }
5105
+ getPortfolio(ctx) {
5106
+ return ctx.get((output) => output[this.name]) || useAppStore.getState().portfolio;
5107
+ }
5108
+ format(inputs) {
5109
+ const {
5110
+ holding,
5111
+ positions: positions3,
5112
+ markPrices,
5113
+ indexPrices,
5114
+ accountInfo,
5115
+ symbolsInfo,
5116
+ tokensInfo
5117
+ } = inputs;
5118
+ if (!holding || !positions3 || !Array.isArray(positions3.rows) || !markPrices || !indexPrices || !accountInfo) {
5154
5119
  return null;
5155
5120
  }
5156
- return new utils.Decimal(Number(est_funding_rate2) * 100).toFixed(
5157
- 4,
5158
- utils.Decimal.ROUND_DOWN
5121
+ const unsettledPnL = ramda.pathOr(0, ["total_unsettled_pnl"])(positions3);
5122
+ const unrealizedPnL = ramda.pathOr(0, ["total_unreal_pnl"])(positions3);
5123
+ const [USDC_holding, nonUSDC] = parseHolding(
5124
+ holding,
5125
+ indexPrices,
5126
+ tokensInfo
5159
5127
  );
5160
- }, [data]);
5161
- return {
5162
- ...data,
5163
- est_funding_rate,
5164
- countDown
5165
- };
5166
- };
5167
- var useFundingDetails = (symbol) => {
5168
- if (!symbol) {
5169
- throw new types.SDKError("symbol is required");
5128
+ const usdc = holding.find((item) => item.token === "USDC");
5129
+ const totalCollateral = perp.account.totalCollateral({
5130
+ USDCHolding: USDC_holding,
5131
+ nonUSDCHolding: nonUSDC,
5132
+ unsettlementPnL: unsettledPnL
5133
+ });
5134
+ const totalValue = perp.account.totalValue({
5135
+ totalUnsettlementPnL: unsettledPnL,
5136
+ USDCHolding: USDC_holding,
5137
+ nonUSDCHolding: nonUSDC
5138
+ });
5139
+ const totalUnrealizedROI = perp.account.totalUnrealizedROI({
5140
+ totalUnrealizedPnL: unrealizedPnL,
5141
+ totalValue: totalValue.toNumber()
5142
+ });
5143
+ const totalInitialMarginWithOrders = perp.account.totalInitialMarginWithQty({
5144
+ positions: positions3.rows,
5145
+ markPrices,
5146
+ IMR_Factors: accountInfo.imr_factor,
5147
+ maxLeverage: accountInfo.max_leverage,
5148
+ symbolInfo: createGetter({ ...symbolsInfo })
5149
+ });
5150
+ const freeCollateral = perp.account.freeCollateral({
5151
+ totalCollateral,
5152
+ totalInitialMarginWithOrders
5153
+ });
5154
+ const availableBalance = perp.account.availableBalance({
5155
+ USDCHolding: usdc?.holding ?? 0,
5156
+ unsettlementPnL: positions3.total_unsettled_pnl ?? 0
5157
+ });
5158
+ return {
5159
+ totalCollateral,
5160
+ totalValue,
5161
+ totalUnrealizedROI,
5162
+ freeCollateral,
5163
+ availableBalance,
5164
+ unsettledPnL,
5165
+ holding
5166
+ };
5170
5167
  }
5171
- return useQuery(`/v1/public/info/${symbol}`, {
5172
- errorRetryCount: 3,
5173
- revalidateOnFocus: false
5174
- });
5175
- };
5176
- var calculatePositiveRate = (periodData, period) => {
5177
- if (!periodData || !period) {
5178
- return 0;
5168
+ update(data, scope) {
5169
+ if (data) {
5170
+ useAppStore.getState().actions.batchUpdateForPortfolio({
5171
+ totalCollateral: data.totalCollateral,
5172
+ totalValue: data.totalValue,
5173
+ freeCollateral: data.freeCollateral,
5174
+ availableBalance: data.availableBalance,
5175
+ totalUnrealizedROI: data.totalUnrealizedROI,
5176
+ unsettledPnL: data.unsettledPnL,
5177
+ holding: Array.isArray(data.holding) ? data.holding : []
5178
+ });
5179
+ }
5179
5180
  }
5180
- const daysMap = {
5181
- "1d": 1,
5182
- "3d": 3,
5183
- "7d": 7,
5184
- "14d": 14,
5185
- "30d": 30,
5186
- "90d": 90
5187
- };
5188
- const totalTimes = daysMap[period] * 3;
5189
- return periodData.positive / totalTimes;
5190
5181
  };
5191
- var useFundingRateHistory = () => {
5192
- const { data: historyData, isLoading } = useQuery(
5193
- "/v1/public/market_info/funding_history"
5194
- );
5195
- const getPositiveRates = React2.useCallback(
5196
- (data, period) => {
5197
- if (!data?.length) {
5198
- return {};
5199
- }
5200
- return data.reduce(
5201
- (acc, item) => {
5202
- acc[item.symbol] = calculatePositiveRate(
5203
- item.funding[period],
5204
- period
5205
- );
5206
- return acc;
5207
- },
5208
- {}
5209
- );
5210
- },
5211
- []
5212
- );
5213
- return React2.useMemo(() => {
5214
- return {
5215
- data: historyData ?? types.EMPTY_LIST,
5216
- isLoading,
5217
- getPositiveRates
5218
- };
5219
- }, [historyData, isLoading, getPositiveRates]);
5182
+
5183
+ // src/useCalculatorService.ts
5184
+ var useCalculatorService = () => {
5185
+ const { get: get3 } = useSimpleDI();
5186
+ const calculatorService = useConstant__default.default(() => {
5187
+ let calculatorService2 = get3(CalculatorServiceID);
5188
+ if (!calculatorService2) {
5189
+ const positionCalculator = new PositionCalculator();
5190
+ const portfolioCalculator = new PortfolioCalculator();
5191
+ const markPriceCalculator = new MarkPriceCalculator();
5192
+ const indexPriceCalculator = new IndexPriceCalculator();
5193
+ calculatorService2 = new CalculatorService(new ShardingScheduler(), [
5194
+ [
5195
+ "markPrice" /* MARK_PRICE */,
5196
+ [
5197
+ markPriceCalculator,
5198
+ positionCalculator,
5199
+ portfolioCalculator,
5200
+ positionCalculator
5201
+ ]
5202
+ ],
5203
+ ["position" /* POSITION */, [positionCalculator, portfolioCalculator]],
5204
+ ["portfolio" /* PORTFOLIO */, [portfolioCalculator]],
5205
+ // indexPrice
5206
+ [
5207
+ "indexPrice" /* INDEX_PRICE */,
5208
+ [indexPriceCalculator, positionCalculator]
5209
+ ]
5210
+ ]);
5211
+ core.SimpleDI.registerByName(CalculatorServiceID, calculatorService2);
5212
+ }
5213
+ return calculatorService2;
5214
+ });
5215
+ return calculatorService;
5220
5216
  };
5217
+
5218
+ // src/orderly/usePositionStream/usePositionStream.ts
5221
5219
  var scopes = [
5222
5220
  "position" /* POSITION */,
5223
5221
  "markPrice" /* MARK_PRICE */,
@@ -7143,6 +7141,252 @@ var useSettleSubscription = (options) => {
7143
7141
  }
7144
7142
  );
7145
7143
  };
7144
+ var usePrivateDataObserver = (options) => {
7145
+ const ws = useWS();
7146
+ const ee = useEventEmitter();
7147
+ const { state, account: account9 } = useAccount();
7148
+ const { setAccountInfo, restoreHolding, cleanAll } = useAppStore(
7149
+ (state2) => state2.actions
7150
+ );
7151
+ const statusActions = useApiStatusActions();
7152
+ const calculatorService = useCalculatorService();
7153
+ const positionsActions = usePositionActions();
7154
+ const { data: clientInfo } = usePrivateQuery(
7155
+ "/v1/client/info",
7156
+ {
7157
+ revalidateOnFocus: false
7158
+ }
7159
+ );
7160
+ React2.useEffect(() => {
7161
+ if (clientInfo) {
7162
+ setAccountInfo(clientInfo);
7163
+ }
7164
+ }, [clientInfo, setAccountInfo]);
7165
+ const { data: positions3, isLoading: isPositionLoading } = usePrivateQuery("/v1/positions", {
7166
+ formatter: (data) => data,
7167
+ onError: (error) => {
7168
+ statusActions.updateApiError("positions", error.message);
7169
+ }
7170
+ // revalidateOnFocus: false,
7171
+ });
7172
+ React2.useEffect(() => {
7173
+ const handler = (state2) => {
7174
+ if (!state2.accountId) {
7175
+ calculatorService.stop();
7176
+ cleanAll();
7177
+ positionsActions.clearAll();
7178
+ }
7179
+ };
7180
+ account9.on(core.EVENT_NAMES.statusChanged, handler);
7181
+ return () => {
7182
+ account9.off(core.EVENT_NAMES.statusChanged, handler);
7183
+ };
7184
+ }, []);
7185
+ React2.useEffect(() => {
7186
+ if (isPositionLoading) {
7187
+ statusActions.updateApiLoading("positions", isPositionLoading);
7188
+ }
7189
+ }, [isPositionLoading, statusActions]);
7190
+ React2.useEffect(() => {
7191
+ if (positions3 && Array.isArray(positions3.rows)) {
7192
+ calculatorService.calc("position" /* POSITION */, positions3);
7193
+ }
7194
+ }, [calculatorService, positions3]);
7195
+ const { data: holding } = usePrivateQuery(
7196
+ "/v1/client/holding",
7197
+ {
7198
+ formatter: (data) => data.holding
7199
+ // revalidateOnFocus: false,
7200
+ }
7201
+ );
7202
+ React2.useEffect(() => {
7203
+ if (!account9.accountId)
7204
+ return;
7205
+ const unsubscribe = ws.privateSubscribe(
7206
+ {
7207
+ id: "balance",
7208
+ event: "subscribe",
7209
+ topic: "balance",
7210
+ ts: Date.now()
7211
+ },
7212
+ {
7213
+ onMessage: (data) => {
7214
+ const holding2 = data?.balances ?? {};
7215
+ if (holding2) {
7216
+ calculatorService.calc("portfolio" /* PORTFOLIO */, { holding: holding2 });
7217
+ }
7218
+ }
7219
+ }
7220
+ );
7221
+ return () => unsubscribe?.();
7222
+ }, [account9.accountId]);
7223
+ const isHoldingInit = React2.useRef(false);
7224
+ React2.useEffect(() => {
7225
+ isHoldingInit.current = false;
7226
+ }, [state.address]);
7227
+ React2.useEffect(() => {
7228
+ if (!holding) {
7229
+ return;
7230
+ }
7231
+ if (isHoldingInit.current) {
7232
+ calculatorService.calc("portfolio" /* PORTFOLIO */, { holding });
7233
+ } else {
7234
+ restoreHolding(holding);
7235
+ }
7236
+ isHoldingInit.current = true;
7237
+ }, [holding]);
7238
+ const [subOrder] = useLocalStorage("orderly_subscribe_order", true);
7239
+ const updateOrders = (data, isAlgoOrder) => {
7240
+ const keysMap = options.getKeysMap("orders");
7241
+ const filteredKeys = /* @__PURE__ */ new Map();
7242
+ const keyStartWith = isAlgoOrder ? "algoOrders" : "orders";
7243
+ const keys = keysMap.keys();
7244
+ for (const key of keys) {
7245
+ if (key.startsWith(keyStartWith)) {
7246
+ filteredKeys.set(key, keysMap.get(key));
7247
+ }
7248
+ }
7249
+ let fieldChanges = {};
7250
+ filteredKeys.forEach((getKey, key) => {
7251
+ useSWR.mutate(
7252
+ useSWRInfinite.unstable_serialize((index, prevData) => [
7253
+ getKey(index, prevData),
7254
+ state.accountId
7255
+ ]),
7256
+ (prevData) => {
7257
+ try {
7258
+ if (isAlgoOrder) {
7259
+ const res = updateAlgoOrdersHandler(
7260
+ key,
7261
+ data,
7262
+ prevData
7263
+ );
7264
+ fieldChanges = res?.fieldChanges || {};
7265
+ return res?.mergedOrders;
7266
+ }
7267
+ return updateOrdersHandler(key, data, prevData);
7268
+ } catch (error) {
7269
+ return prevData;
7270
+ }
7271
+ },
7272
+ {
7273
+ revalidate: false
7274
+ }
7275
+ );
7276
+ });
7277
+ const formattedData = isAlgoOrder ? AlgoOrderMergeHandler.groupOrders(data) : object2underscore(data);
7278
+ ee.emit("orders:changed", {
7279
+ ...formattedData,
7280
+ status: isAlgoOrder ? formattedData.algo_status : data.status,
7281
+ // custom field name
7282
+ fieldChanges
7283
+ });
7284
+ };
7285
+ React2.useEffect(() => {
7286
+ if (!state.accountId) {
7287
+ return;
7288
+ }
7289
+ if (subOrder !== true) {
7290
+ return;
7291
+ }
7292
+ const unsubscribe = ws.privateSubscribe("executionreport", {
7293
+ onMessage: (data) => {
7294
+ updateOrders(data, false);
7295
+ }
7296
+ });
7297
+ return () => unsubscribe?.();
7298
+ }, [state.accountId, subOrder]);
7299
+ React2.useEffect(() => {
7300
+ if (!state.accountId)
7301
+ return;
7302
+ if (subOrder !== true)
7303
+ return;
7304
+ const unsubscribe = ws.privateSubscribe("algoexecutionreport", {
7305
+ onMessage: (data) => {
7306
+ updateOrders(data, true);
7307
+ }
7308
+ });
7309
+ return () => unsubscribe?.();
7310
+ }, [state.accountId, subOrder]);
7311
+ React2.useEffect(() => {
7312
+ if (!state.accountId)
7313
+ return;
7314
+ const key = ["/v1/positions", state.accountId];
7315
+ const unsubscribe = ws.privateSubscribe("account", {
7316
+ onMessage: (data) => {
7317
+ const { symbol, leverage } = data?.accountDetail?.symbolLeverage || {};
7318
+ if (symbol && leverage) {
7319
+ useSWR.mutate(
7320
+ key,
7321
+ (prevPositions) => {
7322
+ if (prevPositions?.rows?.length) {
7323
+ return {
7324
+ ...prevPositions,
7325
+ rows: prevPositions.rows.map((row) => {
7326
+ return row.symbol === symbol ? { ...row, leverage } : row;
7327
+ })
7328
+ };
7329
+ }
7330
+ return prevPositions;
7331
+ },
7332
+ {
7333
+ revalidate: false
7334
+ }
7335
+ );
7336
+ }
7337
+ }
7338
+ });
7339
+ return () => unsubscribe?.();
7340
+ }, [state.accountId]);
7341
+ React2.useEffect(() => {
7342
+ if (!state.accountId) {
7343
+ return;
7344
+ }
7345
+ const key = ["/v1/positions", state.accountId];
7346
+ const unsubscribe = ws.privateSubscribe("position", {
7347
+ onMessage: (data) => {
7348
+ const { positions: nextPositions } = data;
7349
+ useSWR.mutate(
7350
+ key,
7351
+ (prevPositions) => {
7352
+ if (!!prevPositions) {
7353
+ const newPositions = {
7354
+ ...prevPositions,
7355
+ rows: prevPositions.rows.map((row) => {
7356
+ const itemIndex = nextPositions.findIndex(
7357
+ (item) => item.symbol === row.symbol
7358
+ );
7359
+ if (itemIndex >= 0) {
7360
+ const itemArr = nextPositions.splice(itemIndex, 1);
7361
+ const item = itemArr[0];
7362
+ if (item.averageOpenPrice === 0 && item.positionQty !== 0) {
7363
+ return row;
7364
+ }
7365
+ return object2underscore(item);
7366
+ }
7367
+ return row;
7368
+ })
7369
+ };
7370
+ if (nextPositions.length > 0) {
7371
+ newPositions.rows = [
7372
+ ...newPositions.rows,
7373
+ ...nextPositions.map(object2underscore)
7374
+ ];
7375
+ }
7376
+ return newPositions;
7377
+ }
7378
+ },
7379
+ {
7380
+ revalidate: false
7381
+ }
7382
+ );
7383
+ }
7384
+ });
7385
+ return () => {
7386
+ unsubscribe?.();
7387
+ };
7388
+ }, [state.accountId]);
7389
+ };
7146
7390
  var useSymbolPriceRange = (symbol, side, price) => {
7147
7391
  const config = useSymbolsInfo();
7148
7392
  const priceRange = config?.[symbol]("price_range");
@@ -7595,12 +7839,12 @@ async function bracketOrderValidator(values, config) {
7595
7839
  const result = /* @__PURE__ */ Object.create(null);
7596
7840
  await Promise.resolve();
7597
7841
  const {
7598
- tp_enable,
7842
+ // tp_enable,
7843
+ // sl_enable,
7599
7844
  tp_trigger_price,
7600
7845
  tp_order_price,
7601
7846
  tp_order_type,
7602
7847
  sl_trigger_price,
7603
- sl_enable,
7604
7848
  sl_order_price,
7605
7849
  sl_order_type,
7606
7850
  side
@@ -7620,12 +7864,6 @@ async function bracketOrderValidator(values, config) {
7620
7864
  if (Number(sl_trigger_price) < 0) {
7621
7865
  result.sl_trigger_price = OrderValidation.min("sl_trigger_price", 0);
7622
7866
  }
7623
- if (tp_enable && !tp_trigger_price) {
7624
- result.tp_trigger_price = OrderValidation.required("tp_trigger_price");
7625
- }
7626
- if (sl_enable && !sl_trigger_price) {
7627
- result.sl_trigger_price = OrderValidation.required("sl_trigger_price");
7628
- }
7629
7867
  if (tp_order_type === types.OrderType.LIMIT && !tp_order_price) {
7630
7868
  result.tp_order_price = OrderValidation.required("tp_order_price");
7631
7869
  }
@@ -8445,8 +8683,8 @@ var BaseAlgoOrderCreator = class {
8445
8683
  tp_trigger_price,
8446
8684
  sl_trigger_price,
8447
8685
  side,
8448
- tp_enable,
8449
- sl_enable,
8686
+ // tp_enable,
8687
+ // sl_enable,
8450
8688
  tp_order_type,
8451
8689
  sl_order_type,
8452
8690
  tp_order_price,
@@ -8475,12 +8713,6 @@ var BaseAlgoOrderCreator = class {
8475
8713
  if (Number(sl_trigger_price) < 0) {
8476
8714
  result.sl_trigger_price = OrderValidation.min("sl_trigger_price", 0);
8477
8715
  }
8478
- if (tp_enable && !tp_trigger_price) {
8479
- result.tp_trigger_price = OrderValidation.required("tp_trigger_price");
8480
- }
8481
- if (sl_enable && !sl_trigger_price) {
8482
- result.sl_trigger_price = OrderValidation.required("sl_trigger_price");
8483
- }
8484
8716
  if (tp_order_type === types.OrderType.LIMIT && !tp_order_price) {
8485
8717
  result.tp_order_price = OrderValidation.required("tp_order_price");
8486
8718
  }
@@ -8991,9 +9223,6 @@ var tpslFields = [
8991
9223
  "sl_offset_percentage"
8992
9224
  ];
8993
9225
  var isBracketOrder = (order) => {
8994
- if (order.sl_enable || order.tp_enable) {
8995
- return true;
8996
- }
8997
9226
  return !!order.tp_trigger_price || !!order.sl_trigger_price;
8998
9227
  };
8999
9228
  var hasTPSL = (order) => {
@@ -9523,28 +9752,6 @@ function useSubAccountMaxWithdrawal(options) {
9523
9752
  }
9524
9753
 
9525
9754
  // src/orderly/useTakeProfitAndStopLoss/useTPSL.ts
9526
- var checkIsEnableTpSL = (order) => {
9527
- const result = {
9528
- tp_enable: true,
9529
- sl_enable: true
9530
- };
9531
- if (!order) {
9532
- return result;
9533
- }
9534
- const tp = order.child_orders.find(
9535
- (o) => o.algo_type === types.AlgoOrderType.TAKE_PROFIT && o.is_activated
9536
- );
9537
- const sl = order.child_orders.find(
9538
- (o) => o.algo_type === types.AlgoOrderType.STOP_LOSS && o.is_activated
9539
- );
9540
- if (!tp) {
9541
- result.tp_enable = false;
9542
- }
9543
- if (!sl) {
9544
- result.sl_enable = false;
9545
- }
9546
- return result;
9547
- };
9548
9755
  var useTaskProfitAndStopLossInternal = (position, options) => {
9549
9756
  const isEditing = typeof options?.isEditing !== "undefined" ? options.isEditing : !!options?.defaultOrder;
9550
9757
  const [order, setOrder] = React2.useState({
@@ -9559,8 +9766,12 @@ var useTaskProfitAndStopLossInternal = (position, options) => {
9559
9766
  // quantity:
9560
9767
  // options?.defaultOrder?.quantity || Math.abs(position.position_qty),
9561
9768
  algo_type: options?.defaultOrder?.algo_type,
9562
- tp_enable: isEditing ? checkIsEnableTpSL(options?.defaultOrder).tp_enable : options?.tpslEnable?.tp_enable,
9563
- sl_enable: isEditing ? checkIsEnableTpSL(options?.defaultOrder).sl_enable : options?.tpslEnable?.sl_enable,
9769
+ // tp_enable: isEditing
9770
+ // ? checkIsEnableTpSL(options?.defaultOrder).tp_enable
9771
+ // : options?.tpslEnable?.tp_enable,
9772
+ // sl_enable: isEditing
9773
+ // ? checkIsEnableTpSL(options?.defaultOrder).sl_enable
9774
+ // : options?.tpslEnable?.sl_enable,
9564
9775
  position_type: options?.positionType
9565
9776
  });
9566
9777
  const symbolInfo = useSymbolsInfo()[position.symbol]();
@@ -10212,254 +10423,6 @@ var useStorageLedgerAddress = () => {
10212
10423
  ledgerWallet
10213
10424
  };
10214
10425
  };
10215
-
10216
- // src/orderly/usePrivateDataObserver.ts
10217
- var usePrivateDataObserver = (options) => {
10218
- const ws = useWS();
10219
- const ee = useEventEmitter();
10220
- const { state, account: account9 } = useAccount();
10221
- const { setAccountInfo, restoreHolding, cleanAll } = useAppStore(
10222
- (state2) => state2.actions
10223
- );
10224
- const statusActions = useApiStatusActions();
10225
- const calculatorService = useCalculatorService();
10226
- const positionsActions = usePositionActions();
10227
- const { data: clientInfo } = usePrivateQuery(
10228
- "/v1/client/info",
10229
- {
10230
- revalidateOnFocus: false
10231
- }
10232
- );
10233
- React2.useEffect(() => {
10234
- if (clientInfo) {
10235
- setAccountInfo(clientInfo);
10236
- }
10237
- }, [clientInfo, setAccountInfo]);
10238
- const { data: positions3, isLoading: isPositionLoading } = usePrivateQuery("/v1/positions", {
10239
- formatter: (data) => data,
10240
- onError: (error) => {
10241
- statusActions.updateApiError("positions", error.message);
10242
- }
10243
- // revalidateOnFocus: false,
10244
- });
10245
- React2.useEffect(() => {
10246
- const handler = (state2) => {
10247
- if (!state2.accountId) {
10248
- calculatorService.stop();
10249
- cleanAll();
10250
- positionsActions.clearAll();
10251
- }
10252
- };
10253
- account9.on(core.EVENT_NAMES.statusChanged, handler);
10254
- return () => {
10255
- account9.off(core.EVENT_NAMES.statusChanged, handler);
10256
- };
10257
- }, []);
10258
- React2.useEffect(() => {
10259
- if (isPositionLoading) {
10260
- statusActions.updateApiLoading("positions", isPositionLoading);
10261
- }
10262
- }, [isPositionLoading, statusActions]);
10263
- React2.useEffect(() => {
10264
- if (positions3 && Array.isArray(positions3.rows)) {
10265
- calculatorService.calc("position" /* POSITION */, positions3);
10266
- }
10267
- }, [calculatorService, positions3]);
10268
- const { data: holding } = usePrivateQuery(
10269
- "/v1/client/holding",
10270
- {
10271
- formatter: (data) => data.holding
10272
- // revalidateOnFocus: false,
10273
- }
10274
- );
10275
- React2.useEffect(() => {
10276
- if (!account9.accountId)
10277
- return;
10278
- const unsubscribe = ws.privateSubscribe(
10279
- {
10280
- id: "balance",
10281
- event: "subscribe",
10282
- topic: "balance",
10283
- ts: Date.now()
10284
- },
10285
- {
10286
- onMessage: (data) => {
10287
- const holding2 = data?.balances ?? {};
10288
- if (holding2) {
10289
- calculatorService.calc("portfolio" /* PORTFOLIO */, { holding: holding2 });
10290
- }
10291
- }
10292
- }
10293
- );
10294
- return () => unsubscribe?.();
10295
- }, [account9.accountId]);
10296
- const isHoldingInit = React2.useRef(false);
10297
- React2.useEffect(() => {
10298
- isHoldingInit.current = false;
10299
- }, [state.address]);
10300
- React2.useEffect(() => {
10301
- if (!holding) {
10302
- return;
10303
- }
10304
- if (isHoldingInit.current) {
10305
- calculatorService.calc("portfolio" /* PORTFOLIO */, { holding });
10306
- } else {
10307
- restoreHolding(holding);
10308
- }
10309
- isHoldingInit.current = true;
10310
- }, [holding]);
10311
- const [subOrder] = useLocalStorage("orderly_subscribe_order", true);
10312
- const updateOrders = (data, isAlgoOrder) => {
10313
- const keysMap = options.getKeysMap("orders");
10314
- const filteredKeys = /* @__PURE__ */ new Map();
10315
- const keyStartWith = isAlgoOrder ? "algoOrders" : "orders";
10316
- const keys = keysMap.keys();
10317
- for (const key of keys) {
10318
- if (key.startsWith(keyStartWith)) {
10319
- filteredKeys.set(key, keysMap.get(key));
10320
- }
10321
- }
10322
- let fieldChanges = {};
10323
- filteredKeys.forEach((getKey, key) => {
10324
- useSWR.mutate(
10325
- useSWRInfinite.unstable_serialize((index, prevData) => [
10326
- getKey(index, prevData),
10327
- state.accountId
10328
- ]),
10329
- (prevData) => {
10330
- try {
10331
- if (isAlgoOrder) {
10332
- const res = updateAlgoOrdersHandler(
10333
- key,
10334
- data,
10335
- prevData
10336
- );
10337
- fieldChanges = res?.fieldChanges || {};
10338
- return res?.mergedOrders;
10339
- }
10340
- return updateOrdersHandler(key, data, prevData);
10341
- } catch (error) {
10342
- return prevData;
10343
- }
10344
- },
10345
- {
10346
- revalidate: false
10347
- }
10348
- );
10349
- });
10350
- const formattedData = isAlgoOrder ? AlgoOrderMergeHandler.groupOrders(data) : object2underscore(data);
10351
- ee.emit("orders:changed", {
10352
- ...formattedData,
10353
- status: isAlgoOrder ? formattedData.algo_status : data.status,
10354
- // custom field name
10355
- fieldChanges
10356
- });
10357
- };
10358
- React2.useEffect(() => {
10359
- if (!state.accountId) {
10360
- return;
10361
- }
10362
- if (subOrder !== true) {
10363
- return;
10364
- }
10365
- const unsubscribe = ws.privateSubscribe("executionreport", {
10366
- onMessage: (data) => {
10367
- updateOrders(data, false);
10368
- }
10369
- });
10370
- return () => unsubscribe?.();
10371
- }, [state.accountId, subOrder]);
10372
- React2.useEffect(() => {
10373
- if (!state.accountId)
10374
- return;
10375
- if (subOrder !== true)
10376
- return;
10377
- const unsubscribe = ws.privateSubscribe("algoexecutionreport", {
10378
- onMessage: (data) => {
10379
- updateOrders(data, true);
10380
- }
10381
- });
10382
- return () => unsubscribe?.();
10383
- }, [state.accountId, subOrder]);
10384
- React2.useEffect(() => {
10385
- if (!state.accountId)
10386
- return;
10387
- const key = ["/v1/positions", state.accountId];
10388
- const unsubscribe = ws.privateSubscribe("account", {
10389
- onMessage: (data) => {
10390
- const { symbol, leverage } = data?.accountDetail?.symbolLeverage || {};
10391
- if (symbol && leverage) {
10392
- useSWR.mutate(
10393
- key,
10394
- (prevPositions) => {
10395
- if (prevPositions?.rows?.length) {
10396
- return {
10397
- ...prevPositions,
10398
- rows: prevPositions.rows.map((row) => {
10399
- return row.symbol === symbol ? { ...row, leverage } : row;
10400
- })
10401
- };
10402
- }
10403
- return prevPositions;
10404
- },
10405
- {
10406
- revalidate: false
10407
- }
10408
- );
10409
- }
10410
- }
10411
- });
10412
- return () => unsubscribe?.();
10413
- }, [state.accountId]);
10414
- React2.useEffect(() => {
10415
- if (!state.accountId) {
10416
- return;
10417
- }
10418
- const key = ["/v1/positions", state.accountId];
10419
- const unsubscribe = ws.privateSubscribe("position", {
10420
- onMessage: (data) => {
10421
- const { positions: nextPositions } = data;
10422
- useSWR.mutate(
10423
- key,
10424
- (prevPositions) => {
10425
- if (!!prevPositions) {
10426
- const newPositions = {
10427
- ...prevPositions,
10428
- rows: prevPositions.rows.map((row) => {
10429
- const itemIndex = nextPositions.findIndex(
10430
- (item) => item.symbol === row.symbol
10431
- );
10432
- if (itemIndex >= 0) {
10433
- const itemArr = nextPositions.splice(itemIndex, 1);
10434
- const item = itemArr[0];
10435
- if (item.averageOpenPrice === 0 && item.positionQty !== 0) {
10436
- return row;
10437
- }
10438
- return object2underscore(item);
10439
- }
10440
- return row;
10441
- })
10442
- };
10443
- if (nextPositions.length > 0) {
10444
- newPositions.rows = [
10445
- ...newPositions.rows,
10446
- ...nextPositions.map(object2underscore)
10447
- ];
10448
- }
10449
- return newPositions;
10450
- }
10451
- },
10452
- {
10453
- revalidate: false
10454
- }
10455
- );
10456
- }
10457
- });
10458
- return () => {
10459
- unsubscribe?.();
10460
- };
10461
- }, [state.accountId]);
10462
- };
10463
10426
  var useMarketStore = zustand.create(
10464
10427
  (set, get3) => ({
10465
10428
  market: [],
@@ -18149,6 +18112,7 @@ exports.useFeeState = useFeeState;
18149
18112
  exports.useFundingDetails = useFundingDetails;
18150
18113
  exports.useFundingFeeHistory = useFundingFeeHistory;
18151
18114
  exports.useFundingRate = useFundingRate;
18115
+ exports.useFundingRateBySymbol = useFundingRateBySymbol;
18152
18116
  exports.useFundingRateHistory = useFundingRateHistory;
18153
18117
  exports.useFundingRates = useFundingRates;
18154
18118
  exports.useFundingRatesStore = useFundingRatesStore;