- PrimaWin_<idx> helper columns in Trades (per fereastră) - DASH_WIN_COL dict refactor (zero hardcoded litere) - Coloana Filtru (D) în window grid: rânduri Toate/Prima paralele - Score split T (Score_Toate) + U (Score_Prima), ambele hidden - TOP CANDIDATE: 2 sub-secțiuni — TOP 5 Toate + TOP 5 Prima - Config!B17 escape hatch (DA/NU) pentru performanță - 5 sample rows care exercită Prima edge cases - scripts/verify_template.py: 18 aserțiuni smoke test - .gitignore: data/backtest.backup-*.xlsx - sterse 2 backup-uri vechi din git Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
238 lines
7.7 KiB
Python
238 lines
7.7 KiB
Python
"""Smoke test for data/backtest.xlsx after regeneration.
|
|
|
|
Asserts column positions, sample row formula structure, and TOP CANDIDATE
|
|
formula sources. Run AFTER `python scripts/generate_template.py`.
|
|
|
|
Exit codes: 0 = all PASS, 1 = at least one FAIL, 2 = workbook missing.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from openpyxl import load_workbook
|
|
from openpyxl.utils import get_column_letter
|
|
|
|
# Romanian diacritics in test names — force UTF-8 stdout on Windows cp1252.
|
|
try:
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
except (AttributeError, OSError):
|
|
pass
|
|
|
|
OUTPUT = Path(__file__).resolve().parent.parent / "data" / "backtest.xlsx"
|
|
|
|
FAILURES: list[str] = []
|
|
|
|
|
|
def _report(name: str, ok: bool, detail: str = "") -> None:
|
|
tag = "[PASS]" if ok else "[FAIL]"
|
|
msg = f"{tag} {name}"
|
|
if detail:
|
|
msg += f" -- {detail}"
|
|
print(msg)
|
|
if not ok:
|
|
FAILURES.append(name)
|
|
|
|
|
|
def _find_row_with(ws, col_letter: str, needle: str) -> int | None:
|
|
"""Return 1-based row where ws[col_letter+row].value == needle, else None."""
|
|
for row in range(1, ws.max_row + 1):
|
|
val = ws[f"{col_letter}{row}"].value
|
|
if val == needle:
|
|
return row
|
|
return None
|
|
|
|
|
|
def _find_col_for_header(ws, row: int, needle: str) -> str | None:
|
|
for col_idx in range(1, ws.max_column + 1):
|
|
if ws.cell(row=row, column=col_idx).value == needle:
|
|
return get_column_letter(col_idx)
|
|
return None
|
|
|
|
|
|
def check_window_grid_headers(wb) -> None:
|
|
ws = wb["Dashboard"]
|
|
header_row = _find_row_with(ws, "A", "Fereastra")
|
|
if header_row is None:
|
|
_report("Dashboard window grid header row", False, "no row with A='Fereastra'")
|
|
return
|
|
_report("Dashboard window grid header row", True, f"row {header_row}")
|
|
|
|
expected = {
|
|
"A": "Fereastra",
|
|
"D": "Filtru",
|
|
"E": "Strategie",
|
|
"F": "N",
|
|
"S": "Status Edge",
|
|
"T": "Score_Toate",
|
|
"U": "Score_Prima",
|
|
}
|
|
for col, want in expected.items():
|
|
got = ws[f"{col}{header_row}"].value
|
|
if got == want:
|
|
_report(f"Dashboard header {col}={want!r}", True)
|
|
else:
|
|
actual_col = _find_col_for_header(ws, header_row, want)
|
|
detail = (
|
|
f"got {got!r} at {col}; expected {want!r}. "
|
|
f"Header {want!r} actually at column {actual_col}"
|
|
)
|
|
_report(f"Dashboard header {col}={want!r}", False, detail)
|
|
|
|
|
|
def check_hidden_columns(wb) -> None:
|
|
ws = wb["Dashboard"]
|
|
for col in ("T", "U"):
|
|
hidden = ws.column_dimensions[col].hidden is True
|
|
_report(
|
|
f"Dashboard column {col} hidden",
|
|
hidden,
|
|
"" if hidden else f"hidden={ws.column_dimensions[col].hidden!r}",
|
|
)
|
|
|
|
|
|
def check_primawin_headers(wb) -> tuple[str | None, int]:
|
|
"""Return (column letter of PrimaWin_0, count of PrimaWin_* headers)."""
|
|
ws = wb["Trades"]
|
|
win0_col: str | None = None
|
|
count = 0
|
|
for col_idx in range(1, ws.max_column + 1):
|
|
v = ws.cell(row=1, column=col_idx).value
|
|
if isinstance(v, str) and v.startswith("PrimaWin_"):
|
|
count += 1
|
|
if v == "PrimaWin_0":
|
|
win0_col = get_column_letter(col_idx)
|
|
if count == 0:
|
|
_report("Trades has PrimaWin_* headers", False, "none found")
|
|
else:
|
|
_report(
|
|
"Trades has PrimaWin_* headers",
|
|
True,
|
|
f"{count} columns; PrimaWin_0 at column {win0_col}",
|
|
)
|
|
return win0_col, count
|
|
|
|
|
|
def check_primawin_formula(wb, win0_col: str | None) -> None:
|
|
if win0_col is None:
|
|
_report("PrimaWin_0 sample row formula", False, "no PrimaWin_0 column")
|
|
return
|
|
ws = wb["Trades"]
|
|
formula = ws[f"{win0_col}2"].value
|
|
if not isinstance(formula, str):
|
|
_report(
|
|
"PrimaWin_0 sample row formula",
|
|
False,
|
|
f"row 2 value is {formula!r}, not a formula",
|
|
)
|
|
return
|
|
must_have = ["TIME(16,30,0)", "Config!$B$17", "COUNTIFS"]
|
|
missing = [token for token in must_have if token not in formula]
|
|
if missing:
|
|
_report(
|
|
"PrimaWin_0 sample row formula",
|
|
False,
|
|
f"missing tokens: {missing}; formula starts: {formula[:120]!r}",
|
|
)
|
|
else:
|
|
_report("PrimaWin_0 sample row formula", True, "TIME(16,30,0) + Config!$B$17 present")
|
|
|
|
|
|
def check_top_candidate_sources(wb) -> None:
|
|
ws = wb["Dashboard"]
|
|
title_toate = "TOP 5 FERESTRE — Toate trade-urile"
|
|
title_prima = "TOP 5 FERESTRE — Prima per Indicator"
|
|
|
|
# Fallback: scan loosely if the em-dash doesn't match.
|
|
def _find_title(prefix: str) -> int | None:
|
|
for row in range(1, ws.max_row + 1):
|
|
v = ws.cell(row=row, column=1).value
|
|
if isinstance(v, str) and v.startswith(prefix):
|
|
return row
|
|
return None
|
|
|
|
cases = [
|
|
("TOP 5 Toate", title_toate, "Toate", "LARGE($T$"),
|
|
("TOP 5 Prima", title_prima, "Prima per Indicator", "LARGE($U$"),
|
|
]
|
|
for name, exact_title, fallback_prefix, expected_token in cases:
|
|
title_row = _find_row_with(ws, "A", exact_title)
|
|
if title_row is None:
|
|
title_row = _find_title(f"TOP 5 FERESTRE")
|
|
# Narrow: must mention the discriminator
|
|
title_row = None
|
|
for row in range(1, ws.max_row + 1):
|
|
v = ws.cell(row=row, column=1).value
|
|
if isinstance(v, str) and "TOP 5 FERESTRE" in v and fallback_prefix in v:
|
|
title_row = row
|
|
break
|
|
if title_row is None:
|
|
_report(f"{name} title found", False, f"no row containing 'TOP 5 FERESTRE' + {fallback_prefix!r}")
|
|
continue
|
|
_report(f"{name} title found", True, f"row {title_row}")
|
|
|
|
# Look at the next few rows for a LARGE() formula in any column.
|
|
found = False
|
|
for offset in (1, 2, 3):
|
|
scan_row = title_row + offset
|
|
for col_idx in range(1, ws.max_column + 1):
|
|
cell = ws.cell(row=scan_row, column=col_idx).value
|
|
if isinstance(cell, str) and expected_token in cell:
|
|
_report(
|
|
f"{name} uses {expected_token}",
|
|
True,
|
|
f"row {scan_row} col {get_column_letter(col_idx)}",
|
|
)
|
|
found = True
|
|
break
|
|
if found:
|
|
break
|
|
if not found:
|
|
# Sample a formula for diagnostics.
|
|
sample = ws.cell(row=title_row + 1, column=1).value
|
|
_report(
|
|
f"{name} uses {expected_token}",
|
|
False,
|
|
f"token not found in rows {title_row+1}..{title_row+3}; sample A: {sample!r}",
|
|
)
|
|
|
|
|
|
def check_config_escape_hatch(wb) -> None:
|
|
ws = wb["Config"]
|
|
a17 = ws["A17"].value
|
|
b17 = ws["B17"].value
|
|
_report(
|
|
"Config!A17 = 'Activează filtru Prima'",
|
|
a17 == "Activează filtru Prima",
|
|
f"got {a17!r}",
|
|
)
|
|
_report("Config!B17 = 'DA'", b17 == "DA", f"got {b17!r}")
|
|
|
|
|
|
def main() -> int:
|
|
if not OUTPUT.exists():
|
|
print(f"Workbook not found: {OUTPUT}")
|
|
print("RUN python scripts/generate_template.py FIRST")
|
|
return 2
|
|
|
|
wb = load_workbook(OUTPUT, data_only=False)
|
|
|
|
print(f"Verifying {OUTPUT}")
|
|
print("-" * 60)
|
|
check_window_grid_headers(wb)
|
|
check_hidden_columns(wb)
|
|
win0_col, _ = check_primawin_headers(wb)
|
|
check_primawin_formula(wb, win0_col)
|
|
check_top_candidate_sources(wb)
|
|
check_config_escape_hatch(wb)
|
|
print("-" * 60)
|
|
if FAILURES:
|
|
print(f"{len(FAILURES)} FAIL: {FAILURES}")
|
|
return 1
|
|
print("ALL CHECKS PASSED")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|