Overall Statistics
Total Orders
557
Average Win
2.55%
Average Loss
-1.37%
Compounding Annual Return
49.678%
Drawdown
56.900%
Expectancy
0.219
Start Equity
100000000
End Equity
181742434.9
Net Profit
81.742%
Sharpe Ratio
0.915
Sortino Ratio
1.057
Probabilistic Sharpe Ratio
35.907%
Loss Rate
57%
Win Rate
43%
Profit-Loss Ratio
1.86
Alpha
-0.18
Beta
5.834
Annual Standard Deviation
0.71
Annual Variance
0.504
Information Ratio
0.814
Tracking Error
0.623
Treynor Ratio
0.111
Total Fees
$500387.55
Estimated Strategy Capacity
$3400000.00
Lowest Capacity Asset
NG YLCMOZLFS05D
Portfolio Turnover
36.50%
from AlgorithmImports import *

class InverseVolatilityRankAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 3, 1)  # Set Start Date
        self.set_cash(100000000)     # For a large future universe, the fund needed would be large
        
        for contract in ["VX", "ES", "NQ", "YM", "B", "RB", "HO", "NG"]:
            future = self.add_future(contract)
        
        self.schedule.on(self.date_rules.every(DayOfWeek.MONDAY), self.time_rules.at(9, 31), self.rebalance)

    def rebalance(self):
        df = self.history(list(set(x.canonical for x in self.securities.keys())), 22, Resolution.DAILY).close.droplevel([0]).unstack(0).pct_change().dropna()
        inv_vol = 1. / df.std(axis=0, ddof=1)
        inv_vol /= np.sum(inv_vol)
        for contract, weight in inv_vol.items():
            self.set_holdings(self.securities[contract].mapped, weight / 2)

    def on_symbol_changed_events(self, symbols_changed):
        for symbol, changed_event in symbols_changed.items():
            quantity = self.portfolio[changed_event.old_symbol].quantity
            self.liquidate(changed_event.old_symbol)
            if changed_event.new_symbol in self.securities:
                self.market_order(changed_event.new_symbol, quantity // self.securities[symbol].symbol_properties.contract_multiplier)