| Overall Statistics |
|
Total Trades 2 Average Win 15.98% Average Loss 0% Compounding Annual Return -22.886% Drawdown 0.500% Expectancy 0 Net Profit -0.333% Sharpe Ratio -6.605 Probabilistic Sharpe Ratio 14.218% Loss Rate 0% Win Rate 100% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0.029 Annual Variance 0.001 Information Ratio -6.605 Tracking Error 0.029 Treynor Ratio 0 Total Fees $2.00 Estimated Strategy Capacity $14000000.00 Lowest Capacity Asset SPX 31KC0UJJBN6EM|SPX 31 |
class TestIndexOptionAlgorithm(QCAlgorithm):
def Initialize(self):
# Backtesting parameters
self.SetStartDate(2021, 1, 11)
self.SetEndDate(2021, 1, 15)
self.SetCash(1000000)
# Index Ticker Symbol
self.ticker = "SPX"
# Time Resolution
self.timeResolution = Resolution.Minute # Resolution.Minute .Hour .Daily
# Set brokerage model and margin account
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
# Days to Expiration
self.dte = 5
# Number of strikes to retrieve from the option chain universe (nStrikes on each side of ATM)
self.nStrikes = 100
# Add the underlying Index
index = self.AddIndex(self.ticker, self.timeResolution)
index.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.underlyingSymbol = index.Symbol
# Keep track of the option contract subscriptions
self.optionContractsSubscriptions = []
# Add option
option = self.AddIndexOption(index.Symbol, self.timeResolution)
self.optionSymbol = option.Symbol
# Set the option chain filter function
option.SetFilter(self.optionChainFilter)
# Set option pricing model for retrieving the Greeks. It appears that the Delta computed by the platform is incurrect!
#option.PriceModel = OptionPriceModels.CrankNicolsonFD() # Handles both European & American options
# The pricing model needs some warm-up time to get the Greeks
#self.SetWarmUp(TimeSpan.FromDays(7))
option.PriceModel = OptionPriceModels.BlackScholes() # European Options
# Set Security Initializer (This does not seem to solve the issue with the benchmark below)
self.SetSecurityInitializer(self.security_initializer)
# Setting the benchmark below causes the following error:
# Runtime Error: Sorry Hour is not a supported resolution for TradeBar and SecurityType.Index. Please change your AddData to use one of the supported resolutions (Minute). in DataManager.cs:line 455 (Open Stacktrace)
#self.SetBenchmark(index.Symbol)
# -----------------------------------------------------------------------------
# Scheduled function: every day, 25 minutes after the market open
# -----------------------------------------------------------------------------
self.Schedule.On(self.DateRules.EveryDay(self.underlyingSymbol)
, self.TimeRules.AfterMarketOpen(self.underlyingSymbol, 25)
, Action(self.openPosition)
)
def security_initializer(self, security):
if security.Type == SecurityType.Equity:
security.SetDataNormalizationMode(DataNormalizationMode.Raw)
elif security.Type == SecurityType.Option:
security.SetMarketPrice(self.GetLastKnownPrice(security))
def optionChainFilter(self, universe):
return universe.IncludeWeeklys()\
.Strikes(-self.nStrikes, self.nStrikes)\
.Expiration(0, self.dte)
def getOptionChain(self, slice):
# Loop through all chains
for chain in slice.OptionChains:
# Look for the specified optionSymbol
if chain.Key != self.optionSymbol:
continue
# Make sure there are any contracts in this chain
if chain.Value.Contracts.Count != 0:
return chain.Value
def openPosition(self):
self.Debug("Entering method openPosition")
# Get the option chain
chain = self.getOptionChain(self.CurrentSlice)
# Exit if we got no chains
if chain == None:
self.Debug(" -> No chains!")
return
# Log the number of contracts that were found
self.Debug(" -> Found " + str(chain.Contracts.Count) + " contracts in the option chain!")
# Do not open any new positions if we have already one open
if self.Portfolio.Invested:
return
# Get the furthest expiry date
expiry = sorted(chain, key = lambda x: x.Expiry, reverse = True)[0].Expiry
# Sort all Put contracts (with the given expiry date) by the strike price in reverse order
puts = sorted([contract for contract in chain
if contract.Expiry == expiry
and contract.Right == OptionRight.Put
]
, key = lambda x: x.Strike
, reverse = True
)
# Get the ATM put
contract = puts[0]
# Subscribe to the option contract data feed
if not contract.Symbol in self.optionContractsSubscriptions:
self.AddOptionContract(contract.Symbol, self.timeResolution)
self.optionContractsSubscriptions.append(contract.Symbol)
# Sell the Put
self.MarketOrder(contract.Symbol, -1, True)
def OnOrderEvent(self, orderEvent):
self.Debug(orderEvent)
def OnData(self, slice):
pass