2025-07-29 20:51:11 +00:00

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:

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:

uv run python main.py

Outputs:

  • Per-run trade logs in backtest_logs/ named like trade_log_<TIMEFRAME>_sl<STOPLOSS>.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:

# 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_<TIMEFRAME>_sl<STOPLOSS>.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
Description
No description provided
Readme 82 KiB
Languages
Python 100%