Simple backtesting framework for trading strategies
Project description
Simple Backtest
A high-performance backtesting framework for trading strategies
Features • Installation • Quick Start • Documentation • Examples
📖 About
Simple Backtest is a Python framework designed to make backtesting trading strategies straightforward and accessible. Whether you're testing a simple moving average crossover or a complex machine learning model, Simple Backtest provides the tools you need.
Key Philosophy: Bring your own data from any library/api/csv/etc, we dont provide any data source, just the framework to test your strategies. Inherit from Strategy, Commission and Optimizer, and create your own strategies. We have some built-in classes and examples to get you started but the main goal is to be able to use the framework with your own data and strategy, and let simple-backtest handle the rest.
✨ Features
🚀 Performance
|
📊 Analytics
|
🎯 Design
|
🔧 Flexibility
|
Supported Assets
Works with any asset providing OHLC(V) price data:
| Asset Type | Support | Notes |
|---|---|---|
| 📈 Stocks | ✅ Full | Fractional or whole shares |
| 💱 Forex | ✅ Full | Volume optional |
| ₿ Crypto | ✅ Full | Fractional units supported |
| 📊 ETFs | ✅ Full | Same as stocks |
| 🛢️ Commodities | ✅ Full | Gold, oil, etc. |
| 📉 Futures | ⚠️ Partial | No margin/leverage modeling |
| 📊 Options | ❌ No | Requires Greeks, strikes, expiration |
📓 Examples
Interactive Notebooks
Explore comprehensive examples in Jupyter notebooks. Click "Open in Colab" to run them directly in your browser:
📦 Installation
# Using pip
pip install simple-backtest
# Using uv (recommended)
uv add simple-backtest
# From source
git clone https://github.com/LGuillermoAngaritaG/simple-backtest.git
cd simple-backtest
uv sync --all-extras
Requirements: Python 3.10+
🚀 Quick Start
Get up and running in 3 simple steps:
# 1. Get data (using yfinance for demo, but you can use any other data source)
import yfinance as yf
data = yf.download("AAPL", start="2020-01-01", end="2023-12-31")
# 2. Create strategy (you can use a basic one or create your own)
from simple_backtest import Backtest, BacktestConfig, MovingAverageStrategy
strategy = MovingAverageStrategy(short_window=10, long_window=30, shares=10)
# 3. Run backtest
config = BacktestConfig.default(initial_capital=10000)
backtest = Backtest(data, config)
results = backtest.run([strategy])
# View results
print(results.get_strategy(strategy.get_name()).summary())
Output:
Total Return: 227.91%
CAGR: 36.84%
Sharpe Ratio: 1.09
Max Drawdown: -30.60%
Win Rate: 100.00%
📚 Documentation
Creating a Custom Strategy
Implement your own strategy by inheriting from Strategy and defining the predict() method:
from simple_backtest import Strategy
class MyStrategy(Strategy):
"""Custom trading strategy."""
def __init__(self, threshold=100, name=None):
super().__init__(name=name or "MyStrategy")
self.threshold = threshold
def predict(self, data, trade_history):
"""Generate trading signal.
Args:
data: OHLCV DataFrame with lookback window
trade_history: List of past trades
Returns:
Dict with keys: signal ("buy"/"hold"/"sell"), size, order_ids
"""
current_price = data['Close'].iloc[-1]
# Simple logic: buy below threshold, sell above
if current_price < self.threshold and not self.has_position():
return self.buy(10) # Buy 10 shares
elif current_price > self.threshold * 1.2 and self.has_position():
return self.sell_all() # Sell all positions
else:
return self.hold() # Do nothing
Strategy Helper Methods:
self.has_position()- Check if holding any sharesself.get_position()- Get current share countself.get_cash()- Get available cashself.get_portfolio_value()- Get total portfolio valueself.buy(shares)- Return buy signalself.sell(shares)- Return sell signalself.sell_all()- Sell all positionsself.buy_percent(percent)- Buy shares worth % of portfolioself.buy_cash(amount)- Buy shares worth specific amount
Configuration Presets
Quick configurations for common scenarios:
from simple_backtest import BacktestConfig
# Zero commission (for testing)
config = BacktestConfig.zero_commission(initial_capital=10000)
# High-frequency trading (short lookback, flat commission, VWAP execution)
config = BacktestConfig.high_frequency(initial_capital=100000)
# Swing trading (longer lookback, typical retail commission)
config = BacktestConfig.swing_trading(initial_capital=10000)
# Low commission brokers (0.01% commission)
config = BacktestConfig.low_commission(initial_capital=10000)
Comparing Multiple Strategies
from simple_backtest import (
Backtest,
BacktestConfig,
MovingAverageStrategy,
BuyAndHoldStrategy,
DCAStrategy
)
# Create strategies
strategies = [
MovingAverageStrategy(short_window=10, long_window=30, shares=10),
BuyAndHoldStrategy(shares=50),
DCAStrategy(investment_amount=500, interval_days=30)
]
# Run backtest
config = BacktestConfig.default(initial_capital=10000)
backtest = Backtest(data, config)
results = backtest.run(strategies)
# Compare strategies
comparison = results.compare()
print(comparison)
# Get best strategy
best = results.best_strategy('sharpe_ratio')
print(f"Best: {best.name} (Sharpe: {best.metrics['sharpe_ratio']:.2f})")
# Visualize
results.plot_comparison().show()
Parameter Optimization
Find optimal strategy parameters using built-in optimizers:
from simple_backtest import GridSearchOptimizer, BacktestConfig
# Define parameter space
param_space = {
'short_window': [5, 10, 15, 20],
'long_window': [30, 40, 50, 60],
'shares': [10]
}
# Run optimization
optimizer = GridSearchOptimizer(verbose=True)
results = optimizer.optimize(
data=data,
config=BacktestConfig.default(),
strategy_class=MovingAverageStrategy,
param_space=param_space,
metric='sharpe_ratio'
)
# View top results
print(results.head(5))
Available Optimizers:
GridSearchOptimizer- Exhaustive search (best for small spaces)RandomSearchOptimizer- Random sampling (faster for large spaces)WalkForwardOptimizer- Train/test split (prevents overfitting)
Custom Commission Models
Create custom commission structures:
from simple_backtest import Commission
class TieredWithMinimum(Commission):
"""Tiered commission with minimum fee."""
def __init__(self):
super().__init__(name="TieredMin")
def calculate(self, shares, price):
trade_value = shares * price
if trade_value < 1000:
commission = max(trade_value * 0.002, 1.0) # 0.2%, min $1
elif trade_value < 10000:
commission = trade_value * 0.001 # 0.1%
else:
commission = trade_value * 0.0005 # 0.05%
return commission
# Use in config
from simple_backtest import Portfolio
portfolio = Portfolio(10000)
portfolio.commission_calculator = TieredWithMinimum()
Logging Control
Control framework verbosity:
from simple_backtest.utils import setup_logging, disable_logging, enable_debug_logging
import logging
# Default: WARNING level (minimal output)
# For verbose output during optimization
setup_logging(level=logging.INFO)
# For debugging issues
enable_debug_logging()
# To suppress all output
disable_logging()
📊 Performance Metrics
The framework calculates 20+ metrics automatically:
Returns
- Total Return (%)
- CAGR (Compound Annual Growth Rate)
- Annualized Return
Risk Metrics
- Volatility (annualized standard deviation)
- Sharpe Ratio (risk-adjusted return)
- Sortino Ratio (downside risk-adjusted return)
- Calmar Ratio (return vs max drawdown)
- Max Drawdown (%)
- Max Drawdown Duration
Trade Statistics
- Total Trades
- Win Rate (%)
- Profit Factor
- Average Trade P&L
- Trade Expectancy
- Average Win / Average Loss
Benchmark Comparison
- Alpha (excess return vs benchmark)
- Beta (correlation with benchmark)
- Information Ratio
- Correlation with benchmark
🛠️ Development
Setup Development Environment
# Clone repository
git clone https://github.com/LGuillermoAngaritaG/simple-backtest.git
cd simple-backtest
# Install with uv (recommended)
uv sync --all-extras
# Or with pip
pip install -e ".[dev]"
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=simple_backtest
# Run specific test file
uv run pytest tests/test_strategy.py
# Run specific test
uv run pytest tests/test_strategy.py::test_strategy_initialization
Code Quality
# Lint code
uv run ruff check simple_backtest
# Auto-fix linting issues
uv run ruff check simple_backtest --fix
# Format code
uv run ruff format simple_backtest
# Run pre-commit hooks
pre-commit run --all-files
Pre-commit Hooks
Pre-commit hooks automatically run linting, formatting, and tests on commit:
# Install hooks
pre-commit install
# Run manually
pre-commit run --all-files
🤝 Contributing
Contributions are welcome! Whether you're fixing bugs, adding features, or improving documentation, your help is appreciated.
How to Contribute
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Run tests:
uv run pytest - Run linting:
uv run ruff check simple_backtest - Commit your changes:
git commit -m "Add amazing feature" - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Built with Pydantic for configuration validation
- Uses Plotly for interactive visualizations
- Parallel processing with Joblib
- Testing with Pytest
- Code quality with Ruff
📬 Contact & Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: guille2005_13@hotmail.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simple_backtest-0.3.0.tar.gz.
File metadata
- Download URL: simple_backtest-0.3.0.tar.gz
- Upload date:
- Size: 48.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40507431ff33b7ef64b87fc66d88d1fa78972c23a1ced7f38fa341f58e365fc2
|
|
| MD5 |
e58513b28bc3babbcce7fe4c77b7e4fe
|
|
| BLAKE2b-256 |
b60b53b53fb043426739ff8f18bfbcb38c60b51142cb1d308547509890515e2a
|
File details
Details for the file simple_backtest-0.3.0-py3-none-any.whl.
File metadata
- Download URL: simple_backtest-0.3.0-py3-none-any.whl
- Upload date:
- Size: 56.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f0cc0353db44169da698456bf8177218fd37309af3841cb5ad869166d51c24f5
|
|
| MD5 |
fa9a3da03ea4fb97fe2b1beb7042b2d2
|
|
| BLAKE2b-256 |
297dc8bf42b243b1cb326d563cf8166fbe803ea4e2142b2f878ae3d7208c3381
|