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,59 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lighter-family public market data (`lighter`, `lighter-rh`)
|
|
2
|
+
* Lighter-family public market data (`lighter`, `lighter-rh`).
|
|
3
3
|
*
|
|
4
4
|
* Sources (REST base from venues/lighter-family.ts, per venue + network):
|
|
5
5
|
* GET /api/v1/orderBookDetails → perps in `order_book_details` (decimals, min
|
|
6
6
|
* amounts, status, mark/index price, IMF, OI, volume). It carries every
|
|
7
7
|
* field /orderBooks has, so /orderBooks is not fetched (one fewer call
|
|
8
8
|
* under the 60 req/min standard limit).
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2026-09-30: 57 "lighter" rows = RH's 57 perps, matched by market_id).
|
|
9
|
+
* WS market_stats/all (first frame) → per market `funding_rate` (LAST SETTLED, percent per
|
|
10
|
+
* 1 h; = the newest GET /fundings print) and `current_funding_rate` (predicted). REST
|
|
11
|
+
* /funding-rates is the predicted rate × 8 and is not read here (fetchLighterFundingStats).
|
|
13
12
|
*
|
|
14
|
-
* FUNDING:
|
|
15
|
-
*
|
|
13
|
+
* FUNDING: market / funding rows carry the settled rate per 1 h (hourly settlement; Lighter and
|
|
14
|
+
* Lighter RH identical).
|
|
16
15
|
*
|
|
17
16
|
* ASSET CLASS: the API has no asset-class field (the undocumented
|
|
18
17
|
* `strategy_index` groups markets on Lighter main but is 0 for every RH market),
|
|
19
18
|
* so classification is an explicit per-venue table built from live listings on
|
|
20
19
|
* 2026-09-30:
|
|
21
|
-
* - lighter-rh: every listed perp is in
|
|
22
|
-
*
|
|
23
|
-
* so no class can be assumed).
|
|
20
|
+
* - lighter-rh: every listed perp is in LIGHTER_RH_CLASS (src/ws/unclassified.ts). An unlisted symbol is
|
|
21
|
+
* "unclassified" (listed, canonical null) until it is classified (RH lists
|
|
22
|
+
* mostly equities, so no class can be assumed).
|
|
24
23
|
* - lighter: crypto-centric venue. LIGHTER_NON_CRYPTO lists every non-crypto
|
|
25
24
|
* perp; any other symbol is crypto by this venue's definition.
|
|
26
|
-
*
|
|
27
|
-
* produce a false cross-venue match.
|
|
25
|
+
* UNCLASSIFIED_TICKERS.lighter (src/ws/unclassified.ts) are "unclassified" (listed, canonical null) so
|
|
26
|
+
* they cannot produce a false cross-venue match.
|
|
28
27
|
*
|
|
29
28
|
* OPEN INTEREST: `open_interest` in base units (same value legacy getMarkets reports).
|
|
30
29
|
*/
|
|
31
30
|
import { PerpError } from "../errors.js";
|
|
32
|
-
import { venueRateField } from "../utils/numeric.js";
|
|
33
31
|
import { acquireRate, rateLimitedError } from "./rate-limiter.js";
|
|
34
32
|
import { canonicalFor } from "./canonical-symbols.js";
|
|
33
|
+
import { isLighterMarketLive } from "../ws/lighter-market.js";
|
|
34
|
+
import { isUnclassifiedTicker, LIGHTER_RH_CLASS } from "../ws/unclassified.js";
|
|
35
|
+
import { addDecimal, mulDecimal } from "./precision.js";
|
|
36
|
+
import { nextTopOfHourMs } from "./venue-types.js";
|
|
35
37
|
import { lighterFamilyNetworks, lighterFamilyRateLimit } from "./venues/lighter-family.js";
|
|
36
|
-
|
|
38
|
+
/** Market rows / funding rows carry the settled HOURLY rate (market_stats funding_rate): per 1 h. */
|
|
39
|
+
const BASIS_1H = { kind: "perHours", hours: 1 };
|
|
37
40
|
const SETTLEMENT_INTERVAL_HOURS = 1;
|
|
38
41
|
// ── Classification tables (live listings 2026-09-30) ──
|
|
39
|
-
|
|
40
|
-
// crypto
|
|
41
|
-
BTC: "crypto", ETH: "crypto", SOL: "crypto", HYPE: "crypto", ZEC: "crypto", LIT: "crypto",
|
|
42
|
-
VVV: "crypto", SUI: "crypto", NEAR: "crypto", XRP: "crypto", CASHCAT: "crypto", PONS: "crypto",
|
|
43
|
-
// commodity (canonical via EXPLICIT_CANONICAL: XAU → GOLD, XAG → SILVER)
|
|
44
|
-
XAU: "commodity", XAG: "commodity",
|
|
45
|
-
// etf
|
|
46
|
-
SPY: "etf", QQQ: "etf", SOXL: "etf", SLV: "etf", USO: "etf", SGOV: "etf",
|
|
47
|
-
// equity
|
|
48
|
-
AAPL: "equity", AMZN: "equity", GOOGL: "equity", META: "equity", MSFT: "equity", NVDA: "equity",
|
|
49
|
-
TSLA: "equity", AMD: "equity", INTC: "equity", MU: "equity", TSM: "equity", ORCL: "equity",
|
|
50
|
-
PLTR: "equity", COIN: "equity", CRCL: "equity", CRWV: "equity", BABA: "equity", SNDK: "equity",
|
|
51
|
-
SMCI: "equity", SOFI: "equity", AMC: "equity", RGTI: "equity", QBTS: "equity", CLSK: "equity",
|
|
52
|
-
IREN: "equity", WULF: "equity", LUNR: "equity", ASTS: "equity", BE: "equity", USAR: "equity",
|
|
53
|
-
SPCX: "equity", SKHY: "equity",
|
|
54
|
-
// pre-IPO / private (explicit-only canonical → null unless mapped)
|
|
55
|
-
OPENAI: "preIpo", ANTHROPIC: "preIpo", SHEIN: "preIpo", ANSEM: "preIpo", AI: "preIpo",
|
|
56
|
-
};
|
|
42
|
+
// RH_CLASS (Lighter RH listing classification) lives in src/ws/unclassified.ts, shared with the WS directory.
|
|
57
43
|
const LIGHTER_NON_CRYPTO = {
|
|
58
44
|
// commodity
|
|
59
45
|
XAU: "commodity", XAG: "commodity", XPD: "commodity", XPT: "commodity", XCU: "commodity",
|
|
@@ -85,21 +71,19 @@ const LIGHTER_NON_CRYPTO = {
|
|
|
85
71
|
ZHIPU: "preIpo", UNITREE: "preIpo", MINIMAX: "preIpo", AI: "preIpo", ANSEM: "preIpo",
|
|
86
72
|
STABLECOINX: "preIpo", OURA: "preIpo", PRL: "preIpo", ADI: "preIpo",
|
|
87
73
|
};
|
|
74
|
+
// Ambiguous Lighter tickers (BB, BOT, WEN, QNT, SPX): UNCLASSIFIED_TICKERS in src/ws/unclassified.ts.
|
|
88
75
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* - SPX: the SPX6900 token on Lighter; its ticker equals the canonical key of the S&P 500 index ("SPX").
|
|
76
|
+
* Asset class for a venue symbol; "unclassified" (canonical null — listed, never matched across
|
|
77
|
+
* venues) when it is ambiguous or not mapped.
|
|
92
78
|
*/
|
|
93
|
-
const LIGHTER_AMBIGUOUS = new Set(["BB", "BOT", "WEN", "QNT", "SPX"]);
|
|
94
|
-
/** Asset class for a venue symbol, or null when it must be excluded (unclassified / ambiguous). */
|
|
95
79
|
function classify(venue, symbol) {
|
|
96
80
|
const up = symbol.toUpperCase();
|
|
97
81
|
switch (venue) {
|
|
98
82
|
case "lighter-rh":
|
|
99
|
-
return
|
|
83
|
+
return LIGHTER_RH_CLASS[up] ?? "unclassified";
|
|
100
84
|
case "lighter":
|
|
101
|
-
if (
|
|
102
|
-
return
|
|
85
|
+
if (isUnclassifiedTicker("lighter", up))
|
|
86
|
+
return "unclassified";
|
|
103
87
|
return LIGHTER_NON_CRYPTO[up] ?? "crypto"; // crypto-centric venue: non-crypto is listed explicitly
|
|
104
88
|
}
|
|
105
89
|
}
|
|
@@ -130,7 +114,56 @@ export function lighterWafChallenge(venue, status, body) {
|
|
|
130
114
|
remediation: `Wait 1–2 minutes before the next ${venue} request, then retry at a lower request rate (standard accounts: 60 requests per rolling minute)`,
|
|
131
115
|
});
|
|
132
116
|
}
|
|
133
|
-
|
|
117
|
+
/** `limit` of GET /api/v1/orderBookOrders: the documented maximum (apidocs.lighter.xyz/reference/orderbookorders, 1..250). */
|
|
118
|
+
export const LIGHTER_BOOK_ORDERS_LIMIT = 250;
|
|
119
|
+
/**
|
|
120
|
+
* One side of GET /api/v1/orderBookOrders (individual resting ORDERS, best price first) → price
|
|
121
|
+
* levels: `remaining_base_amount` summed per price, in the venue's order — the shape of the WS
|
|
122
|
+
* `order_book/{id}` channel (levels). A side of exactly `limit` orders may have more orders at its
|
|
123
|
+
* last price beyond the page, so that (possibly partial) level is dropped. A row without a price /
|
|
124
|
+
* remaining amount is a payload error (never skipped, never "0").
|
|
125
|
+
*/
|
|
126
|
+
export function lighterBookLevels(venue, rows, what, limit = LIGHTER_BOOK_ORDERS_LIMIT) {
|
|
127
|
+
if (rows === undefined || rows === null)
|
|
128
|
+
return [];
|
|
129
|
+
if (!Array.isArray(rows))
|
|
130
|
+
throw new PerpError("EXCHANGE_ERROR", `${venue} orderBookOrders ${what}: side is not a list`, { exchange: venue });
|
|
131
|
+
const levels = new Map();
|
|
132
|
+
for (const r of rows) {
|
|
133
|
+
const price = r?.price;
|
|
134
|
+
const size = r?.remaining_base_amount ?? r?.size;
|
|
135
|
+
if (typeof price !== "string" || typeof size !== "string" || !Number.isFinite(Number(price)) || !Number.isFinite(Number(size))) {
|
|
136
|
+
throw new PerpError("EXCHANGE_ERROR", `${venue} orderBookOrders ${what}: order without price / remaining size: ${JSON.stringify(r).slice(0, 160)}`, { exchange: venue });
|
|
137
|
+
}
|
|
138
|
+
const prev = levels.get(price);
|
|
139
|
+
levels.set(price, prev === undefined ? size : addDecimal(prev, size));
|
|
140
|
+
}
|
|
141
|
+
const out = Array.from(levels);
|
|
142
|
+
if (rows.length >= limit && out.length > 1)
|
|
143
|
+
out.pop();
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A GET /orderBookOrders answer → price levels per side, cut to `depth`. A side whose page is full
|
|
148
|
+
* (LIGHTER_BOOK_ORDERS_LIMIT orders, the endpoint max) but aggregates to fewer than `depth` levels
|
|
149
|
+
* cannot be paged: INVALID_PARAMS naming the 250-order cap, never a silently shallower book.
|
|
150
|
+
*/
|
|
151
|
+
export function lighterBook(venue, res, what, depth) {
|
|
152
|
+
const bids = lighterBookLevels(venue, res.bids, `${what} bids`);
|
|
153
|
+
const asks = lighterBookLevels(venue, res.asks, `${what} asks`);
|
|
154
|
+
if (depth === undefined)
|
|
155
|
+
return { bids, asks };
|
|
156
|
+
const full = (rows) => Array.isArray(rows) && rows.length >= LIGHTER_BOOK_ORDERS_LIMIT;
|
|
157
|
+
for (const [side, rows, levels] of [["bids", res.bids, bids], ["asks", res.asks, asks]]) {
|
|
158
|
+
if (full(rows) && levels.length < depth) {
|
|
159
|
+
throw new PerpError("INVALID_PARAMS", `${venue} order book: ${what} ${side} — ${LIGHTER_BOOK_ORDERS_LIMIT} orders (the endpoint max, no paging) make only ${levels.length} price levels; --depth ${depth} cannot be served`, {
|
|
160
|
+
exchange: venue, remediation: `Use --depth ${levels.length} or less`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { bids: bids.slice(0, depth), asks: asks.slice(0, depth) };
|
|
165
|
+
}
|
|
166
|
+
async function getJson(venue, net, path, endpoint, notFound) {
|
|
134
167
|
await acquireRate(`${venue}:${net}`, lighterFamilyRateLimit(venue), endpoint);
|
|
135
168
|
const base = lighterFamilyNetworks(venue)[net].restUrl;
|
|
136
169
|
const res = await fetch(`${base}/api/v1${path}`, { signal: AbortSignal.timeout(15_000) });
|
|
@@ -140,6 +173,8 @@ async function getJson(venue, net, path, endpoint) {
|
|
|
140
173
|
const waf = lighterWafChallenge(venue, res.status, text);
|
|
141
174
|
if (waf !== null)
|
|
142
175
|
throw waf;
|
|
176
|
+
if (notFound !== undefined && notFound(res.status, text))
|
|
177
|
+
return null;
|
|
143
178
|
if (!res.ok) {
|
|
144
179
|
throw new PerpError("EXCHANGE_ERROR", `${venue} GET ${path} HTTP ${res.status}`, { body: text.slice(0, 500) });
|
|
145
180
|
}
|
|
@@ -181,23 +216,150 @@ async function fetchPerpDetails(venue, net) {
|
|
|
181
216
|
warnExcluded(venue, "malformed market(s)", malformed);
|
|
182
217
|
return out;
|
|
183
218
|
}
|
|
184
|
-
/**
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Every account of an L1 (EVM) address — `GET /account?by=l1_address&value=` (public). One
|
|
221
|
+
* address can own several accounts (sub-accounts). An address without a Lighter account answers
|
|
222
|
+
* HTTP 400 `{"code":21100,"message":"account not found"}` (live 2026-10-19) → null, not an error.
|
|
223
|
+
*/
|
|
224
|
+
export async function fetchLighterAccountsByL1(venue, net, address) {
|
|
225
|
+
const json = await getJson(venue, net, `/account?by=l1_address&value=${encodeURIComponent(address)}`, "account", (status, text) => status === 400 && /"code"\s*:\s*21100\b/.test(text));
|
|
226
|
+
if (json === null)
|
|
227
|
+
return null;
|
|
228
|
+
const list = json.accounts;
|
|
229
|
+
if (!Array.isArray(list))
|
|
230
|
+
throw new PerpError("EXCHANGE_ERROR", `${venue} account: missing accounts array`, { exchange: venue });
|
|
231
|
+
return list;
|
|
232
|
+
}
|
|
233
|
+
/** Perp price decimals by symbol (upper case) — `GET /orderBooks` `supported_price_decimals`, as the adapter's market map. */
|
|
234
|
+
export async function fetchLighterPriceDecimals(venue, net) {
|
|
235
|
+
const json = await getJson(venue, net, "/orderBooks", "orderBooks");
|
|
236
|
+
const list = json.order_books;
|
|
237
|
+
if (!Array.isArray(list))
|
|
238
|
+
throw new PerpError("EXCHANGE_ERROR", `${venue} orderBooks: missing order_books array`, { exchange: venue });
|
|
239
|
+
const out = new Map();
|
|
240
|
+
for (const d of list) {
|
|
241
|
+
if (d.market_type === "perp" && typeof d.symbol === "string" && Number.isInteger(d.supported_price_decimals))
|
|
242
|
+
out.set(d.symbol.toUpperCase(), d.supported_price_decimals);
|
|
190
243
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
/** market_stats rates are PERCENT per 1 h (codec notes): exact × 0.01. undefined = malformed. */
|
|
247
|
+
function percentToFraction(v) {
|
|
248
|
+
if (v === undefined || v === null)
|
|
249
|
+
return null;
|
|
250
|
+
const s = typeof v === "string" ? v.trim() : typeof v === "number" && Number.isFinite(v) ? String(v) : null;
|
|
251
|
+
if (s === null || !/^-?\d+(\.\d+)?$/.test(s))
|
|
252
|
+
return undefined;
|
|
253
|
+
return mulDecimal(s, "0.01"); // exact (no float)
|
|
254
|
+
}
|
|
255
|
+
const STATS_TTL_MS = 10_000;
|
|
256
|
+
/** A failed read is remembered this long (one stderr line / meta warning, not a socket per call). */
|
|
257
|
+
const STATS_FAILURE_TTL_MS = 60_000;
|
|
258
|
+
const STATS_TIMEOUT_MS = 4_000;
|
|
259
|
+
const statsCache = new Map();
|
|
260
|
+
/** Drop the shared market_stats snapshots and remembered failures (tests; a network switch). */
|
|
261
|
+
export function clearLighterFundingStatsCache() {
|
|
262
|
+
statsCache.clear();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Why the settled rate of `venue` / `net` is unavailable right now (the remembered failure of the
|
|
266
|
+
* last snapshot read), null when it was read or not tried. Callers put it in `meta.warnings`
|
|
267
|
+
* (CLI --json, MCP) next to the rows whose fundingRate is null.
|
|
268
|
+
*/
|
|
269
|
+
export function lighterFundingStatsWarning(venue, net) {
|
|
270
|
+
const hit = statsCache.get(`${venue}:${net}`);
|
|
271
|
+
return hit !== undefined && hit.failure !== null && Date.now() - hit.at < STATS_FAILURE_TTL_MS
|
|
272
|
+
? `${venue}: fundingRate null — settled funding rate unavailable (${hit.failure})` : null;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Settled + predicted hourly funding of every market, keyed by market_id: the SNAPSHOT frame
|
|
276
|
+
* (`subscribed/market_stats`, channel `market_stats:all` — later `update/…` frames carry only the
|
|
277
|
+
* markets that changed) of the WS `market_stats/all` channel, the source of the perp-ws `rate` /
|
|
278
|
+
* `predictedRate`. REST has no bulk settled source: /funding-rates is the PREDICTED rate × 8 (live
|
|
279
|
+
* 2026-10-19: ETH 0.0009 % /h predicted → 0.000072; settled 0.0012 % /h), and /fundings is one
|
|
280
|
+
* market per request. One snapshot per venue / network is shared STATS_TTL_MS; a failure is
|
|
281
|
+
* remembered STATS_FAILURE_TTL_MS (lighterFundingStatsWarning).
|
|
282
|
+
*/
|
|
283
|
+
export function fetchLighterFundingStats(venue, net) {
|
|
284
|
+
const key = `${venue}:${net}`;
|
|
285
|
+
const hit = statsCache.get(key);
|
|
286
|
+
if (hit !== undefined && Date.now() - hit.at < (hit.failure === null ? STATS_TTL_MS : STATS_FAILURE_TTL_MS))
|
|
287
|
+
return hit.value;
|
|
288
|
+
const value = readFundingStats(venue, net);
|
|
289
|
+
const entry = { at: Date.now(), value, failure: null };
|
|
290
|
+
statsCache.set(key, entry);
|
|
291
|
+
value.catch((e) => {
|
|
292
|
+
const code = e instanceof PerpError ? `${e.structured.code}: ` : "";
|
|
293
|
+
entry.failure = `${code}${e instanceof Error ? e.message : String(e)}`;
|
|
294
|
+
entry.at = Date.now();
|
|
295
|
+
});
|
|
296
|
+
return value;
|
|
297
|
+
}
|
|
298
|
+
async function readFundingStats(venue, net) {
|
|
299
|
+
const url = lighterFamilyNetworks(venue)[net].wsUrl;
|
|
300
|
+
if (url === null) {
|
|
301
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", `${venue} ${net} publishes no WebSocket endpoint, so its settled funding rate (market_stats) cannot be read`, { exchange: venue });
|
|
199
302
|
}
|
|
200
|
-
|
|
303
|
+
const { default: WebSocket } = await import("ws");
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
const ws = new WebSocket(url);
|
|
306
|
+
let settled = false;
|
|
307
|
+
const done = (err, value) => {
|
|
308
|
+
if (settled)
|
|
309
|
+
return;
|
|
310
|
+
settled = true;
|
|
311
|
+
clearTimeout(timer);
|
|
312
|
+
try {
|
|
313
|
+
ws.close();
|
|
314
|
+
}
|
|
315
|
+
catch { /* already closed */ }
|
|
316
|
+
if (err !== null)
|
|
317
|
+
reject(err);
|
|
318
|
+
else
|
|
319
|
+
resolve(value);
|
|
320
|
+
};
|
|
321
|
+
const timer = setTimeout(() => done(new PerpError("EXCHANGE_UNREACHABLE", `${venue} market_stats: no snapshot within ${STATS_TIMEOUT_MS / 1000} s`, { exchange: venue })), STATS_TIMEOUT_MS);
|
|
322
|
+
ws.on("open", () => ws.send(JSON.stringify({ type: "subscribe", channel: "market_stats/all" })));
|
|
323
|
+
ws.on("error", (e) => done(new PerpError("EXCHANGE_UNREACHABLE", `${venue} market_stats: ${e.message}`, { exchange: venue })));
|
|
324
|
+
ws.on("close", (code) => done(new PerpError("EXCHANGE_UNREACHABLE", `${venue} market_stats: socket closed (code ${code}) before the snapshot`, { exchange: venue })));
|
|
325
|
+
ws.on("message", (data) => {
|
|
326
|
+
let f;
|
|
327
|
+
try {
|
|
328
|
+
f = JSON.parse(String(data));
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (f.error !== undefined) {
|
|
334
|
+
done(new PerpError("EXCHANGE_ERROR", `${venue} market_stats: ${JSON.stringify(f.error).slice(0, 200)}`, { exchange: venue }));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
// Only the snapshot frame has every market ("connected" and update frames are skipped).
|
|
338
|
+
if (f.type !== "subscribed/market_stats" || f.channel !== "market_stats:all")
|
|
339
|
+
return;
|
|
340
|
+
const stats = f.market_stats;
|
|
341
|
+
if (stats === null || typeof stats !== "object") {
|
|
342
|
+
done(new PerpError("EXCHANGE_ERROR", `${venue} market_stats snapshot without market_stats`, { exchange: venue }));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const out = new Map();
|
|
346
|
+
const malformed = [];
|
|
347
|
+
for (const [id, r] of Object.entries(stats)) {
|
|
348
|
+
if (r === null || typeof r !== "object") {
|
|
349
|
+
malformed.push(`#${id}`);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const rate = percentToFraction(r.funding_rate);
|
|
353
|
+
const predictedRate = percentToFraction(r.current_funding_rate);
|
|
354
|
+
if (rate === undefined || predictedRate === undefined)
|
|
355
|
+
malformed.push(typeof r.symbol === "string" ? r.symbol : `#${id}`);
|
|
356
|
+
out.set(Number(id), { rate: rate ?? null, predictedRate: predictedRate ?? null });
|
|
357
|
+
}
|
|
358
|
+
if (malformed.length > 0)
|
|
359
|
+
console.warn(`[${venue}] market_stats: ${malformed.length} market(s) with a malformed funding value (rate null): ${malformed.slice(0, 10).join(", ")}`);
|
|
360
|
+
done(null, out);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
201
363
|
}
|
|
202
364
|
function stepFromDecimals(d) {
|
|
203
365
|
return d === 0 ? "1" : `0.${"0".repeat(d - 1)}1`;
|
|
@@ -216,8 +378,9 @@ function toMarket(venue, net, d, assetClass, fr) {
|
|
|
216
378
|
quoteAsset: null,
|
|
217
379
|
markPrice: d.mark_price,
|
|
218
380
|
indexPrice: isNumStr(d.index_price) ? d.index_price : null,
|
|
219
|
-
|
|
220
|
-
|
|
381
|
+
// Last settled hourly rate (market_stats funding_rate), per 1 h — the perp-ws `rate`.
|
|
382
|
+
fundingRate: fr?.rate ?? null,
|
|
383
|
+
fundingRateBasis: BASIS_1H,
|
|
221
384
|
settlementIntervalHours: SETTLEMENT_INTERVAL_HOURS,
|
|
222
385
|
openInterest: typeof d.open_interest === "number" ? String(d.open_interest) : null,
|
|
223
386
|
volume24h: typeof d.daily_quote_token_volume === "number" ? String(d.daily_quote_token_volume) : null,
|
|
@@ -228,48 +391,53 @@ function toMarket(venue, net, d, assetClass, fr) {
|
|
|
228
391
|
maxLeverage: Math.floor(10000 / d.min_initial_margin_fraction),
|
|
229
392
|
marginTiers: null,
|
|
230
393
|
session: null, // RH: 24/7, no session info published
|
|
231
|
-
tradable: d
|
|
394
|
+
tradable: isLighterMarketLive(d),
|
|
232
395
|
};
|
|
233
396
|
}
|
|
234
397
|
export function lighterPublicData(venue) {
|
|
235
398
|
return {
|
|
236
|
-
async fetchMarkets(net) {
|
|
237
|
-
|
|
399
|
+
async fetchMarkets(net, opts) {
|
|
400
|
+
// The settled rate is the WS snapshot: unreadable → the rows are listed with fundingRate null
|
|
401
|
+
// (unknown, never 0), and the reason is printed.
|
|
402
|
+
const [details, funding] = await Promise.all([
|
|
403
|
+
fetchPerpDetails(venue, net),
|
|
404
|
+
opts?.funding === false ? Promise.resolve(new Map()) : fetchLighterFundingStats(venue, net).catch((e) => {
|
|
405
|
+
console.warn(`[${venue}] settled funding rate unavailable (fundingRate null): ${e instanceof Error ? e.message : String(e)}`);
|
|
406
|
+
return new Map();
|
|
407
|
+
}),
|
|
408
|
+
]);
|
|
238
409
|
const out = [];
|
|
239
|
-
const unclassified = [];
|
|
240
410
|
for (const d of details) {
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
unclassified.push(d.symbol);
|
|
411
|
+
// Live markets only (the rule of market list / the WS directory); inactive ones are not listed.
|
|
412
|
+
if (!isLighterMarketLive(d))
|
|
244
413
|
continue;
|
|
245
|
-
|
|
414
|
+
const cls = classify(venue, d.symbol);
|
|
246
415
|
out.push(toMarket(venue, net, d, cls, funding.get(d.market_id)));
|
|
247
416
|
}
|
|
248
|
-
warnExcluded(venue, "unclassified/ambiguous market(s)", unclassified);
|
|
249
417
|
return out;
|
|
250
418
|
},
|
|
251
419
|
async fetchFundingRates(net) {
|
|
252
|
-
|
|
420
|
+
// Settled hourly rates of the live markets (the market rows' fundingRate). A failed snapshot
|
|
421
|
+
// propagates: the venue is reported as failed, never a list without Lighter rates.
|
|
422
|
+
const [details, funding] = await Promise.all([fetchPerpDetails(venue, net), fetchLighterFundingStats(venue, net)]);
|
|
253
423
|
const out = [];
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
const cls = classify(venue, r.symbol);
|
|
257
|
-
if (cls === null) {
|
|
258
|
-
unclassified.push(r.symbol);
|
|
424
|
+
for (const d of details) {
|
|
425
|
+
if (!isLighterMarketLive(d))
|
|
259
426
|
continue;
|
|
260
|
-
|
|
427
|
+
const rate = funding.get(d.market_id)?.rate ?? null;
|
|
428
|
+
if (rate === null)
|
|
429
|
+
continue; // no settled print yet: unknown, not 0
|
|
261
430
|
out.push({
|
|
262
431
|
venue,
|
|
263
|
-
venueSymbol: String(
|
|
264
|
-
symbol:
|
|
265
|
-
canonical: canonicalFor(venue,
|
|
266
|
-
rate
|
|
267
|
-
basis:
|
|
268
|
-
markPrice:
|
|
269
|
-
nextFundingTime:
|
|
432
|
+
venueSymbol: String(d.market_id),
|
|
433
|
+
symbol: d.symbol,
|
|
434
|
+
canonical: canonicalFor(venue, d.symbol, classify(venue, d.symbol)),
|
|
435
|
+
rate,
|
|
436
|
+
basis: BASIS_1H,
|
|
437
|
+
markPrice: d.mark_price,
|
|
438
|
+
nextFundingTime: nextTopOfHourMs(), // not published; docs: "Funding payments occur at each hour mark"
|
|
270
439
|
});
|
|
271
440
|
}
|
|
272
|
-
warnExcluded(venue, "unclassified/ambiguous market(s)", unclassified);
|
|
273
441
|
return out;
|
|
274
442
|
},
|
|
275
443
|
};
|
|
@@ -22,7 +22,7 @@ export declare const LIGHTER_ALL_MARKETS = 255;
|
|
|
22
22
|
/**
|
|
23
23
|
* One Go runtime of the official signer. The Go `main` registers its functions on
|
|
24
24
|
* globalThis and then blocks; they are captured synchronously right after `go.run()`
|
|
25
|
-
* and the globals restored, so several runtimes (one per venue+network
|
|
25
|
+
* and the globals restored, so several runtimes (one per venue+network)
|
|
26
26
|
* never see each other's functions.
|
|
27
27
|
*/
|
|
28
28
|
export declare class LighterWasmSigner {
|
|
@@ -55,7 +55,7 @@ function nonceArg(v) {
|
|
|
55
55
|
/**
|
|
56
56
|
* One Go runtime of the official signer. The Go `main` registers its functions on
|
|
57
57
|
* globalThis and then blocks; they are captured synchronously right after `go.run()`
|
|
58
|
-
* and the globals restored, so several runtimes (one per venue+network
|
|
58
|
+
* and the globals restored, so several runtimes (one per venue+network)
|
|
59
59
|
* never see each other's functions.
|
|
60
60
|
*/
|
|
61
61
|
export class LighterWasmSigner {
|
|
@@ -14,6 +14,12 @@ import type { LighterAdapter } from "./lighter.js";
|
|
|
14
14
|
* (`filled_base_amount`, the venue's own field — never size − remaining).
|
|
15
15
|
*/
|
|
16
16
|
export declare const ACCOUNT_ORDERS_FILL_SOURCE = "account-orders";
|
|
17
|
+
/**
|
|
18
|
+
* Spot balances of one Lighter-family account object (GET /account `assets`) — the one mapper
|
|
19
|
+
* behind the keyed spot adapter and the keyless `account balance --address`. `quote` is the
|
|
20
|
+
* venue collateral ("USDC" on Lighter, "USDG" on Lighter RH).
|
|
21
|
+
*/
|
|
22
|
+
export declare function lighterSpotBalanceRows(acct: Record<string, unknown>, venue: string, quote: string): SpotBalance[];
|
|
17
23
|
export declare class LighterSpotAdapter implements SpotAdapter {
|
|
18
24
|
/** Venue id of the wrapped LighterAdapter ("lighter" | "lighter-rh"). */
|
|
19
25
|
readonly name: string;
|
|
@@ -34,7 +40,7 @@ export declare class LighterSpotAdapter implements SpotAdapter {
|
|
|
34
40
|
private _getDecimals;
|
|
35
41
|
private resolveSymbol;
|
|
36
42
|
getSpotMarkets(): Promise<SpotMarketInfo[]>;
|
|
37
|
-
getSpotOrderbook(symbol: string): Promise<{
|
|
43
|
+
getSpotOrderbook(symbol: string, depth?: number): Promise<{
|
|
38
44
|
bids: [string, string][];
|
|
39
45
|
asks: [string, string][];
|
|
40
46
|
}>;
|
|
@@ -74,7 +80,7 @@ export declare class LighterSpotAdapter implements SpotAdapter {
|
|
|
74
80
|
tif?: string;
|
|
75
81
|
}): Promise<unknown>;
|
|
76
82
|
/**
|
|
77
|
-
* TIF mapping, as perps
|
|
83
|
+
* TIF mapping, as perps: GTC/GTT → GTT, IOC → IOC, ALO / POST_ONLY → post-only (2).
|
|
78
84
|
* FOK and anything else → CAPABILITY_UNSUPPORTED (never silently GTT).
|
|
79
85
|
*/
|
|
80
86
|
private _limitTif;
|
|
@@ -107,7 +113,7 @@ export declare class LighterSpotAdapter implements SpotAdapter {
|
|
|
107
113
|
transferUsdcToSpot(amount: number): Promise<unknown>;
|
|
108
114
|
/** Transfer USDC from spot account back to perp account */
|
|
109
115
|
transferUsdcToPerp(amount: number): Promise<unknown>;
|
|
110
|
-
/** SpotTransferCapable
|
|
116
|
+
/** SpotTransferCapable: only the venue collateral (USDC on Lighter, USDG on Lighter RH). */
|
|
111
117
|
transferToSpot(amount: string, asset: string): Promise<unknown>;
|
|
112
118
|
transferToPerp(amount: string, asset: string): Promise<unknown>;
|
|
113
119
|
private _transferAmount;
|
|
@@ -139,8 +145,7 @@ export declare class LighterSpotAdapter implements SpotAdapter {
|
|
|
139
145
|
private _nextClientOrderIndex;
|
|
140
146
|
private _getNextNonce;
|
|
141
147
|
/**
|
|
142
|
-
* POST /sendTx. `kind` "place" (order placement): failures are typed
|
|
143
|
-
* §12.3): before the request → ORDER_REJECTED; transport error, HTTP 5xx or an
|
|
148
|
+
* POST /sendTx. `kind` "place" (order placement): failures are typed: before the request → ORDER_REJECTED; transport error, HTTP 5xx or an
|
|
144
149
|
* unreadable body after it → ORDER_PENDING (the tx may have been accepted — never
|
|
145
150
|
* resubmit); an explicit non-200 code → ORDER_REJECTED. "order" (cancels) / "action"
|
|
146
151
|
* (transfers): an explicit non-200 code → ORDER_REJECTED / ACTION_REJECTED
|