Mt5pro-bot2/python/backtest/metrics.py

37 lines
1.8 KiB
Python

import numpy as np
import pandas as pd
def compute_metrics(trades: pd.DataFrame, equity: pd.Series, initial: float) -> dict:
m = {"trades": int(len(trades)), "initial": initial}
final = float(equity.iloc[-1]) if len(equity) else initial
m["final"] = final
m["net"] = final - initial
m["return_pct"] = (final / initial - 1) * 100
if len(equity):
peak = equity.cummax()
m["max_dd_pct"] = float(((peak - equity) / peak).max() * 100)
daily = equity.groupby(equity.index.date).last().pct_change().dropna()
m["sharpe"] = float(daily.mean() / daily.std() * np.sqrt(252)) if len(daily) > 5 and daily.std() > 0 else 0.0
else:
m["max_dd_pct"], m["sharpe"] = 0.0, 0.0
if len(trades):
w, l = trades[trades.pnl > 0].pnl, trades[trades.pnl <= 0].pnl
m["win_rate"] = len(w) / len(trades) * 100
m["profit_factor"] = float(w.sum() / -l.sum()) if l.sum() < 0 else float("inf")
m["expectancy"] = float(trades.pnl.mean())
m["avg_win"] = float(w.mean()) if len(w) else 0.0
m["avg_loss"] = float(l.mean()) if len(l) else 0.0
m["avg_r"] = float(trades.r.mean())
streak = best = 0
for p in trades.pnl:
streak = streak + 1 if p <= 0 else 0
best = max(best, streak)
m["max_loss_streak"] = best
else:
m.update(win_rate=0.0, profit_factor=0.0, expectancy=0.0, avg_win=0.0, avg_loss=0.0, avg_r=0.0, max_loss_streak=0)
return m
def fmt(m: dict) -> str:
pf = "inf" if m["profit_factor"] == float("inf") else f"{m['profit_factor']:.2f}"
return (f"Trades {m['trades']} | Net {m['net']:+.0f}$ ({m['return_pct']:+.1f}%) | "
f"Winrate {m['win_rate']:.0f}% | PF {pf} | MaxDD {m['max_dd_pct']:.1f}% | Sharpe {m['sharpe']:.2f}")