perp-cli 0.14.3 → 0.16.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.ja.md +243 -0
- package/README.ko.md +243 -0
- package/README.md +138 -405
- package/README.zh.md +243 -0
- package/dist/agent-wallet/store.d.ts +3 -2
- package/dist/agent-wallet/store.js +2 -2
- package/dist/api/public/hyperliquid.js +4 -0
- package/dist/api/public/lighter.d.ts +5 -0
- package/dist/api/public/lighter.js +5 -0
- package/dist/api/public/pacifica.js +85 -15
- package/dist/api/public/urls.d.ts +2 -0
- package/dist/api/public/urls.js +3 -0
- package/dist/arb/sizing.js +4 -2
- package/dist/arb/state.d.ts +6 -0
- package/dist/arb/utils.d.ts +1 -2
- package/dist/arb/utils.js +1 -2
- package/dist/balance-by-address.d.ts +39 -0
- package/dist/balance-by-address.js +44 -0
- package/dist/bot/apex/pulse.js +3 -0
- package/dist/bot/apex/radar.js +2 -1
- package/dist/bot/engine.d.ts +10 -1
- package/dist/bot/engine.js +14 -6
- package/dist/bot/indicators.d.ts +7 -0
- package/dist/bot/indicators.js +16 -0
- package/dist/bot/perp-order.d.ts +4 -4
- package/dist/bot/perp-order.js +5 -5
- package/dist/bot/quarantine.d.ts +1 -1
- package/dist/bot/quarantine.js +1 -1
- package/dist/bot/quote-rules.js +1 -1
- package/dist/bot/strategies/apex-strategy.js +3 -2
- package/dist/bot/strategies/engine-mm.js +6 -5
- package/dist/bot/strategies/funding-arb-strategy.js +12 -5
- package/dist/bot/strategies/funding-arb-utils.d.ts +15 -3
- package/dist/bot/strategies/funding-arb-utils.js +53 -20
- package/dist/bot/strategies/funding-auto.js +2 -2
- package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
- package/dist/bot/strategies/liquidation-mm.js +8 -2
- package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
- package/dist/bot/strategies/momentum-breakout.js +11 -3
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-arb-strategy.js +6 -4
- package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-executor.js +3 -3
- package/dist/bot/strategy-types.d.ts +2 -1
- package/dist/bot/tui/BotDashboard.d.ts +1 -1
- package/dist/bot/tui/BotDashboard.js +1 -1
- package/dist/cache.js +12 -6
- package/dist/cli-program.d.ts +15 -0
- package/dist/cli-program.js +126 -6
- package/dist/commands/account.js +168 -24
- package/dist/commands/agent-import.d.ts +7 -0
- package/dist/commands/agent-import.js +30 -1
- package/dist/commands/agent.d.ts +6 -3
- package/dist/commands/agent.js +160 -12
- package/dist/commands/arb/index.js +23 -81
- package/dist/commands/arb-auto.d.ts +24 -0
- package/dist/commands/arb-auto.js +175 -97
- package/dist/commands/arb.d.ts +20 -0
- package/dist/commands/arb.js +107 -163
- package/dist/commands/backtest.js +23 -21
- package/dist/commands/bot.js +1 -1
- package/dist/commands/builder.js +1 -1
- package/dist/commands/funds.js +113 -3
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +27 -7
- package/dist/commands/manage.js +23 -21
- package/dist/commands/market.d.ts +48 -0
- package/dist/commands/market.js +395 -100
- package/dist/commands/options.d.ts +14 -0
- package/dist/commands/options.js +151 -0
- package/dist/commands/pacifica-earn.d.ts +5 -0
- package/dist/commands/pacifica-earn.js +455 -0
- package/dist/commands/pacifica-orders.d.ts +14 -0
- package/dist/commands/pacifica-orders.js +142 -0
- package/dist/commands/portfolio.d.ts +16 -0
- package/dist/commands/portfolio.js +83 -55
- package/dist/commands/rebalance.d.ts +1 -0
- package/dist/commands/rebalance.js +15 -0
- package/dist/commands/risk.d.ts +11 -2
- package/dist/commands/risk.js +39 -44
- package/dist/commands/trade.d.ts +1 -0
- package/dist/commands/trade.js +67 -62
- package/dist/commands/wallet.js +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +15 -4
- package/dist/constants.js +6 -0
- package/dist/cross-chain-margin.d.ts +3 -1
- package/dist/cross-chain-margin.js +4 -2
- package/dist/dashboard/server.js +3 -3
- package/dist/dashboard/ui.js +9 -6
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +35 -3
- package/dist/exchanges/address.d.ts +8 -0
- package/dist/exchanges/address.js +47 -0
- package/dist/exchanges/arcus/adapter.d.ts +16 -12
- package/dist/exchanges/arcus/adapter.js +45 -18
- package/dist/exchanges/arcus/agent.d.ts +1 -1
- package/dist/exchanges/arcus/agent.js +1 -1
- package/dist/exchanges/arcus/client.js +1 -1
- package/dist/exchanges/arcus/public.js +3 -2
- package/dist/exchanges/arcus/signing.d.ts +2 -3
- package/dist/exchanges/arcus/signing.js +1 -1
- package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
- package/dist/exchanges/arcus/spot-chain.js +1 -1
- package/dist/exchanges/arcus/spot-router.d.ts +1 -1
- package/dist/exchanges/arcus/spot-router.js +2 -2
- package/dist/exchanges/arcus/spot.d.ts +2 -3
- package/dist/exchanges/arcus/spot.js +2 -3
- package/dist/exchanges/aster-spot.d.ts +3 -3
- package/dist/exchanges/aster-spot.js +6 -9
- package/dist/exchanges/aster-typed-data.d.ts +1 -1
- package/dist/exchanges/aster-typed-data.js +1 -1
- package/dist/exchanges/aster.d.ts +30 -10
- package/dist/exchanges/aster.js +153 -53
- package/dist/exchanges/builder-fee.d.ts +1 -1
- package/dist/exchanges/builder-fee.js +13 -2
- package/dist/exchanges/canonical-symbols.d.ts +8 -2
- package/dist/exchanges/canonical-symbols.js +46 -2
- package/dist/exchanges/capabilities.d.ts +2 -0
- package/dist/exchanges/capabilities.js +2 -1
- package/dist/exchanges/derive/client.d.ts +37 -0
- package/dist/exchanges/derive/client.js +135 -0
- package/dist/exchanges/derive/options.d.ts +73 -0
- package/dist/exchanges/derive/options.js +379 -0
- package/dist/exchanges/extended/adapter.d.ts +96 -0
- package/dist/exchanges/extended/adapter.js +241 -0
- package/dist/exchanges/extended/client.d.ts +38 -0
- package/dist/exchanges/extended/client.js +88 -0
- package/dist/exchanges/extended/public.d.ts +133 -0
- package/dist/exchanges/extended/public.js +436 -0
- package/dist/exchanges/grvt/adapter.d.ts +100 -0
- package/dist/exchanges/grvt/adapter.js +283 -0
- package/dist/exchanges/grvt/client.d.ts +25 -0
- package/dist/exchanges/grvt/client.js +86 -0
- package/dist/exchanges/grvt/public.d.ts +151 -0
- package/dist/exchanges/grvt/public.js +438 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +2 -0
- package/dist/exchanges/hyperliquid-outcome.js +29 -1
- package/dist/exchanges/hyperliquid-spot.d.ts +9 -1
- package/dist/exchanges/hyperliquid-spot.js +24 -16
- package/dist/exchanges/hyperliquid.d.ts +199 -22
- package/dist/exchanges/hyperliquid.js +550 -202
- package/dist/exchanges/index.d.ts +1 -1
- package/dist/exchanges/index.js +1 -1
- package/dist/exchanges/interface.d.ts +157 -13
- package/dist/exchanges/interface.js +47 -0
- package/dist/exchanges/ioc-outcome.d.ts +1 -1
- package/dist/exchanges/ioc-outcome.js +1 -1
- package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
- package/dist/exchanges/legacy-trading-v2.js +7 -2
- package/dist/exchanges/lighter-public.d.ts +67 -12
- package/dist/exchanges/lighter-public.js +250 -82
- package/dist/exchanges/lighter-signer.d.ts +1 -1
- package/dist/exchanges/lighter-signer.js +1 -1
- package/dist/exchanges/lighter-spot.d.ts +10 -5
- package/dist/exchanges/lighter-spot.js +105 -97
- package/dist/exchanges/lighter.d.ts +80 -21
- package/dist/exchanges/lighter.js +312 -105
- package/dist/exchanges/ondo/adapter.d.ts +38 -13
- package/dist/exchanges/ondo/adapter.js +86 -27
- package/dist/exchanges/ondo/agent.d.ts +1 -1
- package/dist/exchanges/ondo/agent.js +1 -1
- package/dist/exchanges/ondo/client.d.ts +1 -1
- package/dist/exchanges/ondo/client.js +2 -2
- package/dist/exchanges/ondo/public.d.ts +35 -3
- package/dist/exchanges/ondo/public.js +75 -32
- package/dist/exchanges/ondo/spot.d.ts +2 -2
- package/dist/exchanges/ondo/spot.js +3 -3
- package/dist/exchanges/options-interface.d.ts +158 -0
- package/dist/exchanges/options-interface.js +17 -0
- package/dist/exchanges/options-venues.d.ts +12 -0
- package/dist/exchanges/options-venues.js +35 -0
- package/dist/exchanges/outcome-interface.d.ts +4 -0
- package/dist/exchanges/pacifica-atomic.d.ts +211 -0
- package/dist/exchanges/pacifica-atomic.js +440 -0
- package/dist/exchanges/pacifica-decimal.d.ts +10 -0
- package/dist/exchanges/pacifica-decimal.js +51 -0
- package/dist/exchanges/pacifica-presigned.d.ts +53 -0
- package/dist/exchanges/pacifica-presigned.js +141 -0
- package/dist/exchanges/pacifica-print.d.ts +200 -0
- package/dist/exchanges/pacifica-print.js +417 -0
- package/dist/exchanges/pacifica-spot.d.ts +8 -2
- package/dist/exchanges/pacifica-spot.js +26 -18
- package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
- package/dist/exchanges/pacifica-typed-data.js +1 -1
- package/dist/exchanges/pacifica-vaults.d.ts +130 -0
- package/dist/exchanges/pacifica-vaults.js +277 -0
- package/dist/exchanges/pacifica.d.ts +108 -10
- package/dist/exchanges/pacifica.js +428 -171
- package/dist/exchanges/precision.d.ts +3 -1
- package/dist/exchanges/precision.js +9 -2
- package/dist/exchanges/public-balances.d.ts +25 -0
- package/dist/exchanges/public-balances.js +158 -0
- package/dist/exchanges/public-positions.d.ts +43 -0
- package/dist/exchanges/public-positions.js +154 -0
- package/dist/exchanges/qfex/adapter.d.ts +69 -13
- package/dist/exchanges/qfex/adapter.js +102 -17
- package/dist/exchanges/qfex/agent.d.ts +1 -1
- package/dist/exchanges/qfex/agent.js +1 -1
- package/dist/exchanges/qfex/client.d.ts +1 -1
- package/dist/exchanges/qfex/client.js +1 -1
- package/dist/exchanges/qfex/errors.d.ts +1 -1
- package/dist/exchanges/qfex/errors.js +2 -2
- package/dist/exchanges/qfex/public.d.ts +65 -12
- package/dist/exchanges/qfex/public.js +131 -25
- package/dist/exchanges/qfex/signing.d.ts +2 -2
- package/dist/exchanges/qfex/signing.js +4 -5
- package/dist/exchanges/qfex/ws-transport.d.ts +3 -3
- package/dist/exchanges/qfex/ws-transport.js +3 -3
- package/dist/exchanges/rate-limiter.d.ts +1 -1
- package/dist/exchanges/rate-limiter.js +5 -2
- package/dist/exchanges/registry.d.ts +1 -1
- package/dist/exchanges/registry.js +1 -1
- package/dist/exchanges/risex/adapter.d.ts +160 -0
- package/dist/exchanges/risex/adapter.js +842 -0
- package/dist/exchanges/risex/agent.d.ts +154 -0
- package/dist/exchanges/risex/agent.js +384 -0
- package/dist/exchanges/risex/client.d.ts +47 -0
- package/dist/exchanges/risex/client.js +174 -0
- package/dist/exchanges/risex/nonce.d.ts +43 -0
- package/dist/exchanges/risex/nonce.js +212 -0
- package/dist/exchanges/risex/public.d.ts +64 -0
- package/dist/exchanges/risex/public.js +150 -0
- package/dist/exchanges/risex/session.d.ts +71 -0
- package/dist/exchanges/risex/session.js +171 -0
- package/dist/exchanges/risex/typed-data.d.ts +173 -0
- package/dist/exchanges/risex/typed-data.js +240 -0
- package/dist/exchanges/risex/units.d.ts +29 -0
- package/dist/exchanges/risex/units.js +104 -0
- package/dist/exchanges/slippage.d.ts +1 -1
- package/dist/exchanges/slippage.js +1 -1
- package/dist/exchanges/spot-factory.d.ts +3 -3
- package/dist/exchanges/spot-factory.js +8 -8
- package/dist/exchanges/spot-interface.d.ts +5 -5
- package/dist/exchanges/spot-interface.js +2 -2
- package/dist/exchanges/spot-result.d.ts +3 -3
- package/dist/exchanges/spot-result.js +3 -3
- package/dist/exchanges/trading-v2.d.ts +3 -3
- package/dist/exchanges/trading-v2.js +9 -6
- package/dist/exchanges/txflow/adapter.d.ts +61 -0
- package/dist/exchanges/txflow/adapter.js +150 -0
- package/dist/exchanges/txflow/client.d.ts +47 -0
- package/dist/exchanges/txflow/client.js +155 -0
- package/dist/exchanges/txflow/public.d.ts +87 -0
- package/dist/exchanges/txflow/public.js +355 -0
- package/dist/exchanges/unknown-symbol.d.ts +34 -0
- package/dist/exchanges/unknown-symbol.js +56 -0
- package/dist/exchanges/variational/adapter.d.ts +67 -0
- package/dist/exchanges/variational/adapter.js +137 -0
- package/dist/exchanges/variational/client.d.ts +21 -0
- package/dist/exchanges/variational/client.js +66 -0
- package/dist/exchanges/variational/public.d.ts +126 -0
- package/dist/exchanges/variational/public.js +430 -0
- package/dist/exchanges/venue-types.d.ts +52 -10
- package/dist/exchanges/venue-types.js +82 -11
- package/dist/exchanges/venue.d.ts +150 -11
- package/dist/exchanges/venue.js +126 -4
- package/dist/exchanges/venues/arcus.d.ts +1 -2
- package/dist/exchanges/venues/arcus.js +5 -4
- package/dist/exchanges/venues/aster.d.ts +2 -2
- package/dist/exchanges/venues/aster.js +6 -4
- package/dist/exchanges/venues/create-helpers.d.ts +1 -1
- package/dist/exchanges/venues/create-helpers.js +3 -3
- package/dist/exchanges/venues/extended.d.ts +21 -0
- package/dist/exchanges/venues/extended.js +96 -0
- package/dist/exchanges/venues/grvt.d.ts +30 -0
- package/dist/exchanges/venues/grvt.js +98 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
- package/dist/exchanges/venues/hyperliquid.js +19 -8
- package/dist/exchanges/venues/index.d.ts +7 -2
- package/dist/exchanges/venues/index.js +8 -2
- package/dist/exchanges/venues/lighter-family.d.ts +7 -9
- package/dist/exchanges/venues/lighter-family.js +9 -11
- package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
- package/dist/exchanges/venues/lighter-rh.js +20 -8
- package/dist/exchanges/venues/lighter.d.ts +1 -1
- package/dist/exchanges/venues/lighter.js +18 -6
- package/dist/exchanges/venues/ondo.d.ts +1 -1
- package/dist/exchanges/venues/ondo.js +5 -3
- package/dist/exchanges/venues/pacifica.d.ts +2 -2
- package/dist/exchanges/venues/pacifica.js +15 -4
- package/dist/exchanges/venues/qfex.d.ts +1 -1
- package/dist/exchanges/venues/qfex.js +4 -2
- package/dist/exchanges/venues/risex-release.d.ts +14 -0
- package/dist/exchanges/venues/risex-release.js +14 -0
- package/dist/exchanges/venues/risex.d.ts +31 -0
- package/dist/exchanges/venues/risex.js +131 -0
- package/dist/exchanges/venues/txflow.d.ts +18 -0
- package/dist/exchanges/venues/txflow.js +77 -0
- package/dist/exchanges/venues/variational.d.ts +21 -0
- package/dist/exchanges/venues/variational.js +85 -0
- package/dist/execution-log.d.ts +1 -1
- package/dist/funding/legacy-public.d.ts +6 -4
- package/dist/funding/legacy-public.js +42 -41
- package/dist/funding/normalize.d.ts +1 -1
- package/dist/funding/normalize.js +1 -1
- package/dist/funding/rates.d.ts +95 -6
- package/dist/funding/rates.js +99 -19
- package/dist/index.js +7 -6
- package/dist/jobs.d.ts +1 -0
- package/dist/jobs.js +25 -1
- package/dist/landing.d.ts +2 -0
- package/dist/landing.js +1 -1
- package/dist/liquidity.d.ts +3 -1
- package/dist/liquidity.js +8 -4
- package/dist/mcp-pacifica-earn.d.ts +9 -0
- package/dist/mcp-pacifica-earn.js +64 -0
- package/dist/mcp-server.js +211 -68
- package/dist/pacifica/client.d.ts +69 -5
- package/dist/pacifica/client.js +181 -8
- package/dist/pacifica/rate-limit.d.ts +144 -0
- package/dist/pacifica/rate-limit.js +267 -0
- package/dist/pacifica/types/order.d.ts +2 -1
- package/dist/plan-executor.js +7 -2
- package/dist/positions-by-address.d.ts +34 -0
- package/dist/positions-by-address.js +46 -0
- package/dist/rebalance.js +1 -1
- package/dist/risk.d.ts +2 -1
- package/dist/risk.js +2 -4
- package/dist/settings.d.ts +32 -3
- package/dist/signer/ows-evm.d.ts +1 -1
- package/dist/signer/ows-evm.js +1 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/smart-order.js +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +36 -63
- package/dist/trade-validator.js +2 -2
- package/dist/utils.d.ts +22 -3
- package/dist/utils.js +11 -3
- package/dist/venue-health.d.ts +40 -0
- package/dist/venue-health.js +75 -0
- package/dist/withdraw-gate.d.ts +1 -1
- package/dist/withdraw-gate.js +1 -1
- package/dist/ws/arcus-market.d.ts +9 -0
- package/dist/ws/arcus-market.js +9 -0
- package/dist/ws/aster-market.d.ts +33 -0
- package/dist/ws/aster-market.js +30 -0
- package/dist/ws/index.d.ts +980 -0
- package/dist/ws/index.js +4460 -0
- package/dist/ws/lighter-market.d.ts +14 -0
- package/dist/ws/lighter-market.js +13 -0
- package/dist/ws/node.d.ts +996 -0
- package/dist/ws/node.js +4554 -0
- package/dist/ws/pacifica-instrument.d.ts +8 -0
- package/dist/ws/pacifica-instrument.js +8 -0
- package/dist/ws/unclassified.d.ts +16 -0
- package/dist/ws/unclassified.js +43 -0
- package/dist/ws-cli/credentials.d.ts +62 -0
- package/dist/ws-cli/credentials.js +126 -0
- package/package.json +71 -10
- package/skills/perp-cli/SKILL.md +153 -36
- package/skills/perp-cli/package.json +15 -2
- package/skills/perp-cli/references/agent-operations.md +22 -12
- package/skills/perp-cli/references/commands.md +143 -17
- package/skills/perp-cli/references/strategies.md +38 -3
- package/skills/perp-cli/references/ws-library.md +523 -0
- package/skills/perp-cli/scripts/arb-monitor.sh +13 -10
- package/skills/perp-cli/scripts/funding-analysis.sh +57 -29
- package/skills/perp-cli/scripts/preflight.sh +27 -19
- package/skills/perp-cli/scripts/spot-perp-scan.sh +58 -38
- package/skills/perp-cli/scripts/validate-arb.sh +56 -39
- package/dist/cli-spec.d.ts +0 -46
- package/dist/cli-spec.js +0 -81
|
@@ -1,73 +1,103 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Funding rate analysis across all exchanges
|
|
3
|
-
# Usage: ./funding-analysis.sh [--json] [--symbol ETH] [--top 10]
|
|
4
|
-
#
|
|
3
|
+
# Usage: ./funding-analysis.sh [--json] [--symbol ETH] [--top 10] [--hours 24] [-n mainnet|testnet]
|
|
4
|
+
# --symbol: cross-venue compare + each verified venue's SETTLED funding history
|
|
5
|
+
# (`-e <venue> market funding <SYM> -l <hours>`, averaged per venue). `arb scan --history` is only
|
|
6
|
+
# the locally recorded snapshots (written by earlier `arb scan --rates` runs) and is often empty.
|
|
7
|
+
# Testnet funding / spreads are not meaningful — use mainnet (the default) for yield analysis.
|
|
5
8
|
|
|
6
9
|
set -euo pipefail
|
|
7
10
|
|
|
8
|
-
#
|
|
9
|
-
if command -v perp &>/dev/null; then
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
# perp must be installed (no npx / auto-install path: the user decides what gets installed).
|
|
12
|
+
if ! command -v perp &>/dev/null; then
|
|
13
|
+
echo "perp not found — ask the user to install perp-cli >= 0.16.0" >&2
|
|
14
|
+
echo '{"ok":false,"error":{"code":"PERP_NOT_FOUND","message":"perp not found — ask the user to install perp-cli >= 0.16.0"}}'
|
|
15
|
+
exit 1
|
|
13
16
|
fi
|
|
17
|
+
PERP="perp"
|
|
14
18
|
|
|
15
19
|
# 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
|
|
17
|
-
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
20
|
+
# (never append a fallback document); empty output (crash) → an explicit NO_OUTPUT error document.
|
|
21
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false,"error":{"code":"NO_OUTPUT","message":"perp printed nothing (crashed?)"}}'; fi; }
|
|
18
22
|
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
19
23
|
|
|
20
24
|
JSON_MODE=false
|
|
21
25
|
SYMBOL=""
|
|
22
26
|
TOP=10
|
|
27
|
+
HOURS=24
|
|
28
|
+
NETWORK="mainnet"
|
|
23
29
|
while [[ $# -gt 0 ]]; do
|
|
24
30
|
case "$1" in
|
|
25
31
|
--json) JSON_MODE=true; shift ;;
|
|
26
32
|
--symbol) SYMBOL="$2"; shift 2 ;;
|
|
27
33
|
--top) TOP="$2"; shift 2 ;;
|
|
28
|
-
|
|
34
|
+
--hours) HOURS="$2"; shift 2 ;;
|
|
35
|
+
-n|--network) NETWORK="$2"; shift 2 ;;
|
|
36
|
+
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
29
37
|
esac
|
|
30
38
|
done
|
|
39
|
+
PERP="perp -n $NETWORK"
|
|
40
|
+
|
|
41
|
+
# Row label per scan mode (perp-perp rows: longExch/shortExch; spot-perp rows: spotExchanges/perpExchange/direction)
|
|
42
|
+
ROW_JQ='if .mode == "spot-perp" then
|
|
43
|
+
" \(.symbol) | spot-perp | net \(.netSpread)%/yr | \(.direction) spot:\(.spotExchanges | join("/")) perp:\(.perpExchange)"
|
|
44
|
+
+ (if .direction == "long-spot-short-perp" then "" else " [NOT executable with arb exec]" end)
|
|
45
|
+
else
|
|
46
|
+
" \(.symbol) | perp-perp | net \(.netSpread)%/yr | long \(.longExch) / short \(.shortExch)"
|
|
47
|
+
end'
|
|
31
48
|
|
|
32
49
|
if [[ -n "$SYMBOL" ]]; then
|
|
33
50
|
# Single symbol detailed analysis
|
|
34
|
-
FUNDING=$(pj --json arb scan --history "$SYMBOL")
|
|
35
51
|
COMPARE=$(pj --json arb scan --compare "$SYMBOL")
|
|
52
|
+
LOCAL_HISTORY=$(pj --json arb scan --history "$SYMBOL" --hours "$HOURS")
|
|
36
53
|
SCAN=$(pj --json arb scan --mode perp-perp --min 0)
|
|
37
54
|
SPOT_SCAN=$(pj --json arb scan --mode spot-perp --min 0)
|
|
38
55
|
|
|
56
|
+
# Settled funding history per verified venue of the compare (rows newest first; rate = fraction per intervalHours)
|
|
57
|
+
SETTLED="[]"
|
|
58
|
+
if isok "$COMPARE" && [[ "$(jq -r '.data.available' <<<"$COMPARE")" != "false" ]]; then
|
|
59
|
+
for VENUE in $(jq -r '.data.rates[] | select(.fundingVerified) | .exchange' <<<"$COMPARE"); do
|
|
60
|
+
H=$(pj --json -e "$VENUE" market funding "$SYMBOL" -l "$HOURS")
|
|
61
|
+
ROW=$(jq -c --arg v "$VENUE" '
|
|
62
|
+
if .ok then
|
|
63
|
+
{ exchange: $v, rows: (.data | length),
|
|
64
|
+
avgHourlyRate: (if (.data | length) > 0 and all(.data[]; .rate != null and .intervalHours != null)
|
|
65
|
+
then ([.data[] | ((.rate | tonumber) / .intervalHours)] | add / length) else null end) }
|
|
66
|
+
| .avgAnnualPct = (if .avgHourlyRate == null then null else .avgHourlyRate * 8760 * 100 end)
|
|
67
|
+
else { exchange: $v, error: .error.code, message: .error.message } end' <<<"$H")
|
|
68
|
+
SETTLED=$(jq -c --argjson r "$ROW" '. + [$r]' <<<"$SETTLED")
|
|
69
|
+
done
|
|
70
|
+
fi
|
|
71
|
+
|
|
39
72
|
if $JSON_MODE; then
|
|
40
73
|
cat <<EOF
|
|
41
74
|
{
|
|
42
75
|
"ok": true,
|
|
76
|
+
"network": "$NETWORK",
|
|
43
77
|
"symbol": "$SYMBOL",
|
|
78
|
+
"hours": $HOURS,
|
|
44
79
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
45
|
-
"fundingHistory": $FUNDING,
|
|
46
80
|
"compare": $COMPARE,
|
|
81
|
+
"settledHistory": $SETTLED,
|
|
82
|
+
"localSnapshotHistory": $LOCAL_HISTORY,
|
|
47
83
|
"perpPerpArb": $SCAN,
|
|
48
84
|
"spotPerpArb": $SPOT_SCAN
|
|
49
85
|
}
|
|
50
86
|
EOF
|
|
51
87
|
else
|
|
52
|
-
echo "=== Funding Analysis: $SYMBOL ==="
|
|
88
|
+
echo "=== Funding Analysis: $SYMBOL ($NETWORK) ==="
|
|
53
89
|
echo ""
|
|
54
90
|
echo "--- Cross-Exchange Compare ---"
|
|
55
|
-
|
|
91
|
+
if isok "$COMPARE"; then jq -r '.data' <<<"$COMPARE"; else echo " ERROR: $(jq -r '.error.code' <<<"$COMPARE")"; fi
|
|
56
92
|
echo ""
|
|
57
|
-
echo "---
|
|
58
|
-
|
|
93
|
+
echo "--- Settled funding, last $HOURS rows per venue (avg annualized %) ---"
|
|
94
|
+
jq -r '.[] | if .error then " \(.exchange): ERROR \(.error)" else " \(.exchange): \(.rows) rows, avg \(.avgAnnualPct)%/yr" end' <<<"$SETTLED"
|
|
59
95
|
echo ""
|
|
60
96
|
echo "--- Perp-Perp Arb ---"
|
|
61
|
-
|
|
62
|
-
.data // [] | map(select(.symbol == "'"$SYMBOL"'")) | .[] |
|
|
63
|
-
" \(.longExch // .longExchange)→\(.shortExch // .shortExchange): net \(.netSpread)% annual"
|
|
64
|
-
' 2>/dev/null || echo " (no opportunity)"
|
|
97
|
+
if isok "$SCAN"; then jq -r --arg s "$SYMBOL" ".data[] | select(.symbol == \$s) | $ROW_JQ" <<<"$SCAN"; else echo " ERROR: $(jq -r '.error.code' <<<"$SCAN")"; fi
|
|
65
98
|
echo ""
|
|
66
99
|
echo "--- Spot+Perp Arb ---"
|
|
67
|
-
|
|
68
|
-
.data // [] | map(select(.symbol == "'"$SYMBOL"'")) | .[] |
|
|
69
|
-
" \(.direction) spot:\((.spotExchanges // []) | join("/"))→\(.perpExchange): net \(.netSpreadPct)% annual"
|
|
70
|
-
' 2>/dev/null || echo " (no opportunity)"
|
|
100
|
+
if isok "$SPOT_SCAN"; then jq -r --arg s "$SYMBOL" ".data[] | select(.symbol == \$s) | $ROW_JQ" <<<"$SPOT_SCAN"; else echo " ERROR: $(jq -r '.error.code' <<<"$SPOT_SCAN")"; fi
|
|
71
101
|
fi
|
|
72
102
|
else
|
|
73
103
|
# All symbols scan
|
|
@@ -78,6 +108,7 @@ else
|
|
|
78
108
|
cat <<EOF
|
|
79
109
|
{
|
|
80
110
|
"ok": true,
|
|
111
|
+
"network": "$NETWORK",
|
|
81
112
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
82
113
|
"topN": $TOP,
|
|
83
114
|
"allOpportunities": $SCAN,
|
|
@@ -85,11 +116,8 @@ else
|
|
|
85
116
|
}
|
|
86
117
|
EOF
|
|
87
118
|
else
|
|
88
|
-
echo "=== Funding Rate Overview (top $TOP) ==="
|
|
119
|
+
echo "=== Funding Rate Overview (top $TOP, $NETWORK) ==="
|
|
89
120
|
echo ""
|
|
90
|
-
|
|
91
|
-
.data // [] | .[0:$TOP] | .[] |
|
|
92
|
-
\" \(.symbol) | \(.mode // \"perp\") | net \(.netSpread // .netSpreadPct)%/yr | \(.longExch // (\"spot:\" + ((.spotExchanges // []) | join(\"/\"))))→\(.shortExch // .perpExchange)\"
|
|
93
|
-
" 2>/dev/null || echo "(no data)"
|
|
121
|
+
if isok "$SCAN"; then jq -r ".data[0:$TOP][] | $ROW_JQ" <<<"$SCAN"; else echo " ERROR: $(jq -r '.error.code' <<<"$SCAN")"; fi
|
|
94
122
|
fi
|
|
95
123
|
fi
|
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
if command -v perp &>/dev/null; then
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
# perp must be installed (no npx / auto-install path: the user decides what gets installed).
|
|
10
|
+
if ! command -v perp &>/dev/null; then
|
|
11
|
+
echo "perp not found — ask the user to install perp-cli >= 0.16.0" >&2
|
|
12
|
+
echo '{"ok":false,"error":{"code":"PERP_NOT_FOUND","message":"perp not found — ask the user to install perp-cli >= 0.16.0"}}'
|
|
13
|
+
exit 1
|
|
14
14
|
fi
|
|
15
|
+
PERP="perp"
|
|
15
16
|
|
|
16
17
|
# 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
|
|
18
|
-
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
18
|
+
# (never append a fallback document); empty output (crash) → an explicit NO_OUTPUT error document.
|
|
19
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false,"error":{"code":"NO_OUTPUT","message":"perp printed nothing (crashed?)"}}'; fi; }
|
|
19
20
|
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
20
21
|
|
|
21
22
|
JSON_MODE=false
|
|
@@ -30,28 +31,35 @@ add_check() {
|
|
|
30
31
|
if [[ "$status" == "fail" ]]; then issues+=("$name: $detail"); fi
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
# 1. perp-cli
|
|
34
|
-
VERSION=$($PERP --version 2>/dev/null ||
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
# 1. perp-cli version (must be >= 0.16.0)
|
|
35
|
+
VERSION=$($PERP --version 2>/dev/null || true)
|
|
36
|
+
MIN_VERSION="0.16.0"
|
|
37
|
+
if [[ -z "$VERSION" ]]; then
|
|
38
|
+
add_check "install" "fail" "perp --version printed nothing — ask the user to (re)install perp-cli >= $MIN_VERSION"
|
|
39
|
+
elif [[ "$(printf '%s\n%s\n' "$MIN_VERSION" "$VERSION" | sort -V | head -1)" != "$MIN_VERSION" ]]; then
|
|
40
|
+
add_check "install" "fail" "perp-cli $VERSION < $MIN_VERSION — ask the user to upgrade"
|
|
37
41
|
else
|
|
38
|
-
add_check "install" "
|
|
42
|
+
add_check "install" "pass" "perp-cli $VERSION"
|
|
39
43
|
fi
|
|
40
44
|
|
|
41
|
-
# 2. Wallet configured
|
|
45
|
+
# 2. Wallet configured (with an OWS wallet, `exchanges: []` in wallet show is normal)
|
|
42
46
|
WALLET_OUT=$(pj --json wallet show)
|
|
43
47
|
if isok "$WALLET_OUT"; then
|
|
44
48
|
add_check "wallet" "pass" "Wallet configured"
|
|
45
49
|
else
|
|
46
|
-
add_check "wallet" "fail" "
|
|
50
|
+
add_check "wallet" "fail" "wallet show failed ($(jq -r '.error.code' <<<"$WALLET_OUT")) — the user registers a key: perp wallet set <exchange> (stdin / hidden prompt)"
|
|
47
51
|
fi
|
|
48
52
|
|
|
49
|
-
# 3.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
# 3. Venue connectivity: perp health (healthy = every OFFICIAL-API venue is up;
|
|
54
|
+
# allHealthy also counts unofficial APIs such as TxFlow)
|
|
55
|
+
HEALTH_OUT=$(pj --json health)
|
|
56
|
+
if ! isok "$HEALTH_OUT"; then
|
|
57
|
+
add_check "connectivity" "fail" "perp health failed ($(jq -r '.error.code' <<<"$HEALTH_OUT"))"
|
|
58
|
+
elif [[ "$(jq -r '.data.healthy' <<<"$HEALTH_OUT")" == "true" ]]; then
|
|
59
|
+
add_check "connectivity" "pass" "All official venues reachable"
|
|
53
60
|
else
|
|
54
|
-
|
|
61
|
+
DOWN=$(jq -r '[.data.exchanges[] | select(.status != "ok") | .exchange] | join(",")' <<<"$HEALTH_OUT")
|
|
62
|
+
add_check "connectivity" "warn" "Official venues down: $DOWN — do not use them until perp health shows them ok"
|
|
55
63
|
fi
|
|
56
64
|
|
|
57
65
|
# 4. Portfolio & balances
|
|
@@ -1,56 +1,70 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Scan spot+perp funding rate arbitrage opportunities
|
|
3
|
-
# Usage: ./spot-perp-scan.sh [--json] [--mode all|spot-perp|perp-perp] [--min 10]
|
|
4
|
-
# Combines
|
|
2
|
+
# Scan spot+perp (and perp-perp) funding rate arbitrage opportunities
|
|
3
|
+
# Usage: ./spot-perp-scan.sh [--json] [--mode all|spot-perp|perp-perp] [--min 10] [-n mainnet|testnet] [--no-portfolio]
|
|
4
|
+
# Combines the scan with balances (portfolio) and venue health (perp health).
|
|
5
|
+
# --no-portfolio: skip the balance read (no wallet needed); the output says it was skipped.
|
|
6
|
+
# Spot-perp rows: only direction == "long-spot-short-perp" is executable with `arb exec <SYM> spot:<venue> <perp>`.
|
|
7
|
+
# Testnet funding / spreads are not meaningful — use mainnet (the default) for yield analysis.
|
|
5
8
|
|
|
6
9
|
set -euo pipefail
|
|
7
10
|
|
|
8
|
-
#
|
|
9
|
-
if command -v perp &>/dev/null; then
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
# perp must be installed (no npx / auto-install path: the user decides what gets installed).
|
|
12
|
+
if ! command -v perp &>/dev/null; then
|
|
13
|
+
echo "perp not found — ask the user to install perp-cli >= 0.16.0" >&2
|
|
14
|
+
echo '{"ok":false,"error":{"code":"PERP_NOT_FOUND","message":"perp not found — ask the user to install perp-cli >= 0.16.0"}}'
|
|
15
|
+
exit 1
|
|
13
16
|
fi
|
|
17
|
+
PERP="perp"
|
|
14
18
|
|
|
15
19
|
# 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
|
|
17
|
-
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
20
|
+
# (never append a fallback document); empty output (crash) → an explicit NO_OUTPUT error document.
|
|
21
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false,"error":{"code":"NO_OUTPUT","message":"perp printed nothing (crashed?)"}}'; fi; }
|
|
18
22
|
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
19
23
|
|
|
20
24
|
JSON_MODE=false
|
|
21
25
|
MODE="all"
|
|
22
26
|
MIN_SPREAD=10
|
|
27
|
+
NETWORK="mainnet"
|
|
28
|
+
WITH_PORTFOLIO=true
|
|
23
29
|
while [[ $# -gt 0 ]]; do
|
|
24
30
|
case "$1" in
|
|
25
31
|
--json) JSON_MODE=true; shift ;;
|
|
26
32
|
--mode) MODE="$2"; shift 2 ;;
|
|
27
33
|
--min) MIN_SPREAD="$2"; shift 2 ;;
|
|
28
|
-
|
|
34
|
+
-n|--network) NETWORK="$2"; shift 2 ;;
|
|
35
|
+
--no-portfolio) WITH_PORTFOLIO=false; shift ;;
|
|
36
|
+
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
29
37
|
esac
|
|
30
38
|
done
|
|
39
|
+
PERP="perp -n $NETWORK"
|
|
31
40
|
|
|
32
|
-
# 1. Portfolio
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if $
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
# 1. Portfolio (balances). Explicitly skipped with --no-portfolio; a failed read aborts.
|
|
42
|
+
if $WITH_PORTFOLIO; then
|
|
43
|
+
PORTFOLIO=$(pj --json portfolio)
|
|
44
|
+
if ! isok "$PORTFOLIO"; then
|
|
45
|
+
if $JSON_MODE; then
|
|
46
|
+
jq -c '{ok:false, error:{code:.error.code, message:.error.message, source:"perp --json portfolio", remediation:"Check the wallet setup, or rerun with --no-portfolio to scan without balances"}}' <<<"$PORTFOLIO"
|
|
47
|
+
else
|
|
48
|
+
echo "ERROR: portfolio failed ($(jq -r '.error.code' <<<"$PORTFOLIO")). Check the wallet setup, or rerun with --no-portfolio." >&2
|
|
49
|
+
fi
|
|
50
|
+
exit 1
|
|
39
51
|
fi
|
|
40
|
-
|
|
52
|
+
else
|
|
53
|
+
PORTFOLIO='{"skipped":true,"reason":"--no-portfolio"}'
|
|
41
54
|
fi
|
|
42
55
|
|
|
43
56
|
# 2. Run appropriate scan
|
|
44
57
|
SCAN_RESULT=$(pj --json arb scan --mode "$MODE" --min "$MIN_SPREAD")
|
|
45
58
|
|
|
46
|
-
# 3.
|
|
47
|
-
HEALTH=$(pj --json
|
|
59
|
+
# 3. Venue health (data.healthy = every official-API venue is up)
|
|
60
|
+
HEALTH=$(pj --json health)
|
|
48
61
|
|
|
49
62
|
if $JSON_MODE; then
|
|
50
63
|
cat <<EOF
|
|
51
64
|
{
|
|
52
65
|
"ok": true,
|
|
53
66
|
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
67
|
+
"network": "$NETWORK",
|
|
54
68
|
"mode": "$MODE",
|
|
55
69
|
"minSpread": $MIN_SPREAD,
|
|
56
70
|
"portfolio": $PORTFOLIO,
|
|
@@ -60,29 +74,35 @@ if $JSON_MODE; then
|
|
|
60
74
|
EOF
|
|
61
75
|
else
|
|
62
76
|
echo "=== Spot+Perp Arb Scanner ==="
|
|
63
|
-
echo "Mode: $MODE | Min spread: ${MIN_SPREAD}% annual"
|
|
77
|
+
echo "Network: $NETWORK | Mode: $MODE | Min spread: ${MIN_SPREAD}% annual"
|
|
64
78
|
echo ""
|
|
65
79
|
|
|
66
|
-
# Show balances
|
|
67
80
|
echo "--- Balances ---"
|
|
68
|
-
|
|
69
|
-
.data.exchanges
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
if $WITH_PORTFOLIO; then
|
|
82
|
+
jq -r '.data.exchanges[] | " \(.name): equity $\(.perp.equity) available $\(.perp.available)"' <<<"$PORTFOLIO"
|
|
83
|
+
else
|
|
84
|
+
echo " (skipped: --no-portfolio)"
|
|
85
|
+
fi
|
|
72
86
|
echo ""
|
|
73
87
|
|
|
74
|
-
# Show opportunities
|
|
75
88
|
echo "--- Opportunities ---"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
if isok "$SCAN_RESULT"; then
|
|
90
|
+
jq -r '.data[] |
|
|
91
|
+
if .mode == "spot-perp" then
|
|
92
|
+
" \(.symbol) | spot-perp | net \(.netSpread)%/yr | \(.direction) spot:\(.spotExchanges | join("/")) perp:\(.perpExchange)"
|
|
93
|
+
+ (if .direction == "long-spot-short-perp" then "" else " [NOT executable with arb exec]" end)
|
|
94
|
+
else
|
|
95
|
+
" \(.symbol) | perp-perp | net \(.netSpread)%/yr | long \(.longExch) / short \(.shortExch)"
|
|
96
|
+
end' <<<"$SCAN_RESULT"
|
|
97
|
+
else
|
|
98
|
+
echo " ERROR: arb scan failed: $(jq -r '"\(.error.code): \(.error.message)"' <<<"$SCAN_RESULT")"
|
|
99
|
+
fi
|
|
80
100
|
|
|
81
|
-
# Show exchange health
|
|
82
101
|
echo ""
|
|
83
|
-
echo "---
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
echo "--- Venue Health ---"
|
|
103
|
+
if isok "$HEALTH"; then
|
|
104
|
+
jq -r '" healthy (official venues): \(.data.healthy)", (.data.exchanges[] | " \(.exchange): \(.status)")' <<<"$HEALTH"
|
|
105
|
+
else
|
|
106
|
+
echo " ERROR: perp health failed: $(jq -r '.error.code' <<<"$HEALTH")"
|
|
107
|
+
fi
|
|
88
108
|
fi
|
|
@@ -3,20 +3,22 @@
|
|
|
3
3
|
# Usage: ./validate-arb.sh <SYMBOL> <LONG_EX> <SHORT_EX> <SIZE_USD> [--leverage N]
|
|
4
4
|
# Performs: price check, liquidity check, balance check, risk check, dry-run via 'arb exec'
|
|
5
5
|
# Returns JSON validation report
|
|
6
|
-
# NOTE: For spot+perp arb, pass spot:<exch> as LONG_EX (e.g. spot:hl)
|
|
6
|
+
# NOTE: For spot+perp arb, pass spot:<exch> as LONG_EX (e.g. spot:hl) — only for a scan row with
|
|
7
|
+
# direction == "long-spot-short-perp" (arb exec spot: always buys spot + shorts the perp).
|
|
7
8
|
|
|
8
9
|
set -euo pipefail
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
-
if command -v perp &>/dev/null; then
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
# perp must be installed (no npx / auto-install path: the user decides what gets installed).
|
|
12
|
+
if ! command -v perp &>/dev/null; then
|
|
13
|
+
echo "perp not found — ask the user to install perp-cli >= 0.16.0" >&2
|
|
14
|
+
echo '{"ok":false,"error":{"code":"PERP_NOT_FOUND","message":"perp not found — ask the user to install perp-cli >= 0.16.0"}}'
|
|
15
|
+
exit 1
|
|
15
16
|
fi
|
|
17
|
+
PERP="perp"
|
|
16
18
|
|
|
17
19
|
# 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
|
|
19
|
-
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false}'; fi; }
|
|
20
|
+
# (never append a fallback document); empty output (crash) → an explicit NO_OUTPUT error document.
|
|
21
|
+
pj() { local out; out=$($PERP "$@" 2>/dev/null) || true; if [[ -n "$out" ]]; then printf '%s\n' "$out"; else echo '{"ok":false,"error":{"code":"NO_OUTPUT","message":"perp printed nothing (crashed?)"}}'; fi; }
|
|
20
22
|
isok() { jq -e '.ok == true' >/dev/null 2>&1 <<<"$1"; }
|
|
21
23
|
|
|
22
24
|
SYM="${1:?Usage: validate-arb.sh <SYMBOL> <LONG_EX> <SHORT_EX> <SIZE_USD> [--leverage N]}"
|
|
@@ -42,24 +44,23 @@ add_result() {
|
|
|
42
44
|
if [[ "$ok" == "false" ]]; then pass=false; fi
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
# Venue of the long leg (spot:<venue> → <venue>) for the per-venue reads below
|
|
47
|
+
# Venue of the long leg (spot:<venue> → <venue>) for the per-venue reads below.
|
|
48
|
+
# Aliases (hl, pac, lt, lrh, ast, …) are accepted everywhere, portfolio included.
|
|
46
49
|
LONG_VENUE="${LONG_EX#spot:}"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if [[ "$LONG_PRICE" == "0" || "$SHORT_PRICE" == "0" ]]; then
|
|
60
|
-
add_result "price" "false" "Cannot fetch price: long=$LONG_PRICE short=$SHORT_PRICE"
|
|
50
|
+
SPOT_MODE=false
|
|
51
|
+
[[ "$LONG_EX" == spot:* ]] && SPOT_MODE=true
|
|
52
|
+
|
|
53
|
+
# 1. Price comparison — both exchanges must report similar prices.
|
|
54
|
+
# `market mid` reads the PERP book; there is no spot price read — in spot mode the long price
|
|
55
|
+
# is the spot venue's perp mid (arb exec itself compares the spot and perp books and refuses
|
|
56
|
+
# an empty book or a > 5% gap).
|
|
57
|
+
LONG_MID=$(pj --json -e "$LONG_VENUE" market mid "$SYM")
|
|
58
|
+
SHORT_MID=$(pj --json -e "$SHORT_EX" market mid "$SYM")
|
|
59
|
+
if ! isok "$LONG_MID" || ! isok "$SHORT_MID"; then
|
|
60
|
+
add_result "price" "false" "Cannot fetch mid: long=$(jq -r '.error.code // .data.mid' <<<"$LONG_MID") short=$(jq -r '.error.code // .data.mid' <<<"$SHORT_MID")"
|
|
61
61
|
else
|
|
62
|
-
|
|
62
|
+
LONG_PRICE=$(jq -r '.data.mid' <<<"$LONG_MID")
|
|
63
|
+
SHORT_PRICE=$(jq -r '.data.mid' <<<"$SHORT_MID")
|
|
63
64
|
DEVIATION=$(echo "$LONG_PRICE $SHORT_PRICE" | awk '{d=($1-$2)/$2*100; print (d<0?-d:d)}')
|
|
64
65
|
if (( $(echo "$DEVIATION > 5" | bc -l) )); then
|
|
65
66
|
add_result "price" "false" "Price deviation ${DEVIATION}% > 5% — possible wrong token"
|
|
@@ -68,21 +69,37 @@ else
|
|
|
68
69
|
fi
|
|
69
70
|
fi
|
|
70
71
|
|
|
71
|
-
# 2.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
# 2. Balances. Perp legs: portfolio .perp.available only (missing / unreadable = fail, never 0 or equity).
|
|
73
|
+
# The comparison against the full SIZE is conservative (arb exec needs notional / leverage × buffer).
|
|
74
|
+
perp_available() {
|
|
75
|
+
local out; out=$(pj --json portfolio -e "$1")
|
|
76
|
+
if ! isok "$out"; then echo "ERROR:$(jq -r '.error.code' <<<"$out")"; return; fi
|
|
77
|
+
jq -r '.data.exchanges[0].perp.available // "MISSING"' <<<"$out"
|
|
78
|
+
}
|
|
79
|
+
check_perp_balance() {
|
|
80
|
+
local name="$1" venue="$2" bal; bal=$(perp_available "$venue")
|
|
81
|
+
if [[ "$bal" == ERROR:* || "$bal" == "MISSING" ]]; then
|
|
82
|
+
add_result "$name" "false" "$venue perp available unreadable ($bal)"
|
|
83
|
+
elif (( $(echo "$bal < $SIZE" | bc -l) )); then
|
|
84
|
+
add_result "$name" "false" "$venue perp available $bal < $SIZE"
|
|
85
|
+
else
|
|
86
|
+
add_result "$name" "true" "$venue perp available=$bal"
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
if $SPOT_MODE; then
|
|
90
|
+
# Spot leg: needs spot quote balance (or perp funds the CLI transfers perp→spot on that venue).
|
|
91
|
+
# The exact rule (shared vs separate spot margin, transfer shortfall) is enforced by the arb exec
|
|
92
|
+
# dry-run below; here the spot balances are read so a read failure is surfaced.
|
|
93
|
+
SPOT_BAL=$(pj --json -e "$LONG_VENUE" trade spot-balance)
|
|
94
|
+
if isok "$SPOT_BAL"; then
|
|
95
|
+
add_result "balance_spot" "true" "$LONG_VENUE spot balances: $(jq -c '[.data.balances[] | {token, available}]' <<<"$SPOT_BAL" | tr -d '"') — sufficiency checked by the dry-run"
|
|
96
|
+
else
|
|
97
|
+
add_result "balance_spot" "false" "$LONG_VENUE spot balance unreadable ($(jq -r '.error.code' <<<"$SPOT_BAL"))"
|
|
98
|
+
fi
|
|
83
99
|
else
|
|
84
|
-
|
|
100
|
+
check_perp_balance "balance_long" "$LONG_VENUE"
|
|
85
101
|
fi
|
|
102
|
+
check_perp_balance "balance_short" "$SHORT_EX"
|
|
86
103
|
|
|
87
104
|
# 3. Market info (symbol availability)
|
|
88
105
|
LONG_INFO=$(pj --json -e "$LONG_VENUE" market info "$SYM")
|
|
@@ -101,7 +118,7 @@ RISK_CHECK=$(pj --json risk check --notional "$SIZE" --leverage "${LEVERAGE:-1}"
|
|
|
101
118
|
if isok "$RISK_CHECK" && [[ "$(jq -r '.data.allowed' <<<"$RISK_CHECK" 2>/dev/null || true)" == "true" ]]; then
|
|
102
119
|
add_result "risk" "true" "Within risk limits"
|
|
103
120
|
else
|
|
104
|
-
RISK_WHY=$(jq -r '.data.reason
|
|
121
|
+
if isok "$RISK_CHECK"; then RISK_WHY=$(jq -r '.data.reason' <<<"$RISK_CHECK"); else RISK_WHY=$(jq -r '.error.code' <<<"$RISK_CHECK"); fi
|
|
105
122
|
add_result "risk" "false" "Risk check failed: $RISK_WHY"
|
|
106
123
|
fi
|
|
107
124
|
|
|
@@ -110,7 +127,7 @@ DRY_RUN=$(pj --json --dry-run arb exec "$SYM" "$LONG_EX" "$SHORT_EX" "$SIZE" $LE
|
|
|
110
127
|
if isok "$DRY_RUN"; then
|
|
111
128
|
add_result "dry_run" "true" "Dry-run succeeded"
|
|
112
129
|
else
|
|
113
|
-
DRY_ERR=$(jq -r '.error.code
|
|
130
|
+
DRY_ERR=$(jq -r '"\(.error.code): \(.error.message)"' <<<"$DRY_RUN" | tr -d '"\\')
|
|
114
131
|
add_result "dry_run" "false" "Dry-run failed: $DRY_ERR"
|
|
115
132
|
fi
|
|
116
133
|
|
package/dist/cli-spec.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
2
|
-
interface ArgSpec {
|
|
3
|
-
name: string;
|
|
4
|
-
required: boolean;
|
|
5
|
-
}
|
|
6
|
-
interface OptionSpec {
|
|
7
|
-
flags: string;
|
|
8
|
-
description: string;
|
|
9
|
-
default?: unknown;
|
|
10
|
-
}
|
|
11
|
-
interface CommandSpec {
|
|
12
|
-
name: string;
|
|
13
|
-
description: string;
|
|
14
|
-
args: ArgSpec[];
|
|
15
|
-
options: OptionSpec[];
|
|
16
|
-
subcommands?: CommandSpec[];
|
|
17
|
-
}
|
|
18
|
-
interface CliSpec {
|
|
19
|
-
name: string;
|
|
20
|
-
version: string;
|
|
21
|
-
description: string;
|
|
22
|
-
globalOptions: OptionSpec[];
|
|
23
|
-
commands: CommandSpec[];
|
|
24
|
-
exchanges: string[];
|
|
25
|
-
envelope: {
|
|
26
|
-
success: string;
|
|
27
|
-
error: string;
|
|
28
|
-
example: {
|
|
29
|
-
ok: boolean;
|
|
30
|
-
data: {
|
|
31
|
-
equity: string;
|
|
32
|
-
};
|
|
33
|
-
meta: {
|
|
34
|
-
timestamp: string;
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
errorCodes: Record<string, {
|
|
39
|
-
status: number;
|
|
40
|
-
retryable: boolean;
|
|
41
|
-
description: string;
|
|
42
|
-
}>;
|
|
43
|
-
tips: string[];
|
|
44
|
-
}
|
|
45
|
-
export declare function getCliSpec(program: Command): CliSpec;
|
|
46
|
-
export {};
|