diff --git a/Algorithm.CSharp/OptionChainsMultipleFullDataRegressionAlgorithm.cs b/Algorithm.CSharp/OptionChainsMultipleFullDataRegressionAlgorithm.cs
new file mode 100644
index 000000000000..b457a6662eb4
--- /dev/null
+++ b/Algorithm.CSharp/OptionChainsMultipleFullDataRegressionAlgorithm.cs
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ *
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using QuantConnect.Data;
+using QuantConnect.Data.Market;
+using QuantConnect.Interfaces;
+using QuantConnect.Securities;
+
+namespace QuantConnect.Algorithm.CSharp
+{
+ ///
+ /// Regression algorithm illustrating the usage of the method
+ /// to get multiple option chains, which contains additional data besides the symbols, including prices, implied volatility and greeks.
+ /// It also shows how this data can be used to filter the contracts based on certain criteria.
+ ///
+ public class OptionChainsMultipleFullDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
+ {
+ private Symbol _googOptionContract;
+ private Symbol _spxOptionContract;
+
+ public override void Initialize()
+ {
+ SetStartDate(2015, 12, 24);
+ SetEndDate(2015, 12, 24);
+ SetCash(100000);
+
+ var goog = AddEquity("GOOG").Symbol;
+ var spx = AddIndex("SPX").Symbol;
+
+ var chains = OptionChains(new[] { goog, spx });
+
+ _googOptionContract = GetContract(chains, goog, TimeSpan.FromDays(10));
+ _spxOptionContract = GetContract(chains, spx, TimeSpan.FromDays(60));
+
+ AddOptionContract(_googOptionContract);
+ AddIndexOptionContract(_spxOptionContract);
+ }
+
+ private Symbol GetContract(OptionChains chains, Symbol underlying, TimeSpan expirySpan)
+ {
+ return chains
+ .Where(kvp => kvp.Key.Underlying == underlying)
+ .Select(kvp => kvp.Value)
+ .Single()
+ // Get contracts expiring within a given span, with an implied volatility greater than 0.5 and a delta less than 0.5
+ .Where(contractData => contractData.Symbol.ID.Date - Time <= expirySpan &&
+ contractData.ImpliedVolatility > 0.5m &&
+ contractData.Delta < 0.5m)
+ // Get the contract with the latest expiration date
+ .OrderByDescending(x => x.Symbol.ID.Date)
+ .First()
+ .Symbol;
+ }
+
+ public override void OnData(Slice slice)
+ {
+ // Do some trading with the selected contract for sample purposes
+ if (!Portfolio.Invested)
+ {
+ MarketOrder(_googOptionContract, 1);
+ }
+ else
+ {
+ Liquidate();
+ }
+ }
+
+ ///
+ /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
+ ///
+ public bool CanRunLocally { get; } = true;
+
+ ///
+ /// This is used by the regression test system to indicate which languages this algorithm is written in.
+ ///
+ public virtual List Languages { get; } = new() { Language.CSharp, Language.Python };
+
+ ///
+ /// Data Points count of all timeslices of algorithm
+ ///
+ public long DataPoints => 1059;
+
+ ///
+ /// Data Points count of the algorithm history
+ ///
+ public int AlgorithmHistoryDataPoints => 2;
+
+ ///
+ /// Final status of the algorithm
+ ///
+ public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
+
+ ///
+ /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
+ ///
+ public Dictionary ExpectedStatistics => new Dictionary
+ {
+ {"Total Orders", "210"},
+ {"Average Win", "0%"},
+ {"Average Loss", "0%"},
+ {"Compounding Annual Return", "0%"},
+ {"Drawdown", "0%"},
+ {"Expectancy", "0"},
+ {"Start Equity", "100000"},
+ {"End Equity", "96041"},
+ {"Net Profit", "0%"},
+ {"Sharpe Ratio", "0"},
+ {"Sortino Ratio", "0"},
+ {"Probabilistic Sharpe Ratio", "0%"},
+ {"Loss Rate", "0%"},
+ {"Win Rate", "0%"},
+ {"Profit-Loss Ratio", "0"},
+ {"Alpha", "0"},
+ {"Beta", "0"},
+ {"Annual Standard Deviation", "0"},
+ {"Annual Variance", "0"},
+ {"Information Ratio", "0"},
+ {"Tracking Error", "0"},
+ {"Treynor Ratio", "0"},
+ {"Total Fees", "$209.00"},
+ {"Estimated Strategy Capacity", "$0"},
+ {"Lowest Capacity Asset", "GOOCV W6U7PD1F2WYU|GOOCV VP83T1ZUHROL"},
+ {"Portfolio Turnover", "85.46%"},
+ {"OrderListHash", "a7ab1a9e64fe9ba76ea33a40a78a4e3b"}
+ };
+ }
+}
diff --git a/Algorithm.CSharp/OptionPriceModelForOptionStylesBaseRegressionAlgorithm.cs b/Algorithm.CSharp/OptionPriceModelForOptionStylesBaseRegressionAlgorithm.cs
index 37852f18056a..2157c9a7316e 100644
--- a/Algorithm.CSharp/OptionPriceModelForOptionStylesBaseRegressionAlgorithm.cs
+++ b/Algorithm.CSharp/OptionPriceModelForOptionStylesBaseRegressionAlgorithm.cs
@@ -110,12 +110,8 @@ public void CheckGreeks(OptionChain contracts)
// Greeks should be valid if they were successfuly accessed for supported option style
if (_optionStyleIsSupported)
{
- if (greeks == null)
- {
- greeks = new ModeledGreeks();
- }
-
- if (greeks.Delta == 0m && greeks.Gamma == 0m && greeks.Theta == 0m && greeks.Vega == 0m && greeks.Rho == 0m)
+ if (greeks == null ||
+ (greeks.Delta == 0m && greeks.Gamma == 0m && greeks.Theta == 0m && greeks.Vega == 0m && greeks.Rho == 0m))
{
throw new RegressionTestException($"Expected greeks to not be zero simultaneously for {contract.Symbol.Value}, an {_option.Style} style option, using {_option?.PriceModel.GetType().Name}, but they were");
}
diff --git a/Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py b/Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py
index d230e517f7dd..53c5fb1a310e 100644
--- a/Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py
+++ b/Algorithm.Python/OptionChainFullDataRegressionAlgorithm.py
@@ -12,6 +12,7 @@
# limitations under the License.
from AlgorithmImports import *
+from datetime import timedelta
###
### Regression algorithm illustrating the usage of the method
@@ -30,19 +31,13 @@ def initialize(self):
option_chain = self.option_chain(goog)
# Demonstration using data frame:
+ df = option_chain.data_frame
# Get contracts expiring within 10 days, with an implied volatility greater than 0.5 and a delta less than 0.5
- contracts = [
- symbol
- # Index is a tuple and the first element is the symbol
- for (symbol,), contract_data in option_chain.data_frame.iterrows()
- if symbol.id.date - self.time <= timedelta(days=10) and contract_data["impliedvolatility"] > 0.5 and contract_data["delta"] < 0.5
- ]
-
- # Get the contract with the latest expiration date
- option_contract = sorted(contracts, key=lambda x: x.id.date, reverse=True)[0]
-
- # Can use the symbol instance to index the data frame
- self.debug(f"Option contract data:\n{option_chain.data_frame.loc[(option_contract)]}")
+ contracts = df.loc[(df['expiry'] <= self.time + timedelta(days=10)) & (df['impliedvolatility'] > 0.5) & (df['delta'] < 0.5)]
+
+ # Get the contract with the latest expiration date.
+ # Note: the result of df.loc[] is a series, and its name is a tuple with a single element (contract symbol)
+ option_contract = contracts.loc[contracts['expiry'].idxmax()].name[0]
self._option_contract = self.add_option_contract(option_contract)
diff --git a/Algorithm.Python/OptionChainsMultipleFullDataRegressionAlgorithm.py b/Algorithm.Python/OptionChainsMultipleFullDataRegressionAlgorithm.py
new file mode 100644
index 000000000000..8cf04c969a91
--- /dev/null
+++ b/Algorithm.Python/OptionChainsMultipleFullDataRegressionAlgorithm.py
@@ -0,0 +1,62 @@
+# 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 AlgorithmImports import *
+from datetime import timedelta
+
+###
+### Regression algorithm illustrating the usage of the method
+### to get multiple option chains, which contains additional data besides the symbols, including prices, implied volatility and greeks.
+### It also shows how this data can be used to filter the contracts based on certain criteria.
+###
+class OptionChainsMultipleFullDataRegressionAlgorithm(QCAlgorithm):
+
+ def initialize(self):
+ self.set_start_date(2015, 12, 24)
+ self.set_end_date(2015, 12, 24)
+ self.set_cash(100000)
+
+ goog = self.add_equity("GOOG").symbol
+ spx = self.add_index("SPX").symbol
+
+ chains = self.option_chains([goog, spx])
+
+ self._goog_option_contract = self.get_contract(chains, goog, timedelta(days=10))
+ self._spx_option_contract = self.get_contract(chains, spx, timedelta(days=60))
+
+ self.add_option_contract(self._goog_option_contract)
+ self.add_index_option_contract(self._spx_option_contract)
+
+ def get_contract(self, chains: OptionChains, underlying: Symbol, expiry_span: timedelta) -> Symbol:
+ df = chains.data_frame
+
+ # Index by the requested underlying, by getting all data with canonicals which underlying is the requested underlying symbol:
+ canonicals = df.index.get_level_values('canonical')
+ condition = [canonical for canonical in canonicals if getattr(canonical, 'underlying') == underlying]
+ df = df.loc[condition]
+
+ # Get contracts expiring in the next 10 days with an implied volatility greater than 0.5 and a delta less than 0.5
+ contracts = df.loc[(df['expiry'] <= self.time + expiry_span) & (df['impliedvolatility'] > 0.5) & (df['delta'] < 0.5)]
+
+ # Select the contract with the latest expiry date
+ contracts.sort_values(by='expiry', ascending=False, inplace=True)
+
+ # Get the symbol: the resulting series name is a tuple (canonical symbol, contract symbol)
+ return contracts.iloc[0].name[1]
+
+ def on_data(self, data):
+ # Do some trading with the selected contract for sample purposes
+ if not self.portfolio.invested:
+ self.market_order(self._goog_option_contract, 1)
+ else:
+ self.liquidate()
diff --git a/Algorithm/QCAlgorithm.Python.cs b/Algorithm/QCAlgorithm.Python.cs
index dbca952c68b3..607f42ad8f0f 100644
--- a/Algorithm/QCAlgorithm.Python.cs
+++ b/Algorithm/QCAlgorithm.Python.cs
@@ -1638,6 +1638,21 @@ public void AddCommand(PyObject type)
};
}
+
+ ///
+ /// Get the option chains for the specified symbols at the current time ()
+ ///
+ ///
+ /// The symbols for which the option chain is asked for.
+ /// It can be either the canonical options or the underlying symbols.
+ ///
+ /// The option chains
+ [DocumentationAttribute(AddingData)]
+ public OptionChains OptionChains(PyObject symbols)
+ {
+ return OptionChains(symbols.ConvertToSymbolEnumerable());
+ }
+
///
/// Gets indicator base type
///
diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs
index 31c1a8d12098..1741ea787858 100644
--- a/Algorithm/QCAlgorithm.cs
+++ b/Algorithm/QCAlgorithm.cs
@@ -3382,8 +3382,7 @@ public OptionChains OptionChains(IEnumerable symbols)
var optionCanonicalSymbols = canonicalSymbols.Where(x => x.SecurityType != SecurityType.FutureOption);
var futureOptionCanonicalSymbols = canonicalSymbols.Where(x => x.SecurityType == SecurityType.FutureOption);
- // TODO: Resolution.Daily should not be necessary. Remove when GH#8343 is resolved
- var optionChainsData = History(optionCanonicalSymbols, 1, Resolution.Daily).GetUniverseData()
+ var optionChainsData = History(optionCanonicalSymbols, 1).GetUniverseData()
.Select(x => (x.Keys.Single(), x.Values.Single().Cast()));
// TODO: For FOPs, we fall back to the option chain provider until OptionUniverse supports them
@@ -3411,20 +3410,6 @@ public OptionChains OptionChains(IEnumerable symbols)
return chains;
}
- ///
- /// Get the option chains for the specified symbols at the current time ()
- ///
- ///
- /// The symbols for which the option chain is asked for.
- /// It can be either the canonical options or the underlying symbols.
- ///
- /// The option chains
- [DocumentationAttribute(AddingData)]
- public OptionChains OptionChains(PyObject symbols)
- {
- return OptionChains(symbols.ConvertToSymbolEnumerable());
- }
-
///
/// Register a command type to be used
///