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.
- package/.beads/.br_history/issues.20260308_235202_180577215.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235202_387414163.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235202_564422794.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235202_742600597.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235208_133360069.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235505_473406307.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235505_662360489.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235505_843935624.jsonl +57 -0
- package/.beads/.br_history/issues.20260308_235506_044530221.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_002618_115728731.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_003748_878174586.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_004057_868755623.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_004058_512842163.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_004058_994445226.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_004059_475988596.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_161902_566857851.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_170512_277017739.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_170512_477876921.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_170512_664382701.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_170512_859400333.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_212326_082771164.jsonl +57 -0
- package/.beads/.br_history/issues.20260309_212326_245619716.jsonl +58 -0
- package/.beads/.br_history/issues.20260309_212326_403198317.jsonl +59 -0
- package/.beads/.br_history/issues.20260309_212332_539197678.jsonl +60 -0
- package/.beads/.br_history/issues.20260309_212332_731373599.jsonl +60 -0
- package/.beads/.br_history/issues.20260309_212332_928710953.jsonl +60 -0
- package/.beads/.br_history/issues.20260309_213021_341505240.jsonl +60 -0
- package/.beads/.br_history/issues.20260309_213022_023136934.jsonl +60 -0
- package/.beads/.br_history/issues.20260309_213022_400050719.jsonl +60 -0
- package/.beads/issues.jsonl +20 -17
- package/README.md +1 -1
- package/__tests__/adapter-schema.test.js +3 -0
- package/__tests__/aws-plugin.test.js +84 -0
- package/__tests__/az-plugin.test.js +84 -0
- package/__tests__/builtin-adapter.test.js +29 -0
- package/__tests__/cline-plugin.test.js +109 -0
- package/__tests__/cline-skill.test.js +49 -0
- package/__tests__/docker-plugin.test.js +3 -1
- package/__tests__/eza-plugin.test.js +81 -0
- package/__tests__/gcloud-plugin.test.js +86 -0
- package/__tests__/gh-plugin.test.js +86 -0
- package/__tests__/helm-plugin.test.js +81 -0
- package/__tests__/http-adapter.test.js +118 -0
- package/__tests__/just-plugin.test.js +82 -0
- package/__tests__/kubectl-plugin.test.js +83 -0
- package/__tests__/linear-plugin.test.js +81 -0
- package/__tests__/mcp-adapter.test.js +187 -0
- package/__tests__/nextest-plugin.test.js +82 -0
- package/__tests__/npm-plugin.test.js +81 -0
- package/__tests__/nullclaw-plugin.test.js +157 -0
- package/__tests__/openapi-adapter.test.js +199 -0
- package/__tests__/plugin-agency-agents.test.js +6 -6
- package/__tests__/plugin-nullclaw.test.js +78 -0
- package/__tests__/plugin-visual-explainer.test.js +62 -0
- package/__tests__/plugins-manager.test.js +59 -10
- package/__tests__/pnpm-plugin.test.js +81 -0
- package/__tests__/poetry-plugin.test.js +83 -0
- package/__tests__/process-adapter.test.js +124 -90
- package/__tests__/pulumi-plugin.test.js +81 -0
- package/__tests__/railway-plugin.test.js +84 -0
- package/__tests__/server-app.test.js +67 -0
- package/__tests__/server-config-service.test.js +79 -0
- package/__tests__/server-routes-ask.test.js +89 -0
- package/__tests__/server-routes-commands.test.js +55 -0
- package/__tests__/server-routes-config.test.js +87 -0
- package/__tests__/server-routes-jobs.test.js +53 -0
- package/__tests__/server-routes-misc.test.js +112 -0
- package/__tests__/server-storage-adapter.test.js +40 -0
- package/__tests__/server-storage-file.test.js +73 -0
- package/__tests__/server-storage-mongo.test.js +74 -0
- package/__tests__/shell-adapter.test.js +81 -22
- package/__tests__/stripe-plugin.test.js +3 -1
- package/__tests__/supabase-plugin.test.js +86 -0
- package/__tests__/terraform-plugin.test.js +83 -0
- package/__tests__/uv-plugin.test.js +81 -0
- package/__tests__/vercel-plugin.test.js +81 -0
- package/__tests__/watchexec-plugin.test.js +80 -0
- package/cli/adapter-schema.js +5 -0
- package/cli/adapters/process.js +53 -2
- package/cli/plugin-install-guidance.js +320 -0
- package/cli/plugins-manager.js +272 -212
- package/cli/skills.js +16 -5
- package/cli/supercli.js +26 -2
- package/docs/plugins.md +2 -0
- package/docs/skills/cline-non-interactive/SKILL.md +59 -0
- package/docs/skills-catalog.md +35 -0
- package/docs/visual-overview.md +21 -0
- package/jest.config.js +4 -1
- package/package.json +4 -3
- package/plugins/agency-agents/plugin.json +5 -0
- package/{cli/plugin-agency-agents.js → plugins/agency-agents/scripts/post-install.js} +13 -3
- package/plugins/aws/README.md +46 -0
- package/plugins/aws/plugin.json +42 -0
- package/plugins/az/README.md +46 -0
- package/plugins/az/plugin.json +42 -0
- package/plugins/clickup/plugin.json +38 -0
- package/plugins/clickup/scripts/post-install.js +107 -0
- package/plugins/clickup/scripts/post-uninstall.js +30 -0
- package/plugins/cline/README.md +48 -0
- package/plugins/cline/plugin.json +92 -0
- package/plugins/eza/README.md +40 -0
- package/plugins/eza/plugin.json +42 -0
- package/plugins/gcloud/README.md +46 -0
- package/plugins/gcloud/plugin.json +42 -0
- package/plugins/gh/README.md +46 -0
- package/plugins/gh/plugin.json +43 -0
- package/plugins/helm/README.md +42 -0
- package/plugins/helm/plugin.json +42 -0
- package/plugins/just/README.md +42 -0
- package/plugins/just/plugin.json +42 -0
- package/plugins/kubectl/README.md +46 -0
- package/plugins/kubectl/plugin.json +42 -0
- package/plugins/linear/README.md +60 -0
- package/plugins/linear/plugin.json +42 -0
- package/plugins/nextest/README.md +42 -0
- package/plugins/nextest/plugin.json +42 -0
- package/plugins/npm/README.md +46 -0
- package/plugins/npm/plugin.json +42 -0
- package/plugins/nullclaw/README.md +45 -0
- package/plugins/nullclaw/plugin.json +64 -0
- package/plugins/nullclaw/scripts/post-install.js +189 -0
- package/plugins/nullclaw/scripts/post-uninstall.js +25 -0
- package/plugins/openfang/plugin.json +37 -0
- package/plugins/openfang/scripts/post-install.js +163 -0
- package/plugins/openfang/scripts/post-uninstall.js +30 -0
- package/plugins/plugins.json +234 -0
- package/plugins/pnpm/README.md +46 -0
- package/plugins/pnpm/plugin.json +42 -0
- package/plugins/poetry/README.md +46 -0
- package/plugins/poetry/plugin.json +42 -0
- package/plugins/pulumi/README.md +46 -0
- package/plugins/pulumi/plugin.json +42 -0
- package/plugins/railway/README.md +58 -0
- package/plugins/railway/plugin.json +43 -0
- package/plugins/supabase/README.md +55 -0
- package/plugins/supabase/plugin.json +42 -0
- package/plugins/superpowers/plugin.json +22 -0
- package/plugins/superpowers/scripts/post-install.js +124 -0
- package/plugins/superpowers/scripts/post-uninstall.js +30 -0
- package/plugins/terraform/README.md +46 -0
- package/plugins/terraform/plugin.json +42 -0
- package/plugins/uv/README.md +46 -0
- package/plugins/uv/plugin.json +42 -0
- package/plugins/vercel/README.md +47 -0
- package/plugins/vercel/plugin.json +42 -0
- package/plugins/visual-explainer/plugin.json +15 -0
- package/plugins/visual-explainer/scripts/post-install.js +111 -0
- package/plugins/watchexec/README.md +40 -0
- package/plugins/watchexec/plugin.json +42 -0
- package/tests/test-aws-smoke.sh +56 -0
- package/tests/test-az-smoke.sh +56 -0
- package/tests/test-cline-smoke.sh +37 -0
- package/tests/test-eza-smoke.sh +33 -0
- package/tests/test-gcloud-smoke.sh +56 -0
- package/tests/test-gh-smoke.sh +56 -0
- package/tests/test-helm-smoke.sh +33 -0
- package/tests/test-just-smoke.sh +40 -0
- package/tests/test-kubectl-smoke.sh +37 -0
- package/tests/test-linear-smoke.sh +97 -0
- package/tests/test-nextest-smoke.sh +33 -0
- package/tests/test-npm-smoke.sh +32 -0
- package/tests/test-nullclaw-smoke.sh +51 -0
- package/tests/test-plugins-registry.js +110 -0
- package/tests/test-pnpm-smoke.sh +33 -0
- package/tests/test-poetry-smoke.sh +33 -0
- package/tests/test-pulumi-smoke.sh +33 -0
- package/tests/test-railway-smoke.sh +95 -0
- package/tests/test-supabase-smoke.sh +95 -0
- package/tests/test-terraform-smoke.sh +33 -0
- package/tests/test-uv-smoke.sh +33 -0
- package/tests/test-vercel-smoke.sh +55 -0
- package/tests/test-watchexec-smoke.sh +33 -0
- package/.beads/.br_history/issues.20260308_180927_477542428.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181032_020230108.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181032_180539413.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181032_372621506.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181032_565142225.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181311_336346464.jsonl +0 -12
- package/.beads/.br_history/issues.20260308_181444_039234498.jsonl +0 -13
- package/.beads/.br_history/issues.20260308_181503_794764403.jsonl +0 -13
- package/.beads/.br_history/issues.20260308_181503_950163105.jsonl +0 -13
- package/.beads/.br_history/issues.20260308_192031_852553505.jsonl +0 -13
- package/.beads/.br_history/issues.20260308_193552_846920518.jsonl +0 -14
- package/.beads/.br_history/issues.20260308_194054_394884833.jsonl +0 -14
- package/.beads/.br_history/issues.20260308_194209_440472460.jsonl +0 -15
- package/.beads/.br_history/issues.20260308_195319_099391899.jsonl +0 -15
- package/.beads/.br_history/issues.20260308_195324_176987204.jsonl +0 -16
- package/.beads/.br_history/issues.20260308_195436_929114019.jsonl +0 -16
- package/.beads/.br_history/issues.20260308_195437_055808298.jsonl +0 -17
- package/.beads/.br_history/issues.20260308_195437_304297399.jsonl +0 -18
- package/.beads/.br_history/issues.20260308_195437_556007332.jsonl +0 -19
- package/.beads/.br_history/issues.20260308_195444_987209695.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195445_133350193.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195445_400185615.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195445_689886334.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195445_949947727.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195745_580473297.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195745_725920532.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195745_968227911.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_195746_224276322.jsonl +0 -20
- package/.beads/.br_history/issues.20260308_200018_386890807.jsonl +0 -20
- package/ref-btcbot/.env.example +0 -19
- package/ref-btcbot/README.md +0 -3
- package/ref-btcbot/docs/bot.md +0 -72
- package/ref-btcbot/docs/skills/btcbot.backtest/SKILL.md +0 -70
- package/ref-btcbot/docs/skills/btcbot.config/SKILL.md +0 -79
- package/ref-btcbot/docs/skills/btcbot.orders/SKILL.md +0 -60
- package/ref-btcbot/docs/skills/btcbot.positions/SKILL.md +0 -54
- package/ref-btcbot/docs/skills/btcbot.risk/SKILL.md +0 -69
- package/ref-btcbot/docs/skills/btcbot.run-loop/SKILL.md +0 -63
- package/ref-btcbot/docs/skills/btcbot.run-once/SKILL.md +0 -63
- package/ref-btcbot/docs/skills/btcbot.status/SKILL.md +0 -59
- package/ref-btcbot/examples/sample-candles.json +0 -52
- package/ref-btcbot/package.json +0 -18
- package/ref-btcbot/plugin/plugin.json +0 -146
- package/ref-btcbot/src/cli.js +0 -104
- package/ref-btcbot/src/config.js +0 -80
- package/ref-btcbot/src/core/bot-runner.js +0 -78
- package/ref-btcbot/src/core/exchange-factory.js +0 -19
- package/ref-btcbot/src/core/live-exchange.js +0 -12
- package/ref-btcbot/src/core/paper-exchange.js +0 -82
- package/ref-btcbot/src/core/risk-engine.js +0 -42
- package/ref-btcbot/src/core/state-repository.js +0 -47
- package/ref-btcbot/src/services/backtest-service.js +0 -44
- package/ref-btcbot/src/services/market-data.js +0 -32
- package/ref-btcbot/src/storage/json-store.js +0 -45
- package/ref-btcbot/src/strategy/hedge-strategy.js +0 -65
- package/ref-btcbot/src/strategy/indicators.js +0 -56
- package/ref-btcbot/src/web/api.js +0 -50
- package/ref-btcbot/src/web/app.js +0 -51
- package/ref-btcbot/src/web/index.html +0 -70
- package/ref-btcbot/src/web/server.js +0 -33
- /package/.beads/.br_history/{issues.20260308_180927_477542428.jsonl.meta.json → issues.20260308_235202_180577215.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181032_020230108.jsonl.meta.json → issues.20260308_235202_387414163.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181032_180539413.jsonl.meta.json → issues.20260308_235202_564422794.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181032_372621506.jsonl.meta.json → issues.20260308_235202_742600597.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181032_565142225.jsonl.meta.json → issues.20260308_235208_133360069.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181311_336346464.jsonl.meta.json → issues.20260308_235505_473406307.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181444_039234498.jsonl.meta.json → issues.20260308_235505_662360489.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181503_794764403.jsonl.meta.json → issues.20260308_235505_843935624.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_181503_950163105.jsonl.meta.json → issues.20260308_235506_044530221.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_192031_852553505.jsonl.meta.json → issues.20260309_002618_115728731.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_193552_846920518.jsonl.meta.json → issues.20260309_003748_878174586.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_194054_394884833.jsonl.meta.json → issues.20260309_004057_868755623.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_194209_440472460.jsonl.meta.json → issues.20260309_004058_512842163.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195319_099391899.jsonl.meta.json → issues.20260309_004058_994445226.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195324_176987204.jsonl.meta.json → issues.20260309_004059_475988596.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195436_929114019.jsonl.meta.json → issues.20260309_161902_566857851.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195437_055808298.jsonl.meta.json → issues.20260309_170512_277017739.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195437_304297399.jsonl.meta.json → issues.20260309_170512_477876921.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195437_556007332.jsonl.meta.json → issues.20260309_170512_664382701.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195444_987209695.jsonl.meta.json → issues.20260309_170512_859400333.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195445_133350193.jsonl.meta.json → issues.20260309_212326_082771164.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195445_400185615.jsonl.meta.json → issues.20260309_212326_245619716.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195445_689886334.jsonl.meta.json → issues.20260309_212326_403198317.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195445_949947727.jsonl.meta.json → issues.20260309_212332_539197678.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195745_580473297.jsonl.meta.json → issues.20260309_212332_731373599.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195745_725920532.jsonl.meta.json → issues.20260309_212332_928710953.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195745_968227911.jsonl.meta.json → issues.20260309_213021_341505240.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_195746_224276322.jsonl.meta.json → issues.20260309_213022_023136934.jsonl.meta.json} +0 -0
- /package/.beads/.br_history/{issues.20260308_200018_386890807.jsonl.meta.json → issues.20260309_213022_400050719.jsonl.meta.json} +0 -0
package/ref-btcbot/src/cli.js
DELETED
|
@@ -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
|
-
})
|
package/ref-btcbot/src/config.js
DELETED
|
@@ -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,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
|
-
}
|