Files
atm-backtesting/scripts/generate_dashboard.py
Marius 45e6505afa separa Dashboard de Trades: backtest.xlsx 39MB -> 1MB
backtest.xlsx ramane doar Config + Trades (editat zilnic, rapid la salvat).
Dashboard-ul devine fisier separat data/Dashboard.xlsx, generat la comanda:

- scripts/generate_dashboard.py: citeste backtest.xlsx read-only/data_only,
  reutilizeaza build_dashboard() pe un sheet Trades static, scrie Dashboard.xlsx
- scripts/strip_dashboard.py: migrare unica prin chirurgie pe zip (pastreaza
  dropdown-urile x14 din Trades; openpyxl le-ar fi sters)
- refresh_dashboard.bat: wrapper dublu-click (regenereaza + deschide)
- build_workbook() nu mai include Dashboard; graficele de echitate eliminate
- data/Dashboard.xlsx ignorat (output regenerabil)

Sincronizare la comanda (nu live): ruleaza refresh_dashboard.bat dupa ce
salvezi backtest.xlsx in Excel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 23:52:58 +03:00

121 lines
4.3 KiB
Python

# -*- 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())