Thank You Derek for your insights!
I've used your code to get the greeks correctly
from StopTakeUnrealizedRiskModel import StopTakeUnrealizedRiskModel
from MaximumExpiryMarginRiskModel import MaximumExpiryMarginRiskModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from QuantConnect.Securities.Option import OptionPriceModels
from QuantConnect import Resolution
from QuantConnect import OptionRight
from datetime import date, datetime, timedelta
class TachyonQuantumEngine(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 1, 1)
self.SetCash(10000)
self.alpha = CannaMaxLeverage()
self.AddAlpha(self.alpha)
self.risk = StopTakeUnrealizedRiskModel(0.20,0.40)
self.AddRiskManagement(self.risk)
self.risk2 = MaximumExpiryMarginRiskModel(45)
self.AddRiskManagement(self.risk2)
tickers = ['SPY','QQQQ'] #di quali ticker prendere le opzioni
for x in tickers:
self.option = self.AddOption(x)
self.option.SetFilter(-25, +25, 180, 900)
# set the pricing model for Greeks and volatility
# find more pricing models https://www.quantconnect.com/lean/documentation/topic27704.html
self.option.PriceModel = OptionPriceModels.CrankNicolsonFD()
self.SetExecution(ImmediateExecutionModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.Settings.FreePortfolioValuePercentage = 0.10
# set the warm-up period for the pricing model
self.SetWarmUp(TimeSpan.FromDays(4))
class CannaMaxLeverage(AlphaModel):
def __init__(self):
pass
def Update(self, algorithm, data):
if algorithm.IsWarmingUp or algorithm.Portfolio.Invested:
return []
#https://www.quantconnect.com/lean/documentation/topic24200.html
chain = [x.Value for x in data.OptionChains]
if len(chain):
weight = 1/len(chain)
else:
return []
contracts = [[]] * len(chain)
for x in range(len(chain)):
#https://www.quantconnect.com/lean/documentation/topic24233.html
contracts[x] = [y for y in chain[x] if y.Right==OptionRight.Call]
maxExpiry = sorted(contracts[x], key = lambda y: y.Expiry, reverse=True)[0]
contracts[x] = [y for y in contracts[x] if y.Expiry==maxExpiry.Expiry]
symbols = []
for x in range(len(contracts)):
qta = [0] * len(contracts[x])
capacity = [0] * len(contracts[x])
i = 0
for y in contracts[x]:
qta[i] = round(algorithm.CalculateOrderQuantity(y.Symbol, weight),0)
capacity[i] = qta[i] * y.Greeks.Delta
i = i+1
imax = capacity.index(np.max(capacity))
symbols.append(contracts[x][imax].Symbol)
insights=[]
if symbols != []:
for x in symbols:
insights.append(Insight.Price(x,timedelta(days=900), InsightDirection.Up))
return insights
But now the problem is that it arrives at 75% and then never finish backtesting.
I also wonder if it's possible to use the Option Universe Selection Model instead of AddOption/SetFilter to get greeks inside the Alpha Model. I've tried to modify the stock code from LEAN to add the Pricing Model, but the Greeks stay at 0. Maybe it's because with this methodology they can't warmup properly?
#from Selection.OptionUniverseSelectionModel import OptionUniverseSelectionModel
from datetime import date, timedelta
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from clr import AddReference
from clr import GetClrType as typeof
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")
from QuantConnect import *
from QuantConnect.Securities import *
from QuantConnect.Data.Auxiliary import ZipEntryName
from QuantConnect.Data.UniverseSelection import OptionChainUniverse
from Selection.UniverseSelectionModel import UniverseSelectionModel
from datetime import datetime
from QuantConnect.Securities.Option import OptionPriceModels
class OptionUniverseSelectionModel(UniverseSelectionModel):
'''Provides an implementation of IUniverseSelectionMode that subscribes to option chains'''
def __init__(self,
refreshInterval,
optionChainSymbolSelector,
universeSettings = None,
securityInitializer = None):
'''Creates a new instance of OptionUniverseSelectionModel
Args:
refreshInterval: Time interval between universe refreshes</param>
optionChainSymbolSelector: Selects symbols from the provided option chain
universeSettings: Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed
securityInitializer: [Obsolete, will not be used] Performs extra initialization (such as setting models) after we create a new security object'''
self.nextRefreshTimeUtc = datetime.min
self.refreshInterval = refreshInterval
self.optionChainSymbolSelector = optionChainSymbolSelector
self.universeSettings = universeSettings
self.securityInitializer = securityInitializer
def GetNextRefreshTimeUtc(self):
'''Gets the next time the framework should invoke the `CreateUniverses` method to refresh the set of universes.'''
return self.nextRefreshTimeUtc
def CreateUniverses(self, algorithm):
'''Creates a new fundamental universe using this class's selection functions
Args:
algorithm: The algorithm instance to create universes for
Returns:
The universe defined by this model'''
self.nextRefreshTimeUtc = (algorithm.UtcTime + self.refreshInterval).date()
uniqueUnderlyingSymbols = set()
for optionSymbol in self.optionChainSymbolSelector(algorithm.UtcTime):
if optionSymbol.SecurityType != SecurityType.Option:
raise ValueError("optionChainSymbolSelector must return option symbols.")
# prevent creating duplicate option chains -- one per underlying
if optionSymbol.Underlying not in uniqueUnderlyingSymbols:
uniqueUnderlyingSymbols.add(optionSymbol.Underlying)
yield self.CreateOptionChain(algorithm, optionSymbol)
def CreateOptionChain(self, algorithm, symbol):
'''Creates a OptionChainUniverse for a given symbol
Args:
algorithm: The algorithm instance to create universes for
symbol: Symbol of the option
Returns:
OptionChainUniverse for the given symbol'''
if symbol.SecurityType != SecurityType.Option:
raise ValueError("CreateOptionChain requires an option symbol.")
# rewrite non-canonical symbols to be canonical
market = symbol.ID.Market
underlying = symbol.Underlying
if not symbol.IsCanonical():
alias = f"?{underlying.Value}"
symbol = Symbol.Create(underlying.Value, SecurityType.Option, market, alias)
# resolve defaults if not specified
settings = self.universeSettings if self.universeSettings is not None else algorithm.UniverseSettings
initializer = self.securityInitializer if self.securityInitializer is not None else algorithm.SecurityInitializer
# create canonical security object, but don't duplicate if it already exists
securities = [s for s in algorithm.Securities if s.Key == symbol]
if len(securities) == 0:
optionChain = self.CreateOptionChainSecurity(algorithm, symbol, settings, initializer)
else:
optionChain = securities[0]
# set the option chain contract filter function
optionChain.SetFilter(self.Filter)
# force option chain security to not be directly tradable AFTER it's configured to ensure it's not overwritten
optionChain.IsTradable = False
#################### setting PricingModel ##########################################
optionChain.PriceModel = OptionPriceModels.CrankNicolsonFD()
####################################################################################
return OptionChainUniverse(optionChain, settings, initializer, algorithm.LiveMode)
def CreateOptionChainSecurity(self, algorithm, symbol, settings, initializer):
'''Creates the canonical option chain security for a given symbol
Args:
algorithm: The algorithm instance to create universes for
symbol: Symbol of the option
settings: Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed
initializer: [Obsolete, will not be used] Performs extra initialization (such as setting models) after we create a new security object
Returns
Option for the given symbol'''
config = algorithm.SubscriptionManager.SubscriptionDataConfigService.Add(typeof(ZipEntryName),
symbol,
settings.Resolution,
settings.FillForward,
settings.ExtendedMarketHours,
False)
return algorithm.Securities.CreateSecurity(symbol, config, settings.Leverage, False)
def Filter(self, filter):
## Cerco le opzioni tra +/- 10 strike, a partire da 6 mesi in avanti(180) +1 anno(540) o +2 anni(900)
# vorrei solo le Call LEAPS di Gennaio
#filter è un tipo particolare di oggetto:
#https://www.quantconnect.com/lean/documentation/topic26710.html
filtered = (filter.Strikes(-25, +25)
.Expiration(timedelta(180), timedelta(900)))
return (filtered)