perp-cli 0.12.17 → 0.14.0
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 +247 -27
- package/dist/_stderr-filter.js +0 -1
- package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
- package/dist/agent-wallet/lighter-keystore.js +92 -10
- package/dist/agent-wallet/passphrase.d.ts +1 -1
- package/dist/agent-wallet/passphrase.js +16 -3
- package/dist/agent-wallet/store.d.ts +37 -4
- package/dist/agent-wallet/store.js +132 -0
- package/dist/agent-wallet/venue-keystore.d.ts +14 -0
- package/dist/agent-wallet/venue-keystore.js +121 -0
- package/dist/api/public/hyperliquid.js +4 -4
- package/dist/api/public/lighter.js +5 -5
- package/dist/api/public/pacifica.d.ts +0 -1
- package/dist/api/public/pacifica.js +4 -13
- package/dist/api/public/urls.d.ts +9 -1
- package/dist/api/public/urls.js +35 -13
- package/dist/arb/file-lock.d.ts +11 -0
- package/dist/arb/file-lock.js +74 -0
- package/dist/arb/index.d.ts +2 -2
- package/dist/arb/index.js +2 -2
- package/dist/arb/pending-entry.d.ts +32 -0
- package/dist/arb/pending-entry.js +77 -0
- package/dist/arb/sizing.d.ts +6 -13
- package/dist/arb/sizing.js +12 -51
- package/dist/arb/state.d.ts +45 -12
- package/dist/arb/state.js +214 -42
- package/dist/arb/utils.d.ts +20 -6
- package/dist/arb/utils.js +43 -39
- package/dist/bot/conditions.d.ts +4 -4
- package/dist/bot/conditions.js +11 -6
- package/dist/bot/config.d.ts +17 -1
- package/dist/bot/config.js +48 -12
- package/dist/bot/engine.d.ts +61 -2
- package/dist/bot/engine.js +610 -144
- package/dist/bot/fill-size.d.ts +7 -0
- package/dist/bot/fill-size.js +18 -0
- package/dist/bot/index.d.ts +1 -1
- package/dist/bot/index.js +1 -1
- package/dist/bot/perp-order.d.ts +99 -0
- package/dist/bot/perp-order.js +299 -0
- package/dist/bot/presets.d.ts +7 -0
- package/dist/bot/presets.js +17 -0
- package/dist/bot/quarantine.d.ts +75 -0
- package/dist/bot/quarantine.js +190 -0
- package/dist/bot/quote-rules.d.ts +23 -0
- package/dist/bot/quote-rules.js +69 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
- package/dist/bot/strategies/avellaneda-mm.js +72 -70
- package/dist/bot/strategies/basis-arb.js +2 -0
- package/dist/bot/strategies/dca-strategy.d.ts +2 -0
- package/dist/bot/strategies/dca-strategy.js +5 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
- package/dist/bot/strategies/funding-arb-strategy.js +571 -380
- package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
- package/dist/bot/strategies/funding-arb-utils.js +159 -195
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +258 -47
- package/dist/bot/strategies/funding-auto.js +215 -194
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
- package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
- package/dist/bot/strategies/spot-perp-executor.js +319 -120
- package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
- package/dist/bot/strategies/spot-perp-state.js +70 -0
- package/dist/bot/strategy-types.d.ts +6 -0
- package/dist/bot/tui/BotDashboard.d.ts +7 -2
- package/dist/bot/tui/BotDashboard.js +6 -3
- package/dist/bridge-engine.d.ts +120 -19
- package/dist/bridge-engine.js +710 -422
- package/dist/bridge-signers.d.ts +54 -0
- package/dist/bridge-signers.js +161 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +11 -0
- package/dist/cli-flags.d.ts +50 -0
- package/dist/cli-flags.js +104 -0
- package/dist/cli-program.d.ts +35 -0
- package/dist/cli-program.js +286 -0
- package/dist/cli-spec.js +1 -1
- package/dist/client-id-tracker.js +11 -2
- package/dist/commands/account.d.ts +7 -1
- package/dist/commands/account.js +70 -92
- package/dist/commands/agent-import.d.ts +114 -0
- package/dist/commands/agent-import.js +794 -0
- package/dist/commands/agent.d.ts +232 -2
- package/dist/commands/agent.js +1858 -955
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/alerts.js +31 -28
- package/dist/commands/arb/index.d.ts +60 -1
- package/dist/commands/arb/index.js +381 -384
- package/dist/commands/arb-auto.d.ts +132 -4
- package/dist/commands/arb-auto.js +1474 -967
- package/dist/commands/arb.d.ts +3 -1
- package/dist/commands/arb.js +62 -30
- package/dist/commands/bot.js +127 -47
- package/dist/commands/bridge.d.ts +2 -1
- package/dist/commands/bridge.js +339 -192
- package/dist/commands/builder.d.ts +55 -0
- package/dist/commands/builder.js +387 -0
- package/dist/commands/funds-onchain.d.ts +32 -0
- package/dist/commands/funds-onchain.js +286 -0
- package/dist/commands/funds.d.ts +25 -0
- package/dist/commands/funds.js +939 -245
- package/dist/commands/history.js +72 -45
- package/dist/commands/init.d.ts +107 -15
- package/dist/commands/init.js +1116 -249
- package/dist/commands/jobs.d.ts +2 -1
- package/dist/commands/jobs.js +77 -15
- package/dist/commands/manage.js +136 -91
- package/dist/commands/market.d.ts +4 -1
- package/dist/commands/market.js +35 -32
- package/dist/commands/outcome.d.ts +10 -0
- package/dist/commands/outcome.js +396 -0
- package/dist/commands/plan.js +52 -12
- package/dist/commands/portfolio.d.ts +1 -0
- package/dist/commands/portfolio.js +60 -72
- package/dist/commands/rebalance.d.ts +2 -1
- package/dist/commands/rebalance.js +33 -43
- package/dist/commands/risk.js +46 -19
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +246 -51
- package/dist/commands/trade.d.ts +50 -0
- package/dist/commands/trade.js +1943 -695
- package/dist/commands/wallet.d.ts +30 -1
- package/dist/commands/wallet.js +316 -337
- package/dist/config.d.ts +60 -9
- package/dist/config.js +252 -82
- package/dist/constants.d.ts +29 -4
- package/dist/constants.js +62 -20
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +9 -6
- package/dist/dashboard/ui.js +6 -4
- package/dist/dashboard/ws-feeds.js +20 -8
- package/dist/dex-asset-map.js +5 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +194 -0
- package/dist/event-stream.js +75 -19
- package/dist/evm-master-tx.d.ts +59 -0
- package/dist/evm-master-tx.js +154 -0
- package/dist/exchanges/adapter-network.d.ts +10 -0
- package/dist/exchanges/adapter-network.js +10 -0
- package/dist/exchanges/arcus/adapter.d.ts +196 -0
- package/dist/exchanges/arcus/adapter.js +1092 -0
- package/dist/exchanges/arcus/agent.d.ts +55 -0
- package/dist/exchanges/arcus/agent.js +188 -0
- package/dist/exchanges/arcus/client.d.ts +63 -0
- package/dist/exchanges/arcus/client.js +285 -0
- package/dist/exchanges/arcus/public.d.ts +65 -0
- package/dist/exchanges/arcus/public.js +138 -0
- package/dist/exchanges/arcus/signing.d.ts +176 -0
- package/dist/exchanges/arcus/signing.js +288 -0
- package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
- package/dist/exchanges/arcus/spot-chain.js +241 -0
- package/dist/exchanges/arcus/spot-router.d.ts +177 -0
- package/dist/exchanges/arcus/spot-router.js +183 -0
- package/dist/exchanges/arcus/spot.d.ts +109 -0
- package/dist/exchanges/arcus/spot.js +645 -0
- package/dist/exchanges/aster-funds.d.ts +127 -0
- package/dist/exchanges/aster-funds.js +252 -0
- package/dist/exchanges/aster-nonce.d.ts +2 -0
- package/dist/exchanges/aster-nonce.js +107 -0
- package/dist/exchanges/aster-spot.d.ts +117 -0
- package/dist/exchanges/aster-spot.js +686 -0
- package/dist/exchanges/aster-typed-data.d.ts +133 -7
- package/dist/exchanges/aster-typed-data.js +121 -7
- package/dist/exchanges/aster.d.ts +219 -6
- package/dist/exchanges/aster.js +1023 -153
- package/dist/exchanges/builder-fee.d.ts +254 -0
- package/dist/exchanges/builder-fee.js +421 -0
- package/dist/exchanges/canonical-symbols.d.ts +32 -0
- package/dist/exchanges/canonical-symbols.js +96 -0
- package/dist/exchanges/capabilities.d.ts +7 -1
- package/dist/exchanges/capabilities.js +7 -0
- package/dist/exchanges/hl-signing.d.ts +165 -0
- package/dist/exchanges/hl-signing.js +158 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
- package/dist/exchanges/hyperliquid-outcome.js +646 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
- package/dist/exchanges/hyperliquid-spot.js +308 -117
- package/dist/exchanges/hyperliquid.d.ts +462 -57
- package/dist/exchanges/hyperliquid.js +1575 -449
- package/dist/exchanges/index.d.ts +6 -0
- package/dist/exchanges/index.js +4 -0
- package/dist/exchanges/interface.d.ts +63 -10
- package/dist/exchanges/interface.js +18 -3
- package/dist/exchanges/ioc-outcome.d.ts +44 -0
- package/dist/exchanges/ioc-outcome.js +57 -0
- package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
- package/dist/exchanges/legacy-trading-v2.js +940 -0
- package/dist/exchanges/lighter-public.d.ts +43 -0
- package/dist/exchanges/lighter-public.js +276 -0
- package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
- package/dist/exchanges/lighter-rh-deposit.js +185 -0
- package/dist/exchanges/lighter-signer.d.ts +123 -0
- package/dist/exchanges/lighter-signer.js +215 -0
- package/dist/exchanges/lighter-spot.d.ts +91 -13
- package/dist/exchanges/lighter-spot.js +595 -233
- package/dist/exchanges/lighter.d.ts +611 -39
- package/dist/exchanges/lighter.js +1890 -649
- package/dist/exchanges/ondo/adapter.d.ts +166 -0
- package/dist/exchanges/ondo/adapter.js +835 -0
- package/dist/exchanges/ondo/agent.d.ts +31 -0
- package/dist/exchanges/ondo/agent.js +101 -0
- package/dist/exchanges/ondo/client.d.ts +60 -0
- package/dist/exchanges/ondo/client.js +297 -0
- package/dist/exchanges/ondo/funds.d.ts +122 -0
- package/dist/exchanges/ondo/funds.js +290 -0
- package/dist/exchanges/ondo/public.d.ts +95 -0
- package/dist/exchanges/ondo/public.js +214 -0
- package/dist/exchanges/ondo/spot.d.ts +127 -0
- package/dist/exchanges/ondo/spot.js +392 -0
- package/dist/exchanges/outcome-interface.d.ts +140 -0
- package/dist/exchanges/outcome-interface.js +16 -0
- package/dist/exchanges/pacifica-spot.d.ts +83 -0
- package/dist/exchanges/pacifica-spot.js +324 -0
- package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
- package/dist/exchanges/pacifica-typed-data.js +57 -9
- package/dist/exchanges/pacifica.d.ts +288 -4
- package/dist/exchanges/pacifica.js +1013 -76
- package/dist/exchanges/precision.d.ts +47 -0
- package/dist/exchanges/precision.js +209 -0
- package/dist/exchanges/qfex/adapter.d.ts +341 -0
- package/dist/exchanges/qfex/adapter.js +1383 -0
- package/dist/exchanges/qfex/agent.d.ts +58 -0
- package/dist/exchanges/qfex/agent.js +188 -0
- package/dist/exchanges/qfex/client.d.ts +54 -0
- package/dist/exchanges/qfex/client.js +138 -0
- package/dist/exchanges/qfex/errors.d.ts +70 -0
- package/dist/exchanges/qfex/errors.js +236 -0
- package/dist/exchanges/qfex/public.d.ts +122 -0
- package/dist/exchanges/qfex/public.js +268 -0
- package/dist/exchanges/qfex/signing.d.ts +160 -0
- package/dist/exchanges/qfex/signing.js +283 -0
- package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
- package/dist/exchanges/qfex/ws-transport.js +798 -0
- package/dist/exchanges/rate-limiter.d.ts +12 -0
- package/dist/exchanges/rate-limiter.js +64 -0
- package/dist/exchanges/registry.d.ts +14 -2
- package/dist/exchanges/registry.js +20 -32
- package/dist/exchanges/slippage.d.ts +37 -0
- package/dist/exchanges/slippage.js +70 -0
- package/dist/exchanges/spot-factory.d.ts +166 -0
- package/dist/exchanges/spot-factory.js +431 -0
- package/dist/exchanges/spot-interface.d.ts +98 -4
- package/dist/exchanges/spot-interface.js +27 -1
- package/dist/exchanges/spot-result.d.ts +79 -0
- package/dist/exchanges/spot-result.js +273 -0
- package/dist/exchanges/trading-v2.d.ts +117 -0
- package/dist/exchanges/trading-v2.js +301 -0
- package/dist/exchanges/venue-types.d.ts +231 -0
- package/dist/exchanges/venue-types.js +87 -0
- package/dist/exchanges/venue.d.ts +211 -0
- package/dist/exchanges/venue.js +132 -0
- package/dist/exchanges/venues/arcus.d.ts +8 -0
- package/dist/exchanges/venues/arcus.js +109 -0
- package/dist/exchanges/venues/aster.d.ts +49 -0
- package/dist/exchanges/venues/aster.js +176 -0
- package/dist/exchanges/venues/create-helpers.d.ts +18 -0
- package/dist/exchanges/venues/create-helpers.js +199 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
- package/dist/exchanges/venues/hyperliquid.js +126 -0
- package/dist/exchanges/venues/index.d.ts +16 -0
- package/dist/exchanges/venues/index.js +26 -0
- package/dist/exchanges/venues/lighter-family.d.ts +92 -0
- package/dist/exchanges/venues/lighter-family.js +166 -0
- package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
- package/dist/exchanges/venues/lighter-rh.js +58 -0
- package/dist/exchanges/venues/lighter.d.ts +6 -0
- package/dist/exchanges/venues/lighter.js +55 -0
- package/dist/exchanges/venues/ondo.d.ts +34 -0
- package/dist/exchanges/venues/ondo.js +140 -0
- package/dist/exchanges/venues/pacifica.d.ts +8 -0
- package/dist/exchanges/venues/pacifica.js +95 -0
- package/dist/exchanges/venues/qfex.d.ts +28 -0
- package/dist/exchanges/venues/qfex.js +132 -0
- package/dist/execution-log.js +10 -2
- package/dist/funding/legacy-public.d.ts +31 -0
- package/dist/funding/legacy-public.js +369 -0
- package/dist/funding/normalize.d.ts +28 -16
- package/dist/funding/normalize.js +61 -29
- package/dist/funding/rates.d.ts +46 -11
- package/dist/funding/rates.js +228 -277
- package/dist/guardrail/perp-guardrail.js +0 -0
- package/dist/index.js +156 -684
- package/dist/jobs.d.ts +84 -6
- package/dist/jobs.js +239 -41
- package/dist/landing.d.ts +7 -2
- package/dist/landing.js +19 -3
- package/dist/mcp-server.d.ts +14 -4
- package/dist/mcp-server.js +820 -402
- package/dist/mcp-trade-guard.d.ts +16 -0
- package/dist/mcp-trade-guard.js +59 -0
- package/dist/pacifica/client.d.ts +41 -10
- package/dist/pacifica/client.js +167 -63
- package/dist/pacifica/constants.d.ts +0 -1
- package/dist/pacifica/constants.js +0 -5
- package/dist/pacifica/deposit.d.ts +30 -3
- package/dist/pacifica/deposit.js +119 -2
- package/dist/pacifica/faucet.d.ts +29 -0
- package/dist/pacifica/faucet.js +100 -0
- package/dist/pacifica/sign-tx.d.ts +15 -0
- package/dist/pacifica/sign-tx.js +41 -0
- package/dist/pacifica/types/account.d.ts +28 -4
- package/dist/pacifica/types/lake.d.ts +20 -5
- package/dist/pacifica/types/lake.js +4 -0
- package/dist/pacifica/types/market.d.ts +5 -0
- package/dist/pacifica/types/order.d.ts +4 -1
- package/dist/pacifica/types/position.d.ts +9 -1
- package/dist/plan-executor.d.ts +10 -5
- package/dist/plan-executor.js +165 -54
- package/dist/position-history.js +13 -0
- package/dist/rebalance.d.ts +26 -2
- package/dist/rebalance.js +86 -7
- package/dist/relay-swap.d.ts +232 -0
- package/dist/relay-swap.js +630 -0
- package/dist/risk.js +5 -1
- package/dist/secret-env.d.ts +15 -0
- package/dist/secret-env.js +36 -0
- package/dist/settings.d.ts +169 -6
- package/dist/settings.js +219 -38
- package/dist/signer/ows-evm.d.ts +13 -0
- package/dist/signer/ows-evm.js +22 -0
- package/dist/smart-order.d.ts +9 -0
- package/dist/smart-order.js +53 -8
- package/dist/strategies/dca.d.ts +6 -2
- package/dist/strategies/dca.js +66 -26
- package/dist/strategies/funding-arb.d.ts +11 -1
- package/dist/strategies/funding-arb.js +202 -89
- package/dist/strategies/grid.d.ts +31 -0
- package/dist/strategies/grid.js +89 -11
- package/dist/strategies/pending-stop.d.ts +8 -0
- package/dist/strategies/pending-stop.js +12 -0
- package/dist/strategies/split-order.d.ts +45 -17
- package/dist/strategies/split-order.js +157 -91
- package/dist/strategies/trailing-stop.d.ts +2 -1
- package/dist/strategies/trailing-stop.js +52 -6
- package/dist/strategies/twap.d.ts +6 -1
- package/dist/strategies/twap.js +39 -11
- package/dist/trade-validator.d.ts +22 -0
- package/dist/trade-validator.js +119 -15
- package/dist/utils/numeric.d.ts +46 -0
- package/dist/utils/numeric.js +79 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +96 -19
- package/dist/withdraw-gate.d.ts +49 -0
- package/dist/withdraw-gate.js +131 -0
- package/package.json +15 -6
- package/skills/perp-cli/SKILL.md +142 -55
- package/skills/perp-cli/references/agent-operations.md +110 -56
- package/skills/perp-cli/references/commands.md +266 -71
- package/skills/perp-cli/references/strategies.md +34 -31
- package/skills/perp-cli/scripts/arb-monitor.sh +10 -5
- package/skills/perp-cli/scripts/funding-analysis.sh +14 -9
- package/skills/perp-cli/scripts/preflight.sh +21 -14
- package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
- package/skills/perp-cli/scripts/validate-arb.sh +31 -15
- package/vendor/lighter-signer/SHA256SUMS +2 -0
- package/vendor/lighter-signer/VERSION +8 -0
- package/vendor/lighter-signer/lighter-signer.wasm +0 -0
- package/vendor/lighter-signer/wasm_exec.js +575 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
- package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
package/dist/commands/bot.js
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
import { parsePositiveDecimal } from "../errors.js";
|
|
2
|
+
import { isDryRun, networkOf, supportsDryRun } from "../cli-flags.js";
|
|
3
|
+
import { setQuarantineNetwork } from "../bot/quarantine.js";
|
|
4
|
+
import { assertStrategySimulatable } from "../bot/engine.js";
|
|
1
5
|
import chalk from "chalk";
|
|
2
6
|
import { printJson, jsonOk, jsonError } from "../utils.js";
|
|
3
|
-
import { loadBotConfig, parseStrategy, quickGridConfig, quickDCAConfig, runBot, PRESETS, getPreset, getPresetsByStrategy } from "../bot/index.js";
|
|
7
|
+
import { loadBotConfig, parseStrategy, quickGridConfig, quickDCAConfig, runBot, PRESETS, getPreset, getPresetsByStrategy, withPresetSize } from "../bot/index.js";
|
|
8
|
+
import { resolveVenueId } from "../exchanges/venue.js";
|
|
9
|
+
import { PerpError } from "../errors.js";
|
|
4
10
|
import { updateJobState } from "../jobs.js";
|
|
5
11
|
import { runTWAP, runFundingArb, runGrid, runDCA, runTrailingStop } from "../strategies/index.js";
|
|
6
12
|
import { registerStrategyPlanCommands } from "./plan.js";
|
|
7
13
|
export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJson) {
|
|
8
14
|
const bot = program.command("strategy").description("Trading strategies — long-running bot algorithms + one-shot scripted plans");
|
|
15
|
+
// Quarantine checks / entries of every strategy command (bots, plans, DCA / TWAP) are scoped to its -n network.
|
|
16
|
+
bot.hook("preAction", (_cmd, action) => setQuarantineNetwork(networkOf(action)));
|
|
17
|
+
/** The root `-e` selection: resolved id + whether it was given on the command line (not the default). */
|
|
18
|
+
const cliVenue = () => ({
|
|
19
|
+
id: resolveVenueId(String(program.opts().exchange)),
|
|
20
|
+
explicit: program.getOptionValueSource("exchange") === "cli",
|
|
21
|
+
});
|
|
9
22
|
// ── bot start <config> ──
|
|
10
23
|
bot
|
|
11
24
|
.command("start <config>")
|
|
@@ -14,26 +27,26 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
14
27
|
.option("--headless", "Run without TUI dashboard")
|
|
15
28
|
.option("--job-id <id>", "Job ID (set by background runner)")
|
|
16
29
|
.action(async (configPath, opts) => {
|
|
17
|
-
|
|
30
|
+
// config.exchange (resolved) is the venue for EVERY strategy type; a contradicting explicit -e is refused.
|
|
31
|
+
const config = loadBotConfig(configPath, cliVenue());
|
|
18
32
|
if (opts.background) {
|
|
19
|
-
const { startJob } = await import("../jobs.js");
|
|
33
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
20
34
|
const job = startJob({
|
|
21
35
|
strategy: `bot:${config.strategy.type}`,
|
|
22
36
|
exchange: config.exchange,
|
|
23
37
|
params: { config: configPath, name: config.name },
|
|
24
|
-
|
|
38
|
+
venue: config.exchange, network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
39
|
+
cliArgs: [`strategy`, `start`, configPath],
|
|
25
40
|
});
|
|
26
41
|
if (isJson())
|
|
27
42
|
return printJson(jsonOk(job));
|
|
28
43
|
printBotJobStarted(config.name, job.id);
|
|
29
44
|
return;
|
|
30
45
|
}
|
|
31
|
-
const adapter = config.
|
|
32
|
-
? await getAdapterFor(config.exchange)
|
|
33
|
-
: await getAdapter();
|
|
46
|
+
const adapter = await getAdapterFor(config.exchange);
|
|
34
47
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
35
48
|
const log = makeLog();
|
|
36
|
-
await runBot(adapter, config, opts.jobId, log, undefined, mode);
|
|
49
|
+
await runBot(adapter, config, opts.jobId, log, undefined, mode, false);
|
|
37
50
|
});
|
|
38
51
|
// ── bot quick grid ──
|
|
39
52
|
bot
|
|
@@ -63,9 +76,9 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
63
76
|
leverage: opts.leverage ? parseInt(opts.leverage) : undefined,
|
|
64
77
|
});
|
|
65
78
|
if (opts.background) {
|
|
66
|
-
const { startJob } = await import("../jobs.js");
|
|
79
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
67
80
|
const cliArgs = [
|
|
68
|
-
|
|
81
|
+
`strategy`, `quick-grid`, symbol.toUpperCase(),
|
|
69
82
|
`--range`, opts.range, `--grids`, opts.grids, `--size`, opts.size,
|
|
70
83
|
`--side`, opts.side, `--max-drawdown`, opts.maxDrawdown,
|
|
71
84
|
`--max-runtime`, opts.maxRuntime,
|
|
@@ -74,6 +87,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
74
87
|
const job = startJob({
|
|
75
88
|
strategy: `bot:grid`,
|
|
76
89
|
exchange: adapter.name,
|
|
90
|
+
venue: adapter.name, network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
77
91
|
params: { symbol: symbol.toUpperCase(), ...opts },
|
|
78
92
|
cliArgs,
|
|
79
93
|
});
|
|
@@ -84,7 +98,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
84
98
|
}
|
|
85
99
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
86
100
|
const log = makeLog();
|
|
87
|
-
await runBot(adapter, config, opts.jobId, log, undefined, mode);
|
|
101
|
+
await runBot(adapter, config, opts.jobId, log, undefined, mode, false);
|
|
88
102
|
});
|
|
89
103
|
// ── bot quick dca ──
|
|
90
104
|
bot
|
|
@@ -103,7 +117,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
103
117
|
exchange: adapter.name,
|
|
104
118
|
symbol: symbol.toUpperCase(),
|
|
105
119
|
side: side.toLowerCase(),
|
|
106
|
-
amount:
|
|
120
|
+
amount: Number(parsePositiveDecimal(amount, "amount")),
|
|
107
121
|
intervalSec: parseInt(interval),
|
|
108
122
|
orders: parseInt(opts.orders),
|
|
109
123
|
triggerDrop: opts.triggerDrop ? parseFloat(opts.triggerDrop) : undefined,
|
|
@@ -111,9 +125,9 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
111
125
|
maxDrawdown: parseFloat(opts.maxDrawdown),
|
|
112
126
|
});
|
|
113
127
|
if (opts.background) {
|
|
114
|
-
const { startJob } = await import("../jobs.js");
|
|
128
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
115
129
|
const cliArgs = [
|
|
116
|
-
|
|
130
|
+
`strategy`, `quick-dca`, symbol.toUpperCase(), side.toLowerCase(),
|
|
117
131
|
amount, interval, `--orders`, opts.orders, `--max-drawdown`, opts.maxDrawdown,
|
|
118
132
|
...(opts.priceLimit ? [`--price-limit`, opts.priceLimit] : []),
|
|
119
133
|
...(opts.triggerDrop ? [`--trigger-drop`, opts.triggerDrop] : []),
|
|
@@ -121,6 +135,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
121
135
|
const job = startJob({
|
|
122
136
|
strategy: `bot:dca`,
|
|
123
137
|
exchange: adapter.name,
|
|
138
|
+
venue: adapter.name, network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
124
139
|
params: { symbol: symbol.toUpperCase(), ...opts },
|
|
125
140
|
cliArgs,
|
|
126
141
|
});
|
|
@@ -131,7 +146,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
131
146
|
}
|
|
132
147
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
133
148
|
const log = makeLog();
|
|
134
|
-
await runBot(adapter, config, opts.jobId, log, undefined, mode);
|
|
149
|
+
await runBot(adapter, config, opts.jobId, log, undefined, mode, false);
|
|
135
150
|
});
|
|
136
151
|
// ── bot quick arb ──
|
|
137
152
|
bot
|
|
@@ -187,9 +202,9 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
187
202
|
monitor_interval_sec: parseInt(opts.interval),
|
|
188
203
|
};
|
|
189
204
|
if (opts.background) {
|
|
190
|
-
const { startJob } = await import("../jobs.js");
|
|
205
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
191
206
|
const cliArgs = [
|
|
192
|
-
`quick-arb`,
|
|
207
|
+
`strategy`, `quick-arb`,
|
|
193
208
|
`--min-spread`, opts.minSpread, `--close-spread`, opts.closeSpread,
|
|
194
209
|
`--size`, opts.size, `--max-positions`, opts.maxPositions,
|
|
195
210
|
`--exchanges`, opts.exchanges, `--interval`, opts.interval,
|
|
@@ -198,6 +213,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
198
213
|
const job = startJob({
|
|
199
214
|
strategy: `bot:funding-arb`,
|
|
200
215
|
exchange: "multi",
|
|
216
|
+
network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
201
217
|
params: { ...opts },
|
|
202
218
|
cliArgs,
|
|
203
219
|
});
|
|
@@ -208,7 +224,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
208
224
|
}
|
|
209
225
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
210
226
|
const log = makeLog();
|
|
211
|
-
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode);
|
|
227
|
+
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode, false);
|
|
212
228
|
});
|
|
213
229
|
// ── bot delta-neutral ──
|
|
214
230
|
bot
|
|
@@ -266,9 +282,9 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
266
282
|
monitor_interval_sec: parseInt(opts.interval),
|
|
267
283
|
};
|
|
268
284
|
if (opts.background) {
|
|
269
|
-
const { startJob } = await import("../jobs.js");
|
|
285
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
270
286
|
const cliArgs = [
|
|
271
|
-
`delta-neutral`,
|
|
287
|
+
`strategy`, `delta-neutral`,
|
|
272
288
|
`--min-rate`, opts.minRate, `--close-rate`, opts.closeRate,
|
|
273
289
|
`--size`, opts.size, `--max-positions`, opts.maxPositions,
|
|
274
290
|
`--leverage`, opts.leverage, `--exchanges`, opts.exchanges,
|
|
@@ -277,6 +293,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
277
293
|
const job = startJob({
|
|
278
294
|
strategy: `bot:delta-neutral`,
|
|
279
295
|
exchange: "multi",
|
|
296
|
+
network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
280
297
|
params: { ...opts },
|
|
281
298
|
cliArgs,
|
|
282
299
|
});
|
|
@@ -287,7 +304,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
287
304
|
}
|
|
288
305
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
289
306
|
const log = makeLog();
|
|
290
|
-
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode);
|
|
307
|
+
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode, false);
|
|
291
308
|
});
|
|
292
309
|
// ── bot preset list ──
|
|
293
310
|
bot
|
|
@@ -324,6 +341,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
324
341
|
.option("--background", "Run in background (tmux)")
|
|
325
342
|
.option("--headless", "Run without TUI dashboard")
|
|
326
343
|
.option("--job-id <id>", "Job ID")
|
|
344
|
+
.option("--size <n>", "Override the preset size: grid total base size, DCA base size per order, arb $ per leg")
|
|
327
345
|
.action(async (name, symbol, opts) => {
|
|
328
346
|
const preset = getPreset(name);
|
|
329
347
|
if (!preset) {
|
|
@@ -334,7 +352,8 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
334
352
|
const sym = symbol?.toUpperCase() ?? "ETH";
|
|
335
353
|
// For arb presets, need multiple adapters
|
|
336
354
|
if (preset.strategy === "funding-arb") {
|
|
337
|
-
const
|
|
355
|
+
const built = preset.buildConfig("multi", sym);
|
|
356
|
+
const config = opts.size !== undefined ? withPresetSize(built, opts.size) : built;
|
|
338
357
|
const arbStrategy = config.strategy;
|
|
339
358
|
const adapters = new Map();
|
|
340
359
|
for (const exName of arbStrategy.exchanges) {
|
|
@@ -352,11 +371,12 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
352
371
|
const primaryAdapter = adapters.values().next().value;
|
|
353
372
|
config.exchange = primaryAdapter.name;
|
|
354
373
|
if (opts.background) {
|
|
355
|
-
const { startJob } = await import("../jobs.js");
|
|
356
|
-
const cliArgs = [`preset`, name, sym];
|
|
374
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
375
|
+
const cliArgs = [`strategy`, `preset`, name, sym, ...(opts.size !== undefined ? [`--size`, opts.size] : [])];
|
|
357
376
|
const job = startJob({
|
|
358
377
|
strategy: `bot:${preset.strategy}`,
|
|
359
378
|
exchange: "multi",
|
|
379
|
+
network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
360
380
|
params: { preset: name, symbol: sym },
|
|
361
381
|
cliArgs,
|
|
362
382
|
});
|
|
@@ -371,18 +391,20 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
371
391
|
console.log(chalk.gray(` ${preset.description}\n`));
|
|
372
392
|
}
|
|
373
393
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
374
|
-
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode);
|
|
394
|
+
await runBot(primaryAdapter, config, opts.jobId, log, adapters, mode, false);
|
|
375
395
|
return;
|
|
376
396
|
}
|
|
377
397
|
// Grid / DCA presets
|
|
378
398
|
const adapter = await getAdapter();
|
|
379
|
-
const
|
|
399
|
+
const built = preset.buildConfig(adapter.name, sym);
|
|
400
|
+
const config = opts.size !== undefined ? withPresetSize(built, opts.size) : built;
|
|
380
401
|
if (opts.background) {
|
|
381
|
-
const { startJob } = await import("../jobs.js");
|
|
382
|
-
const cliArgs = [`preset`, name, sym];
|
|
402
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
403
|
+
const cliArgs = [`strategy`, `preset`, name, sym, ...(opts.size !== undefined ? [`--size`, opts.size] : [])];
|
|
383
404
|
const job = startJob({
|
|
384
405
|
strategy: `bot:${preset.strategy}`,
|
|
385
406
|
exchange: adapter.name,
|
|
407
|
+
venue: adapter.name, network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
386
408
|
params: { preset: name, symbol: sym },
|
|
387
409
|
cliArgs,
|
|
388
410
|
});
|
|
@@ -397,7 +419,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
397
419
|
console.log(chalk.gray(` ${preset.description}\n`));
|
|
398
420
|
}
|
|
399
421
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
400
|
-
await runBot(adapter, config, opts.jobId, log, undefined, mode);
|
|
422
|
+
await runBot(adapter, config, opts.jobId, log, undefined, mode, false);
|
|
401
423
|
});
|
|
402
424
|
// ── bot list-strategies ──
|
|
403
425
|
bot
|
|
@@ -416,6 +438,44 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
416
438
|
}
|
|
417
439
|
console.log();
|
|
418
440
|
});
|
|
441
|
+
// ── quarantine (design §12.3): positions the bot refuses to touch until a human clears them ──
|
|
442
|
+
bot
|
|
443
|
+
.command("quarantine")
|
|
444
|
+
.description("List quarantined symbols (unknown / unverified order outcome or unhedged leg) — blocked for every bot strategy, arb exec and arb auto (~/.perp/bot-quarantine.json)")
|
|
445
|
+
.action(async () => {
|
|
446
|
+
const { listQuarantine, QUARANTINE_FILE } = await import("../bot/quarantine.js");
|
|
447
|
+
const entries = listQuarantine();
|
|
448
|
+
if (isJson())
|
|
449
|
+
return printJson(jsonOk({ file: QUARANTINE_FILE, entries }));
|
|
450
|
+
if (entries.length === 0) {
|
|
451
|
+
console.log(chalk.gray("\n No quarantined bot positions.\n"));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
console.log(chalk.red.bold(`\n Quarantined symbols — blocked for all bots and arb paths (${QUARANTINE_FILE}):\n`));
|
|
455
|
+
for (const e of entries) {
|
|
456
|
+
console.log(` ${chalk.white(`${e.strategy} ${e.symbol}`)} ${chalk.gray(`${e.time} · ${e.network ?? "any network (set by a process that did not declare one)"}`)}`);
|
|
457
|
+
console.log(` ${e.reason}`);
|
|
458
|
+
console.log(chalk.yellow(` ${e.remediation}`));
|
|
459
|
+
}
|
|
460
|
+
console.log(chalk.gray("\n After resolving on the venue: perp strategy quarantine-clear <symbol> [--strategy <name>]\n"));
|
|
461
|
+
});
|
|
462
|
+
bot
|
|
463
|
+
.command("quarantine-clear <symbol>")
|
|
464
|
+
.description("Clear a quarantine after verifying / resolving the position on the venues manually (running bots pick it up on their next tick)")
|
|
465
|
+
.option("--strategy <name>", "Only entries recorded by this source (funding-arb, funding-arb-v2, spot-perp-arb, arb-exec, arb-close); default: all — the symbol stays blocked while any entry remains")
|
|
466
|
+
.action(async (symbol, opts) => {
|
|
467
|
+
const { clearQuarantine } = await import("../bot/quarantine.js");
|
|
468
|
+
const removed = clearQuarantine(symbol, opts.strategy ?? null);
|
|
469
|
+
if (isJson())
|
|
470
|
+
return printJson(jsonOk({ symbol: symbol.toUpperCase(), removed }));
|
|
471
|
+
if (removed.length === 0) {
|
|
472
|
+
console.log(chalk.yellow(`\n No quarantine entry for ${symbol.toUpperCase()}${opts.strategy ? ` (${opts.strategy})` : ""}.\n`));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
for (const e of removed)
|
|
476
|
+
console.log(chalk.green(`\n Cleared ${e.strategy} ${e.symbol}`));
|
|
477
|
+
console.log();
|
|
478
|
+
});
|
|
419
479
|
// ── bot apex ──
|
|
420
480
|
bot
|
|
421
481
|
.command("apex [symbol]")
|
|
@@ -450,15 +510,16 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
450
510
|
monitor_interval_sec: 10,
|
|
451
511
|
};
|
|
452
512
|
if (opts.background) {
|
|
453
|
-
const { startJob } = await import("../jobs.js");
|
|
513
|
+
const { startJob, jobSelectorsOf } = await import("../jobs.js");
|
|
454
514
|
const cliArgs = [
|
|
455
|
-
|
|
515
|
+
`strategy`, `apex`, sym,
|
|
456
516
|
`--preset`, opts.preset, `--max-slots`, opts.maxSlots,
|
|
457
517
|
`--daily-limit`, opts.dailyLimit,
|
|
458
518
|
];
|
|
459
519
|
const job = startJob({
|
|
460
520
|
strategy: `bot:apex`,
|
|
461
521
|
exchange: adapter.name,
|
|
522
|
+
venue: adapter.name, network: networkOf(program), dryRun: isDryRun(program), selectors: jobSelectorsOf(program),
|
|
462
523
|
params: { symbol: sym, ...opts },
|
|
463
524
|
cliArgs,
|
|
464
525
|
});
|
|
@@ -469,7 +530,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
469
530
|
}
|
|
470
531
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
471
532
|
const log = makeLog();
|
|
472
|
-
await runBot(adapter, config, opts.jobId, log, undefined, mode);
|
|
533
|
+
await runBot(adapter, config, opts.jobId, log, undefined, mode, false);
|
|
473
534
|
});
|
|
474
535
|
// ── bot reflect ──
|
|
475
536
|
bot
|
|
@@ -510,9 +571,8 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
510
571
|
console.log();
|
|
511
572
|
});
|
|
512
573
|
// ── bot run <strategy> <symbol> ──
|
|
513
|
-
bot
|
|
514
|
-
.
|
|
515
|
-
.description("Run a strategy (use 'perp strategy list-strategies' to see all). Symbol optional for multi-symbol strategies.")
|
|
574
|
+
supportsDryRun(bot.command("run [strategy] [symbol]"))
|
|
575
|
+
.description("Run a strategy (use 'perp strategy list-strategies' to see all). Symbol optional for multi-symbol strategies. Supports --dry-run (actions logged, never sent; strategies that execute directly refuse it).")
|
|
516
576
|
.option("--config <path>", "YAML/JSON config file")
|
|
517
577
|
.option("--headless", "Run without TUI dashboard")
|
|
518
578
|
.option("--param <key=value>", "Strategy parameter (repeatable)", (val, acc) => [...acc, val], [])
|
|
@@ -521,7 +581,8 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
521
581
|
.option("--min-spread <pct>", "Min annual spread to enter (%)")
|
|
522
582
|
.option("--close-spread <pct>", "Close when spread drops below (%)")
|
|
523
583
|
.option("--exchanges <list>", "Comma-separated exchanges")
|
|
524
|
-
.action(async (strategyName, symbol, opts) => {
|
|
584
|
+
.action(async (strategyName, symbol, opts, cmd) => {
|
|
585
|
+
const dryRun = isDryRun(cmd);
|
|
525
586
|
if (!strategyName) {
|
|
526
587
|
await import("../bot/engine.js");
|
|
527
588
|
const { listStrategies: listStrats } = await import("../bot/strategy-registry.js");
|
|
@@ -537,6 +598,9 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
537
598
|
return;
|
|
538
599
|
}
|
|
539
600
|
const sym = symbol?.toUpperCase() || "ALL";
|
|
601
|
+
// Refuse --dry-run for strategies that cannot simulate before any adapter is built.
|
|
602
|
+
if (dryRun)
|
|
603
|
+
assertStrategySimulatable(strategyName);
|
|
540
604
|
// Strategies that require an explicit symbol
|
|
541
605
|
const symbolRequiredStrategies = [
|
|
542
606
|
"grid", "dca", "simple-mm", "engine-mm", "avellaneda-mm",
|
|
@@ -555,7 +619,7 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
555
619
|
// Load or build config
|
|
556
620
|
let config;
|
|
557
621
|
if (opts.config) {
|
|
558
|
-
config = loadBotConfig(opts.config);
|
|
622
|
+
config = loadBotConfig(opts.config, cliVenue());
|
|
559
623
|
// Override strategy type and symbol from CLI args
|
|
560
624
|
config.strategy = { ...config.strategy, type: strategyName };
|
|
561
625
|
config.symbol = sym;
|
|
@@ -625,8 +689,8 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
625
689
|
console.error();
|
|
626
690
|
return process.exit(1);
|
|
627
691
|
}
|
|
628
|
-
|
|
629
|
-
|
|
692
|
+
// --config: the config's venue (resolved against -e above); otherwise the -e venue.
|
|
693
|
+
const adapter = await getAdapterFor(config.exchange);
|
|
630
694
|
const mode = resolveOutputMode(isJson, opts.headless);
|
|
631
695
|
const log = makeLog();
|
|
632
696
|
// Multi-exchange strategies need all adapters
|
|
@@ -634,17 +698,29 @@ export function registerStrategyCommands(program, getAdapter, getAdapterFor, isJ
|
|
|
634
698
|
const multiExchangeStrategies = ["funding-auto", "funding-arb", "funding-arb-v2", "spot-perp-arb", "basis-arb", "hedge-agent"];
|
|
635
699
|
if (multiExchangeStrategies.includes(strategyName) && getAdapterFor) {
|
|
636
700
|
extraAdapters = new Map();
|
|
637
|
-
|
|
701
|
+
// --exchanges restricts the venue set (each listed venue must load); the default set
|
|
702
|
+
// skips a venue that cannot load, with a warning.
|
|
703
|
+
const explicit = opts.exchanges !== undefined;
|
|
704
|
+
const venues = explicit ? opts.exchanges.split(",").map(e => resolveVenueId(e.trim())) : ["pacifica", "hyperliquid", "lighter", "aster"];
|
|
705
|
+
if (explicit && !venues.includes(adapter.name)) {
|
|
706
|
+
throw new PerpError("INVALID_PARAMS", `--exchanges ${opts.exchanges} does not include the bot's own venue ${adapter.name}`, {
|
|
707
|
+
remediation: `Add ${adapter.name} to --exchanges, or select another venue with -e`,
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
for (const ex of venues) {
|
|
638
711
|
if (ex === adapter.name)
|
|
639
712
|
continue;
|
|
640
713
|
try {
|
|
641
|
-
|
|
642
|
-
|
|
714
|
+
extraAdapters.set(ex, await getAdapterFor(ex));
|
|
715
|
+
}
|
|
716
|
+
catch (e) {
|
|
717
|
+
if (explicit)
|
|
718
|
+
throw e;
|
|
719
|
+
log(` WARNING: ${ex} unavailable (${e instanceof Error ? e.message : String(e)}) — running without it`);
|
|
643
720
|
}
|
|
644
|
-
catch { /* skip unavailable exchanges */ }
|
|
645
721
|
}
|
|
646
722
|
}
|
|
647
|
-
await runBot(adapter, config, undefined, log, extraAdapters, mode);
|
|
723
|
+
await runBot(adapter, config, undefined, log, extraAdapters, mode, dryRun);
|
|
648
724
|
});
|
|
649
725
|
// ── bot example ──
|
|
650
726
|
bot
|
|
@@ -778,6 +854,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
778
854
|
.description("Run client-side TWAP (splits market orders over time)");
|
|
779
855
|
twapCmd
|
|
780
856
|
.option("-s, --slices <n>", "Number of slices (default: auto)")
|
|
857
|
+
.option("--reduce-only", "Reduce only (every slice)")
|
|
781
858
|
.option("--job-id <id>", "Job ID (set automatically for background jobs)")
|
|
782
859
|
.action(async (symbol, side, size, duration, opts) => {
|
|
783
860
|
const s = side.toLowerCase();
|
|
@@ -792,6 +869,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
792
869
|
totalSize: parseFloat(size),
|
|
793
870
|
durationSec: parseInt(duration),
|
|
794
871
|
slices: opts.slices ? parseInt(opts.slices) : undefined,
|
|
872
|
+
reduceOnly: opts.reduceOnly === true,
|
|
795
873
|
};
|
|
796
874
|
const log = (msg) => {
|
|
797
875
|
const ts = new Date().toLocaleTimeString();
|
|
@@ -813,7 +891,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
813
891
|
.option("--size-usd <usd>", "Position size per leg in USD (alternative)")
|
|
814
892
|
.option("--symbols <list>", "Comma-separated symbol filter")
|
|
815
893
|
.option("--interval <sec>", "Check interval in seconds", "60")
|
|
816
|
-
.option("--auto-execute", "Actually place trades (default: monitor only)")
|
|
894
|
+
.option("--auto-execute", "Actually place trades (default: monitor only). Legs are sequential and verified (long, then short sized to the long's fill); closes are reduce-only from the venue position size; an unknown outcome quarantines the symbol")
|
|
817
895
|
.option("--max-positions <n>", "Max simultaneous positions", "3")
|
|
818
896
|
.option("--exchanges <list>", "Comma-separated exchanges to use", "pacifica,hyperliquid,lighter,aster")
|
|
819
897
|
.option("--auto-rebalance", "Trigger rebalance alerts when exchange balance is low")
|
|
@@ -859,7 +937,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
859
937
|
// ── Grid Bot ──
|
|
860
938
|
const gridCmd = run
|
|
861
939
|
.command("grid <symbol>")
|
|
862
|
-
.description("Run grid trading bot (places limit orders across a price range)");
|
|
940
|
+
.description("Run grid trading bot (places limit orders across a price range). At stop the remaining orders are cancelled; the grid's inventory (position change since start) is left open with a warning unless --close-on-stop");
|
|
863
941
|
gridCmd
|
|
864
942
|
.requiredOption("--upper <price>", "Upper price bound")
|
|
865
943
|
.requiredOption("--lower <price>", "Lower price bound")
|
|
@@ -870,6 +948,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
870
948
|
.option("--interval <sec>", "Check interval in seconds", "10")
|
|
871
949
|
.option("--max-runtime <sec>", "Max runtime in seconds (0 = forever)", "0")
|
|
872
950
|
.option("--trailing-stop <pct>", "Stop if equity drops by this % from peak")
|
|
951
|
+
.option("--close-on-stop", "At stop, close the grid's own inventory (position change since start) reduce-only; default: leave it open and warn")
|
|
873
952
|
.option("--job-id <id>", "Job ID (set automatically for background jobs)")
|
|
874
953
|
.action(async (symbol, opts) => {
|
|
875
954
|
const side = opts.side.toLowerCase();
|
|
@@ -889,6 +968,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
889
968
|
intervalSec: parseInt(opts.interval),
|
|
890
969
|
maxRuntime: parseInt(opts.maxRuntime),
|
|
891
970
|
trailingStop: opts.trailingStop ? parseFloat(opts.trailingStop) : undefined,
|
|
971
|
+
closeOnStop: opts.closeOnStop === true,
|
|
892
972
|
};
|
|
893
973
|
const log = (msg) => {
|
|
894
974
|
const ts = new Date().toLocaleTimeString();
|
|
@@ -920,7 +1000,7 @@ function registerRunSubcommands(parent, getAdapter, getAdapterFor, isJson) {
|
|
|
920
1000
|
const params = {
|
|
921
1001
|
symbol: symbol.toUpperCase(),
|
|
922
1002
|
side: s,
|
|
923
|
-
amountPerOrder:
|
|
1003
|
+
amountPerOrder: Number(parsePositiveDecimal(amount, "amount")),
|
|
924
1004
|
intervalSec: parseInt(interval),
|
|
925
1005
|
totalOrders: parseInt(opts.orders),
|
|
926
1006
|
priceLimit: opts.priceLimit ? parseFloat(opts.priceLimit) : undefined,
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
|
|
2
|
+
import type { Network } from "../exchanges/venue-types.js";
|
|
3
|
+
export declare function registerFundsBridgeCommands(parent: Command, isJson: () => boolean, getNetwork: () => Network): void;
|