Skip to content

Commit faf0f26

Browse files
committed
wip
1 parent b7f6d4e commit faf0f26

7 files changed

Lines changed: 450 additions & 27 deletions

File tree

cli/src/helpers/getArgs.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ export const getArgs = singleshot(() => {
161161
type: "boolean",
162162
default: false,
163163
},
164+
tune: {
165+
type: "boolean",
166+
default: false,
167+
},
168+
split: {
169+
type: "string",
170+
default: "",
171+
},
164172
commit: {
165173
type: "string",
166174
default: "",

cli/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import "./main/pine";
2121
import "./main/editor";
2222
import "./main/dump";
2323
import "./main/pnldebug";
24+
import "./main/brokerdebug";
2425
import "./main/simulator";
26+
import "./main/tune";
2527
import "./main/flush";
2628
import "./main/init";
2729
import "./main/docker";

cli/src/main/help.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Modes:
2121
--dump Fetch and save raw OHLCV candles
2222
--pnldebug Simulate PnL per minute for a given entry price and direction
2323
--brokerdebug Fire a single broker commit against the live broker adapter
24-
--simulator <ideas.jsonl> Sweep exit/entry parameters over crowd ideas, train author whitelist
24+
--simulator <ideas.jsonl> Feasibility probe over crowd ideas: is there a profitable corridor at all
25+
--tune <ideas.jsonl> Walk-forward parameter search: train on the head, one frozen shot on the tail
2526
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
2627
--init Scaffold a new project in the current directory
2728
--docker Scaffold a Docker workspace for running strategies in a container
@@ -156,10 +157,13 @@ Simulator flags (--simulator):
156157
"author": string }. The file is validated BEFORE any work; a line that does not
157158
match the structure aborts the run with an error.
158159
159-
Runs the backtest-kit Simulator entity: one 5-day candle pass per idea, flood
160-
dedupe (one idea per author per direction per 8h), default-ban author filter
161-
(ban thresholds are grid axes), grid evaluation with time-based Sharpe/Sortino,
162-
four ranking winners. The report includes the production author whitelist.
160+
A FEASIBILITY PROBE, not a parameter search (that is --tune): the profit-harvesting
161+
machinery is off (no profit lock, inert trailing, no weighted consensus), and a
162+
fast 48-point grid of hard stop x hold x ban rule answers one question — does the
163+
feed contain a profitable corridor at all. One 5-day candle pass per idea, flood
164+
dedupe (one idea per author per direction per 8h), default-ban author filter,
165+
time-based Sharpe/Sortino, four ranking winners. The report carries the corridor
166+
share and the whitelist of the sharpe winner's ban rule as evidence.
163167
Ideas of other symbols are filtered out — one shared feed serves any --symbol.
164168
165169
No output flag → print the Markdown summary to stdout. With --verbose every
@@ -169,6 +173,31 @@ Simulator flags (--simulator):
169173
Module file ./modules/simulator.module is loaded automatically if it exists
170174
(register your exchange there); without it CCXT Binance is used by default.
171175
176+
Tune flags (--tune):
177+
178+
--symbol <string> Trading pair to tune (default: BTCUSDT)
179+
--exchange <string> Exchange name (default: first registered)
180+
--split <string> Train share of the feed time range, 0..1 (default: 0.7)
181+
--output <string> Output file base name (default: tune_{SYMBOL}_{TIMESTAMP})
182+
--json Save { trainSplit, train, test } to ./dump/<output>.json
183+
--markdown Save walk-forward report to ./dump/<output>.md
184+
--verbose Log simulator lifecycle callbacks to the console
185+
186+
Positional: path to an ideas .jsonl file — same shape and validation as --simulator.
187+
188+
The PARAMETER SEARCH counterpart of the --simulator probe: the full grid with the
189+
profit-harvesting machinery on — profit lock, trailing take, weighted consensus,
190+
Wilson-bound ban, both author-hit metrics (close/reach). Honesty is structural:
191+
training sees ONLY the head of the feed (--split of its time range), then the
192+
sharpe winner is frozen — point and raw author track record — and fired exactly
193+
once on the tail via Simulator.test. Nothing is trained on the tail; authors
194+
unseen in training are banned by default. The report carries the train winners,
195+
the out-of-sample result with the trade list, and the frozen author track record
196+
to hardcode for production Simulator.test.
197+
198+
Module file ./modules/tune.module is loaded automatically if it exists
199+
(register your exchange there); without it CCXT Binance is used by default.
200+
172201
Flush flags (--flush):
173202
174203
One or more positional entry points. For each entry point the following
@@ -202,7 +231,8 @@ Module hooks (loaded automatically by each mode):
202231
modules/dump.module --dump Exchange schema for candle dumps
203232
modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
204233
modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
205-
modules/simulator.module --simulator Exchange schema for crowd-ideas simulation
234+
modules/simulator.module --simulator Exchange schema for the crowd-ideas feasibility probe
235+
modules/tune.module --tune Exchange schema for the walk-forward parameter search
206236
207237
--flush has no associated module. It only removes dump subdirectories.
208238
@@ -232,7 +262,9 @@ Examples:
232262
node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
233263
node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
234264
node ${ENTRY_PATH} --simulator --symbol BTCUSDT ./assets/tv-ideas.normalized.jsonl
235-
node ${ENTRY_PATH} --simulator --symbol BTCUSDT --json --output jun_2026_sweep ./assets/tv-ideas.normalized.jsonl
265+
node ${ENTRY_PATH} --simulator --symbol BTCUSDT --json --output jun_2026_probe ./assets/tv-ideas.normalized.jsonl
266+
node ${ENTRY_PATH} --tune --symbol BTCUSDT ./assets/tv-ideas.normalized.jsonl
267+
node ${ENTRY_PATH} --tune --symbol BTCUSDT --split 0.7 --markdown --output jun_2026_tune ./assets/tv-ideas.normalized.jsonl
236268
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
237269
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
238270
node ${ENTRY_PATH} --init --output my-trading-bot

cli/src/main/simulator.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const validateIdea = (idea: any, line: number): string | null => {
3535
};
3636

3737
const toMarkdown = (result: ISimulatorResult): string => {
38+
const profitable = result.reports.filter(
39+
({ totalPnlPercent }) => totalPnlPercent > 0,
40+
).length;
3841
const lines: string[] = [];
3942
lines.push(`# Simulator Report — ${result.symbol}`);
4043
lines.push("");
@@ -43,30 +46,33 @@ const toMarkdown = (result: ISimulatorResult): string => {
4346
lines.push(`| Ideas (total / directional) | ${result.ideasTotal} / ${result.ideasDirectional} |`);
4447
lines.push(`| Profiles (truncated) | ${result.profileCount} (${result.truncatedCount}) |`);
4548
lines.push(`| Authors allowed / banned | ${result.allowedAuthors.length} / ${result.bannedAuthors.length} |`);
46-
lines.push(`| Grid points | ${result.reports.length} |`);
49+
lines.push(`| Profitable corridor | ${profitable} / ${result.reports.length} grid points |`);
4750
lines.push(`| Hold minutes avg / p95 / p99 | ${result.avgHoldMinutes.toFixed(0)} / ${result.p95HoldMinutes} / ${result.p99HoldMinutes} |`);
4851
lines.push("");
4952
lines.push(`## Ranking winners`);
5053
lines.push("");
51-
lines.push(`| Criterion | Stop% | Trailing% | Hold | Aligned | Track | HitRate | Trades | PNL% | WinRate | Sharpe | Sortino |`);
52-
lines.push(`| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |`);
54+
lines.push(`| Criterion | Stop% | Hold | Track | HitRate | Trades | PNL% | WinRate | Sharpe | Sortino |`);
55+
lines.push(`| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |`);
5356
for (const best of result.best) {
5457
if (!best.report) {
55-
lines.push(`| ${best.criterion} | — | — | — | — | — | — | — | — | — | — | — |`);
58+
lines.push(`| ${best.criterion} | — | — | — | — | — | — | — | — | — |`);
5659
continue;
5760
}
5861
const { point } = best.report;
5962
lines.push(
60-
`| ${best.criterion} | ${point.hardStopPercent} | ${point.trailingTakePercent} | ${point.holdMinutes / 60}h | ${point.minIdeasAligned} | ${point.minAuthorTrack} | ${point.minAuthorHitRate} | ` +
63+
`| ${best.criterion} | ${point.hardStopPercent} | ${point.holdMinutes / 60}h | ${point.minAuthorTrack} | ${point.minAuthorHitRate} | ` +
6164
`${best.report.trades} | ${best.report.totalPnlPercent.toFixed(2)}% | ${(best.report.winRate * 100).toFixed(0)}% | ${best.report.sharpe.toFixed(2)} | ${best.report.sortino.toFixed(2)} |`,
6265
);
6366
}
6467
lines.push("");
65-
lines.push(`## Allowed authors (production whitelist)`);
68+
lines.push(`## Allowed authors (whitelist of the sharpe winner rule)`);
6669
lines.push("");
6770
lines.push(`| Author | Ideas | Hits | HitRate |`);
6871
lines.push(`| --- | --- | --- | --- |`);
69-
for (const stat of result.authorStats.filter(({ banned }) => !banned)) {
72+
// артефакт авторов живёт по-победительно в best[] — ран-левел
73+
// authorStats в ISimulatorResult больше нет
74+
const sharpeBest = result.best.find(({ criterion }) => criterion === "sharpe");
75+
for (const stat of (sharpeBest?.authorStats ?? []).filter(({ banned }) => !banned)) {
7076
lines.push(`| ${stat.author} | ${stat.ideas} | ${stat.hits} | ${(stat.hitRate * 100).toFixed(0)}% |`);
7177
}
7278
return lines.join("\n");
@@ -172,22 +178,31 @@ export const main = async () => {
172178

173179
const symbol = <string>values.symbol || "BTCUSDT";
174180

181+
// Проба осуществимости, НЕ поиск заработка (подбор параметров — у
182+
// --tune): собирающая прибыль механика выключена, остаётся вопрос
183+
// "есть ли прибыльный коридор" по стопу x холду x правилу бана —
184+
// 48 точек, быстрый вердикт
175185
addSimulatorSchema({
176186
simulatorName: SIMULATOR_NAME,
177187
exchangeName,
178188
gridAxes: {
179-
hardStopPercent: [1, 1.5, 2, 2.5, 3, 4, 5, 7],
180-
trailingTakePercent: [0.5, 1, 1.5, 2, 3, 4],
189+
// грубая шкала катастрофы: коридор должен быть широким, не точкой
190+
hardStopPercent: [2, 3, 5, 7],
191+
// инертен: проба не собирает прибыль, выход — по времени или стопу
192+
trailingTakePercent: [100],
181193
holdMinutes: [24 * 60, 2 * 24 * 60, 3 * 24 * 60],
182-
minIdeasAligned: [1, 2, 3],
183-
minAuthorTrack: [2, 3, 5],
194+
// одного проверенного автора достаточно — консенсус не перебираем
195+
minIdeasAligned: [1],
196+
// правило бана — единственная перебираемая "умность" пробы
197+
minAuthorTrack: [3, 5],
184198
minAuthorHitRate: [0.5, 0.6],
185-
minWeightAligned: [0, 0.6, 1.2],
186-
profitLockPercent: [0, 1.5, 2.5],
187-
authorMetric: ["close", "reach"],
199+
minAuthorWilson: [0],
200+
minWeightAligned: [0],
201+
profitLockPercent: [0],
202+
authorMetric: ["close"],
188203
banCriteria: ["sharpe", "pnl"],
189-
minAuthorWilson: [0, 0.6],
190204
},
205+
reportOrder: "sharpe",
191206
callbacks: {
192207
onProgress: (symbol, stage, processed, total) => {
193208
if (values.verbose) {

cli/src/main/start.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import getEntry from "../helpers/getEntry";
33

44
declare const __PACKAGE_VERSION__: string;
55

6-
const MODES = ["backtest", "walker", "paper", "live", "main", "pine", "editor", "dump", "pnldebug", "brokerdebug", "simulator", "flush", "init", "docker", "help", "version"] as const;
6+
const MODES = ["backtest", "walker", "paper", "live", "main", "pine", "editor", "dump", "pnldebug", "brokerdebug", "simulator", "tune", "flush", "init", "docker", "help", "version"] as const;
77

88
const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
99

0 commit comments

Comments
 (0)