From eb44066b5fbb1a797921755d7def1bbac06bf8b7 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Wed, 16 Jan 2019 08:54:06 -0800 Subject: [PATCH] Making list with datetime --- .gitignore | 1 + ExpenseRatio.py | 25 ++++-------- Functions.py | 15 +++++++ StockData.py | 65 ++++++++++++++++++++++-------- StockReturn.py | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 58 +++++++++++++++------------ 6 files changed, 205 insertions(+), 61 deletions(-) create mode 100644 Functions.py create mode 100644 StockReturn.py diff --git a/.gitignore b/.gitignore index b254bd6..46723b9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ *.pyc quickstart.py creds.json +test/ diff --git a/ExpenseRatio.py b/ExpenseRatio.py index 3d14d05..f6d6df0 100644 --- a/ExpenseRatio.py +++ b/ExpenseRatio.py @@ -5,35 +5,24 @@ ''' Asks user for expense ratio of stock (I don't think there's an API for expense ratios) Runs corrrelation study (I'm not sure if I want another class for this or not) -''' +''' import numpy #import urllib2, re from urllib.request import urlopen import re +class ExpenseRatio: + def __init__(self): + + def main(): # For testing purposes - ''' + ''' a = [1,2,3] b = [2,4,6] c = numpy.corrcoef(a, b)[0, 1] print(c) - ''' - #http://finance.yahoo.com/q/pr?s=spy+profile - stockSymbols = [ "VDIGX", "VFIAX" ] - expenses = [ [ "Fund", "Most Recent Expense Ratio" ] ] - for stockSymbol in stockSymbols: - page = urlopen("http://finance.yahoo.com/q/pr?s=" + stockSymbol + "+profile" ) - data = str(page.read()) - row = re.findall("Annual Report Expense Ratio.*?", data) - if len(row) > 0: - ER = re.findall("(\d+\.\d+).*?", row[0] )[0] - expenses.append( [ stockSymbol, ER ] ) - else: - print(stockSymbol, "does not appear to be a fund with an expense ratio") - print("\n".join( i[0] + "," + i[1] for i in expenses)) - - + ''' if __name__ == "__main__": main() diff --git a/Functions.py b/Functions.py new file mode 100644 index 0000000..fc58eba --- /dev/null +++ b/Functions.py @@ -0,0 +1,15 @@ +# Python file for general functions +class Functions: + def getNearest(items, pivot): + return min(items, key=lambda x: abs(x - pivot)) + def stringToDate(date): + from datetime import datetime + #datetime_object = datetime.strptime('Jun 1 2005 1:33PM', '%b %d %Y %I:%M%p') + datetime_object = datetime.strptime(date, '%Y-%m-%d').date() + return(datetime_object) + +def main(): + exit() + +if __name__ == "__main__": + main() diff --git a/StockData.py b/StockData.py index 0d6cff7..76485f5 100644 --- a/StockData.py +++ b/StockData.py @@ -33,16 +33,16 @@ import requests, json, socket import importlib.util, sys # To check whether a package is installed from datetime import datetime -class Stock: +class StockData: - def __init__(self, newName = '', newFirstLastDates = [], newAbsFirstLastDates = [], newFinalDatesAndClose = [], newAllLists = []): + def __init__(self, newName = '', newAbsFirstLastDates = [], newFinalDatesAndClose = [], newFinalDatesAndClose2 = [],newAllLists = []): self.name = newName # Name of stock - self.firstLastDates = newFirstLastDates # Dates that at least 2 sources have (or should it be all?) - Maybe let user decide self.absFirstLastDates = newAbsFirstLastDates # Absolute first and last dates from all sources - self.finalDatesAndClose = newFinalDatesAndClose # All available dates + self.finalDatesAndClose = newFinalDatesAndClose # All available dates with corresponding close values + self.finalDatesAndClose2 = newFinalDatesAndClose2 # After some consideration, I decided to keep what I had already done here and make a new list that's the same except dates are in datetime format self.allLists = newAllLists ''' - Format: + Format: # List from each source containing: [firstDate, lastDate, allDates, values, timeFrame] # firstDate & lastDate = '2018-12-18' (year-month-date) allDates = ['2018-12-17', '2018-12-14'] (year-month-date) @@ -59,9 +59,18 @@ class Stock: def setName(self, newName): self.name = newName - - def getAllLists(self): + def returnName(self): + return self.name + def returnAllLists(self): return self.allLists + def returnAbsFirstLastDates(self): + return self.absFirstLastDates + def returnAllLists(self): + return self.allLists + def returnFinalDatesAndClose(self): + return self.finalDatesAndClose + def returnFinalDatesAndClose2(self): + return self.finalDatesAndClose2 def getIEX(self): url = ''.join(('https://api.iextrading.com/1.0/stock/', self.name, '/chart/5y')) @@ -424,12 +433,29 @@ class Stock: # Want lists from most recent to oldest, comment this out if you don't want that finalDates = list(reversed(finalDates)) - finalClose = list(reversed(finalClose)) + finalClose = list(reversed(finalClose)) finalDatesAndClose.append(finalDates) finalDatesAndClose.append(finalClose) return finalDatesAndClose + def datetimeDates(self): + finalDatesAndClose2 = [] + finalDatesAndClose = self.finalDatesAndClose + finalDatesStrings = finalDatesAndClose[0] + finalClose = finalDatesAndClose[1] + finalDates = [] + + for i in range(0, len(finalDatesStrings), 1): + from Functions import Functions + temp = Functions.stringToDate(finalDatesStrings[i]) + finalDates.append(temp) + #print(finalDates) + + finalDatesAndClose2.append(finalDates) + finalDatesAndClose2.append(finalClose) + return(finalDatesAndClose2) + def is_connected(): try: # connect to the host -- tells us if the host is actually @@ -451,7 +477,7 @@ class Stock: print(package_name +" is not installed\nPlease type in 'pip install -r requirements.txt' to install all required packages") # Test internet connection - internetConnection = Stock.is_connected() + internetConnection = StockData.is_connected() if internetConnection == False: return @@ -460,7 +486,7 @@ class Stock: # IEX print("\nIEX") - listIEX = Stock.getIEX(self) + listIEX = StockData.getIEX(self) #print(listIEX) if listIEX != 'Not available': listOfFirstLastDates.append((listIEX[0], listIEX[1])) @@ -468,7 +494,7 @@ class Stock: # Alpha Vantage print("\nAlpha Vantage (AV)") - listAV = Stock.getAV(self) + listAV = StockData.getAV(self) #print(listAV) if listAV != 'Not available': listOfFirstLastDates.append((listAV[0], listAV[1])) @@ -477,38 +503,43 @@ class Stock: # COMMENTED OUT FOR NOW B/C LIMITED ''' print("\nTiingo") - listTiingo = Stock.getTiingo(self) + listTiingo = StockData.getTiingo(self) #print(listTiingo) if listTiingo != 'Not available': listOfFirstLastDates.append((listTiingo[0], listTiingo[1])) self.allLists.append(listTiingo) ''' - + #print(self.allLists) #print(listOfFirstLastDates) if (len(self.allLists) > 0): print("\n") print(len(self.allLists), "available sources for", self.name) - self.absFirstLastDates = Stock.getFirstLastDate(self, listOfFirstLastDates) + self.absFirstLastDates = StockData.getFirstLastDate(self, listOfFirstLastDates) print("\nThe absolute first date with close values is:", self.absFirstLastDates[0]) print("The absolute last date with close values is:", self.absFirstLastDates[1]) print("\nCombining dates and averaging close values") - self.finalDatesAndClose = Stock.getFinalDatesAndClose(self) # Returns [List of Dates, List of Corresponding Close Values] + self.finalDatesAndClose = StockData.getFinalDatesAndClose(self) # Returns [List of Dates, List of Corresponding Close Values] #print("All dates available:", self.finalDatesAndClose[0]) #print("All close values:\n", self.finalDatesAndClose[1]) finalDates = self.finalDatesAndClose[0] finalClose = self.finalDatesAndClose[1] print(len(finalDates), "unique dates:", finalDates[len(finalDates)-1], "...", finalDates[0]) print(len(finalClose), "close values:", finalClose[len(finalClose)-1], "...", finalClose[0]) + + print("\nConverting list of final dates to datetime") + self.finalDatesAndClose2 = StockData.datetimeDates(self) + #print(self.finalDatesAndClose2[0][0]) + else: print("No sources have data for", self.name) def main(): # For testing purposes stockName = 'spy' - stock1 = Stock(stockName) + stock1 = StockData(stockName) print("Finding available dates and close values for", stock1.name) - Stock.main(stock1) + StockData.main(stock1) if __name__ == "__main__": main() diff --git a/StockReturn.py b/StockReturn.py new file mode 100644 index 0000000..1f5bf53 --- /dev/null +++ b/StockReturn.py @@ -0,0 +1,102 @@ +# ExpenseRatio.py +# Andrew Dinh +# Python 3.6.7 +# Description: +''' +Calculates return for each stock from the lists from ExpenseRatio.py +listOfReturn = [Unadjsted Return, Sharpe Ratio, Sortino Ratio, Treynor Ratio, Jensen's Alpha] +''' + +from StockData import StockData +import datetime +from Functions import Functions + +class Return: + def __init__(self, newListOfReturn = [], newTimeFrame = [], newBeta = 0, newStandardDeviation = 0, newNegativeStandardDeviation = 0, newMarketReturn = 0, newSize = 0, newSizeOfNeg = 0, newFirstLastDates = [], newAllLists = [], newAbsFirstLastDates = ''): + self.listOfReturn = newListOfReturn + self.timeFrame = newTimeFrame # [year, months (30 days)] + self.beta = newBeta + self.standardDeviation = newStandardDeviation + self.negativeStandardDeviation = newNegativeStandardDeviation + self.marketReturn = newMarketReturn + self.size = newSize + self.sizeOfNeg = newSizeOfNeg + self.firstLastDates = newFirstLastDates + + def getFirstLastDates(self, stock): + firstLastDates = [] + timeFrame = self.timeFrame + firstDate = datetime.datetime.now() - datetime.timedelta(days=timeFrame[0]*365) + firstDate = firstDate - datetime.timedelta(days=timeFrame[1]*30) + firstDate = ''.join((str(firstDate.year),'-', str(firstDate.month), '-', str(firstDate.day))) + + lastDate = StockData.returnAbsFirstLastDates(stock)[1] + #print(lastDate) + firstLastDates.append(firstDate) + firstLastDates.append(lastDate) + return firstLastDates + + def getUnadjustedReturn(self, stock): + finalDatesAndClose = StockData.returnFinalDatesAndClose(stock) + finalDatesAndClose2 = StockData.returnFinalDatesAndClose2(stock) + firstDate = self.firstLastDates[0] + lastDate = self.firstLastDates[1] + finalDates = finalDatesAndClose[0] + finalClose = finalDatesAndClose[1] + + firstClose = 0 + for i in range(0, len(finalDates), 1): + if finalDates[i] == firstDate: + firstClose = finalClose[i] + elif finalDates[i] == lastDate: + lastClose = finalClose[i] + i = len(finalDates) + + if firstClose == 0: + print("Could not find first date. Changing first date to closest date") + temp = Functions.stringToDate(firstDate) # Change to datetime + print('Original first date: ', temp) + newFirstDate = Functions.getNearest(finalDatesAndClose2[0], temp) + print('New first date: ', newFirstDate) + + for i in range(0, len(finalDates), 1): + if finalDates[i] == str(newFirstDate): + firstClose = finalClose[i] + + print(firstClose) + print(lastClose) + +# def getBeta(self, timeFrame): + +# def getStandardDeviation(self, timeFrame): + + def main(self, stock): + # Find date to start from and last date + self.timeFrame = [] + print("\nPlease enter a time frame in years: ", end='') + #timeFrameYear = int(input()) + timeFrameYear = 5 + self.timeFrame.append(timeFrameYear) + print("Please enter a time frame in months (30 days): ", end='') + #timeFrameMonth = int(input()) + timeFrameMonth = 0 + print('') + self.timeFrame.append(timeFrameMonth) + #print(self.timeFrame) + self.firstLastDates = Return.getFirstLastDates(self, stock) + print('Dates: ', self.firstLastDates) + + print('\nGetting unadjusted return') + Return.getUnadjustedReturn(self, stock) + +def main(): + stockName = 'spy' + stock1 = StockData(stockName) + print("Finding available dates and close values for", stock1.name) + StockData.main(stock1) + + stock1Return = Return() + Return.main(stock1Return, stock1) + +if __name__ == "__main__": + main() diff --git a/main.py b/main.py index dc8fe5e..ccf3692 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ # main.py # Andrew Dinh # Python 3.6.1 -# Description: +# Description: ''' Asks users for mutual funds/stocks to compare Asks to be compared (expense ratio, turnover, market capitalization, or persistence) @@ -15,7 +15,7 @@ Gives correlation value using equation at the end (from 0 to 1) FIRST TESTING WITH EXPENSE RATIO ''' -from StockData import Stock +from StockData import StockData listOfStocks = [] numberOfStocks = int(input("How many stocks or mutual funds would you like to analyze? ")) @@ -23,48 +23,54 @@ for i in range(0, numberOfStocks, 1): print("Stock", i+1, ": ", end='') stockName = str(input()) listOfStocks.append(i) - listOfStocks[i] = Stock() + listOfStocks[i] = StockData() listOfStocks[i].setName(stockName) #print(listOfStocks[i].name) sumOfListLengths = 0 for i in range(0, numberOfStocks, 1): print(listOfStocks[i].name) - Stock.main(listOfStocks[i]) + StockData.main(listOfStocks[i]) # Count how many stocks are available - temp = Stock.getAllLists(listOfStocks[i]) + temp = StockData.returnAllLists(listOfStocks[i]) sumOfListLengths = sumOfListLengths + len(temp) if sumOfListLengths == 0: - print("No sources have stock data for given stocks") + print("No sources have data for given stocks") + exit() -else: - #print(listOfStocks[0].name, listOfStocks[0].absFirstLastDates, listOfStocks[0].finalDatesAndClose) - indicatorFound = False - while indicatorFound == False: - print("\n1. Expense Ratio\n2. Asset Size\n3. Turnover\n4. Persistence\nWhich indicator would you like to look at? ", end='') - indicator = str(input()) - indicatorFound = True +# Find return over time using either Jensen's Alpha, Sharpe Ratio, Sortino Ratio, or Treynor Ratio +#from StockReturn import Return - if indicator == 'Expense Ratio' or indicator == '1' or indicator == 'expense ratio': - print('\nExpense Ratio') - elif indicator == 'Asset Size' or indicator == '2' or indicator == 'asset size': - print('\nAsset Size') +# Runs correlation or regression study +#print(listOfStocks[0].name, listOfStocks[0].absFirstLastDates, listOfStocks[0].finalDatesAndClose) +indicatorFound = False +while indicatorFound == False: + print("\n1. Expense Ratio\n2. Asset Size\n3. Turnover\n4. Persistence\nWhich indicator would you like to look at? ", end='') + indicator = str(input()) + indicatorFound = True - elif indicator == 'Turnover' or indicator == '3' or indicator == 'turnover': - print('\nTurnover') + if indicator == 'Expense Ratio' or indicator == '1' or indicator == 'expense ratio': + #from ExpenseRatio import ExpenseRatio + print('\nExpense Ratio') - elif indicator == 'Persistence' or indicator == '4' or indicator == 'persistence': - print('\nPersistence') + elif indicator == 'Asset Size' or indicator == '2' or indicator == 'asset size': + print('\nAsset Size') - else: - indicatorFound = False - print('\nInvalid input, please enter indicator again') + elif indicator == 'Turnover' or indicator == '3' or indicator == 'turnover': + print('\nTurnover') + + elif indicator == 'Persistence' or indicator == '4' or indicator == 'persistence': + print('\nPersistence') + + else: + indicatorFound = False + print('\nInvalid input, please enter indicator again') ''' stockName = 'IWV' stock1 = Stock(stockName) print("Finding available dates and close values for", stock1.name) -Stock.main(stock1) -''' \ No newline at end of file +StockData.main(stock1) +'''