perp-cli 0.3.3

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -0
  3. package/dist/__tests__/alert-logic.test.d.ts +1 -0
  4. package/dist/__tests__/alert-logic.test.js +107 -0
  5. package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
  6. package/dist/__tests__/arb-auto-3dex.test.js +397 -0
  7. package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
  8. package/dist/__tests__/arb-history-stats.test.js +176 -0
  9. package/dist/__tests__/arb-logic.test.d.ts +1 -0
  10. package/dist/__tests__/arb-logic.test.js +84 -0
  11. package/dist/__tests__/arb-manage.test.d.ts +1 -0
  12. package/dist/__tests__/arb-manage.test.js +253 -0
  13. package/dist/__tests__/arb-new-features.test.d.ts +1 -0
  14. package/dist/__tests__/arb-new-features.test.js +457 -0
  15. package/dist/__tests__/arb-sizing.test.d.ts +1 -0
  16. package/dist/__tests__/arb-sizing.test.js +48 -0
  17. package/dist/__tests__/arb-state.test.d.ts +1 -0
  18. package/dist/__tests__/arb-state.test.js +284 -0
  19. package/dist/__tests__/arb-userflow.test.d.ts +1 -0
  20. package/dist/__tests__/arb-userflow.test.js +945 -0
  21. package/dist/__tests__/arb-utils.test.d.ts +1 -0
  22. package/dist/__tests__/arb-utils.test.js +264 -0
  23. package/dist/__tests__/bot-conditions.test.d.ts +1 -0
  24. package/dist/__tests__/bot-conditions.test.js +341 -0
  25. package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
  26. package/dist/__tests__/client-id-tracker.test.js +137 -0
  27. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
  28. package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
  29. package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
  30. package/dist/__tests__/commands/order-intent.test.js +600 -0
  31. package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
  32. package/dist/__tests__/commands/trade-commands.test.js +821 -0
  33. package/dist/__tests__/config.test.d.ts +1 -0
  34. package/dist/__tests__/config.test.js +86 -0
  35. package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
  36. package/dist/__tests__/cross-chain-margin.test.js +287 -0
  37. package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
  38. package/dist/__tests__/dex-asset-map.test.js +191 -0
  39. package/dist/__tests__/errors.test.d.ts +1 -0
  40. package/dist/__tests__/errors.test.js +110 -0
  41. package/dist/__tests__/event-stream.test.d.ts +1 -0
  42. package/dist/__tests__/event-stream.test.js +276 -0
  43. package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
  44. package/dist/__tests__/exchanges/interface.test.js +132 -0
  45. package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
  46. package/dist/__tests__/exchanges/mock-adapter.js +137 -0
  47. package/dist/__tests__/execution-log.test.d.ts +1 -0
  48. package/dist/__tests__/execution-log.test.js +106 -0
  49. package/dist/__tests__/funding-calc.test.d.ts +1 -0
  50. package/dist/__tests__/funding-calc.test.js +71 -0
  51. package/dist/__tests__/funding-history.test.d.ts +1 -0
  52. package/dist/__tests__/funding-history.test.js +343 -0
  53. package/dist/__tests__/funding-rates.test.d.ts +1 -0
  54. package/dist/__tests__/funding-rates.test.js +342 -0
  55. package/dist/__tests__/funding.test.d.ts +1 -0
  56. package/dist/__tests__/funding.test.js +173 -0
  57. package/dist/__tests__/gap-logic.test.d.ts +1 -0
  58. package/dist/__tests__/gap-logic.test.js +43 -0
  59. package/dist/__tests__/hip3-dex.test.d.ts +1 -0
  60. package/dist/__tests__/hip3-dex.test.js +234 -0
  61. package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
  62. package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
  63. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
  64. package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
  65. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
  66. package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
  67. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
  68. package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
  69. package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
  70. package/dist/__tests__/integration/bridge.integration.test.js +309 -0
  71. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
  72. package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
  73. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
  74. package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
  75. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
  76. package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
  77. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
  78. package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
  79. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
  80. package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
  81. package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
  82. package/dist/__tests__/integration/lighter.integration.test.js +53 -0
  83. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
  84. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
  85. package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
  86. package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
  87. package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
  88. package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
  89. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
  90. package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
  91. package/dist/__tests__/liquidity.test.d.ts +1 -0
  92. package/dist/__tests__/liquidity.test.js +225 -0
  93. package/dist/__tests__/plan-executor.test.d.ts +1 -0
  94. package/dist/__tests__/plan-executor.test.js +314 -0
  95. package/dist/__tests__/position-history.test.d.ts +1 -0
  96. package/dist/__tests__/position-history.test.js +367 -0
  97. package/dist/__tests__/retry.test.d.ts +1 -0
  98. package/dist/__tests__/retry.test.js +310 -0
  99. package/dist/__tests__/risk-assessment.test.d.ts +1 -0
  100. package/dist/__tests__/risk-assessment.test.js +145 -0
  101. package/dist/__tests__/security-adversarial.test.d.ts +1 -0
  102. package/dist/__tests__/security-adversarial.test.js +574 -0
  103. package/dist/__tests__/strategies.test.d.ts +1 -0
  104. package/dist/__tests__/strategies.test.js +539 -0
  105. package/dist/__tests__/trade-execution.test.d.ts +1 -0
  106. package/dist/__tests__/trade-execution.test.js +129 -0
  107. package/dist/__tests__/trade-validator.test.d.ts +1 -0
  108. package/dist/__tests__/trade-validator.test.js +655 -0
  109. package/dist/__tests__/utils.test.d.ts +1 -0
  110. package/dist/__tests__/utils.test.js +76 -0
  111. package/dist/api/public/hyperliquid.d.ts +18 -0
  112. package/dist/api/public/hyperliquid.js +82 -0
  113. package/dist/api/public/index.d.ts +8 -0
  114. package/dist/api/public/index.js +8 -0
  115. package/dist/api/public/lighter.d.ts +24 -0
  116. package/dist/api/public/lighter.js +100 -0
  117. package/dist/api/public/pacifica.d.ts +17 -0
  118. package/dist/api/public/pacifica.js +54 -0
  119. package/dist/api/public/urls.d.ts +12 -0
  120. package/dist/api/public/urls.js +33 -0
  121. package/dist/arb/history-stats.d.ts +44 -0
  122. package/dist/arb/history-stats.js +135 -0
  123. package/dist/arb/index.d.ts +4 -0
  124. package/dist/arb/index.js +4 -0
  125. package/dist/arb/sizing.d.ts +23 -0
  126. package/dist/arb/sizing.js +96 -0
  127. package/dist/arb/state.d.ts +51 -0
  128. package/dist/arb/state.js +112 -0
  129. package/dist/arb/utils.d.ts +81 -0
  130. package/dist/arb/utils.js +267 -0
  131. package/dist/arb-history-stats.d.ts +5 -0
  132. package/dist/arb-history-stats.js +5 -0
  133. package/dist/arb-sizing.d.ts +5 -0
  134. package/dist/arb-sizing.js +5 -0
  135. package/dist/arb-state.d.ts +5 -0
  136. package/dist/arb-state.js +5 -0
  137. package/dist/arb-utils.d.ts +5 -0
  138. package/dist/arb-utils.js +5 -0
  139. package/dist/bot/conditions.d.ts +32 -0
  140. package/dist/bot/conditions.js +141 -0
  141. package/dist/bot/config.d.ts +76 -0
  142. package/dist/bot/config.js +160 -0
  143. package/dist/bot/engine.d.ts +8 -0
  144. package/dist/bot/engine.js +519 -0
  145. package/dist/bot/presets.d.ts +11 -0
  146. package/dist/bot/presets.js +296 -0
  147. package/dist/bridge-engine.d.ts +133 -0
  148. package/dist/bridge-engine.js +1487 -0
  149. package/dist/cache.d.ts +25 -0
  150. package/dist/cache.js +99 -0
  151. package/dist/cli-spec.d.ts +50 -0
  152. package/dist/cli-spec.js +75 -0
  153. package/dist/client-id-tracker.d.ts +25 -0
  154. package/dist/client-id-tracker.js +76 -0
  155. package/dist/commands/account.d.ts +3 -0
  156. package/dist/commands/account.js +425 -0
  157. package/dist/commands/agent.d.ts +3 -0
  158. package/dist/commands/agent.js +386 -0
  159. package/dist/commands/alert.d.ts +2 -0
  160. package/dist/commands/alert.js +421 -0
  161. package/dist/commands/analytics.d.ts +3 -0
  162. package/dist/commands/analytics.js +311 -0
  163. package/dist/commands/arb/index.d.ts +3 -0
  164. package/dist/commands/arb/index.js +921 -0
  165. package/dist/commands/arb-auto.d.ts +54 -0
  166. package/dist/commands/arb-auto.js +1328 -0
  167. package/dist/commands/arb-manage.d.ts +5 -0
  168. package/dist/commands/arb-manage.js +5 -0
  169. package/dist/commands/arb.d.ts +2 -0
  170. package/dist/commands/arb.js +347 -0
  171. package/dist/commands/backtest.d.ts +2 -0
  172. package/dist/commands/backtest.js +327 -0
  173. package/dist/commands/bot.d.ts +3 -0
  174. package/dist/commands/bot.js +412 -0
  175. package/dist/commands/bridge.d.ts +2 -0
  176. package/dist/commands/bridge.js +396 -0
  177. package/dist/commands/dashboard.d.ts +3 -0
  178. package/dist/commands/dashboard.js +176 -0
  179. package/dist/commands/deposit.d.ts +4 -0
  180. package/dist/commands/deposit.js +573 -0
  181. package/dist/commands/dex.d.ts +3 -0
  182. package/dist/commands/dex.js +114 -0
  183. package/dist/commands/env.d.ts +2 -0
  184. package/dist/commands/env.js +136 -0
  185. package/dist/commands/funding.d.ts +2 -0
  186. package/dist/commands/funding.js +347 -0
  187. package/dist/commands/gap.d.ts +2 -0
  188. package/dist/commands/gap.js +305 -0
  189. package/dist/commands/health.d.ts +2 -0
  190. package/dist/commands/health.js +67 -0
  191. package/dist/commands/history.d.ts +2 -0
  192. package/dist/commands/history.js +235 -0
  193. package/dist/commands/init.d.ts +15 -0
  194. package/dist/commands/init.js +266 -0
  195. package/dist/commands/jobs.d.ts +2 -0
  196. package/dist/commands/jobs.js +133 -0
  197. package/dist/commands/manage.d.ts +4 -0
  198. package/dist/commands/manage.js +309 -0
  199. package/dist/commands/market.d.ts +3 -0
  200. package/dist/commands/market.js +225 -0
  201. package/dist/commands/plan.d.ts +3 -0
  202. package/dist/commands/plan.js +95 -0
  203. package/dist/commands/portfolio.d.ts +3 -0
  204. package/dist/commands/portfolio.js +169 -0
  205. package/dist/commands/rebalance.d.ts +3 -0
  206. package/dist/commands/rebalance.js +293 -0
  207. package/dist/commands/risk.d.ts +3 -0
  208. package/dist/commands/risk.js +169 -0
  209. package/dist/commands/run.d.ts +3 -0
  210. package/dist/commands/run.js +202 -0
  211. package/dist/commands/settings.d.ts +2 -0
  212. package/dist/commands/settings.js +102 -0
  213. package/dist/commands/stream.d.ts +5 -0
  214. package/dist/commands/stream.js +123 -0
  215. package/dist/commands/trade.d.ts +3 -0
  216. package/dist/commands/trade.js +1273 -0
  217. package/dist/commands/wallet.d.ts +14 -0
  218. package/dist/commands/wallet.js +602 -0
  219. package/dist/commands/withdraw.d.ts +3 -0
  220. package/dist/commands/withdraw.js +187 -0
  221. package/dist/config.d.ts +5 -0
  222. package/dist/config.js +68 -0
  223. package/dist/cross-chain-margin.d.ts +46 -0
  224. package/dist/cross-chain-margin.js +107 -0
  225. package/dist/dashboard/server.d.ts +80 -0
  226. package/dist/dashboard/server.js +340 -0
  227. package/dist/dashboard/ui.d.ts +4 -0
  228. package/dist/dashboard/ui.js +538 -0
  229. package/dist/dashboard/ws-feeds.d.ts +29 -0
  230. package/dist/dashboard/ws-feeds.js +660 -0
  231. package/dist/dex-asset-map.d.ts +80 -0
  232. package/dist/dex-asset-map.js +201 -0
  233. package/dist/errors.d.ts +109 -0
  234. package/dist/errors.js +84 -0
  235. package/dist/event-stream.d.ts +25 -0
  236. package/dist/event-stream.js +168 -0
  237. package/dist/exchanges/hyperliquid.d.ts +212 -0
  238. package/dist/exchanges/hyperliquid.js +931 -0
  239. package/dist/exchanges/interface.d.ts +95 -0
  240. package/dist/exchanges/interface.js +5 -0
  241. package/dist/exchanges/lighter.d.ts +159 -0
  242. package/dist/exchanges/lighter.js +793 -0
  243. package/dist/exchanges/pacifica.d.ts +51 -0
  244. package/dist/exchanges/pacifica.js +248 -0
  245. package/dist/execution-log.d.ts +36 -0
  246. package/dist/execution-log.js +102 -0
  247. package/dist/funding/history.d.ts +63 -0
  248. package/dist/funding/history.js +266 -0
  249. package/dist/funding/index.d.ts +3 -0
  250. package/dist/funding/index.js +3 -0
  251. package/dist/funding/normalize.d.ts +39 -0
  252. package/dist/funding/normalize.js +66 -0
  253. package/dist/funding/rates.d.ts +45 -0
  254. package/dist/funding/rates.js +172 -0
  255. package/dist/funding-history.d.ts +5 -0
  256. package/dist/funding-history.js +5 -0
  257. package/dist/funding-rates.d.ts +5 -0
  258. package/dist/funding-rates.js +5 -0
  259. package/dist/funding.d.ts +5 -0
  260. package/dist/funding.js +5 -0
  261. package/dist/index.d.ts +2 -0
  262. package/dist/index.js +458 -0
  263. package/dist/jobs.d.ts +37 -0
  264. package/dist/jobs.js +152 -0
  265. package/dist/liquidity.d.ts +34 -0
  266. package/dist/liquidity.js +100 -0
  267. package/dist/mcp-server.d.ts +9 -0
  268. package/dist/mcp-server.js +1206 -0
  269. package/dist/pacifica/client.d.ts +111 -0
  270. package/dist/pacifica/client.js +310 -0
  271. package/dist/pacifica/constants.d.ts +27 -0
  272. package/dist/pacifica/constants.js +47 -0
  273. package/dist/pacifica/deposit.d.ts +14 -0
  274. package/dist/pacifica/deposit.js +78 -0
  275. package/dist/pacifica/index.d.ts +6 -0
  276. package/dist/pacifica/index.js +11 -0
  277. package/dist/pacifica/signing.d.ts +49 -0
  278. package/dist/pacifica/signing.js +97 -0
  279. package/dist/pacifica/types/account.d.ts +42 -0
  280. package/dist/pacifica/types/account.js +1 -0
  281. package/dist/pacifica/types/index.d.ts +6 -0
  282. package/dist/pacifica/types/index.js +6 -0
  283. package/dist/pacifica/types/lake.d.ts +18 -0
  284. package/dist/pacifica/types/lake.js +1 -0
  285. package/dist/pacifica/types/market.d.ts +64 -0
  286. package/dist/pacifica/types/market.js +1 -0
  287. package/dist/pacifica/types/order.d.ts +92 -0
  288. package/dist/pacifica/types/order.js +1 -0
  289. package/dist/pacifica/types/position.d.ts +25 -0
  290. package/dist/pacifica/types/position.js +1 -0
  291. package/dist/pacifica/types/ws.d.ts +34 -0
  292. package/dist/pacifica/types/ws.js +41 -0
  293. package/dist/pacifica/ws-client.d.ts +42 -0
  294. package/dist/pacifica/ws-client.js +180 -0
  295. package/dist/plan-executor.d.ts +48 -0
  296. package/dist/plan-executor.js +280 -0
  297. package/dist/position-history.d.ts +68 -0
  298. package/dist/position-history.js +222 -0
  299. package/dist/rebalance.d.ts +64 -0
  300. package/dist/rebalance.js +142 -0
  301. package/dist/retry.d.ts +74 -0
  302. package/dist/retry.js +129 -0
  303. package/dist/risk.d.ts +48 -0
  304. package/dist/risk.js +156 -0
  305. package/dist/settings.d.ts +19 -0
  306. package/dist/settings.js +45 -0
  307. package/dist/shared-api.d.ts +5 -0
  308. package/dist/shared-api.js +5 -0
  309. package/dist/strategies/dca.d.ts +25 -0
  310. package/dist/strategies/dca.js +114 -0
  311. package/dist/strategies/funding-arb.d.ts +15 -0
  312. package/dist/strategies/funding-arb.js +281 -0
  313. package/dist/strategies/grid.d.ts +34 -0
  314. package/dist/strategies/grid.js +185 -0
  315. package/dist/strategies/trailing-stop.d.ts +17 -0
  316. package/dist/strategies/trailing-stop.js +121 -0
  317. package/dist/strategies/twap.d.ts +20 -0
  318. package/dist/strategies/twap.js +78 -0
  319. package/dist/trade-validator.d.ts +39 -0
  320. package/dist/trade-validator.js +154 -0
  321. package/dist/utils.d.ts +38 -0
  322. package/dist/utils.js +110 -0
  323. package/package.json +63 -0
  324. package/skills/perp-cli/SKILL.md +149 -0
  325. package/skills/perp-cli/references/commands.md +143 -0
@@ -0,0 +1,142 @@
1
+ import { EXCHANGE_TO_CHAIN } from "./bridge-engine.js";
2
+ // Re-export for backward compat
3
+ const EXCHANGE_CHAINS = Object.fromEntries(Object.entries(EXCHANGE_TO_CHAIN).map(([ex, chain]) => [ex, { chain }]));
4
+ /**
5
+ * Fetch balances from all exchanges in parallel.
6
+ */
7
+ export async function fetchAllBalances(adapters) {
8
+ const entries = [...adapters.entries()];
9
+ const results = await Promise.allSettled(entries.map(async ([name, adapter]) => {
10
+ const bal = await adapter.getBalance();
11
+ return {
12
+ exchange: name,
13
+ equity: Number(bal.equity),
14
+ available: Number(bal.available),
15
+ marginUsed: Number(bal.marginUsed),
16
+ unrealizedPnl: Number(bal.unrealizedPnl),
17
+ };
18
+ }));
19
+ return results
20
+ .filter((r) => r.status === "fulfilled")
21
+ .map((r) => r.value);
22
+ }
23
+ /**
24
+ * Compute a rebalancing plan to equalize available balance across exchanges.
25
+ *
26
+ * Strategy: equal-weight — target = totalAvailable / numExchanges
27
+ * Only moves from exchanges with surplus to those with deficit.
28
+ * Respects a minimum move threshold to avoid tiny transfers.
29
+ */
30
+ export function computeRebalancePlan(snapshots, opts = {}) {
31
+ const minMove = opts.minMove ?? 50;
32
+ const reserve = opts.reserve ?? 20;
33
+ const totalEquity = snapshots.reduce((s, e) => s + e.equity, 0);
34
+ const totalAvailable = snapshots.reduce((s, e) => s + e.available, 0);
35
+ // Calculate targets
36
+ const weights = opts.weights ?? Object.fromEntries(snapshots.map((s) => [s.exchange, 1 / snapshots.length]));
37
+ const targets = new Map();
38
+ for (const snap of snapshots) {
39
+ const w = weights[snap.exchange] ?? 1 / snapshots.length;
40
+ targets.set(snap.exchange, totalAvailable * w);
41
+ }
42
+ // Calculate deltas (positive = surplus, negative = deficit)
43
+ const deltas = new Map();
44
+ for (const snap of snapshots) {
45
+ const target = targets.get(snap.exchange) ?? 0;
46
+ const movable = Math.max(0, snap.available - reserve);
47
+ const delta = movable - Math.max(0, target - reserve);
48
+ deltas.set(snap.exchange, delta);
49
+ }
50
+ // Match surplus → deficit
51
+ const moves = [];
52
+ const surpluses = [...deltas.entries()].filter(([, d]) => d > minMove).sort((a, b) => b[1] - a[1]);
53
+ const deficits = [...deltas.entries()].filter(([, d]) => d < -minMove).sort((a, b) => a[1] - b[1]);
54
+ for (const [fromEx, surplus] of surpluses) {
55
+ let remaining = surplus;
56
+ for (const deficit of deficits) {
57
+ if (remaining < minMove)
58
+ break;
59
+ const [toEx, deficitAmt] = deficit;
60
+ if (deficitAmt >= -minMove)
61
+ continue;
62
+ const moveAmt = Math.min(remaining, Math.abs(deficitAmt));
63
+ if (moveAmt < minMove)
64
+ continue;
65
+ moves.push({
66
+ from: fromEx,
67
+ to: toEx,
68
+ amount: Math.floor(moveAmt),
69
+ reason: `Rebalance: ${fromEx} has $${Math.floor(surplus)} surplus, ${toEx} needs $${Math.floor(Math.abs(deficitAmt))}`,
70
+ });
71
+ remaining -= moveAmt;
72
+ deficit[1] += moveAmt; // reduce deficit
73
+ }
74
+ }
75
+ const summary = moves.length === 0
76
+ ? "Balanced — no moves needed"
77
+ : `${moves.length} move(s): ${moves.map((m) => `$${m.amount} ${m.from}→${m.to}`).join(", ")}`;
78
+ return {
79
+ snapshots,
80
+ totalEquity,
81
+ targetPerExchange: totalAvailable / snapshots.length,
82
+ moves,
83
+ summary,
84
+ };
85
+ }
86
+ /**
87
+ * Check if an exchange has enough available balance for a given trade size.
88
+ */
89
+ export function hasEnoughBalance(snapshots, exchange, requiredUsd, marginBuffer = 1.5) {
90
+ const snap = snapshots.find((s) => s.exchange === exchange);
91
+ if (!snap)
92
+ return false;
93
+ return snap.available >= requiredUsd * marginBuffer;
94
+ }
95
+ /**
96
+ * Get the chain/bridge info for an exchange.
97
+ */
98
+ export function getExchangeChain(exchange) {
99
+ return EXCHANGE_CHAINS[exchange];
100
+ }
101
+ /**
102
+ * Describe the bridge route for a rebalance move.
103
+ */
104
+ export function describeBridgeRoute(move) {
105
+ const from = EXCHANGE_CHAINS[move.from];
106
+ const to = EXCHANGE_CHAINS[move.to];
107
+ if (!from || !to)
108
+ return `${move.from} → ${move.to} (unknown route)`;
109
+ if (from.chain === to.chain)
110
+ return `Internal transfer on ${from.chain}`;
111
+ // All cross-chain moves go through USDC bridging
112
+ const steps = [];
113
+ steps.push(`1. Withdraw $${move.amount} USDC from ${move.from} (${from.chain})`);
114
+ // Determine bridge method
115
+ if (from.chain === "solana" || to.chain === "solana") {
116
+ steps.push(`2. Bridge via CCTP/Wormhole (${from.chain} → ${to.chain})`);
117
+ }
118
+ else {
119
+ steps.push(`2. Bridge via CCTP (${from.chain} → ${to.chain})`);
120
+ }
121
+ steps.push(`3. Deposit into ${move.to} (${to.chain})`);
122
+ return steps.join("\n ");
123
+ }
124
+ /**
125
+ * Estimate total time for a rebalance move.
126
+ */
127
+ export function estimateMoveTime(move) {
128
+ const from = EXCHANGE_CHAINS[move.from]?.chain;
129
+ const to = EXCHANGE_CHAINS[move.to]?.chain;
130
+ // Withdrawal times
131
+ const withdrawTime = {
132
+ pacifica: "~10s",
133
+ hyperliquid: "~5min",
134
+ lighter: "~12h (standard)",
135
+ };
136
+ // Bridge times
137
+ let bridgeTime = "~1-3min (CCTP)";
138
+ if (from === "solana" || to === "solana") {
139
+ bridgeTime = "~15min (Wormhole/CCTP)";
140
+ }
141
+ return `Withdraw: ${withdrawTime[move.from] ?? "?"} → Bridge: ${bridgeTime} → Deposit: ~1min`;
142
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Auto-retry middleware for exchange operations.
3
+ * Retries operations that fail with retryable error codes.
4
+ */
5
+ import { type StructuredError } from "./errors.js";
6
+ import type { ExchangeAdapter } from "./exchanges/interface.js";
7
+ export interface RetryOptions {
8
+ maxRetries?: number;
9
+ baseDelayMs?: number;
10
+ maxDelayMs?: number;
11
+ backoffMultiplier?: number;
12
+ onRetry?: (attempt: number, error: StructuredError, delayMs: number) => void;
13
+ }
14
+ export interface RetryResult<T> {
15
+ data: T;
16
+ attempts: number;
17
+ totalDelayMs: number;
18
+ retries: {
19
+ attempt: number;
20
+ error: StructuredError;
21
+ delayMs: number;
22
+ }[];
23
+ }
24
+ /** Error thrown when all retries are exhausted */
25
+ export declare class RetriesExhaustedError extends Error {
26
+ readonly lastError: StructuredError;
27
+ readonly attempts: number;
28
+ readonly retries: {
29
+ attempt: number;
30
+ error: StructuredError;
31
+ delayMs: number;
32
+ }[];
33
+ constructor(lastError: StructuredError, attempts: number, retries: {
34
+ attempt: number;
35
+ error: StructuredError;
36
+ delayMs: number;
37
+ }[]);
38
+ }
39
+ /**
40
+ * Apply jitter of +/-20% to a delay value.
41
+ * Returns a value in [delay * 0.8, delay * 1.2].
42
+ */
43
+ export declare function applyJitter(delayMs: number): number;
44
+ /**
45
+ * Compute the delay for a given retry attempt.
46
+ * Uses exponential backoff, respects retryAfterMs from the error code,
47
+ * applies jitter, and caps at maxDelayMs.
48
+ */
49
+ export declare function computeDelay(attempt: number, error: StructuredError, opts: Required<Omit<RetryOptions, "onRetry">>): number;
50
+ /**
51
+ * Execute `fn` with automatic retry on retryable errors.
52
+ *
53
+ * - On failure, classifies the error via `classifyError`.
54
+ * - If `retryable: true`, waits with exponential backoff and retries.
55
+ * - If `retryable: false`, throws immediately.
56
+ * - Uses `retryAfterMs` from the error code if available (e.g., rate limit = 1000ms).
57
+ * - Caps delay at `maxDelayMs`.
58
+ * - Adds jitter (+/-20%) to prevent thundering herd.
59
+ * - Returns the result with retry metadata.
60
+ */
61
+ export declare function withRetry<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<RetryResult<T>>;
62
+ /**
63
+ * Simpler version of withRetry that just returns T (no metadata).
64
+ * For use in places where you just want automatic retry without caring about details.
65
+ */
66
+ export declare function withRetrySimple<T>(fn: () => Promise<T>, maxRetries?: number): Promise<T>;
67
+ /**
68
+ * Wrap an ExchangeAdapter so every async method is automatically retried.
69
+ *
70
+ * Returns a Proxy that intercepts property access:
71
+ * - For non-function properties (like `name`), returns the value directly.
72
+ * - For function properties, returns a wrapper that calls `withRetry` around the original method.
73
+ */
74
+ export declare function wrapAdapterWithRetry(adapter: ExchangeAdapter, opts?: RetryOptions): ExchangeAdapter;
package/dist/retry.js ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Auto-retry middleware for exchange operations.
3
+ * Retries operations that fail with retryable error codes.
4
+ */
5
+ import { classifyError } from "./errors.js";
6
+ /** Error thrown when all retries are exhausted */
7
+ export class RetriesExhaustedError extends Error {
8
+ lastError;
9
+ attempts;
10
+ retries;
11
+ constructor(lastError, attempts, retries) {
12
+ super(`All ${attempts} attempts failed. Last error: [${lastError.code}] ${lastError.message}`);
13
+ this.name = "RetriesExhaustedError";
14
+ this.lastError = lastError;
15
+ this.attempts = attempts;
16
+ this.retries = retries;
17
+ }
18
+ }
19
+ const DEFAULT_OPTIONS = {
20
+ maxRetries: 3,
21
+ baseDelayMs: 1000,
22
+ maxDelayMs: 30000,
23
+ backoffMultiplier: 2,
24
+ };
25
+ /**
26
+ * Apply jitter of +/-20% to a delay value.
27
+ * Returns a value in [delay * 0.8, delay * 1.2].
28
+ */
29
+ export function applyJitter(delayMs) {
30
+ const jitterFactor = 0.8 + Math.random() * 0.4; // [0.8, 1.2]
31
+ return Math.round(delayMs * jitterFactor);
32
+ }
33
+ /**
34
+ * Compute the delay for a given retry attempt.
35
+ * Uses exponential backoff, respects retryAfterMs from the error code,
36
+ * applies jitter, and caps at maxDelayMs.
37
+ */
38
+ export function computeDelay(attempt, error, opts) {
39
+ // Start with exponential backoff: baseDelay * multiplier^(attempt-1)
40
+ let delay = opts.baseDelayMs * Math.pow(opts.backoffMultiplier, attempt - 1);
41
+ // If the error specifies a retryAfterMs, use the larger of the two
42
+ if (error.retryAfterMs !== undefined) {
43
+ delay = Math.max(delay, error.retryAfterMs);
44
+ }
45
+ // Cap at maxDelayMs
46
+ delay = Math.min(delay, opts.maxDelayMs);
47
+ // Apply jitter
48
+ return applyJitter(delay);
49
+ }
50
+ /** Internal sleep helper */
51
+ function sleep(ms) {
52
+ return new Promise((resolve) => setTimeout(resolve, ms));
53
+ }
54
+ /**
55
+ * Execute `fn` with automatic retry on retryable errors.
56
+ *
57
+ * - On failure, classifies the error via `classifyError`.
58
+ * - If `retryable: true`, waits with exponential backoff and retries.
59
+ * - If `retryable: false`, throws immediately.
60
+ * - Uses `retryAfterMs` from the error code if available (e.g., rate limit = 1000ms).
61
+ * - Caps delay at `maxDelayMs`.
62
+ * - Adds jitter (+/-20%) to prevent thundering herd.
63
+ * - Returns the result with retry metadata.
64
+ */
65
+ export async function withRetry(fn, opts) {
66
+ const { maxRetries, baseDelayMs, maxDelayMs, backoffMultiplier } = {
67
+ ...DEFAULT_OPTIONS,
68
+ ...opts,
69
+ };
70
+ const resolvedOpts = { maxRetries, baseDelayMs, maxDelayMs, backoffMultiplier };
71
+ const retries = [];
72
+ let totalDelayMs = 0;
73
+ for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
74
+ try {
75
+ const data = await fn();
76
+ return { data, attempts: attempt, totalDelayMs, retries };
77
+ }
78
+ catch (err) {
79
+ const classified = classifyError(err);
80
+ // Non-retryable error: throw immediately
81
+ if (!classified.retryable) {
82
+ throw err;
83
+ }
84
+ // Out of retries: throw with exhaustion info
85
+ if (attempt > maxRetries) {
86
+ throw new RetriesExhaustedError(classified, attempt, retries);
87
+ }
88
+ const delayMs = computeDelay(attempt, classified, resolvedOpts);
89
+ // Call onRetry callback if provided
90
+ opts?.onRetry?.(attempt, classified, delayMs);
91
+ retries.push({ attempt, error: classified, delayMs });
92
+ totalDelayMs += delayMs;
93
+ await sleep(delayMs);
94
+ }
95
+ }
96
+ // Unreachable, but TypeScript needs it
97
+ throw new Error("withRetry: unexpected exit from retry loop");
98
+ }
99
+ /**
100
+ * Simpler version of withRetry that just returns T (no metadata).
101
+ * For use in places where you just want automatic retry without caring about details.
102
+ */
103
+ export async function withRetrySimple(fn, maxRetries) {
104
+ const result = await withRetry(fn, maxRetries !== undefined ? { maxRetries } : undefined);
105
+ return result.data;
106
+ }
107
+ /**
108
+ * Wrap an ExchangeAdapter so every async method is automatically retried.
109
+ *
110
+ * Returns a Proxy that intercepts property access:
111
+ * - For non-function properties (like `name`), returns the value directly.
112
+ * - For function properties, returns a wrapper that calls `withRetry` around the original method.
113
+ */
114
+ export function wrapAdapterWithRetry(adapter, opts) {
115
+ return new Proxy(adapter, {
116
+ get(target, prop, receiver) {
117
+ const value = Reflect.get(target, prop, receiver);
118
+ // Pass through non-function properties (e.g., `name` getter)
119
+ if (typeof value !== "function") {
120
+ return value;
121
+ }
122
+ // Wrap function in retry logic
123
+ return async function (...args) {
124
+ const result = await withRetry(() => value.apply(target, args), opts);
125
+ return result.data;
126
+ };
127
+ },
128
+ });
129
+ }
package/dist/risk.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import type { ExchangeBalance, ExchangePosition } from "./exchanges/interface.js";
2
+ export interface RiskLimits {
3
+ maxDrawdownUsd: number;
4
+ maxPositionUsd: number;
5
+ maxTotalExposureUsd: number;
6
+ dailyLossLimitUsd: number;
7
+ maxPositions: number;
8
+ maxLeverage: number;
9
+ maxMarginUtilization: number;
10
+ }
11
+ export declare function loadRiskLimits(): RiskLimits;
12
+ export declare function saveRiskLimits(limits: RiskLimits): void;
13
+ export type RiskLevel = "low" | "medium" | "high" | "critical";
14
+ export interface RiskViolation {
15
+ rule: string;
16
+ severity: RiskLevel;
17
+ message: string;
18
+ current: number;
19
+ limit: number;
20
+ }
21
+ export interface RiskAssessment {
22
+ level: RiskLevel;
23
+ violations: RiskViolation[];
24
+ metrics: {
25
+ totalEquity: number;
26
+ totalUnrealizedPnl: number;
27
+ totalMarginUsed: number;
28
+ totalExposure: number;
29
+ positionCount: number;
30
+ marginUtilization: number;
31
+ largestPositionUsd: number;
32
+ maxLeverageUsed: number;
33
+ };
34
+ limits: RiskLimits;
35
+ canTrade: boolean;
36
+ }
37
+ export declare function assessRisk(balances: {
38
+ exchange: string;
39
+ balance: ExchangeBalance;
40
+ }[], positions: {
41
+ exchange: string;
42
+ position: ExchangePosition;
43
+ }[], limits?: RiskLimits): RiskAssessment;
44
+ /** Pre-trade check: would this new order violate risk limits? */
45
+ export declare function preTradeCheck(assessment: RiskAssessment, newOrderNotional: number, newOrderLeverage: number): {
46
+ allowed: boolean;
47
+ reason?: string;
48
+ };
package/dist/risk.js ADDED
@@ -0,0 +1,156 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
2
+ import { resolve } from "path";
3
+ const DEFAULT_LIMITS = {
4
+ maxDrawdownUsd: 500,
5
+ maxPositionUsd: 5000,
6
+ maxTotalExposureUsd: 20000,
7
+ dailyLossLimitUsd: 200,
8
+ maxPositions: 10,
9
+ maxLeverage: 20,
10
+ maxMarginUtilization: 80,
11
+ };
12
+ const PERP_DIR = resolve(process.env.HOME || "~", ".perp");
13
+ const RISK_FILE = resolve(PERP_DIR, "risk.json");
14
+ export function loadRiskLimits() {
15
+ if (!existsSync(RISK_FILE))
16
+ return { ...DEFAULT_LIMITS };
17
+ try {
18
+ const stored = JSON.parse(readFileSync(RISK_FILE, "utf-8"));
19
+ return { ...DEFAULT_LIMITS, ...stored };
20
+ }
21
+ catch {
22
+ return { ...DEFAULT_LIMITS };
23
+ }
24
+ }
25
+ export function saveRiskLimits(limits) {
26
+ if (!existsSync(PERP_DIR))
27
+ mkdirSync(PERP_DIR, { recursive: true, mode: 0o700 });
28
+ writeFileSync(RISK_FILE, JSON.stringify(limits, null, 2), { mode: 0o600 });
29
+ }
30
+ export function assessRisk(balances, positions, limits) {
31
+ const lim = limits ?? loadRiskLimits();
32
+ const violations = [];
33
+ // Compute metrics
34
+ let totalEquity = 0;
35
+ let totalUnrealizedPnl = 0;
36
+ let totalMarginUsed = 0;
37
+ for (const { balance } of balances) {
38
+ totalEquity += Number(balance.equity);
39
+ totalUnrealizedPnl += Number(balance.unrealizedPnl);
40
+ totalMarginUsed += Number(balance.marginUsed);
41
+ }
42
+ let totalExposure = 0;
43
+ let largestPositionUsd = 0;
44
+ let maxLeverageUsed = 0;
45
+ for (const { position: p } of positions) {
46
+ const notional = Math.abs(Number(p.size) * Number(p.markPrice));
47
+ totalExposure += notional;
48
+ if (notional > largestPositionUsd)
49
+ largestPositionUsd = notional;
50
+ const lev = Number(p.leverage) || 0;
51
+ if (lev > maxLeverageUsed)
52
+ maxLeverageUsed = lev;
53
+ }
54
+ const marginUtilization = totalEquity > 0 ? (totalMarginUsed / totalEquity) * 100 : 0;
55
+ // Check violations
56
+ if (totalUnrealizedPnl < -lim.maxDrawdownUsd) {
57
+ violations.push({
58
+ rule: "max_drawdown",
59
+ severity: "critical",
60
+ message: `Unrealized loss $${Math.abs(totalUnrealizedPnl).toFixed(2)} exceeds max drawdown $${lim.maxDrawdownUsd}`,
61
+ current: Math.abs(totalUnrealizedPnl),
62
+ limit: lim.maxDrawdownUsd,
63
+ });
64
+ }
65
+ if (largestPositionUsd > lim.maxPositionUsd) {
66
+ violations.push({
67
+ rule: "max_position_size",
68
+ severity: "high",
69
+ message: `Largest position $${largestPositionUsd.toFixed(2)} exceeds limit $${lim.maxPositionUsd}`,
70
+ current: largestPositionUsd,
71
+ limit: lim.maxPositionUsd,
72
+ });
73
+ }
74
+ if (totalExposure > lim.maxTotalExposureUsd) {
75
+ violations.push({
76
+ rule: "max_total_exposure",
77
+ severity: "high",
78
+ message: `Total exposure $${totalExposure.toFixed(2)} exceeds limit $${lim.maxTotalExposureUsd}`,
79
+ current: totalExposure,
80
+ limit: lim.maxTotalExposureUsd,
81
+ });
82
+ }
83
+ if (positions.length > lim.maxPositions) {
84
+ violations.push({
85
+ rule: "max_positions",
86
+ severity: "medium",
87
+ message: `${positions.length} positions exceeds limit of ${lim.maxPositions}`,
88
+ current: positions.length,
89
+ limit: lim.maxPositions,
90
+ });
91
+ }
92
+ if (maxLeverageUsed > lim.maxLeverage) {
93
+ violations.push({
94
+ rule: "max_leverage",
95
+ severity: "high",
96
+ message: `Leverage ${maxLeverageUsed}x exceeds limit ${lim.maxLeverage}x`,
97
+ current: maxLeverageUsed,
98
+ limit: lim.maxLeverage,
99
+ });
100
+ }
101
+ if (marginUtilization > lim.maxMarginUtilization) {
102
+ violations.push({
103
+ rule: "max_margin_utilization",
104
+ severity: marginUtilization > 90 ? "critical" : "high",
105
+ message: `Margin utilization ${marginUtilization.toFixed(1)}% exceeds limit ${lim.maxMarginUtilization}%`,
106
+ current: marginUtilization,
107
+ limit: lim.maxMarginUtilization,
108
+ });
109
+ }
110
+ // Determine overall risk level
111
+ let level = "low";
112
+ if (violations.some(v => v.severity === "critical"))
113
+ level = "critical";
114
+ else if (violations.some(v => v.severity === "high"))
115
+ level = "high";
116
+ else if (violations.some(v => v.severity === "medium"))
117
+ level = "medium";
118
+ // Can trade only if no critical violations
119
+ const canTrade = !violations.some(v => v.severity === "critical");
120
+ return {
121
+ level,
122
+ violations,
123
+ metrics: {
124
+ totalEquity,
125
+ totalUnrealizedPnl,
126
+ totalMarginUsed,
127
+ totalExposure,
128
+ positionCount: positions.length,
129
+ marginUtilization,
130
+ largestPositionUsd,
131
+ maxLeverageUsed,
132
+ },
133
+ limits: lim,
134
+ canTrade,
135
+ };
136
+ }
137
+ /** Pre-trade check: would this new order violate risk limits? */
138
+ export function preTradeCheck(assessment, newOrderNotional, newOrderLeverage) {
139
+ if (!assessment.canTrade) {
140
+ return { allowed: false, reason: "Trading suspended: critical risk violation active" };
141
+ }
142
+ const lim = assessment.limits;
143
+ if (newOrderNotional > lim.maxPositionUsd) {
144
+ return { allowed: false, reason: `Order notional $${newOrderNotional.toFixed(0)} exceeds max position size $${lim.maxPositionUsd}` };
145
+ }
146
+ if (assessment.metrics.totalExposure + newOrderNotional > lim.maxTotalExposureUsd) {
147
+ return { allowed: false, reason: `Would exceed total exposure limit ($${(assessment.metrics.totalExposure + newOrderNotional).toFixed(0)} > $${lim.maxTotalExposureUsd})` };
148
+ }
149
+ if (assessment.metrics.positionCount + 1 > lim.maxPositions) {
150
+ return { allowed: false, reason: `Would exceed max positions (${assessment.metrics.positionCount + 1} > ${lim.maxPositions})` };
151
+ }
152
+ if (newOrderLeverage > lim.maxLeverage) {
153
+ return { allowed: false, reason: `Leverage ${newOrderLeverage}x exceeds max ${lim.maxLeverage}x` };
154
+ }
155
+ return { allowed: true };
156
+ }
@@ -0,0 +1,19 @@
1
+ export interface Settings {
2
+ /** Default exchange when -e flag is omitted */
3
+ defaultExchange: string;
4
+ /** Enable referral/builder codes (default: false — opt-in only) */
5
+ referrals: boolean;
6
+ /** Per-exchange referral codes (used when referrals=true) */
7
+ referralCodes: {
8
+ pacifica: string;
9
+ hyperliquid: string;
10
+ lighter: string;
11
+ };
12
+ /** Track which exchanges have had referral codes applied (one-time) */
13
+ referralApplied: {
14
+ hyperliquid: boolean;
15
+ lighter: boolean;
16
+ };
17
+ }
18
+ export declare function loadSettings(): Settings;
19
+ export declare function saveSettings(settings: Settings): void;
@@ -0,0 +1,45 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
2
+ import { resolve } from "path";
3
+ const PERP_DIR = resolve(process.env.HOME || "~", ".perp");
4
+ const SETTINGS_FILE = resolve(PERP_DIR, "settings.json");
5
+ const DEFAULTS = {
6
+ defaultExchange: "",
7
+ referrals: false,
8
+ referralCodes: {
9
+ pacifica: "",
10
+ hyperliquid: "HYPERCASH",
11
+ lighter: "718585MY",
12
+ },
13
+ referralApplied: {
14
+ hyperliquid: false,
15
+ lighter: false,
16
+ },
17
+ };
18
+ export function loadSettings() {
19
+ if (!existsSync(SETTINGS_FILE))
20
+ return { ...DEFAULTS, referralCodes: { ...DEFAULTS.referralCodes } };
21
+ try {
22
+ const stored = JSON.parse(readFileSync(SETTINGS_FILE, "utf-8"));
23
+ return {
24
+ defaultExchange: stored.defaultExchange ?? DEFAULTS.defaultExchange,
25
+ referrals: stored.referrals ?? DEFAULTS.referrals,
26
+ referralCodes: {
27
+ pacifica: stored.referralCodes?.pacifica ?? DEFAULTS.referralCodes.pacifica,
28
+ hyperliquid: stored.referralCodes?.hyperliquid ?? DEFAULTS.referralCodes.hyperliquid,
29
+ lighter: stored.referralCodes?.lighter ?? DEFAULTS.referralCodes.lighter,
30
+ },
31
+ referralApplied: {
32
+ hyperliquid: stored.referralApplied?.hyperliquid ?? DEFAULTS.referralApplied.hyperliquid,
33
+ lighter: stored.referralApplied?.lighter ?? DEFAULTS.referralApplied.lighter,
34
+ },
35
+ };
36
+ }
37
+ catch {
38
+ return { ...DEFAULTS, referralCodes: { ...DEFAULTS.referralCodes } };
39
+ }
40
+ }
41
+ export function saveSettings(settings) {
42
+ if (!existsSync(PERP_DIR))
43
+ mkdirSync(PERP_DIR, { recursive: true, mode: 0o700 });
44
+ writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), { mode: 0o600 });
45
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Barrel re-export wrapper for backward compatibility.
3
+ * Actual implementation moved to src/api/public/.
4
+ */
5
+ export * from "./api/public/index.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Barrel re-export wrapper for backward compatibility.
3
+ * Actual implementation moved to src/api/public/.
4
+ */
5
+ export * from "./api/public/index.js";
@@ -0,0 +1,25 @@
1
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ export interface DCAParams {
3
+ symbol: string;
4
+ side: "buy" | "sell";
5
+ amountPerOrder: number;
6
+ intervalSec: number;
7
+ totalOrders: number;
8
+ priceLimit?: number;
9
+ maxRuntime?: number;
10
+ }
11
+ export interface DCAState {
12
+ ordersPlaced: number;
13
+ totalFilled: number;
14
+ totalCost: number;
15
+ avgPrice: number;
16
+ errors: number;
17
+ startedAt: number;
18
+ running: boolean;
19
+ }
20
+ export declare function runDCA(adapter: ExchangeAdapter, params: DCAParams, jobId?: string, log?: (msg: string) => void): Promise<{
21
+ ordersPlaced: number;
22
+ totalFilled: number;
23
+ avgPrice: number;
24
+ runtime: number;
25
+ }>;