Overall Statistics
Total Orders
16
Average Win
20.55%
Average Loss
-4.29%
Compounding Annual Return
10.566%
Drawdown
33.600%
Expectancy
3.136
Start Equity
1000000
End Equity
2730098.62
Net Profit
173.010%
Sharpe Ratio
0.493
Sortino Ratio
0.43
Probabilistic Sharpe Ratio
6.920%
Loss Rate
29%
Win Rate
71%
Profit-Loss Ratio
4.79
Alpha
0.004
Beta
0.788
Annual Standard Deviation
0.129
Annual Variance
0.017
Information Ratio
-0.175
Tracking Error
0.067
Treynor Ratio
0.081
Total Fees
$443.83
Estimated Strategy Capacity
$1900000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
0.41%
#region imports
from AlgorithmImports import *
#endregion

class BollingerBandsMeanReversion(QCAlgorithm):
    def initialize(self):
        # Set algorithm parameters
        self.set_start_date(2014, 1, 1)  # Backtest start date
        self.set_end_date(2024, 1, 1)    # Backtest end date
        self.set_cash(1_000_000)        # Starting cash

        # Add equity asset
        self.symbol = self.add_equity("SPY", Resolution.Daily).symbol

        # Set up Bollinger Bands indicator
        self.bollinger = self.BB(self.symbol, 20, 2, Resolution.Daily)

        # Warm-up period to initialize the Bollinger Bands
        self.set_warm_up(20)

    def on_data(self, data: Slice):
        if self.is_warming_up:
            return

        # Check if the Bollinger Bands indicator is ready
        if not self.bollinger.IsReady:
            return

        # Get the current price and Bollinger Bands values
        price = self.securities[self.symbol].price
        upper_band = self.bollinger.UpperBand.Current.Value
        middle_band = self.bollinger.MiddleBand.Current.Value
        lower_band = self.bollinger.LowerBand.Current.Value

        # Debug the Bollinger Bands values
        self.debug(f"Price: {price}, Upper Band: {upper_band}, Lower Band: {lower_band}")

        # Check current holdings
        holdings = self.portfolio[self.symbol].quantity

        # Buy signal: Price below the lower Bollinger Band
        if price < lower_band and holdings <= 0:
            self.set_holdings(self.symbol, 1)  # Invest 100% of portfolio
            self.debug(f"BUY {self.symbol} at {price}")

        # Sell signal: Price above the upper Bollinger Band
        elif price > upper_band and holdings > 0:
            self.liquidate(self.symbol)
            self.debug(f"SELL {self.symbol} at {price}")