### lowkey_backtest — Supertrend Backtester ### Overview Backtest a simple, long-only strategy driven by a meta Supertrend signal on aggregated OHLCV data. The script: - Loads 1-minute BTC/USD data from `../data/btcusd_1-min_data.csv` - Aggregates to multiple timeframes (e.g., `5min`, `15min`, `30min`, `1h`, `4h`, `1d`) - Computes three Supertrend variants and creates a meta signal when all agree - Executes entries/exits at the aggregated bar open price - Applies OKX spot fee assumptions (taker by default) - Evaluates stop-loss using intra-bar 1-minute data - Writes detailed trade logs and a summary CSV ### Requirements - Python 3.12+ - Dependencies: `pandas`, `numpy`, `ta` - Package management: `uv` Install dependencies with uv: ```bash uv sync # If a dependency is missing, add it explicitly and sync uv add pandas numpy ta uv sync ``` ### Data - Expected CSV location: `../data/btcusd_1-min_data.csv` (relative to the repo root) - Required columns: `Timestamp`, `Open`, `High`, `Low`, `Close`, `Volume` - `Timestamp` should be UNIX seconds; zero-volume rows are ignored ### Quickstart Run the backtest with defaults: ```bash uv run python main.py ``` Outputs: - Per-run trade logs in `backtest_logs/` named like `trade_log__sl.csv` - Run-level summary in `backtest_summary.csv` ### Configuring a Run Adjust parameters directly in `main.py`: - Date range (in `load_data`): `load_data('2021-11-01', '2024-10-16')` - Timeframes to test (any subset of `"5min", "15min", "30min", "1h", "4h", "1d"`): - `timeframes = ["5min", "15min", "30min", "1h", "4h", "1d"]` - Stop-loss percentages: - `stoplosses = [0.03, 0.05, 0.1]` - Supertrend settings (in `add_supertrend_indicators`): `(period, multiplier)` pairs `(12, 3.0)`, `(10, 1.0)`, `(11, 2.0)` - Fee model (in `calculate_okx_taker_maker_fee`): taker `0.0010`, maker `0.0008` ### What the Backtester Does - Aggregation: Resamples 1-minute data to your selected timeframe using OHLCV rules - Supertrend signals: Computes three Supertrends and derives a meta trend of `+1` (bullish) or `-1` (bearish) when all agree; otherwise `0` - Trade logic (long-only): - Entry when the meta trend changes to bullish; uses aggregated bar open price - Exit when the meta trend changes to bearish; uses aggregated bar open price - Stop-loss: For each aggregated bar, scans corresponding 1-minute closes to detect stop-loss and exits using a realistic fill (threshold or next 1-minute open if gapped) - Performance metrics: total return, max drawdown, Sharpe (daily, factor 252), win rate, number of trades, final/initial equity, and total fees ### Important: Lookahead Bias Note The current implementation uses the meta Supertrend signal of the same bar for entries/exits, which introduces lookahead bias. To avoid this, lag the signal by one bar inside `backtest()` in `main.py`: ```python # Replace the current line meta_trend_signal = meta_trend # With a one-bar lag to remove lookahead # meta_trend_signal = np.roll(meta_trend, 1) # meta_trend_signal[0] = 0 ``` ### Outputs - `backtest_logs/trade_log__sl.csv`: trade-by-trade records including type (`buy`, `sell`, `stop_loss`, `forced_close`), timestamps, prices, balances, PnL, and fees - `backtest_summary.csv`: one row per (timeframe, stop-loss) combination with `timeframe`, `stop_loss`, `total_return`, `max_drawdown`, `sharpe_ratio`, `win_rate`, `num_trades`, `final_equity`, `initial_equity`, `num_stop_losses`, `total_fees` ### Troubleshooting - CSV not found: Ensure the dataset is located at `../data/btcusd_1-min_data.csv` - Missing packages: Run `uv add pandas numpy ta` then `uv sync` - Memory/performance: Large date ranges on 1-minute data can be heavy; narrow the date span or test fewer timeframes