Lecture on Backtesting.py

Jul 12, 2024

Lecture on Backtesting.py

Introduction to Backtesting.py

  • Instructor: Chad
  • A lightweight backtesting framework
  • Comparison with other libraries like VectorBT or Backtrader:
    • Backtrader and VectorBT are comprehensive but overwhelming
    • Backtesting.py focuses only on essential backtesting
  • Key features:
    • Simple documentation
    • Fast performance (event-driven backtester)
    • Built-in parameter optimizer
    • No support for multiple assets or fractional shares

Getting Started

Installation

  • Create and activate a virtual environment:
    virtualenv venv
    source venv/bin/activate
    
  • Install necessary packages:
    pip install backtesting
    pip install talib (optional)
    

Writing the First Strategy

  • Create a new Python file example.py
  • Import the essentials from backtesting.py and other necessary libraries:
    from backtesting import Backtest, Strategy
    from backtesting.test import GOOG
    import talib
    
  • Define a simple RSI-based strategy:
    class RSI(Strategy):
        def init(self):
            self.rsi = self.I(talib.RSI, self.data.Close, 14)
        def next(self):
            if self.rsi[-1] < 30:
                self.buy()
            elif self.rsi[-1] > 70:
                self.sell()
    
  • Running the backtest:
    bt = Backtest(GOOG, RSI, cash=10_000)
    stats = bt.run()
    bt.plot()
    

Strategy Customization

Adding Custom Parameters

  • Adding parameters for optimization:
    class RSI(Strategy):
        upper_bound = 70
        lower_bound = 30
        rsi_window = 14
        def init(self):
            self.rsi = self.I(talib.RSI, self.data.Close, self.rsi_window)
        def next(self):
            if self.rsi[-1] < self.lower_bound:
                self.buy()
            elif self.rsi[-1] > self.upper_bound:
                self.sell()
    

Optimization with optimize

  • Running an optimizer with specified ranges:
    stats, heatmap = bt.optimize(upper_bound=range(50, 85, 5), lower_bound=range(10, 45, 5), rsi_window=range(10, 30, 2), 
                                 maximize='Sharpe Ratio', return_heatmap=True)
    
  • Plotting heatmaps:
    import seaborn as sns
    import matplotlib.pyplot as plt
    hm = heatmap.groupby(['upper_bound', 'lower_bound']).mean().unstack()
    sns.heatmap(hm)
    plt.show()
    

Advanced Techniques

Multi-Time Frame Analysis

  • Using weekly and daily RSI:
    from backtesting.lib import resample_apply
    class MultiFrameRSI(Strategy):
        def init(self):
            self.daily_rsi = self.I(talib.RSI, self.data.Close, 14)
            self.weekly_rsi = resample_apply('W-FRI', talib.RSI, self.data.Close, 14)
        def next(self):
            if self.daily_rsi[-1] < 30 and self.weekly_rsi[-1] < 30:
                self.buy()
            elif self.daily_rsi[-1] > 70 and self.weekly_rsi[-1] > 70:
                self.sell()
    

Short Selling and Advanced Orders

  • Implementing short selling:
    def next(self):
        if self.daily_rsi[-1] > 70:
            if self.position.is_long:
                self.position.close()
            self.sell()
        elif self.daily_rsi[-1] < 30:
            if self.position.is_short:
                self.position.close()
            self.buy()
    
  • Implementing stop loss and take profit:
    def next(self):
        price = self.data.Close[-1]
        sl = price * 0.95
        tp = price * 1.15
        if self.daily_rsi[-1] < 30:
            self.buy(sl=sl, tp=tp)
        elif self.daily_rsi[-1] > 70:
            self.sell()
    

Position Sizing

  • Using different position sizes:

    def next(self):
        if self.daily_rsi[-1] < 30:
            self.buy(size=0.1)
        elif self.daily_rsi[-1] > 70:
            self.sell()
    
  • Alternative method specifying exact number of shares:

    def next(self):
        if self.daily_rsi[-1] < 30:
            self.buy(size=1)
        elif self.daily_rsi[-1] > 70:
            self.sell()
    

Extracting Trades Data

  • Extract and analyze trade details:
    trades = stats['_trades']
    print(trades.to_string())
    

Summary

  • Backtesting.py is suitable for simple to moderately complex strategies.
  • It’s efficient and user-friendly but lacks support for multiple assets and fractional shares.

Use the documentation: With its small size and simplicity, you should be able to master it quickly.

  • Incorporate optimizations, multi-time frame analysis, and advanced trade handling into your strategies.
  • Happy backtesting!