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
|
@@ -6,10 +6,11 @@ You are not expected to follow rigid rules — use this as a decision framework
|
|
|
6
6
|
## Core Concepts
|
|
7
7
|
|
|
8
8
|
### Funding Rate Mechanics
|
|
9
|
-
- Funding
|
|
9
|
+
- Funding settles **every 1 hour** on every venue except Aster, whose interval is **per symbol** (from its fundingInfo `fundingIntervalHours`)
|
|
10
|
+
- QFEX funding rates are unverified: excluded from scans / arb; shown only with `arb scan --rates --include-unverified` (display only)
|
|
10
11
|
- Positive rate = longs pay shorts, negative rate = shorts pay longs
|
|
11
|
-
- Rates are annualized
|
|
12
|
-
- Scan opportunities: `perp --json arb scan --min 5` (shows
|
|
12
|
+
- Rates are displayed annualized: per hour ≈ `annual / 8760`
|
|
13
|
+
- Scan opportunities: `perp --json arb scan --min 5` (shows grossSpread, netSpread, longExch, shortExch)
|
|
13
14
|
|
|
14
15
|
### Opportunity Cost Awareness
|
|
15
16
|
|
|
@@ -69,12 +70,14 @@ The `arb scan` output tells you EXACTLY what to do:
|
|
|
69
70
|
```
|
|
70
71
|
longExch: "hyperliquid" → open LONG on this exchange
|
|
71
72
|
shortExch: "pacifica" → open SHORT on this exchange
|
|
72
|
-
|
|
73
|
+
grossSpread: 15.2 → funding spread, annualized %
|
|
74
|
+
netSpread: 12.87 → annualized % after round-trip trading costs amortized over --hold-days (default 7)
|
|
75
|
+
(and --bridge-cost if given)
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
**Rules:**
|
|
76
79
|
1. **ALWAYS follow longExch/shortExch directions exactly.** DO NOT reverse them.
|
|
77
|
-
2. **NEVER enter if netSpread ≤ 0.** Negative netSpread = loss after fees.
|
|
80
|
+
2. **NEVER enter if netSpread ≤ 0.** Negative netSpread = loss after fees over the assumed hold. A shorter real hold makes costs weigh more — re-scan with your `--hold-days`.
|
|
78
81
|
3. The direction logic: go LONG where funding is negative (longs receive), go SHORT where funding is positive (shorts receive).
|
|
79
82
|
|
|
80
83
|
**Why these directions?**
|
|
@@ -94,7 +97,7 @@ When evaluating an arb opportunity:
|
|
|
94
97
|
|
|
95
98
|
3. **How long will the spread persist?**
|
|
96
99
|
- Historical funding tends to mean-revert
|
|
97
|
-
- Higher confidence in moderate, stable spreads (
|
|
100
|
+
- Higher confidence in moderate, stable spreads (e.g. 10–50% annualized netSpread) than extreme spikes (hundreds of % annualized)
|
|
98
101
|
|
|
99
102
|
4. **Can I execute both legs atomically?**
|
|
100
103
|
- Both positions should open near-simultaneously to minimize directional exposure
|
|
@@ -105,19 +108,20 @@ When evaluating an arb opportunity:
|
|
|
105
108
|
**Before entering any arb position, set a target hold duration.** This is critical for calculating whether the position is worth entering.
|
|
106
109
|
|
|
107
110
|
```
|
|
108
|
-
Expected
|
|
109
|
-
Entry/Exit Cost
|
|
110
|
-
Net Profit
|
|
111
|
+
Expected Funding = notional × (grossSpread% / 100) × target_hours / 8760
|
|
112
|
+
Entry/Exit Cost = open_fees + close_fees + slippage (both legs)
|
|
113
|
+
Net Profit = Expected Funding - Entry/Exit Cost
|
|
111
114
|
```
|
|
115
|
+
(`netSpread` already does this amortization for `--hold-days`; pass `--hold-days` = your target to read it directly.)
|
|
112
116
|
|
|
113
117
|
**Only enter if Net Profit > 0 with a comfortable margin.**
|
|
114
118
|
|
|
115
119
|
Guidelines for hold duration:
|
|
116
|
-
- **
|
|
117
|
-
- **Elevated spreads (50
|
|
118
|
-
- **
|
|
120
|
+
- **Moderate, stable spreads (≈10–50% annualized):** target days, not hours — the funding must accumulate past entry/exit costs.
|
|
121
|
+
- **Elevated spreads (≈50–100% annualized):** shorter targets; likely to compress. Take profit earlier.
|
|
122
|
+
- **Spikes (well above 100% annualized):** revert quickly — enter only if the funding over a few settlements covers entry/exit costs.
|
|
119
123
|
|
|
120
|
-
**Re-evaluate at each funding settlement (
|
|
124
|
+
**Re-evaluate at each funding settlement (hourly on most venues):**
|
|
121
125
|
1. Is the spread still above breakeven for the remaining target hours?
|
|
122
126
|
2. If spread compressed, should I exit early or extend the hold?
|
|
123
127
|
3. Has a better opportunity appeared? (Remember: switching has its own cost)
|
|
@@ -126,12 +130,12 @@ Guidelines for hold duration:
|
|
|
126
130
|
|
|
127
131
|
Example entry decision log:
|
|
128
132
|
```
|
|
129
|
-
Entry: BTC HL↔PAC |
|
|
130
|
-
Expected:
|
|
131
|
-
Entry/exit cost:
|
|
132
|
-
Net expected:
|
|
133
|
-
|
|
134
|
-
Actual hold:
|
|
133
|
+
Entry: BTC HL↔PAC | $1000/leg | grossSpread 80% annual | Target hold: 7d
|
|
134
|
+
Expected funding: 1000 × 0.80 × 168 / 8760 ≈ $15.3
|
|
135
|
+
Entry/exit cost: ~$4 (fees + slippage, both legs, open + close)
|
|
136
|
+
Net expected: ≈ $11 → ENTER (arb scan --hold-days 7 netSpread > 0 agrees)
|
|
137
|
+
Day 3 check: grossSpread compressed to 10% → remaining 4d earn ≈ $1.1 → EXIT if it keeps falling / flips, or a better net opportunity exists
|
|
138
|
+
Actual hold: 3d | log actual funding vs estimate
|
|
135
139
|
```
|
|
136
140
|
|
|
137
141
|
### Monitoring Active Positions
|
|
@@ -181,6 +185,8 @@ Round `ORDER_SIZE` to the coarser step size of the two exchanges. If the matched
|
|
|
181
185
|
|
|
182
186
|
#### Step 2: Execute Legs Sequentially with Same Size
|
|
183
187
|
|
|
188
|
+
Prefer `perp --json arb exec <SYM> <LONG_EX> <SHORT_EX> <USD>` — it does exactly this: long first, then the short sized to the verified long fill, reconcile always on, and an unconfirmed leg quarantines the symbol. Manual legs (below) only when `arb exec` cannot be used.
|
|
189
|
+
|
|
184
190
|
Once you have a single `ORDER_SIZE`, execute both legs using THAT EXACT SIZE:
|
|
185
191
|
|
|
186
192
|
```bash
|
|
@@ -243,7 +249,7 @@ Leg B: perp --json -e <SHORT_EX> trade split <SYM> sell 5000
|
|
|
243
249
|
#### Using Limit Orders for Better Execution
|
|
244
250
|
For non-urgent entries, use limit orders at best bid/ask instead of market:
|
|
245
251
|
```bash
|
|
246
|
-
perp --json -e <EX> trade
|
|
252
|
+
perp --json -e <EX> trade limit <SYM> buy <PRICE> <ORDER_SIZE> # limit order (GTC default; --tif ALO = post-only)
|
|
247
253
|
```
|
|
248
254
|
This avoids crossing the spread but risks not getting filled. Set a timeout and fall back to market if unfilled. **Both legs must still use the same `ORDER_SIZE`.**
|
|
249
255
|
|
|
@@ -311,7 +317,7 @@ perp --json -e <EX> trade leverage <SYM> <LEVERAGE>
|
|
|
311
317
|
**For funding rate arb, use ISOLATED margin.** Each leg should be independent — if one side gets liquidated, the other side survives.
|
|
312
318
|
|
|
313
319
|
```bash
|
|
314
|
-
perp --json manage margin <SYM> isolated
|
|
320
|
+
perp --json -e <EX> wallet manage margin <SYM> isolated # set isolated margin
|
|
315
321
|
perp --json -e <EX> trade leverage <SYM> <LEV> --isolated # set leverage + isolated at once
|
|
316
322
|
```
|
|
317
323
|
|
|
@@ -407,8 +413,8 @@ Rules of thumb:
|
|
|
407
413
|
|
|
408
414
|
Each exchange holds its own separate balance. Before entering any arb:
|
|
409
415
|
```bash
|
|
410
|
-
perp --json -e <EX_A>
|
|
411
|
-
perp --json -e <EX_B>
|
|
416
|
+
perp --json portfolio -e <EX_A> # check available balance on exchange A
|
|
417
|
+
perp --json portfolio -e <EX_B> # check available balance on exchange B
|
|
412
418
|
```
|
|
413
419
|
|
|
414
420
|
**Size each leg to fit the available balance on that exchange:**
|
|
@@ -428,15 +434,12 @@ Even "market-neutral" arb can lose money if:
|
|
|
428
434
|
- Extreme funding spike in the wrong direction
|
|
429
435
|
- Slippage on entry/exit far exceeds estimates
|
|
430
436
|
|
|
431
|
-
**Always set stop losses on both legs
|
|
432
|
-
```bash
|
|
433
|
-
perp --json -e <EX_A> trade sl <SYM> <PRICE> # stop loss on leg A
|
|
434
|
-
perp --json -e <EX_B> trade sl <SYM> <PRICE> # stop loss on leg B
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
Or use TP/SL together:
|
|
437
|
+
**Always set stop losses on both legs** (`long|short` = the OPEN position's side; legs are reduce-only, sized to the live position):
|
|
438
438
|
```bash
|
|
439
|
-
perp --json -e <
|
|
439
|
+
perp --json --dry-run -e <EX_A> trade tpsl <SYM> long --sl <PRICE> # leg A (long): SL below mark
|
|
440
|
+
perp --json -e <EX_A> trade tpsl <SYM> long --sl <PRICE>
|
|
441
|
+
perp --json -e <EX_B> trade tpsl <SYM> short --sl <PRICE> # leg B (short): SL above mark
|
|
442
|
+
perp --json -e <EX> trade tpsl <SYM> long --sl <PRICE> --tp <PRICE> # TP + SL together
|
|
440
443
|
```
|
|
441
444
|
|
|
442
445
|
### What to Track Over Time
|
|
@@ -13,6 +13,11 @@ else
|
|
|
13
13
|
PERP="npx -y perp-cli@latest"
|
|
14
14
|
fi
|
|
15
15
|
|
|
16
|
+
# perp exits 1 on {"ok":false} and prints exactly one JSON document: capture it as-is
|
|
17
|
+
# (never append a fallback document); empty output (crash / not installed) → {"ok":false}.
|
|
18
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
19
|
+
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
20
|
+
|
|
16
21
|
JSON_MODE=false
|
|
17
22
|
MIN_SPREAD=5
|
|
18
23
|
while [[ $# -gt 0 ]]; do
|
|
@@ -24,8 +29,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
24
29
|
done
|
|
25
30
|
|
|
26
31
|
# 1. Get current arb status (already includes funding earned per period)
|
|
27
|
-
ARB_STATUS=$(
|
|
28
|
-
if !
|
|
32
|
+
ARB_STATUS=$(pj --json arb status)
|
|
33
|
+
if ! isok "$ARB_STATUS"; then
|
|
29
34
|
if $JSON_MODE; then
|
|
30
35
|
echo '{"ok":false,"error":"No arb positions or arb status unavailable"}'
|
|
31
36
|
else
|
|
@@ -35,13 +40,13 @@ if ! echo "$ARB_STATUS" | grep -q '"ok":true'; then
|
|
|
35
40
|
fi
|
|
36
41
|
|
|
37
42
|
# 2. Get current spreads (all modes)
|
|
38
|
-
ARB_SCAN=$(
|
|
43
|
+
ARB_SCAN=$(pj --json arb scan --mode all --min 0)
|
|
39
44
|
|
|
40
45
|
# 3. Get liquidation distances
|
|
41
|
-
LIQ_DIST=$(
|
|
46
|
+
LIQ_DIST=$(pj --json risk liquidation-distance)
|
|
42
47
|
|
|
43
48
|
# 4. Get risk overview
|
|
44
|
-
RISK_STATUS=$(
|
|
49
|
+
RISK_STATUS=$(pj --json risk status)
|
|
45
50
|
|
|
46
51
|
# 5. Output combined analysis
|
|
47
52
|
if $JSON_MODE; then
|
|
@@ -12,6 +12,11 @@ else
|
|
|
12
12
|
PERP="npx -y perp-cli@latest"
|
|
13
13
|
fi
|
|
14
14
|
|
|
15
|
+
# perp exits 1 on {"ok":false} and prints exactly one JSON document: capture it as-is
|
|
16
|
+
# (never append a fallback document); empty output (crash / not installed) → {"ok":false}.
|
|
17
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
18
|
+
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
19
|
+
|
|
15
20
|
JSON_MODE=false
|
|
16
21
|
SYMBOL=""
|
|
17
22
|
TOP=10
|
|
@@ -26,10 +31,10 @@ done
|
|
|
26
31
|
|
|
27
32
|
if [[ -n "$SYMBOL" ]]; then
|
|
28
33
|
# Single symbol detailed analysis
|
|
29
|
-
FUNDING=$(
|
|
30
|
-
COMPARE=$(
|
|
31
|
-
SCAN=$(
|
|
32
|
-
SPOT_SCAN=$(
|
|
34
|
+
FUNDING=$(pj --json arb scan --history "$SYMBOL")
|
|
35
|
+
COMPARE=$(pj --json arb scan --compare "$SYMBOL")
|
|
36
|
+
SCAN=$(pj --json arb scan --mode perp-perp --min 0)
|
|
37
|
+
SPOT_SCAN=$(pj --json arb scan --mode spot-perp --min 0)
|
|
33
38
|
|
|
34
39
|
if $JSON_MODE; then
|
|
35
40
|
cat <<EOF
|
|
@@ -55,19 +60,19 @@ EOF
|
|
|
55
60
|
echo "--- Perp-Perp Arb ---"
|
|
56
61
|
echo "$SCAN" | jq -r '
|
|
57
62
|
.data // [] | map(select(.symbol == "'"$SYMBOL"'")) | .[] |
|
|
58
|
-
" \(.longExch // .longExchange)→\(.shortExch // .shortExchange): \(.netSpread
|
|
63
|
+
" \(.longExch // .longExchange)→\(.shortExch // .shortExchange): net \(.netSpread)% annual"
|
|
59
64
|
' 2>/dev/null || echo " (no opportunity)"
|
|
60
65
|
echo ""
|
|
61
66
|
echo "--- Spot+Perp Arb ---"
|
|
62
67
|
echo "$SPOT_SCAN" | jq -r '
|
|
63
68
|
.data // [] | map(select(.symbol == "'"$SYMBOL"'")) | .[] |
|
|
64
|
-
" \(.direction)
|
|
69
|
+
" \(.direction) spot:\((.spotExchanges // []) | join("/"))→\(.perpExchange): net \(.netSpreadPct)% annual"
|
|
65
70
|
' 2>/dev/null || echo " (no opportunity)"
|
|
66
71
|
fi
|
|
67
72
|
else
|
|
68
73
|
# All symbols scan
|
|
69
|
-
SCAN=$(
|
|
70
|
-
FUNDING_DETAIL=$(
|
|
74
|
+
SCAN=$(pj --json arb scan --mode all --min 0)
|
|
75
|
+
FUNDING_DETAIL=$(pj --json arb scan --rates)
|
|
71
76
|
|
|
72
77
|
if $JSON_MODE; then
|
|
73
78
|
cat <<EOF
|
|
@@ -84,7 +89,7 @@ EOF
|
|
|
84
89
|
echo ""
|
|
85
90
|
echo "$SCAN" | jq -r "
|
|
86
91
|
.data // [] | .[0:$TOP] | .[] |
|
|
87
|
-
\" \(.symbol) | \(.mode // \"perp\") | \(.netSpread // .
|
|
92
|
+
\" \(.symbol) | \(.mode // \"perp\") | net \(.netSpread // .netSpreadPct)%/yr | \(.longExch // (\"spot:\" + ((.spotExchanges // []) | join(\"/\"))))→\(.shortExch // .perpExchange)\"
|
|
88
93
|
" 2>/dev/null || echo "(no data)"
|
|
89
94
|
fi
|
|
90
95
|
fi
|
|
@@ -13,6 +13,11 @@ else
|
|
|
13
13
|
PERP="npx -y perp-cli@latest"
|
|
14
14
|
fi
|
|
15
15
|
|
|
16
|
+
# perp exits 1 on {"ok":false} and prints exactly one JSON document: capture it as-is
|
|
17
|
+
# (never append a fallback document); empty output (crash / not installed) → {"ok":false}.
|
|
18
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
19
|
+
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
20
|
+
|
|
16
21
|
JSON_MODE=false
|
|
17
22
|
[[ "${1:-}" == "--json" ]] && JSON_MODE=true
|
|
18
23
|
|
|
@@ -22,7 +27,7 @@ checks=()
|
|
|
22
27
|
add_check() {
|
|
23
28
|
local name="$1" status="$2" detail="$3"
|
|
24
29
|
checks+=("{\"name\":\"$name\",\"status\":\"$status\",\"detail\":\"$detail\"}")
|
|
25
|
-
[[ "$status" == "fail" ]]
|
|
30
|
+
if [[ "$status" == "fail" ]]; then issues+=("$name: $detail"); fi
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
# 1. perp-cli installed and version check
|
|
@@ -30,38 +35,39 @@ VERSION=$($PERP --version 2>/dev/null || echo "unknown")
|
|
|
30
35
|
if [[ "$VERSION" != "unknown" ]]; then
|
|
31
36
|
add_check "install" "pass" "perp-cli $VERSION ($(command -v perp &>/dev/null && echo 'global' || echo 'npx'))"
|
|
32
37
|
else
|
|
33
|
-
add_check "install" "fail" "perp-cli not found —
|
|
38
|
+
add_check "install" "fail" "perp-cli not found — ask the user before installing (npm i -g perp-cli@latest) or use npx"
|
|
34
39
|
fi
|
|
35
40
|
|
|
36
41
|
# 2. Wallet configured
|
|
37
|
-
WALLET_OUT=$(
|
|
38
|
-
if
|
|
42
|
+
WALLET_OUT=$(pj --json wallet show)
|
|
43
|
+
if isok "$WALLET_OUT"; then
|
|
39
44
|
add_check "wallet" "pass" "Wallet configured"
|
|
40
45
|
else
|
|
41
|
-
add_check "wallet" "fail" "No wallet configured — run: perp wallet set <exchange>
|
|
46
|
+
add_check "wallet" "fail" "No wallet configured — run: perp wallet set <exchange> (key via hidden prompt or stdin)"
|
|
42
47
|
fi
|
|
43
48
|
|
|
44
49
|
# 3. Exchange connectivity (proxy via unauthenticated market prices fetch)
|
|
45
|
-
HEALTH_OUT=$(
|
|
46
|
-
if
|
|
50
|
+
HEALTH_OUT=$(pj --json market prices)
|
|
51
|
+
if isok "$HEALTH_OUT"; then
|
|
47
52
|
add_check "connectivity" "pass" "Exchange reachable"
|
|
48
53
|
else
|
|
49
54
|
add_check "connectivity" "warn" "Exchange unreachable"
|
|
50
55
|
fi
|
|
51
56
|
|
|
52
57
|
# 4. Portfolio & balances
|
|
53
|
-
PORTFOLIO_OUT=$(
|
|
54
|
-
if
|
|
58
|
+
PORTFOLIO_OUT=$(pj --json portfolio)
|
|
59
|
+
if isok "$PORTFOLIO_OUT"; then
|
|
55
60
|
add_check "portfolio" "pass" "Portfolio accessible"
|
|
56
61
|
else
|
|
57
62
|
add_check "portfolio" "warn" "Could not fetch portfolio"
|
|
58
63
|
fi
|
|
59
64
|
|
|
60
65
|
# 5. Risk status
|
|
61
|
-
RISK_OUT=$(
|
|
62
|
-
|
|
66
|
+
RISK_OUT=$(pj --json risk status)
|
|
67
|
+
CAN_TRADE=$(jq -r '.data.canTrade // empty' <<<"$RISK_OUT" 2>/dev/null || true)
|
|
68
|
+
if [[ "$CAN_TRADE" == "true" ]]; then
|
|
63
69
|
add_check "risk" "pass" "Trading allowed (canTrade=true)"
|
|
64
|
-
elif
|
|
70
|
+
elif [[ "$CAN_TRADE" == "false" ]]; then
|
|
65
71
|
add_check "risk" "fail" "Trading blocked — check risk violations"
|
|
66
72
|
else
|
|
67
73
|
add_check "risk" "warn" "Could not check risk status"
|
|
@@ -69,11 +75,12 @@ fi
|
|
|
69
75
|
|
|
70
76
|
# Output
|
|
71
77
|
READY=true
|
|
72
|
-
[[ ${#issues[@]} -gt 0 ]]
|
|
78
|
+
if [[ ${#issues[@]} -gt 0 ]]; then READY=false; fi
|
|
73
79
|
|
|
74
80
|
if $JSON_MODE; then
|
|
75
81
|
CHECKS_JSON=$(IFS=,; echo "${checks[*]}")
|
|
76
|
-
ISSUES_JSON
|
|
82
|
+
ISSUES_JSON='[]'
|
|
83
|
+
if [[ ${#issues[@]} -gt 0 ]]; then ISSUES_JSON=$(printf '%s\n' "${issues[@]}" | jq -R . | jq -s .); fi
|
|
77
84
|
echo "{\"ready\":$READY,\"checks\":[$CHECKS_JSON],\"issues\":$ISSUES_JSON}"
|
|
78
85
|
else
|
|
79
86
|
echo "=== perp-cli Pre-flight Check ==="
|
|
@@ -12,6 +12,11 @@ else
|
|
|
12
12
|
PERP="npx -y perp-cli@latest"
|
|
13
13
|
fi
|
|
14
14
|
|
|
15
|
+
# perp exits 1 on {"ok":false} and prints exactly one JSON document: capture it as-is
|
|
16
|
+
# (never append a fallback document); empty output (crash / not installed) → {"ok":false}.
|
|
17
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
18
|
+
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
19
|
+
|
|
15
20
|
JSON_MODE=false
|
|
16
21
|
MODE="all"
|
|
17
22
|
MIN_SPREAD=10
|
|
@@ -25,8 +30,8 @@ while [[ $# -gt 0 ]]; do
|
|
|
25
30
|
done
|
|
26
31
|
|
|
27
32
|
# 1. Portfolio check (ensure we have balances)
|
|
28
|
-
PORTFOLIO=$(
|
|
29
|
-
if !
|
|
33
|
+
PORTFOLIO=$(pj --json portfolio)
|
|
34
|
+
if ! isok "$PORTFOLIO"; then
|
|
30
35
|
if $JSON_MODE; then
|
|
31
36
|
echo '{"ok":false,"error":"Cannot fetch portfolio — check wallet setup"}'
|
|
32
37
|
else
|
|
@@ -36,10 +41,10 @@ if ! echo "$PORTFOLIO" | grep -q '"ok":true'; then
|
|
|
36
41
|
fi
|
|
37
42
|
|
|
38
43
|
# 2. Run appropriate scan
|
|
39
|
-
SCAN_RESULT=$(
|
|
44
|
+
SCAN_RESULT=$(pj --json arb scan --mode "$MODE" --min "$MIN_SPREAD")
|
|
40
45
|
|
|
41
46
|
# 3. Get exchange health for context (proxy via market prices fetch)
|
|
42
|
-
HEALTH=$(
|
|
47
|
+
HEALTH=$(pj --json market prices)
|
|
43
48
|
|
|
44
49
|
if $JSON_MODE; then
|
|
45
50
|
cat <<EOF
|
|
@@ -55,14 +60,14 @@ if $JSON_MODE; then
|
|
|
55
60
|
EOF
|
|
56
61
|
else
|
|
57
62
|
echo "=== Spot+Perp Arb Scanner ==="
|
|
58
|
-
echo "Mode: $MODE | Min spread: ${MIN_SPREAD}
|
|
63
|
+
echo "Mode: $MODE | Min spread: ${MIN_SPREAD}% annual"
|
|
59
64
|
echo ""
|
|
60
65
|
|
|
61
66
|
# Show balances
|
|
62
67
|
echo "--- Balances ---"
|
|
63
68
|
echo "$PORTFOLIO" | jq -r '
|
|
64
69
|
.data.exchanges // [] | .[] |
|
|
65
|
-
" \(.
|
|
70
|
+
" \(.name): equity $\(.perp.equity // "N/A")"
|
|
66
71
|
' 2>/dev/null || echo " (unable to parse)"
|
|
67
72
|
echo ""
|
|
68
73
|
|
|
@@ -70,7 +75,7 @@ else
|
|
|
70
75
|
echo "--- Opportunities ---"
|
|
71
76
|
echo "$SCAN_RESULT" | jq -r '
|
|
72
77
|
.data // [] | .[] |
|
|
73
|
-
" \(.symbol) | \(.mode // "perp-perp") |
|
|
78
|
+
" \(.symbol) | \(.mode // "perp-perp") | net: \(.netSpread // .netSpreadPct)%/yr | \(.longExch // ("spot:" + ((.spotExchanges // []) | join("/"))))→\(.shortExch // .perpExchange)"
|
|
74
79
|
' 2>/dev/null || echo " (no opportunities or parse error)"
|
|
75
80
|
|
|
76
81
|
# Show exchange health
|
|
@@ -14,6 +14,11 @@ else
|
|
|
14
14
|
PERP="npx -y perp-cli@latest"
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
|
+
# perp exits 1 on {"ok":false} and prints exactly one JSON document: capture it as-is
|
|
18
|
+
# (never append a fallback document); empty output (crash / not installed) → {"ok":false}.
|
|
19
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
20
|
+
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
21
|
+
|
|
17
22
|
SYM="${1:?Usage: validate-arb.sh <SYMBOL> <LONG_EX> <SHORT_EX> <SIZE_USD> [--leverage N]}"
|
|
18
23
|
LONG_EX="${2:?Usage: validate-arb.sh <SYMBOL> <LONG_EX> <SHORT_EX> <SIZE_USD>}"
|
|
19
24
|
SHORT_EX="${3:?Usage: validate-arb.sh <SYMBOL> <LONG_EX> <SHORT_EX> <SIZE_USD>}"
|
|
@@ -34,12 +39,22 @@ pass=true
|
|
|
34
39
|
add_result() {
|
|
35
40
|
local name="$1" ok="$2" detail="$3"
|
|
36
41
|
checks+=("{\"check\":\"$name\",\"ok\":$ok,\"detail\":\"$detail\"}")
|
|
37
|
-
[[ "$ok" == "false" ]]
|
|
42
|
+
if [[ "$ok" == "false" ]]; then pass=false; fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Venue of the long leg (spot:<venue> → <venue>) for the per-venue reads below
|
|
46
|
+
LONG_VENUE="${LONG_EX#spot:}"
|
|
47
|
+
# portfolio -e accepts only full venue ids (no aliases)
|
|
48
|
+
venue_id() {
|
|
49
|
+
case "$1" in
|
|
50
|
+
pac) echo pacifica ;; hl) echo hyperliquid ;; lt) echo lighter ;; lrh|rh) echo lighter-rh ;;
|
|
51
|
+
ast) echo aster ;; ondo-perps) echo ondo ;; qf) echo qfex ;; *) echo "$1" ;;
|
|
52
|
+
esac
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
# 1. Price comparison — both exchanges must report similar prices
|
|
41
|
-
LONG_PRICE=$(
|
|
42
|
-
SHORT_PRICE=$(
|
|
56
|
+
LONG_PRICE=$(pj --json -e "$LONG_VENUE" market mid "$SYM" | jq -r '.data.mid // "0"' 2>/dev/null || echo 0)
|
|
57
|
+
SHORT_PRICE=$(pj --json -e "$SHORT_EX" market mid "$SYM" | jq -r '.data.mid // "0"' 2>/dev/null || echo 0)
|
|
43
58
|
|
|
44
59
|
if [[ "$LONG_PRICE" == "0" || "$SHORT_PRICE" == "0" ]]; then
|
|
45
60
|
add_result "price" "false" "Cannot fetch price: long=$LONG_PRICE short=$SHORT_PRICE"
|
|
@@ -54,8 +69,8 @@ else
|
|
|
54
69
|
fi
|
|
55
70
|
|
|
56
71
|
# 2. Balance check on both exchanges
|
|
57
|
-
LONG_BAL=$(
|
|
58
|
-
SHORT_BAL=$(
|
|
72
|
+
LONG_BAL=$(pj --json portfolio -e "$(venue_id "$LONG_VENUE")" | jq -r '.data.exchanges[0].perp.available // .data.exchanges[0].perp.equity // "0"' 2>/dev/null || echo 0)
|
|
73
|
+
SHORT_BAL=$(pj --json portfolio -e "$(venue_id "$SHORT_EX")" | jq -r '.data.exchanges[0].perp.available // .data.exchanges[0].perp.equity // "0"' 2>/dev/null || echo 0)
|
|
59
74
|
|
|
60
75
|
NEEDED=$(echo "$SIZE" | awk '{print $1}')
|
|
61
76
|
if (( $(echo "$LONG_BAL < $NEEDED" | bc -l 2>/dev/null || echo 1) )); then
|
|
@@ -70,31 +85,32 @@ else
|
|
|
70
85
|
fi
|
|
71
86
|
|
|
72
87
|
# 3. Market info (symbol availability)
|
|
73
|
-
LONG_INFO=$(
|
|
74
|
-
SHORT_INFO=$(
|
|
88
|
+
LONG_INFO=$(pj --json -e "$LONG_VENUE" market info "$SYM")
|
|
89
|
+
SHORT_INFO=$(pj --json -e "$SHORT_EX" market info "$SYM")
|
|
75
90
|
|
|
76
|
-
if
|
|
91
|
+
if isok "$LONG_INFO" && isok "$SHORT_INFO"; then
|
|
77
92
|
add_result "market_info" "true" "Both exchanges support $SYM"
|
|
78
93
|
else
|
|
79
94
|
add_result "market_info" "false" "Symbol $SYM not available on one or both exchanges"
|
|
80
95
|
fi
|
|
81
96
|
|
|
82
|
-
# 4. Risk pre-check
|
|
97
|
+
# 4. Risk pre-check (risk check requires --leverage; default 1x when not given)
|
|
83
98
|
LEV_FLAG=""
|
|
84
99
|
[[ -n "$LEVERAGE" ]] && LEV_FLAG="--leverage $LEVERAGE"
|
|
85
|
-
RISK_CHECK=$(
|
|
86
|
-
if
|
|
100
|
+
RISK_CHECK=$(pj --json risk check --notional "$SIZE" --leverage "${LEVERAGE:-1}")
|
|
101
|
+
if isok "$RISK_CHECK" && [[ "$(jq -r '.data.allowed' <<<"$RISK_CHECK" 2>/dev/null || true)" == "true" ]]; then
|
|
87
102
|
add_result "risk" "true" "Within risk limits"
|
|
88
103
|
else
|
|
89
|
-
|
|
104
|
+
RISK_WHY=$(jq -r '.data.reason // .error.code // "unknown"' <<<"$RISK_CHECK" 2>/dev/null || echo unknown)
|
|
105
|
+
add_result "risk" "false" "Risk check failed: $RISK_WHY"
|
|
90
106
|
fi
|
|
91
107
|
|
|
92
108
|
# 5. Dry-run
|
|
93
|
-
DRY_RUN=$(
|
|
94
|
-
if
|
|
109
|
+
DRY_RUN=$(pj --json --dry-run arb exec "$SYM" "$LONG_EX" "$SHORT_EX" "$SIZE" $LEV_FLAG)
|
|
110
|
+
if isok "$DRY_RUN"; then
|
|
95
111
|
add_result "dry_run" "true" "Dry-run succeeded"
|
|
96
112
|
else
|
|
97
|
-
DRY_ERR=$(
|
|
113
|
+
DRY_ERR=$(jq -r '.error.code // "unknown"' <<<"$DRY_RUN" 2>/dev/null || echo unknown)
|
|
98
114
|
add_result "dry_run" "false" "Dry-run failed: $DRY_ERR"
|
|
99
115
|
fi
|
|
100
116
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
lighter-signer.wasm: elliottech/lighter-go release v1.0.9 (2026-09-14), asset "lighter-signer.wasm"
|
|
2
|
+
https://github.com/elliottech/lighter-go/releases/download/v1.0.9/lighter-signer.wasm
|
|
3
|
+
built from lighter-go 8854554 ("allow markets gr 4095 (#89)") with go1.25.6 (GOOS=js GOARCH=wasm)
|
|
4
|
+
sha256 matches the release asset digest published by GitHub.
|
|
5
|
+
wasm_exec.js: Go go1.25.6 lib/wasm/wasm_exec.js (the runtime glue matching the toolchain the wasm was built with)
|
|
6
|
+
https://github.com/golang/go/blob/go1.25.6/lib/wasm/wasm_exec.js
|
|
7
|
+
Functions/arguments: https://apidocs.lighter.xyz/docs/wasm-signer
|
|
8
|
+
Both files are verified against SHA256SUMS (also pinned in src/exchanges/lighter-signer.ts) before load; a mismatch refuses to sign.
|
|
Binary file
|