Overall Statistics
Total Orders
655
Average Win
4.20%
Average Loss
-4.22%
Compounding Annual Return
-12.572%
Drawdown
83.000%
Expectancy
0.009
Start Equity
100000
End Equity
51060.54
Net Profit
-48.939%
Sharpe Ratio
0.166
Sortino Ratio
0.202
Probabilistic Sharpe Ratio
1.654%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
0.99
Alpha
-0.058
Beta
2.274
Annual Standard Deviation
0.675
Annual Variance
0.456
Information Ratio
0.06
Tracking Error
0.621
Treynor Ratio
0.049
Total Fees
$3345.25
Estimated Strategy Capacity
$190000000.00
Lowest Capacity Asset
ZC Z0PZXJ6AS0F9
Portfolio Turnover
46.00%
Drawdown Recovery
225
# region imports
from AlgorithmImports import *
from datetime import datetime
import math
# endregionfs


class InverseVolatilityFuturesAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(self.end_date - timedelta(5*365))
        self.settings.seed_initial_prices = True
        # Add a universe of Futures.
        tickers = [
            Futures.Indices.VIX,
            Futures.Indices.SP_500_E_MINI,
            Futures.Indices.NASDAQ_100_E_MINI,
            Futures.Indices.DOW_30_E_MINI,
            Futures.Energies.BRENT_CRUDE,
            Futures.Energies.GASOLINE,
            Futures.Energies.HEATING_OIL,
            Futures.Energies.NATURAL_GAS,
            Futures.Grains.CORN,
            Futures.Grains.OATS,
            Futures.Grains.SOYBEANS,
            Futures.Grains.WHEAT
        ]
        self._futures = []
        for ticker in tickers:
            future = self.add_future(ticker, extended_market_hours=True)
            # Add a volaility indicator on the continuous contract.
            future.vol = IndicatorExtensions.of(
                StandardDeviation(30), 
                self.roc(future, 1, Resolution.DAILY)
            )
            self._futures.append(future)
        # Warm up the indicators.
        self.set_warmup(31, Resolution.DAILY)

    def on_warmup_finished(self):
        # Add a Scheduled Event to rebalance the portfolio.
        time_rule = self.time_rules.at(10, 0)
        self.schedule.on(self.date_rules.week_start(), time_rule, self._rebalance)
        # Rebalance the portfolio today too.
        if self.live_mode:
            self._rebalance()
        else:
            self.schedule.on(self.date_rules.today, time_rule, self._rebalance)

    def _rebalance(self):
        # Select the 5 Futures with the lowest volatility.
        futures = [
            future for future in self._futures
            if (future.mapped and
                future.vol.is_ready and 
                future.exchange.hours.is_open(self.time, True))
        ]
        selected = sorted(futures, key=lambda future: future.vol)[:5]
        # Sum the inverse of volatilities.
        inverse_sum = sum([1/future.vol.current.value for future in selected])
        if inverse_sum == 0:
            return 
        # Form the inverse-volaility weighted portfolio.
        targets = [
            PortfolioTarget(future.mapped, 1/future.vol.current.value/inverse_sum/2)
            for future in selected
        ]
        self.set_holdings(targets, True)