@orderly.network/ui-positions 2.8.8 → 2.8.9-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +317 -140
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +321 -144
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -13
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { registerSimpleDialog, useModal, SimpleDialog, Flex, Text,
|
|
2
|
-
import React2, { createContext, useMemo, useCallback, useContext,
|
|
1
|
+
import { registerSimpleDialog, useModal, SimpleDialog, Flex, Text, Badge, Divider, CloseIcon, Button, ThrottledButton, useScreen, ArrowDownShortIcon, ArrowUpShortIcon, Grid, Statistic, ExclamationFillIcon, modal, Tooltip, DataTable, cn, ListView, SimpleSheet, usePagination, DataFilter, toast, formatAddress, Box, HoverCard, ArrowLeftRightIcon, capitalizeFirstLetter, ShareIcon, EditIcon, Input, inputFormatter, Select, PopoverRoot, PopoverTrigger, PopoverContent, Slider } from '@orderly.network/ui';
|
|
2
|
+
import React2, { createContext, useMemo, useState, useCallback, useContext, useEffect, useRef } from 'react';
|
|
3
3
|
import { i18n, useTranslation } from '@orderly.network/i18n';
|
|
4
|
-
import { OrderSide, OrderType, EMPTY_LIST, AccountStatusEnum, TrackerEventName, PositionType, AlgoOrderType } from '@orderly.network/types';
|
|
4
|
+
import { OrderSide, OrderType, EMPTY_LIST, AccountStatusEnum, TrackerEventName, OrderStatus, AlgoOrderRootType, PositionType, AlgoOrderType } from '@orderly.network/types';
|
|
5
5
|
import { commifyOptional, Decimal, formatNum, getTimestamp, commify } from '@orderly.network/utils';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { useSymbolsInfo, useMarkPrice,
|
|
8
|
-
import { produce } from 'immer';
|
|
9
|
-
import { positions, account } from '@orderly.network/perp';
|
|
7
|
+
import { useSymbolsInfo, useMarkPrice, useSubAccountMutation, useLocalStorage, usePrivateInfiniteQuery, useBoolean, useSessionStorage, useAccount, usePrivateQuery, useTrack, usePositionStream, usePositionClose, useSWR, fetcher, useEventEmitter, useDebouncedCallback, useGetRwaSymbolOpenStatus, useTpslPriceChecker, useConfig, findPositionTPSLFromOrders, findTPSLFromOrder, useReferralInfo, useAccountInfo, useLeverageBySymbol, utils, useMaxLeverage } from '@orderly.network/hooks';
|
|
10
8
|
import { useDataTap, useOrderEntryFormErrorMsg } from '@orderly.network/react-app';
|
|
9
|
+
import { positions, account } from '@orderly.network/perp';
|
|
11
10
|
import { AuthGuardDataTable } from '@orderly.network/ui-connector';
|
|
12
11
|
import { SymbolLeverageDialogId, SymbolLeverageSheetId } from '@orderly.network/ui-leverage';
|
|
13
12
|
import { SharePnLDialogId, SharePnLBottomSheetId } from '@orderly.network/ui-share';
|
|
@@ -593,6 +592,102 @@ var FundingFeeButton = ({ fee, symbol, start_t, end_t }) => {
|
|
|
593
592
|
)
|
|
594
593
|
] });
|
|
595
594
|
};
|
|
595
|
+
var calculatePositions = (positions2, symbolsInfo, accountInfo, tpslOrders) => {
|
|
596
|
+
return positions2.map((item) => {
|
|
597
|
+
const info = symbolsInfo[item.symbol];
|
|
598
|
+
const notional = positions.notional(item.position_qty, item.mark_price);
|
|
599
|
+
const account2 = accountInfo.find(
|
|
600
|
+
(acc) => acc.account_id === item.account_id
|
|
601
|
+
);
|
|
602
|
+
const baseMMR = info?.("base_mmr");
|
|
603
|
+
const baseIMR = info?.("base_imr");
|
|
604
|
+
if (!baseMMR || !baseIMR) {
|
|
605
|
+
return item;
|
|
606
|
+
}
|
|
607
|
+
const MMR = positions.MMR({
|
|
608
|
+
baseMMR,
|
|
609
|
+
baseIMR,
|
|
610
|
+
IMRFactor: account2?.imr_factor[item.symbol] ?? 0,
|
|
611
|
+
positionNotional: notional,
|
|
612
|
+
IMR_factor_power: 4 / 5
|
|
613
|
+
});
|
|
614
|
+
const mm = positions.maintenanceMargin({
|
|
615
|
+
positionQty: item.position_qty,
|
|
616
|
+
markPrice: item.mark_price,
|
|
617
|
+
MMR
|
|
618
|
+
});
|
|
619
|
+
const unrealPnl = positions.unrealizedPnL({
|
|
620
|
+
qty: item.position_qty,
|
|
621
|
+
openPrice: item?.average_open_price,
|
|
622
|
+
markPrice: item.mark_price
|
|
623
|
+
});
|
|
624
|
+
const maxLeverage = item.leverage || 1;
|
|
625
|
+
const imr = account.IMR({
|
|
626
|
+
maxLeverage,
|
|
627
|
+
baseIMR,
|
|
628
|
+
IMR_Factor: account2?.imr_factor[item.symbol] ?? 0,
|
|
629
|
+
positionNotional: notional,
|
|
630
|
+
ordersNotional: 0,
|
|
631
|
+
IMR_factor_power: 4 / 5
|
|
632
|
+
});
|
|
633
|
+
const unrealPnlROI = positions.unrealizedPnLROI({
|
|
634
|
+
positionQty: item.position_qty,
|
|
635
|
+
openPrice: item.average_open_price,
|
|
636
|
+
IMR: imr,
|
|
637
|
+
unrealizedPnL: unrealPnl
|
|
638
|
+
});
|
|
639
|
+
let unrealPnl_index = 0;
|
|
640
|
+
let unrealPnlROI_index = 0;
|
|
641
|
+
if (item.index_price) {
|
|
642
|
+
unrealPnl_index = positions.unrealizedPnL({
|
|
643
|
+
qty: item.position_qty,
|
|
644
|
+
openPrice: item?.average_open_price,
|
|
645
|
+
markPrice: item.index_price
|
|
646
|
+
});
|
|
647
|
+
unrealPnlROI_index = positions.unrealizedPnLROI({
|
|
648
|
+
positionQty: item.position_qty,
|
|
649
|
+
openPrice: item.average_open_price,
|
|
650
|
+
IMR: imr,
|
|
651
|
+
unrealizedPnL: unrealPnl_index
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
const filteredTPSLOrders = tpslOrders.filter(
|
|
655
|
+
(tpslOrder) => tpslOrder.account_id === item.account_id
|
|
656
|
+
);
|
|
657
|
+
const tpsl = formatTPSL(filteredTPSLOrders, item.symbol);
|
|
658
|
+
return {
|
|
659
|
+
...item,
|
|
660
|
+
...tpsl,
|
|
661
|
+
mmr: MMR,
|
|
662
|
+
mm,
|
|
663
|
+
notional,
|
|
664
|
+
unrealized_pnl: unrealPnl,
|
|
665
|
+
unrealized_pnl_ROI: unrealPnlROI,
|
|
666
|
+
unrealized_pnl_ROI_index: unrealPnlROI_index
|
|
667
|
+
};
|
|
668
|
+
});
|
|
669
|
+
};
|
|
670
|
+
function formatTPSL(tpslOrders, symbol) {
|
|
671
|
+
if (Array.isArray(tpslOrders) && tpslOrders.length) {
|
|
672
|
+
const { fullPositionOrder, partialPositionOrders } = findPositionTPSLFromOrders(tpslOrders, symbol);
|
|
673
|
+
const full_tp_sl = fullPositionOrder ? findTPSLFromOrder(fullPositionOrder) : void 0;
|
|
674
|
+
const partialPossitionOrder = partialPositionOrders && partialPositionOrders.length ? partialPositionOrders[0] : void 0;
|
|
675
|
+
const partial_tp_sl = partialPossitionOrder ? findTPSLFromOrder(partialPossitionOrder) : void 0;
|
|
676
|
+
return {
|
|
677
|
+
full_tp_sl: {
|
|
678
|
+
tp_trigger_price: full_tp_sl?.tp_trigger_price,
|
|
679
|
+
sl_trigger_price: full_tp_sl?.sl_trigger_price,
|
|
680
|
+
algo_order: fullPositionOrder
|
|
681
|
+
},
|
|
682
|
+
partial_tp_sl: {
|
|
683
|
+
order_num: partialPositionOrders?.length ?? 0,
|
|
684
|
+
tp_trigger_price: partial_tp_sl?.tp_trigger_price,
|
|
685
|
+
sl_trigger_price: partial_tp_sl?.sl_trigger_price,
|
|
686
|
+
algo_order: partialPossitionOrder
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
}
|
|
596
691
|
var signatureMiddleware = (account2, accountId) => {
|
|
597
692
|
const apiBaseUrl = useConfig("apiBaseUrl");
|
|
598
693
|
return (useSWRNext) => {
|
|
@@ -642,6 +737,48 @@ var useSubAccountQuery = (query, options) => {
|
|
|
642
737
|
}
|
|
643
738
|
);
|
|
644
739
|
};
|
|
740
|
+
function useSubAccountTPSL(subAccountIds) {
|
|
741
|
+
const ee = useEventEmitter();
|
|
742
|
+
const { data: algoOrdersResponse, mutate: mutateTPSLOrders } = useSubAccountQuery(
|
|
743
|
+
`/v1/algo/orders?size=100&page=1&status=${OrderStatus.INCOMPLETE}`,
|
|
744
|
+
{
|
|
745
|
+
accountId: subAccountIds,
|
|
746
|
+
formatter: (data) => data,
|
|
747
|
+
revalidateOnFocus: false
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
const tpslOrders = useMemo(() => {
|
|
751
|
+
if (!Array.isArray(algoOrdersResponse)) {
|
|
752
|
+
return [];
|
|
753
|
+
}
|
|
754
|
+
const algoOrders = algoOrdersResponse?.map(
|
|
755
|
+
(item, index) => item.rows.map((order) => ({
|
|
756
|
+
...order,
|
|
757
|
+
account_id: subAccountIds[index]
|
|
758
|
+
}))
|
|
759
|
+
)?.flat();
|
|
760
|
+
return algoOrders?.filter(
|
|
761
|
+
(order) => [AlgoOrderRootType.POSITIONAL_TP_SL, AlgoOrderRootType.TP_SL].includes(
|
|
762
|
+
order.algo_type
|
|
763
|
+
)
|
|
764
|
+
);
|
|
765
|
+
}, [algoOrdersResponse, subAccountIds]);
|
|
766
|
+
const refresh = useDebouncedCallback(() => {
|
|
767
|
+
mutateTPSLOrders();
|
|
768
|
+
}, 200);
|
|
769
|
+
useEffect(() => {
|
|
770
|
+
const handler = (position) => {
|
|
771
|
+
if (position.account_id) {
|
|
772
|
+
refresh();
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
ee.on("tpsl:updateOrder", handler);
|
|
776
|
+
return () => {
|
|
777
|
+
ee.off("tpsl:updateOrder", handler);
|
|
778
|
+
};
|
|
779
|
+
}, [ee]);
|
|
780
|
+
return { tpslOrders, mutateTPSLOrders };
|
|
781
|
+
}
|
|
645
782
|
|
|
646
783
|
// src/components/positions/combinePositions.script.ts
|
|
647
784
|
var useCombinePositionsScript = (props) => {
|
|
@@ -655,12 +792,9 @@ var useCombinePositionsScript = (props) => {
|
|
|
655
792
|
selectedAccount
|
|
656
793
|
} = props;
|
|
657
794
|
const { pagination, setPage } = usePagination({ pageSize: 50 });
|
|
658
|
-
useEffect(() => {
|
|
659
|
-
setPage(1);
|
|
660
|
-
}, [symbol]);
|
|
661
795
|
const symbolsInfo = useSymbolsInfo();
|
|
662
796
|
const { state } = useAccount();
|
|
663
|
-
const [
|
|
797
|
+
const [mainAccountPositions, , { isLoading }] = usePositionStream(symbol, {
|
|
664
798
|
calcMode,
|
|
665
799
|
includedPendingOrder
|
|
666
800
|
});
|
|
@@ -669,90 +803,37 @@ var useCombinePositionsScript = (props) => {
|
|
|
669
803
|
isLoading: isPositionLoading,
|
|
670
804
|
mutate: mutatePositions
|
|
671
805
|
} = usePrivateQuery("/v1/client/aggregate/positions", {
|
|
672
|
-
// formatter: (data) => data,
|
|
673
806
|
errorRetryCount: 3
|
|
674
807
|
});
|
|
808
|
+
const { allAccountIds, subAccountIds } = useMemo(() => {
|
|
809
|
+
const uniqueIds = new Set(
|
|
810
|
+
newPositions.filter((item) => item.account_id).map((item) => item.account_id).filter(Boolean)
|
|
811
|
+
);
|
|
812
|
+
const allAccountIds2 = Array.from(uniqueIds);
|
|
813
|
+
const subAccountIds2 = allAccountIds2.filter(
|
|
814
|
+
(item) => item !== state.mainAccountId
|
|
815
|
+
);
|
|
816
|
+
return { allAccountIds: allAccountIds2, subAccountIds: subAccountIds2 };
|
|
817
|
+
}, [newPositions, state.mainAccountId]);
|
|
675
818
|
const { data: accountInfo = [], isLoading: isAccountInfoLoading } = useSubAccountQuery("/v1/client/info", {
|
|
676
|
-
accountId:
|
|
819
|
+
accountId: allAccountIds,
|
|
677
820
|
revalidateOnFocus: false
|
|
678
821
|
});
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
(
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
baseMMR,
|
|
695
|
-
baseIMR,
|
|
696
|
-
IMRFactor: account2?.imr_factor[item.symbol] ?? 0,
|
|
697
|
-
positionNotional: notional,
|
|
698
|
-
IMR_factor_power: 4 / 5
|
|
699
|
-
});
|
|
700
|
-
const mm = positions.maintenanceMargin({
|
|
701
|
-
positionQty: item.position_qty,
|
|
702
|
-
markPrice: item.mark_price,
|
|
703
|
-
MMR
|
|
704
|
-
});
|
|
705
|
-
const unrealPnl = positions.unrealizedPnL({
|
|
706
|
-
qty: item.position_qty,
|
|
707
|
-
openPrice: item?.average_open_price,
|
|
708
|
-
// markPrice: unRealizedPrice,
|
|
709
|
-
markPrice: item.mark_price
|
|
710
|
-
});
|
|
711
|
-
const maxLeverage = item.leverage || 1;
|
|
712
|
-
const imr = account.IMR({
|
|
713
|
-
maxLeverage,
|
|
714
|
-
baseIMR,
|
|
715
|
-
IMR_Factor: account2?.imr_factor[item.symbol] ?? 0,
|
|
716
|
-
positionNotional: notional,
|
|
717
|
-
ordersNotional: 0,
|
|
718
|
-
IMR_factor_power: 4 / 5
|
|
719
|
-
});
|
|
720
|
-
const unrealPnlROI = positions.unrealizedPnLROI({
|
|
721
|
-
positionQty: item.position_qty,
|
|
722
|
-
openPrice: item.average_open_price,
|
|
723
|
-
IMR: imr,
|
|
724
|
-
unrealizedPnL: unrealPnl
|
|
725
|
-
});
|
|
726
|
-
let unrealPnl_index = 0;
|
|
727
|
-
let unrealPnlROI_index = 0;
|
|
728
|
-
if (item.index_price) {
|
|
729
|
-
unrealPnl_index = positions.unrealizedPnL({
|
|
730
|
-
qty: item.position_qty,
|
|
731
|
-
openPrice: item?.average_open_price,
|
|
732
|
-
// markPrice: unRealizedPrice,
|
|
733
|
-
markPrice: item.index_price
|
|
734
|
-
});
|
|
735
|
-
unrealPnlROI_index = positions.unrealizedPnLROI({
|
|
736
|
-
positionQty: item.position_qty,
|
|
737
|
-
openPrice: item.average_open_price,
|
|
738
|
-
IMR: imr,
|
|
739
|
-
unrealizedPnL: unrealPnl_index
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
item.mmr = MMR;
|
|
743
|
-
item.mm = mm;
|
|
744
|
-
item.notional = notional;
|
|
745
|
-
item.unrealized_pnl = unrealPnl;
|
|
746
|
-
item.unrealized_pnl_ROI = unrealPnlROI;
|
|
747
|
-
item.unrealized_pnl_ROI_index = unrealPnlROI_index;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
);
|
|
751
|
-
const dataSource = useDataTap(
|
|
752
|
-
[...oldPositions?.rows, ...processPositions].filter(
|
|
753
|
-
(acc) => acc.position_qty !== 0
|
|
754
|
-
)
|
|
755
|
-
) ?? [];
|
|
822
|
+
const { tpslOrders, mutateTPSLOrders } = useSubAccountTPSL(subAccountIds);
|
|
823
|
+
const subAccountPositions = useMemo(() => {
|
|
824
|
+
return calculatePositions(
|
|
825
|
+
newPositions.filter((item) => item.account_id !== state.mainAccountId),
|
|
826
|
+
symbolsInfo,
|
|
827
|
+
accountInfo,
|
|
828
|
+
tpslOrders
|
|
829
|
+
);
|
|
830
|
+
}, [newPositions, symbolsInfo, accountInfo, state.mainAccountId, tpslOrders]);
|
|
831
|
+
const allPositions = useMemo(() => {
|
|
832
|
+
return [...mainAccountPositions?.rows, ...subAccountPositions].filter(
|
|
833
|
+
(item) => item.position_qty !== 0
|
|
834
|
+
);
|
|
835
|
+
}, [mainAccountPositions, subAccountPositions]);
|
|
836
|
+
const dataSource = useDataTap(allPositions) ?? [];
|
|
756
837
|
const filtered = useMemo(() => {
|
|
757
838
|
if (!selectedAccount || selectedAccount === "All accounts" /* ALL */) {
|
|
758
839
|
return dataSource;
|
|
@@ -771,18 +852,23 @@ var useCombinePositionsScript = (props) => {
|
|
|
771
852
|
subAccounts: state.subAccounts
|
|
772
853
|
});
|
|
773
854
|
}, [filtered, state.mainAccountId, state.subAccounts]);
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
855
|
+
const loading = isLoading || isPositionLoading || isAccountInfoLoading;
|
|
856
|
+
useEffect(() => {
|
|
857
|
+
setPage(1);
|
|
858
|
+
}, [symbol]);
|
|
859
|
+
const mutateList = useCallback(() => {
|
|
860
|
+
mutatePositions();
|
|
861
|
+
mutateTPSLOrders();
|
|
862
|
+
}, []);
|
|
777
863
|
return {
|
|
778
864
|
tableData: groupDataSource,
|
|
779
|
-
isLoading:
|
|
865
|
+
isLoading: loading,
|
|
780
866
|
pnlNotionalDecimalPrecision,
|
|
781
867
|
sharePnLConfig,
|
|
782
868
|
symbol,
|
|
783
869
|
onSymbolChange,
|
|
784
870
|
pagination,
|
|
785
|
-
mutatePositions
|
|
871
|
+
mutatePositions: mutateList
|
|
786
872
|
};
|
|
787
873
|
};
|
|
788
874
|
var groupDataByAccount = (data, options) => {
|
|
@@ -790,12 +876,12 @@ var groupDataByAccount = (data, options) => {
|
|
|
790
876
|
const map = /* @__PURE__ */ new Map();
|
|
791
877
|
for (const item of data) {
|
|
792
878
|
const accountId = item.account_id || mainAccountId;
|
|
793
|
-
const findSubAccount = subAccounts.find((
|
|
879
|
+
const findSubAccount = subAccounts.find((item2) => item2.id === accountId);
|
|
794
880
|
if (map.has(accountId)) {
|
|
795
881
|
map.get(accountId)?.children?.push(item);
|
|
796
882
|
} else {
|
|
797
883
|
map.set(accountId, {
|
|
798
|
-
|
|
884
|
+
account_id: accountId,
|
|
799
885
|
description: accountId === mainAccountId ? i18n.t("common.mainAccount") : findSubAccount?.description || formatAddress(findSubAccount?.id || ""),
|
|
800
886
|
children: [item]
|
|
801
887
|
});
|
|
@@ -956,7 +1042,14 @@ var OrderInfoCard = ({
|
|
|
956
1042
|
);
|
|
957
1043
|
};
|
|
958
1044
|
var ReversePosition = (props) => {
|
|
959
|
-
const {
|
|
1045
|
+
const {
|
|
1046
|
+
displayInfo,
|
|
1047
|
+
validationError,
|
|
1048
|
+
className,
|
|
1049
|
+
style,
|
|
1050
|
+
onConfirm,
|
|
1051
|
+
onCancel
|
|
1052
|
+
} = props;
|
|
960
1053
|
const { t } = useTranslation();
|
|
961
1054
|
if (!displayInfo) {
|
|
962
1055
|
return null;
|
|
@@ -983,6 +1076,7 @@ var ReversePosition = (props) => {
|
|
|
983
1076
|
/* @__PURE__ */ jsx(Text.numeral, { dp: quoteDp, size: "sm", intensity: 80, children: markPrice }),
|
|
984
1077
|
/* @__PURE__ */ jsx(Text, { size: "sm", intensity: 36, children: quote })
|
|
985
1078
|
] });
|
|
1079
|
+
const showBelowMinError = validationError === "belowMin";
|
|
986
1080
|
return /* @__PURE__ */ jsxs(
|
|
987
1081
|
Flex,
|
|
988
1082
|
{
|
|
@@ -1042,11 +1136,12 @@ var ReversePosition = (props) => {
|
|
|
1042
1136
|
baseDp
|
|
1043
1137
|
}
|
|
1044
1138
|
),
|
|
1045
|
-
/* @__PURE__ */ jsx(Text, { size: "2xs", color: "warning", weight: "semibold", children: t("positions.reverse.description") })
|
|
1139
|
+
showBelowMinError ? /* @__PURE__ */ jsx(Text, { size: "2xs", color: "danger", weight: "semibold", children: t("positions.reverse.error.belowMin") }) : /* @__PURE__ */ jsx(Text, { size: "2xs", color: "warning", weight: "semibold", children: t("positions.reverse.description") })
|
|
1046
1140
|
]
|
|
1047
1141
|
}
|
|
1048
1142
|
);
|
|
1049
1143
|
};
|
|
1144
|
+
var MAX_BATCH_ORDER_SIZE = 20;
|
|
1050
1145
|
var useReversePositionEnabled = () => {
|
|
1051
1146
|
const { isMobile, isDesktop } = useScreen();
|
|
1052
1147
|
const [desktopEnabled, setDesktopEnabled] = useLocalStorage(
|
|
@@ -1083,12 +1178,26 @@ var useReversePositionEnabled = () => {
|
|
|
1083
1178
|
};
|
|
1084
1179
|
var useReversePositionScript = (options) => {
|
|
1085
1180
|
const { position, onSuccess, onError } = options || {};
|
|
1086
|
-
const { t } = useTranslation();
|
|
1087
1181
|
const { isEnabled, setEnabled } = useReversePositionEnabled();
|
|
1088
1182
|
const symbolsInfo = useSymbolsInfo();
|
|
1089
1183
|
const symbol = position?.symbol || "";
|
|
1090
1184
|
const { data: symbolMarketPrice } = useMarkPrice(symbol);
|
|
1091
1185
|
const symbolInfo = symbolsInfo?.[symbol];
|
|
1186
|
+
const baseMin = useMemo(() => {
|
|
1187
|
+
if (!symbolInfo)
|
|
1188
|
+
return 0;
|
|
1189
|
+
return symbolInfo("base_min") || 0;
|
|
1190
|
+
}, [symbolInfo]);
|
|
1191
|
+
const baseMax = useMemo(() => {
|
|
1192
|
+
if (!symbolInfo)
|
|
1193
|
+
return 0;
|
|
1194
|
+
return symbolInfo("base_max") || 0;
|
|
1195
|
+
}, [symbolInfo]);
|
|
1196
|
+
const baseDp = useMemo(() => {
|
|
1197
|
+
if (!symbolInfo)
|
|
1198
|
+
return 6;
|
|
1199
|
+
return symbolInfo("base_dp") || 6;
|
|
1200
|
+
}, [symbolInfo]);
|
|
1092
1201
|
const positionQty = useMemo(() => {
|
|
1093
1202
|
if (!position)
|
|
1094
1203
|
return 0;
|
|
@@ -1099,21 +1208,62 @@ var useReversePositionScript = (options) => {
|
|
|
1099
1208
|
return false;
|
|
1100
1209
|
return position.position_qty > 0;
|
|
1101
1210
|
}, [position]);
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
return 0;
|
|
1109
|
-
const desiredQty = positionQty;
|
|
1110
|
-
if (desiredQty > maxOpenQty && maxOpenQty > 0) {
|
|
1111
|
-
return maxOpenQty;
|
|
1211
|
+
const reverseQty = positionQty;
|
|
1212
|
+
const validationError = useMemo(() => {
|
|
1213
|
+
if (!position || !symbolInfo)
|
|
1214
|
+
return null;
|
|
1215
|
+
if (baseMin > 0 && reverseQty < baseMin) {
|
|
1216
|
+
return "belowMin";
|
|
1112
1217
|
}
|
|
1113
|
-
return
|
|
1114
|
-
}, [
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1218
|
+
return null;
|
|
1219
|
+
}, [position, symbolInfo, reverseQty, baseMin]);
|
|
1220
|
+
const splitOrders = useMemo(() => {
|
|
1221
|
+
if (!position || !symbolInfo || baseMax <= 0 || reverseQty <= 0) {
|
|
1222
|
+
return { needsSplit: false, orders: [] };
|
|
1223
|
+
}
|
|
1224
|
+
const buildOrder = (qty, side, reduceOnly) => {
|
|
1225
|
+
return {
|
|
1226
|
+
symbol: position.symbol,
|
|
1227
|
+
order_type: OrderType.MARKET,
|
|
1228
|
+
side,
|
|
1229
|
+
order_quantity: new Decimal(qty).todp(baseDp).toString(),
|
|
1230
|
+
reduce_only: reduceOnly
|
|
1231
|
+
};
|
|
1232
|
+
};
|
|
1233
|
+
const closeSide = isLong ? OrderSide.SELL : OrderSide.BUY;
|
|
1234
|
+
const openSide = isLong ? OrderSide.SELL : OrderSide.BUY;
|
|
1235
|
+
if (reverseQty <= baseMax) {
|
|
1236
|
+
return {
|
|
1237
|
+
needsSplit: false,
|
|
1238
|
+
orders: [
|
|
1239
|
+
buildOrder(reverseQty, closeSide, true),
|
|
1240
|
+
buildOrder(reverseQty, openSide, false)
|
|
1241
|
+
]
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
const orders = [];
|
|
1245
|
+
const perOrderQty = baseMax;
|
|
1246
|
+
const numOrders = Math.ceil(reverseQty / baseMax);
|
|
1247
|
+
for (let i = 0; i < numOrders - 1; i++) {
|
|
1248
|
+
orders.push(buildOrder(perOrderQty, closeSide, true));
|
|
1249
|
+
}
|
|
1250
|
+
orders.push(
|
|
1251
|
+
buildOrder(reverseQty - perOrderQty * (numOrders - 1), closeSide, true)
|
|
1252
|
+
);
|
|
1253
|
+
for (let i = 0; i < numOrders - 1; i++) {
|
|
1254
|
+
orders.push(buildOrder(perOrderQty, openSide, false));
|
|
1255
|
+
}
|
|
1256
|
+
orders.push(
|
|
1257
|
+
buildOrder(reverseQty - perOrderQty * (numOrders - 1), openSide, false)
|
|
1258
|
+
);
|
|
1259
|
+
return {
|
|
1260
|
+
needsSplit: true,
|
|
1261
|
+
orders
|
|
1262
|
+
};
|
|
1263
|
+
}, [position, symbolInfo, reverseQty, baseMax, baseDp]);
|
|
1264
|
+
const [isReversing, setIsReversing] = useState(false);
|
|
1265
|
+
const [doBatchCreateOrder] = useSubAccountMutation(
|
|
1266
|
+
"/v1/batch-order",
|
|
1117
1267
|
"POST",
|
|
1118
1268
|
{
|
|
1119
1269
|
accountId: position?.account_id
|
|
@@ -1121,55 +1271,74 @@ var useReversePositionScript = (options) => {
|
|
|
1121
1271
|
);
|
|
1122
1272
|
const reversePosition = useCallback(async () => {
|
|
1123
1273
|
if (!position || positionQty === 0) {
|
|
1124
|
-
toast.error("No position to reverse");
|
|
1125
1274
|
return false;
|
|
1126
1275
|
}
|
|
1276
|
+
if (validationError) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
setIsReversing(true);
|
|
1127
1280
|
try {
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
);
|
|
1281
|
+
const ordersArray = splitOrders.orders;
|
|
1282
|
+
if (ordersArray.length > MAX_BATCH_ORDER_SIZE) {
|
|
1283
|
+
for (let i = 0; i < ordersArray.length; i += MAX_BATCH_ORDER_SIZE) {
|
|
1284
|
+
const batch = ordersArray.slice(i, i + MAX_BATCH_ORDER_SIZE);
|
|
1285
|
+
const result = await doBatchCreateOrder({
|
|
1286
|
+
orders: batch,
|
|
1287
|
+
symbol: position.symbol
|
|
1288
|
+
});
|
|
1289
|
+
await new Promise(
|
|
1290
|
+
(resolve) => setTimeout(resolve, batch.length * 110)
|
|
1291
|
+
);
|
|
1292
|
+
if (!result || result.error) {
|
|
1293
|
+
throw result?.error || new Error("Batch order failed");
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
} else {
|
|
1297
|
+
const result = await doBatchCreateOrder({
|
|
1298
|
+
orders: ordersArray,
|
|
1299
|
+
symbol: position.symbol
|
|
1300
|
+
});
|
|
1301
|
+
if (!result || result.error) {
|
|
1302
|
+
throw result?.error || new Error("Batch order failed");
|
|
1303
|
+
}
|
|
1148
1304
|
}
|
|
1149
1305
|
onSuccess?.();
|
|
1150
1306
|
return true;
|
|
1151
1307
|
} catch (error) {
|
|
1152
1308
|
onError?.(error);
|
|
1153
1309
|
return false;
|
|
1310
|
+
} finally {
|
|
1311
|
+
setIsReversing(false);
|
|
1154
1312
|
}
|
|
1155
|
-
}, [
|
|
1313
|
+
}, [
|
|
1314
|
+
position,
|
|
1315
|
+
positionQty,
|
|
1316
|
+
reverseQty,
|
|
1317
|
+
isLong,
|
|
1318
|
+
doBatchCreateOrder,
|
|
1319
|
+
splitOrders,
|
|
1320
|
+
symbolInfo,
|
|
1321
|
+
validationError,
|
|
1322
|
+
onSuccess,
|
|
1323
|
+
onError
|
|
1324
|
+
]);
|
|
1156
1325
|
const displayInfo = useMemo(() => {
|
|
1157
1326
|
if (!position || !symbolInfo) {
|
|
1158
1327
|
return null;
|
|
1159
1328
|
}
|
|
1160
1329
|
const base = symbolInfo("base");
|
|
1161
1330
|
const quote = symbolInfo("quote");
|
|
1162
|
-
const
|
|
1331
|
+
const baseDp2 = symbolInfo("base_dp");
|
|
1163
1332
|
const quoteDp = symbolInfo("quote_dp");
|
|
1164
1333
|
const leverage = position.leverage || 1;
|
|
1165
1334
|
return {
|
|
1166
1335
|
symbol: position.symbol,
|
|
1167
1336
|
base,
|
|
1168
1337
|
quote,
|
|
1169
|
-
baseDp,
|
|
1338
|
+
baseDp: baseDp2,
|
|
1170
1339
|
quoteDp,
|
|
1171
|
-
positionQty: new Decimal(positionQty).todp(
|
|
1172
|
-
reverseQty: new Decimal(reverseQty).todp(
|
|
1340
|
+
positionQty: new Decimal(positionQty).todp(baseDp2).toString(),
|
|
1341
|
+
reverseQty: new Decimal(reverseQty).todp(baseDp2).toString(),
|
|
1173
1342
|
markPrice: symbolMarketPrice ? new Decimal(symbolMarketPrice).todp(quoteDp).toString() : "--",
|
|
1174
1343
|
leverage,
|
|
1175
1344
|
isLong
|
|
@@ -1186,11 +1355,13 @@ var useReversePositionScript = (options) => {
|
|
|
1186
1355
|
isEnabled,
|
|
1187
1356
|
setEnabled,
|
|
1188
1357
|
reversePosition,
|
|
1189
|
-
isReversing
|
|
1358
|
+
isReversing,
|
|
1190
1359
|
displayInfo,
|
|
1191
1360
|
positionQty,
|
|
1192
1361
|
reverseQty,
|
|
1193
|
-
isLong
|
|
1362
|
+
isLong,
|
|
1363
|
+
validationError,
|
|
1364
|
+
splitOrders
|
|
1194
1365
|
};
|
|
1195
1366
|
};
|
|
1196
1367
|
var ReversePositionWidget = (props) => {
|
|
@@ -1209,6 +1380,7 @@ var ReversePositionWidget = (props) => {
|
|
|
1209
1380
|
}
|
|
1210
1381
|
});
|
|
1211
1382
|
const actions = useMemo(() => {
|
|
1383
|
+
const hasValidationError = !!state.validationError;
|
|
1212
1384
|
return {
|
|
1213
1385
|
primary: {
|
|
1214
1386
|
label: t("common.confirm"),
|
|
@@ -1229,10 +1401,11 @@ var ReversePositionWidget = (props) => {
|
|
|
1229
1401
|
}
|
|
1230
1402
|
},
|
|
1231
1403
|
loading: state.isReversing,
|
|
1232
|
-
disabled: state.isReversing || !state.displayInfo
|
|
1404
|
+
disabled: state.isReversing || !state.displayInfo || hasValidationError
|
|
1233
1405
|
},
|
|
1234
1406
|
secondary: {
|
|
1235
1407
|
label: t("common.cancel"),
|
|
1408
|
+
disabled: state.isReversing,
|
|
1236
1409
|
onClick: async () => {
|
|
1237
1410
|
reject?.("cancel");
|
|
1238
1411
|
hide();
|
|
@@ -1252,6 +1425,7 @@ var ReversePositionWidget = (props) => {
|
|
|
1252
1425
|
content: "oui-border oui-border-line-6"
|
|
1253
1426
|
},
|
|
1254
1427
|
actions,
|
|
1428
|
+
closable: !state.isReversing,
|
|
1255
1429
|
children: /* @__PURE__ */ jsx(ReversePosition, { ...state })
|
|
1256
1430
|
}
|
|
1257
1431
|
);
|
|
@@ -2014,12 +2188,12 @@ var TPSLEditIcon = () => {
|
|
|
2014
2188
|
};
|
|
2015
2189
|
var AddIcon = (props) => {
|
|
2016
2190
|
const { position, baseDp, quoteDp, tpslOrder } = usePositionsRowContext();
|
|
2017
|
-
const [needConfirm] = useLocalStorage("orderly_order_confirm", true);
|
|
2018
2191
|
const { t } = useTranslation();
|
|
2019
2192
|
const { isMobile } = useScreen();
|
|
2020
2193
|
const onAdd = () => {
|
|
2021
2194
|
const dialogId = isMobile ? TPSLSheetId : TPSLDialogId;
|
|
2022
2195
|
const modalParams = {
|
|
2196
|
+
position,
|
|
2023
2197
|
symbol: position.symbol,
|
|
2024
2198
|
baseDP: baseDp,
|
|
2025
2199
|
quoteDP: quoteDp,
|
|
@@ -3429,7 +3603,7 @@ var CombinePositions = (props) => {
|
|
|
3429
3603
|
dataSource,
|
|
3430
3604
|
expanded: true,
|
|
3431
3605
|
getSubRows: (row) => row.children,
|
|
3432
|
-
generatedRowKey: (record) => record.
|
|
3606
|
+
generatedRowKey: (record) => `${record.account_id}${record.symbol || ""}`,
|
|
3433
3607
|
onCell: (column, record) => {
|
|
3434
3608
|
const isGroup = (record.children ?? []).length > 0;
|
|
3435
3609
|
if (isGroup) {
|
|
@@ -3453,6 +3627,9 @@ var CombinePositions = (props) => {
|
|
|
3453
3627
|
},
|
|
3454
3628
|
manualPagination: false,
|
|
3455
3629
|
pagination,
|
|
3630
|
+
classNames: {
|
|
3631
|
+
scroll: "oui-pb-10"
|
|
3632
|
+
},
|
|
3456
3633
|
testIds: {
|
|
3457
3634
|
body: "oui-testid-dataList-position-tab-body"
|
|
3458
3635
|
}
|