Is there an example on how to get Greeks Values from inside an Alpha Model?
I saw that the Option Universe Selection model inside the Wizard returns Option object, but Greeks are inside OptionContracts objects and they also need a Pricing Model and a little bit of WarmUp.
I'm, quite new to Python so I tried something, but I always get 0 as the value of Delta. It will be useful to have a simple example on how to get Greeks inside an Alpha Model
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from OptionsUniverseSelectionModel import OptionsUniverseSelectionModel
from QuantConnect.Securities.Option import OptionPriceModels
from QuantConnect import Resolution
from QuantConnect import OptionRight
import numpy as np
tickers = ['SPY','QQQQ']
symbolList = []
weight = 1/len(tickers)
class CannaMaxLeverageAlphaModel(AlphaModel):
def __init__(self):
self.emitted = []
self.previousSymbols = []
pass
def OnSecuritiesChanged(self, algorithm, changes):
alloptions = changes.AddedSecurities
for ticker in symbolList:
alloptions = [x for x in alloptions if x.Symbol.ID!=ticker.ID.Underlying ]
#
myOptions = [x for x in alloptions if x.Right==OptionRight.Call]
maxExpiry = sorted(alloptions, key = lambda x: x.Expiry, reverse=True)[0]
myOptions = [x for x in alloptions if x.Expiry==maxExpiry.Expiry]
equities=[x for x in changes.AddedSecurities if x.Symbol.ID in [ticker.ID.Underlying for ticker in symbolList]]
for x in myOptions:
#pricing models: https://www.quantconnect.com/lean/documentation/topic27704.html
x.PriceModel = OptionPriceModels.CrankNicolsonFD()
#symbols = [x.Symbol for x in myOptions]
#history = algorithm.History(symbols, timedelta(days=7), Resolution.Minute)
def Update(self, algorithm, data):
insights = []
#https://www.quantconnect.com/lean/documentation/topic24200.html
chain = [x.Value for x in data.OptionChains]
contracts = [[]] * len(tickers)
for x in range(len(tickers)):
#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))
#emit insights once
if not (contracts[x][imax].Symbol in self.emitted):
self.emitted.append(contracts[x][imax].Symbol)
symbols.append(contracts[x][imax].Symbol)
insights=[]
if symbols != []:
for x in symbols:
insights.append(Insight.Price(x,timedelta(days=28), InsightDirection.Up))
if self.previousSymbols != []:
for y in self.previousSymbols:
insights.append(Insight.Price(y,timedelta(28), InsightDirection.Flat))
self.previousSymbols = symbols
return insights
Derek Melchin
Hi Alessio,
This is difficult to debug as the algorithm class is not published above. However, the greeks can have a value of 0 when the pricing model is not set or hasn't been warmed up properly. See the attached backtest for an example algorithm which demonstrates how we can retrieve the greeks inside an alpha model.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Alessio Magri
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)
Derek Melchin
Hi Alessio,
The TachyonQuantumEngine algorithm filters to such a large amount of contracts, the backtest takes too long for me to reproduce the issue of it stopping after 75% completion. Just backtesting over a couple of months, the algorithm was producing errors related to the available margin. I adjusted the FreePortfolioValuePercentage to 0.5 and it allowed an error-free backtest over this time period. Note that these alterations were made while disabling the custom risk management models since their definitions were not included in the code above. So I can further assist with debugging, can you publish the date of when the backtest fails and attach the code for the risk management models?
We can use the OptionUniverseSelectionModel. We just need to ensure we warm up the underlying volatility model. This is done in the alpha model's OnSecuritiesChanged method.
def OnSecuritiesChanged(self, algorithm, changes): equities = [x for x in changes.AddedSecurities if x.Type == SecurityType.Equity] if equities: symbols = [x.Symbol for x in equities] history = algorithm.History(symbols, 30, Resolution.Daily) for equity in equities: df = history.loc[equity.Symbol] for idx, row in df.iterrows(): tradebar = TradeBar(idx, equity.Symbol, row.open, row.high, row.low, row.close, row.volume) equity.VolatilityModel.Update(equity, tradebar)
See the attached backtest for reference.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Alessio Magri
Thank You Derek!
Here is the code of the custom Risk Models
# 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 AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Algorithm.Framework") from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel from datetime import timedelta class MaximumExpiryMarginRiskModel(RiskManagementModel): '''Provides an implementation of IRiskManagementModel that limits the unrealized profit per holding to the specified percentage''' def __init__(self, maximumExpiry = 30): '''Initializes a new instance of the MaximumUnrealizedProfitPercentPerSecurity class Args: maximumExpiry: The maximum number of days allowed till expiration. Defaults 30 days per security''' self.maximumExpiry = abs(maximumExpiry) def ManageRisk(self, algorithm, targets): '''Manages the algorithm's risk at each time step Args: algorithm: The algorithm instance targets: The current portfolio targets to be assessed for risk''' targets = [] for kvp in algorithm.Securities: security = kvp.Value if not security.Invested: continue expiry = security.Holdings.Symbol.ID.Date if timedelta(days=self.maximumExpiry) >= expiry - algorithm.Time: # liquidate targets.append(PortfolioTarget(security.Symbol, 0)) return targets # 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 AddReference("System") AddReference("QuantConnect.Common") AddReference("QuantConnect.Algorithm") AddReference("QuantConnect.Algorithm.Framework") from QuantConnect import * from QuantConnect.Algorithm import * from QuantConnect.Algorithm.Framework import * from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel class StopTakeUnrealizedRiskModel(RiskManagementModel): '''Provides an implementation of IRiskManagementModel that limits the unrealized profit per holding to the specified percentage''' def __init__(self, takeUnrealized = 0.05, stopUnrealized = 0.05): '''Initializes a new instance of the MaximumUnrealizedProfitPercentPerSecurity class Args: takeUnrealized: The maximum percentage unrealized profit allowed for any single security holding, defaults to 5% gain per security stopUnrealized: The maximum percentage unrealized Loss allowed for any single security holding, defaults to 5% drawdown per security''' self.takeUnrealized = abs(takeUnrealized) self.stopUnrealized = -1 * abs(stopUnrealized) def ManageRisk(self, algorithm, targets): '''Manages the algorithm's risk at each time step Args: algorithm: The algorithm instance targets: The current portfolio targets to be assessed for risk''' targets = [] for kvp in algorithm.Securities: security = kvp.Value if not security.Invested: continue pnl = security.Holdings.UnrealizedProfitPercent if (pnl > self.takeUnrealized) or (pnl < self.stopUnrealized): # liquidate targets.append(PortfolioTarget(security.Symbol, 0)) return targets
I think the problem is between February and March, maybe the strong depreciation of that period pushes too many contracts in the chain?
Also by removing some contracts, in that period the algorithm is too slow. Used this code to remove some Options:
for x in changes.RemovedSecurities: algorithm.RemoveSecurity(x.Symbol) for symbol in algorithm.Securities.Keys: if symbol.SecurityType == SecurityType.Option and symbol.Underlying == x.Symbol: algorithm.RemoveSecurity(symbol)
Best Regards.
Derek Melchin
Hi Alessio,
Thank you for providing the code for the risk models. After backtesting this algorithm over the February and March periods, it seems the algorithm tries to invest in option contracts deemed non-tradable. To fix this, we can extend our contract filtering process to check if the security is tradeable.
contracts[x] = [y for y in chain[x] if y.Right==OptionRight.Call and algorithm.Securities[y.Symbol].IsTradable]
See the attached backtest for reference.
Best,
Derek Melchin
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Alessio Magri
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!