Refactor code

Fix research paper link

add note that this project is no longer maintained
This commit is contained in:
Andrew Dinh 2020-07-30 13:16:24 -07:00
parent ab371aaa12
commit cdeac8c96f
3 changed files with 67 additions and 73 deletions

Binary file not shown.

View File

@ -6,7 +6,9 @@
![](https://img.shields.io/github/languages/code-size/andrewkdinh/fund-indicators.svg) ![](https://img.shields.io/github/languages/code-size/andrewkdinh/fund-indicators.svg)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2667/badge)](https://bestpractices.coreinfrastructure.org/projects/2667) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2667/badge)](https://bestpractices.coreinfrastructure.org/projects/2667)
fund-indicators is a cross-platform Python application that allows users to easily find relationships between various attributes of mutual funds and previous performance. This project is based on research from [*Performance Indicators of Mutual Funds*](https://nextcloud.andrewkdinh.com/s/MBJ6q6t26LAXfZY). fund-indicators is a cross-platform Python application that allows users to easily find relationships between various attributes of mutual funds and previous performance. This project is based on research from [*Performance Indicators of Mutual Funds*](./Performance_Indicators_of_Mutual_Funds.pdf).
**NOTE:** This program is no longer functional nor actively developed.
[![asciicast demo](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW.svg)](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW?autoplay=1&preload=1) [![asciicast demo](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW.svg)](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW?autoplay=1&preload=1)

116
main.py
View File

@ -321,8 +321,8 @@ class Stock:
if listYahoo[1][i] is None: if listYahoo[1][i] is None:
del listYahoo[1][i] del listYahoo[1][i]
del listYahoo[0][i] del listYahoo[0][i]
i = i - 1 i -= 1
i = i + 1 i += 1
else: else:
break break
@ -398,10 +398,7 @@ class Stock:
while len(closeValues) != len(dates): while len(closeValues) != len(dates):
closeValues.remove(closeValues[0]) closeValues.remove(closeValues[0])
datesAndCloseList2 = [] datesAndCloseList2 = [dates, closeValues]
datesAndCloseList2.append(dates)
datesAndCloseList2.append(closeValues)
print(len(dates), 'dates and', len(closeValues), 'close values') print(len(dates), 'dates and', len(closeValues), 'close values')
return datesAndCloseList2 return datesAndCloseList2
@ -742,25 +739,6 @@ class Stock:
return marketCap return marketCap
elif Stock.indicator == 'Turnover': elif Stock.indicator == 'Turnover':
if stockType == 'Stock':
print(self.name, 'is a stock, and therefore does not have turnover')
return 'Stock'
if stockType == 'Mutual Fund':
raw_html = t.text
soup = BeautifulSoup(raw_html, 'html.parser')
r = soup.find_all(
'span', attrs={'class': 'Trsdu(0.3s)'})
if r == []:
print('Something went wrong without scraping turnover')
return 'N/A'
turnover = 0
for i in range(len(r)-1, 0, -1):
s = r[i].text.strip()
if s[-1] == '%':
turnover = float(s.replace('%', ''))
break
if stockType == 'ETF': if stockType == 'ETF':
url = ''.join(('https://finance.yahoo.com/quote/', url = ''.join(('https://finance.yahoo.com/quote/',
self.name, '/profile?p=', self.name)) self.name, '/profile?p=', self.name))
@ -784,6 +762,25 @@ class Stock:
turnover = float(s.replace('%', '')) turnover = float(s.replace('%', ''))
break break
elif stockType == 'Mutual Fund':
raw_html = t.text
soup = BeautifulSoup(raw_html, 'html.parser')
r = soup.find_all(
'span', attrs={'class': 'Trsdu(0.3s)'})
if r == []:
print('Something went wrong without scraping turnover')
return 'N/A'
turnover = 0
for i in range(len(r)-1, 0, -1):
s = r[i].text.strip()
if s[-1] == '%':
turnover = float(s.replace('%', ''))
break
elif stockType == 'Stock':
print(self.name, 'is a stock, and therefore does not have turnover')
return 'Stock'
if turnover == 0: if turnover == 0:
print('Something went wrong with scraping turnover') print('Something went wrong with scraping turnover')
return 'N/A' return 'N/A'
@ -793,20 +790,20 @@ class Stock:
def indicatorManual(self): def indicatorManual(self):
indicatorValueFound = False indicatorValueFound = False
while indicatorValueFound is False: while not indicatorValueFound:
if Stock.indicator == 'Expense Ratio': if Stock.indicator == 'Expense Ratio':
indicatorValue = str( indicatorValue = str(
input(Stock.indicator + ' for ' + self.name + ' (%): ')) input(Stock.indicator + ' for ' + self.name + ' (%): '))
elif Stock.indicator == 'Market Capitalization':
indicatorValue = str(
input(Stock.indicator + ' of ' + self.name + ': '))
elif Stock.indicator == 'Persistence': elif Stock.indicator == 'Persistence':
indicatorValue = str( indicatorValue = str(
input(Stock.indicator + ' for ' + self.name + ' (years): ')) input(Stock.indicator + ' for ' + self.name + ' (years): '))
elif Stock.indicator == 'Turnover': elif Stock.indicator == 'Turnover':
indicatorValue = str(input( indicatorValue = str(input(
Stock.indicator + ' for ' + self.name + ' in the last ' + str(Stock.timeFrame) + ' years: ')) Stock.indicator + ' for ' + self.name + ' in the last ' + str(Stock.timeFrame) + ' years: '))
elif Stock.indicator == 'Market Capitalization':
indicatorValue = str(
input(Stock.indicator + ' of ' + self.name + ': '))
if Functions.strintIsFloat(indicatorValue): if Functions.strintIsFloat(indicatorValue):
indicatorValueFound = True indicatorValueFound = True
return float(indicatorValue) return float(indicatorValue)
@ -880,15 +877,15 @@ def stocksInit():
print('For simplicity, all of them will be referred to as "stock"') print('For simplicity, all of them will be referred to as "stock"')
found = False found = False
while found is False:
print('\nMethods:')
method = 0
methods = ['Read from a file', 'Enter manually', methods = ['Read from a file', 'Enter manually',
'Kiplinger top-performing funds (50)', 'Kiplinger top-performing funds (50)',
'TheStreet top-rated mutual funds (20)', 'TheStreet top-rated mutual funds (20)',
'Money best mutual funds (50)', 'Money best mutual funds (50)',
'Investors Business Daily best mutual funds (~45)'] 'Investors Business Daily best mutual funds (~45)']
while not found:
print('\nMethods:')
method = 0
for i in range(0, len(methods), 1): for i in range(0, len(methods), 1):
print('[' + str(i+1) + '] ' + methods[i]) print('[' + str(i+1) + '] ' + methods[i])
while method == 0 or method > len(methods): while method == 0 or method > len(methods):
@ -919,10 +916,13 @@ def stocksInit():
for i in range(0, len(listOfFiles), 1): for i in range(0, len(listOfFiles), 1):
if listOfFiles[i][0] != '.': if listOfFiles[i][0] != '.':
print('[' + str(i+1) + '] ' + listOfFiles[i]) print('[' + str(i+1) + '] ' + listOfFiles[i])
while stocksFound is False: while not stocksFound:
fileName = str(input('What is the file number/name? ')) fileName = str(input('What is the file number/name? '))
if Functions.stringIsInt(fileName): if (
if int(fileName) < len(listOfFiles)+1 and int(fileName) > 0: Functions.stringIsInt(fileName)
and int(fileName) < len(listOfFiles) + 1
and int(fileName) > 0
):
fileName = listOfFiles[int(fileName)-1] fileName = listOfFiles[int(fileName)-1]
print(fileName) print(fileName)
if Functions.fileExists(fileName): if Functions.fileExists(fileName):
@ -948,7 +948,7 @@ def stocksInit():
elif method == 2: elif method == 2:
isInteger = False isInteger = False
while isInteger is False: while not isInteger:
temp = input('Number of stocks to analyze (2 minimum): ') temp = input('Number of stocks to analyze (2 minimum): ')
isInteger = Functions.stringIsInt(temp) isInteger = Functions.stringIsInt(temp)
if isInteger: if isInteger:
@ -1081,9 +1081,12 @@ def stocksInit():
for k in r: for k in r:
t = k.text.strip() t = k.text.strip()
if len(t) == 5 and Functions.strintIsFloat(t) is False: if (
if t not in listOfStocksOriginal or listOfStocksOriginal == []: len(t) == 5
if t[-1] != '%': and Functions.strintIsFloat(t) is False
and (t not in listOfStocksOriginal or listOfStocksOriginal == [])
and t[-1] != '%'
):
listOfStocksOriginal.append(t) listOfStocksOriginal.append(t)
print(t, end=' ') print(t, end=' ')
listOfStocks.append(k.text.strip()) listOfStocks.append(k.text.strip())
@ -1154,7 +1157,7 @@ def sendAsync(url):
def timeFrameInit(): def timeFrameInit():
isInteger = False isInteger = False
print('') print('')
while isInteger is False: while not isInteger:
print( print(
'Please enter the time frame in months (<60 recommended):', end='') 'Please enter the time frame in months (<60 recommended):', end='')
temp = input(' ') temp = input(' ')
@ -1171,8 +1174,7 @@ def timeFrameInit():
else: else:
print('Please type an integer') print('Please type an integer')
timeFrame = months return months
return timeFrame
def dataMain(listOfStocks): def dataMain(listOfStocks):
@ -1378,9 +1380,7 @@ def calcIndicatorRegression(listOfIndicatorValues, listOfReturns):
b = [b_0, b_1] b = [b_0, b_1]
regression = [] regression = [b[0], b[1]]
regression.append(b[0])
regression.append(b[1])
regressionList.append(regression) regressionList.append(regression)
if Stock.plotIndicatorRegression: if Stock.plotIndicatorRegression:
@ -1405,7 +1405,7 @@ def plot_regression_line(x, y, b, i):
'Sharpe Ratio', 'Sortino Ratio', 'Treynor Ratio', 'Alpha'] 'Sharpe Ratio', 'Sortino Ratio', 'Treynor Ratio', 'Alpha']
plt.title(Stock.indicator + ' and ' + listOfReturnStrings[i]) plt.title(Stock.indicator + ' and ' + listOfReturnStrings[i])
if Stock.indicator == 'Expense Ratio' or Stock.indicator == 'Turnover': if Stock.indicator in ['Expense Ratio', 'Turnover']:
plt.xlabel(Stock.indicator + ' (%)') plt.xlabel(Stock.indicator + ' (%)')
elif Stock.indicator == 'Persistence': elif Stock.indicator == 'Persistence':
plt.xlabel(Stock.indicator + ' (Difference in average monthly return)') plt.xlabel(Stock.indicator + ' (Difference in average monthly return)')
@ -1443,7 +1443,7 @@ def plot_regression_line(x, y, b, i):
def persistenceTimeFrame(): def persistenceTimeFrame():
print('\nTime frame you chose was', Stock.timeFrame, 'months') print('\nTime frame you chose was', Stock.timeFrame, 'months')
persTimeFrameFound = False persTimeFrameFound = False
while persTimeFrameFound is False: while not persTimeFrameFound:
persistenceTimeFrame = str( persistenceTimeFrame = str(
input('Please choose how many months to measure persistence: ')) input('Please choose how many months to measure persistence: '))
if Functions.stringIsInt(persistenceTimeFrame): if Functions.stringIsInt(persistenceTimeFrame):
@ -1480,15 +1480,11 @@ def indicatorMain(listOfStocks):
print('\nWould you like to enter a ' + str(Stock.indicator.lower() print('\nWould you like to enter a ' + str(Stock.indicator.lower()
) + ' value for ' + str(listOfStocks[i].name) + '?') ) + ' value for ' + str(listOfStocks[i].name) + '?')
r = Functions.trueOrFalse() r = Functions.trueOrFalse()
if r is False: listOfStocks[i].indicatorValue = 'Remove' if r is False else 'N/A'
listOfStocks[i].indicatorValue = 'Remove'
else:
listOfStocks[i].indicatorValue = 'N/A'
if listOfStocks[i].indicatorValue == 'N/A': if listOfStocks[i].indicatorValue == 'N/A':
listOfStocks[i].indicatorValue = Stock.indicatorManual( listOfStocks[i].indicatorValue = Stock.indicatorManual(
listOfStocks[i]) listOfStocks[i])
elif listOfStocks[i].indicatorValue == 'Stock' or listOfStocks[i].indicatorValue == 'Remove': elif listOfStocks[i].indicatorValue in ['Stock', 'Remove']:
cprint('Removing ' + cprint('Removing ' +
listOfStocks[i].name + ' from list of stocks', 'yellow') listOfStocks[i].name + ' from list of stocks', 'yellow')
del listOfStocks[i] del listOfStocks[i]
@ -1501,7 +1497,7 @@ def indicatorMain(listOfStocks):
listOfStocks[i].indicatorValue = float( listOfStocks[i].indicatorValue = float(
listOfStocks[i].indicatorValue) listOfStocks[i].indicatorValue)
listOfStocksIndicatorValues.append(listOfStocks[i].indicatorValue) listOfStocksIndicatorValues.append(listOfStocks[i].indicatorValue)
i = i + 1 i += 1
print('') print('')
# Remove outliers # Remove outliers
@ -1522,7 +1518,7 @@ def indicatorMain(listOfStocks):
cprint('Removing ' + listOfStocks[i].name + ' because it has a ' + str( cprint('Removing ' + listOfStocks[i].name + ' because it has a ' + str(
Stock.indicator.lower()) + ' value of ' + str(listOfStocks[i].indicatorValue), 'yellow') Stock.indicator.lower()) + ' value of ' + str(listOfStocks[i].indicatorValue), 'yellow')
del listOfStocks[i] del listOfStocks[i]
i = i - 1 i -= 1
break break
i += 1 i += 1
# print('New list:', listOfStocksIndicatorValues, '\n') # print('New list:', listOfStocksIndicatorValues, '\n')
@ -1588,8 +1584,7 @@ def checkConfig(fileName):
print('Config file is not valid') print('Config file is not valid')
return 'N/A' return 'N/A'
t = json.loads(n) t = json.loads(n)
r = t['Config'] return t['Config']
return r
def continueProgram(): def continueProgram():
@ -1609,10 +1604,7 @@ def plotIndicatorRegression():
print('\nWould you like to plot indicator linear regression ' print('\nWould you like to plot indicator linear regression '
'results?') 'results?')
plotLinear = Functions.trueOrFalse() plotLinear = Functions.trueOrFalse()
if plotLinear: Stock.plotIndicatorRegression = plotLinear
Stock.plotIndicatorRegression = True
else:
Stock.plotIndicatorRegression = False
else: else:
Stock.plotIndicatorRegression = False Stock.plotIndicatorRegression = False