Auto Multi-Company Valuation

Multi-Company - Auto Valuation 1.0

Auto - Public Company Valuation

This program takes the ticker of publically traded companies as input and returns the value of the companies by extracting the financial data from Yahoo Finance.

In [1]:
from bs4 import BeautifulSoup
import requests
import pandas as pd
from pandas import Series, DataFrame
import matplotlib.pyplot as plt
import matplotlib.dates
import numpy as np
import re
%matplotlib inline
In [2]:
def getData(xtable,xdata):
    xRows = xtable[0].findAll('tr')
    for tr in xRows:
        cols = tr.findAll('td')
        for td in cols:
            text = td.find(text=True)
            xdata.append(text)
    return xdata
In [3]:
def addTickerCol(data,tickerCol):

    for value in tickerCol:
        data.append(value)
    return data
In [4]:
def getCols(xData,numCols):

    cols = [Series(xData[i:i+numCols+1]) for i in range(0, len(xData),numCols+1)]

    return cols
In [5]:
def getDF(xCols):
    x_df = pd.concat(xCols, axis=1)
    x_df.columns = x_df.iloc[0]
    x_df = x_df[1:]
    return x_df
In [6]:
def is_int(s):
    try:
        int(s)
        return True
    except ValueError:
        return False
In [7]:
def getType(df):

    for col in df.columns.values:

        for i, value in enumerate(df[col]):

            if value =='-':
                df[col][i+1] = '0'

        if (col!='Period Ending') & (col!='Ticker'):
            df[col] = df[col].astype(int)

        elif col=='Period Ending':
            df[col] = pd.to_datetime(df[col])

    return df
In [8]:
ticker = raw_input('Insert Ticker(s) of the publically traded company(ies) you would like to know the value of. Separate Tickers using commas.').upper()

removeChars = [' ','!','@','#','$','%','^','&','*','(',')','-','+','=','{','}','[',']',':',';','"',"'",'<','>','.','?']

for character in removeChars:
    ticker = ticker.replace(character,'')

tickerList = ticker.split(',')
tickerList = filter(None, tickerList)
tickerSet = set(tickerList) # remove duplicates in list
print tickerSet

for ticker in tickerSet:
    try:
        incomeStatementURL = 'https://finance.yahoo.com/quote/{a}/financials?p={a}'.format(a = ticker)
        incomeStatementPage = requests.get(incomeStatementURL)
        incomeStatementPageParsed = BeautifulSoup(incomeStatementPage.content,'html.parser')
        incomeStatementPageSummary = incomeStatementPageParsed.find('div',{'class':'Mt(10px) Ovx(a) W(100%)'})
        incomeStatementTable = incomeStatementPageSummary.find_all('table')
    except:
        print("Oops! {a} is an invalid Ticker value. Please try again.".format(a=ticker))
Insert Ticker(s) of the publically traded company(ies) you would like to know the value of. Separate Tickers using commas.amzn, agn, c, hd, low
set(['AGN', 'AMZN', 'C', 'HD', 'LOW'])
In [9]:
incomeStatementDF = pd.DataFrame()
balanceSheetDF = pd.DataFrame()
#cashFlowDF = pd.DataFrame

for ticker in tickerSet:

    incomeStatementURL = 'https://finance.yahoo.com/quote/{a}/financials?p={a}'.format(a = ticker)
    balanceSheetURL = 'https://finance.yahoo.com/quote/{a}/balance-sheet?p={a}'.format(a = ticker)
    #cashFlowURL = 'https://finance.yahoo.com/quote/{a}/cash-flow?p={a}'.format(a = ticker)

    incomeStatementPage = requests.get(incomeStatementURL)
    balanceSheetPage = requests.get(balanceSheetURL)
    #cashFlowPage = requests.get(cashFlowURL)

    incomeStatementPageParsed = BeautifulSoup(incomeStatementPage.content,'html.parser')
    balanceSheetPageParsed = BeautifulSoup(balanceSheetPage.content,'html.parser')
    #cashFlowPageParsed = BeautifulSoup(cashFlowPage.content,'html.parser')

    incomeStatementPageSummary = incomeStatementPageParsed.find('div',{'class':'Mt(10px) Ovx(a) W(100%)'})
    incomeStatementTable = incomeStatementPageSummary.find_all('table')
    balanceSheetPageSummary = balanceSheetPageParsed.find('div',{'class':'Mt(10px) Mb(50px) Ovx(a) W(100%)'})
    balanceSheetTable = balanceSheetPageSummary.find_all('table')
    #cashFlowPageSummary = cashFlowPageParsed.find('div',{'class':'Mt(10px) Mb(50px) Ovx(a) W(100%)'})
    #cashFlowTable = cashFlowPageSummary.find_all('table')

    incomeStatementData = []
    balanceSheetData = []
    #cashFlowData = []

    incomeStatementData = getData(incomeStatementTable,incomeStatementData)
    balanceSheetData = getData(balanceSheetTable,balanceSheetData)
    #cashFlowData = getData(cashFlowTable,cashFlowData)

    incomeStatementData.remove('Net Income')
    incomeStatementData = [e for e in incomeStatementData if e not in ('Operating Expenses','Income from Continuing Operations','Non-recurring Events')]

    balanceSheetData = [e for e in balanceSheetData if e not in('Current Assets','Current Liabilities',"Stockholders' Equity")]

    #cashFlowData = [e for e in cashFlowData if e not in ("Operating Activities, Cash Flows Provided By or Used In",'Investing Activities, Cash Flows Provided By or Used In','Financing Activities, Cash Flows Provided By or Used In')]

    incomeStatementData[0]='Period Ending'
    incomeStatementData = [value.replace(',','') for value in incomeStatementData]

    balanceSheetData = [value.replace(',','') for value in balanceSheetData]
    #cashFlowData = [value.replace(',','') for value in cashFlowData]

    colsNumber=-1
    for value in incomeStatementData:
        if value == 'Total Revenue':
            break
        else:
            colsNumber +=1

    tickerCol = ['Ticker']
    for i in range(colsNumber):
        tickerCol.append(str(ticker))

    addTickerCol(incomeStatementData,tickerCol)
    addTickerCol(balanceSheetData,tickerCol)
    #addTickerCol(cashFlowData,tickerCol)

    incomeStatementCols = getCols(incomeStatementData, colsNumber)
    balanceSheetCols = getCols(balanceSheetData, colsNumber)
    #cashFlowCols = getCols(cashFlowData, colsNumber)

    incomeStatement_df = getDF(incomeStatementCols)
    balanceSheet_df = getDF(balanceSheetCols)
    #cashFlow_df = getDF(cashFlowCols)

    incomeStatement_df = getType(incomeStatement_df)
    balanceSheet_df = getType(balanceSheet_df)
    #cashFlow_df = getType(cashFlow_df)

    if balanceSheet_df['Total Current Assets'].any() == 0: #.all()
        balanceSheet_df['Total Current Assets'] = balanceSheet_df['Cash And Cash Equivalents'] + balanceSheet_df['Short Term Investments'] + balanceSheet_df['Net Receivables'] + balanceSheet_df['Inventory'] + balanceSheet_df['Other Current Assets']

    if balanceSheet_df['Total Current Liabilities'].any() == 0:
        balanceSheet_df['Total Current Liabilities'] = balanceSheet_df['Accounts Payable'] + balanceSheet_df['Short/Current Long Term Debt'] + balanceSheet_df['Other Current Liabilities']

    incomeStatementDF = pd.concat([incomeStatementDF,incomeStatement_df])
    balanceSheetDF = pd.concat([balanceSheetDF,balanceSheet_df])
    #cashFlowDF = pd.concat([cashFlowDF,cashFlow_df])
C:\Users\nwerner\AppData\Local\Continuum\Anaconda2\lib\site-packages\ipykernel_launcher.py:8: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

In [10]:
#incomeStatementDF['Period Ending'] = pd.to_datetime(incomeStatementDF['Period Ending'])
#balanceSheetDF['Period Ending'] = pd.to_datetime(balanceSheetDF['Period Ending'])
#cashFlowDF['Period Ending'] = pd.to_datetime(cashFlowDF['Period Ending'])
In [11]:
incomeStatementDF.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 15 entries, 1 to 3
Data columns (total 25 columns):
Period Ending                             15 non-null datetime64[ns]
Total Revenue                             15 non-null int32
Cost of Revenue                           15 non-null int32
Gross Profit                              15 non-null int32
Research Development                      15 non-null int32
Selling General and Administrative        15 non-null int32
Non Recurring                             15 non-null int32
Others                                    15 non-null int32
Total Operating Expenses                  15 non-null int32
Operating Income or Loss                  15 non-null int32
Total Other Income/Expenses Net           15 non-null int32
Earnings Before Interest and Taxes        15 non-null int32
Interest Expense                          15 non-null int32
Income Before Tax                         15 non-null int32
Income Tax Expense                        15 non-null int32
Minority Interest                         15 non-null int32
Net Income From Continuing Ops            15 non-null int32
Discontinued Operations                   15 non-null int32
Extraordinary Items                       15 non-null int32
Effect Of Accounting Changes              15 non-null int32
Other Items                               15 non-null int32
Net Income                                15 non-null int32
Preferred Stock And Other Adjustments     15 non-null int32
Net Income Applicable To Common Shares    15 non-null int32
Ticker                                    15 non-null object
dtypes: datetime64[ns](1), int32(23), object(1)
memory usage: 1.7+ KB
In [12]:
incomeStatementDF
Out[12]:
Period Ending Total Revenue Cost of Revenue Gross Profit Research Development Selling General and Administrative Non Recurring Others Total Operating Expenses Operating Income or Loss ... Minority Interest Net Income From Continuing Ops Discontinued Operations Extraordinary Items Effect Of Accounting Changes Other Items Net Income Preferred Stock And Other Adjustments Net Income Applicable To Common Shares Ticker
1 2017-12-31 15940700 2168000 13772700 2100100 5016700 5380000 7197100 19693900 -5921200 ... 16000 -3716000 -402900 0 0 0 -4125500 0 -4403900 AGN
2 2016-12-31 14570600 1860800 12709800 2575700 4740300 748900 6470400 14535300 -1825500 ... 7800 -935000 15914500 0 0 0 14973400 0 14695000 AGN
3 2015-12-31 12688100 2751800 9936300 2358500 4481500 783600 5443700 13067300 -3131000 ... -2100 -2941600 6861000 0 0 0 3915200 0 3683200 AGN
1 2017-12-31 177866000 111934000 65932000 0 61826000 0 0 0 4106000 ... 0 3033000 0 0 0 0 3033000 0 3033000 AMZN
2 2016-12-31 135987000 88265000 47722000 0 43536000 0 0 0 4186000 ... 0 2371000 0 0 0 0 2371000 0 2371000 AMZN
3 2015-12-31 107006000 71651000 35355000 0 33122000 0 0 0 2233000 ... 0 596000 0 0 0 0 596000 0 596000 AMZN
1 2017-12-31 63283000 0 63283000 0 41346000 9167000 7342000 0 21983000 ... 932000 -6627000 -111000 0 0 0 -6798000 0 -6798000 C
2 2016-12-31 62565000 0 62565000 0 41620000 10207000 6778000 0 20529000 ... 1023000 15033000 -58000 0 0 0 14912000 0 14912000 C
3 2015-12-31 68024000 0 68024000 0 44346000 11105000 7182000 0 24144000 ... 1235000 17386000 -54000 0 0 0 17242000 0 17242000 C
1 2017-01-29 94595000 62282000 32313000 0 17132000 0 1754000 0 13427000 ... 0 7957000 0 0 0 0 7957000 0 7957000 HD
2 2016-01-31 88519000 58254000 30265000 0 16801000 0 1690000 0 11774000 ... 0 7009000 0 0 0 0 7009000 0 7009000 HD
3 2015-02-01 83176000 54787000 28389000 0 16280000 0 1640000 0 10469000 ... 0 6345000 0 0 0 0 6345000 0 6345000 HD
1 2017-02-03 65017000 42553000 22464000 0 15129000 0 1489000 0 5846000 ... 0 3093000 0 0 0 0 3093000 0 3093000 LOW
2 2016-01-29 59074000 38504000 20570000 0 14105000 0 1494000 0 4971000 ... 0 2546000 0 0 0 0 2546000 0 2546000 LOW
3 2015-01-30 56223000 36665000 19558000 0 13272000 0 1494000 0 4792000 ... 0 2698000 0 0 0 0 2698000 0 2698000 LOW

15 rows × 25 columns

In [13]:
balanceSheetDF
Out[13]:
Period Ending Cash And Cash Equivalents Short Term Investments Net Receivables Inventory Other Current Assets Total Current Assets Long Term Investments Property Plant and Equipment Goodwill ... Redeemable Preferred Stock Preferred Stock Common Stock Retained Earnings Treasury Stock Capital Surplus Other Stockholder Equity Total Stockholder Equity Net Tangible Assets Ticker
1 2017-12-31 1817200 4632100 2899000 904500 1123900 11376700 267900 1867000 49862900 ... 0 0 0 12957200 0 54013500 1920700 73821100 -30690100 AGN
2 2016-12-31 1724000 11501500 2531000 718000 1383400 17857900 282100 1638300 46356100 ... 0 0 0 18342500 0 53958900 -1038400 76192700 -32782000 AGN
3 2015-12-31 1096000 9300 2125400 757500 4590900 8579100 408700 12244600 46465200 ... 0 0 0 3647500 0 68508300 -494100 76591400 -37710000 AGN
1 2017-12-31 20522000 10464000 13164000 16047000 0 60197000 0 48866000 13350000 ... 0 0 5000 8636000 -1837000 21389000 -484000 27709000 14359000 AMZN
2 2016-12-31 19334000 6647000 8339000 11461000 0 45781000 0 29114000 3784000 ... 0 0 5000 4916000 -1837000 17186000 -985000 19285000 15501000 AMZN
3 2015-12-31 15890000 3918000 5654000 10243000 0 35705000 0 21838000 3759000 ... 0 0 5000 2545000 -1837000 13394000 -723000 13384000 9625000 AMZN
1 2017-12-31 412994000 0 38384000 0 0 451378000 1258525000 0 22256000 ... 0 0 31000 138425000 -30309000 108008000 -34668000 200740000 173896000 C
2 2016-12-31 397307000 0 28887000 0 0 426194000 1209538000 0 21659000 ... 0 0 31000 146477000 -16302000 108042000 -32381000 225120000 198347000 C
3 2015-12-31 352772000 0 27683000 0 0 380455000 1189161000 0 22349000 ... 0 0 31000 133841000 -7677000 108288000 -29344000 221857000 195787000 C
1 2017-01-29 2538000 0 2029000 12549000 608000 17724000 0 21914000 2093000 ... 0 0 88000 35519000 -40194000 9787000 -867000 4333000 2240000 HD
2 2016-01-31 2216000 0 1890000 11809000 569000 16484000 0 22191000 2102000 ... 0 0 88000 30973000 -33194000 9347000 -898000 6316000 4214000 HD
3 2015-02-01 1723000 0 1484000 11079000 1016000 15302000 0 22720000 1353000 ... 0 0 88000 26995000 -26194000 8885000 -452000 9322000 7969000 HD
1 2017-02-03 558000 100000 0 10458000 884000 12000000 366000 19949000 1082000 ... 0 0 433000 6241000 0 0 -240000 6434000 5352000 LOW
2 2016-01-29 405000 307000 0 9458000 391000 10561000 222000 19577000 154000 ... 0 0 455000 7593000 0 0 -394000 7654000 7500000 LOW
3 2015-01-30 466000 125000 0 8911000 349000 9851000 354000 20034000 0 ... 0 0 480000 9591000 0 0 -103000 9968000 9968000 LOW

15 rows × 36 columns

In [14]:
# Ratios

ratiosDF = pd.DataFrame()

ratiosDF['Period Ending'] = incomeStatementDF['Period Ending']
ratiosDF['Ticker'] = incomeStatementDF['Ticker']
ratiosDF['Current Ratio'] = balanceSheetDF['Total Current Assets']/balanceSheetDF['Total Current Liabilities']
ratiosDF['Quick Ratio'] = (balanceSheetDF['Cash And Cash Equivalents']+balanceSheetDF['Net Receivables'])/balanceSheetDF['Total Current Liabilities']
ratiosDF['L-T Debt to Book Value of Equity'] = balanceSheetDF['Long Term Debt']/balanceSheetDF['Total Stockholder Equity']
ratiosDF['Gross Margin'] = incomeStatementDF['Gross Profit']/incomeStatementDF['Total Revenue']*100
ratiosDF['EBITDA Margin'] = incomeStatementDF['Income Before Tax']/incomeStatementDF['Total Revenue']*100
ratiosDF['Pre-Tax Income Margin'] = incomeStatementDF['Earnings Before Interest and Taxes']/incomeStatementDF['Total Revenue']*100
In [15]:
ratiosDF
Out[15]:
Period Ending Ticker Current Ratio Quick Ratio L-T Debt to Book Value of Equity Gross Margin EBITDA Margin Pre-Tax Income Margin
1 2017-12-31 AGN 1.155218 0.478894 0.371403 86.399593 -65.156486 -58.283513
2 2016-12-31 AGN 2.267756 0.540338 0.404986 87.229078 -19.436399 -10.544521
3 2015-12-31 AGN 1.034624 0.388495 0.534470 78.311962 -35.840670 -26.435794
1 2017-12-31 AMZN 1.039977 0.581967 0.892959 37.068355 2.139813 2.616577
2 2016-12-31 AMZN 1.044847 0.631573 0.398963 35.093060 2.862038 3.217955
3 2015-12-31 AMZN 1.053649 0.635760 0.614689 33.040203 1.465338 1.894286
1 2017-12-31 C 0.369409 0.369409 1.797131 100.000000 35.967005 62.067222
2 2016-12-31 C 0.367700 0.367700 1.533507 100.000000 34.327499 54.324303
3 2015-12-31 C 0.336929 0.336929 1.436903 100.000000 36.495943 54.020640
1 2017-01-29 HD 1.254086 0.323144 5.157858 34.159311 13.204715 14.232253
2 2016-01-31 HD 1.316193 0.327851 3.291482 34.190400 12.450434 13.488630
3 2015-02-01 HD 1.357884 0.284586 1.809590 34.131240 11.993844 12.991728
1 2017-02-03 LOW 1.002171 0.046601 2.237177 34.550964 7.999446 7.999446
2 2016-01-29 LOW 1.006576 0.038601 1.508362 34.820733 7.480448 7.480448
3 2015-01-30 LOW 1.053808 0.049850 1.084069 34.786475 7.605428 7.605428
In [16]:
# Function for Looping through Specific Ticker Value Ratios

def plotValues(dataFrame,ax,yValue):
    for ticker in tickerSet:
        df = dataFrame[dataFrame['Ticker']==ticker]
        ax.plot_date(df['Period Ending'],df[yValue], ls='-', marker='o')
In [17]:
fig = plt.figure(figsize=(8,30))

ax1 = fig.add_subplot(611)
ax1.set_title('Current Ratio')
ax1.set_ylabel('Ratio Value')
ax1.grid(True)
plotValues(ratiosDF,ax1,'Current Ratio')

ax2 = fig.add_subplot(612)
ax2.set_title('Quick Ratio')
ax2.set_ylabel('Ratio Value')
ax2.grid(True)
plotValues(ratiosDF,ax2,'Quick Ratio')

ax3 = fig.add_subplot(613)
ax3.set_title('L-T Debt to Book Value of Equity')
ax3.set_ylabel('Percent (%)')
ax3.grid(True)
plotValues(ratiosDF,ax3,'L-T Debt to Book Value of Equity')

ax4 = fig.add_subplot(614)
ax4.set_title('Gross Margin')
ax4.set_ylabel('Percent (%)')
ax4.grid(True)
plotValues(ratiosDF,ax4,'Gross Margin')

ax5 = fig.add_subplot(615)
ax5.set_title('EBITDA Margin')
ax5.set_ylabel('Percent (%)')
ax5.grid(True)
plotValues(ratiosDF,ax5,'EBITDA Margin')

ax6 = fig.add_subplot(616)
ax6.set_title('Pre-Tax Income Margin')
ax6.set_ylabel('Percent (%)')
ax6.grid(True)
plotValues(ratiosDF,ax6,'Pre-Tax Income Margin')

fig.autofmt_xdate(rotation=45)

plt.legend(list(tickerSet))
#plt.figure(figsize=(1,1))
plt.show()

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora