create-tradejs 3.1.26 → 3.1.27-beta.247

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.
@@ -619,18 +619,32 @@ const equitySvg = (board) => {
619
619
  const position = y(value);
620
620
  return `<line x1="${left}" x2="${width - right}" y1="${position}" y2="${position}" class="grid"/><text x="${left - 18}" y="${position + 5}" text-anchor="end" class="axis">${escapeXml(formatCompact(value))}</text>`;
621
621
  }).join('');
622
- const years = [];
623
- const startYear = new Date(minTime).getUTCFullYear();
624
- const endYear = new Date(maxTime).getUTCFullYear();
625
- for (let year = startYear; year <= endYear; year += 1) {
626
- const timestamp = Date.UTC(year, 0, 1);
627
- if (timestamp >= minTime && timestamp <= maxTime)
628
- years.push({ year, timestamp });
629
- }
630
- const xGrid = years
631
- .map(({ year, timestamp }) => {
622
+ const spanDays = (maxTime - minTime) / 86_400_000;
623
+ const xTicks = Array.from({ length: 6 }, (_, index) => {
624
+ const timestamp = Math.round(
625
+ minTime + ((maxTime - minTime) * index) / 5,
626
+ );
627
+ const date = new Date(timestamp);
628
+ const label =
629
+ spanDays >= 730
630
+ ? `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}`
631
+ : spanDays >= 90
632
+ ? date.toLocaleDateString('en-CA', {
633
+ timeZone: 'UTC',
634
+ year: 'numeric',
635
+ month: 'short',
636
+ })
637
+ : date.toLocaleDateString('en-CA', {
638
+ timeZone: 'UTC',
639
+ month: 'short',
640
+ day: '2-digit',
641
+ });
642
+ return { label, timestamp };
643
+ });
644
+ const xGrid = xTicks
645
+ .map(({ label, timestamp }) => {
632
646
  const position = x(timestamp);
633
- return `<line x1="${position}" x2="${position}" y1="${top}" y2="${height - bottom}" class="grid faint"/><text x="${position}" y="${height - bottom + 34}" text-anchor="middle" class="axis">${year}</text>`;
647
+ return `<line x1="${position}" x2="${position}" y1="${top}" y2="${height - bottom}" class="grid faint"/><text x="${position}" y="${height - bottom + 34}" text-anchor="middle" class="axis">${escapeXml(label)}</text>`;
634
648
  })
635
649
  .join('');
636
650
  const curves = board.candidates
@@ -652,12 +666,12 @@ const equitySvg = (board) => {
652
666
  const column = index % columns;
653
667
  const row = Math.floor(index / columns);
654
668
  const lx = left + column * legendWidth;
655
- const ly = height - bottom + 85 + row * 58;
669
+ const ly = height - bottom + 110 + row * 58;
656
670
  return `<g transform="translate(${lx},${ly})"><rect width="18" height="18" rx="3" fill="${candidate.color}"/><text x="28" y="15" class="legendLabel">${escapeXml(truncate(candidate.label, 35))}</text><text x="28" y="37" class="legendMetric">N=${candidate.metrics.trades} · PnL=${formatNumber(candidate.metrics.pnl, 1)} · DD=${formatNumber(candidate.metrics.maxDrawdown, 1)}</text></g>`;
657
671
  })
658
672
  .join('');
659
673
  const selected = board.candidates.find(({ id }) => id === board.selectedId);
660
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(board.strategy)} final composition equity"><style>.bg{fill:#fff}.title{font:700 36px Arial,sans-serif;fill:#172033}.subtitle{font:18px Arial,sans-serif;fill:#687184}.axis{font:15px Arial,sans-serif;fill:#687184}.grid{stroke:#e2e6eb;stroke-width:1.5}.faint{opacity:.55}.legendLabel{font:600 17px Arial,sans-serif;fill:#263044}.legendMetric{font:15px Arial,sans-serif;fill:#687184}.axisTitle{font:17px Arial,sans-serif;fill:#394459}</style><rect width="100%" height="100%" class="bg"/><text x="${left}" y="58" class="title">${escapeXml(board.title)}</text><text x="${left}" y="92" class="subtitle">Baseline = production core + current AI-gate · candidates = core + own deterministic gate</text><text x="${left}" y="120" class="subtitle">Selected: ${escapeXml(selected.label)} · ${escapeXml(board.subtitle)}</text>${yGrid}${xGrid}<line x1="${left}" x2="${width - right}" y1="${y(0)}" y2="${y(0)}" stroke="#aab2bd" stroke-width="1.5"/>${curves}<line x1="${left}" x2="${left}" y1="${top}" y2="${height - bottom}" stroke="#7d8795" stroke-width="1.5"/><line x1="${left}" x2="${width - right}" y1="${height - bottom}" y2="${height - bottom}" stroke="#7d8795" stroke-width="1.5"/><text x="30" y="${top + plotHeight / 2}" transform="rotate(-90 30 ${top + plotHeight / 2})" text-anchor="middle" class="axisTitle">Cumulative PnL (${escapeXml(board.normalization.pnlUnit)})</text>${legend}</svg>`;
674
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeXml(board.strategy)} final composition equity"><style>.bg{fill:#fff}.title{font:700 36px Arial,sans-serif;fill:#172033}.subtitle{font:18px Arial,sans-serif;fill:#687184}.axis{font:15px Arial,sans-serif;fill:#687184}.grid{stroke:#e2e6eb;stroke-width:1.5}.faint{opacity:.55}.legendLabel{font:600 17px Arial,sans-serif;fill:#263044}.legendMetric{font:15px Arial,sans-serif;fill:#687184}.axisTitle{font:17px Arial,sans-serif;fill:#394459}</style><rect width="100%" height="100%" class="bg"/><text x="${left}" y="58" class="title">${escapeXml(board.title)}</text><text x="${left}" y="92" class="subtitle">Baseline = production core + current AI-gate · candidates = core + own deterministic gate</text><text x="${left}" y="120" class="subtitle">Selected: ${escapeXml(selected.label)} · ${escapeXml(board.subtitle)}</text>${yGrid}${xGrid}<line x1="${left}" x2="${width - right}" y1="${y(0)}" y2="${y(0)}" stroke="#aab2bd" stroke-width="1.5"/>${curves}<line x1="${left}" x2="${left}" y1="${top}" y2="${height - bottom}" stroke="#7d8795" stroke-width="1.5"/><line x1="${left}" x2="${width - right}" y1="${height - bottom}" y2="${height - bottom}" stroke="#7d8795" stroke-width="1.5"/><text x="${left + plotWidth / 2}" y="${height - bottom + 67}" text-anchor="middle" class="axisTitle">Exit date (UTC)</text><text x="30" y="${top + plotHeight / 2}" transform="rotate(-90 30 ${top + plotHeight / 2})" text-anchor="middle" class="axisTitle">Cumulative PnL (${escapeXml(board.normalization.pnlUnit)})</text>${legend}</svg>`;
661
675
  };
662
676
 
663
677
  const verifyCandidateArtifacts = async (board, artifactRoot) => {
@@ -176,6 +176,9 @@ test('verifies candidate artifacts and renders the dashboard and equity board',
176
176
  assert.match(dashboard, /Final compositions: PnL ↔ drawdown/u);
177
177
  assert.match(equity, /production core \+ current AI-gate/u);
178
178
  assert.match(equity, /candidate \+ own gate/u);
179
+ assert.match(equity, /Exit date \(UTC\)/u);
180
+ assert.match(equity, /Nov 2023/u);
181
+ assert.match(equity, /Mar 2024/u);
179
182
  });
180
183
 
181
184
  test('renders an additional terminal comparison without changing selectedId', async () => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "tradejs-skill-bundle/v1",
3
3
  "source": "TradeJS-Dev/TradeJS:.codex/skills",
4
- "bundleSha256": "30e3254a3cc0e82d8625b754897f226d23861d2357e23196197f69bfa6a568ee",
4
+ "bundleSha256": "80078cbf06c8239edffb266a36db3a9b7127000a26001381f7fa99b92aa3e4eb",
5
5
  "skills": [
6
6
  "ai-train-local-research",
7
7
  "backtest-config-redis",
@@ -44,8 +44,8 @@
44
44
  ".codex/skills/strategy-improvement-research/references/final-composition-board.md": "05b49ea4aba85792ccea0aeaf4ee2637b808157962f0a38631d6468c342349d6",
45
45
  ".codex/skills/strategy-improvement-research/scripts/build-final-composition-spec.mjs": "b51873691fafbcf2dd70d8c9a6da842fec5f8358346c632cfe79211db08b18ba",
46
46
  ".codex/skills/strategy-improvement-research/scripts/build-final-composition-spec.test.mjs": "85d06b8fe09ee7611a3fb989173c361ba32f0e9364422925ff221bfe127b2bfa",
47
- ".codex/skills/strategy-improvement-research/scripts/final-composition-board.mjs": "e5c5a1f91467f25e76f4f294b803c2602221d62fa5bd2be454a4345c3097773d",
48
- ".codex/skills/strategy-improvement-research/scripts/final-composition-board.test.mjs": "240c4cf13208a9fbbfcade622789dc9f142b68e79b490da7583ab793795d2f35",
47
+ ".codex/skills/strategy-improvement-research/scripts/final-composition-board.mjs": "759ed89f0afcfdff8c710a13d907b959f9bce648d185a15d16034b898656deda",
48
+ ".codex/skills/strategy-improvement-research/scripts/final-composition-board.test.mjs": "9bcb15f7a53ca9c55a1e45fc80f6030135952c69cc71179a18f0a714e21be38c",
49
49
  ".codex/skills/strategy-improvement-research/scripts/freeze-gate-variants.mjs": "17b3c868880ddd5b326d309e6e42eaeef1ea22d625a5db947997ac73b221ad6d",
50
50
  ".codex/skills/strategy-improvement-research/scripts/freeze-gate-variants.test.mjs": "963e1c64ab3deef11fd48a96e10f3827af38036dfe2eb28daf7849f19dd414d8",
51
51
  ".codex/skills/strategy-improvement-research/SKILL.md": "6964eb154e40b0b775aa2735b2ebbe998984479f46333ed03eab88d2ee150d48",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tradejs",
3
- "version": "3.1.26",
3
+ "version": "3.1.27-beta.247",
4
4
  "description": "Create a ready-to-run TradeJS project with local infrastructure and the Web UI.",
5
5
  "keywords": [
6
6
  "tradejs",