%matplotlib inline from lime import lime from datetime import timedelta class LimeInvalidTicker(Exception): ''' An Exception for handling invalid tickers ''' def __init__(self): self.msg = ('Lime was not able to find that ticker symbol on Netfonds.' 'Please check your ticker and try again.') def __str__(self): return repr(self.msg) class LimeInvalidQuery(Exception): ''' An Exception for handling invalid requests ###Parameters * url -- string, should be self._url used to access NetFonds ''' def __init__(self, url): self.url = url self.msg = ('Lime was not able to contact Netfonds, based' ' on the information you provided.' 'Please check your request and try again.') def __str__(self): return repr("Invalid Query:{}:\n{}".format(self.msg, self.url)) class LimeInvalidDate(Exception): ''' An Exception for handling invalid dates for requests Invalid ###Parameters * start -- datetime object, should be self.start_date * end -- datetime object, should be self.end_date ''' def __init__(self, start, end): self.msg = self.check_date(start, end) self.start = start self.end = end def check_date(self): ''' returns the appropriate error message based on the provided dates. ''' if (self.end - self.start) > timedelta(21): self.msg = ('Date range is greater than 21 days.' 'Data does not exist on NetFonds beyond 21 days.\n' 'Please reconstruct your query and try again.') if self.start > self.end: self.msg = ('The Start Date is greater than the End Date.\n' 'You most likely reversed the begining_date and end_date.') def __str__(self): return repr("Invalid Date:{}:\n{}".format(self.msg, self.date)) import datetime from datetime import timedelta from pandas import DataFrame, concat, date_range, read_csv from .exceptions import LimeInvalidQuery, LimeInvalidDate, LimeInvalidTicker class Lime: ''' A simple API for extracting stock tick data. ###Parameters * start_date -- datetime, date beginning the retrieval window * end_date -- datetime, date ending the retrieval window * exchange -- string ( optional ), ticker's exchange: ['Nasdaq', 'Nyse', 'Amex'] * ticker -- string ( optional ), stock ticker symbol. With or with out Netfonds exchange extension. ''' def __init__(self, start_date, end_date=None, exchange=None, ticker=None): self.start_date = self.initialize_date(start_date) self.end_date = self.initialize_date(end_date) self.ticker = None self._exchange = exchange self._file_format = 'csv' self._df = None self._exchanges = { 'Nasdaq': '.O', 'Nyse': '.N', 'Amex': '.A' } self.exchange_extensions = ['O', 'N', 'A'] self._url = 'http://www.netfonds.no/quotes/tradedump.php' self.uri = None def get_exchange(self): ''' Returns the exchange chosen ''' return self._exchange def get_df(self): ''' Gets the stored tick data ''' return self._df def set_df(self, dataframe): ''' Sets stored tick data Parameters * dataframe -- pandas.DataFrame() ''' self._df = concat([self.get_df(), dataframe]) if self._df is None else dataframe self.process_data() def initialize_date(self, date): ''' Returns parsed todays date, a parsed supplied date ###Parameters * date -- datetime, date to be parsed ''' if not date: date = datetime.date.today() return self.date_parse(date) def date_parse(self, date): ''' Parses date to YYYY/MM/DD. ###Parameters * date -- datetime, date to be parsed ''' return date.strftime('%Y%m%d') def check_date(self, start, end): ''' Checks whether supplied dates are acceptable. ###Parameters * start -- datetime, date beginning the retrieval window * end -- datetime, date ending the retrieval window ''' if timedelta(0) > (end - start) > timedelta(21): raise LimeInvalidDate(start, end) return True def format_ticker_with_exchange_extenstion(self): self.ticker = "{}{}".format(self.ticker, self._exchanges[self._exchange.title()]) return self.ticker def validate_ticker_exchange_extenstion(self): '''Checks if ticker has a valid exchange extension. ''' extension = self.ticker.split('.')[1] if extension in self.exchange_extensions: return True return False def check_ticker_exchange_extenstion(self): ''' Check's whether the appropriate netfonds extension, ( '.N', '.O', '.A' ), has been added. If it hasn't, but the ticker's exchange has, it adds the appropriate extension. If neither have; it raises a LimeInvalidTicker exception. ''' try: self.validate_ticker_exchange_extenstion() except IndexError: if not self._exchange: self.get_exchange_extension_from_ticker() self.format_ticker_with_exchange_extenstion() else: raise LimeInvalidTicker() return self.ticker def get_exchange_extension_from_ticker(self): ''' Loops through the three exchanges Netfonds supports, ( Nasdaq, NYSE, Amex), and returns the correct exchange extension if it exists. ''' for key in self._exchanges.keys(): self.ticker = "{}{}".format(self.ticker, self._exchanges[key]) self._get_tick_data() if self._df is not None and (len(self._df.columns) > 1): self._exchange = key self.format_ticker_with_exchange_extenstion() return self._exchange raise LimeInvalidTicker() def set_start_end_dates(self, start, end=None): ''' Parses and Prepares Start and End dates. ###Parameters * start -- datetime * end -- ( optional ) datetime, defaults to today's date ''' self.start_date = self.date_parse(start) self.end_date = self.date_parse(end) if end else self.get_date_today() self.check_date(start, end) def process_data(self): ''' Cleans data after its retrieved from Netfonds ''' df = self.get_df() try: df.time = df.time.apply(lambda x: datetime.datetime.strptime(x, '%Y%m%dT%H%M%S')) df = df.set_index(df.time) except AttributeError: raise LimeInvalidQuery(self.uri) def _get_tick_data(self): ''' Retrieves tick data from Netfonds from a known ticker. ''' self.uri = '{}?date={}&paper={}&csv_format={}'.format(self._url, self.start_date, self.ticker, self._file_format) self.set_df(read_csv(self.uri)) def get_trades(self, ticker, exchange=None): ''' Gets the trades made for a ticker on a specified day. ###Parameters * ticker -- string, stock ticker symbol ''' if exchange: self.exchange = exchange self.ticker = ticker self.check_ticker_exchange_extenstion() self._get_tick_data() return self.get_df() def get_trade_history(self, ticker, start_date, end_date=None): ''' Retrieves the trades made for a ticker from a range of days. ###Parameters * ticker -- string, stock ticker symbol * start_date -- datetime, starting date of retrieval window * end_date -- datetime (optional), ending date of retrieval window. defaults to today, if committed. Note: Tick data only persist for 21 days on Netfonds. Any queries greater than that window will raise a LimeInvalidQuery exception. ''' self.ticker = ticker self.set_start_end_dates(start_date, end_date) for day in date_range(start=start_date, end=self.end_date, freq='B'): self.start_date = self.date_parse(day) self.set_df(self.get_trades(self.ticker)) return self.get_df() start_date = datetime.datetime.today() - timedelta(days=5) lime = Lime(start_date, exchange="Nasdaq") AAPL_single_day = lime.get_trades('AAPL') AAPL_single_day fig = plt.figure(figsize(18,4), dpi=1600) AAPL_single_day.price.plot() plt.title("Apple stock movements on: " + str(lime.start_date)) AAPL_many_days = lime.get_many(begining_date=datetime.date(2013,5,29), end_date=datetime.date(2013,6,18), ticker='AAPL') AAPL_many_days fig = plt.figure(figsize(18,4), dpi=1600) AAPL_many_days.price.plot() plt.title('Apple price movements over the last 21 days') savefig('Apple.png', bbox_inches=0) goog = lime.get_many(begining_date=datetime.date(2013,5,29), end_date=datetime.date(2013,6,18), ticker='GOOG') fig = plt.figure(figsize(18,4), dpi=1600) goog.price.plot() plt.title("Google's price movements over the last 21 days") savefig('GOOG.png', bbox_inches=0) import pandas #df = pandas.merge(AAPL_many_days, goog, on='time', suffixes=('_AAPL', '_GOOG')) plt.figure(figsize(18,8), dpi=1600) plt.subplot(212) df.price_AAPL.plot() legend(loc='best') plt.subplot(211) df.price_GOOG.plot(color='g') plt.title("Apple's and Google's price movements"); legend(loc='best') !git add lime_demo.ipynb !git commit