| Overall Statistics |
|
Total Trades 8 Average Win 2.88% Average Loss 0% Compounding Annual Return 10.557% Drawdown 2.000% Expectancy 0 Net Profit 11.985% Sharpe Ratio 2.027 Probabilistic Sharpe Ratio 91.890% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0.069 Beta -0.084 Annual Standard Deviation 0.036 Annual Variance 0.001 Information Ratio 0.542 Tracking Error 0.214 Treynor Ratio -0.862 Total Fees $20.00 Estimated Strategy Capacity $24000000.00 Lowest Capacity Asset SPY 323UYHQN3M1YE|SPY R735QTJ8XC9X |
#region imports
from AlgorithmImports import *
#endregion
class VirtualRedDogfish(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 12, 1)
# self.SetEndDate(2022, 10, 1)
self.SetCash(1000000)
self.vix = self.AddIndex('VIX', Resolution.Minute)
self.stock = self.AddEquity("SPY", Resolution.Minute)
self.stock.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.stock_symbol = self.stock.Symbol
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
self.contract = None #Initialize the option contract
self.trade = False #Setup flag to trade
self.option_wt = 0.05 #Target portfolio weight
#Set parameters for optimization
self.option_premium = float(self.GetParameter("option_premium"))
# self.option_premium = 0.95 # % Strike price premium/discount to underlying
self.option_dte = float(self.GetParameter("option_dte"))
# self.option_dte = 90 #Target days-to-expiration for option contract
#Schedule trade #1
self.Schedule.On(self.DateRules.On(2021, 12, 21), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupBuyTrade)
self.Schedule.On(self.DateRules.On(2022, 1, 24), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupSellTrade)
#Schedule trade #2
self.Schedule.On(self.DateRules.On(2022, 2, 9), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupBuyTrade)
self.Schedule.On(self.DateRules.On(2022, 2, 24), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupSellTrade)
#Schedule trade #3
self.Schedule.On(self.DateRules.On(2022, 4, 12), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupBuyTrade)
self.Schedule.On(self.DateRules.On(2022, 4, 29), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupSellTrade)
#Schedule trade #4
self.Schedule.On(self.DateRules.On(2022, 8, 8), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupBuyTrade)
self.Schedule.On(self.DateRules.On(2022, 9, 23), \
self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
self.SetupSellTrade)
# #Schedule trade #5
# self.Schedule.On(self.DateRules.On(2023, 1, 13), \
# self.TimeRules.AfterMarketOpen(self.stock_symbol, 30), \
# self.SetupBuyTrade)
def OnData(self, slice):
#Liquidate any options that are within 1.5 days of expiration
option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
if option_invested:
expirydate = self.contract.ID.Date
if (expirydate - self.Time) < timedelta(1.5):
# self.Debug("Sell option, less than 2 days before expiry")
self.Liquidate(self.contract)
self.contract = None
if self.trade:
# self.Log("Buy trade fired at : {0}".format(self.Time))
self.InitiateTrade()
def SetupBuyTrade(self):
#Check to see if already invested in option
option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
if not option_invested:
self.contract = self.GetPut()
self.trade = True
def SetupSellTrade(self):
#Check to see if already invested in option
option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
if option_invested:
# self.Log("Exit trade fired at : {0}".format(self.Time))
self.Liquidate(self.contract)
self.contract = None
self.trade = False
def GetPut(self):
targetStrike = self.Securities[self.stock_symbol].Price * self.option_premium
contracts = self.OptionChainProvider.GetOptionContractList(self.stock_symbol, self.Time)
# self.Debug(f"SPY total contracts found: {len(contracts)}")
puts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put]
puts = sorted(sorted(puts, key = lambda x: x.ID.Date),
key = lambda x: x.ID.StrikePrice, reverse = False)
# self.Debug(f"SPY puts found: {len(puts)}")
puts = [x for x in puts if (x.ID.StrikePrice - targetStrike) >= 0
and (x.ID.StrikePrice - targetStrike) < 100]
puts = [x for x in puts if self.option_dte < (x.ID.Date - self.Time).days <= self.option_dte+100] #self.option_dte+100 sets dte buffer of 100 days
if len(puts) == 0:
# self.Debug(f"!!! No puts available")
return None
# else: self.Debug(f"SPY puts w/ good strikes found: {len(puts)}")
self.Debug(f"Selected option contract: {puts[0]} &, expiration: {puts[0].ID.Date} \
&, strike: {puts[0].ID.StrikePrice} &, SPY: {self.stock.Price} & , VIX: {self.vix.Price}")
self.AddOptionContract(puts[0], Resolution.Minute)
return puts[0]
def InitiateTrade(self):
if self.contract is not None:
self.SetHoldings(self.contract, self.option_wt)
self.trade = False #Reset trading flag