superacli 1.1.0 → 1.1.2

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 (261) hide show
  1. package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +57 -0
  2. package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +57 -0
  3. package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +57 -0
  4. package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +57 -0
  5. package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +57 -0
  6. package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +57 -0
  7. package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +57 -0
  8. package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +57 -0
  9. package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +57 -0
  10. package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +57 -0
  11. package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +57 -0
  12. package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +57 -0
  13. package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +57 -0
  14. package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +57 -0
  15. package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +57 -0
  16. package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +57 -0
  17. package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +57 -0
  18. package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +57 -0
  19. package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +57 -0
  20. package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +57 -0
  21. package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +57 -0
  22. package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +58 -0
  23. package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +59 -0
  24. package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +60 -0
  25. package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +60 -0
  26. package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +60 -0
  27. package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +60 -0
  28. package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +60 -0
  29. package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +60 -0
  30. package/.beads/issues.jsonl +20 -17
  31. package/README.md +1 -1
  32. package/__tests__/adapter-schema.test.js +3 -0
  33. package/__tests__/aws-plugin.test.js +84 -0
  34. package/__tests__/az-plugin.test.js +84 -0
  35. package/__tests__/builtin-adapter.test.js +29 -0
  36. package/__tests__/cline-plugin.test.js +109 -0
  37. package/__tests__/cline-skill.test.js +49 -0
  38. package/__tests__/docker-plugin.test.js +3 -1
  39. package/__tests__/eza-plugin.test.js +81 -0
  40. package/__tests__/gcloud-plugin.test.js +86 -0
  41. package/__tests__/gh-plugin.test.js +86 -0
  42. package/__tests__/helm-plugin.test.js +81 -0
  43. package/__tests__/http-adapter.test.js +118 -0
  44. package/__tests__/just-plugin.test.js +82 -0
  45. package/__tests__/kubectl-plugin.test.js +83 -0
  46. package/__tests__/linear-plugin.test.js +81 -0
  47. package/__tests__/mcp-adapter.test.js +187 -0
  48. package/__tests__/nextest-plugin.test.js +82 -0
  49. package/__tests__/npm-plugin.test.js +81 -0
  50. package/__tests__/nullclaw-plugin.test.js +157 -0
  51. package/__tests__/openapi-adapter.test.js +199 -0
  52. package/__tests__/plugin-agency-agents.test.js +6 -6
  53. package/__tests__/plugin-nullclaw.test.js +78 -0
  54. package/__tests__/plugin-visual-explainer.test.js +62 -0
  55. package/__tests__/plugins-manager.test.js +59 -10
  56. package/__tests__/pnpm-plugin.test.js +81 -0
  57. package/__tests__/poetry-plugin.test.js +83 -0
  58. package/__tests__/process-adapter.test.js +124 -90
  59. package/__tests__/pulumi-plugin.test.js +81 -0
  60. package/__tests__/railway-plugin.test.js +84 -0
  61. package/__tests__/server-app.test.js +67 -0
  62. package/__tests__/server-config-service.test.js +79 -0
  63. package/__tests__/server-routes-ask.test.js +89 -0
  64. package/__tests__/server-routes-commands.test.js +55 -0
  65. package/__tests__/server-routes-config.test.js +87 -0
  66. package/__tests__/server-routes-jobs.test.js +53 -0
  67. package/__tests__/server-routes-misc.test.js +112 -0
  68. package/__tests__/server-storage-adapter.test.js +40 -0
  69. package/__tests__/server-storage-file.test.js +73 -0
  70. package/__tests__/server-storage-mongo.test.js +74 -0
  71. package/__tests__/shell-adapter.test.js +81 -22
  72. package/__tests__/stripe-plugin.test.js +3 -1
  73. package/__tests__/supabase-plugin.test.js +86 -0
  74. package/__tests__/terraform-plugin.test.js +83 -0
  75. package/__tests__/uv-plugin.test.js +81 -0
  76. package/__tests__/vercel-plugin.test.js +81 -0
  77. package/__tests__/watchexec-plugin.test.js +80 -0
  78. package/cli/adapter-schema.js +5 -0
  79. package/cli/adapters/process.js +53 -2
  80. package/cli/plugin-install-guidance.js +320 -0
  81. package/cli/plugins-manager.js +272 -212
  82. package/cli/skills.js +16 -5
  83. package/cli/supercli.js +26 -2
  84. package/docs/plugins.md +2 -0
  85. package/docs/skills/cline-non-interactive/SKILL.md +59 -0
  86. package/docs/skills-catalog.md +35 -0
  87. package/docs/visual-overview.md +21 -0
  88. package/jest.config.js +4 -1
  89. package/package.json +4 -3
  90. package/plugins/agency-agents/plugin.json +5 -0
  91. package/{cli/plugin-agency-agents.js → plugins/agency-agents/scripts/post-install.js} +13 -3
  92. package/plugins/aws/README.md +46 -0
  93. package/plugins/aws/plugin.json +42 -0
  94. package/plugins/az/README.md +46 -0
  95. package/plugins/az/plugin.json +42 -0
  96. package/plugins/clickup/plugin.json +38 -0
  97. package/plugins/clickup/scripts/post-install.js +107 -0
  98. package/plugins/clickup/scripts/post-uninstall.js +30 -0
  99. package/plugins/cline/README.md +48 -0
  100. package/plugins/cline/plugin.json +92 -0
  101. package/plugins/eza/README.md +40 -0
  102. package/plugins/eza/plugin.json +42 -0
  103. package/plugins/gcloud/README.md +46 -0
  104. package/plugins/gcloud/plugin.json +42 -0
  105. package/plugins/gh/README.md +46 -0
  106. package/plugins/gh/plugin.json +43 -0
  107. package/plugins/helm/README.md +42 -0
  108. package/plugins/helm/plugin.json +42 -0
  109. package/plugins/just/README.md +42 -0
  110. package/plugins/just/plugin.json +42 -0
  111. package/plugins/kubectl/README.md +46 -0
  112. package/plugins/kubectl/plugin.json +42 -0
  113. package/plugins/linear/README.md +60 -0
  114. package/plugins/linear/plugin.json +42 -0
  115. package/plugins/nextest/README.md +42 -0
  116. package/plugins/nextest/plugin.json +42 -0
  117. package/plugins/npm/README.md +46 -0
  118. package/plugins/npm/plugin.json +42 -0
  119. package/plugins/nullclaw/README.md +45 -0
  120. package/plugins/nullclaw/plugin.json +64 -0
  121. package/plugins/nullclaw/scripts/post-install.js +189 -0
  122. package/plugins/nullclaw/scripts/post-uninstall.js +25 -0
  123. package/plugins/openfang/plugin.json +37 -0
  124. package/plugins/openfang/scripts/post-install.js +163 -0
  125. package/plugins/openfang/scripts/post-uninstall.js +30 -0
  126. package/plugins/plugins.json +234 -0
  127. package/plugins/pnpm/README.md +46 -0
  128. package/plugins/pnpm/plugin.json +42 -0
  129. package/plugins/poetry/README.md +46 -0
  130. package/plugins/poetry/plugin.json +42 -0
  131. package/plugins/pulumi/README.md +46 -0
  132. package/plugins/pulumi/plugin.json +42 -0
  133. package/plugins/railway/README.md +58 -0
  134. package/plugins/railway/plugin.json +43 -0
  135. package/plugins/supabase/README.md +55 -0
  136. package/plugins/supabase/plugin.json +42 -0
  137. package/plugins/superpowers/plugin.json +22 -0
  138. package/plugins/superpowers/scripts/post-install.js +124 -0
  139. package/plugins/superpowers/scripts/post-uninstall.js +30 -0
  140. package/plugins/terraform/README.md +46 -0
  141. package/plugins/terraform/plugin.json +42 -0
  142. package/plugins/uv/README.md +46 -0
  143. package/plugins/uv/plugin.json +42 -0
  144. package/plugins/vercel/README.md +47 -0
  145. package/plugins/vercel/plugin.json +42 -0
  146. package/plugins/visual-explainer/plugin.json +15 -0
  147. package/plugins/visual-explainer/scripts/post-install.js +111 -0
  148. package/plugins/watchexec/README.md +40 -0
  149. package/plugins/watchexec/plugin.json +42 -0
  150. package/tests/test-aws-smoke.sh +56 -0
  151. package/tests/test-az-smoke.sh +56 -0
  152. package/tests/test-cline-smoke.sh +37 -0
  153. package/tests/test-eza-smoke.sh +33 -0
  154. package/tests/test-gcloud-smoke.sh +56 -0
  155. package/tests/test-gh-smoke.sh +56 -0
  156. package/tests/test-helm-smoke.sh +33 -0
  157. package/tests/test-just-smoke.sh +40 -0
  158. package/tests/test-kubectl-smoke.sh +37 -0
  159. package/tests/test-linear-smoke.sh +97 -0
  160. package/tests/test-nextest-smoke.sh +33 -0
  161. package/tests/test-npm-smoke.sh +32 -0
  162. package/tests/test-nullclaw-smoke.sh +51 -0
  163. package/tests/test-plugins-registry.js +110 -0
  164. package/tests/test-pnpm-smoke.sh +33 -0
  165. package/tests/test-poetry-smoke.sh +33 -0
  166. package/tests/test-pulumi-smoke.sh +33 -0
  167. package/tests/test-railway-smoke.sh +95 -0
  168. package/tests/test-supabase-smoke.sh +95 -0
  169. package/tests/test-terraform-smoke.sh +33 -0
  170. package/tests/test-uv-smoke.sh +33 -0
  171. package/tests/test-vercel-smoke.sh +55 -0
  172. package/tests/test-watchexec-smoke.sh +33 -0
  173. package/.beads/.br_history/issues.20260308_180927_477542428.jsonl +0 -12
  174. package/.beads/.br_history/issues.20260308_181032_020230108.jsonl +0 -12
  175. package/.beads/.br_history/issues.20260308_181032_180539413.jsonl +0 -12
  176. package/.beads/.br_history/issues.20260308_181032_372621506.jsonl +0 -12
  177. package/.beads/.br_history/issues.20260308_181032_565142225.jsonl +0 -12
  178. package/.beads/.br_history/issues.20260308_181311_336346464.jsonl +0 -12
  179. package/.beads/.br_history/issues.20260308_181444_039234498.jsonl +0 -13
  180. package/.beads/.br_history/issues.20260308_181503_794764403.jsonl +0 -13
  181. package/.beads/.br_history/issues.20260308_181503_950163105.jsonl +0 -13
  182. package/.beads/.br_history/issues.20260308_192031_852553505.jsonl +0 -13
  183. package/.beads/.br_history/issues.20260308_193552_846920518.jsonl +0 -14
  184. package/.beads/.br_history/issues.20260308_194054_394884833.jsonl +0 -14
  185. package/.beads/.br_history/issues.20260308_194209_440472460.jsonl +0 -15
  186. package/.beads/.br_history/issues.20260308_195319_099391899.jsonl +0 -15
  187. package/.beads/.br_history/issues.20260308_195324_176987204.jsonl +0 -16
  188. package/.beads/.br_history/issues.20260308_195436_929114019.jsonl +0 -16
  189. package/.beads/.br_history/issues.20260308_195437_055808298.jsonl +0 -17
  190. package/.beads/.br_history/issues.20260308_195437_304297399.jsonl +0 -18
  191. package/.beads/.br_history/issues.20260308_195437_556007332.jsonl +0 -19
  192. package/.beads/.br_history/issues.20260308_195444_987209695.jsonl +0 -20
  193. package/.beads/.br_history/issues.20260308_195445_133350193.jsonl +0 -20
  194. package/.beads/.br_history/issues.20260308_195445_400185615.jsonl +0 -20
  195. package/.beads/.br_history/issues.20260308_195445_689886334.jsonl +0 -20
  196. package/.beads/.br_history/issues.20260308_195445_949947727.jsonl +0 -20
  197. package/.beads/.br_history/issues.20260308_195745_580473297.jsonl +0 -20
  198. package/.beads/.br_history/issues.20260308_195745_725920532.jsonl +0 -20
  199. package/.beads/.br_history/issues.20260308_195745_968227911.jsonl +0 -20
  200. package/.beads/.br_history/issues.20260308_195746_224276322.jsonl +0 -20
  201. package/.beads/.br_history/issues.20260308_200018_386890807.jsonl +0 -20
  202. package/ref-btcbot/.env.example +0 -19
  203. package/ref-btcbot/README.md +0 -3
  204. package/ref-btcbot/docs/bot.md +0 -72
  205. package/ref-btcbot/docs/skills/btcbot.backtest/SKILL.md +0 -70
  206. package/ref-btcbot/docs/skills/btcbot.config/SKILL.md +0 -79
  207. package/ref-btcbot/docs/skills/btcbot.orders/SKILL.md +0 -60
  208. package/ref-btcbot/docs/skills/btcbot.positions/SKILL.md +0 -54
  209. package/ref-btcbot/docs/skills/btcbot.risk/SKILL.md +0 -69
  210. package/ref-btcbot/docs/skills/btcbot.run-loop/SKILL.md +0 -63
  211. package/ref-btcbot/docs/skills/btcbot.run-once/SKILL.md +0 -63
  212. package/ref-btcbot/docs/skills/btcbot.status/SKILL.md +0 -59
  213. package/ref-btcbot/examples/sample-candles.json +0 -52
  214. package/ref-btcbot/package.json +0 -18
  215. package/ref-btcbot/plugin/plugin.json +0 -146
  216. package/ref-btcbot/src/cli.js +0 -104
  217. package/ref-btcbot/src/config.js +0 -80
  218. package/ref-btcbot/src/core/bot-runner.js +0 -78
  219. package/ref-btcbot/src/core/exchange-factory.js +0 -19
  220. package/ref-btcbot/src/core/live-exchange.js +0 -12
  221. package/ref-btcbot/src/core/paper-exchange.js +0 -82
  222. package/ref-btcbot/src/core/risk-engine.js +0 -42
  223. package/ref-btcbot/src/core/state-repository.js +0 -47
  224. package/ref-btcbot/src/services/backtest-service.js +0 -44
  225. package/ref-btcbot/src/services/market-data.js +0 -32
  226. package/ref-btcbot/src/storage/json-store.js +0 -45
  227. package/ref-btcbot/src/strategy/hedge-strategy.js +0 -65
  228. package/ref-btcbot/src/strategy/indicators.js +0 -56
  229. package/ref-btcbot/src/web/api.js +0 -50
  230. package/ref-btcbot/src/web/app.js +0 -51
  231. package/ref-btcbot/src/web/index.html +0 -70
  232. package/ref-btcbot/src/web/server.js +0 -33
  233. /package/.beads/.br_history/{issues.20260308_180927_477542428.jsonl.meta.json → issues.20260308_235202_180577215.jsonl.meta.json} +0 -0
  234. /package/.beads/.br_history/{issues.20260308_181032_020230108.jsonl.meta.json → issues.20260308_235202_387414163.jsonl.meta.json} +0 -0
  235. /package/.beads/.br_history/{issues.20260308_181032_180539413.jsonl.meta.json → issues.20260308_235202_564422794.jsonl.meta.json} +0 -0
  236. /package/.beads/.br_history/{issues.20260308_181032_372621506.jsonl.meta.json → issues.20260308_235202_742600597.jsonl.meta.json} +0 -0
  237. /package/.beads/.br_history/{issues.20260308_181032_565142225.jsonl.meta.json → issues.20260308_235208_133360069.jsonl.meta.json} +0 -0
  238. /package/.beads/.br_history/{issues.20260308_181311_336346464.jsonl.meta.json → issues.20260308_235505_473406307.jsonl.meta.json} +0 -0
  239. /package/.beads/.br_history/{issues.20260308_181444_039234498.jsonl.meta.json → issues.20260308_235505_662360489.jsonl.meta.json} +0 -0
  240. /package/.beads/.br_history/{issues.20260308_181503_794764403.jsonl.meta.json → issues.20260308_235505_843935624.jsonl.meta.json} +0 -0
  241. /package/.beads/.br_history/{issues.20260308_181503_950163105.jsonl.meta.json → issues.20260308_235506_044530221.jsonl.meta.json} +0 -0
  242. /package/.beads/.br_history/{issues.20260308_192031_852553505.jsonl.meta.json → issues.20260309_002618_115728731.jsonl.meta.json} +0 -0
  243. /package/.beads/.br_history/{issues.20260308_193552_846920518.jsonl.meta.json → issues.20260309_003748_878174586.jsonl.meta.json} +0 -0
  244. /package/.beads/.br_history/{issues.20260308_194054_394884833.jsonl.meta.json → issues.20260309_004057_868755623.jsonl.meta.json} +0 -0
  245. /package/.beads/.br_history/{issues.20260308_194209_440472460.jsonl.meta.json → issues.20260309_004058_512842163.jsonl.meta.json} +0 -0
  246. /package/.beads/.br_history/{issues.20260308_195319_099391899.jsonl.meta.json → issues.20260309_004058_994445226.jsonl.meta.json} +0 -0
  247. /package/.beads/.br_history/{issues.20260308_195324_176987204.jsonl.meta.json → issues.20260309_004059_475988596.jsonl.meta.json} +0 -0
  248. /package/.beads/.br_history/{issues.20260308_195436_929114019.jsonl.meta.json → issues.20260309_161902_566857851.jsonl.meta.json} +0 -0
  249. /package/.beads/.br_history/{issues.20260308_195437_055808298.jsonl.meta.json → issues.20260309_170512_277017739.jsonl.meta.json} +0 -0
  250. /package/.beads/.br_history/{issues.20260308_195437_304297399.jsonl.meta.json → issues.20260309_170512_477876921.jsonl.meta.json} +0 -0
  251. /package/.beads/.br_history/{issues.20260308_195437_556007332.jsonl.meta.json → issues.20260309_170512_664382701.jsonl.meta.json} +0 -0
  252. /package/.beads/.br_history/{issues.20260308_195444_987209695.jsonl.meta.json → issues.20260309_170512_859400333.jsonl.meta.json} +0 -0
  253. /package/.beads/.br_history/{issues.20260308_195445_133350193.jsonl.meta.json → issues.20260309_212326_082771164.jsonl.meta.json} +0 -0
  254. /package/.beads/.br_history/{issues.20260308_195445_400185615.jsonl.meta.json → issues.20260309_212326_245619716.jsonl.meta.json} +0 -0
  255. /package/.beads/.br_history/{issues.20260308_195445_689886334.jsonl.meta.json → issues.20260309_212326_403198317.jsonl.meta.json} +0 -0
  256. /package/.beads/.br_history/{issues.20260308_195445_949947727.jsonl.meta.json → issues.20260309_212332_539197678.jsonl.meta.json} +0 -0
  257. /package/.beads/.br_history/{issues.20260308_195745_580473297.jsonl.meta.json → issues.20260309_212332_731373599.jsonl.meta.json} +0 -0
  258. /package/.beads/.br_history/{issues.20260308_195745_725920532.jsonl.meta.json → issues.20260309_212332_928710953.jsonl.meta.json} +0 -0
  259. /package/.beads/.br_history/{issues.20260308_195745_968227911.jsonl.meta.json → issues.20260309_213021_341505240.jsonl.meta.json} +0 -0
  260. /package/.beads/.br_history/{issues.20260308_195746_224276322.jsonl.meta.json → issues.20260309_213022_023136934.jsonl.meta.json} +0 -0
  261. /package/.beads/.br_history/{issues.20260308_200018_386890807.jsonl.meta.json → issues.20260309_213022_400050719.jsonl.meta.json} +0 -0
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { getConfig, ensureDataDirs, validateConfig } = require("./config")
4
- const { StateRepository } = require("./core/state-repository")
5
- const { runCycle, runLoop } = require("./core/bot-runner")
6
- const { createServer } = require("./web/server")
7
- const { loadCandles, runBacktest } = require("./services/backtest-service")
8
-
9
- function output(data) {
10
- console.log(JSON.stringify(data, null, 2))
11
- }
12
-
13
- async function main() {
14
- const config = getConfig()
15
- ensureDataDirs(config)
16
- const validation = validateConfig(config)
17
- const repository = new StateRepository(config)
18
- const [command, subcommand, arg] = process.argv.slice(2)
19
-
20
- if (command === "config" && subcommand === "validate") {
21
- output(validation)
22
- process.exit(validation.ok ? 0 : 1)
23
- }
24
-
25
- if (!validation.ok) {
26
- output(validation)
27
- process.exit(1)
28
- }
29
-
30
- if (command === "status" && subcommand === "show") {
31
- output({
32
- ok: true,
33
- state: repository.getState(),
34
- snapshot: repository.getSnapshot(),
35
- orders: repository.listOrders().slice(0, 20)
36
- })
37
- return
38
- }
39
-
40
- if (command === "positions" && subcommand === "list") {
41
- output({ ok: true, position: repository.getState().position, snapshot: repository.getSnapshot() })
42
- return
43
- }
44
-
45
- if (command === "orders" && subcommand === "list") {
46
- output({ ok: true, orders: repository.listOrders() })
47
- return
48
- }
49
-
50
- if (command === "risk" && subcommand === "report") {
51
- const snapshot = repository.getSnapshot()
52
- output({ ok: true, risk: snapshot ? snapshot.risk : null, runtime: repository.getState().runtime })
53
- return
54
- }
55
-
56
- if (command === "run" && subcommand === "once") {
57
- output(await runCycle(config, repository))
58
- return
59
- }
60
-
61
- if (command === "run" && subcommand === "loop") {
62
- const iterations = Number(arg) > 0 ? Number(arg) : 3
63
- output(await runLoop(config, repository, { iterations }))
64
- return
65
- }
66
-
67
- if (command === "backtest" && subcommand === "run") {
68
- if (!arg) {
69
- output({ ok: false, error: "Pass a candle JSON file path" })
70
- process.exit(1)
71
- }
72
- output(runBacktest(config, loadCandles(arg)))
73
- return
74
- }
75
-
76
- if (command === "server") {
77
- const server = createServer(config, repository)
78
- await server.start()
79
- output({ ok: true, url: `http://localhost:${config.port}` })
80
- return
81
- }
82
-
83
- output({
84
- ok: false,
85
- error: "Unknown command",
86
- commands: [
87
- "config validate",
88
- "status show",
89
- "positions list",
90
- "orders list",
91
- "risk report",
92
- "run once",
93
- "run loop [iterations]",
94
- "backtest run <file>",
95
- "server"
96
- ]
97
- })
98
- process.exit(1)
99
- }
100
-
101
- main().catch((error) => {
102
- output({ ok: false, error: error.message })
103
- process.exit(1)
104
- })
@@ -1,80 +0,0 @@
1
- const fs = require("fs")
2
- const path = require("path")
3
- const dotenv = require("dotenv")
4
-
5
- let loaded = false
6
-
7
- function loadEnv() {
8
- if (loaded) return
9
- dotenv.config({ path: path.resolve(process.cwd(), ".env"), quiet: true })
10
- loaded = true
11
- }
12
-
13
- function toNumber(value, fallback) {
14
- const num = Number(value)
15
- return Number.isFinite(num) ? num : fallback
16
- }
17
-
18
- function toBool(value, fallback) {
19
- if (value === undefined) return fallback
20
- return String(value).toLowerCase() === "true"
21
- }
22
-
23
- function getConfig(overrides = {}) {
24
- loadEnv()
25
- const env = { ...process.env, ...overrides }
26
- return {
27
- port: toNumber(env.PORT, 4310),
28
- dataDir: path.resolve(process.cwd(), env.DATA_DIR || "./data"),
29
- market: {
30
- symbol: env.BTC_SYMBOL || "BTCUSDT",
31
- interval: env.BTC_INTERVAL || "15m",
32
- klinesUrl: env.BINANCE_KLINES_URL || "https://api.binance.com/api/v3/klines"
33
- },
34
- strategy: {
35
- spotInventoryBtc: toNumber(env.SPOT_INVENTORY_BTC, 0.25),
36
- minHedgeRatio: toNumber(env.MIN_HEDGE_RATIO, 0),
37
- maxHedgeRatio: toNumber(env.MAX_HEDGE_RATIO, 1),
38
- volWindow: toNumber(env.VOL_WINDOW, 20),
39
- trendFast: toNumber(env.TREND_FAST, 8),
40
- trendSlow: toNumber(env.TREND_SLOW, 21),
41
- atrWindow: toNumber(env.ATR_WINDOW, 14),
42
- zscoreWindow: toNumber(env.ZSCORE_WINDOW, 20),
43
- rebalanceThresholdBtc: toNumber(env.REBALANCE_THRESHOLD_BTC, 0.01)
44
- },
45
- risk: {
46
- maxGrossNotional: toNumber(env.MAX_GROSS_NOTIONAL, 50000),
47
- maxNotionalPerOrder: toNumber(env.MAX_NOTIONAL_PER_ORDER, 15000),
48
- cooldownMs: toNumber(env.RISK_COOLDOWN_MS, 900000)
49
- },
50
- runtime: {
51
- pollIntervalMs: toNumber(env.POLL_INTERVAL_MS, 5000),
52
- liveTradingEnabled: toBool(env.LIVE_TRADING_ENABLED, false)
53
- }
54
- }
55
- }
56
-
57
- function ensureDataDirs(config) {
58
- const dirs = [config.dataDir, path.join(config.dataDir, "ledgers")]
59
- dirs.forEach((dir) => fs.mkdirSync(dir, { recursive: true }))
60
- }
61
-
62
- function validateConfig(config) {
63
- const issues = []
64
- if (config.strategy.spotInventoryBtc <= 0) issues.push("SPOT_INVENTORY_BTC must be > 0")
65
- if (config.strategy.maxHedgeRatio < config.strategy.minHedgeRatio) issues.push("MAX_HEDGE_RATIO must be >= MIN_HEDGE_RATIO")
66
- if (config.risk.maxGrossNotional <= 0) issues.push("MAX_GROSS_NOTIONAL must be > 0")
67
- if (config.risk.maxNotionalPerOrder <= 0) issues.push("MAX_NOTIONAL_PER_ORDER must be > 0")
68
- if (config.runtime.pollIntervalMs <= 0) issues.push("POLL_INTERVAL_MS must be > 0")
69
- return {
70
- ok: issues.length === 0,
71
- issues,
72
- config
73
- }
74
- }
75
-
76
- module.exports = {
77
- getConfig,
78
- ensureDataDirs,
79
- validateConfig
80
- }
@@ -1,78 +0,0 @@
1
- const { fetchCandles } = require("../services/market-data")
2
- const { buildSignal } = require("../strategy/hedge-strategy")
3
- const { markPrice } = require("./paper-exchange")
4
- const { createExchange } = require("./exchange-factory")
5
- const { evaluateRisk, applyRiskCooldown } = require("./risk-engine")
6
-
7
- function buildReport(state, signal, mark, order, risk) {
8
- return {
9
- ts: new Date().toISOString(),
10
- mode: state.mode,
11
- signal,
12
- mark,
13
- risk,
14
- position: state.position,
15
- runtime: state.runtime,
16
- order: order || null
17
- }
18
- }
19
-
20
- async function runCycle(config, repository) {
21
- const state = repository.getState()
22
- const exchange = createExchange(config, state)
23
- const candles = await fetchCandles(config, 160)
24
- const signal = buildSignal(candles, config.strategy, state)
25
- const latestPrice = signal.price || candles[candles.length - 1].close
26
- const risk = evaluateRisk(config, state, signal)
27
- let order = null
28
-
29
- if (risk.ok && signal.shouldRebalance) {
30
- order = exchange.executeOrder(
31
- signal.deltaBtc < 0 ? "sell" : "buy",
32
- Math.abs(signal.deltaBtc),
33
- latestPrice
34
- )
35
- repository.appendOrder({ ...order, reasons: signal.reasons })
36
- }
37
-
38
- if (!risk.ok && risk.violations.includes("order_notional_limit")) {
39
- applyRiskCooldown(state, config, risk.violations.join(","))
40
- }
41
-
42
- state.runtime.lastSignal = signal
43
- state.runtime.lastRunAt = new Date().toISOString()
44
- const mark = markPrice(state, latestPrice)
45
- const report = buildReport(state, signal, mark, order, risk)
46
-
47
- repository.saveState(state)
48
- repository.saveSnapshot(report)
49
- repository.appendRun(report)
50
-
51
- return report
52
- }
53
-
54
- async function runLoop(config, repository, options = {}) {
55
- const iterations = Number(options.iterations) > 0 ? Number(options.iterations) : null
56
- const waitMs = config.runtime.pollIntervalMs
57
- let count = 0
58
- const reports = []
59
-
60
- while (iterations === null || count < iterations) {
61
- const report = await runCycle(config, repository)
62
- reports.push(report)
63
- count += 1
64
- if (iterations !== null && count >= iterations) break
65
- await new Promise((resolve) => setTimeout(resolve, waitMs))
66
- }
67
-
68
- return {
69
- ok: true,
70
- iterations: count,
71
- last: reports[reports.length - 1]
72
- }
73
- }
74
-
75
- module.exports = {
76
- runCycle,
77
- runLoop
78
- }
@@ -1,19 +0,0 @@
1
- const { executePerpMarketOrder } = require("./paper-exchange")
2
- const { createLiveExchange } = require("./live-exchange")
3
-
4
- function createExchange(config, state) {
5
- if (config.runtime.liveTradingEnabled) {
6
- return createLiveExchange(config, state)
7
- }
8
-
9
- return {
10
- mode: "paper",
11
- executeOrder(side, qty, price) {
12
- return executePerpMarketOrder(state, side, qty, price)
13
- }
14
- }
15
- }
16
-
17
- module.exports = {
18
- createExchange
19
- }
@@ -1,12 +0,0 @@
1
- function createLiveExchange() {
2
- return {
3
- mode: "live",
4
- executeOrder() {
5
- throw new Error("Live trading is not implemented in v1. Keep LIVE_TRADING_ENABLED=false.")
6
- }
7
- }
8
- }
9
-
10
- module.exports = {
11
- createLiveExchange
12
- }
@@ -1,82 +0,0 @@
1
- function round(value) {
2
- return Number(value.toFixed(6))
3
- }
4
-
5
- function createInitialState(config) {
6
- return {
7
- mode: "paper",
8
- createdAt: new Date().toISOString(),
9
- position: {
10
- spotQty: round(config.strategy.spotInventoryBtc),
11
- perpQty: 0,
12
- avgPerpEntry: 0,
13
- realizedPnl: 0
14
- },
15
- balances: {
16
- quote: 100000
17
- },
18
- runtime: {
19
- loopActive: false,
20
- cooldownUntil: null,
21
- consecutiveLosses: 0,
22
- lastSignal: null,
23
- lastError: null,
24
- lastRunAt: null
25
- }
26
- }
27
- }
28
-
29
- function markPrice(state, price) {
30
- const perpUnrealized = state.position.perpQty === 0
31
- ? 0
32
- : (price - state.position.avgPerpEntry) * state.position.perpQty
33
- const spotValue = state.position.spotQty * price
34
- const netDelta = state.position.spotQty + state.position.perpQty
35
- return {
36
- spotValue: round(spotValue),
37
- perpUnrealized: round(perpUnrealized),
38
- netDeltaBtc: round(netDelta),
39
- grossNotional: round((Math.abs(state.position.spotQty) + Math.abs(state.position.perpQty)) * price),
40
- equityEstimate: round(state.balances.quote + spotValue + state.position.realizedPnl + perpUnrealized)
41
- }
42
- }
43
-
44
- function executePerpMarketOrder(state, side, qty, price) {
45
- const signedQty = side === "sell" ? -Math.abs(qty) : Math.abs(qty)
46
- const previousQty = state.position.perpQty
47
- const nextQty = round(previousQty + signedQty)
48
- let realized = 0
49
- let nextEntry = state.position.avgPerpEntry
50
-
51
- if (previousQty === 0 || Math.sign(previousQty) === Math.sign(nextQty)) {
52
- const weightedNotional = Math.abs(previousQty * state.position.avgPerpEntry) + Math.abs(signedQty * price)
53
- nextEntry = nextQty === 0 ? 0 : round(weightedNotional / Math.abs(nextQty))
54
- } else {
55
- const closingQty = Math.min(Math.abs(previousQty), Math.abs(signedQty))
56
- realized = round((price - state.position.avgPerpEntry) * Math.sign(previousQty) * closingQty)
57
- nextEntry = nextQty === 0 ? 0 : round(price)
58
- }
59
-
60
- state.position.perpQty = nextQty
61
- state.position.avgPerpEntry = nextEntry
62
- state.position.realizedPnl = round(state.position.realizedPnl + realized)
63
- state.runtime.consecutiveLosses = realized < 0 ? state.runtime.consecutiveLosses + 1 : 0
64
-
65
- return {
66
- id: `ord_${Date.now()}`,
67
- ts: new Date().toISOString(),
68
- mode: state.mode,
69
- side,
70
- qty: round(Math.abs(qty)),
71
- price: round(price),
72
- realizedPnl: realized,
73
- perpQtyAfter: nextQty,
74
- avgPerpEntryAfter: nextEntry
75
- }
76
- }
77
-
78
- module.exports = {
79
- createInitialState,
80
- markPrice,
81
- executePerpMarketOrder
82
- }
@@ -1,42 +0,0 @@
1
- function nowMs() {
2
- return Date.now()
3
- }
4
-
5
- function evaluateRisk(config, state, signal) {
6
- const violations = []
7
- const cooldownUntil = state.runtime.cooldownUntil ? Date.parse(state.runtime.cooldownUntil) : null
8
-
9
- if (cooldownUntil && cooldownUntil > nowMs()) {
10
- violations.push("cooldown_active")
11
- }
12
-
13
- if (!signal.ready) {
14
- violations.push("signal_not_ready")
15
- }
16
-
17
- const orderNotional = Math.abs(signal.deltaBtc || 0) * (signal.price || 0)
18
- if (orderNotional > config.risk.maxNotionalPerOrder) {
19
- violations.push("order_notional_limit")
20
- }
21
-
22
- const grossAfter = (Math.abs(state.position.spotQty) + Math.abs(signal.targetPerpBtc || 0)) * (signal.price || 0)
23
- if (grossAfter > config.risk.maxGrossNotional) {
24
- violations.push("gross_notional_limit")
25
- }
26
-
27
- return {
28
- ok: violations.length === 0,
29
- violations
30
- }
31
- }
32
-
33
- function applyRiskCooldown(state, config, reason) {
34
- state.runtime.cooldownUntil = new Date(Date.now() + config.risk.cooldownMs).toISOString()
35
- state.runtime.lastError = reason
36
- return state
37
- }
38
-
39
- module.exports = {
40
- evaluateRisk,
41
- applyRiskCooldown
42
- }
@@ -1,47 +0,0 @@
1
- const path = require("path")
2
- const { JsonStore } = require("../storage/json-store")
3
- const { createInitialState } = require("./paper-exchange")
4
-
5
- class StateRepository {
6
- constructor(config) {
7
- this.config = config
8
- this.store = new JsonStore(config.dataDir)
9
- this.ledger = new JsonStore(path.join(config.dataDir, "ledgers"))
10
- }
11
-
12
- getState() {
13
- return this.store.read("state", createInitialState(this.config))
14
- }
15
-
16
- saveState(state) {
17
- return this.store.write("state", state)
18
- }
19
-
20
- appendOrder(order) {
21
- return this.ledger.append("orders", order, 1000)
22
- }
23
-
24
- appendRun(run) {
25
- return this.ledger.append("runs", run, 1000)
26
- }
27
-
28
- saveSnapshot(snapshot) {
29
- return this.store.write("snapshot", snapshot)
30
- }
31
-
32
- getSnapshot() {
33
- return this.store.read("snapshot", null)
34
- }
35
-
36
- listOrders() {
37
- return this.ledger.read("orders", [])
38
- }
39
-
40
- listRuns() {
41
- return this.ledger.read("runs", [])
42
- }
43
- }
44
-
45
- module.exports = {
46
- StateRepository
47
- }
@@ -1,44 +0,0 @@
1
- const fs = require("fs")
2
- const path = require("path")
3
- const { buildSignal } = require("../strategy/hedge-strategy")
4
- const { createInitialState, executePerpMarketOrder, markPrice } = require("../core/paper-exchange")
5
- const { evaluateRisk } = require("../core/risk-engine")
6
-
7
- function loadCandles(file) {
8
- const target = path.resolve(process.cwd(), file)
9
- const raw = JSON.parse(fs.readFileSync(target, "utf-8"))
10
- return Array.isArray(raw) ? raw : raw.candles
11
- }
12
-
13
- function runBacktest(config, candles) {
14
- const state = createInitialState(config)
15
- const trades = []
16
-
17
- for (let i = 40; i < candles.length; i += 1) {
18
- const window = candles.slice(0, i + 1)
19
- const signal = buildSignal(window, config.strategy, state)
20
- const risk = evaluateRisk(config, state, signal)
21
- if (!signal.ready || !risk.ok || !signal.shouldRebalance) continue
22
- const order = executePerpMarketOrder(
23
- state,
24
- signal.deltaBtc < 0 ? "sell" : "buy",
25
- Math.abs(signal.deltaBtc),
26
- signal.price
27
- )
28
- trades.push({ ...order, reasons: signal.reasons })
29
- state.runtime.lastSignal = signal
30
- }
31
-
32
- const latest = candles[candles.length - 1]
33
- return {
34
- ok: true,
35
- trades,
36
- totals: markPrice(state, latest.close),
37
- position: state.position
38
- }
39
- }
40
-
41
- module.exports = {
42
- loadCandles,
43
- runBacktest
44
- }
@@ -1,32 +0,0 @@
1
- async function fetchJson(url) {
2
- const response = await fetch(url)
3
- if (!response.ok) {
4
- throw new Error(`Market data request failed with status ${response.status}`)
5
- }
6
- return response.json()
7
- }
8
-
9
- function normalizeKline(row) {
10
- return {
11
- openTime: Number(row[0]),
12
- open: Number(row[1]),
13
- high: Number(row[2]),
14
- low: Number(row[3]),
15
- close: Number(row[4]),
16
- volume: Number(row[5]),
17
- closeTime: Number(row[6])
18
- }
19
- }
20
-
21
- async function fetchCandles(config, limit = 120) {
22
- const url = new URL(config.market.klinesUrl)
23
- url.searchParams.set("symbol", config.market.symbol)
24
- url.searchParams.set("interval", config.market.interval)
25
- url.searchParams.set("limit", String(limit))
26
- const rows = await fetchJson(url.toString())
27
- return rows.map(normalizeKline)
28
- }
29
-
30
- module.exports = {
31
- fetchCandles
32
- }
@@ -1,45 +0,0 @@
1
- const fs = require("fs")
2
- const path = require("path")
3
-
4
- class JsonStore {
5
- constructor(baseDir) {
6
- this.baseDir = baseDir
7
- fs.mkdirSync(baseDir, { recursive: true })
8
- }
9
-
10
- file(name) {
11
- return path.join(this.baseDir, `${name}.json`)
12
- }
13
-
14
- read(name, fallback) {
15
- const file = this.file(name)
16
- if (!fs.existsSync(file)) return fallback
17
- const raw = fs.readFileSync(file, "utf-8")
18
- if (!raw.trim()) return fallback
19
- return JSON.parse(raw)
20
- }
21
-
22
- write(name, value) {
23
- const file = this.file(name)
24
- fs.writeFileSync(file, JSON.stringify(value, null, 2))
25
- return value
26
- }
27
-
28
- update(name, fallback, updater) {
29
- const current = this.read(name, fallback)
30
- const next = updater(current)
31
- return this.write(name, next)
32
- }
33
-
34
- append(name, item, maxItems = 500) {
35
- return this.update(name, [], (current) => {
36
- const next = Array.isArray(current) ? current.slice() : []
37
- next.unshift(item)
38
- return next.slice(0, maxItems)
39
- })
40
- }
41
- }
42
-
43
- module.exports = {
44
- JsonStore
45
- }
@@ -1,65 +0,0 @@
1
- const { sma, atr, zscore, realizedVol } = require("./indicators")
2
-
3
- function clamp(value, min, max) {
4
- return Math.max(min, Math.min(max, value))
5
- }
6
-
7
- function round(value) {
8
- return Number(value.toFixed(6))
9
- }
10
-
11
- function buildSignal(candles, config, state) {
12
- const closes = candles.map((c) => c.close)
13
- const fast = sma(closes, config.trendFast)
14
- const slow = sma(closes, config.trendSlow)
15
- const vol = realizedVol(closes, config.volWindow)
16
- const range = atr(candles, config.atrWindow)
17
- const meanReversionZ = zscore(closes, config.zscoreWindow)
18
- const latest = candles[candles.length - 1]
19
-
20
- if (!fast || !slow || !vol || !range || latest === undefined) {
21
- return {
22
- ready: false,
23
- reasons: ["insufficient_market_history"]
24
- }
25
- }
26
-
27
- const trendBias = fast < slow ? 0.2 : -0.1
28
- const volBias = clamp(vol / 0.8, 0, 1)
29
- const reversionBias = clamp(Math.max(0, meanReversionZ) / 2.5, 0, 0.35)
30
- const drawdownBias = state.runtime && state.runtime.consecutiveLosses >= 2 ? 0.15 : 0
31
-
32
- const hedgeRatio = clamp(
33
- config.minHedgeRatio + volBias + reversionBias + trendBias + drawdownBias,
34
- config.minHedgeRatio,
35
- config.maxHedgeRatio
36
- )
37
-
38
- const targetPerpBtc = round(-config.spotInventoryBtc * hedgeRatio)
39
- const currentPerpBtc = round((state.position && state.position.perpQty) || 0)
40
- const deltaBtc = round(targetPerpBtc - currentPerpBtc)
41
- const shouldRebalance = Math.abs(deltaBtc) >= config.rebalanceThresholdBtc
42
-
43
- return {
44
- ready: true,
45
- price: latest.close,
46
- trend: round((fast - slow) / slow),
47
- volatility: round(vol),
48
- atr: round(range),
49
- meanReversionZ: round(meanReversionZ),
50
- hedgeRatio: round(hedgeRatio),
51
- targetPerpBtc,
52
- currentPerpBtc,
53
- deltaBtc,
54
- shouldRebalance,
55
- reasons: [
56
- volBias > 0.55 ? "high_realized_vol" : "moderate_realized_vol",
57
- meanReversionZ > 1 ? "price_extended_above_mean" : "mean_reversion_neutral",
58
- fast < slow ? "downtrend_requires_more_hedge" : "trend_supportive"
59
- ]
60
- }
61
- }
62
-
63
- module.exports = {
64
- buildSignal
65
- }