Overall Statistics
Total Orders
39
Average Win
0.64%
Average Loss
-0.44%
Compounding Annual Return
4.307%
Drawdown
1.800%
Expectancy
0.222
Start Equity
100000
End Equity
101415.99
Net Profit
1.416%
Sharpe Ratio
-0.503
Sortino Ratio
-0.435
Probabilistic Sharpe Ratio
40.150%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.44
Alpha
-0.035
Beta
0.21
Annual Standard Deviation
0.048
Annual Variance
0.002
Information Ratio
-0.791
Tracking Error
0.094
Treynor Ratio
-0.115
Total Fees
$37.00
Estimated Strategy Capacity
$1300000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
30.23%
Drawdown Recovery
38
# region imports
from AlgorithmImports import *
# endregion


class WarmUpCustomIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(100_000)
        self.settings.seed_initial_prices = True
        self._equity = self.add_equity("SPY", Resolution.DAILY)
        self._equity.volatility = CustomVolatility(100)
        # Warm the custom indicator with daily trade bars.
        for bar in self.history[TradeBar](self._equity, self._equity.volatility.warm_up_period, Resolution.DAILY):
            self._equity.volatility.update(bar)
        self.register_indicator(self._equity, self._equity.volatility)

    def on_data(self, data: Slice) -> None:
        if not self._equity.volatility.is_ready:
            return
        # Buy if volatility is increasing.
        if not self._equity.invested and self._equity.volatility.value > self._equity.volatility.previous.value:
            self.set_holdings(self._equity, 1)
        # Sell if volatility is decreasing or we are invested.
        elif self._equity.invested and self._equity.volatility.value < self._equity.volatility.previous.value:
            self.liquidate()


class CustomVolatility(PythonIndicator):

    def __init__(self, period, trading_days_per_year=252):
        super().__init__()
        self.warm_up_period = period
        self._trading_days_per_year = trading_days_per_year
        self.value = 0
        self._log_return = LogReturn(1)
        self._standard_deviation = IndicatorExtensions.of(StandardDeviation(period), self._log_return)

    def update(self, input_: BaseData):
        # Annualized log-return volatility.
        price = input_.value
        if price <= 0:
            return
        self._log_return.update(input_.end_time, price)
        if self.is_ready:
            self.value = self._standard_deviation.current.value * math.sqrt(self._trading_days_per_year) * 100.0
        return self.is_ready

    @property
    def is_ready(self) -> bool:
        return self._standard_deviation.is_ready