perp-cli 0.6.3 → 0.7.1
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 +7 -2
- package/dist/api/public/hyperliquid.d.ts +2 -2
- package/dist/api/public/hyperliquid.js +0 -10
- package/dist/api/public/lighter.d.ts +2 -1
- package/dist/api/public/pacifica.d.ts +2 -1
- package/dist/arb/index.d.ts +7 -4
- package/dist/arb/index.js +5 -4
- package/dist/arb/sizing.d.ts +1 -1
- package/dist/arb/state.d.ts +1 -1
- package/dist/arb/state.js +4 -3
- package/dist/arb/utils.d.ts +0 -10
- package/dist/arb/utils.js +0 -42
- package/dist/bot/apex/guard.d.ts +26 -0
- package/dist/bot/apex/guard.js +86 -0
- package/dist/bot/apex/index.d.ts +6 -0
- package/dist/bot/apex/index.js +4 -0
- package/dist/bot/apex/pulse.d.ts +22 -0
- package/dist/bot/apex/pulse.js +107 -0
- package/dist/bot/apex/radar.d.ts +17 -0
- package/dist/bot/apex/radar.js +150 -0
- package/dist/bot/conditions.d.ts +1 -1
- package/dist/bot/config.d.ts +5 -1
- package/dist/bot/config.js +1 -1
- package/dist/bot/engine.d.ts +23 -2
- package/dist/bot/engine.js +296 -351
- package/dist/bot/index.d.ts +16 -0
- package/dist/bot/index.js +10 -0
- package/dist/bot/indicators.d.ts +29 -0
- package/dist/bot/indicators.js +144 -0
- package/dist/bot/reflect.d.ts +34 -0
- package/dist/bot/reflect.js +155 -0
- package/dist/bot/strategies/aggressive-taker.d.ts +36 -0
- package/dist/bot/strategies/aggressive-taker.js +125 -0
- package/dist/bot/strategies/apex-strategy.d.ts +36 -0
- package/dist/bot/strategies/apex-strategy.js +185 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +27 -0
- package/dist/bot/strategies/avellaneda-mm.js +111 -0
- package/dist/bot/strategies/basis-arb.d.ts +27 -0
- package/dist/bot/strategies/basis-arb.js +97 -0
- package/dist/bot/strategies/claude-agent.d.ts +31 -0
- package/dist/bot/strategies/claude-agent.js +155 -0
- package/dist/bot/strategies/dca-strategy.d.ts +25 -0
- package/dist/bot/strategies/dca-strategy.js +59 -0
- package/dist/bot/strategies/engine-mm.d.ts +29 -0
- package/dist/bot/strategies/engine-mm.js +141 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-strategy.js +168 -0
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +222 -0
- package/dist/bot/strategies/funding-auto.d.ts +1 -0
- package/dist/bot/strategies/funding-auto.js +572 -0
- package/dist/bot/strategies/grid-mm.d.ts +25 -0
- package/dist/bot/strategies/grid-mm.js +99 -0
- package/dist/bot/strategies/grid-strategy.d.ts +39 -0
- package/dist/bot/strategies/grid-strategy.js +176 -0
- package/dist/bot/strategies/hedge-agent.d.ts +25 -0
- package/dist/bot/strategies/hedge-agent.js +79 -0
- package/dist/bot/strategies/liquidation-mm.d.ts +25 -0
- package/dist/bot/strategies/liquidation-mm.js +108 -0
- package/dist/bot/strategies/mean-reversion.d.ts +35 -0
- package/dist/bot/strategies/mean-reversion.js +118 -0
- package/dist/bot/strategies/momentum-breakout.d.ts +36 -0
- package/dist/bot/strategies/momentum-breakout.js +159 -0
- package/dist/bot/strategies/regime-mm.d.ts +25 -0
- package/dist/bot/strategies/regime-mm.js +111 -0
- package/dist/bot/strategies/rfq-agent.d.ts +25 -0
- package/dist/bot/strategies/rfq-agent.js +71 -0
- package/dist/bot/strategies/simple-mm.d.ts +25 -0
- package/dist/bot/strategies/simple-mm.js +76 -0
- package/dist/bot/strategy-registry.d.ts +9 -0
- package/dist/bot/strategy-registry.js +14 -0
- package/dist/bot/strategy-types.d.ts +64 -0
- package/dist/bot/strategy-types.js +5 -0
- package/dist/bot/trade-journal.d.ts +27 -0
- package/dist/bot/trade-journal.js +53 -0
- package/dist/bot/tui/BotDashboard.d.ts +49 -0
- package/dist/bot/tui/BotDashboard.js +93 -0
- package/dist/bot/tui/index.d.ts +6 -0
- package/dist/bot/tui/index.js +8 -0
- package/dist/bridge-engine.d.ts +0 -1
- package/dist/bridge-engine.js +0 -1
- package/dist/cache.d.ts +0 -1
- package/dist/cache.js +0 -1
- package/dist/commands/account.d.ts +1 -1
- package/dist/commands/account.js +106 -75
- package/dist/commands/agent.d.ts +1 -1
- package/dist/commands/arb/index.d.ts +1 -1
- package/dist/commands/arb/index.js +1 -1
- package/dist/commands/arb-auto.d.ts +1 -1
- package/dist/commands/arb-auto.js +1 -1
- package/dist/commands/arb.d.ts +1 -1
- package/dist/commands/bot.d.ts +1 -1
- package/dist/commands/bot.js +198 -14
- package/dist/commands/dashboard.d.ts +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/funds.d.ts +1 -1
- package/dist/commands/funds.js +33 -20
- package/dist/commands/history.d.ts +1 -1
- package/dist/commands/history.js +5 -4
- package/dist/commands/manage.d.ts +2 -3
- package/dist/commands/manage.js +11 -18
- package/dist/commands/market.d.ts +1 -1
- package/dist/commands/market.js +7 -7
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/rebalance.d.ts +1 -1
- package/dist/commands/rebalance.js +8 -9
- package/dist/commands/risk.d.ts +1 -1
- package/dist/commands/settings.d.ts +1 -1
- package/dist/commands/settings.js +3 -1
- package/dist/commands/trade.d.ts +1 -1
- package/dist/commands/trade.js +39 -52
- package/dist/commands/wallet.d.ts +0 -2
- package/dist/commands/wallet.js +0 -5
- package/dist/constants.d.ts +0 -3
- package/dist/constants.js +0 -4
- package/dist/cross-chain-margin.d.ts +1 -1
- package/dist/dashboard/index.d.ts +4 -0
- package/dist/dashboard/index.js +3 -0
- package/dist/dashboard/server.d.ts +1 -1
- package/dist/dashboard/server.js +1 -1
- package/dist/dashboard/ws-feeds.d.ts +1 -1
- package/dist/event-stream.d.ts +1 -1
- package/dist/exchanges/aster.d.ts +58 -0
- package/dist/exchanges/aster.js +409 -0
- package/dist/exchanges/capabilities.d.ts +74 -0
- package/dist/exchanges/capabilities.js +44 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
- package/dist/exchanges/hyperliquid-spot.js +20 -12
- package/dist/exchanges/hyperliquid.d.ts +8 -2
- package/dist/exchanges/hyperliquid.js +21 -6
- package/dist/exchanges/index.d.ts +11 -0
- package/dist/exchanges/index.js +10 -0
- package/dist/exchanges/interface.d.ts +3 -0
- package/dist/exchanges/lighter-spot.d.ts +13 -0
- package/dist/exchanges/lighter-spot.js +98 -26
- package/dist/exchanges/lighter.d.ts +9 -3
- package/dist/exchanges/lighter.js +18 -8
- package/dist/exchanges/nado.d.ts +52 -0
- package/dist/exchanges/nado.js +223 -0
- package/dist/exchanges/pacifica.d.ts +7 -1
- package/dist/exchanges/pacifica.js +17 -2
- package/dist/exchanges/registry.d.ts +13 -0
- package/dist/exchanges/registry.js +37 -0
- package/dist/exchanges/spot-interface.d.ts +9 -0
- package/dist/funding/rates.js +1 -1
- package/dist/index.js +2 -6
- package/dist/jobs.js +1 -1
- package/dist/liquidity.d.ts +1 -2
- package/dist/liquidity.js +12 -5
- package/dist/mcp-server.js +4 -2
- package/dist/pacifica/constants.d.ts +0 -3
- package/dist/pacifica/constants.js +0 -4
- package/dist/pacifica/types/account.d.ts +0 -8
- package/dist/pacifica/types/lake.d.ts +0 -6
- package/dist/pacifica/types/position.d.ts +0 -12
- package/dist/pacifica/types/ws.d.ts +0 -24
- package/dist/pacifica/types/ws.js +1 -41
- package/dist/plan-executor.d.ts +1 -1
- package/dist/rebalance.d.ts +1 -7
- package/dist/rebalance.js +0 -6
- package/dist/risk.d.ts +1 -1
- package/dist/risk.js +3 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/strategies/dca.d.ts +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +193 -181
- package/dist/strategies/grid.d.ts +1 -1
- package/dist/strategies/grid.js +1 -1
- package/dist/strategies/index.d.ts +12 -0
- package/dist/strategies/index.js +7 -0
- package/dist/strategies/split-order.d.ts +1 -1
- package/dist/strategies/trailing-stop.d.ts +1 -1
- package/dist/strategies/twap.d.ts +1 -1
- package/dist/trade-validator.d.ts +1 -1
- package/package.json +7 -1
- package/skills/perp-cli/SKILL.md +20 -0
- package/skills/perp-cli/references/commands.md +22 -8
package/dist/commands/market.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { PacificaAdapter } from "../exchanges/pacifica.js";
|
|
2
|
-
import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
|
|
3
1
|
import { makeTable, formatUsd, formatPercent, printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
|
|
4
2
|
import chalk from "chalk";
|
|
3
|
+
import { hasPacificaSdk, isDexCapable } from "../exchanges/capabilities.js";
|
|
5
4
|
const EXCHANGES = ["pacifica", "hyperliquid", "lighter"];
|
|
6
5
|
export function registerMarketCommands(program, getAdapter, isJson, getAdapterForExchange) {
|
|
7
6
|
const market = program.command("market").description("Market data commands");
|
|
@@ -30,7 +29,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
30
29
|
const adapter = await getAdapterForExchange(ex);
|
|
31
30
|
grouped[ex] = await adapter.getMarkets();
|
|
32
31
|
// --hip3: fetch HIP-3 dex markets
|
|
33
|
-
if (opts.hip3 && ex === "hyperliquid" && adapter
|
|
32
|
+
if (opts.hip3 && ex === "hyperliquid" && isDexCapable(adapter)) {
|
|
34
33
|
const dexes = await adapter.listDeployedDexes();
|
|
35
34
|
await Promise.allSettled(dexes.map(async (d) => {
|
|
36
35
|
const dexAdapter = Object.create(adapter);
|
|
@@ -67,7 +66,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
67
66
|
const markets = await adapter.getMarkets();
|
|
68
67
|
// --hip3: fetch HIP-3 dex markets
|
|
69
68
|
const hip3 = {};
|
|
70
|
-
if (opts.hip3 && adapter
|
|
69
|
+
if (opts.hip3 && isDexCapable(adapter)) {
|
|
71
70
|
const dexes = await adapter.listDeployedDexes();
|
|
72
71
|
await Promise.allSettled(dexes.map(async (d) => {
|
|
73
72
|
const dexAdapter = Object.create(adapter);
|
|
@@ -127,8 +126,9 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
127
126
|
return;
|
|
128
127
|
}
|
|
129
128
|
const adapter = await getAdapter();
|
|
130
|
-
if (adapter
|
|
131
|
-
const
|
|
129
|
+
if (hasPacificaSdk(adapter)) {
|
|
130
|
+
const sdk = adapter.sdk;
|
|
131
|
+
const prices = await sdk.getPrices();
|
|
132
132
|
if (isJson())
|
|
133
133
|
return printJson(jsonOk(prices));
|
|
134
134
|
const rows = prices.map((p) => [
|
|
@@ -351,7 +351,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
351
351
|
.action(async () => {
|
|
352
352
|
await withJsonErrors(isJson(), async () => {
|
|
353
353
|
const adapter = await getAdapter();
|
|
354
|
-
if (!(adapter
|
|
354
|
+
if (!(isDexCapable(adapter))) {
|
|
355
355
|
if (isJson())
|
|
356
356
|
return printJson(jsonError("INVALID_EXCHANGE", "HIP-3 dexes are only available on Hyperliquid. Use -e hyperliquid."));
|
|
357
357
|
console.error(chalk.red("\n HIP-3 dexes are only available on Hyperliquid. Use -e hyperliquid.\n"));
|
package/dist/commands/plan.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import type { ExchangeAdapter } from "../exchanges/
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
3
|
export declare function registerPlanCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean): void;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import type { ExchangeAdapter } from "../exchanges/
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
3
|
export declare function registerRebalanceCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
|
|
@@ -229,29 +229,28 @@ export function registerRebalanceCommands(program, getAdapterForExchange, isJson
|
|
|
229
229
|
* Execute a withdrawal from a source exchange.
|
|
230
230
|
*/
|
|
231
231
|
async function executeWithdraw(move, adapters) {
|
|
232
|
+
const { hasPacificaSdk, isWithdrawCapable, hasEvmAddress } = await import("../exchanges/capabilities.js");
|
|
232
233
|
const adapter = adapters.get(move.from);
|
|
233
234
|
if (!adapter)
|
|
234
235
|
throw new Error(`No adapter for ${move.from}`);
|
|
235
|
-
switch (
|
|
236
|
+
switch (adapter.name) {
|
|
236
237
|
case "pacifica": {
|
|
237
|
-
|
|
238
|
-
if (!(adapter instanceof PacificaAdapter))
|
|
238
|
+
if (!hasPacificaSdk(adapter))
|
|
239
239
|
throw new Error("Invalid adapter");
|
|
240
|
-
|
|
240
|
+
const sdk = adapter.sdk;
|
|
241
|
+
await sdk.withdraw({ amount: String(move.amount), dest_address: adapter.publicKey }, adapter.publicKey, adapter.signer);
|
|
241
242
|
break;
|
|
242
243
|
}
|
|
243
244
|
case "hyperliquid": {
|
|
244
|
-
|
|
245
|
-
if (!(adapter instanceof HyperliquidAdapter))
|
|
245
|
+
if (!isWithdrawCapable(adapter) || !hasEvmAddress(adapter))
|
|
246
246
|
throw new Error("Invalid adapter");
|
|
247
247
|
await adapter.withdraw(String(move.amount), adapter.address);
|
|
248
248
|
break;
|
|
249
249
|
}
|
|
250
250
|
case "lighter": {
|
|
251
|
-
|
|
252
|
-
if (!(adapter instanceof LighterAdapter))
|
|
251
|
+
if (!isWithdrawCapable(adapter))
|
|
253
252
|
throw new Error("Invalid adapter");
|
|
254
|
-
await adapter.withdraw(move.amount);
|
|
253
|
+
await adapter.withdraw(String(move.amount), "");
|
|
255
254
|
break;
|
|
256
255
|
}
|
|
257
256
|
default:
|
package/dist/commands/risk.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import type { ExchangeAdapter } from "../exchanges/
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
3
|
/** Run health check and return results + render. Exported for use by status --health. */
|
|
4
4
|
export declare function runHealthCheck(isJson: () => boolean): Promise<void>;
|
|
5
5
|
export declare function registerRiskCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import type { ExchangeAdapter } from "../exchanges/
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
3
|
export declare function registerSettingsCommands(program: Command, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { printJson, jsonOk, withJsonErrors } from "../utils.js";
|
|
2
|
+
import { printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
|
|
3
3
|
import { loadSettings, saveSettings } from "../settings.js";
|
|
4
4
|
import { ENV_FILE, loadEnvFile, setEnvVar, EXCHANGE_ENV_MAP, validateKey } from "./init.js";
|
|
5
5
|
// Resolve exchange alias → env var key
|
|
@@ -80,6 +80,8 @@ export function registerSettingsCommands(program, isJson, getAdapterForExchange)
|
|
|
80
80
|
console.log(chalk.yellow("\n Referrals disabled. No codes will be sent.\n"));
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
|
+
if (isJson())
|
|
84
|
+
return printJson(jsonError("INVALID_ARGS", `Invalid action "${action}". Usage: perp settings referrals <on|off>`));
|
|
83
85
|
console.error(chalk.red(`\n Usage: perp settings referrals <on|off>\n`));
|
|
84
86
|
}
|
|
85
87
|
});
|
package/dist/commands/trade.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import type { ExchangeAdapter } from "../exchanges/
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
3
|
export declare function registerTradeCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, isDryRun?: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;
|
package/dist/commands/trade.js
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
|
-
import { PacificaAdapter } from "../exchanges/pacifica.js";
|
|
2
|
-
import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
|
|
3
|
-
import { LighterAdapter } from "../exchanges/lighter.js";
|
|
4
1
|
import { printJson, errorAndExit, withJsonErrors, jsonOk, jsonError, symbolMatch, formatUsd } from "../utils.js";
|
|
5
2
|
import { logExecution } from "../execution-log.js";
|
|
6
3
|
import { validateTrade } from "../trade-validator.js";
|
|
7
4
|
import { generateClientId, logClientId, isOrderDuplicate } from "../client-id-tracker.js";
|
|
8
5
|
import { smartOrder } from "../smart-order.js";
|
|
9
6
|
import chalk from "chalk";
|
|
10
|
-
|
|
11
|
-
if (!(adapter instanceof PacificaAdapter))
|
|
12
|
-
throw new Error("This command requires --exchange pacifica");
|
|
13
|
-
return adapter;
|
|
14
|
-
}
|
|
7
|
+
import { hasPacificaSdk, isTwapCapable, isTriggerOrderCapable, isQueryOrderCapable } from "../exchanges/capabilities.js";
|
|
15
8
|
export function registerTradeCommands(program, getAdapter, isJson, isDryRun = () => false, getAdapterForExchange) {
|
|
16
9
|
/** Guard: if --dry-run is active, log the intended action and return without executing. */
|
|
17
10
|
function dryRunGuard(action, details) {
|
|
@@ -130,7 +123,6 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
130
123
|
if (isJson())
|
|
131
124
|
return printJson(jsonOk(clientId ? { ...result, clientOrderId: clientId } : result));
|
|
132
125
|
console.log(chalk.green(`\n Market ${s.toUpperCase()} ${size} ${symbol.toUpperCase()} placed on ${adapter.name}.${opts.smart ? " (smart)" : ""}${clientId ? ` (id: ${clientId})` : ""}\n`));
|
|
133
|
-
printJson(jsonOk(result));
|
|
134
126
|
});
|
|
135
127
|
// Shortcuts: trade buy / trade sell
|
|
136
128
|
trade
|
|
@@ -170,7 +162,6 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
170
162
|
if (isJson())
|
|
171
163
|
return printJson(jsonOk(clientId ? { ...result, clientOrderId: clientId } : result));
|
|
172
164
|
console.log(chalk.green(`\n Market BUY ${size} ${symbol.toUpperCase()} placed on ${adapter.name}.${opts.smart ? " (smart)" : ""}\n`));
|
|
173
|
-
printJson(jsonOk(result));
|
|
174
165
|
});
|
|
175
166
|
trade
|
|
176
167
|
.command("sell <symbol> <size>")
|
|
@@ -209,7 +200,6 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
209
200
|
if (isJson())
|
|
210
201
|
return printJson(jsonOk(clientId ? { ...result, clientOrderId: clientId } : result));
|
|
211
202
|
console.log(chalk.green(`\n Market SELL ${size} ${symbol.toUpperCase()} placed on ${adapter.name}.${opts.smart ? " (smart)" : ""}\n`));
|
|
212
|
-
printJson(jsonOk(result));
|
|
213
203
|
});
|
|
214
204
|
// ── Split Order (orderbook-aware) ──
|
|
215
205
|
trade
|
|
@@ -227,10 +217,9 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
227
217
|
errorAndExit("Side must be 'buy' or 'sell'");
|
|
228
218
|
if (isNaN(totalUsd) || totalUsd <= 0)
|
|
229
219
|
errorAndExit("USD amount must be > 0");
|
|
230
|
-
const exchange = program.opts().exchange ?? "unknown";
|
|
231
|
-
if (dryRunGuard("split-order", { exchange, symbol: sym, side: orderSide, totalUsd, ...opts }))
|
|
232
|
-
return;
|
|
233
220
|
const adapter = await getAdapter();
|
|
221
|
+
if (dryRunGuard("split-order", { exchange: adapter.name, symbol: sym, side: orderSide, totalUsd, ...opts }))
|
|
222
|
+
return;
|
|
234
223
|
const { runSplitOrder } = await import("../strategies/split-order.js");
|
|
235
224
|
const result = await runSplitOrder(adapter, {
|
|
236
225
|
symbol: sym,
|
|
@@ -308,7 +297,6 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
308
297
|
if (isJson())
|
|
309
298
|
return printJson(jsonOk(clientId ? { ...result, clientOrderId: clientId } : result));
|
|
310
299
|
console.log(chalk.green(`\n Limit ${s.toUpperCase()} ${size} ${symbol.toUpperCase()} @ $${price} placed on ${adapter.name}.${clientId ? ` (id: ${clientId})` : ""}\n`));
|
|
311
|
-
printJson(jsonOk(result));
|
|
312
300
|
});
|
|
313
301
|
trade
|
|
314
302
|
.command("cancel <symbolOrOrderId> [orderId]")
|
|
@@ -439,23 +427,12 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
439
427
|
return;
|
|
440
428
|
}
|
|
441
429
|
const adapter = await getAdapter();
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
const { runTWAP } = await import("../strategies/twap.js");
|
|
445
|
-
const result = await runTWAP(adapter, {
|
|
446
|
-
symbol: symbol.toUpperCase(),
|
|
447
|
-
side: s,
|
|
448
|
-
totalSize: parseFloat(size),
|
|
449
|
-
durationSec: parseInt(duration),
|
|
450
|
-
slices: opts.slices ? parseInt(opts.slices) : undefined,
|
|
451
|
-
});
|
|
452
|
-
if (isJson())
|
|
453
|
-
return printJson(jsonOk(result));
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
430
|
+
// Native TWAP: Pacifica (SDK) or Hyperliquid (twapOrder)
|
|
431
|
+
// Fallback: client-side TWAP for any exchange without native support
|
|
456
432
|
let result;
|
|
457
|
-
if (adapter
|
|
458
|
-
|
|
433
|
+
if (hasPacificaSdk(adapter)) {
|
|
434
|
+
const sdk = adapter.sdk;
|
|
435
|
+
result = await sdk.createTWAP({
|
|
459
436
|
symbol: symbol.toUpperCase(),
|
|
460
437
|
amount: size,
|
|
461
438
|
side: s === "buy" ? "bid" : "ask",
|
|
@@ -464,27 +441,37 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
464
441
|
duration_in_seconds: parseInt(duration),
|
|
465
442
|
}, adapter.publicKey, adapter.signer);
|
|
466
443
|
}
|
|
467
|
-
else if (adapter
|
|
444
|
+
else if (isTwapCapable(adapter)) {
|
|
468
445
|
const minutes = parseInt(duration);
|
|
469
446
|
if (minutes < 5 || minutes > 1440)
|
|
470
447
|
errorAndExit("HL TWAP duration must be 5-1440 minutes");
|
|
471
448
|
result = await adapter.twapOrder(symbol.toUpperCase(), s, size, minutes, { reduceOnly: opts.reduceOnly });
|
|
472
449
|
}
|
|
473
450
|
else {
|
|
474
|
-
|
|
451
|
+
// Client-side TWAP (foreground) for exchanges without native TWAP
|
|
452
|
+
const { runTWAP } = await import("../strategies/twap.js");
|
|
453
|
+
const twapResult = await runTWAP(adapter, {
|
|
454
|
+
symbol: symbol.toUpperCase(),
|
|
455
|
+
side: s,
|
|
456
|
+
totalSize: parseFloat(size),
|
|
457
|
+
durationSec: parseInt(duration),
|
|
458
|
+
slices: opts.slices ? parseInt(opts.slices) : undefined,
|
|
459
|
+
});
|
|
460
|
+
if (isJson())
|
|
461
|
+
return printJson(jsonOk(twapResult));
|
|
462
|
+
return;
|
|
475
463
|
}
|
|
476
464
|
if (isJson())
|
|
477
465
|
return printJson(jsonOk(result));
|
|
478
466
|
console.log(chalk.green(`\n TWAP ${s.toUpperCase()} ${size} ${symbol.toUpperCase()} over ${duration} placed on ${adapter.name}.\n`));
|
|
479
|
-
printJson(jsonOk(result));
|
|
480
467
|
});
|
|
481
468
|
// === Stop / Trigger orders — Pacifica + Hyperliquid ===
|
|
482
469
|
trade
|
|
483
|
-
.command("stop <symbol> <side> <
|
|
470
|
+
.command("stop <symbol> <side> <size> <stopPrice>")
|
|
484
471
|
.description("Place a stop order")
|
|
485
472
|
.option("--limit-price <price>", "Limit price (makes it stop-limit)")
|
|
486
473
|
.option("--reduce-only", "Reduce only order")
|
|
487
|
-
.action(async (symbol, side,
|
|
474
|
+
.action(async (symbol, side, size, stopPrice, opts) => {
|
|
488
475
|
const s = side.toLowerCase();
|
|
489
476
|
if (s !== "buy" && s !== "sell")
|
|
490
477
|
errorAndExit("Side must be buy or sell");
|
|
@@ -503,7 +490,6 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
503
490
|
if (isJson())
|
|
504
491
|
return printJson(jsonOk(result));
|
|
505
492
|
console.log(chalk.green(`\n Stop order placed on ${adapter.name}.\n`));
|
|
506
|
-
printJson(jsonOk(result));
|
|
507
493
|
});
|
|
508
494
|
// === TP/SL — Pacifica + Hyperliquid ===
|
|
509
495
|
trade
|
|
@@ -522,7 +508,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
522
508
|
const adapter = await getAdapter();
|
|
523
509
|
if (dryRunGuard("tpsl", { exchange: adapter.name, symbol: symbol.toUpperCase(), side: s, tp: opts.tp ?? "none", sl: opts.sl ?? "none" }))
|
|
524
510
|
return;
|
|
525
|
-
if (adapter
|
|
511
|
+
if (hasPacificaSdk(adapter)) {
|
|
526
512
|
// TP/SL side is opposite of position: LONG position → "ask" to close
|
|
527
513
|
const params = {
|
|
528
514
|
symbol: symbol.toUpperCase(),
|
|
@@ -532,12 +518,13 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
532
518
|
params.take_profit = { stop_price: opts.tp, limit_price: opts.tpLimit };
|
|
533
519
|
if (opts.sl)
|
|
534
520
|
params.stop_loss = { stop_price: opts.sl };
|
|
535
|
-
const
|
|
521
|
+
const sdk = adapter.sdk;
|
|
522
|
+
const result = await sdk.setTPSL(params, adapter.publicKey, adapter.signer);
|
|
536
523
|
if (isJson())
|
|
537
524
|
return printJson(jsonOk(result));
|
|
538
525
|
console.log(chalk.green(`\n TP/SL set for ${symbol.toUpperCase()} on Pacifica.\n`));
|
|
539
526
|
}
|
|
540
|
-
else if (adapter
|
|
527
|
+
else if (isTriggerOrderCapable(adapter)) {
|
|
541
528
|
// HL uses trigger orders for TP/SL
|
|
542
529
|
const results = [];
|
|
543
530
|
const posSize = opts.size || "0"; // 0 = full position via positionTpsl grouping
|
|
@@ -552,8 +539,8 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
552
539
|
return printJson(jsonOk(results));
|
|
553
540
|
console.log(chalk.green(`\n TP/SL set for ${symbol.toUpperCase()} on Hyperliquid.\n`));
|
|
554
541
|
}
|
|
555
|
-
else
|
|
556
|
-
//
|
|
542
|
+
else {
|
|
543
|
+
// Fallback: use stopOrder for TP/SL (Lighter and others)
|
|
557
544
|
const closeSide = s === "buy" ? "sell" : "buy"; // Close opposite side
|
|
558
545
|
const results = [];
|
|
559
546
|
if (opts.tp) {
|
|
@@ -564,10 +551,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
564
551
|
}
|
|
565
552
|
if (isJson())
|
|
566
553
|
return printJson(jsonOk(results));
|
|
567
|
-
console.log(chalk.green(`\n TP/SL set for ${symbol.toUpperCase()} on
|
|
568
|
-
}
|
|
569
|
-
else {
|
|
570
|
-
errorAndExit("TP/SL requires --exchange pacifica, hyperliquid, or lighter");
|
|
554
|
+
console.log(chalk.green(`\n TP/SL set for ${symbol.toUpperCase()} on ${adapter.name}.\n`));
|
|
571
555
|
}
|
|
572
556
|
});
|
|
573
557
|
// === Scaled Take-Profit (분할익절) ===
|
|
@@ -672,8 +656,10 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
672
656
|
.description("Cancel a stop order (Pacifica)")
|
|
673
657
|
.action(async (symbol, stopOrderId) => {
|
|
674
658
|
const adapter = await getAdapter();
|
|
675
|
-
|
|
676
|
-
|
|
659
|
+
if (!hasPacificaSdk(adapter))
|
|
660
|
+
throw new Error("This command requires --exchange pacifica");
|
|
661
|
+
const sdk = adapter.sdk;
|
|
662
|
+
const result = await sdk.cancelStopOrder({ symbol: symbol.toUpperCase(), order_id: Number(stopOrderId) }, adapter.publicKey, adapter.signer);
|
|
677
663
|
if (isJson())
|
|
678
664
|
return printJson(jsonOk(result));
|
|
679
665
|
console.log(chalk.green(`\n Stop order ${stopOrderId} cancelled.\n`));
|
|
@@ -684,10 +670,11 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
684
670
|
.action(async (symbol, twapOrderId) => {
|
|
685
671
|
const adapter = await getAdapter();
|
|
686
672
|
let result;
|
|
687
|
-
if (adapter
|
|
688
|
-
|
|
673
|
+
if (hasPacificaSdk(adapter)) {
|
|
674
|
+
const sdk = adapter.sdk;
|
|
675
|
+
result = await sdk.cancelTWAP({ symbol: symbol.toUpperCase(), twap_order_id: Number(twapOrderId) }, adapter.publicKey, adapter.signer);
|
|
689
676
|
}
|
|
690
|
-
else if (adapter
|
|
677
|
+
else if (isTwapCapable(adapter)) {
|
|
691
678
|
result = await adapter.twapCancel(symbol.toUpperCase(), Number(twapOrderId));
|
|
692
679
|
}
|
|
693
680
|
else {
|
|
@@ -903,7 +890,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
903
890
|
.action(async (orderId) => {
|
|
904
891
|
await withJsonErrors(isJson(), async () => {
|
|
905
892
|
const adapter = await getAdapter();
|
|
906
|
-
if (adapter
|
|
893
|
+
if (isQueryOrderCapable(adapter)) {
|
|
907
894
|
const result = await adapter.queryOrder(Number(orderId));
|
|
908
895
|
if (isJson())
|
|
909
896
|
return printJson(jsonOk(result));
|
|
@@ -13,6 +13,4 @@ export declare function getWalletSetupStatus(): {
|
|
|
13
13
|
export declare function getActiveWalletKey(exchange: string): string | null;
|
|
14
14
|
/** Get wallet key by name. Returns null if wallet doesn't exist. */
|
|
15
15
|
export declare function getWalletKeyByName(name: string): string | null;
|
|
16
|
-
/** Get all wallet names. */
|
|
17
|
-
export declare function getWalletNames(): string[];
|
|
18
16
|
export declare function registerWalletCommands(program: Command, isJson: () => boolean): void;
|
package/dist/commands/wallet.js
CHANGED
|
@@ -47,11 +47,6 @@ export function getWalletKeyByName(name) {
|
|
|
47
47
|
const store = loadStore();
|
|
48
48
|
return store.wallets[name]?.privateKey ?? null;
|
|
49
49
|
}
|
|
50
|
-
/** Get all wallet names. */
|
|
51
|
-
export function getWalletNames() {
|
|
52
|
-
const store = loadStore();
|
|
53
|
-
return Object.keys(store.wallets);
|
|
54
|
-
}
|
|
55
50
|
async function getSolanaBalances(address, isTestnet) {
|
|
56
51
|
const { Connection, PublicKey } = await import("@solana/web3.js");
|
|
57
52
|
const rpcUrl = isTestnet
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared constants for perp-cli.
|
|
3
3
|
*/
|
|
4
|
-
import { type ExchangeFees } from "./settings.js";
|
|
5
4
|
/** Default taker fee per exchange (as fraction, e.g. 0.00035 = 0.035%) */
|
|
6
5
|
export declare const TAKER_FEES: Record<string, number>;
|
|
7
6
|
/** Get taker fee for an exchange. Uses settings if available, falls back to defaults. */
|
|
8
7
|
export declare function getTakerFee(exchange: string): number;
|
|
9
8
|
/** Get maker fee for an exchange. Uses settings if available. */
|
|
10
9
|
export declare function getMakerFee(exchange: string): number;
|
|
11
|
-
/** Get full fee info for an exchange. */
|
|
12
|
-
export declare function getExchangeFees(exchange: string): ExchangeFees;
|
|
13
10
|
/** Default taker fee for single-exchange use (when exchange is unknown). */
|
|
14
11
|
export declare const DEFAULT_TAKER_FEE = 0.00035;
|
package/dist/constants.js
CHANGED
|
@@ -32,9 +32,5 @@ export function getMakerFee(exchange) {
|
|
|
32
32
|
catch { /* fallback */ }
|
|
33
33
|
return 0;
|
|
34
34
|
}
|
|
35
|
-
/** Get full fee info for an exchange. */
|
|
36
|
-
export function getExchangeFees(exchange) {
|
|
37
|
-
return { taker: getTakerFee(exchange), maker: getMakerFee(exchange) };
|
|
38
|
-
}
|
|
39
35
|
/** Default taker fee for single-exchange use (when exchange is unknown). */
|
|
40
36
|
export const DEFAULT_TAKER_FEE = 0.00035;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Per-exchange margin status checking
|
|
6
6
|
* - Auto-size computation based on orderbook depth and margin limits
|
|
7
7
|
*/
|
|
8
|
-
import type { ExchangeAdapter } from "./exchanges/
|
|
8
|
+
import type { ExchangeAdapter } from "./exchanges/index.js";
|
|
9
9
|
export interface ChainMarginStatus {
|
|
10
10
|
exchange: string;
|
|
11
11
|
chain: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Polls all configured exchange adapters and pushes updates to connected clients.
|
|
5
5
|
* Includes cross-exchange arb data: funding rate comparison + dex arb scan.
|
|
6
6
|
*/
|
|
7
|
-
import type { ExchangeAdapter, ExchangeBalance, ExchangePosition, ExchangeOrder, ExchangeMarketInfo } from "../exchanges/
|
|
7
|
+
import type { ExchangeAdapter, ExchangeBalance, ExchangePosition, ExchangeOrder, ExchangeMarketInfo } from "../exchanges/index.js";
|
|
8
8
|
export interface DashboardExchange {
|
|
9
9
|
name: string;
|
|
10
10
|
adapter: ExchangeAdapter;
|
package/dist/dashboard/server.js
CHANGED
|
@@ -318,7 +318,7 @@ export async function startDashboard(exchanges, opts = {}) {
|
|
|
318
318
|
}, { once: true });
|
|
319
319
|
}
|
|
320
320
|
return new Promise((resolve) => {
|
|
321
|
-
server.listen(port, async () => {
|
|
321
|
+
server.listen(port, "127.0.0.1", async () => {
|
|
322
322
|
// Start WS feeds (connects to exchange WS APIs)
|
|
323
323
|
await feedMgr.start();
|
|
324
324
|
// Start arb REST polling (30s cycle)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Falls back to REST polling if WS connection fails.
|
|
6
6
|
* Arb/market data stays on REST (cross-exchange aggregation, no single WS covers it).
|
|
7
7
|
*/
|
|
8
|
-
import type { ExchangeBalance, ExchangePosition, ExchangeOrder } from "../exchanges/
|
|
8
|
+
import type { ExchangeBalance, ExchangePosition, ExchangeOrder } from "../exchanges/index.js";
|
|
9
9
|
import type { DashboardExchange } from "./server.js";
|
|
10
10
|
export interface WsFeedState {
|
|
11
11
|
balance: ExchangeBalance;
|
package/dist/event-stream.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Streams normalized account events as NDJSON (one JSON per line).
|
|
5
5
|
* Uses poll-based diffing for cross-exchange compatibility.
|
|
6
6
|
*/
|
|
7
|
-
import type { ExchangeAdapter } from "./exchanges/
|
|
7
|
+
import type { ExchangeAdapter } from "./exchanges/index.js";
|
|
8
8
|
export type EventType = "position_opened" | "position_closed" | "position_updated" | "order_placed" | "order_filled" | "order_cancelled" | "balance_update" | "liquidation_warning" | "margin_call" | "heartbeat" | "error";
|
|
9
9
|
export interface StreamEvent {
|
|
10
10
|
type: EventType;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aster DEX adapter — Binance Futures API compatible REST wrapper.
|
|
3
|
+
* Docs: https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation
|
|
4
|
+
* Base: https://fapi.asterdex.com
|
|
5
|
+
*/
|
|
6
|
+
import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
|
|
7
|
+
export declare class AsterAdapter implements ExchangeAdapter {
|
|
8
|
+
readonly name = "aster";
|
|
9
|
+
readonly chain = "bnb";
|
|
10
|
+
readonly aliases: readonly ["ast"];
|
|
11
|
+
private _apiKey;
|
|
12
|
+
private _apiSecret;
|
|
13
|
+
private _baseUrl;
|
|
14
|
+
private _testnet;
|
|
15
|
+
constructor(apiKey?: string, apiSecret?: string, testnet?: boolean);
|
|
16
|
+
get isReadOnly(): boolean;
|
|
17
|
+
init(): Promise<void>;
|
|
18
|
+
getMarkets(): Promise<ExchangeMarketInfo[]>;
|
|
19
|
+
getOrderbook(symbol: string): Promise<{
|
|
20
|
+
bids: [string, string][];
|
|
21
|
+
asks: [string, string][];
|
|
22
|
+
}>;
|
|
23
|
+
getRecentTrades(symbol: string, limit?: number): Promise<ExchangeTrade[]>;
|
|
24
|
+
getFundingHistory(symbol: string, limit?: number): Promise<{
|
|
25
|
+
time: number;
|
|
26
|
+
rate: string;
|
|
27
|
+
price: string | null;
|
|
28
|
+
}[]>;
|
|
29
|
+
getKlines(symbol: string, interval: string, startTime: number, endTime: number): Promise<ExchangeKline[]>;
|
|
30
|
+
getBalance(): Promise<ExchangeBalance>;
|
|
31
|
+
getPositions(): Promise<ExchangePosition[]>;
|
|
32
|
+
getOpenOrders(): Promise<ExchangeOrder[]>;
|
|
33
|
+
getOrderHistory(limit?: number): Promise<ExchangeOrder[]>;
|
|
34
|
+
getTradeHistory(limit?: number): Promise<ExchangeTrade[]>;
|
|
35
|
+
getFundingPayments(limit?: number): Promise<ExchangeFundingPayment[]>;
|
|
36
|
+
marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
|
|
37
|
+
limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
|
|
38
|
+
reduceOnly?: boolean;
|
|
39
|
+
tif?: string;
|
|
40
|
+
}): Promise<unknown>;
|
|
41
|
+
editOrder(symbol: string, orderId: string, price: string, size: string): Promise<unknown>;
|
|
42
|
+
cancelOrder(symbol: string, orderId: string): Promise<unknown>;
|
|
43
|
+
cancelAllOrders(symbol?: string): Promise<unknown>;
|
|
44
|
+
setLeverage(symbol: string, leverage: number, marginMode?: "cross" | "isolated"): Promise<unknown>;
|
|
45
|
+
stopOrder(symbol: string, side: "buy" | "sell", size: string, triggerPrice: string, opts?: {
|
|
46
|
+
limitPrice?: string;
|
|
47
|
+
reduceOnly?: boolean;
|
|
48
|
+
}): Promise<unknown>;
|
|
49
|
+
withdraw(amount: string, _destination: string, _opts?: {
|
|
50
|
+
assetId?: number;
|
|
51
|
+
routeType?: number;
|
|
52
|
+
}): Promise<unknown>;
|
|
53
|
+
private _sign;
|
|
54
|
+
private _publicGet;
|
|
55
|
+
private _signedGet;
|
|
56
|
+
private _signedPost;
|
|
57
|
+
private _signedDelete;
|
|
58
|
+
}
|