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,959 @@
1
+ /**
2
+ * Venue ids and networks for perp-cli/ws. A local copy (src/ws imports only src/ws/**), kept in
3
+ * line with src/exchanges/venue-types.ts by a parity test. Ids are stable.
4
+ */
5
+ /**
6
+ * Every perp-cli venue id. "ondo" and "qfex" are included for type stability but have no perp-cli/ws
7
+ * codec until 0.15.1 (there is no `ondo` / `qfex` export to pass to createVenueStream yet).
8
+ */
9
+ type VenueId = "pacifica" | "hyperliquid" | "lighter" | "aster" | "lighter-rh" | "ondo" | "arcus" | "qfex";
10
+ declare const VENUE_IDS: readonly VenueId[];
11
+ type Network = "mainnet" | "testnet";
12
+
13
+ /**
14
+ * perp-cli/ws error leaf module (no imports). `WsErrorCode` ⊂ the CLI's `ErrorCode`; status and
15
+ * retryable match src/errors.ts ERROR_CODES (parity test). The CLI's classifyError accepts a
16
+ * WsError structurally (isPerpError).
17
+ */
18
+ declare const WS_ERROR_CODES: {
19
+ readonly WS_PROTOCOL_ERROR: {
20
+ readonly status: 502;
21
+ readonly retryable: false;
22
+ };
23
+ readonly WS_AUTH_FAILED: {
24
+ readonly status: 401;
25
+ readonly retryable: false;
26
+ };
27
+ readonly SUBSCRIPTION_LIMIT: {
28
+ readonly status: 400;
29
+ readonly retryable: false;
30
+ };
31
+ readonly STREAM_OVERFLOW: {
32
+ readonly status: 507;
33
+ readonly retryable: false;
34
+ };
35
+ readonly CAPABILITY_UNSUPPORTED: {
36
+ readonly status: 400;
37
+ readonly retryable: false;
38
+ };
39
+ readonly EXCHANGE_UNREACHABLE: {
40
+ readonly status: 503;
41
+ readonly retryable: true;
42
+ };
43
+ readonly INVALID_PARAMS: {
44
+ readonly status: 400;
45
+ readonly retryable: false;
46
+ };
47
+ readonly RATE_LIMITED: {
48
+ readonly status: 429;
49
+ readonly retryable: true;
50
+ };
51
+ readonly EXCHANGE_ERROR: {
52
+ readonly status: 502;
53
+ readonly retryable: true;
54
+ };
55
+ readonly SYMBOL_NOT_FOUND: {
56
+ readonly status: 404;
57
+ readonly retryable: false;
58
+ };
59
+ };
60
+ type WsErrorCode = keyof typeof WS_ERROR_CODES;
61
+ interface WsStructuredError {
62
+ code: WsErrorCode;
63
+ message: string;
64
+ status: number;
65
+ retryable: boolean;
66
+ exchange?: string;
67
+ remediation?: string;
68
+ details?: Record<string, unknown>;
69
+ }
70
+ declare class WsError extends Error {
71
+ readonly structured: WsStructuredError;
72
+ constructor(code: WsErrorCode, message: string, opts?: {
73
+ exchange?: string;
74
+ remediation?: string;
75
+ details?: Record<string, unknown>;
76
+ });
77
+ }
78
+ /** Structural check: a PerpError (CLI) or a WsError — anything carrying `structured.{code,message}`. */
79
+ declare function isPerpError(e: unknown): e is {
80
+ structured: {
81
+ code: string;
82
+ message: string;
83
+ };
84
+ };
85
+
86
+ /**
87
+ * perp-cli/ws public types (plan ws-0.14.4 §4). @experimental through 0.15.x (stable from 0.16.0 after consumer sign-off): breaking changes
88
+ * only in a minor bump; every event carries `schema: 1`.
89
+ *
90
+ * Null rules (§3): unknown = null. A field the venue never sent is null; a field the latest frame
91
+ * did not carry keeps its last value with a per-field `asOf`. `Dec` = the venue's JSON value as a
92
+ * string (a JSON string verbatim, a JSON number via String(value) — precision bounded by JSON
93
+ * parsing). The one documented exception is Lighter's WS funding, sent in PERCENT per hour and
94
+ * converted to a fraction by an exact decimal shift (no float).
95
+ */
96
+
97
+ type Dec = string;
98
+ interface WsLike {
99
+ readonly readyState: number;
100
+ send(data: string): void;
101
+ close(code?: number, reason?: string): void;
102
+ onopen: ((ev?: unknown) => void) | null;
103
+ onmessage: ((ev: {
104
+ data: unknown;
105
+ }) => void) | null;
106
+ onclose: ((ev: {
107
+ code: number;
108
+ reason: string;
109
+ wasClean?: boolean;
110
+ }) => void) | null;
111
+ onerror: ((ev: unknown) => void) | null;
112
+ }
113
+ type WsFactory = (url: string) => WsLike;
114
+ type FetchLike = (input: string, init?: {
115
+ method?: string;
116
+ headers?: Record<string, string>;
117
+ body?: string;
118
+ }) => Promise<{
119
+ ok: boolean;
120
+ status: number;
121
+ json(): Promise<unknown>;
122
+ }>;
123
+ interface CloseReason {
124
+ code: WsErrorCode | "CLIENT_CLOSE";
125
+ message: string;
126
+ wsCode: number | null;
127
+ remediation: string | null;
128
+ }
129
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
130
+ type ConnectionState = {
131
+ status: "idle";
132
+ } | {
133
+ status: "connecting";
134
+ attempt: number;
135
+ }
136
+ /** Several sockets (Pacifica): `sockets` live ones; `pending` = sockets still opening for the first time
137
+ * (just added, waiting on the new-connection budget, or retrying a failed first handshake) — their subscriptions are not ready yet. */
138
+ | {
139
+ status: "live";
140
+ since: number;
141
+ sockets: number;
142
+ pending?: number;
143
+ } | {
144
+ status: "stale";
145
+ since: number;
146
+ silentMs: number;
147
+ channel: string | null;
148
+ } | {
149
+ status: "reconnecting";
150
+ attempt: number;
151
+ inMs: number;
152
+ reason: CloseReason;
153
+ } | {
154
+ status: "closed";
155
+ reason: CloseReason;
156
+ };
157
+ type Canonicalize = (venue: VenueId, m: {
158
+ venueSymbol: string;
159
+ dex: string | null;
160
+ base: string;
161
+ quote: string | null;
162
+ }) => string | null;
163
+ type Source = "ws" | "snapshot" | `rest:${string}` | "venue-definition";
164
+ type RateKind = "lastSettled" | "accruing" | "predicted";
165
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
166
+ interface MarketDirectoryEntry {
167
+ venue: VenueId;
168
+ dex: string | null;
169
+ symbol: string;
170
+ venueSymbol: string;
171
+ base: string;
172
+ quote: string | null;
173
+ canonical: string | null;
174
+ rateIntervalHours: number | null;
175
+ settlementIntervalHours: number | null;
176
+ source: Source;
177
+ }
178
+ type MarketField = "mark" | "index" | "bestBid" | "bestAsk" | "last" | "rate" | "predictedRate" | "nextFundingTime" | "fundingInterval" | "openInterest" | "volume24hNotional";
179
+ interface FundingState {
180
+ rate: Dec | null;
181
+ rateKind: RateKind | null;
182
+ rateSource: Source | null;
183
+ rateIntervalHours: number | null;
184
+ settlementIntervalHours: number | null;
185
+ intervalSource: Source | null;
186
+ nextFundingTime: number | null;
187
+ /** Always kind "predicted". */
188
+ predictedRate: Dec | null;
189
+ /** QFEX out of hours → rate null + true. null when the venue has no such concept. */
190
+ marketClosed: boolean | null;
191
+ }
192
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
193
+ interface MarketUpdate {
194
+ type: "market";
195
+ schema: 1;
196
+ venue: VenueId;
197
+ dex: string | null;
198
+ symbol: string;
199
+ venueSymbol: string;
200
+ quote: string | null;
201
+ canonical: string | null;
202
+ mark: Dec | null;
203
+ index: Dec | null;
204
+ bestBid: Dec | null;
205
+ bestAsk: Dec | null;
206
+ last: Dec | null;
207
+ funding: FundingState;
208
+ openInterest: {
209
+ value: Dec | null;
210
+ unit: "base" | "usd" | null;
211
+ sides: "one" | "both" | null;
212
+ };
213
+ volume24hNotional: Dec | null;
214
+ venueTs: number | null;
215
+ recvTs: number;
216
+ source: Source;
217
+ changed: ReadonlyArray<MarketField>;
218
+ asOf: Partial<Record<MarketField, number>>;
219
+ }
220
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
221
+ interface BboUpdate {
222
+ type: "bbo";
223
+ schema: 1;
224
+ venue: VenueId;
225
+ dex: string | null;
226
+ symbol: string;
227
+ bid: Dec | null;
228
+ bidSize: Dec | null;
229
+ ask: Dec | null;
230
+ askSize: Dec | null;
231
+ venueTs: number | null;
232
+ recvTs: number;
233
+ }
234
+ interface BookUpdate {
235
+ type: "book";
236
+ schema: 1;
237
+ venue: VenueId;
238
+ symbol: string;
239
+ bids: [Dec, Dec][];
240
+ asks: [Dec, Dec][];
241
+ seq: string | null;
242
+ venueTs: number | null;
243
+ recvTs: number;
244
+ source: Source;
245
+ depth: number;
246
+ }
247
+ interface TradeUpdate {
248
+ type: "trade";
249
+ schema: 1;
250
+ venue: VenueId;
251
+ symbol: string;
252
+ price: Dec;
253
+ size: Dec;
254
+ side: "buy" | "sell" | null;
255
+ tradeId: string | null;
256
+ venueTs: number | null;
257
+ recvTs: number;
258
+ }
259
+ type UserChannel = "account" | "positions" | "orders" | "fills";
260
+ interface PositionRow {
261
+ symbol: string;
262
+ dex: string | null;
263
+ side: "long" | "short" | null;
264
+ /** Absolute size in base units. */
265
+ size: Dec | null;
266
+ entryPrice: Dec | null;
267
+ /** Mark price is not part of every venue's position stream; join MarketUpdate by symbol. */
268
+ markPrice: Dec | null;
269
+ positionValue: Dec | null;
270
+ unrealizedPnl: Dec | null;
271
+ liquidationPrice: Dec | null;
272
+ marginUsed: Dec | null;
273
+ leverage: Dec | null;
274
+ venueTs: number | null;
275
+ }
276
+ interface OrderRow {
277
+ orderId: string;
278
+ clientId: string | null;
279
+ symbol: string;
280
+ dex: string | null;
281
+ side: "buy" | "sell" | null;
282
+ type: string | null;
283
+ /** Venue status string, verbatim. */
284
+ status: string | null;
285
+ price: Dec | null;
286
+ size: Dec | null;
287
+ filled: Dec | null;
288
+ remaining: Dec | null;
289
+ reduceOnly: boolean | null;
290
+ createdAt: number | null;
291
+ updatedAt: number | null;
292
+ }
293
+ interface FillRow {
294
+ /** Venue fill key (HL tid, Pacifica h:i, Lighter trade_id, Arcus tradeId). With `side` and `orderId` it is the dedupe key. */
295
+ fillId: string;
296
+ /** The client order id of the filled order when the fill frame carries it (see the capability notes per venue). */
297
+ clientId: string | null;
298
+ symbol: string;
299
+ dex: string | null;
300
+ side: "buy" | "sell" | null;
301
+ price: Dec | null;
302
+ size: Dec | null;
303
+ /** Fee amount in `feeToken` (negative = rebate). */
304
+ fee: Dec | null;
305
+ feeToken: string | null;
306
+ closedPnl: Dec | null;
307
+ orderId: string | null;
308
+ liquidity: "maker" | "taker" | null;
309
+ time: number | null;
310
+ }
311
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
312
+ type UserUpdate = {
313
+ type: "account";
314
+ schema: 1;
315
+ venue: VenueId;
316
+ /** HL: one account per perp dex ("" native → null); other venues null. */
317
+ dex: string | null;
318
+ equity: Dec | null;
319
+ available: Dec | null;
320
+ marginUsed: Dec | null;
321
+ unrealizedPnl: Dec | null;
322
+ venueTs: number | null;
323
+ source: Source;
324
+ recvTs: number;
325
+ }
326
+ /** The full current position set (snapshot + merged deltas). snapshot = it came from (or coalesced) a venue snapshot. */
327
+ | {
328
+ type: "positions";
329
+ schema: 1;
330
+ venue: VenueId;
331
+ snapshot: boolean;
332
+ positions: PositionRow[];
333
+ source: Source;
334
+ recvTs: number;
335
+ }
336
+ /**
337
+ * The current OPEN order set (snapshot + merged deltas). `complete`: the set is known to be every open
338
+ * order — true once the venue sent a snapshot for every part of it; false for venues without an
339
+ * order snapshot (Pacifica: refetch over REST; the library cannot be told) and after a refetch gap.
340
+ */
341
+ | {
342
+ type: "orders";
343
+ schema: 1;
344
+ venue: VenueId;
345
+ snapshot: boolean;
346
+ complete: boolean;
347
+ orders: OrderRow[];
348
+ source: Source;
349
+ recvTs: number;
350
+ }
351
+ /** One live fill (de-duplicated in-library). */
352
+ | {
353
+ type: "fill";
354
+ schema: 1;
355
+ venue: VenueId;
356
+ snapshot: false;
357
+ fillId: string;
358
+ fill: FillRow;
359
+ source: Source;
360
+ recvTs: number;
361
+ }
362
+ /**
363
+ * A venue fill SNAPSHOT (HL / Arcus replay recent fills on every (re)subscribe; up to 2000 on HL) as ONE
364
+ * event: only fills not seen before, oldest venue order preserved. Never split, so it cannot overflow
365
+ * a subscription buffer. Empty snapshots (everything already seen) are not emitted.
366
+ */
367
+ | {
368
+ type: "fills";
369
+ schema: 1;
370
+ venue: VenueId;
371
+ snapshot: true;
372
+ fills: FillRow[];
373
+ source: Source;
374
+ recvTs: number;
375
+ };
376
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
377
+ type UserRef = {
378
+ venue: "hyperliquid";
379
+ address: `0x${string}`;
380
+ } | {
381
+ venue: "pacifica";
382
+ account: string;
383
+ } | {
384
+ venue: "lighter" | "lighter-rh";
385
+ accountIndex: number;
386
+ }
387
+ /** KNOWN GAP in 0.15.0: Aster user channels are not implemented (CAPABILITY_UNSUPPORTED); 0.15.1. */
388
+ | {
389
+ venue: "aster";
390
+ user: `0x${string}`;
391
+ signer: `0x${string}`;
392
+ }
393
+ /** subaccount null = the venue default account (0). */
394
+ | {
395
+ venue: "arcus";
396
+ address: `0x${string}`;
397
+ subaccount: number | null;
398
+ } | {
399
+ venue: "ondo";
400
+ keyId: string;
401
+ } | {
402
+ venue: "qfex";
403
+ publicKey: string;
404
+ accountId: string;
405
+ };
406
+ interface TypedData {
407
+ domain: Record<string, unknown>;
408
+ types: Record<string, Array<{
409
+ name: string;
410
+ type: string;
411
+ }>>;
412
+ primaryType: string;
413
+ message: Record<string, unknown>;
414
+ }
415
+ /**
416
+ * Async credentials; the library never holds keys (only short-lived tokens it is handed).
417
+ * @experimental 0.15.x (stable from 0.16.0 after consumer sign-off)
418
+ */
419
+ interface CredentialProvider {
420
+ user(): Promise<UserRef>;
421
+ /** Ondo login, QFEX auth (secret stays with the provider). Lowercase hex. */
422
+ hmacSha256?(message: string): Promise<string>;
423
+ /** Aster V3 USER_STREAM (API-wallet signer). */
424
+ signTypedData?(td: TypedData): Promise<`0x${string}`>;
425
+ /** Lighter auth token (a read-only `ro:` token is recommended in browsers). Re-called at 80 % of its TTL. */
426
+ authToken?(req: {
427
+ purpose: string;
428
+ maxTtlSec: number;
429
+ }): Promise<{
430
+ token: string;
431
+ expiresAt: number;
432
+ }>;
433
+ }
434
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
435
+ type StreamControl = {
436
+ type: "state";
437
+ venue: VenueId;
438
+ state: ConnectionState;
439
+ } | {
440
+ type: "gap";
441
+ venue: VenueId;
442
+ channel: string;
443
+ symbol: string | null;
444
+ expected: string | null;
445
+ got: string | null;
446
+ action: "resnapshot" | "refetch-required";
447
+ } | {
448
+ type: "error";
449
+ venue: VenueId;
450
+ code: WsErrorCode;
451
+ message: string;
452
+ remediation: string | null;
453
+ /** Redacted excerpt (≤ 200 chars) of the offending frame, or null. */
454
+ raw: string | null;
455
+ fatal: boolean;
456
+ /** Structured context, when the code has any (e.g. SYMBOL_NOT_FOUND: the missing canonicals). */
457
+ details?: StreamErrorDetails;
458
+ };
459
+ /**
460
+ * `details` of a `{ type: "error" }` control. SYMBOL_NOT_FOUND (non-fatal, kind:"allMarkets" with
461
+ * `canonicals`): `missing` = requested canonicals with no market on this venue (the others are
462
+ * subscribed); `suggestions` = per missing canonical, close directory canonicals (may be empty).
463
+ */
464
+ interface StreamErrorDetails {
465
+ missing?: string[];
466
+ suggestions?: Record<string, string[]>;
467
+ }
468
+ interface StreamLog {
469
+ ts: number;
470
+ venue: VenueId;
471
+ level: "info" | "warn" | "error";
472
+ event: string;
473
+ message: string;
474
+ details?: Record<string, unknown>;
475
+ }
476
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
477
+ interface VenueWsCapabilities {
478
+ /** true = works from a browser page (Origin/CORS checked, Phase 0); "in-page-secrets" = needs allowInPageSecrets. */
479
+ browser: boolean | "in-page-secrets";
480
+ networks: Record<Network, boolean>;
481
+ channels: {
482
+ allMarkets: boolean;
483
+ market: boolean;
484
+ bbo: boolean;
485
+ book: boolean;
486
+ trades: boolean;
487
+ user: boolean;
488
+ };
489
+ /** Market fields this venue publishes over WS (or via a labelled REST source). false → always null. */
490
+ fields: Record<Exclude<MarketField, "rate">, boolean>;
491
+ /** Why a field or channel is null / unsupported. */
492
+ notes: Record<string, string>;
493
+ }
494
+ /**
495
+ * All-market state, optionally narrowed (pass at most one of the two filters). With `canonicals`,
496
+ * the ones this venue lists are subscribed and the rest are reported once as a non-fatal
497
+ * `{ type: "error", code: "SYMBOL_NOT_FOUND", fatal: false, details: { missing, suggestions } }`;
498
+ * only when none exist does `ready` reject INVALID_PARAMS.
499
+ * - `symbols`: this venue's DIRECTORY symbols, exact and case-sensitive (`BTC` on hyperliquid, `BTCUSDT`
500
+ * on aster, `BTC-USD` on arcus) — see stream.markets();
501
+ * - `canonicals`: canonical names (`"BTC"`, HIP-3 `"xyz:TSLA"`) → every directory market whose canonical
502
+ * (opts.canonicalize, default USD-like quotes collapse) matches, e.g. aster BTCUSDT + BTCUSD1. Use it
503
+ * for the same subscription on several venues.
504
+ */
505
+ interface AllMarketsTarget {
506
+ kind: "allMarkets";
507
+ symbols?: string[];
508
+ canonicals?: string[];
509
+ }
510
+ type MarketTarget = AllMarketsTarget | {
511
+ kind: "user";
512
+ channels: UserChannel[];
513
+ } | {
514
+ kind: "market";
515
+ symbol: string;
516
+ channels: Array<"market" | "bbo" | "book" | "trades">;
517
+ };
518
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
519
+ interface VenueStreamOptions {
520
+ network?: Network;
521
+ /** hyperliquid: HIP-3 dexes to include. Default [] = native only. */
522
+ hip3Dexes?: string[] | "all";
523
+ /** Default: globalThis.WebSocket, resolved at call time (perp-cli/ws/node supplies `ws`). */
524
+ transport?: WsFactory;
525
+ /** Default: globalThis.fetch, resolved at call time. */
526
+ fetch?: FetchLike;
527
+ canonicalize?: Canonicalize;
528
+ /** Needed for kind:"user". */
529
+ credentials?: CredentialProvider;
530
+ /** Default false. Required for user channels whose auth needs a signing secret in the page (Aster/Ondo/QFEX, 0.15.1). */
531
+ allowInPageSecrets?: boolean;
532
+ /** ≤ venue limits. */
533
+ budget?: {
534
+ maxSubscriptions?: number;
535
+ maxSockets?: number;
536
+ newConnectionsPerMin?: number;
537
+ };
538
+ /** Overrides the per-venue default for every public channel. */
539
+ staleAfterMs?: number;
540
+ /** Default: document.visibilityState when present, else none. */
541
+ visibility?: {
542
+ hidden(): boolean;
543
+ onChange(cb: () => void): () => void;
544
+ } | null;
545
+ /** Default { maxAttempts: null, baseMs: 1000, maxMs: 30000 }. */
546
+ reconnect?: {
547
+ maxAttempts: number | null;
548
+ baseMs: number;
549
+ maxMs: number;
550
+ };
551
+ /** Async-iterator buffer per subscription (default 1024). */
552
+ buffer?: {
553
+ size: number;
554
+ };
555
+ /** Receives redacted entries only. */
556
+ logger?: (e: StreamLog) => void;
557
+ clock?: () => number;
558
+ }
559
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
560
+ interface Subscription<E> extends AsyncIterable<E | StreamControl> {
561
+ /** Unbuffered callback. Returns an unsubscribe-from-callback function. */
562
+ on(cb: (e: E | StreamControl) => void): () => void;
563
+ readonly status: "pending" | "active" | "rejected" | "closed";
564
+ /** Resolves once every wire channel is acknowledged; rejects with a WsError. */
565
+ readonly ready: Promise<void>;
566
+ /** The wire unsubscribe is sent only when the ref-count reaches 0. */
567
+ unsubscribe(): Promise<void>;
568
+ }
569
+ interface VenueStream {
570
+ readonly venue: VenueId;
571
+ readonly network: Network;
572
+ readonly state: ConnectionState;
573
+ readonly capabilities: VenueWsCapabilities;
574
+ subscribe(t: AllMarketsTarget): Subscription<MarketUpdate>;
575
+ subscribe(t: {
576
+ kind: "market";
577
+ symbol: string;
578
+ channels: Array<"market" | "bbo" | "book" | "trades">;
579
+ }): Subscription<MarketUpdate | BboUpdate | BookUpdate | TradeUpdate>;
580
+ subscribe(t: {
581
+ kind: "user";
582
+ channels: UserChannel[];
583
+ }): Subscription<UserUpdate>;
584
+ onState(cb: (s: ConnectionState) => void): () => void;
585
+ /** The market directory (REST bootstrap, loaded once). */
586
+ markets(): Promise<MarketDirectoryEntry[]>;
587
+ /** Venue `staleAfterMs` of the all-market channel (used by the funding comparator). */
588
+ readonly staleAfterMs: number;
589
+ close(): Promise<void>;
590
+ }
591
+ /** A wire subscription (one venue channel). `key` is unique per venue. */
592
+ interface WireSub {
593
+ key: string;
594
+ /** Silence beyond this → `stale`; 2× → forced reconnect. null = no stale check. */
595
+ staleAfterMs: number | null;
596
+ params: Record<string, unknown>;
597
+ /** Needs an auth token from CredentialProvider.authToken (sent with the subscribe). */
598
+ auth?: {
599
+ purpose: string;
600
+ maxTtlSec: number;
601
+ };
602
+ /** Per-socket channel group for venue caps (limits.perGroup), e.g. Pacifica "bbo" ≤ 20. */
603
+ group?: string;
604
+ /** Non-replayable channels: gap{action:"refetch-required"} after a reconnect / at subscribe. */
605
+ gaps?: {
606
+ onReconnect?: Array<"fills" | "orders">;
607
+ onSubscribe?: Array<"orders">;
608
+ };
609
+ }
610
+ /** A user-state change decoded from one frame. `scope` partitions snapshots (HL: per dex). */
611
+ type UserDelta = {
612
+ kind: "account";
613
+ snapshot: boolean;
614
+ dex: string | null;
615
+ equity?: Dec | null;
616
+ available?: Dec | null;
617
+ marginUsed?: Dec | null;
618
+ unrealizedPnl?: Dec | null;
619
+ venueTs: number | null;
620
+ }
621
+ /** snapshot: replace every row in `scope`; else upsert rows and remove `remove` keys (symbol). */
622
+ | {
623
+ kind: "positions";
624
+ snapshot: boolean;
625
+ scope: string;
626
+ rows: PositionRow[];
627
+ remove: string[];
628
+ }
629
+ /** snapshot: replace every open order in `scope`; else upsert rows and remove `remove` order ids. */
630
+ | {
631
+ kind: "orders";
632
+ snapshot: boolean;
633
+ scope: string;
634
+ rows: OrderRow[];
635
+ remove: string[];
636
+ } | {
637
+ kind: "fills";
638
+ snapshot: boolean;
639
+ fills: FillRow[];
640
+ };
641
+ /** A partial market patch: a key present = the frame carried it (its value may be null). */
642
+ interface MarketPatch {
643
+ symbol: string;
644
+ venueTs: number | null;
645
+ mark?: Dec | null;
646
+ index?: Dec | null;
647
+ bestBid?: Dec | null;
648
+ bestAsk?: Dec | null;
649
+ last?: Dec | null;
650
+ rate?: Dec | null;
651
+ predictedRate?: Dec | null;
652
+ nextFundingTime?: number | null;
653
+ openInterest?: Dec | null;
654
+ volume24hNotional?: Dec | null;
655
+ }
656
+ type DecodedEvent = {
657
+ t: "market";
658
+ wire: string;
659
+ patches: MarketPatch[];
660
+ }
661
+ /** seq: a venue ordering number for the wire's snapshots (frames below the last seen are stale and dropped; equal is applied). */
662
+ | {
663
+ t: "user";
664
+ wire: string;
665
+ deltas: UserDelta[];
666
+ seq?: number | null;
667
+ }
668
+ /** seq: a venue ordering number (only drops out-of-order/duplicate frames; never a gap signal). */
669
+ | {
670
+ t: "bbo";
671
+ wire: string;
672
+ symbol: string;
673
+ seq: number | null;
674
+ bid: Dec | null;
675
+ bidSize: Dec | null;
676
+ ask: Dec | null;
677
+ askSize: Dec | null;
678
+ venueTs: number | null;
679
+ } | {
680
+ t: "ack";
681
+ wire: string | null;
682
+ reqId: number | null;
683
+ unsubscribe: boolean;
684
+ } | {
685
+ t: "pong";
686
+ }
687
+ /** An allowlisted, documented control frame with nothing to do (e.g. "connected"). */
688
+ | {
689
+ t: "ignore";
690
+ what: string;
691
+ } | {
692
+ t: "error";
693
+ code: WsErrorCode;
694
+ message: string;
695
+ wire: string | null;
696
+ fatal: boolean;
697
+ remediation: string | null;
698
+ /** The request id the error answers (venues that echo it, e.g. Aster). */
699
+ reqId?: number;
700
+ };
701
+ interface CodecDirectory {
702
+ entries: MarketDirectoryEntry[];
703
+ /** Codec-private lookup data (e.g. HL positional universes, Lighter market ids). */
704
+ data: unknown;
705
+ /** Non-fatal problems in the bootstrap responses (e.g. malformed rows), surfaced as error events. */
706
+ warnings?: string[];
707
+ }
708
+ interface CodecContext {
709
+ network: Network;
710
+ fetch: FetchLike;
711
+ restUrl: string;
712
+ hip3Dexes: string[] | "all";
713
+ canonicalize: Canonicalize;
714
+ clock: () => number;
715
+ }
716
+ /** Per-symbol funding metadata a codec attaches (static or from a labelled REST source). */
717
+ interface FundingMeta {
718
+ rateKind: RateKind | null;
719
+ rateSource: Source | null;
720
+ rateIntervalHours: number | null;
721
+ settlementIntervalHours: number | null;
722
+ intervalSource: Source | null;
723
+ /** When a labelled REST interval was fetched; null for a static (venue-definition) interval. */
724
+ intervalAsOf: number | null;
725
+ marketClosed: boolean | null;
726
+ }
727
+ interface LabelledRefresh {
728
+ /** e.g. "rest:/fapi/v3/fundingInfo" */
729
+ source: `rest:${string}`;
730
+ everyMs: number;
731
+ /** Re-fetch and apply into the codec's directory data; throws WsError on failure; resolves to warnings. */
732
+ run(ctx: CodecContext, dir: CodecDirectory): Promise<string[]>;
733
+ }
734
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
735
+ interface VenueCodec {
736
+ readonly venue: VenueId;
737
+ readonly capabilities: VenueWsCapabilities;
738
+ /** Wire URL, or null (network unsupported → CAPABILITY_UNSUPPORTED). */
739
+ url(network: Network): string | null;
740
+ restUrl(network: Network): string | null;
741
+ /** Redaction: secret JSON keys and URL path/query patterns. */
742
+ readonly secretKeys: readonly string[];
743
+ readonly secretUrlPatterns: readonly RegExp[];
744
+ /** App-level ping, or null (server protocol pings only). */
745
+ readonly ping: {
746
+ message: string;
747
+ everyMs: number;
748
+ } | null;
749
+ /** Venue limits. */
750
+ readonly limits: {
751
+ subscriptionsPerSocket: number;
752
+ newConnectionsPerMin: number;
753
+ outboundPerMin: number;
754
+ /** Per-socket caps per WireSub.group. */
755
+ perGroup?: Record<string, number>;
756
+ /** Sockets one stream may open to spread channels over the per-socket caps. Absent = 1 (the
757
+ * venue's limits are per IP, so extra sockets would not add room). */
758
+ maxSockets?: number;
759
+ };
760
+ /** true → the venue drops the socket (no error frame) on an invalid subscribe (HL). */
761
+ readonly closesOnBadSubscribe: boolean;
762
+ readonly staleAfterMs: number;
763
+ readonly openInterest: {
764
+ unit: "base" | "usd" | null;
765
+ sides: "one" | "both" | null;
766
+ };
767
+ loadDirectory(ctx: CodecContext): Promise<CodecDirectory>;
768
+ readonly refresh: LabelledRefresh | null;
769
+ fundingMeta(entry: MarketDirectoryEntry, dir: CodecDirectory): FundingMeta;
770
+ /** Wire channels for a target; throws WsError(INVALID_PARAMS / CAPABILITY_UNSUPPORTED). */
771
+ wiresFor(target: Exclude<MarketTarget, {
772
+ kind: "user";
773
+ }>, dir: CodecDirectory, staleOverrideMs: number | null): WireSub[];
774
+ /** User channels, or null when this release does not support them (→ CAPABILITY_UNSUPPORTED). */
775
+ readonly user: {
776
+ /** Auth needs a signing secret in the page (gated by allowInPageSecrets in browsers). */
777
+ inPageSecrets: boolean;
778
+ wires(ref: UserRef, channels: UserChannel[], dir: CodecDirectory): WireSub[];
779
+ } | null;
780
+ /** `auth`: the token for a WireSub with `auth`, else null. */
781
+ encodeSubscribe(w: WireSub, reqId: number, auth: string | null): string;
782
+ encodeUnsubscribe(w: WireSub, reqId: number): string;
783
+ /** Pure decode of one text frame. Throws nothing: problems are `error` events. */
784
+ decode(text: string, dir: CodecDirectory, recvTs: number): DecodedEvent[];
785
+ }
786
+
787
+ /**
788
+ * createVenueStream (§4): directory bootstrap, ref-counted wire subscriptions, dispatch of decoded
789
+ * frames into merged market state, labelled REST refresh, error/state fan-out. Socket lifecycle is in
790
+ * connection.ts; venue specifics are in the codec.
791
+ *
792
+ * Sockets: one per stream unless the codec's limits.maxSockets allows more (per-socket venue caps,
793
+ * e.g. Pacifica 20 per channel). Each wire lives on one socket (first fit under subscriptionsPerSocket
794
+ * and limits.perGroup); a socket reconnects and resubscribes its own wires, and frames count only on the
795
+ * socket their wire lives on. The stream state is the aggregate of its sockets (aggregateState); one
796
+ * socket stopping for good (reconnects exhausted) closes the stream.
797
+ */
798
+
799
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
800
+ declare function createVenueStream(codec: VenueCodec, opts?: VenueStreamOptions): VenueStream;
801
+
802
+ /** Default market canonicalization (injectable via opts.canonicalize / comparator canonicalize). */
803
+
804
+ /**
805
+ * Quotes that collapse to "<BASE>". USDG is included because lighter-rh and Arcus settle in USDG
806
+ * (USD-pegged); a caller who wants them apart passes its own `canonicalize`.
807
+ */
808
+ declare const USD_LIKE_QUOTES: ReadonlySet<string>;
809
+ /**
810
+ * HIP-3 (dex set) → "<dex>:<BASE>". A USD-like quote → "<BASE>". Any other quote, or an unknown
811
+ * quote (null), → null (not comparable by default).
812
+ */
813
+ declare const defaultCanonicalize: Canonicalize;
814
+
815
+ /**
816
+ * Funding comparator + toHourly/toApr (§4). Compares like-for-like rate kinds: a leg without the
817
+ * requested kind has rate null and `kindUnavailable: true` (never substituted). Rates stay raw (Dec,
818
+ * per the venue's interval); annualize only with toApr.
819
+ *
820
+ * kind:
821
+ * - "current" (default): each leg's `rate`; rows whose legs differ in rateKind → mixedKinds.
822
+ * - "predicted": predictedRate (or rate when its kind is "predicted").
823
+ * - "lastSettled" / "accruing": rate only when its kind matches.
824
+ * - "next": the forward-looking rate per venue — accruing when published, else predicted.
825
+ * A venue with only a lastSettled rate is flagged (kindUnavailable), not substituted.
826
+ */
827
+
828
+ type ComparatorKind = RateKind | "current" | "next";
829
+ interface FundingLeg {
830
+ venue: VenueId;
831
+ dex: string | null;
832
+ symbol: string;
833
+ quote: string | null;
834
+ mark: Dec | null;
835
+ /** The rate of the requested kind, in the venue's interval (funding.rateIntervalHours). */
836
+ rate: Dec | null;
837
+ rateKind: RateKind | null;
838
+ /** The venue does not publish the requested kind for this market (rate is null, not substituted). */
839
+ kindUnavailable: boolean;
840
+ funding: MarketUpdate["funding"];
841
+ fundingVerified: boolean;
842
+ asOf: number | null;
843
+ stale: boolean;
844
+ }
845
+ interface FundingRow {
846
+ canonical: string;
847
+ mixedKinds: boolean;
848
+ legs: FundingLeg[];
849
+ }
850
+ interface FundingComparatorOptions {
851
+ streams: VenueStream[];
852
+ /** Minimum venues with a non-null rate for a row to be listed (default 2). */
853
+ minVenues?: number;
854
+ /** Canonical allowlist. */
855
+ include?: string[];
856
+ canonicalize?: Canonicalize;
857
+ kind?: ComparatorKind;
858
+ /** Leg stale when the stream ≠ live or now − asOf > this (default 2 × venue staleAfterMs; Arcus 3×). */
859
+ staleAfterMs?: number;
860
+ /** Stream control events (state / error) of the underlying subscriptions, and errors thrown by
861
+ * a comparator `subscribe` listener (`listener-error`; the other listeners still run). */
862
+ onControl?: (c: StreamControl | ComparatorListenerError) => void;
863
+ clock?: () => number;
864
+ }
865
+ interface ComparatorListenerError {
866
+ type: "listener-error";
867
+ message: string;
868
+ }
869
+ interface FundingComparator {
870
+ rows(): FundingRow[];
871
+ subscribe(cb: (rows: FundingRow[], changed: string[]) => void): () => void;
872
+ close(): void;
873
+ }
874
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
875
+ declare function toHourly(rate: Dec | null, rateIntervalHours: number | null): number | null;
876
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
877
+ declare function toApr(rate: Dec | null, rateIntervalHours: number | null): number | null;
878
+ /** @experimental 0.15.x (stable from 0.16.0 after consumer sign-off) */
879
+ declare function createFundingComparator(opts: FundingComparatorOptions): FundingComparator;
880
+
881
+ /**
882
+ * Hyperliquid codec (§6.1). All markets = ONE `allDexsAssetCtxs` subscription (every dex), filtered
883
+ * client-side by opts.hip3Dexes. Its ctx arrays are POSITIONAL (no coin): index i = meta{dex}.universe[i]
884
+ * — the directory loads meta per selected dex and every frame is length-checked (mismatch →
885
+ * WS_PROTOCOL_ERROR for that dex, never mis-assigned). kind:"market" uses `activeAssetCtx{coin}`.
886
+ * An unknown coin in a subscribe makes HL drop the socket (no error frame) → coins are validated
887
+ * against the directory before sending (closesOnBadSubscribe).
888
+ *
889
+ * Docs: hyperliquid.gitbook.io …/api/websocket/subscriptions; Phase 0 fixtures in
890
+ * src/__tests__/fixtures/ws/hyperliquid/.
891
+ */
892
+
893
+ declare const hyperliquid: VenueCodec;
894
+
895
+ /**
896
+ * Pacifica codec (§6.1). All markets = `prices` (one subscription, every ~3 s). funding = last
897
+ * settled hourly rate, next_funding = predicted (Phase 0: == REST funding_rate history). open_interest
898
+ * is base units; volume_24h USD. No bid/ask in `prices` (bbo channel, Phase 2).
899
+ * An unknown symbol is acked with no data and no error frame → symbols are validated first.
900
+ * The directory is the `instrument_type: "perpetual"` rows of GET /info (spot rows such as SOL-USDC are
901
+ * also in /info and `prices`). A row without instrument_type cannot be classified: it is left out and
902
+ * reported as a directory warning (non-fatal EXCHANGE_ERROR on each subscription; the other markets stream).
903
+ * Every wire carries `group` = its channel `source` (venue cap: 20 per channel per connection; the stream
904
+ * spreads further ones over more sockets, venue-static.ts).
905
+ *
906
+ * Docs: docs.pacifica.fi/api-documentation/api/websocket; fixtures in
907
+ * src/__tests__/fixtures/ws/pacifica/.
908
+ */
909
+
910
+ declare const pacifica: VenueCodec;
911
+
912
+ /**
913
+ * Lighter / Lighter RH codec (§6.1). All markets = `market_stats/all`: the first frame
914
+ * (`subscribed/market_stats`) has every market, then `update/market_stats` ~1 s with ONLY the
915
+ * changed markets (each entry complete) → merged per market.
916
+ *
917
+ * Units (Phase 0, the key correction): WS `funding_rate` (last settled) and `current_funding_rate`
918
+ * (predicted) are PERCENT per 1 h → converted to a fraction by an exact 2-place decimal shift,
919
+ * rateIntervalHours = settlementIntervalHours = 1. `open_interest` is USD notional (not base).
920
+ * Errors arrive as {error:{code,message}} with no channel field (matched by the channel name in
921
+ * the message). Spot markets (market_type "spot" in /api/v1/orderBooks) are not perps and skipped.
922
+ *
923
+ * Docs: apidocs.lighter.xyz/docs/websocket-reference; fixtures src/__tests__/fixtures/ws/lighter{,-rh}/.
924
+ */
925
+
926
+ declare const lighter: VenueCodec;
927
+ /** Lighter RH: separate directory (different market ids); USDG collateral. */
928
+ declare const lighterRh: VenueCodec;
929
+
930
+ /**
931
+ * Aster codec (§6.1, public only in 0.15.0). One combined-stream socket (`/stream`, frames
932
+ * {stream, data}) with `!markPrice@arr@1s` (every symbol, 1 s) + `!ticker@arr` (only changed symbols).
933
+ * r = accruing current-period rate (Phase 0: == premiumIndex.lastFundingRate ≠ last settled).
934
+ * The funding interval is per market from the LABELLED REST source `rest:/fapi/v3/fundingInfo`
935
+ * (hourly refresh; missing → null). OI and the predicted rate are not published (null).
936
+ * The directory is exchangeInfo (all quotes; base/quote never parsed from the suffix). markPrice@arr
937
+ * also carries symbols that exchangeInfo does not list (delisted/hidden, no base/quote metadata):
938
+ * those are learnt from rest:/fapi/v3/premiumIndex at bootstrap and skipped by documented rule.
939
+ * Unknown stream names are accepted silently by the venue → only fixed stream names are used.
940
+ *
941
+ * Docs: github.com/asterdex/api-docs (V3 futures); fixtures src/__tests__/fixtures/ws/aster/.
942
+ */
943
+
944
+ declare const aster: VenueCodec;
945
+
946
+ /**
947
+ * Arcus codec (§6.1, C1). All markets = ONE `markets` subscription: a full all-market snapshot
948
+ * (`subscribed`, then `channel_data` every ~5 s and on funding apply). It carries markPrice (no
949
+ * oraclePrices subscription needed), fundingRate = last applied (lastSettled; Phase 0: == REST
950
+ * /v1/fundingRates latest), nextFundingRate = predicted, nextFundingAt in unix SECONDS, openInterest
951
+ * base (one side), volume24hNotional USD (volume24h is base).
952
+ * Errors are {type:"error", message} with no channel/id.
953
+ *
954
+ * Docs: docs.arcus.xyz/api-reference/market-data/markets; fixtures src/__tests__/fixtures/ws/arcus/.
955
+ */
956
+
957
+ declare const arcus: VenueCodec;
958
+
959
+ export { type AllMarketsTarget, type BboUpdate, type BookUpdate, type Canonicalize, type CloseReason, type CodecContext, type CodecDirectory, type ComparatorKind, type ComparatorListenerError, type ConnectionState, type CredentialProvider, type Dec, type DecodedEvent, type FetchLike, type FillRow, type FundingComparator, type FundingComparatorOptions, type FundingLeg, type FundingMeta, type FundingRow, type FundingState, type LabelledRefresh, type MarketDirectoryEntry, type MarketField, type MarketPatch, type MarketTarget, type MarketUpdate, type Network, type OrderRow, type PositionRow, type RateKind, type Source, type StreamControl, type StreamErrorDetails, type StreamLog, type Subscription, type TradeUpdate, type TypedData, USD_LIKE_QUOTES, type UserChannel, type UserDelta, type UserRef, type UserUpdate, VENUE_IDS, type VenueCodec, type VenueId, type VenueStream, type VenueStreamOptions, type VenueWsCapabilities, WS_ERROR_CODES, type WireSub, WsError, type WsErrorCode, type WsFactory, type WsLike, type WsStructuredError, arcus, aster, createFundingComparator, createVenueStream, defaultCanonicalize, hyperliquid, isPerpError, lighter, lighterRh, pacifica, toApr, toHourly };