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
@@ -291,7 +291,7 @@ export class LighterSpotAdapter {
291
291
  }
292
292
  async spotMarketOrder(symbol, side, size, opts) {
293
293
  // Everything up to sendTx is pre-send: a failure there is a definite "not placed"
294
- // (ORDER_REJECTED); sendTx transport failures are ORDER_PENDING (design §12.3).
294
+ // (ORDER_REJECTED); sendTx transport failures are ORDER_PENDING.
295
295
  let prep;
296
296
  try {
297
297
  prep = await this._prepareMarketOrder(symbol, side, size, opts.slippage.bps);
@@ -400,7 +400,7 @@ export class LighterSpotAdapter {
400
400
  if (minSize && sizeNum < minSize.base) {
401
401
  throw new Error(`Size ${size} below min_base_amount ${minSize.base} for ${resolved}`);
402
402
  }
403
- // Protection price = best opposite quote ± the resolved slippage bound (design §13.2)
403
+ // Protection price = best opposite quote ± the resolved slippage bound
404
404
  const ob = await this.getSpotOrderbook(resolved);
405
405
  let refPrice;
406
406
  if (side === "buy") {
@@ -453,7 +453,7 @@ export class LighterSpotAdapter {
453
453
  const sizeStep = dec.size <= 0 ? "1" : `0.${"0".repeat(dec.size - 1)}1`;
454
454
  const tick = dec.price <= 0 ? "1" : `0.${"0".repeat(dec.price - 1)}1`;
455
455
  // Size floored to the size step (never rounded up); price must already be on tick
456
- // (INVALID_TICK, never rounded — design §10.10).
456
+ // (INVALID_TICK, never rounded).
457
457
  const sentSize = floorSize(size, sizeStep);
458
458
  const sentPrice = requireOnTick(price, tick);
459
459
  // Venue minimums: refused, never bumped up to the minimum (the order would exceed the request).
@@ -505,7 +505,7 @@ export class LighterSpotAdapter {
505
505
  };
506
506
  }
507
507
  /**
508
- * TIF mapping, as perps (§13.3): GTC/GTT → GTT, IOC → IOC, ALO / POST_ONLY → post-only (2).
508
+ * TIF mapping, as perps: GTC/GTT → GTT, IOC → IOC, ALO / POST_ONLY → post-only (2).
509
509
  * FOK and anything else → CAPABILITY_UNSUPPORTED (never silently GTT).
510
510
  */
511
511
  _limitTif(tif) {
@@ -656,7 +656,7 @@ export class LighterSpotAdapter {
656
656
  async transferUsdcToPerp(amount) {
657
657
  return this._selfTransfer(String(amount), 1, 0); // route: Spot(1) → Perp(0)
658
658
  }
659
- /** SpotTransferCapable (§12.4 #4): only the venue collateral (USDC on Lighter, USDG on Lighter RH). */
659
+ /** SpotTransferCapable: only the venue collateral (USDC on Lighter, USDG on Lighter RH). */
660
660
  async transferToSpot(amount, asset) {
661
661
  return this._selfTransfer(this._transferAmount(amount, asset), 0, 1);
662
662
  }
@@ -864,8 +864,7 @@ export class LighterSpotAdapter {
864
864
  return nonce;
865
865
  }
866
866
  /**
867
- * POST /sendTx. `kind` "place" (order placement): failures are typed (design
868
- * §12.3): before the request → ORDER_REJECTED; transport error, HTTP 5xx or an
867
+ * POST /sendTx. `kind` "place" (order placement): failures are typed: before the request → ORDER_REJECTED; transport error, HTTP 5xx or an
869
868
  * unreadable body after it → ORDER_PENDING (the tx may have been accepted — never
870
869
  * resubmit); an explicit non-200 code → ORDER_REJECTED. "order" (cancels) / "action"
871
870
  * (transfers): an explicit non-200 code → ORDER_REJECTED / ACTION_REJECTED
@@ -1,4 +1,4 @@
1
- import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
1
+ import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangePublicTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
2
2
  import type { ResolvedSlippage } from "./slippage.js";
3
3
  import type { EvmSigner } from "../signer/index.js";
4
4
  import type { AgentMeta } from "../settings.js";
@@ -34,7 +34,7 @@ export declare const LIGHTER_TIF: {
34
34
  readonly POST_ONLY: 2;
35
35
  };
36
36
  /**
37
- * CLI format rule for a Lighter client order id (§13.10 #9): the venue's
37
+ * CLI format rule for a Lighter client order id: the venue's
38
38
  * `clientOrderIndex` is numeric, so the id must be a plain decimal integer string
39
39
  * (digits only, no sign / leading zero / exponent) in 1 ≤ n ≤ 2^48 − 1.
40
40
  * Anything else → INVALID_PARAMS before anything is sent.
@@ -276,7 +276,7 @@ export interface LighterAdapterOpts {
276
276
  apiKeyIndex?: number;
277
277
  }
278
278
  export declare class LighterAdapter implements ExchangeAdapter {
279
- /** GTC→GTT resting-order lifetime (28 days) for the execution record (§13.10 #14). */
279
+ /** GTC→GTT resting-order lifetime (28 days) for the execution record. */
280
280
  static readonly gttExpiryMs: number;
281
281
  /** Venue id ("lighter" | "lighter-rh"). */
282
282
  readonly name: LighterVenueId;
@@ -468,7 +468,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
468
468
  */
469
469
  private pollTx;
470
470
  /**
471
- * Definite IOC outcome (§13.10 #4) from the executed tx: poll /tx by hash. A taker order the
471
+ * Definite IOC outcome from the executed tx: poll /tx by hash. A taker order the
472
472
  * venue reports "filled" (event_info `to.st` 3, `is` = sent) filled the whole order. Any other
473
473
  * status (the remainder was cancelled — the venue zeroes `rs`, so is − rs is NOT a fill) →
474
474
  * the venue's own `filled_base_amount` from accountOrders by client order index. Zero fill →
@@ -501,7 +501,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
501
501
  */
502
502
  orderRowOutcome(clientOrderIndex: number, orderId: string | null, what: string, settled?: (out: LighterOrderOutcome, row: Record<string, unknown>) => boolean, match?: LighterRowMatch | null): Promise<LighterOrderOutcome | null>;
503
503
  /**
504
- * Final outcome of a placed resting / post-only order (§13.10 #4 analogue for non-IOC): poll
504
+ * Final outcome of a placed resting / post-only order (analogue for non-IOC): poll
505
505
  * /tx (rejected → ORDER_REJECTED). Our order (`to`, matched by client order index) reported
506
506
  * "filled" in event_info → filled = is (the whole order). Any other status — open, or a
507
507
  * canceled-* whose remainder the venue zeroed (`rs` 0, so is − rs is NOT a fill) — or an
@@ -558,7 +558,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
558
558
  /** Order / trade side from Lighter's `is_ask`; a missing / non-boolean value throws (never defaults to "buy"). */
559
559
  private sideOfIsAsk;
560
560
  /**
561
- * Slippage-bounded IOC (§13.2): protection price = orderbook mid ± opts.slippage.bps,
561
+ * Slippage-bounded IOC: protection price = orderbook mid ± opts.slippage.bps,
562
562
  * tick-aligned toward crossing. The returned raw sendTx result carries `protection`
563
563
  * and, when the venue reported the fill, `iocOutcome` (see iocOutcomeOf).
564
564
  * `opts.clientOrderIndex` (optional) = caller's numeric client order id (lighterClientOrderIndex rule).
@@ -569,9 +569,9 @@ export declare class LighterAdapter implements ExchangeAdapter {
569
569
  clientOrderIndex?: number;
570
570
  }): Promise<unknown>;
571
571
  /**
572
- * TIF mapping (§13.3): GTC/GTT → GTT (28-day expiry, gttExpiryMs), IOC → IOC,
572
+ * TIF mapping: GTC/GTT → GTT (28-day expiry, gttExpiryMs), IOC → IOC,
573
573
  * ALO / POST_ONLY → time-in-force 2 (POST_ONLY per apidocs trading.md and the official
574
- * lighter-go signer; 28-day expiry like GTT — testnet confirmation pending, design §11).
574
+ * lighter-go signer; 28-day expiry like GTT — testnet confirmation pending).
575
575
  * FOK and any other value → CAPABILITY_UNSUPPORTED (never silently GTT).
576
576
  * `opts.clientOrderIndex` as in marketOrder.
577
577
  * IOC: the same definite outcome as marketOrder (awaitIocOutcome — zero fill →
@@ -640,7 +640,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
640
640
  /**
641
641
  * Trigger order. Market trigger (no limitPrice): execution price bound = trigger price ±
642
642
  * `opts.slippage` bps (the caller's resolveSlippage() output — required; the adapter never
643
- * reads settings, §13.10 #1), tick-aligned toward crossing (§13.10 #3), recorded in the
643
+ * reads settings), tick-aligned toward crossing, recorded in the
644
644
  * result as `protection`. Accepted risk: a fast move through the bound leaves the
645
645
  * triggered order unfilled.
646
646
  * TP vs SL: `opts.kind` given (TP/SL legs) → that type as given (a trigger already crossed
@@ -665,7 +665,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
665
665
  private _withdrawRaw;
666
666
  editOrder(symbol: string, orderId: string, price: string, size: string): Promise<unknown>;
667
667
  setLeverage(symbol: string, leverage: number, marginMode?: "cross" | "isolated"): Promise<unknown>;
668
- getRecentTrades(symbol: string, limit?: number): Promise<ExchangeTrade[]>;
668
+ getRecentTrades(symbol: string, limit?: number): Promise<ExchangePublicTrade[]>;
669
669
  getFundingHistory(symbol: string, limit?: number): Promise<{
670
670
  time: number;
671
671
  rate: string;
@@ -779,7 +779,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
779
779
  private nextClientOrderIndex;
780
780
  private ensureSigner;
781
781
  /**
782
- * One WASM signer (one Go runtime) per `${venueId}:${network}` (design §10.5).
782
+ * One WASM signer (one Go runtime) per `${venueId}:${network}`.
783
783
  * lighter-go keys its clients by (accountIndex, apiKeyIndex) only and keeps a
784
784
  * package-global chainId for L1 messages, so two venues must never share a
785
785
  * runtime. Keyed by venue+network rather than chainId because Lighter testnet
@@ -40,7 +40,7 @@ function lighterStep(decimals) {
40
40
  return decimals <= 0 ? "1" : `0.${"0".repeat(decimals - 1)}1`;
41
41
  }
42
42
  /**
43
- * CLI format rule for a Lighter client order id (§13.10 #9): the venue's
43
+ * CLI format rule for a Lighter client order id: the venue's
44
44
  * `clientOrderIndex` is numeric, so the id must be a plain decimal integer string
45
45
  * (digits only, no sign / leading zero / exponent) in 1 ≤ n ≤ 2^48 − 1.
46
46
  * Anything else → INVALID_PARAMS before anything is sent.
@@ -409,8 +409,16 @@ export function lighterSelectAccount(accounts, accountIndex, venue) {
409
409
  * window is unknown (SYMBOL_NOT_FOUND) until the entry expires.
410
410
  */
411
411
  export const LIGHTER_MARKET_LIST_TTL_MS = 5 * 60_000;
412
+ /** A candle volume field: a finite number / numeric string → its string form; absent or non-numeric → null (never "" / "undefined"). */
413
+ function candleNumber(v) {
414
+ if (typeof v === "number" && Number.isFinite(v))
415
+ return String(v);
416
+ if (typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v)))
417
+ return v;
418
+ return null;
419
+ }
412
420
  export class LighterAdapter {
413
- /** GTC→GTT resting-order lifetime (28 days) for the execution record (§13.10 #14). */
421
+ /** GTC→GTT resting-order lifetime (28 days) for the execution record. */
414
422
  static gttExpiryMs = LIGHTER_GTT_EXPIRY_MS;
415
423
  /** Venue id ("lighter" | "lighter-rh"). */
416
424
  name;
@@ -679,7 +687,7 @@ export class LighterAdapter {
679
687
  return { tier: "master" };
680
688
  if (this._evmKey)
681
689
  return { tier: "pk" };
682
- // Lighter RH master key is OWS-only (design §10.3): no env-key remediation for it.
690
+ // Lighter RH master key is OWS-only: no env-key remediation for it.
683
691
  const remediation = this.venueId === "lighter"
684
692
  ? "Run one of: (a) perp wallet agent approve lighter --master <wallet>; (b) perp wallet generate && perp -e lighter --ows <wallet>; (c) export LIGHTER_PRIVATE_KEY=0x..."
685
693
  : `Run one of: (a) perp wallet agent approve ${this.name} --master <wallet>; (b) perp wallet generate && perp -e ${this.name} --ows <wallet>`;
@@ -863,7 +871,7 @@ export class LighterAdapter {
863
871
  getMarketIndex(symbol) {
864
872
  const idx = this._marketMap.get(symbol.toUpperCase());
865
873
  if (idx === undefined)
866
- throw new PerpError("SYMBOL_NOT_FOUND", `Unknown Lighter market: ${symbol}`, { exchange: this.name });
874
+ throw new PerpError("SYMBOL_NOT_FOUND", `Unknown Lighter market: ${symbol}`, { exchange: this.name, remediation: `List markets: perp -e ${this.name} market list` });
867
875
  return idx;
868
876
  }
869
877
  /**
@@ -976,7 +984,7 @@ export class LighterAdapter {
976
984
  return last;
977
985
  }
978
986
  /**
979
- * Definite IOC outcome (§13.10 #4) from the executed tx: poll /tx by hash. A taker order the
987
+ * Definite IOC outcome from the executed tx: poll /tx by hash. A taker order the
980
988
  * venue reports "filled" (event_info `to.st` 3, `is` = sent) filled the whole order. Any other
981
989
  * status (the remainder was cancelled — the venue zeroes `rs`, so is − rs is NOT a fill) →
982
990
  * the venue's own `filled_base_amount` from accountOrders by client order index. Zero fill →
@@ -1117,7 +1125,7 @@ export class LighterAdapter {
1117
1125
  return null;
1118
1126
  }
1119
1127
  /**
1120
- * Final outcome of a placed resting / post-only order (§13.10 #4 analogue for non-IOC): poll
1128
+ * Final outcome of a placed resting / post-only order (analogue for non-IOC): poll
1121
1129
  * /tx (rejected → ORDER_REJECTED). Our order (`to`, matched by client order index) reported
1122
1130
  * "filled" in event_info → filled = is (the whole order). Any other status — open, or a
1123
1131
  * canceled-* whose remainder the venue zeroed (`rs` 0, so is − rs is NOT a fill) — or an
@@ -1451,7 +1459,7 @@ export class LighterAdapter {
1451
1459
  return isAsk ? "sell" : "buy";
1452
1460
  }
1453
1461
  /**
1454
- * Slippage-bounded IOC (§13.2): protection price = orderbook mid ± opts.slippage.bps,
1462
+ * Slippage-bounded IOC: protection price = orderbook mid ± opts.slippage.bps,
1455
1463
  * tick-aligned toward crossing. The returned raw sendTx result carries `protection`
1456
1464
  * and, when the venue reported the fill, `iocOutcome` (see iocOutcomeOf).
1457
1465
  * `opts.clientOrderIndex` (optional) = caller's numeric client order id (lighterClientOrderIndex rule).
@@ -1502,9 +1510,9 @@ export class LighterAdapter {
1502
1510
  return this.awaitIocOutcome(result, symbol, baseAmount, `market ${side} ${size} ${symbol}`, { protection, clientOrderIndex }, match);
1503
1511
  }
1504
1512
  /**
1505
- * TIF mapping (§13.3): GTC/GTT → GTT (28-day expiry, gttExpiryMs), IOC → IOC,
1513
+ * TIF mapping: GTC/GTT → GTT (28-day expiry, gttExpiryMs), IOC → IOC,
1506
1514
  * ALO / POST_ONLY → time-in-force 2 (POST_ONLY per apidocs trading.md and the official
1507
- * lighter-go signer; 28-day expiry like GTT — testnet confirmation pending, design §11).
1515
+ * lighter-go signer; 28-day expiry like GTT — testnet confirmation pending).
1508
1516
  * FOK and any other value → CAPABILITY_UNSUPPORTED (never silently GTT).
1509
1517
  * `opts.clientOrderIndex` as in marketOrder.
1510
1518
  * IOC: the same definite outcome as marketOrder (awaitIocOutcome — zero fill →
@@ -1841,7 +1849,7 @@ export class LighterAdapter {
1841
1849
  /**
1842
1850
  * Trigger order. Market trigger (no limitPrice): execution price bound = trigger price ±
1843
1851
  * `opts.slippage` bps (the caller's resolveSlippage() output — required; the adapter never
1844
- * reads settings, §13.10 #1), tick-aligned toward crossing (§13.10 #3), recorded in the
1852
+ * reads settings), tick-aligned toward crossing, recorded in the
1845
1853
  * result as `protection`. Accepted risk: a fast move through the bound leaves the
1846
1854
  * triggered order unfilled.
1847
1855
  * TP vs SL: `opts.kind` given (TP/SL legs) → that type as given (a trigger already crossed
@@ -2013,10 +2021,13 @@ export class LighterAdapter {
2013
2021
  side: t.is_maker_ask ? "buy" : "sell",
2014
2022
  price: String(t.price ?? "0"),
2015
2023
  size: String(t.size ?? t.base_amount ?? t.amount ?? ""),
2016
- fee: String(t.fee ?? "0"),
2024
+ // The tape's taker_fee / maker_fee are the parties' fee RATES, not a fee the viewer paid.
2025
+ fee: null,
2017
2026
  }));
2018
2027
  }
2019
2028
  async getFundingHistory(symbol, limit = 10) {
2029
+ await this.ensureMarketMap();
2030
+ this.getMarketIndex(symbol); // unlisted market: SYMBOL_NOT_FOUND, not an empty history
2020
2031
  const rates = await this.getFundingRates();
2021
2032
  const r = rates.get(symbol.toUpperCase());
2022
2033
  if (!r)
@@ -2035,8 +2046,11 @@ export class LighterAdapter {
2035
2046
  high: String(c.high ?? c.h ?? "0"),
2036
2047
  low: String(c.low ?? c.l ?? "0"),
2037
2048
  close: String(c.close ?? c.c ?? "0"),
2038
- volume: String(c.base_token_volume ?? c.v ?? ""),
2039
- trades: Number(c.trades_count ?? c.n ?? 0),
2049
+ volume: candleNumber(c.base_token_volume ?? c.v),
2050
+ // /candles: v = base volume, V = quote (USD) volume; no per-candle trade count (live
2051
+ // 2026-10-14 keys: t o h l c v V i — `i` is the last trade id, not a count).
2052
+ quoteVolume: candleNumber(c.V),
2053
+ trades: typeof c.trades_count === "number" ? c.trades_count : typeof c.n === "number" ? c.n : null,
2040
2054
  }));
2041
2055
  }
2042
2056
  /**
@@ -2428,7 +2442,7 @@ export class LighterAdapter {
2428
2442
  }
2429
2443
  }
2430
2444
  /**
2431
- * One WASM signer (one Go runtime) per `${venueId}:${network}` (design §10.5).
2445
+ * One WASM signer (one Go runtime) per `${venueId}:${network}`.
2432
2446
  * lighter-go keys its clients by (accountIndex, apiKeyIndex) only and keeps a
2433
2447
  * package-global chainId for L1 messages, so two venues must never share a
2434
2448
  * runtime. Keyed by venue+network rather than chainId because Lighter testnet
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Ondo Perps adapter — native TradingV2 + legacy ExchangeAdapter shim
3
- * (design §10.7, §10.10).
4
3
  *
5
4
  * Signer: HMAC API key only (OndoAgentMeta → ~/.perp/ondo-agents/<keyId>.json).
6
5
  * There is no master-key signing path for trading; read-only adapters serve
@@ -9,8 +8,8 @@
9
8
  * Order entry:
10
9
  * - market orders: IOC LIMIT at the protection price
11
10
  * alignProtectionPrice(slippagePrice(mark, side, bps), quoteIncrement, side),
12
- * bps = the caller's resolveSlippage() value (design §13.2);
13
- * - TIF (design §13.3): GTC | IOC | ALO, where ALO = GTC + postOnly; FOK is
11
+ * bps = the caller's resolveSlippage() value;
12
+ * - TIF: GTC | IOC | ALO, where ALO = GTC + postOnly; FOK is
14
13
  * not offered (uniform CAPABILITY_UNSUPPORTED, never emulated); a limit order
15
14
  * without a TIF is GTC;
16
15
  * - limit orders without a caller clientOrderId get a generated one that
@@ -19,7 +18,7 @@
19
18
  * - TP/SL attached to the entry via takeProfit/stopLoss;
20
19
  * - stopMarket = position-level stop (POST /v1/perps/stop_order), reduce-only,
21
20
  * mark-triggered; stopLimit is not offered by the venue;
22
- * - amend = cancel, then place (design §13.10 #10, #11). `size` is the NEW OPEN
21
+ * - amend = cancel, then place. `size` is the NEW OPEN
23
22
  * size. The replacement keeps side / reduceOnly / TIF; post-only comes from
24
23
  * the original's generated clientOrderId, or the caller's explicit TIF — an
25
24
  * order whose post-only flag is not decodable (placed elsewhere / caller id)
@@ -35,7 +34,7 @@
35
34
  * Limit prices and TP/SL triggers must be on tick (INVALID_TICK); sizes are
36
35
  * floored to the step (SIZE_TOO_SMALL when that reaches zero).
37
36
  */
38
- import type { ExchangeAdapter, ExchangeBalance, ExchangeFundingPayment, ExchangeKline, ExchangeMarketInfo, ExchangeOrder, ExchangePosition, ExchangeTrade } from "../interface.js";
37
+ import type { ExchangeAdapter, ExchangeBalance, ExchangeFundingPayment, ExchangeKline, ExchangeMarketInfo, ExchangeOrder, ExchangePosition, ExchangeTrade, ExchangePublicTrade } from "../interface.js";
39
38
  import type { ResolvedSlippage } from "../slippage.js";
40
39
  import type { TradingV2 } from "../trading-v2.js";
41
40
  import type { VenueCreateContext, VenueDefinition } from "../venue.js";
@@ -105,7 +104,7 @@ export declare class OndoAdapter implements ExchangeAdapter, TradingV2 {
105
104
  bids: [string, string][];
106
105
  asks: [string, string][];
107
106
  }>;
108
- getRecentTrades(symbol: string, limit?: number): Promise<ExchangeTrade[]>;
107
+ getRecentTrades(symbol: string, limit?: number): Promise<ExchangePublicTrade[]>;
109
108
  getFundingHistory(symbol: string, limit?: number): Promise<{
110
109
  time: number;
111
110
  rate: string;
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * Ondo Perps adapter — native TradingV2 + legacy ExchangeAdapter shim
3
- * (design §10.7, §10.10).
4
3
  *
5
4
  * Signer: HMAC API key only (OndoAgentMeta → ~/.perp/ondo-agents/<keyId>.json).
6
5
  * There is no master-key signing path for trading; read-only adapters serve
@@ -9,8 +8,8 @@
9
8
  * Order entry:
10
9
  * - market orders: IOC LIMIT at the protection price
11
10
  * alignProtectionPrice(slippagePrice(mark, side, bps), quoteIncrement, side),
12
- * bps = the caller's resolveSlippage() value (design §13.2);
13
- * - TIF (design §13.3): GTC | IOC | ALO, where ALO = GTC + postOnly; FOK is
11
+ * bps = the caller's resolveSlippage() value;
12
+ * - TIF: GTC | IOC | ALO, where ALO = GTC + postOnly; FOK is
14
13
  * not offered (uniform CAPABILITY_UNSUPPORTED, never emulated); a limit order
15
14
  * without a TIF is GTC;
16
15
  * - limit orders without a caller clientOrderId get a generated one that
@@ -19,7 +18,7 @@
19
18
  * - TP/SL attached to the entry via takeProfit/stopLoss;
20
19
  * - stopMarket = position-level stop (POST /v1/perps/stop_order), reduce-only,
21
20
  * mark-triggered; stopLimit is not offered by the venue;
22
- * - amend = cancel, then place (design §13.10 #10, #11). `size` is the NEW OPEN
21
+ * - amend = cancel, then place. `size` is the NEW OPEN
23
22
  * size. The replacement keeps side / reduceOnly / TIF; post-only comes from
24
23
  * the original's generated clientOrderId, or the caller's explicit TIF — an
25
24
  * order whose post-only flag is not decodable (placed elsewhere / caller id)
@@ -35,6 +34,7 @@
35
34
  * Limit prices and TP/SL triggers must be on tick (INVALID_TICK); sizes are
36
35
  * floored to the step (SIZE_TOO_SMALL when that reaches zero).
37
36
  */
37
+ import { nonEmptyOrListed } from "../unknown-symbol.js";
38
38
  import { randomUUID } from "node:crypto";
39
39
  import { PerpError, orderNotSent, orderOutcomeUnknown } from "../../errors.js";
40
40
  import { alignProtectionPrice, floorSize, formatComputedPrice, requireOnTick, slippagePrice, subDecimal } from "../precision.js";
@@ -57,7 +57,7 @@ function isPositive(s) {
57
57
  return Number(s) > 0;
58
58
  }
59
59
  /**
60
- * Generated clientOrderId carrying the post-only flag (design §13.10 #11): the
60
+ * Generated clientOrderId carrying the post-only flag: the
61
61
  * Ondo API never returns postOnly, so amend recovers it from the id instead of
62
62
  * a local file. `pcli-p-` = post-only, `pcli-n-` = not post-only; 39 chars of
63
63
  * [A-Za-z0-9_-] (venue rule: 1–64).
@@ -410,7 +410,7 @@ export class OndoAdapter {
410
410
  }
411
411
  }
412
412
  async placeOrderOnVenue(input) {
413
- // §13.3: GTC is the uniform default for a limit order without a TIF.
413
+ // GTC is the uniform default for a limit order without a TIF.
414
414
  const tifRequested = input.tif;
415
415
  const req = input.type === "limit" && input.tif === null ? { ...input, tif: "GTC" } : input;
416
416
  if (req.tif === "FOK") {
@@ -558,7 +558,7 @@ export class OndoAdapter {
558
558
  remediation: `Do NOT resubmit. Check the order: perp trade status ${original.orderId} --symbol ${req.symbol} -e ondo`,
559
559
  });
560
560
  }
561
- // req.size = new OPEN size; fills that raced the cancel reduce the replacement (§13.10 #10).
561
+ // req.size = new OPEN size; fills that raced the cancel reduce the replacement.
562
562
  const racedFill = subDecimal(cancelled.filledSize, filledAtRead);
563
563
  const replace = subDecimal(req.size, racedFill.startsWith("-") ? "0" : racedFill);
564
564
  let size = null;
@@ -669,14 +669,15 @@ export class OndoAdapter {
669
669
  const trades = await this.client.publicGet("/v1/perps/trades", {
670
670
  market: ondoVenueSymbol(symbol), limit: limit === undefined ? null : limit,
671
671
  });
672
- return listOf(trades).map(t => ({
672
+ // `200 []` for an unlisted market (live 2026-10-14): checked against the market directory.
673
+ return nonEmptyOrListed(this, symbol, listOf(trades).map(t => ({
673
674
  time: parseOndoTime(t.time, "trade"),
674
675
  symbol: ondoPlainSymbol(t.market),
675
676
  side: t.aggressor_side,
676
677
  price: t.price,
677
678
  size: t.size,
678
- fee: "0", // public tape: no fee applies to the viewer
679
- }));
679
+ fee: null, // public tape: no fee applies to the viewer
680
+ })), ondoPlainSymbol(ondoVenueSymbol(symbol)));
680
681
  }
681
682
  async getFundingHistory(symbol, limit) {
682
683
  const rows = await this.client.publicGet("/v1/perps/funding_rate_history", {
@@ -692,15 +693,16 @@ export class OndoAdapter {
692
693
  from: Math.floor(startTime / 1000),
693
694
  to: Math.floor(endTime / 1000),
694
695
  });
695
- return listOf(candles).map(c => ({
696
+ return nonEmptyOrListed(this, symbol, listOf(candles).map(c => ({
696
697
  time: parseOndoTime(c.startTime, "candle"),
697
698
  open: c.open,
698
699
  high: c.high,
699
700
  low: c.low,
700
701
  close: c.close,
701
702
  volume: c.volume,
703
+ quoteVolume: null,
702
704
  trades: null, // Ondo publishes no per-candle trade count
703
- }));
705
+ })), ondoPlainSymbol(ondoVenueSymbol(symbol)));
704
706
  }
705
707
  // ── Legacy ExchangeAdapter: account ──
706
708
  async getBalance() {
@@ -760,7 +762,7 @@ export class OndoAdapter {
760
762
  const rows = await this.client.get("/v1/perps/funding_fees", { limit: limit === undefined ? null : limit });
761
763
  return listOf(rows).map(r => ({ time: parseOndoTime(r.time, "funding fee"), symbol: ondoPlainSymbol(r.market), payment: r.amount }));
762
764
  }
763
- // ── Legacy ExchangeAdapter: trading (shim → TradingV2, design §10.7) ──
765
+ // ── Legacy ExchangeAdapter: trading (shim → TradingV2) ──
764
766
  async marketOrder(symbol, side, size, opts) {
765
767
  return this.placeOrder({
766
768
  symbol, side, type: "market", size, price: null, triggerPrice: null, triggerSource: null, tif: null,
@@ -768,7 +770,7 @@ export class OndoAdapter {
768
770
  });
769
771
  }
770
772
  async limitOrder(symbol, side, price, size, opts) {
771
- // §13.3: no TIF → GTC (uniform default); FOK → uniform CAPABILITY_UNSUPPORTED.
773
+ // no TIF → GTC (uniform default); FOK → uniform CAPABILITY_UNSUPPORTED.
772
774
  const tifInput = opts?.tif;
773
775
  const tif = tifInput === undefined || tifInput === "" ? null : validateLegacyTif(this.venue, this.caps, tifInput);
774
776
  return this.placeOrder({
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps API-key ("agent") registration (design §10.4).
2
+ * Ondo Perps API-key ("agent") registration.
3
3
  *
4
4
  * The HMAC key id + secret are created by the user in the Ondo Perps UI and
5
5
  * entered via stdin by the CLI (never flags). The secret is persisted only in
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps API-key ("agent") registration (design §10.4).
2
+ * Ondo Perps API-key ("agent") registration.
3
3
  *
4
4
  * The HMAC key id + secret are created by the user in the Ondo Perps UI and
5
5
  * entered via stdin by the CLI (never flags). The secret is persisted only in
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps raw REST client (design §9.2: raw fetch + node:crypto HMAC, no SDK).
2
+ * Ondo Perps raw REST client (raw fetch + node:crypto HMAC, no SDK).
3
3
  *
4
4
  * Auth (live-verified, scratchpad/sdk-findings.md §2 — the OpenAPI spec's
5
5
  * `X-API-KEY-ID` header name is wrong):
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps raw REST client (design §9.2: raw fetch + node:crypto HMAC, no SDK).
2
+ * Ondo Perps raw REST client (raw fetch + node:crypto HMAC, no SDK).
3
3
  *
4
4
  * Auth (live-verified, scratchpad/sdk-findings.md §2 — the OpenAPI spec's
5
5
  * `X-API-KEY-ID` header name is wrong):
@@ -220,7 +220,7 @@ export class OndoClient {
220
220
  }
221
221
  // Timeout covers the whole exchange (headers + body). A GET failure is definite
222
222
  // (nothing changed); a POST failure may have reached the venue → outcome unknown
223
- // (ORDER_PENDING, never resubmit — design §12.3).
223
+ // (ORDER_PENDING, never resubmit).
224
224
  const controller = new AbortController();
225
225
  const timer = setTimeout(() => controller.abort(), ONDO_REQUEST_TIMEOUT_MS);
226
226
  let res;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps public market data (design §10.6).
2
+ * Ondo Perps public market data.
3
3
  *
4
4
  * Sources (all public, live-verified 2026-09-30):
5
5
  * GET /v1/markets result.perps.tradingPairs[] — increments, margin
@@ -88,6 +88,13 @@ export declare function ondoPlainSymbol(venueSymbol: string): string;
88
88
  export declare function ondoVenueSymbol(symbol: string): string;
89
89
  /** Empty string / absent → null. Ondo omits or blanks fields it does not publish. */
90
90
  export declare function strOrNull(v: string | undefined | null): string | null;
91
+ /**
92
+ * A price field that the venue zeroes when it has no value: /v1/perps/contracts reports
93
+ * indexPrice "0" (and lastPrice "0") on disabled contracts (live 2026-10-14: ADBE, BE, CELH, IBM,
94
+ * NOK, OKLO, RDDT, SHEIN, SMCI, IBB, VXX, EURUSD — all `disabled: true`). A price is never 0, so
95
+ * "0" means "no index" → null (unknown), never $0.00. Volume / OI zeros stay: 0 is a real amount.
96
+ */
97
+ export declare function priceOrNull(v: string | undefined | null): string | null;
91
98
  export declare function parseOndoTime(iso: string, what: string): number;
92
99
  export declare function ondoPublicClient(net: Network): OndoClient;
93
100
  export declare function fetchOndoTradingPairs(client: OndoClient): Promise<OndoTradingPair[]>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps public market data (design §10.6).
2
+ * Ondo Perps public market data.
3
3
  *
4
4
  * Sources (all public, live-verified 2026-09-30):
5
5
  * GET /v1/markets result.perps.tradingPairs[] — increments, margin
@@ -33,6 +33,7 @@ const TAG_TO_ASSET_CLASS = {
33
33
  Index: "index",
34
34
  Commodity: "commodity",
35
35
  FX: "fx",
36
+ "Pre-IPO": "preIpo", // live 2026-10-19: OPENAI-USD.P
36
37
  };
37
38
  export function ondoAssetClass(market, tags) {
38
39
  if (tags === null || tags === undefined) {
@@ -64,6 +65,16 @@ export function ondoVenueSymbol(symbol) {
64
65
  export function strOrNull(v) {
65
66
  return v === undefined || v === null || v === "" ? null : v;
66
67
  }
68
+ /**
69
+ * A price field that the venue zeroes when it has no value: /v1/perps/contracts reports
70
+ * indexPrice "0" (and lastPrice "0") on disabled contracts (live 2026-10-14: ADBE, BE, CELH, IBM,
71
+ * NOK, OKLO, RDDT, SHEIN, SMCI, IBB, VXX, EURUSD — all `disabled: true`). A price is never 0, so
72
+ * "0" means "no index" → null (unknown), never $0.00. Volume / OI zeros stay: 0 is a real amount.
73
+ */
74
+ export function priceOrNull(v) {
75
+ const s = strOrNull(v);
76
+ return s === null || Number(s) === 0 ? null : s;
77
+ }
67
78
  export function parseOndoTime(iso, what) {
68
79
  const t = Date.parse(iso);
69
80
  if (!Number.isFinite(t))
@@ -166,7 +177,7 @@ async function fetchMarkets(net) {
166
177
  settle: "USDC",
167
178
  quoteAsset: pair.pair.quote,
168
179
  markPrice: mark.markPrice,
169
- indexPrice: strOrNull(contract.indexPrice),
180
+ indexPrice: priceOrNull(contract.indexPrice),
170
181
  fundingRate: strOrNull(contract.fundingRate),
171
182
  fundingRateBasis: ONDO_FUNDING_BASIS,
172
183
  settlementIntervalHours: ONDO_SETTLEMENT_HOURS,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps spot adapter (design §12, §12.4).
2
+ * Ondo Perps spot adapter.
3
3
  *
4
4
  * Same host and HMAC API key as perps (shares OndoAdapter's OndoClient).
5
5
  * Account model "separate": the Spot wallet ("spot") next to the Perps margin
@@ -35,7 +35,7 @@
35
35
  * Market orders: IOC LIMIT at
36
36
  * alignProtectionPrice(slippagePrice(mid, side, bps), quoteIncrement, side)
37
37
  * which is the web app's own pattern. bps = the caller's resolveSlippage()
38
- * value (design §13.2). The mid comes from the spot
38
+ * value. The mid comes from the spot
39
39
  * ticker's bid/ask; a one-sided book is PRICE_STALE (no lastPrice fallback).
40
40
  *
41
41
  * Unverified (needs a funded key):
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Ondo Perps spot adapter (design §12, §12.4).
2
+ * Ondo Perps spot adapter.
3
3
  *
4
4
  * Same host and HMAC API key as perps (shares OndoAdapter's OndoClient).
5
5
  * Account model "separate": the Spot wallet ("spot") next to the Perps margin
@@ -35,7 +35,7 @@
35
35
  * Market orders: IOC LIMIT at
36
36
  * alignProtectionPrice(slippagePrice(mid, side, bps), quoteIncrement, side)
37
37
  * which is the web app's own pattern. bps = the caller's resolveSlippage()
38
- * value (design §13.2). The mid comes from the spot
38
+ * value. The mid comes from the spot
39
39
  * ticker's bid/ask; a one-sided book is PRICE_STALE (no lastPrice fallback).
40
40
  *
41
41
  * Unverified (needs a funded key):
@@ -361,7 +361,7 @@ export class OndoSpotAdapter {
361
361
  }
362
362
  }
363
363
  /**
364
- * Classify a POST /v1/spot/orders failure (design §12.3). Definite: a definite-code
364
+ * Classify a POST /v1/spot/orders failure. Definite: a definite-code
365
365
  * PerpError (auth, signer, validation) or a venue error envelope on HTTP 4xx (generic
366
366
  * EXCHANGE_ERROR → ORDER_REJECTED). RATE_LIMITED is kept (rejected before processing).
367
367
  * Unknown → ORDER_PENDING: network failure, HTTP 5xx, a non-JSON / result-less body,