I have the following script to populate my database with yahoo finance information:
from multiprocessing import Pool
import json, time, yfinance
import django
django.setup()
from dividends_info.functions.stock_info import save_stock_info_data
from dividends_info.models import StockInfo
with open('tickers/nyse_tickers.json') as tickers_file:
TICKERS = json.load(tickers_file)
TICKERS = TICKERS[0:100]
# https://www.digitalocean.com/community/tutorials/python-multiprocessing-example
def update_a_stock(ticker):
stock, created = StockInfo.objects.get_or_create(ticker=ticker)
yahoo_stock_obj = yfinance.Ticker(ticker.upper())
earnings_history = yahoo_stock_obj.earnings_history
save_stock_info_data(yahoo_stock_obj, ticker, stock, earnings_history)
if not stock.earnings:
print(f"No earnings for {ticker}")
def pool_handler():
start = time.time()
p = Pool(2)
p.map(update_a_stock, TICKERS)
with open("time_taken_to_populate.txt", "w") as time_file:
time_taken = round((time.time() - start), 2)
time_file.write(f"Time taken = {time_taken:.10f}")
if __name__ == '__main__':
pool_handler()
The important thing here is
yahoo_stock_obj = yfinance.Ticker(ticker.upper())
earnings_history = yahoo_stock_obj.earnings_history
whereas my save_stock_info_data prints out the type of the earnings history:
ticker in save stock info func: ABEV
<class 'pandas.core.frame.DataFrame'>
ticker in save stock info func: A
<class 'pandas.core.frame.DataFrame'>
Saved new information for stock ABEV
Saved new information for stock A
ticker in save stock info func: ABG
<class 'pandas.core.frame.DataFrame'>
ticker in save stock info func: AA
<class 'pandas.core.frame.DataFrame'>
Saved new information for stock ABG
Saved new information for stock AA
ticker in save stock info func: ABM
<class 'pandas.core.frame.DataFrame'>
As you can see the script starts off fine, and for the first 13 calls it will save earnings data:
python3 count_db_items.py
21 many stocks
name: 18, summary: 18, dividends: 16, earnings: 13
After the first 13 earnings are saved, earnings_history is no longer available from the api:
ticker in save stock info func: AB
<class 'NoneType'>
Could not find data for ACCO.
ticker in save stock info func: ACCO
<class 'NoneType'>
Saved new information for stock AB
No earnings for AB
Saved new information for stock ACCO
No earnings for ACCO
Could not find data for ABB.
ticker in save stock info func: ABB
<class 'NoneType'>
each earnings_history object gathered from the yahoo_stock_object of yfinance is 'None'. This happens each time the script is ran after the first 13 are saved. All the other data including dividends is available. I think dividends is sent as an array whereas earnings_history is sent as a pandas dataframe.
When I ran the script for all tickers I had 1800 stocks, 1700 dividends and only 25 results for earnings.
Running the script synchronously does not help in fact it is worse where NO earnings are saved at all:
import json, os, sys, time, yfinance
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dividends_project.settings")
sys.path.append('../..')
import django
django.setup()
from dividends_info.functions.stock_info import save_stock_info_data
from dividends_info.models import StockInfo
# https://stackoverflow.com/questions/59159991/modulenotfounderror-no-module-named-foo-how-can-i-import-a-model-into-a-djang
# https://pythonspeed.com/articles/python-multiprocessing/
# https://github.com/pytorch/pytorch/issues/3492
f = open('tickers/nyse_tickers.json')
TICKERS = json.load(f)
f.close()
print(len(TICKERS))
TICKERS = TICKERS[:50]
def update_a_stock(ticker):
stock, created = StockInfo.objects.get_or_create(ticker=ticker)
yahoo_stock_obj = yfinance.Ticker(ticker.upper())
earnings_history = yahoo_stock_obj.earnings_history
# stock, yahoo_obj = save_stock_info_data(ticker, stock)
save_stock_info_data(yahoo_stock_obj, ticker, stock, earnings_history)
if not stock.earnings:
print("earnings didn't save for this stock...the earnings are:")
print(earnings_history)
def run():
start = time.time()
for ticker in TICKERS:
update_a_stock(ticker)
with open("time_taken_to_populate.txt", "w") as time_file:
time_taken = round((time.time() - start), 2)
time_file.write(f"Time taken = {time_taken:.10f}")
if __name__ == "__main__":
run()
without the earnings data my website is pretty much useless. It doesn't seem a rate limiting issue since running a synchronous version of the script doesn't work either.
If I access an individual stock it has earnings:
import sys
ticker = sys.argv[1]
def update_a_stock(ticker):
stock, created = StockInfo.objects.get_or_create(ticker=ticker)
yahoo_stock_obj = yfinance.Ticker(ticker.upper())
earnings_history = yahoo_stock_obj.earnings_history
earnings = gather_earnings_object
in ipdb:
ipdb> earnings
[{'date': datetime.date(2022, 7, 29), 'expected': 3.31, 'actual': 3.37, 'surprise': '+1.69'}, {'date': datetime.date(2022, 4, 29), 'expected': 3.14, 'actual': 3.16, 'surprise': '+0.57'}, {'date': datetime.date(2022, 2, 2), 'expected': 3.29, 'actual': 3.31, 'surprise': '+0.73'}, {'date': datetime.date(2021, 10, 29), 'expected': 3.22, 'actual': 3.33, 'surprise': '+3.32'}, ....
I would have to manually run my script for the single api call 10000 times to populate the data with all american stocks.
How can I run an asynchronous script calling yfinance to populate my db?