@raintree-technology/perps 0.1.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 (316) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/dist/adapters/aevo.d.ts +64 -0
  5. package/dist/adapters/aevo.js +899 -0
  6. package/dist/adapters/certification.d.ts +33 -0
  7. package/dist/adapters/certification.js +99 -0
  8. package/dist/adapters/decibel/order-manager.d.ts +45 -0
  9. package/dist/adapters/decibel/order-manager.js +140 -0
  10. package/dist/adapters/decibel/rest-client.d.ts +176 -0
  11. package/dist/adapters/decibel/rest-client.js +155 -0
  12. package/dist/adapters/decibel/ws-feed.d.ts +28 -0
  13. package/dist/adapters/decibel/ws-feed.js +166 -0
  14. package/dist/adapters/decibel.d.ts +108 -0
  15. package/dist/adapters/decibel.js +1377 -0
  16. package/dist/adapters/hyperliquid.d.ts +63 -0
  17. package/dist/adapters/hyperliquid.js +797 -0
  18. package/dist/adapters/index.d.ts +11 -0
  19. package/dist/adapters/index.js +12 -0
  20. package/dist/adapters/interface.d.ts +310 -0
  21. package/dist/adapters/interface.js +15 -0
  22. package/dist/adapters/orderly.d.ts +70 -0
  23. package/dist/adapters/orderly.js +936 -0
  24. package/dist/adapters/paradex.d.ts +69 -0
  25. package/dist/adapters/paradex.js +862 -0
  26. package/dist/adapters/utils.d.ts +17 -0
  27. package/dist/adapters/utils.js +122 -0
  28. package/dist/cli/command-metadata.d.ts +2 -0
  29. package/dist/cli/command-metadata.js +44 -0
  30. package/dist/cli/context.d.ts +14 -0
  31. package/dist/cli/context.js +59 -0
  32. package/dist/cli/experience.d.ts +48 -0
  33. package/dist/cli/experience.js +243 -0
  34. package/dist/cli/ink/app/AppShell.d.ts +12 -0
  35. package/dist/cli/ink/app/AppShell.js +32 -0
  36. package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
  37. package/dist/cli/ink/app/MetricStrip.js +14 -0
  38. package/dist/cli/ink/app/Panel.d.ts +9 -0
  39. package/dist/cli/ink/app/Panel.js +7 -0
  40. package/dist/cli/ink/app/ascii.d.ts +2 -0
  41. package/dist/cli/ink/app/ascii.js +46 -0
  42. package/dist/cli/ink/app/index.d.ts +5 -0
  43. package/dist/cli/ink/app/index.js +4 -0
  44. package/dist/cli/ink/app/types.d.ts +15 -0
  45. package/dist/cli/ink/app/types.js +1 -0
  46. package/dist/cli/ink/components/PnL.d.ts +12 -0
  47. package/dist/cli/ink/components/PnL.js +23 -0
  48. package/dist/cli/ink/components/Spinner.d.ts +13 -0
  49. package/dist/cli/ink/components/Spinner.js +13 -0
  50. package/dist/cli/ink/components/Table.d.ts +14 -0
  51. package/dist/cli/ink/components/Table.js +42 -0
  52. package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
  53. package/dist/cli/ink/components/WatchHeader.js +18 -0
  54. package/dist/cli/ink/components/index.d.ts +4 -0
  55. package/dist/cli/ink/components/index.js +4 -0
  56. package/dist/cli/ink/index.d.ts +4 -0
  57. package/dist/cli/ink/index.js +4 -0
  58. package/dist/cli/ink/render.d.ts +12 -0
  59. package/dist/cli/ink/render.js +21 -0
  60. package/dist/cli/ink/theme.d.ts +29 -0
  61. package/dist/cli/ink/theme.js +40 -0
  62. package/dist/cli/network-defaults.d.ts +10 -0
  63. package/dist/cli/network-defaults.js +35 -0
  64. package/dist/cli/output.d.ts +11 -0
  65. package/dist/cli/output.js +115 -0
  66. package/dist/cli/program.d.ts +18 -0
  67. package/dist/cli/program.js +164 -0
  68. package/dist/cli/watch.d.ts +19 -0
  69. package/dist/cli/watch.js +35 -0
  70. package/dist/client/index.d.ts +55 -0
  71. package/dist/client/index.js +157 -0
  72. package/dist/commands/account/add.d.ts +2 -0
  73. package/dist/commands/account/add.js +510 -0
  74. package/dist/commands/account/balances-simple.d.ts +5 -0
  75. package/dist/commands/account/balances-simple.js +63 -0
  76. package/dist/commands/account/index.d.ts +2 -0
  77. package/dist/commands/account/index.js +17 -0
  78. package/dist/commands/account/ls.d.ts +2 -0
  79. package/dist/commands/account/ls.js +95 -0
  80. package/dist/commands/account/positions-simple.d.ts +5 -0
  81. package/dist/commands/account/positions-simple.js +77 -0
  82. package/dist/commands/account/remove.d.ts +2 -0
  83. package/dist/commands/account/remove.js +47 -0
  84. package/dist/commands/account/set-default.d.ts +2 -0
  85. package/dist/commands/account/set-default.js +47 -0
  86. package/dist/commands/agent/index.d.ts +2 -0
  87. package/dist/commands/agent/index.js +126 -0
  88. package/dist/commands/arb/alert.d.ts +6 -0
  89. package/dist/commands/arb/alert.js +88 -0
  90. package/dist/commands/arb/basis-execute.d.ts +6 -0
  91. package/dist/commands/arb/basis-execute.js +332 -0
  92. package/dist/commands/arb/basis.d.ts +6 -0
  93. package/dist/commands/arb/basis.js +181 -0
  94. package/dist/commands/arb/compare.d.ts +6 -0
  95. package/dist/commands/arb/compare.js +216 -0
  96. package/dist/commands/arb/execute.d.ts +6 -0
  97. package/dist/commands/arb/execute.js +467 -0
  98. package/dist/commands/arb/funding.d.ts +6 -0
  99. package/dist/commands/arb/funding.js +201 -0
  100. package/dist/commands/arb/history.d.ts +6 -0
  101. package/dist/commands/arb/history.js +153 -0
  102. package/dist/commands/arb/index.d.ts +6 -0
  103. package/dist/commands/arb/index.js +29 -0
  104. package/dist/commands/arb/positions.d.ts +6 -0
  105. package/dist/commands/arb/positions.js +158 -0
  106. package/dist/commands/arb/spread.d.ts +6 -0
  107. package/dist/commands/arb/spread.js +253 -0
  108. package/dist/commands/arb/track.d.ts +6 -0
  109. package/dist/commands/arb/track.js +259 -0
  110. package/dist/commands/asset/book-simple.d.ts +5 -0
  111. package/dist/commands/asset/book-simple.js +77 -0
  112. package/dist/commands/asset/index.d.ts +2 -0
  113. package/dist/commands/asset/index.js +5 -0
  114. package/dist/commands/completion.d.ts +2 -0
  115. package/dist/commands/completion.js +161 -0
  116. package/dist/commands/config/index.d.ts +5 -0
  117. package/dist/commands/config/index.js +109 -0
  118. package/dist/commands/data/index.d.ts +31 -0
  119. package/dist/commands/data/index.js +1466 -0
  120. package/dist/commands/doctor.d.ts +2 -0
  121. package/dist/commands/doctor.js +201 -0
  122. package/dist/commands/exchange/index.d.ts +2 -0
  123. package/dist/commands/exchange/index.js +107 -0
  124. package/dist/commands/index.d.ts +2 -0
  125. package/dist/commands/index.js +48 -0
  126. package/dist/commands/markets/index.d.ts +2 -0
  127. package/dist/commands/markets/index.js +5 -0
  128. package/dist/commands/markets/ls-simple.d.ts +7 -0
  129. package/dist/commands/markets/ls-simple.js +277 -0
  130. package/dist/commands/operator/index.d.ts +2 -0
  131. package/dist/commands/operator/index.js +146 -0
  132. package/dist/commands/order/cancel-simple.d.ts +5 -0
  133. package/dist/commands/order/cancel-simple.js +104 -0
  134. package/dist/commands/order/index.d.ts +2 -0
  135. package/dist/commands/order/index.js +13 -0
  136. package/dist/commands/order/limit-simple.d.ts +5 -0
  137. package/dist/commands/order/limit-simple.js +195 -0
  138. package/dist/commands/order/market-simple.d.ts +5 -0
  139. package/dist/commands/order/market-simple.js +190 -0
  140. package/dist/commands/order/shared.d.ts +17 -0
  141. package/dist/commands/order/shared.js +51 -0
  142. package/dist/commands/order/trigger-simple.d.ts +5 -0
  143. package/dist/commands/order/trigger-simple.js +246 -0
  144. package/dist/commands/referral/index.d.ts +2 -0
  145. package/dist/commands/referral/index.js +7 -0
  146. package/dist/commands/referral/set.d.ts +2 -0
  147. package/dist/commands/referral/set.js +26 -0
  148. package/dist/commands/referral/status.d.ts +2 -0
  149. package/dist/commands/referral/status.js +31 -0
  150. package/dist/commands/replay/index.d.ts +2 -0
  151. package/dist/commands/replay/index.js +152 -0
  152. package/dist/commands/risk/analytics.d.ts +2 -0
  153. package/dist/commands/risk/analytics.js +64 -0
  154. package/dist/commands/risk/audit.d.ts +2 -0
  155. package/dist/commands/risk/audit.js +52 -0
  156. package/dist/commands/risk/index.d.ts +2 -0
  157. package/dist/commands/risk/index.js +9 -0
  158. package/dist/commands/risk/rules.d.ts +2 -0
  159. package/dist/commands/risk/rules.js +102 -0
  160. package/dist/commands/server.d.ts +2 -0
  161. package/dist/commands/server.js +208 -0
  162. package/dist/commands/setup/index.d.ts +2 -0
  163. package/dist/commands/setup/index.js +478 -0
  164. package/dist/commands/signal/index.d.ts +2 -0
  165. package/dist/commands/signal/index.js +129 -0
  166. package/dist/commands/state/index.d.ts +2 -0
  167. package/dist/commands/state/index.js +5 -0
  168. package/dist/commands/state/show.d.ts +2 -0
  169. package/dist/commands/state/show.js +105 -0
  170. package/dist/commands/strategy/index.d.ts +4 -0
  171. package/dist/commands/strategy/index.js +73 -0
  172. package/dist/commands/traces/index.d.ts +2 -0
  173. package/dist/commands/traces/index.js +76 -0
  174. package/dist/commands/ui/demo.d.ts +9 -0
  175. package/dist/commands/ui/demo.js +195 -0
  176. package/dist/commands/ui/index.d.ts +2 -0
  177. package/dist/commands/ui/index.js +7 -0
  178. package/dist/commands/ui/terminal.d.ts +2 -0
  179. package/dist/commands/ui/terminal.js +255 -0
  180. package/dist/commands/upgrade.d.ts +2 -0
  181. package/dist/commands/upgrade.js +98 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +4 -0
  184. package/dist/lib/agent/audit.d.ts +12 -0
  185. package/dist/lib/agent/audit.js +13 -0
  186. package/dist/lib/agent/gateway.d.ts +13 -0
  187. package/dist/lib/agent/gateway.js +598 -0
  188. package/dist/lib/agent/metrics.d.ts +33 -0
  189. package/dist/lib/agent/metrics.js +175 -0
  190. package/dist/lib/agent/signature.d.ts +8 -0
  191. package/dist/lib/agent/signature.js +28 -0
  192. package/dist/lib/agent/tools.d.ts +28 -0
  193. package/dist/lib/agent/tools.js +453 -0
  194. package/dist/lib/agent/x402.d.ts +23 -0
  195. package/dist/lib/agent/x402.js +62 -0
  196. package/dist/lib/api-wallet.d.ts +69 -0
  197. package/dist/lib/api-wallet.js +101 -0
  198. package/dist/lib/balance-watcher.d.ts +25 -0
  199. package/dist/lib/balance-watcher.js +83 -0
  200. package/dist/lib/book-watcher.d.ts +25 -0
  201. package/dist/lib/book-watcher.js +48 -0
  202. package/dist/lib/config.d.ts +88 -0
  203. package/dist/lib/config.js +427 -0
  204. package/dist/lib/constants.d.ts +50 -0
  205. package/dist/lib/constants.js +84 -0
  206. package/dist/lib/contracts.d.ts +7 -0
  207. package/dist/lib/contracts.js +8 -0
  208. package/dist/lib/credential-vault.d.ts +22 -0
  209. package/dist/lib/credential-vault.js +109 -0
  210. package/dist/lib/db/accounts.d.ts +83 -0
  211. package/dist/lib/db/accounts.js +203 -0
  212. package/dist/lib/db/funding-history.d.ts +69 -0
  213. package/dist/lib/db/funding-history.js +183 -0
  214. package/dist/lib/db/index.d.ts +11 -0
  215. package/dist/lib/db/index.js +272 -0
  216. package/dist/lib/events/bus.d.ts +10 -0
  217. package/dist/lib/events/bus.js +17 -0
  218. package/dist/lib/events/types.d.ts +51 -0
  219. package/dist/lib/events/types.js +1 -0
  220. package/dist/lib/exchange.d.ts +30 -0
  221. package/dist/lib/exchange.js +84 -0
  222. package/dist/lib/execution/journal.d.ts +25 -0
  223. package/dist/lib/execution/journal.js +158 -0
  224. package/dist/lib/execution/safety.d.ts +34 -0
  225. package/dist/lib/execution/safety.js +197 -0
  226. package/dist/lib/exit-codes.d.ts +18 -0
  227. package/dist/lib/exit-codes.js +60 -0
  228. package/dist/lib/fetch.d.ts +18 -0
  229. package/dist/lib/fetch.js +66 -0
  230. package/dist/lib/fs-security.d.ts +10 -0
  231. package/dist/lib/fs-security.js +26 -0
  232. package/dist/lib/funding-tracker.d.ts +40 -0
  233. package/dist/lib/funding-tracker.js +118 -0
  234. package/dist/lib/logger.d.ts +27 -0
  235. package/dist/lib/logger.js +82 -0
  236. package/dist/lib/network-model.d.ts +13 -0
  237. package/dist/lib/network-model.js +30 -0
  238. package/dist/lib/onboarding.d.ts +133 -0
  239. package/dist/lib/onboarding.js +1459 -0
  240. package/dist/lib/operator-state.d.ts +25 -0
  241. package/dist/lib/operator-state.js +82 -0
  242. package/dist/lib/orders-watcher.d.ts +24 -0
  243. package/dist/lib/orders-watcher.js +74 -0
  244. package/dist/lib/paths.d.ts +20 -0
  245. package/dist/lib/paths.js +23 -0
  246. package/dist/lib/portfolio-watcher.d.ts +33 -0
  247. package/dist/lib/portfolio-watcher.js +95 -0
  248. package/dist/lib/position-watcher.d.ts +16 -0
  249. package/dist/lib/position-watcher.js +44 -0
  250. package/dist/lib/price-watcher.d.ts +15 -0
  251. package/dist/lib/price-watcher.js +84 -0
  252. package/dist/lib/prompts.d.ts +32 -0
  253. package/dist/lib/prompts.js +105 -0
  254. package/dist/lib/rate-limit.d.ts +32 -0
  255. package/dist/lib/rate-limit.js +88 -0
  256. package/dist/lib/risk/analytics.d.ts +39 -0
  257. package/dist/lib/risk/analytics.js +98 -0
  258. package/dist/lib/risk/drawdown.d.ts +18 -0
  259. package/dist/lib/risk/drawdown.js +49 -0
  260. package/dist/lib/risk/evaluation-log.d.ts +29 -0
  261. package/dist/lib/risk/evaluation-log.js +61 -0
  262. package/dist/lib/risk/index.d.ts +4 -0
  263. package/dist/lib/risk/index.js +4 -0
  264. package/dist/lib/risk/limits.d.ts +23 -0
  265. package/dist/lib/risk/limits.js +27 -0
  266. package/dist/lib/risk/manager.d.ts +32 -0
  267. package/dist/lib/risk/manager.js +85 -0
  268. package/dist/lib/risk/policy-middleware.d.ts +33 -0
  269. package/dist/lib/risk/policy-middleware.js +267 -0
  270. package/dist/lib/risk/position-sizer.d.ts +9 -0
  271. package/dist/lib/risk/position-sizer.js +14 -0
  272. package/dist/lib/risk/rules-store.d.ts +16 -0
  273. package/dist/lib/risk/rules-store.js +47 -0
  274. package/dist/lib/schema.d.ts +254 -0
  275. package/dist/lib/schema.js +199 -0
  276. package/dist/lib/secrets.d.ts +3 -0
  277. package/dist/lib/secrets.js +62 -0
  278. package/dist/lib/settings.d.ts +24 -0
  279. package/dist/lib/settings.js +86 -0
  280. package/dist/lib/signals.d.ts +73 -0
  281. package/dist/lib/signals.js +136 -0
  282. package/dist/lib/stable-stringify.d.ts +6 -0
  283. package/dist/lib/stable-stringify.js +17 -0
  284. package/dist/lib/state-context.d.ts +44 -0
  285. package/dist/lib/state-context.js +133 -0
  286. package/dist/lib/strategy/basis-trade.d.ts +2 -0
  287. package/dist/lib/strategy/basis-trade.js +24 -0
  288. package/dist/lib/strategy/funding-arb.d.ts +2 -0
  289. package/dist/lib/strategy/funding-arb.js +23 -0
  290. package/dist/lib/strategy/interface.d.ts +23 -0
  291. package/dist/lib/strategy/interface.js +1 -0
  292. package/dist/lib/strategy/registry.d.ts +4 -0
  293. package/dist/lib/strategy/registry.js +10 -0
  294. package/dist/lib/telemetry.d.ts +25 -0
  295. package/dist/lib/telemetry.js +101 -0
  296. package/dist/lib/trace-queries.d.ts +20 -0
  297. package/dist/lib/trace-queries.js +133 -0
  298. package/dist/lib/trace.d.ts +1 -0
  299. package/dist/lib/trace.js +4 -0
  300. package/dist/lib/trade-reputation.d.ts +6 -0
  301. package/dist/lib/trade-reputation.js +99 -0
  302. package/dist/lib/ui-tokens.d.ts +21 -0
  303. package/dist/lib/ui-tokens.js +26 -0
  304. package/dist/lib/validate.d.ts +39 -0
  305. package/dist/lib/validate.js +108 -0
  306. package/dist/lib/validation.d.ts +9 -0
  307. package/dist/lib/validation.js +64 -0
  308. package/dist/server/cache.d.ts +38 -0
  309. package/dist/server/cache.js +56 -0
  310. package/dist/server/index.d.ts +2 -0
  311. package/dist/server/index.js +89 -0
  312. package/dist/server/ipc.d.ts +18 -0
  313. package/dist/server/ipc.js +159 -0
  314. package/dist/server/subscriptions.d.ts +18 -0
  315. package/dist/server/subscriptions.js +114 -0
  316. package/package.json +124 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Arb History Command
3
+ * View historical funding rate spreads
4
+ */
5
+ import { getOutputOptions } from "../../cli/program.js";
6
+ import { output, outputError } from "../../cli/output.js";
7
+ import { getFundingHistory, getLatestFundingRates, getSpreadStats, getFundingSpreads, getRecordCount, } from "../../lib/db/funding-history.js";
8
+ import { DEFAULT_HISTORY_HOURS } from "../../lib/constants.js";
9
+ import { validateAsset, parsePositiveInt } from "../../lib/validate.js";
10
+ export function registerArbHistoryCommand(arb) {
11
+ arb
12
+ .command("history [asset]")
13
+ .description("View historical funding rate data")
14
+ .option("-h, --hours <hours>", "Hours of history to show", String(DEFAULT_HISTORY_HOURS))
15
+ .option("--stats", "Show statistics summary")
16
+ .option("--spreads", "Show hourly spread history")
17
+ .action(async function (asset) {
18
+ const outputOpts = getOutputOptions(this);
19
+ const opts = this.opts();
20
+ const hours = parsePositiveInt(opts.hours, "hours", DEFAULT_HISTORY_HOURS);
21
+ const market = `${validateAsset(asset)}-PERP`;
22
+ try {
23
+ const recordCount = getRecordCount();
24
+ if (recordCount === 0) {
25
+ if (outputOpts.json) {
26
+ output({
27
+ status: "no_data",
28
+ market,
29
+ hours,
30
+ history: [],
31
+ latestRates: [],
32
+ message: "No funding history recorded yet. Run `perps arb track` first.",
33
+ }, outputOpts);
34
+ return;
35
+ }
36
+ console.log("\n No funding history recorded yet.");
37
+ console.log(" Run \x1b[36mperps arb track\x1b[0m to start collecting data.\n");
38
+ return;
39
+ }
40
+ if (opts.stats) {
41
+ showStats(market, hours, outputOpts.json);
42
+ }
43
+ else if (opts.spreads) {
44
+ showSpreads(market, hours, outputOpts.json);
45
+ }
46
+ else {
47
+ showHistory(market, hours, outputOpts.json);
48
+ }
49
+ }
50
+ catch (err) {
51
+ outputError(err instanceof Error ? err.message : String(err));
52
+ process.exit(1);
53
+ }
54
+ });
55
+ }
56
+ function showHistory(market, hours, json) {
57
+ const rates = getLatestFundingRates(market);
58
+ const history = getFundingHistory(market, { hours });
59
+ if (json) {
60
+ output({ market, hours, latestRates: rates, history }, { json: true });
61
+ return;
62
+ }
63
+ console.log(`\n ${market} Funding History (last ${hours}h)\n`);
64
+ if (rates.length === 0) {
65
+ console.log(` No data for ${market}`);
66
+ return;
67
+ }
68
+ // Show latest rates
69
+ console.log(" \x1b[1mLatest Rates:\x1b[0m");
70
+ console.log(" " + "Exchange".padEnd(18) + "Rate (1h)".padEnd(14) + "Annualized".padEnd(14) + "Last Updated");
71
+ console.log(" " + "─".repeat(70));
72
+ for (const r of rates) {
73
+ const rateColor = r.rate >= 0 ? "\x1b[32m" : "\x1b[31m";
74
+ const sign = r.rate >= 0 ? "+" : "";
75
+ const annPercent = r.annualized;
76
+ const timeAgo = formatTimeAgo(r.recordedAt);
77
+ console.log(` ${r.exchange.padEnd(16)}` +
78
+ `${rateColor}${sign}${(r.rate * 100).toFixed(4)}%\x1b[0m`.padEnd(23) +
79
+ `${rateColor}${sign}${annPercent.toFixed(2)}%\x1b[0m`.padEnd(23) +
80
+ timeAgo);
81
+ }
82
+ // Show sample count
83
+ console.log(`\n \x1b[90m${history.length} samples in last ${hours}h\x1b[0m\n`);
84
+ }
85
+ function showStats(market, hours, json) {
86
+ const stats = getSpreadStats(market, hours);
87
+ if (json) {
88
+ output({ market, hours, stats }, { json: true });
89
+ return;
90
+ }
91
+ console.log(`\n ${market} Spread Statistics (last ${hours}h)\n`);
92
+ if (stats.samples === 0) {
93
+ console.log(` No spread data for ${market}`);
94
+ console.log(` Run \x1b[36mperps arb track\x1b[0m to start collecting data.\n`);
95
+ return;
96
+ }
97
+ console.log(" " + "─".repeat(50));
98
+ const annualizedPct = stats.avgAnnualized * 100; // Convert decimal to percentage
99
+ const spreadColor = annualizedPct > 10 ? "\x1b[32m" : "\x1b[33m";
100
+ const consistColor = stats.consistency > 80 ? "\x1b[32m" : stats.consistency > 50 ? "\x1b[33m" : "\x1b[31m";
101
+ console.log(` Avg Spread: ${spreadColor}${(stats.avgSpread * 100).toFixed(4)}%\x1b[0m per hour`);
102
+ console.log(` Avg Annualized: ${spreadColor}${annualizedPct.toFixed(2)}%\x1b[0m`);
103
+ console.log(` Min Spread: ${(stats.minSpread * 100).toFixed(4)}%`);
104
+ console.log(` Max Spread: ${(stats.maxSpread * 100).toFixed(4)}%`);
105
+ console.log(` Consistency: ${consistColor}${stats.consistency.toFixed(1)}%\x1b[0m of samples have positive spread`);
106
+ console.log(` Samples: ${stats.samples}`);
107
+ if (stats.bestPair) {
108
+ console.log(`\n Best Pair: Short ${stats.bestPair.high} / Long ${stats.bestPair.low}`);
109
+ }
110
+ // Profitability estimate
111
+ if (stats.avgAnnualized > 0) {
112
+ // avgSpread is decimal (e.g., 0.0001 = 0.01%), so multiply by position size
113
+ const dailyOnTenK = stats.avgSpread * 24 * 10000;
114
+ console.log(`\n \x1b[36mEstimated Daily Profit:\x1b[0m $${dailyOnTenK.toFixed(2)} on $10k delta-neutral`);
115
+ console.log(` \x1b[90m(Based on average spread, actual results vary)\x1b[0m`);
116
+ }
117
+ console.log("\n " + "─".repeat(50) + "\n");
118
+ }
119
+ function showSpreads(market, hours, json) {
120
+ const spreads = getFundingSpreads(market, hours);
121
+ if (json) {
122
+ output({ market, hours, spreads }, { json: true });
123
+ return;
124
+ }
125
+ console.log(`\n ${market} Hourly Spreads (last ${hours}h)\n`);
126
+ if (spreads.length === 0) {
127
+ console.log(` No spread data for ${market}`);
128
+ return;
129
+ }
130
+ console.log(" " + "Time".padEnd(14) + "High".padEnd(20) + "Low".padEnd(20) + "Spread".padEnd(14) + "Ann.");
131
+ console.log(" " + "─".repeat(75));
132
+ for (const s of spreads.slice(0, 24)) { // Last 24 entries
133
+ const time = new Date(s.recordedAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
134
+ const annPct = s.annualizedSpread * 100; // Convert decimal to percentage
135
+ const spreadColor = annPct > 10 ? "\x1b[32m" : "\x1b[33m";
136
+ console.log(` ${time.padEnd(12)}` +
137
+ `${s.highExchange.padEnd(10)} ${(s.highRate * 100).toFixed(4)}%`.padEnd(20) +
138
+ `${s.lowExchange.padEnd(10)} ${(s.lowRate * 100).toFixed(4)}%`.padEnd(20) +
139
+ `${(s.spread * 100).toFixed(4)}%`.padEnd(14) +
140
+ `${spreadColor}${annPct.toFixed(1)}%\x1b[0m`);
141
+ }
142
+ console.log("");
143
+ }
144
+ function formatTimeAgo(timestamp) {
145
+ const seconds = Math.floor((Date.now() - timestamp) / 1000);
146
+ if (seconds < 60)
147
+ return `${seconds}s ago`;
148
+ if (seconds < 3600)
149
+ return `${Math.floor(seconds / 60)}m ago`;
150
+ if (seconds < 86400)
151
+ return `${Math.floor(seconds / 3600)}h ago`;
152
+ return `${Math.floor(seconds / 86400)}d ago`;
153
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arbitrage Commands
3
+ * Cross-exchange price and funding comparisons
4
+ */
5
+ import type { Command } from "commander";
6
+ export declare function registerArbCommands(program: Command): void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Arbitrage Commands
3
+ * Cross-exchange price and funding comparisons
4
+ */
5
+ import { registerArbSpreadCommand } from "./spread.js";
6
+ import { registerArbFundingCommand } from "./funding.js";
7
+ import { registerArbHistoryCommand } from "./history.js";
8
+ import { registerArbTrackCommand } from "./track.js";
9
+ import { registerArbExecuteCommand } from "./execute.js";
10
+ import { registerArbAlertCommand } from "./alert.js";
11
+ import { registerArbPositionsCommand } from "./positions.js";
12
+ import { registerArbCompareCommand } from "./compare.js";
13
+ import { registerArbBasisCommand } from "./basis.js";
14
+ import { registerArbBasisExecuteCommand } from "./basis-execute.js";
15
+ export function registerArbCommands(program) {
16
+ const arb = program
17
+ .command("arb")
18
+ .description("Cross-exchange arbitrage tools");
19
+ registerArbSpreadCommand(arb);
20
+ registerArbFundingCommand(arb);
21
+ registerArbHistoryCommand(arb);
22
+ registerArbTrackCommand(arb);
23
+ registerArbExecuteCommand(arb);
24
+ registerArbAlertCommand(arb);
25
+ registerArbPositionsCommand(arb);
26
+ registerArbCompareCommand(arb);
27
+ registerArbBasisCommand(arb);
28
+ registerArbBasisExecuteCommand(arb);
29
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arb Positions Command
3
+ * View all open positions across exchanges
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerArbPositionsCommand(arb: Command): void;
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Arb Positions Command
3
+ * View all open positions across exchanges
4
+ */
5
+ import { getContext, getOutputOptions } from "../../cli/program.js";
6
+ import { output, outputError } from "../../cli/output.js";
7
+ import { getExchangeAdapterById, getAvailableExchanges } from "../../lib/exchange.js";
8
+ export function registerArbPositionsCommand(arb) {
9
+ arb
10
+ .command("positions")
11
+ .description("View all open positions across exchanges")
12
+ .option("--exchange <exchanges>", "Comma-separated exchanges to check (default: all)")
13
+ .action(async function () {
14
+ const ctx = getContext(this);
15
+ const outputOpts = getOutputOptions(this);
16
+ const opts = this.opts();
17
+ const isTestnet = ctx.config.testnet;
18
+ const exchanges = opts.exchange
19
+ ? opts.exchange.split(",").map(e => e.trim().toLowerCase())
20
+ : getAvailableExchanges();
21
+ try {
22
+ if (!outputOpts.json) {
23
+ console.log("\n Fetching positions across exchanges...\n");
24
+ }
25
+ const allPositions = [];
26
+ const errors = [];
27
+ for (const exchangeId of exchanges) {
28
+ let adapter = null;
29
+ let connected = false;
30
+ try {
31
+ adapter = getExchangeAdapterById(exchangeId);
32
+ await adapter.connect({ testnet: isTestnet });
33
+ connected = true;
34
+ const positions = await adapter.getPositions();
35
+ for (const pos of positions) {
36
+ if (parseFloat(pos.size) === 0)
37
+ continue;
38
+ allPositions.push({
39
+ exchange: adapter.info.name,
40
+ market: pos.market,
41
+ side: pos.side,
42
+ size: pos.size,
43
+ entryPrice: pos.entryPrice,
44
+ markPrice: pos.markPrice,
45
+ unrealizedPnl: pos.unrealizedPnl,
46
+ leverage: pos.leverage,
47
+ });
48
+ }
49
+ }
50
+ catch (err) {
51
+ errors.push({
52
+ exchange: adapter?.info.name ?? exchangeId,
53
+ error: err instanceof Error ? err.message : "Unknown error",
54
+ });
55
+ }
56
+ finally {
57
+ if (connected && adapter) {
58
+ await adapter.disconnect().catch(() => undefined);
59
+ }
60
+ }
61
+ }
62
+ if (outputOpts.json) {
63
+ output({ positions: allPositions, errors }, { json: true });
64
+ return;
65
+ }
66
+ if (allPositions.length === 0) {
67
+ console.log(" No open positions found.\n");
68
+ if (errors.length > 0) {
69
+ console.log(" \x1b[33mNote:\x1b[0m Some exchanges require authentication:");
70
+ for (const e of errors) {
71
+ console.log(` ${e.exchange}: ${e.error}`);
72
+ }
73
+ console.log("");
74
+ }
75
+ return;
76
+ }
77
+ // Display positions
78
+ console.log(" \x1b[1mOpen Positions\x1b[0m\n");
79
+ console.log(" " +
80
+ "Exchange".padEnd(14) +
81
+ "Market".padEnd(12) +
82
+ "Side".padEnd(8) +
83
+ "Size".padEnd(14) +
84
+ "Entry".padEnd(12) +
85
+ "Mark".padEnd(12) +
86
+ "PnL");
87
+ console.log(" " + "─".repeat(85));
88
+ let totalPnl = 0;
89
+ for (const pos of allPositions) {
90
+ const sideColor = pos.side === "long" ? "\x1b[32m" : "\x1b[31m";
91
+ const pnl = parseFloat(pos.unrealizedPnl);
92
+ const pnlColor = pnl >= 0 ? "\x1b[32m" : "\x1b[31m";
93
+ const pnlSign = pnl >= 0 ? "+" : "";
94
+ totalPnl += pnl;
95
+ console.log(` ${pos.exchange.padEnd(12)}` +
96
+ `${pos.market.padEnd(12)}` +
97
+ `${sideColor}${pos.side.toUpperCase().padEnd(8)}\x1b[0m` +
98
+ `${pos.size.padEnd(14)}` +
99
+ `$${parseFloat(pos.entryPrice).toFixed(2).padEnd(11)}` +
100
+ `$${parseFloat(pos.markPrice).toFixed(2).padEnd(11)}` +
101
+ `${pnlColor}${pnlSign}$${pnl.toFixed(2)}\x1b[0m`);
102
+ }
103
+ console.log(" " + "─".repeat(85));
104
+ const totalPnlColor = totalPnl >= 0 ? "\x1b[32m" : "\x1b[31m";
105
+ const totalPnlSign = totalPnl >= 0 ? "+" : "";
106
+ console.log(` ${"Total PnL:".padEnd(66)}${totalPnlColor}${totalPnlSign}$${totalPnl.toFixed(2)}\x1b[0m`);
107
+ // Calculate net exposure per market
108
+ console.log("\n \x1b[1mNet Exposure\x1b[0m\n");
109
+ const exposureByMarket = new Map();
110
+ for (const pos of allPositions) {
111
+ const existing = exposureByMarket.get(pos.market) || {
112
+ market: pos.market,
113
+ long: 0,
114
+ short: 0,
115
+ net: 0,
116
+ value: 0,
117
+ };
118
+ const size = parseFloat(pos.size);
119
+ const price = parseFloat(pos.markPrice);
120
+ if (pos.side === "long") {
121
+ existing.long += size;
122
+ }
123
+ else {
124
+ existing.short += size;
125
+ }
126
+ existing.net = existing.long - existing.short;
127
+ existing.value = existing.net * price;
128
+ exposureByMarket.set(pos.market, existing);
129
+ }
130
+ console.log(" " + "Market".padEnd(12) + "Long".padEnd(14) + "Short".padEnd(14) + "Net".padEnd(14) + "Value");
131
+ console.log(" " + "─".repeat(60));
132
+ for (const exposure of exposureByMarket.values()) {
133
+ const netColor = Math.abs(exposure.net) < 0.0001
134
+ ? "\x1b[32m"
135
+ : exposure.net > 0
136
+ ? "\x1b[33m"
137
+ : "\x1b[31m";
138
+ const netStr = Math.abs(exposure.net) < 0.0001
139
+ ? "HEDGED"
140
+ : exposure.net.toFixed(6);
141
+ console.log(` ${exposure.market.padEnd(12)}` +
142
+ `${exposure.long.toFixed(6).padEnd(14)}` +
143
+ `${exposure.short.toFixed(6).padEnd(14)}` +
144
+ `${netColor}${netStr.padEnd(14)}\x1b[0m` +
145
+ `$${exposure.value.toFixed(2)}`);
146
+ }
147
+ console.log("");
148
+ if (errors.length > 0) {
149
+ console.log(" \x1b[33mCould not check:\x1b[0m " + errors.map(e => e.exchange).join(", "));
150
+ console.log("");
151
+ }
152
+ }
153
+ catch (err) {
154
+ outputError(err instanceof Error ? err.message : String(err));
155
+ process.exit(1);
156
+ }
157
+ });
158
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arb Spread Command
3
+ * Compare prices across all exchanges for arbitrage opportunities
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerArbSpreadCommand(arb: Command): void;
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Arb Spread Command
3
+ * Compare prices across all exchanges for arbitrage opportunities
4
+ */
5
+ import { getContext, getOutputOptions } from "../../cli/program.js";
6
+ import { output, outputError } from "../../cli/output.js";
7
+ import { getExchangeAdapterById, getAvailableExchanges } from "../../lib/exchange.js";
8
+ export function registerArbSpreadCommand(arb) {
9
+ arb
10
+ .command("spread [asset]")
11
+ .description("Compare prices across exchanges")
12
+ .option("-w, --watch", "Watch mode - continuously update")
13
+ .option("--all", "Show all common markets")
14
+ .action(async function (asset) {
15
+ const ctx = getContext(this);
16
+ const outputOpts = getOutputOptions(this);
17
+ const opts = this.opts();
18
+ const isTestnet = ctx.config.testnet;
19
+ try {
20
+ const exchanges = getAvailableExchanges();
21
+ if (outputOpts.json && opts.watch) {
22
+ throw new Error("Watch mode is not supported with --json");
23
+ }
24
+ if (opts.all) {
25
+ await showAllSpreads(exchanges, outputOpts.json, isTestnet);
26
+ }
27
+ else if (asset) {
28
+ if (opts.watch) {
29
+ await watchSpread(asset.toUpperCase(), exchanges, isTestnet);
30
+ }
31
+ else {
32
+ await showSpread(asset.toUpperCase(), exchanges, outputOpts.json, isTestnet);
33
+ }
34
+ }
35
+ else {
36
+ // Default: show BTC spread
37
+ await showSpread("BTC", exchanges, outputOpts.json, isTestnet);
38
+ }
39
+ }
40
+ catch (err) {
41
+ outputError(err instanceof Error ? err.message : String(err));
42
+ process.exit(1);
43
+ }
44
+ });
45
+ }
46
+ async function fetchPrices(asset, exchanges, isTestnet) {
47
+ const results = [];
48
+ const promises = exchanges.map(async (exchangeId) => {
49
+ const adapter = getExchangeAdapterById(exchangeId);
50
+ let connected = false;
51
+ try {
52
+ await adapter.connect({ testnet: isTestnet });
53
+ connected = true;
54
+ // Try to get ticker for this asset
55
+ const symbol = `${asset}-PERP`;
56
+ const ticker = await adapter.getTicker(symbol);
57
+ return {
58
+ exchange: adapter.info.name,
59
+ symbol: ticker.market,
60
+ markPrice: parseFloat(ticker.markPrice),
61
+ indexPrice: parseFloat(ticker.indexPrice),
62
+ bid: parseFloat(ticker.bid),
63
+ ask: parseFloat(ticker.ask),
64
+ fundingRate: parseFloat(ticker.fundingRate) * 100, // Convert to percentage
65
+ };
66
+ }
67
+ catch (err) {
68
+ return {
69
+ exchange: adapter.info.name,
70
+ symbol: `${asset}-PERP`,
71
+ markPrice: 0,
72
+ indexPrice: 0,
73
+ bid: 0,
74
+ ask: 0,
75
+ fundingRate: 0,
76
+ error: err instanceof Error ? err.message : "Unknown error",
77
+ };
78
+ }
79
+ finally {
80
+ if (connected) {
81
+ await adapter.disconnect().catch(() => undefined);
82
+ }
83
+ }
84
+ });
85
+ const settled = await Promise.allSettled(promises);
86
+ for (const result of settled) {
87
+ if (result.status === "fulfilled") {
88
+ results.push(result.value);
89
+ }
90
+ }
91
+ return results;
92
+ }
93
+ async function showSpread(asset, exchanges, json, isTestnet) {
94
+ if (!json) {
95
+ console.log(`\nFetching ${asset} prices across ${exchanges.length} exchanges...\n`);
96
+ }
97
+ const prices = await fetchPrices(asset, exchanges, isTestnet);
98
+ const validPrices = prices.filter(p => p.markPrice > 0);
99
+ if (validPrices.length === 0) {
100
+ if (json) {
101
+ output({
102
+ status: "no_data",
103
+ asset,
104
+ prices: [],
105
+ exchangesScanned: exchanges.length,
106
+ }, { json: true });
107
+ return;
108
+ }
109
+ console.log(`No prices found for ${asset} on any exchange`);
110
+ return;
111
+ }
112
+ // Find best bid (highest) and best ask (lowest)
113
+ const bestBid = validPrices.reduce((best, p) => p.bid > best.bid ? p : best, validPrices[0]);
114
+ const bestAsk = validPrices.reduce((best, p) => p.ask < best.ask || best.ask === 0 ? p : best, validPrices[0]);
115
+ // Calculate spread
116
+ const spreadPct = ((bestBid.bid - bestAsk.ask) / bestAsk.ask) * 100;
117
+ const data = {
118
+ asset,
119
+ prices: validPrices,
120
+ bestBid: { exchange: bestBid.exchange, price: bestBid.bid },
121
+ bestAsk: { exchange: bestAsk.exchange, price: bestAsk.ask },
122
+ spreadPct,
123
+ timestamp: Date.now(),
124
+ };
125
+ if (json) {
126
+ output(data, { json: true });
127
+ return;
128
+ }
129
+ // Table output
130
+ console.log(` ${asset}-PERP Price Comparison\n`);
131
+ console.log(" Exchange".padEnd(18) + "Mark Price".padEnd(14) + "Bid".padEnd(14) + "Ask".padEnd(14) + "Funding");
132
+ console.log(" " + "─".repeat(70));
133
+ // Sort by mark price descending
134
+ validPrices.sort((a, b) => b.markPrice - a.markPrice);
135
+ for (const p of validPrices) {
136
+ const isBestBid = p.exchange === bestBid.exchange;
137
+ const isBestAsk = p.exchange === bestAsk.exchange;
138
+ const bidStr = isBestBid ? `\x1b[32m${p.bid.toFixed(2)}\x1b[0m` : p.bid.toFixed(2);
139
+ const askStr = isBestAsk ? `\x1b[31m${p.ask.toFixed(2)}\x1b[0m` : p.ask.toFixed(2);
140
+ const fundingStr = p.fundingRate >= 0
141
+ ? `\x1b[32m+${p.fundingRate.toFixed(4)}%\x1b[0m`
142
+ : `\x1b[31m${p.fundingRate.toFixed(4)}%\x1b[0m`;
143
+ console.log(` ${p.exchange.padEnd(16)}` +
144
+ `$${p.markPrice.toFixed(2).padEnd(13)}` +
145
+ `${bidStr.padEnd(14 + (isBestBid ? 9 : 0))}` + // Account for ANSI codes
146
+ `${askStr.padEnd(14 + (isBestAsk ? 9 : 0))}` +
147
+ fundingStr);
148
+ }
149
+ // Show errors
150
+ const errors = prices.filter(p => p.error);
151
+ if (errors.length > 0) {
152
+ console.log("\n \x1b[33mNot available on:\x1b[0m " + errors.map(e => e.exchange).join(", "));
153
+ }
154
+ // Show spread summary
155
+ console.log("\n " + "─".repeat(70));
156
+ if (spreadPct > 0) {
157
+ console.log(`\n \x1b[32mArbitrage Opportunity: ${spreadPct.toFixed(4)}%\x1b[0m`);
158
+ console.log(` Buy on ${bestAsk.exchange} @ $${bestAsk.ask.toFixed(2)}`);
159
+ console.log(` Sell on ${bestBid.exchange} @ $${bestBid.bid.toFixed(2)}`);
160
+ console.log(` Profit: $${(bestBid.bid - bestAsk.ask).toFixed(2)} per unit`);
161
+ }
162
+ else {
163
+ console.log(`\n No arbitrage opportunity (spread: ${spreadPct.toFixed(4)}%)`);
164
+ }
165
+ console.log("");
166
+ }
167
+ async function showAllSpreads(exchanges, json, isTestnet) {
168
+ // Common assets across exchanges
169
+ const assets = ["BTC", "ETH", "SOL", "DOGE", "ARB", "OP", "WIF", "TIA"];
170
+ const rows = [];
171
+ if (!json) {
172
+ console.log("\nFetching prices for common assets...\n");
173
+ console.log(" Asset".padEnd(10) + "Best Bid".padEnd(24) + "Best Ask".padEnd(24) + "Spread");
174
+ console.log(" " + "─".repeat(70));
175
+ }
176
+ for (const asset of assets) {
177
+ const prices = await fetchPrices(asset, exchanges, isTestnet);
178
+ const validPrices = prices.filter(p => p.markPrice > 0);
179
+ if (validPrices.length < 2) {
180
+ if (!json) {
181
+ console.log(` ${asset.padEnd(8)} Available on <2 exchanges`);
182
+ }
183
+ continue;
184
+ }
185
+ const bestBid = validPrices.reduce((best, p) => p.bid > best.bid ? p : best, validPrices[0]);
186
+ const bestAsk = validPrices.reduce((best, p) => p.ask < best.ask || best.ask === 0 ? p : best, validPrices[0]);
187
+ const spreadPct = ((bestBid.bid - bestAsk.ask) / bestAsk.ask) * 100;
188
+ rows.push({
189
+ asset,
190
+ bestBid: { exchange: bestBid.exchange, price: bestBid.bid },
191
+ bestAsk: { exchange: bestAsk.exchange, price: bestAsk.ask },
192
+ spreadPct,
193
+ timestamp: Date.now(),
194
+ });
195
+ if (!json) {
196
+ const spreadColor = spreadPct > 0 ? "\x1b[32m" : "\x1b[90m";
197
+ console.log(` ${asset.padEnd(8)}` +
198
+ `${bestBid.exchange.padEnd(12)} $${bestBid.bid.toFixed(2).padEnd(10)}` +
199
+ `${bestAsk.exchange.padEnd(12)} $${bestAsk.ask.toFixed(2).padEnd(10)}` +
200
+ `${spreadColor}${spreadPct.toFixed(4)}%\x1b[0m`);
201
+ }
202
+ }
203
+ if (json) {
204
+ output(rows, { json: true });
205
+ return;
206
+ }
207
+ console.log("");
208
+ }
209
+ async function watchSpread(asset, exchanges, isTestnet) {
210
+ console.log(`\nWatching ${asset} spread (Ctrl+C to exit)...\n`);
211
+ const refresh = async () => {
212
+ // Clear screen
213
+ process.stdout.write("\x1B[2J\x1B[0f");
214
+ console.log(`\n ${asset}-PERP Spread Monitor (${new Date().toLocaleTimeString()})\n`);
215
+ const prices = await fetchPrices(asset, exchanges, isTestnet);
216
+ const validPrices = prices.filter(p => p.markPrice > 0);
217
+ if (validPrices.length === 0) {
218
+ console.log(` No prices available`);
219
+ return;
220
+ }
221
+ console.log(" Exchange".padEnd(18) + "Mark".padEnd(14) + "Bid".padEnd(14) + "Ask".padEnd(14) + "Funding");
222
+ console.log(" " + "─".repeat(65));
223
+ validPrices.sort((a, b) => b.markPrice - a.markPrice);
224
+ for (const p of validPrices) {
225
+ const fundingColor = p.fundingRate >= 0 ? "\x1b[32m" : "\x1b[31m";
226
+ console.log(` ${p.exchange.padEnd(16)}` +
227
+ `$${p.markPrice.toFixed(2).padEnd(13)}` +
228
+ `$${p.bid.toFixed(2).padEnd(13)}` +
229
+ `$${p.ask.toFixed(2).padEnd(13)}` +
230
+ `${fundingColor}${p.fundingRate.toFixed(4)}%\x1b[0m`);
231
+ }
232
+ // Calculate spread
233
+ const bestBid = validPrices.reduce((best, p) => p.bid > best.bid ? p : best, validPrices[0]);
234
+ const bestAsk = validPrices.reduce((best, p) => p.ask < best.ask ? p : best, validPrices[0]);
235
+ const spread = ((bestBid.bid - bestAsk.ask) / bestAsk.ask) * 100;
236
+ console.log("\n " + "─".repeat(65));
237
+ console.log(`\n Spread: ${spread >= 0 ? "\x1b[32m" : "\x1b[90m"}${spread.toFixed(4)}%\x1b[0m`);
238
+ console.log(` Best bid: ${bestBid.exchange} @ $${bestBid.bid.toFixed(2)}`);
239
+ console.log(` Best ask: ${bestAsk.exchange} @ $${bestAsk.ask.toFixed(2)}`);
240
+ console.log("\n Press Ctrl+C to exit");
241
+ };
242
+ // Initial refresh
243
+ await refresh();
244
+ // Refresh every 5 seconds
245
+ const interval = setInterval(refresh, 5000);
246
+ process.once("SIGINT", () => {
247
+ clearInterval(interval);
248
+ console.log("\n");
249
+ process.exit(0);
250
+ });
251
+ // Keep alive
252
+ await new Promise(() => { });
253
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arb Track Command
3
+ * Start background funding rate tracking with live UI
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerArbTrackCommand(arb: Command): void;