You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a logging statement to the line in this plugin, it will tell that:
20250222 21:42:29.014 TRACE:: ThetaDataProvider.GetHistory: [DEBUG] ?SPY - Daily - Quote - 2/21/2025 5:00:00 AM - 2/22/2025 9:42:29 PM
20250222 21:42:29.014 TRACE:: ThetaDataProvider.GetHistory: Unsupported SecurityType 'Option' for symbol '?SPY'
which was yield by the following block:
// QuantConnect.ThetaData/ThetaDataHistoryProvider.cs
...Log.Trace($"{nameof(ThetaDataProvider)}.{nameof(GetHistory)}: [DEBUG] {historyRequest.Symbol} - {historyRequest.Resolution} - {historyRequest.TickType} - {startDateTimeUtc} - {historyRequest.EndTimeUtc}");if(!CanSubscribe(historyRequest.Symbol)){if(!_invalidSecurityTypeWarningFired){_invalidSecurityTypeWarningFired=true;Log.Trace($"{nameof(ThetaDataProvider)}.{nameof(GetHistory)}: Unsupported SecurityType '{historyRequest.Symbol.SecurityType}' for symbol '{historyRequest.Symbol}'");}returnnull;}
It is clear that it tries to pass ?SPY into the GetHistory method, however, GetHistory is intended for fetching data for individual contracts, and not for fetching the entire option chain, thus causing this issue. And if it didn't directly return null inside from this if-block, the code will then subsequently invoke the GetHistoricalQuoteData methed (issue #11 is trying to resolve how GetHistoricalQuoteData behaves when there's no valid quote bar for construction, but that would not resolve this issue, since this if-block will directly return null when we pass in a ?SPY instead of the individual contract symbols).
From my observation and experiment, in live trading, this GetHistory method was invoked by OptionChain method defined in Lean/Algorithm/QCAlgorithm.cs:
namely the file-based universe selection process, which was introduced starting here: QuantConnect/Lean@16c4259 .
However, even then, it still tries to get the universe content via a network request, instead of retrieving directly from the universe csv file on that day, which should be the correct way if we are using Lean CLI to do live trading.
Expected Behavior
As long as we have built the Universe according to the sample universe format given by https://github.com/QuantConnect/Lean/blob/master/Data/option/usa/universes/spy/20240109.csv , we should be able to avoid network request for fetching the option chain from the data provider.
Currently it is doing extra work here (the local universe has been located and loaded before this redundant request), and it is doing it incorrectly.
Additionally, even if it is going to fetch the most recent market price for all the contracts, individual requests over 10k contracts are not acceptable (SetFilter right now is not working as expected in my experiment with ThetaData, I will investigate and open an issue for Lean main repository if it is involved), currently this plugin hasn't utilized bulk request to get whole universe data.
However, this is not an urgent issue. Won't affect the behavior in live trading.
Some code snippets that might be helpful for debugging
Code:
// Lean/Algorithm/QCAlgorithm.cs
...[DocumentationAttribute(AddingData)]publicOptionChainsOptionChains(IEnumerable<Symbol>symbols,boolflatten=false){Log($"OptionChains: received {symbols.Count()} symbols: {string.Join(", ",symbols.Select(x =>x.Value))}");varcanonicalSymbols=symbols.Select(GetCanonicalOptionSymbol).ToList();varoptionCanonicalSymbols=canonicalSymbols.Where(x =>x.SecurityType!=SecurityType.FutureOption);varfutureOptionCanonicalSymbols=canonicalSymbols.Where(x =>x.SecurityType==SecurityType.FutureOption);// var optionChainsData = History(optionCanonicalSymbols, 1).GetUniverseData()// .Select(x => (x.Keys.Single(), x.Values.Single().Cast<OptionUniverse>()));// TODO: why do we need to make a historical request here, especially this will yield a Daily resolution request on the quote data// I cannot see the point for options here. For options, we only cares about the contracts list, all data should be from OnData methodLog($"OptionChains: requesting history for Resolution={GetResolution(optionCanonicalSymbols.First(),null,typeof(QuoteBar))}");varhistoryData=History(optionCanonicalSymbols,1);Log($"HistoryData type: {historyData.GetType().FullName}");Log($"History retrieved for canonical symbols: {string.Join(", ",optionCanonicalSymbols.Select(x =>x.Value))}");Log($"History length: {historyData.Count()}");varuniverseData=historyData.GetUniverseData();Log($"UniverseData type: {universeData.GetType().FullName}");Log("Extracted universe data from history.");foreach(varentryinuniverseData){varkey=entry.Keys.FirstOrDefault();varvalues=entry.Values.FirstOrDefault();if(key!=null){varvalueTypes=values!=null?string.Join(", ",values.Select(v =>v.GetType().Name).Distinct()):"no values";intcount=values?.Count()??0;Log($"UniverseData entry - Key: {key}, Count: {count}, Value Types: {valueTypes}");varfirstFew=values.Take(5);foreach(variteminfirstFew){Log($" ItemType: {item.GetType().Name}, Item: {item}");}}else{Log("UniverseData entry with no key.");}}varoptionChainsData=universeData.Select(dataEntry =>{varcanonicalOptionSymbol=dataEntry.Keys.Single();varoptionUniverseCollection=dataEntry.Values.Single().Cast<OptionUniverse>();Log($"Processing universe data for canonical symbol: {canonicalOptionSymbol}");Log($"Option universe collection size: {optionUniverseCollection.Count()}");return(canonicalOptionSymbol,optionUniverseCollection);});// TODO: For FOPs, we fall back to the option chain provider until OptionUniverse supports themvarfutureOptionChainsData=futureOptionCanonicalSymbols.Select(symbol =>{varoptionChainData=OptionChainProvider.GetOptionContractList(symbol,Time).Select(contractSymbol =>newOptionUniverse(){Symbol=contractSymbol,EndTime=Time.Date,});return(symbol,optionChainData);});vartime=Time.Date;varchains=newOptionChains(time,flatten);foreach(var(symbol,contracts)inoptionChainsData.Concat(futureOptionChainsData)){varsymbolProperties=SymbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market,symbol,symbol.SecurityType,AccountCurrency);if(symbolProperties!=null){Log("SymbolProperties type: "+symbolProperties.GetType().FullName);Log("SymbolProperties content: "+symbolProperties);}varoptionChain=newOptionChain(symbol,time,contracts,symbolProperties,flatten);chains.Add(symbol,optionChain);}returnchains;}
Problem Description
This is likely an issue caused by file-based universe selection, which has been mentioned here: QuantConnect/Lean.DataSource.Polygon#19
Add a logging statement to the line in this plugin, it will tell that:
which was yield by the following block:
It is clear that it tries to pass
?SPY
into theGetHistory
method, however,GetHistory
is intended for fetching data for individual contracts, and not for fetching the entire option chain, thus causing this issue. And if it didn't directly return null inside from this if-block, the code will then subsequently invoke theGetHistoricalQuoteData
methed (issue #11 is trying to resolve howGetHistoricalQuoteData
behaves when there's no valid quote bar for construction, but that would not resolve this issue, since this if-block will directly return null when we pass in a?SPY
instead of the individual contract symbols).From my observation and experiment, in live trading, this
GetHistory
method was invoked byOptionChain
method defined inLean/Algorithm/QCAlgorithm.cs
:namely the file-based universe selection process, which was introduced starting here: QuantConnect/Lean@16c4259 .
However, even then, it still tries to get the universe content via a network request, instead of retrieving directly from the universe csv file on that day, which should be the correct way if we are using Lean CLI to do live trading.
Expected Behavior
As long as we have built the Universe according to the sample universe format given by https://github.com/QuantConnect/Lean/blob/master/Data/option/usa/universes/spy/20240109.csv , we should be able to avoid network request for fetching the option chain from the data provider.
Currently it is doing extra work here (the local universe has been located and loaded before this redundant request), and it is doing it incorrectly.
Additionally, even if it is going to fetch the most recent market price for all the contracts, individual requests over 10k contracts are not acceptable (
SetFilter
right now is not working as expected in my experiment with ThetaData, I will investigate and open an issue for Lean main repository if it is involved), currently this plugin hasn't utilizedbulk
request to get whole universe data.However, this is not an urgent issue. Won't affect the behavior in live trading.
Some code snippets that might be helpful for debugging
Code:
Output:
The text was updated successfully, but these errors were encountered: