create-tradejs 3.1.22 → 3.1.23

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 (40) hide show
  1. package/README.md +19 -3
  2. package/dist/index.js +36 -5
  3. package/dist/skill-bundle/.codex/skills/ai-train-local-research/SKILL.md +596 -0
  4. package/dist/skill-bundle/.codex/skills/ai-train-local-research/references/gate-ablation.md +324 -0
  5. package/dist/skill-bundle/.codex/skills/ai-train-local-research/references/reporting.md +227 -0
  6. package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.mjs +5082 -0
  7. package/dist/skill-bundle/.codex/skills/ai-train-local-research/scripts/ai-gate-ablation.test.mjs +1170 -0
  8. package/dist/skill-bundle/.codex/skills/backtest-config-redis/SKILL.md +17 -0
  9. package/dist/skill-bundle/.codex/skills/backtest-config-redis/scripts/get_backtest_config.sh +21 -0
  10. package/dist/skill-bundle/.codex/skills/runtime-parity-mismatch-analysis/SKILL.md +146 -0
  11. package/dist/skill-bundle/.codex/skills/save-strategy-config-from-backtest/SKILL.md +58 -0
  12. package/dist/skill-bundle/.codex/skills/save-strategy-config-from-backtest/agents/openai.yaml +4 -0
  13. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/SKILL.md +334 -0
  14. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/references/research-notes.md +247 -0
  15. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/backtest-run-metrics.mjs +647 -0
  16. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/backtest-run-metrics.test.mjs +321 -0
  17. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/fast-ai-export-metrics.mjs +744 -0
  18. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/fast-ai-export-metrics.test.mjs +553 -0
  19. package/dist/skill-bundle/.codex/skills/strategy-backtest-research/scripts/research-notes-check.mjs +125 -0
  20. package/dist/skill-bundle/.codex/skills/strategy-improvement-research/SKILL.md +18 -1
  21. package/dist/skill-bundle/.codex/skills/strategy-release/SKILL.md +22 -0
  22. package/dist/skill-bundle/.codex/skills/strategy-release/agents/openai.yaml +4 -0
  23. package/dist/skill-bundle/.codex/skills/strategy-release/references/diagnose-live.md +126 -0
  24. package/dist/skill-bundle/.codex/skills/strategy-release/references/direction-policy.md +141 -0
  25. package/dist/skill-bundle/.codex/skills/strategy-release/references/directional-parameter-split.md +93 -0
  26. package/dist/skill-bundle/.codex/skills/strategy-release/references/evidence-limitations.md +76 -0
  27. package/dist/skill-bundle/.codex/skills/strategy-release/references/evidence-retention.md +157 -0
  28. package/dist/skill-bundle/.codex/skills/strategy-release/references/historical-hypothesis-audit.md +163 -0
  29. package/dist/skill-bundle/.codex/skills/strategy-release/references/professional-research-loop.md +198 -0
  30. package/dist/skill-bundle/.codex/skills/strategy-release/references/release-workflow.md +755 -0
  31. package/dist/skill-bundle/.codex/skills/strategy-release/references/research-objective.md +255 -0
  32. package/dist/skill-bundle/.codex/skills/strategy-release/references/verdict-contract.md +200 -0
  33. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/direction-policy-checkpoint.mjs +137 -0
  34. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/direction-policy-checkpoint.test.mjs +85 -0
  35. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/directional-parameter-checkpoint.mjs +149 -0
  36. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/directional-parameter-checkpoint.test.mjs +120 -0
  37. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/release-progress-checkpoint.mjs +621 -0
  38. package/dist/skill-bundle/.codex/skills/strategy-release/scripts/release-progress-checkpoint.test.mjs +349 -0
  39. package/dist/skill-bundle/.codex/tradejs-skill-bundle.json +44 -3
  40. package/package.json +1 -1
@@ -0,0 +1,321 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import {
5
+ buildConfigStatSummaries,
6
+ reconstructTrades,
7
+ summarizeResultStats,
8
+ summarizeTradeWindow,
9
+ } from './backtest-run-metrics.mjs';
10
+
11
+ const DAY_MS = 24 * 60 * 60 * 1000;
12
+
13
+ test('reconstructs scale-in levels and includes entry fees in trade pnl', () => {
14
+ const { trades, increaseEvents, incompleteCycles } = reconstructTrades([
15
+ [
16
+ {
17
+ timestamp: 100,
18
+ type: 'OPEN_LONG',
19
+ positionIntent: 'open',
20
+ profit: -0.1,
21
+ symbol: 'BTCUSDT',
22
+ direction: 'LONG',
23
+ orderId: 'open-1',
24
+ },
25
+ {
26
+ timestamp: 200,
27
+ type: 'OPEN_LONG',
28
+ positionIntent: 'increase',
29
+ profit: -0.2,
30
+ },
31
+ {
32
+ timestamp: 300,
33
+ type: 'OPEN_LONG',
34
+ positionIntent: 'increase',
35
+ profit: -0.3,
36
+ },
37
+ {
38
+ timestamp: 400,
39
+ type: 'OPEN_LONG',
40
+ positionIntent: 'increase',
41
+ profit: -0.4,
42
+ },
43
+ {
44
+ timestamp: 500,
45
+ type: 'TAKE_PROFIT_LONG',
46
+ profit: 5,
47
+ },
48
+ ],
49
+ ]);
50
+
51
+ assert.equal(incompleteCycles, 0);
52
+ assert.deepEqual(
53
+ increaseEvents.map((event) => event.level),
54
+ [2, 3, 4],
55
+ );
56
+ assert.deepEqual(trades, [
57
+ {
58
+ id: 'open-1',
59
+ timestamp: 500,
60
+ pnl: 4,
61
+ symbol: 'BTCUSDT',
62
+ direction: 'LONG',
63
+ exitReason: 'take_profit',
64
+ increases: 3,
65
+ },
66
+ ]);
67
+ });
68
+
69
+ test('keeps a trade open across partial take-profit fills', () => {
70
+ const { trades, incompleteCycles } = reconstructTrades([
71
+ [
72
+ {
73
+ timestamp: 100,
74
+ type: 'OPEN_LONG',
75
+ positionIntent: 'open',
76
+ qty: 1,
77
+ profit: -0.1,
78
+ symbol: 'BTCUSDT',
79
+ direction: 'LONG',
80
+ orderId: 'open-partial',
81
+ },
82
+ {
83
+ timestamp: 200,
84
+ type: 'TAKE_PROFIT_LONG',
85
+ qty: 0.4,
86
+ profit: 2,
87
+ },
88
+ {
89
+ timestamp: 300,
90
+ type: 'TAKE_PROFIT_LONG',
91
+ qty: 0.6,
92
+ profit: 3,
93
+ },
94
+ ],
95
+ ]);
96
+
97
+ assert.equal(incompleteCycles, 0);
98
+ assert.deepEqual(trades, [
99
+ {
100
+ id: 'open-partial',
101
+ timestamp: 300,
102
+ pnl: 4.9,
103
+ symbol: 'BTCUSDT',
104
+ direction: 'LONG',
105
+ exitReason: 'take_profit',
106
+ increases: 0,
107
+ },
108
+ ]);
109
+ });
110
+
111
+ test('summarizes strict loss, losing months, and scale-in counts', () => {
112
+ const trades = [
113
+ {
114
+ timestamp: Date.UTC(2026, 0, 10),
115
+ pnl: 4,
116
+ increases: 3,
117
+ symbol: 'BTCUSDT',
118
+ direction: 'LONG',
119
+ exitReason: 'take_profit',
120
+ },
121
+ {
122
+ timestamp: Date.UTC(2026, 1, 10),
123
+ pnl: -6,
124
+ increases: 1,
125
+ symbol: 'ETHUSDT',
126
+ direction: 'SHORT',
127
+ exitReason: 'stop_loss',
128
+ },
129
+ ];
130
+ const increaseEvents = [
131
+ { timestamp: Date.UTC(2026, 0, 9), level: 2 },
132
+ { timestamp: Date.UTC(2026, 0, 9), level: 3 },
133
+ { timestamp: Date.UTC(2026, 0, 9), level: 4 },
134
+ { timestamp: Date.UTC(2026, 1, 9), level: 2 },
135
+ ];
136
+ const summary = summarizeTradeWindow({
137
+ trades,
138
+ increaseEvents,
139
+ startTimestamp: Date.UTC(2026, 0, 1),
140
+ endTimestamp: Date.UTC(2026, 1, 28),
141
+ });
142
+
143
+ assert.equal(summary.core.trades, 2);
144
+ assert.equal(summary.core.totalPnl, -2);
145
+ assert.equal(summary.distribution.largestLoss, -6);
146
+ assert.equal(summary.risk.losingMonthsCount, 1);
147
+ assert.deepEqual(summary.increases.levels, { 2: 2, 3: 1, 4: 1 });
148
+ assert.deepEqual(summary.losingMonthValues, [{ month: '2026-02', pnl: -6 }]);
149
+ });
150
+
151
+ test('summarizes authoritative Redis stats without order artifacts', () => {
152
+ const summary = summarizeResultStats({
153
+ results: [
154
+ {
155
+ stat: {
156
+ netProfit: 12,
157
+ orders: 3,
158
+ wins: 2,
159
+ losses: 1,
160
+ maxDrawdown: 8,
161
+ },
162
+ },
163
+ {
164
+ stat: {
165
+ profit: -4,
166
+ orders: 2,
167
+ wins: 1,
168
+ losses: 1,
169
+ maxDrawdown: 15,
170
+ },
171
+ },
172
+ ],
173
+ startTimestamp: Date.UTC(2026, 0, 1),
174
+ endTimestamp: Date.UTC(2026, 0, 11),
175
+ });
176
+
177
+ assert.equal(summary.source, 'redis-result-stat');
178
+ assert.equal(summary.authoritativeAggregate, true);
179
+ assert.equal(summary.resultCount, 2);
180
+ assert.equal(summary.windowDays, 10);
181
+ assert.equal(summary.netProfit, 8);
182
+ assert.equal(summary.orders, 5);
183
+ assert.equal(summary.wins, 3);
184
+ assert.equal(summary.losses, 2);
185
+ assert.equal(summary.winRatePct, 60);
186
+ assert.equal(summary.pnlPerTrade, 1.6);
187
+ assert.equal(summary.observedCadenceTradesPerDay, 0.5);
188
+ assert.equal(summary.projectedCadence, null);
189
+ assert.equal(summary.worstSymbolMaxDrawdownPct, 15);
190
+ assert.match(summary.warnings[0], /not portfolio MaxDD/);
191
+ });
192
+
193
+ test('labels projected cadence and scales it by actual result count', () => {
194
+ const summary = summarizeResultStats({
195
+ results: [
196
+ { stat: { netProfit: 0, orders: 10, wins: 5, losses: 5 } },
197
+ { stat: { netProfit: 0, orders: 10, wins: 5, losses: 5 } },
198
+ ],
199
+ startTimestamp: 0,
200
+ endTimestamp: 100 * 24 * 60 * 60 * 1000,
201
+ projectedUniverse: 10,
202
+ });
203
+
204
+ assert.equal(summary.observedCadenceTradesPerDay, 0.2);
205
+ assert.deepEqual(summary.projectedCadence, {
206
+ label: 'projected cadence for 10 results',
207
+ projectedUniverse: 10,
208
+ actualResultCount: 2,
209
+ scaleFactor: 5,
210
+ tradesPerDay: 1,
211
+ });
212
+ });
213
+
214
+ test('uses null for undefined ratios and drawdown when there are no trades', () => {
215
+ const summary = summarizeResultStats({
216
+ results: [{ stat: { netProfit: 0, orders: 0 } }],
217
+ startTimestamp: 100,
218
+ endTimestamp: 100,
219
+ projectedUniverse: 550,
220
+ });
221
+
222
+ assert.equal(summary.winRatePct, null);
223
+ assert.equal(summary.pnlPerTrade, null);
224
+ assert.equal(summary.observedCadenceTradesPerDay, null);
225
+ assert.equal(summary.projectedCadence, null);
226
+ assert.equal(summary.worstSymbolMaxDrawdownPct, null);
227
+ });
228
+
229
+ test('keeps authoritative Redis stats separate for two grid configs', () => {
230
+ const manifest = {
231
+ status: 'completed',
232
+ testSuite: [
233
+ { configId: 'config-a', symbol: 'BTCUSDT' },
234
+ { configId: 'config-a', symbol: 'ETHUSDT' },
235
+ { configId: 'config-b', symbol: 'BTCUSDT' },
236
+ { configId: 'config-b', symbol: 'ETHUSDT' },
237
+ ],
238
+ };
239
+ const results = [
240
+ {
241
+ test: { configId: 'config-a', symbol: 'BTCUSDT' },
242
+ stat: { netProfit: 10, orders: 2, wins: 2, losses: 0 },
243
+ },
244
+ {
245
+ test: { configId: 'config-a', symbol: 'ETHUSDT' },
246
+ stat: { netProfit: -2, orders: 1, wins: 0, losses: 1 },
247
+ },
248
+ {
249
+ test: { configId: 'config-b', symbol: 'BTCUSDT' },
250
+ stat: { netProfit: -20, orders: 4, wins: 0, losses: 4 },
251
+ },
252
+ {
253
+ test: { configId: 'config-b', symbol: 'ETHUSDT' },
254
+ stat: { netProfit: 1, orders: 1, wins: 1, losses: 0 },
255
+ },
256
+ ];
257
+
258
+ const grouped = buildConfigStatSummaries({
259
+ results,
260
+ manifest,
261
+ startTimestamp: 0,
262
+ endTimestamp: 10 * DAY_MS,
263
+ });
264
+
265
+ assert.deepEqual(grouped.configIds, ['config-a', 'config-b']);
266
+ assert.equal(grouped.statSummary, null);
267
+ assert.equal(grouped.statSummariesByConfig['config-a'].netProfit, 8);
268
+ assert.equal(grouped.statSummariesByConfig['config-a'].orders, 3);
269
+ assert.equal(grouped.statSummariesByConfig['config-b'].netProfit, -19);
270
+ assert.equal(grouped.statSummariesByConfig['config-b'].orders, 5);
271
+ assert.equal(
272
+ grouped.statSummariesByConfig['config-a'].authoritativeAggregate,
273
+ true,
274
+ );
275
+ assert.deepEqual(grouped.statSummariesByConfig['config-b'].completion, {
276
+ status: 'complete',
277
+ manifestStatus: 'completed',
278
+ planned: 2,
279
+ completed: 2,
280
+ missing: 0,
281
+ extra: 0,
282
+ plannedSymbols: 2,
283
+ completedSymbols: 2,
284
+ errors: null,
285
+ errorStatus: 'not_persisted',
286
+ });
287
+ assert.match(grouped.warnings[0], /multiple configId buckets/);
288
+ });
289
+
290
+ test('marks a partial config aggregate non-authoritative', () => {
291
+ const grouped = buildConfigStatSummaries({
292
+ results: [
293
+ {
294
+ test: { configId: 'config-a', symbol: 'BTCUSDT' },
295
+ stat: { netProfit: 3, orders: 1, wins: 1, losses: 0 },
296
+ },
297
+ ],
298
+ manifest: {
299
+ status: 'completed',
300
+ testSuite: [
301
+ { configId: 'config-a', symbol: 'BTCUSDT' },
302
+ { configId: 'config-a', symbol: 'ETHUSDT' },
303
+ ],
304
+ },
305
+ startTimestamp: 0,
306
+ endTimestamp: 10 * DAY_MS,
307
+ });
308
+
309
+ assert.equal(grouped.statSummary.authoritativeAggregate, false);
310
+ assert.equal(grouped.statSummary.completion.status, 'partial');
311
+ assert.equal(grouped.statSummary.completion.planned, 2);
312
+ assert.equal(grouped.statSummary.completion.completed, 1);
313
+ assert.equal(grouped.statSummary.completion.missing, 1);
314
+ assert.equal(grouped.statSummary.completion.errors, null);
315
+ assert.equal(grouped.statSummary.completion.errorStatus, 'not_persisted');
316
+ assert.match(grouped.statSummary.warnings.at(-1), /not an authoritative/);
317
+ assert.match(
318
+ grouped.statSummary.warnings.at(-2),
319
+ /inspect the terminal\/report log/,
320
+ );
321
+ });