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,202 @@
1
+ import { printJson, jsonOk } from "../utils.js";
2
+ import { updateJobState } from "../jobs.js";
3
+ import { runTWAP } from "../strategies/twap.js";
4
+ import { runFundingArb } from "../strategies/funding-arb.js";
5
+ import { runGrid } from "../strategies/grid.js";
6
+ import { runDCA } from "../strategies/dca.js";
7
+ import { runTrailingStop } from "../strategies/trailing-stop.js";
8
+ import chalk from "chalk";
9
+ export function registerRunCommands(program, getAdapter, getAdapterFor, isJson) {
10
+ const run = program.command("run").description("Run a strategy (foreground or as background job target)");
11
+ run
12
+ .command("twap <symbol> <side> <size> <duration>")
13
+ .description("Run client-side TWAP (splits market orders over time)")
14
+ .option("-s, --slices <n>", "Number of slices (default: auto)")
15
+ .option("--job-id <id>", "Job ID (set automatically for background jobs)")
16
+ .action(async (symbol, side, size, duration, opts) => {
17
+ const s = side.toLowerCase();
18
+ if (s !== "buy" && s !== "sell") {
19
+ console.error(chalk.red("Side must be buy or sell"));
20
+ process.exit(1);
21
+ }
22
+ const adapter = await getAdapter();
23
+ const params = {
24
+ symbol: symbol.toUpperCase(),
25
+ side: s,
26
+ totalSize: parseFloat(size),
27
+ durationSec: parseInt(duration),
28
+ slices: opts.slices ? parseInt(opts.slices) : undefined,
29
+ };
30
+ const log = (msg) => {
31
+ const ts = new Date().toLocaleTimeString();
32
+ console.log(`${chalk.gray(ts)} ${msg}`);
33
+ };
34
+ const result = await runTWAP(adapter, params, opts.jobId, log);
35
+ if (isJson())
36
+ printJson(jsonOk(result));
37
+ if (opts.jobId) {
38
+ updateJobState(opts.jobId, { status: "done" });
39
+ }
40
+ });
41
+ run
42
+ .command("funding-arb")
43
+ .description("Run funding rate arbitrage monitor/executor")
44
+ .option("--min-spread <pct>", "Min annual spread % to enter", "10")
45
+ .option("--close-spread <pct>", "Close when spread drops below (%)", "5")
46
+ .option("--size <size>", "Position size per leg (base amount)", "0.01")
47
+ .option("--size-usd <usd>", "Position size per leg in USD (alternative)")
48
+ .option("--symbols <list>", "Comma-separated symbol filter")
49
+ .option("--interval <sec>", "Check interval in seconds", "60")
50
+ .option("--auto-execute", "Actually place trades (default: monitor only)")
51
+ .option("--max-positions <n>", "Max simultaneous positions", "3")
52
+ .option("--exchanges <list>", "Comma-separated exchanges to use", "pacifica,hyperliquid,lighter")
53
+ .option("--auto-rebalance", "Trigger rebalance alerts when exchange balance is low")
54
+ .option("--rebalance-threshold <usd>", "Rebalance when available < this ($)", "100")
55
+ .option("--max-drawdown <usd>", "Close all positions if total uPnL exceeds this loss ($)")
56
+ .option("--job-id <id>", "Job ID (set automatically for background jobs)")
57
+ .action(async (opts) => {
58
+ const exchangeNames = opts.exchanges.split(",").map((e) => e.trim());
59
+ const adapters = new Map();
60
+ for (const name of exchangeNames) {
61
+ try {
62
+ const a = await getAdapterFor(name);
63
+ adapters.set(name, a);
64
+ }
65
+ catch {
66
+ if (!isJson())
67
+ console.log(chalk.yellow(` Skipping ${name} (no credentials)`));
68
+ }
69
+ }
70
+ if (adapters.size < 2) {
71
+ console.error(chalk.red("Need at least 2 exchanges for arbitrage. Set credentials for 2+ exchanges."));
72
+ process.exit(1);
73
+ }
74
+ const params = {
75
+ minSpread: parseFloat(opts.minSpread),
76
+ closeSpread: parseFloat(opts.closeSpread),
77
+ size: opts.size,
78
+ sizeUsd: opts.sizeUsd ? parseFloat(opts.sizeUsd) : undefined,
79
+ symbols: opts.symbols ? opts.symbols.split(",").map((s) => s.trim().toUpperCase()) : undefined,
80
+ intervalSec: parseInt(opts.interval),
81
+ autoExecute: opts.autoExecute ?? false,
82
+ maxPositions: parseInt(opts.maxPositions),
83
+ autoRebalance: opts.autoRebalance ?? false,
84
+ rebalanceThreshold: parseFloat(opts.rebalanceThreshold),
85
+ maxDrawdown: opts.maxDrawdown ? parseFloat(opts.maxDrawdown) : undefined,
86
+ };
87
+ const log = (msg) => {
88
+ const ts = new Date().toLocaleTimeString();
89
+ console.log(`${chalk.gray(ts)} ${msg}`);
90
+ };
91
+ await runFundingArb(adapters, params, opts.jobId, log);
92
+ });
93
+ // ── Grid Bot ──
94
+ run
95
+ .command("grid <symbol>")
96
+ .description("Run grid trading bot (places limit orders across a price range)")
97
+ .requiredOption("--upper <price>", "Upper price bound")
98
+ .requiredOption("--lower <price>", "Lower price bound")
99
+ .option("--grids <n>", "Number of grid lines", "10")
100
+ .option("--size <size>", "Total position size (base)", "0.1")
101
+ .option("--side <side>", "Grid bias: long, short, neutral", "neutral")
102
+ .option("--leverage <n>", "Leverage to set")
103
+ .option("--interval <sec>", "Check interval in seconds", "10")
104
+ .option("--max-runtime <sec>", "Max runtime in seconds (0 = forever)", "0")
105
+ .option("--trailing-stop <pct>", "Stop if equity drops by this % from peak")
106
+ .option("--job-id <id>", "Job ID (set automatically for background jobs)")
107
+ .action(async (symbol, opts) => {
108
+ const side = opts.side.toLowerCase();
109
+ if (side !== "long" && side !== "short" && side !== "neutral") {
110
+ console.error(chalk.red("Side must be long, short, or neutral"));
111
+ process.exit(1);
112
+ }
113
+ const adapter = await getAdapter();
114
+ const params = {
115
+ symbol: symbol.toUpperCase(),
116
+ side: side,
117
+ upperPrice: parseFloat(opts.upper),
118
+ lowerPrice: parseFloat(opts.lower),
119
+ grids: parseInt(opts.grids),
120
+ totalSize: parseFloat(opts.size),
121
+ leverage: opts.leverage ? parseInt(opts.leverage) : undefined,
122
+ intervalSec: parseInt(opts.interval),
123
+ maxRuntime: parseInt(opts.maxRuntime),
124
+ trailingStop: opts.trailingStop ? parseFloat(opts.trailingStop) : undefined,
125
+ };
126
+ const log = (msg) => {
127
+ const ts = new Date().toLocaleTimeString();
128
+ console.log(`${chalk.gray(ts)} ${msg}`);
129
+ };
130
+ const result = await runGrid(adapter, params, opts.jobId, log);
131
+ if (isJson())
132
+ printJson(jsonOk(result));
133
+ if (opts.jobId) {
134
+ updateJobState(opts.jobId, { status: "done" });
135
+ }
136
+ });
137
+ // ── DCA (Dollar Cost Averaging) ──
138
+ run
139
+ .command("dca <symbol> <side> <amount> <interval>")
140
+ .description("Run DCA strategy (periodic market orders at fixed intervals)")
141
+ .option("--orders <n>", "Total number of orders (0 = unlimited)", "0")
142
+ .option("--price-limit <price>", "Stop buying above / selling below this price")
143
+ .option("--max-runtime <sec>", "Max runtime in seconds (0 = forever)", "0")
144
+ .option("--job-id <id>", "Job ID (set automatically for background jobs)")
145
+ .action(async (symbol, side, amount, interval, opts) => {
146
+ const s = side.toLowerCase();
147
+ if (s !== "buy" && s !== "sell") {
148
+ console.error(chalk.red("Side must be buy or sell"));
149
+ process.exit(1);
150
+ }
151
+ const adapter = await getAdapter();
152
+ const params = {
153
+ symbol: symbol.toUpperCase(),
154
+ side: s,
155
+ amountPerOrder: parseFloat(amount),
156
+ intervalSec: parseInt(interval),
157
+ totalOrders: parseInt(opts.orders),
158
+ priceLimit: opts.priceLimit ? parseFloat(opts.priceLimit) : undefined,
159
+ maxRuntime: parseInt(opts.maxRuntime),
160
+ };
161
+ const log = (msg) => {
162
+ const ts = new Date().toLocaleTimeString();
163
+ console.log(`${chalk.gray(ts)} ${msg}`);
164
+ };
165
+ const result = await runDCA(adapter, params, opts.jobId, log);
166
+ if (isJson())
167
+ printJson(jsonOk(result));
168
+ if (opts.jobId) {
169
+ updateJobState(opts.jobId, { status: "done" });
170
+ }
171
+ });
172
+ // ── Trailing Stop ──
173
+ run
174
+ .command("trailing-stop <symbol>")
175
+ .description("Run client-side trailing stop (monitors price, closes position when trail % hit)")
176
+ .requiredOption("--trail <pct>", "Trail percentage")
177
+ .option("--interval <sec>", "Check interval in seconds", "5")
178
+ .option("--activation <price>", "Only start trailing after price reaches this level")
179
+ .option("--job-id <id>", "Job ID (set automatically for background jobs)")
180
+ .action(async (symbol, opts) => {
181
+ const sym = symbol.toUpperCase();
182
+ const trailPct = parseFloat(opts.trail);
183
+ const intervalSec = parseInt(opts.interval);
184
+ const activationPrice = opts.activation ? parseFloat(opts.activation) : undefined;
185
+ const adapter = await getAdapter();
186
+ const log = (msg) => {
187
+ const ts = new Date().toLocaleTimeString();
188
+ console.log(`${chalk.gray(ts)} ${msg}`);
189
+ };
190
+ const result = await runTrailingStop(adapter, {
191
+ symbol: sym,
192
+ trailPct,
193
+ intervalSec,
194
+ activationPrice,
195
+ }, opts.jobId, log);
196
+ if (isJson())
197
+ printJson(jsonOk(result));
198
+ if (opts.jobId) {
199
+ updateJobState(opts.jobId, { status: "done" });
200
+ }
201
+ });
202
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerSettingsCommands(program: Command, isJson: () => boolean): void;
@@ -0,0 +1,102 @@
1
+ import chalk from "chalk";
2
+ import { printJson, jsonOk, withJsonErrors } from "../utils.js";
3
+ import { loadSettings, saveSettings } from "../settings.js";
4
+ export function registerSettingsCommands(program, isJson) {
5
+ const settings = program.command("settings").description("CLI settings (referrals, defaults)");
6
+ // ── settings show ──
7
+ settings
8
+ .command("show")
9
+ .description("Show current settings")
10
+ .action(async () => {
11
+ await withJsonErrors(isJson(), async () => {
12
+ const s = loadSettings();
13
+ if (isJson())
14
+ return printJson(jsonOk(s));
15
+ console.log(chalk.cyan.bold("\n CLI Settings\n"));
16
+ console.log(` Default Exchange: ${s.defaultExchange ? chalk.cyan(s.defaultExchange) : chalk.gray("pacifica (built-in)")}`);
17
+ console.log(` Referrals: ${s.referrals ? chalk.green("ON") : chalk.gray("OFF (opt-in)")}`);
18
+ console.log(chalk.white.bold("\n Referral Codes:"));
19
+ console.log(` Pacifica: ${s.referralCodes.pacifica || chalk.gray("(none)")}`);
20
+ console.log(` Hyperliquid: ${s.referralCodes.hyperliquid || chalk.gray("(none)")}`);
21
+ console.log(` Lighter: ${s.referralCodes.lighter || chalk.gray("(none)")}`);
22
+ console.log(chalk.gray(`\n Config: ~/.perp/settings.json\n`));
23
+ });
24
+ });
25
+ // ── settings referrals ──
26
+ settings
27
+ .command("referrals")
28
+ .description("Enable or disable referral codes")
29
+ .argument("<action>", "on | off")
30
+ .action(async (action) => {
31
+ await withJsonErrors(isJson(), async () => {
32
+ const s = loadSettings();
33
+ if (action === "on") {
34
+ s.referrals = true;
35
+ // Reset applied flags so referrals get re-sent on next connection
36
+ s.referralApplied = { hyperliquid: false, lighter: false };
37
+ saveSettings(s);
38
+ if (isJson())
39
+ return printJson(jsonOk({ referrals: true }));
40
+ console.log(chalk.green("\n Referrals enabled. Codes will be sent on next connection.\n"));
41
+ if (s.referralCodes.pacifica)
42
+ console.log(` Pacifica builder code: ${s.referralCodes.pacifica}`);
43
+ if (s.referralCodes.hyperliquid)
44
+ console.log(` Hyperliquid referral: ${s.referralCodes.hyperliquid}`);
45
+ if (s.referralCodes.lighter)
46
+ console.log(` Lighter referral: ${s.referralCodes.lighter}`);
47
+ console.log();
48
+ }
49
+ else if (action === "off") {
50
+ s.referrals = false;
51
+ saveSettings(s);
52
+ if (isJson())
53
+ return printJson(jsonOk({ referrals: false }));
54
+ console.log(chalk.yellow("\n Referrals disabled. No codes will be sent.\n"));
55
+ }
56
+ else {
57
+ console.error(chalk.red(`\n Usage: perp settings referrals <on|off>\n`));
58
+ }
59
+ });
60
+ });
61
+ // ── settings set ──
62
+ settings
63
+ .command("set")
64
+ .description("Set a specific setting")
65
+ .argument("<key>", "Setting key (e.g. referralCodes.hyperliquid)")
66
+ .argument("<value>", "Setting value")
67
+ .action(async (key, value) => {
68
+ await withJsonErrors(isJson(), async () => {
69
+ const s = loadSettings();
70
+ if (key === "default-exchange" || key === "defaultExchange") {
71
+ const valid = ["pacifica", "hyperliquid", "lighter"];
72
+ if (!valid.includes(value.toLowerCase())) {
73
+ console.error(chalk.red(` Invalid exchange: ${value}. Use: ${valid.join(", ")}`));
74
+ return;
75
+ }
76
+ s.defaultExchange = value.toLowerCase();
77
+ }
78
+ else if (key === "referrals") {
79
+ s.referrals = value === "true" || value === "on";
80
+ }
81
+ else if (key.startsWith("referralCodes.")) {
82
+ const exchange = key.split(".")[1];
83
+ if (exchange in s.referralCodes) {
84
+ s.referralCodes[exchange] = value;
85
+ }
86
+ else {
87
+ console.error(chalk.red(` Unknown exchange: ${exchange}`));
88
+ return;
89
+ }
90
+ }
91
+ else {
92
+ console.error(chalk.red(` Unknown key: ${key}`));
93
+ console.log(chalk.gray(" Valid keys: default-exchange, referrals, referralCodes.pacifica, referralCodes.hyperliquid, referralCodes.lighter\n"));
94
+ return;
95
+ }
96
+ saveSettings(s);
97
+ if (isJson())
98
+ return printJson(jsonOk({ key, value }));
99
+ console.log(chalk.green(`\n ${key} = ${value}\n`));
100
+ });
101
+ });
102
+ }
@@ -0,0 +1,5 @@
1
+ import { Command } from "commander";
2
+ import { type Network } from "../pacifica/index.js";
3
+ import type { Exchange } from "../config.js";
4
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
5
+ export declare function registerStreamCommands(program: Command, getNetwork: () => Network, getExchange: () => Exchange, getAdapter?: () => Promise<ExchangeAdapter>): void;
@@ -0,0 +1,123 @@
1
+ import { PacificaWSClient } from "../pacifica/index.js";
2
+ import chalk from "chalk";
3
+ import { formatUsd, formatPercent } from "../utils.js";
4
+ export function registerStreamCommands(program, getNetwork, getExchange, getAdapter) {
5
+ const stream = program.command("stream").description("Live WebSocket streams (Ctrl+C to stop)");
6
+ stream
7
+ .command("prices")
8
+ .description("Stream live prices")
9
+ .action(async () => {
10
+ const ws = new PacificaWSClient({ network: getNetwork() });
11
+ await ws.connect();
12
+ ws.subscribe("prices");
13
+ ws.on("prices", (msg) => {
14
+ const data = msg;
15
+ const d = data.data ?? data;
16
+ if (Array.isArray(d)) {
17
+ d.forEach((p) => {
18
+ const mark = Number(p.mark ?? p.m ?? 0);
19
+ const funding = Number(p.funding ?? p.f ?? 0);
20
+ const sym = String(p.symbol ?? p.s ?? "").padEnd(10);
21
+ console.log(`${chalk.white.bold(sym)} $${formatUsd(mark)} ${formatPercent(funding)}`);
22
+ });
23
+ }
24
+ });
25
+ console.log(chalk.cyan("Streaming prices... (Ctrl+C to stop)\n"));
26
+ await new Promise(() => { }); // keep alive
27
+ });
28
+ stream
29
+ .command("book <symbol>")
30
+ .description("Stream orderbook updates")
31
+ .action(async (symbol) => {
32
+ const ws = new PacificaWSClient({ network: getNetwork() });
33
+ await ws.connect();
34
+ ws.subscribe("book", { symbol: symbol.toUpperCase() });
35
+ ws.on("book", (msg) => {
36
+ const data = msg;
37
+ console.log(`${chalk.cyan(new Date().toLocaleTimeString())} ${JSON.stringify(data.data ?? data)}`);
38
+ });
39
+ console.log(chalk.cyan(`Streaming ${symbol.toUpperCase()} orderbook... (Ctrl+C to stop)\n`));
40
+ await new Promise(() => { });
41
+ });
42
+ stream
43
+ .command("trades <symbol>")
44
+ .description("Stream live trades")
45
+ .action(async (symbol) => {
46
+ const ws = new PacificaWSClient({ network: getNetwork() });
47
+ await ws.connect();
48
+ ws.subscribe("trades", { symbol: symbol.toUpperCase() });
49
+ ws.on("trades", (msg) => {
50
+ const data = msg;
51
+ const d = (data.data ?? data);
52
+ const side = String(d.side ?? d.d ?? "");
53
+ const price = String(d.price ?? d.p ?? "");
54
+ const amount = String(d.amount ?? d.a ?? "");
55
+ const color = side === "bid" || side === "buy" ? chalk.green : chalk.red;
56
+ console.log(`${chalk.gray(new Date().toLocaleTimeString())} ${color(side === "bid" ? "BUY " : "SELL")} $${formatUsd(price)} x ${amount}`);
57
+ });
58
+ console.log(chalk.cyan(`Streaming ${symbol.toUpperCase()} trades... (Ctrl+C to stop)\n`));
59
+ await new Promise(() => { });
60
+ });
61
+ stream
62
+ .command("bbo <symbol>")
63
+ .description("Stream best bid/offer")
64
+ .action(async (symbol) => {
65
+ const ws = new PacificaWSClient({ network: getNetwork() });
66
+ await ws.connect();
67
+ ws.subscribe("bbo", { symbol: symbol.toUpperCase() });
68
+ ws.on("bbo", (msg) => {
69
+ const data = msg.data ?? msg;
70
+ console.log(`${chalk.gray(new Date().toLocaleTimeString())} ${JSON.stringify(data)}`);
71
+ });
72
+ console.log(chalk.cyan(`Streaming ${symbol.toUpperCase()} BBO... (Ctrl+C to stop)\n`));
73
+ await new Promise(() => { });
74
+ });
75
+ stream
76
+ .command("candle <symbol> <interval>")
77
+ .description("Stream candlestick data")
78
+ .action(async (symbol, interval) => {
79
+ const ws = new PacificaWSClient({ network: getNetwork() });
80
+ await ws.connect();
81
+ ws.subscribe("candle", { symbol: symbol.toUpperCase(), interval });
82
+ ws.on("candle", (msg) => {
83
+ const data = msg.data ?? msg;
84
+ const d = data;
85
+ console.log(`${chalk.gray(new Date().toLocaleTimeString())} O:$${formatUsd(String(d.o ?? ""))} H:$${formatUsd(String(d.h ?? ""))} L:$${formatUsd(String(d.l ?? ""))} C:$${formatUsd(String(d.c ?? ""))} V:${d.v ?? ""}`);
86
+ });
87
+ console.log(chalk.cyan(`Streaming ${symbol.toUpperCase()} ${interval} candles... (Ctrl+C to stop)\n`));
88
+ await new Promise(() => { });
89
+ });
90
+ // ── stream events ── (unified account events as NDJSON)
91
+ if (getAdapter) {
92
+ stream
93
+ .command("events")
94
+ .description("Stream account events as NDJSON (positions, orders, balance, liquidation warnings)")
95
+ .option("--interval <ms>", "Polling interval in milliseconds", "5000")
96
+ .option("--liq-warn <pct>", "Liquidation warning distance %", "10")
97
+ .option("--log-positions", "Log position lifecycle events to ~/.perp/positions.jsonl")
98
+ .action(async (opts) => {
99
+ const adapter = await getAdapter();
100
+ const { startEventStream } = await import("../event-stream.js");
101
+ console.error(chalk.cyan(`Streaming ${adapter.name} events... (Ctrl+C to stop)\n`));
102
+ if (opts.logPositions) {
103
+ console.error(chalk.gray(" Position logging enabled → ~/.perp/positions.jsonl\n"));
104
+ }
105
+ const controller = new AbortController();
106
+ process.on("SIGINT", () => controller.abort());
107
+ let onEvent = (event) => {
108
+ // NDJSON: one JSON per line to stdout
109
+ process.stdout.write(JSON.stringify(event) + "\n");
110
+ };
111
+ if (opts.logPositions) {
112
+ const { attachPositionLogger } = await import("../position-history.js");
113
+ onEvent = attachPositionLogger(onEvent);
114
+ }
115
+ await startEventStream(adapter, {
116
+ intervalMs: parseInt(opts.interval),
117
+ liquidationWarningPct: parseFloat(opts.liqWarn),
118
+ onEvent,
119
+ signal: controller.signal,
120
+ });
121
+ });
122
+ }
123
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
3
+ export declare function registerTradeCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, isDryRun?: () => boolean): void;