Fixed bug on portfolio builder equity chart when selecting EA + strategy.

This commit is contained in:
unknown
2026-05-03 19:59:22 +10:00
parent 7cd1ab6a12
commit 045365838b
2 changed files with 60 additions and 15 deletions
+1
View File
@@ -7,3 +7,4 @@ mt5_accounts.json
ftp_config.json
ftp_accounts.json
*.docx
*.pyo
+59 -15
View File
@@ -287,7 +287,12 @@ def _build_equity_chart(
_global_max = min(_global_max, pd.Timestamp(date_to) + pd.Timedelta(days=1))
def _plot_series(times: pd.Series, profits: pd.Series,
name: str, color: str, width: float):
name: str, color: str, width: float,
contribute_to_aggregates: bool = True):
"""Plot one equity line. Only contributes to row 2 (drawdown) and row 3
(daily P&L) when contribute_to_aggregates=True. This prevents
double-counting in modes that show both a combined line and individual
strategy lines for the same underlying trades."""
times = times.reset_index(drop=True)
profits = profits.reset_index(drop=True)
eq_full = deposit + profits.cumsum()
@@ -307,16 +312,17 @@ def _build_equity_chart(
if eq_f.empty:
return
all_dd_frames.append(pd.DataFrame({"t": times_f, "dd": dd_f}))
if contribute_to_aggregates:
all_dd_frames.append(pd.DataFrame({"t": times_f, "dd": dd_f}))
# Daily P&L — sum net_profit per day within the filtered window
profits_f = profits[mask].reset_index(drop=True)
daily_pnl = (pd.DataFrame({"t": times_f, "pnl": profits_f})
.assign(date=lambda x: x["t"].dt.normalize())
.groupby("date")["pnl"].sum()
.reset_index()
.rename(columns={"date": "t"}))
all_daily_frames.append(daily_pnl)
# Daily P&L — sum net_profit per day within the filtered window
profits_f = profits[mask].reset_index(drop=True)
daily_pnl = (pd.DataFrame({"t": times_f, "pnl": profits_f})
.assign(date=lambda x: x["t"].dt.normalize())
.groupby("date")["pnl"].sum()
.reset_index()
.rename(columns={"date": "t"}))
all_daily_frames.append(daily_pnl)
eq_disp = _smooth(eq_f, smooth_window)
@@ -338,12 +344,14 @@ def _build_equity_chart(
if chart_view == "Portfolio":
# Show the selected portfolio / all as one combined line
if not df.empty and "close_time" in df.columns and "net_profit" in df.columns:
_plot_series(df["close_time"], df["net_profit"], active_label, COLORS[0], 2.0)
_plot_series(df["close_time"], df["net_profit"], active_label, COLORS[0], 2.0,
contribute_to_aggregates=True)
if show_stagnation:
_add_stagnation_vrect(fig, df, deposit)
elif chart_view == "Portfolio+Individual":
# Combined line + each member underneath
# Combined line + each member underneath. Only the combined line
# contributes to drawdown / daily-P&L subplots so we don't double-count.
if not df.empty and "close_time" in df.columns and "net_profit" in df.columns:
import os as _os, re as _re2
_cfg = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)), ".streamlit", "config.toml")
@@ -353,9 +361,13 @@ def _build_equity_chart(
if _m: _light = _m.group(1) == "light"
_portfolio_color = "#1a3a5c" if _light else "#FFFFFF"
_plot_series(df["close_time"], df["net_profit"], f"{active_label} (combined)",
_portfolio_color, 2.5)
_portfolio_color, 2.5, contribute_to_aggregates=True)
if show_stagnation:
_add_stagnation_vrect(fig, df, deposit)
# Dedupe individual lines: skip any whose trade fingerprint matches another
# already-plotted line (prevents EA/Strategy duplicates when each EA file
# contains only one strategy comment).
_seen_fingerprints = set()
for i, label in enumerate(selected_strategies):
if label not in eff_dfs:
continue
@@ -363,10 +375,29 @@ def _build_equity_chart(
if "close_time" not in sdf.columns or "net_profit" not in sdf.columns:
continue
sdf_s = sdf.sort_values("close_time")
# Fingerprint: count + sum of net_profit + first/last close_time.
# Two series with the same fingerprint are the same trades.
try:
fp = (
len(sdf_s),
round(float(sdf_s["net_profit"].sum()), 4),
str(pd.to_datetime(sdf_s["close_time"]).min()),
str(pd.to_datetime(sdf_s["close_time"]).max()),
)
except Exception:
fp = (label,)
if fp in _seen_fingerprints:
continue
_seen_fingerprints.add(fp)
_plot_series(sdf_s["close_time"], sdf_s["net_profit"],
label, COLORS[i % len(COLORS)], 1.2)
label, COLORS[i % len(COLORS)], 1.2,
contribute_to_aggregates=False)
else: # Individual
# No combined line; each strategy's trades contribute to aggregates once.
# Dedupe identical trade-sets (e.g. EA == Strategy when each file has
# one strategy comment).
_seen_fingerprints = set()
for i, label in enumerate(selected_strategies):
if label not in eff_dfs:
continue
@@ -374,8 +405,21 @@ def _build_equity_chart(
if "close_time" not in sdf.columns or "net_profit" not in sdf.columns:
continue
sdf_s = sdf.sort_values("close_time")
try:
fp = (
len(sdf_s),
round(float(sdf_s["net_profit"].sum()), 4),
str(pd.to_datetime(sdf_s["close_time"]).min()),
str(pd.to_datetime(sdf_s["close_time"]).max()),
)
except Exception:
fp = (label,)
if fp in _seen_fingerprints:
continue
_seen_fingerprints.add(fp)
_plot_series(sdf_s["close_time"], sdf_s["net_profit"],
label, COLORS[i % len(COLORS)], 1.5)
label, COLORS[i % len(COLORS)], 1.5,
contribute_to_aggregates=True)
# Row 2 — cumulative drawdown from peak
if all_dd_frames: