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.
Metadata
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 | Provenance | Actions |
|---|---|---|---|---|
| bespoke-0.2.0.tar.gz | 523.5 kB | Without provenance | Details for bespoke-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| bespoke-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for bespoke-0.2.0-py3-none-any.whl |
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
|
Security for bespoke 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.