create-tradejs 3.1.24-beta.240 → 3.1.25-beta.242
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/dist/skill-bundle/.codex/skills/ai-train-local-research/references/gate-ablation.md +9 -0
- package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.mjs +98 -1
- package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.test.mjs +46 -0
- package/dist/skill-bundle/.codex/skills/strategy-backtest-research/SKILL.md +5 -4
- package/dist/skill-bundle/.codex/skills/strategy-forward-start/SKILL.md +10 -4
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/SKILL.md +47 -11
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/references/final-composition-board.md +188 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/build-final-composition-spec.mjs +240 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/build-final-composition-spec.test.mjs +159 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/final-composition-board.mjs +825 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/final-composition-board.test.mjs +223 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/freeze-gate-variants.mjs +103 -0
- package/dist/skill-bundle/.codex/skills/strategy-improvement-research/scripts/freeze-gate-variants.test.mjs +52 -0
- package/dist/skill-bundle/.codex/tradejs-skill-bundle.json +14 -7
- package/package.json +1 -1
|
@@ -116,6 +116,10 @@ set, pass `--spec path/to/variants.json`:
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
JSON reports include timestamp-grouped cumulative `equity` arrays for the
|
|
120
|
+
current-gate baseline and every variant. Use these checksum-bound arrays for
|
|
121
|
+
final-composition charts instead of reconstructing curves by hand.
|
|
122
|
+
|
|
119
123
|
## Expression Grammar
|
|
120
124
|
|
|
121
125
|
Expressions support parentheses, `&&`, `||`, and comparisons:
|
|
@@ -151,6 +155,11 @@ Plain `--testSplit` exposes test metrics and cannot be called untouched after
|
|
|
151
155
|
the report is read. Open the sealed tail once with the frozen fixed-rule
|
|
152
156
|
ablation.
|
|
153
157
|
|
|
158
|
+
When several core candidates must be compared, pass the same exact UTC
|
|
159
|
+
`--tuningSince` and `--testSince` boundaries to every candidate ablation.
|
|
160
|
+
Exact boundaries take precedence over ratio splits and keep sparse candidates
|
|
161
|
+
on one calendar partition contract.
|
|
162
|
+
|
|
154
163
|
## Cross-Strategy Feasibility
|
|
155
164
|
|
|
156
165
|
Use `--crossStrategy` to test whether the latest merged export for every
|
package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.mjs
CHANGED
|
@@ -34,6 +34,8 @@ Options:
|
|
|
34
34
|
--terminalWindows <list> Terminal windows in days (default: 180,90,30,7)
|
|
35
35
|
--validationSplit <ratio> Trailing timestamp-grouped tuning share (default: 0.25)
|
|
36
36
|
--testSplit <ratio> Later timestamp-grouped test share (default: 0)
|
|
37
|
+
--tuningSince <timestamp> Exact UTC boundary where tuning starts
|
|
38
|
+
--testSince <timestamp> Exact UTC boundary where test starts
|
|
37
39
|
--capacities <list> Capacity stress limits (default: 1,3,5)
|
|
38
40
|
--maxLossValue <n> Per-order loss budget for capacity stress
|
|
39
41
|
--featurePattern <regex> Inventory matching causal feature paths
|
|
@@ -89,6 +91,8 @@ export const parseCliArgs = (argv) => {
|
|
|
89
91
|
terminalWindows: DEFAULT_WINDOWS,
|
|
90
92
|
validationSplit: 0.25,
|
|
91
93
|
testSplit: 0,
|
|
94
|
+
tuningSince: null,
|
|
95
|
+
testSince: null,
|
|
92
96
|
capacities: DEFAULT_CAPACITIES,
|
|
93
97
|
maxLossValue: null,
|
|
94
98
|
variants: [],
|
|
@@ -161,6 +165,13 @@ export const parseCliArgs = (argv) => {
|
|
|
161
165
|
options.testSplit = Number.isFinite(parsed)
|
|
162
166
|
? Math.max(0, Math.min(0.9, parsed))
|
|
163
167
|
: 0;
|
|
168
|
+
} else if (name === 'tuningSince' || name === 'testSince') {
|
|
169
|
+
const numeric = Number(value);
|
|
170
|
+
const parsed = Number.isFinite(numeric) ? numeric : Date.parse(value);
|
|
171
|
+
if (!Number.isFinite(parsed)) {
|
|
172
|
+
throw new Error(`Invalid timestamp for --${name}: ${value}`);
|
|
173
|
+
}
|
|
174
|
+
options[name] = parsed;
|
|
164
175
|
} else if (name === 'capacities') {
|
|
165
176
|
options.capacities = parseNumberList(value, DEFAULT_CAPACITIES);
|
|
166
177
|
} else if (name === 'maxLossValue') {
|
|
@@ -1016,6 +1027,28 @@ export const splitRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
|
1016
1027
|
};
|
|
1017
1028
|
};
|
|
1018
1029
|
|
|
1030
|
+
export const splitRowsByTimestampBounds = (
|
|
1031
|
+
rows,
|
|
1032
|
+
tuningSince,
|
|
1033
|
+
testSince,
|
|
1034
|
+
) => {
|
|
1035
|
+
if (!Number.isFinite(tuningSince) || !Number.isFinite(testSince)) {
|
|
1036
|
+
throw new Error(
|
|
1037
|
+
'Exact calendar partitions require both tuningSince and testSince',
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
if (tuningSince >= testSince) {
|
|
1041
|
+
throw new Error('tuningSince must be earlier than testSince');
|
|
1042
|
+
}
|
|
1043
|
+
return {
|
|
1044
|
+
train: rows.filter((row) => row.timestamp < tuningSince),
|
|
1045
|
+
tuning: rows.filter(
|
|
1046
|
+
(row) => row.timestamp >= tuningSince && row.timestamp < testSince,
|
|
1047
|
+
),
|
|
1048
|
+
test: rows.filter((row) => row.timestamp >= testSince),
|
|
1049
|
+
};
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1019
1052
|
const summarizeSplit = (rows, selector, summaryOptions) =>
|
|
1020
1053
|
summarizeRows(selectRows(rows, selector), getPeriodDays(rows), {
|
|
1021
1054
|
...summaryOptions,
|
|
@@ -1034,6 +1067,34 @@ const summarizeDirections = (rows, selector, summaryOptions) =>
|
|
|
1034
1067
|
]),
|
|
1035
1068
|
);
|
|
1036
1069
|
|
|
1070
|
+
export const buildEquitySeries = (
|
|
1071
|
+
rows,
|
|
1072
|
+
selector,
|
|
1073
|
+
minTimestamp = rows[0]?.timestamp ?? null,
|
|
1074
|
+
maxTimestamp = rows.at(-1)?.timestamp ?? null,
|
|
1075
|
+
) => {
|
|
1076
|
+
if (!Number.isFinite(minTimestamp) || !Number.isFinite(maxTimestamp)) {
|
|
1077
|
+
return [];
|
|
1078
|
+
}
|
|
1079
|
+
const byTimestamp = new Map();
|
|
1080
|
+
for (const row of rows) {
|
|
1081
|
+
if (!selector(row)) continue;
|
|
1082
|
+
byTimestamp.set(
|
|
1083
|
+
row.timestamp,
|
|
1084
|
+
(byTimestamp.get(row.timestamp) ?? 0) + row.profit,
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
let cumulative = 0;
|
|
1088
|
+
const result = [[minTimestamp, 0]];
|
|
1089
|
+
for (const [timestamp, profit] of byTimestamp) {
|
|
1090
|
+
cumulative += profit;
|
|
1091
|
+
if (timestamp === minTimestamp) result[0] = [timestamp, cumulative];
|
|
1092
|
+
else result.push([timestamp, cumulative]);
|
|
1093
|
+
}
|
|
1094
|
+
if (result.at(-1)[0] !== maxTimestamp) result.push([maxTimestamp, cumulative]);
|
|
1095
|
+
return result;
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1037
1098
|
const buildPeriodDirectionSummaries = ({
|
|
1038
1099
|
rows,
|
|
1039
1100
|
selector,
|
|
@@ -3778,6 +3839,8 @@ export const buildAblationReport = ({
|
|
|
3778
3839
|
terminalWindows,
|
|
3779
3840
|
validationSplit,
|
|
3780
3841
|
testSplit = 0,
|
|
3842
|
+
tuningSince = null,
|
|
3843
|
+
testSince = null,
|
|
3781
3844
|
capacities = DEFAULT_CAPACITIES,
|
|
3782
3845
|
maxLossValue = null,
|
|
3783
3846
|
filePaths,
|
|
@@ -3790,7 +3853,15 @@ export const buildAblationReport = ({
|
|
|
3790
3853
|
if (!rows.length) throw new Error('No rows were evaluated');
|
|
3791
3854
|
const minTimestamp = rows[0].timestamp;
|
|
3792
3855
|
const maxTimestamp = rows.at(-1).timestamp;
|
|
3793
|
-
|
|
3856
|
+
if ((tuningSince == null) !== (testSince == null)) {
|
|
3857
|
+
throw new Error(
|
|
3858
|
+
'Exact calendar partitions require both tuningSince and testSince',
|
|
3859
|
+
);
|
|
3860
|
+
}
|
|
3861
|
+
const exactCalendarPartitions = tuningSince != null;
|
|
3862
|
+
const split = exactCalendarPartitions
|
|
3863
|
+
? splitRowsByTimestampBounds(rows, tuningSince, testSince)
|
|
3864
|
+
: splitRowsByTimestamp(rows, validationSplit, testSplit);
|
|
3794
3865
|
const partitionEvidence = (partitionRows) => ({
|
|
3795
3866
|
rows: partitionRows.length,
|
|
3796
3867
|
events: new Set(partitionRows.map((row) => row.timestamp)).size,
|
|
@@ -3804,6 +3875,12 @@ export const buildAblationReport = ({
|
|
|
3804
3875
|
const summaryOptions = { capacities, maxLossValue };
|
|
3805
3876
|
const baselineSelector = (row) => baselineSelectedAt(row, minQuality);
|
|
3806
3877
|
const baseline = {
|
|
3878
|
+
equity: buildEquitySeries(
|
|
3879
|
+
rows,
|
|
3880
|
+
baselineSelector,
|
|
3881
|
+
minTimestamp,
|
|
3882
|
+
maxTimestamp,
|
|
3883
|
+
),
|
|
3807
3884
|
periods: buildPeriodSummaries({
|
|
3808
3885
|
rows,
|
|
3809
3886
|
selector: baselineSelector,
|
|
@@ -3849,6 +3926,12 @@ export const buildAblationReport = ({
|
|
|
3849
3926
|
quality: variant.quality,
|
|
3850
3927
|
direction: variant.direction,
|
|
3851
3928
|
expression: variant.expression,
|
|
3929
|
+
equity: buildEquitySeries(
|
|
3930
|
+
rows,
|
|
3931
|
+
candidateSelector,
|
|
3932
|
+
minTimestamp,
|
|
3933
|
+
maxTimestamp,
|
|
3934
|
+
),
|
|
3852
3935
|
periods: buildPeriodSummaries({
|
|
3853
3936
|
rows,
|
|
3854
3937
|
selector: candidateSelector,
|
|
@@ -3919,6 +4002,13 @@ export const buildAblationReport = ({
|
|
|
3919
4002
|
terminalWindows,
|
|
3920
4003
|
validationSplit,
|
|
3921
4004
|
testSplit,
|
|
4005
|
+
partitionMode: exactCalendarPartitions ? 'exact-calendar' : 'ratio',
|
|
4006
|
+
tuningSince: exactCalendarPartitions
|
|
4007
|
+
? new Date(tuningSince).toISOString()
|
|
4008
|
+
: null,
|
|
4009
|
+
testSince: exactCalendarPartitions
|
|
4010
|
+
? new Date(testSince).toISOString()
|
|
4011
|
+
: null,
|
|
3922
4012
|
capacities,
|
|
3923
4013
|
maxLossValue,
|
|
3924
4014
|
trainRows: split.train.length,
|
|
@@ -4935,6 +5025,11 @@ export const main = async () => {
|
|
|
4935
5025
|
sourceRepositoryRoot,
|
|
4936
5026
|
);
|
|
4937
5027
|
if (options.crossStrategy) {
|
|
5028
|
+
if (options.tuningSince != null || options.testSince != null) {
|
|
5029
|
+
throw new Error(
|
|
5030
|
+
'--tuningSince/--testSince are supported by candidate ablation only',
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
4938
5033
|
const groups = latestDatasetGroupsByStrategy(
|
|
4939
5034
|
await listDatasetGroups(outDir),
|
|
4940
5035
|
);
|
|
@@ -5037,6 +5132,8 @@ export const main = async () => {
|
|
|
5037
5132
|
terminalWindows: options.terminalWindows,
|
|
5038
5133
|
validationSplit: options.validationSplit,
|
|
5039
5134
|
testSplit: options.testSplit,
|
|
5135
|
+
tuningSince: options.tuningSince,
|
|
5136
|
+
testSince: options.testSince,
|
|
5040
5137
|
capacities: options.capacities,
|
|
5041
5138
|
maxLossValue: options.maxLossValue,
|
|
5042
5139
|
sourceRepositoryRoot,
|
package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.test.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
balanceCrossStrategyRows,
|
|
12
12
|
buildAblationReport,
|
|
13
13
|
buildCrossStrategyReport,
|
|
14
|
+
buildEquitySeries,
|
|
14
15
|
buildMovingAverageVariants,
|
|
15
16
|
calculateMovingAverageGrid,
|
|
16
17
|
buildShiftedProfitLookups,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
partitionCrossStrategyFeatures,
|
|
36
37
|
resolveArtifactProjectRoot,
|
|
37
38
|
splitRowsByTimestamp,
|
|
39
|
+
splitRowsByTimestampBounds,
|
|
38
40
|
summarizeRows,
|
|
39
41
|
summarizeMovingAverageRedundancy,
|
|
40
42
|
} from './ai-gate-ablation.mjs';
|
|
@@ -195,6 +197,8 @@ test('parses repeated variants and research windows', () => {
|
|
|
195
197
|
'--qualityThresholds',
|
|
196
198
|
'4,5',
|
|
197
199
|
'--testSplit=0.2',
|
|
200
|
+
'--tuningSince=2025-01-01T00:00:00.000Z',
|
|
201
|
+
'--testSince=2025-07-01T00:00:00.000Z',
|
|
198
202
|
'--capacities=1,3,5',
|
|
199
203
|
'--maxLossValue=0.2',
|
|
200
204
|
]);
|
|
@@ -207,6 +211,8 @@ test('parses repeated variants and research windows', () => {
|
|
|
207
211
|
assert.deepEqual(options.terminalWindows, [180, 90, 30, 7]);
|
|
208
212
|
assert.deepEqual(options.qualityThresholds, [4, 5]);
|
|
209
213
|
assert.equal(options.testSplit, 0.2);
|
|
214
|
+
assert.equal(options.tuningSince, Date.UTC(2025, 0, 1));
|
|
215
|
+
assert.equal(options.testSince, Date.UTC(2025, 6, 1));
|
|
210
216
|
assert.deepEqual(options.capacities, [1, 3, 5]);
|
|
211
217
|
assert.equal(options.maxLossValue, 0.2);
|
|
212
218
|
});
|
|
@@ -1048,6 +1054,46 @@ test('groups split and fan-out metrics by decision timestamp', () => {
|
|
|
1048
1054
|
);
|
|
1049
1055
|
});
|
|
1050
1056
|
|
|
1057
|
+
test('uses exact calendar boundaries without splitting timestamp events', () => {
|
|
1058
|
+
const train = Date.UTC(2025, 0, 1);
|
|
1059
|
+
const tuning = Date.UTC(2025, 3, 1);
|
|
1060
|
+
const testStart = Date.UTC(2025, 6, 1);
|
|
1061
|
+
const rows = [
|
|
1062
|
+
{ timestamp: train, id: 'train' },
|
|
1063
|
+
{ timestamp: tuning, id: 'tuning-a' },
|
|
1064
|
+
{ timestamp: tuning, id: 'tuning-b' },
|
|
1065
|
+
{ timestamp: testStart, id: 'test' },
|
|
1066
|
+
];
|
|
1067
|
+
|
|
1068
|
+
const split = splitRowsByTimestampBounds(rows, tuning, testStart);
|
|
1069
|
+
|
|
1070
|
+
assert.deepEqual(split.train.map((row) => row.id), ['train']);
|
|
1071
|
+
assert.deepEqual(split.tuning.map((row) => row.id), [
|
|
1072
|
+
'tuning-a',
|
|
1073
|
+
'tuning-b',
|
|
1074
|
+
]);
|
|
1075
|
+
assert.deepEqual(split.test.map((row) => row.id), ['test']);
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
test('builds timestamp-grouped cumulative equity with common endpoints', () => {
|
|
1079
|
+
const start = Date.UTC(2025, 0, 1);
|
|
1080
|
+
const end = Date.UTC(2025, 0, 3);
|
|
1081
|
+
const rows = [
|
|
1082
|
+
{ timestamp: start, profit: 2, keep: true },
|
|
1083
|
+
{ timestamp: start, profit: -1, keep: true },
|
|
1084
|
+
{ timestamp: Date.UTC(2025, 0, 2), profit: 10, keep: false },
|
|
1085
|
+
{ timestamp: end, profit: 3, keep: true },
|
|
1086
|
+
];
|
|
1087
|
+
|
|
1088
|
+
assert.deepEqual(
|
|
1089
|
+
buildEquitySeries(rows, (row) => row.keep, start, end),
|
|
1090
|
+
[
|
|
1091
|
+
[start, 1],
|
|
1092
|
+
[end, 4],
|
|
1093
|
+
],
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1051
1097
|
test('builds full and terminal period comparisons for a candidate', () => {
|
|
1052
1098
|
const start = Date.UTC(2025, 0, 1);
|
|
1053
1099
|
const variants = [parseVariant('keep::filter[SHORT]::feature.keep == true')];
|
|
@@ -113,14 +113,15 @@ and PnL reconcile within the documented per-symbol rounding tolerance, use the
|
|
|
113
113
|
row-level export as the authoritative trade-economic total instead of swapping
|
|
114
114
|
in the cent-rounded Redis aggregate.
|
|
115
115
|
|
|
116
|
-
For
|
|
116
|
+
For every `$strategy-improvement-research` final composition, extend the same permanent report
|
|
117
117
|
to `1095d/1460d/1825d-or-exact-maximum/365d/180d/90d/30d/7d`. When cached
|
|
118
118
|
coverage is shorter than 1825 days, report the exact covered duration (for
|
|
119
119
|
example 1800d) and do not label it a complete five-year window. Reuse this
|
|
120
120
|
tool's full ALL/LONG/SHORT statistics; do not replace them with a compact custom
|
|
121
|
-
parser. The
|
|
122
|
-
on
|
|
123
|
-
the
|
|
121
|
+
parser. The improvement workflow then builds one independently verified
|
|
122
|
+
deterministic gate on each exact core export, including the baseline export, and
|
|
123
|
+
compares only the resulting `core + own gate` compositions. Raw-core metrics
|
|
124
|
+
remain diagnostic and must never be mixed into the final-composition chart.
|
|
124
125
|
|
|
125
126
|
## Required Core Metric Cohorts
|
|
126
127
|
|
|
@@ -51,10 +51,16 @@ Do not infer production from Redis.
|
|
|
51
51
|
Stop before mutation if the target binding is ambiguous, credentials/registry
|
|
52
52
|
authorization is missing, the candidate is not reproducible or implementable,
|
|
53
53
|
the maximum-covered historical edge is non-positive, required evidence/chart
|
|
54
|
-
hashes are missing, required checks fail, another rollout
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
hashes are missing, required checks fail, another rollout for the same strategy
|
|
55
|
+
is active, a deployment or rollout cutover on the target is still in flight, or
|
|
56
|
+
safe atomic deployment is unavailable. An active risk-1 forward test for a
|
|
57
|
+
different strategy on the same deployment or account is not a blocker: preserve
|
|
58
|
+
its declaration and evidence, and do not confuse the presence of its active
|
|
59
|
+
rollout ledger with an in-flight cutover. Multiple strategies may intentionally
|
|
60
|
+
run concurrent forward tests, including when the operator uses
|
|
61
|
+
`MAX_LOSS_VALUE=1` as the forward-test marker. These operational and
|
|
62
|
+
falsifiability boundaries are not waived by operator-directed mode. Give the
|
|
63
|
+
exact command or UI boundary the user must complete; never start an interactive
|
|
58
64
|
authentication flow.
|
|
59
65
|
|
|
60
66
|
## Release and configure
|
|
@@ -48,11 +48,17 @@ hypothesis-family choice, trial budget, parent/child decisions, candidate
|
|
|
48
48
|
selection, and final handoff. Delegate each preregistered core implementation
|
|
49
49
|
and backtest to `$strategy-backtest-research`; that skill returns reconciled
|
|
50
50
|
experiment evidence and does not choose the next candidate. After freezing the
|
|
51
|
-
core
|
|
52
|
-
|
|
51
|
+
complete core board, delegate one independent deterministic-gate board for the
|
|
52
|
+
baseline and for every complete, reconciled, behavior-changing core candidate
|
|
53
|
+
to `$ai-train-local-research`; it must not reopen core selection. Read those
|
|
53
54
|
specialist skills when their stage begins instead of duplicating their command,
|
|
54
55
|
metric, or reporting contracts here.
|
|
55
56
|
|
|
57
|
+
Before preregistration, read
|
|
58
|
+
[`references/final-composition-board.md`](references/final-composition-board.md).
|
|
59
|
+
It defines candidate-specific gate isolation, common partitions, the final
|
|
60
|
+
composition ledger, and the mandatory visual artifacts.
|
|
61
|
+
|
|
56
62
|
## Required contour
|
|
57
63
|
|
|
58
64
|
1. Start a new immutable research lineage. Freeze data bounds, point-in-time
|
|
@@ -74,15 +80,36 @@ metric, or reporting contracts here.
|
|
|
74
80
|
tail. Stop only when a reproducible best candidate is frozen, the fresh
|
|
75
81
|
budget is exhausted, or every remaining family has a recorded hard causal
|
|
76
82
|
blocker.
|
|
77
|
-
6. Keep one chronological tail sealed during discovery when
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
6. Keep one common chronological core tail sealed during core discovery when
|
|
84
|
+
coverage permits. Freeze the complete raw-core board before any gate result
|
|
85
|
+
may influence core-family selection. Track all exposed core and gate tests
|
|
86
|
+
for multiple-testing/deflated-Sharpe interpretation.
|
|
87
|
+
7. Freeze an acceptance-grade export for the baseline and every complete,
|
|
88
|
+
reconciled, behavior-changing core candidate. Give each export its own gate
|
|
89
|
+
discovery and exactly one selected deterministic gate; never reuse another
|
|
90
|
+
core's gate metrics or compare a gated candidate with an ungated baseline.
|
|
91
|
+
On the production core export, also replay the exact current production
|
|
92
|
+
AI-gate and bind it to its checksum-verified gate-authority report. This
|
|
93
|
+
`production core + current AI-gate` composition is the mandatory baseline;
|
|
94
|
+
the production core with its newly rebuilt gate is a separate research
|
|
95
|
+
candidate and must not replace the current-gate baseline.
|
|
96
|
+
Use common calendar train/tuning/test boundaries and open every sealed gate
|
|
97
|
+
tail together only after all per-core gate variants are frozen. Run every
|
|
98
|
+
gate board through `$ai-train-local-research`; do not retune or relabel the
|
|
99
|
+
raw-core result inside gate tooling.
|
|
100
|
+
8. Build the final leaderboard from the mandatory `production core + current
|
|
101
|
+
AI-gate` baseline and `core + own deterministic gate` research candidates,
|
|
102
|
+
including `production core + rebuilt gate` as its own candidate. Preserve
|
|
103
|
+
raw-core metrics in a separate diagnostic table. Apply the final selection
|
|
104
|
+
rules and multiple-testing denominator to the complete core-by-gate trial
|
|
105
|
+
ledger, not only to the winning core or gate.
|
|
106
|
+
9. Generate and checksum the mandatory final-composition dashboard and
|
|
107
|
+
cumulative-equity chart with the permanent script from the referenced
|
|
108
|
+
contract. A metrics-only handoff or a chart mixing raw and gated candidates
|
|
109
|
+
is incomplete.
|
|
110
|
+
10. Run package formatting, typecheck, tests, and build in the lineage worktree.
|
|
111
|
+
Commit only the selected candidate and its tests on that worktree branch;
|
|
112
|
+
preserve rejected experiments as immutable evidence, not source clutter.
|
|
86
113
|
|
|
87
114
|
## Selection objective
|
|
88
115
|
|
|
@@ -102,3 +129,12 @@ source/package SHA, full resolved core config, deterministic gate/context,
|
|
|
102
129
|
direction policy, evidence hashes, trial count, metric matrix, chart, freshness,
|
|
103
130
|
limitations, and forward-test eligibility. End with the selected candidate,
|
|
104
131
|
why it beat production and prior candidates, and exactly one next skill.
|
|
132
|
+
|
|
133
|
+
The handoff must link both visual artifacts for the final compositions:
|
|
134
|
+
|
|
135
|
+
- `final-composition-dashboard.{svg,png}` with KPI deltas against the exact
|
|
136
|
+
production-core + current-AI-gate baseline, terminal
|
|
137
|
+
`365d/180d/90d/30d/7d` PnL, and the all-candidate PnL-versus-MaxDD plane;
|
|
138
|
+
- `final-composition-equity.{svg,png}` with cumulative PnL for the gated
|
|
139
|
+
production baseline, the rebuilt production-core gate, and every final
|
|
140
|
+
`core + own gate` composition over the common comparison window.
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Final composition board
|
|
2
|
+
|
|
3
|
+
Use this contract after raw-core discovery is complete. Its purpose is to make
|
|
4
|
+
the research unit an executable strategy composition, not an intermediate core
|
|
5
|
+
or a gate tuned on another core's signal population.
|
|
6
|
+
|
|
7
|
+
## Gate isolation
|
|
8
|
+
|
|
9
|
+
1. Freeze the raw-core board first. It contains the authoritative baseline and
|
|
10
|
+
every complete, reconciled, behavior-changing core candidate. Core family
|
|
11
|
+
continuation and rescue choices use only raw-core evidence; a gate result
|
|
12
|
+
cannot reopen or rewrite that board.
|
|
13
|
+
2. Produce one acceptance-grade raw AI export for every board member, including
|
|
14
|
+
the baseline. Bind its core research ID, resolved-config SHA-256, source diff
|
|
15
|
+
or commit, export SHA-256, window, universe, execution assumptions, and
|
|
16
|
+
context lineage.
|
|
17
|
+
3. Run `$ai-train-local-research` independently for every export. Each core gets
|
|
18
|
+
its own current-gate control, pocket discovery, five-variant maximum ablation,
|
|
19
|
+
selected gate expression or implementation, full structured report, and
|
|
20
|
+
fingerprint. On the production core export, preserve two distinct final
|
|
21
|
+
compositions: the exact current AI-gate replay, checksum-bound to its
|
|
22
|
+
gate-authority report, and the independently rebuilt gate. The current gate
|
|
23
|
+
is the baseline; the rebuilt gate is a research candidate. A no-op,
|
|
24
|
+
zero-approval, or rejected gate still receives a checksum-bound report and
|
|
25
|
+
explicit disposition; it is not silently omitted.
|
|
26
|
+
4. Freeze common calendar train, tuning, and test boundaries once in the parent
|
|
27
|
+
lineage. Keep all rows sharing a timestamp in one partition. Candidate
|
|
28
|
+
exports may have different row counts, but final comparison windows and test
|
|
29
|
+
timestamps must not move to make one candidate look better. Discovery may
|
|
30
|
+
inspect only train and tuning. Open all candidate test tails together after
|
|
31
|
+
every gate spec is immutable.
|
|
32
|
+
Use `ai-gate-ablation.mjs --tuningSince <UTC> --testSince <UTC>` with the
|
|
33
|
+
same values for every candidate; ratio-only splits are not comparable when
|
|
34
|
+
core event cadence differs.
|
|
35
|
+
5. Count every inspected core and gate behavior in the final trial ledger. The
|
|
36
|
+
selected strategy is `core identity + candidate-specific gate identity +
|
|
37
|
+
context identity + direction policy + quality threshold`. Reusing economics
|
|
38
|
+
from another core, using the production gate without replaying it on the new
|
|
39
|
+
export, or comparing a gated candidate with raw baseline is invalid.
|
|
40
|
+
|
|
41
|
+
The five gate slots are a hard per-core maximum, not a target. Prefer a compact
|
|
42
|
+
causal board: current gate, explicit direction pass-through/block when the raw
|
|
43
|
+
side evidence warrants it, one causal pocket, one protected pocket, and one
|
|
44
|
+
direction-aware replacement. Do not fill slots with adjacent threshold nudges.
|
|
45
|
+
|
|
46
|
+
Freeze independently discovered causal pockets into an auditable candidate
|
|
47
|
+
spec before opening comparison evidence:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node .codex/skills/strategy-improvement-research/scripts/freeze-gate-variants.mjs \
|
|
51
|
+
--pocket data/ai/output/<candidate>-pocket.json \
|
|
52
|
+
--candidateId <candidate> --output data/ai/output/<candidate>-gate-spec.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Final comparison
|
|
56
|
+
|
|
57
|
+
Build two separate tables:
|
|
58
|
+
|
|
59
|
+
- **Raw-core diagnostics**: baseline and every raw core with the fixed
|
|
60
|
+
ALL/LONG/SHORT contract. These metrics explain the mechanism but never enter
|
|
61
|
+
the final strategy leaderboard as if they were deployable compositions.
|
|
62
|
+
- **Final compositions**: the exact production core + current AI-gate baseline,
|
|
63
|
+
the production core + rebuilt gate candidate, and exactly one frozen own-gate
|
|
64
|
+
result for every other raw-core candidate. Apply economic validity, support,
|
|
65
|
+
drawdown, cadence, concentration, stability, cost, and holdout rules here.
|
|
66
|
+
|
|
67
|
+
Normalize PnL and drawdown to one recorded `MAX_LOSS_VALUE` risk unit before
|
|
68
|
+
ranking or charting. Use the same half-open comparison window and terminal
|
|
69
|
+
`365d`, `180d`, `90d`, `30d`, and `7d` slices for every composition, including
|
|
70
|
+
zero-trade windows. A gate without sufficient untouched support remains visible
|
|
71
|
+
as `research-only` or `blocked`; the chart is not evidence of readiness.
|
|
72
|
+
|
|
73
|
+
## Mandatory visual artifacts
|
|
74
|
+
|
|
75
|
+
Create the board from authoritative structured reports with:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
node .codex/skills/strategy-improvement-research/scripts/build-final-composition-spec.mjs \
|
|
79
|
+
--selection data/strategy-release/<lineage>/final-composition-selection.json \
|
|
80
|
+
--output data/strategy-release/<lineage>/final-composition-board.json \
|
|
81
|
+
--terminalComparisonIds <selected-id>,<additional-candidate-id>
|
|
82
|
+
node .codex/skills/strategy-improvement-research/scripts/final-composition-board.mjs \
|
|
83
|
+
--spec data/strategy-release/<lineage>/final-composition-board.json \
|
|
84
|
+
--outDir data/strategy-release/<lineage>/final-composition-charts
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The input uses schema `tradejs-final-composition-board/v1`. Every composition
|
|
88
|
+
must declare `composition.kind = core+deterministic-gate` and `gateSource` as
|
|
89
|
+
`current` or `variant`, the same normalized risk unit, complete full/terminal
|
|
90
|
+
metrics, cumulative equity points, and three checksum-verified inputs: core
|
|
91
|
+
result, core export, and gate report. The baseline additionally requires the
|
|
92
|
+
checksum-verified current `gateAuthority`. The script recomputes those hashes,
|
|
93
|
+
requires `baselineId` to identify the current-gate composition, and derives the
|
|
94
|
+
composition fingerprint; do not paste an unverified fingerprint into the spec.
|
|
95
|
+
`--terminalComparisonIds` is an optional presentation override for a derived
|
|
96
|
+
board: it leaves `selectedId` unchanged and allows a versioned comparison chart
|
|
97
|
+
without rewriting the frozen selection artifact.
|
|
98
|
+
|
|
99
|
+
Required outputs:
|
|
100
|
+
|
|
101
|
+
- `final-composition-dashboard.svg` and `.png`: selected composition KPI cards
|
|
102
|
+
versus the production core + current AI-gate baseline, grouped terminal PnL bars with trade counts for
|
|
103
|
+
the baseline plus `terminalComparisonIds`, a PnL-versus-realized-MaxDD plot
|
|
104
|
+
of every final composition, and limitations. `terminalComparisonIds`
|
|
105
|
+
defaults to `[selectedId]`; it may include up to three candidate IDs, must
|
|
106
|
+
include `selectedId`, and does not change the research selection;
|
|
107
|
+
- `final-composition-equity.svg` and `.png`: cumulative PnL curves for the
|
|
108
|
+
current production composition and every research composition on the common
|
|
109
|
+
time axis;
|
|
110
|
+
- `final-composition-summary.json`: derived composition fingerprints, source
|
|
111
|
+
hashes, normalized metrics, and hashes of all four rendered files.
|
|
112
|
+
|
|
113
|
+
SVG is the canonical deterministic rendering. PNG is the chat-ready rendering
|
|
114
|
+
of the same SVG. Link or display both charts in the final answer and immutable
|
|
115
|
+
research note. Store the complete board spec and summary in Project-owned
|
|
116
|
+
evidence; paths alone do not replace the note's machine-readable metrics.
|
|
117
|
+
|
|
118
|
+
## Spec shape
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"schema": "tradejs-final-composition-board/v1",
|
|
123
|
+
"strategy": "ExampleStrategy",
|
|
124
|
+
"researchId": "example-improvement-20260825-v1",
|
|
125
|
+
"title": "ExampleStrategy final compositions",
|
|
126
|
+
"subtitle": "Common cache-only window; sealed test opened once",
|
|
127
|
+
"baselineId": "production-current-gate",
|
|
128
|
+
"selectedId": "candidate-a-own-gate",
|
|
129
|
+
"terminalComparisonIds": ["candidate-a-own-gate", "candidate-b-own-gate"],
|
|
130
|
+
"comparisonWindow": { "start": 1690848000000, "end": 1787616000000 },
|
|
131
|
+
"normalization": { "pnlUnit": "research PnL", "maxLossValue": 10 },
|
|
132
|
+
"limitations": ["Point-in-time universe is incomplete"],
|
|
133
|
+
"candidates": [
|
|
134
|
+
{
|
|
135
|
+
"id": "production-current-gate",
|
|
136
|
+
"label": "production core + current AI-gate",
|
|
137
|
+
"role": "baseline",
|
|
138
|
+
"status": "eligible",
|
|
139
|
+
"color": "#315f7d",
|
|
140
|
+
"riskUnit": 10,
|
|
141
|
+
"composition": {
|
|
142
|
+
"kind": "core+deterministic-gate",
|
|
143
|
+
"gateSource": "current",
|
|
144
|
+
"coreResearchId": "example-control",
|
|
145
|
+
"coreConfigSha256": "<sha256>",
|
|
146
|
+
"coreResult": {
|
|
147
|
+
"path": "data/research/core/example-control/result.json",
|
|
148
|
+
"sha256": "<sha256>"
|
|
149
|
+
},
|
|
150
|
+
"coreExport": {
|
|
151
|
+
"path": "data/ai/export/example.jsonl",
|
|
152
|
+
"sha256": "<sha256>"
|
|
153
|
+
},
|
|
154
|
+
"gateReport": {
|
|
155
|
+
"path": "data/ai/output/example-gate.json",
|
|
156
|
+
"sha256": "<sha256>"
|
|
157
|
+
},
|
|
158
|
+
"gateAuthority": {
|
|
159
|
+
"path": "data/ai/output/example-current-gate-authority.json",
|
|
160
|
+
"sha256": "<sha256>"
|
|
161
|
+
},
|
|
162
|
+
"gateFingerprint": "<sha256>",
|
|
163
|
+
"configFingerprint": "<sha256>",
|
|
164
|
+
"contextFingerprint": "<sha256>",
|
|
165
|
+
"directionPolicy": "both",
|
|
166
|
+
"minQuality": 4
|
|
167
|
+
},
|
|
168
|
+
"metrics": {
|
|
169
|
+
"trades": 100,
|
|
170
|
+
"pnl": 250,
|
|
171
|
+
"profitFactor": 1.5,
|
|
172
|
+
"maxDrawdown": 70
|
|
173
|
+
},
|
|
174
|
+
"terminal": [
|
|
175
|
+
{ "days": 365, "trades": 40, "pnl": 120 },
|
|
176
|
+
{ "days": 180, "trades": 20, "pnl": 60 },
|
|
177
|
+
{ "days": 90, "trades": 10, "pnl": 30 },
|
|
178
|
+
{ "days": 30, "trades": 3, "pnl": 8 },
|
|
179
|
+
{ "days": 7, "trades": 1, "pnl": 2 }
|
|
180
|
+
],
|
|
181
|
+
"equity": [
|
|
182
|
+
[1690848000000, 0],
|
|
183
|
+
[1787615999999, 250]
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
```
|