perp-cli 0.7.47 → 0.7.48
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.
|
@@ -334,7 +334,9 @@ export class FundingArbStrategy {
|
|
|
334
334
|
const longAdapter = adapters.get(pos.longExchange);
|
|
335
335
|
const shortAdapter = adapters.get(pos.shortExchange);
|
|
336
336
|
if (longAdapter && shortAdapter) {
|
|
337
|
+
const isPositionGone = (msg) => msg.includes("ReduceOnly") || msg.includes("reduceOnly") || msg.includes("-2022");
|
|
337
338
|
let closeLongOk = false;
|
|
339
|
+
let longPositionGone = false;
|
|
338
340
|
try {
|
|
339
341
|
const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size, { reduceOnly: true });
|
|
340
342
|
closeLongOk = true;
|
|
@@ -342,11 +344,18 @@ export class FundingArbStrategy {
|
|
|
342
344
|
}
|
|
343
345
|
catch (err) {
|
|
344
346
|
const msg = err instanceof Error ? err.message : String(err);
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
347
|
+
if (isPositionGone(msg)) {
|
|
348
|
+
// Position already liquidated/closed on this exchange
|
|
349
|
+
longPositionGone = true;
|
|
350
|
+
ctx.log(` [ARB] Long position gone for ${pos.symbol} on ${pos.longExchange} (liquidated?) — force closing short`);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
ctx.log(` [ARB] Close long leg failed for ${pos.symbol}: ${msg} (cooldown 5m)`);
|
|
354
|
+
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" } });
|
|
355
|
+
this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 5 * 60 * 1000);
|
|
356
|
+
}
|
|
348
357
|
}
|
|
349
|
-
if (!closeLongOk)
|
|
358
|
+
if (!closeLongOk && !longPositionGone)
|
|
350
359
|
continue;
|
|
351
360
|
try {
|
|
352
361
|
const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size, { reduceOnly: true });
|
|
@@ -354,25 +363,44 @@ export class FundingArbStrategy {
|
|
|
354
363
|
}
|
|
355
364
|
catch (err) {
|
|
356
365
|
const msg = err instanceof Error ? err.message : String(err);
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
try {
|
|
361
|
-
await longAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
362
|
-
ctx.log(` [ARB] Rollback: re-opened long ${pos.size} ${pos.symbol} on ${pos.longExchange}`);
|
|
363
|
-
// Note: re-open is NOT reduceOnly (it's a new position)
|
|
364
|
-
logExecution({ type: "multi_leg_rollback", exchange: pos.longExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false });
|
|
366
|
+
if (isPositionGone(msg)) {
|
|
367
|
+
// Short position also gone — both liquidated, just clean up tracking
|
|
368
|
+
ctx.log(` [ARB] Short position also gone for ${pos.symbol} on ${pos.shortExchange} — removing from tracking`);
|
|
365
369
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
ctx.log(` [ARB]
|
|
369
|
-
logExecution({ type: "
|
|
370
|
+
else if (closeLongOk) {
|
|
371
|
+
// Long was closed but short failed (not liquidated) — rollback long
|
|
372
|
+
ctx.log(` [ARB] Close short leg failed for ${pos.symbol}: ${msg} — re-opening long to restore hedge`);
|
|
373
|
+
logExecution({ type: "arb_close", exchange: pos.shortExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "failed", error: msg, dryRun: false, meta: { mode: pos.mode, leg: "short" } });
|
|
374
|
+
try {
|
|
375
|
+
await longAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
376
|
+
ctx.log(` [ARB] Rollback: re-opened long ${pos.size} ${pos.symbol} on ${pos.longExchange}`);
|
|
377
|
+
logExecution({ type: "multi_leg_rollback", exchange: pos.longExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false });
|
|
378
|
+
}
|
|
379
|
+
catch (rbErr) {
|
|
380
|
+
const rbMsg = rbErr instanceof Error ? rbErr.message : String(rbErr);
|
|
381
|
+
ctx.log(` [ARB] CRITICAL: Close rollback failed for ${pos.symbol}: ${rbMsg}`);
|
|
382
|
+
logExecution({ type: "multi_leg_rollback", exchange: pos.longExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "failed", error: rbMsg, dryRun: false });
|
|
383
|
+
}
|
|
384
|
+
this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 5 * 60 * 1000);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// Long was gone, short failed (not liquidated) — orphaned short, force close without reduceOnly
|
|
389
|
+
ctx.log(` [ARB] Force closing orphaned short for ${pos.symbol}: ${msg}`);
|
|
390
|
+
try {
|
|
391
|
+
await shortAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
392
|
+
ctx.log(` [ARB] Force closed short ${pos.size} ${pos.symbol} on ${pos.shortExchange}`);
|
|
393
|
+
}
|
|
394
|
+
catch (forceErr) {
|
|
395
|
+
const forceMsg = forceErr instanceof Error ? forceErr.message : String(forceErr);
|
|
396
|
+
ctx.log(` [ARB] CRITICAL: Force close short failed for ${pos.symbol}: ${forceMsg}`);
|
|
397
|
+
this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 10 * 60 * 1000);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
370
400
|
}
|
|
371
|
-
this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 5 * 60 * 1000);
|
|
372
|
-
continue;
|
|
373
401
|
}
|
|
374
402
|
toClose.push(pos);
|
|
375
|
-
ctx.log(` [ARB] Closed ${pos.symbol} position`);
|
|
403
|
+
ctx.log(` [ARB] Closed ${pos.symbol} position${longPositionGone ? ' (long was liquidated)' : ''}`);
|
|
376
404
|
}
|
|
377
405
|
}
|
|
378
406
|
}
|