perp-cli 0.14.3 → 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 (311) 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 +2 -2
  23. package/dist/bot/strategies/funding-arb-utils.d.ts +1 -1
  24. package/dist/bot/strategies/funding-arb-utils.js +5 -5
  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 +2 -2
  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 +167 -35
  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/errors.d.ts +24 -1
  73. package/dist/errors.js +25 -3
  74. package/dist/exchanges/arcus/adapter.d.ts +7 -7
  75. package/dist/exchanges/arcus/adapter.js +12 -12
  76. package/dist/exchanges/arcus/agent.d.ts +1 -1
  77. package/dist/exchanges/arcus/agent.js +1 -1
  78. package/dist/exchanges/arcus/client.js +1 -1
  79. package/dist/exchanges/arcus/signing.d.ts +2 -3
  80. package/dist/exchanges/arcus/signing.js +1 -1
  81. package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
  82. package/dist/exchanges/arcus/spot-chain.js +1 -1
  83. package/dist/exchanges/arcus/spot-router.d.ts +1 -1
  84. package/dist/exchanges/arcus/spot-router.js +2 -2
  85. package/dist/exchanges/arcus/spot.d.ts +2 -3
  86. package/dist/exchanges/arcus/spot.js +2 -3
  87. package/dist/exchanges/aster-spot.d.ts +3 -3
  88. package/dist/exchanges/aster-spot.js +6 -9
  89. package/dist/exchanges/aster-typed-data.d.ts +1 -1
  90. package/dist/exchanges/aster-typed-data.js +1 -1
  91. package/dist/exchanges/aster.d.ts +4 -4
  92. package/dist/exchanges/aster.js +31 -13
  93. package/dist/exchanges/builder-fee.d.ts +1 -1
  94. package/dist/exchanges/builder-fee.js +13 -2
  95. package/dist/exchanges/canonical-symbols.d.ts +1 -2
  96. package/dist/exchanges/canonical-symbols.js +26 -2
  97. package/dist/exchanges/derive/client.d.ts +37 -0
  98. package/dist/exchanges/derive/client.js +135 -0
  99. package/dist/exchanges/derive/options.d.ts +68 -0
  100. package/dist/exchanges/derive/options.js +360 -0
  101. package/dist/exchanges/extended/adapter.d.ts +82 -0
  102. package/dist/exchanges/extended/adapter.js +179 -0
  103. package/dist/exchanges/extended/client.d.ts +38 -0
  104. package/dist/exchanges/extended/client.js +88 -0
  105. package/dist/exchanges/extended/public.d.ts +130 -0
  106. package/dist/exchanges/extended/public.js +429 -0
  107. package/dist/exchanges/grvt/adapter.d.ts +86 -0
  108. package/dist/exchanges/grvt/adapter.js +206 -0
  109. package/dist/exchanges/grvt/client.d.ts +25 -0
  110. package/dist/exchanges/grvt/client.js +86 -0
  111. package/dist/exchanges/grvt/public.d.ts +151 -0
  112. package/dist/exchanges/grvt/public.js +438 -0
  113. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  114. package/dist/exchanges/hyperliquid-spot.js +5 -5
  115. package/dist/exchanges/hyperliquid.d.ts +22 -12
  116. package/dist/exchanges/hyperliquid.js +80 -34
  117. package/dist/exchanges/index.d.ts +1 -1
  118. package/dist/exchanges/index.js +1 -1
  119. package/dist/exchanges/interface.d.ts +94 -6
  120. package/dist/exchanges/interface.js +16 -0
  121. package/dist/exchanges/ioc-outcome.d.ts +1 -1
  122. package/dist/exchanges/ioc-outcome.js +1 -1
  123. package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
  124. package/dist/exchanges/legacy-trading-v2.js +7 -2
  125. package/dist/exchanges/lighter-public.d.ts +2 -2
  126. package/dist/exchanges/lighter-public.js +2 -2
  127. package/dist/exchanges/lighter-signer.d.ts +1 -1
  128. package/dist/exchanges/lighter-signer.js +1 -1
  129. package/dist/exchanges/lighter-spot.d.ts +3 -4
  130. package/dist/exchanges/lighter-spot.js +6 -7
  131. package/dist/exchanges/lighter.d.ts +11 -11
  132. package/dist/exchanges/lighter.js +28 -14
  133. package/dist/exchanges/ondo/adapter.d.ts +5 -6
  134. package/dist/exchanges/ondo/adapter.js +16 -14
  135. package/dist/exchanges/ondo/agent.d.ts +1 -1
  136. package/dist/exchanges/ondo/agent.js +1 -1
  137. package/dist/exchanges/ondo/client.d.ts +1 -1
  138. package/dist/exchanges/ondo/client.js +2 -2
  139. package/dist/exchanges/ondo/public.d.ts +8 -1
  140. package/dist/exchanges/ondo/public.js +13 -2
  141. package/dist/exchanges/ondo/spot.d.ts +2 -2
  142. package/dist/exchanges/ondo/spot.js +3 -3
  143. package/dist/exchanges/options-interface.d.ts +158 -0
  144. package/dist/exchanges/options-interface.js +17 -0
  145. package/dist/exchanges/options-venues.d.ts +12 -0
  146. package/dist/exchanges/options-venues.js +35 -0
  147. package/dist/exchanges/pacifica-atomic.d.ts +211 -0
  148. package/dist/exchanges/pacifica-atomic.js +440 -0
  149. package/dist/exchanges/pacifica-decimal.d.ts +10 -0
  150. package/dist/exchanges/pacifica-decimal.js +51 -0
  151. package/dist/exchanges/pacifica-presigned.d.ts +53 -0
  152. package/dist/exchanges/pacifica-presigned.js +141 -0
  153. package/dist/exchanges/pacifica-print.d.ts +200 -0
  154. package/dist/exchanges/pacifica-print.js +417 -0
  155. package/dist/exchanges/pacifica-spot.d.ts +2 -2
  156. package/dist/exchanges/pacifica-spot.js +3 -3
  157. package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
  158. package/dist/exchanges/pacifica-typed-data.js +1 -1
  159. package/dist/exchanges/pacifica-vaults.d.ts +121 -0
  160. package/dist/exchanges/pacifica-vaults.js +264 -0
  161. package/dist/exchanges/pacifica.d.ts +32 -5
  162. package/dist/exchanges/pacifica.js +114 -48
  163. package/dist/exchanges/precision.d.ts +3 -1
  164. package/dist/exchanges/precision.js +9 -2
  165. package/dist/exchanges/qfex/adapter.d.ts +18 -5
  166. package/dist/exchanges/qfex/adapter.js +32 -12
  167. package/dist/exchanges/qfex/agent.d.ts +1 -1
  168. package/dist/exchanges/qfex/agent.js +1 -1
  169. package/dist/exchanges/qfex/client.d.ts +1 -1
  170. package/dist/exchanges/qfex/client.js +1 -1
  171. package/dist/exchanges/qfex/errors.d.ts +1 -1
  172. package/dist/exchanges/qfex/errors.js +2 -2
  173. package/dist/exchanges/qfex/public.d.ts +3 -3
  174. package/dist/exchanges/qfex/public.js +3 -3
  175. package/dist/exchanges/qfex/signing.d.ts +2 -2
  176. package/dist/exchanges/qfex/signing.js +4 -5
  177. package/dist/exchanges/qfex/ws-transport.d.ts +2 -2
  178. package/dist/exchanges/qfex/ws-transport.js +3 -3
  179. package/dist/exchanges/rate-limiter.d.ts +1 -1
  180. package/dist/exchanges/rate-limiter.js +5 -2
  181. package/dist/exchanges/registry.d.ts +1 -1
  182. package/dist/exchanges/registry.js +1 -1
  183. package/dist/exchanges/risex/adapter.d.ts +148 -0
  184. package/dist/exchanges/risex/adapter.js +796 -0
  185. package/dist/exchanges/risex/agent.d.ts +154 -0
  186. package/dist/exchanges/risex/agent.js +384 -0
  187. package/dist/exchanges/risex/client.d.ts +47 -0
  188. package/dist/exchanges/risex/client.js +174 -0
  189. package/dist/exchanges/risex/nonce.d.ts +43 -0
  190. package/dist/exchanges/risex/nonce.js +212 -0
  191. package/dist/exchanges/risex/public.d.ts +64 -0
  192. package/dist/exchanges/risex/public.js +150 -0
  193. package/dist/exchanges/risex/session.d.ts +71 -0
  194. package/dist/exchanges/risex/session.js +171 -0
  195. package/dist/exchanges/risex/typed-data.d.ts +173 -0
  196. package/dist/exchanges/risex/typed-data.js +240 -0
  197. package/dist/exchanges/risex/units.d.ts +29 -0
  198. package/dist/exchanges/risex/units.js +104 -0
  199. package/dist/exchanges/slippage.d.ts +1 -1
  200. package/dist/exchanges/slippage.js +1 -1
  201. package/dist/exchanges/spot-factory.d.ts +3 -3
  202. package/dist/exchanges/spot-factory.js +7 -7
  203. package/dist/exchanges/spot-interface.d.ts +5 -5
  204. package/dist/exchanges/spot-interface.js +2 -2
  205. package/dist/exchanges/spot-result.d.ts +3 -3
  206. package/dist/exchanges/spot-result.js +3 -3
  207. package/dist/exchanges/trading-v2.d.ts +3 -3
  208. package/dist/exchanges/trading-v2.js +9 -6
  209. package/dist/exchanges/txflow/adapter.d.ts +54 -0
  210. package/dist/exchanges/txflow/adapter.js +118 -0
  211. package/dist/exchanges/txflow/client.d.ts +47 -0
  212. package/dist/exchanges/txflow/client.js +153 -0
  213. package/dist/exchanges/txflow/public.d.ts +76 -0
  214. package/dist/exchanges/txflow/public.js +284 -0
  215. package/dist/exchanges/unknown-symbol.d.ts +29 -0
  216. package/dist/exchanges/unknown-symbol.js +51 -0
  217. package/dist/exchanges/variational/adapter.d.ts +71 -0
  218. package/dist/exchanges/variational/adapter.js +137 -0
  219. package/dist/exchanges/variational/client.d.ts +21 -0
  220. package/dist/exchanges/variational/client.js +66 -0
  221. package/dist/exchanges/variational/public.d.ts +126 -0
  222. package/dist/exchanges/variational/public.js +430 -0
  223. package/dist/exchanges/venue-types.d.ts +6 -7
  224. package/dist/exchanges/venue-types.js +2 -2
  225. package/dist/exchanges/venue.d.ts +51 -8
  226. package/dist/exchanges/venue.js +54 -4
  227. package/dist/exchanges/venues/arcus.d.ts +1 -2
  228. package/dist/exchanges/venues/arcus.js +5 -4
  229. package/dist/exchanges/venues/aster.d.ts +2 -2
  230. package/dist/exchanges/venues/aster.js +6 -4
  231. package/dist/exchanges/venues/create-helpers.d.ts +1 -1
  232. package/dist/exchanges/venues/create-helpers.js +3 -3
  233. package/dist/exchanges/venues/extended.d.ts +21 -0
  234. package/dist/exchanges/venues/extended.js +96 -0
  235. package/dist/exchanges/venues/grvt.d.ts +30 -0
  236. package/dist/exchanges/venues/grvt.js +98 -0
  237. package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
  238. package/dist/exchanges/venues/hyperliquid.js +10 -8
  239. package/dist/exchanges/venues/index.d.ts +7 -2
  240. package/dist/exchanges/venues/index.js +8 -2
  241. package/dist/exchanges/venues/lighter-family.d.ts +7 -9
  242. package/dist/exchanges/venues/lighter-family.js +9 -11
  243. package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
  244. package/dist/exchanges/venues/lighter-rh.js +7 -5
  245. package/dist/exchanges/venues/lighter.d.ts +1 -1
  246. package/dist/exchanges/venues/lighter.js +5 -3
  247. package/dist/exchanges/venues/ondo.d.ts +1 -1
  248. package/dist/exchanges/venues/ondo.js +5 -3
  249. package/dist/exchanges/venues/pacifica.d.ts +2 -2
  250. package/dist/exchanges/venues/pacifica.js +6 -4
  251. package/dist/exchanges/venues/qfex.d.ts +1 -1
  252. package/dist/exchanges/venues/qfex.js +4 -2
  253. package/dist/exchanges/venues/risex-release.d.ts +14 -0
  254. package/dist/exchanges/venues/risex-release.js +14 -0
  255. package/dist/exchanges/venues/risex.d.ts +31 -0
  256. package/dist/exchanges/venues/risex.js +116 -0
  257. package/dist/exchanges/venues/txflow.d.ts +18 -0
  258. package/dist/exchanges/venues/txflow.js +77 -0
  259. package/dist/exchanges/venues/variational.d.ts +21 -0
  260. package/dist/exchanges/venues/variational.js +85 -0
  261. package/dist/execution-log.d.ts +1 -1
  262. package/dist/funding/legacy-public.d.ts +1 -1
  263. package/dist/funding/legacy-public.js +9 -4
  264. package/dist/funding/normalize.d.ts +1 -1
  265. package/dist/funding/normalize.js +1 -1
  266. package/dist/funding/rates.d.ts +44 -4
  267. package/dist/funding/rates.js +56 -12
  268. package/dist/index.js +7 -6
  269. package/dist/jobs.d.ts +1 -0
  270. package/dist/jobs.js +25 -1
  271. package/dist/landing.d.ts +2 -0
  272. package/dist/landing.js +1 -1
  273. package/dist/mcp-pacifica-earn.d.ts +9 -0
  274. package/dist/mcp-pacifica-earn.js +58 -0
  275. package/dist/mcp-server.js +113 -59
  276. package/dist/pacifica/client.d.ts +67 -4
  277. package/dist/pacifica/client.js +170 -8
  278. package/dist/pacifica/rate-limit.d.ts +144 -0
  279. package/dist/pacifica/rate-limit.js +267 -0
  280. package/dist/pacifica/types/order.d.ts +2 -1
  281. package/dist/plan-executor.js +7 -2
  282. package/dist/rebalance.js +1 -1
  283. package/dist/risk.d.ts +2 -1
  284. package/dist/risk.js +2 -4
  285. package/dist/settings.d.ts +32 -3
  286. package/dist/signer/ows-evm.d.ts +1 -1
  287. package/dist/signer/ows-evm.js +1 -1
  288. package/dist/smart-order.d.ts +1 -1
  289. package/dist/smart-order.js +1 -1
  290. package/dist/strategies/funding-arb.d.ts +1 -1
  291. package/dist/strategies/funding-arb.js +1 -1
  292. package/dist/trade-validator.js +2 -2
  293. package/dist/utils.d.ts +12 -3
  294. package/dist/utils.js +11 -3
  295. package/dist/venue-health.d.ts +40 -0
  296. package/dist/venue-health.js +75 -0
  297. package/dist/withdraw-gate.d.ts +1 -1
  298. package/dist/withdraw-gate.js +1 -1
  299. package/dist/ws/index.d.ts +959 -0
  300. package/dist/ws/index.js +4295 -0
  301. package/dist/ws/node.d.ts +975 -0
  302. package/dist/ws/node.js +4366 -0
  303. package/dist/ws/pacifica-instrument.d.ts +8 -0
  304. package/dist/ws/pacifica-instrument.js +8 -0
  305. package/dist/ws-cli/credentials.d.ts +62 -0
  306. package/dist/ws-cli/credentials.js +126 -0
  307. package/package.json +58 -10
  308. package/skills/perp-cli/SKILL.md +62 -9
  309. package/skills/perp-cli/references/agent-operations.md +7 -0
  310. package/skills/perp-cli/references/commands.md +76 -5
  311. package/skills/perp-cli/references/ws-library.md +492 -0
@@ -18,6 +18,7 @@
18
18
  * Docs: https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation
19
19
  * Base: https://fapi.asterdex.com
20
20
  */
21
+ import { nonEmptyOrListed } from "./unknown-symbol.js";
21
22
  import { accountCacheEpoch } from "../cache.js";
22
23
  import { isExpired } from "../agent-wallet/expiry.js";
23
24
  import { classifyError, DEFINITE_ORDER_FAILURE_CODES, isTransportFailure, orderNotSent, orderOutcomeUnknown, PerpError, sendFailure } from "../errors.js";
@@ -30,7 +31,7 @@ import { approvalFromMax, bpsToRate, builderGateOf, NO_BUILDER, rateToBps, requi
30
31
  import { ASTER_CLIENT_ID_RE, ASTER_FUNDS_CHAINS, asterVenue } from "./venues/aster.js";
31
32
  import { assertAsterWithdrawAmount, assertAsterWithdrawSignature, parseAsterWithdrawLimits, requireAsterFundsNetwork, } from "./aster-funds.js";
32
33
  import { nextAsterNonce } from "./aster-nonce.js";
33
- /** Common TIF (§13.3) → Aster perp timeInForce. Live exchangeInfo lists GTC/IOC/GTX/HIDDEN — no FOK. */
34
+ /** Common TIF → Aster perp timeInForce. Live exchangeInfo lists GTC/IOC/GTX/HIDDEN — no FOK. */
34
35
  function asterTif(raw) {
35
36
  const t = raw.toUpperCase();
36
37
  if (t === "GTC" || t === "IOC" || t === "GTX")
@@ -72,6 +73,16 @@ export function asterIocOutcome(raw) {
72
73
  const filledSize = plainQty(r.executedQty);
73
74
  return filledSize === null ? null : { filledSize, final: true };
74
75
  }
76
+ /** The numeric `code` of an Aster error body ({"code":-1121,"msg":"Invalid symbol."}); null when absent. */
77
+ function asterErrorCode(body) {
78
+ try {
79
+ const j = JSON.parse(body);
80
+ return typeof j.code === "number" ? j.code : null;
81
+ }
82
+ catch {
83
+ return null; // not JSON (e.g. an HTML error page): no venue code
84
+ }
85
+ }
75
86
  /**
76
87
  * Order-placement refusals by documented venue code (asterdex/api-docs V3 futures "Error
77
88
  * Codes"; "Codes are universal,but messages can vary"). [PerpError code, docs name, remediation].
@@ -283,7 +294,7 @@ export class AsterAdapter {
283
294
  return null;
284
295
  }
285
296
  }
286
- // ── Spot host access (design §12, used by AsterSpotAdapter) ──
297
+ // ── Spot host access (used by AsterSpotAdapter) ──
287
298
  /**
288
299
  * Aster spot REST host. Spot shares the perp EIP-712 Domain B signing
289
300
  * (chainId 1666 mainnet / 714 testnet) and the per-agent nonce space.
@@ -466,7 +477,7 @@ export class AsterAdapter {
466
477
  this._ingestPriceFilters(info.symbols);
467
478
  }
468
479
  if (!this._priceFilters.has(apiSymbol)) {
469
- throw new PerpError("SYMBOL_NOT_FOUND", `Aster market ${apiSymbol} not found in exchangeInfo`, { exchange: "aster" });
480
+ throw new PerpError("SYMBOL_NOT_FOUND", `Aster market ${apiSymbol} not found in exchangeInfo`, { exchange: "aster", remediation: "List markets: perp -e aster market list" });
470
481
  }
471
482
  const f = this._priceFilters.get(apiSymbol);
472
483
  if (!f) {
@@ -475,7 +486,7 @@ export class AsterAdapter {
475
486
  return f;
476
487
  }
477
488
  /**
478
- * Declared protection-price reference (§13.10 #5): the venue mark price from
489
+ * Declared protection-price reference: the venue mark price from
479
490
  * GET /fapi/v3/premiumIndex — the same price PERCENT_PRICE is evaluated against.
480
491
  * No runtime substitute (no mid / last fallback): unavailable → PRICE_STALE.
481
492
  */
@@ -555,7 +566,7 @@ export class AsterAdapter {
555
566
  side: t.isBuyerMaker ? "sell" : "buy",
556
567
  price: String(t.price ?? "0"),
557
568
  size: String(t.qty ?? "0"),
558
- fee: "0",
569
+ fee: null, // the public tape carries no fee
559
570
  }));
560
571
  }
561
572
  async getFundingHistory(symbol, limit = 10) {
@@ -566,7 +577,8 @@ export class AsterAdapter {
566
577
  if (!Array.isArray(data)) {
567
578
  throw new PerpError("EXCHANGE_ERROR", `Aster fundingRate history is not a list (${JSON.stringify(data).slice(0, 120)})`, { exchange: "aster" });
568
579
  }
569
- return data.map((d) => {
580
+ // `200 []` for an unlisted symbol (live 2026-10-14): checked against the market directory.
581
+ return nonEmptyOrListed(this, symbol, data.map((d) => {
570
582
  const time = Number(d.fundingTime);
571
583
  if (d.fundingTime === undefined || d.fundingTime === null || !Number.isFinite(time) || time <= 0) {
572
584
  throw new PerpError("EXCHANGE_ERROR", `Aster fundingRate row without a numeric fundingTime: ${JSON.stringify(d).slice(0, 160)}`, { exchange: "aster" });
@@ -576,7 +588,7 @@ export class AsterAdapter {
576
588
  rate: venueRateField(d.fundingRate, "fundingRate fundingRate", "aster"),
577
589
  price: d.markPrice ? String(d.markPrice) : null,
578
590
  };
579
- });
591
+ }), this._fromApi(this._toApi(symbol)));
580
592
  }
581
593
  /**
582
594
  * Candles over [startTime, endTime]. GET /fapi/v3/klines returns at most 1500 bars per request
@@ -607,10 +619,11 @@ export class AsterAdapter {
607
619
  for (const k of data) {
608
620
  const time = Number(k[0]);
609
621
  const trades = Number(k[8]);
610
- if (!Number.isFinite(time) || !Number.isFinite(trades)) {
611
- throw new PerpError("EXCHANGE_ERROR", `Aster kline row without open time / trade count: ${JSON.stringify(k).slice(0, 120)}`, { exchange: "aster" });
622
+ const numeric = (v) => (typeof v === "string" || typeof v === "number") && String(v).trim() !== "" && Number.isFinite(Number(v));
623
+ if (!Number.isFinite(time) || !Number.isFinite(trades) || !numeric(k[5]) || !numeric(k[7])) {
624
+ throw new PerpError("EXCHANGE_ERROR", `Aster kline row without open time / volume / quote volume / trade count: ${JSON.stringify(k).slice(0, 120)}`, { exchange: "aster" });
612
625
  }
613
- out.push({ time, open: String(k[1]), high: String(k[2]), low: String(k[3]), close: String(k[4]), volume: String(k[5]), trades });
626
+ out.push({ time, open: String(k[1]), high: String(k[2]), low: String(k[3]), close: String(k[4]), volume: String(k[5]), quoteVolume: String(k[7]), trades });
614
627
  }
615
628
  if (data.length < ASTER_KLINE_PAGE)
616
629
  break;
@@ -975,7 +988,7 @@ export class AsterAdapter {
975
988
  }
976
989
  // ── Trading ──
977
990
  async marketOrder(symbol, side, size, opts) {
978
- // Design §13.2 / §13.10 #6: never native MARKET (no price cap) — an IOC LIMIT at the
991
+ // Never native MARKET (no price cap) — an IOC LIMIT at the
979
992
  // protection price (mark ± opts.slippage.bps, tick-aligned toward crossing, inside
980
993
  // PRICE_FILTER / PERCENT_PRICE). newOrderRespType=RESULT makes the response carry the
981
994
  // final IOC status (FILLED / EXPIRED) and executedQty.
@@ -1072,7 +1085,7 @@ export class AsterAdapter {
1072
1085
  const clientOrderId = opts?.clientOrderId === undefined ? null : requireAsterClientOrderId(opts.clientOrderId);
1073
1086
  let params;
1074
1087
  try {
1075
- // Common TIF names (§13.3): GTC / IOC / ALO→GTX (GTX accepted as alias); FOK unsupported.
1088
+ // Common TIF names: GTC / IOC / ALO→GTX (GTX accepted as alias); FOK unsupported.
1076
1089
  const tif = asterTif(opts?.tif ?? "GTC");
1077
1090
  // Price must already be on PRICE_FILTER.tickSize — validated, never rounded (INVALID_TICK).
1078
1091
  const px = requireOnTick(price, (await this._symbolPriceFilters(apiSymbol)).tickSize);
@@ -1104,7 +1117,7 @@ export class AsterAdapter {
1104
1117
  /**
1105
1118
  * Native modify (PUT /fapi/v3/order, V3 futures docs "Modify Order"): LIMIT orders
1106
1119
  * only; TIF (incl. GTX) and reduceOnly stay on the order. `size` is the NEW UNFILLED
1107
- * quantity (§13.10 #10); the venue's `quantity` is the order total, so
1120
+ * quantity; the venue's `quantity` is the order total, so
1108
1121
  * quantity = size + executedQty (read just before). A fill landing between the read
1109
1122
  * and the PUT can only shrink the remainder, never over-size it. A venue rejection
1110
1123
  * leaves the original order untouched (docs).
@@ -1757,6 +1770,11 @@ export class AsterAdapter {
1757
1770
  if (!res.ok) {
1758
1771
  const text = await res.text();
1759
1772
  const clean = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim().slice(0, 120);
1773
+ // -1121 BAD_SYMBOL ("Invalid symbol.", live on /fapi/v3/depth for an unlisted symbol): the
1774
+ // symbol does not exist — SYMBOL_NOT_FOUND, not an UNKNOWN 500.
1775
+ if (asterErrorCode(text) === -1121) {
1776
+ throw new PerpError("SYMBOL_NOT_FOUND", `Aster: ${clean} [${method} ${path}]`, { exchange: "aster", remediation: "List markets: perp -e aster market list" });
1777
+ }
1760
1778
  throw new Error(`${method} ${path} failed (${res.status}): ${clean}`);
1761
1779
  }
1762
1780
  return res.json();
@@ -21,7 +21,7 @@
21
21
  * `builder: { b: <address>, f: <tenths of a bp> }`; max 0.1% perps / 1% spot; status
22
22
  * info `{ type: "maxBuilderFee", user, builder }`. HIP-3 dexes are not covered by the
23
23
  * docs → never attached there.
24
- * - pacifica https://pacifica.gitbook.io/docs/programs/builder-program.md
24
+ * - pacifica https://docs.pacifica.fi/programs/builder-program.md
25
25
  * approve_builder_code { builder_code, max_fee_rate } signed by the account; per-order
26
26
  * `builder_code` on create_market / create / stop/create / positions/tpsl. TWAP
27
27
  * (create_twap_order) does not document builder_code → never sent there.
@@ -21,7 +21,7 @@
21
21
  * `builder: { b: <address>, f: <tenths of a bp> }`; max 0.1% perps / 1% spot; status
22
22
  * info `{ type: "maxBuilderFee", user, builder }`. HIP-3 dexes are not covered by the
23
23
  * docs → never attached there.
24
- * - pacifica https://pacifica.gitbook.io/docs/programs/builder-program.md
24
+ * - pacifica https://docs.pacifica.fi/programs/builder-program.md
25
25
  * approve_builder_code { builder_code, max_fee_rate } signed by the account; per-order
26
26
  * `builder_code` on create_market / create / stop/create / positions/tpsl. TWAP
27
27
  * (create_twap_order) does not document builder_code → never sent there.
@@ -49,6 +49,7 @@
49
49
  */
50
50
  import { PerpError, classifyError, isDefiniteOrderFailure, isTransportFailure, isUnknownOutcomeServerError } from "../errors.js";
51
51
  import { isVenueId } from "./venue-types.js";
52
+ import { VARIATIONAL_READ_ONLY_REMEDIATION } from "./venues/variational.js";
52
53
  export const BUILDER_FEE_CAPABILITIES = {
53
54
  hyperliquid: {
54
55
  approval: "master", identity: "address", perpCapBps: 10, spotCapBps: 100, hip3: false, testnet: true,
@@ -56,7 +57,7 @@ export const BUILDER_FEE_CAPABILITIES = {
56
57
  },
57
58
  pacifica: {
58
59
  approval: "master", identity: "code", perpCapBps: 10, spotCapBps: null, hip3: null, testnet: true,
59
- feeStepBps: 0.01, feeKind: "max", docs: "https://pacifica.gitbook.io/docs/programs/builder-program.md",
60
+ feeStepBps: 0.01, feeKind: "max", docs: "https://docs.pacifica.fi/programs/builder-program.md",
60
61
  },
61
62
  aster: {
62
63
  approval: "master", identity: "address", perpCapBps: 10, spotCapBps: null, hip3: null, testnet: false,
@@ -73,11 +74,21 @@ export const BUILDER_FEE_CAPABILITIES = {
73
74
  lighter: null,
74
75
  "lighter-rh": null,
75
76
  arcus: null,
77
+ risex: null,
78
+ txflow: null,
79
+ variational: null,
80
+ grvt: null,
81
+ extended: null,
76
82
  };
77
83
  /** Why a venue has no builder-fee support (shown in CAPABILITY_UNSUPPORTED). */
78
84
  export const BUILDER_FEE_UNSUPPORTED = {
79
85
  lighter: "not yet implemented in perp-cli — the official lighter-go signer can sign ApproveIntegrator and the per-order integrator fields, but perp-cli sends them as 0 (https://apidocs.lighter.xyz/docs/partner-integration)",
80
86
  "lighter-rh": "not yet implemented in perp-cli — Lighter RH uses the same official lighter-go signer; partner-attribution fields are sent as 0 (https://apidocs.rh.lighter.xyz/docs/partner-attribution.md)",
87
+ risex: "not implemented yet in perp-cli (RISEx builder codes are planned; note the venue's \"bps\" field is parts-per-million)",
88
+ txflow: "TxFlow is integrated through an unofficial read-only API (market data only)",
89
+ variational: VARIATIONAL_READ_ONLY_REMEDIATION,
90
+ grvt: "GRVT is read-only (market data) in this version; trading is planned for a later release",
91
+ extended: "Extended is available for market data only in this version (trading is planned for a later release)",
81
92
  arcus: "Arcus has no perps builder fee; its integrator fee applies to the spot router only and needs a partner key from Arcus (https://docs.arcus.xyz/concepts/spot-router)",
82
93
  };
83
94
  export function builderFeeUnsupported(venue) {
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Cross-venue canonical symbols — the single mapping source used to decide
3
- * that two markets on different venues are the same underlying
4
- * (design §4 L4, §10.9).
3
+ * that two markets on different venues are the same underlying.
5
4
  *
6
5
  * Rules by asset class:
7
6
  * - crypto: ticker identity after the adapter strips its quote suffix, plus
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Cross-venue canonical symbols — the single mapping source used to decide
3
- * that two markets on different venues are the same underlying
4
- * (design §4 L4, §10.9).
3
+ * that two markets on different venues are the same underlying.
5
4
  *
6
5
  * Rules by asset class:
7
6
  * - crypto: ticker identity after the adapter strips its quote suffix, plus
@@ -47,19 +46,44 @@ export const EXPLICIT_CANONICAL = {
47
46
  "hl:xyz|SILVER": "SILVER",
48
47
  "qfex|GOLD": "GOLD",
49
48
  "qfex|SILVER": "SILVER",
49
+ "risex|XAU": "GOLD",
50
+ "risex|XAG": "SILVER",
51
+ "txflow|XAU": "GOLD",
52
+ "txflow|XAG": "SILVER",
53
+ "variational|XAU": "GOLD",
54
+ "variational|XAG": "SILVER",
55
+ "grvt|XAU": "GOLD",
56
+ "grvt|XAG": "SILVER",
57
+ "extended|XAU": "GOLD",
58
+ "extended|XAG": "SILVER",
50
59
  // Crude oil (WTI)
51
60
  "ondo|WTI": "CRUDE_OIL_WTI",
52
61
  "hl:xyz|CL": "CRUDE_OIL_WTI",
53
62
  "hl:flx|OIL": "CRUDE_OIL_WTI",
54
63
  // QFEX CL blends expiring futures; roll-week gaps are caught by the cross-venue price filter.
55
64
  "qfex|CL": "CRUDE_OIL_WTI",
65
+ "risex|CL": "CRUDE_OIL_WTI",
66
+ // Brent crude
67
+ "risex|BZ": "CRUDE_OIL_BRENT",
68
+ "txflow|BZ": "CRUDE_OIL_BRENT",
69
+ "txflow|CL": "CRUDE_OIL_WTI",
70
+ "variational|BZ": "CRUDE_OIL_BRENT",
71
+ "variational|CL": "CRUDE_OIL_WTI",
72
+ "grvt|BZ": "CRUDE_OIL_BRENT",
73
+ "grvt|CL": "CRUDE_OIL_WTI",
74
+ "extended|WTI": "CRUDE_OIL_WTI",
75
+ "extended|XBR": "CRUDE_OIL_BRENT",
56
76
  // US equity indices
57
77
  "ondo|US500": "SPX",
58
78
  "ondo|US100": "NDX",
59
79
  "qfex|US500": "SPX",
60
80
  "qfex|US100": "NDX",
81
+ // Extended SPX500m / TECH100m: S&P 500 / Nasdaq-100 index perps (uiName SPX-USD / NDX-USD).
82
+ "extended|SPX500M": "SPX",
83
+ "extended|TECH100M": "NDX",
61
84
  // FX
62
85
  "ondo|USDJPY": "USDJPY",
86
+ "extended|USDJPY": "USDJPY",
63
87
  };
64
88
  /** Pairs verified to be DIFFERENT products despite similar names. Never match. */
65
89
  export const NEVER_MATCH = new Set([
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Derive (derive.xyz, formerly Lyra) v3 public JSON-RPC over HTTP — official API, no auth.
3
+ *
4
+ * - `POST <base>/public/<method>` with the params object as the body → `{id, result}` or
5
+ * `{id, error: {code, message, data}}` (docs.derive.xyz/error-codes.md; live 2026-10-18).
6
+ * - Errors are all PerpErrors, each with a remediation: transport failure → EXCHANGE_UNREACHABLE;
7
+ * HTTP 429 or RPC -32000..-32099 → RATE_LIMITED (retry hint from the RPC `data` "Retry after {ms} ms",
8
+ * else the `retry-after` header); any other RPC error → EXCHANGE_ERROR. A JSON-RPC -32602 is a
9
+ * Derive-side rejection too: user input is validated before any request, so it is never reported as
10
+ * the user's INVALID_PARAMS. Every RPC error carries `details.rpcCode` / `rpcData` so callers can
11
+ * classify the venue code (12001 instrument_not_found → SYMBOL_NOT_FOUND is done by the caller,
12
+ * which knows what was asked).
13
+ * - Public budget (docs rate-limits.md, mainnet reference): 5 TPS per IP, FIXED 5 s window of 25
14
+ * points, 1 point per request. The local bucket (burst 5, 4/s) allows at most 5 + 4·5 = 25 in any 5 s.
15
+ */
16
+ import { PerpError } from "../../errors.js";
17
+ import type { RateLimitPolicy } from "../venue.js";
18
+ import type { Network } from "../venue-types.js";
19
+ export declare const DERIVE_REST_URLS: Readonly<Record<Network, string>>;
20
+ export declare const DERIVE_HTTP_TIMEOUT_MS = 15000;
21
+ export declare const DERIVE_RATE_LIMIT: RateLimitPolicy;
22
+ /** Derive application code for an unknown instrument / currency (`instrument_not_found`). */
23
+ export declare const DERIVE_INSTRUMENT_NOT_FOUND = 12001;
24
+ /** JSON-RPC invalid params — Derive also uses it for "Ticker not found for instrument" (live 2026-10-18). */
25
+ export declare const DERIVE_INVALID_PARAMS = -32602;
26
+ /** Shape drift: an explicit EXCHANGE_ERROR, never a guessed value. */
27
+ export declare function deriveShapeError(what: string): PerpError;
28
+ /** The RPC error code a Derive call failed with, or null (not an RPC error). */
29
+ export declare function deriveRpcCode(e: unknown): number | null;
30
+ export declare class DeriveClient {
31
+ readonly network: Network;
32
+ private readonly baseUrl;
33
+ private readonly rateLimit;
34
+ constructor(network: Network, baseUrl?: string, rateLimit?: RateLimitPolicy);
35
+ /** `public/<method>`; resolves with `result`, every failure is a PerpError. */
36
+ call<T = unknown>(method: string, params: Record<string, unknown>): Promise<T>;
37
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Derive (derive.xyz, formerly Lyra) v3 public JSON-RPC over HTTP — official API, no auth.
3
+ *
4
+ * - `POST <base>/public/<method>` with the params object as the body → `{id, result}` or
5
+ * `{id, error: {code, message, data}}` (docs.derive.xyz/error-codes.md; live 2026-10-18).
6
+ * - Errors are all PerpErrors, each with a remediation: transport failure → EXCHANGE_UNREACHABLE;
7
+ * HTTP 429 or RPC -32000..-32099 → RATE_LIMITED (retry hint from the RPC `data` "Retry after {ms} ms",
8
+ * else the `retry-after` header); any other RPC error → EXCHANGE_ERROR. A JSON-RPC -32602 is a
9
+ * Derive-side rejection too: user input is validated before any request, so it is never reported as
10
+ * the user's INVALID_PARAMS. Every RPC error carries `details.rpcCode` / `rpcData` so callers can
11
+ * classify the venue code (12001 instrument_not_found → SYMBOL_NOT_FOUND is done by the caller,
12
+ * which knows what was asked).
13
+ * - Public budget (docs rate-limits.md, mainnet reference): 5 TPS per IP, FIXED 5 s window of 25
14
+ * points, 1 point per request. The local bucket (burst 5, 4/s) allows at most 5 + 4·5 = 25 in any 5 s.
15
+ */
16
+ import { PerpError, extractErrorMessage } from "../../errors.js";
17
+ import { acquireRate, rateLimitedError } from "../rate-limiter.js";
18
+ export const DERIVE_REST_URLS = {
19
+ mainnet: "https://api.derive.xyz/v3",
20
+ testnet: "https://testnet.api.derive.xyz/v3",
21
+ };
22
+ export const DERIVE_HTTP_TIMEOUT_MS = 15_000;
23
+ export const DERIVE_RATE_LIMIT = { capacity: 5, refillPerSec: 4, weights: null };
24
+ /** Derive application code for an unknown instrument / currency (`instrument_not_found`). */
25
+ export const DERIVE_INSTRUMENT_NOT_FOUND = 12001;
26
+ /** JSON-RPC invalid params — Derive also uses it for "Ticker not found for instrument" (live 2026-10-18). */
27
+ export const DERIVE_INVALID_PARAMS = -32602;
28
+ const UNREACHABLE_REMEDIATION = "Check the network / api.derive.xyz status and retry";
29
+ const RATE_LIMIT_REMEDIATION = "Derive's public budget is 5 requests/s per IP (25 per 5 s window); wait and retry";
30
+ /** Shape drift: an explicit EXCHANGE_ERROR, never a guessed value. */
31
+ export function deriveShapeError(what) {
32
+ return new PerpError("EXCHANGE_ERROR", `Derive API returned an unexpected shape: ${what}`, {
33
+ exchange: "derive",
34
+ remediation: "Derive's v3 API response changed; perp-cli needs an update to read it",
35
+ });
36
+ }
37
+ /** The RPC error code a Derive call failed with, or null (not an RPC error). */
38
+ export function deriveRpcCode(e) {
39
+ if (!(e instanceof PerpError))
40
+ return null;
41
+ const code = e.structured.details?.rpcCode;
42
+ return typeof code === "number" ? code : null;
43
+ }
44
+ export class DeriveClient {
45
+ network;
46
+ baseUrl;
47
+ rateLimit;
48
+ constructor(network, baseUrl = DERIVE_REST_URLS[network], rateLimit = DERIVE_RATE_LIMIT) {
49
+ this.network = network;
50
+ this.baseUrl = baseUrl;
51
+ this.rateLimit = rateLimit;
52
+ }
53
+ /** `public/<method>`; resolves with `result`, every failure is a PerpError. */
54
+ async call(method, params) {
55
+ await acquireRate(`derive:${this.baseUrl}`, this.rateLimit, method);
56
+ const what = `Derive public/${method}`;
57
+ let res;
58
+ try {
59
+ res = await fetch(`${this.baseUrl}/public/${method}`, {
60
+ method: "POST",
61
+ headers: { "content-type": "application/json" },
62
+ body: JSON.stringify(params),
63
+ signal: AbortSignal.timeout(DERIVE_HTTP_TIMEOUT_MS),
64
+ });
65
+ }
66
+ catch (e) {
67
+ throw new PerpError("EXCHANGE_UNREACHABLE", `${what}: ${extractErrorMessage(e)}`, { exchange: "derive", remediation: UNREACHABLE_REMEDIATION });
68
+ }
69
+ const text = await res.text().catch((e) => {
70
+ throw new PerpError("EXCHANGE_UNREACHABLE", `${what}: response body unreadable (${extractErrorMessage(e)})`, {
71
+ exchange: "derive", remediation: UNREACHABLE_REMEDIATION,
72
+ });
73
+ });
74
+ let body;
75
+ try {
76
+ body = JSON.parse(text);
77
+ }
78
+ catch {
79
+ body = undefined;
80
+ }
81
+ const rpcError = body !== null && typeof body === "object" ? body.error : undefined;
82
+ const hasRpcError = rpcError !== undefined && rpcError !== null;
83
+ if (res.status === 429) {
84
+ // The RPC body's "Retry after N ms" is preferred; the retry-after header only when the body has none.
85
+ const fromRpc = hasRpcError ? retryAfterMsOf(rpcError) : null;
86
+ if (fromRpc !== null)
87
+ throw rateLimited(`${what}: HTTP 429`, fromRpc, rpcCodeOf(rpcError));
88
+ throw rateLimitedError("derive", res.headers.get("retry-after"), text);
89
+ }
90
+ if (hasRpcError)
91
+ throw rpcErrorOf(what, rpcError);
92
+ if (!res.ok) {
93
+ throw new PerpError("EXCHANGE_ERROR", `${what}: HTTP ${res.status} ${text.slice(0, 200)}`, {
94
+ exchange: "derive", status: res.status, remediation: "Derive answered with an HTTP error; check api.derive.xyz status and retry",
95
+ });
96
+ }
97
+ if (body === undefined)
98
+ throw deriveShapeError(`${method} returned a non-JSON body`);
99
+ if (body === null || typeof body !== "object" || !("result" in body))
100
+ throw deriveShapeError(`${method} returned no result`);
101
+ return body.result;
102
+ }
103
+ }
104
+ function rpcErrorOf(what, err) {
105
+ if (err === null || typeof err !== "object")
106
+ return deriveShapeError(`${what}: error is not an object`);
107
+ const { code, message, data } = err;
108
+ if (typeof code !== "number" || typeof message !== "string")
109
+ return deriveShapeError(`${what}: error without code / message`);
110
+ const detail = typeof data === "string" && data !== "" && data !== message ? `${message} (${data})` : message;
111
+ if (code <= -32000 && code >= -32099)
112
+ return rateLimited(`${what}: ${detail}`, retryAfterMsOf(err), code);
113
+ return new PerpError("EXCHANGE_ERROR", `${what}: request rejected by Derive: ${detail} [code ${code}]`, {
114
+ exchange: "derive", rpcCode: code, rpcData: typeof data === "string" ? data : null,
115
+ remediation: "Retry later; if it persists, Derive's API changed and perp-cli needs an update",
116
+ });
117
+ }
118
+ function rateLimited(message, retryAfterMs, rpcCode) {
119
+ return new PerpError("RATE_LIMITED", message, {
120
+ exchange: "derive",
121
+ ...(rpcCode !== null ? { rpcCode } : {}),
122
+ ...(retryAfterMs !== null ? { retryAfterMs } : {}),
123
+ remediation: RATE_LIMIT_REMEDIATION,
124
+ });
125
+ }
126
+ function rpcCodeOf(err) {
127
+ const code = err !== null && typeof err === "object" ? err.code : undefined;
128
+ return typeof code === "number" ? code : null;
129
+ }
130
+ /** "Retry after {ms} ms" from an RPC error's `data`, or null. */
131
+ function retryAfterMsOf(err) {
132
+ const data = err !== null && typeof err === "object" ? err.data : undefined;
133
+ const m = typeof data === "string" ? /Retry after (\d+) ?ms/i.exec(data) : null;
134
+ return m !== null ? Number(m[1]) : null;
135
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Derive options — read-only `OptionsVenue` over the v3 public API (official, no auth).
3
+ *
4
+ * Reads (docs.derive.xyz/openapi.json, shapes checked live 2026-10-18):
5
+ * - `get_all_currencies` → currencies with an `option` asset (underlyings).
6
+ * - `get_all_instruments {instrument_type:"option", currency, expired:false, page_size:1000, page}`
7
+ * → instruments; `option_details {expiry (unix s), strike, option_type "C"|"P"}`. Paginated
8
+ * (`pagination.num_pages`, page_size ≤ 1000). `is_active:false` = listed for a future activation
9
+ * (`scheduled_activation`), not tradable and with no ticker — left out of expiries / chains.
10
+ * An unknown currency is RPC 12001.
11
+ * - `get_tickers {instrument_type:"option", currency, expiry_date:"YYYYMMDD"}` → `{tickers: {name: TickerSlim}}`;
12
+ * expiry_date is required for options. ONE call per chain (never one per instrument).
13
+ * - `get_instrument` + `get_ticker` for a single quote (unknown instrument = RPC 12001).
14
+ *
15
+ * Ticker encoding (live): an empty book side is price "0" / size "0" → `null` side; an IV the
16
+ * pricer could not compute (no quote, or a quote below intrinsic) is "0" → `null`; 24 h high / low
17
+ * are "0" when there were no trades → `null`. Greeks are passed through verbatim (a delta of 0 is real).
18
+ */
19
+ import type { Network } from "../venue-types.js";
20
+ import { type OptionChain, type OptionChainQuery, type OptionExpiries, type OptionExpiry, type OptionKind, type OptionQuote, type OptionsVenue, type OptionUnderlyings } from "../options-interface.js";
21
+ import { DeriveClient } from "./client.js";
22
+ /** One option instrument, as perp-cli uses it. */
23
+ export interface DeriveOptionInstrument {
24
+ name: string;
25
+ underlying: string;
26
+ quoteCurrency: string;
27
+ kind: OptionKind;
28
+ strike: string;
29
+ expiryMs: number;
30
+ /** UTC YYYY-MM-DD of the expiry. */
31
+ expiry: string;
32
+ isActive: boolean;
33
+ /** ms epoch of `scheduled_activation`. */
34
+ activationMs: number;
35
+ }
36
+ /** ms epoch → UTC YYYY-MM-DD. */
37
+ export declare function utcDate(ms: number): string;
38
+ /** User expiry, exactly "2026-12-25" or "20261225" (not mixed) → YYYY-MM-DD; anything else INVALID_PARAMS. */
39
+ export declare function normalizeExpiry(input: string): string;
40
+ /** User underlying → venue currency (upper case); INVALID_PARAMS for anything that is not a ticker. */
41
+ export declare function normalizeUnderlying(input: string): string;
42
+ export declare function parseDeriveInstrument(raw: unknown): DeriveOptionInstrument;
43
+ /** A ticker_slim snapshot (get_ticker / get_tickers value) of `inst` → OptionQuote. */
44
+ export declare function parseDeriveOptionTicker(raw: unknown, inst: DeriveOptionInstrument): OptionQuote;
45
+ /** Distinct active expiries of `instruments`, soonest first. */
46
+ export declare function expiriesOf(instruments: readonly DeriveOptionInstrument[]): OptionExpiry[];
47
+ /** The `n` strikes closest to `price` (ties → lower strike), returned ascending. */
48
+ export declare function strikesAround(strikes: readonly string[], price: string, n: number): string[];
49
+ /**
50
+ * Chain for one expiry from its instruments and the `get_tickers` map. Rows come from the listed
51
+ * instruments; a listed instrument without a ticker leaves its side null and is named in
52
+ * `missingTickers`. Index / forward are those of the newest ticker (largest `t`) — one ticker's values,
53
+ * not an average over the expiry.
54
+ */
55
+ export declare function buildChain(underlying: string, expiry: OptionExpiry, selection: OptionChain["expirySelection"], instruments: readonly DeriveOptionInstrument[], tickersRaw: unknown, strikesAroundAtm: number | null): OptionChain;
56
+ export declare class DeriveOptionsVenue implements OptionsVenue {
57
+ private readonly client;
58
+ readonly id = "derive";
59
+ readonly displayName = "Derive";
60
+ constructor(client: DeriveClient);
61
+ static forNetwork(network: Network): DeriveOptionsVenue;
62
+ listUnderlyings(): Promise<OptionUnderlyings>;
63
+ /** Every non-expired option instrument of `underlying` (all pages). 12001 → SYMBOL_NOT_FOUND. */
64
+ instruments(underlying: string): Promise<DeriveOptionInstrument[]>;
65
+ listExpiries(underlyingInput: string): Promise<OptionExpiries>;
66
+ getChain(q: OptionChainQuery): Promise<OptionChain>;
67
+ getQuote(instrumentInput: string): Promise<OptionQuote>;
68
+ }