| Overall Statistics |
|
Total Orders 11 Average Win 0% Average Loss -0.68% Compounding Annual Return 21.584% Drawdown 6.800% Expectancy -1 Start Equity 100000 End Equity 128000.53 Net Profit 28.001% Sharpe Ratio 1.176 Sortino Ratio 1.445 Probabilistic Sharpe Ratio 84.226% Loss Rate 100% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.001 Beta 0.696 Annual Standard Deviation 0.079 Annual Variance 0.006 Information Ratio -0.922 Tracking Error 0.046 Treynor Ratio 0.134 Total Fees $14.00 Estimated Strategy Capacity $130000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 0.21% |
# region imports
from AlgorithmImports import *
from datetime import timedelta
# to get pricing data for volatility or VIX
#from QuantConnect.Data.Custom.CBOE import *
# endregion
class SleepyLightBrownGaur(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 8, 6)
self.set_end_date(2025, 1, 31)
self.set_cash(100000)
self.equity = self.add_equity("SPY", Resolution.MINUTE)
self.equity.set_data_normalization_mode(DataNormalizationMode.RAW)
self.symbol = self.equity.symbol
self.vix = self.add_data(CBOE, "VIX").symbol
self.rank = 0
self.contract = str()
self.contractsAdded = set()
self.DaysBeforeExp = 2
self.DTE = 25
self.OTM = 0.01
self.lookbackIV = 150
self.IVlvl = 0.5
self.percentage = 0.9
self.options_alloc = 90
self.schedule.On(self.date_rules.every_day(self.symbol), \
self.time_rules.after_market_open(self.symbol, 30), \
self.Plotting)
self.schedule.on(self.date_rules.every_day(self.symbol), \
self.time_rules.after_market_open(self.symbol, 30), \
self.VIXRank)
def VIXRank(self):
history = self.history(CBOE, self.vix, self.lookbackIV, Resolution.DAILY)
self.rank = ((self.securities[self.vix].price - min(history[::-1]["low"]))/ (max(history[::-1]["high"]) - min(history[::-1]["low"])))
def on_data(self, data: Slice):
if self.is_warming_up:
return
if not self.portfolio[self.symbol].invested:
self.set_holdings(self.symbol, self.percentage)
if self.rank > self.IVlvl:
self.BuyPut(data)
if self.contract:
if (self.contract.ID.date - self.time) <= timedelta(self.DaysBeforeExp):
self.liquidate(self.contract)
self.log("Closed: Too close to expiration")
self.contract = str()
def BuyPut(self, data):
if self.contract == str():
self.contract = self.OptionsFilter(data)
return
elif not self.portfolio[self.contract].invested and data.contains_key(self.contract):
#buy 1 put option for every 90 shares of underlying security that we own
self.buy(self.contract,round(self.portfolio[self.symbol].quantity / self.options_alloc))
def OptionsFilter(self, data):
#one way is to use set filter and to iterate over available option chain object
#this can take a lot of time to backtest
#instead we use option chan provider which allows you to manually
#add only those options which you want to trade
# at the moment option chain provider doesn't allow option greeks and option implied volatilty
contracts = self.option_chain_provider.get_option_contract_list(self.symbol, self.time)
self.underlyingprice = self.securities[self.symbol].price
otm_puts = [i for i in contracts if i.id.OptionRight==OptionRight.PUT and
self.underlyingprice - i.id.strike_price > self.OTM *self.underlyingprice and
self.DTE - 8 < (i.id.date - data.time).days < self.DTE + 8
]
if len(otm_puts)> 0:
contract = sorted(sorted(otm_puts, key = lambda x: abs((x.ID.date - self.time).days - self.DTE)),
key = lambda x: self.underlyingprice - x.ID.strike_price)[0]
if contract not in self.contractsAdded:
self.contractsAdded.add(contract)
self.add_option_contract(contract, Resolution.MINUTE)
return contract
else:
return str()
def Plotting(self):
self.plot("Vol Chart", "Rank", self.rank)
self.plot("Vol Chart", "lvl", self.IVlvl)
self.plot("Data Chart", self.symbol, self.securities[self.symbol].close)
# option_invested = [x.key for x in self.portfolio if x.value.invested and x.value.type == self.security_type.option]
# if option_invested:
# self.plot("Data Chart","Strike", option_invested[0].ID.strike_price)
def on_order_event(self, order_event):
self.log(str(order_event))