perp-cli 0.12.17 → 0.14.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 (361) hide show
  1. package/README.md +247 -27
  2. package/dist/_stderr-filter.js +0 -1
  3. package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
  4. package/dist/agent-wallet/lighter-keystore.js +92 -10
  5. package/dist/agent-wallet/passphrase.d.ts +1 -1
  6. package/dist/agent-wallet/passphrase.js +16 -3
  7. package/dist/agent-wallet/store.d.ts +37 -4
  8. package/dist/agent-wallet/store.js +132 -0
  9. package/dist/agent-wallet/venue-keystore.d.ts +14 -0
  10. package/dist/agent-wallet/venue-keystore.js +121 -0
  11. package/dist/api/public/hyperliquid.js +4 -4
  12. package/dist/api/public/lighter.js +5 -5
  13. package/dist/api/public/pacifica.d.ts +0 -1
  14. package/dist/api/public/pacifica.js +4 -13
  15. package/dist/api/public/urls.d.ts +9 -1
  16. package/dist/api/public/urls.js +35 -13
  17. package/dist/arb/file-lock.d.ts +11 -0
  18. package/dist/arb/file-lock.js +74 -0
  19. package/dist/arb/index.d.ts +2 -2
  20. package/dist/arb/index.js +2 -2
  21. package/dist/arb/pending-entry.d.ts +32 -0
  22. package/dist/arb/pending-entry.js +77 -0
  23. package/dist/arb/sizing.d.ts +6 -13
  24. package/dist/arb/sizing.js +12 -51
  25. package/dist/arb/state.d.ts +45 -12
  26. package/dist/arb/state.js +214 -42
  27. package/dist/arb/utils.d.ts +20 -6
  28. package/dist/arb/utils.js +43 -39
  29. package/dist/bot/conditions.d.ts +4 -4
  30. package/dist/bot/conditions.js +11 -6
  31. package/dist/bot/config.d.ts +17 -1
  32. package/dist/bot/config.js +48 -12
  33. package/dist/bot/engine.d.ts +61 -2
  34. package/dist/bot/engine.js +610 -144
  35. package/dist/bot/fill-size.d.ts +7 -0
  36. package/dist/bot/fill-size.js +18 -0
  37. package/dist/bot/index.d.ts +1 -1
  38. package/dist/bot/index.js +1 -1
  39. package/dist/bot/perp-order.d.ts +99 -0
  40. package/dist/bot/perp-order.js +299 -0
  41. package/dist/bot/presets.d.ts +7 -0
  42. package/dist/bot/presets.js +17 -0
  43. package/dist/bot/quarantine.d.ts +75 -0
  44. package/dist/bot/quarantine.js +190 -0
  45. package/dist/bot/quote-rules.d.ts +23 -0
  46. package/dist/bot/quote-rules.js +69 -0
  47. package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
  48. package/dist/bot/strategies/avellaneda-mm.js +72 -70
  49. package/dist/bot/strategies/basis-arb.js +2 -0
  50. package/dist/bot/strategies/dca-strategy.d.ts +2 -0
  51. package/dist/bot/strategies/dca-strategy.js +5 -0
  52. package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
  53. package/dist/bot/strategies/funding-arb-strategy.js +571 -380
  54. package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
  55. package/dist/bot/strategies/funding-arb-utils.js +159 -195
  56. package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
  57. package/dist/bot/strategies/funding-arb-v2.js +258 -47
  58. package/dist/bot/strategies/funding-auto.js +215 -194
  59. package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
  60. package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
  61. package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
  62. package/dist/bot/strategies/spot-perp-executor.js +319 -120
  63. package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
  64. package/dist/bot/strategies/spot-perp-state.js +70 -0
  65. package/dist/bot/strategy-types.d.ts +6 -0
  66. package/dist/bot/tui/BotDashboard.d.ts +7 -2
  67. package/dist/bot/tui/BotDashboard.js +6 -3
  68. package/dist/bridge-engine.d.ts +120 -19
  69. package/dist/bridge-engine.js +710 -422
  70. package/dist/bridge-signers.d.ts +54 -0
  71. package/dist/bridge-signers.js +161 -0
  72. package/dist/cache.d.ts +4 -1
  73. package/dist/cache.js +11 -0
  74. package/dist/cli-flags.d.ts +50 -0
  75. package/dist/cli-flags.js +104 -0
  76. package/dist/cli-program.d.ts +35 -0
  77. package/dist/cli-program.js +286 -0
  78. package/dist/cli-spec.js +1 -1
  79. package/dist/client-id-tracker.js +11 -2
  80. package/dist/commands/account.d.ts +7 -1
  81. package/dist/commands/account.js +70 -92
  82. package/dist/commands/agent-import.d.ts +114 -0
  83. package/dist/commands/agent-import.js +794 -0
  84. package/dist/commands/agent.d.ts +232 -2
  85. package/dist/commands/agent.js +1858 -955
  86. package/dist/commands/alerts.d.ts +1 -0
  87. package/dist/commands/alerts.js +31 -28
  88. package/dist/commands/arb/index.d.ts +60 -1
  89. package/dist/commands/arb/index.js +381 -384
  90. package/dist/commands/arb-auto.d.ts +132 -4
  91. package/dist/commands/arb-auto.js +1474 -967
  92. package/dist/commands/arb.d.ts +3 -1
  93. package/dist/commands/arb.js +62 -30
  94. package/dist/commands/bot.js +127 -47
  95. package/dist/commands/bridge.d.ts +2 -1
  96. package/dist/commands/bridge.js +339 -192
  97. package/dist/commands/builder.d.ts +55 -0
  98. package/dist/commands/builder.js +387 -0
  99. package/dist/commands/funds-onchain.d.ts +32 -0
  100. package/dist/commands/funds-onchain.js +286 -0
  101. package/dist/commands/funds.d.ts +25 -0
  102. package/dist/commands/funds.js +939 -245
  103. package/dist/commands/history.js +72 -45
  104. package/dist/commands/init.d.ts +107 -15
  105. package/dist/commands/init.js +1116 -249
  106. package/dist/commands/jobs.d.ts +2 -1
  107. package/dist/commands/jobs.js +77 -15
  108. package/dist/commands/manage.js +136 -91
  109. package/dist/commands/market.d.ts +4 -1
  110. package/dist/commands/market.js +35 -32
  111. package/dist/commands/outcome.d.ts +10 -0
  112. package/dist/commands/outcome.js +396 -0
  113. package/dist/commands/plan.js +52 -12
  114. package/dist/commands/portfolio.d.ts +1 -0
  115. package/dist/commands/portfolio.js +60 -72
  116. package/dist/commands/rebalance.d.ts +2 -1
  117. package/dist/commands/rebalance.js +33 -43
  118. package/dist/commands/risk.js +46 -19
  119. package/dist/commands/settings.d.ts +1 -0
  120. package/dist/commands/settings.js +246 -51
  121. package/dist/commands/trade.d.ts +50 -0
  122. package/dist/commands/trade.js +1943 -695
  123. package/dist/commands/wallet.d.ts +30 -1
  124. package/dist/commands/wallet.js +316 -337
  125. package/dist/config.d.ts +60 -9
  126. package/dist/config.js +252 -82
  127. package/dist/constants.d.ts +29 -4
  128. package/dist/constants.js +62 -20
  129. package/dist/dashboard/server.d.ts +2 -0
  130. package/dist/dashboard/server.js +9 -6
  131. package/dist/dashboard/ui.js +6 -4
  132. package/dist/dashboard/ws-feeds.js +20 -8
  133. package/dist/dex-asset-map.js +5 -3
  134. package/dist/errors.d.ts +122 -0
  135. package/dist/errors.js +194 -0
  136. package/dist/event-stream.js +75 -19
  137. package/dist/evm-master-tx.d.ts +59 -0
  138. package/dist/evm-master-tx.js +154 -0
  139. package/dist/exchanges/adapter-network.d.ts +10 -0
  140. package/dist/exchanges/adapter-network.js +10 -0
  141. package/dist/exchanges/arcus/adapter.d.ts +196 -0
  142. package/dist/exchanges/arcus/adapter.js +1092 -0
  143. package/dist/exchanges/arcus/agent.d.ts +55 -0
  144. package/dist/exchanges/arcus/agent.js +188 -0
  145. package/dist/exchanges/arcus/client.d.ts +63 -0
  146. package/dist/exchanges/arcus/client.js +285 -0
  147. package/dist/exchanges/arcus/public.d.ts +65 -0
  148. package/dist/exchanges/arcus/public.js +138 -0
  149. package/dist/exchanges/arcus/signing.d.ts +176 -0
  150. package/dist/exchanges/arcus/signing.js +288 -0
  151. package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
  152. package/dist/exchanges/arcus/spot-chain.js +241 -0
  153. package/dist/exchanges/arcus/spot-router.d.ts +177 -0
  154. package/dist/exchanges/arcus/spot-router.js +183 -0
  155. package/dist/exchanges/arcus/spot.d.ts +109 -0
  156. package/dist/exchanges/arcus/spot.js +645 -0
  157. package/dist/exchanges/aster-funds.d.ts +127 -0
  158. package/dist/exchanges/aster-funds.js +252 -0
  159. package/dist/exchanges/aster-nonce.d.ts +2 -0
  160. package/dist/exchanges/aster-nonce.js +107 -0
  161. package/dist/exchanges/aster-spot.d.ts +117 -0
  162. package/dist/exchanges/aster-spot.js +686 -0
  163. package/dist/exchanges/aster-typed-data.d.ts +133 -7
  164. package/dist/exchanges/aster-typed-data.js +121 -7
  165. package/dist/exchanges/aster.d.ts +219 -6
  166. package/dist/exchanges/aster.js +1023 -153
  167. package/dist/exchanges/builder-fee.d.ts +254 -0
  168. package/dist/exchanges/builder-fee.js +421 -0
  169. package/dist/exchanges/canonical-symbols.d.ts +32 -0
  170. package/dist/exchanges/canonical-symbols.js +96 -0
  171. package/dist/exchanges/capabilities.d.ts +7 -1
  172. package/dist/exchanges/capabilities.js +7 -0
  173. package/dist/exchanges/hl-signing.d.ts +165 -0
  174. package/dist/exchanges/hl-signing.js +158 -0
  175. package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
  176. package/dist/exchanges/hyperliquid-outcome.js +646 -0
  177. package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
  178. package/dist/exchanges/hyperliquid-spot.js +308 -117
  179. package/dist/exchanges/hyperliquid.d.ts +462 -57
  180. package/dist/exchanges/hyperliquid.js +1575 -449
  181. package/dist/exchanges/index.d.ts +6 -0
  182. package/dist/exchanges/index.js +4 -0
  183. package/dist/exchanges/interface.d.ts +63 -10
  184. package/dist/exchanges/interface.js +18 -3
  185. package/dist/exchanges/ioc-outcome.d.ts +44 -0
  186. package/dist/exchanges/ioc-outcome.js +57 -0
  187. package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
  188. package/dist/exchanges/legacy-trading-v2.js +940 -0
  189. package/dist/exchanges/lighter-public.d.ts +43 -0
  190. package/dist/exchanges/lighter-public.js +276 -0
  191. package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
  192. package/dist/exchanges/lighter-rh-deposit.js +185 -0
  193. package/dist/exchanges/lighter-signer.d.ts +123 -0
  194. package/dist/exchanges/lighter-signer.js +215 -0
  195. package/dist/exchanges/lighter-spot.d.ts +91 -13
  196. package/dist/exchanges/lighter-spot.js +595 -233
  197. package/dist/exchanges/lighter.d.ts +611 -39
  198. package/dist/exchanges/lighter.js +1890 -649
  199. package/dist/exchanges/ondo/adapter.d.ts +166 -0
  200. package/dist/exchanges/ondo/adapter.js +835 -0
  201. package/dist/exchanges/ondo/agent.d.ts +31 -0
  202. package/dist/exchanges/ondo/agent.js +101 -0
  203. package/dist/exchanges/ondo/client.d.ts +60 -0
  204. package/dist/exchanges/ondo/client.js +297 -0
  205. package/dist/exchanges/ondo/funds.d.ts +122 -0
  206. package/dist/exchanges/ondo/funds.js +290 -0
  207. package/dist/exchanges/ondo/public.d.ts +95 -0
  208. package/dist/exchanges/ondo/public.js +214 -0
  209. package/dist/exchanges/ondo/spot.d.ts +127 -0
  210. package/dist/exchanges/ondo/spot.js +392 -0
  211. package/dist/exchanges/outcome-interface.d.ts +140 -0
  212. package/dist/exchanges/outcome-interface.js +16 -0
  213. package/dist/exchanges/pacifica-spot.d.ts +83 -0
  214. package/dist/exchanges/pacifica-spot.js +324 -0
  215. package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
  216. package/dist/exchanges/pacifica-typed-data.js +57 -9
  217. package/dist/exchanges/pacifica.d.ts +288 -4
  218. package/dist/exchanges/pacifica.js +1013 -76
  219. package/dist/exchanges/precision.d.ts +47 -0
  220. package/dist/exchanges/precision.js +209 -0
  221. package/dist/exchanges/qfex/adapter.d.ts +341 -0
  222. package/dist/exchanges/qfex/adapter.js +1383 -0
  223. package/dist/exchanges/qfex/agent.d.ts +58 -0
  224. package/dist/exchanges/qfex/agent.js +188 -0
  225. package/dist/exchanges/qfex/client.d.ts +54 -0
  226. package/dist/exchanges/qfex/client.js +138 -0
  227. package/dist/exchanges/qfex/errors.d.ts +70 -0
  228. package/dist/exchanges/qfex/errors.js +236 -0
  229. package/dist/exchanges/qfex/public.d.ts +122 -0
  230. package/dist/exchanges/qfex/public.js +268 -0
  231. package/dist/exchanges/qfex/signing.d.ts +160 -0
  232. package/dist/exchanges/qfex/signing.js +283 -0
  233. package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
  234. package/dist/exchanges/qfex/ws-transport.js +798 -0
  235. package/dist/exchanges/rate-limiter.d.ts +12 -0
  236. package/dist/exchanges/rate-limiter.js +64 -0
  237. package/dist/exchanges/registry.d.ts +14 -2
  238. package/dist/exchanges/registry.js +20 -32
  239. package/dist/exchanges/slippage.d.ts +37 -0
  240. package/dist/exchanges/slippage.js +70 -0
  241. package/dist/exchanges/spot-factory.d.ts +166 -0
  242. package/dist/exchanges/spot-factory.js +431 -0
  243. package/dist/exchanges/spot-interface.d.ts +98 -4
  244. package/dist/exchanges/spot-interface.js +27 -1
  245. package/dist/exchanges/spot-result.d.ts +79 -0
  246. package/dist/exchanges/spot-result.js +273 -0
  247. package/dist/exchanges/trading-v2.d.ts +117 -0
  248. package/dist/exchanges/trading-v2.js +301 -0
  249. package/dist/exchanges/venue-types.d.ts +231 -0
  250. package/dist/exchanges/venue-types.js +87 -0
  251. package/dist/exchanges/venue.d.ts +211 -0
  252. package/dist/exchanges/venue.js +132 -0
  253. package/dist/exchanges/venues/arcus.d.ts +8 -0
  254. package/dist/exchanges/venues/arcus.js +109 -0
  255. package/dist/exchanges/venues/aster.d.ts +49 -0
  256. package/dist/exchanges/venues/aster.js +176 -0
  257. package/dist/exchanges/venues/create-helpers.d.ts +18 -0
  258. package/dist/exchanges/venues/create-helpers.js +199 -0
  259. package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
  260. package/dist/exchanges/venues/hyperliquid.js +126 -0
  261. package/dist/exchanges/venues/index.d.ts +16 -0
  262. package/dist/exchanges/venues/index.js +26 -0
  263. package/dist/exchanges/venues/lighter-family.d.ts +92 -0
  264. package/dist/exchanges/venues/lighter-family.js +166 -0
  265. package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
  266. package/dist/exchanges/venues/lighter-rh.js +58 -0
  267. package/dist/exchanges/venues/lighter.d.ts +6 -0
  268. package/dist/exchanges/venues/lighter.js +55 -0
  269. package/dist/exchanges/venues/ondo.d.ts +34 -0
  270. package/dist/exchanges/venues/ondo.js +140 -0
  271. package/dist/exchanges/venues/pacifica.d.ts +8 -0
  272. package/dist/exchanges/venues/pacifica.js +95 -0
  273. package/dist/exchanges/venues/qfex.d.ts +28 -0
  274. package/dist/exchanges/venues/qfex.js +132 -0
  275. package/dist/execution-log.js +10 -2
  276. package/dist/funding/legacy-public.d.ts +31 -0
  277. package/dist/funding/legacy-public.js +369 -0
  278. package/dist/funding/normalize.d.ts +28 -16
  279. package/dist/funding/normalize.js +61 -29
  280. package/dist/funding/rates.d.ts +46 -11
  281. package/dist/funding/rates.js +228 -277
  282. package/dist/guardrail/perp-guardrail.js +0 -0
  283. package/dist/index.js +156 -684
  284. package/dist/jobs.d.ts +84 -6
  285. package/dist/jobs.js +239 -41
  286. package/dist/landing.d.ts +7 -2
  287. package/dist/landing.js +19 -3
  288. package/dist/mcp-server.d.ts +14 -4
  289. package/dist/mcp-server.js +820 -402
  290. package/dist/mcp-trade-guard.d.ts +16 -0
  291. package/dist/mcp-trade-guard.js +59 -0
  292. package/dist/pacifica/client.d.ts +41 -10
  293. package/dist/pacifica/client.js +167 -63
  294. package/dist/pacifica/constants.d.ts +0 -1
  295. package/dist/pacifica/constants.js +0 -5
  296. package/dist/pacifica/deposit.d.ts +30 -3
  297. package/dist/pacifica/deposit.js +119 -2
  298. package/dist/pacifica/faucet.d.ts +29 -0
  299. package/dist/pacifica/faucet.js +100 -0
  300. package/dist/pacifica/sign-tx.d.ts +15 -0
  301. package/dist/pacifica/sign-tx.js +41 -0
  302. package/dist/pacifica/types/account.d.ts +28 -4
  303. package/dist/pacifica/types/lake.d.ts +20 -5
  304. package/dist/pacifica/types/lake.js +4 -0
  305. package/dist/pacifica/types/market.d.ts +5 -0
  306. package/dist/pacifica/types/order.d.ts +4 -1
  307. package/dist/pacifica/types/position.d.ts +9 -1
  308. package/dist/plan-executor.d.ts +10 -5
  309. package/dist/plan-executor.js +165 -54
  310. package/dist/position-history.js +13 -0
  311. package/dist/rebalance.d.ts +26 -2
  312. package/dist/rebalance.js +86 -7
  313. package/dist/relay-swap.d.ts +232 -0
  314. package/dist/relay-swap.js +630 -0
  315. package/dist/risk.js +5 -1
  316. package/dist/secret-env.d.ts +15 -0
  317. package/dist/secret-env.js +36 -0
  318. package/dist/settings.d.ts +169 -6
  319. package/dist/settings.js +219 -38
  320. package/dist/signer/ows-evm.d.ts +13 -0
  321. package/dist/signer/ows-evm.js +22 -0
  322. package/dist/smart-order.d.ts +9 -0
  323. package/dist/smart-order.js +53 -8
  324. package/dist/strategies/dca.d.ts +6 -2
  325. package/dist/strategies/dca.js +66 -26
  326. package/dist/strategies/funding-arb.d.ts +11 -1
  327. package/dist/strategies/funding-arb.js +202 -89
  328. package/dist/strategies/grid.d.ts +31 -0
  329. package/dist/strategies/grid.js +89 -11
  330. package/dist/strategies/pending-stop.d.ts +8 -0
  331. package/dist/strategies/pending-stop.js +12 -0
  332. package/dist/strategies/split-order.d.ts +45 -17
  333. package/dist/strategies/split-order.js +157 -91
  334. package/dist/strategies/trailing-stop.d.ts +2 -1
  335. package/dist/strategies/trailing-stop.js +52 -6
  336. package/dist/strategies/twap.d.ts +6 -1
  337. package/dist/strategies/twap.js +39 -11
  338. package/dist/trade-validator.d.ts +22 -0
  339. package/dist/trade-validator.js +119 -15
  340. package/dist/utils/numeric.d.ts +46 -0
  341. package/dist/utils/numeric.js +79 -0
  342. package/dist/utils.d.ts +19 -0
  343. package/dist/utils.js +96 -19
  344. package/dist/withdraw-gate.d.ts +49 -0
  345. package/dist/withdraw-gate.js +131 -0
  346. package/package.json +15 -6
  347. package/skills/perp-cli/SKILL.md +142 -55
  348. package/skills/perp-cli/references/agent-operations.md +110 -56
  349. package/skills/perp-cli/references/commands.md +266 -71
  350. package/skills/perp-cli/references/strategies.md +34 -31
  351. package/skills/perp-cli/scripts/arb-monitor.sh +10 -5
  352. package/skills/perp-cli/scripts/funding-analysis.sh +14 -9
  353. package/skills/perp-cli/scripts/preflight.sh +21 -14
  354. package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
  355. package/skills/perp-cli/scripts/validate-arb.sh +31 -15
  356. package/vendor/lighter-signer/SHA256SUMS +2 -0
  357. package/vendor/lighter-signer/VERSION +8 -0
  358. package/vendor/lighter-signer/lighter-signer.wasm +0 -0
  359. package/vendor/lighter-signer/wasm_exec.js +575 -0
  360. package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
  361. package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Per-process token-bucket rate limiter keyed by venue (design §3 L4, §10.9).
3
+ *
4
+ * Limitation (documented): this only coordinates requests inside one process.
5
+ * CLI + MCP server + bots running at once can still exceed a venue's limit.
6
+ */
7
+ import { PerpError } from "../errors.js";
8
+ import type { RateLimitPolicy } from "./venue.js";
9
+ /** Acquire capacity for one request. `endpoint` is looked up in policy.weights (unlisted = 1). */
10
+ export declare function acquireRate(key: string, policy: RateLimitPolicy, endpoint: string): Promise<void>;
11
+ /** Map an HTTP 429 into a structured RATE_LIMITED error. */
12
+ export declare function rateLimitedError(venue: string, retryAfterHeader: string | null, body: string): PerpError;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Per-process token-bucket rate limiter keyed by venue (design §3 L4, §10.9).
3
+ *
4
+ * Limitation (documented): this only coordinates requests inside one process.
5
+ * CLI + MCP server + bots running at once can still exceed a venue's limit.
6
+ */
7
+ import { PerpError } from "../errors.js";
8
+ class TokenBucket {
9
+ policy;
10
+ tokens;
11
+ last = Date.now();
12
+ queue = Promise.resolve();
13
+ constructor(policy) {
14
+ this.policy = policy;
15
+ if (!(policy.capacity > 0) || !(policy.refillPerSec > 0)) {
16
+ throw new Error(`invalid rate limit policy: ${JSON.stringify(policy)}`);
17
+ }
18
+ this.tokens = policy.capacity;
19
+ }
20
+ refill() {
21
+ const now = Date.now();
22
+ this.tokens = Math.min(this.policy.capacity, this.tokens + ((now - this.last) / 1000) * this.policy.refillPerSec);
23
+ this.last = now;
24
+ }
25
+ acquire(weight) {
26
+ if (weight > this.policy.capacity) {
27
+ throw new PerpError("RATE_LIMITED", `request weight ${weight} exceeds bucket capacity ${this.policy.capacity}`);
28
+ }
29
+ const run = async () => {
30
+ for (;;) {
31
+ this.refill();
32
+ if (this.tokens >= weight) {
33
+ this.tokens -= weight;
34
+ return;
35
+ }
36
+ const waitMs = Math.ceil(((weight - this.tokens) / this.policy.refillPerSec) * 1000);
37
+ await new Promise(r => setTimeout(r, waitMs));
38
+ }
39
+ };
40
+ const p = this.queue.then(run);
41
+ this.queue = p.catch(() => undefined);
42
+ return p;
43
+ }
44
+ }
45
+ const buckets = new Map();
46
+ /** Acquire capacity for one request. `endpoint` is looked up in policy.weights (unlisted = 1). */
47
+ export async function acquireRate(key, policy, endpoint) {
48
+ let b = buckets.get(key);
49
+ if (!b) {
50
+ b = new TokenBucket(policy);
51
+ buckets.set(key, b);
52
+ }
53
+ const weight = policy.weights?.[endpoint] ?? 1; // unlisted endpoints cost 1 by the policy's definition
54
+ await b.acquire(weight);
55
+ }
56
+ /** Map an HTTP 429 into a structured RATE_LIMITED error. */
57
+ export function rateLimitedError(venue, retryAfterHeader, body) {
58
+ const retryAfterSec = retryAfterHeader !== null ? Number(retryAfterHeader) : NaN;
59
+ return new PerpError("RATE_LIMITED", `${venue}: rate limited (HTTP 429)`, {
60
+ ...(Number.isFinite(retryAfterSec) ? { retryAfterMs: retryAfterSec * 1000 } : {}),
61
+ body: body.slice(0, 500),
62
+ remediation: `Reduce request frequency for ${venue} or request a higher API limit from the venue`,
63
+ });
64
+ }
@@ -1,13 +1,25 @@
1
1
  /**
2
- * Adapter registry for plugin-style exchange discovery.
3
- * New exchanges register here — commands use registry to resolve names/aliases.
2
+ * Adapter registry — legacy name/alias API, now derived from the venue
3
+ * definitions (src/exchanges/venues/, design §10.10). New venues are added by
4
+ * registering a VenueDefinition, not here.
4
5
  */
6
+ import "./venues/index.js";
5
7
  export interface AdapterRegistration {
6
8
  name: string;
7
9
  aliases: string[];
10
+ /**
11
+ * Legacy field = the adapter's `chain` value (pacifica "solana", hyperliquid "evm",
12
+ * lighter "ethereum", aster "bnb"). For key-kind decisions use the venue's
13
+ * masterKeyKind; for where funds live use networks.<net>.settlementChain.
14
+ */
8
15
  chain: string;
9
16
  }
17
+ /**
18
+ * @deprecated Registration happens through VenueDefinition (registerVenue in ./venue.ts).
19
+ * Kept for API compatibility; throws so a stray call cannot create an adapter-less entry.
20
+ */
10
21
  export declare function registerAdapter(reg: AdapterRegistration): void;
11
22
  export declare function getAdapterRegistration(nameOrAlias: string): AdapterRegistration | undefined;
12
23
  export declare function listExchanges(): string[];
24
+ /** Resolve an alias to its canonical venue id. Throws UNKNOWN_VENUE for unknown names. */
13
25
  export declare function resolveExchangeName(nameOrAlias: string): string;
@@ -1,42 +1,30 @@
1
1
  /**
2
- * Adapter registry for plugin-style exchange discovery.
3
- * New exchanges register here — commands use registry to resolve names/aliases.
2
+ * Adapter registry — legacy name/alias API, now derived from the venue
3
+ * definitions (src/exchanges/venues/, design §10.10). New venues are added by
4
+ * registering a VenueDefinition, not here.
5
+ */
6
+ import { getVenue, listVenues, tryResolveVenueId } from "./venue.js";
7
+ import "./venues/index.js";
8
+ function toRegistration(def) {
9
+ // HyperliquidAdapter.chain is "evm"; every other adapter's chain is its mainnet settlement chain.
10
+ const chain = def.family === "hyperliquid" ? "evm" : def.networks.mainnet.settlementChain;
11
+ return { name: def.id, aliases: [...def.aliases], chain };
12
+ }
13
+ /**
14
+ * @deprecated Registration happens through VenueDefinition (registerVenue in ./venue.ts).
15
+ * Kept for API compatibility; throws so a stray call cannot create an adapter-less entry.
4
16
  */
5
- const registry = new Map();
6
17
  export function registerAdapter(reg) {
7
- registry.set(reg.name, reg);
8
- for (const alias of reg.aliases) {
9
- registry.set(alias, reg);
10
- }
18
+ throw new Error(`registerAdapter("${reg.name}") is no longer supported — add a VenueDefinition under src/exchanges/venues/`);
11
19
  }
12
20
  export function getAdapterRegistration(nameOrAlias) {
13
- return registry.get(nameOrAlias.toLowerCase());
21
+ const id = tryResolveVenueId(nameOrAlias);
22
+ return id === null ? undefined : toRegistration(getVenue(id));
14
23
  }
15
24
  export function listExchanges() {
16
- return [...new Set([...registry.values()].map(r => r.name))];
25
+ return listVenues().map(v => v.id);
17
26
  }
27
+ /** Resolve an alias to its canonical venue id. Throws UNKNOWN_VENUE for unknown names. */
18
28
  export function resolveExchangeName(nameOrAlias) {
19
- const reg = registry.get(nameOrAlias.toLowerCase());
20
- return reg?.name ?? nameOrAlias.toLowerCase();
29
+ return getVenue(nameOrAlias).id;
21
30
  }
22
- // ── Built-in adapter registrations ──
23
- registerAdapter({
24
- name: "pacifica",
25
- aliases: ["pac"],
26
- chain: "solana",
27
- });
28
- registerAdapter({
29
- name: "hyperliquid",
30
- aliases: ["hl"],
31
- chain: "evm",
32
- });
33
- registerAdapter({
34
- name: "lighter",
35
- aliases: ["lt"],
36
- chain: "ethereum",
37
- });
38
- registerAdapter({
39
- name: "aster",
40
- aliases: ["ast"],
41
- chain: "bnb",
42
- });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Single slippage resolver for every market-type order (design §13.2, §13.10 #1).
3
+ *
4
+ * Precedence (all explicit configuration, no hidden constants):
5
+ * --slippage-bps flag → settings.slippageBps[venue] → settings.defaultSlippageBps.
6
+ * Adapters never read settings; they receive the resolved value as an argument.
7
+ */
8
+ import type { VenueId } from "./venue-types.js";
9
+ import "./venues/index.js";
10
+ export type SlippageSource = "flag" | "venue" | "default";
11
+ export interface ResolvedSlippage {
12
+ bps: number;
13
+ source: SlippageSource;
14
+ }
15
+ /** Largest slippage bound (bps) the venue accepts (VenueDefinition.maxSlippageBps). */
16
+ export declare function venueMaxSlippageBps(venue: VenueId): number;
17
+ /** defaultSlippageBps applies to every trading venue, so it must fit the tightest venue maximum. */
18
+ export declare function maxDefaultSlippageBps(): number;
19
+ /**
20
+ * Parse a bps value for a settings key (`defaultSlippageBps` / `slippageBps.<venue>`):
21
+ * a number ≥ 1 and ≤ `max`. Out of range → INVALID_PARAMS; never clamps.
22
+ * Shared by `perp settings set` and `perp setup` so both accept exactly what resolveSlippage() accepts.
23
+ */
24
+ export declare function parseSlippageSettingBps(key: string, value: string, max: number): number;
25
+ /**
26
+ * Resolve the slippage bound for a market-type order on `venue`.
27
+ * `flagBps` = caller's explicit per-call value (CLI --slippage-bps), null when absent.
28
+ * Out of range (bps < 1 or > venue max) → INVALID_PARAMS before anything is sent; never clamps.
29
+ */
30
+ export declare function resolveSlippage(venue: VenueId, flagBps: number | null): ResolvedSlippage;
31
+ /**
32
+ * resolveSlippage() for an adapter (its `name` is a venue id). Bot/arb paths pass
33
+ * flagBps = null, so the bound comes from settings (slippageBps[venue] → defaultSlippageBps).
34
+ */
35
+ export declare function resolveAdapterSlippage(adapter: {
36
+ readonly name: string;
37
+ }, flagBps: number | null): ResolvedSlippage;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Single slippage resolver for every market-type order (design §13.2, §13.10 #1).
3
+ *
4
+ * Precedence (all explicit configuration, no hidden constants):
5
+ * --slippage-bps flag → settings.slippageBps[venue] → settings.defaultSlippageBps.
6
+ * Adapters never read settings; they receive the resolved value as an argument.
7
+ */
8
+ import { PerpError } from "../errors.js";
9
+ import { loadSettings, isValidSlippageBps } from "../settings.js";
10
+ import { getVenue, listVenuesWithRole, resolveVenueId } from "./venue.js";
11
+ import "./venues/index.js";
12
+ /** Largest slippage bound (bps) the venue accepts (VenueDefinition.maxSlippageBps). */
13
+ export function venueMaxSlippageBps(venue) {
14
+ return getVenue(venue).maxSlippageBps;
15
+ }
16
+ /** defaultSlippageBps applies to every trading venue, so it must fit the tightest venue maximum. */
17
+ export function maxDefaultSlippageBps() {
18
+ return Math.min(...listVenuesWithRole("trading").map(v => v.maxSlippageBps));
19
+ }
20
+ /**
21
+ * Parse a bps value for a settings key (`defaultSlippageBps` / `slippageBps.<venue>`):
22
+ * a number ≥ 1 and ≤ `max`. Out of range → INVALID_PARAMS; never clamps.
23
+ * Shared by `perp settings set` and `perp setup` so both accept exactly what resolveSlippage() accepts.
24
+ */
25
+ export function parseSlippageSettingBps(key, value, max) {
26
+ const bps = Number(value);
27
+ if (!isValidSlippageBps(bps) || bps > max) {
28
+ throw new PerpError("INVALID_PARAMS", `${key} must be a number of basis points in 1..${max} (got "${value}")`);
29
+ }
30
+ return bps;
31
+ }
32
+ /**
33
+ * Resolve the slippage bound for a market-type order on `venue`.
34
+ * `flagBps` = caller's explicit per-call value (CLI --slippage-bps), null when absent.
35
+ * Out of range (bps < 1 or > venue max) → INVALID_PARAMS before anything is sent; never clamps.
36
+ */
37
+ export function resolveSlippage(venue, flagBps) {
38
+ let resolved;
39
+ if (flagBps !== null) {
40
+ resolved = { bps: flagBps, source: "flag" };
41
+ }
42
+ else {
43
+ const s = loadSettings();
44
+ const perVenue = s.slippageBps[venue];
45
+ resolved = perVenue !== undefined
46
+ ? { bps: perVenue, source: "venue" }
47
+ : { bps: s.defaultSlippageBps, source: "default" };
48
+ }
49
+ const max = venueMaxSlippageBps(venue);
50
+ if (!Number.isFinite(resolved.bps) || resolved.bps < 1 || resolved.bps > max) {
51
+ const where = resolved.source === "flag" ? "--slippage-bps"
52
+ : resolved.source === "venue" ? `settings slippageBps.${venue}` : "settings defaultSlippageBps";
53
+ throw new PerpError("INVALID_PARAMS", `Slippage bound ${resolved.bps} bps (from ${where}) is outside 1..${max} bps for ${venue}`, {
54
+ exchange: venue,
55
+ remediation: resolved.source === "flag"
56
+ ? `Pass --slippage-bps between 1 and ${max}`
57
+ : resolved.source === "venue"
58
+ ? `Run: perp settings set slippageBps.${venue} <1..${max}> (or perp settings unset slippageBps.${venue})`
59
+ : `Run: perp settings set defaultSlippageBps <1..${max}> (or perp settings set slippageBps.${venue} <n>)`,
60
+ });
61
+ }
62
+ return resolved;
63
+ }
64
+ /**
65
+ * resolveSlippage() for an adapter (its `name` is a venue id). Bot/arb paths pass
66
+ * flagBps = null, so the bound comes from settings (slippageBps[venue] → defaultSlippageBps).
67
+ */
68
+ export function resolveAdapterSlippage(adapter, flagBps) {
69
+ return resolveSlippage(resolveVenueId(adapter.name), flagBps);
70
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Spot adapter factory — the only place spot adapters are created (design §12.4 #1).
3
+ *
4
+ * - `getSpotAdapter` returns null iff the venue has no spot (`def.spot === null`).
5
+ * Creation / init / network errors propagate (SSOT Rule #2 — no catch).
6
+ * - `createSpot` (venue definition) constructs and init()s the adapter.
7
+ * - Cached per perp adapter instance (each instance is bound to one venue + network,
8
+ * so the key is the adapter's own network, not the process-wide one) and per
9
+ * on-chain-approve option. Read-only and signed perp adapters keep separate entries.
10
+ *
11
+ * Bot scope (§12.4 #3): bots only trade spot on venues where `isBotSpotVenue(def)`.
12
+ * Quote/base detection (§12.4 #6) goes through `def.spot.quoteAssets`.
13
+ */
14
+ import type { AssetClass, Network, VenueMarket } from "./venue-types.js";
15
+ import type { ExchangeAdapter } from "./interface.js";
16
+ import { type SpotAdapter, type SpotBalance, type SpotMarketInfo } from "./spot-interface.js";
17
+ import { type SpotCreateOptions, type VenueDefinition, type VenueSpotDefinition } from "./venue.js";
18
+ import "./venues/index.js";
19
+ /** Options for every non-CLI caller: on-chain approve txs are never sent implicitly. */
20
+ export declare const NO_ONCHAIN_APPROVE: SpotCreateOptions;
21
+ /** Venues with spot support, for remediation text. */
22
+ export declare function listSpotVenues(): VenueDefinition[];
23
+ /**
24
+ * Get (or create + init) the spot adapter for a venue. null iff the venue has no spot.
25
+ * `opts` defaults to NO_ONCHAIN_APPROVE (the safe deny); only the CLI passes --yes.
26
+ */
27
+ export declare function getSpotAdapter(venue: string, perp: ExchangeAdapter, opts?: SpotCreateOptions): Promise<SpotAdapter | null>;
28
+ /** The venue's spot definition, or CAPABILITY_UNSUPPORTED. */
29
+ export declare function requireSpotDefinition(def: VenueDefinition): VenueSpotDefinition;
30
+ /** CLI form: throws CAPABILITY_UNSUPPORTED when the venue has no spot. */
31
+ export declare function requireSpotAdapter(venue: string, perp: ExchangeAdapter, opts?: SpotCreateOptions): Promise<SpotAdapter>;
32
+ /**
33
+ * Bots (funding-arb*, spot-perp-*, arb-auto, arb/index) only use spot venues with a
34
+ * separate (or runtime-decided) spot balance and a spot↔perp transfer. Pacifica
35
+ * (shared margin) and Arcus (on-chain swap) are excluded.
36
+ */
37
+ export declare function isBotSpotVenue(def: VenueDefinition): boolean;
38
+ export declare function listBotSpotVenues(): VenueDefinition[];
39
+ /** Bot form: throws CAPABILITY_UNSUPPORTED for venues outside the bot spot scope. */
40
+ export declare function requireBotSpotVenue(venue: string): VenueDefinition & {
41
+ spot: VenueSpotDefinition;
42
+ };
43
+ /**
44
+ * Bot spot adapter for an adapter-map name. null when the name is not a venue, or the
45
+ * venue is outside the bot spot scope. Creation errors propagate.
46
+ */
47
+ export declare function getBotSpotAdapter(name: string, perp: ExchangeAdapter): Promise<SpotAdapter | null>;
48
+ /** Upper-cased token with venue balance suffixes removed ("USDC_SPOT" / "PURR-SPOT" → base). */
49
+ export declare function spotTokenBase(token: string): string;
50
+ /** The venue's spot quote assets on `net` (network-keyed: Lighter RH quotes USDG on mainnet, USDC on testnet). */
51
+ export declare function spotQuoteAssets(def: VenueDefinition, net: Network): readonly string[];
52
+ /** Default quote on `net` (bare-base symbols, transfers). */
53
+ export declare function defaultSpotQuote(def: VenueDefinition, net: Network): string;
54
+ /** The network a spot adapter was created for (getSpotAdapter). */
55
+ export declare function spotNetworkOf(spot: SpotAdapter): Network;
56
+ export declare function isSpotQuoteAsset(def: VenueDefinition, net: Network, token: string): boolean;
57
+ /** "BASE/QUOTE" for a bare base on this venue (quoteAssets[net][0]). */
58
+ export declare function spotPairSymbol(def: VenueDefinition, net: Network, base: string): string;
59
+ /** The balance row of the venue's default quote asset (transfer asset), or null. */
60
+ export declare function findSpotQuoteBalance(def: VenueDefinition, net: Network, balances: readonly SpotBalance[]): SpotBalance | null;
61
+ /** Non-quote balances with a positive total (the "holdings" side of spot-perp hedges). */
62
+ export declare function nonQuoteSpotBalances(def: VenueDefinition, net: Network, balances: readonly SpotBalance[]): SpotBalance[];
63
+ /**
64
+ * true when spot quote balance is the same money as perp margin for this perp
65
+ * adapter: accountModel "unified", or "runtime" with a unified perp account (HL).
66
+ */
67
+ export declare function spotSharesPerpMargin(def: VenueDefinition, perp: ExchangeAdapter): boolean;
68
+ export interface ValuedSpotBalance extends SpotBalance {
69
+ valueUsd: number;
70
+ }
71
+ /**
72
+ * USD value of positive spot balances. Quote assets (stablecoins) are 1:1; other
73
+ * tokens use the venue's spot mark price. Tokens without a usable price are
74
+ * EXCLUDED and reported via `warn` — never valued at 0 (SSOT Rule #2).
75
+ * `shared` is carried through so aggregators can skip money already in perp equity.
76
+ */
77
+ export declare function valueSpotBalances(def: VenueDefinition, net: Network, balances: readonly SpotBalance[], markets: readonly SpotValuationPrice[], warn: (msg: string) => void): ValuedSpotBalance[];
78
+ /** Timeout for one venue's spot valuation (balances + prices) in aggregate views (portfolio, landing). */
79
+ export declare const SPOT_VALUATION_TIMEOUT_MS = 5000;
80
+ /** The price fields valuation reads (a spot market, or a held token's price — SpotHoldingPrice). */
81
+ export type SpotValuationPrice = Pick<SpotMarketInfo, "baseToken" | "markPrice">;
82
+ /**
83
+ * Prices used to value `balances`. Wallet-model venues (Arcus) price only the held
84
+ * non-quote tokens (`getSpotPrices`); exchange venues use `getSpotMarkets()`, plus — when the
85
+ * adapter is SpotHoldingPricesCapable — `getHoldingPrices()` for held tokens that are not a spot
86
+ * market (Hyperliquid HIP-4 outcome tokens). A holding price counts only when it is quoted in one of
87
+ * the venue's spot quote assets (valued 1:1 like the quote balance); any other quote (or an unknown
88
+ * one) → markPrice null → the token stays unpriced (never 0).
89
+ */
90
+ export declare function spotMarketsForValuation(def: VenueDefinition, spot: SpotAdapter, balances: readonly SpotBalance[], net: Network): Promise<SpotValuationPrice[]>;
91
+ /**
92
+ * Balances + valuation for aggregate views, bounded by `timeoutMs` (rejects with a
93
+ * TIMEOUT error; the caller reports it — never a partial or zero value).
94
+ */
95
+ export declare function fetchValuedSpotBalances(def: VenueDefinition, spot: SpotAdapter, warn: (msg: string) => void, timeoutMs?: number): Promise<ValuedSpotBalance[]>;
96
+ /** Reject with TIMEOUT after `timeoutMs` (the timer is cleared either way). */
97
+ export declare function withSpotTimeout<T>(p: Promise<T>, what: string, timeoutMs?: number): Promise<T>;
98
+ /**
99
+ * Fixed-point decimal string for a transfer amount, floored to TRANSFER_STEP — never
100
+ * `String(n)` (exponent "1e-7" or float artefacts "0.30000000000000004").
101
+ */
102
+ export declare function transferAmountString(amount: number): string;
103
+ /**
104
+ * Move the venue's quote asset (quoteAssets[0]) perp → spot.
105
+ * Returns false (no-op, nothing moved) when spot and perp share margin; throws
106
+ * CAPABILITY_UNSUPPORTED when the venue has no transfer. Callers log "Transferred" only on true.
107
+ */
108
+ export declare function transferQuoteToSpot(spot: SpotAdapter, amount: number): Promise<boolean>;
109
+ /**
110
+ * A spot↔perp transfer the venue refused for lack of funds (classifyError → INSUFFICIENT_BALANCE,
111
+ * e.g. HL "Insufficient balance for withdrawal.") → INSUFFICIENT_BALANCE that says how much the
112
+ * SOURCE side can move OF THE TRANSFER ASSET (`readAvailable`: perp withdrawable for to-spot, spot
113
+ * quote available for to-perp) — never an account-wide equity figure: a multi-asset margin
114
+ * account can hold margin and none of the asset. A failed read is reported as such (never a
115
+ * guessed figure). Any other error → unchanged.
116
+ */
117
+ export declare function transferShortfallError(e: unknown, opts: {
118
+ venue: string;
119
+ direction: "to-spot" | "to-perp";
120
+ amount: string;
121
+ asset: string;
122
+ readAvailable: () => Promise<number>;
123
+ }): Promise<unknown>;
124
+ /** Spot → perp counterpart of transferQuoteToSpot. */
125
+ export declare function transferQuoteToPerp(spot: SpotAdapter, amount: number): Promise<boolean>;
126
+ /**
127
+ * Spot quote (quoteAssets[0]) `available` read UNCACHED; 0 when the venue holds no
128
+ * quote row. Errors propagate. Used as the baseline for sweepSpotQuoteToPerp().
129
+ */
130
+ export declare function readSpotQuoteAvailableFresh(spot: SpotAdapter): Promise<number>;
131
+ /**
132
+ * Sweep back to perp ONLY the spot quote this trade produced / left over: the part of
133
+ * the (uncached) available balance above `keep` (the quote that was there before the
134
+ * trade — never touched), capped at `max` (e.g. what this trade transferred in; null =
135
+ * no cap). Returns the amount moved; 0 when the balance is shared with perp or the
136
+ * amount is ≤ minAmount. Errors propagate — callers surface them (funds remain in spot).
137
+ */
138
+ export declare function sweepSpotQuoteToPerp(spot: SpotAdapter, minAmount: number, opts: {
139
+ keep: number;
140
+ max: number | null;
141
+ }): Promise<number>;
142
+ export interface SpotPerpMatch {
143
+ /** Spot base as the spot adapter names it (bare base → quoteAssets[0]). */
144
+ spotBase: string;
145
+ /** Perp market symbol on the perp venue (VenueMarket.symbol). */
146
+ perpSymbol: string;
147
+ canonical: string;
148
+ assetClass: AssetClass;
149
+ spotPrice: number;
150
+ perpPrice: number;
151
+ /** |spot − perp| / perp × 100 */
152
+ deviationPct: number;
153
+ }
154
+ /**
155
+ * Match a spot venue's markets (default quote only) to a perp venue's markets by
156
+ * canonical symbol — `canonicalFor(spotVenue, base, perp.assetClass)` must equal the
157
+ * perp market's canonical (`null` never matches, NEVER_MATCH pairs excluded) — plus a
158
+ * price guard: |spot − perp mark| ≤ MAX_PRICE_DEVIATION_PCT. Spot markets without a
159
+ * price cannot be validated and are not matched. Bare-ticker equality is never enough.
160
+ *
161
+ * `perpMarkets` = the perp venue's public markets (`fetchPerpMarketsForMatching`).
162
+ * Returns spot base (upper-case) → match.
163
+ */
164
+ export declare function matchSpotPerp(spotVenue: string, spotMarkets: readonly SpotMarketInfo[], perpVenue: string, perpMarkets: readonly VenueMarket[], warn: (msg: string) => void, net: Network): Map<string, SpotPerpMatch>;
165
+ /** The perp venue's public markets (canonical + asset class + mark) on `net` (default: process network). */
166
+ export declare function fetchPerpMarketsForMatching(perpVenue: string, net?: Network): Promise<VenueMarket[]>;