perp-cli 0.7.21 → 0.7.24

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.
@@ -119,15 +119,19 @@ export class FundingArbStrategy {
119
119
  }
120
120
  }
121
121
  const nonUsdc = spotBalances.filter(b => Number(b.total) > 0 && !b.token.toUpperCase().startsWith("USDC"));
122
+ ctx.log(` [ARB] ${name} spot balances: ${nonUsdc.map(b => `${b.token}=${b.total}`).join(", ") || "none"}`);
122
123
  if (nonUsdc.length === 0)
123
124
  continue;
124
125
  const perpPositions = positionsByExchange.get(name) ?? [];
125
126
  for (const perp of perpPositions) {
126
127
  const perpKey = `${name}:${perp.symbol}`;
127
- if (used.has(perpKey))
128
+ if (used.has(perpKey)) {
129
+ ctx.log(` [ARB] ${perpKey} already matched cross-exchange, skip spot-perp`);
128
130
  continue;
131
+ }
129
132
  const base = perp.symbol.replace(/-PERP$/, "").toUpperCase();
130
133
  const spotBal = nonUsdc.find(b => b.token.toUpperCase() === base);
134
+ ctx.log(` [ARB] ${name} perp ${perp.symbol}(${perp.side}) → base=${base}, spotMatch=${spotBal ? spotBal.token : "none"}`);
131
135
  if (spotBal && perp.side === "short") {
132
136
  recovered.push({ symbol: base, longExchange: `${name}-spot`, shortExchange: name, entrySpread: 0, size: perp.size, mode: "spot-perp" });
133
137
  used.add(perpKey);
@@ -371,18 +375,25 @@ export class FundingArbStrategy {
371
375
  }
372
376
  opportunities.sort((a, b) => b.spread - a.spread);
373
377
  if (opportunities.length > 0) {
374
- const top = opportunities.slice(0, 3);
375
- for (const opp of top) {
376
- if (opp.mode === "spot-perp") {
377
- ctx.log(` [ARB] ${opp.symbol}: ${opp.spread.toFixed(1)}% spread [spot-perp] (short ${opp.shortExchange} ${(opp.shortRate * 100).toFixed(4)}%)`);
378
- }
379
- else {
380
- ctx.log(` [ARB] ${opp.symbol}: ${opp.spread.toFixed(1)}% spread (long ${opp.longExchange} ${(opp.longRate * 100).toFixed(4)}% / short ${opp.shortExchange} ${(opp.shortRate * 100).toFixed(4)}%)`);
378
+ // Only log opportunities when there's room for new positions
379
+ if (arbPositions < params.max_positions) {
380
+ const top = opportunities.slice(0, 3);
381
+ for (const opp of top) {
382
+ if (opp.mode === "spot-perp") {
383
+ ctx.log(` [ARB] ${opp.symbol}: ${opp.spread.toFixed(1)}% spread [spot-perp] (short ${opp.shortExchange} ${(opp.shortRate * 100).toFixed(4)}%)`);
384
+ }
385
+ else {
386
+ ctx.log(` [ARB] ${opp.symbol}: ${opp.spread.toFixed(1)}% spread (long ${opp.longExchange} ${(opp.longRate * 100).toFixed(4)}% / short ${opp.shortExchange} ${(opp.shortRate * 100).toFixed(4)}%)`);
387
+ }
381
388
  }
382
389
  }
383
390
  // Auto-execute: try each opportunity until one succeeds
384
391
  if (arbPositions < params.max_positions) {
392
+ // Skip symbols already in open positions
393
+ const openSymbols = new Set(ctx.state.get("arbOpenPositions").map(p => p.symbol.toUpperCase()));
385
394
  for (const best of opportunities) {
395
+ if (openSymbols.has(best.symbol.toUpperCase()))
396
+ continue;
386
397
  if (best.mode === "spot-perp") {
387
398
  const opened = await this.openSpotPerp(best, adapters, ctx);
388
399
  if (opened)
@@ -18,7 +18,7 @@ const EXCHANGE_FUNDING_HOURS = {
18
18
  hyperliquid: 1,
19
19
  pacifica: 1,
20
20
  lighter: 8,
21
- aster: 8,
21
+ aster: 1,
22
22
  };
23
23
  /**
24
24
  * Get funding period in hours for an exchange.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perp-cli",
3
- "version": "0.7.21",
3
+ "version": "0.7.24",
4
4
  "description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter, Aster",
5
5
  "bin": {
6
6
  "perp": "dist/index.js",