Overall Statistics
Total Trades
384
Average Win
6.45%
Average Loss
-5.59%
Compounding Annual Return
-38.130%
Drawdown
82.200%
Expectancy
0.016
Net Profit
-36.289%
Sharpe Ratio
0.652
Probabilistic Sharpe Ratio
26.744%
Loss Rate
53%
Win Rate
47%
Profit-Loss Ratio
1.16
Alpha
1.189
Beta
2.773
Annual Standard Deviation
1.381
Annual Variance
1.907
Information Ratio
0.767
Tracking Error
1.311
Treynor Ratio
0.325
Total Fees
$2830.64
Estimated Strategy Capacity
$8300000.00
Lowest Capacity Asset
ZO Y6RTF5MGZVOL
# region imports
from AlgorithmImports import *
from datetime import datetime
import math
# endregionfs

class InverseVolatility(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2022, 1, 1)
        self.SetEndDate(2022, 12, 31)
        
        self.SetSecurityInitializer(BrokerageModelSecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrices)))

        self.volatility = {}
        tickers = [
            Futures.Indices.VIX,
            Futures.Indices.SP500EMini,
            Futures.Indices.NASDAQ100EMini,
            Futures.Indices.Dow30EMini,
            Futures.Energies.BrentCrude,
            Futures.Energies.Gasoline,
            Futures.Energies.HeatingOil,
            Futures.Energies.NaturalGas,
            Futures.Grains.Corn,
            Futures.Grains.Oats,
            Futures.Grains.Soybeans,
            Futures.Grains.Wheat
            ]
        for ticker in tickers:
            future = self.AddFuture(ticker, extendedMarketHours=True)

            # 30-day standard deviation of 1-day returns
            roc = self.ROC(future.Symbol, 1, Resolution.Daily)
            self.volatility[future] = IndicatorExtensions.Of(StandardDeviation(30), roc)

        self.SetWarmup(31, Resolution.Daily)

        self.Schedule.On(
            self.DateRules.WeekStart(),
            self.TimeRules.At(10,0,0),
            self.Rebalance)

    def Rebalance(self):
        if self.IsWarmingUp:
            return

        sorted_by_value = sorted([kvp for kvp in self.volatility.items() if kvp[1].IsReady], 
            key=lambda item: item[1].Current.Value)
        top5 = {k: v for k, v in sorted_by_value[:5]}

        # Sum the inverse STD of ROC
        inverse_sum = sum([1/std.Current.Value for std in top5.values()])

        if inverse_sum == 0:
            return 
        
        # Create Portfoliotarget for the inverse weighted STD
        targets = [PortfolioTarget(future.Mapped, 1/std.Current.Value/inverse_sum)
            for future,std in top5.items() if future.Mapped]

        # Liquidate if invested and no new negative news
        for symbol, holdings in self.Portfolio.items():
            if holdings.Invested and symbol not in top5:
                targets.append(PortfolioTarget(symbol, 0))

        #for target in targets:
        #    self.Plot("Targets", target.Symbol.Value, target.Quantity)

        self.SetHoldings(targets)