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,412 @@
1
+ import chalk from "chalk";
2
+ import { printJson, jsonOk } from "../utils.js";
3
+ import { loadBotConfig, quickGridConfig, quickDCAConfig } from "../bot/config.js";
4
+ import { runBot } from "../bot/engine.js";
5
+ import { PRESETS, getPreset, getPresetsByStrategy } from "../bot/presets.js";
6
+ export function registerBotCommands(program, getAdapter, getAdapterFor, isJson) {
7
+ const bot = program.command("bot").description("Automated trading bots with condition monitoring & risk management");
8
+ // ── bot start <config> ──
9
+ bot
10
+ .command("start <config>")
11
+ .description("Start a bot from a YAML/JSON config file")
12
+ .option("--background", "Run in background (tmux)")
13
+ .option("--job-id <id>", "Job ID (set by background runner)")
14
+ .action(async (configPath, opts) => {
15
+ const config = loadBotConfig(configPath);
16
+ if (opts.background) {
17
+ const { startJob } = await import("../jobs.js");
18
+ const job = startJob({
19
+ strategy: `bot:${config.strategy.type}`,
20
+ exchange: config.exchange,
21
+ params: { config: configPath, name: config.name },
22
+ cliArgs: [`-e`, config.exchange, `start`, configPath],
23
+ });
24
+ if (isJson())
25
+ return printJson(jsonOk(job));
26
+ printBotJobStarted(config.name, job.id);
27
+ return;
28
+ }
29
+ const adapter = config.strategy.type === "funding-arb"
30
+ ? await getAdapterFor(config.exchange)
31
+ : await getAdapter();
32
+ const log = makeLog();
33
+ await runBot(adapter, config, opts.jobId, log);
34
+ });
35
+ // ── bot quick grid ──
36
+ bot
37
+ .command("quick-grid <symbol>")
38
+ .description("Quick-start a grid bot with smart defaults")
39
+ .option("--range <pct>", "Price range ±% from current", "3")
40
+ .option("--grids <n>", "Number of grid lines", "10")
41
+ .option("--size <size>", "Total position size (base)", "0.1")
42
+ .option("--side <side>", "Bias: long, short, neutral", "neutral")
43
+ .option("--leverage <n>", "Leverage")
44
+ .option("--max-drawdown <usd>", "Stop if drawdown exceeds ($)", "100")
45
+ .option("--max-runtime <sec>", "Max runtime in seconds", "0")
46
+ .option("--background", "Run in background (tmux)")
47
+ .option("--job-id <id>", "Job ID")
48
+ .action(async (symbol, opts) => {
49
+ const adapter = await getAdapter();
50
+ const config = quickGridConfig({
51
+ exchange: adapter.name,
52
+ symbol: symbol.toUpperCase(),
53
+ rangePct: parseFloat(opts.range),
54
+ grids: parseInt(opts.grids),
55
+ size: parseFloat(opts.size),
56
+ side: opts.side,
57
+ maxDrawdown: parseFloat(opts.maxDrawdown),
58
+ maxRuntime: opts.maxRuntime !== "0" ? parseInt(opts.maxRuntime) : undefined,
59
+ leverage: opts.leverage ? parseInt(opts.leverage) : undefined,
60
+ });
61
+ if (opts.background) {
62
+ const { startJob } = await import("../jobs.js");
63
+ const cliArgs = [
64
+ `-e`, adapter.name, `quick-grid`, symbol.toUpperCase(),
65
+ `--range`, opts.range, `--grids`, opts.grids, `--size`, opts.size,
66
+ `--side`, opts.side, `--max-drawdown`, opts.maxDrawdown,
67
+ `--max-runtime`, opts.maxRuntime,
68
+ ...(opts.leverage ? [`--leverage`, opts.leverage] : []),
69
+ ];
70
+ const job = startJob({
71
+ strategy: `bot:grid`,
72
+ exchange: adapter.name,
73
+ params: { symbol: symbol.toUpperCase(), ...opts },
74
+ cliArgs,
75
+ });
76
+ if (isJson())
77
+ return printJson(jsonOk(job));
78
+ printBotJobStarted(config.name, job.id);
79
+ return;
80
+ }
81
+ const log = makeLog();
82
+ await runBot(adapter, config, opts.jobId, log);
83
+ });
84
+ // ── bot quick dca ──
85
+ bot
86
+ .command("quick-dca <symbol> <side> <amount> <interval>")
87
+ .description("Quick-start a DCA bot")
88
+ .option("--orders <n>", "Total orders (0 = unlimited)", "0")
89
+ .option("--price-limit <price>", "Don't buy above / sell below this price")
90
+ .option("--trigger-drop <pct>", "Only buy when price drops X% from recent high")
91
+ .option("--max-drawdown <usd>", "Stop if drawdown exceeds ($)", "100")
92
+ .option("--background", "Run in background (tmux)")
93
+ .option("--job-id <id>", "Job ID")
94
+ .action(async (symbol, side, amount, interval, opts) => {
95
+ const adapter = await getAdapter();
96
+ const config = quickDCAConfig({
97
+ exchange: adapter.name,
98
+ symbol: symbol.toUpperCase(),
99
+ side: side.toLowerCase(),
100
+ amount: parseFloat(amount),
101
+ intervalSec: parseInt(interval),
102
+ orders: parseInt(opts.orders),
103
+ triggerDrop: opts.triggerDrop ? parseFloat(opts.triggerDrop) : undefined,
104
+ priceLimit: opts.priceLimit ? parseFloat(opts.priceLimit) : undefined,
105
+ maxDrawdown: parseFloat(opts.maxDrawdown),
106
+ });
107
+ if (opts.background) {
108
+ const { startJob } = await import("../jobs.js");
109
+ const cliArgs = [
110
+ `-e`, adapter.name, `quick-dca`, symbol.toUpperCase(), side.toLowerCase(),
111
+ amount, interval, `--orders`, opts.orders, `--max-drawdown`, opts.maxDrawdown,
112
+ ...(opts.priceLimit ? [`--price-limit`, opts.priceLimit] : []),
113
+ ...(opts.triggerDrop ? [`--trigger-drop`, opts.triggerDrop] : []),
114
+ ];
115
+ const job = startJob({
116
+ strategy: `bot:dca`,
117
+ exchange: adapter.name,
118
+ params: { symbol: symbol.toUpperCase(), ...opts },
119
+ cliArgs,
120
+ });
121
+ if (isJson())
122
+ return printJson(jsonOk(job));
123
+ printBotJobStarted(config.name, job.id);
124
+ return;
125
+ }
126
+ const log = makeLog();
127
+ await runBot(adapter, config, opts.jobId, log);
128
+ });
129
+ // ── bot quick arb ──
130
+ bot
131
+ .command("quick-arb")
132
+ .description("Quick-start a funding rate arbitrage bot")
133
+ .option("--min-spread <pct>", "Min annual spread to enter (%)", "20")
134
+ .option("--close-spread <pct>", "Close when spread drops below (%)", "5")
135
+ .option("--size <usd>", "Position size per leg ($)", "50")
136
+ .option("--max-positions <n>", "Max simultaneous positions", "3")
137
+ .option("--exchanges <list>", "Comma-separated exchanges", "pacifica,hyperliquid,lighter")
138
+ .option("--interval <sec>", "Check interval in seconds", "60")
139
+ .option("--max-drawdown <usd>", "Stop if drawdown exceeds ($)", "200")
140
+ .option("--background", "Run in background (tmux)")
141
+ .option("--job-id <id>", "Job ID")
142
+ .action(async (opts) => {
143
+ const exchangeNames = opts.exchanges.split(",").map(e => e.trim());
144
+ const adapters = new Map();
145
+ for (const name of exchangeNames) {
146
+ try {
147
+ adapters.set(name, await getAdapterFor(name));
148
+ }
149
+ catch {
150
+ // skip unavailable
151
+ }
152
+ }
153
+ if (adapters.size < 2) {
154
+ console.error(chalk.red("\n Need at least 2 exchanges for arbitrage.\n"));
155
+ return;
156
+ }
157
+ const primaryAdapter = adapters.values().next().value;
158
+ const config = {
159
+ name: `arb-funding-${Date.now().toString(36)}`,
160
+ exchange: primaryAdapter.name,
161
+ symbol: "ETH", // multi-symbol scanning
162
+ strategy: {
163
+ type: "funding-arb",
164
+ min_spread: parseFloat(opts.minSpread),
165
+ close_spread: parseFloat(opts.closeSpread),
166
+ size_usd: parseFloat(opts.size),
167
+ max_positions: parseInt(opts.maxPositions),
168
+ exchanges: exchangeNames,
169
+ },
170
+ entry_conditions: [{ type: "always", value: 0 }],
171
+ exit_conditions: [],
172
+ risk: {
173
+ max_position_usd: parseFloat(opts.size) * parseInt(opts.maxPositions) * 2,
174
+ max_daily_loss: parseFloat(opts.maxDrawdown) / 2,
175
+ max_drawdown: parseFloat(opts.maxDrawdown),
176
+ pause_after_loss_sec: 300,
177
+ max_open_bots: 1,
178
+ },
179
+ monitor_interval_sec: parseInt(opts.interval),
180
+ };
181
+ if (opts.background) {
182
+ const { startJob } = await import("../jobs.js");
183
+ const cliArgs = [
184
+ `quick-arb`,
185
+ `--min-spread`, opts.minSpread, `--close-spread`, opts.closeSpread,
186
+ `--size`, opts.size, `--max-positions`, opts.maxPositions,
187
+ `--exchanges`, opts.exchanges, `--interval`, opts.interval,
188
+ `--max-drawdown`, opts.maxDrawdown,
189
+ ];
190
+ const job = startJob({
191
+ strategy: `bot:funding-arb`,
192
+ exchange: "multi",
193
+ params: { ...opts },
194
+ cliArgs,
195
+ });
196
+ if (isJson())
197
+ return printJson(jsonOk(job));
198
+ printBotJobStarted(config.name, job.id);
199
+ return;
200
+ }
201
+ const log = makeLog();
202
+ await runBot(primaryAdapter, config, opts.jobId, log, adapters);
203
+ });
204
+ // ── bot preset list ──
205
+ bot
206
+ .command("preset-list")
207
+ .description("List all available strategy presets")
208
+ .option("--strategy <type>", "Filter by strategy: grid, dca, funding-arb")
209
+ .action((opts) => {
210
+ const presets = opts.strategy ? getPresetsByStrategy(opts.strategy) : PRESETS;
211
+ if (isJson())
212
+ return printJson(jsonOk(presets.map(p => ({ name: p.name, strategy: p.strategy, risk: p.risk, description: p.description }))));
213
+ console.log(chalk.cyan.bold("\n Strategy Presets\n"));
214
+ const groups = new Map();
215
+ for (const p of presets) {
216
+ const list = groups.get(p.strategy) ?? [];
217
+ list.push(p);
218
+ groups.set(p.strategy, list);
219
+ }
220
+ for (const [strat, list] of groups) {
221
+ console.log(chalk.white.bold(` ${strat.toUpperCase()}`));
222
+ for (const p of list) {
223
+ const riskColor = p.risk === "low" ? chalk.green : p.risk === "medium" ? chalk.yellow : chalk.red;
224
+ console.log(` ${chalk.cyan(p.name.padEnd(22))} ${riskColor(`[${p.risk}]`.padEnd(10))} ${chalk.gray(p.description)}`);
225
+ }
226
+ console.log();
227
+ }
228
+ console.log(chalk.gray(` Usage: perp bot preset <name> <symbol>`));
229
+ console.log(chalk.gray(` perp bot preset grid-standard ETH`));
230
+ console.log(chalk.gray(` perp bot preset arb-conservative --background\n`));
231
+ });
232
+ // ── bot preset <name> <symbol> ──
233
+ bot
234
+ .command("preset <name> [symbol]")
235
+ .description("Start a bot from a preset (use 'preset-list' to see options)")
236
+ .option("--background", "Run in background (tmux)")
237
+ .option("--job-id <id>", "Job ID")
238
+ .action(async (name, symbol, opts) => {
239
+ const preset = getPreset(name);
240
+ if (!preset) {
241
+ console.error(chalk.red(`\n Unknown preset: "${name}"`));
242
+ console.error(chalk.gray(` Run 'perp bot preset-list' to see available presets.\n`));
243
+ return;
244
+ }
245
+ const sym = symbol?.toUpperCase() ?? "ETH";
246
+ // For arb presets, need multiple adapters
247
+ if (preset.strategy === "funding-arb") {
248
+ const config = preset.buildConfig("multi", sym);
249
+ const arbStrategy = config.strategy;
250
+ const adapters = new Map();
251
+ for (const exName of arbStrategy.exchanges) {
252
+ try {
253
+ adapters.set(exName, await getAdapterFor(exName));
254
+ }
255
+ catch {
256
+ // skip unavailable
257
+ }
258
+ }
259
+ if (adapters.size < 2) {
260
+ console.error(chalk.red("\n Need at least 2 exchanges for arbitrage.\n"));
261
+ return;
262
+ }
263
+ const primaryAdapter = adapters.values().next().value;
264
+ config.exchange = primaryAdapter.name;
265
+ if (opts.background) {
266
+ const { startJob } = await import("../jobs.js");
267
+ const cliArgs = [`preset`, name, sym];
268
+ const job = startJob({
269
+ strategy: `bot:${preset.strategy}`,
270
+ exchange: "multi",
271
+ params: { preset: name, symbol: sym },
272
+ cliArgs,
273
+ });
274
+ if (isJson())
275
+ return printJson(jsonOk(job));
276
+ printBotJobStarted(config.name, job.id);
277
+ return;
278
+ }
279
+ const log = makeLog();
280
+ if (!isJson()) {
281
+ console.log(chalk.cyan.bold(`\n Starting preset: ${chalk.white(preset.name)}`));
282
+ console.log(chalk.gray(` ${preset.description}\n`));
283
+ }
284
+ await runBot(primaryAdapter, config, opts.jobId, log, adapters);
285
+ return;
286
+ }
287
+ // Grid / DCA presets
288
+ const adapter = await getAdapter();
289
+ const config = preset.buildConfig(adapter.name, sym);
290
+ if (opts.background) {
291
+ const { startJob } = await import("../jobs.js");
292
+ const cliArgs = [`preset`, name, sym];
293
+ const job = startJob({
294
+ strategy: `bot:${preset.strategy}`,
295
+ exchange: adapter.name,
296
+ params: { preset: name, symbol: sym },
297
+ cliArgs,
298
+ });
299
+ if (isJson())
300
+ return printJson(jsonOk(job));
301
+ printBotJobStarted(config.name, job.id);
302
+ return;
303
+ }
304
+ const log = makeLog();
305
+ if (!isJson()) {
306
+ console.log(chalk.cyan.bold(`\n Starting preset: ${chalk.white(preset.name)}`));
307
+ console.log(chalk.gray(` ${preset.description}\n`));
308
+ }
309
+ await runBot(adapter, config, opts.jobId, log);
310
+ });
311
+ // ── bot example ──
312
+ bot
313
+ .command("example")
314
+ .description("Print example YAML bot configs")
315
+ .action(() => {
316
+ console.log(chalk.cyan.bold("\n Example Bot Configs\n"));
317
+ console.log(chalk.white.bold(" 1. Grid Bot (auto range)"));
318
+ console.log(chalk.gray(` Save to ~/.perp/bots/eth-grid.yaml:\n`));
319
+ console.log(`${GRID_EXAMPLE}\n`);
320
+ console.log(chalk.white.bold(" 2. DCA Bot (buy the dip)"));
321
+ console.log(chalk.gray(` Save to ~/.perp/bots/eth-dca.yaml:\n`));
322
+ console.log(`${DCA_EXAMPLE}\n`);
323
+ console.log(chalk.white.bold(" 3. Funding Arb Bot"));
324
+ console.log(chalk.gray(` Save to ~/.perp/bots/funding-arb.yaml:\n`));
325
+ console.log(`${ARB_EXAMPLE}\n`);
326
+ console.log(chalk.gray(` Usage: perp bot start ~/.perp/bots/eth-grid.yaml`));
327
+ console.log(chalk.gray(` perp bot start ~/.perp/bots/eth-grid.yaml --background\n`));
328
+ });
329
+ }
330
+ function makeLog() {
331
+ return (msg) => {
332
+ const ts = new Date().toLocaleTimeString();
333
+ console.log(`${chalk.gray(ts)} ${msg}`);
334
+ };
335
+ }
336
+ function printBotJobStarted(name, jobId) {
337
+ console.log(chalk.green(`\n Bot "${name}" started in background.`));
338
+ console.log(` Job ID: ${chalk.white.bold(jobId)}`);
339
+ console.log(` Logs: ${chalk.gray(`perp jobs logs ${jobId} -f`)}`);
340
+ console.log(` Stop: ${chalk.gray(`perp jobs stop ${jobId}`)}\n`);
341
+ }
342
+ // ── Example YAML configs ──
343
+ const GRID_EXAMPLE = chalk.gray(` name: eth-grid-bot
344
+ exchange: hyperliquid
345
+ symbol: ETH
346
+
347
+ strategy:
348
+ type: grid
349
+ grids: 15
350
+ size: 0.1
351
+ side: neutral
352
+ range_mode: auto
353
+ range_pct: 3
354
+ rebalance: true
355
+ leverage: 5
356
+
357
+ entry_conditions:
358
+ - type: volatility_below
359
+ value: 5
360
+
361
+ exit_conditions:
362
+ - type: volatility_above
363
+ value: 10
364
+ - type: time_after
365
+ value: 86400
366
+
367
+ risk:
368
+ max_drawdown: 50
369
+ max_daily_loss: 30
370
+ pause_after_loss_sec: 300`);
371
+ const DCA_EXAMPLE = chalk.gray(` name: eth-dca-dip
372
+ exchange: hyperliquid
373
+ symbol: ETH
374
+
375
+ strategy:
376
+ type: dca
377
+ amount: 0.01
378
+ interval_sec: 3600
379
+ total_orders: 24
380
+
381
+ entry_conditions:
382
+ - type: price_below
383
+ value: 2500
384
+
385
+ exit_conditions:
386
+ - type: price_above
387
+ value: 2800
388
+
389
+ risk:
390
+ max_drawdown: 100
391
+ max_daily_loss: 50`);
392
+ const ARB_EXAMPLE = chalk.gray(` name: funding-arb
393
+ exchange: hyperliquid
394
+ symbol: ETH
395
+
396
+ strategy:
397
+ type: funding-arb
398
+ min_spread: 20
399
+ close_spread: 5
400
+ size_usd: 100
401
+ max_positions: 3
402
+ exchanges:
403
+ - pacifica
404
+ - hyperliquid
405
+
406
+ entry_conditions:
407
+ - type: always
408
+ value: 0
409
+
410
+ risk:
411
+ max_drawdown: 200
412
+ max_daily_loss: 50`);
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerBridgeCommands(program: Command, isJson: () => boolean): void;