Get Option Close Price ParallellyΒΆ

This example gets option closing prices for a specified ticker and start date. It uses client.map() to reduce overhead from slower I/O operations.

"""
This program gets closing price of a given ticker and start dates. This program is revised based on
https://stackoverflow.com/a/77342764

Usage:
    $ git clone https://github.com/finos/opengris-scaler && cd opengris-scaler
    $ pip install -r examples/applications/requirements_applications.txt
    $ python -m examples.applications.yfinance_historical_price
"""

import datetime
import os
from typing import Optional

import pandas as pd
import psutil
import yfinance as yf

from scaler import Client
from scaler.cluster.combo import SchedulerClusterCombo


def get_option_data(stock_symbol: str, expiration_date: Optional[str], option_type: str, strike: float):
    stock = yf.Ticker(stock_symbol)
    option_chain = stock.option_chain(expiration_date)
    options = getattr(option_chain, "calls" if option_type.startswith("call") else "puts")
    option_data = options[options["strike"] == strike]
    return option_data


def get_option_history_data(contract_symbol, days_before_expiration: int = 30):
    option = yf.Ticker(contract_symbol)
    option_info = option.info
    option_expiration_date = datetime.datetime.fromtimestamp(option_info["expireDate"])

    start_date = option_expiration_date - datetime.timedelta(days=days_before_expiration)
    option_history = option.history(start=start_date)
    return option_history


def get_option_close_prices_with_strike(strike):
    # from yfinance.base import YFRateLimitError
    # stock_symbol = "AAPL"
    # expiration_date = None  # User may wish to specify expiration_date
    # days_before_expiration = 30
    # option_type = "call"

    # res = []
    # try:
    #     option_data = get_option_data(stock_symbol, expiration_date, option_type, strike)
    #     for _, od in option_data.iterrows():
    #         contract_symbol = od["contractSymbol"]
    #         option_history = get_option_history_data(contract_symbol, days_before_expiration)
    #         first_option_history = option_history.iloc[0]
    #         first_option_history_date = option_history.index[0]
    #         first_option_history_close = first_option_history["Close"]
    #         res.append((contract_symbol, first_option_history_close, first_option_history_date))
    # except YFRateLimitError:
    #     print("Early return due to requesting too many requests.")
    # return res

    # NOTE: Here, we are mocking data that we will be receiving, and pass them back. This is to avoid creating network
    # traffic to a third party. If you wish to get data from Yahoo, comment out below section, and uncomment everything
    # that's above.
    df = pd.read_csv(os.path.join(os.path.dirname(__file__), "downloaded_data.csv"))
    mock_data = [(row["contractSymbol"], float(row["close"]), pd.to_datetime(row["date"])) for _, row in df.iterrows()]

    if strike % 5 != 0:
        return []
    res = [mock_data[(strike - 170) // 5]]
    return res


def main():
    n_workers = psutil.cpu_count()
    if n_workers is None:
        n_workers = 4

    strike_start = 170
    strike_end = 240

    cluster = SchedulerClusterCombo(n_workers=n_workers)

    with Client(address=cluster.get_address()) as client:
        results = client.map(get_option_close_prices_with_strike, range(strike_start, strike_end))

    cluster.shutdown()

    for lists_of_closing_dates in results:
        for contract_symbol, first_option_history_close, first_option_history_date in lists_of_closing_dates:
            print(
                f"For {contract_symbol}, the closing price was ${first_option_history_close:.2f} on "
                f"{first_option_history_date}."
            )


if __name__ == "__main__":
    main()