dipcoin-cli 1.3.5 → 1.3.6

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/README.md CHANGED
@@ -181,6 +181,10 @@ dipcoin-cli position tpsl <symbol> --side <buy|sell> --quantity <q> --leverage <
181
181
  dipcoin-cli position tpsl <symbol> --side sell --quantity 0.01 --leverage 10 \
182
182
  --tp-trigger 105000 --tp-type limit --tp-price 105000
183
183
 
184
+ # Set or edit TP/SL on a vault position. Existing plan IDs are discovered automatically.
185
+ dipcoin-cli position tpsl BTC --side sell --quantity 0.01 --leverage 10 \
186
+ --vault <vaultId> --tp-trigger 105000 --sl-trigger 90000
187
+
184
188
  # Close positions — CLI figures out side & qty
185
189
  dipcoin-cli position close BTC # close 100% of BTC position
186
190
  dipcoin-cli position close BTC --percent 50 # close half
package/dist/cli.cjs CHANGED
@@ -17700,7 +17700,8 @@ class DipCoinPerpSDK {
17700
17700
  }
17701
17701
  // Use sub-account keypair for signing if available
17702
17702
  const signingKeypair = this.subKeypair || this.keypair;
17703
- const { symbol, market, side, isLong, leverage, quantity, reduceOnly = true, postOnly = false, orderbookOnly = true, ioc = false, tp, sl, } = params;
17703
+ const { symbol, market, creator, side, isLong, leverage, quantity, reduceOnly = true, postOnly = false, orderbookOnly = true, ioc = false, tp, sl, } = params;
17704
+ const orderCreator = creator || this.walletAddress;
17704
17705
  if (!this.isPositiveNumber(quantity)) {
17705
17706
  return {
17706
17707
  status: false,
@@ -17724,7 +17725,7 @@ class DipCoinPerpSDK {
17724
17725
  symbol,
17725
17726
  side,
17726
17727
  leverage: leverageWei,
17727
- creator: this.walletAddress,
17728
+ creator: orderCreator,
17728
17729
  };
17729
17730
  const sendPlanCloseRequest = async (payload) => {
17730
17731
  let response = await this.httpClient.post(API_ENDPOINTS.PLAN_CLOSE_ORDER, payload);
@@ -17754,7 +17755,7 @@ class DipCoinPerpSDK {
17754
17755
  : new BigNumber__default["default"](0);
17755
17756
  const tpOrder = {
17756
17757
  market,
17757
- creator: this.walletAddress,
17758
+ creator: orderCreator,
17758
17759
  isLong: !isLong,
17759
17760
  reduceOnly,
17760
17761
  postOnly,
@@ -17799,7 +17800,7 @@ class DipCoinPerpSDK {
17799
17800
  : new BigNumber__default["default"](0);
17800
17801
  const slOrder = {
17801
17802
  market,
17802
- creator: this.walletAddress,
17803
+ creator: orderCreator,
17803
17804
  isLong: !isLong,
17804
17805
  reduceOnly,
17805
17806
  postOnly,
@@ -17871,7 +17872,7 @@ class DipCoinPerpSDK {
17871
17872
  /**
17872
17873
  * Get TP/SL orders for a position
17873
17874
  */
17874
- async getPositionTpSl(positionId, tpslType = "normal") {
17875
+ async getPositionTpSl(positionId, tpslType = "normal", parentAddress) {
17875
17876
  try {
17876
17877
  const authResult = await this.authenticate();
17877
17878
  if (!authResult.status) {
@@ -17883,6 +17884,7 @@ class DipCoinPerpSDK {
17883
17884
  const params = {
17884
17885
  positionId,
17885
17886
  tpslType,
17887
+ ...(parentAddress ? { parentAddress } : {}),
17886
17888
  };
17887
17889
  let response = await this.httpClient.get(API_ENDPOINTS.GET_POSITION_TPSL, { params });
17888
17890
  if (response.code === 1000) {
@@ -20740,6 +20742,9 @@ function registerPositionCommands(program) {
20740
20742
  .option("--sl-trigger <price>", "SL trigger price")
20741
20743
  .option("--sl-type <type>", "SL order type: market or limit", "market")
20742
20744
  .option("--sl-price <price>", "SL order price (for limit)")
20745
+ .option("--vault <address>", "Target a vault position (vault object ID)")
20746
+ .option("--tp-plan-id <id>", "Existing TP plan ID to edit")
20747
+ .option("--sl-plan-id <id>", "Existing SL plan ID to edit")
20743
20748
  .action(async (symbol, opts) => {
20744
20749
  try {
20745
20750
  const sdk = getSDK();
@@ -20747,11 +20752,94 @@ function registerPositionCommands(program) {
20747
20752
  const perpId = await sdk.getPerpetualID(symbol);
20748
20753
  if (!perpId)
20749
20754
  return handleError(`PerpetualID not found for ${symbol}`);
20750
- const side = opts.side.toUpperCase() === "BUY" ? OrderSide.BUY : OrderSide.SELL;
20755
+ const sideInput = String(opts.side).toUpperCase();
20756
+ if (sideInput !== OrderSide.BUY && sideInput !== OrderSide.SELL) {
20757
+ return handleError(`--side must be buy or sell, got ${opts.side}`);
20758
+ }
20759
+ const side = sideInput;
20751
20760
  const isLong = side === OrderSide.SELL;
20761
+ if (!opts.tpTrigger && !opts.slTrigger) {
20762
+ return handleError("At least one of --tp-trigger or --sl-trigger is required.");
20763
+ }
20764
+ if (opts.tpPlanId && !opts.tpTrigger) {
20765
+ return handleError("--tp-plan-id requires --tp-trigger.");
20766
+ }
20767
+ if (opts.slPlanId && !opts.slTrigger) {
20768
+ return handleError("--sl-plan-id requires --sl-trigger.");
20769
+ }
20770
+ let tpPlanId = opts.tpPlanId;
20771
+ let slPlanId = opts.slPlanId;
20772
+ const shouldFindTpPlan = Boolean(opts.tpTrigger && !tpPlanId);
20773
+ const shouldFindSlPlan = Boolean(opts.slTrigger && !slPlanId);
20774
+ const parentAddress = opts.vault || sdk.address;
20775
+ const positionsResult = await sdk.getPositions({ parentAddress, symbol });
20776
+ if (!positionsResult.status) {
20777
+ return handleError(positionsResult.error || "Failed to fetch position");
20778
+ }
20779
+ const matchingPositions = (positionsResult.data || []).filter((position) => position.symbol === symbol);
20780
+ if (matchingPositions.length !== 1) {
20781
+ return handleError(matchingPositions.length === 0
20782
+ ? `No open position for ${symbol}.`
20783
+ : `Multiple positions found for ${symbol}; cannot choose one safely.`);
20784
+ }
20785
+ const activePosition = matchingPositions[0];
20786
+ const positionSide = String(activePosition.side).toUpperCase();
20787
+ const expectedClosingSide = positionSide === "BUY" || positionSide === "LONG"
20788
+ ? OrderSide.SELL
20789
+ : positionSide === "SELL" || positionSide === "SHORT"
20790
+ ? OrderSide.BUY
20791
+ : undefined;
20792
+ if (!expectedClosingSide) {
20793
+ return handleError(`Unsupported position side: ${activePosition.side}`);
20794
+ }
20795
+ if (side !== expectedClosingSide) {
20796
+ return handleError(`--side must be ${expectedClosingSide.toLowerCase()} to close a ${positionSide} position.`);
20797
+ }
20798
+ const requestedQuantity = new BigNumber__default["default"](opts.quantity);
20799
+ const positionQuantity = new BigNumber__default["default"](activePosition.quantity).shiftedBy(-18);
20800
+ if (!requestedQuantity.isFinite() ||
20801
+ requestedQuantity.lte(0) ||
20802
+ requestedQuantity.gt(positionQuantity)) {
20803
+ return handleError(`--quantity must be greater than 0 and no more than the position quantity (${positionQuantity.toFixed()}).`);
20804
+ }
20805
+ const requestedLeverage = new BigNumber__default["default"](opts.leverage);
20806
+ const positionLeverage = new BigNumber__default["default"](activePosition.leverage).shiftedBy(-18);
20807
+ if (!requestedLeverage.isFinite() || !requestedLeverage.eq(positionLeverage)) {
20808
+ return handleError(`--leverage must match the position leverage (${positionLeverage.toFixed()}).`);
20809
+ }
20810
+ // Editing a plan requires its existing ID. Resolve it automatically for the
20811
+ // position-wide TP/SL pair so repeating this command updates instead of duplicates.
20812
+ if (shouldFindTpPlan || shouldFindSlPlan) {
20813
+ const positionId = activePosition.id ?? activePosition.positionId;
20814
+ if (positionId === undefined || positionId === null) {
20815
+ return handleError(`Position ID missing for ${symbol}.`);
20816
+ }
20817
+ const plansResult = await sdk.getPositionTpSl(positionId, "position", opts.vault);
20818
+ if (!plansResult.status) {
20819
+ return handleError(plansResult.error || "Failed to fetch existing TP/SL orders");
20820
+ }
20821
+ const findExistingPlanId = (planOrderType) => {
20822
+ const matches = (plansResult.data || []).filter((order) => String(order.planOrderType).toLowerCase() === planOrderType.toLowerCase());
20823
+ if (matches.length > 1) {
20824
+ throw new Error(`Multiple ${planOrderType} plans found; use --${planOrderType === "takeProfit" ? "tp" : "sl"}-plan-id to choose one.`);
20825
+ }
20826
+ if (!matches.length)
20827
+ return undefined;
20828
+ const planId = planOrderType === "takeProfit" ? matches[0].tpPlanId : matches[0].slPlanId;
20829
+ if (planId === undefined || planId === null) {
20830
+ throw new Error(`${planOrderType} plan is missing its editable plan ID.`);
20831
+ }
20832
+ return planId;
20833
+ };
20834
+ if (shouldFindTpPlan)
20835
+ tpPlanId = findExistingPlanId("takeProfit");
20836
+ if (shouldFindSlPlan)
20837
+ slPlanId = findExistingPlanId("stopLoss");
20838
+ }
20752
20839
  const params = {
20753
20840
  symbol,
20754
20841
  market: perpId,
20842
+ ...(opts.vault ? { creator: opts.vault } : {}),
20755
20843
  side,
20756
20844
  isLong,
20757
20845
  quantity: opts.quantity,
@@ -20762,6 +20850,7 @@ function registerPositionCommands(program) {
20762
20850
  triggerPrice: opts.tpTrigger,
20763
20851
  orderType: opts.tpType?.toUpperCase() === "LIMIT" ? OrderType.LIMIT : OrderType.MARKET,
20764
20852
  ...(opts.tpPrice ? { orderPrice: opts.tpPrice } : {}),
20853
+ ...(tpPlanId !== undefined ? { planId: tpPlanId } : {}),
20765
20854
  tpslType: "position",
20766
20855
  };
20767
20856
  }
@@ -20770,6 +20859,7 @@ function registerPositionCommands(program) {
20770
20859
  triggerPrice: opts.slTrigger,
20771
20860
  orderType: opts.slType?.toUpperCase() === "LIMIT" ? OrderType.LIMIT : OrderType.MARKET,
20772
20861
  ...(opts.slPrice ? { orderPrice: opts.slPrice } : {}),
20862
+ ...(slPlanId !== undefined ? { planId: slPlanId } : {}),
20773
20863
  tpslType: "position",
20774
20864
  };
20775
20865
  }