Jupyter Notebook Guide for Backtrader¶
This guide provides comprehensive instructions for using Backtrader in Jupyter notebooks, enabling interactive backtesting, visualization, and strategy development.
Table of Contents¶
Installation and Setup¶
Basic Installation¶
# Install Jupyter
pip install jupyter jupyterlab
# Install plotting libraries
pip install matplotlib plotly ipywidgets
# Install backtrader (from source)
git clone <https://github.com/cloudQuant/backtrader.git>
cd backtrader && pip install -U .
# For enhanced features
pip install pandas numpy scipy ipympl
```bash
### Notebook Configuration
```python
%matplotlib widget # For interactive matplotlib
# or
%matplotlib inline # For static inline plots
import backtrader as bt
import pandas as pd
import numpy as np
from IPython.display import display, HTML
import ipywidgets as widgets
```bash
- --
## Quick Start
### Minimal Backtest Example
```python
# Cell 1: Setup and Data
import backtrader as bt
from datetime import datetime
data = bt.feeds.CSVData(
dataname='path/to/data.csv',
fromdate=datetime(2020, 1, 1),
todate=datetime(2023, 12, 31)
)
# Cell 2: Define Strategy
class SimpleStrategy(bt.Strategy):
params = (('period', 20),)
def __init__(self):
self.sma = bt.indicators.SMA(self.data.close, period=self.params.period)
def next(self):
if not self.position:
if self.data.close[0] > self.sma[0]:
self.buy()
else:
if self.data.close[0] < self.sma[0]:
self.sell()
# Cell 3: Run Backtest
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(SimpleStrategy, period=20)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.001)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
results = cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
```bash
- --
## Interactive Backtesting Workflow
### Data Exploration
```python
def explore_data(filepath):
"""Display data statistics and preview."""
df = pd.read_csv(filepath, parse_dates=['datetime'], index_col='datetime')
print(f"Shape: {df.shape}")
print(f"Columns: {list(df.columns)}")
display(df.describe())
display(df.head())
```bash
### Interactive Data Visualization
```python
def plot_candles(data, title='Price Chart'):
"""Plot candlestick chart with volume using Plotly."""
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.DataFrame({
'open': data.open.array, 'high': data.high.array,
'low': data.low.array, 'close': data.close.array,
'volume': data.volume.array
}, index=pd.to_datetime(data.datetime.array))
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
vertical_spacing=0.03, row_heights=[0.7, 0.3])
fig.add_trace(go.Candlestick(x=df.index, open=df['open'], high=df['high'],
low=df['low'], close=df['close'], name='OHLC'),
row=1, col=1)
fig.add_trace(go.Bar(x=df.index, y=df['volume'], name='Volume',
marker_color='rgba(0,0,255,0.3)'), row=2, col=1)
fig.update_layout(title=title, xaxis_rangeslider_visible=False, height=600)
fig.show()
```bash
### Progress Tracking
```python
from IPython.display import clear_output
class ProgressStrategy(bt.Strategy):
def __init__(self):
self.total_bars = len(self.data)
self.counter = 0
self.progress_update = max(1, self.total_bars // 100)
def next(self):
self.counter += 1
if self.counter % self.progress_update == 0:
progress = (self.counter / self.total_bars) * 100
clear_output(wait=True)
print(f"Progress: {progress:.1f}%")
```bash
- --
## Visualization and Plotting
### Plotly Interactive Plotting
```python
# Enable Plotly plotting in Jupyter
cerebro.plot(style='plotly', iplot=True)
```bash
### Dashboard-style Visualization
```python
def create_backtest_dashboard(cerebro, results):
"""Create a multi-panel dashboard with metrics and plot."""
strat = results[0]
metrics_html = """
<div style="border: 1px solid #ddd; padding: 15px; border-radius: 5px;">
<h3>Backtest Performance Metrics</h3>
<table style="width: 100%;">
<tr>
<td><b>Final Value:</b></td>
<td>${final_value:,.2f}</td>
<td><b>Total Return:</b></td>
<td>{total_return:.2%}</td>
</tr>
</table>
</div>
"""
display(HTML(metrics_html.format(
final_value=strat.broker.getvalue(),
total_return=(strat.broker.getvalue() - 10000) / 10000
)))
cerebro.plot(style='plotly', iplot=True)
```bash
- --
## Parameter Sensitivity Analysis
### Single Parameter Sweep
```python
def parameter_sweep(strategy_class, param_name, param_range, data):
"""Sweep a single parameter and visualize results."""
results_list = []
for value in param_range:
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(strategy_class, **{param_name: value})
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.001)
strat = cerebro.run()[0]
returns = (strat.broker.getvalue() - 10000) / 10000
results_list.append({'value': value, 'returns': returns})
df = pd.DataFrame(results_list)
# Plot results
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=df['value'], y=df['returns'], mode='lines+markers',
name='Returns', line=dict(color='blue', width=2)))
fig.update_layout(title=f'Parameter Sensitivity: {param_name}',
xaxis_title=param_name, yaxis_title='Returns')
fig.show()
return df
# Usage
results_df = parameter_sweep(SimpleStrategy, 'period', range(5, 51, 5), data)
display(results_df)
```bash
### Interactive Parameter Sliders
```python
def interactive_backtest(data):
"""Create interactive sliders for strategy parameters."""
def run_backtest(period, commission):
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(SimpleStrategy, period=period)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=commission)
results = cerebro.run()
final_value = cerebro.broker.getvalue()
returns = (final_value - 10000) / 10000 * 100
clear_output(wait=True)
print(f"Period: {period}, Commission: {commission:.4f}")
print(f"Final Value: ${final_value:.2f}, Returns: {returns:.2f}%")
return final_value
widget = widgets.interactive(
run_backtest,
period=widgets.IntSlider(min=5, max=50, step=1, value=20, description='SMA Period:'),
commission=widgets.FloatSlider(min=0.0, max=0.01, step=0.0001, value=0.001, description='Commission:')
)
return widget
# Usage
interactive_widget = interactive_backtest(data)
display(interactive_widget)
```bash
### Multi-Parameter Heatmap
```python
def multi_parameter_heatmap(data, strategy_class, param1_name, param1_range,
param2_name, param2_range):
"""Create a heatmap for two-parameter optimization."""
results = np.zeros((len(param1_range), len(param2_range)))
for i, p1 in enumerate(param1_range):
for j, p2 in enumerate(param2_range):
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(strategy_class, **{param1_name: p1, param2_name: p2})
cerebro.broker.setcash(10000.0)
strat = cerebro.run()[0]
results[i, j] = (strat.broker.getvalue() - 10000) / 10000
import plotly.figure_factory as ff
fig = ff.create_annotated_heatmap(
z=results, x=list(param2_range), y=list(param1_range),
colorscale='RdYlGn', annotation_text=np.round(results * 100, 1).astype(str)
)
fig.update_layout(title=f'{param1_name} vs {param2_name} Returns (%)')
fig.show()
return results
```bash
- --
## Multi-Strategy Comparison
### Compare Multiple Strategies
```python
def compare_strategies(data, strategy_configs):
"""Compare multiple strategies side-by-side.
Args:
data: Backtrader data feed
strategy_configs: List of (StrategyClass, params_dict, name) tuples
"""
comparison_results = []
for StrategyClass, params, name in strategy_configs:
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(StrategyClass, **params)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(commission=0.001)
# Add analyzers
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
strat = cerebro.run()[0]
comparison_results.append({
'name': name,
'final_value': strat.broker.getvalue(),
'returns': strat.analyzers.returns.get_analysis()['rtot'],
'sharpe': strat.analyzers.sharpe.get_analysis().get('sharperatio', 0),
'max_drawdown': strat.analyzers.drawdown.get_analysis()['max']['drawdown']
})
df = pd.DataFrame(comparison_results)
display(df)
# Plot comparison
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Bar(x=df['name'], y=df['returns'] * 100,
name='Returns (%)', marker_color='steelblue'))
fig.update_layout(title='Strategy Comparison', xaxis_title='Strategy',
yaxis_title='Returns (%)')
fig.show()
return df
# Usage
strategy_configs = [
(SimpleStrategy, {'period': 10}, 'SMA(10)'),
(SimpleStrategy, {'period': 20}, 'SMA(20)'),
(SimpleStrategy, {'period': 30}, 'SMA(30)'),
]
comparison_df = compare_strategies(data, strategy_configs)
```bash
### Equity Curve Comparison
```python
def plot_equity_curves(data, strategy_configs):
"""Plot equity curves for multiple strategies."""
import plotly.graph_objects as go
fig = go.Figure()
for StrategyClass, params, name in strategy_configs:
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(StrategyClass, **params)
cerebro.broker.setcash(10000.0)
# Track equity
class EquityTracker(bt.Analyzer):
def __init__(self):
self.equity = []
def next(self):
self.equity.append(self.strategy.broker.getvalue())
cerebro.addanalyzer(EquityTracker, _name='equity')
equity = cerebro.run()[0].analyzers.equity.equity
fig.add_trace(go.Scatter(y=equity, mode='lines', name=name, line=dict(width=2)))
fig.update_layout(title='Equity Curve Comparison', xaxis_title='Bar',
yaxis_title='Portfolio Value', hovermode='x unified')
fig.show()
```bash
- --
## Real-time Data Monitoring
### Live Data Streaming
```python
import time
class LiveMonitor:
"""Monitor live data in Jupyter notebook."""
def __init__(self, cerebro):
self.cerebro = cerebro
self.is_running = False
def start(self, interval=5):
"""Start monitoring with specified update interval."""
self.is_running = True
while self.is_running:
clear_output(wait=True)
value = self.cerebro.broker.getvalue()
display(HTML(f"""
<div style="padding: 20px; background: #f0f0f0; border-radius: 10px;">
<h2>Live Portfolio Monitor</h2>
<p><b>Current Value:</b> ${value:,.2f}</p>
<p><b>Update Time:</b> {datetime.now().strftime('%H:%M:%S')}</p>
</div>
"""))
time.sleep(interval)
def stop(self):
"""Stop monitoring."""
self.is_running = False
```bash
### WebSocket Data Display
```python
def create_live_ticker(symbol='BTCUSDT'):
"""Create a live price ticker widget."""
ticker_widget = widgets.HTML(value="<h3>Connecting to {}...</h3>".format(symbol))
def update_ticker():
while True:
ticker_widget.value = f"""
<div style="padding: 15px; background: #e8f4f8; border-radius: 5px;">
<h4>{symbol} Live Price</h4>
<h2>${data.close[0]:,.2f}</h2>
<p>Volume: {data.volume[0]:,.0f}</p>
</div>
"""
time.sleep(5)
return ticker_widget
```bash
- --
## Exporting Results and Reports
### Export to CSV
```python
def export_trades_to_csv(cerebro, filename='trades.csv'):
"""Export all trades to CSV file."""
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
strat = cerebro.run()[0]
trades_list = []
for trade in strat.trades:
trades_list.append({
'entry_date': trade.open_dt,
'exit_date': trade.close_dt,
'entry_price': trade.price,
'exit_price': trade.pclose,
'size': trade.size,
'pnl': trade.pnl,
'pnl_net': trade.pnlcomm
})
df = pd.DataFrame(trades_list)
df.to_csv(filename, index=False)
print(f"Exported {len(df)} trades to {filename}")
return df
```bash
### Export to Excel
```python
def export_backtest_report(cerebro, results, filename='backtest_report.xlsx'):
"""Export comprehensive backtest report to Excel."""
strat = results[0]
with pd.ExcelWriter(filename, engine='xlsxwriter') as writer:
summary_data = {
'Metric': ['Initial Capital', 'Final Value', 'Total Return',
'Sharpe Ratio', 'Max Drawdown', 'Total Trades'],
'Value': [10000, strat.broker.getvalue(),
(strat.broker.getvalue() - 10000) / 10000,
strat.analyzers.sharpe.get_analysis().get('sharperatio', 0),
strat.analyzers.drawdown.get_analysis()['max']['drawdown'],
strat.analyzers.trades.get_analysis().get('total', {}).get('total', 0)]
}
pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False)
trades_df = export_trades_to_csv(cerebro)
trades_df.to_excel(writer, sheet_name='Trades', index=False)
print(f"Report exported to {filename}")
```bash
### Generate HTML Report
```python
def generate_html_report(cerebro, results, filename='backtest_report.html'):
"""Generate a standalone HTML report."""
strat = results[0]
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Backtest Report</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 40px; }}
.header {{ background: #2E86AB; color: white; padding: 20px; border-radius: 5px; }}
.metric {{ display: inline-block; margin: 20px; padding: 15px;
background: #f8f9fa; border-radius: 5px; min-width: 200px; }}
.metric-value {{ font-size: 24px; font-weight: bold; }}
</style>
</head>
<body>
<div class="header">
<h1>Backtest Report</h1>
<p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<div class="metrics">
<div class="metric">
<div>Final Value</div>
<div class="metric-value">${strat.broker.getvalue():,.2f}</div>
</div>
<div class="metric">
<div>Total Return</div>
<div class="metric-value">{(strat.broker.getvalue() - 10000) / 10000:.2%}</div>
</div>
</div>
</body>
</html>
"""
with open(filename, 'w') as f:
f.write(html_content)
print(f"HTML report saved to {filename}")
return filename
```bash
- --
## Notebook Best Practices
### Cell Organization
```python
# Cell 1: Imports and configuration
# Cell 2: Helper functions
# Cell 3: Data loading
# Cell 4: Strategy definition
# Cell 5: Backtest execution
# Cell 6: Results visualization
# Cell 7: Export and reporting
```bash
### Memory Management
```python
def cleanup_cerebro(cerebro):
"""Properly clean up Cerebro instance."""
cerebro.strats = []
cerebro.runningstrats = []
cerebro.datas = []
cerebro.analyzers = []
import gc
gc.collect()
```bash
### Progress Indicators
```python
from ipywidgets import IntProgress, HTML, VBox
def create_progress_bar(max_value):
"""Create a progress bar widget."""
progress = IntProgress(value=0, min=0, max=max_value,
description='Running:', bar_style='info')
label = HTML()
box = VBox([progress, label])
display(box)
return progress, label
```bash
### Error Handling
```python
def safe_backtest(cerebro):
"""Run backtest with error handling."""
try:
results = cerebro.run()
return results, None
except Exception as e:
import traceback
error_msg = f"Error: {str(e)}\n\n{traceback.format_exc()}"
display(HTML(f"<div style='color:red'>{error_msg}</div>"))
return None, error_msg
```bash
- --
## Advanced Techniques
### Parallel Backtesting
```python
from concurrent.futures import ProcessPoolExecutor
import multiprocessing
def run_single_backtest(params):
"""Run a single backtest with given parameters."""
cerebro = bt.Cerebro()
# Configure cerebro with params
cerebro.run()
return cerebro.broker.getvalue()
def parallel_optimization(param_combinations, n_jobs=None):
"""Run multiple backtests in parallel."""
if n_jobs is None:
n_jobs = multiprocessing.cpu_count()
with ProcessPoolExecutor(max_workers=n_jobs) as executor:
results = list(executor.map(run_single_backtest, param_combinations))
return results
```bash
### Strategy Persistence
```python
import pickle
def save_strategy(cerebro, filename):
"""Save strategy state to file."""
with open(filename, 'wb') as f:
pickle.dump(cerebro, f)
def load_strategy(filename):
"""Load strategy state from file."""
with open(filename, 'rb') as f:
cerebro = pickle.load(f)
return cerebro
```bash
### Walk-Forward Analysis
```python
def walk_forward_analysis(data, strategy_class, train_size=252, test_size=63):
"""Perform walk-forward analysis."""
results = []
for start in range(0, len(data) - train_size - test_size, test_size):
test_data = data[start + train_size:start + train_size + test_size]
cerebro = bt.Cerebro()
cerebro.adddata(test_data)
cerebro.addstrategy(strategy_class)
result = cerebro.run()[0]
results.append({
'period': f"{start}-{start + train_size + test_size}",
'return': (result.broker.getvalue() - 10000) / 10000
})
return pd.DataFrame(results)
```bash
- --
## Collaboration and Sharing
### Export Notebook to HTML
```bash
# Command line
jupyter nbconvert --to html your_notebook.ipynb
# In notebook
!jupyter nbconvert --to html notebook_guide.ipynb
```bash
### Template Notebooks
Save common patterns as template notebooks:
- `strategy_template.ipynb` - Base strategy structure
- `analysis_template.ipynb` - Analysis and visualization
- `optimization_template.ipynb` - Parameter optimization
### Version Control
```python
notebook_metadata = {
'version': '1.0',
'author': 'Your Name',
'date': datetime.now().isoformat(),
'backtrader_version': bt.__version__
}
```bash
- --
## Quick Reference
### Common Notebook Patterns
```python
# Pattern 1: Quick backtest
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(MyStrategy)
results = cerebro.run()
cerebro.plot(iplot=True)
# Pattern 2: Parameter optimization
cerebro.optstrategy(MyStrategy, period=range(10, 31))
results = cerebro.run()
# Pattern 3: Compare with benchmark
cerebro.addstrategy(MyStrategy)
cerebro.addstrategy(BuyAndHold)
results = cerebro.run()
```bash
### Keyboard Shortcuts
- `Shift + Enter`: Run cell and advance
- `Ctrl + Enter`: Run cell in place
- `A`: Insert cell above
- `B`: Insert cell below
- `DD`: Delete cell
- `M`: Change to markdown
- `Y`: Change to code
- --
## Summary
This guide covers the essential aspects of using Backtrader in Jupyter notebooks:
1. **Setup**: Install Jupyter and visualization libraries
2. **Interactive Workflow**: Use widgets and progress tracking
3. **Visualization**: Both Plotly (interactive) and Matplotlib (static)
4. **Analysis**: Parameter sweeps, heatmaps, multi-strategy comparison
5. **Export**: Save results to CSV, Excel, and HTML reports
For more information, see:
- [Quick Start Guide](../opts/getting_started/quickstart.md)
- [Strategy Development Guide](../opts/user_guide/strategies.md)
- [Optimization Guide](../opts/user_guide/optimization.md)