In reference to my earlier post: “Challenges Accessing SPX Options,” after much tinkering, I've gotten options information.
Apologies about starting a new post, but doing a “reply” on that discussion would have made both the text and code attached very messy and nearly impossible to read.
There were real hurdles to printing a complete list. So you may want to copy this code straight away!
For the arbitrary date of June 9, 2023, with a strike range of 100 from ATM, and at 10 am US Eastern time (minute basis used): This code will print a separate table for each expiration date. Each table will include relevant option information (i.e., Contract, Strike, OptionRight, Expiration, Last Price, Volume, Open Interest, Greeks).
There are 3 new challenges involving the data:
(1) The first challenge is that nearly all “Volume” information is “NaN” and all “Open Interest” information is “None.” In other words, it's missing. Is it the case perhaps that only daily volume information is available?
(2) Many expiration dates are are simply missing. For instance, the first expiry is June 16, 2023. The expiration dates of June 12, June 13, June 14 and June 15 are missing, as are many many others.
(3) Except for the implied volatility, most of the data does not match what I'm viewing from other options sources generally considered reliable, such as OptionsNet Explorer, Options Omega and others. Quantconnect's data is from Algoseek, which generally has a solid reputation, so I'm not sure what is wrong.
I'd love to hear from Quantconnect personnel and others. Also, if you're interested in advanced options and related trading, please feel free to comment and reach out to me.
Code:
from datetime import datetime, timedelta
import pandas as pd
import numpy as np
from scipy.stats import norm
from IPython.display import display
# Initialize QuantBook
qb = QuantBook()
# Define the analysis datetime (10 AM ET on June 9, 2023)
analysis_datetime = datetime(2023, 6, 9, 14, 0, 0) # 10 AM ET is 14:00 UTC
# Add the SPX index symbol
spx = qb.AddIndex("SPX", Resolution.Minute).Symbol
# Use the OptionChainProvider to get the list of option contracts
contracts = qb.OptionChainProvider.GetOptionContractList(spx, analysis_datetime)
if not contracts:
print(f"No option contracts found for SPX on {analysis_datetime.date()}")
else:
# Retrieve SPX price at the analysis datetime
spx_price_data = qb.History(spx, analysis_datetime - timedelta(minutes=1), analysis_datetime, Resolution.Minute)
if not spx_price_data.empty:
spx_price = spx_price_data['close'].iloc[-1]
print(f"SPX price at {analysis_datetime.strftime('%Y-%m-%d %H:%M:%S')} UTC: {spx_price}")
else:
raise Exception("SPX price data not available.")
# Set strike price ranges around the SPX price
strike_range = 100 # Adjust as needed
lower_strike = int(spx_price - strike_range)
upper_strike = int(spx_price + strike_range)
# Filter contracts based on strike price and expiration date
selected_contracts = [contract for contract in contracts
if (lower_strike <= contract.ID.StrikePrice <= upper_strike)
and (contract.ID.Date.date() >= analysis_datetime.date())
]
print(f"Total selected contracts: {len(selected_contracts)}")
# Initialize a list to collect the data
data_list = []
risk_free_rate = 0.05 # Adjust this rate as needed
# Define the function to compute Greeks using the 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 = gamma = vega = theta = None
return delta, gamma, vega, theta
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
# Loop through the selected contracts
for contract in selected_contracts:
symbol = contract
# Retrieve historical data for the contract up to 10 AM
history = qb.History(symbol, analysis_datetime - timedelta(minutes=1), analysis_datetime, Resolution.Minute)
if history.empty:
print(f"No data available for {symbol} at {analysis_datetime}")
continue
# Time to expiration in years
T = (symbol.ID.Date - analysis_datetime).total_seconds() / (365 * 24 * 60 * 60)
if T <= 0:
continue # Skip expired options
# Option price
option_price = history['close'].iloc[-1]
# Implied volatility (Assuming a value, as it's not readily available)
implied_volatility = 0.2 # Adjust as needed based on market conditions
sigma = implied_volatility
# Volume
volume = history['volume'].iloc[-1] if 'volume' in history.columns else None
# Open Interest
open_interest = history['openinterest'].iloc[-1] if 'openinterest' in history.columns else None
# Compute Greeks
option_type = 'Call' if symbol.ID.OptionRight == OptionRight.Call else 'Put'
# Calculate Greeks using the Black-Scholes formula
delta, gamma, vega, theta = calculate_greeks(
option_type, spx_price, symbol.ID.StrikePrice, T, risk_free_rate, sigma)
data_list.append({
'Contract': symbol.Value,
'Strike': symbol.ID.StrikePrice,
'OptionRight': option_type,
'Expiration': symbol.ID.Date.date(),
'Last Price': option_price,
'Implied Volatility': sigma,
'Volume': volume,
'Open Interest': open_interest,
'Delta': delta,
'Gamma': gamma,
'Vega': vega,
'Theta': theta,
})
# Convert to DataFrame
df_options = pd.DataFrame(data_list)
# Set Pandas display options to show all rows and columns
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
# Check if DataFrame is empty before sorting
if not df_options.empty:
df_options = df_options.sort_values(by=['OptionRight', 'Strike'])
# Group by Expiration date and create separate tables
grouped = df_options.groupby('Expiration')
for expiration_date, group_df in grouped:
print(f"\nOptions expiring on {expiration_date}:")
display(group_df)
else:
print("No data available to display.")
Mia Alissi
Here are some insights and solutions to the challenges you're facing with the SPX options data:
Volume and Open Interest Data:
OptionContract
class in QuantConnect has properties forvolume
andopen_interest
. However, these values might not always be available or accurate due to data provider limitations or the specific market conditions at the time of the query. It's possible that daily volume information is more readily available than intraday data. If you are seeingNaN
orNone
, it might be due to the lack of intraday data for these metrics.Missing Expiration Dates:
CMEOptionsTradeDatesAndExpiration
class provides a list of expiration dates for future options. If certain expiration dates are missing, it could be due to the data provider not having that information or the specific options not being traded on those dates. Ensure that your data request covers all possible expiration dates and that the data provider supports them.Data Discrepancies:
Recommendations:
Example Usage:
OptionChainProvider
to get a list of option contracts and manually filter them based on your criteria. This allows you to handle cases where certain data might not be available or accurate.If you continue to experience issues, consider reaching out to QuantConnect support or the community forums for more specific guidance.
Sources: Option Chain Provider Algorithm, Index Option Call I T M Greeks Expiry Regression Algorithm, Greeks and IV Implementation, Basic Template S P X Weekly Index Options Algorithm, Option Open Interest Regression Algorithm
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.
Alexandre Catarino
Hi Cameron Tousi ,
The expirations are missing because you are expecting weeklies, so you should use SPXW symbol:
“NaN” Volume means no trades for a given bar.
We receive one open interest tick/bar daily and all other periods will have NaN.
Best regards,
Alex
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!