In one of my first posts, I showed how to pull the options chain using TD Ameritrade. This version will also get the options chain, but this time from Yahoo Finance. This is more of a webscraping solution.
A copy of the notebook can be found on my GitHub and the bulk of the code will be shown below.
The code below gets the tickers of the S&P 500 using Wikipedia. In addition, I provide an example of how additional tickers could also be used.
def get_tickers():
sp_df = pd.read_html('https://en.wikipedia.org/wiki/List_of_S%26P_500_companies')[0]
sp_tickers = list(sp_df['Symbol'])[:]
other_tickers = ['SPY','QQQ']
unique_tickers = sorted(list(set(sp_tickers + other_tickers)))
return unique_tickers
The code below gets for the ticker(s)
- The current price
- The options chain for the next 12 expiration dates. I set it to 12, but it can be any number (or even not set)
def get_current_price(ticker):
try:
stock_info = yf.Ticker(ticker).info
current_price = stock_info.get('ask') or stock_info.get('lastPrice')
if current_price is not None:
return current_price
else:
# If ask or lastPrice is not available, fetch today's closing price
ticker_object = yf.Ticker(ticker)
todays_data = ticker_object.history(period='1d')
if not todays_data.empty and 'Close' in todays_data.columns:
return todays_data['Close'][0]
else:
print(f"Warning: Unable to fetch current price for {ticker}.")
return None
except Exception as e:
print(f"Error fetching current price for {ticker}: {str(e)}")
return None
def get_options_chain(tickers):
all_data = []
for ticker in tickers:
print (ticker)
try:
# Get the next 12 available expiration dates
expiration_dates = yf.Ticker(ticker).options[:1]
for expiration_date in expiration_dates:
option_chain = yf.Ticker(ticker).option_chain(expiration_date)
call_data = option_chain.calls
put_data = option_chain.puts
# Extract relevant information for calls
call_info = call_data[['contractSymbol', 'strike', 'openInterest', 'volume', 'bid', 'ask']]
call_info['type'] = 'Call' # Add a column to specify call option
# Extract relevant information for puts
put_info = put_data[['contractSymbol', 'strike', 'openInterest', 'volume', 'bid', 'ask']]
put_info['type'] = 'Put' # Add a column to specify put option
# Combine call and put information
options_data = pd.concat([call_info, put_info], ignore_index=True)
# Add 'Ticker' and 'ExpirationDate' columns to the DataFrame
options_data['Ticker'] = ticker
options_data['ExpirationDate'] = expiration_date
# Fetch current price for the ticker
current_price = get_current_price(ticker)
if current_price is not None:
# Add current price to the DataFrame
options_data['CurrentPrice'] = current_price
all_data.append(options_data)
except Exception as e:
print(f"Error fetching options data for {ticker}: {str(e)}")
if all_data:
return pd.concat(all_data, ignore_index=True)
else:
return None
The code below takes all of the results from the above and saves the results as csv with a standardized name
def save_combined_data_to_csv(combined_data, folder_path='Options_Data'):
try:
os.makedirs(folder_path, exist_ok=True)
# Create a timestamp for the filename in military time format
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# If 'CurrentPrice' column exists, format it to have 4 decimal places
if 'CurrentPrice' in combined_data.columns:
combined_data['CurrentPrice'] = combined_data['CurrentPrice'].apply(lambda x: f'{x:.4f}' if pd.notna(x) else x)
# Save the DataFrame to a CSV file
filename = f"{folder_path}/combined_data_{timestamp}.csv"
combined_data.to_csv(filename, index=False)
print(f"Data saved to {filename}")
except Exception as e:
print(f"Error saving combined data to CSV: {str(e)}")
Putting it all together and running the code every hour
# Run
def run_hourly_job(tickers):
# Run the job immediately
options_chain_df = get_options_chain(tickers)
save_combined_data_to_csv(options_chain_df)
while True:
# Calculate the time until the next hour
current_time = datetime.now()
next_hour = current_time.replace(hour=current_time.hour + 1, minute=0, second=0, microsecond=0)
time_to_wait = (next_hour - current_time).total_seconds()
# Wait until the next hour
time.sleep(time_to_wait)
# Run the job
options_chain_df = get_options_chain(tickers)
save_combined_data_to_csv(options_chain_df)
# List of tickers to monitor
tickers_to_monitor = get_tickers()
# Run the hourly job
run_hourly_job(tickers_to_monitor)