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.
Files changed (363) hide show
  1. package/README.ja.md +243 -0
  2. package/README.ko.md +243 -0
  3. package/README.md +138 -405
  4. package/README.zh.md +243 -0
  5. package/dist/agent-wallet/store.d.ts +3 -2
  6. package/dist/agent-wallet/store.js +2 -2
  7. package/dist/api/public/hyperliquid.js +4 -0
  8. package/dist/api/public/lighter.d.ts +5 -0
  9. package/dist/api/public/lighter.js +5 -0
  10. package/dist/api/public/pacifica.js +85 -15
  11. package/dist/api/public/urls.d.ts +2 -0
  12. package/dist/api/public/urls.js +3 -0
  13. package/dist/arb/sizing.js +4 -2
  14. package/dist/arb/state.d.ts +6 -0
  15. package/dist/arb/utils.d.ts +1 -2
  16. package/dist/arb/utils.js +1 -2
  17. package/dist/balance-by-address.d.ts +39 -0
  18. package/dist/balance-by-address.js +44 -0
  19. package/dist/bot/apex/pulse.js +3 -0
  20. package/dist/bot/apex/radar.js +2 -1
  21. package/dist/bot/engine.d.ts +10 -1
  22. package/dist/bot/engine.js +14 -6
  23. package/dist/bot/indicators.d.ts +7 -0
  24. package/dist/bot/indicators.js +16 -0
  25. package/dist/bot/perp-order.d.ts +4 -4
  26. package/dist/bot/perp-order.js +5 -5
  27. package/dist/bot/quarantine.d.ts +1 -1
  28. package/dist/bot/quarantine.js +1 -1
  29. package/dist/bot/quote-rules.js +1 -1
  30. package/dist/bot/strategies/apex-strategy.js +3 -2
  31. package/dist/bot/strategies/engine-mm.js +6 -5
  32. package/dist/bot/strategies/funding-arb-strategy.js +12 -5
  33. package/dist/bot/strategies/funding-arb-utils.d.ts +15 -3
  34. package/dist/bot/strategies/funding-arb-utils.js +53 -20
  35. package/dist/bot/strategies/funding-auto.js +2 -2
  36. package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
  37. package/dist/bot/strategies/liquidation-mm.js +8 -2
  38. package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
  39. package/dist/bot/strategies/momentum-breakout.js +11 -3
  40. package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +1 -1
  41. package/dist/bot/strategies/spot-perp-arb-strategy.js +6 -4
  42. package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
  43. package/dist/bot/strategies/spot-perp-executor.js +3 -3
  44. package/dist/bot/strategy-types.d.ts +2 -1
  45. package/dist/bot/tui/BotDashboard.d.ts +1 -1
  46. package/dist/bot/tui/BotDashboard.js +1 -1
  47. package/dist/cache.js +12 -6
  48. package/dist/cli-program.d.ts +15 -0
  49. package/dist/cli-program.js +126 -6
  50. package/dist/commands/account.js +168 -24
  51. package/dist/commands/agent-import.d.ts +7 -0
  52. package/dist/commands/agent-import.js +30 -1
  53. package/dist/commands/agent.d.ts +6 -3
  54. package/dist/commands/agent.js +160 -12
  55. package/dist/commands/arb/index.js +23 -81
  56. package/dist/commands/arb-auto.d.ts +24 -0
  57. package/dist/commands/arb-auto.js +175 -97
  58. package/dist/commands/arb.d.ts +20 -0
  59. package/dist/commands/arb.js +107 -163
  60. package/dist/commands/backtest.js +23 -21
  61. package/dist/commands/bot.js +1 -1
  62. package/dist/commands/builder.js +1 -1
  63. package/dist/commands/funds.js +113 -3
  64. package/dist/commands/init.d.ts +1 -1
  65. package/dist/commands/init.js +27 -7
  66. package/dist/commands/manage.js +23 -21
  67. package/dist/commands/market.d.ts +48 -0
  68. package/dist/commands/market.js +395 -100
  69. package/dist/commands/options.d.ts +14 -0
  70. package/dist/commands/options.js +151 -0
  71. package/dist/commands/pacifica-earn.d.ts +5 -0
  72. package/dist/commands/pacifica-earn.js +455 -0
  73. package/dist/commands/pacifica-orders.d.ts +14 -0
  74. package/dist/commands/pacifica-orders.js +142 -0
  75. package/dist/commands/portfolio.d.ts +16 -0
  76. package/dist/commands/portfolio.js +83 -55
  77. package/dist/commands/rebalance.d.ts +1 -0
  78. package/dist/commands/rebalance.js +15 -0
  79. package/dist/commands/risk.d.ts +11 -2
  80. package/dist/commands/risk.js +39 -44
  81. package/dist/commands/trade.d.ts +1 -0
  82. package/dist/commands/trade.js +67 -62
  83. package/dist/commands/wallet.js +1 -1
  84. package/dist/config.d.ts +2 -2
  85. package/dist/config.js +15 -4
  86. package/dist/constants.js +6 -0
  87. package/dist/cross-chain-margin.d.ts +3 -1
  88. package/dist/cross-chain-margin.js +4 -2
  89. package/dist/dashboard/server.js +3 -3
  90. package/dist/dashboard/ui.js +9 -6
  91. package/dist/errors.d.ts +29 -1
  92. package/dist/errors.js +35 -3
  93. package/dist/exchanges/address.d.ts +8 -0
  94. package/dist/exchanges/address.js +47 -0
  95. package/dist/exchanges/arcus/adapter.d.ts +16 -12
  96. package/dist/exchanges/arcus/adapter.js +45 -18
  97. package/dist/exchanges/arcus/agent.d.ts +1 -1
  98. package/dist/exchanges/arcus/agent.js +1 -1
  99. package/dist/exchanges/arcus/client.js +1 -1
  100. package/dist/exchanges/arcus/public.js +3 -2
  101. package/dist/exchanges/arcus/signing.d.ts +2 -3
  102. package/dist/exchanges/arcus/signing.js +1 -1
  103. package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
  104. package/dist/exchanges/arcus/spot-chain.js +1 -1
  105. package/dist/exchanges/arcus/spot-router.d.ts +1 -1
  106. package/dist/exchanges/arcus/spot-router.js +2 -2
  107. package/dist/exchanges/arcus/spot.d.ts +2 -3
  108. package/dist/exchanges/arcus/spot.js +2 -3
  109. package/dist/exchanges/aster-spot.d.ts +3 -3
  110. package/dist/exchanges/aster-spot.js +6 -9
  111. package/dist/exchanges/aster-typed-data.d.ts +1 -1
  112. package/dist/exchanges/aster-typed-data.js +1 -1
  113. package/dist/exchanges/aster.d.ts +30 -10
  114. package/dist/exchanges/aster.js +153 -53
  115. package/dist/exchanges/builder-fee.d.ts +1 -1
  116. package/dist/exchanges/builder-fee.js +13 -2
  117. package/dist/exchanges/canonical-symbols.d.ts +8 -2
  118. package/dist/exchanges/canonical-symbols.js +46 -2
  119. package/dist/exchanges/capabilities.d.ts +2 -0
  120. package/dist/exchanges/capabilities.js +2 -1
  121. package/dist/exchanges/derive/client.d.ts +37 -0
  122. package/dist/exchanges/derive/client.js +135 -0
  123. package/dist/exchanges/derive/options.d.ts +73 -0
  124. package/dist/exchanges/derive/options.js +379 -0
  125. package/dist/exchanges/extended/adapter.d.ts +96 -0
  126. package/dist/exchanges/extended/adapter.js +241 -0
  127. package/dist/exchanges/extended/client.d.ts +38 -0
  128. package/dist/exchanges/extended/client.js +88 -0
  129. package/dist/exchanges/extended/public.d.ts +133 -0
  130. package/dist/exchanges/extended/public.js +436 -0
  131. package/dist/exchanges/grvt/adapter.d.ts +100 -0
  132. package/dist/exchanges/grvt/adapter.js +283 -0
  133. package/dist/exchanges/grvt/client.d.ts +25 -0
  134. package/dist/exchanges/grvt/client.js +86 -0
  135. package/dist/exchanges/grvt/public.d.ts +151 -0
  136. package/dist/exchanges/grvt/public.js +438 -0
  137. package/dist/exchanges/hyperliquid-outcome.d.ts +2 -0
  138. package/dist/exchanges/hyperliquid-outcome.js +29 -1
  139. package/dist/exchanges/hyperliquid-spot.d.ts +9 -1
  140. package/dist/exchanges/hyperliquid-spot.js +24 -16
  141. package/dist/exchanges/hyperliquid.d.ts +199 -22
  142. package/dist/exchanges/hyperliquid.js +550 -202
  143. package/dist/exchanges/index.d.ts +1 -1
  144. package/dist/exchanges/index.js +1 -1
  145. package/dist/exchanges/interface.d.ts +157 -13
  146. package/dist/exchanges/interface.js +47 -0
  147. package/dist/exchanges/ioc-outcome.d.ts +1 -1
  148. package/dist/exchanges/ioc-outcome.js +1 -1
  149. package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
  150. package/dist/exchanges/legacy-trading-v2.js +7 -2
  151. package/dist/exchanges/lighter-public.d.ts +67 -12
  152. package/dist/exchanges/lighter-public.js +250 -82
  153. package/dist/exchanges/lighter-signer.d.ts +1 -1
  154. package/dist/exchanges/lighter-signer.js +1 -1
  155. package/dist/exchanges/lighter-spot.d.ts +10 -5
  156. package/dist/exchanges/lighter-spot.js +105 -97
  157. package/dist/exchanges/lighter.d.ts +80 -21
  158. package/dist/exchanges/lighter.js +312 -105
  159. package/dist/exchanges/ondo/adapter.d.ts +38 -13
  160. package/dist/exchanges/ondo/adapter.js +86 -27
  161. package/dist/exchanges/ondo/agent.d.ts +1 -1
  162. package/dist/exchanges/ondo/agent.js +1 -1
  163. package/dist/exchanges/ondo/client.d.ts +1 -1
  164. package/dist/exchanges/ondo/client.js +2 -2
  165. package/dist/exchanges/ondo/public.d.ts +35 -3
  166. package/dist/exchanges/ondo/public.js +75 -32
  167. package/dist/exchanges/ondo/spot.d.ts +2 -2
  168. package/dist/exchanges/ondo/spot.js +3 -3
  169. package/dist/exchanges/options-interface.d.ts +158 -0
  170. package/dist/exchanges/options-interface.js +17 -0
  171. package/dist/exchanges/options-venues.d.ts +12 -0
  172. package/dist/exchanges/options-venues.js +35 -0
  173. package/dist/exchanges/outcome-interface.d.ts +4 -0
  174. package/dist/exchanges/pacifica-atomic.d.ts +211 -0
  175. package/dist/exchanges/pacifica-atomic.js +440 -0
  176. package/dist/exchanges/pacifica-decimal.d.ts +10 -0
  177. package/dist/exchanges/pacifica-decimal.js +51 -0
  178. package/dist/exchanges/pacifica-presigned.d.ts +53 -0
  179. package/dist/exchanges/pacifica-presigned.js +141 -0
  180. package/dist/exchanges/pacifica-print.d.ts +200 -0
  181. package/dist/exchanges/pacifica-print.js +417 -0
  182. package/dist/exchanges/pacifica-spot.d.ts +8 -2
  183. package/dist/exchanges/pacifica-spot.js +26 -18
  184. package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
  185. package/dist/exchanges/pacifica-typed-data.js +1 -1
  186. package/dist/exchanges/pacifica-vaults.d.ts +130 -0
  187. package/dist/exchanges/pacifica-vaults.js +277 -0
  188. package/dist/exchanges/pacifica.d.ts +108 -10
  189. package/dist/exchanges/pacifica.js +428 -171
  190. package/dist/exchanges/precision.d.ts +3 -1
  191. package/dist/exchanges/precision.js +9 -2
  192. package/dist/exchanges/public-balances.d.ts +25 -0
  193. package/dist/exchanges/public-balances.js +158 -0
  194. package/dist/exchanges/public-positions.d.ts +43 -0
  195. package/dist/exchanges/public-positions.js +154 -0
  196. package/dist/exchanges/qfex/adapter.d.ts +69 -13
  197. package/dist/exchanges/qfex/adapter.js +102 -17
  198. package/dist/exchanges/qfex/agent.d.ts +1 -1
  199. package/dist/exchanges/qfex/agent.js +1 -1
  200. package/dist/exchanges/qfex/client.d.ts +1 -1
  201. package/dist/exchanges/qfex/client.js +1 -1
  202. package/dist/exchanges/qfex/errors.d.ts +1 -1
  203. package/dist/exchanges/qfex/errors.js +2 -2
  204. package/dist/exchanges/qfex/public.d.ts +65 -12
  205. package/dist/exchanges/qfex/public.js +131 -25
  206. package/dist/exchanges/qfex/signing.d.ts +2 -2
  207. package/dist/exchanges/qfex/signing.js +4 -5
  208. package/dist/exchanges/qfex/ws-transport.d.ts +3 -3
  209. package/dist/exchanges/qfex/ws-transport.js +3 -3
  210. package/dist/exchanges/rate-limiter.d.ts +1 -1
  211. package/dist/exchanges/rate-limiter.js +5 -2
  212. package/dist/exchanges/registry.d.ts +1 -1
  213. package/dist/exchanges/registry.js +1 -1
  214. package/dist/exchanges/risex/adapter.d.ts +160 -0
  215. package/dist/exchanges/risex/adapter.js +842 -0
  216. package/dist/exchanges/risex/agent.d.ts +154 -0
  217. package/dist/exchanges/risex/agent.js +384 -0
  218. package/dist/exchanges/risex/client.d.ts +47 -0
  219. package/dist/exchanges/risex/client.js +174 -0
  220. package/dist/exchanges/risex/nonce.d.ts +43 -0
  221. package/dist/exchanges/risex/nonce.js +212 -0
  222. package/dist/exchanges/risex/public.d.ts +64 -0
  223. package/dist/exchanges/risex/public.js +150 -0
  224. package/dist/exchanges/risex/session.d.ts +71 -0
  225. package/dist/exchanges/risex/session.js +171 -0
  226. package/dist/exchanges/risex/typed-data.d.ts +173 -0
  227. package/dist/exchanges/risex/typed-data.js +240 -0
  228. package/dist/exchanges/risex/units.d.ts +29 -0
  229. package/dist/exchanges/risex/units.js +104 -0
  230. package/dist/exchanges/slippage.d.ts +1 -1
  231. package/dist/exchanges/slippage.js +1 -1
  232. package/dist/exchanges/spot-factory.d.ts +3 -3
  233. package/dist/exchanges/spot-factory.js +8 -8
  234. package/dist/exchanges/spot-interface.d.ts +5 -5
  235. package/dist/exchanges/spot-interface.js +2 -2
  236. package/dist/exchanges/spot-result.d.ts +3 -3
  237. package/dist/exchanges/spot-result.js +3 -3
  238. package/dist/exchanges/trading-v2.d.ts +3 -3
  239. package/dist/exchanges/trading-v2.js +9 -6
  240. package/dist/exchanges/txflow/adapter.d.ts +61 -0
  241. package/dist/exchanges/txflow/adapter.js +150 -0
  242. package/dist/exchanges/txflow/client.d.ts +47 -0
  243. package/dist/exchanges/txflow/client.js +155 -0
  244. package/dist/exchanges/txflow/public.d.ts +87 -0
  245. package/dist/exchanges/txflow/public.js +355 -0
  246. package/dist/exchanges/unknown-symbol.d.ts +34 -0
  247. package/dist/exchanges/unknown-symbol.js +56 -0
  248. package/dist/exchanges/variational/adapter.d.ts +67 -0
  249. package/dist/exchanges/variational/adapter.js +137 -0
  250. package/dist/exchanges/variational/client.d.ts +21 -0
  251. package/dist/exchanges/variational/client.js +66 -0
  252. package/dist/exchanges/variational/public.d.ts +126 -0
  253. package/dist/exchanges/variational/public.js +430 -0
  254. package/dist/exchanges/venue-types.d.ts +52 -10
  255. package/dist/exchanges/venue-types.js +82 -11
  256. package/dist/exchanges/venue.d.ts +150 -11
  257. package/dist/exchanges/venue.js +126 -4
  258. package/dist/exchanges/venues/arcus.d.ts +1 -2
  259. package/dist/exchanges/venues/arcus.js +5 -4
  260. package/dist/exchanges/venues/aster.d.ts +2 -2
  261. package/dist/exchanges/venues/aster.js +6 -4
  262. package/dist/exchanges/venues/create-helpers.d.ts +1 -1
  263. package/dist/exchanges/venues/create-helpers.js +3 -3
  264. package/dist/exchanges/venues/extended.d.ts +21 -0
  265. package/dist/exchanges/venues/extended.js +96 -0
  266. package/dist/exchanges/venues/grvt.d.ts +30 -0
  267. package/dist/exchanges/venues/grvt.js +98 -0
  268. package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
  269. package/dist/exchanges/venues/hyperliquid.js +19 -8
  270. package/dist/exchanges/venues/index.d.ts +7 -2
  271. package/dist/exchanges/venues/index.js +8 -2
  272. package/dist/exchanges/venues/lighter-family.d.ts +7 -9
  273. package/dist/exchanges/venues/lighter-family.js +9 -11
  274. package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
  275. package/dist/exchanges/venues/lighter-rh.js +20 -8
  276. package/dist/exchanges/venues/lighter.d.ts +1 -1
  277. package/dist/exchanges/venues/lighter.js +18 -6
  278. package/dist/exchanges/venues/ondo.d.ts +1 -1
  279. package/dist/exchanges/venues/ondo.js +5 -3
  280. package/dist/exchanges/venues/pacifica.d.ts +2 -2
  281. package/dist/exchanges/venues/pacifica.js +15 -4
  282. package/dist/exchanges/venues/qfex.d.ts +1 -1
  283. package/dist/exchanges/venues/qfex.js +4 -2
  284. package/dist/exchanges/venues/risex-release.d.ts +14 -0
  285. package/dist/exchanges/venues/risex-release.js +14 -0
  286. package/dist/exchanges/venues/risex.d.ts +31 -0
  287. package/dist/exchanges/venues/risex.js +131 -0
  288. package/dist/exchanges/venues/txflow.d.ts +18 -0
  289. package/dist/exchanges/venues/txflow.js +77 -0
  290. package/dist/exchanges/venues/variational.d.ts +21 -0
  291. package/dist/exchanges/venues/variational.js +85 -0
  292. package/dist/execution-log.d.ts +1 -1
  293. package/dist/funding/legacy-public.d.ts +6 -4
  294. package/dist/funding/legacy-public.js +42 -41
  295. package/dist/funding/normalize.d.ts +1 -1
  296. package/dist/funding/normalize.js +1 -1
  297. package/dist/funding/rates.d.ts +95 -6
  298. package/dist/funding/rates.js +99 -19
  299. package/dist/index.js +7 -6
  300. package/dist/jobs.d.ts +1 -0
  301. package/dist/jobs.js +25 -1
  302. package/dist/landing.d.ts +2 -0
  303. package/dist/landing.js +1 -1
  304. package/dist/liquidity.d.ts +3 -1
  305. package/dist/liquidity.js +8 -4
  306. package/dist/mcp-pacifica-earn.d.ts +9 -0
  307. package/dist/mcp-pacifica-earn.js +64 -0
  308. package/dist/mcp-server.js +211 -68
  309. package/dist/pacifica/client.d.ts +69 -5
  310. package/dist/pacifica/client.js +181 -8
  311. package/dist/pacifica/rate-limit.d.ts +144 -0
  312. package/dist/pacifica/rate-limit.js +267 -0
  313. package/dist/pacifica/types/order.d.ts +2 -1
  314. package/dist/plan-executor.js +7 -2
  315. package/dist/positions-by-address.d.ts +34 -0
  316. package/dist/positions-by-address.js +46 -0
  317. package/dist/rebalance.js +1 -1
  318. package/dist/risk.d.ts +2 -1
  319. package/dist/risk.js +2 -4
  320. package/dist/settings.d.ts +32 -3
  321. package/dist/signer/ows-evm.d.ts +1 -1
  322. package/dist/signer/ows-evm.js +1 -1
  323. package/dist/smart-order.d.ts +1 -1
  324. package/dist/smart-order.js +1 -1
  325. package/dist/strategies/funding-arb.d.ts +1 -1
  326. package/dist/strategies/funding-arb.js +36 -63
  327. package/dist/trade-validator.js +2 -2
  328. package/dist/utils.d.ts +22 -3
  329. package/dist/utils.js +11 -3
  330. package/dist/venue-health.d.ts +40 -0
  331. package/dist/venue-health.js +75 -0
  332. package/dist/withdraw-gate.d.ts +1 -1
  333. package/dist/withdraw-gate.js +1 -1
  334. package/dist/ws/arcus-market.d.ts +9 -0
  335. package/dist/ws/arcus-market.js +9 -0
  336. package/dist/ws/aster-market.d.ts +33 -0
  337. package/dist/ws/aster-market.js +30 -0
  338. package/dist/ws/index.d.ts +980 -0
  339. package/dist/ws/index.js +4460 -0
  340. package/dist/ws/lighter-market.d.ts +14 -0
  341. package/dist/ws/lighter-market.js +13 -0
  342. package/dist/ws/node.d.ts +996 -0
  343. package/dist/ws/node.js +4554 -0
  344. package/dist/ws/pacifica-instrument.d.ts +8 -0
  345. package/dist/ws/pacifica-instrument.js +8 -0
  346. package/dist/ws/unclassified.d.ts +16 -0
  347. package/dist/ws/unclassified.js +43 -0
  348. package/dist/ws-cli/credentials.d.ts +62 -0
  349. package/dist/ws-cli/credentials.js +126 -0
  350. package/package.json +71 -10
  351. package/skills/perp-cli/SKILL.md +153 -36
  352. package/skills/perp-cli/package.json +15 -2
  353. package/skills/perp-cli/references/agent-operations.md +22 -12
  354. package/skills/perp-cli/references/commands.md +143 -17
  355. package/skills/perp-cli/references/strategies.md +38 -3
  356. package/skills/perp-cli/references/ws-library.md +523 -0
  357. package/skills/perp-cli/scripts/arb-monitor.sh +13 -10
  358. package/skills/perp-cli/scripts/funding-analysis.sh +57 -29
  359. package/skills/perp-cli/scripts/preflight.sh +27 -19
  360. package/skills/perp-cli/scripts/spot-perp-scan.sh +58 -38
  361. package/skills/perp-cli/scripts/validate-arb.sh +56 -39
  362. package/dist/cli-spec.d.ts +0 -46
  363. package/dist/cli-spec.js +0 -81
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Unknown-symbol handling shared by the adapters' public market-data reads.
3
+ *
4
+ * Several venues answer a read for a market they do not list with an empty success
5
+ * (Pacifica /trades, /kline, /funding_rate/history; Ondo /v1/perps/trades and candles; Aster
6
+ * /fapi/v3/fundingRate — all `200 []`, live 2026-10-14). An empty list must not stand for
7
+ * "no such market": after an empty answer the symbol is checked against the venue's own market
8
+ * directory (getMarkets) — SYMBOL_NOT_FOUND when it is not listed, the empty list when it is
9
+ * (a listed market with no trades / candles in the window is a real empty result).
10
+ */
11
+ import { PerpError } from "../errors.js";
12
+ import type { ExchangeMarketInfo } from "./interface.js";
13
+ /** SYMBOL_NOT_FOUND for `symbol` on `venue`, with the listing command as remediation. */
14
+ export declare function symbolNotFound(venue: string, symbol: string, message?: string): PerpError;
15
+ /**
16
+ * Directory match on the exact wire symbol (case-sensitive), a "-PERP" display suffix ignored on
17
+ * either side. Case-sensitive on purpose: the venues are (Pacifica `KPEPE` answers `200 []` where
18
+ * `kPEPE` has rows), so a casing miss must be SYMBOL_NOT_FOUND, never "no data". The adapter
19
+ * resolves the typed symbol to the venue's spelling before the request.
20
+ */
21
+ export declare function listsSymbol(markets: readonly Pick<ExchangeMarketInfo, "symbol">[], symbol: string): boolean;
22
+ /**
23
+ * `rows` as returned by the venue, unless they are empty and the market is not in the venue's
24
+ * market directory — then SYMBOL_NOT_FOUND. The directory is read only for an empty answer.
25
+ *
26
+ * `directorySymbol` is the symbol in the directory's form, derived by the adapter from what the
27
+ * user typed (Ondo "TSLA-USD.P" → "TSLA", Aster "BTCUSDT" → "BTC"); `symbol` is kept for messages.
28
+ * A failed directory read is re-thrown with its code (and so its retryable flag), saying what was
29
+ * being checked — never turned into SYMBOL_NOT_FOUND or an empty result.
30
+ */
31
+ export declare function nonEmptyOrListed<T>(adapter: {
32
+ readonly name: string;
33
+ getMarkets(): Promise<Pick<ExchangeMarketInfo, "symbol">[]>;
34
+ }, symbol: string, rows: T[], directorySymbol?: string): Promise<T[]>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Unknown-symbol handling shared by the adapters' public market-data reads.
3
+ *
4
+ * Several venues answer a read for a market they do not list with an empty success
5
+ * (Pacifica /trades, /kline, /funding_rate/history; Ondo /v1/perps/trades and candles; Aster
6
+ * /fapi/v3/fundingRate — all `200 []`, live 2026-10-14). An empty list must not stand for
7
+ * "no such market": after an empty answer the symbol is checked against the venue's own market
8
+ * directory (getMarkets) — SYMBOL_NOT_FOUND when it is not listed, the empty list when it is
9
+ * (a listed market with no trades / candles in the window is a real empty result).
10
+ */
11
+ import { PerpError, classifyError } from "../errors.js";
12
+ /** SYMBOL_NOT_FOUND for `symbol` on `venue`, with the listing command as remediation. */
13
+ export function symbolNotFound(venue, symbol, message) {
14
+ return new PerpError("SYMBOL_NOT_FOUND", message ?? `${venue} has no market ${symbol.toUpperCase()}`, {
15
+ exchange: venue,
16
+ remediation: `List markets: perp -e ${venue} market list`,
17
+ });
18
+ }
19
+ /**
20
+ * Directory match on the exact wire symbol (case-sensitive), a "-PERP" display suffix ignored on
21
+ * either side. Case-sensitive on purpose: the venues are (Pacifica `KPEPE` answers `200 []` where
22
+ * `kPEPE` has rows), so a casing miss must be SYMBOL_NOT_FOUND, never "no data". The adapter
23
+ * resolves the typed symbol to the venue's spelling before the request.
24
+ */
25
+ export function listsSymbol(markets, symbol) {
26
+ const norm = (s) => s.replace(/-PERP$/i, "");
27
+ const want = norm(symbol);
28
+ return markets.some(m => norm(m.symbol) === want);
29
+ }
30
+ /**
31
+ * `rows` as returned by the venue, unless they are empty and the market is not in the venue's
32
+ * market directory — then SYMBOL_NOT_FOUND. The directory is read only for an empty answer.
33
+ *
34
+ * `directorySymbol` is the symbol in the directory's form, derived by the adapter from what the
35
+ * user typed (Ondo "TSLA-USD.P" → "TSLA", Aster "BTCUSDT" → "BTC"); `symbol` is kept for messages.
36
+ * A failed directory read is re-thrown with its code (and so its retryable flag), saying what was
37
+ * being checked — never turned into SYMBOL_NOT_FOUND or an empty result.
38
+ */
39
+ export async function nonEmptyOrListed(adapter, symbol, rows, directorySymbol = symbol) {
40
+ if (rows.length > 0)
41
+ return rows;
42
+ let markets;
43
+ try {
44
+ markets = await adapter.getMarkets();
45
+ }
46
+ catch (e) {
47
+ const c = classifyError(e, adapter.name);
48
+ throw new PerpError(c.code, `${adapter.name}: ${symbol} returned no rows and the market list could not be read to check the symbol — ${c.message}`, {
49
+ exchange: adapter.name,
50
+ ...(c.remediation !== undefined ? { remediation: c.remediation } : {}),
51
+ });
52
+ }
53
+ if (!listsSymbol(markets, directorySymbol))
54
+ throw symbolNotFound(adapter.name, symbol);
55
+ return rows;
56
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Variational public-only adapter — OFFICIAL read-only API (venues/variational.ts).
3
+ *
4
+ * Markets, current funding terms and the OLP's indicative RFQ quotes come from the one stats
5
+ * payload (variational/public.ts). Variational is an RFQ venue with a single maker, so there is no
6
+ * order book, trade tape or candle feed in the API: getOrderbook / getRecentTrades / getKlines /
7
+ * getFundingHistory refuse with CAPABILITY_UNSUPPORTED (with the command that does work), and every
8
+ * account / trading method refuses before any request.
9
+ */
10
+ import type { CurrentFundingTerms, RfqQuotes, ExchangeAdapter, ExchangeBalance, ExchangeFundingPayment, ExchangeKline, ExchangeMarketInfo, ExchangeOrder, ExchangePosition, ExchangeTrade, ExchangePublicTrade, FundingHistoryRow } from "../interface.js";
11
+ import { type VenueDefinition } from "../venue.js";
12
+ import { type Network } from "../venue-types.js";
13
+ import { type VariationalSwapRate } from "./public.js";
14
+ /** Legacy market row plus the Variational-only fields every row carries in JSON output. */
15
+ export type VariationalMarketInfo = ExchangeMarketInfo & {
16
+ venueApi: "official";
17
+ ticker: string;
18
+ contractType: "perpetual" | "swap";
19
+ name: string | null;
20
+ /** "predicted" for a perpetual with a rate; null otherwise. */
21
+ fundingRateKind: "predicted" | null;
22
+ nextFundingTime: number | null;
23
+ /** USDC, single-sided (trader positions only; the OLP is the counterparty). */
24
+ openInterestUsd: {
25
+ long: string;
26
+ short: string;
27
+ };
28
+ swapFunding: {
29
+ last: VariationalSwapRate | null;
30
+ next: VariationalSwapRate | null;
31
+ } | null;
32
+ };
33
+ export declare class VariationalPublicAdapter implements ExchangeAdapter {
34
+ readonly venue: VenueDefinition;
35
+ readonly network: Network;
36
+ readonly name: "variational";
37
+ readonly aliases: readonly ["var", "omni"];
38
+ constructor(venue: VenueDefinition, network: Network);
39
+ private warn;
40
+ private listing;
41
+ getMarkets(): Promise<VariationalMarketInfo[]>;
42
+ /** Current funding terms of one market (the API publishes no funding history). */
43
+ getFundingTerms(symbol: string): Promise<CurrentFundingTerms>;
44
+ /** The OLP's indicative RFQ quotes (Variational has no order book). */
45
+ getRfqQuotes(symbol: string): Promise<RfqQuotes>;
46
+ getOrderbook(): Promise<{
47
+ bids: [string, string][];
48
+ asks: [string, string][];
49
+ }>;
50
+ getRecentTrades(): Promise<ExchangePublicTrade[]>;
51
+ getFundingHistory(): Promise<FundingHistoryRow[]>;
52
+ getKlines(): Promise<ExchangeKline[]>;
53
+ private refuse;
54
+ getBalance(): Promise<ExchangeBalance>;
55
+ getPositions(): Promise<ExchangePosition[]>;
56
+ getOpenOrders(): Promise<ExchangeOrder[]>;
57
+ getOrderHistory(): Promise<ExchangeOrder[]>;
58
+ getTradeHistory(): Promise<ExchangeTrade[]>;
59
+ getFundingPayments(): Promise<ExchangeFundingPayment[]>;
60
+ marketOrder(): Promise<unknown>;
61
+ limitOrder(): Promise<unknown>;
62
+ editOrder(): Promise<unknown>;
63
+ cancelOrder(): Promise<unknown>;
64
+ cancelAllOrders(): Promise<unknown>;
65
+ setLeverage(): Promise<unknown>;
66
+ stopOrder(): Promise<unknown>;
67
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Variational public-only adapter — OFFICIAL read-only API (venues/variational.ts).
3
+ *
4
+ * Markets, current funding terms and the OLP's indicative RFQ quotes come from the one stats
5
+ * payload (variational/public.ts). Variational is an RFQ venue with a single maker, so there is no
6
+ * order book, trade tape or candle feed in the API: getOrderbook / getRecentTrades / getKlines /
7
+ * getFundingHistory refuse with CAPABILITY_UNSUPPORTED (with the command that does work), and every
8
+ * account / trading method refuses before any request.
9
+ */
10
+ import { unsupported } from "../trading-v2.js";
11
+ import { roleUnsupported } from "../venue.js";
12
+ import { rateToHourly } from "../venue-types.js";
13
+ import { annualizeHourlyRate } from "../../funding/normalize.js";
14
+ import { currentPerpRate, fetchVariationalListings, fundingBasisOf, resolveVariationalListing, } from "./public.js";
15
+ const RFQ_NOTE = "Variational is an RFQ venue: the single market maker (OLP) quotes every trade. These are its INDICATIVE quotes at fixed notionals (may be cached up to 600 s), not order-book levels; the executed price is a firm quote at submit time.";
16
+ export class VariationalPublicAdapter {
17
+ venue;
18
+ network;
19
+ name = "variational";
20
+ aliases = ["var", "omni"];
21
+ constructor(venue, network) {
22
+ this.venue = venue;
23
+ this.network = network;
24
+ }
25
+ warn(msg) {
26
+ process.stderr.write(`${msg}\n`);
27
+ }
28
+ async listing(symbol) {
29
+ return resolveVariationalListing(await fetchVariationalListings(this.network), symbol);
30
+ }
31
+ // ── Market data ──
32
+ async getMarkets() {
33
+ return (await fetchVariationalListings(this.network)).map(l => {
34
+ const rate = currentPerpRate(l, m => this.warn(m));
35
+ const f = l.funding;
36
+ return {
37
+ symbol: l.symbol,
38
+ markPrice: l.markPrice,
39
+ indexPrice: null,
40
+ fundingRate: rate,
41
+ volume24h: l.volume24h,
42
+ // Base-unit OI is not published (USD, single-sided only — see openInterestUsd).
43
+ openInterest: null,
44
+ maxLeverage: null,
45
+ sizeDecimals: null,
46
+ stepSize: null,
47
+ fundingHours: f === null ? null : f.intervalSeconds / 3600,
48
+ marginTiers: null,
49
+ venueApi: "official",
50
+ ticker: l.ticker,
51
+ contractType: l.contractType,
52
+ name: l.name,
53
+ fundingRateKind: rate === null ? null : "predicted",
54
+ nextFundingTime: f === null ? null : f.nextFundingTime,
55
+ openInterestUsd: { long: l.longOpenInterestUsd, short: l.shortOpenInterestUsd },
56
+ swapFunding: f !== null && f.kind === "swap" ? { last: f.last, next: f.next } : null,
57
+ };
58
+ });
59
+ }
60
+ /** Current funding terms of one market (the API publishes no funding history). */
61
+ async getFundingTerms(symbol) {
62
+ const l = await this.listing(symbol);
63
+ const f = l.funding;
64
+ const rate = currentPerpRate(l, m => this.warn(m));
65
+ const base = { kind: "currentFunding", venue: "variational", symbol: l.symbol, ticker: l.ticker, contractType: l.contractType };
66
+ if (f === null) {
67
+ return { ...base, rateKind: null, intervalRate: null, intervalHours: null, hourlyRate: null, annualizedPct: null, venueAnnualizedRate: null, nextFundingTime: null, swap: null, note: "Variational publishes no funding terms for this market right now" };
68
+ }
69
+ const intervalHours = f.intervalSeconds / 3600;
70
+ if (f.kind === "swap") {
71
+ return {
72
+ ...base, rateKind: null, intervalRate: null, intervalHours, hourlyRate: null, annualizedPct: null, venueAnnualizedRate: null,
73
+ nextFundingTime: f.nextFundingTime, swap: { last: f.last, next: f.next },
74
+ note: "Swap: separate long / short rates paid once per trading day (positive long rate = longs pay, positive short rate = shorts receive); no single funding rate",
75
+ };
76
+ }
77
+ const hourlyRate = rate === null ? null : rateToHourly(Number(rate), fundingBasisOf(f));
78
+ return {
79
+ ...base,
80
+ rateKind: rate === null ? null : "predicted",
81
+ intervalRate: rate,
82
+ intervalHours,
83
+ hourlyRate,
84
+ annualizedPct: hourlyRate === null ? null : annualizeHourlyRate(hourlyRate),
85
+ venueAnnualizedRate: f.last.annualized,
86
+ nextFundingTime: f.nextFundingTime,
87
+ swap: null,
88
+ note: rate === null
89
+ ? "The published interval and annualized rates disagree; funding is not shown"
90
+ : `Current (predicted) rate for the ${intervalHours}h window ending at nextFundingTime; positive = longs pay. Variational publishes no funding history`,
91
+ };
92
+ }
93
+ /** The OLP's indicative RFQ quotes (Variational has no order book). */
94
+ async getRfqQuotes(symbol) {
95
+ const l = await this.listing(symbol);
96
+ return {
97
+ kind: "rfqQuotes",
98
+ venue: "variational",
99
+ symbol: l.symbol,
100
+ ticker: l.ticker,
101
+ markPrice: l.markPrice,
102
+ updatedAt: l.quotes === null ? null : l.quotes.updatedAt,
103
+ baseSpreadBps: l.baseSpreadBps,
104
+ quotes: l.quotes === null ? [] : l.quotes.tiers,
105
+ note: RFQ_NOTE,
106
+ };
107
+ }
108
+ async getOrderbook() {
109
+ throw unsupported("variational", "orderbook", "Variational is an RFQ venue with a single market maker (OLP); there is no order book", "See the OLP's indicative RFQ quotes at fixed notionals: perp -e variational market book <symbol>");
110
+ }
111
+ async getRecentTrades() {
112
+ throw unsupported("variational", "recentTrades", "Variational's public API publishes no trade tape", "Market stats (24h volume, open interest): perp -e variational market info <symbol>");
113
+ }
114
+ async getFundingHistory() {
115
+ throw unsupported("variational", "fundingHistory", "Variational's public API publishes no funding history", "Current funding terms: perp -e variational market funding <symbol>, or perp arb scan --rates");
116
+ }
117
+ async getKlines() {
118
+ throw unsupported("variational", "klines", "Variational's public API publishes no candles", "Current mark price: perp -e variational market info <symbol>");
119
+ }
120
+ // ── Account / trading: refused (market-data-only venue, no public trading API) ──
121
+ refuse(feature) {
122
+ throw roleUnsupported(this.venue, feature);
123
+ }
124
+ async getBalance() { return this.refuse("account"); }
125
+ async getPositions() { return this.refuse("account"); }
126
+ async getOpenOrders() { return this.refuse("account"); }
127
+ async getOrderHistory() { return this.refuse("account"); }
128
+ async getTradeHistory() { return this.refuse("account"); }
129
+ async getFundingPayments() { return this.refuse("account"); }
130
+ async marketOrder() { return this.refuse("trading"); }
131
+ async limitOrder() { return this.refuse("trading"); }
132
+ async editOrder() { return this.refuse("trading"); }
133
+ async cancelOrder() { return this.refuse("trading"); }
134
+ async cancelAllOrders() { return this.refuse("trading"); }
135
+ async setLeverage() { return this.refuse("trading"); }
136
+ async stopOrder() { return this.refuse("trading"); }
137
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Variational transport — the OFFICIAL read-only API (docs.variational.io/technical-documentation/api).
3
+ *
4
+ * One endpoint, `GET <rest>/metadata/v2/stats`, serves every market. Limits (documented): 10
5
+ * requests / 10 s per IP, 1000 / min globally, shared with the legacy v1 endpoint; over the limit
6
+ * the venue answers HTTP 429 with a JSON body whose `retry_after_seconds` says when to retry.
7
+ * Every request first takes a token from the per-process limiter (venue rateLimit policy).
8
+ */
9
+ import { PerpError } from "../../errors.js";
10
+ import type { RateLimitPolicy } from "../venue.js";
11
+ export declare const VARIATIONAL_HTTP_TIMEOUT_MS = 15000;
12
+ export declare const VARIATIONAL_STATS_PATH = "/metadata/v2/stats";
13
+ /** Payload drift in the documented API: an explicit EXCHANGE_ERROR, never a guessed value. */
14
+ export declare function variationalShapeError(what: string): PerpError;
15
+ export declare class VariationalClient {
16
+ private readonly restUrl;
17
+ private readonly rateLimit;
18
+ constructor(restUrl: string, rateLimit: RateLimitPolicy);
19
+ /** GET /metadata/v2/stats. Returns the parsed body; every error path is a PerpError. */
20
+ stats(): Promise<unknown>;
21
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Variational transport — the OFFICIAL read-only API (docs.variational.io/technical-documentation/api).
3
+ *
4
+ * One endpoint, `GET <rest>/metadata/v2/stats`, serves every market. Limits (documented): 10
5
+ * requests / 10 s per IP, 1000 / min globally, shared with the legacy v1 endpoint; over the limit
6
+ * the venue answers HTTP 429 with a JSON body whose `retry_after_seconds` says when to retry.
7
+ * Every request first takes a token from the per-process limiter (venue rateLimit policy).
8
+ */
9
+ import { PerpError, extractErrorMessage } from "../../errors.js";
10
+ import { acquireRate, rateLimitedError } from "../rate-limiter.js";
11
+ export const VARIATIONAL_HTTP_TIMEOUT_MS = 15_000;
12
+ export const VARIATIONAL_STATS_PATH = "/metadata/v2/stats";
13
+ /** Payload drift in the documented API: an explicit EXCHANGE_ERROR, never a guessed value. */
14
+ export function variationalShapeError(what) {
15
+ return new PerpError("EXCHANGE_ERROR", `Variational ${VARIATIONAL_STATS_PATH} shape changed: ${what}`, {
16
+ exchange: "variational",
17
+ remediation: "Variational's stats payload no longer matches its documentation (docs.variational.io/technical-documentation/api); perp-cli needs an update to read it",
18
+ });
19
+ }
20
+ /** `retry_after_seconds` from a 429 body (documented), else the Retry-After header. */
21
+ function retryAfterOf(res, parsed) {
22
+ const v = parsed?.retry_after_seconds;
23
+ if (typeof v === "number" && Number.isFinite(v))
24
+ return String(v);
25
+ return res.headers.get("retry-after");
26
+ }
27
+ export class VariationalClient {
28
+ restUrl;
29
+ rateLimit;
30
+ constructor(restUrl, rateLimit) {
31
+ this.restUrl = restUrl;
32
+ this.rateLimit = rateLimit;
33
+ }
34
+ /** GET /metadata/v2/stats. Returns the parsed body; every error path is a PerpError. */
35
+ async stats() {
36
+ await acquireRate("variational", this.rateLimit, VARIATIONAL_STATS_PATH);
37
+ let res;
38
+ try {
39
+ res = await fetch(`${this.restUrl}${VARIATIONAL_STATS_PATH}`, { signal: AbortSignal.timeout(VARIATIONAL_HTTP_TIMEOUT_MS) });
40
+ }
41
+ catch (e) {
42
+ throw new PerpError("EXCHANGE_UNREACHABLE", `Variational stats: ${extractErrorMessage(e)}`, { exchange: "variational" });
43
+ }
44
+ const text = await res.text().catch((e) => {
45
+ throw new PerpError("EXCHANGE_UNREACHABLE", `Variational stats: response body unreadable (${extractErrorMessage(e)})`, { exchange: "variational" });
46
+ });
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(text);
50
+ }
51
+ catch {
52
+ parsed = undefined;
53
+ }
54
+ if (res.status === 429)
55
+ throw rateLimitedError("variational", retryAfterOf(res, parsed), text);
56
+ if (!res.ok) {
57
+ throw new PerpError("EXCHANGE_ERROR", `Variational stats: HTTP ${res.status} ${text.slice(0, 200)}`, {
58
+ exchange: "variational", status: res.status,
59
+ remediation: "Variational's public stats API answered with an error; retry later",
60
+ });
61
+ }
62
+ if (parsed === undefined)
63
+ throw variationalShapeError("response is not JSON");
64
+ return parsed;
65
+ }
66
+ }
@@ -0,0 +1,126 @@
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 { type VenuePublicData } from "../venue.js";
31
+ import type { AssetClass, FundingRateBasis, Network, VenueFundingRate, VenueMarket } from "../venue-types.js";
32
+ import type { SwapFundingRates } from "../interface.js";
33
+ /** How long one stats payload serves every reader (the venue itself sends max-age=30). */
34
+ export declare const VARIATIONAL_STATS_TTL_MS = 5000;
35
+ /**
36
+ * Hint for a cross-venue lookup by a Variational ticker whose canonical symbol differs (arb scan
37
+ * --compare US500): where it is compared instead, or that it is never matched. null = no override.
38
+ */
39
+ export declare function canonicalHintFor(symbol: string): string | null;
40
+ export declare function looksTradFi(name: string | null): boolean;
41
+ export declare function warnOnce(warn: (msg: string) => void, msg: string): void;
42
+ export type QuoteTier = "base" | "size_1k" | "size_100k" | "size_1m";
43
+ export declare const QUOTE_TIERS: readonly QuoteTier[];
44
+ /** USD notional of each quote tier; "base" = the market's minimum order size (notional not published). */
45
+ export declare const QUOTE_TIER_NOTIONAL_USD: Readonly<Record<QuoteTier, string | null>>;
46
+ export interface VariationalQuote {
47
+ tier: QuoteTier;
48
+ notionalUsd: string | null;
49
+ bid: string;
50
+ ask: string;
51
+ }
52
+ export interface VariationalQuotes {
53
+ updatedAt: string;
54
+ tiers: VariationalQuote[];
55
+ }
56
+ export interface VariationalPerpRate {
57
+ annualized: string;
58
+ interval: string;
59
+ }
60
+ export type VariationalSwapRate = SwapFundingRates;
61
+ export type VariationalFunding = {
62
+ kind: "perpetual";
63
+ intervalSeconds: number;
64
+ nextFundingTime: number | null;
65
+ last: VariationalPerpRate;
66
+ next: VariationalPerpRate | null;
67
+ } | {
68
+ kind: "swap";
69
+ intervalSeconds: number;
70
+ nextFundingTime: number | null;
71
+ last: VariationalSwapRate | null;
72
+ next: VariationalSwapRate | null;
73
+ };
74
+ export interface VariationalListing {
75
+ ticker: string;
76
+ contractType: "perpetual" | "swap";
77
+ /** Display symbol: "BTC" for BTC-PERP, "XAU-SWAP" for a swap. */
78
+ symbol: string;
79
+ base: string;
80
+ name: string | null;
81
+ markPrice: string;
82
+ volume24h: string;
83
+ longOpenInterestUsd: string;
84
+ shortOpenInterestUsd: string;
85
+ funding: VariationalFunding | null;
86
+ baseSpreadBps: string | null;
87
+ quotes: VariationalQuotes | null;
88
+ assetClass: AssetClass;
89
+ canonical: string | null;
90
+ }
91
+ /**
92
+ * Parse the stats payload. A non-object payload / missing listings array is EXCHANGE_ERROR; a
93
+ * single malformed or unclassifiable listing is dropped with a warning naming it (never guessed).
94
+ */
95
+ export declare function parseVariationalStats(body: unknown, warn: (msg: string) => void): VariationalListing[];
96
+ /** interval_rate agrees with annualized_rate × interval / 365 d (both published, both rounded). */
97
+ export declare function perpRateConsistent(rate: VariationalPerpRate, intervalSeconds: number): boolean;
98
+ /** Basis of one perp's interval_rate: perHours(interval_seconds / 3600). */
99
+ export declare function fundingBasisOf(f: VariationalFunding): FundingRateBasis;
100
+ /**
101
+ * The current (predicted) funding rate per interval, or null: swaps (no single rate), no funding
102
+ * terms, or a perp whose two published representations disagree (unit drift — warned).
103
+ */
104
+ export declare function currentPerpRate(l: VariationalListing, warn: (msg: string) => void): string | null;
105
+ export interface VariationalPublicDeps {
106
+ stats: (net: Network) => Promise<unknown>;
107
+ cache: {
108
+ get: (key: string) => unknown;
109
+ set: (key: string, value: unknown, ttlMs: number) => void;
110
+ };
111
+ warn: (msg: string) => void;
112
+ }
113
+ export declare const statsCacheKey: (net: Network) => string;
114
+ /**
115
+ * The raw stats body: from the cache when fresh, else ONE request shared by every concurrent
116
+ * caller in this process. Only the raw JSON body is cached (JSON-safe); parsing runs per read.
117
+ */
118
+ export declare function fetchVariationalStatsRaw(net: Network, deps?: VariationalPublicDeps): Promise<unknown>;
119
+ export declare function fetchVariationalListings(net: Network, deps?: VariationalPublicDeps): Promise<VariationalListing[]>;
120
+ /** null when the listing has no funding terms (its interval — part of VenueMarket — is unknown). */
121
+ export declare function toVariationalVenueMarket(l: VariationalListing, warn: (msg: string) => void): VenueMarket | null;
122
+ export declare function fetchVariationalMarkets(net: Network, deps?: VariationalPublicDeps): Promise<VenueMarket[]>;
123
+ export declare function fetchVariationalFundingRates(net: Network, deps?: VariationalPublicDeps): Promise<VenueFundingRate[]>;
124
+ export declare const variationalPublicData: VenuePublicData;
125
+ /** Resolve a CLI symbol ("btc", "BTC-PERP", "xau-swap") against the parsed listings. */
126
+ export declare function resolveVariationalListing(listings: readonly VariationalListing[], input: string): VariationalListing;