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