Files
atm-backtesting/scripts/generate_mentor_check.py
Marius Mutu 11dfe59116 verificare backtest mentor: M2D DOW una pe seara
Adaugat generate_mentor_check.py care transpune backtest-ul mentorului
(M2D DOW UNA PE SEARA.xlsx) in schema noastra (Config + Trades + Dashboard)
ca sa verificam ca WR, R:R si profit factor ies la fel cu cifrele declarate
de mentor. Output: backtest-atm-m2d-una-pe-seara.xlsx.
2026-05-26 00:17:25 +03:00

602 lines
24 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Generator pentru data/backtest-atm-m2d-una-pe-seara.xlsx.
Transpune backtest-ul mentorului (M2D DOW UNA PE SEARA.xlsx) în schema noastră
completă (Config + Trades + Dashboard) ca să verificăm dacă WR, R:R, profit
factor etc. ies la fel cu cifrele declarate de mentor.
Reguli de transpunere:
- Mentor TP1 → TP0 nostru ; Mentor TP2 → TP1 nostru ; Mentor TP3 → TP2 nostru
- Regula R:R 1:1: SL plan = TP3 plan (când e atins) sau |valoarea negativă|
- Pentru cazurile intermediare (TP3=0), SL = ultimul TP atins × 1.36
Output: data/backtest-atm-m2d-una-pe-seara.xlsx
Rulare:
python scripts/generate_mentor_check.py
"""
from __future__ import annotations
from datetime import date, datetime, time
from pathlib import Path
from openpyxl import load_workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
# Reutilizare directă: schema completă (Config, Trades cu formule, Dashboard)
from generate_template import (
BORDER,
CENTER,
COL,
DERIVED_FILL,
HEADER_FILL,
HEADER_FONT,
INPUT_FILL,
STRAT_KEYS,
STRAT_LABELS,
SUBTITLE_FONT,
TITLE_FONT,
build_workbook,
)
MENTOR_FILE = Path(__file__).resolve().parent.parent / "data" / "M2D DOW UNA PE SEARA.xlsx"
OUTPUT = Path(__file__).resolve().parent.parent / "data" / "backtest-atm-m2d-una-pe-seara.xlsx"
# Raport mediu TP3/TP2 observat în datele complete ale mentorului
RATIO = 1.36
# Cutoff calcule mentor: mentorul a actualizat statisticile doar pe trade-urile
# până la 24.04.2026 INCLUSIV; restul (post-24.04) au fost doar înregistrate.
CUTOFF_DATE = date(2026, 4, 24)
# Stiluri suplimentare pentru sheet-ul Comparatie
OK_FILL = PatternFill("solid", fgColor="C6EFCE")
DIFF_FILL = PatternFill("solid", fgColor="FFC7CE")
NOTE_FONT = Font(name="Calibri", size=10, italic=True, color="595959")
# ---------------------------------------------------------------------------
# Derivare per trade
# ---------------------------------------------------------------------------
def _round2(x: float) -> float:
return round(x, 2)
def derive(tp1m, tp2m, tp3m, ls):
"""Mapează (TP1, TP2, TP3, L/S) mentor → (sl, tp0, tp1, tp2, outcome, direction).
Cele 4 cazuri din planul aprobat:
1. Valoare negativă pe orice coloană → SL hit
2. TP3 > 0 → a atins maximul (R:R 1:1)
3. TP2 > 0, TP3 = 0 → intermediar 2
4. TP1 > 0, TP2 = TP3 = 0 → intermediar 1
"""
direction = "Buy" if ls == "L" else "Sell"
vals = [tp1m or 0, tp2m or 0, tp3m or 0]
if min(vals) < 0:
sl = abs(min(vals))
tp2 = sl
tp1 = sl / RATIO
tp0 = tp1 / 1.65
outcome = "SL"
elif tp3m and tp3m > 0:
sl = tp3m
tp0, tp1, tp2 = tp1m, tp2m, tp3m
outcome = "TP2"
elif tp2m and tp2m > 0:
sl = tp2m * RATIO
tp0, tp1, tp2 = tp1m, tp2m, sl
outcome = "TP1"
elif tp1m and tp1m > 0:
sl = tp1m * RATIO
tp0 = tp1m
tp2 = sl
tp1 = (tp0 + tp2) / 2
outcome = "TP0"
else:
# Toate 0 — BE (nu ar trebui să apară pentru rânduri cu L/S non-null, dar fallback)
sl = 0.10
tp0, tp1, tp2 = 0.04, 0.07, 0.10
outcome = ""
return _round2(sl), _round2(tp0), _round2(tp1), _round2(tp2), outcome, direction
# ---------------------------------------------------------------------------
# Citire mentor file
# ---------------------------------------------------------------------------
def _parse_date(v):
if isinstance(v, datetime):
return v.date()
if isinstance(v, date):
return v
if isinstance(v, str):
try:
return datetime.strptime(v, "%d.%m.%Y").date()
except ValueError:
return None
return None
def read_mentor():
"""Returnează lista trade-urilor și statistici (subset vs total).
Important: mentorul a actualizat calculele finale (rândurile 167-175) DOAR
pe trade-urile până la CUTOFF_DATE (24.04.2026 inclusiv). Tot ce e după
e doar înregistrat, dar nu intră în statisticile lui declarate.
"""
wb = load_workbook(MENTOR_FILE, data_only=True)
ws = wb.active
trades = [] # toate trade-urile (114) — populate în Trades sheet
raw_pl_tp1 = [] # P/L TP1 mentor pentru fiecare trade, pentru reconciliere
in_subset_flags = [] # bool: True dacă data <= CUTOFF_DATE
days_no_trade_total = 0
days_no_trade_subset = 0
for r in range(2, 167):
data_v = _parse_date(ws.cell(r, 2).value)
if data_v is None:
continue
ls = ws.cell(r, 3).value
if ls is None:
days_no_trade_total += 1
if data_v <= CUTOFF_DATE:
days_no_trade_subset += 1
continue
ora = ws.cell(r, 4).value
tp1m = ws.cell(r, 6).value
tp2m = ws.cell(r, 7).value
tp3m = ws.cell(r, 8).value
sl, tp0, tp1, tp2, outcome, direction = derive(tp1m, tp2m, tp3m, ls)
# Sanity check: dacă SL < TP0 sau ordinea TP-urilor e inversată, marchează
notes = ""
if sl > 0 and tp0 > sl:
notes = f"ANOMALIE: TP0={tp0} > SL={sl}. Verifică mentor file (probabil zecimale lipsă): TP1m={tp1m}, TP2m={tp2m}, TP3m={tp3m}"
elif sl > 0 and tp1 > sl:
notes = f"ANOMALIE: TP1={tp1} > SL={sl}. Verifică mentor file: TP1m={tp1m}, TP2m={tp2m}, TP3m={tp3m}"
trades.append({
"data": data_v,
"ora": ora,
"direction": direction,
"sl": sl,
"tp0": tp0,
"tp1": tp1,
"tp2": tp2,
"outcome": outcome,
"notes": notes,
})
raw_pl_tp1.append(tp1m or 0)
in_subset_flags.append(data_v <= CUTOFF_DATE)
return trades, raw_pl_tp1, in_subset_flags, days_no_trade_total, days_no_trade_subset
# ---------------------------------------------------------------------------
# Override Config + parametri suplimentari
# ---------------------------------------------------------------------------
def override_config(wb):
"""Configurează cont prop $100K cu scaling teoretic 1% = $1000 (poziție = cont).
Vezi nota din plan: cifrele declarate de mentor sunt scalate la nominal $100K
(poziție full-leverage), nu la cele 0.8 contracte CFD reale.
"""
ws = wb["Config"]
ws["B4"] = 100000 # Account Size Start ($)
ws["B9"] = 100000 # Account Prop Start ($)
ws["B10"] = 1 # Contracte per trade (teoretic 1 = poziție = cont)
ws["B11"] = 1000 # $ per 1% per contract ($100K nominal × 1%)
ws["C11"] = "Setup mentor: 1% pe $100K nominal = $1,000; deci 0.10% = $100"
# Parametri suplimentari pentru sheet Comparatie (B18-B20)
ws["A18"] = "Nominal cont prop ($)"
ws["B18"] = 100000
ws["C18"] = "Folosit în Comparatie pentru conversia P/L % → $"
ws["A19"] = "Conturi prop simultane"
ws["B19"] = 8
ws["C19"] = "D175 din fișierul mentorului (= 8 conturi $100K)"
ws["A20"] = "Luni operare"
ws["B20"] = 7
ws["C20"] = "Divizor din H175 (oct-mai = 7 luni complete)"
for r in (18, 19, 20):
ws.cell(row=r, column=2).fill = INPUT_FILL
ws.cell(row=r, column=2).border = BORDER
ws["B18"].number_format = "$#,##0"
# ---------------------------------------------------------------------------
# Populare Trades sheet
# ---------------------------------------------------------------------------
def populate_trades(wb, trades):
"""Populează celulele input ale Trades pentru fiecare trade transpus.
Formulele derivate (Zi, Sesiune, R_*, $_*, Bal_*, etc.) sunt deja prezente
pe toate rândurile (puse de build_trades în generate_template) și citesc
celulele input. Doar input cells trebuie completate.
"""
ws = wb["Trades"]
# Şterg sample rows existente (rândurile 2-6 sunt sample din build_trades)
# Voi suprascrie primele len(trades) rânduri, dar trebuie să curăţ Notes
# pe sample rows ca să nu rămână comentarii vechi.
for r in range(2, 2 + len(trades)):
ws[f'{COL["Notes"]}{r}'] = None
for offset, t in enumerate(trades):
r = 2 + offset
ws[f"B{r}"] = t["data"]
ws[f"C{r}"] = t["ora"]
ws[f'{COL["Strategie"]}{r}'] = "M2D"
ws[f'{COL["Indicator"]}{r}'] = "DIA"
ws[f'{COL["TF"]}{r}'] = "3min"
ws[f'{COL["Direcție"]}{r}'] = t["direction"]
ws[f'{COL["SL %"]}{r}'] = t["sl"]
ws[f'{COL["TP0 %"]}{r}'] = t["tp0"]
ws[f'{COL["TP1 %"]}{r}'] = t["tp1"]
ws[f'{COL["TP2 %"]}{r}'] = t["tp2"]
ws[f'{COL["Outcome"]}{r}'] = t["outcome"]
if t.get("notes"):
ws[f'{COL["Notes"]}{r}'] = t["notes"]
# ---------------------------------------------------------------------------
# Sheet Comparatie
# ---------------------------------------------------------------------------
def _style_header(cell):
cell.font = HEADER_FONT
cell.fill = HEADER_FILL
cell.alignment = CENTER
cell.border = BORDER
def _style_ok_diff(cell, threshold_cell_addr):
"""Aplică OK/DIFF în funcție de coloana Diff."""
cell.alignment = CENTER
cell.border = BORDER
def build_comparatie(wb, trades, raw_pl_tp1, in_subset_flags,
days_no_trade_subset, days_no_trade_total):
"""Construieşte sheet-ul Comparatie cu 3 secţiuni clare.
Cheia: mentorul a actualizat calculele finale DOAR pe primele 104 trade-uri
(până la 24.04.2026 inclusiv). Restul (10 trade-uri post-cutoff) au fost
înregistrate dar NU intră în statistici. Secțiunea A reconciliază exact
pe subset, deci toate metricile trebuie să iasă OK.
"""
ws = wb.create_sheet("Comparatie")
ws.sheet_view.showGridLines = False
n_trades = len(trades)
n_subset = sum(in_subset_flags) # 104
last_row = 1 + n_trades # rândul ultimei tranzacții (header = row 1)
last_row_subset = 1 + n_subset # ultimul rând din subset (105)
# ---------- TITLU + CONTEXT ----------
ws["A1"] = "COMPARATIE — programul reproduce calculele mentorului?"
ws["A1"].font = TITLE_FONT
ws.merge_cells("A1:F1")
context = (
"Mentorul a actualizat statisticile finale (rândurile 167-175 din fișierul lui) "
"DOAR pe primele 104 trade-uri (până la 24.04.2026 inclusiv). "
"Trade-urile #105-114 (29.04 → 15.05.2026) au fost înregistrate dar NU intră în "
"statisticile lui declarate.\n"
"→ Secțiunea A reconciliază pe SUBSET (104 trade-uri = ce a calculat mentorul). "
"Dacă programul îi reproduce calculele, toate trebuie să iasă OK.\n"
"→ Secțiunea A2 arată cifrele pe TOATE 114 (informativ — diff-ul față de subset = "
"rezultatul trade-urilor post-cutoff).\n"
"→ Secțiunea B simulează profitul lunar pe cont prop (replică formula H175 a mentorului)."
)
ws["A2"] = context
ws["A2"].font = NOTE_FONT
ws["A2"].alignment = Alignment(wrap_text=True, vertical="top")
ws.merge_cells("A2:F2")
ws.row_dimensions[2].height = 85
# Stochez P/L TP1 mentor în coloana AC pentru formule (ascunsă)
# Primele n_subset valori (rândurile 2..1+n_subset) sunt subset
# Restul (1+n_subset+1..1+n_trades) sunt post-cutoff
ws["AC1"] = "P/L TP1 mentor (stocat — primele 104 = subset, restul = post-24.04)"
ws["AC1"].font = Font(name="Calibri", size=9, italic=True, color="9E9E9E")
# Reordonez: pun întâi cele din subset, apoi cele din rest, ca să corespundă
# cu range-urile AC2:AC105 (subset) și AC106:AC115 (rest)
subset_vals = [v for v, in_s in zip(raw_pl_tp1, in_subset_flags) if in_s]
rest_vals = [v for v, in_s in zip(raw_pl_tp1, in_subset_flags) if not in_s]
all_ordered = subset_vals + rest_vals
for offset, v in enumerate(all_ordered):
ws.cell(row=2 + offset, column=29, value=v)
ws.column_dimensions["AC"].hidden = True
rng_subset = f"$AC$2:$AC${last_row_subset}" # 104 trade-uri
rng_total = f"$AC$2:$AC${1 + n_trades}" # 114 trade-uri
# ============================================================
# SECŢIUNE A: Reconciliere cifre mentor (pe subset 104)
# ============================================================
row = 4
ws.cell(row=row, column=1,
value="A. Reconciliere cifre mentor pe subset (primele 104 trade-uri, până 24.04.2026)").font = SUBTITLE_FONT
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=6)
row += 1
headers = ["Metric", "Mentor declarat", "Program (subset 104)", "Diff", "OK?", "Notă"]
for c, h in enumerate(headers, start=1):
_style_header(ws.cell(row=row, column=c, value=h))
row += 1
rows_A = [
("Total trade-uri", 104,
f"=COUNT({rng_subset})", "D170 din mentor"),
("Wins (P/L TP1 > 0)", 81,
f"=COUNTIF({rng_subset},\">0\")", "D168"),
("Losses (P/L TP1 < 0)", 23,
f"=COUNTIF({rng_subset},\"<0\")", "D167"),
("Zile fără trade (în subset)", 36, days_no_trade_subset, "D169"),
("Win Rate", 0.7788,
f"=COUNTIF({rng_subset},\">0\")/(COUNTIF({rng_subset},\">0\")+COUNTIF({rng_subset},\"<0\"))",
"D171 = wins/(wins+losses)"),
("Avg Win (%)", 0.0774,
f"=AVERAGEIF({rng_subset},\">0\")", "D173"),
("Avg Loss (%) abs", 0.1243,
f"=-AVERAGEIF({rng_subset},\"<0\")", "D172"),
("Profit Factor (avg_win/avg_loss)", 0.6227,
f"=AVERAGEIF({rng_subset},\">0\")/(-AVERAGEIF({rng_subset},\"<0\"))",
"D174 — definiție mentor: raport medii"),
]
section_A_first_row = row
for label, mentor_val, prog_formula, note in rows_A:
ws.cell(row=row, column=1, value=label).border = BORDER
ws.cell(row=row, column=2, value=mentor_val).border = BORDER
ws.cell(row=row, column=3, value=prog_formula).border = BORDER
ws.cell(row=row, column=4, value=f"=C{row}-B{row}").border = BORDER
tolerance = 0.5 if isinstance(mentor_val, int) and mentor_val > 10 else 0.001
ok_formula = f'=IF(ABS(D{row})<{tolerance},"OK","DIFF")'
ws.cell(row=row, column=5, value=ok_formula).border = BORDER
ws.cell(row=row, column=5).alignment = CENTER
ws.cell(row=row, column=6, value=note).font = NOTE_FONT
ws.cell(row=row, column=6).border = BORDER
ws.cell(row=row, column=6).alignment = Alignment(wrap_text=True, vertical="center")
row += 1
# Format procentual pentru rândurile de procent (oraș 4=WR, 5=AvgWin, 6=AvgLoss)
for pct_row_offset in (4, 5, 6):
for c in (2, 3):
ws.cell(row=section_A_first_row + pct_row_offset, column=c).number_format = "0.0000"
for c in (2, 3):
ws.cell(row=section_A_first_row + 7, column=c).number_format = "0.0000" # PF
row += 2
# ============================================================
# SECŢIUNE A2: Cifre pe TOTAL 114 trade-uri (informativ)
# ============================================================
ws.cell(row=row, column=1,
value="A2. Cifre pe TOATE 114 trade-uri (informativ — include și trade-urile post-24.04)").font = SUBTITLE_FONT
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=6)
row += 1
headers_A2 = ["Metric", "Subset (104, până 24.04)", "Total (114)", "Delta post-cutoff", "", "Notă"]
for c, h in enumerate(headers_A2, start=1):
_style_header(ws.cell(row=row, column=c, value=h))
row += 1
rows_A2 = [
("Total trade-uri",
f"=COUNT({rng_subset})", f"=COUNT({rng_total})",
"Mentor a omis 10 trade-uri post-cutoff."),
("Wins",
f"=COUNTIF({rng_subset},\">0\")", f"=COUNTIF({rng_total},\">0\")",
"+8 wins în post-cutoff."),
("Losses",
f"=COUNTIF({rng_subset},\"<0\")", f"=COUNTIF({rng_total},\"<0\")",
"+2 losses în post-cutoff."),
("Win Rate",
f"=COUNTIF({rng_subset},\">0\")/(COUNTIF({rng_subset},\">0\")+COUNTIF({rng_subset},\"<0\"))",
f"=COUNTIF({rng_total},\">0\")/(COUNTIF({rng_total},\">0\")+COUNTIF({rng_total},\"<0\"))",
"Variație minoră."),
("Sum P/L TP1 (%)",
f"=SUM({rng_subset})", f"=SUM({rng_total})",
"Cumulativ în %."),
("Avg Win (%)",
f"=AVERAGEIF({rng_subset},\">0\")", f"=AVERAGEIF({rng_total},\">0\")",
""),
("Avg Loss (%) abs",
f"=-AVERAGEIF({rng_subset},\"<0\")", f"=-AVERAGEIF({rng_total},\"<0\")",
""),
]
section_A2_first_row = row
for label, subset_f, total_f, note in rows_A2:
ws.cell(row=row, column=1, value=label).border = BORDER
ws.cell(row=row, column=2, value=subset_f).border = BORDER
ws.cell(row=row, column=3, value=total_f).border = BORDER
ws.cell(row=row, column=4, value=f"=C{row}-B{row}").border = BORDER
ws.cell(row=row, column=6, value=note).font = NOTE_FONT
ws.cell(row=row, column=6).border = BORDER
ws.cell(row=row, column=6).alignment = Alignment(wrap_text=True, vertical="center")
row += 1
# Format pentru WR și avg-uri (3, 5, 6 = wr, avg_win, avg_loss... rândurile 3-6 din A2)
for pct_row_offset in (3, 4, 5, 6):
for c in (2, 3, 4):
ws.cell(row=section_A2_first_row + pct_row_offset, column=c).number_format = "0.0000"
row += 2
# ============================================================
# SECŢIUNE B: Simulare PROP (formula H175)
# ============================================================
ws.cell(row=row, column=1,
value="B. Simulare profit lunar pe cont prop (replică formula H175 a mentorului)").font = SUBTITLE_FONT
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=6)
row += 1
note_B = (
"Formula H175 a mentorului: (avg_win$ × N_conturi × wins N_conturi × avg_loss$ × zile_fără_trade) / 7_luni.\n"
"Surface o eroare: formula folosește 'zile fără trade' (D169=36) în loc de 'losses' (D167=23). "
"Cu cifrele declarate ale mentorului, asta dă $2,050.97/lună (= ce arată H175 în fișierul lui). "
"Corectând D169→D167, ar fi ieșit $3,897.71/lună.\n"
"Scaling: cifrele $ sunt teoretice — poziție = cont prop $100K (Config!B10=1, B11=$1000). "
"Pentru P&L real pe 0.8 contracte CFD ($40K poziție efectivă), setați B10=0.8 și B11=$500 (cifrele se vor scala × 0.4)."
)
ws.cell(row=row, column=1, value=note_B).font = NOTE_FONT
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=6)
ws.cell(row=row, column=1).alignment = Alignment(wrap_text=True, vertical="top")
ws.row_dimensions[row].height = 90
row += 2
headers_B = ["Variantă", "Rezultat ($/lună)", "", "", "", "Notă"]
for c, h in enumerate(headers_B, start=1):
_style_header(ws.cell(row=row, column=c, value=h))
row += 1
# Cifrele mentorului DECLARATE (coloana B din Secțiunea A)
avg_win_decl = f"B{section_A_first_row + 5}"
avg_loss_decl = f"B{section_A_first_row + 6}"
wins_decl = f"B{section_A_first_row + 1}"
losses_decl = f"B{section_A_first_row + 2}"
days_decl = f"B{section_A_first_row + 3}"
rows_B = [
("Mentor H175 as-is (cu D169 = zile_fără_trade)",
f"=(({avg_win_decl}*Config!$B$18/100)*Config!$B$19*{wins_decl} - "
f"Config!$B$19*({avg_loss_decl}*Config!$B$18/100)*{days_decl})/Config!$B$20",
"Replică EXACT cifra mentorului. Așteptat: $2,050.97."),
("Mentor H175 corectat (D169 → D167 = losses)",
f"=(({avg_win_decl}*Config!$B$18/100)*{wins_decl} - "
f"({avg_loss_decl}*Config!$B$18/100)*{losses_decl}) * Config!$B$19 / Config!$B$20",
"Așteptat: ~$3,897.71. Cu cifrele declarate, formula corectă."),
]
# Plus pentru fiecare strategie: Sum $_strat × N_accounts / N_luni
# Folosim subset (primele 104 = până la 24.04.2026) ca să fie comparabil
for strat in STRAT_KEYS:
sum_dollar_subset = (
f"SUM(Trades!{COL[f'$_{strat}']}2:{COL[f'$_{strat}']}{last_row_subset})"
)
rows_B.append((
f"Program — {STRAT_LABELS[strat]} (pe subset 104)",
f"={sum_dollar_subset} * Config!$B$19 / Config!$B$20",
f"Sum $_{strat} pe primele 104 trade-uri × N_conturi / N_luni.",
))
for label, formula, note in rows_B:
ws.cell(row=row, column=1, value=label).border = BORDER
ws.cell(row=row, column=2, value=formula).border = BORDER
ws.cell(row=row, column=2).number_format = '"$"#,##0.00'
ws.cell(row=row, column=6, value=note).font = NOTE_FONT
ws.cell(row=row, column=6).border = BORDER
ws.cell(row=row, column=6).alignment = Alignment(wrap_text=True, vertical="center")
row += 1
row += 2
# ============================================================
# SECŢIUNE C: Per strategie pe TOATE trade-urile
# ============================================================
ws.cell(row=row, column=1,
value="C. Metrici per strategie (pe TOATE 114 trade-uri)").font = SUBTITLE_FONT
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=6)
row += 1
headers_C = ["Strategie", "Sum R", "Expectancy R", "Balance final ($)", "Max DD ($)", "Win Rate"]
for c, h in enumerate(headers_C, start=1):
_style_header(ws.cell(row=row, column=c, value=h))
row += 1
for strat in STRAT_KEYS:
r_range = f"Trades!{COL[f'R_{strat}']}2:{COL[f'R_{strat}']}{last_row}"
dd_range = f"Trades!{COL[f'DD_{strat}']}2:{COL[f'DD_{strat}']}{last_row}"
win_range = f"Trades!{COL[f'Win_{strat}']}2:{COL[f'Win_{strat}']}{last_row}"
bal_last_cell = f"Trades!{COL[f'Bal_{strat}']}{last_row}"
ws.cell(row=row, column=1, value=STRAT_LABELS[strat]).border = BORDER
ws.cell(row=row, column=2, value=f"=SUM({r_range})").border = BORDER
ws.cell(row=row, column=2).number_format = "+0.00;-0.00;0.00"
ws.cell(row=row, column=3, value=f"=AVERAGE({r_range})").border = BORDER
ws.cell(row=row, column=3).number_format = "+0.000;-0.000;0.000"
ws.cell(row=row, column=4, value=f"={bal_last_cell}").border = BORDER
ws.cell(row=row, column=4).number_format = '"$"#,##0.00'
ws.cell(row=row, column=5, value=f"=MAX({dd_range})").border = BORDER
ws.cell(row=row, column=5).number_format = '"$"#,##0.00'
ws.cell(row=row, column=6, value=f"=SUM({win_range})/COUNT({win_range})").border = BORDER
ws.cell(row=row, column=6).number_format = "0.00%"
row += 1
# Column widths
widths_comparatie = {
"A": 48, "B": 24, "C": 24, "D": 18, "E": 10, "F": 50,
}
for col_letter, w in widths_comparatie.items():
ws.column_dimensions[col_letter].width = w
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main() -> int:
print(f"Citesc mentor: {MENTOR_FILE}")
trades, raw_pl_tp1, in_subset_flags, days_total, days_subset = read_mentor()
n_subset = sum(in_subset_flags)
print(f" {len(trades)} trade-uri ({n_subset} în subset până 24.04.2026, "
f"{len(trades) - n_subset} post-cutoff)")
print(f" Zile fără trade: {days_total} total ({days_subset} în subset)")
# Sanity check
from collections import Counter
oc = Counter(t["outcome"] for t in trades)
print(f" Outcome distribution: {dict(oc)}")
# Reordonez trade-urile: subset întâi, post-cutoff după.
# Asta aliniază cu coloana AC din Comparatie (subset_vals + rest_vals).
subset_trades = [t for t, in_s in zip(trades, in_subset_flags) if in_s]
rest_trades = [t for t, in_s in zip(trades, in_subset_flags) if not in_s]
trades_ordered = subset_trades + rest_trades
print("Construiesc workbook (schema completă)...")
wb = build_workbook()
print("Override Config pentru cont prop $100K...")
override_config(wb)
print("Populez Trades cu datele transpuse...")
populate_trades(wb, trades_ordered)
print("Construiesc sheet Comparatie...")
build_comparatie(wb, trades_ordered, raw_pl_tp1, in_subset_flags,
days_subset, days_total)
wb.active = wb.sheetnames.index("Comparatie")
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
wb.save(OUTPUT)
print(f"Wrote {OUTPUT}")
return 0
if __name__ == "__main__":
raise SystemExit(main())