Skip to main content

A backtesting framework for cryptocurrency trading strategies on Binance

Project description

Crypto Backtester

PyPI version PyPI - Downloads Python 3.11+ License: MIT Tests Code Quality codecov Ruff

A backtesting framework for cryptocurrency trading strategies on Binance. Supports spot and perpetual futures markets with risk management, position sizing, and performance analytics.

Features

  • Historical Data Collection: Automated collection and caching of OHLCV, trades, funding rates, and open interest data
  • Strategy Backtesting: Run strategies over historical data with configurable time steps
  • Risk Management: User-defined position manager for custom risk screening and position sizing
  • OMS Simulation: Order management system that tracks positions, balances, and trade history
  • Performance Metrics: Returns, Sharpe ratio, drawdown, and permutation testing
  • Multiple Market Types: Support for both spot and perpetual futures markets

Prerequisites

  • Python 3.11 or higher
  • pip or uv package manager

Installation

From PyPI (recommended)

pip install crypto-backtester-binance

From Source

git clone https://github.com/LevRoz630/crypto-backtester.git
cd crypto-backtester
pip install -e .

With development dependencies:

pip install -e ".[dev,test,docs]"

Data Directory

The framework will automatically download and cache historical data. You can specify a custom path when initializing the Backtester, or it defaults to ./historical_data.

Project Structure

crypto-backtester-binance/
├── src/crypto_backtester_binance/  # Core library
│   ├── backtester.py               # Main backtest orchestrator
│   ├── oms_simulation.py           # Order management system
│   ├── hist_data.py                # Historical data collector
│   └── utils.py                    # Utility functions
├── examples/                       # Example scripts
│   ├── position_manager.py         # Reference position manager (user-defined)
│   ├── 01_simple_backtest.py       # Buy-and-hold strategy
│   ├── 02_momentum_strategy.py     # SMA crossover strategy
│   └── 03_long_short_strategy.py   # Long BTC / Short Alts
├── tests/                          # Test suite
├── docs/                           # Documentation
└── historical_data/                # Cached data (created on first run)

Quick Start

Running Your First Backtest

from datetime import datetime, timedelta, UTC
from typing import Any

from crypto_backtester_binance.backtester import Backtester
from crypto_backtester_binance.hist_data import HistoricalDataCollector
from crypto_backtester_binance.oms_simulation import OMSClient


# Define a simple strategy
class HoldStrategy:
    def __init__(self, symbols: list[str], lookback_days: int = 0):
        self.symbols = symbols
        self.lookback_days = lookback_days
        self.has_bought = False

    def run_strategy(self, oms_client: OMSClient, data_manager: HistoricalDataCollector):
        orders = []
        if not self.has_bought:
            for symbol in self.symbols:
                orders.append({"symbol": symbol, "instrument_type": "future", "side": "LONG"})
            self.has_bought = True
        return orders


# Define a position manager (user-defined risk management)
class SimplePositionManager:
    def filter_orders(
        self, orders: list[dict[str, Any]], oms_client: Any, data_manager: HistoricalDataCollector
    ) -> list[dict[str, Any]] | None:
        """Add position sizing to orders. Must return orders with 'value' field."""
        if not orders:
            return None
        # Simple equal-weight allocation: 10% of balance per order
        budget = oms_client.balance["USDT"] * 0.1 / len(orders)
        return [{**order, "value": budget} for order in orders]


# Initialize backtester
backtester = Backtester(historical_data_dir="./historical_data")

# Create strategy and position manager
strategy = HoldStrategy(symbols=["BTC-USDT", "ETH-USDT", "SOL-USDT"])
position_manager = SimplePositionManager()

# Set backtest period
start_date = datetime.now(UTC) - timedelta(days=50)
end_date = datetime.now(UTC) - timedelta(days=1)

# Run backtest
results = backtester.run_backtest(
    strategy=strategy,
    position_manager=position_manager,
    start_date=start_date,
    end_date=end_date,
    time_step=timedelta(days=1),
    market_type="futures",
)

# View results
print(f"Total Return: {results['total_return']:.2%}")
print(f"Sharpe Ratio: {results['sharpe_ratio']:.2f}")
print(f"Max Drawdown: {results['max_drawdown']:.2%}")

# Generate plots
backtester.plot_portfolio_value()
backtester.plot_positions(results)

The first run will download historical data automatically. Subsequent runs use cached data.

Running Example Scripts

# Simple buy-and-hold
python examples/01_simple_backtest.py

# Momentum strategy (SMA crossover)
python examples/02_momentum_strategy.py

# Long BTC / Short Alts
python examples/03_long_short_strategy.py

Creating Custom Strategies

A strategy must implement the run_strategy method that returns a list of order dictionaries:

from datetime import timedelta

from crypto_backtester_binance.hist_data import HistoricalDataCollector
from crypto_backtester_binance.oms_simulation import OMSClient


class MyStrategy:
    def __init__(self, symbols: list[str], lookback_days: int):
        self.symbols = symbols
        self.lookback_days = lookback_days

    def run_strategy(
        self,
        oms_client: OMSClient,
        data_manager: HistoricalDataCollector,
    ) -> list[dict]:
        """
        Generate trading orders based on strategy logic.

        Returns:
            List of order dictionaries with keys:
            - symbol: str (e.g., "BTC-USDT")
            - instrument_type: str ("spot" or "future")
            - side: str ("LONG", "SHORT", or "CLOSE")
            - value: float (optional, USDT notional)
        """
        orders = []

        for symbol in self.symbols:
            # Load historical data
            data = data_manager.load_data_period(
                symbol=symbol,
                timeframe="1h",
                data_type="mark_ohlcv_futures",
                start_date=oms_client.current_time - timedelta(days=self.lookback_days),
                end_date=oms_client.current_time,
            )

            # Your strategy logic here...

            orders.append({
                "symbol": symbol,
                "instrument_type": "future",
                "side": "LONG",  # or "SHORT" or "CLOSE"
            })

        return orders

Configuration

Market Types

  • "futures": Uses perpetual futures data with margin-based positions

Time Steps

Supported time deltas map to data timeframes:

  • timedelta(minutes=1)"1m"
  • timedelta(minutes=5)"5m"
  • timedelta(minutes=15)"15m" (default)
  • timedelta(minutes=30)"30m"
  • timedelta(hours=1)"1h"

Position Manager

The position manager is user-defined - you create it to match your risk management needs. It must implement a filter_orders method with this signature:

def filter_orders(
    self,
    orders: list[dict],
    oms_client: OMSClient,
    data_manager: HistoricalDataCollector
) -> list[dict] | None:
    """
    Process orders from strategy before execution.

    Args:
        orders: Raw orders from strategy
        oms_client: Access to balance, positions, current time
        data_manager: Access to historical data

    Returns:
        List of orders with 'value' field set, or None to skip
    """

See examples/position_manager.py for a reference implementation with:

  • Volatility-based risk screening
  • Inverse-volatility position sizing
  • Budget enforcement

Documentation

Building Documentation

If you installed with [docs]:

# Build static HTML
mkdocs build

# Serve locally for preview
mkdocs serve

Then open http://127.0.0.1:8000 in your browser.

Documentation Structure

  • Overview: Architecture and data flow (docs/overview.md)
  • API Reference: Auto-generated from docstrings (docs/api/)
  • Examples: Strategy implementations (examples/)

Permutation Testing

Test strategy significance using randomized returns:

results = backtester.run_permutation_backtest(
    strategy=strategy,
    position_manager=position_manager,
    start_date=start_date,
    end_date=end_date,
    time_step=timedelta(days=1),
    market_type="futures",
    permutations=100,  # Number of random permutations
)

print(f"P-value: {results['p_value']:.4f}")
print(f"Observed Sharpe: {results['sharpe_ratio']:.2f}")

Troubleshooting

Data Download Issues

  • Ensure you have internet connectivity for first-time data collection
  • Check that the historical_data directory is writable
  • Data is cached in Parquet format for fast subsequent loads

Import Errors

If you see import errors, ensure the package is installed:

pip install crypto-backtester-binance

Or for development:

pip install -e .

Memory Issues

For large backtests:

  • Reduce lookback_days in your strategy
  • Use longer time_step intervals
  • Process data in chunks

License

See LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Submit a pull request

Support

For issues and questions, please open an issue on the repository.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

crypto_backtester_binance-1.0.0.tar.gz (263.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

crypto_backtester_binance-1.0.0-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file crypto_backtester_binance-1.0.0.tar.gz.

File metadata

File hashes

Hashes for crypto_backtester_binance-1.0.0.tar.gz
Algorithm Hash digest
SHA256 69440f52d5938d359ac6c50ffc2cd27be60f96029c2afcc80035d7f612b4d9ad
MD5 6b68dc003debd29eb56fabb4e5039d83
BLAKE2b-256 c045e5096c89aceb8f528a4983b55a408940f075dfc1c795357013b2d3201938

See more details on using hashes here.

File details

Details for the file crypto_backtester_binance-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for crypto_backtester_binance-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c39c0f3952f623671ee342c13eeb3d9dd1d76846eed7c42a438dc439b6a04e5d
MD5 fa17eaf07b849fe92e288859348843d6
BLAKE2b-256 0cbe3d7946320600ae626da3b713699968cdb2eb6eacd215c29cb651e4b11984

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page