bespoke
A Python library for backtesting, comparing, and building trading strategies. Bespoke provides a clean API for defining strategy logic, running historical simulations with realistic slippage modeling, and evaluating performance through standard financial metrics like Sharpe ratio, CAGR, max drawdown, and rolling-window composite scores.
Install
pip install bespoke
Quick Start
from bespoke import Backtester
from bespoke.strategies import get_strategy
strategy = get_strategy("spy_buy_and_hold")
bt = Backtester(strategy, start="2020-01-01", end="2025-12-31")
result = bt.run()
print(f"Return: {result['metrics']['total_return']:.2%}")
print(f"Sharpe: {result['metrics']['sharpe_ratio']:.2f}")
CLI
Bespoke includes a command-line interface for quick analysis.
# List all available strategies
bespoke list
# Show details for a strategy
bespoke info spy_buy_and_hold
# Run a backtest
bespoke backtest spy_buy_and_hold --start 2020-01-01 --end 2025-12-31
# Run on all 28 rolling windows (1Y-15Y x 4 end years)
bespoke backtest spy_buy_and_hold --windows
# Compare two strategies side by side
bespoke compare spy_buy_and_hold balanced_sixty_forty
# Save results to JSON
bespoke save spy_buy_and_hold --output results.json
Creating a Custom Strategy
Subclass BaseStrategy and implement generate_signals:
from bespoke.strategies.base import BaseStrategy, StrategyConfig
class MomentumStrategy(BaseStrategy):
def __init__(self):
super().__init__(StrategyConfig(
name="momentum",
description="Buy assets with positive 50-day momentum",
universe=["SPY", "QQQ", "IWM"],
rebalance_frequency="monthly",
max_position_size=0.50,
))
def generate_signals(self, date, prices, portfolio, data):
signals = {}
for sym in self.universe:
if sym in data and date in data[sym].index:
sma50 = data[sym].loc[:date, "SMA_50"].iloc[-1]
if prices.get(sym, 0) > sma50:
signals[sym] = 1.0
return signals
Then backtest it:
from bespoke import Backtester
bt = Backtester(MomentumStrategy(), start="2020-01-01")
result = bt.run()
Architecture
bespoke/
core/
backtester.py Event-driven backtester with slippage + rebalancing
portfolio.py Position tracking, cash management, trade history
metrics.py Sharpe, Sortino, CAGR, drawdown, alpha/beta, composite
data/
fetcher.py OHLCV via yfinance with parquet caching + indicators
strategies/
base.py BaseStrategy + StrategyConfig dataclass
registry.py Auto-discovery and name-based lookup
generic/ Baseline strategies (SPY, 60/40, three-fund)
compat/
loader.py Bridge to load external strategy collections
cli/
main.py Command-line interface (argparse)
Data flow:
Backtesterloads OHLCV data viafetch_ohlcv(cached locally)- Technical indicators are added (SMA, RSI, MACD, Bollinger, ATR)
- On each trading day,
Strategy.generate_signals()returns target weights Portfolioexecutes rebalancing trades with slippagecompute_metrics()evaluates the resulting equity curve
Available Strategies
Three baseline strategies are included out of the box:
| Name | Description | Universe |
|---|---|---|
spy_buy_and_hold |
100% S&P 500, never trade | SPY |
balanced_sixty_forty |
60% stocks / 40% bonds, monthly rebalance | SPY, AGG |
three_fund_passive |
50% SPY + 30% QQQ + 20% GLD | SPY, QQQ, GLD |
These baselines serve as the performance bar that any custom strategy must clear to justify its complexity.
The bespoke.compat module provides a bridge to load additional strategy collections. See bespoke/compat/loader.py for details.
Metrics
Every backtest produces:
- Total Return and CAGR -- absolute and annualized performance
- Sharpe Ratio -- risk-adjusted return (excess over risk-free rate)
- Sortino Ratio -- penalizes only downside volatility
- Max Drawdown -- worst peak-to-trough decline
- Calmar Ratio -- CAGR / max drawdown
- Win Rate -- fraction of positive trading days
- Alpha / Beta -- vs benchmark (when benchmark data available)
- Composite Score -- rolling-window consistency metric
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-strategy) - Add your strategy in
src/bespoke/strategies/ - Write tests in
tests/ - Run
pytestto verify - Submit a pull request
License
MIT License. See LICENSE for details.
Release files for bespoke 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bespoke-0.2.0.tar.gz | 523.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bespoke-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 770.9 kB
Release files / bespoke-0.2.0.tar.gz
| Download URL | bespoke-0.2.0.tar.gz |
|---|---|
| Size | 523.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f71ea18356b5bb4ecbcb8cb9a6293dc80bc1a99b08a83a67e3b4c0e4b6791302
|
|
BLAKE2b-256 checksum How to use checksums |
627431f9c0b795f1e93cc15859ee1253f09638af3f71e54f82f0654ab58bb37f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.26.0
|
Release files / bespoke-0.2.0-py3-none-any.whl
| Download URL | bespoke-0.2.0-py3-none-any.whl |
|---|---|
| Size | 247.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9251c9974635296f2fde4ef774712e7be1d0d1f7c1ade407673e79b5f1ec11a
|
|
BLAKE2b-256 checksum How to use checksums |
04a9fa57d580c4ceb937428cec0045405f3ceadfd7d6c4848c636f105c4f3d6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
python-httpx/0.26.0
|