prop
This commit is contained in:
BIN
data/backtest.backup-prop.xlsx
Normal file
BIN
data/backtest.backup-prop.xlsx
Normal file
Binary file not shown.
Binary file not shown.
@@ -81,11 +81,16 @@ DERIVED_HEADERS = (
|
|||||||
[f"R_{s}" for s in STRAT_KEYS]
|
[f"R_{s}" for s in STRAT_KEYS]
|
||||||
+ [f"$_{s}" for s in STRAT_KEYS]
|
+ [f"$_{s}" for s in STRAT_KEYS]
|
||||||
+ [f"Bal_{s}" for s in STRAT_KEYS]
|
+ [f"Bal_{s}" for s in STRAT_KEYS]
|
||||||
|
+ [f"$Prop_{s}" for s in STRAT_KEYS]
|
||||||
|
+ [f"BalProp_{s}" for s in STRAT_KEYS]
|
||||||
)
|
)
|
||||||
HELPER_HEADERS = (
|
HELPER_HEADERS = (
|
||||||
[f"Win_{s}" for s in STRAT_KEYS]
|
[f"Win_{s}" for s in STRAT_KEYS]
|
||||||
+ [f"Peak_{s}" for s in STRAT_KEYS]
|
+ [f"Peak_{s}" for s in STRAT_KEYS]
|
||||||
+ [f"DD_{s}" for s in STRAT_KEYS]
|
+ [f"DD_{s}" for s in STRAT_KEYS]
|
||||||
|
+ [f"DailyPL_{s}" for s in STRAT_KEYS]
|
||||||
|
+ [f"PeakProp_{s}" for s in STRAT_KEYS]
|
||||||
|
+ [f"DDProp_{s}" for s in STRAT_KEYS]
|
||||||
)
|
)
|
||||||
TRADES_HEADERS = INPUT_HEADERS + DERIVED_HEADERS + HELPER_HEADERS
|
TRADES_HEADERS = INPUT_HEADERS + DERIVED_HEADERS + HELPER_HEADERS
|
||||||
|
|
||||||
@@ -128,7 +133,7 @@ def build_config(wb: Workbook) -> None:
|
|||||||
|
|
||||||
ws["A4"] = "Account Size Start ($)"
|
ws["A4"] = "Account Size Start ($)"
|
||||||
ws["B4"] = 10000
|
ws["B4"] = 10000
|
||||||
ws["C4"] = "Balanța inițială pentru calcule $ și HWM"
|
ws["C4"] = "Balanța inițială pentru calcule $ și HWM (model abstract)"
|
||||||
|
|
||||||
ws["A5"] = "Risk per Trade (%)"
|
ws["A5"] = "Risk per Trade (%)"
|
||||||
ws["B5"] = 1.0
|
ws["B5"] = 1.0
|
||||||
@@ -147,6 +152,54 @@ def build_config(wb: Workbook) -> None:
|
|||||||
ws["B5"].number_format = '0.0"%"'
|
ws["B5"].number_format = '0.0"%"'
|
||||||
ws["B6"].number_format = "$#,##0.00"
|
ws["B6"].number_format = "$#,##0.00"
|
||||||
|
|
||||||
|
# ---- Bloc Cont Prop Firm (separat de modelul abstract de mai sus) ----
|
||||||
|
ws["A8"] = "Cont Prop Firm"
|
||||||
|
ws["A8"].font = SUBTITLE_FONT
|
||||||
|
ws.merge_cells("A8:C8")
|
||||||
|
|
||||||
|
ws["A9"] = "Account Prop Start ($)"
|
||||||
|
ws["B9"] = 50000
|
||||||
|
ws["C9"] = "Balanța contului de prop"
|
||||||
|
|
||||||
|
ws["A10"] = "Position Usage (%)"
|
||||||
|
ws["B10"] = 80
|
||||||
|
ws["C10"] = "% din cont folosit ca notional (max contracte)"
|
||||||
|
|
||||||
|
ws["A11"] = "Position Size ($)"
|
||||||
|
ws["B11"] = "=B9*B10/100"
|
||||||
|
ws["C11"] = "Auto — notional efectiv pe trade"
|
||||||
|
|
||||||
|
ws["A12"] = "Daily Loss Limit (%)"
|
||||||
|
ws["B12"] = 4.0
|
||||||
|
ws["C12"] = "Limită zilnică prop firm; depășire = cont mort"
|
||||||
|
|
||||||
|
ws["A13"] = "Daily Loss Limit ($)"
|
||||||
|
ws["B13"] = "=B9*B12/100"
|
||||||
|
ws["C13"] = "Auto — derivat din B9 și B12"
|
||||||
|
|
||||||
|
ws["A14"] = "Max Loss Limit (%)"
|
||||||
|
ws["B14"] = 7.0
|
||||||
|
ws["C14"] = "Limită totală pe cont; depășire = cont mort"
|
||||||
|
|
||||||
|
ws["A15"] = "Max Loss Limit ($)"
|
||||||
|
ws["B15"] = "=B9*B14/100"
|
||||||
|
ws["C15"] = "Auto — derivat din B9 și B14"
|
||||||
|
|
||||||
|
for r in (9, 10, 12, 14): # inputuri galbene
|
||||||
|
ws.cell(row=r, column=2).fill = INPUT_FILL
|
||||||
|
ws.cell(row=r, column=2).border = BORDER
|
||||||
|
for r in (11, 13, 15): # derived blue
|
||||||
|
ws.cell(row=r, column=2).fill = DERIVED_FILL
|
||||||
|
ws.cell(row=r, column=2).border = BORDER
|
||||||
|
|
||||||
|
ws["B9"].number_format = "$#,##0"
|
||||||
|
ws["B10"].number_format = '0"%"'
|
||||||
|
ws["B11"].number_format = "$#,##0"
|
||||||
|
ws["B12"].number_format = '0.0"%"'
|
||||||
|
ws["B13"].number_format = "$#,##0"
|
||||||
|
ws["B14"].number_format = '0.0"%"'
|
||||||
|
ws["B15"].number_format = "$#,##0"
|
||||||
|
|
||||||
# Liste dropdown — coloanele E–J (6 coloane)
|
# Liste dropdown — coloanele E–J (6 coloane)
|
||||||
list_columns = [
|
list_columns = [
|
||||||
("Strategii", STRATEGIES),
|
("Strategii", STRATEGIES),
|
||||||
@@ -295,6 +348,29 @@ def _f_drawdown(r: int, peak_col: str, balance_col: str) -> str:
|
|||||||
return f'=IF({bc}="","",{pc}-{bc})'
|
return f'=IF({bc}="","",{pc}-{bc})'
|
||||||
|
|
||||||
|
|
||||||
|
def _f_dollar_prop(r: int, r_col: str) -> str:
|
||||||
|
"""$ P&L pe contul de prop. Variabil per trade = R × SL%/100 × Position Size."""
|
||||||
|
rc = f"{COL[r_col]}{r}"
|
||||||
|
sl = f"{COL['SL %']}{r}"
|
||||||
|
return f'=IF({rc}="","",{rc}*{sl}/100*Config!$B$11)'
|
||||||
|
|
||||||
|
|
||||||
|
def _f_balance_prop(r: int, dollar_col: str) -> str:
|
||||||
|
dc = COL[dollar_col]
|
||||||
|
return f'=IF({dc}{r}="","",Config!$B$9 + SUM(${dc}$2:{dc}{r}))'
|
||||||
|
|
||||||
|
|
||||||
|
def _f_daily_pl(r: int, dollar_col: str) -> str:
|
||||||
|
"""Cumul P&L pe ziua curentă (până la rândul r inclusiv)."""
|
||||||
|
dc = COL[dollar_col]
|
||||||
|
d_col = COL["Data"]
|
||||||
|
d = f"{d_col}{r}"
|
||||||
|
return (
|
||||||
|
f'=IF(OR({dc}{r}="",{d}=""),"",'
|
||||||
|
f'SUMIFS(${dc}$2:{dc}{r},${d_col}$2:{d_col}{r},{d}))'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Trades sheet
|
# Trades sheet
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -330,6 +406,16 @@ def build_trades(wb: Workbook) -> None:
|
|||||||
ws[f'{COL[f"DD_{strat}"]}{r}'] = _f_drawdown(
|
ws[f'{COL[f"DD_{strat}"]}{r}'] = _f_drawdown(
|
||||||
r, f"Peak_{strat}", f"Bal_{strat}"
|
r, f"Peak_{strat}", f"Bal_{strat}"
|
||||||
)
|
)
|
||||||
|
# Prop firm tracking — paralel cu modelul abstract
|
||||||
|
ws[f'{COL[f"$Prop_{strat}"]}{r}'] = _f_dollar_prop(r, f"R_{strat}")
|
||||||
|
ws[f'{COL[f"BalProp_{strat}"]}{r}'] = _f_balance_prop(r, f"$Prop_{strat}")
|
||||||
|
ws[f'{COL[f"DailyPL_{strat}"]}{r}'] = _f_daily_pl(r, f"$Prop_{strat}")
|
||||||
|
ws[f'{COL[f"PeakProp_{strat}"]}{r}'] = _f_peak(
|
||||||
|
r, f"BalProp_{strat}", f"PeakProp_{strat}"
|
||||||
|
)
|
||||||
|
ws[f'{COL[f"DDProp_{strat}"]}{r}'] = _f_drawdown(
|
||||||
|
r, f"PeakProp_{strat}", f"BalProp_{strat}"
|
||||||
|
)
|
||||||
|
|
||||||
# Sample row 2
|
# Sample row 2
|
||||||
ws["B2"] = date(2026, 5, 13)
|
ws["B2"] = date(2026, 5, 13)
|
||||||
@@ -353,7 +439,10 @@ def build_trades(wb: Workbook) -> None:
|
|||||||
for strat in STRAT_KEYS:
|
for strat in STRAT_KEYS:
|
||||||
for r in range(2, MAX_ROWS + 2):
|
for r in range(2, MAX_ROWS + 2):
|
||||||
ws[f"{COL[f'R_{strat}']}{r}"].number_format = "+0.000;-0.000;0.000"
|
ws[f"{COL[f'R_{strat}']}{r}"].number_format = "+0.000;-0.000;0.000"
|
||||||
for prefix in ("$_", "Bal_", "Peak_", "DD_"):
|
for prefix in (
|
||||||
|
"$_", "Bal_", "Peak_", "DD_",
|
||||||
|
"$Prop_", "BalProp_", "DailyPL_", "PeakProp_", "DDProp_",
|
||||||
|
):
|
||||||
ws[f"{COL[f'{prefix}{strat}']}{r}"].number_format = '"$"#,##0.00'
|
ws[f"{COL[f'{prefix}{strat}']}{r}"].number_format = '"$"#,##0.00'
|
||||||
|
|
||||||
for r in range(2, MAX_ROWS + 2):
|
for r in range(2, MAX_ROWS + 2):
|
||||||
@@ -370,12 +459,11 @@ def build_trades(wb: Workbook) -> None:
|
|||||||
}
|
}
|
||||||
derived_letters = {COL["Zi"], COL["Sesiune"]}
|
derived_letters = {COL["Zi"], COL["Sesiune"]}
|
||||||
for strat in STRAT_KEYS:
|
for strat in STRAT_KEYS:
|
||||||
derived_letters.add(COL[f"R_{strat}"])
|
for prefix in ("R_", "$_", "Bal_", "$Prop_", "BalProp_"):
|
||||||
derived_letters.add(COL[f"$_{strat}"])
|
derived_letters.add(COL[f"{prefix}{strat}"])
|
||||||
derived_letters.add(COL[f"Bal_{strat}"])
|
|
||||||
helper_letters = set()
|
helper_letters = set()
|
||||||
for strat in STRAT_KEYS:
|
for strat in STRAT_KEYS:
|
||||||
for prefix in ("Win_", "Peak_", "DD_"):
|
for prefix in ("Win_", "Peak_", "DD_", "DailyPL_", "PeakProp_", "DDProp_"):
|
||||||
helper_letters.add(COL[f"{prefix}{strat}"])
|
helper_letters.add(COL[f"{prefix}{strat}"])
|
||||||
|
|
||||||
for r in range(2, MAX_ROWS + 2):
|
for r in range(2, MAX_ROWS + 2):
|
||||||
@@ -397,9 +485,19 @@ def build_trades(wb: Workbook) -> None:
|
|||||||
ws.column_dimensions[col].width = w
|
ws.column_dimensions[col].width = w
|
||||||
# Derived + helper: width 11
|
# Derived + helper: width 11
|
||||||
for strat in STRAT_KEYS:
|
for strat in STRAT_KEYS:
|
||||||
for prefix in ("R_", "$_", "Bal_", "Win_", "Peak_", "DD_"):
|
for prefix in (
|
||||||
|
"R_", "$_", "Bal_", "Win_", "Peak_", "DD_",
|
||||||
|
"$Prop_", "BalProp_", "DailyPL_", "PeakProp_", "DDProp_",
|
||||||
|
):
|
||||||
ws.column_dimensions[COL[f"{prefix}{strat}"]].width = 11
|
ws.column_dimensions[COL[f"{prefix}{strat}"]].width = 11
|
||||||
|
|
||||||
|
# Ascund helper-ele prop firm într-un outline collapsible
|
||||||
|
for strat in STRAT_KEYS:
|
||||||
|
for prefix in ("DailyPL_", "PeakProp_", "DDProp_"):
|
||||||
|
cl = COL[f"{prefix}{strat}"]
|
||||||
|
ws.column_dimensions[cl].outlineLevel = 1
|
||||||
|
ws.column_dimensions[cl].hidden = True
|
||||||
|
|
||||||
# Data validation dropdowns
|
# Data validation dropdowns
|
||||||
def _add_dv(col_name: str, source: str) -> None:
|
def _add_dv(col_name: str, source: str) -> None:
|
||||||
cl = COL[col_name]
|
cl = COL[col_name]
|
||||||
@@ -514,6 +612,46 @@ METRIC_HINTS: dict[str, str] = {
|
|||||||
"Exemplu: ai urcat la $11,500, ai coborât la $9,800 — DD = $1,700, adică 17% din peak.\n"
|
"Exemplu: ai urcat la $11,500, ai coborât la $9,800 — DD = $1,700, adică 17% din peak.\n"
|
||||||
"Un drawdown mare la backtest e foarte greu de tolerat în live cu bani reali — așteaptă-te să renunți."
|
"Un drawdown mare la backtest e foarte greu de tolerat în live cu bani reali — așteaptă-te să renunți."
|
||||||
),
|
),
|
||||||
|
# ---- Prop firm metrics ----
|
||||||
|
"Account Prop Start ($)": (
|
||||||
|
"Capitalul de start al contului de prop firm (default $50,000).\n"
|
||||||
|
"Editabil în Config B9."
|
||||||
|
),
|
||||||
|
"Position Size ($)": (
|
||||||
|
"Notional efectiv pe trade = Account Prop × Position Usage %.\n"
|
||||||
|
"Default: 80% × $50,000 = $40,000. Pe DIA, ≈ 0.8 contracte din prețul curent.\n"
|
||||||
|
"Pierderea pe SL = SL% × Position Size (NU procent fix din cont)."
|
||||||
|
),
|
||||||
|
"Cumulative P&L Prop ($)": (
|
||||||
|
"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"
|
||||||
|
"Adunat peste $50,000 dă balanța finală reală."
|
||||||
|
),
|
||||||
|
"Final Balance Prop ($)": (
|
||||||
|
"Balanța finală a contului de prop = $50,000 + Cumulative P&L Prop.\n"
|
||||||
|
"Compar-o cu pragul de stop-out al firmei de prop: $50,000 − $3,500 = $46,500."
|
||||||
|
),
|
||||||
|
"Worst Daily Loss ($)": (
|
||||||
|
"Cea mai proastă pierdere cumulativă într-o zi calendaristică.\n"
|
||||||
|
"Dacă e mai mică decât −$2,000, ai depășit Daily Loss Limit într-o zi — cont mort.\n"
|
||||||
|
"Atenție: un singur breach = pierdere cont, indiferent dacă ai recuperat ulterior."
|
||||||
|
),
|
||||||
|
"Daily Limit Status": (
|
||||||
|
"PASS dacă nicio zi nu a depășit Daily Loss Limit ($2,000 default).\n"
|
||||||
|
"FAIL = strategia ar fi pierdut contul prin daily breach pe traseul logat."
|
||||||
|
),
|
||||||
|
"Max Account Drawdown ($)": (
|
||||||
|
"Cea mai mare cădere de la peak pe contul de prop.\n"
|
||||||
|
"Dacă > $3,500 (7% din $50k), ai depășit Max Loss Limit — cont mort."
|
||||||
|
),
|
||||||
|
"Max Loss Status": (
|
||||||
|
"PASS dacă Max Account Drawdown ≤ $3,500.\n"
|
||||||
|
"FAIL = strategia ar fi pierdut contul prin drawdown cumulativ."
|
||||||
|
),
|
||||||
|
"Overall Prop Status": (
|
||||||
|
"CONFORM = strategia ar fi supraviețuit pe contul de prop pe traseul logat.\n"
|
||||||
|
"CONT PIERDUT = cel puțin o breach (daily sau max) — strategia nu e viabilă pe acest cont prop."
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -664,11 +802,160 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
after_strat + 2, "PER INDICATOR (overlay: Hybrid + BE)", "Indicator",
|
after_strat + 2, "PER INDICATOR (overlay: Hybrid + BE)", "Indicator",
|
||||||
INDICATORS, _range("Indicator"), overlay,
|
INDICATORS, _range("Indicator"), overlay,
|
||||||
)
|
)
|
||||||
_emit_breakdown(
|
after_dir = _emit_breakdown(
|
||||||
after_ind + 2, "PER DIRECȚIE (overlay: Hybrid + BE)", "Direcție",
|
after_ind + 2, "PER DIRECȚIE (overlay: Hybrid + BE)", "Direcție",
|
||||||
DIRECTIONS, _range("Direcție"), overlay,
|
DIRECTIONS, _range("Direcție"), overlay,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---- PROP FIRM COMPLIANCE ----
|
||||||
|
PROP_RANGES = {
|
||||||
|
"dollar": {s: _range(f"$Prop_{s}") for s in STRAT_KEYS},
|
||||||
|
"balance": {s: _range(f"BalProp_{s}") for s in STRAT_KEYS},
|
||||||
|
"daily": {s: _range(f"DailyPL_{s}") for s in STRAT_KEYS},
|
||||||
|
"dd": {s: _range(f"DDProp_{s}") for s in STRAT_KEYS},
|
||||||
|
}
|
||||||
|
|
||||||
|
prop_title_row = after_dir + 2
|
||||||
|
ws[f"A{prop_title_row}"] = "PROP FIRM COMPLIANCE"
|
||||||
|
ws[f"A{prop_title_row}"].font = SUBTITLE_FONT
|
||||||
|
ws.merge_cells(f"A{prop_title_row}:G{prop_title_row}")
|
||||||
|
|
||||||
|
# Header pentru tabel
|
||||||
|
prop_header_row = prop_title_row + 1
|
||||||
|
ws[f"A{prop_header_row}"] = "Metric"
|
||||||
|
for strat in STRAT_KEYS:
|
||||||
|
ws[f"{strat_cols[strat]}{prop_header_row}"] = STRAT_LABELS[strat]
|
||||||
|
ws[f"G{prop_header_row}"] = "Cum citesc"
|
||||||
|
for letter in ["A"] + list(strat_cols.values()) + ["G"]:
|
||||||
|
c = ws[f"{letter}{prop_header_row}"]
|
||||||
|
c.font = HEADER_FONT
|
||||||
|
c.fill = HEADER_FILL
|
||||||
|
c.alignment = CENTER
|
||||||
|
c.border = BORDER
|
||||||
|
|
||||||
|
# Definițiile rândurilor — (label, formula_fn(strat), number_format)
|
||||||
|
fail_pass_fmt = "@"
|
||||||
|
prop_metrics: list[tuple[str, callable, str]] = [
|
||||||
|
(
|
||||||
|
"Account Prop Start ($)",
|
||||||
|
lambda s: "=Config!$B$9",
|
||||||
|
'"$"#,##0',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Position Size ($)",
|
||||||
|
lambda s: "=Config!$B$11",
|
||||||
|
'"$"#,##0',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Cumulative P&L Prop ($)",
|
||||||
|
lambda s: f"=SUM({PROP_RANGES['dollar'][s]})",
|
||||||
|
'"$"#,##0.00',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Final Balance Prop ($)",
|
||||||
|
lambda s: f"=Config!$B$9+SUM({PROP_RANGES['dollar'][s]})",
|
||||||
|
'"$"#,##0.00',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Worst Daily Loss ($)",
|
||||||
|
lambda s: f"=IFERROR(MIN({PROP_RANGES['daily'][s]}),0)",
|
||||||
|
'"$"#,##0.00',
|
||||||
|
),
|
||||||
|
# placeholder pentru Daily Status — depinde de Worst Daily de mai sus
|
||||||
|
("Daily Limit Status", lambda s: None, fail_pass_fmt),
|
||||||
|
(
|
||||||
|
"Max Account Drawdown ($)",
|
||||||
|
lambda s: f"=IFERROR(MAX({PROP_RANGES['dd'][s]}),0)",
|
||||||
|
'"$"#,##0.00',
|
||||||
|
),
|
||||||
|
# placeholder pentru Max Status — depinde de Max DD de mai sus
|
||||||
|
("Max Loss Status", lambda s: None, fail_pass_fmt),
|
||||||
|
# placeholder pentru Overall — depinde de cele două statuses
|
||||||
|
("Overall Prop Status", lambda s: None, fail_pass_fmt),
|
||||||
|
]
|
||||||
|
|
||||||
|
prop_label_to_row = {
|
||||||
|
label: prop_header_row + 1 + idx
|
||||||
|
for idx, (label, _, _) in enumerate(prop_metrics)
|
||||||
|
}
|
||||||
|
worst_daily_row = prop_label_to_row["Worst Daily Loss ($)"]
|
||||||
|
daily_status_row = prop_label_to_row["Daily Limit Status"]
|
||||||
|
max_dd_row = prop_label_to_row["Max Account Drawdown ($)"]
|
||||||
|
max_status_row = prop_label_to_row["Max Loss Status"]
|
||||||
|
|
||||||
|
for idx, (label, fn, fmt) in enumerate(prop_metrics):
|
||||||
|
r = prop_header_row + 1 + idx
|
||||||
|
ws[f"A{r}"] = label
|
||||||
|
ws[f"A{r}"].font = Font(name="Calibri", size=11, bold=True)
|
||||||
|
ws[f"A{r}"].border = BORDER
|
||||||
|
ws[f"A{r}"].alignment = LEFT
|
||||||
|
for strat in STRAT_KEYS:
|
||||||
|
letter = strat_cols[strat]
|
||||||
|
if label == "Daily Limit Status":
|
||||||
|
formula = (
|
||||||
|
f'=IF({letter}{worst_daily_row}<-Config!$B$13,"FAIL","PASS")'
|
||||||
|
)
|
||||||
|
elif label == "Max Loss Status":
|
||||||
|
formula = (
|
||||||
|
f'=IF({letter}{max_dd_row}>Config!$B$15,"FAIL","PASS")'
|
||||||
|
)
|
||||||
|
elif label == "Overall Prop Status":
|
||||||
|
formula = (
|
||||||
|
f'=IF(OR({letter}{daily_status_row}="FAIL",'
|
||||||
|
f'{letter}{max_status_row}="FAIL"),'
|
||||||
|
f'"CONT PIERDUT","CONFORM")'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
formula = fn(strat)
|
||||||
|
cell = ws[f"{letter}{r}"]
|
||||||
|
cell.value = formula
|
||||||
|
cell.number_format = fmt
|
||||||
|
cell.fill = DERIVED_FILL
|
||||||
|
cell.border = BORDER
|
||||||
|
cell.alignment = RIGHT if fmt != fail_pass_fmt else CENTER
|
||||||
|
# Hint în coloana G
|
||||||
|
hint_cell = ws[f"G{r}"]
|
||||||
|
hint_cell.value = METRIC_HINTS.get(label, "")
|
||||||
|
hint_cell.font = Font(name="Calibri", size=10, color="595959")
|
||||||
|
hint_cell.alignment = Alignment(
|
||||||
|
horizontal="left", vertical="top", wrap_text=True
|
||||||
|
)
|
||||||
|
hint_cell.border = BORDER
|
||||||
|
|
||||||
|
# Conditional formatting pe status rows — verde PASS/CONFORM, roșu FAIL/CONT PIERDUT
|
||||||
|
pass_fill = PatternFill("solid", fgColor="C6EFCE")
|
||||||
|
fail_fill = PatternFill("solid", fgColor="FFC7CE")
|
||||||
|
for status_row in (daily_status_row, max_status_row):
|
||||||
|
rng = (
|
||||||
|
f"{strat_cols[STRAT_KEYS[0]]}{status_row}:"
|
||||||
|
f"{strat_cols[STRAT_KEYS[-1]]}{status_row}"
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
rng, CellIsRule(operator="equal", formula=['"PASS"'], fill=pass_fill)
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
rng, CellIsRule(operator="equal", formula=['"FAIL"'], fill=fail_fill)
|
||||||
|
)
|
||||||
|
overall_row = prop_label_to_row["Overall Prop Status"]
|
||||||
|
overall_rng = (
|
||||||
|
f"{strat_cols[STRAT_KEYS[0]]}{overall_row}:"
|
||||||
|
f"{strat_cols[STRAT_KEYS[-1]]}{overall_row}"
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
overall_rng,
|
||||||
|
CellIsRule(operator="equal", formula=['"CONFORM"'], fill=pass_fill),
|
||||||
|
)
|
||||||
|
ws.conditional_formatting.add(
|
||||||
|
overall_rng,
|
||||||
|
CellIsRule(
|
||||||
|
operator="equal", formula=['"CONT PIERDUT"'], fill=fail_fill
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Înălțime rânduri prop (cu hint multi-line)
|
||||||
|
for r in range(prop_header_row + 1, prop_header_row + 1 + len(prop_metrics)):
|
||||||
|
ws.row_dimensions[r].height = 60
|
||||||
|
|
||||||
# Column widths
|
# Column widths
|
||||||
widths = {"A": 22, "B": 14, "C": 14, "D": 14, "E": 16, "F": 16, "G": 75}
|
widths = {"A": 22, "B": 14, "C": 14, "D": 14, "E": 16, "F": 16, "G": 75}
|
||||||
for col, w in widths.items():
|
for col, w in widths.items():
|
||||||
@@ -702,6 +989,26 @@ def build_dashboard(wb: Workbook) -> None:
|
|||||||
chart.set_categories(cats)
|
chart.set_categories(cats)
|
||||||
ws.add_chart(chart, "H4")
|
ws.add_chart(chart, "H4")
|
||||||
|
|
||||||
|
# Equity curve prop — al doilea chart, separat de modelul abstract
|
||||||
|
chart_prop = LineChart()
|
||||||
|
chart_prop.title = "Equity Curve — Prop ($50k start)"
|
||||||
|
chart_prop.style = 12
|
||||||
|
chart_prop.y_axis.title = "Balance Prop ($)"
|
||||||
|
chart_prop.x_axis.title = "Trade #"
|
||||||
|
chart_prop.height = 12
|
||||||
|
chart_prop.width = 24
|
||||||
|
|
||||||
|
data_prop = Reference(
|
||||||
|
wb["Trades"],
|
||||||
|
min_col=_col_to_int(COL[f"BalProp_{STRAT_KEYS[0]}"]),
|
||||||
|
max_col=_col_to_int(COL[f"BalProp_{STRAT_KEYS[-1]}"]),
|
||||||
|
min_row=1,
|
||||||
|
max_row=MAX_ROWS + 1,
|
||||||
|
)
|
||||||
|
chart_prop.add_data(data_prop, titles_from_data=True)
|
||||||
|
chart_prop.set_categories(cats)
|
||||||
|
ws.add_chart(chart_prop, "H30")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Main
|
# Main
|
||||||
|
|||||||
Reference in New Issue
Block a user