# -*- coding: utf-8 -*- """Generează data/Dashboard.xlsx dintr-un snapshot al data/backtest.xlsx. CITEȘTE backtest.xlsx (read-only, data_only=True) și SCRIE un fișier SEPARAT data/Dashboard.xlsx. NU atinge backtest.xlsx. Refolosește build_config() și build_dashboard() din generate_template.py — aceeași logică de Dashboard, dar pe un sheet Trades static (valori, fără formule), ca să țină backtest.xlsx mic. Reruleaza prin refresh_dashboard.bat (sau direct): python scripts/generate_dashboard.py IMPORTANT: deschide și SALVEAZĂ backtest.xlsx în Excel cel puțin o dată după ultima editare înainte de refresh. Scriptul citește valorile DEJA calculate de Excel (R_/$_/Bal_/helpere). Dacă nu ai salvat în Excel, cache-ul de valori lipsește și Dashboard-ul iese gol. (Aceeași constrângere ca Ferestre v2.) """ from pathlib import Path import openpyxl from openpyxl import Workbook from generate_template import ( build_config, build_dashboard, TRADES_HEADERS, MAX_ROWS, ) SRC = Path(__file__).resolve().parent.parent / "data" / "backtest.xlsx" OUT = Path(__file__).resolve().parent.parent / "data" / "Dashboard.xlsx" # Rândurile de input (galbene) din sheet-ul Config — singurele pe care le purtăm # din workbook-ul real (Account, Lot, limite prop, calibrare $/punct). Restul # celulelor Config sunt formule recreate de build_config(). CONFIG_INPUT_ROWS = [4, 5, 9, 10, 12, 14, 17, 19, 20, 21, 22, 25] def read_config_inputs(ws_cfg) -> dict[int, object]: """Citește valorile din coloana B a sheet-ului Config (read-only).""" vals: dict[int, object] = {} for r, row in enumerate( ws_cfg.iter_rows(min_row=1, max_row=40, min_col=2, max_col=2), start=1 ): # read_only poate întoarce EmptyCell (fără .value) pentru celule goale vals[r] = getattr(row[0], "value", None) return vals def apply_config_inputs(wb: Workbook, cfg_inputs: dict[int, object]) -> None: """Suprascrie inputurile Config cu valorile reale ale lui Marius.""" ws = wb["Config"] for r in CONFIG_INPUT_ROWS: v = cfg_inputs.get(r) if v is not None: ws.cell(row=r, column=2, value=v) def copy_trades_values(wb: Workbook, ws_src) -> None: """Creează un sheet Trades static (valori) în ordinea exactă TRADES_HEADERS. Mapează după NUMELE coloanei din sursă, ca literele din COL să corespundă cu ce așteaptă formulele/charturile din build_dashboard, indiferent de ordinea fizică din backtest.xlsx. """ ws = wb.create_sheet("Trades", 1) src_rows = ws_src.iter_rows(min_row=1, values_only=True) src_hdr = next(src_rows) src_idx = {name: i for i, name in enumerate(src_hdr) if name is not None} # Header (necesar pentru titles_from_data al charturilor Bal_*/BalProp_*) for col_idx, name in enumerate(TRADES_HEADERS, start=1): ws.cell(row=1, column=col_idx, value=name) # Date — rândurile 2..MAX_ROWS+1, ca rangurile Trades!$X$2:$X$501 să se alinieze r_out = 2 for src_row in src_rows: if r_out > MAX_ROWS + 1: break for col_idx, name in enumerate(TRADES_HEADERS, start=1): si = src_idx.get(name) if si is None or si >= len(src_row): continue val = src_row[si] if val is not None: ws.cell(row=r_out, column=col_idx, value=val) r_out += 1 ws.sheet_state = "hidden" # snapshot intern; Dashboard e singurul vizibil util def main() -> int: if not SRC.exists(): print(f"EROARE: nu găsesc {SRC}") return 1 wb_src = openpyxl.load_workbook(SRC, read_only=True, data_only=True) if "Trades" not in wb_src.sheetnames or "Config" not in wb_src.sheetnames: print("EROARE: backtest.xlsx nu are sheet-urile Trades + Config.") return 1 cfg_inputs = read_config_inputs(wb_src["Config"]) wb = Workbook() wb.remove(wb.active) build_config(wb) # Config la index 0 (cu formule) apply_config_inputs(wb, cfg_inputs) copy_trades_values(wb, wb_src["Trades"]) # Trades static la index 1 (ascuns) build_dashboard(wb) # Dashboard la index 2 — formule + charturi wb.active = wb.sheetnames.index("Dashboard") wb_src.close() wb.save(OUT) print(f"Scris {OUT}") return 0 if __name__ == "__main__": raise SystemExit(main())