I'm having a very hard time accessing SPX options using Quantconnect, and when they're accessed, they differ widely from other options databases.
These results are from using Quantconnect's Jupyter Notebook, on the cloud (research.ipynb):
Scenario (1) (Code and Output provided below), in summary: My attempt to retrieve SPX options data for January 1, 2021 to January 7, 2023. Result: The latest date with SPXW options is January 4, 2021. [BTW, “SPXW” may seem weird to use, but for Quantconnect, you must identify SPX as the underlying first, and then the weekly's, otherwise the weekly's will not be found. I learned this from their support.]
Scenario (2) (Code and Output provided below): My attempt to retrieve SPX options for June 30, 2022: Result: There are 7072 contracts, and no historical options data available for this date.
Scenario (3) (Code and Output provided below): My attempt to retrieve SPX options for January 4, 2021, which was supposed to be available: Result: There are 5667 contracts, and no historical options data available for this date.
Here are thoughts from ChatGPT's latest model, o1-preview (not yet publicly available), on the code and results :
[SPXW Options Data Availability
Index Options Data: QuantConnect's data coverage primarily includes options on equities (stock options) and ETFs, but it may have limited or no coverage for options on indexes like SPX or SPXW.
Data Provider Limitations: Providing historical data for index options like SPX and SPXW involves additional licensing and costs, which could limit its availability on QuantConnect.
Historical Data for SPX Options: As of now, QuantConnect does not provide historical options data for SPX or SPXW options. This limitation is due to the constraints in their data subscriptions and licensing agreements with data providers.]
============================================================
Scenario (1) (Code and Output below):
Code:
{
from datetime import datetime, timedelta
import pandas as pd
# Initialize QuantBook
qb = QuantBook()
# Define the date range to check
start_date = datetime(2021, 1, 1)
end_date = datetime(2023, 7, 1)
date_range = pd.date_range(start=start_date, end=end_date, freq='B') # 'B' for business days
# Add the SPX index symbol
spx = qb.AddIndex("SPX", Resolution.Minute).Symbol
# Create the canonical option symbol for SPXW options
spxw = Symbol.CreateCanonicalOption(spx, Market.USA, "?SPXW")
# Initialize a list to collect dates with available data
dates_with_data = []
print("Checking data availability for SPXW options over the date range...")
for date in date_range:
contracts = qb.OptionChainProvider.GetOptionContractList(spxw, date)
if contracts:
# Attempt to get historical data for one of the contracts
sample_contract = contracts[0]
history = qb.History(sample_contract, date, date + timedelta(days=1), Resolution.Daily)
if not history.empty:
print(f"Data available for SPXW options on {date.date()}")
dates_with_data.append(date)
# Break after finding the latest date with data
break
else:
print(f"No contracts retrieved for {date.date()}")
if not dates_with_data:
print("No dates with available SPXW options data were found in the specified range.")
else:
latest_date_with_data = dates_with_data[0]
print(f"\nLatest date with available SPXW options data: {latest_date_with_data.date()}")
}
Output:
{
Checking data availability for SPXW options over the date range...
Data available for SPXW options on 2021-01-04
Latest date with available SPXW options data: 2021-01-04
}
============================================================
Scenario (2) (Code and Output):
Code:
{
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
from scipy.stats import norm
# Initialize QuantBook
qb = QuantBook()
# Use the date with available data
analysis_date = datetime(2022, 6, 30) # Adjust this date based on data availability
# Set the algorithm's time to the analysis date
qb.SetDateTime(analysis_date)
# Add the SPX index symbol
spx = qb.AddIndex("SPX", Resolution.Minute).Symbol
# Create the canonical option symbol for SPXW options
spxw = Symbol.CreateCanonicalOption(spx, Market.USA, "?SPXW")
# Get the list of option contracts for SPXW on the specified date
contracts = qb.OptionChainProvider.GetOptionContractList(spxw, analysis_date)
# Filter contracts that have expiration dates after analysis_date
contracts = [c for c in contracts if c.ID.Date.date() >= analysis_date.date()]
print(f"Total contracts retrieved: {len(contracts)}")
# Filter for calls with strike prices between 3490 and 4240
call_contracts = [
c for c in contracts
if c.ID.OptionRight == OptionRight.Call and 3490 <= c.ID.StrikePrice <= 4240
]
# Filter for puts with strike prices between 4030 and 4240
put_contracts = [
c for c in contracts
if c.ID.OptionRight == OptionRight.Put and 4030 <= c.ID.StrikePrice <= 4240
]
# Combine the filtered contracts
selected_contracts = call_contracts + put_contracts
# Verify data availability for each contract
available_contracts = []
for contract in selected_contracts:
history = qb.History(contract, analysis_date, analysis_date + timedelta(days=1), Resolution.Daily)
if not history.empty:
available_contracts.append(contract)
else:
print(f"No historical data found for {contract.Value} on {analysis_date.date()}.")
selected_contracts = available_contracts
# If no contracts have data, exit
if not selected_contracts:
print("No contracts with available data were found.")
else:
# Add the selected contracts to the data feed
for contract in selected_contracts:
qb.AddOptionContract(contract, Resolution.Minute)
# Get historical data for the selected contracts and underlying on the specified date
start_time = analysis_date
end_time = analysis_date + timedelta(days=1)
# Get history for options
history_options = qb.History(selected_contracts, start_time, end_time, Resolution.Daily)
# Get history for underlying
history_underlying = qb.History(spx, start_time, end_time, Resolution.Daily)
# Get the underlying price
if not history_underlying.empty:
underlying_price = history_underlying['close'].iloc[-1]
else:
raise Exception("Underlying price data not found.")
# Define functions to compute Greeks using Black-Scholes model
def calculate_greeks(option_type, S, K, T, r, sigma):
d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
if option_type == 'Call':
delta = norm.cdf(d1)
theta = (- (S * sigma * norm.pdf(d1)) / (2 * np.sqrt(T))
- r * K * np.exp(-r * T) * norm.cdf(d2)) / 365
elif option_type == 'Put':
delta = norm.cdf(d1) - 1
theta = (- (S * sigma * norm.pdf(d1)) / (2 * np.sqrt(T))
+ r * K * np.exp(-r * T) * norm.cdf(-d2)) / 365
else:
delta = None
theta = None
gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
vega = S * norm.pdf(d1) * np.sqrt(T) / 100 # Vega per 1% change in volatility
return delta, gamma, vega, theta
# Initialize a list to collect the data
data_list = []
# Risk-free rate (annualized)
risk_free_rate = 0.02 # Adjust this rate based on the analysis date
for contract in selected_contracts:
symbol_value = contract.Value
if symbol_value in history_options.index.get_level_values(0):
df_option = history_options.loc[symbol_value].reset_index()
else:
print(f"No data available for {symbol_value} on {analysis_date.date()}.")
continue
if not df_option.empty:
last_data = df_option.iloc[-1]
# Time to expiration in years
T = (contract.ID.Date.date() - analysis_date.date()).days / 365.0
if T <= 0:
continue # Skip expired options
# Option price
option_price = last_data['close']
# Implied volatility
implied_volatility = last_data['impliedvolatility'] if 'impliedvolatility' in last_data else None
if implied_volatility is None or np.isnan(implied_volatility):
implied_volatility = 0.2 # Assume 20% if not available; adjust as needed
sigma = implied_volatility
# Compute Greeks
option_type = 'Call' if contract.ID.OptionRight == OptionRight.Call else 'Put'
delta, gamma, vega, theta = calculate_greeks(
option_type, underlying_price, contract.ID.StrikePrice, T, risk_free_rate, sigma)
data_list.append({
'Contract': symbol_value,
'Strike': contract.ID.StrikePrice,
'OptionRight': option_type,
'Expiration': contract.ID.Date.date(),
'Last Price': option_price,
'Volume': last_data['volume'] if 'volume' in last_data else None,
'Open Interest': last_data['openinterest'] if 'openinterest' in last_data else None,
'Implied Volatility': sigma,
'Delta': delta,
'Gamma': gamma,
'Vega': vega,
'Theta': theta,
})
else:
print(f"No data available for {symbol_value} on {analysis_date.date()}.")
# Convert to DataFrame
df_options = pd.DataFrame(data_list)
# Check if DataFrame is empty before sorting
if not df_options.empty:
df_options = df_options.sort_values(by=['OptionRight', 'Strike'])
print(df_options)
else:
print("No data available to display.")
}
Output:
{
Total contracts retrieved: 7072
No historical data found for SPX 220715C03490000 on 2022-06-30.
No historical data found for SPX 220819C03490000 on 2022-06-30.
No historical data found for SPX 220916C03490000 on 2022-06-30.
No historical data found for SPX 221021C03490000 on 2022-06-30.
No historical data found for SPX 221118C03490000 on 2022-06-30.
No historical data found for SPX 221216C03490000 on 2022-06-30.
No historical data found for SPX 220715C03495000 on 2022-06-30.
No historical data found for SPX 220819C03495000 on 2022-06-30.
No historical data found for SPX 220916C03495000 on 2022-06-30.
No historical data found for SPX 221021C03495000 on 2022-06-30.
No historical data found for SPX 220715C03500000 on 2022-06-30.
No historical data found for SPX 220819C03500000 on 2022-06-30.
No historical data found for SPX 220916C03500000 on 2022-06-30.
No historical data found for SPX 221021C03500000 on 2022-06-30.
No historical data found for SPX 221118C03500000 on 2022-06-30.
No historical data found for SPX 221216C03500000 on 2022-06-30.
No historical data found for SPX 230120C03500000 on 2022-06-30.
No historical data found for SPX 230217C03500000 on 2022-06-30.
No historical data found for SPX 230317C03500000 on 2022-06-30.
No historical data found for SPX 230421C03500000 on 2022-06-30.
No historical data found for SPX 230519C03500000 on 2022-06-30.
No historical data found for SPX 230616C03500000 on 2022-06-30.
No historical data found for SPX 230721C03500000 on 2022-06-30.
No historical data found for SPX 230915C03500000 on 2022-06-30.
...
No historical data found for SPX 220916P04240000 on 2022-06-30.
No historical data found for SPX 221021P04240000 on 2022-06-30.
No historical data found for SPX 221118P04240000 on 2022-06-30.
No contracts with available data were found.
}
============================================================
Scenario (3) (Code and Output below):
Code:
{
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
from scipy.stats import norm
# Initialize QuantBook
qb = QuantBook()
# Use the latest date with available data
analysis_date = datetime(2021, 1, 4)
# Set the algorithm's time to the analysis date
qb.SetDateTime(analysis_date)
# Add the SPX index symbol
spx = qb.AddIndex("SPX", Resolution.Minute).Symbol
# Create the canonical option symbol for SPXW options
spxw = Symbol.CreateCanonicalOption(spx, Market.USA, "?SPXW")
# Get the list of option contracts for SPXW on the specified date
contracts = qb.OptionChainProvider.GetOptionContractList(spxw, analysis_date)
# Filter contracts that have expiration dates after analysis_date
contracts = [c for c in contracts if c.ID.Date.date() >= analysis_date.date()]
print(f"Total contracts retrieved: {len(contracts)}")
# Filter for calls with strike prices between 3490 and 4240
call_contracts = [
c for c in contracts
if c.ID.OptionRight == OptionRight.Call and 3490 <= c.ID.StrikePrice <= 4240
]
# Filter for puts with strike prices between 4030 and 4240
put_contracts = [
c for c in contracts
if c.ID.OptionRight == OptionRight.Put and 4030 <= c.ID.StrikePrice <= 4240
]
# Combine the filtered contracts
selected_contracts = call_contracts + put_contracts
# Verify data availability for each contract
available_contracts = []
for contract in selected_contracts:
history = qb.History(contract, analysis_date, analysis_date + timedelta(days=1), Resolution.Daily)
if not history.empty:
available_contracts.append(contract)
else:
print(f"No historical data found for {contract.Value} on {analysis_date.date()}.")
selected_contracts = available_contracts
# If no contracts have data, exit
if not selected_contracts:
print("No contracts with available data were found.")
else:
# Add the selected contracts to the data feed
for contract in selected_contracts:
qb.AddOptionContract(contract, Resolution.Minute)
# Get historical data for the selected contracts and underlying on the specified date
start_time = analysis_date
end_time = analysis_date + timedelta(days=1)
# Get history for options
history_options = qb.History(selected_contracts, start_time, end_time, Resolution.Daily)
# Get history for underlying
history_underlying = qb.History(spx, start_time, end_time, Resolution.Daily)
# Get the underlying price
if not history_underlying.empty:
underlying_price = history_underlying['close'].iloc[-1]
else:
raise Exception("Underlying price data not found.")
# Define functions to compute Greeks using Black-Scholes model
def calculate_greeks(option_type, S, K, T, r, sigma):
d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
if option_type == 'Call':
delta = norm.cdf(d1)
theta = (- (S * sigma * norm.pdf(d1)) / (2 * np.sqrt(T))
- r * K * np.exp(-r * T) * norm.cdf(d2)) / 365
elif option_type == 'Put':
delta = norm.cdf(d1) - 1
theta = (- (S * sigma * norm.pdf(d1)) / (2 * np.sqrt(T))
+ r * K * np.exp(-r * T) * norm.cdf(-d2)) / 365
else:
delta = None
theta = None
gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T))
vega = S * norm.pdf(d1) * np.sqrt(T) / 100 # Vega per 1% change in volatility
return delta, gamma, vega, theta
# Initialize a list to collect the data
data_list = []
# Risk-free rate (annualized)
risk_free_rate = 0.01 # Adjust this rate based on the analysis date
for contract in selected_contracts:
symbol_value = contract.Value
if symbol_value in history_options.index.get_level_values(0):
df_option = history_options.loc[symbol_value].reset_index()
else:
print(f"No data available for {symbol_value} on {analysis_date.date()}.")
continue
if not df_option.empty:
last_data = df_option.iloc[-1]
# Time to expiration in years
T = (contract.ID.Date.date() - analysis_date.date()).days / 365.0
if T <= 0:
continue # Skip expired options
# Option price
option_price = last_data['close']
# Implied volatility
implied_volatility = last_data['impliedvolatility'] if 'impliedvolatility' in last_data else None
if implied_volatility is None or np.isnan(implied_volatility):
implied_volatility = 0.2 # Assume 20% if not available; adjust as needed
sigma = implied_volatility
# Compute Greeks
option_type = 'Call' if contract.ID.OptionRight == OptionRight.Call else 'Put'
delta, gamma, vega, theta = calculate_greeks(
option_type, underlying_price, contract.ID.StrikePrice, T, risk_free_rate, sigma)
data_list.append({
'Contract': symbol_value,
'Strike': contract.ID.StrikePrice,
'OptionRight': option_type,
'Expiration': contract.ID.Date.date(),
'Last Price': option_price,
'Volume': last_data['volume'] if 'volume' in last_data else None,
'Open Interest': last_data['openinterest'] if 'openinterest' in last_data else None,
'Implied Volatility': sigma,
'Delta': delta,
'Gamma': gamma,
'Vega': vega,
'Theta': theta,
})
else:
print(f"No data available for {symbol_value} on {analysis_date.date()}.")
# Convert to DataFrame
df_options = pd.DataFrame(data_list)
# Check if DataFrame is empty before sorting
if not df_options.empty:
df_options = df_options.sort_values(by=['OptionRight', 'Strike'])
print(df_options)
else:
print("No data available to display.")
}
Output:
{
Total contracts retrieved: 5667
No historical data found for SPX 210115C03490000 on 2021-01-04.
No historical data found for SPX 210219C03490000 on 2021-01-04.
No historical data found for SPX 210319C03490000 on 2021-01-04.
No historical data found for SPX 210416C03490000 on 2021-01-04.
No historical data found for SPX 210521C03490000 on 2021-01-04.
No historical data found for SPX 210618C03490000 on 2021-01-04.
No historical data found for SPX 210115C03495000 on 2021-01-04.
No historical data found for SPX 210219C03495000 on 2021-01-04.
No historical data found for SPX 210319C03495000 on 2021-01-04.
No historical data found for SPX 210115C03500000 on 2021-01-04.
No historical data found for SPX 210219C03500000 on 2021-01-04.
No historical data found for SPX 210319C03500000 on 2021-01-04.
No historical data found for SPX 210416C03500000 on 2021-01-04.
No historical data found for SPX 210521C03500000 on 2021-01-04.
No historical data found for SPX 210618C03500000 on 2021-01-04.
No historical data found for SPX 210917C03500000 on 2021-01-04.
No historical data found for SPX 211217C03500000 on 2021-01-04.
No historical data found for SPX 220121C03500000 on 2021-01-04.
No historical data found for SPX 220318C03500000 on 2021-01-04.
No historical data found for SPX 220617C03500000 on 2021-01-04.
No historical data found for SPX 221216C03500000 on 2021-01-04.
No historical data found for SPX 231215C03500000 on 2021-01-04.
No historical data found for SPX 210115C03505000 on 2021-01-04.
No historical data found for SPX 210219C03505000 on 2021-01-04.
...
No historical data found for SPX 220617P04225000 on 2021-01-04.
No historical data found for SPX 221216P04225000 on 2021-01-04.
No historical data found for SPX 231215P04225000 on 2021-01-04.
No contracts with available data were found.
}
Mia Alissi
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.
Cameron Tousi
The output from Quantconnect's AI, Mia Alissi, is provided below. Please note that it's the same as one of the outputs I provided above:
{Checking data availability for SPXW options over the date range...Data available for SPXW options on 2021-01-04
Latest date with available SPXW options data: 2021-01-04}
Cameron Tousi
Please refer to an update of this post here:
Louis Szeto
Hi Cameron
If you set the “StartDate” and request the historical data later, it will return nothing to avoid look-ahead bias. Instead, request the historical data before the “StartDate”. For how to subscribe and request index option data in research notebook, please refer to this docs.
Best
Louis
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.
Cameron Tousi
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!