Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_expiration_dates returns empty strings instead of dates #116

Open
Rahul-Holla opened this issue Apr 15, 2024 · 11 comments
Open

get_expiration_dates returns empty strings instead of dates #116

Rahul-Holla opened this issue Apr 15, 2024 · 11 comments

Comments

@Rahul-Holla
Copy link

from yahoo_fin import options

option_expiry_dates = options.get_expiration_dates(ticker)
print(option_expiry_dates)
nearest_expiry = option_expiry_dates[0]
call_df = options.get_calls(ticker, nearest_expiry)
put_df = options.get_puts(ticker, nearest_expiry)

I get this error:

[
0:"
"
]
Error due to Unable to parse datetime string:

I am using yahoo-fin - Version: 0.8.9.1.
I am getting the same error for all the stock tickers and facing them from yesterday (15-04-2024). Previously, it was working fine.

@onlypencil
Copy link

Im having the same issue with the same error

@adadoucette
Copy link

Also having the same issue. Returning ['\n']. Also, the "get_options_chain()" command doesn't work either.

@benoitdr
Copy link

Same here, also get_stats() doesn't work anymore.
Probably due to the new yahoo!finance web interface

@benoitdr
Copy link

Switching to yfinance ...

@onlypencil
Copy link

I got it working but had to make my own functions based on their code

@Rahul-Holla
Copy link
Author

@onlypencil can you please share it here so I can have a look as well. Much appreciated!

@onlypencil
Copy link

onlypencil commented May 2, 2024

@onlypencil can you please share it here so I can have a look as well. Much appreciated!

this worked for me and its still working for what i need it.

from bs4 import BeautifulSoup
try:
    from requests_html import HTMLSession
except Exception:
    pass
def force_float(elt):

    try:
        return float(elt)
    except:
        return elt
def get_calls(ticker, date = None):

    """Extracts call option table for input ticker and expiration date

       @param: ticker
       @param: date"""

    options_chain = get_options_chain(ticker, date)

    return options_chain["calls"]



def get_puts(ticker, date = None):

    """Extracts put option table for input ticker and expiration date

       @param: ticker
       @param: date"""

    options_chain = get_options_chain(ticker, date)

    return options_chain["puts"]
def get_expiration_dates(ticker):
    """Scrapes the expiration dates from each option chain for input ticker.

    @param ticker: str - Ticker symbol of the stock/options
    @return: list of expiration dates as strings
    """

    try:
        # Assuming build_options_url is a function that constructs the URL for the options page
        site = build_options_url(ticker)

        # Create a session and fetch the page
        session = HTMLSession()
        response = session.get(site)
        response.raise_for_status()  # Raises an HTTPError for bad responses

        # Parse the HTML
        soup = BeautifulSoup(response.content, 'html.parser')
        options = soup.select('div.itm')  # Adjust selector based on actual HTML structure

        # Extract dates from each option element
        dates = [option.text.strip() for option in options if option.text.strip() != '']

        return dates

    except Exception as e:
        print(f"An error occurred: {e}")
        return []

    finally:
        session.close()

def build_options_url(ticker, date = None):

    """Constructs the URL pointing to options chain"""

    url = "https://finance.yahoo.com/quote/" + ticker + "/options?p=" + ticker

    if date is not None:
        url = url + "&date=" + str(int(pd.Timestamp(date).timestamp()))

    return url

def get_options_chain(ticker, date=None, raw=True, headers=None):
    """
    Fetch options data for the given ticker and expiration date.
    @param ticker: str - Stock ticker symbol
    @param date: str - Optional, the expiration date to retrieve options for
    @param raw: bool - If False, format certain columns of data
    @param headers: dict - HTTP headers to use for the request
    @return: dict - Dictionary containing DataFrame for calls and puts
    """
    if headers is None:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
        }

    try:
        # Building URL for options data
        site = build_options_url(ticker, date)

        # Fetch the HTML and parse it using BeautifulSoup
        response = requests.get(site, headers=headers)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')

        # Locate tables by class name
        tables = soup.find_all('table')

        # Convert found tables to DataFrames
        dataframes = [pd.read_html(str(table)) for table in tables]

        # Handling the presence of either one or two tables
        if len(dataframes) == 1:
            calls = dataframes[0][0].copy()
            puts = pd.DataFrame(columns=calls.columns)
        elif len(dataframes) > 1:
            calls, puts = dataframes[0][0].copy(), dataframes[1][0].copy()

        # Optionally clean and format the data
        if not raw:
            for df in (calls, puts):
                # Clean and convert data as necessary
                df['% Change'] = df['% Change'].str.replace('%', '').str.replace('-', '0').astype(float) / 100
                df['Volume'] = df['Volume'].str.replace('-', '0').replace(',', '', regex=True).astype(int)
                df['Open Interest'] = df['Open Interest'].str.replace('-', '0').replace(',', '', regex=True).astype(int)

        return {"calls": calls, "puts": puts}
    except Exception as e:
        print(f"An error occurred: {e}")  # Debug: Print any error that occurs
        return {"calls": pd.DataFrame(), "puts": pd.DataFrame()}

@higrm
Copy link

higrm commented May 18, 2024

I got it to work for me by modifying the get_expiration_dates function with this code:

def get_expiration_dates(ticker):
    site = build_options_url(ticker)
    
    session = HTMLSession()
    resp = session.get(site)
    
    html = resp.html.raw_html.decode()
    splits = html.split('tabindex="-1" data-value=') #change to split criteria
    
    dates = [elt[elt.find(">")+1:elt.find("<")-1] for elt in splits[1:]] # tweak
    dates = [elt for elt in dates if ', ' in elt] # just dates # added the space after the comma to account for expensive stocks
    
    session.close()
    
    return dates

@qenops
Copy link

qenops commented May 27, 2024

@higrm Note this will have a problem if the strike prices are over $1000. Just check NVDA. Adding a space after the comma, so the line becomes
dates = [elt for elt in dates if ', ' in elt]
will fix it.

@higrm
Copy link

higrm commented May 27, 2024

@higrm Note this will have a problem if the strike prices are over $1000. Just check NVDA. Adding a space after the comma, so the line becomes dates = [elt for elt in dates if ', ' in elt] will fix it.

Good catch. Just shows you I'm not looking at those high priced stocks. :)

@Icemap
Copy link

Icemap commented May 30, 2024

I offered a PR to fix it. But seems like this project has not been updated for a long while. So I forked it and updated the function in my repository, and maybe, if you want to use it, you can use pip install yahoo_fin_cheese to cover the original yahoo_fin and you can use it just like a yahoo_fin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants