perp-cli 0.7.42 → 0.7.43

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.
@@ -331,7 +331,7 @@ export class FundingArbStrategy {
331
331
  if (longAdapter && shortAdapter) {
332
332
  let closeLongOk = false;
333
333
  try {
334
- const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size);
334
+ const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size, { reduceOnly: true });
335
335
  closeLongOk = true;
336
336
  logExecution({ type: "arb_close", exchange: pos.longExchange, symbol: pos.symbol, side: "sell", size: pos.size, status: "success", dryRun: false, meta: { mode: pos.mode, leg: "long", signedSpread: signedSpread, response: closeLongResult } });
337
337
  }
@@ -342,9 +342,9 @@ export class FundingArbStrategy {
342
342
  this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 5 * 60 * 1000);
343
343
  }
344
344
  if (!closeLongOk)
345
- continue; // don't close short if long failed — keep hedge intact
345
+ continue;
346
346
  try {
347
- const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size);
347
+ const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size, { reduceOnly: true });
348
348
  logExecution({ type: "arb_close", exchange: pos.shortExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false, meta: { mode: pos.mode, leg: "short", signedSpread: signedSpread, response: closeShortResult } });
349
349
  }
350
350
  catch (err) {
@@ -355,6 +355,7 @@ export class FundingArbStrategy {
355
355
  try {
356
356
  await longAdapter.marketOrder(pos.symbol, "buy", pos.size);
357
357
  ctx.log(` [ARB] Rollback: re-opened long ${pos.size} ${pos.symbol} on ${pos.longExchange}`);
358
+ // Note: re-open is NOT reduceOnly (it's a new position)
358
359
  logExecution({ type: "multi_leg_rollback", exchange: pos.longExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false });
359
360
  }
360
361
  catch (rbErr) {
@@ -570,7 +571,7 @@ export class FundingArbStrategy {
570
571
  logExecution({ type: "arb_entry", exchange: best.shortExchange, symbol: best.symbol, side: "sell", size: matched.size, status: "failed", error: msg, dryRun: false, meta: { mode: "perp-perp", leg: "short" } });
571
572
  // Rollback: close the long position
572
573
  try {
573
- await longAdapter.marketOrder(best.symbol, "sell", matched.size);
574
+ await longAdapter.marketOrder(best.symbol, "sell", matched.size, { reduceOnly: true });
574
575
  ctx.log(` [ARB] Rollback success: closed long ${matched.size} ${best.symbol} on ${best.longExchange}`);
575
576
  logExecution({ type: "multi_leg_rollback", exchange: best.longExchange, symbol: best.symbol, side: "sell", size: matched.size, status: "success", dryRun: false });
576
577
  }
@@ -617,14 +618,14 @@ export class FundingArbStrategy {
617
618
  logExecution({ type: "arb_entry", exchange: `${best.longExchange}↔${best.shortExchange}`, symbol: best.symbol, side: "verify", size: matched.size, status: "failed", error: `Position missing after fill: long=${longExists} short=${shortExists}`, dryRun: false });
618
619
  if (longExists && !shortExists) {
619
620
  try {
620
- await longAdapter.marketOrder(best.symbol, "sell", matched.size);
621
+ await longAdapter.marketOrder(best.symbol, "sell", matched.size, { reduceOnly: true });
621
622
  ctx.log(` [ARB] Rollback: closed phantom long ${best.symbol}`);
622
623
  }
623
624
  catch { /* best effort */ }
624
625
  }
625
626
  if (shortExists && !longExists) {
626
627
  try {
627
- await shortAdapter.marketOrder(best.symbol, "buy", matched.size);
628
+ await shortAdapter.marketOrder(best.symbol, "buy", matched.size, { reduceOnly: true });
628
629
  ctx.log(` [ARB] Rollback: closed phantom short ${best.symbol}`);
629
630
  }
630
631
  catch { /* best effort */ }
@@ -683,7 +684,7 @@ export class FundingArbStrategy {
683
684
  if (longAdapter && shortAdapter) {
684
685
  ctx.log(` [ARB] Closing ${pos.symbol} on stop (${pos.size})`);
685
686
  try {
686
- const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size);
687
+ const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size, { reduceOnly: true });
687
688
  logExecution({ type: "arb_close", exchange: pos.longExchange, symbol: pos.symbol, side: "sell", size: pos.size, status: "success", dryRun: false, meta: { mode: pos.mode, leg: "long", stop: true, response: closeLongResult } });
688
689
  }
689
690
  catch (err) {
@@ -692,7 +693,7 @@ export class FundingArbStrategy {
692
693
  logExecution({ type: "arb_close", exchange: pos.longExchange, symbol: pos.symbol, side: "sell", size: pos.size, status: "failed", error: msg, dryRun: false, meta: { mode: pos.mode, leg: "long", stop: true } });
693
694
  }
694
695
  try {
695
- const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size);
696
+ const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size, { reduceOnly: true });
696
697
  logExecution({ type: "arb_close", exchange: pos.shortExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false, meta: { mode: pos.mode, leg: "short", stop: true, response: closeShortResult } });
697
698
  }
698
699
  catch (err) {
@@ -758,7 +759,7 @@ export class FundingArbStrategy {
758
759
  // Step 3: Close perp short
759
760
  try {
760
761
  const perpSymbol = this.getPerpSymbol(pos.symbol, pos.shortExchange);
761
- await perpAdapter.marketOrder(perpSymbol, "buy", pos.size);
762
+ await perpAdapter.marketOrder(perpSymbol, "buy", pos.size, { reduceOnly: true });
762
763
  ctx.log(` [ARB] Closed perp short ${pos.size} ${perpSymbol}`);
763
764
  }
764
765
  catch (err) {
@@ -44,7 +44,9 @@ export declare class AsterAdapter implements ExchangeAdapter {
44
44
  getOrderHistory(limit?: number): Promise<ExchangeOrder[]>;
45
45
  getTradeHistory(limit?: number): Promise<ExchangeTrade[]>;
46
46
  getFundingPayments(limit?: number): Promise<ExchangeFundingPayment[]>;
47
- marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
47
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts?: {
48
+ reduceOnly?: boolean;
49
+ }): Promise<unknown>;
48
50
  limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
49
51
  reduceOnly?: boolean;
50
52
  tif?: string;
@@ -283,14 +283,17 @@ export class AsterAdapter {
283
283
  }));
284
284
  }
285
285
  // ── Trading ──
286
- async marketOrder(symbol, side, size) {
286
+ async marketOrder(symbol, side, size, opts) {
287
287
  const apiSymbol = this._toApi(symbol);
288
- const result = await this._signedPost("/fapi/v1/order", {
288
+ const params = {
289
289
  symbol: apiSymbol,
290
290
  side: side.toUpperCase(),
291
291
  type: "MARKET",
292
292
  quantity: size,
293
- });
293
+ };
294
+ if (opts?.reduceOnly)
295
+ params.reduceOnly = "true";
296
+ const result = await this._signedPost("/fapi/v1/order", params);
294
297
  const r = result;
295
298
  const executedQty = Number(r.executedQty ?? 0);
296
299
  const orderId = String(r.orderId ?? "");
@@ -55,7 +55,9 @@ export declare class HyperliquidAdapter implements ExchangeAdapter {
55
55
  getPositions(): Promise<ExchangePosition[]>;
56
56
  getOpenOrders(): Promise<ExchangeOrder[]>;
57
57
  private _invalidateAccountCache;
58
- marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
58
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts?: {
59
+ reduceOnly?: boolean;
60
+ }): Promise<unknown>;
59
61
  /**
60
62
  * Place a market order on a HIP-3 deployed dex.
61
63
  * Bypasses SDK's symbolConversion (which only knows native perps)
@@ -393,7 +393,7 @@ export class HyperliquidAdapter {
393
393
  }
394
394
  catch { /* ignore */ }
395
395
  }
396
- async marketOrder(symbol, side, size) {
396
+ async marketOrder(symbol, side, size, opts) {
397
397
  this.ensureSigner();
398
398
  if (this._dex) {
399
399
  const r = await this._dexMarketOrder(symbol, side, size);
@@ -85,7 +85,9 @@ export interface ExchangeAdapter {
85
85
  getOrderHistory(limit?: number): Promise<ExchangeOrder[]>;
86
86
  getTradeHistory(limit?: number): Promise<ExchangeTrade[]>;
87
87
  getFundingPayments(limit?: number): Promise<ExchangeFundingPayment[]>;
88
- marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
88
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts?: {
89
+ reduceOnly?: boolean;
90
+ }): Promise<unknown>;
89
91
  limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
90
92
  reduceOnly?: boolean;
91
93
  tif?: string;
@@ -62,7 +62,9 @@ export declare class LighterAdapter implements ExchangeAdapter {
62
62
  private getAuthToken;
63
63
  private restGetAuth;
64
64
  getOpenOrders(): Promise<ExchangeOrder[]>;
65
- marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
65
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts?: {
66
+ reduceOnly?: boolean;
67
+ }): Promise<unknown>;
66
68
  limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
67
69
  reduceOnly?: boolean;
68
70
  tif?: string;
@@ -454,8 +454,9 @@ export class LighterAdapter {
454
454
  return [];
455
455
  }
456
456
  }
457
- async marketOrder(symbol, side, size) {
457
+ async marketOrder(symbol, side, size, opts) {
458
458
  this.ensureSigner();
459
+ void opts; // lighter doesn't support reduceOnly on market orders
459
460
  await this.ensureMarketMap();
460
461
  const nonce = await this.getNextNonce();
461
462
  const marketIndex = this.getMarketIndex(symbol);
@@ -33,7 +33,9 @@ export declare class PacificaAdapter implements ExchangeAdapter {
33
33
  getBalance(): Promise<ExchangeBalance>;
34
34
  getPositions(): Promise<ExchangePosition[]>;
35
35
  getOpenOrders(): Promise<ExchangeOrder[]>;
36
- marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
36
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts?: {
37
+ reduceOnly?: boolean;
38
+ }): Promise<unknown>;
37
39
  limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
38
40
  reduceOnly?: boolean;
39
41
  tif?: string;
@@ -180,9 +180,9 @@ export class PacificaAdapter {
180
180
  };
181
181
  });
182
182
  }
183
- async marketOrder(symbol, side, size) {
183
+ async marketOrder(symbol, side, size, opts) {
184
184
  this.ensureSigner();
185
- return this.client.createMarketOrder({ symbol, amount: size, side: side === "buy" ? "bid" : "ask", reduce_only: false, slippage_percent: "1" }, this.account, this.signMessage);
185
+ return this.client.createMarketOrder({ symbol, amount: size, side: side === "buy" ? "bid" : "ask", reduce_only: opts?.reduceOnly ?? false, slippage_percent: "1" }, this.account, this.signMessage);
186
186
  }
187
187
  async limitOrder(symbol, side, price, size, opts) {
188
188
  this.ensureSigner();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perp-cli",
3
- "version": "0.7.42",
3
+ "version": "0.7.43",
4
4
  "description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter, Aster",
5
5
  "bin": {
6
6
  "perp": "dist/index.js",