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,32 @@
1
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { Condition } from "./config.js";
3
+ export interface MarketSnapshot {
4
+ price: number;
5
+ high24h: number;
6
+ low24h: number;
7
+ volume24h: number;
8
+ fundingRate: number;
9
+ volatility24h: number;
10
+ rsi: number;
11
+ spreadPct: number;
12
+ }
13
+ /**
14
+ * Calculate RSI from an array of closing prices using Wilder's smoothing method.
15
+ * @param closes Array of closing prices, oldest first.
16
+ * @param period RSI period (default 14).
17
+ * @returns RSI value between 0 and 100, or NaN if insufficient data.
18
+ */
19
+ export declare function calculateRSI(closes: number[], period?: number): number;
20
+ export declare function getMarketSnapshot(adapter: ExchangeAdapter, symbol: string): Promise<MarketSnapshot>;
21
+ export declare function evaluateCondition(condition: Condition, snapshot: MarketSnapshot, context: {
22
+ equity: number;
23
+ startTime: number;
24
+ peakEquity: number;
25
+ dailyPnl: number;
26
+ }): boolean;
27
+ export declare function evaluateAllConditions(conditions: Condition[], snapshot: MarketSnapshot, context: {
28
+ equity: number;
29
+ startTime: number;
30
+ peakEquity: number;
31
+ dailyPnl: number;
32
+ }, mode?: "all" | "any"): boolean;
@@ -0,0 +1,141 @@
1
+ // ── RSI Calculation (Wilder smoothing) ──
2
+ /**
3
+ * Calculate RSI from an array of closing prices using Wilder's smoothing method.
4
+ * @param closes Array of closing prices, oldest first.
5
+ * @param period RSI period (default 14).
6
+ * @returns RSI value between 0 and 100, or NaN if insufficient data.
7
+ */
8
+ export function calculateRSI(closes, period = 14) {
9
+ if (closes.length < period + 1)
10
+ return NaN;
11
+ // Calculate price changes (deltas)
12
+ const deltas = [];
13
+ for (let i = 1; i < closes.length; i++) {
14
+ deltas.push(closes[i] - closes[i - 1]);
15
+ }
16
+ // Separate gains and losses
17
+ const gains = deltas.map(d => (d > 0 ? d : 0));
18
+ const losses = deltas.map(d => (d < 0 ? -d : 0));
19
+ // First average: simple mean of first `period` values
20
+ let avgGain = 0;
21
+ let avgLoss = 0;
22
+ for (let i = 0; i < period; i++) {
23
+ avgGain += gains[i];
24
+ avgLoss += losses[i];
25
+ }
26
+ avgGain /= period;
27
+ avgLoss /= period;
28
+ // Subsequent averages: Wilder smoothing
29
+ for (let i = period; i < deltas.length; i++) {
30
+ avgGain = (avgGain * (period - 1) + gains[i]) / period;
31
+ avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
32
+ }
33
+ // Handle edge case: no losses at all
34
+ if (avgLoss === 0) {
35
+ return avgGain === 0 ? 50 : 100;
36
+ }
37
+ const rs = avgGain / avgLoss;
38
+ return 100 - 100 / (1 + rs);
39
+ }
40
+ export async function getMarketSnapshot(adapter, symbol) {
41
+ const markets = await adapter.getMarkets();
42
+ const m = markets.find(mk => mk.symbol.toUpperCase() === symbol.toUpperCase());
43
+ if (!m) {
44
+ return { price: 0, high24h: 0, low24h: 0, volume24h: 0, fundingRate: 0, volatility24h: 0, rsi: NaN, spreadPct: 0 };
45
+ }
46
+ const price = parseFloat(m.markPrice);
47
+ const fundingRate = parseFloat(m.fundingRate);
48
+ const volume24h = parseFloat(m.volume24h);
49
+ // Try to get kline data for 24h high/low + RSI
50
+ let high24h = price;
51
+ let low24h = price;
52
+ let rsi = NaN;
53
+ try {
54
+ const now = Date.now();
55
+ // Fetch enough candles for RSI (need period+1 candles minimum; fetch 48h for safety)
56
+ const klines = await adapter.getKlines(symbol, "1h", now - 48 * 3600 * 1000, now);
57
+ if (klines.length > 0) {
58
+ // Sort by time ascending
59
+ klines.sort((a, b) => a.time - b.time);
60
+ // 24h high/low: use only the last 24 candles
61
+ const last24h = klines.filter(k => k.time >= now - 24 * 3600 * 1000);
62
+ if (last24h.length > 0) {
63
+ high24h = Math.max(...last24h.map(k => parseFloat(k.high)));
64
+ low24h = Math.min(...last24h.map(k => parseFloat(k.low)));
65
+ }
66
+ // RSI from all available close prices
67
+ const closes = klines.map(k => parseFloat(k.close));
68
+ rsi = calculateRSI(closes, 14);
69
+ }
70
+ }
71
+ catch {
72
+ // non-critical
73
+ }
74
+ // Calculate bid-ask spread from orderbook
75
+ let spreadPct = 0;
76
+ try {
77
+ const ob = await adapter.getOrderbook(symbol);
78
+ if (ob.bids.length > 0 && ob.asks.length > 0) {
79
+ const bestBid = parseFloat(ob.bids[0][0]);
80
+ const bestAsk = parseFloat(ob.asks[0][0]);
81
+ const midPrice = (bestBid + bestAsk) / 2;
82
+ if (midPrice > 0) {
83
+ spreadPct = ((bestAsk - bestBid) / midPrice) * 100;
84
+ }
85
+ }
86
+ }
87
+ catch {
88
+ // non-critical
89
+ }
90
+ const volatility24h = price > 0 ? ((high24h - low24h) / price) * 100 : 0;
91
+ return { price, high24h, low24h, volume24h, fundingRate, volatility24h, rsi, spreadPct };
92
+ }
93
+ export function evaluateCondition(condition, snapshot, context) {
94
+ const val = typeof condition.value === "number" ? condition.value : parseFloat(String(condition.value));
95
+ switch (condition.type) {
96
+ case "always":
97
+ return true;
98
+ case "price_above":
99
+ return snapshot.price > val;
100
+ case "price_below":
101
+ return snapshot.price < val;
102
+ case "volatility_above":
103
+ return snapshot.volatility24h > val;
104
+ case "volatility_below":
105
+ return snapshot.volatility24h < val;
106
+ case "funding_rate_above":
107
+ return snapshot.fundingRate > val;
108
+ case "funding_rate_below":
109
+ return snapshot.fundingRate < val;
110
+ case "rsi_above":
111
+ // RSI was pre-computed in the snapshot from 1h kline data.
112
+ // If RSI is NaN (insufficient data), the condition is not met.
113
+ return !isNaN(snapshot.rsi) && snapshot.rsi > val;
114
+ case "rsi_below":
115
+ return !isNaN(snapshot.rsi) && snapshot.rsi < val;
116
+ case "balance_above":
117
+ return context.equity > val;
118
+ case "balance_below":
119
+ return context.equity < val;
120
+ case "spread_above":
121
+ // Bid-ask spread percentage, pre-computed from orderbook in snapshot.
122
+ return snapshot.spreadPct > val;
123
+ case "time_after": {
124
+ const elapsed = (Date.now() - context.startTime) / 1000;
125
+ return elapsed > val;
126
+ }
127
+ // Custom exit conditions (not standard ConditionType but used in exit logic)
128
+ case "max_drawdown":
129
+ return (context.peakEquity - context.equity) > val;
130
+ default:
131
+ return false;
132
+ }
133
+ }
134
+ export function evaluateAllConditions(conditions, snapshot, context, mode = "all") {
135
+ if (conditions.length === 0)
136
+ return true;
137
+ if (mode === "all") {
138
+ return conditions.every(c => evaluateCondition(c, snapshot, context));
139
+ }
140
+ return conditions.some(c => evaluateCondition(c, snapshot, context));
141
+ }
@@ -0,0 +1,76 @@
1
+ export type ConditionType = "price_above" | "price_below" | "volatility_above" | "volatility_below" | "funding_rate_above" | "funding_rate_below" | "spread_above" | "rsi_above" | "rsi_below" | "balance_above" | "balance_below" | "time_after" | "always";
2
+ export interface Condition {
3
+ type: ConditionType;
4
+ value: number | string;
5
+ symbol?: string;
6
+ exchange?: string;
7
+ }
8
+ export interface RiskConfig {
9
+ max_position_usd: number;
10
+ max_daily_loss: number;
11
+ max_drawdown: number;
12
+ pause_after_loss_sec: number;
13
+ max_open_bots: number;
14
+ }
15
+ export interface GridStrategyParams {
16
+ type: "grid";
17
+ grids: number;
18
+ size: number;
19
+ side: "long" | "short" | "neutral";
20
+ range_mode: "auto" | "fixed";
21
+ range_pct?: number;
22
+ upper?: number;
23
+ lower?: number;
24
+ rebalance: boolean;
25
+ rebalance_cooldown: number;
26
+ leverage?: number;
27
+ }
28
+ export interface DCAStrategyParams {
29
+ type: "dca";
30
+ amount: number;
31
+ interval_sec: number;
32
+ total_orders: number;
33
+ price_limit?: number;
34
+ }
35
+ export interface FundingArbStrategyParams {
36
+ type: "funding-arb";
37
+ min_spread: number;
38
+ close_spread: number;
39
+ size_usd: number;
40
+ max_positions: number;
41
+ exchanges: string[];
42
+ }
43
+ export type StrategyParams = GridStrategyParams | DCAStrategyParams | FundingArbStrategyParams;
44
+ export interface BotConfig {
45
+ name: string;
46
+ exchange: string;
47
+ symbol: string;
48
+ strategy: StrategyParams;
49
+ entry_conditions: Condition[];
50
+ exit_conditions: Condition[];
51
+ risk: RiskConfig;
52
+ monitor_interval_sec: number;
53
+ }
54
+ export declare function loadBotConfig(pathOrInline: string): BotConfig;
55
+ export declare function quickGridConfig(opts: {
56
+ exchange: string;
57
+ symbol: string;
58
+ rangePct: number;
59
+ grids: number;
60
+ size: number;
61
+ side: string;
62
+ maxDrawdown: number;
63
+ maxRuntime?: number;
64
+ leverage?: number;
65
+ }): BotConfig;
66
+ export declare function quickDCAConfig(opts: {
67
+ exchange: string;
68
+ symbol: string;
69
+ side: string;
70
+ amount: number;
71
+ intervalSec: number;
72
+ orders: number;
73
+ triggerDrop?: number;
74
+ priceLimit?: number;
75
+ maxDrawdown: number;
76
+ }): BotConfig;
@@ -0,0 +1,160 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { parse as parseYaml } from "yaml";
3
+ // ── Defaults ──
4
+ const DEFAULT_RISK = {
5
+ max_position_usd: 1000,
6
+ max_daily_loss: 100,
7
+ max_drawdown: 200,
8
+ pause_after_loss_sec: 300,
9
+ max_open_bots: 5,
10
+ };
11
+ // ── Loader ──
12
+ export function loadBotConfig(pathOrInline) {
13
+ let raw;
14
+ if (existsSync(pathOrInline)) {
15
+ const content = readFileSync(pathOrInline, "utf-8");
16
+ if (pathOrInline.endsWith(".json")) {
17
+ raw = JSON.parse(content);
18
+ }
19
+ else {
20
+ raw = parseYaml(content);
21
+ }
22
+ }
23
+ else {
24
+ // Try parsing as inline JSON
25
+ raw = JSON.parse(pathOrInline);
26
+ }
27
+ return parseBotConfig(raw);
28
+ }
29
+ function parseBotConfig(raw) {
30
+ const name = String(raw.name ?? `bot-${Date.now()}`);
31
+ const exchange = String(raw.exchange ?? "hyperliquid");
32
+ const symbol = String(raw.symbol ?? "ETH");
33
+ const monitorInterval = Number(raw.monitor_interval_sec ?? raw.interval ?? 10);
34
+ // Strategy
35
+ const stratRaw = (raw.strategy ?? raw.params ?? {});
36
+ const stratType = String(stratRaw.type ?? raw.strategy_type ?? "grid");
37
+ const strategy = parseStrategy(stratType, stratRaw);
38
+ // Conditions
39
+ const entryConds = parseConditions(raw.entry_conditions);
40
+ const exitConds = parseConditions(raw.exit_conditions);
41
+ // If no entry conditions, default to "always"
42
+ if (entryConds.length === 0) {
43
+ entryConds.push({ type: "always", value: 0 });
44
+ }
45
+ // Risk
46
+ const riskRaw = (raw.risk ?? {});
47
+ const risk = {
48
+ max_position_usd: Number(riskRaw.max_position_usd ?? DEFAULT_RISK.max_position_usd),
49
+ max_daily_loss: Number(riskRaw.max_daily_loss ?? DEFAULT_RISK.max_daily_loss),
50
+ max_drawdown: Number(riskRaw.max_drawdown ?? DEFAULT_RISK.max_drawdown),
51
+ pause_after_loss_sec: Number(riskRaw.pause_after_loss_sec ?? riskRaw.pause_after_loss ?? DEFAULT_RISK.pause_after_loss_sec),
52
+ max_open_bots: Number(riskRaw.max_open_bots ?? DEFAULT_RISK.max_open_bots),
53
+ };
54
+ return { name, exchange, symbol, strategy, entry_conditions: entryConds, exit_conditions: exitConds, risk, monitor_interval_sec: monitorInterval };
55
+ }
56
+ function parseStrategy(type, raw) {
57
+ switch (type) {
58
+ case "grid":
59
+ return {
60
+ type: "grid",
61
+ grids: Number(raw.grids ?? 10),
62
+ size: Number(raw.size ?? 0.1),
63
+ side: (String(raw.side ?? "neutral")),
64
+ range_mode: String(raw.range_mode ?? "auto"),
65
+ range_pct: raw.range_pct !== undefined ? Number(raw.range_pct) : 3,
66
+ upper: raw.upper !== undefined ? Number(raw.upper) : undefined,
67
+ lower: raw.lower !== undefined ? Number(raw.lower) : undefined,
68
+ rebalance: raw.rebalance !== false,
69
+ rebalance_cooldown: Number(raw.rebalance_cooldown ?? 60),
70
+ leverage: raw.leverage !== undefined ? Number(raw.leverage) : undefined,
71
+ };
72
+ case "dca":
73
+ return {
74
+ type: "dca",
75
+ amount: Number(raw.amount ?? 0.01),
76
+ interval_sec: Number(raw.interval_sec ?? raw.interval ?? 60),
77
+ total_orders: Number(raw.total_orders ?? raw.orders ?? 0),
78
+ price_limit: raw.price_limit !== undefined ? Number(raw.price_limit) : undefined,
79
+ };
80
+ case "funding-arb":
81
+ return {
82
+ type: "funding-arb",
83
+ min_spread: Number(raw.min_spread ?? 20),
84
+ close_spread: Number(raw.close_spread ?? 5),
85
+ size_usd: Number(raw.size_usd ?? raw.size ?? 50),
86
+ max_positions: Number(raw.max_positions ?? 3),
87
+ exchanges: Array.isArray(raw.exchanges)
88
+ ? raw.exchanges.map(String)
89
+ : String(raw.exchanges ?? "pacifica,hyperliquid").split(",").map(s => s.trim()),
90
+ };
91
+ default:
92
+ throw new Error(`Unknown strategy type: ${type}`);
93
+ }
94
+ }
95
+ function parseConditions(raw) {
96
+ if (!raw || !Array.isArray(raw))
97
+ return [];
98
+ return raw.map((c) => {
99
+ const cond = c;
100
+ return {
101
+ type: String(cond.type ?? "always"),
102
+ value: cond.value !== undefined ? (typeof cond.value === "number" ? cond.value : String(cond.value)) : 0,
103
+ symbol: cond.symbol ? String(cond.symbol) : undefined,
104
+ exchange: cond.exchange ? String(cond.exchange) : undefined,
105
+ };
106
+ });
107
+ }
108
+ // ── Quick config builders ──
109
+ export function quickGridConfig(opts) {
110
+ return {
111
+ name: `grid-${opts.symbol.toLowerCase()}-${Date.now().toString(36)}`,
112
+ exchange: opts.exchange,
113
+ symbol: opts.symbol,
114
+ strategy: {
115
+ type: "grid",
116
+ grids: opts.grids,
117
+ size: opts.size,
118
+ side: opts.side,
119
+ range_mode: "auto",
120
+ range_pct: opts.rangePct,
121
+ rebalance: true,
122
+ rebalance_cooldown: 60,
123
+ leverage: opts.leverage,
124
+ },
125
+ entry_conditions: [{ type: "always", value: 0 }],
126
+ exit_conditions: [
127
+ { type: "max_drawdown", value: opts.maxDrawdown },
128
+ ...(opts.maxRuntime ? [{ type: "time_after", value: opts.maxRuntime }] : []),
129
+ ],
130
+ risk: { ...DEFAULT_RISK, max_drawdown: opts.maxDrawdown },
131
+ monitor_interval_sec: 10,
132
+ };
133
+ }
134
+ export function quickDCAConfig(opts) {
135
+ const entryConds = [];
136
+ if (opts.triggerDrop) {
137
+ entryConds.push({ type: "price_below", value: opts.triggerDrop });
138
+ }
139
+ else {
140
+ entryConds.push({ type: "always", value: 0 });
141
+ }
142
+ return {
143
+ name: `dca-${opts.symbol.toLowerCase()}-${Date.now().toString(36)}`,
144
+ exchange: opts.exchange,
145
+ symbol: opts.symbol,
146
+ strategy: {
147
+ type: "dca",
148
+ amount: opts.amount,
149
+ interval_sec: opts.intervalSec,
150
+ total_orders: opts.orders,
151
+ price_limit: opts.priceLimit,
152
+ },
153
+ entry_conditions: entryConds,
154
+ exit_conditions: [
155
+ { type: "max_drawdown", value: opts.maxDrawdown },
156
+ ],
157
+ risk: { ...DEFAULT_RISK, max_drawdown: opts.maxDrawdown },
158
+ monitor_interval_sec: opts.intervalSec,
159
+ };
160
+ }
@@ -0,0 +1,8 @@
1
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { BotConfig } from "./config.js";
3
+ export type BotLog = (msg: string) => void;
4
+ export declare function runBot(adapter: ExchangeAdapter, config: BotConfig, jobId?: string, log?: BotLog, extraAdapters?: Map<string, ExchangeAdapter>): Promise<{
5
+ fills: number;
6
+ totalPnl: number;
7
+ runtime: number;
8
+ }>;