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:
70
backtest.py
Normal file
70
backtest.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
from trade import TradeState, enter_long, exit_long, maybe_trailing_stop
|
||||
from indicators import add_supertrends, compute_meta_trend
|
||||
from metrics import compute_metrics
|
||||
from logging_utils import write_trade_log
|
||||
|
||||
DEFAULT_ST_SETTINGS = [(12, 3.0), (10, 1.0), (11, 2.0)]
|
||||
|
||||
def backtest(
|
||||
df: pd.DataFrame,
|
||||
df_1min: pd.DataFrame,
|
||||
timeframe_minutes: int,
|
||||
stop_loss: float,
|
||||
exit_on_bearish_flip: bool,
|
||||
fee_bps: float,
|
||||
slippage_bps: float,
|
||||
log_path: Path | None = None,
|
||||
):
|
||||
df = add_supertrends(df, DEFAULT_ST_SETTINGS)
|
||||
df["meta_bull"] = compute_meta_trend(df, DEFAULT_ST_SETTINGS)
|
||||
|
||||
state = TradeState(stop_loss_frac=stop_loss, fee_bps=fee_bps, slippage_bps=slippage_bps)
|
||||
equity, trades = [], []
|
||||
|
||||
for i, row in df.iterrows():
|
||||
price = float(row["Close"])
|
||||
ts = pd.Timestamp(row["Timestamp"])
|
||||
|
||||
if state.qty <= 0 and row["meta_bull"] == 1:
|
||||
evt = enter_long(state, price)
|
||||
if evt:
|
||||
evt.update({"t": ts.isoformat(), "reason": "bull_flip"})
|
||||
trades.append(evt)
|
||||
|
||||
start = ts
|
||||
end = df["Timestamp"].iat[i + 1] if i + 1 < len(df) else ts + pd.Timedelta(minutes=timeframe_minutes)
|
||||
|
||||
if state.qty > 0:
|
||||
win = df_1min[(df_1min["Timestamp"] >= start) & (df_1min["Timestamp"] < end)]
|
||||
for _, m in win.iterrows():
|
||||
hi = float(m["High"])
|
||||
lo = float(m["Low"])
|
||||
state.max_px = max(state.max_px or hi, hi)
|
||||
trail = state.max_px * (1.0 - state.stop_loss_frac)
|
||||
if lo <= trail:
|
||||
evt = exit_long(state, trail)
|
||||
if evt:
|
||||
prev = trades[-1]
|
||||
pnl = (evt["price"] - (prev.get("price") or evt["price"])) * (prev.get("qty") or 0.0)
|
||||
evt.update({"t": pd.Timestamp(m["Timestamp"]).isoformat(), "reason": "stop", "pnl": pnl})
|
||||
trades.append(evt)
|
||||
break
|
||||
|
||||
if state.qty > 0 and exit_on_bearish_flip and row["meta_bull"] == 0:
|
||||
evt = exit_long(state, price)
|
||||
if evt:
|
||||
prev = trades[-1]
|
||||
pnl = (evt["price"] - (prev.get("price") or evt["price"])) * (prev.get("qty") or 0.0)
|
||||
evt.update({"t": ts.isoformat(), "reason": "bearish_flip", "pnl": pnl})
|
||||
trades.append(evt)
|
||||
|
||||
equity.append(state.cash + state.qty * price)
|
||||
|
||||
equity_curve = pd.Series(equity, index=df["Timestamp"])
|
||||
if log_path:
|
||||
write_trade_log(trades, log_path)
|
||||
perf = compute_metrics(equity_curve, trades)
|
||||
return perf, equity_curve, trades
|
||||
Reference in New Issue
Block a user