Skip to content

Commit 2b4fd7c

Browse files
committed
Merge pull request #3822 from gliptak/googleoptions
Change Finance Options signatures and deprecate year/month parameters
2 parents 4c3cf70 + 62e4168 commit 2b4fd7c

File tree

3 files changed

+105
-56
lines changed

3 files changed

+105
-56
lines changed

doc/source/release.rst

+3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ pandas 0.11.1
156156
``load`` will give deprecation warning.
157157
- the ``method`` and ``axis`` arguments of ``DataFrame.replace()`` are
158158
deprecated
159+
- set FutureWarning to require data_source, and to replace year/month with
160+
expiry date in pandas.io options. This is in preparation to add options
161+
data from google (:issue:`3822`)
159162
- the ``method`` and ``axis`` arguments of ``DataFrame.replace()`` are
160163
deprecated
161164
- Implement ``__nonzero__`` for ``NDFrame`` objects (:issue:`3691`, :issue:`3696`)

pandas/io/data.py

+46-56
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import urllib
1111
import urllib2
1212
import time
13+
import warnings
1314

1415
from zipfile import ZipFile
1516
from pandas.util.py3compat import StringIO, BytesIO, bytes_to_str
@@ -111,12 +112,7 @@ def get_quote_yahoo(symbols):
111112
urlStr = 'http://finance.yahoo.com/d/quotes.csv?s=%s&f=%s' % (
112113
sym_list, request)
113114

114-
try:
115-
lines = urllib2.urlopen(urlStr).readlines()
116-
except Exception, e:
117-
s = "Failed to download:\n{0}".format(e)
118-
print (s)
119-
return None
115+
lines = urllib2.urlopen(urlStr).readlines()
120116

121117
for line in lines:
122118
fields = line.decode('utf-8').strip().split(',')
@@ -539,7 +535,7 @@ def _parse_options_data(table):
539535

540536
class Options(object):
541537
"""
542-
This class fetches call/put data for a given stock/exipry month.
538+
This class fetches call/put data for a given stock/expiry month.
543539
544540
It is instantiated with a string representing the ticker symbol.
545541
@@ -553,7 +549,7 @@ class Options(object):
553549
Examples
554550
--------
555551
# Instantiate object with ticker
556-
>>> aapl = Options('aapl')
552+
>>> aapl = Options('aapl', 'yahoo')
557553
558554
# Fetch September 2012 call data
559555
>>> calls = aapl.get_call_data(9, 2012)
@@ -576,24 +572,25 @@ class Options(object):
576572
577573
"""
578574

579-
def __init__(self, symbol):
575+
def __init__(self, symbol, data_source=None):
580576
""" Instantiates options_data with a ticker saved as symbol """
581577
self.symbol = str(symbol).upper()
578+
if (data_source is None):
579+
warnings.warn("Options(symbol) is deprecated, use Options(symbol, data_source) instead",
580+
FutureWarning)
581+
data_source = "yahoo"
582+
if (data_source != "yahoo"):
583+
raise NotImplementedError("currently only yahoo supported")
582584

583-
def get_options_data(self, month=None, year=None):
585+
def get_options_data(self, month=None, year=None, expiry=None):
584586
"""
585587
Gets call/put data for the stock with the expiration data in the
586588
given month and year
587589
588590
Parameters
589591
----------
590-
month: number, int, optional(default=None)
591-
The month the options expire. This should be either 1 or 2
592-
digits.
593-
594-
year: number, int, optional(default=None)
595-
The year the options expire. This sould be a 4 digit int.
596-
592+
expiry: datetime.date, optional(default=None)
593+
The date when options expire (defaults to current month)
597594
598595
Returns
599596
-------
@@ -609,7 +606,7 @@ def get_options_data(self, month=None, year=None):
609606
When called, this function will add instance variables named
610607
calls and puts. See the following example:
611608
612-
>>> aapl = Options('aapl') # Create object
609+
>>> aapl = Options('aapl', 'yahoo') # Create object
613610
>>> aapl.calls # will give an AttributeError
614611
>>> aapl.get_options_data() # Get data and set ivars
615612
>>> aapl.calls # Doesn't throw AttributeError
@@ -621,6 +618,8 @@ def get_options_data(self, month=None, year=None):
621618
representations of the month and year for the expiry of the
622619
options.
623620
"""
621+
year, month = self._try_parse_dates(year,month,expiry)
622+
624623
from lxml.html import parse
625624

626625
if month and year: # try to get specified month from yahoo finance
@@ -659,19 +658,15 @@ def get_options_data(self, month=None, year=None):
659658

660659
return [call_data, put_data]
661660

662-
def get_call_data(self, month=None, year=None):
661+
def get_call_data(self, month=None, year=None, expiry=None):
663662
"""
664663
Gets call/put data for the stock with the expiration data in the
665664
given month and year
666665
667666
Parameters
668667
----------
669-
month: number, int, optional(default=None)
670-
The month the options expire. This should be either 1 or 2
671-
digits.
672-
673-
year: number, int, optional(default=None)
674-
The year the options expire. This sould be a 4 digit int.
668+
expiry: datetime.date, optional(default=None)
669+
The date when options expire (defaults to current month)
675670
676671
Returns
677672
-------
@@ -683,7 +678,7 @@ def get_call_data(self, month=None, year=None):
683678
When called, this function will add instance variables named
684679
calls and puts. See the following example:
685680
686-
>>> aapl = Options('aapl') # Create object
681+
>>> aapl = Options('aapl', 'yahoo') # Create object
687682
>>> aapl.calls # will give an AttributeError
688683
>>> aapl.get_call_data() # Get data and set ivars
689684
>>> aapl.calls # Doesn't throw AttributeError
@@ -694,6 +689,8 @@ def get_call_data(self, month=None, year=None):
694689
repsectively, two digit representations of the month and year
695690
for the expiry of the options.
696691
"""
692+
year, month = self._try_parse_dates(year,month,expiry)
693+
697694
from lxml.html import parse
698695

699696
if month and year: # try to get specified month from yahoo finance
@@ -727,19 +724,15 @@ def get_call_data(self, month=None, year=None):
727724

728725
return call_data
729726

730-
def get_put_data(self, month=None, year=None):
727+
def get_put_data(self, month=None, year=None, expiry=None):
731728
"""
732729
Gets put data for the stock with the expiration data in the
733730
given month and year
734731
735732
Parameters
736733
----------
737-
month: number, int, optional(default=None)
738-
The month the options expire. This should be either 1 or 2
739-
digits.
740-
741-
year: number, int, optional(default=None)
742-
The year the options expire. This sould be a 4 digit int.
734+
expiry: datetime.date, optional(default=None)
735+
The date when options expire (defaults to current month)
743736
744737
Returns
745738
-------
@@ -764,6 +757,8 @@ def get_put_data(self, month=None, year=None):
764757
repsectively, two digit representations of the month and year
765758
for the expiry of the options.
766759
"""
760+
year, month = self._try_parse_dates(year,month,expiry)
761+
767762
from lxml.html import parse
768763

769764
if month and year: # try to get specified month from yahoo finance
@@ -798,7 +793,7 @@ def get_put_data(self, month=None, year=None):
798793
return put_data
799794

800795
def get_near_stock_price(self, above_below=2, call=True, put=False,
801-
month=None, year=None):
796+
month=None, year=None, expiry=None):
802797
"""
803798
Cuts the data frame opt_df that is passed in to only take
804799
options that are near the current stock price.
@@ -810,19 +805,15 @@ def get_near_stock_price(self, above_below=2, call=True, put=False,
810805
should be taken
811806
812807
call: bool
813-
Tells the function weather or not it should be using
808+
Tells the function whether or not it should be using
814809
self.calls
815810
816811
put: bool
817812
Tells the function weather or not it should be using
818813
self.puts
819814
820-
month: number, int, optional(default=None)
821-
The month the options expire. This should be either 1 or 2
822-
digits.
823-
824-
year: number, int, optional(default=None)
825-
The year the options expire. This sould be a 4 digit int.
815+
expiry: datetime.date, optional(default=None)
816+
The date when options expire (defaults to current month)
826817
827818
Returns
828819
-------
@@ -831,6 +822,8 @@ def get_near_stock_price(self, above_below=2, call=True, put=False,
831822
desired. If there isn't data as far out as the user has asked for
832823
then
833824
"""
825+
year, month = self._try_parse_dates(year,month,expiry)
826+
834827
price = float(get_quote_yahoo([self.symbol])['last'])
835828

836829
if call:
@@ -844,13 +837,6 @@ def get_near_stock_price(self, above_below=2, call=True, put=False,
844837
except AttributeError:
845838
df_c = self.get_call_data(month, year)
846839

847-
# NOTE: For some reason the put commas in all values >1000. We remove
848-
# them here
849-
df_c.Strike = df_c.Strike.astype(str).apply(lambda x: \
850-
x.replace(',', ''))
851-
# Now make sure Strike column has dtype float
852-
df_c.Strike = df_c.Strike.astype(float)
853-
854840
start_index = np.where(df_c['Strike'] > price)[0][0]
855841

856842
get_range = range(start_index - above_below,
@@ -872,13 +858,6 @@ def get_near_stock_price(self, above_below=2, call=True, put=False,
872858
except AttributeError:
873859
df_p = self.get_put_data(month, year)
874860

875-
# NOTE: For some reason the put commas in all values >1000. We remove
876-
# them here
877-
df_p.Strike = df_p.Strike.astype(str).apply(lambda x: \
878-
x.replace(',', ''))
879-
# Now make sure Strike column has dtype float
880-
df_p.Strike = df_p.Strike.astype(float)
881-
882861
start_index = np.where(df_p.Strike > price)[0][0]
883862

884863
get_range = range(start_index - above_below,
@@ -897,11 +876,21 @@ def get_near_stock_price(self, above_below=2, call=True, put=False,
897876
else:
898877
return chop_put
899878

879+
def _try_parse_dates(self, year, month, expiry):
880+
if year is not None or month is not None:
881+
warnings.warn("month, year arguments are deprecated, use expiry instead",
882+
FutureWarning)
883+
884+
if expiry is not None:
885+
year=expiry.year
886+
month=expiry.month
887+
return year, month
888+
900889
def get_forward_data(self, months, call=True, put=False, near=False,
901890
above_below=2):
902891
"""
903892
Gets either call, put, or both data for months starting in the current
904-
month and going out in the future a spcified amount of time.
893+
month and going out in the future a specified amount of time.
905894
906895
Parameters
907896
----------
@@ -933,6 +922,7 @@ def get_forward_data(self, months, call=True, put=False, near=False,
933922
If asked for, a DataFrame containing put data from the current
934923
month to the current month plus months.
935924
"""
925+
warnings.warn("get_forward_data() is deprecated", FutureWarning)
936926
in_months = range(cur_month, cur_month + months + 1)
937927
in_years = [cur_year] * (months + 1)
938928

pandas/io/tests/test_yahoo.py

+56
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
import nose
33
from datetime import datetime
4+
import warnings
45

56
import pandas as pd
67
import pandas.io.data as web
@@ -96,6 +97,61 @@ def test_get_data(self):
9697
t= np.array(pan)
9798
assert np.issubdtype(t.dtype, np.floating)
9899

100+
@network
101+
def test_options(self):
102+
try:
103+
import lxml
104+
except ImportError:
105+
raise nose.SkipTest
106+
# aapl has monthlies
107+
aapl = web.Options('aapl', 'yahoo')
108+
today = datetime.today()
109+
year = today.year
110+
month = today.month+1
111+
if (month>12):
112+
year = year +1
113+
month = 1
114+
expiry=datetime(year, month, 1)
115+
(calls, puts) = aapl.get_options_data(expiry=expiry)
116+
assert len(calls)>1
117+
assert len(puts)>1
118+
(calls, puts) = aapl.get_near_stock_price(call=True, put=True, expiry=expiry)
119+
assert len(calls)==5
120+
assert len(puts)==5
121+
calls = aapl.get_call_data(expiry=expiry)
122+
assert len(calls)>1
123+
puts = aapl.get_put_data(expiry=expiry)
124+
assert len(puts)>1
125+
126+
@network
127+
def test_options_warnings(self):
128+
try:
129+
import lxml
130+
except ImportError:
131+
raise nose.SkipTest
132+
with warnings.catch_warnings(record=True) as w:
133+
warnings.resetwarnings()
134+
# Cause all warnings to always be triggered.
135+
warnings.simplefilter("always")
136+
# aapl has monthlies
137+
aapl = web.Options('aapl')
138+
today = datetime.today()
139+
year = today.year
140+
month = today.month+1
141+
if (month>12):
142+
year = year +1
143+
month = 1
144+
(calls, puts) = aapl.get_options_data(month=month, year=year)
145+
(calls, puts) = aapl.get_near_stock_price(call=True, put=True, month=month, year=year)
146+
calls = aapl.get_call_data(month=month, year=year)
147+
puts = aapl.get_put_data(month=month, year=year)
148+
print(w)
149+
assert len(w) == 5
150+
assert "deprecated" in str(w[0].message)
151+
assert "deprecated" in str(w[1].message)
152+
assert "deprecated" in str(w[2].message)
153+
assert "deprecated" in str(w[3].message)
154+
assert "deprecated" in str(w[4].message)
99155

100156
if __name__ == '__main__':
101157
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],

0 commit comments

Comments
 (0)