Add initial implementation of backtesting framework with CLI interface. Introduce core modules for data loading, trade management, performance metrics, and logging. Include Supertrend indicator calculations and slippage estimation. Update .gitignore to exclude logs and CSV files.
This commit is contained in:
54
metrics.py
Normal file
54
metrics.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@dataclass
|
||||
class Perf:
|
||||
total_return: float
|
||||
max_drawdown: float
|
||||
sharpe_ratio: float
|
||||
win_rate: float
|
||||
num_trades: int
|
||||
final_equity: float
|
||||
initial_equity: float
|
||||
num_stop_losses: int
|
||||
total_fees: float
|
||||
total_slippage_usd: float
|
||||
avg_slippage_bps: float
|
||||
|
||||
|
||||
def compute_metrics(equity_curve: pd.Series, trades: list[dict]) -> Perf:
|
||||
ret = equity_curve.pct_change().fillna(0.0)
|
||||
total_return = equity_curve.iat[-1] / equity_curve.iat[0] - 1.0
|
||||
cummax = equity_curve.cummax()
|
||||
dd = (equity_curve / cummax - 1.0).min()
|
||||
max_drawdown = dd
|
||||
|
||||
if ret.std(ddof=0) > 0:
|
||||
sharpe = (ret.mean() / ret.std(ddof=0)) * np.sqrt(252 * 24 * 60) # minute bars -> annualized
|
||||
else:
|
||||
sharpe = 0.0
|
||||
|
||||
closes = [t for t in trades if t.get("side") == "SELL"]
|
||||
wins = [t for t in closes if t.get("pnl", 0.0) > 0]
|
||||
win_rate = (len(wins) / len(closes)) if closes else 0.0
|
||||
|
||||
fees = sum(t.get("fee", 0.0) for t in trades)
|
||||
slip = sum(t.get("slippage", 0.0) for t in trades)
|
||||
slippage_bps = [t.get("slippage_bps", 0.0) for t in trades if "slippage_bps" in t]
|
||||
|
||||
return Perf(
|
||||
total_return=total_return,
|
||||
max_drawdown=max_drawdown,
|
||||
sharpe_ratio=sharpe,
|
||||
win_rate=win_rate,
|
||||
num_trades=len(closes),
|
||||
final_equity=float(equity_curve.iat[-1]),
|
||||
initial_equity=float(equity_curve.iat[0]),
|
||||
num_stop_losses=sum(1 for t in closes if t.get("reason") == "stop"),
|
||||
total_fees=fees,
|
||||
total_slippage_usd=slip,
|
||||
avg_slippage_bps=float(np.mean(slippage_bps)) if slippage_bps else 0.0,
|
||||
)
|
||||
Reference in New Issue
Block a user