Retrieving a stock quote using Python?

John Hunter jdhunter at ace.bsd.uchicago.edu
Thu May 22 13:42:46 EDT 2003


>>>>> "David" == David Lees <abcdebl2nonspammy at verizon.net> writes:

    David> I would like to write a utility in Python that takes a
    David> stock symbol as an input and returns a current price.
    David> Suggestions on how to do this are welcome.  I was thinking
    David> of an approach that would go out to a page like
    David> finance.yahoo.com, entering the stock symbol and then
    David> collecting the results and parsing them for the price.  But
    David> it has been awhile since I used pythton and I'm not sure I
    David> ever really knew how to do this.


This script doesn't get minute by minute quotes, but does parse the
ticker profiles page, which has lots of day-to-day useful information
(market cap, recent high, recent low, short interest, ....) which will
be useful if you want to do quantitative analysis.  It has a method to
insert the results into a database, but you can do whatever you want
with the data.  If you are interested in the mysql code to create the
database, let me know....

It requires lynx, because I use a lynx dump to get the text and then
parse the text rather than the html.  I know, I know, so kill me.

You can use it like

    from Profile import Profile
    ticker = 'ADPT'
    p = Profile(ticker)
    print p

import re, sys, os, string
from mx.DateTime import DateTime, strptime
import MySQLdb

_theDBase = MySQLdb.connect(db='quotes',
                            host='nitace',
                            user='username',
                            passwd='passwd')

def bignum( x, s):
    if s=='K':
        return float(x)*1e3
    if s=='M':
        return float(x)*1e6
    if s=='B':
        return float(x)*1e9
    return x

class __Profile_version_0_1:
    date = None
    min52Date = None
    min52 = None
    max52Date = None
    max52 = None
    recentPrice = None
    beta = None
    volume3month = None
    volume10day = None
    percentChange52 = None    
    percentChange52Relative = None
    marketCap = None
    sharesOutstanding = None
    sharesFloat = None
    annualDividend = None
    dividendYield = None
    lastSplitFactor = None
    lastSplitFactorDate = None
    bookValueMRQPerShare = None
    earningsTTMPerShare = None
    earningsMRQPerShare = None
    salesTTMPerShare = None
    cashMRQPerShare = None
    pOverBookMRQ = None
    pOverEarningsTTM = None
    pOverSalesTTM = None
    salesTTM = None
    EBITDATTM = None
    incomeAvailableTTM = None
    profitMarginTTM = None
    operatingMarginTTM = None
    returnAssetsTTM = None
    returnEquityTTM = None
    currentRatioMRQ = None
    debtEquityMRQ = None
    totalCashMRQ = None
    shortShares = None
    shortPercentFloat = None
    shortSharesPrior = None
    shortRatio = None
    shortDailyVolume = None

    def __init__(self, ticker):
        if len(ticker)==0:
            raise ValueError, 'Empty string passed for ticker'
        self._ticker = ticker
        self._parse()

    def __repr__(self):
        s = ''
        s+= "Date: %s\n" % self.date.strftime('%Y-%m-%d')
        s+= "52-Week Low on %s: $%1.2f\n" % (
            self.min52Date.strftime('%Y-%m-%d'), self.min52)
        s+= "52-Week High on %s: $%1.2f\n" % (
            self.max52Date.strftime('%Y-%m-%d'), self.max52)
        s+= "Recent price: $%1.2f\n" % self.recentPrice
        s+= "Beta: %1.2f\n" % self.beta
        s+= "Volume (30 day): %1.0f\n" % self.volume3month
        s+= "Volume (10 day): %1.0f\n" % self.volume10day
        s+= "52 week change %1.2f%%\n" % self.percentChange52
        s+= "52 week change relative to S&P500 %1.2f%%\n" % \
            self.percentChange52Relative
        s+= "Market Cap: %1.0f\n" % self.marketCap
        s+= "Shares Outstanding: %1.0f\n" % self.sharesOutstanding
        s+= "Float: %1.0f\n" % self.sharesFloat
        if self.annualDividend != None:
            s+= "Annual dividend: $%1.2f\n" % self.annualDividend
        else:
            s+= "Annual dividend: None\n" 

        if self.dividendYield !=None:
            s+= "Dividend Yield: %1.2f%%\n" % self.dividendYield
        else:
            s+= "Dividend Yield: None\n" 
        s+= "Last split factor %d on %s\n" % \
            (self.lastSplitFactor,
             self.lastSplitFactorDate.strftime('%Y-%m-%d'))
        s+= "Per share Book value (mrq*): %1.2f\n" % self.bookValueMRQPerShare
        s+= "Per share Earnings (ttm): %1.2f\n" % self.earningsTTMPerShare
        s+= "Per share Earnings (mrq): %1.2f\n" % self.earningsMRQPerShare
        s+= "Per share Sales (ttm): %1.2f\n" % self.salesTTMPerShare
        s+= "Per share Cash (mrq*): %1.2f\n" % self.cashMRQPerShare
        if self.pOverBookMRQ != None:
            s+= "Price/Book (mrq*): %1.2f\n" % self.pOverBookMRQ
        else:
            s+= "Price/Book (mrq*): N/A\n" 

        if self.pOverEarningsTTM != None:
            s+= "Price/Earnings (ttm): %1.2f\n" % self.pOverEarningsTTM
        else:
            s+= "Price/Earnings (ttm): N/A\n" 

        s+= "Price/Sales (ttm): %1.2f\n" % self.pOverSalesTTM
        s+= "Sales (ttm): %1.0f\n" % self.salesTTM
        s+= "EBITDA (ttm*): %1.0f\n" % self.EBITDATTM
        s+= "Income available to common (ttm): %1.0f\n" % self.incomeAvailableTTM
        s+= "Profit Margin (ttm): %1.2f%%\n" % self.profitMarginTTM
        s+= "Operating Margin (ttm): %1.2f%%\n" % self.operatingMarginTTM
        s+= "Return on assets (ttm): %1.2f%%\n" % self.returnAssetsTTM
        s+= "Return on equity (ttm): %1.2f%%\n" % self.returnEquityTTM
        s+= "Current Ratio (mrq*): %1.2f\n" % self.currentRatioMRQ
        s+= "Debt/Equity (mrq*): %1.2f\n" % self.debtEquityMRQ
        s+= "Total Cash (mrq*): $%1.0f\n" % self.totalCashMRQ
        s+= "Shares Short: %1.0f\n" % self.shortShares
        s+= "Short Percent of Float: %1.2f%%\n" % self.shortPercentFloat
        s+= "Shares Short (prior month): %1.0f\n" % self.shortSharesPrior
        s+= "Short Ratio: %1.2f\n" % self.shortRatio
        s+= "Short Daily Volume: %1.0f\n" % self.shortDailyVolume
        return s

    def _parse(self):
        ticker = self._ticker.lower()
        firstLetter = ticker[0]
        url = 'http://biz.yahoo.com/p/%s/%s.html' % (firstLetter, ticker)
        fh = os.popen( 'lynx -dump %s' % url)

        lines = fh.readlines()
        index = -1
        N  = len(lines)
        while(index<N-1):
            index += 1
            line = lines[index]

            if self.date==None:
                m = re.match('^\s+Statistics at a Glance .* (\d+-\w+-\d+)$',
                             line)
                if m:
                    self.date = strptime(m.group(1), '%d-%b-%Y')
                    continue
            if self.min52==None and re.match('^\s+52-Week Low', line):
                index += 1
                line = lines[index]
                m = re.match('^\s+on\s+(\d+-\w+-\d+)\s+\$([\d.]+)', line)
                if m:
                    self.min52Date = strptime(m.group(1), '%d-%b-%Y')
                    self.min52 = float(m.group(2))
                    continue
            if self.max52==None and re.match('^\s+52-Week\s+High', line):
                index += 1
                line = lines[index]
                m = re.match('^\s+on\s+(\d+-\w+-\d+)\s+\$([\d.]+)', line)
                if m:
                    self.max52Date = strptime(m.group(1), '%d-%b-%Y')
                    self.max52 = float(m.group(2))
                    continue
            if self.recentPrice == None:
                m = re.match('^\s+Recent Price\s+\$([\d.]+)$', line)
                if m:
                    self.recentPrice = float(m.group(1))
                    continue
            if self.beta == None:
                m = re.match('^\s+Beta\s+([\d.]+)$', line)
                if m:
                    self.beta = float(m.group(1))
                    continue
            if self.volume3month == None:
                m = re.match('^\s+Daily Volume\s+\(3-month avg\)\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.volume3month = bignum( q, m.group(2) )
                    continue
            if self.volume10day == None:
                m = re.match('^\s+Daily Volume\s+\(10-day avg\)\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.volume10day = bignum( q, m.group(2) )
                    continue
            if self.percentChange52 == None:
                m = re.match('^\s+52-Week Change\s+([-+\d.]+)%$', line)
                if m:
                    self.percentChange52 = float( m.group(1) )
                    continue
            if self.percentChange52Relative == None:
                m = re.match('^\s+relative to S&P500\s+([-+\d.]+)%$', line)
                if m:
                    self.percentChange52Relative = float( m.group(1) )
                    continue
            if self.marketCap == None:
                m = re.match('^\s+Market Capitalization\s+\$([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.marketCap = bignum( q, m.group(2) )
                    continue
            if self.sharesOutstanding == None:
                m = re.match('^\s+Shares Outstanding\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.sharesOutstanding = bignum( q, m.group(2) )
                    continue
            if self.sharesFloat == None:
                m = re.match('^\s+Float\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.sharesFloat = bignum( q, m.group(2) )
                    continue
            if self.annualDividend == None:
                m = re.match('^\s+Annual Dividend\s+\(indicated\)\s+\$?([\d.]+)$', line)
                if m:
                    self.annualDividend = float( m.group(1) )
                    continue
            if self.dividendYield == None:
                m = re.match('^\s+Dividend Yield\s+([\d.]+)%$', line)
                if m:
                    self.dividendYield = float( m.group(1) )
                    continue
            if self.lastSplitFactor == None:
                m = re.match('^\s+Last Split: factor\s+([\d.]+)\s+on\s+(\d+-\w+-\d+)$', line)
                if m:
                    self.lastSplitFactor = float( m.group(1) )
                    self.lastSplitFactorDate = strptime(m.group(2), '%d-%b-%Y')
                    continue
            if self.bookValueMRQPerShare == None:
                m = re.match('^\s+Book Value\s+\(mrq\*?\)\s+(-)?\$([\d.]+)\s*$', line)
                if m:
                    self.bookValueMRQPerShare = float(m.group(2))
                    if m.group(1)=='-':
                        self.bookValueMRQPerShare = -self.bookValueMRQPerShare
                    continue
            if self.earningsTTMPerShare == None:
                m = re.match('^\s+Earnings\s+\(ttm\)\s+(-)?\$([\d.]+)$', line)
                if m:
                    self.earningsTTMPerShare = float( m.group(2) )
                    if m.group(1)=='-':
                        self.earningsTTMPerShare = -self.earningsTTMPerShare
                        continue
            if self.salesTTMPerShare == None:
                m = re.match('^\s+Sales\s+\(ttm\)\s+\$([\d.]+)$', line)
                if m:
                    self.salesTTMPerShare = float( m.group(1) )
                    continue
            if self.earningsMRQPerShare == None:
                m = re.match('^\s+Earnings\s+\(mrq\)\s+(-)?\$([\d.]+)$', line)
                if m:
                    self.earningsMRQPerShare = float( m.group(2) )
                    if m.group(1)=='-':
                        self.earningsMRQPerShare = -self.earningsMRQPerShare
                        continue
            if self.cashMRQPerShare == None:
                m = re.match('^\s+Cash\s+\(mrq\*?\)\s+\$([\d.]+)$', line)
                if m:
                    self.cashMRQPerShare = float( m.group(1) )
                    continue
            if self.pOverBookMRQ == None:
                m = re.match('^\s+Price/Book\s+\(mrq\*?\)\s+([\d.]+)$', line)
                if m:
                    self.pOverBookMRQ = float(m.group(1))
                    continue
            if self.pOverEarningsTTM == None:
                m = re.match('^\s+Price/Earnings\s+\(ttm\)\s+([\d.]+)$', line)
                if m:
                    self.pOverEarningsTTM = float(m.group(1))
                    continue
            if self.pOverSalesTTM == None:
                m = re.match('^\s+Price/Sales\s+\(ttm\)\s+([\d.]+)$', line)
                if m:
                    self.pOverSalesTTM = float(m.group(1))
                    continue
            if self.salesTTM == None:
                m = re.match('^\s+Sales\s+\(ttm\)\s+\$([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.salesTTM = bignum( q, m.group(2) )
                    continue
            if self.EBITDATTM == None:
                m = re.match('^\s+EBITDA\s+\(ttm\*?\)\s+(-)?\$([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(2).replace(',', '')
                    self.EBITDATTM = bignum( q, m.group(3) )
                    if m.group(1)=='-':
                        self.EBITDATTM = -self.EBITDATTM
                        continue
            if self.incomeAvailableTTM == None:
                m = re.match('^\s+Income available to common\s+\(ttm\)\s+(-)?\$([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(2).replace(',', '')
                    self.incomeAvailableTTM = bignum( q, m.group(3) )
                    if m.group(1)=='-':
                        self.incomeAvailableTTM = -self.incomeAvailableTTM
                        continue
            if self.profitMarginTTM == None:
                m = re.match('^\s+Profit Margin\s+\(ttm\)\s+([-\d.]+)%$', line)
                if m:
                    self.profitMarginTTM = float( m.group(1) )
                    continue
            if self.operatingMarginTTM == None:
                m = re.match('^\s+Operating Margin\s+\(ttm\)\s+([-\d.]+)%$', line)
                if m:
                    self.operatingMarginTTM = float( m.group(1) )
                    continue
            if self.returnAssetsTTM == None:
                    m = re.match('^\s+Return on Assets\s+\(ttm\)\s+([-\d.]+)%$', line)
                    if m:
                        self.returnAssetsTTM = float( m.group(1) )
                        continue
            if self.returnEquityTTM == None:
                m = re.match('^\s+Return on Equity\s+\(ttm\)\s+([-\d.]+)%$', line)
                if m:
                    self.returnEquityTTM = float( m.group(1) )
                    continue
            if self.currentRatioMRQ == None:
                m = re.match('^\s+Current Ratio\s+\(mrq\*?\)\s+([\d.]+)$', line)
                if m:
                    self.currentRatioMRQ = float( m.group(1) )
                    continue
            if self.debtEquityMRQ == None:
                m = re.match('^\s+(?:Long-Term )?Debt/Equity\s+\(mrq\*?\)\s+([\d.]+)$', line)
                if m:
                    self.debtEquityMRQ = float( m.group(1) )
                    continue
            if self.totalCashMRQ == None:
                m = re.match('^\s+Total Cash\s+\(mrq\*?\)\s+\$([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.totalCashMRQ = bignum( q, m.group(2) )
                    continue
            if self.shortShares == None:
                m = re.match('^\s+Shares Short\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.shortShares = bignum( q, m.group(2) )
                    continue
            if self.shortPercentFloat == None:
                m = re.match('^\s+Percent of Float\s+([\d.]+)%$', line)
                if m:
                    self.shortPercentFloat = float( m.group(1) )
                    continue
            if self.shortSharesPrior == None:
                m = re.match('^\s+\(Prior Month\)\s+([\d.]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.shortSharesPrior = bignum( q, m.group(2) )
                    continue
            if self.shortRatio == None:
                m = re.match('^\s+Short Ratio\s+([\d.]+)$', line)
                if m:
                    self.shortRatio = float( m.group(1) )
                    continue
            if self.shortDailyVolume == None:
                m = re.match('^\s+Daily Volume\s+([\d.,]+)(K|M|B)?$', line)
                if m:
                    q = m.group(1).replace(',', '')
                    self.shortDailyVolume = bignum( q, m.group(2) )
                    continue
        # With successful this should be not None
        if self.min52==None:
            raise ValueError, string.join(lines, '\n')

    def insert_dbase(self):
        percentChange52 = self.percentChange52
        percentChange52Relative = self.percentChange52Relative
        annualDividend = self.annualDividend
        dividendYield = self.dividendYield
        earningsTTMPerShare = self.earningsTTMPerShare
        earningsMRQPerShare = self.earningsMRQPerShare
        salesTTMPerShare = self.salesTTMPerShare
        pOverBookMRQ = self.pOverBookMRQ
        pOverEarningsTTM = self.pOverEarningsTTM
        pOverSalesTTM = self.pOverSalesTTM
        profitMarginTTM = self.profitMarginTTM
        operatingMarginTTM = self.operatingMarginTTM
        lastSplitFactor = self.lastSplitFactor
        lastSplitFactorDate = self.lastSplitFactorDate
        currentRatioMRQ = self.currentRatioMRQ
        beta = self.beta
        returnAssetsTTM = self.returnAssetsTTM
        returnEquityTTM = self.returnEquityTTM
        debtEquityMRQ = self.debtEquityMRQ
        salesTTM = self.salesTTM
        EBITDATTM = self.EBITDATTM
        cashMRQPerShare = self.cashMRQPerShare
        totalCashMRQ = self.totalCashMRQ
        
        if annualDividend==None:
            annualDividend = 'NULL'
        if dividendYield==None:
            dividendYield = 'NULL'
        if earningsTTMPerShare==None:
            earningsTTMPerShare = 'NULL'
        if earningsMRQPerShare==None:
            earningsMRQPerShare='NULL'
        if salesTTMPerShare==None:
            salesTTMPerShare='NULL'
        if pOverBookMRQ==None:
            pOverBookMRQ = 'NULL'
        if pOverEarningsTTM==None:
            pOverEarningsTTM = 'NULL'
        if pOverSalesTTM==None:
            pOverSalesTTM = 'NULL'
        if profitMarginTTM==None:
            profitMarginTTM = 'NULL'
        if operatingMarginTTM==None:
            operatingMarginTTM = 'NULL'
        if returnAssetsTTM==None:
            returnAssetsTTM = 'NULL'
        if returnEquityTTM==None:
            returnEquityTTM = 'NULL'
        if lastSplitFactor==None or lastSplitFactor==None:
            lastSplitFactor = 'NULL'
            lastSplitFactorDate = '0000-00-00'
        if currentRatioMRQ==None:
            currentRatioMRQ = 'NULL'
        if beta==None:
            beta = 'NULL'
        if debtEquityMRQ==None:
            debtEquityMRQ='NULL'
        if EBITDATTM==None:
            EBITDATTM='NULL'
        if salesTTM==None:
            salesTTM='NULL'
        if totalCashMRQ==None:
            totalCashMRQ = 'NULL'
        if cashMRQPerShare==None:
            cashMRQPerShare = 'NULL'
        if percentChange52Relative==None:
            percentChange52Relative = 'NULL'
        if percentChange52==None:
            percentChange52 = 'NULL'


        q = "INSERT INTO profile VALUES ( '%s', '%s', '%s', %s, '%s', %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, '%s', %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % \
            ( self._ticker,
              self.date,
              self.min52Date,
              self.min52,
              self.max52Date,
              self.max52,
              self.recentPrice,
              beta,
              self.volume3month,
              self.volume10day,
              percentChange52,
              percentChange52Relative,
              self.marketCap,
              self.sharesOutstanding,
              self.sharesFloat,
              annualDividend,
              dividendYield,
              lastSplitFactor,
              lastSplitFactorDate,
              self.bookValueMRQPerShare,
              earningsTTMPerShare,
              earningsMRQPerShare,
              salesTTMPerShare,
              cashMRQPerShare,
              pOverBookMRQ,
              pOverEarningsTTM,
              pOverSalesTTM,
              salesTTM,
              EBITDATTM,
              self.incomeAvailableTTM,
              profitMarginTTM,
              operatingMarginTTM,
              returnAssetsTTM,
              returnEquityTTM,
              currentRatioMRQ,
              debtEquityMRQ,
              totalCashMRQ,
              self.shortShares,
              self.shortPercentFloat,
              self.shortSharesPrior,
              self.shortRatio,
              self.shortDailyVolume
              )
        try:
            _theDBase.cursor().execute(q)
        except:
            raise RuntimeError, q




























More information about the Python-list mailing list