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)