| Overall Statistics |
|
Total Orders 11 Average Win 0% Average Loss -2.35% Compounding Annual Return -17.803% Drawdown 36.800% Expectancy -0.571 Start Equity 10000 End Equity 8603.52 Net Profit -13.965% Sharpe Ratio 0.037 Sortino Ratio 0.036 Probabilistic Sharpe Ratio 17.603% Loss Rate 57% Win Rate 43% Profit-Loss Ratio 0 Alpha -0.142 Beta 3.757 Annual Standard Deviation 0.656 Annual Variance 0.43 Information Ratio -0.034 Tracking Error 0.589 Treynor Ratio 0.007 Total Fees $7.00 Estimated Strategy Capacity $610000.00 Lowest Capacity Asset VXXB WRBPJAJZ2Q91 Portfolio Turnover 1.04% |
# region imports
from AlgorithmImports import *
# endregion
class VXXPutOnHighVIX(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 6, 1)
self.set_cash(10000)
self.vxx = self.add_equity("VXX", Resolution.MINUTE).Symbol
# # Add VIX index as custom data (or use VIXY ETF as proxy if needed)
self.vixy = self.add_equity("VIXY", Resolution.MINUTE).Symbol
# Optimization parameters
self.vixy_threshold = 55 #self.get_parameters("vixy_threshold") or 25
self.min_expiry_days = 10
self.max_expiry_days =45
self.strike_offset = 0*0.05 # 0 = ATM, 0.05 = 5% OTM
# Add VXX options
self.option = self.add_option("VXX", Resolution.MINUTE)
self.option.SetFilter(-10, -1, self.min_expiry_days, self.max_expiry_days)
self.last_trade_date = None
self.option_positions = {}
def OnData(self, data):
if not self.securities[self.vixy].HasData:
return
vixy_price = self.securities[self.vixy].Price
if vixy_price < self.vixy_threshold:
return
# Avoid overtrading
if self.last_trade_date and (self.time - self.last_trade_date).days < 30:
return
# Get option chain
chain = data.OptionChains.get(self.option.Symbol)
if not chain:
return
# Select a put option: 30-45 days to expiry, slightly OTM
puts = [x for x in chain if x.right == OptionRight.PUT and x.expiry > self.time + timedelta(self.min_expiry_days)]
if not puts:
return
# sorted_puts = sorted(puts, key=lambda x: x.Strike)
# selected_contract = sorted_puts[0]
# Sort by strike offset
underlying_price = self.securities[self.vxx].Price
target_strike = underlying_price * (1 - self.strike_offset)
selected_contract = sorted(puts, key=lambda x: abs(x.Strike - target_strike))[0]
self.market_order(selected_contract.Symbol, 1)
self.debug(f"Bought VXX Put: {selected_contract.Symbol} at {self.time}")
self.option_positions[selected_contract.Symbol] = {
"entry_time": self.time,
"entry_price": selected_contract.AskPrice
}
self.last_trade_date = self.time
# Check for exit conditions
for symbol, info in list(self.option_positions.items()):
if symbol not in data or not data[symbol]:
continue
holding = self.portfolio[symbol]
if not holding.invested:
del self.option_positions[symbol]
continue
current_price = data[symbol].Price
self.debug(f"Current Price {current_price}, Entry price {info['entry_price']}")
entry_price = info["entry_price"]
holding_days = (self.time - info["entry_time"]).days
# Exit if held for 7+ days or profit > 100x
if holding_days >= 7 or (current_price / entry_price) >= 10:
self.liquidate(symbol)
self.debug(f"Exited {symbol} on {self.time} | Held {holding_days} days | Return: {current_price / entry_price:.2f}x")
del self.option_positions[symbol]
# # Custom data class for VIX index
# class CBOE(PythonData):
# def GetSource(self, config, date, isLiveMode):
# return SubscriptionDataSource("https://www.cboe.com/us/futures/market_statistics/historical_data/", SubscriptionTransportMedium.RemoteFile)
# def Reader(self, config, line, date, isLiveMode):
# # Implement parsing logic if using real VIX data
# return None