71 lines
2.7 KiB
Python
71 lines
2.7 KiB
Python
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
|