0
# Backtesting Engine
1
2
Core backtesting functionality for combining strategies with data, executing backtests, and analyzing results. The engine provides comprehensive statistics, visualization capabilities, and specialized result analysis tools.
3
4
## Capabilities
5
6
### Main Backtesting Class
7
8
The primary class for combining strategies with historical data to produce backtest results.
9
10
```python { .api }
11
class Backtest:
12
"""
13
Main backtesting class that combines strategy with data.
14
15
Args:
16
strategy: Strategy instance to backtest
17
data: Historical price data (DataFrame)
18
name (str, optional): Backtest name
19
initial_capital (float): Starting capital (default 1,000,000)
20
commissions (callable, optional): Commission calculation function
21
integer_positions (bool): Whether to use integer position sizes
22
progress_bar (bool): Show progress during execution
23
additional_data (dict, optional): Additional data for strategy use
24
"""
25
def __init__(self, strategy, data, name=None, initial_capital=1000000.0,
26
commissions=None, integer_positions=True, progress_bar=False,
27
additional_data=None): ...
28
29
def run(self):
30
"""Execute the backtest."""
31
...
32
33
# Properties
34
@property
35
def strategy(self): ...
36
@property
37
def data(self): ...
38
@property
39
def dates(self): ...
40
@property
41
def initial_capital(self) -> float: ...
42
@property
43
def name(self) -> str: ...
44
@property
45
def stats(self): ...
46
@property
47
def has_run(self) -> bool: ...
48
@property
49
def weights(self): ...
50
@property
51
def positions(self): ...
52
@property
53
def security_weights(self): ...
54
@property
55
def herfindahl_index(self): ...
56
@property
57
def turnover(self): ...
58
@property
59
def additional_data(self): ...
60
```
61
62
### Backtest Execution
63
64
Primary function for running multiple backtests and comparing results.
65
66
```python { .api }
67
def run(*backtests):
68
"""
69
Run multiple backtests and return Result object.
70
71
Args:
72
*backtests: Variable number of Backtest instances
73
74
Returns:
75
Result: Combined results object for analysis
76
"""
77
...
78
```
79
80
### Result Analysis Class
81
82
Comprehensive results container with analysis and visualization capabilities.
83
84
```python { .api }
85
class Result:
86
"""
87
Results analysis class inheriting from ffn.GroupStats.
88
Container for backtest results with analysis capabilities.
89
90
Args:
91
*backtests: Variable number of completed Backtest instances
92
"""
93
def __init__(self, *backtests): ...
94
95
def display_monthly_returns(self, backtest=0):
96
"""
97
Display monthly return table.
98
99
Args:
100
backtest (int): Index of backtest to display
101
"""
102
...
103
104
def get_weights(self, backtest=0, filter=None):
105
"""
106
Get strategy weights over time.
107
108
Args:
109
backtest (int): Index of backtest
110
filter (callable, optional): Filter function for weights
111
112
Returns:
113
DataFrame: Weight data over time
114
"""
115
...
116
117
def plot_weights(self, backtest=0, filter=None, figsize=(15, 5), **kwds):
118
"""
119
Plot strategy weights over time.
120
121
Args:
122
backtest (int): Index of backtest
123
filter (callable, optional): Filter function for weights
124
figsize (tuple): Figure size
125
**kwds: Additional plotting arguments
126
"""
127
...
128
129
def get_security_weights(self, backtest=0, filter=None):
130
"""
131
Get security-level weights over time.
132
133
Args:
134
backtest (int): Index of backtest
135
filter (callable, optional): Filter function for weights
136
137
Returns:
138
DataFrame: Security weight data over time
139
"""
140
...
141
142
def plot_security_weights(self, backtest=0, filter=None, figsize=(15, 5), **kwds):
143
"""
144
Plot security-level weights over time.
145
146
Args:
147
backtest (int): Index of backtest
148
filter (callable, optional): Filter function for weights
149
figsize (tuple): Figure size
150
**kwds: Additional plotting arguments
151
"""
152
...
153
154
def plot_histogram(self, backtest=0, **kwds):
155
"""
156
Plot return histogram.
157
158
Args:
159
backtest (int): Index of backtest
160
**kwds: Additional plotting arguments
161
"""
162
...
163
164
def get_transactions(self, strategy_name=None):
165
"""
166
Get transaction data for analysis.
167
168
Args:
169
strategy_name (str, optional): Specific strategy name to filter
170
171
Returns:
172
DataFrame: Transaction history
173
"""
174
...
175
176
# Properties
177
@property
178
def backtest_list(self) -> list: ...
179
@property
180
def backtests(self) -> dict: ...
181
```
182
183
### Benchmarking Functions
184
185
Utilities for comparing strategies against random benchmarks.
186
187
```python { .api }
188
def benchmark_random(backtest, random_strategy, nsim=100):
189
"""
190
Benchmark strategy against random portfolios.
191
192
Args:
193
backtest: Backtest instance to benchmark
194
random_strategy: Random strategy for comparison
195
nsim (int): Number of random simulations
196
197
Returns:
198
RandomBenchmarkResult: Benchmarking results
199
"""
200
...
201
202
class RandomBenchmarkResult(Result):
203
"""
204
Results for random strategy benchmarking.
205
206
Args:
207
*backtests: Backtest instances including base and random strategies
208
"""
209
def __init__(self, *backtests): ...
210
211
def plot_histogram(self, statistic="monthly_sharpe", figsize=(15, 5),
212
title=None, bins=20, **kwargs):
213
"""
214
Plot benchmark histogram comparing strategy to random portfolios.
215
216
Args:
217
statistic (str): Statistic to compare
218
figsize (tuple): Figure size
219
title (str, optional): Plot title
220
bins (int): Number of histogram bins
221
**kwargs: Additional plotting arguments
222
"""
223
...
224
225
# Properties
226
@property
227
def base_name(self) -> str: ...
228
@property
229
def r_stats(self): ... # Random strategy statistics
230
@property
231
def b_stats(self): ... # Base strategy statistics
232
```
233
234
### Fixed Income Results
235
236
Specialized result class for fixed income strategy analysis with renormalization.
237
238
```python { .api }
239
class RenormalizedFixedIncomeResult(Result):
240
"""
241
Results for comparing FixedIncomeStrategy with different normalizations.
242
243
Args:
244
normalizing_value: Value used for renormalization
245
*backtests: Backtest instances to compare
246
"""
247
def __init__(self, normalizing_value, *backtests): ...
248
249
def _price(self, s, v):
250
"""Internal price calculation with renormalization."""
251
...
252
```
253
254
## Usage Examples
255
256
### Basic Backtesting
257
258
```python
259
import bt
260
import pandas as pd
261
262
# Get data
263
data = bt.get('SPY,TLT', start='2010-01-01', end='2020-01-01')
264
265
# Create strategy
266
strategy = bt.Strategy('EqualWeight', [
267
bt.algos.RunMonthly(),
268
bt.algos.SelectAll(),
269
bt.algos.WeighEqually(),
270
bt.algos.Rebalance()
271
])
272
273
# Run backtest
274
backtest = bt.Backtest(strategy, data, initial_capital=100000)
275
result = bt.run(backtest)
276
277
# Analyze results
278
print(result.stats)
279
result.plot()
280
result.display_monthly_returns()
281
```
282
283
### Multiple Strategy Comparison
284
285
```python
286
# Create multiple strategies
287
equal_weight = bt.Strategy('EqualWeight', [
288
bt.algos.RunMonthly(),
289
bt.algos.SelectAll(),
290
bt.algos.WeighEqually(),
291
bt.algos.Rebalance()
292
])
293
294
risk_parity = bt.Strategy('RiskParity', [
295
bt.algos.RunMonthly(),
296
bt.algos.SelectAll(),
297
bt.algos.WeighInvVol(),
298
bt.algos.Rebalance()
299
])
300
301
# Run multiple backtests
302
bt1 = bt.Backtest(equal_weight, data)
303
bt2 = bt.Backtest(risk_parity, data)
304
result = bt.run(bt1, bt2)
305
306
# Compare results
307
result.plot()
308
result.display()
309
```
310
311
### Custom Commission Functions
312
313
```python
314
def custom_commission(quantity, price):
315
"""Custom commission: $0.01 per share, min $1."""
316
return max(abs(quantity) * 0.01, 1.0)
317
318
# Use in backtest
319
backtest = bt.Backtest(strategy, data, commissions=custom_commission)
320
result = bt.run(backtest)
321
```
322
323
### Weight Analysis
324
325
```python
326
# Analyze strategy weights over time
327
result = bt.run(backtest)
328
329
# Get weights data
330
weights = result.get_weights(0)
331
print(weights.head())
332
333
# Plot weights
334
result.plot_weights(0, figsize=(12, 6))
335
336
# Get security-level weights
337
sec_weights = result.get_security_weights(0)
338
result.plot_security_weights(0)
339
```
340
341
### Transaction Analysis
342
343
```python
344
# Get transaction history
345
transactions = result.get_transactions()
346
print(transactions.head())
347
348
# Analyze turnover
349
print(f"Average monthly turnover: {backtest.turnover.mean():.2%}")
350
351
# Plot turnover over time
352
backtest.turnover.plot(title='Monthly Turnover')
353
```
354
355
### Random Benchmarking
356
357
```python
358
# Create random strategy for benchmarking
359
random_strat = bt.Strategy('Random', [
360
bt.algos.RunMonthly(),
361
bt.algos.SelectAll(),
362
bt.algos.WeighRandomly(),
363
bt.algos.Rebalance()
364
])
365
366
# Benchmark against random portfolios
367
benchmark_result = bt.benchmark_random(backtest, random_strat, nsim=1000)
368
benchmark_result.plot_histogram('monthly_sharpe', bins=50)
369
```
370
371
### Performance Statistics
372
373
The Result class inherits from ffn.GroupStats, providing access to comprehensive performance statistics:
374
375
```python
376
# Access built-in statistics
377
stats = result.stats
378
print(f"Total Return: {stats.loc['total_return'].iloc[0]:.2%}")
379
print(f"Sharpe Ratio: {stats.loc['monthly_sharpe'].iloc[0]:.2f}")
380
print(f"Max Drawdown: {stats.loc['max_drawdown'].iloc[0]:.2%}")
381
print(f"Volatility: {stats.loc['monthly_vol'].iloc[0]:.2%}")
382
383
# Plot equity curves
384
result.plot()
385
386
# Plot drawdown
387
result.plot_histogram()
388
```