@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,467 @@
1
+ /**
2
+ * Arb Execute Command
3
+ * Execute delta-neutral funding arbitrage across exchanges
4
+ */
5
+ import { confirm } from "@inquirer/prompts";
6
+ import { getContext, getOutputOptions } from "../../cli/program.js";
7
+ import { output, outputError } from "../../cli/output.js";
8
+ import { getExchangeAdapterById } from "../../lib/exchange.js";
9
+ import { getLatestFundingRates } from "../../lib/db/funding-history.js";
10
+ import { getExchangeIdByName, DEFAULT_ARB_SIZE_USD } from "../../lib/constants.js";
11
+ import { validateAsset, validateSize } from "../../lib/validate.js";
12
+ import { getExchangeCredentials } from "../../lib/config.js";
13
+ import { RiskPolicyMiddleware } from "../../lib/risk/policy-middleware.js";
14
+ import { executeOrderWithSafety } from "../../lib/execution/safety.js";
15
+ import { runWithExecutionJournal } from "../../lib/execution/journal.js";
16
+ import { withJsonContract } from "../../lib/contracts.js";
17
+ import { CLIError, EXIT_CODES, inferExitCode } from "../../lib/exit-codes.js";
18
+ export function registerArbExecuteCommand(arb) {
19
+ arb
20
+ .command("execute [asset]")
21
+ .description("Execute delta-neutral funding arbitrage")
22
+ .option("-s, --size <usd>", "Position size in USD", String(DEFAULT_ARB_SIZE_USD))
23
+ .option("--confidence <0-1>", "Signal confidence for risk policy (default: 1.0)")
24
+ .option("--tp <pct>", "Attach take-profit (% from entry) to each leg")
25
+ .option("--sl <pct>", "Attach stop-loss (% from entry) to each leg")
26
+ .option("--no-close-then-flip", "Disable auto close-then-flip behavior")
27
+ .option("--no-spread-aware", "Disable spread-aware execution pricing")
28
+ .option("--idempotency-key <key>", "Idempotency key prefix for safe retries (leg keys use :short/:long suffixes)")
29
+ .option("--dry-run", "Show execution plan without trading")
30
+ .option("-y, --yes", "Skip confirmation prompts")
31
+ .action(async function (asset) {
32
+ const ctx = getContext(this);
33
+ const outputOpts = getOutputOptions(this);
34
+ const opts = this.opts();
35
+ const emitJson = (data) => {
36
+ output(withJsonContract("arb.execute.result", data), outputOpts);
37
+ };
38
+ const isJson = outputOpts.json;
39
+ try {
40
+ if (isJson && !opts.yes && !opts.dryRun) {
41
+ throw new CLIError("Use --yes with --json for non-interactive arb execution", EXIT_CODES.VALIDATION_ERROR);
42
+ }
43
+ const assetName = validateAsset(asset);
44
+ const market = `${assetName}-PERP`;
45
+ const requestedSizeUsd = validateSize(opts.size, DEFAULT_ARB_SIZE_USD);
46
+ const confidence = opts.confidence ? parseFloat(opts.confidence) : 1;
47
+ if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
48
+ throw new CLIError("Confidence must be a number between 0 and 1", EXIT_CODES.VALIDATION_ERROR);
49
+ }
50
+ const rates = getLatestFundingRates(market);
51
+ if (rates.length < 2) {
52
+ if (isJson) {
53
+ emitJson({
54
+ status: "no_data",
55
+ reason: "not_enough_exchange_data",
56
+ market,
57
+ requiredExchanges: 2,
58
+ availableExchanges: rates.length,
59
+ });
60
+ }
61
+ else {
62
+ console.log("\n Not enough exchange data. Run \x1b[36mperps arb track --once\x1b[0m first.\n");
63
+ }
64
+ process.exitCode = EXIT_CODES.DATA_UNAVAILABLE;
65
+ return;
66
+ }
67
+ const sorted = [...rates].sort((a, b) => b.rate - a.rate);
68
+ const highest = sorted[0];
69
+ const lowest = sorted[sorted.length - 1];
70
+ const spread = highest.rate - lowest.rate;
71
+ if (spread <= 0) {
72
+ if (isJson) {
73
+ emitJson({
74
+ status: "no_opportunity",
75
+ reason: "non_positive_spread",
76
+ market,
77
+ spread,
78
+ });
79
+ }
80
+ else {
81
+ console.log("\n No positive spread found. Arbitrage not profitable right now.\n");
82
+ }
83
+ process.exitCode = EXIT_CODES.NO_OPPORTUNITY;
84
+ return;
85
+ }
86
+ const shortExchangeId = getExchangeIdByName(highest.exchange);
87
+ const longExchangeId = getExchangeIdByName(lowest.exchange);
88
+ if (shortExchangeId === longExchangeId) {
89
+ if (isJson) {
90
+ emitJson({
91
+ status: "no_opportunity",
92
+ reason: "same_exchange_legs",
93
+ market,
94
+ exchangeId: shortExchangeId,
95
+ });
96
+ }
97
+ else {
98
+ console.log("\n Best long/short legs resolved to the same exchange. Skipping.\n");
99
+ }
100
+ process.exitCode = EXIT_CODES.NO_OPPORTUNITY;
101
+ return;
102
+ }
103
+ const shortAdapter = getExchangeAdapterById(shortExchangeId);
104
+ const longAdapter = getExchangeAdapterById(longExchangeId);
105
+ const shortCredentials = getExchangeCredentials(ctx.config, shortExchangeId, {
106
+ requireTrading: true,
107
+ });
108
+ const longCredentials = getExchangeCredentials(ctx.config, longExchangeId, {
109
+ requireTrading: true,
110
+ });
111
+ await shortAdapter.connect({
112
+ testnet: ctx.config.testnet,
113
+ rpcUrl: shortCredentials.fullnodeUrl,
114
+ wsUrl: shortCredentials.wsUrl,
115
+ credentials: shortCredentials,
116
+ });
117
+ await longAdapter.connect({
118
+ testnet: ctx.config.testnet,
119
+ rpcUrl: longCredentials.fullnodeUrl,
120
+ wsUrl: longCredentials.wsUrl,
121
+ credentials: longCredentials,
122
+ });
123
+ try {
124
+ const shortTicker = await shortAdapter.getTicker(market);
125
+ const longTicker = await longAdapter.getTicker(market);
126
+ const shortPrice = parseFloat(shortTicker.lastPrice);
127
+ const longPrice = parseFloat(longTicker.lastPrice);
128
+ if (!Number.isFinite(shortPrice) || !Number.isFinite(longPrice)) {
129
+ throw new CLIError("Unable to fetch reliable ticker prices for arb sizing", EXIT_CODES.DATA_UNAVAILABLE);
130
+ }
131
+ // Run risk policy independently on both legs and size to the stricter leg.
132
+ const shortRisk = new RiskPolicyMiddleware(ctx.config, shortExchangeId);
133
+ const longRisk = new RiskPolicyMiddleware(ctx.config, longExchangeId);
134
+ const shortEval = await shortRisk.evaluateUsdSignal(shortAdapter, { market, side: "short", confidence, reason: "arb:short", timestamp: Date.now() }, requestedSizeUsd);
135
+ const longEval = await longRisk.evaluateUsdSignal(longAdapter, { market, side: "long", confidence, reason: "arb:long", timestamp: Date.now() }, requestedSizeUsd);
136
+ if (!shortEval.allowed) {
137
+ throw new CLIError(`Short leg blocked by risk policy: ${shortEval.reason ?? "unknown"}`, EXIT_CODES.EXECUTION_ERROR);
138
+ }
139
+ if (!longEval.allowed) {
140
+ throw new CLIError(`Long leg blocked by risk policy: ${longEval.reason ?? "unknown"}`, EXIT_CODES.EXECUTION_ERROR);
141
+ }
142
+ const sizeUsd = Math.min(requestedSizeUsd, shortEval.sizeUsd, longEval.sizeUsd);
143
+ if (!Number.isFinite(sizeUsd) || sizeUsd <= 0) {
144
+ throw new CLIError("Risk policy reduced arb size to zero", EXIT_CODES.EXECUTION_ERROR);
145
+ }
146
+ const annualized = spread * 24 * 365 * 100;
147
+ const dailyProfit = spread * 24 * sizeUsd;
148
+ const plan = {
149
+ asset: assetName,
150
+ size: sizeUsd,
151
+ shortExchange: highest.exchange,
152
+ shortPrice,
153
+ longExchange: lowest.exchange,
154
+ longPrice,
155
+ spread: spread * 100,
156
+ annualized,
157
+ dailyProfit,
158
+ };
159
+ if (!isJson) {
160
+ console.log("\n \x1b[1m━━━ Delta-Neutral Execution Plan ━━━\x1b[0m\n");
161
+ console.log(` Asset: ${assetName}`);
162
+ console.log(` Position Size: $${sizeUsd.toLocaleString()}`);
163
+ if (Math.abs(sizeUsd - requestedSizeUsd) > 1e-9) {
164
+ console.log(` Risk Adjusted: from $${requestedSizeUsd.toLocaleString()}`);
165
+ }
166
+ console.log("");
167
+ console.log(` \x1b[31m▼ SHORT\x1b[0m ${highest.exchange}`);
168
+ console.log(` Price: $${shortPrice.toLocaleString()}`);
169
+ console.log(` Funding: ${(highest.rate * 100).toFixed(4)}%/hr (you receive)`);
170
+ console.log("");
171
+ console.log(` \x1b[32m▲ LONG\x1b[0m ${lowest.exchange}`);
172
+ console.log(` Price: $${longPrice.toLocaleString()}`);
173
+ console.log(` Funding: ${(lowest.rate * 100).toFixed(4)}%/hr (you pay)`);
174
+ console.log("");
175
+ console.log(" " + "─".repeat(45));
176
+ console.log(` Net Spread: \x1b[32m${plan.spread.toFixed(4)}%/hr\x1b[0m`);
177
+ console.log(` Annualized: \x1b[32m${annualized.toFixed(2)}%\x1b[0m`);
178
+ console.log(` Est. Daily: \x1b[32m$${dailyProfit.toFixed(2)}\x1b[0m`);
179
+ console.log(" " + "─".repeat(45));
180
+ console.log("");
181
+ }
182
+ if (opts.dryRun) {
183
+ if (isJson) {
184
+ emitJson({
185
+ status: "dry_run",
186
+ market,
187
+ asset: assetName,
188
+ testnet: ctx.config.testnet,
189
+ requestedSizeUsd,
190
+ plan,
191
+ });
192
+ }
193
+ else {
194
+ console.log(" \x1b[33m[DRY RUN]\x1b[0m Execution skipped.\n");
195
+ }
196
+ return;
197
+ }
198
+ if (!opts.yes) {
199
+ const confirmed = await confirm({
200
+ message: "Execute this arbitrage trade?",
201
+ default: false,
202
+ });
203
+ if (!confirmed) {
204
+ if (isJson) {
205
+ emitJson({
206
+ status: "cancelled",
207
+ reason: "user_declined_confirmation",
208
+ market,
209
+ asset: assetName,
210
+ plan,
211
+ });
212
+ }
213
+ else {
214
+ console.log("\n Execution cancelled.\n");
215
+ }
216
+ process.exitCode = EXIT_CODES.CANCELLED;
217
+ return;
218
+ }
219
+ }
220
+ const shortSize = (sizeUsd / shortPrice).toFixed(8);
221
+ const longSize = (sizeUsd / longPrice).toFixed(8);
222
+ const shouldAttachTpSl = opts.tp !== undefined || opts.sl !== undefined;
223
+ const tp = opts.tp !== undefined ? parseFloat(opts.tp) : ctx.config.executionSafety.takeProfitPct;
224
+ const sl = opts.sl !== undefined ? parseFloat(opts.sl) : ctx.config.executionSafety.stopLossPct;
225
+ const closeThenFlip = opts.closeThenFlip ?? true;
226
+ const spreadAware = opts.spreadAware ?? true;
227
+ const idempotencyPrefix = opts.idempotencyKey?.trim() || undefined;
228
+ if (shouldAttachTpSl) {
229
+ if (!Number.isFinite(tp) || tp <= 0) {
230
+ throw new CLIError("TP must be a positive number", EXIT_CODES.VALIDATION_ERROR);
231
+ }
232
+ if (!Number.isFinite(sl) || sl <= 0) {
233
+ throw new CLIError("SL must be a positive number", EXIT_CODES.VALIDATION_ERROR);
234
+ }
235
+ }
236
+ if (!isJson) {
237
+ console.log("\n \x1b[36mExecuting...\x1b[0m\n");
238
+ }
239
+ if (!isJson) {
240
+ console.log(` Opening SHORT on ${highest.exchange}...`);
241
+ }
242
+ const shortJournal = await runWithExecutionJournal({
243
+ idempotencyKey: idempotencyPrefix ? `${idempotencyPrefix}:short` : undefined,
244
+ metadata: {
245
+ command: "arb.execute.short",
246
+ exchange: shortExchangeId,
247
+ testnet: ctx.config.testnet,
248
+ market,
249
+ side: "short",
250
+ orderType: "market",
251
+ },
252
+ request: {
253
+ market,
254
+ side: "short",
255
+ size: shortSize,
256
+ closeThenFlip,
257
+ spreadAware,
258
+ spreadOffset: ctx.config.executionSafety.spreadOffset,
259
+ tp: shouldAttachTpSl ? tp : null,
260
+ sl: shouldAttachTpSl ? sl : null,
261
+ requestedSizeUsd,
262
+ adjustedSizeUsd: sizeUsd,
263
+ confidence,
264
+ },
265
+ execute: async () => {
266
+ const result = await executeOrderWithSafety(shortAdapter, {
267
+ market,
268
+ side: "short",
269
+ type: "market",
270
+ size: shortSize,
271
+ }, {
272
+ closeThenFlip,
273
+ spreadAwarePricing: spreadAware,
274
+ spreadOffset: ctx.config.executionSafety.spreadOffset,
275
+ attachTpSl: shouldAttachTpSl,
276
+ tpSlConfig: shouldAttachTpSl
277
+ ? { stopLossPct: sl, takeProfitPct: tp }
278
+ : undefined,
279
+ });
280
+ return {
281
+ order: {
282
+ id: result.order.id,
283
+ market: result.order.market,
284
+ side: result.order.side,
285
+ size: result.order.size,
286
+ status: result.order.status,
287
+ price: result.order.price,
288
+ },
289
+ safety: {
290
+ closedOppositePosition: result.closedOppositePosition,
291
+ tpSlOrderIds: result.tpSlOrderIds,
292
+ pricing: result.pricing,
293
+ },
294
+ };
295
+ },
296
+ });
297
+ if (!isJson) {
298
+ console.log(` ✓ Short ${shortJournal.replayed ? "replayed" : "opened"}: ${shortJournal.result.order.id}`);
299
+ }
300
+ if (!isJson) {
301
+ console.log(` Opening LONG on ${lowest.exchange}...`);
302
+ }
303
+ let longJournal = null;
304
+ try {
305
+ longJournal = await runWithExecutionJournal({
306
+ idempotencyKey: idempotencyPrefix ? `${idempotencyPrefix}:long` : undefined,
307
+ metadata: {
308
+ command: "arb.execute.long",
309
+ exchange: longExchangeId,
310
+ testnet: ctx.config.testnet,
311
+ market,
312
+ side: "long",
313
+ orderType: "market",
314
+ },
315
+ request: {
316
+ market,
317
+ side: "long",
318
+ size: longSize,
319
+ closeThenFlip,
320
+ spreadAware,
321
+ spreadOffset: ctx.config.executionSafety.spreadOffset,
322
+ tp: shouldAttachTpSl ? tp : null,
323
+ sl: shouldAttachTpSl ? sl : null,
324
+ requestedSizeUsd,
325
+ adjustedSizeUsd: sizeUsd,
326
+ confidence,
327
+ },
328
+ execute: async () => {
329
+ const result = await executeOrderWithSafety(longAdapter, {
330
+ market,
331
+ side: "long",
332
+ type: "market",
333
+ size: longSize,
334
+ }, {
335
+ closeThenFlip,
336
+ spreadAwarePricing: spreadAware,
337
+ spreadOffset: ctx.config.executionSafety.spreadOffset,
338
+ attachTpSl: shouldAttachTpSl,
339
+ tpSlConfig: shouldAttachTpSl
340
+ ? { stopLossPct: sl, takeProfitPct: tp }
341
+ : undefined,
342
+ });
343
+ return {
344
+ order: {
345
+ id: result.order.id,
346
+ market: result.order.market,
347
+ side: result.order.side,
348
+ size: result.order.size,
349
+ status: result.order.status,
350
+ price: result.order.price,
351
+ },
352
+ safety: {
353
+ closedOppositePosition: result.closedOppositePosition,
354
+ tpSlOrderIds: result.tpSlOrderIds,
355
+ pricing: result.pricing,
356
+ },
357
+ };
358
+ },
359
+ });
360
+ }
361
+ catch (err) {
362
+ const message = err instanceof Error ? err.message : String(err);
363
+ if (isJson) {
364
+ emitJson({
365
+ status: "partial_failure",
366
+ market,
367
+ asset: assetName,
368
+ testnet: ctx.config.testnet,
369
+ plan,
370
+ error: {
371
+ message,
372
+ exitCode: EXIT_CODES.PARTIAL_EXECUTION,
373
+ },
374
+ execution: {
375
+ short: {
376
+ exchange: highest.exchange,
377
+ exchangeId: shortExchangeId,
378
+ ...shortJournal.result,
379
+ idempotency: {
380
+ key: shortJournal.idempotencyKey,
381
+ replayed: shortJournal.replayed,
382
+ autoGenerated: shortJournal.autoGeneratedKey,
383
+ journalId: shortJournal.journalId,
384
+ },
385
+ },
386
+ },
387
+ });
388
+ }
389
+ else {
390
+ console.error("\n \x1b[31mExecution failed on long leg:\x1b[0m", message);
391
+ console.log(" \x1b[33mWarning:\x1b[0m Short leg may be open. Check positions manually.\n");
392
+ }
393
+ process.exitCode = EXIT_CODES.PARTIAL_EXECUTION;
394
+ return;
395
+ }
396
+ if (!longJournal) {
397
+ throw new CLIError("Long-leg journal result missing", EXIT_CODES.EXECUTION_ERROR);
398
+ }
399
+ if (!isJson) {
400
+ console.log(` ✓ Long ${longJournal.replayed ? "replayed" : "opened"}: ${longJournal.result.order.id}`);
401
+ console.log("\n \x1b[32m━━━ Arbitrage Executed ━━━\x1b[0m");
402
+ if (shortJournal.result.safety.tpSlOrderIds.length > 0 ||
403
+ longJournal.result.safety.tpSlOrderIds.length > 0) {
404
+ console.log(" TP/SL attached on opened legs");
405
+ }
406
+ console.log(` Monitor positions with: perps -e ${shortExchangeId} account positions`);
407
+ console.log(` and: perps -e ${longExchangeId} account positions`);
408
+ console.log("");
409
+ }
410
+ else {
411
+ emitJson({
412
+ status: "executed",
413
+ market,
414
+ asset: assetName,
415
+ testnet: ctx.config.testnet,
416
+ requestedSizeUsd,
417
+ plan,
418
+ execution: {
419
+ short: {
420
+ exchange: highest.exchange,
421
+ exchangeId: shortExchangeId,
422
+ ...shortJournal.result,
423
+ idempotency: {
424
+ key: shortJournal.idempotencyKey,
425
+ replayed: shortJournal.replayed,
426
+ autoGenerated: shortJournal.autoGeneratedKey,
427
+ journalId: shortJournal.journalId,
428
+ },
429
+ },
430
+ long: {
431
+ exchange: lowest.exchange,
432
+ exchangeId: longExchangeId,
433
+ ...longJournal.result,
434
+ idempotency: {
435
+ key: longJournal.idempotencyKey,
436
+ replayed: longJournal.replayed,
437
+ autoGenerated: longJournal.autoGeneratedKey,
438
+ journalId: longJournal.journalId,
439
+ },
440
+ },
441
+ },
442
+ });
443
+ }
444
+ }
445
+ finally {
446
+ await Promise.allSettled([shortAdapter.disconnect(), longAdapter.disconnect()]);
447
+ }
448
+ }
449
+ catch (err) {
450
+ const code = inferExitCode(err);
451
+ const message = err instanceof Error ? err.message : String(err);
452
+ if (isJson) {
453
+ emitJson({
454
+ status: "error",
455
+ error: {
456
+ message,
457
+ exitCode: code,
458
+ },
459
+ });
460
+ }
461
+ else {
462
+ outputError(message);
463
+ }
464
+ process.exit(code);
465
+ }
466
+ });
467
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arb Funding Command
3
+ * Compare funding rates across exchanges
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerArbFundingCommand(arb: Command): void;
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Arb Funding Command
3
+ * Compare funding rates across exchanges
4
+ */
5
+ import { highlighter } from "../../cli/experience.js";
6
+ import { output, outputError } from "../../cli/output.js";
7
+ import { getContext, getOutputOptions } from "../../cli/program.js";
8
+ import { getAvailableExchanges, getExchangeAdapterById } from "../../lib/exchange.js";
9
+ export function registerArbFundingCommand(arb) {
10
+ arb
11
+ .command("funding [asset]")
12
+ .description("Compare funding rates across exchanges")
13
+ .option("--all", "Show funding for common assets")
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 (opts.all) {
22
+ await showAllFunding(exchanges, outputOpts.json, isTestnet);
23
+ }
24
+ else {
25
+ await showFunding(asset?.toUpperCase() || "BTC", exchanges, outputOpts.json, isTestnet);
26
+ }
27
+ }
28
+ catch (err) {
29
+ outputError(err instanceof Error ? err.message : String(err));
30
+ process.exit(1);
31
+ }
32
+ });
33
+ }
34
+ async function fetchFunding(asset, exchanges, isTestnet) {
35
+ const results = [];
36
+ const promises = exchanges.map(async (exchangeId) => {
37
+ const adapter = getExchangeAdapterById(exchangeId);
38
+ let connected = false;
39
+ try {
40
+ await adapter.connect({ testnet: isTestnet });
41
+ connected = true;
42
+ const symbol = `${asset}-PERP`;
43
+ const funding = await adapter.getFundingRate(symbol);
44
+ const rate = parseFloat(funding.rate) * 100; // Convert to percentage
45
+ return {
46
+ exchange: adapter.info.name,
47
+ symbol: funding.market,
48
+ rate,
49
+ annualized: rate * 24 * 365, // Hourly rate * 24 hours * 365 days
50
+ nextFunding: funding.nextFundingTime,
51
+ };
52
+ }
53
+ catch (err) {
54
+ return {
55
+ exchange: adapter.info.name,
56
+ symbol: `${asset}-PERP`,
57
+ rate: 0,
58
+ annualized: 0,
59
+ nextFunding: 0,
60
+ error: err instanceof Error ? err.message : "Unknown error",
61
+ };
62
+ }
63
+ finally {
64
+ if (connected) {
65
+ await adapter.disconnect().catch(() => undefined);
66
+ }
67
+ }
68
+ });
69
+ const settled = await Promise.allSettled(promises);
70
+ for (const result of settled) {
71
+ if (result.status === "fulfilled") {
72
+ results.push(result.value);
73
+ }
74
+ }
75
+ return results;
76
+ }
77
+ async function showFunding(asset, exchanges, json, isTestnet) {
78
+ if (!json) {
79
+ console.log(`\nFetching ${asset} funding rates...\n`);
80
+ }
81
+ const rates = await fetchFunding(asset, exchanges, isTestnet);
82
+ const validRates = rates.filter((r) => !r.error);
83
+ if (validRates.length === 0) {
84
+ if (json) {
85
+ output({
86
+ status: "no_data",
87
+ asset,
88
+ rates: [],
89
+ exchangesScanned: exchanges.length,
90
+ }, { json: true });
91
+ return;
92
+ }
93
+ console.log(`No funding data found for ${asset}`);
94
+ return;
95
+ }
96
+ // Find highest and lowest
97
+ const sorted = [...validRates].sort((a, b) => b.rate - a.rate);
98
+ const highest = sorted[0];
99
+ const lowest = sorted[sorted.length - 1];
100
+ const delta = highest.rate - lowest.rate;
101
+ const data = {
102
+ asset,
103
+ rates: validRates,
104
+ spread: {
105
+ high: { exchange: highest.exchange, rate: highest.rate },
106
+ low: { exchange: lowest.exchange, rate: lowest.rate },
107
+ delta,
108
+ annualizedDelta: delta * 24 * 365,
109
+ },
110
+ timestamp: Date.now(),
111
+ };
112
+ if (json) {
113
+ output(data, { json: true });
114
+ return;
115
+ }
116
+ // Table output
117
+ console.log(` ${asset}-PERP Funding Rate Comparison\n`);
118
+ console.log(" Exchange".padEnd(18) + "Rate (1h)".padEnd(16) + "Annualized".padEnd(16) + "Next Funding");
119
+ console.log(" " + "─".repeat(65));
120
+ const colorRate = (value, text) => value >= 0 ? highlighter.success(text) : highlighter.error(text);
121
+ for (const r of sorted) {
122
+ const sign = r.rate >= 0 ? "+" : "";
123
+ const rateStr = `${sign}${r.rate.toFixed(4)}%`;
124
+ const annStr = `${sign}${r.annualized.toFixed(2)}%`;
125
+ const nextFundingStr = r.nextFunding > 0 ? new Date(r.nextFunding).toLocaleTimeString() : "N/A";
126
+ // Use fixed-width fields before coloring to avoid ANSI padding issues
127
+ console.log(` ${r.exchange.padEnd(16)}` +
128
+ `${colorRate(r.rate, rateStr.padEnd(16))}` +
129
+ `${colorRate(r.annualized, annStr.padEnd(16))}` +
130
+ nextFundingStr);
131
+ }
132
+ // Show errors
133
+ const errors = rates.filter((r) => r.error);
134
+ if (errors.length > 0) {
135
+ console.log(`\n ${highlighter.warn("Not available on:")} ${errors.map((e) => e.exchange).join(", ")}`);
136
+ }
137
+ // Show arbitrage opportunity
138
+ console.log("\n " + "─".repeat(65));
139
+ if (delta > 0.001) {
140
+ // Only show if meaningful spread
141
+ console.log(`\n ${highlighter.success("Funding Rate Arbitrage Opportunity")}`);
142
+ console.log(` Short on ${highest.exchange} (paying ${highest.rate.toFixed(4)}%)`);
143
+ console.log(` Long on ${lowest.exchange} (paying ${lowest.rate.toFixed(4)}%)`);
144
+ console.log(` Delta: ${delta.toFixed(4)}% per hour (${(delta * 24 * 365).toFixed(2)}% annualized)`);
145
+ // Calculate daily profit on $10k position
146
+ const dailyProfit = (delta / 100) * 24 * 10000;
147
+ console.log(` ~$${dailyProfit.toFixed(2)}/day on $10k delta-neutral position`);
148
+ }
149
+ else {
150
+ console.log(`\n No significant funding rate spread (${delta.toFixed(4)}%)`);
151
+ }
152
+ console.log("");
153
+ }
154
+ async function showAllFunding(exchanges, json, isTestnet) {
155
+ const assets = ["BTC", "ETH", "SOL", "DOGE", "ARB", "OP"];
156
+ const rows = [];
157
+ if (!json) {
158
+ console.log("\nFetching funding rates for common assets...\n");
159
+ console.log(" Asset".padEnd(10) + "Highest".padEnd(28) + "Lowest".padEnd(28) + "Spread (Ann.)");
160
+ console.log(" " + "─".repeat(80));
161
+ }
162
+ for (const asset of assets) {
163
+ const rates = await fetchFunding(asset, exchanges, isTestnet);
164
+ const validRates = rates.filter((r) => !r.error);
165
+ if (validRates.length < 2) {
166
+ if (!json) {
167
+ console.log(` ${asset.padEnd(8)} Available on <2 exchanges`);
168
+ }
169
+ continue;
170
+ }
171
+ const sorted = [...validRates].sort((a, b) => b.rate - a.rate);
172
+ const highest = sorted[0];
173
+ const lowest = sorted[sorted.length - 1];
174
+ const delta = highest.rate - lowest.rate;
175
+ const annDelta = delta * 24 * 365;
176
+ rows.push({
177
+ asset,
178
+ highest: { exchange: highest.exchange, rate: highest.rate },
179
+ lowest: { exchange: lowest.exchange, rate: lowest.rate },
180
+ spreadAnnualized: annDelta,
181
+ timestamp: Date.now(),
182
+ });
183
+ if (!json) {
184
+ const colorRate = (value, text) => value >= 0 ? highlighter.success(text) : highlighter.error(text);
185
+ const highSign = highest.rate >= 0 ? "+" : "";
186
+ const lowSign = lowest.rate >= 0 ? "+" : "";
187
+ const highStr = `${highSign}${highest.rate.toFixed(4)}%`;
188
+ const lowStr = `${lowSign}${lowest.rate.toFixed(4)}%`;
189
+ const deltaStr = `${annDelta.toFixed(2)}%`;
190
+ console.log(` ${asset.padEnd(8)}` +
191
+ `${highest.exchange.padEnd(12)} ${colorRate(highest.rate, highStr.padEnd(12))} ` +
192
+ `${lowest.exchange.padEnd(12)} ${colorRate(lowest.rate, lowStr.padEnd(12))} ` +
193
+ (annDelta > 10 ? highlighter.success(deltaStr) : highlighter.dim(deltaStr)));
194
+ }
195
+ }
196
+ if (json) {
197
+ output(rows, { json: true });
198
+ return;
199
+ }
200
+ console.log("");
201
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Arb History Command
3
+ * View historical funding rate spreads
4
+ */
5
+ import { Command } from "commander";
6
+ export declare function registerArbHistoryCommand(arb: Command): void;