diff --git a/.gitignore b/.gitignore
index b254bd6..293b5a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,6 @@ __pycache__/
*.pyc
quickstart.py
creds.json
+test/
+.vscode/
+listGoogle.py
\ No newline at end of file
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..1ec3db4
--- /dev/null
+++ b/Functions.py
@@ -0,0 +1,24 @@
+# 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)
+ '''
+ dateSplit = date.split('-')
+ year = int(dateSplit[0])
+ month = int(dateSplit[1])
+ day = int(dateSplit[2])
+ datetime_object = datetime.date(year, month, day)
+ '''
+ return datetime_object
+
+def main():
+ exit()
+
+if __name__ == "__main__":
+ main()
diff --git a/README.md b/README.md
index d696909..eead752 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,11 @@ A project to determine indicators of overperforming mutual funds.
This project is written in Python and will examine market capitalization, persistence, turnover, and expense ratios.
### Prerequisites
-```sh
-$ pip install requests
-$ pip install numpy
-```
+
+`$ pip install -r requirements.txt`
+
+or
+
+`$ pip install requests`
Created by Andrew Dinh from Dr. TJ Owens Gilroy Early College Academy
diff --git a/StockData.py b/StockData.py
index 0d6cff7..e2a90a9 100644
--- a/StockData.py
+++ b/StockData.py
@@ -29,20 +29,19 @@ Daily Requests = 20,000
Symbol Requests = 500
'''
-import requests, json, socket
-import importlib.util, sys # To check whether a package is installed
+import requests, json
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 +58,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'))
@@ -133,9 +141,17 @@ class Stock:
def getAV(self):
listAV = []
- url = ''.join(('https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=', self.name, '&apikey=', apiAV))
+ #url = ''.join(('https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=', self.name, '&apikey=', apiAV))
# https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=MSFT&apikey=demo
+
+ #url = ''.join(('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=', self.name, '&outputsize=full&apikey=', apiAV))
+ # https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=MSFT&outputsize=full&apikey=demo
+
+ url = ''.join(('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=', self.name, '&outputsize=full&apikey=', apiAV))
+ # https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=MSFT&outputsize=full&apikey=demo
+
print("\nSending request to:", url)
+ print("(This will take a while)")
f = requests.get(url)
json_data = f.text
loaded_json = json.loads(json_data)
@@ -148,9 +164,9 @@ class Stock:
return 'Not available'
#print(loaded_json['Monthly Time Series'])
- monthlyTimeSeries = loaded_json['Monthly Time Series']
+ dailyTimeSeries = loaded_json['Time Series (Daily)']
#print(monthlyTimeSeries)
- listOfDates = list(monthlyTimeSeries)
+ listOfDates = list(dailyTimeSeries)
#print(listOfDates)
firstDate = listOfDates[-1]
@@ -171,8 +187,9 @@ class Stock:
values = []
for i in range(0, len(listOfDates), 1):
temp = listOfDates[i]
- loaded_json2 = monthlyTimeSeries[temp]
- value = loaded_json2['4. close']
+ loaded_json2 = dailyTimeSeries[temp]
+ #value = loaded_json2['4. close']
+ value = loaded_json2['5. adjusted close']
values.append(value)
listAV.append(values)
#print(listOfDates[0])
@@ -424,13 +441,31 @@ 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 = []
+
+ from Functions import Functions
+ for i in range(0, len(finalDatesStrings), 1):
+ temp = Functions.stringToDate(finalDatesStrings[i])
+ finalDates.append(temp)
+ #print(finalDates)
+
+ finalDatesAndClose2.append(finalDates)
+ finalDatesAndClose2.append(finalClose)
+ return(finalDatesAndClose2)
+
def is_connected():
+ import socket # To check internet connection
try:
# connect to the host -- tells us if the host is actually
# reachable
@@ -442,6 +477,7 @@ class Stock:
return False
def main(self):
+ import importlib.util, sys # To check whether a package is installed
packages = ['requests']
for i in range(0, len(packages), 1):
@@ -451,16 +487,18 @@ 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
listOfFirstLastDates = []
self.allLists = []
+ print('\nNOTE: Only IEX and Alpha Vantage support adjusted returns')
+
# 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 +506,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 +515,44 @@ class Stock:
# COMMENTED OUT FOR NOW B/C LIMITED
'''
print("\nTiingo")
- listTiingo = Stock.getTiingo(self)
+ print("NOTE: Tiingo does not return adjusted returns!!")
+ 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)
+ print(len(self.allLists), "available source(s) for", self.name)
+ 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..44e36bb
--- /dev/null
+++ b/StockReturn.py
@@ -0,0 +1,141 @@
+# 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 getFirstLastDates2(self, stock):
+ finalDatesAndClose = StockData.returnFinalDatesAndClose(stock)
+ finalDatesAndClose2 = StockData.returnFinalDatesAndClose2(stock)
+ firstDate = self.firstLastDates[0]
+ lastDate = self.firstLastDates[1]
+ finalDates = finalDatesAndClose[0]
+
+ firstDateExists = False
+ lastDateExists = False
+ for i in range(0, len(finalDates), 1):
+ if finalDates[i] == str(firstDate):
+ firstDateExists = True
+ elif finalDates[i] == lastDate:
+ lastDateExists = True
+ i = len(finalDates)
+
+ if firstDateExists == False:
+ print("Could not find first date. Changing first date to closest date")
+ tempDate = Functions.stringToDate(firstDate) # Change to datetime
+ print('Original first date:', tempDate)
+ #tempDate = datetime.date(2014,1,17)
+ newFirstDate = Functions.getNearest(finalDatesAndClose2[0], tempDate)
+ print('New first date:', newFirstDate)
+ firstDate = str(newFirstDate)
+
+ if lastDateExists == False:
+ print("Could not find final date. Changing final date to closest date")
+ tempDate2 = Functions.stringToDate(lastDate) # Change to datetime
+ print('Original final date:', tempDate2)
+ #tempDate2 = datetime.date(2014,1,17)
+ newLastDate = Functions.getNearest(finalDatesAndClose2[0], tempDate2)
+ print('New final date:', newLastDate)
+ lastDate = str(newLastDate)
+
+ firstLastDates = []
+ 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]
+
+ for i in range(0, len(finalDates), 1):
+ if finalDates[i] == str(firstDate):
+ firstClose = finalClose[i]
+ elif finalDates[i] == lastDate:
+ lastClose = finalClose[i]
+ i = len(finalDates)
+
+ print('Close values:', firstClose, '...', lastClose)
+ unadjustedReturn = float(lastClose/firstClose)
+ unadjustedReturn = unadjustedReturn * 100
+ return unadjustedReturn
+
+# def getBeta(self, timeFrame):
+
+# def getStandardDeviation(self, timeFrame):
+
+ def main(self, stock):
+ # Find date to start from and last date
+ self.timeFrame = []
+ self.listOfReturn = []
+
+ print("\nPlease enter a time frame in years: ", end='')
+ #timeFrameYear = int(input())
+ timeFrameYear = 5
+ print(timeFrameYear)
+ self.timeFrame.append(timeFrameYear)
+ print("Please enter a time frame in months (30 days): ", end='')
+ #timeFrameMonth = int(input())
+ timeFrameMonth = 0
+ print(timeFrameMonth)
+ self.timeFrame.append(timeFrameMonth)
+ #print(self.timeFrame)
+ self.firstLastDates = Return.getFirstLastDates(self, stock)
+ print('Dates: ', self.firstLastDates)
+
+ print('\nMaking sure dates are within list...')
+ self.firstLastDates = Return.getFirstLastDates2(self, stock)
+ print('New dates: ', self.firstLastDates)
+
+ print('\nGetting unadjusted return')
+ unadjustedReturn = Return.getUnadjustedReturn(self, stock)
+ self.listOfReturn.append(unadjustedReturn)
+ print(self.listOfReturn[0])
+ print(self.listOfReturn[0]/timeFrameYear, '%')
+
+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)
+'''