Clifton_Baldwin

Data Science Journal of Clif Baldwin

A Python Program for a Stock Watchlist

7 April ’20

Simple Python Program for a Raspberry Pi

A little diversion from the usual data science posts Since I usually post an R script that does some data analysis, I thought it could not hurt to show I know some Python also.

<!DOCTYPE html>

StockWatch

StockWatch on Raspberry Pi Zero W

I created a Python program for my Raspberry Pi Zero W that could download stock data every night for a specific set of stocks I wanted to watch. For each stock ticker symbol, the program determines the company name, the most recent closing Price, the closing price from the previous day the market was open, and the percentage that the current price is off from the 52-week high. The data is stored in a CSV file. In the actual program, the CSV file is emailed to my phone, but I do not want to reveal my email information. I figured out how to email with the help of a Google search, and I did not do anything fancy.

In [2]:
# Load the required Python libraries
import pandas as pd # for Excel file
import pickle  # for storing data - in a pickle named 'wsj_financials'
from datetime import date
from pandas_datareader import data

Function price(sym, today)

A function called prices takes the ticker symbol and the current date. It uses DataReader to access Yahoo! for the stock price data. I do not like using Yahoo! because they have changed things in the past, which broke my programs, but I do not know a better option at this time for what I am doing.

The output of this function is a Tuple that consists of the stock's current closing price, the closing price of the previous market session, and the computed percentage that the current price is off the 52-week-high.

In [3]:
def prices(sym, today):
    """Get the current price of a stock, the 52-week high, 
       and 52-week low.
       Return the (price - high)/(high - low) ratio.
    """
   # Get price data for the recent year
    lastYear = date.fromordinal(today.toordinal()-365)

    try:
        price = data.DataReader(sym, start=lastYear, 
                                end=today, data_source='yahoo')
    except:
        print("**** " + sym + " error!")
        with open('./stock_errors.log', 'a') as out_file:
            out_file.write(sym 
                           + " dropped due to price error! "
                           + str(today) + "\n")
        
    p = price['Close'] # 52-weeks (one year) of closing prices
    high52 = p.max() # the 52-week high closing price
    low52 = p.min()  # the 52-week low closing price
    latest = p[-1]   # the most recent closing price
    yesterdays = p[-2] # the closing price on the previous market session
    try:
        priceIndex = ((float(high52) - float(latest))
                      / (float(high52) - float(low52))) # Compute the percentage off the 52-week-high
    except:          # if some stocks do not return sufficient data, save it in an error log
        with open('./stock_errors.log', 'a') as out_file:
            out_file.write(sym 
                           + " has insufficient data for 52-week range - price set to 0.0 "
                           + str(today) + "\n")
        priceIndex = 0.0
    priceTuple = (latest, yesterdays, priceIndex) # Return the desired data
    return priceTuple

The Main Section

The list of stock ticker symbols is saved in an Excel file. This Excel file is read to create an array of ticker sysmbols, which is named wsj_stocks. Just as an aside, I named it wsj_stocks because originally I was going to look at the thousand or so stocks that the Wall Street Journal covers. Not only was that too many stocks for me to "glance" through, I realized I care only for about 50 stocks or less. Also I do not care if the Wall Street Journal covers them or not. So I should rename the array "clif_stocks" but I just kept the original name.

In [4]:
# Look up the stock value of tickers as of today
today = date.today()

# Identify the file to read as an Excel file
xlsx = pd.ExcelFile('special 20200314.xlsx')

# Read the Excel file
wsj_stocks = pd.read_excel(xlsx, 'Sheet1', usecols=[0,1], header=0, skipinitialspace=True)
In [5]:
wsj_stocks  # The list of stock tickers with their company names that will be included in the report.
Out[5]:
COMPANY SYM
0 Activision Inc ATVI
1 Alibaba BABA
2 Alphabet Inc Class A GOOGL
3 Alphabet Inc Class C GOOG
4 Amazon.com AMZN
5 American Airlines Group AAL
6 American Express AXP
7 Amgen AMGN
8 Apple Computer AAPL
9 Applied Materials AMAT
10 Biogen Idec BIIB
11 Canadian National Railway Co. CNI
12 Carnival Corp. CCL
13 Chewy CHWY
14 Costco Wholesale Corp. COST
15 Delta Airlines DAL
16 Electronic Arts Inc EA
17 Energy Transfer ET
18 Exxon Mobil Corp. XOM
19 Fortinet FTNT
20 General Electric GE
21 Johnson & Johnson JNJ
22 Kimberly-Clark KMB
23 Royal Dutch Shell RDS-B
24 Marathon Petroleum MPC
25 Merck & Co. MRK
26 Norfolk Southern Corp. NSC
27 Norweigian Cruise Line Holdings NCLH
28 Nvidia NVDA
29 Palo Alto Networks PANW
30 Regeneron Pharmaceuticals REGN
31 Royal Carribean RCL
32 Southwest Airlines LUV
33 Take Two Interactive TTWO
34 Tesla Motors TSLA
35 Twitter Inc TWTR
36 Visa V
37 Western Midstream WES
38 MasterCard Inc MA
39 Walt Disney Corp DIS
40 CSX Corp CSX
41 Gilead Sciences GILD
42 Lowes Corporation L
43 Home Depot HD
44 T. Rowe Price Group TROW
45 Bristol-Myers Squibb BMY
46 Eli Lilly LLY
47 Tencent (Not a Henry recommendation) TCEHY

For each stock ticker, the program calls the price() function and then saves the data in a data dictionary. Once in a dictionary, the ticker symbols can be sorted.

In [6]:
wsj = {} # dictionary to store the retrieved data
for index, row in wsj_stocks.iterrows(): 
    ticker = row["SYM"].strip()
    company = row["COMPANY"].strip()

    try:  # Attempt to get current price
        priceTuple = prices(ticker, today) # Get the current price information of this stock
        # priceTuple = (latest, yesterdays, priceIndex)
        price = round(priceTuple[0],2)
        yesterdays = round(priceTuple[1],2)
        priceIndex = round(100.0 * priceTuple[2],2)
        # Only save if the price was acquired
        wsj[ticker] = {"PRICE": price, 
                       "YESTERDAY": yesterdays, 
                       "OFFHIGH": priceIndex,
                       "COMPANY": company}
    except:
        continue
# End for loop keys in wsj

A large string is constructed so that the information can be sent in an email or in this case just output to the screen.

In [9]:
# Compose the body of the email to be sent
body = "Select stocks to watch for " + str(today) + " compiled on a Raspberry Pi Zero W.\n"
body = body + "In alphabetical order by ticker symbol\n\n"
body = body + "Numbered, Ticker, Company, Closing Price, Yesterday's Close, Percentage Off High \n"

rating = 0
# Populate the body of the email message in alphabetical order
for sym in sorted(wsj.keys()):
    rating += 1
    thisStock = wsj[sym]
    body = body + str(rating) + ": " + sym + ", " + \
           str(thisStock['COMPANY']) + ", $" + \
           str(thisStock['PRICE']) +  ", $" + \
           str(thisStock['YESTERDAY']) + ", " + \
           str(thisStock['OFFHIGH']) + "% \n"
    

body = body + "\n\nDISCLAIMER: Code has been validated, but the potential for errors still exist!\n\n"
In [10]:
print(body)
Select stocks to watch for 2020-04-07 compiled on a Raspberry Pi Zero W.
In alphabetical order by ticker symbol

Numbered, Ticker, Company, Closing Price, Yesterday's Close, Percentage Off High 
1: AAL, American Airlines Group, $10.22, $9.5, 96.73% 
2: AAPL, Apple Computer, $259.43, $262.47, 44.04% 
3: AMAT, Applied Materials, $47.56, $46.15, 67.47% 
4: AMGN, Amgen, $208.78, $211.58, 44.99% 
5: AMZN, Amazon.com, $2011.6, $1997.59, 32.13% 
6: ATVI, Activision Inc, $59.87, $61.6, 20.29% 
7: AXP, American Express, $87.58, $83.87, 72.61% 
8: BABA, Alibaba, $198.0, $196.45, 39.99% 
9: BIIB, Biogen Idec, $301.21, $311.39, 32.22% 
10: BMY, Bristol-Myers Squibb, $56.9, $57.87, 42.7% 
11: CCL, Carnival Corp., $11.3, $10.21, 93.04% 
12: CHWY, Chewy, $33.31, $33.22, 29.55% 
13: CNI, Canadian National Railway Co., $80.03, $79.98, 56.68% 
14: COST, Costco Wholesale Corp., $303.63, $305.12, 24.2% 
15: CSX, CSX Corp, $61.15, $61.27, 58.95% 
16: DAL, Delta Airlines, $22.25, $22.32, 97.85% 
17: DIS, Walt Disney Corp, $101.24, $99.58, 76.5% 
18: EA, Electronic Arts Inc, $106.32, $106.51, 26.98% 
19: ET, Energy Transfer, $5.58, $5.54, 90.63% 
20: FTNT, Fortinet, $106.69, $107.88, 28.33% 
21: GE, General Electric, $7.03, $7.23, 86.95% 
22: GILD, Gilead Sciences, $74.67, $77.73, 29.84% 
23: GOOG, Alphabet Inc Class C, $1186.51, $1186.92, 69.36% 
24: GOOGL, Alphabet Inc Class A, $1182.56, $1183.19, 70.42% 
25: HD, Home Depot, $192.29, $191.33, 57.69% 
26: JNJ, Johnson & Johnson, $137.48, $139.76, 38.53% 
27: KMB, Kimberly-Clark, $131.39, $133.15, 46.95% 
28: L, Lowes Corporation, $36.27, $35.7, 75.28% 
29: LLY, Eli Lilly, $141.88, $141.61, 13.49% 
30: LUV, Southwest Airlines, $32.77, $30.7, 92.04% 
31: MA, MasterCard Inc, $259.08, $265.94, 60.51% 
32: MPC, Marathon Petroleum, $22.51, $23.09, 88.54% 
33: MRK, Merck & Co., $78.56, $80.31, 52.57% 
34: NCLH, Norweigian Cruise Line Holdings, $11.01, $10.01, 93.75% 
35: NSC, Norfolk Southern Corp., $153.53, $154.75, 63.89% 
36: NVDA, Nvidia, $259.03, $268.4, 30.77% 
37: PANW, Palo Alto Networks, $172.81, $171.34, 65.96% 
38: RCL, Royal Carribean, $33.55, $29.61, 90.05% 
39: RDS-B, Royal Dutch Shell, $35.65, $35.71, 66.09% 
40: REGN, Regeneron Pharmaceuticals, $501.51, $504.27, 1.2% 
41: TCEHY, Tencent (Not a Henry recommendation), $49.47, $49.94, 30.29% 
42: TROW, T. Rowe Price Group, $101.9, $102.7, 69.28% 
43: TSLA, Tesla Motors, $545.45, $516.24, 50.37% 
44: TTWO, Take Two Interactive, $120.06, $121.29, 31.82% 
45: TWTR, Twitter Inc, $25.61, $24.93, 84.59% 
46: V, Visa, $168.59, $169.44, 57.65% 
47: WES, Western Midstream, $4.62, $3.9, 95.13% 
48: XOM, Exxon Mobil Corp., $41.24, $40.47, 81.15% 


DISCLAIMER: Code has been validated, but the potential for errors still exist!


The comma-delimited format of the output allows me to save it as a CSV file also and import it into R or Excel for further analysis at a later time.

  • Dr. Clifton Baldwin
  • April 7, 2020