Overall Statistics |
Total Orders 1850 Average Win 2.56% Average Loss -2.26% Compounding Annual Return 4.361% Drawdown 67.000% Expectancy 0.075 Start Equity 10000000 End Equity 17304322.84 Net Profit 73.043% Sharpe Ratio 0.216 Sortino Ratio 0.207 Probabilistic Sharpe Ratio 0.024% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.13 Alpha 0.128 Beta -0.49 Annual Standard Deviation 0.388 Annual Variance 0.151 Information Ratio -0.015 Tracking Error 0.433 Treynor Ratio -0.171 Total Fees $3609727.16 Estimated Strategy Capacity $46000000.00 Lowest Capacity Asset VX YNNC8UBM7FMX Portfolio Turnover 31.44% |
#region imports from AlgorithmImports import * #endregion class TermStructureOfVixAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2012, 1, 1) self.set_end_date(2024, 11, 1) self.set_cash(10_000_000) # Add the VIX Index and Futures. self._vix_index = self.add_index("VIX") self._vx_future = self.add_future(Futures.Indices.VIX) self._vx_future.set_filter(14, 60) # Create a Scheduled Event to update the portfolio. self._get_liquid_price = False self.schedule.on( self.date_rules.every_day(self._vx_future.symbol), self.time_rules.before_market_close(self._vx_future.symbol, 15), lambda: setattr(self, '_get_liquid_price', True) ) # Set some parameters. self._daily_roll_entry_threshold = 0.05 # The paper uses +/-0.1 self._daily_roll_exit_threshold = 0.025 # The paper uses +/-0.05 self._min_days_to_expiry = 10 self._spread_threshold = 0.1 def on_data(self, data): # For the last 15 minutes of the trading day, select the VIX Futures contract that has: # - >=10 trading days until expiry # - <=$0.1 bid-ask spread vx_price = None selected_contract = None trading_days_until_expiry = None if self._get_liquid_price: chain = data.future_chains.get(self._vx_future.symbol) # Get the VIX Future price. if chain: for contract in chain: # Select the contract with at least 10 trading days until expiry. trading_days_until_expiry = self._trading_days_until_expiry(contract) if trading_days_until_expiry < self._min_days_to_expiry: continue # Wait for the selected contract to have a narrow spread. if (contract.ask_price - contract.bid_price > self._spread_threshold and # If the spread isn't narrow before market close, use the prices right before market close. self._vx_future.exchange.hours.is_open(self.time + timedelta(minutes=1), extended_market_hours=False)): break vx_price = (contract.ask_price + contract.bid_price) / 2 selected_contract = contract if not vx_price: return self._get_liquid_price = False # Calculate the basis and daily roll. vix_price = (self._vix_index.open + self._vix_index.close) / 2 basis = (vx_price - vix_price) in_contango = basis > 0 daily_roll = basis / trading_days_until_expiry self.plot('VIX', 'Future Price', vx_price) self.plot('VIX', 'Spot Price', vix_price) self.plot('In Contango?', 'Value', int(in_contango)) self.plot('Daily Roll', 'Value', daily_roll) # Look for trade entries. # "Short VIX futures positions are entered when the VIX futures basis is in # contango and the daily roll exceeds .10 VIX futures points ($100 per day) and long VIX futures # positions are entered when the VIX futures basis is in backwardation and the daily roll is less # than -.10 VIX futures points" (p. 14) if not self.portfolio.invested: if in_contango and daily_roll > self._daily_roll_entry_threshold: weight = -0.5 # Enter short VIX Future trade. elif not in_contango and daily_roll < -self._daily_roll_entry_threshold: weight = 0.5 # Enter long VIX Future trade. else: return self.set_holdings(selected_contract.symbol, weight, tag=f"Entry: Expires on {selected_contract.expiry}") self._vx_future.invested_contract = selected_contract return # Look for trade exits. # Exit long VIX Future trade when daily_roll < self._daily_roll_exit_threshold or when the contract expires next day. # Exit short VIX Future trade when daily_roll > -self._daily_roll_exit_threshold or when the contract expires next day. trading_days_until_expiry = self._trading_days_until_expiry(self._vx_future.invested_contract) holding = self.portfolio[self._vx_future.invested_contract.symbol] tag = "" if trading_days_until_expiry <= 1: tag = f"Exit: Expires in {trading_days_until_expiry} trading day(s)" elif holding.is_long and daily_roll < self._daily_roll_exit_threshold: tag = f"Exit: daily roll ({daily_roll}) < threshold" elif holding.is_short and daily_roll > -self._daily_roll_exit_threshold: tag = f"Exit: daily roll ({daily_roll}) > -threshold" if tag: self.liquidate(tag=tag) self._vx_future.invested_contract = None def _trading_days_until_expiry(self, contract): return len(list(self.trading_calendar.get_trading_days(self.time, contract.expiry - timedelta(1))))