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