Overall Statistics
Total Trades
470
Average Win
3.81%
Average Loss
-2.82%
Compounding Annual Return
1640.783%
Drawdown
33.900%
Expectancy
0.502
Net Profit
1636.813%
Sharpe Ratio
10.783
Probabilistic Sharpe Ratio
97.847%
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
1.35
Alpha
8.71
Beta
5.08
Annual Standard Deviation
0.901
Annual Variance
0.812
Information Ratio
11.34
Tracking Error
0.839
Treynor Ratio
1.913
Total Fees
$13469.22
Estimated Strategy Capacity
$8300000.00
Lowest Capacity Asset
ES XUERCWA6EWAP
# region imports
from AlgorithmImports import *
from datetime import datetime
import math
# endregionfs

class InverseVolatility(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2021, 1, 1)
        self.SetEndDate(2021, 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)