# visualize_results_v2.py # Модифікований візуалізатор: # - Повертає ті ж графіки, що й оригінал # - На часовому графіку показує дати під кутом і лише Місяць-День # - У легенді відображає назву cfg-файла, таймфрейм і кількість барів from __future__ import annotations import os import pandas as pd from typing import Optional, Dict def _read_initial_equity(summary_csv: str | None, default: float = 200.0) -> float: if not summary_csv or not os.path.exists(summary_csv): return default try: sdf = pd.read_csv(summary_csv) for c in sdf.columns: cl = c.lower() if "initial" in cl and "equity" in cl: return float(sdf[c].iloc[0]) except Exception: pass return default def _detect_pnl_series(df: pd.DataFrame) -> pd.Series: # Готові PnL‑колонки for c in ["realized_pnl", "net_pnl", "pnl", "pnl_usd", "netpnl"]: if c in df.columns: return df[c].astype(float).fillna(0.0) # Відновлення з net_return * notional if "net_return" in df.columns and "notional" in df.columns: return (df["net_return"].astype(float).fillna(0.0) * df["notional"].astype(float).fillna(0.0)) raise ValueError("Не знайдено колонку з PnL (realized_pnl/net_pnl/pnl/...).") def _detect_time_series(df: pd.DataFrame) -> Optional[pd.Series]: for c in ["exit_time", "t_exit", "close_time", "timestamp", "time"]: if c in df.columns: ts = pd.to_datetime(df[c], errors="coerce") if ts.notna().any(): return ts return None def _legend_label(cfg_path: Optional[str], timeframe: Optional[str], bars: Optional[int]) -> str: cfg_name = os.path.basename(cfg_path) if cfg_path else "cfg: n/a" tf = timeframe or "?" b = str(bars) if bars is not None else "?" return f"{cfg_name} | TF: {tf} | bars: {b}" def plot_equity_curves( trades_csv: str = "trades.csv", summary_csv: str | None = "summary.csv", initial_equity: float | None = None, show: bool = True, save_dir: str | None = None, file_prefix: str = "", # Нові необов'язкові параметри для легенди cfg_path: str | None = None, timeframe: str | None = None, bars: int | None = None, ) -> Dict[str, str]: """ Малює: 1) Equity vs Trade # 2) Drawdown vs Trade # 3) (якщо є час у трейдах) Equity vs Time, з форматуванням X‑вісі як MM‑DD Повертає dict із шляхами до збережених картинок (якщо save_dir заданий). """ import matplotlib.pyplot as plt import matplotlib.dates as mdates if not os.path.exists(trades_csv): raise FileNotFoundError(f"Не знайдено файл трейдів: {trades_csv}") df = pd.read_csv(trades_csv) if df.empty: raise ValueError("trades.csv порожній — нічого малювати.") pnl = _detect_pnl_series(df) init_eq = float(initial_equity) if initial_equity is not None else _read_initial_equity(summary_csv, 200.0) equity = init_eq + pnl.cumsum() dd = (equity / equity.cummax()) - 1.0 paths: Dict[str, str] = {} title_suffix = os.path.basename(file_prefix) if file_prefix else os.path.splitext(os.path.basename(trades_csv))[0] legend = _legend_label(cfg_path, timeframe, bars) # 1) Equity vs Trade # plt.figure() plt.plot(equity.values, label=legend) plt.title(f"Equity — vs Trade # ({title_suffix})") plt.xlabel("Trade #") plt.ylabel("Equity") plt.legend() if save_dir: os.makedirs(save_dir, exist_ok=True) p = os.path.join(save_dir, f"{file_prefix or 'equity'}_vs_trade.png") plt.savefig(p, dpi=120, bbox_inches="tight") paths["equity_vs_trade"] = p if show: plt.show() else: plt.close() # 2) Drawdown vs Trade # plt.figure() plt.plot(dd.values * 100.0, label=legend) plt.title(f"Drawdown (%) — vs Trade # ({title_suffix})") plt.xlabel("Trade #") plt.ylabel("Drawdown %") plt.legend() if save_dir: p = os.path.join(save_dir, f"{file_prefix or 'dd'}_vs_trade.png") plt.savefig(p, dpi=120, bbox_inches="tight") paths["dd_vs_trade"] = p if show: plt.show() else: plt.close() # 3) Equity vs Time ts = _detect_time_series(df) if ts is not None: mask = ts.notna() if mask.any(): plt.figure() plt.plot(ts[mask].values, equity[mask].values, label=legend) plt.title(f"Equity — vs Time ({title_suffix})") plt.xlabel("Date (MM‑DD)") plt.ylabel("Equity") # Форматування дати: MM‑DD, без року + кут 45° ax = plt.gca() ax.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d')) plt.xticks(rotation=45) plt.tight_layout() plt.legend() if save_dir: p = os.path.join(save_dir, f"{file_prefix or 'equity'}_vs_time.png") plt.savefig(p, dpi=120, bbox_inches="tight") paths["equity_vs_time"] = p if show: plt.show() else: plt.close() return paths