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.
This commit is contained in:
2026-05-26 00:17:25 +03:00
parent 85ca68b11e
commit 11dfe59116
3 changed files with 601 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,601 @@
"""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())