perp-cli 0.14.3 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (363) hide show
  1. package/README.ja.md +243 -0
  2. package/README.ko.md +243 -0
  3. package/README.md +138 -405
  4. package/README.zh.md +243 -0
  5. package/dist/agent-wallet/store.d.ts +3 -2
  6. package/dist/agent-wallet/store.js +2 -2
  7. package/dist/api/public/hyperliquid.js +4 -0
  8. package/dist/api/public/lighter.d.ts +5 -0
  9. package/dist/api/public/lighter.js +5 -0
  10. package/dist/api/public/pacifica.js +85 -15
  11. package/dist/api/public/urls.d.ts +2 -0
  12. package/dist/api/public/urls.js +3 -0
  13. package/dist/arb/sizing.js +4 -2
  14. package/dist/arb/state.d.ts +6 -0
  15. package/dist/arb/utils.d.ts +1 -2
  16. package/dist/arb/utils.js +1 -2
  17. package/dist/balance-by-address.d.ts +39 -0
  18. package/dist/balance-by-address.js +44 -0
  19. package/dist/bot/apex/pulse.js +3 -0
  20. package/dist/bot/apex/radar.js +2 -1
  21. package/dist/bot/engine.d.ts +10 -1
  22. package/dist/bot/engine.js +14 -6
  23. package/dist/bot/indicators.d.ts +7 -0
  24. package/dist/bot/indicators.js +16 -0
  25. package/dist/bot/perp-order.d.ts +4 -4
  26. package/dist/bot/perp-order.js +5 -5
  27. package/dist/bot/quarantine.d.ts +1 -1
  28. package/dist/bot/quarantine.js +1 -1
  29. package/dist/bot/quote-rules.js +1 -1
  30. package/dist/bot/strategies/apex-strategy.js +3 -2
  31. package/dist/bot/strategies/engine-mm.js +6 -5
  32. package/dist/bot/strategies/funding-arb-strategy.js +12 -5
  33. package/dist/bot/strategies/funding-arb-utils.d.ts +15 -3
  34. package/dist/bot/strategies/funding-arb-utils.js +53 -20
  35. package/dist/bot/strategies/funding-auto.js +2 -2
  36. package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
  37. package/dist/bot/strategies/liquidation-mm.js +8 -2
  38. package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
  39. package/dist/bot/strategies/momentum-breakout.js +11 -3
  40. package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +1 -1
  41. package/dist/bot/strategies/spot-perp-arb-strategy.js +6 -4
  42. package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
  43. package/dist/bot/strategies/spot-perp-executor.js +3 -3
  44. package/dist/bot/strategy-types.d.ts +2 -1
  45. package/dist/bot/tui/BotDashboard.d.ts +1 -1
  46. package/dist/bot/tui/BotDashboard.js +1 -1
  47. package/dist/cache.js +12 -6
  48. package/dist/cli-program.d.ts +15 -0
  49. package/dist/cli-program.js +126 -6
  50. package/dist/commands/account.js +168 -24
  51. package/dist/commands/agent-import.d.ts +7 -0
  52. package/dist/commands/agent-import.js +30 -1
  53. package/dist/commands/agent.d.ts +6 -3
  54. package/dist/commands/agent.js +160 -12
  55. package/dist/commands/arb/index.js +23 -81
  56. package/dist/commands/arb-auto.d.ts +24 -0
  57. package/dist/commands/arb-auto.js +175 -97
  58. package/dist/commands/arb.d.ts +20 -0
  59. package/dist/commands/arb.js +107 -163
  60. package/dist/commands/backtest.js +23 -21
  61. package/dist/commands/bot.js +1 -1
  62. package/dist/commands/builder.js +1 -1
  63. package/dist/commands/funds.js +113 -3
  64. package/dist/commands/init.d.ts +1 -1
  65. package/dist/commands/init.js +27 -7
  66. package/dist/commands/manage.js +23 -21
  67. package/dist/commands/market.d.ts +48 -0
  68. package/dist/commands/market.js +395 -100
  69. package/dist/commands/options.d.ts +14 -0
  70. package/dist/commands/options.js +151 -0
  71. package/dist/commands/pacifica-earn.d.ts +5 -0
  72. package/dist/commands/pacifica-earn.js +455 -0
  73. package/dist/commands/pacifica-orders.d.ts +14 -0
  74. package/dist/commands/pacifica-orders.js +142 -0
  75. package/dist/commands/portfolio.d.ts +16 -0
  76. package/dist/commands/portfolio.js +83 -55
  77. package/dist/commands/rebalance.d.ts +1 -0
  78. package/dist/commands/rebalance.js +15 -0
  79. package/dist/commands/risk.d.ts +11 -2
  80. package/dist/commands/risk.js +39 -44
  81. package/dist/commands/trade.d.ts +1 -0
  82. package/dist/commands/trade.js +67 -62
  83. package/dist/commands/wallet.js +1 -1
  84. package/dist/config.d.ts +2 -2
  85. package/dist/config.js +15 -4
  86. package/dist/constants.js +6 -0
  87. package/dist/cross-chain-margin.d.ts +3 -1
  88. package/dist/cross-chain-margin.js +4 -2
  89. package/dist/dashboard/server.js +3 -3
  90. package/dist/dashboard/ui.js +9 -6
  91. package/dist/errors.d.ts +29 -1
  92. package/dist/errors.js +35 -3
  93. package/dist/exchanges/address.d.ts +8 -0
  94. package/dist/exchanges/address.js +47 -0
  95. package/dist/exchanges/arcus/adapter.d.ts +16 -12
  96. package/dist/exchanges/arcus/adapter.js +45 -18
  97. package/dist/exchanges/arcus/agent.d.ts +1 -1
  98. package/dist/exchanges/arcus/agent.js +1 -1
  99. package/dist/exchanges/arcus/client.js +1 -1
  100. package/dist/exchanges/arcus/public.js +3 -2
  101. package/dist/exchanges/arcus/signing.d.ts +2 -3
  102. package/dist/exchanges/arcus/signing.js +1 -1
  103. package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
  104. package/dist/exchanges/arcus/spot-chain.js +1 -1
  105. package/dist/exchanges/arcus/spot-router.d.ts +1 -1
  106. package/dist/exchanges/arcus/spot-router.js +2 -2
  107. package/dist/exchanges/arcus/spot.d.ts +2 -3
  108. package/dist/exchanges/arcus/spot.js +2 -3
  109. package/dist/exchanges/aster-spot.d.ts +3 -3
  110. package/dist/exchanges/aster-spot.js +6 -9
  111. package/dist/exchanges/aster-typed-data.d.ts +1 -1
  112. package/dist/exchanges/aster-typed-data.js +1 -1
  113. package/dist/exchanges/aster.d.ts +30 -10
  114. package/dist/exchanges/aster.js +153 -53
  115. package/dist/exchanges/builder-fee.d.ts +1 -1
  116. package/dist/exchanges/builder-fee.js +13 -2
  117. package/dist/exchanges/canonical-symbols.d.ts +8 -2
  118. package/dist/exchanges/canonical-symbols.js +46 -2
  119. package/dist/exchanges/capabilities.d.ts +2 -0
  120. package/dist/exchanges/capabilities.js +2 -1
  121. package/dist/exchanges/derive/client.d.ts +37 -0
  122. package/dist/exchanges/derive/client.js +135 -0
  123. package/dist/exchanges/derive/options.d.ts +73 -0
  124. package/dist/exchanges/derive/options.js +379 -0
  125. package/dist/exchanges/extended/adapter.d.ts +96 -0
  126. package/dist/exchanges/extended/adapter.js +241 -0
  127. package/dist/exchanges/extended/client.d.ts +38 -0
  128. package/dist/exchanges/extended/client.js +88 -0
  129. package/dist/exchanges/extended/public.d.ts +133 -0
  130. package/dist/exchanges/extended/public.js +436 -0
  131. package/dist/exchanges/grvt/adapter.d.ts +100 -0
  132. package/dist/exchanges/grvt/adapter.js +283 -0
  133. package/dist/exchanges/grvt/client.d.ts +25 -0
  134. package/dist/exchanges/grvt/client.js +86 -0
  135. package/dist/exchanges/grvt/public.d.ts +151 -0
  136. package/dist/exchanges/grvt/public.js +438 -0
  137. package/dist/exchanges/hyperliquid-outcome.d.ts +2 -0
  138. package/dist/exchanges/hyperliquid-outcome.js +29 -1
  139. package/dist/exchanges/hyperliquid-spot.d.ts +9 -1
  140. package/dist/exchanges/hyperliquid-spot.js +24 -16
  141. package/dist/exchanges/hyperliquid.d.ts +199 -22
  142. package/dist/exchanges/hyperliquid.js +550 -202
  143. package/dist/exchanges/index.d.ts +1 -1
  144. package/dist/exchanges/index.js +1 -1
  145. package/dist/exchanges/interface.d.ts +157 -13
  146. package/dist/exchanges/interface.js +47 -0
  147. package/dist/exchanges/ioc-outcome.d.ts +1 -1
  148. package/dist/exchanges/ioc-outcome.js +1 -1
  149. package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
  150. package/dist/exchanges/legacy-trading-v2.js +7 -2
  151. package/dist/exchanges/lighter-public.d.ts +67 -12
  152. package/dist/exchanges/lighter-public.js +250 -82
  153. package/dist/exchanges/lighter-signer.d.ts +1 -1
  154. package/dist/exchanges/lighter-signer.js +1 -1
  155. package/dist/exchanges/lighter-spot.d.ts +10 -5
  156. package/dist/exchanges/lighter-spot.js +105 -97
  157. package/dist/exchanges/lighter.d.ts +80 -21
  158. package/dist/exchanges/lighter.js +312 -105
  159. package/dist/exchanges/ondo/adapter.d.ts +38 -13
  160. package/dist/exchanges/ondo/adapter.js +86 -27
  161. package/dist/exchanges/ondo/agent.d.ts +1 -1
  162. package/dist/exchanges/ondo/agent.js +1 -1
  163. package/dist/exchanges/ondo/client.d.ts +1 -1
  164. package/dist/exchanges/ondo/client.js +2 -2
  165. package/dist/exchanges/ondo/public.d.ts +35 -3
  166. package/dist/exchanges/ondo/public.js +75 -32
  167. package/dist/exchanges/ondo/spot.d.ts +2 -2
  168. package/dist/exchanges/ondo/spot.js +3 -3
  169. package/dist/exchanges/options-interface.d.ts +158 -0
  170. package/dist/exchanges/options-interface.js +17 -0
  171. package/dist/exchanges/options-venues.d.ts +12 -0
  172. package/dist/exchanges/options-venues.js +35 -0
  173. package/dist/exchanges/outcome-interface.d.ts +4 -0
  174. package/dist/exchanges/pacifica-atomic.d.ts +211 -0
  175. package/dist/exchanges/pacifica-atomic.js +440 -0
  176. package/dist/exchanges/pacifica-decimal.d.ts +10 -0
  177. package/dist/exchanges/pacifica-decimal.js +51 -0
  178. package/dist/exchanges/pacifica-presigned.d.ts +53 -0
  179. package/dist/exchanges/pacifica-presigned.js +141 -0
  180. package/dist/exchanges/pacifica-print.d.ts +200 -0
  181. package/dist/exchanges/pacifica-print.js +417 -0
  182. package/dist/exchanges/pacifica-spot.d.ts +8 -2
  183. package/dist/exchanges/pacifica-spot.js +26 -18
  184. package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
  185. package/dist/exchanges/pacifica-typed-data.js +1 -1
  186. package/dist/exchanges/pacifica-vaults.d.ts +130 -0
  187. package/dist/exchanges/pacifica-vaults.js +277 -0
  188. package/dist/exchanges/pacifica.d.ts +108 -10
  189. package/dist/exchanges/pacifica.js +428 -171
  190. package/dist/exchanges/precision.d.ts +3 -1
  191. package/dist/exchanges/precision.js +9 -2
  192. package/dist/exchanges/public-balances.d.ts +25 -0
  193. package/dist/exchanges/public-balances.js +158 -0
  194. package/dist/exchanges/public-positions.d.ts +43 -0
  195. package/dist/exchanges/public-positions.js +154 -0
  196. package/dist/exchanges/qfex/adapter.d.ts +69 -13
  197. package/dist/exchanges/qfex/adapter.js +102 -17
  198. package/dist/exchanges/qfex/agent.d.ts +1 -1
  199. package/dist/exchanges/qfex/agent.js +1 -1
  200. package/dist/exchanges/qfex/client.d.ts +1 -1
  201. package/dist/exchanges/qfex/client.js +1 -1
  202. package/dist/exchanges/qfex/errors.d.ts +1 -1
  203. package/dist/exchanges/qfex/errors.js +2 -2
  204. package/dist/exchanges/qfex/public.d.ts +65 -12
  205. package/dist/exchanges/qfex/public.js +131 -25
  206. package/dist/exchanges/qfex/signing.d.ts +2 -2
  207. package/dist/exchanges/qfex/signing.js +4 -5
  208. package/dist/exchanges/qfex/ws-transport.d.ts +3 -3
  209. package/dist/exchanges/qfex/ws-transport.js +3 -3
  210. package/dist/exchanges/rate-limiter.d.ts +1 -1
  211. package/dist/exchanges/rate-limiter.js +5 -2
  212. package/dist/exchanges/registry.d.ts +1 -1
  213. package/dist/exchanges/registry.js +1 -1
  214. package/dist/exchanges/risex/adapter.d.ts +160 -0
  215. package/dist/exchanges/risex/adapter.js +842 -0
  216. package/dist/exchanges/risex/agent.d.ts +154 -0
  217. package/dist/exchanges/risex/agent.js +384 -0
  218. package/dist/exchanges/risex/client.d.ts +47 -0
  219. package/dist/exchanges/risex/client.js +174 -0
  220. package/dist/exchanges/risex/nonce.d.ts +43 -0
  221. package/dist/exchanges/risex/nonce.js +212 -0
  222. package/dist/exchanges/risex/public.d.ts +64 -0
  223. package/dist/exchanges/risex/public.js +150 -0
  224. package/dist/exchanges/risex/session.d.ts +71 -0
  225. package/dist/exchanges/risex/session.js +171 -0
  226. package/dist/exchanges/risex/typed-data.d.ts +173 -0
  227. package/dist/exchanges/risex/typed-data.js +240 -0
  228. package/dist/exchanges/risex/units.d.ts +29 -0
  229. package/dist/exchanges/risex/units.js +104 -0
  230. package/dist/exchanges/slippage.d.ts +1 -1
  231. package/dist/exchanges/slippage.js +1 -1
  232. package/dist/exchanges/spot-factory.d.ts +3 -3
  233. package/dist/exchanges/spot-factory.js +8 -8
  234. package/dist/exchanges/spot-interface.d.ts +5 -5
  235. package/dist/exchanges/spot-interface.js +2 -2
  236. package/dist/exchanges/spot-result.d.ts +3 -3
  237. package/dist/exchanges/spot-result.js +3 -3
  238. package/dist/exchanges/trading-v2.d.ts +3 -3
  239. package/dist/exchanges/trading-v2.js +9 -6
  240. package/dist/exchanges/txflow/adapter.d.ts +61 -0
  241. package/dist/exchanges/txflow/adapter.js +150 -0
  242. package/dist/exchanges/txflow/client.d.ts +47 -0
  243. package/dist/exchanges/txflow/client.js +155 -0
  244. package/dist/exchanges/txflow/public.d.ts +87 -0
  245. package/dist/exchanges/txflow/public.js +355 -0
  246. package/dist/exchanges/unknown-symbol.d.ts +34 -0
  247. package/dist/exchanges/unknown-symbol.js +56 -0
  248. package/dist/exchanges/variational/adapter.d.ts +67 -0
  249. package/dist/exchanges/variational/adapter.js +137 -0
  250. package/dist/exchanges/variational/client.d.ts +21 -0
  251. package/dist/exchanges/variational/client.js +66 -0
  252. package/dist/exchanges/variational/public.d.ts +126 -0
  253. package/dist/exchanges/variational/public.js +430 -0
  254. package/dist/exchanges/venue-types.d.ts +52 -10
  255. package/dist/exchanges/venue-types.js +82 -11
  256. package/dist/exchanges/venue.d.ts +150 -11
  257. package/dist/exchanges/venue.js +126 -4
  258. package/dist/exchanges/venues/arcus.d.ts +1 -2
  259. package/dist/exchanges/venues/arcus.js +5 -4
  260. package/dist/exchanges/venues/aster.d.ts +2 -2
  261. package/dist/exchanges/venues/aster.js +6 -4
  262. package/dist/exchanges/venues/create-helpers.d.ts +1 -1
  263. package/dist/exchanges/venues/create-helpers.js +3 -3
  264. package/dist/exchanges/venues/extended.d.ts +21 -0
  265. package/dist/exchanges/venues/extended.js +96 -0
  266. package/dist/exchanges/venues/grvt.d.ts +30 -0
  267. package/dist/exchanges/venues/grvt.js +98 -0
  268. package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
  269. package/dist/exchanges/venues/hyperliquid.js +19 -8
  270. package/dist/exchanges/venues/index.d.ts +7 -2
  271. package/dist/exchanges/venues/index.js +8 -2
  272. package/dist/exchanges/venues/lighter-family.d.ts +7 -9
  273. package/dist/exchanges/venues/lighter-family.js +9 -11
  274. package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
  275. package/dist/exchanges/venues/lighter-rh.js +20 -8
  276. package/dist/exchanges/venues/lighter.d.ts +1 -1
  277. package/dist/exchanges/venues/lighter.js +18 -6
  278. package/dist/exchanges/venues/ondo.d.ts +1 -1
  279. package/dist/exchanges/venues/ondo.js +5 -3
  280. package/dist/exchanges/venues/pacifica.d.ts +2 -2
  281. package/dist/exchanges/venues/pacifica.js +15 -4
  282. package/dist/exchanges/venues/qfex.d.ts +1 -1
  283. package/dist/exchanges/venues/qfex.js +4 -2
  284. package/dist/exchanges/venues/risex-release.d.ts +14 -0
  285. package/dist/exchanges/venues/risex-release.js +14 -0
  286. package/dist/exchanges/venues/risex.d.ts +31 -0
  287. package/dist/exchanges/venues/risex.js +131 -0
  288. package/dist/exchanges/venues/txflow.d.ts +18 -0
  289. package/dist/exchanges/venues/txflow.js +77 -0
  290. package/dist/exchanges/venues/variational.d.ts +21 -0
  291. package/dist/exchanges/venues/variational.js +85 -0
  292. package/dist/execution-log.d.ts +1 -1
  293. package/dist/funding/legacy-public.d.ts +6 -4
  294. package/dist/funding/legacy-public.js +42 -41
  295. package/dist/funding/normalize.d.ts +1 -1
  296. package/dist/funding/normalize.js +1 -1
  297. package/dist/funding/rates.d.ts +95 -6
  298. package/dist/funding/rates.js +99 -19
  299. package/dist/index.js +7 -6
  300. package/dist/jobs.d.ts +1 -0
  301. package/dist/jobs.js +25 -1
  302. package/dist/landing.d.ts +2 -0
  303. package/dist/landing.js +1 -1
  304. package/dist/liquidity.d.ts +3 -1
  305. package/dist/liquidity.js +8 -4
  306. package/dist/mcp-pacifica-earn.d.ts +9 -0
  307. package/dist/mcp-pacifica-earn.js +64 -0
  308. package/dist/mcp-server.js +211 -68
  309. package/dist/pacifica/client.d.ts +69 -5
  310. package/dist/pacifica/client.js +181 -8
  311. package/dist/pacifica/rate-limit.d.ts +144 -0
  312. package/dist/pacifica/rate-limit.js +267 -0
  313. package/dist/pacifica/types/order.d.ts +2 -1
  314. package/dist/plan-executor.js +7 -2
  315. package/dist/positions-by-address.d.ts +34 -0
  316. package/dist/positions-by-address.js +46 -0
  317. package/dist/rebalance.js +1 -1
  318. package/dist/risk.d.ts +2 -1
  319. package/dist/risk.js +2 -4
  320. package/dist/settings.d.ts +32 -3
  321. package/dist/signer/ows-evm.d.ts +1 -1
  322. package/dist/signer/ows-evm.js +1 -1
  323. package/dist/smart-order.d.ts +1 -1
  324. package/dist/smart-order.js +1 -1
  325. package/dist/strategies/funding-arb.d.ts +1 -1
  326. package/dist/strategies/funding-arb.js +36 -63
  327. package/dist/trade-validator.js +2 -2
  328. package/dist/utils.d.ts +22 -3
  329. package/dist/utils.js +11 -3
  330. package/dist/venue-health.d.ts +40 -0
  331. package/dist/venue-health.js +75 -0
  332. package/dist/withdraw-gate.d.ts +1 -1
  333. package/dist/withdraw-gate.js +1 -1
  334. package/dist/ws/arcus-market.d.ts +9 -0
  335. package/dist/ws/arcus-market.js +9 -0
  336. package/dist/ws/aster-market.d.ts +33 -0
  337. package/dist/ws/aster-market.js +30 -0
  338. package/dist/ws/index.d.ts +980 -0
  339. package/dist/ws/index.js +4460 -0
  340. package/dist/ws/lighter-market.d.ts +14 -0
  341. package/dist/ws/lighter-market.js +13 -0
  342. package/dist/ws/node.d.ts +996 -0
  343. package/dist/ws/node.js +4554 -0
  344. package/dist/ws/pacifica-instrument.d.ts +8 -0
  345. package/dist/ws/pacifica-instrument.js +8 -0
  346. package/dist/ws/unclassified.d.ts +16 -0
  347. package/dist/ws/unclassified.js +43 -0
  348. package/dist/ws-cli/credentials.d.ts +62 -0
  349. package/dist/ws-cli/credentials.js +126 -0
  350. package/package.json +71 -10
  351. package/skills/perp-cli/SKILL.md +153 -36
  352. package/skills/perp-cli/package.json +15 -2
  353. package/skills/perp-cli/references/agent-operations.md +22 -12
  354. package/skills/perp-cli/references/commands.md +143 -17
  355. package/skills/perp-cli/references/strategies.md +38 -3
  356. package/skills/perp-cli/references/ws-library.md +523 -0
  357. package/skills/perp-cli/scripts/arb-monitor.sh +13 -10
  358. package/skills/perp-cli/scripts/funding-analysis.sh +57 -29
  359. package/skills/perp-cli/scripts/preflight.sh +27 -19
  360. package/skills/perp-cli/scripts/spot-perp-scan.sh +58 -38
  361. package/skills/perp-cli/scripts/validate-arb.sh +56 -39
  362. package/dist/cli-spec.d.ts +0 -46
  363. package/dist/cli-spec.js +0 -81
@@ -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,13 +34,14 @@
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, FundingHistoryRow } 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";
42
41
  import type { AmendRequest, CancelResult, LeverageResult, Network, OrderRef, OrderRequest, OrderResult, VenueMarket } from "../venue-types.js";
43
42
  import { type BuilderFeeGate } from "../builder-fee.js";
44
43
  import { OndoClient } from "./client.js";
44
+ import { type OndoListedStatus } from "./public.js";
45
45
  export declare class OndoAdapter implements ExchangeAdapter, TradingV2 {
46
46
  readonly name: "ondo";
47
47
  readonly aliases: readonly ["ondo-perps"];
@@ -50,6 +50,7 @@ export declare class OndoAdapter implements ExchangeAdapter, TradingV2 {
50
50
  private readonly caps;
51
51
  private readonly client;
52
52
  private pairsCache;
53
+ private contractsCache;
53
54
  /**
54
55
  * Builder-fee gate (settings builder.ondo; Ondo has no approval step); null = not
55
56
  * configured. Set by the venue definition's create — the adapter never reads settings.
@@ -66,6 +67,8 @@ export declare class OndoAdapter implements ExchangeAdapter, TradingV2 {
66
67
  get restClient(): OndoClient;
67
68
  /** Account id from GET /v1/account — the `from.id`/`to.id` of withdrawals and spot↔perp transfers. */
68
69
  accountId(): Promise<string>;
70
+ private pairsByMarket;
71
+ private contractsByMarket;
69
72
  private pairOf;
70
73
  /**
71
74
  * An order the venue returned as "pending" (not yet open / filled) is read back by id
@@ -100,18 +103,40 @@ export declare class OndoAdapter implements ExchangeAdapter, TradingV2 {
100
103
  cancelAllV2(symbol: string | null): Promise<CancelResult>;
101
104
  applyLeverage(symbol: string, leverage: number, marginMode: "cross" | "isolated" | null): Promise<LeverageResult>;
102
105
  getOrderStatus(_symbol: string, ref: OrderRef): Promise<OrderResult>;
103
- getMarkets(): Promise<ExchangeMarketInfo[]>;
106
+ /** Legacy rows of LISTED markets plus `tradable` (status "active") and `status` (active / closed / reduceOnly). */
107
+ getMarkets(): Promise<Array<ExchangeMarketInfo & {
108
+ tradable: boolean;
109
+ status: OndoListedStatus;
110
+ }>>;
111
+ /**
112
+ * `market info` for one market, resolved with the Ondo normalizer (ondoVenueSymbol: "AAPL",
113
+ * "AAPL-USD", "AAPL-USD.P", "AAPL-PERP"). A disabled market is refused like every per-symbol read.
114
+ */
115
+ getMarketInfo(symbol: string): Promise<ExchangeMarketInfo & {
116
+ tradable: boolean;
117
+ status: OndoListedStatus;
118
+ }>;
119
+ /**
120
+ * Venue symbol of a per-symbol read (book / trades / funding / kline), via the one normalizer
121
+ * (ondoVenueSymbol). A disabled (delisted / paused) listing → SYMBOL_NOT_FOUND with status
122
+ * "disabled": its prices are zeroed or frozen and its funding rate is stale, so nothing it returns
123
+ * is live market data. An unlisted symbol is left to each read's own SYMBOL_NOT_FOUND
124
+ * (nonEmptyOrListed / the venue's answer).
125
+ */
126
+ private readableMarket;
104
127
  getOrderbook(symbol: string): Promise<{
105
128
  bids: [string, string][];
106
129
  asks: [string, string][];
107
130
  }>;
108
- getRecentTrades(symbol: string, limit?: number): Promise<ExchangeTrade[]>;
109
- getFundingHistory(symbol: string, limit?: number): Promise<{
110
- time: number;
111
- rate: string;
112
- price: string | null;
113
- }[]>;
114
- /** GET /v1/perps/candles — authenticated (see klinesRequireAuth). */
131
+ getRecentTrades(symbol: string, limit?: number): Promise<ExchangePublicTrade[]>;
132
+ getFundingHistory(symbol: string, limit?: number): Promise<FundingHistoryRow[]>;
133
+ /**
134
+ * GET /v1/perps/candles — authenticated (see klinesRequireAuth).
135
+ * Minute-based resolutions are bucketed from `from` (live 2026-10-19: from=10:01:07 → candles at
136
+ * :01), so `from` is floored to the interval boundary first; D / W / M are venue-aligned already.
137
+ * The venue also returns a zero-volume bucket that opens after `to`; candles opening after the
138
+ * requested end are not part of the range and are left out.
139
+ */
115
140
  getKlines(symbol: string, interval: string, startTime: number, endTime: number): Promise<ExchangeKline[]>;
116
141
  getBalance(): Promise<ExchangeBalance>;
117
142
  /**
@@ -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";
@@ -44,7 +44,7 @@ import { parseOrderRef, toLegacyMarketInfo } from "../venue-types.js";
44
44
  import { builderGateOf, builderRejectionOf, NO_BUILDER, requireFeeBps } from "../builder-fee.js";
45
45
  import { loadOndoCredentials, ONDO_APPROVE_REMEDIATION } from "./agent.js";
46
46
  import { OndoClient } from "./client.js";
47
- import { fetchOndoTradingPairs, ondoPlainSymbol, ondoPublicData, ondoVenueSymbol, parseOndoTime, strOrNull, } from "./public.js";
47
+ import { fetchOndoMarketsWithStatus, fetchOndoTradingPairs, ondoDisabled, ondoPlainSymbol, ondoPublicData, ondoVenueSymbol, parseOndoTime, strOrNull, } from "./public.js";
48
48
  const ORDERS_PAGE_LIMIT = 100;
49
49
  const DEPTH_LEVELS = 50;
50
50
  const PAIRS_TTL_MS = 5 * 60_000;
@@ -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).
@@ -164,6 +164,7 @@ export class OndoAdapter {
164
164
  caps;
165
165
  client;
166
166
  pairsCache = null;
167
+ contractsCache = null;
167
168
  /**
168
169
  * Builder-fee gate (settings builder.ondo; Ondo has no approval step); null = not
169
170
  * configured. Set by the venue definition's create — the adapter never reads settings.
@@ -217,13 +218,25 @@ export class OndoAdapter {
217
218
  return account.accountID;
218
219
  }
219
220
  // ── Helpers ──
220
- async pairOf(symbol) {
221
- const market = ondoVenueSymbol(symbol);
221
+ async pairsByMarket() {
222
222
  if (this.pairsCache === null || Date.now() - this.pairsCache.at > PAIRS_TTL_MS) {
223
223
  const pairs = await fetchOndoTradingPairs(this.client);
224
224
  this.pairsCache = { at: Date.now(), byMarket: new Map(pairs.map(p => [p.market, p])) };
225
225
  }
226
- const pair = this.pairsCache.byMarket.get(market);
226
+ return this.pairsCache.byMarket;
227
+ }
228
+ async contractsByMarket() {
229
+ if (this.contractsCache === null || Date.now() - this.contractsCache.at > PAIRS_TTL_MS) {
230
+ const rows = await this.client.publicGet("/v1/perps/contracts", null);
231
+ if (!Array.isArray(rows))
232
+ throw new PerpError("EXCHANGE_ERROR", "Ondo Perps: /v1/perps/contracts is not a list");
233
+ this.contractsCache = { at: Date.now(), byMarket: new Map(rows.map(c => [c.market, c])) };
234
+ }
235
+ return this.contractsCache.byMarket;
236
+ }
237
+ async pairOf(symbol) {
238
+ const market = ondoVenueSymbol(symbol);
239
+ const pair = (await this.pairsByMarket()).get(market);
227
240
  if (pair === undefined) {
228
241
  throw new PerpError("SYMBOL_NOT_FOUND", `Ondo Perps has no market ${market}`, {
229
242
  remediation: "List markets with: perp market list -e ondo",
@@ -410,7 +423,7 @@ export class OndoAdapter {
410
423
  }
411
424
  }
412
425
  async placeOrderOnVenue(input) {
413
- // §13.3: GTC is the uniform default for a limit order without a TIF.
426
+ // GTC is the uniform default for a limit order without a TIF.
414
427
  const tifRequested = input.tif;
415
428
  const req = input.type === "limit" && input.tif === null ? { ...input, tif: "GTC" } : input;
416
429
  if (req.tif === "FOK") {
@@ -558,7 +571,7 @@ export class OndoAdapter {
558
571
  remediation: `Do NOT resubmit. Check the order: perp trade status ${original.orderId} --symbol ${req.symbol} -e ondo`,
559
572
  });
560
573
  }
561
- // req.size = new OPEN size; fills that raced the cancel reduce the replacement (§13.10 #10).
574
+ // req.size = new OPEN size; fills that raced the cancel reduce the replacement.
562
575
  const racedFill = subDecimal(cancelled.filledSize, filledAtRead);
563
576
  const replace = subDecimal(req.size, racedFill.startsWith("-") ? "0" : racedFill);
564
577
  let size = null;
@@ -657,50 +670,96 @@ export class OndoAdapter {
657
670
  return this.toOrderResult(order, order, readExecution(order));
658
671
  }
659
672
  // ── Legacy ExchangeAdapter: market data ──
673
+ /** Legacy rows of LISTED markets plus `tradable` (status "active") and `status` (active / closed / reduceOnly). */
660
674
  async getMarkets() {
661
- return (await this.getVenueMarkets()).map(toLegacyMarketInfo);
675
+ return (await fetchOndoMarketsWithStatus(this.network)).map(r => ({ ...toLegacyMarketInfo(r.market), tradable: r.market.tradable, status: r.status }));
676
+ }
677
+ /**
678
+ * `market info` for one market, resolved with the Ondo normalizer (ondoVenueSymbol: "AAPL",
679
+ * "AAPL-USD", "AAPL-USD.P", "AAPL-PERP"). A disabled market is refused like every per-symbol read.
680
+ */
681
+ async getMarketInfo(symbol) {
682
+ const market = (await this.pairOf(symbol)).market; // unlisted → SYMBOL_NOT_FOUND
683
+ await this.readableMarket(market);
684
+ const row = (await this.getMarkets()).find(m => m.symbol === ondoPlainSymbol(market));
685
+ if (row === undefined) {
686
+ throw new PerpError("SYMBOL_NOT_FOUND", `Ondo Perps ${market} has no mark price (no price feed)`, { exchange: "ondo", remediation: "List markets with: perp market list -e ondo" });
687
+ }
688
+ return row;
689
+ }
690
+ /**
691
+ * Venue symbol of a per-symbol read (book / trades / funding / kline), via the one normalizer
692
+ * (ondoVenueSymbol). A disabled (delisted / paused) listing → SYMBOL_NOT_FOUND with status
693
+ * "disabled": its prices are zeroed or frozen and its funding rate is stale, so nothing it returns
694
+ * is live market data. An unlisted symbol is left to each read's own SYMBOL_NOT_FOUND
695
+ * (nonEmptyOrListed / the venue's answer).
696
+ */
697
+ async readableMarket(symbol) {
698
+ const market = ondoVenueSymbol(symbol);
699
+ const pair = (await this.pairsByMarket()).get(market);
700
+ // Same predicate as the listing (ondoDisabled): the /v1/markets pair OR the contracts row.
701
+ if (pair !== undefined && ondoDisabled(pair, (await this.contractsByMarket()).get(market) ?? null)) {
702
+ throw new PerpError("SYMBOL_NOT_FOUND", `Ondo Perps ${market} is disabled (delisted / paused) — no live market data`, {
703
+ exchange: "ondo", status: "disabled", remediation: "List tradable markets with: perp market list -e ondo",
704
+ });
705
+ }
706
+ return market;
662
707
  }
663
708
  async getOrderbook(symbol) {
664
- const book = await this.client.publicGet("/v1/perps/depth", { market: ondoVenueSymbol(symbol), depth: DEPTH_LEVELS });
709
+ const book = await this.client.publicGet("/v1/perps/depth", { market: await this.readableMarket(symbol), depth: DEPTH_LEVELS });
665
710
  const lvl = (l) => [l[0], l[1]];
666
711
  return { bids: book.bids.map(lvl), asks: book.asks.map(lvl) };
667
712
  }
668
713
  async getRecentTrades(symbol, limit) {
714
+ const market = await this.readableMarket(symbol);
669
715
  const trades = await this.client.publicGet("/v1/perps/trades", {
670
- market: ondoVenueSymbol(symbol), limit: limit === undefined ? null : limit,
716
+ market, limit: limit === undefined ? null : limit,
671
717
  });
672
- return listOf(trades).map(t => ({
718
+ // `200 []` for an unlisted market (live 2026-10-14): checked against the market directory.
719
+ return nonEmptyOrListed(this, symbol, listOf(trades).map(t => ({
673
720
  time: parseOndoTime(t.time, "trade"),
674
721
  symbol: ondoPlainSymbol(t.market),
675
722
  side: t.aggressor_side,
676
723
  price: t.price,
677
724
  size: t.size,
678
- fee: "0", // public tape: no fee applies to the viewer
679
- }));
725
+ fee: null, // public tape: no fee applies to the viewer
726
+ })), ondoPlainSymbol(market));
680
727
  }
681
728
  async getFundingHistory(symbol, limit) {
682
729
  const rows = await this.client.publicGet("/v1/perps/funding_rate_history", {
683
- market: ondoVenueSymbol(symbol), limit: limit === undefined ? null : limit,
730
+ market: await this.readableMarket(symbol), limit: limit === undefined ? null : limit,
684
731
  });
685
- return listOf(rows).map(r => ({ time: parseOndoTime(r.time, "funding"), rate: r.fundingRate, price: null }));
732
+ return listOf(rows).map(r => ({ time: parseOndoTime(r.time, "funding"), rate: r.fundingRate, price: null, intervalHours: 1, rateKind: "lastSettled" }));
686
733
  }
687
- /** GET /v1/perps/candles — authenticated (see klinesRequireAuth). */
734
+ /**
735
+ * GET /v1/perps/candles — authenticated (see klinesRequireAuth).
736
+ * Minute-based resolutions are bucketed from `from` (live 2026-10-19: from=10:01:07 → candles at
737
+ * :01), so `from` is floored to the interval boundary first; D / W / M are venue-aligned already.
738
+ * The venue also returns a zero-volume bucket that opens after `to`; candles opening after the
739
+ * requested end are not part of the range and are left out.
740
+ */
688
741
  async getKlines(symbol, interval, startTime, endTime) {
742
+ const resolution = toResolution(interval);
743
+ const minuteMs = /^\d+$/.test(resolution) ? Number(resolution) * 60_000 : null;
744
+ const from = minuteMs === null ? startTime : Math.floor(startTime / minuteMs) * minuteMs;
745
+ const market = await this.readableMarket(symbol);
689
746
  const candles = await this.client.get("/v1/perps/candles", {
690
- market: ondoVenueSymbol(symbol),
691
- resolution: toResolution(interval),
692
- from: Math.floor(startTime / 1000),
747
+ market,
748
+ resolution,
749
+ from: Math.floor(from / 1000),
693
750
  to: Math.floor(endTime / 1000),
694
751
  });
695
- return listOf(candles).map(c => ({
752
+ const inRange = listOf(candles).filter(c => parseOndoTime(c.startTime, "candle") <= endTime);
753
+ return nonEmptyOrListed(this, symbol, inRange.map(c => ({
696
754
  time: parseOndoTime(c.startTime, "candle"),
697
755
  open: c.open,
698
756
  high: c.high,
699
757
  low: c.low,
700
758
  close: c.close,
701
759
  volume: c.volume,
760
+ quoteVolume: null,
702
761
  trades: null, // Ondo publishes no per-candle trade count
703
- }));
762
+ })), ondoPlainSymbol(market));
704
763
  }
705
764
  // ── Legacy ExchangeAdapter: account ──
706
765
  async getBalance() {
@@ -760,7 +819,7 @@ export class OndoAdapter {
760
819
  const rows = await this.client.get("/v1/perps/funding_fees", { limit: limit === undefined ? null : limit });
761
820
  return listOf(rows).map(r => ({ time: parseOndoTime(r.time, "funding fee"), symbol: ondoPlainSymbol(r.market), payment: r.amount }));
762
821
  }
763
- // ── Legacy ExchangeAdapter: trading (shim → TradingV2, design §10.7) ──
822
+ // ── Legacy ExchangeAdapter: trading (shim → TradingV2) ──
764
823
  async marketOrder(symbol, side, size, opts) {
765
824
  return this.placeOrder({
766
825
  symbol, side, type: "market", size, price: null, triggerPrice: null, triggerSource: null, tif: null,
@@ -768,7 +827,7 @@ export class OndoAdapter {
768
827
  });
769
828
  }
770
829
  async limitOrder(symbol, side, price, size, opts) {
771
- // §13.3: no TIF → GTC (uniform default); FOK → uniform CAPABILITY_UNSUPPORTED.
830
+ // no TIF → GTC (uniform default); FOK → uniform CAPABILITY_UNSUPPORTED.
772
831
  const tifInput = opts?.tif;
773
832
  const tif = tifInput === undefined || tifInput === "" ? null : validateLegacyTif(this.venue, this.caps, tifInput);
774
833
  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
@@ -18,7 +18,7 @@
18
18
  * fetchOndoSpotTradingPairs.
19
19
  */
20
20
  import type { VenuePublicData } from "../venue.js";
21
- import type { AssetClass, FundingRateBasis, Network } from "../venue-types.js";
21
+ import type { AssetClass, FundingRateBasis, Network, VenueMarket } from "../venue-types.js";
22
22
  import { OndoClient } from "./client.js";
23
23
  export declare const ONDO_PERP_SUFFIX = "-USD.P";
24
24
  export declare const ONDO_FUNDING_BASIS: FundingRateBasis;
@@ -84,12 +84,44 @@ export interface OndoMarkPrice {
84
84
  export declare function ondoAssetClass(market: string, tags: readonly string[] | null | undefined): AssetClass;
85
85
  /** "TSLA-USD.P" → "TSLA". Throws for non-perp symbols. */
86
86
  export declare function ondoPlainSymbol(venueSymbol: string): string;
87
- /** CLI symbol ("tsla", "TSLA", "TSLA-USD.P") → venue symbol "TSLA-USD.P". */
87
+ /**
88
+ * The one Ondo symbol normalizer, used by every command: "tsla", "TSLA", "TSLA-USD", "TSLA-USD.P",
89
+ * "TSLA-PERP" → venue symbol "TSLA-USD.P". 0.15.0 appended the suffix to whatever it was given
90
+ * ("AAPL-USD" → "AAPL-USD-USD.P", "AAPL-PERP" → "AAPL-PERP-USD.P").
91
+ */
88
92
  export declare function ondoVenueSymbol(symbol: string): string;
93
+ /**
94
+ * Listing state of an Ondo perp. "disabled" = delisted / paused (`disabled: true` on the
95
+ * /v1/markets pair or the /v1/perps/contracts row; its price fields are zeroed or frozen and its
96
+ * fundingRate is stale); "closed" = `isClosed` (no new trading); "reduceOnly" = closes only.
97
+ * Only "active" is tradable.
98
+ */
99
+ export type OndoMarketStatus = "active" | "disabled" | "closed" | "reduceOnly";
100
+ /** State of a LISTED market: a disabled one is not listed (listing policy: delisted / disabled / inactive → not listed). */
101
+ export type OndoListedStatus = Exclude<OndoMarketStatus, "disabled">;
102
+ /** The one "disabled" predicate (listing and every per-symbol read): either source flags it. */
103
+ export declare function ondoDisabled(pair: Pick<OndoTradingPair, "disabled">, contract: Pick<OndoContract, "disabled"> | null): boolean;
104
+ export declare function ondoMarketStatus(pair: Pick<OndoTradingPair, "disabled">, contract: Pick<OndoContract, "disabled" | "isClosed" | "reduceOnly"> | null): OndoMarketStatus;
89
105
  /** Empty string / absent → null. Ondo omits or blanks fields it does not publish. */
90
106
  export declare function strOrNull(v: string | undefined | null): string | null;
107
+ /**
108
+ * A price field that the venue zeroes when it has no value: /v1/perps/contracts reports
109
+ * indexPrice "0" (and lastPrice "0") on disabled contracts (live 2026-10-14: ADBE, BE, CELH, IBM,
110
+ * NOK, OKLO, RDDT, SHEIN, SMCI, IBB, VXX, EURUSD — all `disabled: true`). A price is never 0, so
111
+ * "0" means "no index" → null (unknown), never $0.00. Volume / OI zeros stay: 0 is a real amount.
112
+ */
113
+ export declare function priceOrNull(v: string | undefined | null): string | null;
91
114
  export declare function parseOndoTime(iso: string, what: string): number;
92
115
  export declare function ondoPublicClient(net: Network): OndoClient;
93
116
  export declare function fetchOndoTradingPairs(client: OndoClient): Promise<OndoTradingPair[]>;
94
117
  export declare function fetchOndoSpotTradingPairs(client: OndoClient): Promise<OndoSpotTradingPair[]>;
118
+ /**
119
+ * Every listed Ondo perp with its state (getMarkets rows carry `status`; VenueMarket has no field
120
+ * for it). Disabled (delisted / paused) listings are NOT listed — per-symbol reads refuse them —
121
+ * while closed / reduce-only markets stay listed with tradable false.
122
+ */
123
+ export declare function fetchOndoMarketsWithStatus(net: Network): Promise<Array<{
124
+ market: VenueMarket;
125
+ status: OndoListedStatus;
126
+ }>>;
95
127
  export declare const ondoPublicData: VenuePublicData;
@@ -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) {
@@ -53,17 +54,44 @@ export function ondoPlainSymbol(venueSymbol) {
53
54
  }
54
55
  return venueSymbol.slice(0, -ONDO_PERP_SUFFIX.length);
55
56
  }
56
- /** CLI symbol ("tsla", "TSLA", "TSLA-USD.P") → venue symbol "TSLA-USD.P". */
57
+ /**
58
+ * The one Ondo symbol normalizer, used by every command: "tsla", "TSLA", "TSLA-USD", "TSLA-USD.P",
59
+ * "TSLA-PERP" → venue symbol "TSLA-USD.P". 0.15.0 appended the suffix to whatever it was given
60
+ * ("AAPL-USD" → "AAPL-USD-USD.P", "AAPL-PERP" → "AAPL-PERP-USD.P").
61
+ */
57
62
  export function ondoVenueSymbol(symbol) {
58
- const up = symbol.trim().toUpperCase();
59
- if (up.length === 0)
63
+ const base = symbol.trim().toUpperCase().replace(/(-USD\.P|-PERP|-USD)$/, "");
64
+ if (base.length === 0)
60
65
  throw new PerpError("INVALID_PARAMS", "symbol is empty");
61
- return up.endsWith(ONDO_PERP_SUFFIX) ? up : `${up}${ONDO_PERP_SUFFIX}`;
66
+ return `${base}${ONDO_PERP_SUFFIX}`;
67
+ }
68
+ /** The one "disabled" predicate (listing and every per-symbol read): either source flags it. */
69
+ export function ondoDisabled(pair, contract) {
70
+ return pair.disabled === true || contract?.disabled === true;
71
+ }
72
+ export function ondoMarketStatus(pair, contract) {
73
+ if (ondoDisabled(pair, contract))
74
+ return "disabled";
75
+ if (contract?.isClosed === true)
76
+ return "closed";
77
+ if (contract?.reduceOnly === true)
78
+ return "reduceOnly";
79
+ return "active";
62
80
  }
63
81
  /** Empty string / absent → null. Ondo omits or blanks fields it does not publish. */
64
82
  export function strOrNull(v) {
65
83
  return v === undefined || v === null || v === "" ? null : v;
66
84
  }
85
+ /**
86
+ * A price field that the venue zeroes when it has no value: /v1/perps/contracts reports
87
+ * indexPrice "0" (and lastPrice "0") on disabled contracts (live 2026-10-14: ADBE, BE, CELH, IBM,
88
+ * NOK, OKLO, RDDT, SHEIN, SMCI, IBB, VXX, EURUSD — all `disabled: true`). A price is never 0, so
89
+ * "0" means "no index" → null (unknown), never $0.00. Volume / OI zeros stay: 0 is a real amount.
90
+ */
91
+ export function priceOrNull(v) {
92
+ const s = strOrNull(v);
93
+ return s === null || Number(s) === 0 ? null : s;
94
+ }
67
95
  export function parseOndoTime(iso, what) {
68
96
  const t = Date.parse(iso);
69
97
  if (!Number.isFinite(t))
@@ -139,7 +167,12 @@ function requireRow(m, market, source) {
139
167
  }
140
168
  return row;
141
169
  }
142
- async function fetchMarkets(net) {
170
+ /**
171
+ * Every listed Ondo perp with its state (getMarkets rows carry `status`; VenueMarket has no field
172
+ * for it). Disabled (delisted / paused) listings are NOT listed — per-symbol reads refuse them —
173
+ * while closed / reduce-only markets stay listed with tradable false.
174
+ */
175
+ export async function fetchOndoMarketsWithStatus(net) {
143
176
  const client = ondoPublicClient(net);
144
177
  const pairs = await fetchOndoTradingPairs(client);
145
178
  const contracts = byMarket(await client.publicGet("/v1/perps/contracts", null));
@@ -151,37 +184,47 @@ async function fetchMarkets(net) {
151
184
  if (noMark.length > 0) {
152
185
  console.warn(`[ondo] excluded ${noMark.length} market(s) without a mark price: ${noMark.join(", ")}`);
153
186
  }
154
- return pairs.filter(p => marks[p.market] !== undefined).map((pair) => {
187
+ const out = [];
188
+ for (const pair of pairs.filter(p => marks[p.market] !== undefined)) {
155
189
  const contract = requireRow(contracts, pair.market, "/v1/perps/contracts");
156
190
  const mark = marks[pair.market];
157
191
  const symbol = ondoPlainSymbol(pair.market);
158
192
  const assetClass = ondoAssetClass(pair.market, pair.tags);
159
193
  const { maxLeverage, tiers } = marginTiersOf(pair);
160
- return {
161
- venue: "ondo",
162
- venueSymbol: pair.market,
163
- symbol,
164
- canonical: canonicalFor("ondo", symbol, assetClass),
165
- assetClass,
166
- settle: "USDC",
167
- quoteAsset: pair.pair.quote,
168
- markPrice: mark.markPrice,
169
- indexPrice: strOrNull(contract.indexPrice),
170
- fundingRate: strOrNull(contract.fundingRate),
171
- fundingRateBasis: ONDO_FUNDING_BASIS,
172
- settlementIntervalHours: ONDO_SETTLEMENT_HOURS,
173
- openInterest: strOrNull(contract.openInterest),
174
- volume24h: strOrNull(contract.usdVolume),
175
- tickSize: pair.quoteIncrement,
176
- stepSize: pair.baseIncrement,
177
- minSize: null,
178
- minNotional: null,
179
- maxLeverage,
180
- marginTiers: tiers,
181
- session: sessionOf(pair, mark),
182
- tradable: pair.disabled !== true && !contract.disabled && contract.isClosed !== true && contract.reduceOnly !== true,
183
- };
184
- });
194
+ const status = ondoMarketStatus(pair, contract);
195
+ if (status === "disabled")
196
+ continue;
197
+ out.push({ status, market: {
198
+ venue: "ondo",
199
+ venueSymbol: pair.market,
200
+ symbol,
201
+ canonical: canonicalFor("ondo", symbol, assetClass),
202
+ assetClass,
203
+ settle: "USDC",
204
+ quoteAsset: pair.pair.quote,
205
+ markPrice: mark.markPrice,
206
+ // /v1/perps/contracts `indexPrice` is frozen (live 2026-10-19: BTC 82702.30 unchanged for 17 min
207
+ // while the mark_prices oracle moved 0.5%); the oracle of /v1/perps/mark_prices is the live index.
208
+ indexPrice: priceOrNull(mark.oraclePrice),
209
+ fundingRate: strOrNull(contract.fundingRate),
210
+ fundingRateBasis: ONDO_FUNDING_BASIS,
211
+ settlementIntervalHours: ONDO_SETTLEMENT_HOURS,
212
+ openInterest: strOrNull(contract.openInterest),
213
+ volume24h: strOrNull(contract.usdVolume),
214
+ tickSize: pair.quoteIncrement,
215
+ stepSize: pair.baseIncrement,
216
+ minSize: null,
217
+ minNotional: null,
218
+ maxLeverage,
219
+ marginTiers: tiers,
220
+ session: sessionOf(pair, mark),
221
+ tradable: status === "active",
222
+ } });
223
+ }
224
+ return out;
225
+ }
226
+ async function fetchMarkets(net) {
227
+ return (await fetchOndoMarketsWithStatus(net)).map(r => ({ ...r.market, status: r.status }));
185
228
  }
186
229
  async function fetchFundingRates(net) {
187
230
  const client = ondoPublicClient(net);
@@ -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,