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
  * QFEX adapter — native TradingV2 + legacy ExchangeAdapter shim
3
- * (design §14.3–§14.6 as amended by §14.12; §10.7, §10.10, §13).
4
3
  *
5
4
  * Signer: imported HMAC API key only (QfexAgentMeta → ~/.perp/qfex-agents/<publicKey>.json).
6
5
  * Orders go over the Trade WebSocket (ws-transport.ts); account reads, leverage and transfers
@@ -51,7 +50,7 @@ import { QFEX_BANKER_NETWORK, QFEX_ENDPOINTS, QFEX_WITHDRAW_DISABLED, QFEX_WITHD
51
50
  import { loadQfexCredentials, QFEX_IMPORT_REMEDIATION } from "./agent.js";
52
51
  import { QfexClient } from "./client.js";
53
52
  import { qfexStatusClass } from "./errors.js";
54
- import { fetchQfexInstruments, qfexPlainSymbol, qfexPublicData, qfexTradable, qfexVenueSymbol, } from "./public.js";
53
+ import { buildQfexFundingRates, buildQfexMarkets, fetchQfexInstruments, fetchQfexMarketsWithInstruments, qfexFundingWindowClosed, qfexIndexPrices, qfexListedStatus, qfexPlainSymbol, qfexPublicData, qfexTradable, qfexVenueSymbol, } from "./public.js";
55
54
  import { QFEX_AUTOMATION_VERIFIED, QFEX_MODIFY_KEEPS_CLIENT_ID, QFEX_REDUCE_ONLY_WIRE, QFEX_STOP_CLIENT_ID_ECHO, decimalsEqual, fromWireNumber, isZeroDecimal, qfexAutomationGate, qfexReduceOnlyGate, reduceOnlyWireValue, subDecimals, sumDecimals, toWireNumber, weightedAveragePrice, } from "./signing.js";
56
55
  import { QFEX_IDLE_CLOSE_MS, QFEX_MDS_TIMEOUT_MS, QFEX_TIMEOUTS, fetchQfexMarketStats, qfexPendingRemediation, qfexTradeSession, } from "./ws-transport.js";
57
56
  /** A mark price entry older than this (its own venue `time`) is not used as an order reference. */
@@ -78,6 +77,24 @@ const STOP_TYPES = new Set(["TAKE_PROFIT", "STOP_LOSS"]);
78
77
  const KLINE_RESOLUTION = {
79
78
  "1m": "1MIN", "5m": "5MINS", "15m": "15MINS", "30m": "30MINS", "1h": "1HOUR", "4h": "4HOURS", "1d": "1DAY",
80
79
  };
80
+ /**
81
+ * Candle volumes. GET /candles returns `usdVolume` (populated) and `baseTokenVolume`, which the
82
+ * venue leaves "0" on traded candles (live 2026-10-14, CL-USD 1HOUR: baseTokenVolume "0" with
83
+ * usdVolume "52534.48506" on every row; startingOpenInterest / orderbookMidPrice* are "0" too).
84
+ * A base "0" next to a non-zero USD volume is a placeholder → null (unknown), never 0; when
85
+ * both are zero the candle really had no volume.
86
+ */
87
+ export function qfexCandleVolumes(c) {
88
+ const decimal = (v, what) => {
89
+ if (typeof v !== "string" || v.trim() === "" || !Number.isFinite(Number(v))) {
90
+ throw new PerpError("EXCHANGE_ERROR", `QFEX candles: ${what} is not a decimal string (${JSON.stringify(v)})`, { exchange: "qfex" });
91
+ }
92
+ return v;
93
+ };
94
+ const quoteVolume = decimal(c.usdVolume, "usdVolume");
95
+ const base = decimal(c.baseTokenVolume, "baseTokenVolume");
96
+ return { volume: Number(base) === 0 && Number(quoteVolume) !== 0 ? null : base, quoteVolume };
97
+ }
81
98
  /** Client order id sent to the venue: a fresh UUID (the format the venue itself generates). */
82
99
  export function qfexClientOrderId() {
83
100
  return randomUUID();
@@ -262,7 +279,21 @@ export class QfexAdapter {
262
279
  const venueSymbol = qfexVenueSymbol(symbol);
263
280
  const i = (await this.instrumentMap()).get(venueSymbol);
264
281
  if (i === undefined) {
265
- throw new PerpError("SYMBOL_NOT_FOUND", `QFEX has no market ${venueSymbol}`, { exchange: "qfex", remediation: "List markets with: perp market list -e qfex" });
282
+ throw new PerpError("SYMBOL_NOT_FOUND", `QFEX has no market ${venueSymbol}`, { exchange: "qfex", remediation: "List markets: perp -e qfex market list" });
283
+ }
284
+ return i;
285
+ }
286
+ /**
287
+ * Instrument of a per-symbol read (book / funding / kline / info). Unknown → SYMBOL_NOT_FOUND; a
288
+ * listed market whose /refdata status is not ACTIVE (DELISTED, INACTIVE) → SYMBOL_NOT_FOUND with
289
+ * that status: it is not a live market. A non-USD ACTIVE market is readable (only orders refuse it).
290
+ */
291
+ async readableInstrument(symbol) {
292
+ const i = await this.instrumentOf(symbol);
293
+ if (i.status !== "ACTIVE") {
294
+ throw new PerpError("SYMBOL_NOT_FOUND", `QFEX ${i.venueSymbol} is ${i.status.toLowerCase()} (status ${i.status}) — not a live market`, {
295
+ exchange: "qfex", status: i.status, remediation: "List tradable markets with: perp market list -e qfex",
296
+ });
266
297
  }
267
298
  return i;
268
299
  }
@@ -339,7 +370,7 @@ export class QfexAdapter {
339
370
  stopOrderGate() {
340
371
  if (QFEX_STOP_CLIENT_ID_ECHO === true)
341
372
  return null;
342
- return unsupported("qfex", "stopOrder", "the stop order acknowledgement is not documented to echo a client order id, so a stop cannot be correlated safely (Q21)", "Set stops / TP / SL on the QFEX web app until docs/TEST-PLAN.md QFEX item Q21 passes");
373
+ return unsupported("qfex", "stopOrder", "the stop order acknowledgement is not documented to echo a client order id, so a stop cannot be correlated safely (Q21)", "Set stops / TP / SL on the QFEX web app until QFEX item Q21 passes");
343
374
  }
344
375
  /**
345
376
  * One reduce-only stop-market order (add_order with order_type TAKE_PROFIT / STOP_LOSS).
@@ -571,7 +602,7 @@ export class QfexAdapter {
571
602
  /**
572
603
  * A reduce-only order the venue did not confirm as reduce-only (`reduce_only` false or absent
573
604
  * on an order frame — reduceOnlyUnconfirmedFrame — or on a read-back row of THIS order). The order may be live as a normal order, so the outcome is
574
- * unknown (ORDER_PENDING, design §14.12 #4e: never re-sent). It is NOT cancelled here: a cancel
605
+ * unknown (ORDER_PENDING, never re-sent). It is NOT cancelled here: a cancel
575
606
  * is a second mutating command with its own unknown outcome and cannot undo a fill.
576
607
  */
577
608
  reduceOnlyEchoError(i, orderId, clientOrderId, echo, venueRaw) {
@@ -746,7 +777,7 @@ export class QfexAdapter {
746
777
  }
747
778
  if (isZeroDecimal(filled)) {
748
779
  if (ctx.immediate) {
749
- // A terminal frame with nothing filled — IOC_CANCELLED or CANCELLED alike (§13.10 #4).
780
+ // A terminal frame with nothing filled — IOC_CANCELLED or CANCELLED alike.
750
781
  throw new PerpError("ORDER_REJECTED", `QFEX ${i.venueSymbol}: the ${t.time_in_force} order filled nothing and was cancelled (${t.status})`, {
751
782
  exchange: "qfex", reason: "iocNoFill", venueStatus: t.status, orderId: add.orderId, clientOrderId: add.clientOrderId, venueRaw,
752
783
  remediation: "Nothing was filled. Widen --slippage-bps (within the price band), reduce the size, or place a resting limit order",
@@ -781,7 +812,7 @@ export class QfexAdapter {
781
812
  }
782
813
  /**
783
814
  * Final filled size of an order placed through the legacy shim — non-null ONLY when a venue
784
- * terminal frame was received (design §14.12 #3). A pending result is never an outcome.
815
+ * terminal frame was received. A pending result is never an outcome.
785
816
  */
786
817
  iocOutcomeOf(raw) {
787
818
  const r = raw;
@@ -1094,24 +1125,70 @@ export class QfexAdapter {
1094
1125
  return { venue: "qfex", symbol: i.symbol, leverage: raw.leverage, marginMode: "cross", venueRaw: raw };
1095
1126
  }
1096
1127
  // ── Legacy ExchangeAdapter: market data ──
1128
+ /**
1129
+ * Legacy rows plus `tradable` (ACTIVE and USD-quoted, qfexTradable), the /refdata `status`
1130
+ * (ACTIVE / INACTIVE / DELISTED) and `quoteAsset` (prices of KRW / JPY / CNY / SEK / HKD markets
1131
+ * are in that currency, not USD).
1132
+ */
1097
1133
  async getMarkets() {
1098
- return (await this.getVenueMarkets()).map(toLegacyMarketInfo);
1134
+ const { markets, instruments, volumeNotional } = await fetchQfexMarketsWithInstruments(this.network);
1135
+ const bySymbol = new Map(instruments.map(i => [i.venueSymbol, i]));
1136
+ return markets.map(m => this.marketRow(m, bySymbol.get(m.venueSymbol), volumeNotional));
1137
+ }
1138
+ marketRow(m, i, volumeNotional) {
1139
+ if (i === undefined)
1140
+ throw new PerpError("EXCHANGE_ERROR", `QFEX ${m.venueSymbol} is missing from /refdata`, { exchange: "qfex" });
1141
+ return {
1142
+ ...toLegacyMarketInfo(m), tradable: m.tradable, status: qfexListedStatus(i), quoteAsset: i.quoteAsset,
1143
+ volume24hQuote: volumeNotional.get(i.venueSymbol) ?? null,
1144
+ };
1099
1145
  }
1146
+ /**
1147
+ * `market info` of one ACTIVE market. fundingRate is the CURRENT /md/contracts `funding_rate`
1148
+ * (the rate `market funding` / `arb scan --rates --include-unverified` show), labelled fundingVerified false: QFEX funding
1149
+ * is unverified (Q4, display only). Market lists keep fundingRate null so no scan / bot reads it.
1150
+ */
1151
+ async getMarketInfo(symbol) {
1152
+ const i = await this.readableInstrument(symbol);
1153
+ const snap = await fetchQfexMarketStats(QFEX_ENDPOINTS[this.network].mdsWs, i.venueSymbol, QFEX_MDS_TIMEOUT_MS);
1154
+ const contracts = await this.client.publicGet("/md/contracts", null);
1155
+ const { markets } = buildQfexMarkets([i], snap, Date.now(), qfexIndexPrices(listOf(contracts.data, "/md/contracts data")));
1156
+ if (markets.length === 0)
1157
+ throw new PerpError("PRICE_STALE", `QFEX ${i.venueSymbol}: no mark price in the market_stats snapshot`, { exchange: "qfex" });
1158
+ const { rates } = buildQfexFundingRates([i], listOf(contracts.data, "/md/contracts data").filter(c => c.ticker_id === i.venueSymbol), snap.marks, Date.now());
1159
+ return {
1160
+ ...this.marketRow(markets[0], i, snap.volumeNotional),
1161
+ fundingRate: rates.length === 0 ? null : rates[0].rate,
1162
+ // true = the window in progress is outside the market's funding hours (fundingRate null).
1163
+ marketClosed: rates.length === 0 ? null : rates[0].marketClosed === true,
1164
+ fundingVerified: false,
1165
+ fundingNote: "QFEX funding rate UNVERIFIED (not confirmed against settled payments) — display only, never a basis for trading",
1166
+ };
1167
+ }
1168
+ /** `quoteAsset`: level prices are in the market's quote currency (KRW for SAMSUNG-KRW). */
1100
1169
  async getOrderbook(symbol) {
1101
- const venueSymbol = qfexVenueSymbol(symbol);
1170
+ // Resolved against refdata first: an unknown symbol is SYMBOL_NOT_FOUND, not the venue's
1171
+ // HTTP 404 surfacing as a retryable EXCHANGE_ERROR.
1172
+ const instrument = await this.readableInstrument(symbol);
1173
+ const venueSymbol = instrument.venueSymbol;
1102
1174
  const book = await this.client.publicGet(`/md/orderbook/${encodeURIComponent(venueSymbol)}`, null);
1103
1175
  // The venue lists empty price levels (quantity "0"); they are not part of the book.
1104
1176
  const side = (v, what) => listOf(v, what)
1105
1177
  .filter(l => Array.isArray(l) && typeof l[0] === "string" && typeof l[1] === "string" && Number(l[1]) > 0)
1106
1178
  .map(l => [l[0], l[1]]);
1107
- return { bids: side(book.bids, "orderbook bids"), asks: side(book.asks, "orderbook asks") };
1179
+ return { bids: side(book.bids, "orderbook bids"), asks: side(book.asks, "orderbook asks"), quoteAsset: instrument.quoteAsset };
1108
1180
  }
1109
1181
  async getRecentTrades() {
1110
1182
  throw unsupported("qfex", "recentTrades", "the documented public trade endpoint (GET /public/trades) answers 404", "Use the order book: perp market book <symbol> -e qfex");
1111
1183
  }
1112
- /** Hourly funding windows (GET /funding/{symbol}, intervalMinutes 60). `limit` = hours back (default 24). */
1184
+ /**
1185
+ * Hourly funding windows (GET /funding/{symbol}, intervalMinutes 60). `limit` = hours back (default 24).
1186
+ * A window outside the market's funding hours (qfexFundingWindowClosed) is `rate: null,
1187
+ * marketClosed: true` — the venue prints 0 there, which is "no funding", not a 0 rate.
1188
+ */
1113
1189
  async getFundingHistory(symbol, limit) {
1114
- const venueSymbol = qfexVenueSymbol(symbol);
1190
+ const instrument = await this.readableInstrument(symbol);
1191
+ const venueSymbol = instrument.venueSymbol;
1115
1192
  const hours = limit === undefined ? 24 : limit;
1116
1193
  if (!Number.isInteger(hours) || hours < 1)
1117
1194
  throw new PerpError("INVALID_PARAMS", `funding history limit must be a positive integer of hours (got ${hours})`);
@@ -1126,23 +1203,31 @@ export class QfexAdapter {
1126
1203
  const time = typeof r.windowStart === "string" ? Date.parse(r.windowStart) : NaN;
1127
1204
  if (!Number.isFinite(time))
1128
1205
  throw new PerpError("EXCHANGE_ERROR", `QFEX funding history: invalid windowStart ${JSON.stringify(r.windowStart)}`);
1129
- out.push({ time, rate: plainNumber(r.rate, "funding rate"), price: null });
1206
+ const rate = plainNumber(r.rate, "funding rate");
1207
+ const marketClosed = qfexFundingWindowClosed(instrument.marketHours, time, Number(rate));
1208
+ out.push({ time, rate: marketClosed ? null : rate, price: null, intervalHours: 1, rateKind: marketClosed ? null : "lastSettled", marketClosed });
1130
1209
  }
1131
1210
  return out.sort((a, b) => a.time - b.time);
1132
1211
  }
1212
+ /** Rows carry `quoteAsset`: OHLC prices are in the market's quote currency (KRW for SAMSUNG-KRW). */
1133
1213
  async getKlines(symbol, interval, startTime, endTime) {
1134
1214
  const resolution = KLINE_RESOLUTION[interval.trim()];
1135
1215
  if (resolution === undefined) {
1136
1216
  throw new PerpError("INVALID_PARAMS", `Unsupported kline interval "${interval}" on QFEX`, { remediation: `Use one of: ${Object.keys(KLINE_RESOLUTION).join(", ")}` });
1137
1217
  }
1138
- const body = await this.client.publicGet(`/candles/${encodeURIComponent(qfexVenueSymbol(symbol))}`, {
1218
+ const instrument = await this.readableInstrument(symbol);
1219
+ const venueSymbol = instrument.venueSymbol;
1220
+ const body = await this.client.publicGet(`/candles/${encodeURIComponent(venueSymbol)}`, {
1139
1221
  resolution, fromISO: new Date(startTime).toISOString(), toISO: new Date(endTime).toISOString(),
1140
1222
  });
1141
1223
  return listOf(body.candles, "candles").map(c => {
1142
1224
  const time = Date.parse(c.startedAt);
1143
1225
  if (!Number.isFinite(time))
1144
1226
  throw new PerpError("EXCHANGE_ERROR", `QFEX candles: invalid startedAt "${c.startedAt}"`);
1145
- return { time, open: c.open, high: c.high, low: c.low, close: c.close, volume: c.baseTokenVolume, trades: null };
1227
+ const v = qfexCandleVolumes(c);
1228
+ // `usdVolume` is Σ price × qty in the QUOTE currency (live 2026-10-19: SAMSUNG-KRW 1m candles
1229
+ // equal the KRW trade tape exactly); quoteVolume is USD → null for a non-USD quote.
1230
+ return { time, open: c.open, high: c.high, low: c.low, close: c.close, ...v, quoteVolume: instrument.quoteAsset === "USD" ? v.quoteVolume : null, trades: null, quoteAsset: instrument.quoteAsset };
1146
1231
  }).sort((a, b) => a.time - b.time);
1147
1232
  }
1148
1233
  // ── Legacy ExchangeAdapter: account ──
@@ -1258,7 +1343,7 @@ export class QfexAdapter {
1258
1343
  async getFundingPayments() {
1259
1344
  throw unsupported("qfex", "fundingPayments", "funding payments are GET /executions rows, but the execution_type that marks them is not documented (Q10)", "See funding in the QFEX web app; net funding per position is in: perp account positions -e qfex --json");
1260
1345
  }
1261
- // ── Legacy ExchangeAdapter: trading (shim → TradingV2, design §10.7). Client ids are generated here. ──
1346
+ // ── Legacy ExchangeAdapter: trading (shim → TradingV2). Client ids are generated here. ──
1262
1347
  async marketOrder(symbol, side, size, opts) {
1263
1348
  return this.placeOrder({
1264
1349
  symbol, side, type: "market", size, price: null, triggerPrice: null, triggerSource: null, tif: null,
@@ -1294,7 +1379,7 @@ export class QfexAdapter {
1294
1379
  reduceOnly: opts?.reduceOnly === true, clientOrderId: qfexClientOrderId(), slippage: null, tpsl: null,
1295
1380
  });
1296
1381
  }
1297
- // ── Funds (design §14.6, §14.12 #8) ──
1382
+ // ── Funds ──
1298
1383
  /**
1299
1384
  * Deposit address for USDC on Arbitrum One (banker GET /address). The address is returned
1300
1385
  * only when the venue confirms exactly that network and asset — anything else is refused,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX API key import (design §14.5 as amended by §14.12 #9).
2
+ * QFEX API key import.
3
3
  *
4
4
  * Keys are created on the QFEX website (Developer Settings, 2FA) — there is no programmatic
5
5
  * approval. The public key + secret are entered via hidden prompt / stdin only (never flags,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX API key import (design §14.5 as amended by §14.12 #9).
2
+ * QFEX API key import.
3
3
  *
4
4
  * Keys are created on the QFEX website (Developer Settings, 2FA) — there is no programmatic
5
5
  * approval. The public key + secret are entered via hidden prompt / stdin only (never flags,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX raw REST client (design §14.1, §14.4, §14.6): raw fetch + node:crypto HMAC, no SDK.
2
+ * QFEX raw REST client: raw fetch + node:crypto HMAC, no SDK.
3
3
  *
4
4
  * Two hosts: the API (`api.qfex.*` — market data, account reads, leverage, subaccount
5
5
  * transfer) and the banker (`banker.qfex.*` — deposit address, withdraw). Both take the same
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX raw REST client (design §14.1, §14.4, §14.6): raw fetch + node:crypto HMAC, no SDK.
2
+ * QFEX raw REST client: raw fetch + node:crypto HMAC, no SDK.
3
3
  *
4
4
  * Two hosts: the API (`api.qfex.*` — market data, account reads, leverage, subaccount
5
5
  * transfer) and the banker (`banker.qfex.*` — deposit address, withdraw). Both take the same
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX venue status / error → PerpError (design §14.3 error table, §14.12 #3, #8, #11).
2
+ * QFEX venue status / error → PerpError.
3
3
  *
4
4
  * Three sources:
5
5
  * - order_response.status engine verdict on one order (definite once correlated);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * QFEX venue status / error → PerpError (design §14.3 error table, §14.12 #3, #8, #11).
2
+ * QFEX venue status / error → PerpError.
3
3
  *
4
4
  * Three sources:
5
5
  * - order_response.status engine verdict on one order (definite once correlated);
@@ -90,7 +90,7 @@ const STATUS_REMEDIATION = {
90
90
  CANNOT_MODIFY_PARTIAL_FILL: "A partially filled order cannot be modified — cancel it and place a new order",
91
91
  CANNOT_MODIFY_ALO_WOULD_CROSS: "The new price would cross the book — choose a passive price, or cancel and place a LIMIT order",
92
92
  REJECTED_MARKET_CLOSED: "The market is closed for this order right now — check the session: perp market info <symbol> -e qfex",
93
- INVALID_CLIENT_ORDER_ID: "The venue refused the generated client order id — report it (docs/TEST-PLAN.md QFEX Q18)",
93
+ INVALID_CLIENT_ORDER_ID: "The venue refused the generated client order id — report it (QFEX item Q18)",
94
94
  };
95
95
  /** "… retry after 200ms" / "… retry after 2 seconds" → ms; null when the message carries no hint. */
96
96
  export function parseRetryAfterMs(message) {
@@ -1,21 +1,30 @@
1
1
  /**
2
- * QFEX public market data (design §14.8 as amended by §14.12 #6, #7, #10).
2
+ * QFEX public market data.
3
3
  *
4
4
  * Sources (all public):
5
5
  * GET /refdata instruments: tick, lot, min/max quantity, price band, status,
6
6
  * quote / margin asset, allowed order types + TIFs, market hours
7
7
  * MDS market_stats snapshot mark price (each entry with its own `time`), open interest,
8
8
  * 24h notional volume
9
- * GET /md/contracts funding_rate / next_funding_rate_timestamp (funding reads only)
9
+ * GET /md/contracts funding_rate / next_funding_rate_timestamp (funding reads);
10
+ * index_price (market rows, best effort)
10
11
  *
11
- * Coupling (§14.12 #10): order-entry precision comes from /refdata ALONE (fetchQfexInstruments).
12
- * Markets need /refdata + the MDS snapshot. Only funding reads need /md/contracts — its failure
13
- * fails fetchFundingRates, never market listing or order entry.
12
+ * Coupling: order-entry precision comes from /refdata ALONE (fetchQfexInstruments).
13
+ * Markets need /refdata + the MDS snapshot; /md/contracts only adds indexPrice — its failure
14
+ * leaves indexPrice null (unknown) with a warning, never fails market listing or order entry.
15
+ * Funding reads need /md/contracts.
16
+ *
17
+ * indexPrice = /md/contracts `index_price` (docs contract-specifications: the Index Price is "the
18
+ * reference spot price for the asset"; the OpenAPI field has no description). Live 2026-10-19 it
19
+ * tracked the MDS `underlier` channel (AAPL 342.47 = 342.47, GOLD 4170.22 vs 4170.05, SAMSUNG-KRW
20
+ * 220092 vs 220082 KRW), moved on 122 of 222 markets within 45 s, and sat a median 0.015 % from
21
+ * the mark (max 5 %, CVNA pre-market). It is in the quote currency (`index_currency`), like the mark.
22
+ * /md/contracts lists ACTIVE markets only; a non-positive / missing value is null.
14
23
  *
15
24
  * Funding is UNVERIFIED (venue flag fundingVerified: false, Q4): the rate's unit is not
16
25
  * confirmed against settled payments and `funding_rate` vs `next_funding_rate` is undecided.
17
26
  * Markets therefore carry fundingRate null; fetchFundingRates returns the raw `funding_rate`
18
- * for the explicit, flagged reads only (`perp funding`, `arb scan --include-unverified`).
27
+ * for the explicit, flagged reads only (`arb scan --rates|--compare --include-unverified`).
19
28
  *
20
29
  * Symbols: "AAPL-USD" → CLI symbol "AAPL". Non-USD quotes keep the suffix ("SKHYNIX-KRW"),
21
30
  * have no canonical symbol and are NOT tradable through perp-cli (USD-assuming sizing / risk).
@@ -60,6 +69,14 @@ export interface QfexContractRow {
60
69
  funding_rate: string;
61
70
  next_funding_rate: string;
62
71
  next_funding_rate_timestamp: number;
72
+ /** Index (reference spot) price in the quote currency — see the module header. */
73
+ index_price?: string;
74
+ /**
75
+ * NOT USD on a non-USD market despite the name: live 2026-10-19 SAMSUNG-KRW 1,234,418 =
76
+ * open_interest 5.58 × index 221,118 → KRW (as is target_volume). Nothing reads it; never wire it
77
+ * up as a USD figure. OI is reported in base units (VenueMarket.openInterest, MDS open_interests).
78
+ */
79
+ open_interest_usd?: string;
63
80
  }
64
81
  /** One instrument, as order entry needs it — from /refdata only. */
65
82
  export interface QfexInstrument {
@@ -89,18 +106,43 @@ export declare function qfexPlainSymbol(venueSymbol: string): string;
89
106
  export declare function qfexVenueSymbol(symbol: string): string;
90
107
  export declare function qfexAssetClass(row: Pick<QfexRefdataRow, "symbol" | "product_category" | "base_asset">): AssetClass;
91
108
  export declare function toQfexInstrument(raw: unknown): QfexInstrument;
92
- /** Is the market one perp-cli may send orders to? USD-quoted and ACTIVE (§14.12 #7). */
109
+ /** Is the market one perp-cli may send orders to? USD-quoted and ACTIVE. */
93
110
  export declare function qfexTradable(i: Pick<QfexInstrument, "status" | "quoteAsset">): boolean;
94
111
  /**
95
- * Whether `nowMs` falls inside a `session: "open"` window of the instrument's market_hours
96
- * (weekday + local time in its `tz`). null = the venue published no usable schedule.
97
- * This is the underlying's regular session only — it is NOT used to infer whether funding
98
- * accrues (the schedule and the documented funding hours do not map 1:1, Q9).
112
+ * Whether `nowMs` falls inside a `session: "open"` window of the instrument's market_hours.
113
+ * null = the venue published no usable schedule. This is the underlying's regular session only;
114
+ * funding hours are qfexFundingWindowClosed's question.
99
115
  */
100
116
  export declare function qfexUnderlyingOpen(marketHours: Record<string, unknown> | null, nowMs: number): boolean | null;
117
+ /**
118
+ * Was the funding window labelled `windowStartMs` outside the market's funding hours? Docs
119
+ * (docs.qfex.com/qfex/funding): "Funding is only active during a symbol's configured
120
+ * market_hours"; outside them "funding rate is 0 and no funding payment is exchanged".
121
+ *
122
+ * The window labelled T covers the hour BEFORE T (live 2026-10-19, GET /funding, 9 symbols over
123
+ * 10 days: AAPL-USD prints its last non-zero rate at Sat 00:00Z = Fri 20:00 ET and its first at
124
+ * Mon 01:00Z = Sun 21:00 ET, i.e. funding Sun 20:00–Fri 20:00 ET as documented, only when each
125
+ * label is read as the end of its hour).
126
+ *
127
+ * market_hours alone does not say whether an unlisted gap funds (US equities fund overnight
128
+ * between post_market and pre_market; Korean equities do not fund outside "open"), so a window is
129
+ * closed when the venue printed exactly 0 AND no part of the hour was in a funding session
130
+ * (anything but "weekend" / "close"). A non-zero rate is never marked closed, and a 0 inside a
131
+ * session stays a real 0. No usable schedule → never closed.
132
+ *
133
+ * Known limits (review B2-4 / B2-5):
134
+ * - market holidays are not in market_hours: a holiday inside a configured weekday session
135
+ * (SAMSUNG-KRW on Hangul Day) prints the venue's 0 as a real-looking "0", marketClosed false;
136
+ * - US equities fund in the weekday overnight gap (post_market → pre_market, docs "Sunday 20:00 –
137
+ * Friday 20:00 ET"), so a gap is never closed by schedule alone — only an exact 0 there is, and
138
+ * a genuine 0 rate in such a gap is (rarely) reported as closed.
139
+ */
140
+ export declare function qfexFundingWindowClosed(marketHours: Record<string, unknown> | null, windowStartMs: number, rate: number): boolean;
101
141
  export declare function qfexPublicClient(net: Network): QfexClient;
102
142
  /** Every instrument of /refdata (all statuses). The only input of order-entry validation. */
103
143
  export declare function fetchQfexInstruments(client: QfexClient): Promise<QfexInstrument[]>;
144
+ /** venue symbol → /md/contracts `index_price` (positive finite only). */
145
+ export declare function qfexIndexPrices(contracts: readonly Partial<QfexContractRow>[]): Map<string, string>;
104
146
  export declare function buildQfexMarkets(instruments: readonly QfexInstrument[], snap: {
105
147
  marks: ReadonlyMap<string, {
106
148
  price: string;
@@ -108,10 +150,21 @@ export declare function buildQfexMarkets(instruments: readonly QfexInstrument[],
108
150
  }>;
109
151
  openInterest: ReadonlyMap<string, string>;
110
152
  volumeNotional: ReadonlyMap<string, string>;
111
- }, nowMs: number): {
153
+ }, nowMs: number, indexPrices?: ReadonlyMap<string, string>): {
112
154
  markets: VenueMarket[];
113
155
  activeWithoutMark: string[];
114
156
  };
157
+ /**
158
+ * Priced markets, the /refdata instruments they were built from (status / quote asset per row) and
159
+ * the 24h notional per venue symbol in its QUOTE currency (KRW for SAMSUNG-KRW).
160
+ */
161
+ export declare function fetchQfexMarketsWithInstruments(net: Network): Promise<{
162
+ markets: VenueMarket[];
163
+ instruments: QfexInstrument[];
164
+ volumeNotional: ReadonlyMap<string, string>;
165
+ }>;
166
+ /** "active" = ACTIVE and USD-quoted; "nonUsdQuote" = ACTIVE, quoted in KRW / JPY / … (readable, never ordered). */
167
+ export declare function qfexListedStatus(i: Pick<QfexInstrument, "status" | "quoteAsset" | "venueSymbol">): "active" | "nonUsdQuote";
115
168
  export declare function buildQfexFundingRates(instruments: readonly QfexInstrument[], contracts: readonly QfexContractRow[], marks: ReadonlyMap<string, {
116
169
  price: string;
117
170
  time: number;