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
package/README.zh.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
[English](README.md) | [한국어](README.ko.md) | **中文** | [日本語](README.ja.md)
|
|
2
|
+
|
|
3
|
+
> 以英文版 [README](README.md) 为准;本译文可能滞后于英文版。
|
|
4
|
+
|
|
5
|
+
# perp-cli
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/perp-cli)
|
|
8
|
+
[](https://www.npmjs.com/package/perp-cli)
|
|
9
|
+
[](https://github.com/hypurrquant/perp-cli/blob/main/LICENSE)
|
|
10
|
+
|
|
11
|
+
一个 CLI 搞定多个 DEX 的永续合约 — 在终端中交易、比较资金费率、调拨资金和运行机器人,或交给 AI agent 使用(JSON 输出、MCP 服务器、agent 技能)。
|
|
12
|
+
|
|
13
|
+
- **8 个可交易场所:** Hyperliquid(含 HIP-3 dex)、Pacifica、Lighter、Lighter RH、Aster、Ondo Perps、Arcus、QFEX(跨链桥和永续-永续套利覆盖最初的四个:Pacifica、Hyperliquid、Lighter、Aster)
|
|
14
|
+
- **仅市场数据:** RISEx、GRVT、Extended、Variational、TxFlow(非官方 API)— 另有 Derive 期权数据
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g perp-cli
|
|
20
|
+
perp --version
|
|
21
|
+
|
|
22
|
+
# Or without a global install
|
|
23
|
+
npx -y perp-cli --json portfolio
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
本软件包附带三个可执行文件:`perp`(CLI 本体)、`perp-mcp`([MCP 服务器](docs/mcp.md),stdio)和 `perp-guardrail`([签名护栏](docs/guardrail.md))。
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
**1. 先看看 — 无需密钥。**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
perp -e hl market list # markets, prices, funding
|
|
34
|
+
perp -e hl market book BTC # order book
|
|
35
|
+
perp arb scan --rates # funding rates across venues
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**2. 添加钱包。** 设置向导会创建或导入一个加密的 [OWS](https://github.com/open-wallet-standard) vault 钱包,选择交易场所并注册 agent 密钥(每次签名前都会先询问,且绝不会移动资金):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
perp setup
|
|
42
|
+
perp wallet show
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
密钥从隐藏输入提示或 stdin 读取 — 绝不从 argv 或 `~/.perp/.env` 读取。一个 EVM 密钥即可覆盖 Hyperliquid、Lighter 和 Aster。其他方式(单行导入密钥、可脚本化的设置、在交易所网站上创建的密钥)请参见 [docs/setup.md](docs/setup.md)。
|
|
46
|
+
|
|
47
|
+
**3. 查看账户并交易。** 在任意命令后加上 `-n testnet` 即可使用测试网。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
perp --json portfolio # balances + positions + risk on every venue
|
|
51
|
+
perp --json --dry-run -e hl trade market BTC buy 0.001 # validate only, nothing is sent
|
|
52
|
+
perp --json -e hl trade market BTC buy 0.001 # market order (bounded by the slippage setting)
|
|
53
|
+
perp --json -e hl trade close BTC
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## AI Agent Skill
|
|
57
|
+
|
|
58
|
+
适用于 Claude Code、Cursor、Codex、Gemini CLI 及其他 agent CLI 的即插即用技能(skill)。内含非交互式命令指南、运维手册、策略框架以及可复用的 shell 脚本(预检、套利监控、校验),让你的 agent 在正确的确认节点停下,并且永远不会卡在交互式向导上。
|
|
59
|
+
|
|
60
|
+
### 安装
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Claude Code — slash command
|
|
64
|
+
/install-skill hypurrquant/perp-cli
|
|
65
|
+
|
|
66
|
+
# Cursor / Codex / generic agent CLIs — via npx
|
|
67
|
+
npx skills add hypurrquant/perp-cli
|
|
68
|
+
|
|
69
|
+
# Manual install (any agent that reads a Skills directory)
|
|
70
|
+
git clone https://github.com/hypurrquant/perp-cli.git
|
|
71
|
+
cp -R perp-cli/skills/perp-cli ~/.claude/skills/ # or your CLI's skills dir
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
该技能随 npm 包一同发布(`perp-cli/skills/perp-cli`),因此全局安装后也可在本地使用:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install -g perp-cli@latest
|
|
78
|
+
ls "$(npm root -g)/perp-cli/skills/perp-cli"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 技能包含的内容
|
|
82
|
+
|
|
83
|
+
| 文件 | 用途 |
|
|
84
|
+
|------|---------|
|
|
85
|
+
| `SKILL.md` | 顶层 agent 指南 — 规则、工作流、示例 |
|
|
86
|
+
| `references/commands.md` | 完整的非交互式命令参考(涵盖所有命令组) |
|
|
87
|
+
| `references/agent-operations.md` | 设置流程、幂等性、错误处理、常见错误 |
|
|
88
|
+
| `references/strategies.md` | 资金费率套利决策框架 + 风险管理 |
|
|
89
|
+
| `scripts/preflight.sh` | 安装 + 钱包 + 连通性 + 风险就绪检查 |
|
|
90
|
+
| `scripts/arb-monitor.sh` | 实时监控未平仓的套利持仓 |
|
|
91
|
+
| `scripts/spot-perp-scan.sh` | 结合余额信息扫描现货+永续机会 |
|
|
92
|
+
| `scripts/funding-analysis.sh` | 跨交易所资金费率概览 / 单交易对深入分析 |
|
|
93
|
+
| `scripts/validate-arb.sh` | 执行前校验(价格 / 余额 / 风险 / dry-run) |
|
|
94
|
+
|
|
95
|
+
完整的 agent 指南请参见 [`skills/perp-cli/SKILL.md`](skills/perp-cli/SKILL.md)。
|
|
96
|
+
|
|
97
|
+
## 功能特性
|
|
98
|
+
|
|
99
|
+
- **资金费率套利** — 永续-永续和现货-永续扫描,一条命令完成双腿开仓与平仓
|
|
100
|
+
- **投资组合** — 一次调用即可返回所有交易场所的余额、持仓和风险
|
|
101
|
+
- **资金** — 充值、提现、划转、多提供商跨链桥(CCTP / Relay / deBridge)、交易所间再平衡
|
|
102
|
+
- **机器人** — TWAP、网格、DCA、追踪止损等,支持后台任务管理
|
|
103
|
+
- **Agent 优先设计** — `--json`、`--fields`、`--ndjson`、`--dry-run`、运行时 schema 自省
|
|
104
|
+
- **安全** — 交易前校验、每笔市价单都有滑点上限、client-id 去重、响应净化
|
|
105
|
+
|
|
106
|
+
## 交易场所
|
|
107
|
+
|
|
108
|
+
| 交易场所 | `-e` | 交易 | 链 / 抵押品 |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| Hyperliquid | `hyperliquid` (`hl`) | ✅ | HyperEVM,USDC;HIP-3 dex 通过 `--dex` |
|
|
111
|
+
| Pacifica | `pacifica` (`pac`) | ✅ | Solana,USDC |
|
|
112
|
+
| Lighter | `lighter` (`lt`) | ✅ | Ethereum L2,USDC |
|
|
113
|
+
| Lighter RH | `lighter-rh` (`lrh`) | ✅ | Robinhood chain,USDG |
|
|
114
|
+
| Aster | `aster` (`ast`) | ✅ | BNB Chain,USDT / USDC |
|
|
115
|
+
| Ondo Perps | `ondo` | ✅ | 链下,USDC |
|
|
116
|
+
| Arcus | `arcus` | ✅(访问白名单) | Robinhood chain,USDG |
|
|
117
|
+
| QFEX | `qfex` (`qf`) | ✅(仅限手动交易) | 托管;股票、指数、大宗商品 |
|
|
118
|
+
| RISEx | `risex` | 只读 | RISE chain,USDC |
|
|
119
|
+
| GRVT | `grvt` | 仅市场数据 | USDT |
|
|
120
|
+
| Extended | `extended` (`ext`) | 仅市场数据 | Starknet;加密货币 + 股票 / 外汇 |
|
|
121
|
+
| Variational | `variational` (`var`) | 仅市场数据 | RFQ 交易场所 |
|
|
122
|
+
| TxFlow | `txflow` (`txf`) | 仅市场数据 | **非官方** API |
|
|
123
|
+
| Derive | `derive` | 仅期权数据 | — |
|
|
124
|
+
|
|
125
|
+
- **[支持矩阵及各交易场所说明](docs/venues.md)** — 资金费率、WebSocket、充值 / 提现、按地址查询、限制地区
|
|
126
|
+
- **[主网验证](docs/verification.md)** — 哪些功能已用真实资金运行过
|
|
127
|
+
- **[交易细节](docs/trading.md)** — 订单有效期(TIF)、止盈/止损、止损单、滑点、现货交易、构建者费用
|
|
128
|
+
|
|
129
|
+
## 常用命令
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Market data
|
|
133
|
+
perp --json -e <EX> market list | book <SYM> | funding <SYM> | kline <SYM> 1h
|
|
134
|
+
|
|
135
|
+
# Trading
|
|
136
|
+
perp --json -e <EX> trade buy <SYM> <SIZE> --tp <PX> --sl <PX> # entry protected by TP/SL
|
|
137
|
+
perp --json -e <EX> trade limit <SYM> buy <PX> <SIZE>
|
|
138
|
+
perp --json -e <EX> trade close <SYM> # or: flatten (every position)
|
|
139
|
+
|
|
140
|
+
# Account
|
|
141
|
+
perp --json portfolio
|
|
142
|
+
perp --json -e <EX> account positions
|
|
143
|
+
perp --json account balance --address <ADDR> # any account, keyless
|
|
144
|
+
|
|
145
|
+
# Funding arb
|
|
146
|
+
perp --json arb scan --min 5
|
|
147
|
+
perp --json arb exec <SYM> <longEx> <shortEx> <USD>
|
|
148
|
+
|
|
149
|
+
# Funds
|
|
150
|
+
perp --json funds deposit hyperliquid 100
|
|
151
|
+
perp --json funds bridge send --from solana --to arbitrum --amount 100
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
包含所有命令组的完整列表:**[docs/commands.md](docs/commands.md)**。全局参数:`--json`、`--fields <f>`、`--ndjson`、`--dry-run`、`-n testnet`、`-e <venue>`、`--dex <name>`(HIP-3)、`-w, --wallet <name>`。
|
|
155
|
+
|
|
156
|
+
## 命令分组
|
|
157
|
+
|
|
158
|
+
| 命令组 | 说明 |
|
|
159
|
+
|-------|-------------|
|
|
160
|
+
| `market` | 价格、订单簿、资金费率、K 线、HIP-3 dex |
|
|
161
|
+
| `account` | 余额、持仓、订单、保证金 |
|
|
162
|
+
| `trade` | 市价 / 限价 / 止损单、平仓、分批、拆单执行 |
|
|
163
|
+
| `outcome` | Hyperliquid Outcome(HIP-4)— 二元 / 区间合约,按 outcome 计价(outcomeMeta `quoteToken`,目前为 USDC),无杠杆 |
|
|
164
|
+
| `options` | 期权市场数据,只读(Derive:`-e derive options underlyings / expiries / chain / quote`) |
|
|
165
|
+
| `print` | Pacifica Print — 每个周期赚取权利金的行权价订单:games、quote、positions、history、events、deposit / cancel(`--dry-run`) |
|
|
166
|
+
| `vault` | Pacifica vaults(API 中称 lakes)— list、info、positions、deposit(仅限主钱包)/ withdraw(`--dry-run`) |
|
|
167
|
+
| `arb` | 资金费率套利 — 扫描、执行、平仓、监控(永续-永续 & 现货-永续) |
|
|
168
|
+
| `strategy` | 19 种机器人算法(grid、dca、twap、APEX、REFLECT、预设)+ 嵌套的脚本化计划 |
|
|
169
|
+
| `funds` | 充值、提现、划转、跨链桥(多提供商)、交易所间再平衡 |
|
|
170
|
+
| `risk` | 风险限额、强平距离、护栏 |
|
|
171
|
+
| `wallet` | 多钱包管理、agent 钱包(`wallet agent ...`)、构建者费用(`wallet builder ...`,默认关闭)、保证金模式 / 子账户 / API 密钥(`wallet manage ...`)、链上余额 |
|
|
172
|
+
| `history` | 执行日志、盈亏、绩效拆解 |
|
|
173
|
+
| `portfolio` | 跨交易所统一概览(取代原来的 `account balance` / `status` / `dashboard`;`account balance --address` 可无需密钥读取任意账户的余额) |
|
|
174
|
+
| `health` | 适配器健康检查:ping 每个交易场所的 REST 端点(连通性 + 延迟) |
|
|
175
|
+
| `settings` | CLI 设置(推荐码、默认值、手续费、构建者费用、deBridge affiliate) |
|
|
176
|
+
| `backtest` | 策略回测 |
|
|
177
|
+
| `background` | 后台进程管理器(用于策略、告警等的 tmux 会话) |
|
|
178
|
+
| `alerts` | 资金费率告警(Telegram / Discord),支持后台守护进程 |
|
|
179
|
+
| `setup` | 设置向导:OWS 钱包、交易场所、agent 钱包、滑点、默认交易场所(别名:`init`;脚本中使用 `--non-interactive`) |
|
|
180
|
+
|
|
181
|
+
## Agent 优先的 CLI 设计
|
|
182
|
+
|
|
183
|
+
遵循 [agent-first CLI principles](https://justin.poehnelt.com/posts/rewrite-your-cli-for-ai-agents/) 构建:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Every command returns structured JSON envelope
|
|
187
|
+
perp --json portfolio
|
|
188
|
+
# → { "ok": true, "data": {...}, "meta": { "timestamp": "..." } }
|
|
189
|
+
|
|
190
|
+
# Filter output to specific fields (saves tokens)
|
|
191
|
+
perp --json --fields totalEquity,risk portfolio
|
|
192
|
+
|
|
193
|
+
# Stream large lists as NDJSON (one JSON per line)
|
|
194
|
+
perp --json --ndjson -e hl market list
|
|
195
|
+
|
|
196
|
+
# Pre-validate before executing
|
|
197
|
+
perp --json -e hl trade check BTC buy 0.01
|
|
198
|
+
perp --json --dry-run -e hl trade market BTC buy 0.01
|
|
199
|
+
|
|
200
|
+
# Idempotent orders with client ID (Hyperliquid: a 128-bit cloid, 0x + 32 hex; --auto-id generates one)
|
|
201
|
+
perp --json -e hl trade market BTC buy 0.01 --client-id 0x1234567890abcdef1234567890abcdef
|
|
202
|
+
perp --json -e hl trade status c:0x1234567890abcdef1234567890abcdef
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
所有响应都会自动净化(去除控制字符,拦截提示词注入模式)。
|
|
206
|
+
错误中包含 `retryable` 标志 — 仅当其为 `true` 时才重试。
|
|
207
|
+
|
|
208
|
+
## MCP 服务器
|
|
209
|
+
|
|
210
|
+
[](https://glama.ai/mcp/servers/hypurrquant/perp-cli)
|
|
211
|
+
|
|
212
|
+
`perp-mcp` 为 Claude Desktop、Cursor 及其他 MCP 客户端提供 31 个工具。查询市场数据无需密钥;如需账户和交易工具,请先运行 `perp setup`(服务器读取的是同一个钱包)。切勿在 MCP 配置中放入私钥。
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"mcpServers": {
|
|
217
|
+
"perp-cli": {
|
|
218
|
+
"command": "npx",
|
|
219
|
+
"args": ["-y", "-p", "perp-cli", "perp-mcp"]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
测试网、完整工具列表、资源和提示词:**[docs/mcp.md](docs/mcp.md)**。
|
|
226
|
+
|
|
227
|
+
## 文档
|
|
228
|
+
|
|
229
|
+
| 文档 | 内容 |
|
|
230
|
+
|---|---|
|
|
231
|
+
| [设置](docs/setup.md) | 钱包、agent 密钥、在交易所网站上创建的密钥、机密信息的存放位置 |
|
|
232
|
+
| [交易场所](docs/venues.md) | 支持矩阵、交易场所 id、QFEX / RISEx / GRVT / Extended / Variational / TxFlow / Derive 详情 |
|
|
233
|
+
| [交易](docs/trading.md) | 订单有效期(TIF)、止盈/止损、止损单、改单、滑点、现货交易、构建者费用 |
|
|
234
|
+
| [命令](docs/commands.md) | 所有命令组及示例、Telegram 告警 |
|
|
235
|
+
| [主网验证](docs/verification.md) | 各交易场所哪些功能已用真实资金运行过 |
|
|
236
|
+
| [MCP 服务器](docs/mcp.md) | 配置、测试网、工具、资源、提示词 |
|
|
237
|
+
| [签名护栏](docs/guardrail.md) | `perp-guardrail`,用于 agent 密钥的 OWS 策略可执行文件 |
|
|
238
|
+
| [测试](docs/TESTING.md) | 测试套件和交易场所检查清单 |
|
|
239
|
+
| [CHANGELOG](CHANGELOG.md) | 版本发布说明 |
|
|
240
|
+
|
|
241
|
+
## 许可证
|
|
242
|
+
|
|
243
|
+
MIT
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentMeta, AgentSource, AnyAgentMeta, ArcusAgentMeta, OndoAgentMeta, QfexAgentMeta } from "../settings.js";
|
|
1
|
+
import type { AgentMeta, AgentSource, AnyAgentMeta, ArcusAgentMeta, OndoAgentMeta, QfexAgentMeta, RisexAgentMeta } from "../settings.js";
|
|
2
2
|
import type { Network } from "../exchanges/venue-types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Attempt to acquire the lockfile for an exchange's agent-approve operation.
|
|
@@ -56,6 +56,7 @@ type VenueAgentMap = {
|
|
|
56
56
|
ondo: OndoAgentMeta;
|
|
57
57
|
arcus: ArcusAgentMeta;
|
|
58
58
|
qfex: QfexAgentMeta;
|
|
59
|
+
risex: RisexAgentMeta;
|
|
59
60
|
};
|
|
60
61
|
/** Named agent, or the only/first registered one when agentName is omitted. */
|
|
61
62
|
export declare function getVenueAgent<V extends keyof VenueAgentMap>(venue: V, agentName?: string): VenueAgentMap[V] | null;
|
|
@@ -65,6 +66,6 @@ export declare function setVenueAgent<V extends keyof VenueAgentMap>(venue: V, m
|
|
|
65
66
|
export declare function deleteVenueAgent(venue: keyof VenueAgentMap, agentName: string): boolean;
|
|
66
67
|
export declare function listVenueAgents(): Array<{
|
|
67
68
|
exchange: keyof VenueAgentMap;
|
|
68
|
-
meta: OndoAgentMeta | ArcusAgentMeta | QfexAgentMeta;
|
|
69
|
+
meta: OndoAgentMeta | ArcusAgentMeta | QfexAgentMeta | RisexAgentMeta;
|
|
69
70
|
}>;
|
|
70
71
|
export {};
|
|
@@ -91,7 +91,7 @@ function isPidAlive(pid) {
|
|
|
91
91
|
* agentName is omitted.
|
|
92
92
|
*/
|
|
93
93
|
/** Venues whose agents use a non-legacy meta shape (see getVenueAgent). */
|
|
94
|
-
const NON_LEGACY_AGENT_VENUES = new Set(["ondo", "arcus", "qfex"]);
|
|
94
|
+
const NON_LEGACY_AGENT_VENUES = new Set(["ondo", "arcus", "qfex", "risex"]);
|
|
95
95
|
function assertLegacyAgentVenue(exchange) {
|
|
96
96
|
if (NON_LEGACY_AGENT_VENUES.has(exchange)) {
|
|
97
97
|
throw new PerpError("INVALID_PARAMS", `${exchange} agents use a venue-specific meta; call getVenueAgent("${exchange}")`);
|
|
@@ -294,7 +294,7 @@ export function deleteVenueAgent(venue, agentName) {
|
|
|
294
294
|
export function listVenueAgents() {
|
|
295
295
|
const settings = loadSettings();
|
|
296
296
|
const out = [];
|
|
297
|
-
for (const v of ["ondo", "arcus", "qfex"]) {
|
|
297
|
+
for (const v of ["ondo", "arcus", "qfex", "risex"]) {
|
|
298
298
|
const m = settings.agents?.[v];
|
|
299
299
|
if (m)
|
|
300
300
|
for (const meta of Object.values(m))
|
|
@@ -22,6 +22,8 @@ export function fetchHyperliquidMeta() {
|
|
|
22
22
|
const ctxs = (json[1] ?? []);
|
|
23
23
|
const out = [];
|
|
24
24
|
universe.forEach((asset, i) => {
|
|
25
|
+
if (asset.isDelisted === true)
|
|
26
|
+
return; // delisted: zeroed funding / mark history, not a market
|
|
25
27
|
const ctx = (ctxs[i] ?? {});
|
|
26
28
|
const symbol = String(asset.name ?? "");
|
|
27
29
|
const funding = Number(ctx.funding);
|
|
@@ -49,6 +51,8 @@ export function parseHyperliquidMetaRaw(raw) {
|
|
|
49
51
|
const ctxs = (raw[1] ?? []);
|
|
50
52
|
const assets = (universe?.universe ?? []);
|
|
51
53
|
assets.forEach((a, i) => {
|
|
54
|
+
if (a.isDelisted === true)
|
|
55
|
+
return; // delisted: not a market (zeroed funding)
|
|
52
56
|
const ctx = (ctxs[i] ?? {});
|
|
53
57
|
const sym = String(a.name ?? "");
|
|
54
58
|
if (!sym)
|
|
@@ -17,6 +17,11 @@ export interface LighterFundingEntry {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function fetchLighterOrderBookDetails(): Promise<LighterMarketDetail[]>;
|
|
19
19
|
export declare function fetchLighterOrderBookDetailsRaw(): Promise<unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* GET /api/v1/funding-rates (Lighter's own rows): the PREDICTED rate × 8 — a fraction per 8 h, NOT
|
|
22
|
+
* the venue basis (per 1 h, the settled rate of the market rows). Not read by perp-cli's funding
|
|
23
|
+
* comparisons (they use fetchVenuesRates); kept for API compatibility. Normalize with 8 h.
|
|
24
|
+
*/
|
|
20
25
|
export declare function fetchLighterFundingRates(): Promise<LighterFundingEntry[]>;
|
|
21
26
|
export declare function fetchLighterFundingRatesRaw(): Promise<unknown>;
|
|
22
27
|
export declare function parseLighterRaw(detailsRaw: unknown, fundingRaw: unknown): {
|
|
@@ -37,6 +37,11 @@ export function fetchLighterOrderBookDetailsRaw() {
|
|
|
37
37
|
return res.json();
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* GET /api/v1/funding-rates (Lighter's own rows): the PREDICTED rate × 8 — a fraction per 8 h, NOT
|
|
42
|
+
* the venue basis (per 1 h, the settled rate of the market rows). Not read by perp-cli's funding
|
|
43
|
+
* comparisons (they use fetchVenuesRates); kept for API compatibility. Normalize with 8 h.
|
|
44
|
+
*/
|
|
40
45
|
export function fetchLighterFundingRates() {
|
|
41
46
|
// SSOT rule #2: error must propagate.
|
|
42
47
|
// Row-level: rate is required (it's the field the endpoint exists for); a
|
|
@@ -1,21 +1,88 @@
|
|
|
1
|
-
import { PACIFICA_API_URL, sharedApiNetwork } from "./urls.js";
|
|
1
|
+
import { PACIFICA_API_URL, PACIFICA_INFO_URL, sharedApiNetwork } from "./urls.js";
|
|
2
|
+
import { PerpError } from "../../errors.js";
|
|
3
|
+
import { isPacificaPerpetual } from "../../ws/pacifica-instrument.js";
|
|
2
4
|
import { assertOk } from "./_http.js";
|
|
3
5
|
import { withCache, TTL_MARKET } from "../../cache.js";
|
|
6
|
+
import { getNetworkConfig } from "../../pacifica/constants.js";
|
|
7
|
+
import { PACIFICA_COST_STANDARD, PACIFICA_READ_NEED, pacificaRateLimiter } from "../../pacifica/rate-limit.js";
|
|
4
8
|
// ── Fetchers ──
|
|
9
|
+
const publicRate = () => pacificaRateLimiter(getNetworkConfig(sharedApiNetwork()).restUrl, undefined, "unsigned");
|
|
10
|
+
/**
|
|
11
|
+
* Unauthenticated GET through the shared Pacifica credit limiter (the IP quota the adapter's
|
|
12
|
+
* keyless client also spends): headers recorded, HTTP 429 → RATE_LIMITED. Never waits
|
|
13
|
+
* (maxWaitMs 0): arb / dashboard callers settle venues with allSettled, so a nearly spent quota
|
|
14
|
+
* fails this leg fast (RATE_LIMITED, nothing sent) instead of stalling the scan.
|
|
15
|
+
*/
|
|
16
|
+
async function pacificaPublicGet(url, what) {
|
|
17
|
+
const rate = publicRate();
|
|
18
|
+
const ticket = await rate.acquire(what, { ...PACIFICA_READ_NEED, maxWaitMs: 0 });
|
|
19
|
+
let res;
|
|
20
|
+
try {
|
|
21
|
+
res = await fetch(url);
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
ticket.release();
|
|
25
|
+
}
|
|
26
|
+
rate.observe(res);
|
|
27
|
+
if (res.status === 429)
|
|
28
|
+
throw rate.rateLimitedError(what, res, await res.text());
|
|
29
|
+
return res;
|
|
30
|
+
}
|
|
31
|
+
const INFO_REMEDIATION = "The Pacifica /info response shape changed; retry, and report it if it persists";
|
|
32
|
+
/**
|
|
33
|
+
* Symbols of GET /info rows with instrument_type "perpetual". `/info/prices` also lists spot
|
|
34
|
+
* markets (SOL-USDC) — they must not reach arb / funding-arb as perps. A row without the field
|
|
35
|
+
* cannot be classified → EXCHANGE_ERROR (same rule as the adapter's getMarkets).
|
|
36
|
+
* Cached as a string[] (the file cache is JSON: a Set would come back as `{}`); the Set is built
|
|
37
|
+
* outside withCache.
|
|
38
|
+
*/
|
|
39
|
+
async function fetchPacificaPerpSymbols() {
|
|
40
|
+
const symbols = await withCache(`pub:pac:${sharedApiNetwork()}:perps:list`, TTL_MARKET, async () => {
|
|
41
|
+
const res = await pacificaPublicGet(PACIFICA_INFO_URL, "GET /info");
|
|
42
|
+
await assertOk(res, "Pacifica info");
|
|
43
|
+
const json = (await res.json());
|
|
44
|
+
if (json === null || typeof json !== "object" || !Array.isArray(json.data)) {
|
|
45
|
+
throw new PerpError("EXCHANGE_ERROR", "Pacifica GET /info has no data array", { exchange: "pacifica", remediation: INFO_REMEDIATION });
|
|
46
|
+
}
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const m of json.data) {
|
|
49
|
+
if (m === null || typeof m !== "object" || Array.isArray(m)) {
|
|
50
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica GET /info row is not an object: ${JSON.stringify(m)}`, { exchange: "pacifica", remediation: INFO_REMEDIATION });
|
|
51
|
+
}
|
|
52
|
+
const row = m;
|
|
53
|
+
if (typeof row.instrument_type !== "string") {
|
|
54
|
+
throw new PerpError("EXCHANGE_ERROR", `Pacifica /info row ${String(row.symbol)} has no instrument_type`, { exchange: "pacifica", remediation: INFO_REMEDIATION });
|
|
55
|
+
}
|
|
56
|
+
if (isPacificaPerpetual(row))
|
|
57
|
+
out.push(String(row.symbol));
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
});
|
|
61
|
+
if (!Array.isArray(symbols)) {
|
|
62
|
+
// A cache entry from before the string[] format (or a corrupt one) is not trusted.
|
|
63
|
+
throw new PerpError("EXCHANGE_ERROR", "Pacifica /info perp list cache entry is not a list", { exchange: "pacifica", remediation: INFO_REMEDIATION });
|
|
64
|
+
}
|
|
65
|
+
return new Set(symbols);
|
|
66
|
+
}
|
|
67
|
+
/** `/info/prices` envelope reduced to perp rows (`{ data }`, the shape parsePacificaRaw reads). */
|
|
68
|
+
async function fetchPacificaPerpPrices() {
|
|
69
|
+
const [res, perps] = await Promise.all([pacificaPublicGet(PACIFICA_API_URL, "GET /info/prices"), fetchPacificaPerpSymbols()]);
|
|
70
|
+
await assertOk(res, "Pacifica prices");
|
|
71
|
+
const json = await res.json();
|
|
72
|
+
const data = json.data ?? json;
|
|
73
|
+
if (!Array.isArray(data))
|
|
74
|
+
return { data: [] };
|
|
75
|
+
return { data: data.filter((p) => perps.has(String(p.symbol))) };
|
|
76
|
+
}
|
|
5
77
|
export function fetchPacificaPrices() {
|
|
6
78
|
// SSOT rule #2: error must propagate. Row-level skipping is applied for
|
|
7
79
|
// missing/invalid mark or funding so a 0 doesn't reach downstream paths.
|
|
8
|
-
return withCache(`pub:pac:${sharedApiNetwork()}:prices`, TTL_MARKET, async () => {
|
|
9
|
-
const
|
|
10
|
-
await assertOk(res, "Pacifica prices");
|
|
11
|
-
const json = await res.json();
|
|
12
|
-
const data = json.data ?? json;
|
|
13
|
-
if (!Array.isArray(data))
|
|
14
|
-
return [];
|
|
80
|
+
return withCache(`pub:pac:${sharedApiNetwork()}:prices:perp`, TTL_MARKET, async () => {
|
|
81
|
+
const { data } = await fetchPacificaPerpPrices();
|
|
15
82
|
const out = [];
|
|
16
83
|
for (const p of data) {
|
|
17
84
|
const symbol = String(p.symbol ?? "");
|
|
18
|
-
const fundingRaw = p.
|
|
85
|
+
const fundingRaw = p.funding; // last settled hourly rate (the market row / WS `funding`), not next_funding
|
|
19
86
|
const funding = Number(fundingRaw);
|
|
20
87
|
const mark = Number(p.mark);
|
|
21
88
|
if (!symbol || fundingRaw === undefined || !Number.isFinite(funding) || !Number.isFinite(mark) || mark <= 0) {
|
|
@@ -32,11 +99,8 @@ export function fetchPacificaPricesRaw() {
|
|
|
32
99
|
// Callers should wrap with Promise.allSettled to keep multi-DEX comparisons
|
|
33
100
|
// running when one DEX is down. Non-2xx responses are also rejected so a
|
|
34
101
|
// 5xx JSON body cannot fulfill as if it were valid data.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
await assertOk(res, "Pacifica prices");
|
|
38
|
-
return res.json();
|
|
39
|
-
});
|
|
102
|
+
// Perp rows only (spot SOL-USDC dropped via /info instrument_type).
|
|
103
|
+
return withCache(`pub:pac:${sharedApiNetwork()}:prices:raw:perp`, TTL_MARKET, fetchPacificaPerpPrices);
|
|
40
104
|
}
|
|
41
105
|
export function parsePacificaRaw(raw) {
|
|
42
106
|
const rates = new Map();
|
|
@@ -49,7 +113,7 @@ export function parsePacificaRaw(raw) {
|
|
|
49
113
|
if (!sym)
|
|
50
114
|
continue;
|
|
51
115
|
// SSOT rule #2: skip rows missing real funding/mark; never publish 0.
|
|
52
|
-
const fundingRaw = p.
|
|
116
|
+
const fundingRaw = p.funding; // last settled hourly rate (the market row / WS `funding`), not next_funding
|
|
53
117
|
const fundingNumber = Number(fundingRaw);
|
|
54
118
|
if (fundingRaw !== undefined && Number.isFinite(fundingNumber)) {
|
|
55
119
|
rates.set(sym, fundingNumber);
|
|
@@ -64,11 +128,17 @@ export function parsePacificaRaw(raw) {
|
|
|
64
128
|
// ── Health check ──
|
|
65
129
|
export async function pingPacifica() {
|
|
66
130
|
const start = Date.now();
|
|
131
|
+
// A health probe asks the venue itself (never throttled locally — an exhausted quota shows as the
|
|
132
|
+
// venue's HTTP 429), but its cost and headers go through the shared limiter.
|
|
133
|
+
const rate = publicRate();
|
|
134
|
+
const ticket = rate.track(PACIFICA_COST_STANDARD);
|
|
67
135
|
try {
|
|
68
136
|
const res = await fetch(PACIFICA_API_URL);
|
|
137
|
+
ticket.settle(res);
|
|
69
138
|
return { ok: res.ok, latencyMs: Date.now() - start, status: res.status };
|
|
70
139
|
}
|
|
71
140
|
catch {
|
|
141
|
+
ticket.release();
|
|
72
142
|
return { ok: false, latencyMs: Date.now() - start, status: 0 };
|
|
73
143
|
}
|
|
74
144
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
type LegacyPublicVenue = "pacifica" | "hyperliquid" | "aster";
|
|
9
9
|
export declare function legacyPublicBaseUrl(venue: LegacyPublicVenue, net: "mainnet" | "testnet"): string;
|
|
10
10
|
export declare let PACIFICA_API_URL: string;
|
|
11
|
+
/** GET /info (instrument_type: which `prices` rows are perps). */
|
|
12
|
+
export declare let PACIFICA_INFO_URL: string;
|
|
11
13
|
export declare let HYPERLIQUID_API_URL: string;
|
|
12
14
|
export declare let LIGHTER_API_URL: string;
|
|
13
15
|
/** Network selected by the CLI entry point (setSharedApiNetwork). */
|
package/dist/api/public/urls.js
CHANGED
|
@@ -35,6 +35,8 @@ const TESTNET_URLS = {
|
|
|
35
35
|
lighter: "https://testnet.zklighter.elliot.ai",
|
|
36
36
|
};
|
|
37
37
|
export let PACIFICA_API_URL = MAINNET_URLS.pacifica;
|
|
38
|
+
/** GET /info (instrument_type: which `prices` rows are perps). */
|
|
39
|
+
export let PACIFICA_INFO_URL = `${PAC_MAINNET_REST}/info`;
|
|
38
40
|
export let HYPERLIQUID_API_URL = MAINNET_URLS.hyperliquid;
|
|
39
41
|
export let LIGHTER_API_URL = MAINNET_URLS.lighter;
|
|
40
42
|
let _sharedNetwork = "mainnet";
|
|
@@ -50,6 +52,7 @@ export function setSharedApiNetwork(network) {
|
|
|
50
52
|
_sharedNetwork = network;
|
|
51
53
|
const urls = network === "testnet" ? TESTNET_URLS : MAINNET_URLS;
|
|
52
54
|
PACIFICA_API_URL = urls.pacifica;
|
|
55
|
+
PACIFICA_INFO_URL = `${network === "testnet" ? PAC_TESTNET_REST : PAC_MAINNET_REST}/info`;
|
|
53
56
|
HYPERLIQUID_API_URL = urls.hyperliquid;
|
|
54
57
|
LIGHTER_API_URL = urls.lighter;
|
|
55
58
|
}
|
package/dist/arb/sizing.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* @returns size string that works for both exchanges, or null if not viable
|
|
6
6
|
*/
|
|
7
7
|
export function computeMatchedSize(sizeUsd, price, longExchange, shortExchange, opts) {
|
|
8
|
-
|
|
8
|
+
// NaN / Infinity (e.g. a "-" mark parsed as a number) would size to "NaN": no size.
|
|
9
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(sizeUsd))
|
|
9
10
|
return null;
|
|
10
11
|
const rawSize = sizeUsd / price;
|
|
11
12
|
// Use explicitly provided decimals if available, else conservative defaults
|
|
@@ -105,7 +106,8 @@ function getMinNotional(exchange) {
|
|
|
105
106
|
* Uses spot exchange's size decimals for the spot leg.
|
|
106
107
|
*/
|
|
107
108
|
export function computeSpotPerpMatchedSize(sizeUsd, price, spotExchange, perpExchange, spotSizeDecimals, perpSizeDecimals) {
|
|
108
|
-
|
|
109
|
+
// NaN / Infinity (e.g. a "-" mark parsed as a number) would size to "NaN": no size.
|
|
110
|
+
if (!Number.isFinite(price) || price <= 0 || !Number.isFinite(sizeUsd))
|
|
109
111
|
return null;
|
|
110
112
|
// If explicit decimals provided, use them; otherwise derive from exchange
|
|
111
113
|
const spotDec = spotSizeDecimals ?? getSizeDecimals(`spot:${spotExchange}`);
|
package/dist/arb/state.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ export interface ArbPositionState {
|
|
|
23
23
|
spotExchange?: string;
|
|
24
24
|
/** Spot symbol, e.g. "ETH/USDC" (for spot-perp mode) */
|
|
25
25
|
spotSymbol?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The legs' VENUE symbols when they differ from `symbol` (the canonical key): Hyperliquid
|
|
28
|
+
* kPEPE ↔ Lighter 1000PEPE. Absent = both legs trade as `symbol`.
|
|
29
|
+
*/
|
|
30
|
+
longSymbol?: string;
|
|
31
|
+
shortSymbol?: string;
|
|
26
32
|
}
|
|
27
33
|
export interface ArbDaemonState {
|
|
28
34
|
version: 1;
|
package/dist/arb/utils.d.ts
CHANGED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Settlement schedules per venue (UTC hours when settlement occurs), generated
|
|
6
|
-
* from `VenueDefinition.settlementIntervalHours`, anchored at 00:00 UTC
|
|
7
|
-
* (design §10.1, §10.13 #1). Venues whose interval is per-market (Aster)
|
|
6
|
+
* from `VenueDefinition.settlementIntervalHours`, anchored at 00:00 UTC. Venues whose interval is per-market (Aster)
|
|
8
7
|
* have no entry — pass the market's interval to getLastSettlement().
|
|
9
8
|
*/
|
|
10
9
|
export declare const SETTLEMENT_SCHEDULES: Record<string, number[]>;
|
package/dist/arb/utils.js
CHANGED
|
@@ -8,8 +8,7 @@ import { ensureVenuesRegistered } from "../exchanges/venues/index.js";
|
|
|
8
8
|
ensureVenuesRegistered();
|
|
9
9
|
/**
|
|
10
10
|
* Settlement schedules per venue (UTC hours when settlement occurs), generated
|
|
11
|
-
* from `VenueDefinition.settlementIntervalHours`, anchored at 00:00 UTC
|
|
12
|
-
* (design §10.1, §10.13 #1). Venues whose interval is per-market (Aster)
|
|
11
|
+
* from `VenueDefinition.settlementIntervalHours`, anchored at 00:00 UTC. Venues whose interval is per-market (Aster)
|
|
13
12
|
* have no entry — pass the market's interval to getLastSettlement().
|
|
14
13
|
*/
|
|
15
14
|
export const SETTLEMENT_SCHEDULES = Object.fromEntries(listVenues()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Network, type PublicBalanceResult } from "./exchanges/venue.js";
|
|
2
|
+
/**
|
|
3
|
+
* The JSON shape of one venue's answer: `balance` and `spot` always (null = not published / per
|
|
4
|
+
* account), the venue-specific keys only on their venue (Lighter `accounts`, Hyperliquid `dexes` +
|
|
5
|
+
* `accountMode`).
|
|
6
|
+
*/
|
|
7
|
+
export type BalanceData = Pick<PublicBalanceResult, "balance" | "spot"> & Partial<{
|
|
8
|
+
accounts: NonNullable<PublicBalanceResult["accounts"]>;
|
|
9
|
+
dexes: NonNullable<PublicBalanceResult["dexes"]>;
|
|
10
|
+
accountMode: string;
|
|
11
|
+
}>;
|
|
12
|
+
export interface AddressBalance {
|
|
13
|
+
venue: string;
|
|
14
|
+
data: BalanceData;
|
|
15
|
+
/** Sub-reads that failed (Hyperliquid HIP-3 dex), keyed "hip3:<dex>". */
|
|
16
|
+
errors: Record<string, {
|
|
17
|
+
code: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
notes: string[];
|
|
21
|
+
/** One human line per failed sub-read: its balance is unknown, not zero. */
|
|
22
|
+
warnings: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface AddressBalanceOptions {
|
|
25
|
+
accountIndex?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function readBalanceByAddress(venue: string, net: Network, address: string, opts?: AddressBalanceOptions): Promise<AddressBalance>;
|
|
28
|
+
export interface AddressBalanceFanOut {
|
|
29
|
+
venues: Record<string, BalanceData>;
|
|
30
|
+
/** A venue that failed (`<venue>`) or a failed sub-read of a venue (`<venue>:hip3:<dex>`). */
|
|
31
|
+
errors: Record<string, {
|
|
32
|
+
code: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}>;
|
|
35
|
+
notes: Record<string, string[]>;
|
|
36
|
+
warnings: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Every venue that reads balances by address; a venue that fails (address format included) is an `errors` entry. */
|
|
39
|
+
export declare function readBalanceByAddressAll(net: Network, address: string, opts?: AddressBalanceOptions): Promise<AddressBalanceFanOut>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `perp account balance --address` and MCP get_balance_by_address: one read path over the
|
|
3
|
+
* registry (VenuePublicData.fetchBalanceByAddress). Keyless; nothing is signed.
|
|
4
|
+
*/
|
|
5
|
+
import { PerpError } from "./errors.js";
|
|
6
|
+
import { fetchBalanceByAddress, getVenue, listPublicBalanceVenues } from "./exchanges/venue.js";
|
|
7
|
+
function dataOf(r) {
|
|
8
|
+
return {
|
|
9
|
+
balance: r.balance,
|
|
10
|
+
spot: r.spot,
|
|
11
|
+
...(r.accounts !== null ? { accounts: r.accounts } : {}),
|
|
12
|
+
...(r.dexes !== null ? { dexes: r.dexes } : {}),
|
|
13
|
+
...(r.accountMode !== null ? { accountMode: r.accountMode } : {}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function warningsOf(venue, errors) {
|
|
17
|
+
return Object.entries(errors).map(([src, e]) => `${venue} ${src} unavailable (${e.code}: ${e.message}) — its balance is missing from this answer (unknown, not zero)`);
|
|
18
|
+
}
|
|
19
|
+
export async function readBalanceByAddress(venue, net, address, opts = {}) {
|
|
20
|
+
const id = getVenue(venue).id;
|
|
21
|
+
const r = await fetchBalanceByAddress(id, net, address, { accountIndex: opts.accountIndex });
|
|
22
|
+
return { venue: id, data: dataOf(r), errors: r.errors, notes: r.notes, warnings: warningsOf(id, r.errors) };
|
|
23
|
+
}
|
|
24
|
+
/** Every venue that reads balances by address; a venue that fails (address format included) is an `errors` entry. */
|
|
25
|
+
export async function readBalanceByAddressAll(net, address, opts = {}) {
|
|
26
|
+
const venues = listPublicBalanceVenues().map(v => v.id);
|
|
27
|
+
const out = { venues: {}, errors: {}, notes: {}, warnings: [] };
|
|
28
|
+
const settled = await Promise.allSettled(venues.map(v => readBalanceByAddress(v, net, address, v === "lighter" || v === "lighter-rh" ? opts : {})));
|
|
29
|
+
settled.forEach((res, i) => {
|
|
30
|
+
const v = venues[i];
|
|
31
|
+
if (res.status === "rejected") {
|
|
32
|
+
const e = res.reason;
|
|
33
|
+
out.errors[v] = e instanceof PerpError ? { code: e.structured.code, message: e.structured.message } : { code: "EXCHANGE_ERROR", message: e instanceof Error ? e.message : String(e) };
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
out.venues[v] = res.value.data;
|
|
37
|
+
for (const [src, e] of Object.entries(res.value.errors))
|
|
38
|
+
out.errors[`${v}:${src}`] = e;
|
|
39
|
+
if (res.value.notes.length > 0)
|
|
40
|
+
out.notes[v] = res.value.notes;
|
|
41
|
+
out.warnings.push(...res.value.warnings);
|
|
42
|
+
});
|
|
43
|
+
return out;
|
|
44
|
+
}
|
package/dist/bot/apex/pulse.js
CHANGED
|
@@ -20,6 +20,9 @@ export function tierRank(tier) {
|
|
|
20
20
|
}
|
|
21
21
|
export function detectPulse(current, history) {
|
|
22
22
|
const symbol = "unknown"; // caller sets the actual symbol
|
|
23
|
+
// Unknown OI → no pulse signal (the OI change cannot be computed).
|
|
24
|
+
if (current.openInterest === null)
|
|
25
|
+
return { symbol, tier: "NONE", confidence: 0, oiChange: 0, volumeRatio: 0 };
|
|
23
26
|
const currentOi = parseFloat(current.openInterest);
|
|
24
27
|
const currentVolume = current.volume24h;
|
|
25
28
|
const past = history.get(symbol) ?? [];
|