perp-cli 0.7.39 → 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.
- package/dist/bot/config.js +2 -2
- package/dist/bot/presets.js +2 -2
- package/dist/bot/strategies/funding-arb-strategy.js +69 -51
- package/dist/cache.d.ts +1 -1
- package/dist/cache.js +1 -1
- package/dist/exchanges/aster.d.ts +3 -1
- package/dist/exchanges/aster.js +6 -3
- package/dist/exchanges/hyperliquid.d.ts +3 -1
- package/dist/exchanges/hyperliquid.js +1 -1
- package/dist/exchanges/interface.d.ts +3 -1
- package/dist/exchanges/lighter.d.ts +3 -1
- package/dist/exchanges/lighter.js +2 -1
- package/dist/exchanges/pacifica.d.ts +3 -1
- package/dist/exchanges/pacifica.js +2 -2
- package/package.json +1 -1
package/dist/bot/config.js
CHANGED
|
@@ -30,7 +30,7 @@ function parseBotConfig(raw) {
|
|
|
30
30
|
const name = String(raw.name ?? `bot-${Date.now()}`);
|
|
31
31
|
const exchange = String(raw.exchange ?? "hyperliquid");
|
|
32
32
|
const symbol = String(raw.symbol ?? "ETH");
|
|
33
|
-
const monitorInterval = Number(raw.monitor_interval_sec ?? raw.interval ??
|
|
33
|
+
const monitorInterval = Number(raw.monitor_interval_sec ?? raw.interval ?? 30);
|
|
34
34
|
// Strategy
|
|
35
35
|
const stratRaw = (raw.strategy ?? raw.params ?? {});
|
|
36
36
|
const stratType = String(stratRaw.type ?? raw.strategy_type ?? "grid");
|
|
@@ -131,7 +131,7 @@ export function quickGridConfig(opts) {
|
|
|
131
131
|
...(opts.maxRuntime ? [{ type: "time_after", value: opts.maxRuntime }] : []),
|
|
132
132
|
],
|
|
133
133
|
risk: { ...DEFAULT_RISK, max_drawdown: opts.maxDrawdown },
|
|
134
|
-
monitor_interval_sec:
|
|
134
|
+
monitor_interval_sec: 30,
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
137
|
export function quickDCAConfig(opts) {
|
package/dist/bot/presets.js
CHANGED
|
@@ -58,7 +58,7 @@ const gridStandard = {
|
|
|
58
58
|
{ type: "volatility_above", value: 12 },
|
|
59
59
|
],
|
|
60
60
|
risk: { ...DEFAULT_RISK, max_drawdown: 100, max_daily_loss: 50 },
|
|
61
|
-
monitor_interval_sec:
|
|
61
|
+
monitor_interval_sec: 30,
|
|
62
62
|
}),
|
|
63
63
|
};
|
|
64
64
|
const gridAggressive = {
|
|
@@ -111,7 +111,7 @@ const gridLongBias = {
|
|
|
111
111
|
entry_conditions: [{ type: "always", value: 0 }],
|
|
112
112
|
exit_conditions: [],
|
|
113
113
|
risk: { ...DEFAULT_RISK, max_drawdown: 150, max_daily_loss: 75 },
|
|
114
|
-
monitor_interval_sec:
|
|
114
|
+
monitor_interval_sec: 30,
|
|
115
115
|
}),
|
|
116
116
|
};
|
|
117
117
|
// ── DCA Presets ──
|
|
@@ -190,9 +190,44 @@ export class FundingArbStrategy {
|
|
|
190
190
|
ctx.log(` [ARB] Need 2+ exchanges, have ${adapters.size}. Skipping.`);
|
|
191
191
|
return [];
|
|
192
192
|
}
|
|
193
|
-
//
|
|
194
|
-
const
|
|
193
|
+
// ── Pre-filter exchanges by margin availability ──
|
|
194
|
+
const exchangeBalances = new Map();
|
|
195
|
+
const availableAdapters = new Map();
|
|
195
196
|
for (const [name, a] of adapters) {
|
|
197
|
+
try {
|
|
198
|
+
const bal = await a.getBalance();
|
|
199
|
+
const equity = parseFloat(bal.equity);
|
|
200
|
+
const marginUsed = parseFloat(bal.marginUsed);
|
|
201
|
+
const marginPct = equity > 0 ? (marginUsed / equity) * 100 : 100;
|
|
202
|
+
exchangeBalances.set(name, { equity, available: parseFloat(bal.available), marginPct });
|
|
203
|
+
if (marginPct < 70) {
|
|
204
|
+
availableAdapters.set(name, a);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
ctx.log(` [ARB] ${name} margin ${marginPct.toFixed(0)}% — skipping for this tick`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
availableAdapters.set(name, a); // on error, include it
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Determine which exchanges need rate fetching.
|
|
215
|
+
// When at max_positions we only need rates for exchanges that hold open positions
|
|
216
|
+
// (to evaluate close conditions). Exchanges with no positions can be skipped.
|
|
217
|
+
const atMaxPositions = openPositions.length >= (params.max_positions ?? 3);
|
|
218
|
+
const positionExchanges = new Set();
|
|
219
|
+
if (atMaxPositions) {
|
|
220
|
+
for (const p of openPositions) {
|
|
221
|
+
positionExchanges.add(p.longExchange.replace(/-spot$/i, ""));
|
|
222
|
+
positionExchanges.add(p.shortExchange);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const exchangesToScan = atMaxPositions
|
|
226
|
+
? [...adapters].filter(([name]) => positionExchanges.has(name))
|
|
227
|
+
: [...adapters];
|
|
228
|
+
// Fetch rates — all exchanges if room for new positions, only position exchanges if full
|
|
229
|
+
const ratesByExchange = new Map();
|
|
230
|
+
for (const [name, a] of exchangesToScan) {
|
|
196
231
|
const rates = await this.fetchRates(a, name);
|
|
197
232
|
const map = new Map();
|
|
198
233
|
for (const r of rates)
|
|
@@ -296,7 +331,7 @@ export class FundingArbStrategy {
|
|
|
296
331
|
if (longAdapter && shortAdapter) {
|
|
297
332
|
let closeLongOk = false;
|
|
298
333
|
try {
|
|
299
|
-
const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size);
|
|
334
|
+
const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size, { reduceOnly: true });
|
|
300
335
|
closeLongOk = true;
|
|
301
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 } });
|
|
302
337
|
}
|
|
@@ -307,9 +342,9 @@ export class FundingArbStrategy {
|
|
|
307
342
|
this._failCooldown.set(`${pos.symbol}:close`, Date.now() + 5 * 60 * 1000);
|
|
308
343
|
}
|
|
309
344
|
if (!closeLongOk)
|
|
310
|
-
continue;
|
|
345
|
+
continue;
|
|
311
346
|
try {
|
|
312
|
-
const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
347
|
+
const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size, { reduceOnly: true });
|
|
313
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 } });
|
|
314
349
|
}
|
|
315
350
|
catch (err) {
|
|
@@ -320,6 +355,7 @@ export class FundingArbStrategy {
|
|
|
320
355
|
try {
|
|
321
356
|
await longAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
322
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)
|
|
323
359
|
logExecution({ type: "multi_leg_rollback", exchange: pos.longExchange, symbol: pos.symbol, side: "buy", size: pos.size, status: "success", dryRun: false });
|
|
324
360
|
}
|
|
325
361
|
catch (rbErr) {
|
|
@@ -345,6 +381,7 @@ export class FundingArbStrategy {
|
|
|
345
381
|
// ── Find new opportunities ──
|
|
346
382
|
const arbPositions = ctx.state.get("arbOpenPositions").length;
|
|
347
383
|
const exchangeNames = [...ratesByExchange.keys()];
|
|
384
|
+
const scanExchangeNames = exchangeNames.filter(n => availableAdapters.has(n));
|
|
348
385
|
const opportunities = [];
|
|
349
386
|
const allSymbols = new Set();
|
|
350
387
|
for (const [, map] of ratesByExchange) {
|
|
@@ -356,7 +393,7 @@ export class FundingArbStrategy {
|
|
|
356
393
|
let minHourly = Infinity, maxHourly = -Infinity;
|
|
357
394
|
let minExchange = "", maxExchange = "";
|
|
358
395
|
let minRate = 0, maxRate = 0;
|
|
359
|
-
for (const exName of
|
|
396
|
+
for (const exName of scanExchangeNames) {
|
|
360
397
|
const rateInfo = this.findRate(ratesByExchange, exName, sym);
|
|
361
398
|
if (!rateInfo)
|
|
362
399
|
continue;
|
|
@@ -442,7 +479,6 @@ export class FundingArbStrategy {
|
|
|
442
479
|
if (arbPositions < params.max_positions) {
|
|
443
480
|
// Skip symbols already in open positions
|
|
444
481
|
const openSymbols = new Set(ctx.state.get("arbOpenPositions").map(p => p.symbol.toUpperCase()));
|
|
445
|
-
let skipMargin = 0;
|
|
446
482
|
let skipCooldown = 0;
|
|
447
483
|
for (const best of opportunities) {
|
|
448
484
|
if (openSymbols.has(best.symbol.toUpperCase()))
|
|
@@ -480,39 +516,24 @@ export class FundingArbStrategy {
|
|
|
480
516
|
]);
|
|
481
517
|
}
|
|
482
518
|
catch { /* non-critical, some exchanges set leverage on order */ }
|
|
483
|
-
// Balance + margin check
|
|
519
|
+
// Balance + margin check (use pre-fetched balances from tick start)
|
|
484
520
|
let targetSizeUsd = params.size_usd;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
skipMargin++;
|
|
521
|
+
const longBal = exchangeBalances.get(best.longExchange);
|
|
522
|
+
const shortBal = exchangeBalances.get(best.shortExchange);
|
|
523
|
+
if (!longBal || !shortBal)
|
|
524
|
+
continue;
|
|
525
|
+
const minAvail = Math.min(longBal.available, shortBal.available);
|
|
526
|
+
const requiredMarginPerLeg = targetSizeUsd / leverage;
|
|
527
|
+
if (minAvail < requiredMarginPerLeg) {
|
|
528
|
+
// Try reducing size to what's affordable
|
|
529
|
+
const maxAffordableNotional = minAvail * leverage * 0.8;
|
|
530
|
+
if (maxAffordableNotional < 10) {
|
|
531
|
+
ctx.log(` [ARB] Skip ${best.symbol}: insufficient margin ($${minAvail.toFixed(2)} < $${requiredMarginPerLeg.toFixed(2)} at ${leverage}x)`);
|
|
497
532
|
continue;
|
|
498
533
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const shortAvail = parseFloat(shortBal.available);
|
|
502
|
-
const minAvail = Math.min(longAvail, shortAvail);
|
|
503
|
-
const requiredMarginPerLeg = targetSizeUsd / leverage;
|
|
504
|
-
if (minAvail < requiredMarginPerLeg) {
|
|
505
|
-
// Try reducing size to what's affordable
|
|
506
|
-
const maxAffordableNotional = minAvail * leverage * 0.8;
|
|
507
|
-
if (maxAffordableNotional < 10) {
|
|
508
|
-
ctx.log(` [ARB] Skip ${best.symbol}: insufficient margin ($${minAvail.toFixed(2)} < $${requiredMarginPerLeg.toFixed(2)} needed at ${leverage}x)`);
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
511
|
-
ctx.log(` [ARB] Size reduced $${targetSizeUsd} → $${maxAffordableNotional.toFixed(0)} (limited by margin $${minAvail.toFixed(2)} at ${leverage}x)`);
|
|
512
|
-
targetSizeUsd = Math.floor(maxAffordableNotional);
|
|
513
|
-
}
|
|
534
|
+
ctx.log(` [ARB] Size reduced $${targetSizeUsd} → $${maxAffordableNotional.toFixed(0)} (limited by margin $${minAvail.toFixed(2)} at ${leverage}x)`);
|
|
535
|
+
targetSizeUsd = Math.floor(maxAffordableNotional);
|
|
514
536
|
}
|
|
515
|
-
catch { /* non-critical, proceed with original size */ }
|
|
516
537
|
// Liquidity check
|
|
517
538
|
const liq = await checkArbLiquidity(longAdapter, shortAdapter, best.symbol, targetSizeUsd, 0.5, (msg) => ctx.log(` ${msg}`));
|
|
518
539
|
if (!liq.viable)
|
|
@@ -550,7 +571,7 @@ export class FundingArbStrategy {
|
|
|
550
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" } });
|
|
551
572
|
// Rollback: close the long position
|
|
552
573
|
try {
|
|
553
|
-
await longAdapter.marketOrder(best.symbol, "sell", matched.size);
|
|
574
|
+
await longAdapter.marketOrder(best.symbol, "sell", matched.size, { reduceOnly: true });
|
|
554
575
|
ctx.log(` [ARB] Rollback success: closed long ${matched.size} ${best.symbol} on ${best.longExchange}`);
|
|
555
576
|
logExecution({ type: "multi_leg_rollback", exchange: best.longExchange, symbol: best.symbol, side: "sell", size: matched.size, status: "success", dryRun: false });
|
|
556
577
|
}
|
|
@@ -574,8 +595,10 @@ export class FundingArbStrategy {
|
|
|
574
595
|
ctx.log(` [ARB] WARNING: Fill reconciliation failed: ${reconMsg}`);
|
|
575
596
|
}
|
|
576
597
|
// Verify both positions actually exist (catches instant liquidation)
|
|
577
|
-
//
|
|
578
|
-
|
|
598
|
+
// Invalidate cache so getPositions returns fresh data
|
|
599
|
+
const { invalidateCache } = await import("../../cache.js");
|
|
600
|
+
invalidateCache("acct");
|
|
601
|
+
await new Promise(resolve => setTimeout(resolve, 2000)); // brief wait for exchange settlement
|
|
579
602
|
try {
|
|
580
603
|
const [longPositions, shortPositions] = await Promise.all([
|
|
581
604
|
longAdapter.getPositions(),
|
|
@@ -595,14 +618,14 @@ export class FundingArbStrategy {
|
|
|
595
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 });
|
|
596
619
|
if (longExists && !shortExists) {
|
|
597
620
|
try {
|
|
598
|
-
await longAdapter.marketOrder(best.symbol, "sell", matched.size);
|
|
621
|
+
await longAdapter.marketOrder(best.symbol, "sell", matched.size, { reduceOnly: true });
|
|
599
622
|
ctx.log(` [ARB] Rollback: closed phantom long ${best.symbol}`);
|
|
600
623
|
}
|
|
601
624
|
catch { /* best effort */ }
|
|
602
625
|
}
|
|
603
626
|
if (shortExists && !longExists) {
|
|
604
627
|
try {
|
|
605
|
-
await shortAdapter.marketOrder(best.symbol, "buy", matched.size);
|
|
628
|
+
await shortAdapter.marketOrder(best.symbol, "buy", matched.size, { reduceOnly: true });
|
|
606
629
|
ctx.log(` [ARB] Rollback: closed phantom short ${best.symbol}`);
|
|
607
630
|
}
|
|
608
631
|
catch { /* best effort */ }
|
|
@@ -631,13 +654,8 @@ export class FundingArbStrategy {
|
|
|
631
654
|
return [{ type: "noop" }];
|
|
632
655
|
}
|
|
633
656
|
// Log skip summary only when there were skips (avoids noise when nothing happened)
|
|
634
|
-
if (
|
|
635
|
-
|
|
636
|
-
if (skipMargin > 0)
|
|
637
|
-
parts.push(`${skipMargin} margin`);
|
|
638
|
-
if (skipCooldown > 0)
|
|
639
|
-
parts.push(`${skipCooldown} cooldown`);
|
|
640
|
-
ctx.log(` [ARB] Skipped ${skipMargin + skipCooldown} opportunities (${parts.join(", ")})`);
|
|
657
|
+
if (skipCooldown > 0) {
|
|
658
|
+
ctx.log(` [ARB] Skipped ${skipCooldown} opportunities (${skipCooldown} cooldown)`);
|
|
641
659
|
}
|
|
642
660
|
}
|
|
643
661
|
}
|
|
@@ -666,7 +684,7 @@ export class FundingArbStrategy {
|
|
|
666
684
|
if (longAdapter && shortAdapter) {
|
|
667
685
|
ctx.log(` [ARB] Closing ${pos.symbol} on stop (${pos.size})`);
|
|
668
686
|
try {
|
|
669
|
-
const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size);
|
|
687
|
+
const closeLongResult = await longAdapter.marketOrder(pos.symbol, "sell", pos.size, { reduceOnly: true });
|
|
670
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 } });
|
|
671
689
|
}
|
|
672
690
|
catch (err) {
|
|
@@ -675,7 +693,7 @@ export class FundingArbStrategy {
|
|
|
675
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 } });
|
|
676
694
|
}
|
|
677
695
|
try {
|
|
678
|
-
const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size);
|
|
696
|
+
const closeShortResult = await shortAdapter.marketOrder(pos.symbol, "buy", pos.size, { reduceOnly: true });
|
|
679
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 } });
|
|
680
698
|
}
|
|
681
699
|
catch (err) {
|
|
@@ -741,7 +759,7 @@ export class FundingArbStrategy {
|
|
|
741
759
|
// Step 3: Close perp short
|
|
742
760
|
try {
|
|
743
761
|
const perpSymbol = this.getPerpSymbol(pos.symbol, pos.shortExchange);
|
|
744
|
-
await perpAdapter.marketOrder(perpSymbol, "buy", pos.size);
|
|
762
|
+
await perpAdapter.marketOrder(perpSymbol, "buy", pos.size, { reduceOnly: true });
|
|
745
763
|
ctx.log(` [ARB] Closed perp short ${pos.size} ${perpSymbol}`);
|
|
746
764
|
}
|
|
747
765
|
catch (err) {
|
package/dist/cache.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Dashboard and CLI share cached data via /tmp/perp-cli-cache/.
|
|
5
5
|
* Writes are atomic (write to .tmp then rename) to prevent partial reads.
|
|
6
6
|
*/
|
|
7
|
-
export declare const TTL_ACCOUNT =
|
|
7
|
+
export declare const TTL_ACCOUNT = 15000;
|
|
8
8
|
export declare const TTL_MARKET = 30000;
|
|
9
9
|
export declare function getCached<T>(key: string): T | null;
|
|
10
10
|
export declare function setCached<T>(key: string, data: T, ttlMs: number): void;
|
package/dist/cache.js
CHANGED
|
@@ -9,7 +9,7 @@ import { join } from "path";
|
|
|
9
9
|
import { tmpdir } from "os";
|
|
10
10
|
const CACHE_DIR = join(tmpdir(), "perp-cli-cache");
|
|
11
11
|
// TTL presets
|
|
12
|
-
export const TTL_ACCOUNT =
|
|
12
|
+
export const TTL_ACCOUNT = 15_000; // 15s — balance, positions, orders (ticks are 30s; 15s ensures same-tick dedup)
|
|
13
13
|
export const TTL_MARKET = 30_000; // 30s — funding rates, prices, markets
|
|
14
14
|
let dirReady = false;
|
|
15
15
|
function ensureDir() {
|
|
@@ -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
|
|
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;
|
package/dist/exchanges/aster.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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();
|