Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Bug: CryptoBacktestEngine assumes hourly data but download_crypto_data provides daily data #1

Description

@leoncuhk

Summary

CryptoBacktestEngine hardcodes periods_per_day=24 (assuming hourly bars), but the system's own download_crypto_data() function downloads daily data (timeframe="1d"). This internal inconsistency causes annualized returns to be inflated by orders of magnitude when using the standard data pipeline.

This is not a user-error scenario — it's a mismatch between two modules in the same system:

  • auto_download.py:153download_crypto_data(timeframe="1d") — downloads daily candles
  • crypto_backtest.py:45CryptoBacktestEngine(periods_per_day=24) — assumes hourly candles
  • auto_evolve.py:141WalkForwardConfig(bars_per_year=8760) — assumes hourly candles

Steps to Reproduce

pip install stratevo ccxt  # v6.0.5
stratevo evolve --market crypto --generations 30 --population 20 --seed 42
# (auto-downloads daily data via download_crypto_data, then backtests assuming hourly)

If ccxt can connect to an exchange, download_crypto_data fetches daily candles (timeframe="1d") and feeds them to CryptoBacktestEngine which processes them as if they were hourly — producing walk-forward window returns like:

Window 0: +462.0%  (66 trades) ✅
Window 1: +1666.1%  (63 trades) ✅ ⚠️
Window 3: +149193.8%  (67 trades) ✅ ⚠️

Root Cause

The annualization formula in crypto_backtest.py:594-597:

periods_per_year = 365 * self.periods_per_day  # 8760 (but data is daily, should be 365)
years = periods_used / periods_per_year          # 194 daily bars → 0.022 years (should be 0.53)
annual_return = ((1 + total_return) ** (1/years) - 1) * 100  # exponential explosion

194 daily bars are treated as 194 hourly bars (~8 days). A 50% return over 194 days becomes: (1.5)^(1/0.022) - 1 = 8,939,994,666%.

Same mismatch affects hold_periods (inflated 12×), Sharpe ratio (~6× inflated), and walk-forward window CAGR chaining.

Controlled Experiment (seed=42, same daily data, same config)

Metric Crypto BUG (ppd=24) Crypto FIXED (ppd=1) US Stocks baseline
Annual Return 374.3% 37.3% 39.2%
Sharpe 2.45 0.59 1.74
Max Drawdown 17.7% 45.1% 7.5%
Win Rate 10.8% 42.5% 56.7%
OOS windows +462% ~ +149,194% -11% ~ +89% +21% ~ +53%
Held-out +149.8% PASSED -15.7% FAILED +28.1% PASSED

"FIXED" = set periods_per_day=1 to match the daily data that download_crypto_data actually provides.

Suggested Fix

Either:

A. Make the download match the engine — change download_crypto_data default to timeframe="1h"

B. Make the engine match the download — auto-detect data frequency from date intervals after loading:

# Infer from date gaps: median interval >= 12h → daily
dates = data[first_symbol]["date"][:50]
intervals = [(dates[i+1] - dates[i]) for i in range(len(dates)-1)]
median_hours = sorted(intervals)[len(intervals)//2].total_seconds() / 3600
periods_per_day = 1 if median_hours >= 12 else 24

Then propagate to CryptoBacktestEngine.periods_per_day, WalkForwardConfig.bars_per_year, and CAGR chaining in auto_evolve.py:1313.

Option B is more robust since users may provide data at any frequency via --data-dir.

Environment

  • stratevo 6.0.5 (PyPI)
  • Python 3.12.13, macOS Darwin 25.5.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions