Overall Statistics
Total Orders
500
Average Win
2.14%
Average Loss
-3.40%
Compounding Annual Return
24.179%
Drawdown
44.800%
Expectancy
0.154
Start Equity
100000.00
End Equity
301616.54
Net Profit
201.617%
Sharpe Ratio
0.587
Sortino Ratio
0.635
Probabilistic Sharpe Ratio
14.104%
Loss Rate
29%
Win Rate
71%
Profit-Loss Ratio
0.63
Alpha
0.16
Beta
0.776
Annual Standard Deviation
0.391
Annual Variance
0.153
Information Ratio
0.378
Tracking Error
0.369
Treynor Ratio
0.296
Total Fees
$0.00
Estimated Strategy Capacity
$28000000000.00
Lowest Capacity Asset
BTCUSD 2XR
Portfolio Turnover
16.25%
Drawdown Recovery
636
#region imports
from AlgorithmImports import *
#endregion


"""
BOLLINGER BAND ALLOCATION MODEL FOR BITCOIN

This example belongs to the technical-indicator block of the strategy playground.
It extends the simplest one-asset QuantConnect models by introducing a formal
indicator: Bollinger Bands. The traded asset is BTCUSD, using daily crypto data
from the GDAX market. The purpose is pedagogical: the strategy shows how an
indicator can be used to convert price behavior into changing portfolio exposure.

The model starts by defining the backtest period, initial capital, and the crypto
subscription. It then creates a Bollinger Band indicator using a 10-day simple
moving average and a narrow band width of 0.25 standard deviations. The indicator
produces three reference levels: the upper band, the middle band, and the lower
band. These levels are used to classify the current Bitcoin price regime.

The portfolio rule is exposure-based. If Bitcoin trades above the upper band, the
model interprets the price as relatively extended and reduces exposure to 10%.
If Bitcoin is between the middle band and the upper band, the model holds a 50%
position. If Bitcoin is between the lower band and the middle band, the model
also holds a 50% position. If Bitcoin trades below the lower band, the strategy
moves to a full 100% allocation, treating the asset as relatively depressed.

The code uses an explicit if/elif/else structure. This is important because the
conditions are mutually exclusive. In the earlier version, the final else was
attached only to the immediately preceding if statement, which could cause
unexpected overwriting of portfolio targets. The improved version makes the
regime logic unambiguous.

The model also plots the strategy portfolio value, a buy-and-hold BTC reference,
the Bitcoin price, and the three Bollinger Band levels. These plots help the
reader visually connect the indicator state to the allocation decision. In the
context of the booklet, this strategy illustrates how technical indicators can
serve as a bridge between raw market data and systematic portfolio rules.
"""


class RetrospectiveYellowGreenAlligator(QCAlgorithm):

    def Initialize(self):

        # ------------------------------------------------------------
        # 1. BACKTEST SETTINGS
        # ------------------------------------------------------------
        self.SetStartDate(2020, 2, 28)
        self.SetEndDate(2025, 4, 1)

        self._cash = 100000
        self.SetCash(self._cash)

        # ------------------------------------------------------------
        # 2. TRADED ASSET
        # ------------------------------------------------------------
        self.ticker = "BTCUSD"
        self.symbol = self.AddCrypto(
            self.ticker,
            Resolution.Daily,
            Market.GDAX
        ).Symbol

        # ------------------------------------------------------------
        # 3. TECHNICAL INDICATOR
        # ------------------------------------------------------------
        self.bollinger_period = 10
        self.bollinger_width = 0.25

        self.Bolband = self.BB(
            self.symbol,
            self.bollinger_period,
            self.bollinger_width,
            MovingAverageType.Simple,
            Resolution.Daily
        )

        self.SetWarmUp(
            self.bollinger_period + 5,
            Resolution.Daily
        )

        # ------------------------------------------------------------
        # 4. BENCHMARK REFERENCE FOR PLOTTING
        # ------------------------------------------------------------
        history = self.History(
            self.symbol,
            10,
            Resolution.Daily
        )

        if not history.empty:
            self._initialValue_ticker = history["close"].iloc[0]
        else:
            self._initialValue_ticker = None

        self.current_target_weight = 0.0

    def OnData(self, data):

        # ------------------------------------------------------------
        # 1. DATA AND INDICATOR READINESS CHECKS
        # ------------------------------------------------------------
        if self.IsWarmingUp:
            return

        if self.symbol not in data:
            return

        if not self.Bolband.IsReady:
            return

        price = self.Securities[self.symbol].Close

        if price <= 0:
            return

        upper_band = self.Bolband.UpperBand.Current.Value
        middle_band = self.Bolband.MiddleBand.Current.Value
        lower_band = self.Bolband.LowerBand.Current.Value

        # ------------------------------------------------------------
        # 2. PORTFOLIO CONSTRUCTION RULE
        # ------------------------------------------------------------
        if price > upper_band:

            # Price is above the upper band:
            # reduce exposure because BTC appears extended.
            target_weight = 0.10

        elif price > middle_band and price <= upper_band:

            # Price is above the middle band but below the upper band:
            # hold a moderate allocation.
            target_weight = 0.50

        elif price >= lower_band and price <= middle_band:

            # Price is below the middle band but above the lower band:
            # hold a moderate allocation.
            target_weight = 0.50

        else:

            # Price is below the lower band:
            # increase exposure because BTC appears depressed.
            target_weight = 1.00

        # Avoid sending repeated orders if the target has not changed.
        if target_weight != self.current_target_weight:
            self.SetHoldings(
                self.symbol,
                target_weight
            )

            self.current_target_weight = target_weight

        # ------------------------------------------------------------
        # 3. PLOTS
        # ------------------------------------------------------------
        if self._initialValue_ticker is not None and self._initialValue_ticker > 0:

            btc_buy_hold = (
                self._cash
                * price
                / self._initialValue_ticker
            )

            self.Plot(
                "Strategy Equity",
                str(self.ticker),
                btc_buy_hold
            )

        self.Plot(
            "Strategy Equity",
            "Portfolio Value",
            self.Portfolio.TotalPortfolioValue
        )

        self.Plot(
            "Bollinger",
            "BB Lower",
            lower_band
        )

        self.Plot(
            "Bollinger",
            "BB Upper",
            upper_band
        )

        self.Plot(
            "Bollinger",
            "BB Middle",
            middle_band
        )

        self.Plot(
            "Bollinger",
            str(self.ticker),
            price
        )

        self.Plot(
            "Portfolio State",
            "Target BTC Weight",
            self.current_target_weight
        )