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,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Venue-level data contracts
|
|
2
|
+
* Venue-level data contracts.
|
|
3
3
|
*
|
|
4
4
|
* SSOT Rule #2: every "unknown" value is `T | null`, never optional, never a
|
|
5
5
|
* silently substituted default.
|
|
6
6
|
*/
|
|
7
7
|
export const VENUE_IDS = [
|
|
8
8
|
"pacifica", "hyperliquid", "lighter", "aster",
|
|
9
|
-
"lighter-rh", "ondo", "arcus", "qfex",
|
|
9
|
+
"lighter-rh", "ondo", "arcus", "qfex", "risex", "txflow", "variational", "grvt", "extended",
|
|
10
10
|
];
|
|
11
11
|
export const LEGACY_VENUE_IDS = ["pacifica", "hyperliquid", "lighter", "aster"];
|
|
12
12
|
export function isVenueId(s) {
|
|
@@ -21,10 +21,17 @@ export function rateToHourly(rate, basis) {
|
|
|
21
21
|
return rate / basis.hours;
|
|
22
22
|
return rate / (24 * 365);
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Quantity decimals of a lot step, or null when the step is not a power of ten ≤ 1 ("0.001" → 3,
|
|
26
|
+
* "1" → 0; TxFlow "10" / "100" and a "0.5" step → null): a decimals count cannot express them, and
|
|
27
|
+
* 0 next to stepSize "10" read as "any integer". `stepSize` is the authoritative grid.
|
|
28
|
+
*/
|
|
29
|
+
export function decimalsOf(step) {
|
|
30
|
+
const s = step.includes("e") || step.includes("E") ? Number(step).toFixed(20) : step;
|
|
31
|
+
const t = s.includes(".") ? s.replace(/0+$/, "").replace(/\.$/, "") : s;
|
|
32
|
+
if (!/^(1|0\.0*1)$/.test(t))
|
|
33
|
+
return null;
|
|
34
|
+
return t === "1" ? 0 : t.length - 2;
|
|
28
35
|
}
|
|
29
36
|
/** Pure mapping used by new-venue adapters to serve the legacy getMarkets(). */
|
|
30
37
|
/** Tick/step/leverage required for order entry. Throws when the venue did not publish them. */
|
|
@@ -34,9 +41,54 @@ export function requireTradingPrecision(m) {
|
|
|
34
41
|
}
|
|
35
42
|
return { tickSize: m.tickSize, stepSize: m.stepSize, maxLeverage: m.maxLeverage };
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
/**
|
|
45
|
+
* What a VenueMarket.fundingRate is per venue (REST source), consistent with the WS `rateKind` where
|
|
46
|
+
* both read the same field. null = not verified. Venues with per-row kinds (GRVT, Extended,
|
|
47
|
+
* Variational) set their own on top.
|
|
48
|
+
* - arcus: /v1/markets fundingRate = the last hourly settlement (WS markets channel: lastSettled).
|
|
49
|
+
* - risex: current_funding_rate equals the last settled print (venues/risex.ts, verified 2026-10-14).
|
|
50
|
+
* - txflow: allAssetCtxs `funding` moves every tick — the running period's accruing estimate.
|
|
51
|
+
* - hyperliquid: metaAndAssetCtxs `funding` (WS allDexsAssetCtxs) — accruing.
|
|
52
|
+
* - aster: premiumIndex lastFundingRate moves during the period (WS markPrice@arr `r`) — accruing.
|
|
53
|
+
* - pacifica: /info/prices `funding` = the last settled hourly rate (WS prices `funding`).
|
|
54
|
+
* - lighter, lighter-rh: WS market_stats funding_rate = the newest /fundings print — lastSettled.
|
|
55
|
+
* - grvt: the ticker's funding rate is the next settlement's (predicted) — as the adapter labels it.
|
|
56
|
+
* - extended: /info/markets fundingRate is the current hour's accruing rate.
|
|
57
|
+
* - variational: the quoted rate is the predicted next payment.
|
|
58
|
+
* - ondo, qfex: not verified against settled prints → null.
|
|
59
|
+
*/
|
|
60
|
+
const VENUE_MARKET_RATE_KIND = {
|
|
61
|
+
arcus: "lastSettled",
|
|
62
|
+
risex: "lastSettled",
|
|
63
|
+
txflow: "accruing",
|
|
64
|
+
hyperliquid: "accruing",
|
|
65
|
+
aster: "accruing",
|
|
66
|
+
pacifica: "lastSettled",
|
|
67
|
+
lighter: "lastSettled",
|
|
68
|
+
"lighter-rh": "lastSettled",
|
|
69
|
+
grvt: "predicted",
|
|
70
|
+
extended: "accruing",
|
|
71
|
+
variational: "predicted",
|
|
72
|
+
// Ondo /v1/perps/contracts `fundingRate` is the last settled print: live 2026-10-19 21:17 UTC it
|
|
73
|
+
// equalled the 21:00 funding_rate_history row on every market whose rate moves (CXMT 0.0001139,
|
|
74
|
+
// BB 0.00003, PURR 0.0000182, WTI 0.0000038, USDJPY 0.000001 — each different from the 20:00 print).
|
|
75
|
+
ondo: "lastSettled",
|
|
76
|
+
};
|
|
77
|
+
export function fundingRateKindOf(venue, fundingRate) {
|
|
78
|
+
if (fundingRate === null)
|
|
79
|
+
return null;
|
|
80
|
+
const k = VENUE_MARKET_RATE_KIND[venue];
|
|
81
|
+
return k === undefined ? null : k;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* THE market-row shape (`perp market list` / `info` / `prices`, ExchangeMarketInfo) of a
|
|
85
|
+
* VenueMarket — the one mapper for the CLI adapters of VenueMarket venues and the keyless MCP
|
|
86
|
+
* `get_markets` public directory rows. It does not require trading precision: a public row
|
|
87
|
+
* without a published tick / lot / leverage maps those to null.
|
|
88
|
+
*/
|
|
89
|
+
export function venueMarketRow(m) {
|
|
39
90
|
const hours = m.fundingRateBasis.kind === "perHours" ? m.fundingRateBasis.hours : null;
|
|
91
|
+
const status = m.status;
|
|
40
92
|
return {
|
|
41
93
|
symbol: m.symbol,
|
|
42
94
|
markPrice: m.markPrice,
|
|
@@ -44,13 +96,22 @@ export function toLegacyMarketInfo(m) {
|
|
|
44
96
|
fundingRate: m.fundingRate,
|
|
45
97
|
volume24h: m.volume24h,
|
|
46
98
|
openInterest: m.openInterest,
|
|
47
|
-
maxLeverage:
|
|
48
|
-
sizeDecimals: decimalsOf(
|
|
49
|
-
stepSize:
|
|
99
|
+
maxLeverage: m.maxLeverage,
|
|
100
|
+
sizeDecimals: m.stepSize === null ? null : decimalsOf(m.stepSize),
|
|
101
|
+
stepSize: m.stepSize,
|
|
50
102
|
fundingHours: hours,
|
|
51
103
|
marginTiers: m.marginTiers,
|
|
104
|
+
tradable: m.tradable,
|
|
105
|
+
fundingRateKind: fundingRateKindOf(m.venue, m.fundingRate),
|
|
106
|
+
quoteAsset: m.quoteAsset,
|
|
107
|
+
status: typeof status === "string" ? status : null,
|
|
52
108
|
};
|
|
53
109
|
}
|
|
110
|
+
/** venueMarketRow for an adapter that trades the market: tick / lot / leverage must be published. */
|
|
111
|
+
export function toLegacyMarketInfo(m) {
|
|
112
|
+
requireTradingPrecision(m);
|
|
113
|
+
return venueMarketRow(m);
|
|
114
|
+
}
|
|
54
115
|
/**
|
|
55
116
|
* Attach an execution record to a result built without one. Fields of `partial`
|
|
56
117
|
* that are absent are recorded as null (unknown), never defaulted to a value.
|
|
@@ -85,3 +146,13 @@ export function parseOrderRef(s) {
|
|
|
85
146
|
return { kind: "clientOrderId", value: s.slice(2) };
|
|
86
147
|
return { kind: "orderId", value: s };
|
|
87
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Next funding settlement of a venue that documents hourly settlement on the hour (UTC): the next
|
|
151
|
+
* top of the hour after `now`. Used only where the venue's docs say so — Hyperliquid ("paid every
|
|
152
|
+
* hour"), Lighter ("at each hour mark"), Pacifica ("at the end of each 1-hour interval") — and
|
|
153
|
+
* their settled history is stamped on the hour (live 2026-10-19: HL 18:00:00.139 … 21:00:00.032,
|
|
154
|
+
* Pacifica 18:00:02.674 … 21:00:02.574). Other venues keep null unless they publish the time.
|
|
155
|
+
*/
|
|
156
|
+
export function nextTopOfHourMs(now = Date.now()) {
|
|
157
|
+
return (Math.floor(now / 3_600_000) + 1) * 3_600_000;
|
|
158
|
+
}
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* Venue definitions — the single source of truth for "which venue, on which
|
|
3
3
|
* network, signed how, funded how, supporting what".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* One adapter class may serve several
|
|
6
6
|
* venues (LighterAdapter serves `lighter` and `lighter-rh`).
|
|
7
7
|
*
|
|
8
8
|
* Venue definition modules must load adapter code lazily (`import()` inside
|
|
9
9
|
* `create`) so that public-data paths never pull in heavy SDKs (lighter WASM).
|
|
10
10
|
*/
|
|
11
|
-
import
|
|
12
|
-
import type {
|
|
11
|
+
import { PerpError } from "../errors.js";
|
|
12
|
+
import type { ExchangeAdapter, ExchangeBalance, ExchangePosition } from "./interface.js";
|
|
13
|
+
import { type AddressKind } from "./address.js";
|
|
14
|
+
import type { SpotAdapter, SpotBalance } from "./spot-interface.js";
|
|
13
15
|
import type { FundingRateBasis, LegacyVenueId, Network, TimeInForce, VenueFundingRate, VenueId, VenueMarket } from "./venue-types.js";
|
|
14
16
|
import { isVenueId } from "./venue-types.js";
|
|
15
17
|
import type { AnyAgentMeta } from "../settings.js";
|
|
@@ -103,7 +105,75 @@ export interface RateLimitPolicy {
|
|
|
103
105
|
}
|
|
104
106
|
export interface VenuePublicData {
|
|
105
107
|
fetchFundingRates(net: Network): Promise<VenueFundingRate[]>;
|
|
106
|
-
|
|
108
|
+
/**
|
|
109
|
+
* `opts.funding: false` = the caller needs marks / precision only: a venue whose funding rate
|
|
110
|
+
* costs an extra read (Lighter: the WS market_stats snapshot) skips it (rows fundingRate null).
|
|
111
|
+
*/
|
|
112
|
+
fetchMarkets(net: Network, opts?: {
|
|
113
|
+
funding?: boolean;
|
|
114
|
+
}): Promise<VenueMarket[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Keyless open positions of ANY account by its public address (`account positions --address`,
|
|
117
|
+
* MCP get_positions_by_address). Present = the venue supports it; `addressKind` is then the
|
|
118
|
+
* address format it takes (checked before any request). Rows are the keyed `getPositions`
|
|
119
|
+
* mapping (the same mapper) with plain symbols.
|
|
120
|
+
*/
|
|
121
|
+
addressKind?: AddressKind;
|
|
122
|
+
fetchPositionsByAddress?(net: Network, address: string, opts: PublicPositionsOptions): Promise<PublicPositionsResult>;
|
|
123
|
+
/**
|
|
124
|
+
* Keyless balances of ANY account by its public address (`account balance --address`, MCP
|
|
125
|
+
* get_balance_by_address). Present = the venue supports it (takes `addressKind`, like
|
|
126
|
+
* fetchPositionsByAddress). The balance is the keyed `getBalance` math (the same function).
|
|
127
|
+
*/
|
|
128
|
+
fetchBalanceByAddress?(net: Network, address: string, opts: PublicPositionsOptions): Promise<PublicBalanceResult>;
|
|
129
|
+
}
|
|
130
|
+
/** One Lighter account (sub-account) of an address: its balance and spot assets. */
|
|
131
|
+
export interface PublicAccountBalance {
|
|
132
|
+
accountIndex: number;
|
|
133
|
+
balance: ExchangeBalance;
|
|
134
|
+
spot: SpotBalance[];
|
|
135
|
+
}
|
|
136
|
+
export interface PublicBalanceResult {
|
|
137
|
+
/** The account's balance (the keyed getBalance fields + the venue's extras); null on Lighter (per account in `accounts`). */
|
|
138
|
+
balance: ExchangeBalance | null;
|
|
139
|
+
/** Spot token balances where the venue publishes them by address; null = not published (nothing invented). */
|
|
140
|
+
spot: SpotBalance[] | null;
|
|
141
|
+
/** Lighter / Lighter RH: every account of the address (or the one of accountIndex); null elsewhere. */
|
|
142
|
+
accounts: PublicAccountBalance[] | null;
|
|
143
|
+
/** Hyperliquid: HIP-3 dexes with a non-zero account value (the dex's own perp clearinghouse); null elsewhere. */
|
|
144
|
+
dexes: Record<string, ExchangeBalance> | null;
|
|
145
|
+
/** Hyperliquid: the account abstraction mode the balance was read under; null elsewhere. */
|
|
146
|
+
accountMode: string | null;
|
|
147
|
+
/** Sub-reads that failed (Hyperliquid: one HIP-3 dex, "hip3:<dex>") — unknown, never zero. */
|
|
148
|
+
errors: Record<string, {
|
|
149
|
+
code: string;
|
|
150
|
+
message: string;
|
|
151
|
+
}>;
|
|
152
|
+
notes: string[];
|
|
153
|
+
}
|
|
154
|
+
export interface PublicPositionsOptions {
|
|
155
|
+
/** Lighter: one account of the address (sub-accounts); default every account. */
|
|
156
|
+
accountIndex?: number;
|
|
157
|
+
}
|
|
158
|
+
/** A position read by public address: the keyed row plus where it lives. */
|
|
159
|
+
export interface PublicPosition extends ExchangePosition {
|
|
160
|
+
/** Hyperliquid: the HIP-3 dex of the row; null = the native perp dex. */
|
|
161
|
+
dex?: string | null;
|
|
162
|
+
/** Lighter: the account (sub-account) index the row belongs to. */
|
|
163
|
+
accountIndex?: number;
|
|
164
|
+
}
|
|
165
|
+
export interface PublicPositionsResult {
|
|
166
|
+
positions: PublicPosition[];
|
|
167
|
+
/**
|
|
168
|
+
* Sub-reads that failed while the others succeeded (Hyperliquid: one HIP-3 dex), keyed by
|
|
169
|
+
* source ("hip3:xyz"). Their rows are unknown — never silently absent: callers surface them.
|
|
170
|
+
*/
|
|
171
|
+
errors: Record<string, {
|
|
172
|
+
code: string;
|
|
173
|
+
message: string;
|
|
174
|
+
}>;
|
|
175
|
+
/** Human notes on an empty answer ("no Lighter account for this address"). */
|
|
176
|
+
notes: string[];
|
|
107
177
|
}
|
|
108
178
|
export interface VenueCredentials {
|
|
109
179
|
/** Raw private key from --private-key or (legacy venues only) env. */
|
|
@@ -127,11 +197,29 @@ export interface VenueCreateContext {
|
|
|
127
197
|
export declare const READ_ONLY_CREDENTIALS: VenueCredentials;
|
|
128
198
|
export type VenueRole = "marketData" | "account" | "trading";
|
|
129
199
|
export type SymbolIdentity = "baseTicker" | "explicit";
|
|
200
|
+
/**
|
|
201
|
+
* How perp-cli reaches the venue. "official" = a documented public API. "unofficial" = an
|
|
202
|
+
* undocumented API (e.g. the one the venue's own web app uses) that may change or be blocked
|
|
203
|
+
* without notice; every user-facing surface (JSON `venueApi` / `apiStatus`, human footnotes,
|
|
204
|
+
* MCP output, the landing screen, the skill docs) labels data read through it.
|
|
205
|
+
*/
|
|
206
|
+
export type VenueApiStatus = {
|
|
207
|
+
kind: "official";
|
|
208
|
+
} | {
|
|
209
|
+
kind: "unofficial";
|
|
210
|
+
note: string;
|
|
211
|
+
docs: string | null;
|
|
212
|
+
};
|
|
213
|
+
export declare const OFFICIAL_API: VenueApiStatus;
|
|
214
|
+
/** "official" | "unofficial" — the per-row label for mixed-venue tables and JSON. */
|
|
215
|
+
export declare function apiStatusOf(venue: VenueId): VenueApiStatus["kind"];
|
|
216
|
+
/** Human footnote for an unofficial-API venue; null for an official one. */
|
|
217
|
+
export declare function unofficialApiNote(def: VenueDefinition): string | null;
|
|
130
218
|
export interface VenueDefinition {
|
|
131
219
|
id: VenueId;
|
|
132
220
|
aliases: readonly string[];
|
|
133
221
|
displayName: string;
|
|
134
|
-
family: "pacifica" | "hyperliquid" | "lighter" | "aster" | "ondo" | "arcus" | "qfex";
|
|
222
|
+
family: "pacifica" | "hyperliquid" | "lighter" | "aster" | "ondo" | "arcus" | "qfex" | "risex" | "txflow" | "variational" | "grvt" | "extended";
|
|
135
223
|
masterKeyKind: MasterKeyKind;
|
|
136
224
|
agentKeyKind: AgentKeyKind;
|
|
137
225
|
secretStorage: SecretStorage;
|
|
@@ -143,12 +231,22 @@ export interface VenueDefinition {
|
|
|
143
231
|
settlementIntervalHours: number | "perMarket";
|
|
144
232
|
/**
|
|
145
233
|
* false = the unit / meaning of the venue's published funding rate is not verified against
|
|
146
|
-
* settled funding payments yet
|
|
234
|
+
* settled funding payments yet. Such a venue is left out of cross-venue
|
|
147
235
|
* funding comparison (fetchVenuesRates drops it unless includeUnverified is passed), its
|
|
148
236
|
* adapter reports fundingRate null to bots, and funding-arb strategies refuse it as a leg.
|
|
149
237
|
*/
|
|
150
238
|
fundingVerified: boolean;
|
|
151
239
|
roles: readonly VenueRole[];
|
|
240
|
+
/**
|
|
241
|
+
* Roles that are implemented but switched off in this release (phased venue rollout, e.g.
|
|
242
|
+
* RISEx trading in 0.15.0). They are NOT in `roles`; roleUnsupported() uses `reason` /
|
|
243
|
+
* `remediation` for them. null = nothing held back.
|
|
244
|
+
*/
|
|
245
|
+
gatedRoles: {
|
|
246
|
+
roles: readonly VenueRole[];
|
|
247
|
+
reason: string;
|
|
248
|
+
remediation: string;
|
|
249
|
+
} | null;
|
|
152
250
|
/** null when the venue has no trading role. */
|
|
153
251
|
capabilities: VenueCapabilities | null;
|
|
154
252
|
rateLimit: RateLimitPolicy;
|
|
@@ -159,11 +257,10 @@ export interface VenueDefinition {
|
|
|
159
257
|
publicData: VenuePublicData;
|
|
160
258
|
/** null when the venue cannot produce an adapter (public data only). */
|
|
161
259
|
create: ((ctx: VenueCreateContext) => Promise<ExchangeAdapter>) | null;
|
|
162
|
-
/** null = venue has no spot trading
|
|
260
|
+
/** null = venue has no spot trading. */
|
|
163
261
|
spot: VenueSpotDefinition | null;
|
|
164
262
|
/**
|
|
165
|
-
* Largest slippage bound (bps) a market-type order may carry on this venue
|
|
166
|
-
* (design §13.10 #1). resolveSlippage() rejects anything above it with
|
|
263
|
+
* Largest slippage bound (bps) a market-type order may carry on this venue. resolveSlippage() rejects anything above it with
|
|
167
264
|
* INVALID_PARAMS before sending — never clamps.
|
|
168
265
|
*/
|
|
169
266
|
maxSlippageBps: number;
|
|
@@ -173,6 +270,13 @@ export interface VenueDefinition {
|
|
|
173
270
|
* settings.builder.<venue> and the approval is confirmed.
|
|
174
271
|
*/
|
|
175
272
|
builderFee: BuilderFeeCapability | null;
|
|
273
|
+
/** Official documented API, or an unofficial one that is labelled everywhere (VenueApiStatus). */
|
|
274
|
+
apiStatus: VenueApiStatus;
|
|
275
|
+
/**
|
|
276
|
+
* Remediation of every role refusal (roleUnsupported) on a venue that is read-only in this
|
|
277
|
+
* version (Variational, GRVT, Extended). Absent = the generic market-data-only remediation.
|
|
278
|
+
*/
|
|
279
|
+
readOnlyRemediation?: string;
|
|
176
280
|
}
|
|
177
281
|
export declare function registerVenue(def: VenueDefinition): void;
|
|
178
282
|
/** Resolve a venue id or alias. Throws UNKNOWN_VENUE. */
|
|
@@ -181,7 +285,33 @@ export declare function tryResolveVenueId(nameOrAlias: string): VenueId | null;
|
|
|
181
285
|
export declare function getVenue(nameOrAlias: string): VenueDefinition;
|
|
182
286
|
export declare function listVenues(): VenueDefinition[];
|
|
183
287
|
export declare function listVenuesWithRole(role: VenueRole): VenueDefinition[];
|
|
288
|
+
/** Venues that read positions by public address (VenuePublicData.fetchPositionsByAddress). */
|
|
289
|
+
export declare function listPublicPositionVenues(): VenueDefinition[];
|
|
290
|
+
/** CAPABILITY_UNSUPPORTED for a venue without public-address position reads (registry-derived). */
|
|
291
|
+
export declare function publicPositionsUnsupported(def: VenueDefinition): PerpError;
|
|
292
|
+
/**
|
|
293
|
+
* Open positions of `address` on `venue`, keyless: unsupported venue → CAPABILITY_UNSUPPORTED,
|
|
294
|
+
* wrong address format → INVALID_PARAMS, both before any request.
|
|
295
|
+
*/
|
|
296
|
+
export declare function fetchPositionsByAddress(venue: string, net: Network, address: string, opts?: PublicPositionsOptions): Promise<PublicPositionsResult>;
|
|
297
|
+
/** Venues that read balances by public address (VenuePublicData.fetchBalanceByAddress). */
|
|
298
|
+
export declare function listPublicBalanceVenues(): VenueDefinition[];
|
|
299
|
+
/** CAPABILITY_UNSUPPORTED for a venue without public-address balance reads (registry-derived). */
|
|
300
|
+
export declare function publicBalanceUnsupported(def: VenueDefinition): PerpError;
|
|
301
|
+
/**
|
|
302
|
+
* Balances of `address` on `venue`, keyless: unsupported venue → CAPABILITY_UNSUPPORTED, wrong
|
|
303
|
+
* address format → INVALID_PARAMS, both before any request.
|
|
304
|
+
*/
|
|
305
|
+
export declare function fetchBalanceByAddress(venue: string, net: Network, address: string, opts?: PublicPositionsOptions): Promise<PublicBalanceResult>;
|
|
184
306
|
export declare function networkConfig(def: VenueDefinition, net: Network): VenueNetworkConfig;
|
|
307
|
+
/**
|
|
308
|
+
* The uniform refusal for any account / trading / funds / agent / builder / bot action on a venue
|
|
309
|
+
* whose roles do not include `role` (a market-data-only venue such as TxFlow). Raised before any
|
|
310
|
+
* credential is resolved or any request is sent.
|
|
311
|
+
*/
|
|
312
|
+
export declare function roleUnsupported(def: VenueDefinition, feature: string, role?: VenueRole): PerpError;
|
|
313
|
+
/** Throws roleUnsupported() unless the venue has `role`. */
|
|
314
|
+
export declare function requireRole(def: VenueDefinition, role: VenueRole, feature: string): void;
|
|
185
315
|
export declare function requireCapabilities(def: VenueDefinition): VenueCapabilities;
|
|
186
316
|
/**
|
|
187
317
|
* A venue reference as it appears in stored data and funding keys:
|
|
@@ -194,7 +324,7 @@ export interface VenueRef {
|
|
|
194
324
|
export declare function parseVenueRef(ref: string): VenueRef;
|
|
195
325
|
/**
|
|
196
326
|
* Funding basis for a venue ref. HIP-3 dexes use the Hyperliquid core basis
|
|
197
|
-
* (current behavior preserved;
|
|
327
|
+
* (current behavior preserved; verify against live data).
|
|
198
328
|
* Throws for venues whose basis is per-market; callers must use the market's value.
|
|
199
329
|
*/
|
|
200
330
|
export declare function fundingBasisOf(ref: string): FundingRateBasis;
|
|
@@ -202,9 +332,18 @@ export declare function settlementIntervalOf(ref: string): number;
|
|
|
202
332
|
/**
|
|
203
333
|
* Refuse a venue whose funding rate is unverified (VenueDefinition.fundingVerified false) as a
|
|
204
334
|
* leg of a funding strategy (arb scan ranking, arb exec / auto, funding bots). Uniform
|
|
205
|
-
* CAPABILITY_UNSUPPORTED
|
|
335
|
+
* CAPABILITY_UNSUPPORTED. Unknown venue refs throw UNKNOWN_VENUE.
|
|
206
336
|
*/
|
|
207
337
|
export declare function assertFundingArbVenue(ref: string): void;
|
|
338
|
+
/**
|
|
339
|
+
* Market rows (`market list|prices|info`, MCP get_markets) state whether their venue's funding rate
|
|
340
|
+
* is verified — on every venue, true or false (VenueDefinition.fundingVerified), so a consumer
|
|
341
|
+
* never has to read a missing field as "verified". A venue ref ("hip3:xyz" → hyperliquid) is
|
|
342
|
+
* resolved; an unknown ref leaves the rows as they are.
|
|
343
|
+
*/
|
|
344
|
+
export declare function withFundingVerified<T extends object>(venue: string, rows: T[]): Array<T & {
|
|
345
|
+
fundingVerified: boolean;
|
|
346
|
+
}> | T[];
|
|
208
347
|
export declare function isLegacyVenue(def: VenueDefinition): def is VenueDefinition & {
|
|
209
348
|
id: LegacyVenueId;
|
|
210
349
|
};
|
package/dist/exchanges/venue.js
CHANGED
|
@@ -2,17 +2,30 @@
|
|
|
2
2
|
* Venue definitions — the single source of truth for "which venue, on which
|
|
3
3
|
* network, signed how, funded how, supporting what".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* One adapter class may serve several
|
|
6
6
|
* venues (LighterAdapter serves `lighter` and `lighter-rh`).
|
|
7
7
|
*
|
|
8
8
|
* Venue definition modules must load adapter code lazily (`import()` inside
|
|
9
9
|
* `create`) so that public-data paths never pull in heavy SDKs (lighter WASM).
|
|
10
10
|
*/
|
|
11
11
|
import { PerpError } from "../errors.js";
|
|
12
|
+
import { requireAddressOfKind } from "./address.js";
|
|
12
13
|
import { isVenueId } from "./venue-types.js";
|
|
14
|
+
import { optionsVenueWords } from "./options-venues.js";
|
|
13
15
|
export const READ_ONLY_CREDENTIALS = {
|
|
14
16
|
pk: null, owsWallet: null, owsPassphrase: null, owsKeyToken: null, agent: null,
|
|
15
17
|
};
|
|
18
|
+
export const OFFICIAL_API = { kind: "official" };
|
|
19
|
+
/** "official" | "unofficial" — the per-row label for mixed-venue tables and JSON. */
|
|
20
|
+
export function apiStatusOf(venue) {
|
|
21
|
+
return getVenue(venue).apiStatus.kind;
|
|
22
|
+
}
|
|
23
|
+
/** Human footnote for an unofficial-API venue; null for an official one. */
|
|
24
|
+
export function unofficialApiNote(def) {
|
|
25
|
+
return def.apiStatus.kind === "unofficial"
|
|
26
|
+
? `${def.displayName}: UNOFFICIAL API — ${def.apiStatus.note}`
|
|
27
|
+
: null;
|
|
28
|
+
}
|
|
16
29
|
// ── Registry ──
|
|
17
30
|
const _venues = new Map();
|
|
18
31
|
const _aliases = new Map();
|
|
@@ -29,8 +42,12 @@ export function registerVenue(def) {
|
|
|
29
42
|
}
|
|
30
43
|
}
|
|
31
44
|
function unknownVenue(input) {
|
|
45
|
+
// An options venue (e.g. derive) is not a perp venue: say where it works instead.
|
|
46
|
+
const optionsOnly = optionsVenueWords().includes(input.toLowerCase())
|
|
47
|
+
? ` — ${input} is options-only: perp -e ${input} options …`
|
|
48
|
+
: "";
|
|
32
49
|
return new PerpError("UNKNOWN_VENUE", `Unknown venue: "${input}"`, {
|
|
33
|
-
remediation: `Use one of: ${[..._venues.keys()].join(", ")}`,
|
|
50
|
+
remediation: `Use one of: ${[..._venues.keys()].join(", ")}${optionsOnly}`,
|
|
34
51
|
});
|
|
35
52
|
}
|
|
36
53
|
/** Resolve a venue id or alias. Throws UNKNOWN_VENUE. */
|
|
@@ -56,6 +73,64 @@ export function listVenues() {
|
|
|
56
73
|
export function listVenuesWithRole(role) {
|
|
57
74
|
return listVenues().filter(v => v.roles.includes(role));
|
|
58
75
|
}
|
|
76
|
+
/** Venues that read positions by public address (VenuePublicData.fetchPositionsByAddress). */
|
|
77
|
+
export function listPublicPositionVenues() {
|
|
78
|
+
return listVenues().filter(v => typeof v.publicData.fetchPositionsByAddress === "function" && v.publicData.addressKind !== undefined);
|
|
79
|
+
}
|
|
80
|
+
/** CAPABILITY_UNSUPPORTED for a venue without public-address position reads (registry-derived). */
|
|
81
|
+
export function publicPositionsUnsupported(def) {
|
|
82
|
+
const reason = !def.roles.includes("account")
|
|
83
|
+
? `${def.displayName} is available for market data only (no account reads)`
|
|
84
|
+
: `${def.displayName} publishes positions only to the account itself (signed or API-key reads)`;
|
|
85
|
+
const supported = listPublicPositionVenues().map(v => v.id).join(", ");
|
|
86
|
+
return new PerpError("CAPABILITY_UNSUPPORTED", `${def.id} does not support publicPositions — ${reason}`, {
|
|
87
|
+
venue: def.id, feature: "publicPositions", reason,
|
|
88
|
+
remediation: `Public-address reads: ${supported}; for your own account: perp -e ${def.id} account positions`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Open positions of `address` on `venue`, keyless: unsupported venue → CAPABILITY_UNSUPPORTED,
|
|
93
|
+
* wrong address format → INVALID_PARAMS, both before any request.
|
|
94
|
+
*/
|
|
95
|
+
export async function fetchPositionsByAddress(venue, net, address, opts = {}) {
|
|
96
|
+
const def = getVenue(venue);
|
|
97
|
+
const pd = def.publicData;
|
|
98
|
+
if (pd.fetchPositionsByAddress === undefined || pd.addressKind === undefined)
|
|
99
|
+
throw publicPositionsUnsupported(def);
|
|
100
|
+
if (opts.accountIndex !== undefined && def.id !== "lighter" && def.id !== "lighter-rh") {
|
|
101
|
+
throw new PerpError("INVALID_PARAMS", `--account-index applies to Lighter only (${def.id} has one account per address)`, { exchange: def.id, remediation: "Drop --account-index" });
|
|
102
|
+
}
|
|
103
|
+
return pd.fetchPositionsByAddress(net, requireAddressOfKind(def.id, pd.addressKind, address), opts);
|
|
104
|
+
}
|
|
105
|
+
/** Venues that read balances by public address (VenuePublicData.fetchBalanceByAddress). */
|
|
106
|
+
export function listPublicBalanceVenues() {
|
|
107
|
+
return listVenues().filter(v => typeof v.publicData.fetchBalanceByAddress === "function" && v.publicData.addressKind !== undefined);
|
|
108
|
+
}
|
|
109
|
+
/** CAPABILITY_UNSUPPORTED for a venue without public-address balance reads (registry-derived). */
|
|
110
|
+
export function publicBalanceUnsupported(def) {
|
|
111
|
+
const reason = !def.roles.includes("account")
|
|
112
|
+
? `${def.displayName} is available for market data only (no account reads)`
|
|
113
|
+
: `${def.displayName} publishes balances only to the account itself (signed or API-key reads)`;
|
|
114
|
+
const supported = listPublicBalanceVenues().map(v => v.id).join(", ");
|
|
115
|
+
return new PerpError("CAPABILITY_UNSUPPORTED", `${def.id} does not support publicBalance — ${reason}`, {
|
|
116
|
+
venue: def.id, feature: "publicBalance", reason,
|
|
117
|
+
remediation: `Public-address reads: ${supported}; for your own account: perp -e ${def.id} portfolio`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Balances of `address` on `venue`, keyless: unsupported venue → CAPABILITY_UNSUPPORTED, wrong
|
|
122
|
+
* address format → INVALID_PARAMS, both before any request.
|
|
123
|
+
*/
|
|
124
|
+
export async function fetchBalanceByAddress(venue, net, address, opts = {}) {
|
|
125
|
+
const def = getVenue(venue);
|
|
126
|
+
const pd = def.publicData;
|
|
127
|
+
if (pd.fetchBalanceByAddress === undefined || pd.addressKind === undefined)
|
|
128
|
+
throw publicBalanceUnsupported(def);
|
|
129
|
+
if (opts.accountIndex !== undefined && def.id !== "lighter" && def.id !== "lighter-rh") {
|
|
130
|
+
throw new PerpError("INVALID_PARAMS", `--account-index applies to Lighter only (${def.id} has one account per address)`, { exchange: def.id, remediation: "Drop --account-index" });
|
|
131
|
+
}
|
|
132
|
+
return pd.fetchBalanceByAddress(net, requireAddressOfKind(def.id, pd.addressKind, address), opts);
|
|
133
|
+
}
|
|
59
134
|
export function networkConfig(def, net) {
|
|
60
135
|
const cfg = def.networks[net];
|
|
61
136
|
if (!cfg) {
|
|
@@ -65,7 +140,41 @@ export function networkConfig(def, net) {
|
|
|
65
140
|
}
|
|
66
141
|
return cfg;
|
|
67
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* The uniform refusal for any account / trading / funds / agent / builder / bot action on a venue
|
|
145
|
+
* whose roles do not include `role` (a market-data-only venue such as TxFlow). Raised before any
|
|
146
|
+
* credential is resolved or any request is sent.
|
|
147
|
+
*/
|
|
148
|
+
export function roleUnsupported(def, feature, role = "trading") {
|
|
149
|
+
if (def.gatedRoles !== null && def.gatedRoles.roles.includes(role)) {
|
|
150
|
+
const { reason, remediation } = def.gatedRoles;
|
|
151
|
+
return new PerpError("CAPABILITY_UNSUPPORTED", `${def.id} does not support ${feature} — ${reason}`, {
|
|
152
|
+
venue: def.id, feature, reason, remediation,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const reason = def.apiStatus.kind === "unofficial"
|
|
156
|
+
? `${def.displayName} is integrated through an unofficial read-only API (market data only)`
|
|
157
|
+
: `${def.displayName} is available for market data only (no ${role} role)`;
|
|
158
|
+
// Precedence: gatedRoles (above: a role switched off this release, e.g. RISEx) → the venue's own
|
|
159
|
+
// readOnlyRemediation (official read-only venues, e.g. Variational) → the generic text (TxFlow).
|
|
160
|
+
const remediation = def.readOnlyRemediation !== undefined
|
|
161
|
+
? def.readOnlyRemediation
|
|
162
|
+
: def.apiStatus.kind === "unofficial"
|
|
163
|
+
? `Trade on the ${def.displayName} website; perp-cli shows ${def.displayName} market data only (perp -e ${def.id} market list|info|funding|book|trades|kline)`
|
|
164
|
+
: `Use the market-data commands for ${def.id} (perp -e ${def.id} market list|funding)`;
|
|
165
|
+
return new PerpError("CAPABILITY_UNSUPPORTED", `${def.id} does not support ${feature} — ${reason}`, {
|
|
166
|
+
venue: def.id, feature, reason, remediation,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** Throws roleUnsupported() unless the venue has `role`. */
|
|
170
|
+
export function requireRole(def, role, feature) {
|
|
171
|
+
if (!def.roles.includes(role))
|
|
172
|
+
throw roleUnsupported(def, feature, role);
|
|
173
|
+
}
|
|
68
174
|
export function requireCapabilities(def) {
|
|
175
|
+
// A venue without the trading role is a definite capability refusal, not "not implemented yet".
|
|
176
|
+
if (!def.roles.includes("trading"))
|
|
177
|
+
throw roleUnsupported(def, "trading");
|
|
69
178
|
if (!def.capabilities) {
|
|
70
179
|
throw new PerpError("NOT_IMPLEMENTED", `${def.displayName} does not support trading via API`, {
|
|
71
180
|
remediation: `${def.id} is available for market data only (funding/market commands)`,
|
|
@@ -88,7 +197,7 @@ export function parseVenueRef(ref) {
|
|
|
88
197
|
}
|
|
89
198
|
/**
|
|
90
199
|
* Funding basis for a venue ref. HIP-3 dexes use the Hyperliquid core basis
|
|
91
|
-
* (current behavior preserved;
|
|
200
|
+
* (current behavior preserved; verify against live data).
|
|
92
201
|
* Throws for venues whose basis is per-market; callers must use the market's value.
|
|
93
202
|
*/
|
|
94
203
|
export function fundingBasisOf(ref) {
|
|
@@ -114,7 +223,7 @@ export function settlementIntervalOf(ref) {
|
|
|
114
223
|
/**
|
|
115
224
|
* Refuse a venue whose funding rate is unverified (VenueDefinition.fundingVerified false) as a
|
|
116
225
|
* leg of a funding strategy (arb scan ranking, arb exec / auto, funding bots). Uniform
|
|
117
|
-
* CAPABILITY_UNSUPPORTED
|
|
226
|
+
* CAPABILITY_UNSUPPORTED. Unknown venue refs throw UNKNOWN_VENUE.
|
|
118
227
|
*/
|
|
119
228
|
export function assertFundingArbVenue(ref) {
|
|
120
229
|
const def = getVenue(parseVenueRef(ref).venue);
|
|
@@ -126,6 +235,19 @@ export function assertFundingArbVenue(ref) {
|
|
|
126
235
|
remediation: `Use venues with verified funding (${listVenues().filter(v => v.fundingVerified && v.roles.includes("trading")).map(v => v.id).join(", ")}). ${def.displayName} rates can still be viewed: perp arb scan --rates --include-unverified`,
|
|
127
236
|
});
|
|
128
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Market rows (`market list|prices|info`, MCP get_markets) state whether their venue's funding rate
|
|
240
|
+
* is verified — on every venue, true or false (VenueDefinition.fundingVerified), so a consumer
|
|
241
|
+
* never has to read a missing field as "verified". A venue ref ("hip3:xyz" → hyperliquid) is
|
|
242
|
+
* resolved; an unknown ref leaves the rows as they are.
|
|
243
|
+
*/
|
|
244
|
+
export function withFundingVerified(venue, rows) {
|
|
245
|
+
const id = tryResolveVenueId(venue.startsWith("hip3:") ? "hyperliquid" : venue);
|
|
246
|
+
const def = id === null ? undefined : _venues.get(id);
|
|
247
|
+
if (def === undefined)
|
|
248
|
+
return rows;
|
|
249
|
+
return rows.map(r => ({ ...r, fundingVerified: def.fundingVerified }));
|
|
250
|
+
}
|
|
129
251
|
export function isLegacyVenue(def) {
|
|
130
252
|
return def.id === "pacifica" || def.id === "hyperliquid" || def.id === "lighter" || def.id === "aster";
|
|
131
253
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Arcus — perps on the Robinhood chain rootchain, USDG collateral
|
|
3
|
-
* (design §4 Arcus column, §10.4, §10.10).
|
|
4
3
|
* Not registered here; the registry module owns registerVenue().
|
|
5
|
-
* Adapter and public-data code are loaded lazily
|
|
4
|
+
* Adapter and public-data code are loaded lazily.
|
|
6
5
|
*/
|
|
7
6
|
import type { VenueDefinition } from "../venue.js";
|
|
8
7
|
export declare const arcusVenue: VenueDefinition;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Arcus — perps on the Robinhood chain rootchain, USDG collateral
|
|
3
|
-
* (design §4 Arcus column, §10.4, §10.10).
|
|
4
3
|
* Not registered here; the registry module owns registerVenue().
|
|
5
|
-
* Adapter and public-data code are loaded lazily
|
|
4
|
+
* Adapter and public-data code are loaded lazily.
|
|
6
5
|
*/
|
|
7
6
|
import { BUILDER_FEE_CAPABILITIES } from "../builder-fee.js";
|
|
8
7
|
const publicData = {
|
|
@@ -50,6 +49,7 @@ export const arcusVenue = {
|
|
|
50
49
|
settlementIntervalHours: 1,
|
|
51
50
|
fundingVerified: true,
|
|
52
51
|
roles: ["marketData", "account", "trading"],
|
|
52
|
+
gatedRoles: null,
|
|
53
53
|
capabilities: {
|
|
54
54
|
marketOrder: "slippageBounded",
|
|
55
55
|
// GTC = GTT at goodTil now + 40 days (ARCUS_GOOD_TIL_HORIZON_MS); GTT kept as the venue alias.
|
|
@@ -83,7 +83,7 @@ export const arcusVenue = {
|
|
|
83
83
|
restrictedRegions: ["US", "CA", "GB"],
|
|
84
84
|
symbolIdentity: "baseTicker",
|
|
85
85
|
publicData,
|
|
86
|
-
//
|
|
86
|
+
// market swaps via router.spot.arcus.xyz, settled on-chain from the master EVM wallet.
|
|
87
87
|
spot: {
|
|
88
88
|
accountModel: "wallet",
|
|
89
89
|
quoteAssets: { mainnet: ["USDG"], testnet: ["USDG"] },
|
|
@@ -99,9 +99,10 @@ export const arcusVenue = {
|
|
|
99
99
|
return spot;
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
|
-
// Arcus venue limit
|
|
102
|
+
// Arcus venue slippage limit.
|
|
103
103
|
maxSlippageBps: 1000,
|
|
104
104
|
builderFee: BUILDER_FEE_CAPABILITIES["arcus"],
|
|
105
|
+
apiStatus: { kind: "official" },
|
|
105
106
|
async create(ctx) {
|
|
106
107
|
const { ArcusAdapter } = await import("../arcus/adapter.js");
|
|
107
108
|
return new ArcusAdapter(arcusVenue, ctx);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Aster venue definition
|
|
3
|
-
* Adapter + public-data code are loaded lazily
|
|
2
|
+
* Aster venue definition.
|
|
3
|
+
* Adapter + public-data code are loaded lazily.
|
|
4
4
|
*/
|
|
5
5
|
import type { VenueDefinition } from "../venue.js";
|
|
6
6
|
/** Aster client order id (V3 futures "New Order" `newClientOrderId` rule). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Aster venue definition
|
|
3
|
-
* Adapter + public-data code are loaded lazily
|
|
2
|
+
* Aster venue definition.
|
|
3
|
+
* Adapter + public-data code are loaded lazily.
|
|
4
4
|
*/
|
|
5
5
|
import { BUILDER_FEE_CAPABILITIES } from "../builder-fee.js";
|
|
6
6
|
/** Aster client order id (V3 futures "New Order" `newClientOrderId` rule). */
|
|
@@ -122,6 +122,7 @@ export const asterVenue = {
|
|
|
122
122
|
settlementIntervalHours: "perMarket",
|
|
123
123
|
fundingVerified: true,
|
|
124
124
|
roles: ["marketData", "account", "trading"],
|
|
125
|
+
gatedRoles: null,
|
|
125
126
|
capabilities: {
|
|
126
127
|
// Market = IOC LIMIT at mark ± slippage (adapter marketOrder), never native MARKET.
|
|
127
128
|
marketOrder: "slippageBounded",
|
|
@@ -146,12 +147,12 @@ export const asterVenue = {
|
|
|
146
147
|
},
|
|
147
148
|
// GET /fapi/v1/exchangeInfo rateLimits: REQUEST_WEIGHT 2400/min
|
|
148
149
|
// (ORDERS 1200/min and 300/10s are separate order-count limits, not modeled).
|
|
149
|
-
// Declared only — per
|
|
150
|
+
// Declared only — per the limiter is applied to new venues + lighter-rh.
|
|
150
151
|
rateLimit: { capacity: 2400, refillPerSec: 40, weights: null },
|
|
151
152
|
restrictedRegions: [],
|
|
152
153
|
symbolIdentity: "baseTicker",
|
|
153
154
|
publicData,
|
|
154
|
-
//
|
|
155
|
+
// separate spot account (sapi /api/v3) + /asset/wallet/transfer. Agent signer needs canSpotTrade.
|
|
155
156
|
spot: {
|
|
156
157
|
accountModel: "separate",
|
|
157
158
|
quoteAssets: { mainnet: ["USDT", "USD1"], testnet: ["USDT", "USD1"] },
|
|
@@ -172,5 +173,6 @@ export const asterVenue = {
|
|
|
172
173
|
// (INVALID_PARAMS) at order time; this is only the outer venue-wide bound.
|
|
173
174
|
maxSlippageBps: 5000,
|
|
174
175
|
builderFee: BUILDER_FEE_CAPABILITIES["aster"],
|
|
176
|
+
apiStatus: { kind: "official" },
|
|
175
177
|
create: async (ctx) => (await import("./create-helpers.js")).createAster(ctx),
|
|
176
178
|
};
|