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
package/dist/index.js ADDED
@@ -0,0 +1,458 @@
1
+ #!/usr/bin/env node
2
+ import { config } from "dotenv";
3
+ import { resolve } from "path";
4
+ // Load ~/.perp/.env first (global), then CWD .env (overrides)
5
+ config({ path: resolve(process.env.HOME || "~", ".perp", ".env") });
6
+ config();
7
+ import { Command } from "commander";
8
+ import chalk from "chalk";
9
+ import { loadPrivateKey, parseSolanaKeypair } from "./config.js";
10
+ import { PacificaAdapter } from "./exchanges/pacifica.js";
11
+ import { HyperliquidAdapter } from "./exchanges/hyperliquid.js";
12
+ import { registerMarketCommands } from "./commands/market.js";
13
+ import { registerAccountCommands } from "./commands/account.js";
14
+ import { registerTradeCommands } from "./commands/trade.js";
15
+ import { registerManageCommands } from "./commands/manage.js";
16
+ import { registerStreamCommands } from "./commands/stream.js";
17
+ import { registerArbCommands } from "./commands/arb.js";
18
+ import { registerWalletCommands } from "./commands/wallet.js";
19
+ import { registerBridgeCommands } from "./commands/bridge.js";
20
+ import { registerDepositCommands } from "./commands/deposit.js";
21
+ import { registerAlertCommands } from "./commands/alert.js";
22
+ import { registerArbAutoCommands } from "./commands/arb-auto.js";
23
+ import { registerArbManageCommands } from "./commands/arb-manage.js";
24
+ import { registerGapCommands } from "./commands/gap.js";
25
+ import { registerAgentCommands } from "./commands/agent.js";
26
+ import { registerWithdrawCommands } from "./commands/withdraw.js";
27
+ import { registerRebalanceCommands } from "./commands/rebalance.js";
28
+ import { registerBotCommands } from "./commands/bot.js";
29
+ import { registerHealthCommands } from "./commands/health.js";
30
+ import { registerPortfolioCommands } from "./commands/portfolio.js";
31
+ import { registerRiskCommands } from "./commands/risk.js";
32
+ import { registerHistoryCommands } from "./commands/history.js";
33
+ import { registerAnalyticsCommands } from "./commands/analytics.js";
34
+ import { registerSettingsCommands } from "./commands/settings.js";
35
+ import { registerDexCommands } from "./commands/dex.js";
36
+ import { registerPlanCommands } from "./commands/plan.js";
37
+ import { registerFundingCommands } from "./commands/funding.js";
38
+ import { registerBacktestCommands } from "./commands/backtest.js";
39
+ import { registerDashboardCommands } from "./commands/dashboard.js";
40
+ import { registerInitCommand, EXCHANGE_ENV_MAP, validateKey } from "./commands/init.js";
41
+ import { registerEnvCommands } from "./commands/env.js";
42
+ import { loadSettings, saveSettings } from "./settings.js";
43
+ import { setSharedApiNetwork } from "./shared-api.js";
44
+ const program = new Command();
45
+ // Resolve default exchange from settings (fallback: "pacifica")
46
+ const _settings = loadSettings();
47
+ const _defaultExchange = _settings.defaultExchange || "pacifica";
48
+ program
49
+ .name("perp")
50
+ .description("Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter)")
51
+ .version("0.3.3")
52
+ .option("-e, --exchange <exchange>", `Exchange: pacifica, hyperliquid, lighter (default: ${_defaultExchange})`, _defaultExchange)
53
+ .option("-n, --network <network>", "Network: mainnet or testnet", "mainnet")
54
+ .option("-k, --private-key <key>", "Private key")
55
+ .option("--json", "Output raw JSON (for piping)")
56
+ .option("--dry-run", "Simulate trades without executing (log as simulated)")
57
+ .option("--dex <name>", "HIP-3 deployed perp dex name (Hyperliquid only)")
58
+ .configureOutput({
59
+ writeErr: (str) => {
60
+ if (process.argv.includes("--json")) {
61
+ const msg = str.replace(/^error:\s*/i, "").trim();
62
+ // Use inline envelope (jsonError import not available in sync context)
63
+ console.log(JSON.stringify({
64
+ ok: false,
65
+ error: { code: "CLI_ERROR", message: msg },
66
+ meta: { timestamp: new Date().toISOString() },
67
+ }));
68
+ }
69
+ else {
70
+ process.stderr.write(str);
71
+ }
72
+ },
73
+ });
74
+ let _adapter = null;
75
+ let _pacificaAdapter = null;
76
+ let _hlAdapter = null;
77
+ let _lighterAdapter = null;
78
+ function getExchange() {
79
+ return program.opts().exchange;
80
+ }
81
+ async function getAdapter() {
82
+ if (_adapter)
83
+ return _adapter;
84
+ const opts = program.opts();
85
+ const exchange = opts.exchange;
86
+ const network = opts.network;
87
+ const isTestnet = network === "testnet";
88
+ switch (exchange) {
89
+ case "pacifica": {
90
+ const pk = await loadPrivateKey("pacifica", opts.privateKey);
91
+ const keypair = parseSolanaKeypair(pk);
92
+ const pacNetwork = (isTestnet ? "testnet" : "mainnet");
93
+ const settings = loadSettings();
94
+ const builderCode = process.env.PACIFICA_BUILDER_CODE || settings.referralCodes.pacifica || "PERPCLI";
95
+ _pacificaAdapter = new PacificaAdapter(keypair, pacNetwork, builderCode);
96
+ _adapter = _pacificaAdapter;
97
+ break;
98
+ }
99
+ case "hyperliquid": {
100
+ const pk = await loadPrivateKey("hyperliquid", opts.privateKey);
101
+ _hlAdapter = new HyperliquidAdapter(pk, isTestnet);
102
+ if (opts.dex)
103
+ _hlAdapter.setDex(opts.dex);
104
+ await _hlAdapter.init();
105
+ const hlSettings = loadSettings();
106
+ if (hlSettings.referrals && !hlSettings.referralApplied.hyperliquid) {
107
+ const hlRef = process.env.HL_REFERRAL_CODE || hlSettings.referralCodes.hyperliquid;
108
+ if (hlRef) {
109
+ _hlAdapter.autoSetReferrer(hlRef).then(() => {
110
+ const s = loadSettings();
111
+ s.referralApplied.hyperliquid = true;
112
+ saveSettings(s);
113
+ }).catch(() => {
114
+ // Already referred or API error — mark as done either way
115
+ const s = loadSettings();
116
+ s.referralApplied.hyperliquid = true;
117
+ saveSettings(s);
118
+ });
119
+ }
120
+ }
121
+ _adapter = _hlAdapter;
122
+ break;
123
+ }
124
+ case "lighter": {
125
+ const pk = await loadPrivateKey("lighter", opts.privateKey);
126
+ const { LighterAdapter } = await import("./exchanges/lighter.js");
127
+ _lighterAdapter = new LighterAdapter(pk, isTestnet);
128
+ await _lighterAdapter.init();
129
+ const ltSettings = loadSettings();
130
+ if (ltSettings.referrals && !ltSettings.referralApplied.lighter) {
131
+ const ltRef = process.env.LIGHTER_REFERRAL_CODE || ltSettings.referralCodes.lighter;
132
+ if (ltRef) {
133
+ _lighterAdapter.useReferralCode(ltRef).then(() => {
134
+ const s = loadSettings();
135
+ s.referralApplied.lighter = true;
136
+ saveSettings(s);
137
+ }).catch(() => {
138
+ // Already referred or API error — mark as done either way
139
+ const s = loadSettings();
140
+ s.referralApplied.lighter = true;
141
+ saveSettings(s);
142
+ });
143
+ }
144
+ }
145
+ _adapter = _lighterAdapter;
146
+ break;
147
+ }
148
+ default:
149
+ throw new Error(`Unknown exchange: ${exchange}`);
150
+ }
151
+ return _adapter;
152
+ }
153
+ // Sync wrapper for commands that need adapter (lazy init)
154
+ function getAdapterSync() {
155
+ if (!_adapter)
156
+ throw new Error("Adapter not initialized");
157
+ return _adapter;
158
+ }
159
+ function isJson() {
160
+ return !!program.opts().json;
161
+ }
162
+ function isDryRun() {
163
+ return !!program.opts().dryRun;
164
+ }
165
+ // Helper to get PacificaAdapter specifically (for Pacifica-only commands)
166
+ function getPacificaAdapter() {
167
+ if (!_pacificaAdapter)
168
+ throw new Error("This command requires --exchange pacifica");
169
+ return _pacificaAdapter;
170
+ }
171
+ function getHLAdapter() {
172
+ if (!_hlAdapter)
173
+ throw new Error("This command requires --exchange hyperliquid");
174
+ return _hlAdapter;
175
+ }
176
+ // Register command groups with async adapter getter
177
+ registerMarketCommands(program, getAdapter, isJson);
178
+ registerAccountCommands(program, getAdapter, isJson);
179
+ registerTradeCommands(program, getAdapter, isJson, isDryRun);
180
+ registerManageCommands(program, getAdapter, isJson, getPacificaAdapter);
181
+ registerStreamCommands(program, () => program.opts().network, getExchange, getAdapter);
182
+ registerArbCommands(program, isJson);
183
+ registerWalletCommands(program, isJson);
184
+ registerBridgeCommands(program, isJson);
185
+ registerDepositCommands(program, getAdapter, isJson, () => program.opts().network);
186
+ registerAlertCommands(program, isJson, getAdapterForExchange);
187
+ // Helper to get adapter for a specific exchange (used by arb-auto)
188
+ async function getAdapterForExchange(exchange) {
189
+ const opts = program.opts();
190
+ const network = opts.network;
191
+ const isTestnet = network === "testnet";
192
+ switch (exchange) {
193
+ case "pacifica": {
194
+ if (_pacificaAdapter)
195
+ return _pacificaAdapter;
196
+ const pk = await loadPrivateKey("pacifica", opts.privateKey);
197
+ const keypair = parseSolanaKeypair(pk);
198
+ const pacNetwork = (isTestnet ? "testnet" : "mainnet");
199
+ const s1 = loadSettings();
200
+ const builderCode = process.env.PACIFICA_BUILDER_CODE || s1.referralCodes.pacifica || "PERPCLI";
201
+ _pacificaAdapter = new PacificaAdapter(keypair, pacNetwork, builderCode);
202
+ if (!_adapter)
203
+ _adapter = _pacificaAdapter;
204
+ return _pacificaAdapter;
205
+ }
206
+ case "hyperliquid": {
207
+ if (_hlAdapter)
208
+ return _hlAdapter;
209
+ const pk = await loadPrivateKey("hyperliquid", opts.privateKey);
210
+ _hlAdapter = new HyperliquidAdapter(pk, isTestnet);
211
+ if (opts.dex)
212
+ _hlAdapter.setDex(opts.dex);
213
+ await _hlAdapter.init();
214
+ const s2 = loadSettings();
215
+ if (s2.referrals && !s2.referralApplied.hyperliquid) {
216
+ const hlRef = process.env.HL_REFERRAL_CODE || s2.referralCodes.hyperliquid;
217
+ if (hlRef) {
218
+ _hlAdapter.autoSetReferrer(hlRef).then(() => {
219
+ const s = loadSettings();
220
+ s.referralApplied.hyperliquid = true;
221
+ saveSettings(s);
222
+ }).catch(() => {
223
+ const s = loadSettings();
224
+ s.referralApplied.hyperliquid = true;
225
+ saveSettings(s);
226
+ });
227
+ }
228
+ }
229
+ if (!_adapter)
230
+ _adapter = _hlAdapter;
231
+ return _hlAdapter;
232
+ }
233
+ case "lighter": {
234
+ if (_lighterAdapter)
235
+ return _lighterAdapter;
236
+ const pk = await loadPrivateKey("lighter", opts.privateKey);
237
+ const { LighterAdapter } = await import("./exchanges/lighter.js");
238
+ _lighterAdapter = new LighterAdapter(pk, isTestnet);
239
+ await _lighterAdapter.init();
240
+ const s3 = loadSettings();
241
+ if (s3.referrals && !s3.referralApplied.lighter) {
242
+ const ltRef = process.env.LIGHTER_REFERRAL_CODE || s3.referralCodes.lighter;
243
+ if (ltRef) {
244
+ _lighterAdapter.useReferralCode(ltRef).then(() => {
245
+ const s = loadSettings();
246
+ s.referralApplied.lighter = true;
247
+ saveSettings(s);
248
+ }).catch(() => {
249
+ const s = loadSettings();
250
+ s.referralApplied.lighter = true;
251
+ saveSettings(s);
252
+ });
253
+ }
254
+ }
255
+ if (!_adapter)
256
+ _adapter = _lighterAdapter;
257
+ return _lighterAdapter;
258
+ }
259
+ default:
260
+ throw new Error(`Unknown exchange: ${exchange}`);
261
+ }
262
+ }
263
+ // Helper to get an HL adapter configured for a specific HIP-3 dex
264
+ const _dexAdapters = new Map();
265
+ async function getHLAdapterForDex(dex) {
266
+ if (_dexAdapters.has(dex))
267
+ return _dexAdapters.get(dex);
268
+ const opts = program.opts();
269
+ const pk = await loadPrivateKey("hyperliquid", opts.privateKey);
270
+ const adapter = new HyperliquidAdapter(pk, opts.network === "testnet");
271
+ if (dex !== "hl")
272
+ adapter.setDex(dex);
273
+ await adapter.init();
274
+ _dexAdapters.set(dex, adapter);
275
+ return adapter;
276
+ }
277
+ registerArbAutoCommands(program, getAdapterForExchange, isJson, getHLAdapterForDex);
278
+ registerArbManageCommands(program, getAdapterForExchange, isJson);
279
+ registerGapCommands(program, isJson);
280
+ registerAgentCommands(program, getAdapter, isJson);
281
+ registerWithdrawCommands(program, getAdapter, isJson);
282
+ registerRebalanceCommands(program, getAdapterForExchange, isJson);
283
+ // Jobs & strategies
284
+ import { registerJobsCommands } from "./commands/jobs.js";
285
+ import { registerRunCommands } from "./commands/run.js";
286
+ registerJobsCommands(program, isJson);
287
+ registerRunCommands(program, getAdapter, getAdapterForExchange, isJson);
288
+ registerBotCommands(program, getAdapter, getAdapterForExchange, isJson);
289
+ // Agent-friendly commands
290
+ registerHealthCommands(program, isJson);
291
+ registerPortfolioCommands(program, getAdapterForExchange, isJson);
292
+ registerRiskCommands(program, getAdapterForExchange, isJson);
293
+ registerHistoryCommands(program, isJson);
294
+ registerAnalyticsCommands(program, getAdapterForExchange, isJson);
295
+ registerSettingsCommands(program, isJson);
296
+ registerDexCommands(program, getAdapter, isJson);
297
+ registerPlanCommands(program, getAdapter, isJson);
298
+ registerFundingCommands(program, isJson);
299
+ registerBacktestCommands(program, isJson);
300
+ registerDashboardCommands(program, getAdapterForExchange, isJson, getHLAdapterForDex);
301
+ registerInitCommand(program);
302
+ registerEnvCommands(program, isJson);
303
+ // Agent discovery: perp api-spec — returns full CLI spec as JSON
304
+ program
305
+ .command("api-spec")
306
+ .description("Return full CLI command spec as JSON (for agent discovery)")
307
+ .action(async () => {
308
+ const { jsonOk, printJson } = await import("./utils.js");
309
+ const { getCliSpec } = await import("./cli-spec.js");
310
+ printJson(jsonOk(getCliSpec(program)));
311
+ });
312
+ // Status command
313
+ program
314
+ .command("status")
315
+ .description("Quick overview: account + positions + open orders")
316
+ .action(async () => {
317
+ const adapter = await getAdapter();
318
+ const json = isJson();
319
+ try {
320
+ const [balance, positions, orders] = await Promise.all([
321
+ adapter.getBalance(),
322
+ adapter.getPositions(),
323
+ adapter.getOpenOrders(),
324
+ ]);
325
+ if (json) {
326
+ const { jsonOk, printJson } = await import("./utils.js");
327
+ return printJson(jsonOk({ exchange: adapter.name, balance, positions, orders }));
328
+ }
329
+ console.log(chalk.cyan.bold(`\n ${adapter.name.toUpperCase()} Account Status\n`));
330
+ console.log(` Equity: $${Number(balance.equity).toFixed(2)}`);
331
+ console.log(` Available: $${Number(balance.available).toFixed(2)}`);
332
+ console.log(` Margin Used: $${Number(balance.marginUsed).toFixed(2)}`);
333
+ console.log(` Positions: ${positions.length}`);
334
+ console.log(` Open Orders: ${orders.length}`);
335
+ if (positions.length > 0) {
336
+ console.log(chalk.cyan.bold("\n Positions:"));
337
+ positions.forEach((p) => {
338
+ const color = p.side === "long" ? chalk.green : chalk.red;
339
+ const pnlNum = Number(p.unrealizedPnl);
340
+ const pnlColor = pnlNum >= 0 ? chalk.green : chalk.red;
341
+ console.log(` ${color(p.side.toUpperCase().padEnd(5))} ${chalk.white(p.symbol.padEnd(12))} ${p.size.padEnd(10)} entry: $${Number(p.entryPrice).toFixed(2)} pnl: ${pnlColor(pnlNum >= 0 ? "+" : "")}$${pnlNum.toFixed(2)}`);
342
+ });
343
+ }
344
+ if (orders.length > 0) {
345
+ console.log(chalk.cyan.bold("\n Open Orders:"));
346
+ orders.forEach((o) => {
347
+ const color = o.side === "buy" ? chalk.green : chalk.red;
348
+ console.log(` ${color(o.side.toUpperCase().padEnd(4))} ${chalk.white(o.symbol.padEnd(12))} ${o.type.padEnd(8)} $${Number(o.price).toFixed(2)} x ${o.size}`);
349
+ });
350
+ }
351
+ console.log();
352
+ }
353
+ catch (err) {
354
+ const msg = err instanceof Error ? err.message : String(err);
355
+ if (isJson()) {
356
+ const { jsonError } = await import("./utils.js");
357
+ console.log(JSON.stringify(jsonError("COMMAND_ERROR", msg)));
358
+ }
359
+ else {
360
+ console.error(chalk.red(`Error: ${msg}`));
361
+ }
362
+ process.exit(1);
363
+ }
364
+ });
365
+ // Switch shared API URLs if --network testnet is used
366
+ program.hook("preAction", () => {
367
+ const network = program.opts().network;
368
+ if (network === "testnet")
369
+ setSharedApiNetwork("testnet");
370
+ });
371
+ // Smart landing page: `perp` with no subcommand
372
+ const rawArgs = process.argv.slice(2);
373
+ const hasSubcommand = rawArgs.some((a) => !a.startsWith("-") && !["pacifica", "hyperliquid", "lighter", "mainnet", "testnet"].includes(a));
374
+ if (rawArgs.length === 0 || (!hasSubcommand && !rawArgs.includes("-h") && !rawArgs.includes("--help") && !rawArgs.includes("-V") && !rawArgs.includes("--version"))) {
375
+ // No subcommand — show smart landing instead of help dump
376
+ (async () => {
377
+ try {
378
+ const { getWalletSetupStatus } = await import("./commands/wallet.js");
379
+ const status = getWalletSetupStatus();
380
+ const settings = loadSettings();
381
+ const hasEnvKey = !!(process.env.PRIVATE_KEY || process.env.PACIFICA_PRIVATE_KEY ||
382
+ process.env.HL_PRIVATE_KEY || process.env.HYPERLIQUID_PRIVATE_KEY ||
383
+ process.env.LIGHTER_PRIVATE_KEY);
384
+ if (!status.hasWallets && !hasEnvKey && !settings.defaultExchange) {
385
+ // Fresh install — onboarding
386
+ console.log(chalk.cyan.bold("\n Welcome to perp-cli!") + chalk.gray(" v0.3.3\n"));
387
+ console.log(" Multi-DEX perpetual futures CLI for Pacifica, Hyperliquid, and Lighter.\n");
388
+ console.log(` Get started: ${chalk.cyan("perp init")}`);
389
+ console.log(chalk.gray(`\n Or explore without a wallet:`));
390
+ console.log(` ${chalk.green("perp market list")} available markets`);
391
+ console.log(` ${chalk.green("perp -e hyperliquid market list")} Hyperliquid markets`);
392
+ console.log(` ${chalk.green("perp arb rates")} funding rate comparison`);
393
+ console.log(` ${chalk.green("perp --help")} all commands\n`);
394
+ }
395
+ else {
396
+ // Configured — show status overview
397
+ const defaultEx = settings.defaultExchange || "pacifica";
398
+ const activeEntries = Object.entries(status.active);
399
+ console.log(chalk.cyan.bold("\n perp-cli") + chalk.gray(" v0.3.3\n"));
400
+ console.log(` Default exchange: ${chalk.cyan(defaultEx)}`);
401
+ if (activeEntries.length > 0) {
402
+ console.log(chalk.white.bold("\n Wallets:"));
403
+ for (const [exchange, walletName] of activeEntries) {
404
+ const w = status.wallets[walletName];
405
+ if (w) {
406
+ const addr = w.address.length > 20
407
+ ? w.address.slice(0, 6) + "..." + w.address.slice(-4)
408
+ : w.address;
409
+ console.log(` ${chalk.cyan(exchange.padEnd(14))} ${chalk.white(walletName)} ${chalk.gray(addr)}`);
410
+ }
411
+ }
412
+ }
413
+ else if (hasEnvKey) {
414
+ console.log(chalk.white.bold("\n Configured:"));
415
+ for (const [exchange, info] of Object.entries(EXCHANGE_ENV_MAP)) {
416
+ const key = process.env[info.envKey];
417
+ if (key) {
418
+ try {
419
+ const { valid, address } = await validateKey(info.chain, key);
420
+ const addr = valid ? address : "(invalid key)";
421
+ console.log(` ${chalk.cyan(exchange.padEnd(14))} ${chalk.green(addr)}`);
422
+ }
423
+ catch {
424
+ console.log(` ${chalk.cyan(exchange.padEnd(14))} ${chalk.gray("(error reading key)")}`);
425
+ }
426
+ }
427
+ }
428
+ }
429
+ console.log(chalk.white.bold("\n Quick commands:"));
430
+ console.log(` ${chalk.green("perp status")} account overview`);
431
+ console.log(` ${chalk.green("perp market list")} available markets`);
432
+ console.log(` ${chalk.green("perp dashboard")} live monitoring`);
433
+ console.log(` ${chalk.green("perp arb rates")} funding rate comparison`);
434
+ console.log(` ${chalk.green("perp --help")} all commands\n`);
435
+ }
436
+ }
437
+ catch {
438
+ program.help();
439
+ }
440
+ })();
441
+ }
442
+ else {
443
+ program.parseAsync().then(() => {
444
+ // Allow a short delay for any pending output, then exit cleanly.
445
+ // Without this, HL SDK's WebSocket keeps the process alive indefinitely.
446
+ setTimeout(() => process.exit(0), 500);
447
+ }).catch(async (err) => {
448
+ const msg = err instanceof Error ? err.message : String(err);
449
+ if (isJson()) {
450
+ const { jsonError } = await import("./utils.js");
451
+ console.log(JSON.stringify(jsonError("FATAL", msg)));
452
+ }
453
+ else {
454
+ console.error(chalk.red(msg));
455
+ }
456
+ process.exit(1);
457
+ });
458
+ }
package/dist/jobs.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ export interface JobEntry {
2
+ id: string;
3
+ strategy: string;
4
+ exchange: string;
5
+ params: Record<string, unknown>;
6
+ tmuxSession: string;
7
+ pid?: number;
8
+ startedAt: string;
9
+ status: "running" | "stopped" | "done" | "error";
10
+ result?: Record<string, unknown>;
11
+ }
12
+ export declare function logFile(id: string): string;
13
+ export declare function saveJob(job: JobEntry): void;
14
+ export declare function loadJob(id: string): JobEntry | null;
15
+ export declare function listJobs(): JobEntry[];
16
+ /**
17
+ * Start a job in a tmux background session.
18
+ * Returns the job entry.
19
+ */
20
+ export declare function startJob(opts: {
21
+ strategy: string;
22
+ exchange: string;
23
+ params: Record<string, unknown>;
24
+ cliArgs: string[];
25
+ }): JobEntry;
26
+ /**
27
+ * Stop a running job.
28
+ */
29
+ export declare function stopJob(id: string): boolean;
30
+ /**
31
+ * Update job state file (called from within the running job).
32
+ */
33
+ export declare function updateJobState(id: string, data: Partial<JobEntry>): void;
34
+ /**
35
+ * Remove a job entry.
36
+ */
37
+ export declare function removeJob(id: string): boolean;
package/dist/jobs.js ADDED
@@ -0,0 +1,152 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ import { randomBytes } from "node:crypto";
6
+ const JOBS_DIR = join(homedir(), ".perp", "jobs");
7
+ const LOGS_DIR = join(homedir(), ".perp", "logs");
8
+ function ensureDirs() {
9
+ mkdirSync(JOBS_DIR, { recursive: true });
10
+ mkdirSync(LOGS_DIR, { recursive: true });
11
+ }
12
+ function jobFile(id) {
13
+ return join(JOBS_DIR, `${id}.json`);
14
+ }
15
+ export function logFile(id) {
16
+ return join(LOGS_DIR, `${id}.log`);
17
+ }
18
+ function genId() {
19
+ return randomBytes(4).toString("hex");
20
+ }
21
+ function hasTmux() {
22
+ try {
23
+ execSync("which tmux", { stdio: "ignore" });
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ function tmuxSessionExists(name) {
31
+ try {
32
+ execSync(`tmux has-session -t ${name} 2>/dev/null`, { stdio: "ignore" });
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export function saveJob(job) {
40
+ ensureDirs();
41
+ writeFileSync(jobFile(job.id), JSON.stringify(job, null, 2));
42
+ }
43
+ export function loadJob(id) {
44
+ const path = jobFile(id);
45
+ if (!existsSync(path))
46
+ return null;
47
+ return JSON.parse(readFileSync(path, "utf-8"));
48
+ }
49
+ export function listJobs() {
50
+ ensureDirs();
51
+ const files = readdirSync(JOBS_DIR).filter((f) => f.endsWith(".json"));
52
+ return files.map((f) => {
53
+ const job = JSON.parse(readFileSync(join(JOBS_DIR, f), "utf-8"));
54
+ // Check if tmux session is still alive
55
+ if (job.status === "running" && !tmuxSessionExists(job.tmuxSession)) {
56
+ job.status = "done";
57
+ saveJob(job);
58
+ }
59
+ return job;
60
+ });
61
+ }
62
+ import { readdirSync } from "node:fs";
63
+ /**
64
+ * Start a job in a tmux background session.
65
+ * Returns the job entry.
66
+ */
67
+ export function startJob(opts) {
68
+ if (!hasTmux()) {
69
+ throw new Error("tmux is required for background jobs. Install with: brew install tmux");
70
+ }
71
+ ensureDirs();
72
+ const id = genId();
73
+ const session = `perp-${id}`;
74
+ const log = logFile(id);
75
+ // Build the command — re-invoke ourselves with `run` subcommand
76
+ const nodeCmd = process.argv[0];
77
+ const cliPath = process.argv[1];
78
+ const envVars = buildEnvString();
79
+ const args = opts.cliArgs.join(" ");
80
+ const cmd = `${envVars} ${nodeCmd} ${cliPath} run ${opts.strategy} ${args} --job-id ${id} 2>&1 | tee -a ${log}`;
81
+ // Create tmux session
82
+ execSync(`tmux new-session -d -s ${session} '${cmd.replace(/'/g, "'\\''")}'`);
83
+ const job = {
84
+ id,
85
+ strategy: opts.strategy,
86
+ exchange: opts.exchange,
87
+ params: opts.params,
88
+ tmuxSession: session,
89
+ startedAt: new Date().toISOString(),
90
+ status: "running",
91
+ };
92
+ saveJob(job);
93
+ return job;
94
+ }
95
+ /**
96
+ * Stop a running job.
97
+ */
98
+ export function stopJob(id) {
99
+ const job = loadJob(id);
100
+ if (!job)
101
+ return false;
102
+ if (tmuxSessionExists(job.tmuxSession)) {
103
+ try {
104
+ execSync(`tmux kill-session -t ${job.tmuxSession}`);
105
+ }
106
+ catch { /* already dead */ }
107
+ }
108
+ job.status = "stopped";
109
+ saveJob(job);
110
+ return true;
111
+ }
112
+ /**
113
+ * Update job state file (called from within the running job).
114
+ */
115
+ export function updateJobState(id, data) {
116
+ const job = loadJob(id);
117
+ if (!job)
118
+ return;
119
+ Object.assign(job, data);
120
+ saveJob(job);
121
+ }
122
+ /**
123
+ * Remove a job entry.
124
+ */
125
+ export function removeJob(id) {
126
+ const path = jobFile(id);
127
+ if (!existsSync(path))
128
+ return false;
129
+ unlinkSync(path);
130
+ // Also remove log
131
+ const log = logFile(id);
132
+ if (existsSync(log))
133
+ unlinkSync(log);
134
+ return true;
135
+ }
136
+ /**
137
+ * Build env string to pass through to tmux session.
138
+ */
139
+ function buildEnvString() {
140
+ const keys = [
141
+ "LIGHTER_PRIVATE_KEY", "LIGHTER_API_KEY", "LIGHTER_ACCOUNT_INDEX", "LIGHTER_API_KEY_INDEX",
142
+ "PRIVATE_KEY", "pk",
143
+ "PACIFICA_BUILDER_CODE", "NEXT_PUBLIC_BUILDER_CODE",
144
+ "HL_REFERRAL_CODE", "LIGHTER_REFERRAL_CODE",
145
+ ];
146
+ const parts = [];
147
+ for (const k of keys) {
148
+ if (process.env[k])
149
+ parts.push(`${k}='${process.env[k]}'`);
150
+ }
151
+ return parts.join(" ");
152
+ }
@@ -0,0 +1,34 @@
1
+ import type { ExchangeAdapter } from "./exchanges/interface.js";
2
+ export interface LiquidityCheck {
3
+ /** Max executable size (base) within slippage tolerance */
4
+ maxSize: number;
5
+ /** Estimated avg fill price for the given size */
6
+ avgFillPrice: number;
7
+ /** Estimated slippage % vs mid price */
8
+ slippagePct: number;
9
+ /** Total depth in USD on the relevant side */
10
+ depthUsd: number;
11
+ /** Whether the full requested size can be filled */
12
+ canFillFull: boolean;
13
+ /** Recommended size (capped by liquidity) */
14
+ recommendedSize: number;
15
+ }
16
+ /**
17
+ * Walk through orderbook levels and compute how much can be filled
18
+ * within the given slippage tolerance.
19
+ *
20
+ * @param levels - [[price, size], ...] from getOrderbook (asks for buy, bids for sell)
21
+ * @param maxSlippagePct - max acceptable slippage from best price (default 0.5%)
22
+ * @param requestedSizeUsd - desired position size in USD
23
+ */
24
+ export declare function computeExecutableSize(levels: [string, string][], requestedSizeUsd: number, maxSlippagePct?: number): LiquidityCheck;
25
+ /**
26
+ * Check liquidity on both sides for an arb entry.
27
+ * Returns adjusted size or 0 if not viable.
28
+ */
29
+ export declare function checkArbLiquidity(longAdapter: ExchangeAdapter, shortAdapter: ExchangeAdapter, symbol: string, sizeUsd: number, maxSlippagePct?: number, log?: (msg: string) => void): Promise<{
30
+ viable: boolean;
31
+ adjustedSizeUsd: number;
32
+ longSlippage: number;
33
+ shortSlippage: number;
34
+ }>;