Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/finance/stock.py
4077 views
1
"""
2
Stock Market Price Series
3
4
AUTHORS:
5
6
- William Stein, 2008
7
8
- Brett Nakayama, 2008
9
10
- Chris Swierczewski, 2008
11
12
TESTS::
13
14
sage: ohlc = sage.finance.stock.OHLC('18-Aug-04', 100.01, 104.06, 95.96, 100.34, 22353092)
15
sage: loads(dumps(ohlc)) == ohlc
16
True
17
"""
18
19
import urllib
20
from sage.structure.all import Sequence
21
from datetime import date
22
23
class OHLC:
24
def __init__(self, timestamp, open, high, low, close, volume):
25
"""
26
Open, high, low, and close information for a stock. Also stores
27
a timestamp for that data along with the volume.
28
29
INPUT:
30
31
- ``timestamp`` -- string
32
33
- ``open``, ``high``, ``low``, ``close`` -- float
34
35
- ``volume`` -- int
36
37
EXAMPLES::
38
39
sage: sage.finance.stock.OHLC('18-Aug-04', 100.01, 104.06, 95.96, 100.34, 22353092)
40
18-Aug-04 100.01 104.06 95.96 100.34 22353092
41
"""
42
self.timestamp = timestamp
43
self.open=float(open); self.high=float(high); self.low=float(low); self.close=float(close)
44
self.volume=int(volume)
45
46
def __repr__(self):
47
"""
48
Return string representation of stock OHLC data.
49
50
EXAMPLES::
51
52
sage: sage.finance.stock.OHLC('18-Aug-04', 100.01, 104.06, 95.96, 100.34, 22353092).__repr__()
53
' 18-Aug-04 100.01 104.06 95.96 100.34 22353092'
54
"""
55
return '%10s %4.2f %4.2f %4.2f %4.2f %10d'%(self.timestamp, self.open, self.high,
56
self.low, self.close, self.volume)
57
58
def __cmp__(self, other):
59
"""
60
Compare ``self`` and ``other``.
61
62
EXAMPLES::
63
64
sage: ohlc = sage.finance.stock.OHLC('18-Aug-04', 100.01, 104.06, 95.96, 100.34, 22353092)
65
sage: ohlc2 = sage.finance.stock.OHLC('18-Aug-04', 101.01, 104.06, 95.96, 100.34, 22353092)
66
sage: cmp(ohlc, ohlc2)
67
-1
68
"""
69
if not isinstance(other, OHLC):
70
return cmp(type(self), type(other))
71
return cmp((self.timestamp, self.open, self.high, self.low, self.close, self.volume),
72
(other.timestamp, other.open, other.high, other.low, other.close, other.volume))
73
74
class Stock:
75
"""
76
Class for retrieval of stock market information.
77
"""
78
def __init__(self, symbol, cid=''):
79
"""
80
Create a ``Stock`` object. Optional initialization by ``cid``: an
81
identifier for each equity used by Google Finance.
82
83
INPUT:
84
85
- ``symbol`` -- string, a ticker symbol (with or without market).
86
Format: ``"MARKET:SYMBOL"`` or ``"SYMBOL"``. If you don't
87
supply the market, it is assumed to be NYSE or NASDAQ.
88
e.g. "goog" or "OTC:NTDOY"
89
90
- ``cid`` -- Integer, a Google contract ID (optional).
91
92
93
.. NOTE::
94
95
Currently, the symbol and cid do not have to match. When using
96
``google()``, the cid will take precedence.
97
98
EXAMPLES::
99
100
sage: S = finance.Stock('ibm')
101
sage: S # random; optional -- internet
102
IBM (127.48)
103
"""
104
self.symbol = symbol.upper()
105
self.cid = cid
106
107
def __repr__(self):
108
"""
109
Return string representation of this stock.
110
111
EXAMPLES::
112
113
sage: finance.Stock('ibm').__repr__() # random; optional -- internet
114
'IBM (127.47)'
115
"""
116
return "%s (%s)"%(self.symbol, self.market_value())
117
118
def market_value(self):
119
"""
120
Return the current market value of this stock.
121
122
OUTPUT:
123
124
A Python float.
125
126
EXAMPLES::
127
128
sage: finance.Stock('goog').market_value() # random; optional - internet
129
575.83000000000004
130
"""
131
return float(self.yahoo()['price'])
132
133
def yahoo(self):
134
"""
135
Get Yahoo current price data for this stock.
136
137
OUTPUT:
138
139
A dictionary.
140
141
EXAMPLES::
142
143
sage: finance.Stock('GOOG').yahoo() # random; optional -- internet
144
{'stock_exchange': '"NasdaqNM"', 'market_cap': '181.1B', '200day_moving_avg': '564.569', '52_week_high': '747.24', 'price_earnings_growth_ratio': '1.04', 'price_sales_ratio': '10.16', 'price': '576.48', 'earnings_per_share': '14.463', '50day_moving_avg': '549.293', 'avg_daily_volume': '6292480', 'volume': '1613507', '52_week_low': '412.11', 'short_ratio': '1.00', 'price_earnings_ratio': '40.50', 'dividend_yield': 'N/A', 'dividend_per_share': '0.00', 'price_book_ratio': '7.55', 'ebitda': '6.513B', 'change': '-9.32', 'book_value': '77.576'}
145
"""
146
url = 'http://finance.yahoo.com/d/quotes.csv?s=%s&f=%s' % (self.symbol, 'l1c1va2xj1b4j4dyekjm3m4rr5p5p6s7')
147
values = urllib.urlopen(url).read().strip().strip('"').split(',')
148
data = {}
149
data['price'] = values[0]
150
data['change'] = values[1]
151
data['volume'] = values[2]
152
data['avg_daily_volume'] = values[3]
153
data['stock_exchange'] = values[4]
154
data['market_cap'] = values[5]
155
data['book_value'] = values[6]
156
data['ebitda'] = values[7]
157
data['dividend_per_share'] = values[8]
158
data['dividend_yield'] = values[9]
159
data['earnings_per_share'] = values[10]
160
data['52_week_high'] = values[11]
161
data['52_week_low'] = values[12]
162
data['50day_moving_avg'] = values[13]
163
data['200day_moving_avg'] = values[14]
164
data['price_earnings_ratio'] = values[15]
165
data['price_earnings_growth_ratio'] = values[16]
166
data['price_sales_ratio'] = values[17]
167
data['price_book_ratio'] = values[18]
168
data['short_ratio'] = values[19]
169
return data
170
171
def google(self,startdate='Jan+1,+1900',enddate=date.today().strftime("%b+%d,+%Y"), histperiod='daily'):
172
"""
173
Return an immutable sequence of historical price data
174
for this stock, obtained from Google. OHLC data is stored
175
internally as well. By default, returns the past year's daily
176
OHLC data.
177
178
Dates ``startdate`` and ``enddate`` should be formatted
179
``'Mon+d,+yyyy'``, where ``'Mon'`` is a three character abbreviation
180
of the month's name.
181
182
.. NOTE::
183
184
Google Finance returns the past year's financial data by default
185
when ``startdate`` is set too low from the equity's date of going
186
public. By default, this function only looks at the NASDAQ and
187
NYSE markets. However, if you specified the market during
188
initialization of the stock (i.e. ``finance.Stock("OTC:NTDOY")``),
189
``Stock.google()`` will give correct results.
190
191
INPUT:
192
193
- ``startdate`` -- string, (default: ``'Jan+1,+1900'``)
194
195
- ``enddate`` -- string, (default: current date)
196
197
- ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)
198
199
OUTPUT:
200
201
A sequence.
202
203
EXAMPLES:
204
205
We get the first five days of VMware's stock history::
206
207
sage: finance.Stock('vmw').google()[:5] # optional -- internet
208
[
209
28-Nov-07 80.57 88.49 80.57 87.69 7496000,
210
29-Nov-07 90.91 93.20 89.50 90.85 5497600,
211
30-Nov-07 95.39 95.60 89.85 91.37 4750200,
212
3-Dec-07 89.87 96.00 88.70 94.97 4401100,
213
4-Dec-07 92.26 97.10 92.05 95.08 2896600
214
]
215
216
sage: finance.Stock('F').google('Jan+3,+1978', 'Jul+7,+2008')[:5] # optional -- internet
217
[
218
3-Jan-78 0.00 1.93 1.89 1.89 1618200,
219
4-Jan-78 0.00 1.89 1.87 1.88 2482700,
220
5-Jan-78 0.00 1.89 1.84 1.84 2994900,
221
6-Jan-78 0.00 1.84 1.82 1.83 3042500,
222
9-Jan-78 0.00 1.81 1.79 1.81 3916400
223
]
224
225
Note that when ``startdate`` is too far prior to a stock's actual start
226
date, Google Finance defaults to a year's worth of stock history
227
leading up to the specified end date. For example, Apple's (AAPL)
228
stock history only dates back to September 7, 1984::
229
230
sage: finance.Stock('AAPL').google('Sep+1,+1900', 'Jan+1,+2000')[0:5] # optional -- internet
231
[
232
4-Jan-99 0.00 10.56 10.00 10.31 34031600,
233
5-Jan-99 0.00 10.98 10.38 10.83 50360400,
234
6-Jan-99 0.00 11.03 10.25 10.44 48160800,
235
7-Jan-99 0.00 11.27 10.53 11.25 51036400,
236
8-Jan-99 0.00 11.72 11.00 11.25 24240000
237
]
238
239
Here is an example where we create and get the history of a stock
240
that is not in NASDAQ or NYSE::
241
242
sage: finance.Stock("OTC:NTDOY").google(startdate="Jan+1,+2007", enddate="Jan+1,+2008")[:5] # optional -- internet
243
[
244
3-Jan-07 32.44 32.75 32.30 32.44 156283,
245
4-Jan-07 31.70 32.40 31.20 31.70 222643,
246
5-Jan-07 30.15 30.50 30.15 30.15 65670,
247
8-Jan-07 30.10 30.50 30.00 30.10 130765,
248
9-Jan-07 29.90 30.05 29.60 29.90 103338
249
]
250
251
252
Here, we create a stock by cid, and get historical data.
253
Note that when using historical, if a cid is specified,
254
it will take precedence over the stock's symbol. So, if
255
the symbol and cid do not match, the history based on the
256
contract id will be returned. ::
257
258
sage: sage.finance.stock.Stock("AAPL", 22144).google(startdate='Jan+1,+1990')[:5] #optional -- internet
259
[
260
2-Jan-90 0.00 9.38 8.75 9.31 6542800,
261
3-Jan-90 0.00 9.50 9.38 9.38 7428400,
262
4-Jan-90 0.00 9.69 9.31 9.41 7911200,
263
5-Jan-90 0.00 9.56 9.25 9.44 4404000,
264
8-Jan-90 0.00 9.50 9.25 9.50 3627600
265
]
266
"""
267
cid = self.cid
268
symbol = self.symbol
269
270
if self.cid=='':
271
if ':' in symbol:
272
R = self._get_data('', startdate, enddate, histperiod)
273
else:
274
R = self._get_data('NASDAQ:', startdate, enddate, histperiod)
275
if "Bad Request" in R:
276
R = self._get_data("NYSE:", startdate, enddate, histperiod)
277
else:
278
R = self._get_data('', startdate, enddate, histperiod)
279
if "Bad Request" in R:
280
raise RuntimeError
281
self.__historical = []
282
self.__historical = self._load_from_csv(R)
283
return self.__historical
284
285
def open(self, *args, **kwds):
286
r"""
287
Return a time series containing historical opening prices for this
288
stock. If no arguments are given, will return last acquired historical
289
data. Otherwise, data will be gotten from Google Finance.
290
291
INPUT:
292
293
- ``startdate`` -- string, (default: ``'Jan+1,+1900'``)
294
295
- ``enddate`` -- string, (default: current date)
296
297
- ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)
298
299
OUTPUT:
300
301
A time series -- close price data.
302
303
EXAMPLES:
304
305
You can directly obtain Open data as so::
306
307
sage: finance.Stock('vmw').open(startdate='Jan+1,+2008', enddate='Feb+1,+2008') # optional -- internet
308
[83.0500, 85.4900, 84.9000, 82.0000, 81.2500 ... 82.0000, 58.2700, 54.4900, 55.6000, 56.9800]
309
310
Or, you can initialize stock data first and then extract the Open
311
data::
312
313
sage: c = finance.Stock('vmw')
314
sage: c.google(startdate='Feb+1,+2008', enddate='Mar+1,+2008')[:5] # optional -- internet
315
[
316
31-Jan-08 55.60 57.35 55.52 56.67 2591100,
317
1-Feb-08 56.98 58.14 55.06 57.85 2473000,
318
4-Feb-08 58.00 60.47 56.91 58.05 1816500,
319
5-Feb-08 57.60 59.30 57.17 59.30 1709000,
320
6-Feb-08 60.32 62.00 59.50 61.52 2191100
321
]
322
sage: c.open() # optional -- internet
323
[55.6000, 56.9800, 58.0000, 57.6000, 60.3200 ... 56.5500, 59.3000, 60.0000, 59.7900, 59.2600]
324
325
Otherwise, ``self.google()`` will be called with the default
326
arguments returning a year's worth of data::
327
328
sage: finance.Stock('vmw').open() # random; optional -- internet
329
[52.1100, 60.9900, 59.0000, 56.0500, 57.2500 ... 83.0500, 85.4900, 84.9000, 82.0000, 81.2500]
330
"""
331
332
from time_series import TimeSeries
333
334
if len(args) != 0:
335
return TimeSeries([x.open for x in self.google(*args, **kwds)])
336
337
try:
338
return TimeSeries([x.open for x in self.__historical])
339
except AttributeError:
340
pass
341
342
return TimeSeries([x.open for x in self.google(*args, **kwds)])
343
344
def close(self, *args, **kwds):
345
r"""
346
Return the time series of all historical closing prices for this stock.
347
If no arguments are given, will return last acquired historical data.
348
Otherwise, data will be gotten from Google Finance.
349
350
INPUT:
351
352
- ``startdate`` -- string, (default: ``'Jan+1,+1900'``)
353
354
- ``enddate`` -- string, (default: current date)
355
356
- ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)
357
358
OUTPUT:
359
360
A time series -- close price data.
361
362
EXAMPLES:
363
364
You can directly obtain close data as so::
365
366
sage: finance.Stock('vmw').close(startdate='Jan+1,+2008', enddate='Feb+1,+2008') # optional -- internet
367
[84.9900, 84.6000, 83.9500, 80.4900, 72.9900 ... 83.0000, 54.8700, 56.4200, 56.6700, 57.8500]
368
369
Or, you can initialize stock data first and then extract the Close
370
data::
371
372
sage: c = finance.Stock('vmw')
373
sage: c.google(startdate='Feb+1,+2008', enddate='Mar+1,+2008')[:5] # optional -- internet
374
[
375
31-Jan-08 55.60 57.35 55.52 56.67 2591100,
376
1-Feb-08 56.98 58.14 55.06 57.85 2473000,
377
4-Feb-08 58.00 60.47 56.91 58.05 1816500,
378
5-Feb-08 57.60 59.30 57.17 59.30 1709000,
379
6-Feb-08 60.32 62.00 59.50 61.52 2191100
380
]
381
sage: c.close() # optional -- internet
382
[56.6700, 57.8500, 58.0500, 59.3000, 61.5200 ... 58.2900, 60.1800, 59.8600, 59.9500, 58.6700]
383
384
Otherwise, ``self.google()`` will be called with the default
385
arguments returning a year's worth of data::
386
387
sage: finance.Stock('vmw').close() # random; optional -- internet
388
[57.7100, 56.9900, 55.5500, 57.3300, 65.9900 ... 84.9900, 84.6000, 83.9500, 80.4900, 72.9900]
389
"""
390
391
from time_series import TimeSeries
392
393
if len(args) != 0:
394
return TimeSeries([x.close for x in self.google(*args, **kwds)])
395
396
try:
397
return TimeSeries([x.close for x in self.__historical])
398
except AttributeError:
399
pass
400
401
return TimeSeries([x.close for x in self.google(*args, **kwds)])
402
403
def load_from_file(self, file):
404
r"""
405
Load historical data from a local csv formatted data file. Note
406
that no symbol data is included in Google Finance's csv data.
407
The csv file must be formatted in the following way, just as
408
on Google Finance::
409
410
Timestamp,Open,High,Low,Close,Volume
411
412
INPUT:
413
414
- ``file`` -- local file with Google Finance formatted OHLC data.
415
416
OUTPUT:
417
418
A sequence -- OHLC data.
419
420
EXAMPLES:
421
422
Suppose you have a file in your home directory containing Apple stock
423
OHLC data, such as that from Google Finance, called
424
``AAPL-minutely.csv``. One can load this information into a Stock
425
object like so. Note that the path must be explicit::
426
427
sage: filename = tmp_filename() + '.csv'
428
sage: open(filename,'w').write("Date,Open,High,Low,Close,Volume\n1212405780,187.80,187.80,187.80,187.80,100\n1212407640,187.75,188.00,187.75,188.00,2000\n1212407700,188.00,188.00,188.00,188.00,1000\n1212408000,188.00,188.11,188.00,188.00,2877\n1212408060,188.00,188.00,188.00,188.00,687")
429
sage: finance.Stock('aapl').load_from_file(filename)[:5]
430
[
431
1212408060 188.00 188.00 188.00 188.00 687,
432
1212408000 188.00 188.11 188.00 188.00 2877,
433
1212407700 188.00 188.00 188.00 188.00 1000,
434
1212407640 187.75 188.00 187.75 188.00 2000,
435
1212405780 187.80 187.80 187.80 187.80 100
436
]
437
438
439
Note that since the source file doesn't contain information on which
440
equity the information comes from, the symbol designated at
441
initialization of Stock need not match the source of the data. For
442
example, we can initialize a Stock object with the symbol ``'goog'``,
443
but load data from ``'aapl'`` stock prices::
444
445
sage: finance.Stock('goog').load_from_file(filename)[:5]
446
[
447
1212408060 188.00 188.00 188.00 188.00 687,
448
1212408000 188.00 188.11 188.00 188.00 2877,
449
1212407700 188.00 188.00 188.00 188.00 1000,
450
1212407640 187.75 188.00 187.75 188.00 2000,
451
1212405780 187.80 187.80 187.80 187.80 100
452
]
453
454
This tests a file that doesn't exist::
455
456
sage: finance.Stock("AAPL").load_from_file("I am not a file")
457
Traceback (most recent call last):
458
...
459
IOError: [Errno 2] No such file or directory: 'I am not a file'
460
"""
461
file_obj = open(file, 'r')
462
R = file_obj.read();
463
self.__historical = self._load_from_csv(R)
464
file_obj.close()
465
return self.__historical
466
467
468
def _load_from_csv(self, R):
469
r"""
470
EXAMPLES:
471
472
This indirectly tests ``_load_from_csv()``::
473
474
sage: filename = tmp_filename() + '.csv'
475
sage: open(filename,'w').write("Date,Open,High,Low,Close,Volume\n1212405780,187.80,187.80,187.80,187.80,100\n1212407640,187.75,188.00,187.75,188.00,2000\n1212407700,188.00,188.00,188.00,188.00,1000\n1212408000,188.00,188.11,188.00,188.00,2877\n1212408060,188.00,188.00,188.00,188.00,687")
476
sage: finance.Stock('aapl').load_from_file(filename)
477
[
478
1212408060 188.00 188.00 188.00 188.00 687,
479
1212408000 188.00 188.11 188.00 188.00 2877,
480
1212407700 188.00 188.00 188.00 188.00 1000,
481
1212407640 187.75 188.00 187.75 188.00 2000,
482
1212405780 187.80 187.80 187.80 187.80 100
483
]
484
"""
485
R = R.splitlines()
486
headings = R[0].split(',')
487
hist_data = []
488
for x in reversed(R[1:]):
489
try:
490
timestamp, opn, high, low, close, volume = x.split(',')
491
ohlc = OHLC(timestamp, opn,high,low,close,volume)
492
hist_data.append(ohlc)
493
except ValueError:
494
pass
495
hist_data = Sequence(hist_data,cr=True,universe=lambda x:x, immutable=True)
496
return hist_data
497
498
def _get_data(self, exchange='', startdate='Jan+1,+1900', enddate=date.today().strftime("%b+%d,+%Y"), histperiod='daily'):
499
"""
500
This function is used internally.
501
502
EXAMPLES:
503
504
This indirectly tests the use of ``_get_data()``::
505
506
sage: finance.Stock('aapl').google(startdate='Jan+1,+1990')[:2] # optional -- internet
507
[
508
2-Jan-90 0.00 9.38 8.75 9.31 6542800,
509
3-Jan-90 0.00 9.50 9.38 9.38 7428400
510
]
511
"""
512
symbol = self.symbol
513
cid = self.cid
514
if cid == '':
515
url = 'http://finance.google.com/finance/historical?q=%s%s&startdate=%s&enddate=%s&histperiod=%s&output=csv'%(exchange, symbol.upper(), startdate, enddate, histperiod)
516
else:
517
url = 'http://finance.google.com/finance/historical?cid=%s&startdate=%s&enddate=%s&histperiod=%s&output=csv'%(cid, startdate, enddate, histperiod)
518
return urllib.urlopen(url).read()
519
520