Search code examples
pythonpandasbooleanstockyfinance

Dividend stocks does not appear from function


I'm trying to gather dividend yields from multiple stocks via yfinance. I have a loop which creates a CSV-file for each ticker with historical data.

When I've downloaded dividend data via a function previously, it has worked - basically I created a function with a for-loop and then appended a dataframe with the stocks. However, now I want to do it the same way but with a boolean expression instead, and it's not working.. I'm not getting any errors but I'm not receiving any ticker symbols (which I know satisfy the condition). I've tried to formulate the boolean loop differently, without success. What am I doing wrong? Below is my code:

import yfinance as yf
import pandas as pd
import os

df = pd.read_csv(r'C:\\Users\Name\Stocks\Trading\teststocks.csv')
tickers = df["Symbol"].tolist()
i=0
listlength = len(tickers)
for ticker in tickers:
    i=i+1
    print("Downloading data for",ticker,",",i,"of",listlength)
    df = yf.download(ticker, period = "max", interval = "1wk", rounding = True)
    df.dropna(inplace=True)
    df.to_csv(os.path.join("C:\\Users\Name\Stocks\dataset",ticker + ".csv"))
def dividend(df):
    info = yf.Ticker(ticker).info
    div = info.get("dividendYield")
    if div is None:
        pass
    elif div > 0.04:
        return True
    else:
        return False
for filename in os.listdir("C:\\Users\Name\Stocks\dataset"):
    df = pd.read_csv("C:\\Users\Name\Stocks\dataset\{}".format(filename))
    if dividend(df):
        print("{}".format(filename))

So this function is looping through the ticker symbols from the dataset folder and getting the dividend data from yfinance, however it's not returning with the ticker that satisfy the condition - which in this case is if the dividend yield is higher than 4%. The first dataframe being read is a CSV file with the ticker symbols in the OMXS30 - so for example HM-B.ST should appear from the dividend function..

Another thing that I want to add is that I'm using the same logic for a function for marketcap, which does work. See below:

def marketcap(df):
    info = yf.Ticker(ticker).info
    mcap = info.get("marketCap") 
    if mcap is None:
        pass
    elif mcap > 10000000000:
        return True
    else:
        return False
for filename in os.listdir("C:\\Users\Name\Stocks\dataset"):
    df = pd.read_csv("C:\\Users\Name\Stocks\dataset\{}".format(filename))
    if marketcap(df):
        print("{}".format(filename))

I do not know why the dividend boolean expression does not work, when the marketcap does work. Thanks in advance.


Solution

  • Neither the function dividend nor marketcap is working as it should. The reason has to do with the following:

    for ticker in tickers:
        # do stuff
    

    Here you are taking a list of tickers and doing some stuff for each ticker in this list. This means that by the end of your loop, the variable ticker equals the last item in the list. E.g. suppose tickers = ['HM-B.ST','AAPL'], then ticker will at the end equal AAPL.

    Now, let's have a look at your function dividend:

    def dividend(df):
        info = yf.Ticker(ticker).info
        div = info.get("dividendYield")
        if div is None:
            pass
        elif div > 0.04:
            return True
        else:
            return False
    

    This function has one argument (df), but it is not actually using it. Instead you are applying yf.Ticker(...).info to a variable ticker, which is no longer being updated at all. If the function is not returning any True values, this must simply mean that the last ticker (e.g. "AAPL") does not represent a dividend stock. So, to fix this you want to change the input for the function: def dividend(ticker). Write something like:

    for filename in os.listdir("C:\\Users\Name\Stocks\dataset"):
        df = pd.read_csv("C:\\Users\Name\Stocks\dataset\{}".format(filename))
        
        # e.g. with filename like "HM-B.ST.csv", split at "."
        # and select only first part
        ticker = filename.split('.')[0]
        if dividend(ticker):
            print("{}".format(filename))
    

    You need to make the same change for your function marketcap. Again, if this function is currently returning True values, this just means that your last list item references a stock has a higher mcap than the threshold.


    Edit: Suggested refactored code

    import yfinance as yf
    import pandas as pd
    
    tickers = ['ABB.ST','TELIA.ST','ELUX-B.ST','HM-B.ST']
    
    def buy_dividend(ticker):
        info = yf.Ticker(ticker).info
        # keys we need
        keys = ['marketCap','trailingPE','dividendYield']
        
        # store returned vals in a `list`. E.g. for 'HM-B.ST':
            # [191261163520, 13.417525, 0.0624], i.e. mcap, PE, divYield
        vals = [info.get(key) for key in keys]
        # if *any* val == `None`, `all()` will be `False`
        if all(vals):
            # returns `True` if *all* conditions are met, else `False`
            return (vals[0] > 1E10) & (vals[1] < 20) & (vals[2] > 0.04)
        return False
    
    for ticker in tickers:
        # `progress=False` suppresses the progress print
        df = yf.download(ticker, period = "max", interval = "1wk", 
                         rounding = True, progress = False) 
    
        df.dropna(inplace=True)
        
        if df.empty:
            continue
    
        # df.to_csv(os.path.join("C:\\Users\Name\Stocks\dataset",ticker + ".csv"))
        # get last close & mean from column `df.Close`
        last_close = df.loc[df.index.max(),'Close']
        mean = df.Close.mean()
        if last_close < mean:
            if buy_dividend(ticker):
                print("{} is a good buy".format(ticker))
            else:
                print("{} is not a good buy".format(ticker))
    

    This will print:

    TELIA.ST is not a good buy
    ELUX-B.ST is a good buy
    HM-B.ST is a good buy
    
    # and will silently pass over 'ABB.ST', since `(last_close < mean) == False` here