perp-cli 0.14.2 → 0.15.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.
Files changed (313) hide show
  1. package/README.md +152 -13
  2. package/dist/agent-wallet/store.d.ts +3 -2
  3. package/dist/agent-wallet/store.js +2 -2
  4. package/dist/api/public/hyperliquid.js +4 -0
  5. package/dist/api/public/pacifica.js +83 -13
  6. package/dist/api/public/urls.d.ts +2 -0
  7. package/dist/api/public/urls.js +3 -0
  8. package/dist/arb/utils.d.ts +1 -2
  9. package/dist/arb/utils.js +1 -2
  10. package/dist/bot/apex/pulse.js +3 -0
  11. package/dist/bot/apex/radar.js +2 -1
  12. package/dist/bot/engine.d.ts +10 -1
  13. package/dist/bot/engine.js +14 -6
  14. package/dist/bot/indicators.d.ts +7 -0
  15. package/dist/bot/indicators.js +16 -0
  16. package/dist/bot/perp-order.d.ts +4 -4
  17. package/dist/bot/perp-order.js +5 -5
  18. package/dist/bot/quarantine.d.ts +1 -1
  19. package/dist/bot/quarantine.js +1 -1
  20. package/dist/bot/strategies/apex-strategy.js +3 -2
  21. package/dist/bot/strategies/engine-mm.js +6 -5
  22. package/dist/bot/strategies/funding-arb-strategy.js +9 -9
  23. package/dist/bot/strategies/funding-arb-utils.d.ts +15 -2
  24. package/dist/bot/strategies/funding-arb-utils.js +43 -24
  25. package/dist/bot/strategies/funding-auto.js +2 -2
  26. package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
  27. package/dist/bot/strategies/liquidation-mm.js +8 -2
  28. package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
  29. package/dist/bot/strategies/momentum-breakout.js +11 -3
  30. package/dist/bot/strategies/spot-perp-arb-strategy.js +23 -12
  31. package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
  32. package/dist/bot/strategies/spot-perp-executor.js +1 -1
  33. package/dist/bot/strategy-types.d.ts +2 -1
  34. package/dist/bot/tui/BotDashboard.d.ts +1 -1
  35. package/dist/bot/tui/BotDashboard.js +1 -1
  36. package/dist/cache.js +12 -6
  37. package/dist/cli-program.d.ts +15 -0
  38. package/dist/cli-program.js +125 -5
  39. package/dist/commands/agent-import.d.ts +7 -0
  40. package/dist/commands/agent-import.js +30 -1
  41. package/dist/commands/agent.d.ts +6 -3
  42. package/dist/commands/agent.js +160 -12
  43. package/dist/commands/arb/index.js +4 -4
  44. package/dist/commands/arb-auto.js +21 -17
  45. package/dist/commands/arb.d.ts +12 -0
  46. package/dist/commands/arb.js +61 -143
  47. package/dist/commands/bot.js +1 -1
  48. package/dist/commands/builder.js +1 -1
  49. package/dist/commands/funds.js +113 -3
  50. package/dist/commands/init.d.ts +1 -1
  51. package/dist/commands/init.js +27 -7
  52. package/dist/commands/manage.js +23 -21
  53. package/dist/commands/market.js +168 -36
  54. package/dist/commands/options.d.ts +9 -0
  55. package/dist/commands/options.js +134 -0
  56. package/dist/commands/pacifica-earn.d.ts +5 -0
  57. package/dist/commands/pacifica-earn.js +468 -0
  58. package/dist/commands/pacifica-orders.d.ts +14 -0
  59. package/dist/commands/pacifica-orders.js +142 -0
  60. package/dist/commands/portfolio.js +32 -47
  61. package/dist/commands/rebalance.d.ts +1 -0
  62. package/dist/commands/rebalance.js +15 -0
  63. package/dist/commands/risk.d.ts +11 -2
  64. package/dist/commands/risk.js +39 -44
  65. package/dist/commands/trade.d.ts +1 -0
  66. package/dist/commands/trade.js +63 -58
  67. package/dist/commands/wallet.js +1 -1
  68. package/dist/config.d.ts +2 -2
  69. package/dist/config.js +15 -4
  70. package/dist/constants.js +6 -0
  71. package/dist/dashboard/server.js +1 -1
  72. package/dist/dashboard/ui.js +1 -1
  73. package/dist/errors.d.ts +24 -1
  74. package/dist/errors.js +25 -3
  75. package/dist/exchanges/arcus/adapter.d.ts +7 -7
  76. package/dist/exchanges/arcus/adapter.js +12 -12
  77. package/dist/exchanges/arcus/agent.d.ts +1 -1
  78. package/dist/exchanges/arcus/agent.js +1 -1
  79. package/dist/exchanges/arcus/client.js +1 -1
  80. package/dist/exchanges/arcus/signing.d.ts +2 -3
  81. package/dist/exchanges/arcus/signing.js +1 -1
  82. package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
  83. package/dist/exchanges/arcus/spot-chain.js +1 -1
  84. package/dist/exchanges/arcus/spot-router.d.ts +1 -1
  85. package/dist/exchanges/arcus/spot-router.js +2 -2
  86. package/dist/exchanges/arcus/spot.d.ts +2 -3
  87. package/dist/exchanges/arcus/spot.js +2 -3
  88. package/dist/exchanges/aster-spot.d.ts +3 -3
  89. package/dist/exchanges/aster-spot.js +6 -9
  90. package/dist/exchanges/aster-typed-data.d.ts +1 -1
  91. package/dist/exchanges/aster-typed-data.js +1 -1
  92. package/dist/exchanges/aster.d.ts +9 -4
  93. package/dist/exchanges/aster.js +125 -55
  94. package/dist/exchanges/builder-fee.d.ts +1 -1
  95. package/dist/exchanges/builder-fee.js +13 -2
  96. package/dist/exchanges/canonical-symbols.d.ts +1 -2
  97. package/dist/exchanges/canonical-symbols.js +26 -2
  98. package/dist/exchanges/derive/client.d.ts +37 -0
  99. package/dist/exchanges/derive/client.js +135 -0
  100. package/dist/exchanges/derive/options.d.ts +68 -0
  101. package/dist/exchanges/derive/options.js +360 -0
  102. package/dist/exchanges/extended/adapter.d.ts +82 -0
  103. package/dist/exchanges/extended/adapter.js +179 -0
  104. package/dist/exchanges/extended/client.d.ts +38 -0
  105. package/dist/exchanges/extended/client.js +88 -0
  106. package/dist/exchanges/extended/public.d.ts +130 -0
  107. package/dist/exchanges/extended/public.js +429 -0
  108. package/dist/exchanges/grvt/adapter.d.ts +86 -0
  109. package/dist/exchanges/grvt/adapter.js +206 -0
  110. package/dist/exchanges/grvt/client.d.ts +25 -0
  111. package/dist/exchanges/grvt/client.js +86 -0
  112. package/dist/exchanges/grvt/public.d.ts +151 -0
  113. package/dist/exchanges/grvt/public.js +438 -0
  114. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  115. package/dist/exchanges/hyperliquid-spot.js +5 -5
  116. package/dist/exchanges/hyperliquid.d.ts +22 -16
  117. package/dist/exchanges/hyperliquid.js +80 -55
  118. package/dist/exchanges/index.d.ts +1 -1
  119. package/dist/exchanges/index.js +1 -1
  120. package/dist/exchanges/interface.d.ts +97 -8
  121. package/dist/exchanges/interface.js +19 -2
  122. package/dist/exchanges/ioc-outcome.d.ts +1 -1
  123. package/dist/exchanges/ioc-outcome.js +1 -1
  124. package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
  125. package/dist/exchanges/legacy-trading-v2.js +7 -2
  126. package/dist/exchanges/lighter-public.d.ts +2 -2
  127. package/dist/exchanges/lighter-public.js +2 -2
  128. package/dist/exchanges/lighter-signer.d.ts +1 -1
  129. package/dist/exchanges/lighter-signer.js +1 -1
  130. package/dist/exchanges/lighter-spot.d.ts +3 -4
  131. package/dist/exchanges/lighter-spot.js +6 -7
  132. package/dist/exchanges/lighter.d.ts +11 -11
  133. package/dist/exchanges/lighter.js +28 -14
  134. package/dist/exchanges/ondo/adapter.d.ts +5 -6
  135. package/dist/exchanges/ondo/adapter.js +16 -14
  136. package/dist/exchanges/ondo/agent.d.ts +1 -1
  137. package/dist/exchanges/ondo/agent.js +1 -1
  138. package/dist/exchanges/ondo/client.d.ts +1 -1
  139. package/dist/exchanges/ondo/client.js +2 -2
  140. package/dist/exchanges/ondo/public.d.ts +8 -1
  141. package/dist/exchanges/ondo/public.js +13 -2
  142. package/dist/exchanges/ondo/spot.d.ts +2 -2
  143. package/dist/exchanges/ondo/spot.js +3 -3
  144. package/dist/exchanges/options-interface.d.ts +158 -0
  145. package/dist/exchanges/options-interface.js +17 -0
  146. package/dist/exchanges/options-venues.d.ts +12 -0
  147. package/dist/exchanges/options-venues.js +35 -0
  148. package/dist/exchanges/pacifica-atomic.d.ts +211 -0
  149. package/dist/exchanges/pacifica-atomic.js +440 -0
  150. package/dist/exchanges/pacifica-decimal.d.ts +10 -0
  151. package/dist/exchanges/pacifica-decimal.js +51 -0
  152. package/dist/exchanges/pacifica-presigned.d.ts +53 -0
  153. package/dist/exchanges/pacifica-presigned.js +141 -0
  154. package/dist/exchanges/pacifica-print.d.ts +200 -0
  155. package/dist/exchanges/pacifica-print.js +417 -0
  156. package/dist/exchanges/pacifica-spot.d.ts +2 -2
  157. package/dist/exchanges/pacifica-spot.js +3 -3
  158. package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
  159. package/dist/exchanges/pacifica-typed-data.js +1 -1
  160. package/dist/exchanges/pacifica-vaults.d.ts +121 -0
  161. package/dist/exchanges/pacifica-vaults.js +264 -0
  162. package/dist/exchanges/pacifica.d.ts +32 -5
  163. package/dist/exchanges/pacifica.js +114 -48
  164. package/dist/exchanges/precision.d.ts +3 -1
  165. package/dist/exchanges/precision.js +9 -2
  166. package/dist/exchanges/qfex/adapter.d.ts +18 -5
  167. package/dist/exchanges/qfex/adapter.js +32 -12
  168. package/dist/exchanges/qfex/agent.d.ts +1 -1
  169. package/dist/exchanges/qfex/agent.js +1 -1
  170. package/dist/exchanges/qfex/client.d.ts +1 -1
  171. package/dist/exchanges/qfex/client.js +1 -1
  172. package/dist/exchanges/qfex/errors.d.ts +1 -1
  173. package/dist/exchanges/qfex/errors.js +2 -2
  174. package/dist/exchanges/qfex/public.d.ts +3 -3
  175. package/dist/exchanges/qfex/public.js +3 -3
  176. package/dist/exchanges/qfex/signing.d.ts +2 -2
  177. package/dist/exchanges/qfex/signing.js +4 -5
  178. package/dist/exchanges/qfex/ws-transport.d.ts +2 -2
  179. package/dist/exchanges/qfex/ws-transport.js +3 -3
  180. package/dist/exchanges/rate-limiter.d.ts +1 -1
  181. package/dist/exchanges/rate-limiter.js +5 -2
  182. package/dist/exchanges/registry.d.ts +1 -1
  183. package/dist/exchanges/registry.js +1 -1
  184. package/dist/exchanges/risex/adapter.d.ts +148 -0
  185. package/dist/exchanges/risex/adapter.js +796 -0
  186. package/dist/exchanges/risex/agent.d.ts +154 -0
  187. package/dist/exchanges/risex/agent.js +384 -0
  188. package/dist/exchanges/risex/client.d.ts +47 -0
  189. package/dist/exchanges/risex/client.js +174 -0
  190. package/dist/exchanges/risex/nonce.d.ts +43 -0
  191. package/dist/exchanges/risex/nonce.js +212 -0
  192. package/dist/exchanges/risex/public.d.ts +64 -0
  193. package/dist/exchanges/risex/public.js +150 -0
  194. package/dist/exchanges/risex/session.d.ts +71 -0
  195. package/dist/exchanges/risex/session.js +171 -0
  196. package/dist/exchanges/risex/typed-data.d.ts +173 -0
  197. package/dist/exchanges/risex/typed-data.js +240 -0
  198. package/dist/exchanges/risex/units.d.ts +29 -0
  199. package/dist/exchanges/risex/units.js +104 -0
  200. package/dist/exchanges/slippage.d.ts +1 -1
  201. package/dist/exchanges/slippage.js +1 -1
  202. package/dist/exchanges/spot-factory.d.ts +3 -3
  203. package/dist/exchanges/spot-factory.js +7 -7
  204. package/dist/exchanges/spot-interface.d.ts +5 -5
  205. package/dist/exchanges/spot-interface.js +2 -2
  206. package/dist/exchanges/spot-result.d.ts +3 -3
  207. package/dist/exchanges/spot-result.js +3 -3
  208. package/dist/exchanges/trading-v2.d.ts +3 -3
  209. package/dist/exchanges/trading-v2.js +9 -6
  210. package/dist/exchanges/txflow/adapter.d.ts +54 -0
  211. package/dist/exchanges/txflow/adapter.js +118 -0
  212. package/dist/exchanges/txflow/client.d.ts +47 -0
  213. package/dist/exchanges/txflow/client.js +153 -0
  214. package/dist/exchanges/txflow/public.d.ts +76 -0
  215. package/dist/exchanges/txflow/public.js +284 -0
  216. package/dist/exchanges/unknown-symbol.d.ts +29 -0
  217. package/dist/exchanges/unknown-symbol.js +51 -0
  218. package/dist/exchanges/variational/adapter.d.ts +71 -0
  219. package/dist/exchanges/variational/adapter.js +137 -0
  220. package/dist/exchanges/variational/client.d.ts +21 -0
  221. package/dist/exchanges/variational/client.js +66 -0
  222. package/dist/exchanges/variational/public.d.ts +126 -0
  223. package/dist/exchanges/variational/public.js +430 -0
  224. package/dist/exchanges/venue-types.d.ts +6 -7
  225. package/dist/exchanges/venue-types.js +2 -2
  226. package/dist/exchanges/venue.d.ts +51 -8
  227. package/dist/exchanges/venue.js +54 -4
  228. package/dist/exchanges/venues/arcus.d.ts +1 -2
  229. package/dist/exchanges/venues/arcus.js +5 -4
  230. package/dist/exchanges/venues/aster.d.ts +2 -2
  231. package/dist/exchanges/venues/aster.js +6 -4
  232. package/dist/exchanges/venues/create-helpers.d.ts +1 -1
  233. package/dist/exchanges/venues/create-helpers.js +3 -3
  234. package/dist/exchanges/venues/extended.d.ts +21 -0
  235. package/dist/exchanges/venues/extended.js +96 -0
  236. package/dist/exchanges/venues/grvt.d.ts +30 -0
  237. package/dist/exchanges/venues/grvt.js +98 -0
  238. package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
  239. package/dist/exchanges/venues/hyperliquid.js +10 -8
  240. package/dist/exchanges/venues/index.d.ts +7 -2
  241. package/dist/exchanges/venues/index.js +8 -2
  242. package/dist/exchanges/venues/lighter-family.d.ts +7 -9
  243. package/dist/exchanges/venues/lighter-family.js +9 -11
  244. package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
  245. package/dist/exchanges/venues/lighter-rh.js +7 -5
  246. package/dist/exchanges/venues/lighter.d.ts +1 -1
  247. package/dist/exchanges/venues/lighter.js +5 -3
  248. package/dist/exchanges/venues/ondo.d.ts +1 -1
  249. package/dist/exchanges/venues/ondo.js +5 -3
  250. package/dist/exchanges/venues/pacifica.d.ts +2 -2
  251. package/dist/exchanges/venues/pacifica.js +6 -4
  252. package/dist/exchanges/venues/qfex.d.ts +1 -1
  253. package/dist/exchanges/venues/qfex.js +4 -2
  254. package/dist/exchanges/venues/risex-release.d.ts +14 -0
  255. package/dist/exchanges/venues/risex-release.js +14 -0
  256. package/dist/exchanges/venues/risex.d.ts +31 -0
  257. package/dist/exchanges/venues/risex.js +116 -0
  258. package/dist/exchanges/venues/txflow.d.ts +18 -0
  259. package/dist/exchanges/venues/txflow.js +77 -0
  260. package/dist/exchanges/venues/variational.d.ts +21 -0
  261. package/dist/exchanges/venues/variational.js +85 -0
  262. package/dist/execution-log.d.ts +1 -1
  263. package/dist/funding/legacy-public.d.ts +1 -1
  264. package/dist/funding/legacy-public.js +9 -4
  265. package/dist/funding/normalize.d.ts +1 -1
  266. package/dist/funding/normalize.js +1 -1
  267. package/dist/funding/rates.d.ts +44 -4
  268. package/dist/funding/rates.js +56 -12
  269. package/dist/index.js +7 -6
  270. package/dist/jobs.d.ts +1 -0
  271. package/dist/jobs.js +25 -1
  272. package/dist/landing.d.ts +2 -0
  273. package/dist/landing.js +1 -1
  274. package/dist/mcp-pacifica-earn.d.ts +9 -0
  275. package/dist/mcp-pacifica-earn.js +58 -0
  276. package/dist/mcp-server.js +113 -59
  277. package/dist/pacifica/client.d.ts +67 -4
  278. package/dist/pacifica/client.js +170 -8
  279. package/dist/pacifica/rate-limit.d.ts +144 -0
  280. package/dist/pacifica/rate-limit.js +267 -0
  281. package/dist/pacifica/types/order.d.ts +2 -1
  282. package/dist/plan-executor.js +7 -2
  283. package/dist/rebalance.js +1 -1
  284. package/dist/risk.d.ts +2 -1
  285. package/dist/risk.js +2 -4
  286. package/dist/settings.d.ts +32 -3
  287. package/dist/signer/ows-evm.d.ts +1 -1
  288. package/dist/signer/ows-evm.js +1 -1
  289. package/dist/smart-order.d.ts +1 -1
  290. package/dist/smart-order.js +1 -1
  291. package/dist/strategies/funding-arb.d.ts +1 -1
  292. package/dist/strategies/funding-arb.js +1 -1
  293. package/dist/trade-validator.d.ts +1 -1
  294. package/dist/trade-validator.js +9 -4
  295. package/dist/utils.d.ts +12 -3
  296. package/dist/utils.js +11 -3
  297. package/dist/venue-health.d.ts +40 -0
  298. package/dist/venue-health.js +75 -0
  299. package/dist/withdraw-gate.d.ts +1 -1
  300. package/dist/withdraw-gate.js +1 -1
  301. package/dist/ws/index.d.ts +959 -0
  302. package/dist/ws/index.js +4295 -0
  303. package/dist/ws/node.d.ts +975 -0
  304. package/dist/ws/node.js +4366 -0
  305. package/dist/ws/pacifica-instrument.d.ts +8 -0
  306. package/dist/ws/pacifica-instrument.js +8 -0
  307. package/dist/ws-cli/credentials.d.ts +62 -0
  308. package/dist/ws-cli/credentials.js +126 -0
  309. package/package.json +58 -10
  310. package/skills/perp-cli/SKILL.md +62 -9
  311. package/skills/perp-cli/references/agent-operations.md +7 -0
  312. package/skills/perp-cli/references/commands.md +76 -5
  313. package/skills/perp-cli/references/ws-library.md +492 -0
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Variational public market data — the OFFICIAL read-only API (venues/variational.ts).
3
+ *
4
+ * Source: `GET /metadata/v2/stats` (docs.variational.io/technical-documentation/api). One call
5
+ * returns every listing; it is shared by every reader for VARIATIONAL_STATS_TTL_MS through the
6
+ * file cache (cache.ts, cross-process, raw JSON body only) plus an in-process in-flight promise,
7
+ * so `market list` + `arb scan` + `perp health` inside the window cost ONE request.
8
+ *
9
+ * Listings (575 on 2026-10-19: 557 perpetuals, 18 swaps):
10
+ * - ticker "BTC-PERP" / "XAU-SWAP", contract_type "perpetual" | "swap". Perp display symbol =
11
+ * the base ("BTC"); a swap keeps its suffix ("XAU-SWAP") so it never collides with "XAU" perp.
12
+ * - mark_price, volume_24h (USDC), open_interest.{long,short}_open_interest (USDC, single-sided:
13
+ * trader positions only — the OLP is the other side). Base-unit OI is NOT published, so the
14
+ * legacy openInterest (base units) is null; the USD figures are carried separately.
15
+ * - quotes.{base,size_1k,size_100k,size_1m}.{bid,ask}: the OLP's INDICATIVE RFQ quotes at fixed
16
+ * notionals (may be cached up to 600 s; quotes.updated_at). Omitted while a market is closed.
17
+ *
18
+ * Funding (docs.variational.io/omni/trading/funding-rates):
19
+ * - perpetual: funding.last.interval_rate is a decimal fraction for ONE interval_seconds window
20
+ * (1h / 4h / 8h, per market), positive = longs pay. annualized_rate is the same rate × 365 d /
21
+ * interval — the identity is checked per row; a row that breaks it gets no rate (warned).
22
+ * - rateKind "predicted": `last` is recomputed during the window (live samples 2026-10-19: 175
23
+ * of 557 perps changed within 2 min under the same next_timestamp) and `next` ("predicted
24
+ * funding rate for the next interval") equals it. It is the rate the OLP will charge at
25
+ * next_timestamp, not a settled print.
26
+ * - swap: separate long / short rates paid once per trading day — there is no single funding
27
+ * rate, so fundingRate is null and swaps are never matched across venues (canonical null);
28
+ * the long / short rates are shown on the Variational rows only.
29
+ */
30
+ import { getCached, setCached } from "../../cache.js";
31
+ import { canonicalFor } from "../canonical-symbols.js";
32
+ import { networkConfig } from "../venue.js";
33
+ import { variationalVenue } from "../venues/variational.js";
34
+ import { variationalShapeError, VariationalClient } from "./client.js";
35
+ import { symbolNotFound } from "../unknown-symbol.js";
36
+ /** How long one stats payload serves every reader (the venue itself sends max-age=30). */
37
+ export const VARIATIONAL_STATS_TTL_MS = 5_000;
38
+ const YEAR_SECONDS = 365 * 86_400;
39
+ /** interval_rate vs annualized_rate × interval / 365 d: both are rounded to ~4 significant digits. */
40
+ const RATE_IDENTITY_REL_TOL = 0.005;
41
+ const RATE_IDENTITY_ABS_TOL = 1e-9;
42
+ /**
43
+ * TradFi perpetuals by class (the API publishes no asset class). Equities / ETFs match by ticker
44
+ * identity; commodities / pre-IPO only through EXPLICIT_CANONICAL. A perp whose `name` reads like
45
+ * a company or fund but is missing here is listed as equity with NO canonical symbol (never matched
46
+ * across venues) until it is classified — see looksTradFi().
47
+ */
48
+ const TRADFI_PERP_CLASS = {
49
+ // Commodities
50
+ XAU: "commodity", XAG: "commodity", XPD: "commodity", XPT: "commodity", CL: "commodity", BZ: "commodity",
51
+ COPPER: "commodity", NATGAS: "commodity",
52
+ // Pre-IPO
53
+ ANTHROPIC: "preIpo", OPENAI: "preIpo",
54
+ // ETFs
55
+ DRAM: "etf", EWJ: "etf", EWT: "etf", EWY: "etf", EWZ: "etf", IWM: "etf", KSTR: "etf", QQQ: "etf", SOXL: "etf",
56
+ SOXS: "etf", SPCX: "etf", TMF: "etf", TZA: "etf", URNM: "etf", UVXY: "etf", XBI: "etf", XLE: "etf", US500: "etf",
57
+ // Equities
58
+ AAOI: "equity", AAPL: "equity", ADBE: "equity", ALAB: "equity", AMAT: "equity", AMD: "equity", AMZN: "equity",
59
+ APP: "equity", ARM: "equity", ASML: "equity", AVGO: "equity", BABA: "equity", BBX: "equity", BE: "equity",
60
+ BMNR: "equity", BNC: "equity", BOT: "equity", BRKB: "equity", BX: "equity", CAT: "equity", CBRS: "equity",
61
+ CIEN: "equity", COIN: "equity", COST: "equity", CRCL: "equity", CRDO: "equity", CRM: "equity", CRWD: "equity",
62
+ CRWV: "equity", CSCO: "equity", DELL: "equity", DIS: "equity", DJT: "equity", DKNG: "equity", EBAY: "equity",
63
+ FLNC: "equity", FWDI: "equity", GLW: "equity", GME: "equity", GOOGL: "equity", GPRO: "equity", HD: "equity",
64
+ HIMS: "equity", HOOD: "equity", HPE: "equity", IBM: "equity", INTC: "equity", IREN: "equity", JPM: "equity",
65
+ KLAC: "equity", KO: "equity", LITE: "equity", LLY: "equity", META: "equity", MRNA: "equity", MRVL: "equity",
66
+ MSFT: "equity", MSTR: "equity", MU: "equity", NBIS: "equity", NFLX: "equity", NOK: "equity", NOW: "equity",
67
+ NVDA: "equity", NVO: "equity", ONDS: "equity", ORCL: "equity", PAYP: "equity", PENG: "equity", PLTR: "equity",
68
+ QCOM: "equity", QNTX: "equity", RDDT: "equity", RIVN: "equity", RKLB: "equity", SHAZ: "equity", SHOP: "equity",
69
+ SKHY: "equity", SMCI: "equity", SNDK: "equity", SNOW: "equity", SOFI: "equity", SONY: "equity", STRC: "equity",
70
+ STXX: "equity", TER: "equity", TSLA: "equity", TSM: "equity", TTWO: "equity", TXN: "equity", UBER: "equity",
71
+ USAR: "equity", VISA: "equity", WEN: "equity", WMT: "equity", ZM: "equity",
72
+ };
73
+ /**
74
+ * Variational tickers whose canonical symbol is NOT the ticker:
75
+ * - US500-PERP is "State Street SPDR S&P 500 ETF Trust" (SPY, mark ≈ $760), not the S&P 500 index
76
+ * that other venues list as US500 (→ SPX).
77
+ * `null` = never matched across venues — the same exclusions the other venues apply:
78
+ * - SPX-PERP is the SPX6900 token; "SPX" is the canonical key of the S&P 500 index, and
79
+ * Hyperliquid / Aster / Lighter all exclude their SPX6900 (legacy-public.ts AMBIGUOUS,
80
+ * lighter-public.ts LIGHTER_AMBIGUOUS), so it could only ever pair with the index.
81
+ * - QNT-PERP is Quant (crypto); QNT is Quantinuum on Arcus / QFEX (LIGHTER_AMBIGUOUS too).
82
+ *
83
+ * Live cross-venue survey (2026-10-19, 440 Variational canonicals shared with another venue):
84
+ * ZETA (ZetaChain vs QFEX Zeta Global), DIA (DIA oracle vs Lighter DIA ETF) and BEAM (Beam vs QFEX
85
+ * Beam Global) also collide with an equity / ETF elsewhere but are the same token on the crypto
86
+ * venues; the wrong pairing is dropped, with a stderr warning, by the cross-venue price guard
87
+ * (rates.ts dropPriceOutliers / pair gap).
88
+ */
89
+ const CANONICAL_OVERRIDE = {
90
+ US500: "SPY",
91
+ SPX: null,
92
+ QNT: null,
93
+ };
94
+ /**
95
+ * Hint for a cross-venue lookup by a Variational ticker whose canonical symbol differs (arb scan
96
+ * --compare US500): where it is compared instead, or that it is never matched. null = no override.
97
+ */
98
+ export function canonicalHintFor(symbol) {
99
+ const up = symbol.trim().toUpperCase().replace(/-PERP$/, "");
100
+ if (!(up in CANONICAL_OVERRIDE))
101
+ return null;
102
+ const canonical = CANONICAL_OVERRIDE[up];
103
+ return canonical === null
104
+ ? `Variational's ${up}-PERP is never matched across venues (the ticker means a different asset elsewhere)`
105
+ : `Variational's ${up}-PERP is compared as ${canonical}: perp arb scan --compare ${canonical} --include-unverified`;
106
+ }
107
+ /** Swaps (daily-funded TradFi swaps) by class — display only: swaps never get a canonical symbol. */
108
+ const SWAP_CLASS = {
109
+ XAU: "commodity", XAG: "commodity", UKOIL: "commodity", USOIL: "commodity", HOIL: "commodity", GASOL: "commodity",
110
+ COCOA: "commodity", COPP: "commodity",
111
+ US30: "index", US100: "index", US500: "index", US400: "index", US2K: "index", CNA50: "index", TWI: "index",
112
+ USFNG: "index", VXI: "index",
113
+ DXI: "fx",
114
+ };
115
+ /** A listing name that reads like a company / fund (an unclassified TradFi perp). */
116
+ const TRADFI_NAME = /\b(Inc|Corp|Corporation|Company|Holdings?|Ltd|Limited|plc|N\.V|A\/S|Oyj|ETF|Common Stock|Class [A-Z])\b\.?/i;
117
+ export function looksTradFi(name) {
118
+ return name !== null && TRADFI_NAME.test(name);
119
+ }
120
+ /** Each distinct warning once per process (the stats body is re-parsed on every read). */
121
+ const warned = new Set();
122
+ export function warnOnce(warn, msg) {
123
+ if (warned.has(msg))
124
+ return;
125
+ warned.add(msg);
126
+ warn(msg);
127
+ }
128
+ export const QUOTE_TIERS = ["base", "size_1k", "size_100k", "size_1m"];
129
+ /** USD notional of each quote tier; "base" = the market's minimum order size (notional not published). */
130
+ export const QUOTE_TIER_NOTIONAL_USD = {
131
+ base: null, size_1k: "1000", size_100k: "100000", size_1m: "1000000",
132
+ };
133
+ const DEC = /^-?\d+(\.\d+)?$/;
134
+ const isDec = (v) => typeof v === "string" && DEC.test(v);
135
+ class RowError extends Error {
136
+ }
137
+ const bad = (what) => { throw new RowError(what); };
138
+ const dec = (v, what) => (isDec(v) ? v : bad(`${what} is not a decimal string`));
139
+ const obj = (v, what) => (v !== null && typeof v === "object" && !Array.isArray(v) ? v : bad(`${what} is not an object`));
140
+ /** Unix seconds → ms; null stays null (documented for swaps). */
141
+ function tsMs(v, what, nullable) {
142
+ if (v === null && nullable)
143
+ return null;
144
+ if (typeof v !== "number" || !Number.isInteger(v) || v <= 0)
145
+ return bad(`${what} is not a unix-seconds integer`);
146
+ return v * 1000;
147
+ }
148
+ function perpRate(v, what) {
149
+ const r = obj(v, what);
150
+ return { annualized: dec(r.annualized_rate, `${what}.annualized_rate`), interval: dec(r.interval_rate, `${what}.interval_rate`) };
151
+ }
152
+ function swapRate(v, what) {
153
+ if (v === null)
154
+ return null;
155
+ const r = obj(v, what);
156
+ return {
157
+ annualizedLong: dec(r.annualized_long_rate, `${what}.annualized_long_rate`),
158
+ annualizedShort: dec(r.annualized_short_rate, `${what}.annualized_short_rate`),
159
+ intervalLong: dec(r.interval_long_rate, `${what}.interval_long_rate`),
160
+ intervalShort: dec(r.interval_short_rate, `${what}.interval_short_rate`),
161
+ };
162
+ }
163
+ function funding(v, kind) {
164
+ if (v === null)
165
+ return null;
166
+ const f = obj(v, "funding");
167
+ const intervalSeconds = f.interval_seconds;
168
+ if (typeof intervalSeconds !== "number" || !Number.isInteger(intervalSeconds) || intervalSeconds <= 0)
169
+ return bad("funding.interval_seconds is not a positive integer");
170
+ if (kind === "perpetual") {
171
+ return {
172
+ kind, intervalSeconds,
173
+ nextFundingTime: tsMs(f.next_timestamp, "funding.next_timestamp", false),
174
+ last: perpRate(f.last, "funding.last"),
175
+ next: f.next === null ? null : perpRate(f.next, "funding.next"),
176
+ };
177
+ }
178
+ return {
179
+ kind, intervalSeconds,
180
+ nextFundingTime: tsMs(f.next_timestamp, "funding.next_timestamp", true),
181
+ last: swapRate(f.last, "funding.last"),
182
+ next: swapRate(f.next, "funding.next"),
183
+ };
184
+ }
185
+ function quotes(v) {
186
+ if (v === undefined)
187
+ return null; // documented: omitted when unavailable (e.g. a closed swap)
188
+ const q = obj(v, "quotes");
189
+ if (typeof q.updated_at !== "string" || Number.isNaN(Date.parse(q.updated_at)))
190
+ return bad("quotes.updated_at is not an ISO time");
191
+ const tiers = [];
192
+ for (const tier of QUOTE_TIERS) {
193
+ if (q[tier] === undefined)
194
+ continue; // documented: individual sizes are omitted when unavailable
195
+ const t = obj(q[tier], `quotes.${tier}`);
196
+ tiers.push({ tier, notionalUsd: QUOTE_TIER_NOTIONAL_USD[tier], bid: dec(t.bid, `quotes.${tier}.bid`), ask: dec(t.ask, `quotes.${tier}.ask`) });
197
+ }
198
+ return { updatedAt: q.updated_at, tiers };
199
+ }
200
+ function classify(base, contractType, name) {
201
+ if (contractType === "swap") {
202
+ const assetClass = SWAP_CLASS[base];
203
+ return assetClass === undefined ? null : { assetClass, canonical: null };
204
+ }
205
+ const known = TRADFI_PERP_CLASS[base];
206
+ const assetClass = known ?? (looksTradFi(name) ? "equity" : "crypto");
207
+ if (known === undefined && assetClass === "equity")
208
+ return { assetClass, canonical: null }; // unclassified TradFi
209
+ if (base in CANONICAL_OVERRIDE)
210
+ return { assetClass, canonical: CANONICAL_OVERRIDE[base] };
211
+ return { assetClass, canonical: canonicalFor("variational", base, assetClass) };
212
+ }
213
+ /** Parse one listing. Throws RowError (caught per row by parseStats) on any shape drift. */
214
+ function parseListing(raw) {
215
+ const r = obj(raw, "listing");
216
+ if (typeof r.ticker !== "string")
217
+ return bad("ticker is not a string");
218
+ const m = /^(.+)-(PERP|SWAP)$/.exec(r.ticker);
219
+ if (m === null)
220
+ return bad(`ticker ${r.ticker} has no -PERP / -SWAP suffix`);
221
+ const contractType = r.contract_type;
222
+ if (contractType !== "perpetual" && contractType !== "swap")
223
+ return { skip: `unknown contract_type ${String(contractType)}` };
224
+ if ((contractType === "perpetual") !== (m[2] === "PERP"))
225
+ return bad(`ticker suffix ${m[2]} disagrees with contract_type ${contractType}`);
226
+ const base = m[1].toUpperCase();
227
+ const name = r.name === undefined ? null : typeof r.name === "string" ? r.name.replace(/​/g, "").trim() : bad("name is not a string");
228
+ const markPrice = dec(r.mark_price, "mark_price");
229
+ if (!(Number(markPrice) > 0))
230
+ return { skip: "no positive mark price" };
231
+ const oi = obj(r.open_interest, "open_interest");
232
+ if (!("funding" in r))
233
+ return bad("funding is missing (documented as always present)");
234
+ const cls = classify(base, contractType, name);
235
+ if (cls === null)
236
+ return { skip: "unclassified swap market" };
237
+ return {
238
+ ticker: r.ticker,
239
+ contractType,
240
+ symbol: contractType === "perpetual" ? base : r.ticker,
241
+ base,
242
+ name,
243
+ markPrice,
244
+ volume24h: dec(r.volume_24h, "volume_24h"),
245
+ longOpenInterestUsd: dec(oi.long_open_interest, "open_interest.long_open_interest"),
246
+ shortOpenInterestUsd: dec(oi.short_open_interest, "open_interest.short_open_interest"),
247
+ funding: funding(r.funding, contractType),
248
+ baseSpreadBps: r.base_spread_bps === undefined ? null : dec(r.base_spread_bps, "base_spread_bps"),
249
+ quotes: quotes(r.quotes),
250
+ assetClass: cls.assetClass,
251
+ canonical: cls.canonical,
252
+ };
253
+ }
254
+ /**
255
+ * Parse the stats payload. A non-object payload / missing listings array is EXCHANGE_ERROR; a
256
+ * single malformed or unclassifiable listing is dropped with a warning naming it (never guessed).
257
+ */
258
+ export function parseVariationalStats(body, warn) {
259
+ if (body === null || typeof body !== "object" || Array.isArray(body))
260
+ throw variationalShapeError("payload is not an object");
261
+ const listings = body.listings;
262
+ if (!Array.isArray(listings))
263
+ throw variationalShapeError("payload has no listings array");
264
+ const out = [];
265
+ const dropped = [];
266
+ const seen = new Set();
267
+ for (const raw of listings) {
268
+ const ticker = typeof raw?.ticker === "string" ? raw.ticker : "?";
269
+ try {
270
+ const l = parseListing(raw);
271
+ if ("skip" in l) {
272
+ dropped.push(`${ticker} (${l.skip})`);
273
+ continue;
274
+ }
275
+ if (seen.has(l.symbol)) {
276
+ dropped.push(`${ticker} (listed twice)`);
277
+ continue;
278
+ }
279
+ seen.add(l.symbol);
280
+ out.push(l);
281
+ }
282
+ catch (e) {
283
+ if (!(e instanceof RowError))
284
+ throw e;
285
+ dropped.push(`${ticker} (${e.message})`);
286
+ }
287
+ }
288
+ if (dropped.length > 0)
289
+ warnOnce(warn, `[variational] dropped ${dropped.length} listing(s): ${dropped.join(", ")}`);
290
+ return out;
291
+ }
292
+ // ── Funding ──
293
+ /** interval_rate agrees with annualized_rate × interval / 365 d (both published, both rounded). */
294
+ export function perpRateConsistent(rate, intervalSeconds) {
295
+ const interval = Number(rate.interval);
296
+ const expected = Number(rate.annualized) * intervalSeconds / YEAR_SECONDS;
297
+ return Math.abs(interval - expected) <= Math.max(RATE_IDENTITY_ABS_TOL, Math.abs(expected) * RATE_IDENTITY_REL_TOL);
298
+ }
299
+ /** Basis of one perp's interval_rate: perHours(interval_seconds / 3600). */
300
+ export function fundingBasisOf(f) {
301
+ return { kind: "perHours", hours: f.intervalSeconds / 3600 };
302
+ }
303
+ /**
304
+ * The current (predicted) funding rate per interval, or null: swaps (no single rate), no funding
305
+ * terms, or a perp whose two published representations disagree (unit drift — warned).
306
+ */
307
+ export function currentPerpRate(l, warn) {
308
+ const f = l.funding;
309
+ if (f === null || f.kind !== "perpetual")
310
+ return null;
311
+ if (!perpRateConsistent(f.last, f.intervalSeconds)) {
312
+ warnOnce(warn, `[variational] ${l.ticker}: interval_rate ${f.last.interval} disagrees with annualized_rate ${f.last.annualized} over ${f.intervalSeconds}s — funding not shown`);
313
+ return null;
314
+ }
315
+ return f.last.interval;
316
+ }
317
+ function clientFor(net) {
318
+ return new VariationalClient(networkConfig(variationalVenue, net).restUrl, variationalVenue.rateLimit); // testnet → NOT_IMPLEMENTED
319
+ }
320
+ const DEFAULT_DEPS = {
321
+ stats: net => clientFor(net).stats(),
322
+ cache: { get: key => getCached(key), set: (key, value, ttl) => setCached(key, value, ttl) },
323
+ warn: msg => process.stderr.write(`${msg}\n`),
324
+ };
325
+ const inflight = new Map();
326
+ export const statsCacheKey = (net) => `pub:variational:${net}:stats`;
327
+ /**
328
+ * The raw stats body: from the cache when fresh, else ONE request shared by every concurrent
329
+ * caller in this process. Only the raw JSON body is cached (JSON-safe); parsing runs per read.
330
+ */
331
+ export function fetchVariationalStatsRaw(net, deps = DEFAULT_DEPS) {
332
+ const key = statsCacheKey(net);
333
+ const pending = inflight.get(key);
334
+ if (pending !== undefined)
335
+ return pending;
336
+ const p = (async () => {
337
+ const hit = deps.cache.get(key);
338
+ if (hit !== null && hit !== undefined)
339
+ return hit;
340
+ const body = await deps.stats(net);
341
+ // Only a body of the documented shape is shared; drift fails this read and is never cached.
342
+ if (!Array.isArray(body?.listings))
343
+ throw variationalShapeError("payload has no listings array");
344
+ deps.cache.set(key, body, VARIATIONAL_STATS_TTL_MS);
345
+ return body;
346
+ })().finally(() => { inflight.delete(key); });
347
+ inflight.set(key, p);
348
+ return p;
349
+ }
350
+ export async function fetchVariationalListings(net, deps = DEFAULT_DEPS) {
351
+ return parseVariationalStats(await fetchVariationalStatsRaw(net, deps), deps.warn);
352
+ }
353
+ // ── Mapping ──
354
+ /** null when the listing has no funding terms (its interval — part of VenueMarket — is unknown). */
355
+ export function toVariationalVenueMarket(l, warn) {
356
+ if (l.funding === null)
357
+ return null;
358
+ return {
359
+ venue: "variational",
360
+ venueSymbol: l.ticker,
361
+ symbol: l.symbol,
362
+ canonical: l.canonical,
363
+ assetClass: l.assetClass,
364
+ settle: "USDC",
365
+ quoteAsset: "USDC",
366
+ markPrice: l.markPrice,
367
+ // No index / oracle price is published.
368
+ indexPrice: null,
369
+ fundingRate: currentPerpRate(l, warn),
370
+ fundingRateBasis: fundingBasisOf(l.funding),
371
+ settlementIntervalHours: l.funding.intervalSeconds / 3600,
372
+ // Published in USD and single-sided only; base-unit OI is unknown.
373
+ openInterest: null,
374
+ volume24h: l.volume24h,
375
+ tickSize: null,
376
+ stepSize: null,
377
+ minSize: null,
378
+ minNotional: null,
379
+ maxLeverage: null,
380
+ marginTiers: null,
381
+ session: null,
382
+ // Quotes are omitted while a market is closed: no quote → not tradable right now.
383
+ tradable: l.quotes !== null && l.quotes.tiers.length > 0,
384
+ };
385
+ }
386
+ export async function fetchVariationalMarkets(net, deps = DEFAULT_DEPS) {
387
+ const out = [];
388
+ const noFunding = [];
389
+ for (const l of await fetchVariationalListings(net, deps)) {
390
+ const m = toVariationalVenueMarket(l, deps.warn);
391
+ if (m === null)
392
+ noFunding.push(l.ticker);
393
+ else
394
+ out.push(m);
395
+ }
396
+ if (noFunding.length > 0)
397
+ warnOnce(deps.warn, `[variational] ${noFunding.length} market(s) publish no funding terms (interval unknown) and are not shown here: ${noFunding.join(", ")}`);
398
+ return out;
399
+ }
400
+ export async function fetchVariationalFundingRates(net, deps = DEFAULT_DEPS) {
401
+ const out = [];
402
+ for (const l of await fetchVariationalListings(net, deps)) {
403
+ const m = toVariationalVenueMarket(l, deps.warn);
404
+ if (m === null || !m.tradable || m.fundingRate === null || l.funding === null)
405
+ continue;
406
+ out.push({
407
+ venue: "variational",
408
+ venueSymbol: m.venueSymbol,
409
+ symbol: m.symbol,
410
+ canonical: m.canonical,
411
+ rate: m.fundingRate,
412
+ basis: m.fundingRateBasis,
413
+ markPrice: m.markPrice,
414
+ nextFundingTime: l.funding.nextFundingTime,
415
+ });
416
+ }
417
+ return out;
418
+ }
419
+ export const variationalPublicData = {
420
+ fetchMarkets: net => fetchVariationalMarkets(net),
421
+ fetchFundingRates: net => fetchVariationalFundingRates(net),
422
+ };
423
+ /** Resolve a CLI symbol ("btc", "BTC-PERP", "xau-swap") against the parsed listings. */
424
+ export function resolveVariationalListing(listings, input) {
425
+ const up = input.trim().toUpperCase();
426
+ const hit = listings.find(l => l.ticker === up || l.symbol === up);
427
+ if (hit === undefined)
428
+ throw symbolNotFound("variational", input, `Variational has no listed market ${input}`);
429
+ return hit;
430
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Venue-level data contracts (docs/DESIGN-venue-abstraction.md §10).
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.
@@ -7,7 +7,7 @@
7
7
  import type { ExchangeMarketInfo } from "./interface.js";
8
8
  import type { ResolvedSlippage, SlippageSource } from "./slippage.js";
9
9
  import type { AppliedBuilderFee } from "./builder-fee.js";
10
- export type VenueId = "pacifica" | "hyperliquid" | "lighter" | "aster" | "lighter-rh" | "ondo" | "arcus" | "qfex";
10
+ export type VenueId = "pacifica" | "hyperliquid" | "lighter" | "aster" | "lighter-rh" | "ondo" | "arcus" | "qfex" | "risex" | "txflow" | "variational" | "grvt" | "extended";
11
11
  export type LegacyVenueId = "pacifica" | "hyperliquid" | "lighter" | "aster";
12
12
  export declare const VENUE_IDS: readonly VenueId[];
13
13
  export declare const LEGACY_VENUE_IDS: readonly LegacyVenueId[];
@@ -48,7 +48,7 @@ export interface VenueMarket {
48
48
  /**
49
49
  * Currency prices are quoted in ("USD", "KRW", ...); null = this venue's data path does not
50
50
  * record it. A non-USD quote is never traded through perp-cli (sizing, risk limits and
51
- * notionals assume USD) — see QFEX, design §14.12 #7.
51
+ * notionals assume USD) — see QFEX,.
52
52
  */
53
53
  quoteAsset: string | null;
54
54
  markPrice: string;
@@ -111,7 +111,7 @@ export interface OrderRequest {
111
111
  /** Required when the venue's orderAck is "async". */
112
112
  clientOrderId: string | null;
113
113
  /**
114
- * Resolved slippage bound (resolveSlippage(), design §13.10 #1). Required for
114
+ * Resolved slippage bound (resolveSlippage()). Required for
115
115
  * type "market"; adapters whose stopMarket / TP-SL legs are protection-priced
116
116
  * (Arcus) require it there too. null only for orders that carry no market leg.
117
117
  */
@@ -138,13 +138,12 @@ export interface OrderResult {
138
138
  /** Filled base size; null = the venue response does not report it (never "0" as a stand-in). */
139
139
  filledSize: string | null;
140
140
  avgPrice: string | null;
141
- /** How the venue adapter actually executed the request (design §13.1 #3, §13.10 #14). */
141
+ /** How the venue adapter actually executed the request. */
142
142
  execution: OrderExecution;
143
143
  venueRaw: unknown;
144
144
  }
145
145
  /**
146
- * Transparent record of the venue-specific handling behind a uniform order
147
- * (design §13.1 #3, §13.2, §13.10 #14). Every field is required; a value the
146
+ * Transparent record of the venue-specific handling behind a uniform order. Every field is required; a value the
148
147
  * venue / adapter did not report is null (SSOT Rule #2), never a guess.
149
148
  */
150
149
  export interface OrderExecution {
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Venue-level data contracts (docs/DESIGN-venue-abstraction.md §10).
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) {
@@ -2,12 +2,13 @@
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
- * See docs/DESIGN-venue-abstraction.md §10. One adapter class may serve several
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 { PerpError } from "../errors.js";
11
12
  import type { ExchangeAdapter } from "./interface.js";
12
13
  import type { SpotAdapter } from "./spot-interface.js";
13
14
  import type { FundingRateBasis, LegacyVenueId, Network, TimeInForce, VenueFundingRate, VenueId, VenueMarket } from "./venue-types.js";
@@ -127,11 +128,29 @@ export interface VenueCreateContext {
127
128
  export declare const READ_ONLY_CREDENTIALS: VenueCredentials;
128
129
  export type VenueRole = "marketData" | "account" | "trading";
129
130
  export type SymbolIdentity = "baseTicker" | "explicit";
131
+ /**
132
+ * How perp-cli reaches the venue. "official" = a documented public API. "unofficial" = an
133
+ * undocumented API (e.g. the one the venue's own web app uses) that may change or be blocked
134
+ * without notice; every user-facing surface (JSON `venueApi` / `apiStatus`, human footnotes,
135
+ * MCP output, the landing screen, the skill docs) labels data read through it.
136
+ */
137
+ export type VenueApiStatus = {
138
+ kind: "official";
139
+ } | {
140
+ kind: "unofficial";
141
+ note: string;
142
+ docs: string | null;
143
+ };
144
+ export declare const OFFICIAL_API: VenueApiStatus;
145
+ /** "official" | "unofficial" — the per-row label for mixed-venue tables and JSON. */
146
+ export declare function apiStatusOf(venue: VenueId): VenueApiStatus["kind"];
147
+ /** Human footnote for an unofficial-API venue; null for an official one. */
148
+ export declare function unofficialApiNote(def: VenueDefinition): string | null;
130
149
  export interface VenueDefinition {
131
150
  id: VenueId;
132
151
  aliases: readonly string[];
133
152
  displayName: string;
134
- family: "pacifica" | "hyperliquid" | "lighter" | "aster" | "ondo" | "arcus" | "qfex";
153
+ family: "pacifica" | "hyperliquid" | "lighter" | "aster" | "ondo" | "arcus" | "qfex" | "risex" | "txflow" | "variational" | "grvt" | "extended";
135
154
  masterKeyKind: MasterKeyKind;
136
155
  agentKeyKind: AgentKeyKind;
137
156
  secretStorage: SecretStorage;
@@ -143,12 +162,22 @@ export interface VenueDefinition {
143
162
  settlementIntervalHours: number | "perMarket";
144
163
  /**
145
164
  * false = the unit / meaning of the venue's published funding rate is not verified against
146
- * settled funding payments yet (design §14.12 #6). Such a venue is left out of cross-venue
165
+ * settled funding payments yet. Such a venue is left out of cross-venue
147
166
  * funding comparison (fetchVenuesRates drops it unless includeUnverified is passed), its
148
167
  * adapter reports fundingRate null to bots, and funding-arb strategies refuse it as a leg.
149
168
  */
150
169
  fundingVerified: boolean;
151
170
  roles: readonly VenueRole[];
171
+ /**
172
+ * Roles that are implemented but switched off in this release (phased venue rollout, e.g.
173
+ * RISEx trading in 0.15.0). They are NOT in `roles`; roleUnsupported() uses `reason` /
174
+ * `remediation` for them. null = nothing held back.
175
+ */
176
+ gatedRoles: {
177
+ roles: readonly VenueRole[];
178
+ reason: string;
179
+ remediation: string;
180
+ } | null;
152
181
  /** null when the venue has no trading role. */
153
182
  capabilities: VenueCapabilities | null;
154
183
  rateLimit: RateLimitPolicy;
@@ -159,11 +188,10 @@ export interface VenueDefinition {
159
188
  publicData: VenuePublicData;
160
189
  /** null when the venue cannot produce an adapter (public data only). */
161
190
  create: ((ctx: VenueCreateContext) => Promise<ExchangeAdapter>) | null;
162
- /** null = venue has no spot trading (§12). */
191
+ /** null = venue has no spot trading. */
163
192
  spot: VenueSpotDefinition | null;
164
193
  /**
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
194
+ * Largest slippage bound (bps) a market-type order may carry on this venue. resolveSlippage() rejects anything above it with
167
195
  * INVALID_PARAMS before sending — never clamps.
168
196
  */
169
197
  maxSlippageBps: number;
@@ -173,6 +201,13 @@ export interface VenueDefinition {
173
201
  * settings.builder.<venue> and the approval is confirmed.
174
202
  */
175
203
  builderFee: BuilderFeeCapability | null;
204
+ /** Official documented API, or an unofficial one that is labelled everywhere (VenueApiStatus). */
205
+ apiStatus: VenueApiStatus;
206
+ /**
207
+ * Remediation of every role refusal (roleUnsupported) on a venue that is read-only in this
208
+ * version (Variational, GRVT, Extended). Absent = the generic market-data-only remediation.
209
+ */
210
+ readOnlyRemediation?: string;
176
211
  }
177
212
  export declare function registerVenue(def: VenueDefinition): void;
178
213
  /** Resolve a venue id or alias. Throws UNKNOWN_VENUE. */
@@ -182,6 +217,14 @@ export declare function getVenue(nameOrAlias: string): VenueDefinition;
182
217
  export declare function listVenues(): VenueDefinition[];
183
218
  export declare function listVenuesWithRole(role: VenueRole): VenueDefinition[];
184
219
  export declare function networkConfig(def: VenueDefinition, net: Network): VenueNetworkConfig;
220
+ /**
221
+ * The uniform refusal for any account / trading / funds / agent / builder / bot action on a venue
222
+ * whose roles do not include `role` (a market-data-only venue such as TxFlow). Raised before any
223
+ * credential is resolved or any request is sent.
224
+ */
225
+ export declare function roleUnsupported(def: VenueDefinition, feature: string, role?: VenueRole): PerpError;
226
+ /** Throws roleUnsupported() unless the venue has `role`. */
227
+ export declare function requireRole(def: VenueDefinition, role: VenueRole, feature: string): void;
185
228
  export declare function requireCapabilities(def: VenueDefinition): VenueCapabilities;
186
229
  /**
187
230
  * A venue reference as it appears in stored data and funding keys:
@@ -194,7 +237,7 @@ export interface VenueRef {
194
237
  export declare function parseVenueRef(ref: string): VenueRef;
195
238
  /**
196
239
  * Funding basis for a venue ref. HIP-3 dexes use the Hyperliquid core basis
197
- * (current behavior preserved; see design §10.1 — verify against live data).
240
+ * (current behavior preserved; verify against live data).
198
241
  * Throws for venues whose basis is per-market; callers must use the market's value.
199
242
  */
200
243
  export declare function fundingBasisOf(ref: string): FundingRateBasis;
@@ -202,7 +245,7 @@ export declare function settlementIntervalOf(ref: string): number;
202
245
  /**
203
246
  * Refuse a venue whose funding rate is unverified (VenueDefinition.fundingVerified false) as a
204
247
  * leg of a funding strategy (arb scan ranking, arb exec / auto, funding bots). Uniform
205
- * CAPABILITY_UNSUPPORTED (design §13.6, §14.12 #6). Unknown venue refs throw UNKNOWN_VENUE.
248
+ * CAPABILITY_UNSUPPORTED. Unknown venue refs throw UNKNOWN_VENUE.
206
249
  */
207
250
  export declare function assertFundingArbVenue(ref: string): void;
208
251
  export declare function isLegacyVenue(def: VenueDefinition): def is VenueDefinition & {