"""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())