ferestre
This commit is contained in:
BIN
data/backtest.backup-20260521-024246.xlsx
Normal file
BIN
data/backtest.backup-20260521-024246.xlsx
Normal file
Binary file not shown.
Binary file not shown.
@@ -195,13 +195,13 @@ def build_config(wb: Workbook) -> None:
|
|||||||
ws["B9"] = 50000
|
ws["B9"] = 50000
|
||||||
ws["C9"] = "Balanța contului de prop"
|
ws["C9"] = "Balanța contului de prop"
|
||||||
|
|
||||||
ws["A10"] = "Position Usage (%)"
|
ws["A10"] = "Contracte per trade"
|
||||||
ws["B10"] = 80
|
ws["B10"] = 1
|
||||||
ws["C10"] = "% din cont folosit ca notional (max contracte)"
|
ws["C10"] = "Număr de contracte tranzacționate per semnal (TradeLocker)"
|
||||||
|
|
||||||
ws["A11"] = "Position Size ($)"
|
ws["A11"] = "$ per 1% per contract"
|
||||||
ws["B11"] = "=B9*B10/100"
|
ws["B11"] = 10000
|
||||||
ws["C11"] = "Auto — notional efectiv pe trade"
|
ws["C11"] = "Pe DIA: 0.10% = $1000 ⇒ 1% = $10,000 (1 contract notional ≈ $1M)"
|
||||||
|
|
||||||
ws["A12"] = "Daily Loss Limit (%)"
|
ws["A12"] = "Daily Loss Limit (%)"
|
||||||
ws["B12"] = 4.0
|
ws["B12"] = 4.0
|
||||||
@@ -219,15 +219,15 @@ def build_config(wb: Workbook) -> None:
|
|||||||
ws["B15"] = "=B9*B14/100"
|
ws["B15"] = "=B9*B14/100"
|
||||||
ws["C15"] = "Auto — derivat din B9 și B14"
|
ws["C15"] = "Auto — derivat din B9 și B14"
|
||||||
|
|
||||||
for r in (9, 10, 12, 14): # inputuri galbene
|
for r in (9, 10, 11, 12, 14): # inputuri galbene
|
||||||
ws.cell(row=r, column=2).fill = INPUT_FILL
|
ws.cell(row=r, column=2).fill = INPUT_FILL
|
||||||
ws.cell(row=r, column=2).border = BORDER
|
ws.cell(row=r, column=2).border = BORDER
|
||||||
for r in (11, 13, 15): # derived blue
|
for r in (13, 15): # derived blue
|
||||||
ws.cell(row=r, column=2).fill = DERIVED_FILL
|
ws.cell(row=r, column=2).fill = DERIVED_FILL
|
||||||
ws.cell(row=r, column=2).border = BORDER
|
ws.cell(row=r, column=2).border = BORDER
|
||||||
|
|
||||||
ws["B9"].number_format = "$#,##0"
|
ws["B9"].number_format = "$#,##0"
|
||||||
ws["B10"].number_format = '0"%"'
|
ws["B10"].number_format = "0"
|
||||||
ws["B11"].number_format = "$#,##0"
|
ws["B11"].number_format = "$#,##0"
|
||||||
ws["B12"].number_format = '0.0"%"'
|
ws["B12"].number_format = '0.0"%"'
|
||||||
ws["B13"].number_format = "$#,##0"
|
ws["B13"].number_format = "$#,##0"
|
||||||
@@ -351,20 +351,22 @@ R_FN: dict[str, callable] = {
|
|||||||
|
|
||||||
|
|
||||||
def _f_dollar(r: int, r_col: str) -> str:
|
def _f_dollar(r: int, r_col: str) -> str:
|
||||||
"""$ P&L pe contul abstract. Variabil per trade = R × SL%/100 × Account Size."""
|
"""$ P&L per trade = R × SL% × Contracte × $/1% per contract (TradeLocker real)."""
|
||||||
rc = f"{COL[r_col]}{r}"
|
rc = f"{COL[r_col]}{r}"
|
||||||
sl = f"{COL['SL %']}{r}"
|
sl = f"{COL['SL %']}{r}"
|
||||||
return f'=IF({rc}="","",{rc}*{sl}/100*Config!$B$4)'
|
return f'=IF({rc}="","",{rc}*{sl}*Config!$B$10*Config!$B$11)'
|
||||||
|
|
||||||
|
|
||||||
def _f_sl_dollar(r: int) -> str:
|
def _f_sl_dollar(r: int) -> str:
|
||||||
|
"""SL $ = SL% × Contracte × $/1% per contract."""
|
||||||
sl = f"{COL['SL %']}{r}"
|
sl = f"{COL['SL %']}{r}"
|
||||||
return f'=IF({sl}="","",{sl}/100*Config!$B$4)'
|
return f'=IF({sl}="","",{sl}*Config!$B$10*Config!$B$11)'
|
||||||
|
|
||||||
|
|
||||||
def _f_sl_dollar_prop(r: int) -> str:
|
def _f_sl_dollar_prop(r: int) -> str:
|
||||||
|
"""SL $ pe contul de prop — același cont real, formula identică cu SL $."""
|
||||||
sl = f"{COL['SL %']}{r}"
|
sl = f"{COL['SL %']}{r}"
|
||||||
return f'=IF({sl}="","",{sl}/100*Config!$B$11)'
|
return f'=IF({sl}="","",{sl}*Config!$B$10*Config!$B$11)'
|
||||||
|
|
||||||
|
|
||||||
def _f_balance(r: int, dollar_col: str) -> str:
|
def _f_balance(r: int, dollar_col: str) -> str:
|
||||||
@@ -395,10 +397,14 @@ def _f_drawdown(r: int, peak_col: str, balance_col: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _f_dollar_prop(r: int, r_col: str) -> str:
|
def _f_dollar_prop(r: int, r_col: str) -> str:
|
||||||
"""$ P&L pe contul de prop. Variabil per trade = R × SL%/100 × Position Size."""
|
"""$ P&L pe contul de prop — același calcul ca _f_dollar (cont real TradeLocker).
|
||||||
|
|
||||||
|
Diferența între cont abstract și prop e doar balanța de start; $-ul per trade
|
||||||
|
e identic pentru că reflectă realitatea contractelor tranzacționate.
|
||||||
|
"""
|
||||||
rc = f"{COL[r_col]}{r}"
|
rc = f"{COL[r_col]}{r}"
|
||||||
sl = f"{COL['SL %']}{r}"
|
sl = f"{COL['SL %']}{r}"
|
||||||
return f'=IF({rc}="","",{rc}*{sl}/100*Config!$B$11)'
|
return f'=IF({rc}="","",{rc}*{sl}*Config!$B$10*Config!$B$11)'
|
||||||
|
|
||||||
|
|
||||||
def _f_balance_prop(r: int, dollar_col: str) -> str:
|
def _f_balance_prop(r: int, dollar_col: str) -> str:
|
||||||
@@ -620,7 +626,7 @@ METRIC_HINTS: dict[str, str] = {
|
|||||||
),
|
),
|
||||||
"Average Loss ($)": (
|
"Average Loss ($)": (
|
||||||
"Pierderea medie pe trade-urile negative (cifra apare cu minus).\n"
|
"Pierderea medie pe trade-urile negative (cifra apare cu minus).\n"
|
||||||
"În dolari reali, −1R depinde de SL%: pierdere ≈ SL% × Account Size Start.\n"
|
"În dolari reali, −1R depinde de SL%: pierdere ≈ SL% × Contracte × $/1% per contract.\n"
|
||||||
"Dacă e mult mai mare decât riscul calculat din SL, ai SL-uri sărite (slippage, gap-uri)."
|
"Dacă e mult mai mare decât riscul calculat din SL, ai SL-uri sărite (slippage, gap-uri)."
|
||||||
),
|
),
|
||||||
"Best Trade ($)": (
|
"Best Trade ($)": (
|
||||||
@@ -630,8 +636,8 @@ METRIC_HINTS: dict[str, str] = {
|
|||||||
),
|
),
|
||||||
"Worst Trade ($)": (
|
"Worst Trade ($)": (
|
||||||
"Cea mai mare pierdere individuală.\n"
|
"Cea mai mare pierdere individuală.\n"
|
||||||
"Ar trebui să fie aproximativ egală cu −1R calculat din SL% × Account Size Start.\n"
|
"Ar trebui să fie aproximativ egală cu −1R calculat din SL% × Contracte × $/1% per contract.\n"
|
||||||
"Dacă e semnificativ mai mare, ai depășit risk-ul plănuit — SL ratat, slippage, gap overnight."
|
"Pe TradeLocker DIA: SL=0.30%, 1 contract → ≈ −$3000. Dacă e mai mare, ai slippage/gap."
|
||||||
),
|
),
|
||||||
"Profit Factor": (
|
"Profit Factor": (
|
||||||
"Total bani câștigați împărțit la total bani pierduți (în valoare absolută).\n"
|
"Total bani câștigați împărțit la total bani pierduți (în valoare absolută).\n"
|
||||||
@@ -650,8 +656,8 @@ METRIC_HINTS: dict[str, str] = {
|
|||||||
"Pragul de GO LIVE: +0.20R sau mai mult."
|
"Pragul de GO LIVE: +0.20R sau mai mult."
|
||||||
),
|
),
|
||||||
"Expectancy ($)": (
|
"Expectancy ($)": (
|
||||||
"Aceeași expectancy convertită în dolari, folosind SL% × Account Size Start per trade.\n"
|
"Aceeași expectancy convertită în dolari, folosind SL% × Contracte × $/1% per contract.\n"
|
||||||
"Util ca să vezi cât câștigi în medie pe trade în bani reali, nu doar în R."
|
"Util ca să vezi cât câștigi în medie pe trade în bani reali (TradeLocker), nu doar în R."
|
||||||
),
|
),
|
||||||
"Cumulative P&L ($)": (
|
"Cumulative P&L ($)": (
|
||||||
"Suma profitului și pierderii pe toate trade-urile logate.\n"
|
"Suma profitului și pierderii pe toate trade-urile logate.\n"
|
||||||
@@ -672,13 +678,14 @@ METRIC_HINTS: dict[str, str] = {
|
|||||||
"Editabil în Config B9."
|
"Editabil în Config B9."
|
||||||
),
|
),
|
||||||
"Position Size ($)": (
|
"Position Size ($)": (
|
||||||
"Notional efectiv pe trade = Account Prop × Position Usage %.\n"
|
"Configurare contract real TradeLocker:\n"
|
||||||
"Default: 80% × $50,000 = $40,000. Pe DIA, ≈ 0.8 contracte din prețul curent.\n"
|
" • Contracte per trade (Config B10) — câte contracte tranzacționezi pe semnal.\n"
|
||||||
"Pierderea pe SL = SL% × Position Size (NU procent fix din cont)."
|
" • $ per 1% per contract (Config B11) — pe DIA: 0.10% = $1000 → 1% = $10,000.\n"
|
||||||
|
"Pierderea pe SL = SL% × Contracte × $/1% per contract. Pentru SL=0.30%, 1 contract → $3000."
|
||||||
),
|
),
|
||||||
"Cumulative P&L Prop ($)": (
|
"Cumulative P&L Prop ($)": (
|
||||||
"Profitul total al contului de prop pe traseul logat.\n"
|
"Profitul total al contului de prop pe traseul logat.\n"
|
||||||
"Reflectă $ real (SL% × Position Size per trade), NU R-multiple-ul abstract de mai sus.\n"
|
"Reflectă $ real (SL% × Contracte × $/1% per contract), nu un procent abstract din cont.\n"
|
||||||
"Adunat peste $50,000 dă balanța finală reală."
|
"Adunat peste $50,000 dă balanța finală reală."
|
||||||
),
|
),
|
||||||
"Final Balance Prop ($)": (
|
"Final Balance Prop ($)": (
|
||||||
@@ -949,11 +956,13 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
ws[f"P{row}"] = f'=IF(N{row}>Config!$B$15,"DA","NU")'
|
ws[f"P{row}"] = f'=IF(N{row}>Config!$B$15,"DA","NU")'
|
||||||
ws[f"Q{row}"] = f'=IF(OR(O{row}="DA",P{row}="DA"),"CONT PIERDUT","CONFORM")'
|
ws[f"Q{row}"] = f'=IF(OR(O{row}="DA",P{row}="DA"),"CONT PIERDUT","CONFORM")'
|
||||||
ws[f"R{row}"] = (
|
ws[f"R{row}"] = (
|
||||||
f'=IF(AND(E{row}>=40,G{row}>=55%,H{row}>=0.2,'
|
f'=IF(E{row}<1,"",'
|
||||||
f'O{row}="NU",P{row}="NU"),"CANDIDAT","NU")'
|
f'IF(OR(O{row}="DA",P{row}="DA"),"BREACH",'
|
||||||
|
f'IF(AND(E{row}>=40,G{row}>=55%,H{row}>=0.2),"CANDIDAT","PRE-CANDIDAT")))'
|
||||||
)
|
)
|
||||||
ws[f"S{row}"] = (
|
ws[f"S{row}"] = (
|
||||||
f'=IF(R{row}="CANDIDAT",H{row}*100000+J{row}*1000+K{row}-L{row}-N{row}/10,-1)'
|
f'=IF(E{row}<1,-1E+12,'
|
||||||
|
f'H{row}*100000+J{row}*1000+K{row}-L{row}-N{row}/10)'
|
||||||
)
|
)
|
||||||
combo_rows.append(row)
|
combo_rows.append(row)
|
||||||
combo_idx += 1
|
combo_idx += 1
|
||||||
@@ -991,14 +1000,26 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
status_rng, CellIsRule(operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill)
|
status_rng, CellIsRule(operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill)
|
||||||
)
|
)
|
||||||
ws.conditional_formatting.add(
|
ws.conditional_formatting.add(
|
||||||
status_rng, CellIsRule(operator="equal", formula=['"NU"'], fill=warn_fill)
|
status_rng, CellIsRule(operator="equal", formula=['"BREACH"'], fill=fail_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
status_rng, CellIsRule(operator="equal", formula=['"PRE-CANDIDAT"'], fill=warn_fill)
|
||||||
)
|
)
|
||||||
|
|
||||||
top_title_row = row + 2
|
top_title_row = row + 2
|
||||||
ws[f"A{top_title_row}"] = "TOP CANDIDATE"
|
ws[f"A{top_title_row}"] = "TOP CANDIDATE"
|
||||||
ws[f"A{top_title_row}"].font = SUBTITLE_FONT
|
ws[f"A{top_title_row}"].font = SUBTITLE_FONT
|
||||||
ws.merge_cells(f"A{top_title_row}:J{top_title_row}")
|
ws.merge_cells(f"A{top_title_row}:J{top_title_row}")
|
||||||
top_header_row = top_title_row + 1
|
top_note_row = top_title_row + 1
|
||||||
|
ws[f"A{top_note_row}"] = (
|
||||||
|
"Top 10 ferestre după scor compus. CANDIDAT = îndeplinește toate pragurile "
|
||||||
|
"(N≥40, WR≥55%, ExpR≥0.2, no breach). PRE-CANDIDAT = N≥1 fără breach dar sub praguri. "
|
||||||
|
"BREACH = ar fi pierdut contul prop."
|
||||||
|
)
|
||||||
|
ws[f"A{top_note_row}"].font = Font(name="Calibri", size=10, italic=True, color="595959")
|
||||||
|
ws[f"A{top_note_row}"].alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
|
||||||
|
ws.merge_cells(f"A{top_note_row}:J{top_note_row}")
|
||||||
|
top_header_row = top_note_row + 1
|
||||||
top_headers = [
|
top_headers = [
|
||||||
"#", "Fereastra", "Strategie", "N", "WR", "Expectancy R",
|
"#", "Fereastra", "Strategie", "N", "WR", "Expectancy R",
|
||||||
"Profit Factor", "Cum P&L $", "Max DD Prop $", "Status Edge",
|
"Profit Factor", "Cum P&L $", "Max DD Prop $", "Status Edge",
|
||||||
@@ -1021,7 +1042,7 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
["A", "D", "E", "G", "H", "J", "K", "N", "R"],
|
["A", "D", "E", "G", "H", "J", "K", "N", "R"],
|
||||||
):
|
):
|
||||||
ws[f"{target}{r}"] = (
|
ws[f"{target}{r}"] = (
|
||||||
f'=IFERROR(IF({rank_formula}<0,"",'
|
f'=IFERROR(IF({rank_formula}<=-1E+11,"",'
|
||||||
f'INDEX(${source}${first_combo}:${source}${last_combo},{match_formula})),"")'
|
f'INDEX(${source}${first_combo}:${source}${last_combo},{match_formula})),"")'
|
||||||
)
|
)
|
||||||
for c in range(1, len(top_headers) + 1):
|
for c in range(1, len(top_headers) + 1):
|
||||||
@@ -1034,56 +1055,86 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
ws[f"H{r}"].number_format = '"$"#,##0.00'
|
ws[f"H{r}"].number_format = '"$"#,##0.00'
|
||||||
ws[f"I{r}"].number_format = '"$"#,##0.00'
|
ws[f"I{r}"].number_format = '"$"#,##0.00'
|
||||||
|
|
||||||
# Helper pentru a emite un block breakdown (per Sesiune / Strategie / etc.)
|
# CF pe coloana Status Edge din TOP CANDIDATE
|
||||||
def _emit_breakdown(
|
top_status_rng = f"J{top_header_row + 1}:J{top_header_row + 10}"
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
top_status_rng, CellIsRule(operator="equal", formula=['"CANDIDAT"'], fill=pass_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
top_status_rng, CellIsRule(operator="equal", formula=['"PRE-CANDIDAT"'], fill=warn_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
top_status_rng, CellIsRule(operator="equal", formula=['"BREACH"'], fill=fail_fill)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Conditional formatting reutilizabil pentru celulele Cum $
|
||||||
|
bd_green = PatternFill("solid", fgColor="C6EFCE")
|
||||||
|
bd_red = PatternFill("solid", fgColor="FFC7CE")
|
||||||
|
|
||||||
|
# Helper pentru breakdown wide: rânduri = items, coloane = 5 strategii Cum $ + N total
|
||||||
|
def _emit_breakdown_strats(
|
||||||
start_row: int, title: str, first_col_label: str,
|
start_row: int, title: str, first_col_label: str,
|
||||||
items: list[str], item_range: str, overlay_strat: str,
|
items: list[str], item_range: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
|
# Layout: A=item, B..F=5 strategii (Cum $), G=N total
|
||||||
|
last_col_idx = 1 + len(STRAT_KEYS) + 1 # A + 5 strategii + N
|
||||||
|
last_letter = get_column_letter(last_col_idx)
|
||||||
ws[f"A{start_row}"] = title
|
ws[f"A{start_row}"] = title
|
||||||
ws[f"A{start_row}"].font = SUBTITLE_FONT
|
ws[f"A{start_row}"].font = SUBTITLE_FONT
|
||||||
ws.merge_cells(f"A{start_row}:F{start_row}")
|
ws.merge_cells(f"A{start_row}:{last_letter}{start_row}")
|
||||||
headers = [first_col_label, "N", "Wins", "WR", "Expectancy R", "Cum $"]
|
headers = [first_col_label] + [STRAT_LABELS[s] for s in STRAT_KEYS] + ["N total"]
|
||||||
for col_idx, h in enumerate(headers, start=1):
|
for col_idx, h in enumerate(headers, start=1):
|
||||||
c = ws.cell(row=start_row + 1, column=col_idx, value=h)
|
c = ws.cell(row=start_row + 1, column=col_idx, value=h)
|
||||||
c.font = HEADER_FONT
|
c.font = HEADER_FONT
|
||||||
c.fill = HEADER_FILL
|
c.fill = HEADER_FILL
|
||||||
c.alignment = CENTER
|
c.alignment = CENTER
|
||||||
c.border = BORDER
|
c.border = BORDER
|
||||||
|
strat_letters = [get_column_letter(2 + i) for i in range(len(STRAT_KEYS))]
|
||||||
|
n_letter = get_column_letter(last_col_idx)
|
||||||
for i, item in enumerate(items):
|
for i, item in enumerate(items):
|
||||||
r = start_row + 2 + i
|
r = start_row + 2 + i
|
||||||
ws[f"A{r}"] = item
|
ws[f"A{r}"] = item
|
||||||
ws[f"B{r}"] = f'=COUNTIF({item_range},"{item}")'
|
for idx, strat in enumerate(STRAT_KEYS):
|
||||||
ws[f"C{r}"] = f'=COUNTIFS({item_range},"{item}",{W[overlay_strat]},1)'
|
cl = strat_letters[idx]
|
||||||
ws[f"D{r}"] = f"=IFERROR(C{r}/B{r},0)"
|
ws[f"{cl}{r}"] = f'=SUMIFS({D[strat]},{item_range},"{item}")'
|
||||||
ws[f"E{r}"] = (
|
ws[f"{cl}{r}"].number_format = '"$"#,##0.00'
|
||||||
f'=IFERROR(AVERAGEIFS({R[overlay_strat]},{item_range},"{item}"),0)'
|
ws[f"{n_letter}{r}"] = f'=COUNTIF({item_range},"{item}")'
|
||||||
|
ws[f"{n_letter}{r}"].number_format = "0"
|
||||||
|
for col_idx in range(1, last_col_idx + 1):
|
||||||
|
cell = ws.cell(row=r, column=col_idx)
|
||||||
|
cell.border = BORDER
|
||||||
|
cell.alignment = LEFT if col_idx == 1 else RIGHT
|
||||||
|
if 2 <= col_idx <= 1 + len(STRAT_KEYS):
|
||||||
|
cell.fill = DERIVED_FILL
|
||||||
|
# CF pe coloanele 5 strategii: verde >0, roșu <0
|
||||||
|
if items:
|
||||||
|
first_data_row = start_row + 2
|
||||||
|
last_data_row = start_row + 1 + len(items)
|
||||||
|
cf_rng = (
|
||||||
|
f"{strat_letters[0]}{first_data_row}:"
|
||||||
|
f"{strat_letters[-1]}{last_data_row}"
|
||||||
)
|
)
|
||||||
ws[f"F{r}"] = f'=SUMIFS({D[overlay_strat]},{item_range},"{item}")'
|
ws.conditional_formatting.add(
|
||||||
ws[f"B{r}"].number_format = "0"
|
cf_rng, CellIsRule(operator="greaterThan", formula=["0"], fill=bd_green)
|
||||||
ws[f"C{r}"].number_format = "0"
|
)
|
||||||
ws[f"D{r}"].number_format = "0.0%"
|
ws.conditional_formatting.add(
|
||||||
ws[f"E{r}"].number_format = "+0.000;-0.000;0.000"
|
cf_rng, CellIsRule(operator="lessThan", formula=["0"], fill=bd_red)
|
||||||
ws[f"F{r}"].number_format = '"$"#,##0.00'
|
)
|
||||||
for c in ("A", "B", "C", "D", "E", "F"):
|
return start_row + 1 + len(items)
|
||||||
ws[f"{c}{r}"].border = BORDER
|
|
||||||
ws[f"{c}{r}"].alignment = RIGHT if c != "A" else LEFT
|
|
||||||
return start_row + 2 + len(items)
|
|
||||||
|
|
||||||
# Breakdowns — toate folosesc overlay-ul Hybrid+BE (recomandat de trader)
|
# Breakdowns — toate cele 5 strategii vizibile, Cum P&L $ per strategie
|
||||||
overlay = "hybrid_be"
|
|
||||||
start = top_header_row + 13
|
start = top_header_row + 13
|
||||||
after_sess = start
|
after_strat = _emit_breakdown_strats(
|
||||||
after_strat = _emit_breakdown(
|
start + 2, "PER STRATEGIE — Cum P&L $ per strategie", "Strategie",
|
||||||
after_sess + 2, "PER STRATEGIE (overlay: Hybrid + BE)", "Strategie",
|
STRATEGIES, _range("Strategie"),
|
||||||
STRATEGIES, _range("Strategie"), overlay,
|
|
||||||
)
|
)
|
||||||
after_ind = _emit_breakdown(
|
after_ind = _emit_breakdown_strats(
|
||||||
after_strat + 2, "PER INDICATOR (overlay: Hybrid + BE)", "Indicator",
|
after_strat + 2, "PER INDICATOR — Cum P&L $ per strategie", "Indicator",
|
||||||
INDICATORS, _range("Indicator"), overlay,
|
INDICATORS, _range("Indicator"),
|
||||||
)
|
)
|
||||||
after_dir = _emit_breakdown(
|
after_dir = _emit_breakdown_strats(
|
||||||
after_ind + 2, "PER DIRECȚIE (overlay: Hybrid + BE)", "Direcție",
|
after_ind + 2, "PER DIRECȚIE — Cum P&L $ per strategie", "Direcție",
|
||||||
DIRECTIONS, _range("Direcție"), overlay,
|
DIRECTIONS, _range("Direcție"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# ---- PROP FIRM COMPLIANCE ----
|
# ---- PROP FIRM COMPLIANCE ----
|
||||||
@@ -1235,9 +1286,72 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
for r in range(prop_header_row + 1, prop_header_row + 1 + len(prop_metrics)):
|
for r in range(prop_header_row + 1, prop_header_row + 1 + len(prop_metrics)):
|
||||||
ws.row_dimensions[r].height = 60
|
ws.row_dimensions[r].height = 60
|
||||||
|
|
||||||
|
# ---- PROP FIRM COMPLIANCE per FEREASTRĂ × STRATEGIE ----
|
||||||
|
# Reshape compliance: rânduri = combo (fereastră × strategie), coloane = metrici compliance.
|
||||||
|
# Datele sunt referențiate direct din FERESTRE CANDIDATE (cols A, D, M, N, O, P, Q).
|
||||||
|
if combo_rows:
|
||||||
|
win_prop_title_row = prop_header_row + 1 + len(prop_metrics) + 2
|
||||||
|
ws[f"A{win_prop_title_row}"] = "PROP FIRM COMPLIANCE — per FEREASTRĂ × STRATEGIE"
|
||||||
|
ws[f"A{win_prop_title_row}"].font = SUBTITLE_FONT
|
||||||
|
ws.merge_cells(f"A{win_prop_title_row}:G{win_prop_title_row}")
|
||||||
|
|
||||||
|
win_prop_note_row = win_prop_title_row + 1
|
||||||
|
ws[f"A{win_prop_note_row}"] = (
|
||||||
|
"Defalcat pe fiecare combinație de fereastră tradabilă × strategie management. "
|
||||||
|
"CONFORM = ar fi supraviețuit pe contul de prop pe acel slot."
|
||||||
|
)
|
||||||
|
ws[f"A{win_prop_note_row}"].font = Font(name="Calibri", size=10, italic=True, color="595959")
|
||||||
|
ws[f"A{win_prop_note_row}"].alignment = Alignment(horizontal="left", vertical="center", wrap_text=True)
|
||||||
|
ws.merge_cells(f"A{win_prop_note_row}:G{win_prop_note_row}")
|
||||||
|
|
||||||
|
win_prop_header_row = win_prop_note_row + 1
|
||||||
|
win_prop_headers = [
|
||||||
|
"Fereastra", "Strategie", "Worst Daily Prop $", "Max DD Prop $",
|
||||||
|
"Daily Breach", "Max Breach", "Overall Prop",
|
||||||
|
]
|
||||||
|
for col_idx, h in enumerate(win_prop_headers, start=1):
|
||||||
|
c = ws.cell(row=win_prop_header_row, column=col_idx, value=h)
|
||||||
|
c.font = HEADER_FONT
|
||||||
|
c.fill = HEADER_FILL
|
||||||
|
c.alignment = CENTER
|
||||||
|
c.border = BORDER
|
||||||
|
|
||||||
|
# source cols din FERESTRE CANDIDATE: A=Fereastra, D=Strategie, M=Worst Daily,
|
||||||
|
# N=Max DD, O=Daily Breach, P=Max Breach, Q=Overall Prop
|
||||||
|
source_cols = ["A", "D", "M", "N", "O", "P", "Q"]
|
||||||
|
for offset, combo_row in enumerate(combo_rows, start=1):
|
||||||
|
r = win_prop_header_row + offset
|
||||||
|
for col_idx, source in enumerate(source_cols, start=1):
|
||||||
|
target = get_column_letter(col_idx)
|
||||||
|
ws[f"{target}{r}"] = f"={source}{combo_row}"
|
||||||
|
cell = ws[f"{target}{r}"]
|
||||||
|
cell.border = BORDER
|
||||||
|
cell.fill = DERIVED_FILL
|
||||||
|
cell.alignment = CENTER if col_idx in (1, 2, 5, 6, 7) else RIGHT
|
||||||
|
ws[f"C{r}"].number_format = '"$"#,##0.00'
|
||||||
|
ws[f"D{r}"].number_format = '"$"#,##0.00'
|
||||||
|
|
||||||
|
# CF pe Overall Prop (col G) și pe Daily/Max Breach (cols E, F)
|
||||||
|
win_prop_first = win_prop_header_row + 1
|
||||||
|
win_prop_last = win_prop_header_row + len(combo_rows)
|
||||||
|
overall_rng_win = f"G{win_prop_first}:G{win_prop_last}"
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
overall_rng_win, CellIsRule(operator="equal", formula=['"CONFORM"'], fill=pass_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
overall_rng_win, CellIsRule(operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill)
|
||||||
|
)
|
||||||
|
breach_rng_win = f"E{win_prop_first}:F{win_prop_last}"
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
breach_rng_win, CellIsRule(operator="equal", formula=['"DA"'], fill=fail_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
breach_rng_win, CellIsRule(operator="equal", formula=['"NU"'], fill=pass_fill)
|
||||||
|
)
|
||||||
|
|
||||||
# Column widths
|
# Column widths
|
||||||
widths = {
|
widths = {
|
||||||
"A": 18, "B": 10, "C": 10, "D": 16, "E": 8, "F": 8, "G": 10,
|
"A": 18, "B": 10, "C": 18, "D": 16, "E": 13, "F": 13, "G": 16,
|
||||||
"H": 13, "I": 13, "J": 12, "K": 13, "L": 15, "M": 20,
|
"H": 13, "I": 13, "J": 12, "K": 13, "L": 15, "M": 20,
|
||||||
"N": 18, "O": 13, "P": 14, "Q": 15, "R": 13, "S": 8,
|
"N": 18, "O": 13, "P": 14, "Q": 15, "R": 13, "S": 8,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user