mirror of
https://github.com/andrewkdinh/fund-indicators.git
synced 2024-11-26 19:54:20 -08:00
Added code of conduct and contributing guidelines.
+ Bug fixes
This commit is contained in:
parent
4fd66eab52
commit
18730bcd43
77
CODE-OF-CONDUCT.md
Normal file
77
CODE-OF-CONDUCT.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at fund-indicators@andrewkdinh.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
|
37
CONTRIBUTING.md
Normal file
37
CONTRIBUTING.md
Normal file
@ -0,0 +1,37 @@
|
||||
# How to Contribute
|
||||
|
||||
**Did you come here to read what you should do before creating an issue?** Scroll down!
|
||||
|
||||
## How you can help
|
||||
|
||||
There are many ways to contribute to fund-indicators:
|
||||
|
||||
- Help with testing and reporting bugs
|
||||
- Help write documentation
|
||||
- Help implement new features
|
||||
- New sources of data
|
||||
- Better web scraping
|
||||
- Better statistical measures
|
||||
- Multithreading (asynchronous requests)
|
||||
|
||||
## Filing an issue
|
||||
|
||||
Thanks for wanting to help out with squashing bugs and more by filing an issue.
|
||||
|
||||
There are a few things you might consider when filing your issue:
|
||||
|
||||
- What made the issue/bug appear? (steps to reproduce)
|
||||
- Do you have the latest version of this program?
|
||||
|
||||
## Getting started with development
|
||||
|
||||
To develop on fund-indicators you'll probably want to:
|
||||
|
||||
1. Read the [code of conduct](https://github.com/andrewkdinh/fund-indicators/CODE_OF_CONDUCT.md)
|
||||
2. Install normally, as documented in the [README](https://github.com/andrewkdinh/fund-indicators#quickstart)
|
||||
3. Read the [documentation](https://github.com/andrewkdinh/fund-indicators/wiki) (although it's not much)
|
||||
4. Read the section below to understand the ways you could best help this project
|
||||
|
||||
## Questions?
|
||||
|
||||
If you have any questions, please email fund-indicators@andrewkdinh.com.
|
46
Functions.py
46
Functions.py
@ -11,7 +11,7 @@ def getNearest(items, 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('Jun 1 2005 1:33PM', '%b %d %Y %I:%M%p')
|
||||
datetime_object = datetime.strptime(date, '%Y-%m-%d').date()
|
||||
return(datetime_object)
|
||||
|
||||
@ -57,17 +57,17 @@ def strintIsFloat(s):
|
||||
def fromCache(r):
|
||||
import requests_cache
|
||||
from termcolor import colored, cprint
|
||||
if r.from_cache == True:
|
||||
if r.from_cache is True:
|
||||
cprint('(Response taken from cache)', 'white', attrs=['dark'])
|
||||
return
|
||||
|
||||
|
||||
def getJoke():
|
||||
import requests
|
||||
import sys
|
||||
from termcolor import colored, cprint
|
||||
import requests_cache
|
||||
from halo import Halo
|
||||
import sys
|
||||
with requests_cache.disabled():
|
||||
'''
|
||||
f = requests.get('https://official-joke-api.appspot.com/jokes/random').json()
|
||||
@ -81,7 +81,7 @@ def getJoke():
|
||||
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get('https://icanhazdadjoke.com/',
|
||||
f = requests.get(url,
|
||||
headers=headers).json()
|
||||
print('')
|
||||
print(colored(f['joke'], 'green'))
|
||||
@ -118,7 +118,7 @@ def checkPackages(listOfPackages):
|
||||
|
||||
def checkPythonVersion():
|
||||
import platform
|
||||
#print('Checking Python version')
|
||||
# print('Checking Python version')
|
||||
i = platform.python_version()
|
||||
r = i.split('.')
|
||||
k = float(''.join((r[0], '.', r[1])))
|
||||
@ -135,7 +135,7 @@ def isConnected():
|
||||
import socket # To check internet connection
|
||||
try:
|
||||
# connect to the host -- tells us if the host is actually reachable
|
||||
socket.create_connection(("www.andrewkdinh.com", 80))
|
||||
socket.create_connection(('1.1.1.1', 53))
|
||||
print('Internet connection is good')
|
||||
return True
|
||||
except OSError:
|
||||
@ -199,9 +199,43 @@ def keyInDict(dict, key):
|
||||
return False
|
||||
|
||||
|
||||
def getWeather():
|
||||
import requests
|
||||
from termcolor import colored, cprint
|
||||
import requests_cache
|
||||
from halo import Halo
|
||||
import sys
|
||||
sys.path.insert(0, './modules')
|
||||
with requests_cache.disabled():
|
||||
url = 'https://wttr.in?format=3'
|
||||
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get(url)
|
||||
print('')
|
||||
print(colored('Current weather in ' + f.text, 'green'), end='')
|
||||
|
||||
|
||||
def main():
|
||||
exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
'''
|
||||
Copyright (C) 2019 Andrew Dinh
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
28
README.md
28
README.md
@ -1,45 +1,55 @@
|
||||
# fund-indicators
|
||||
|
||||
[![License](https://img.shields.io/github/license/andrewkdinh/fund-indicators.svg)](https://raw.githubusercontent.com/andrewkdinh/fund-indicators/master/LICENSE)
|
||||
[![](https://img.shields.io/github/last-commit/andrewkdinh/fund-indicators.svg)](https://github.com/andrewkdinh/fund-indicators/commits/master)
|
||||
[![Latest Commits](https://img.shields.io/github/last-commit/andrewkdinh/fund-indicators.svg)](https://github.com/andrewkdinh/fund-indicators/commits/master)
|
||||
![](https://img.shields.io/github/languages/top/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)
|
||||
|
||||
A project to determine relationships between mutual funds and different factors.
|
||||
A project to determine relationships between mutual fund performance and different factors.
|
||||
|
||||
[![asciicast demo](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW.svg)](https://asciinema.org/a/jLmZapnMFGCRiiSUITY21erLW?autoplay=1&preload=1)
|
||||
|
||||
Calculates relationships between: Previous performance, Alpha, Sharpe Ratio, Sortino Ratio
|
||||
|
||||
and Expense ratios, Turnover, Market Capitalization (Asset Size), Persistence
|
||||
|
||||
Give it a try at [repl.run](https://fund-indicators.andrewkdinh.repl.run) or [repl.it](https://repl.it/@andrewkdinh/fund-indicators)
|
||||
Give it a try at [repl.run](https://fund-indicators.andrewkdinh.repl.run) or [repl.it](https://repl.it/@andrewkdinh/fund-indicators).
|
||||
|
||||
## Key Features
|
||||
|
||||
- 100% automated
|
||||
- Uses multiple API's in case another fails
|
||||
- Uses multiple API's in the case another fails
|
||||
- Caches http requests for future runs
|
||||
- Scrapes data from Yahoo Finance
|
||||
- Color-coded for easy viewing
|
||||
- Optional graphs to easily visualize linear regression results
|
||||
- A new joke every time it runs
|
||||
- A new joke every time
|
||||
- Cross-platform (tested on Windows and Linux)
|
||||
|
||||
## Quickstart
|
||||
|
||||
```shell
|
||||
git clone https://github.com/andrewkdinh/fund-indicators.git && cd fund-indicators
|
||||
pip install -r requirements.txt
|
||||
python main.py
|
||||
```
|
||||
|
||||
Pre-chosen stocks listed in `stocks.txt`
|
||||
- Common mutual funds are listed in `stocks.txt`
|
||||
- Configure and rename `config.example.json` to `config.json` if you would like to skip beginning questions (only for advanced users)
|
||||
|
||||
### Contributing
|
||||
|
||||
Want to help? Great! Check out the [CONTRIBUTING.md](https://github.com/andrewkdinh/fund-indicators/CONTRIBUTING.md) file!
|
||||
|
||||
## Credits
|
||||
|
||||
This project uses a wide variety of open-source projects
|
||||
This project utilizes a wide variety of open-source projects:
|
||||
|
||||
- [NumPy](https://github.com/numpy/numpy), [Termcolor](https://github.com/hfeeki/termcolor), [Beautiful Soup](https://launchpad.net/beautifulsoup), [yahoofinancials](https://github.com/JECSand/yahoofinancials), [requests-cache](https://github.com/reclosedev/requests-cache), [halo](https://github.com/manrajgrover/halo)
|
||||
- [NumPy](https://github.com/numpy/numpy), [Termcolor](https://github.com/hfeeki/termcolor), [Beautiful Soup](https://launchpad.net/beautifulsoup), [yahoofinancials](https://github.com/JECSand/yahoofinancials), [requests-cache](https://github.com/reclosedev/requests-cache), [halo](https://github.com/manrajgrover/halo), [matplotlib](https://github.com/matplotlib/matplotlib), [asciinema](https://github.com/asciinema/asciinema)
|
||||
|
||||
And thank you to those that have helped me with the idea and product:
|
||||
|
||||
- Amber Bruce, [Alex Stoykov](http://stoykov.us/), Doug Achterman, [Stack Overflow](https://stackoverflow.com)
|
||||
|
||||
Created by Andrew Dinh from Dr. TJ Owens Gilroy Early College Academy
|
||||
Licensed under [GPL-3.0](https://raw.githubusercontent.com/andrewkdinh/fund-indicators/master/LICENSE) | Copyright (C) 2019 Andrew Dinh
|
@ -6,7 +6,6 @@
|
||||
"Check Internet Connection": false,
|
||||
"Get Joke": true,
|
||||
"Benchmark": "SPY",
|
||||
"Method": "Kiplinger",
|
||||
"Time Frame": 60,
|
||||
"Indicator": "Expense Ratio",
|
||||
"Remove Outliers": true,
|
||||
@ -40,13 +39,6 @@
|
||||
"VTHR",
|
||||
"EFG"
|
||||
],
|
||||
"Method": [
|
||||
"Read",
|
||||
"Manual",
|
||||
"U.S. News",
|
||||
"Kiplinger",
|
||||
"TheStreet"
|
||||
],
|
||||
"Time Frame": "Any integer",
|
||||
"Indicator": [
|
||||
"Expense Ratio",
|
||||
@ -60,4 +52,4 @@
|
||||
],
|
||||
"Sources": "Choose an order out of ['Alpha Vantage', 'Yahoo', 'IEX', 'Tiingo']"
|
||||
}
|
||||
}
|
||||
}
|
293
main.py
293
main.py
@ -41,7 +41,8 @@ apiTiingo = '2e72b53f2ab4f5f4724c5c1e4d5d4ac0af3f7ca8'
|
||||
apiTradier = 'n26IFFpkOFRVsB5SNTVNXicE5MPD'
|
||||
apiQuandl = 'KUh3U3hxke9tCimjhWEF'
|
||||
# apiIntrinio = 'OmNmN2E5YWI1YzYxN2Q4NzEzZDhhOTgwN2E2NWRhOWNl'
|
||||
# If you're going to take these API keys and abuse it, you should really reconsider your life priorities
|
||||
# If you're going to take these API keys and abuse it, you should really
|
||||
# reconsider your life priorities
|
||||
|
||||
'''
|
||||
API Keys:
|
||||
@ -77,7 +78,7 @@ class Stock:
|
||||
|
||||
# CONFIG
|
||||
removeOutliers = True
|
||||
sourceList = ['Alpha Vantage', 'Yahoo', 'IEX', 'Tiingo']
|
||||
sourceList = ['Yahoo', 'Alpha Vantage', 'IEX', 'Tiingo']
|
||||
config = 'N/A'
|
||||
|
||||
# BENCHMARK VALUES
|
||||
@ -147,7 +148,7 @@ class Stock:
|
||||
|
||||
print("\nFinding all dates given")
|
||||
allDates = []
|
||||
for i in range(0, len(loaded_json), 1): # If you want to do oldest first
|
||||
for i in range(0, len(loaded_json), 1): # For oldest first
|
||||
# for i in range(len(loaded_json)-1, -1, -1):
|
||||
line = loaded_json[i]
|
||||
date = line['date']
|
||||
@ -157,7 +158,7 @@ class Stock:
|
||||
|
||||
# print("\nFinding close values for each date")
|
||||
values = []
|
||||
for i in range(0, len(loaded_json), 1): # If you want to do oldest first
|
||||
for i in range(0, len(loaded_json), 1): # For oldest first
|
||||
# for i in range(len(loaded_json)-1, -1, -1):
|
||||
line = loaded_json[i]
|
||||
value = line['close']
|
||||
@ -215,7 +216,7 @@ class Stock:
|
||||
f = requests.get(url, headers=headers)
|
||||
Functions.fromCache(f)
|
||||
loaded_json = f.json()
|
||||
if len(loaded_json) == 1 or f.status_code != 200 or loaded_json['startDate'] == None:
|
||||
if len(loaded_json) == 1 or f.status_code != 200 or loaded_json['startDate'] is None:
|
||||
print("Tiingo not available")
|
||||
return 'N/A'
|
||||
|
||||
@ -289,8 +290,8 @@ class Stock:
|
||||
# Sometimes close value is a None value
|
||||
i = 0
|
||||
while i < len(listYahoo[1]):
|
||||
if Functions.listIndexExists(listYahoo[1][i]) == True:
|
||||
if listYahoo[1][i] == None:
|
||||
if Functions.listIndexExists(listYahoo[1][i]) is True:
|
||||
if listYahoo[1][i] is None:
|
||||
del listYahoo[1][i]
|
||||
del listYahoo[0][i]
|
||||
i = i - 1
|
||||
@ -381,7 +382,7 @@ class Stock:
|
||||
print(len(dates), 'dates and', len(closeValues), 'close values')
|
||||
return datesAndCloseList2
|
||||
|
||||
def calcAverageMonthlyReturn(self): # pylint: disable=E0202
|
||||
def calcAverageMonthlyReturn(self):
|
||||
# averageMonthlyReturn = (float(self.closeValues[len(self.closeValues)-1]/self.closeValues[0])**(1/(self.timeFrame)))-1
|
||||
# averageMonthlyReturn = averageMonthlyReturn * 100
|
||||
averageMonthlyReturn = sum(self.monthlyReturn)/self.timeFrame
|
||||
@ -687,11 +688,11 @@ class Stock:
|
||||
break
|
||||
if marketCap == 0:
|
||||
somethingWrong = True
|
||||
if somethingWrong == True:
|
||||
if somethingWrong is True:
|
||||
ticker = self.name
|
||||
yahoo_financials = YahooFinancials(ticker)
|
||||
marketCap = yahoo_financials.get_market_cap()
|
||||
if marketCap != None:
|
||||
if marketCap is not None:
|
||||
print('(Taken from yahoofinancials)')
|
||||
print(marketCap)
|
||||
return int(marketCap)
|
||||
@ -748,9 +749,6 @@ class Stock:
|
||||
if s[-1] == '%':
|
||||
turnover = float(s.replace('%', ''))
|
||||
break
|
||||
elif s == 'N/A':
|
||||
print(self.name, 'has a value of N/A for turnover')
|
||||
return 'N/A'
|
||||
|
||||
if turnover == 0:
|
||||
print('Something went wrong with scraping turnover')
|
||||
@ -761,7 +759,7 @@ class Stock:
|
||||
|
||||
def indicatorManual(self):
|
||||
indicatorValueFound = False
|
||||
while indicatorValueFound == False:
|
||||
while indicatorValueFound is False:
|
||||
if Stock.indicator == 'Expense Ratio':
|
||||
indicatorValue = str(
|
||||
input(Stock.indicator + ' for ' + self.name + ' (%): '))
|
||||
@ -780,7 +778,7 @@ class Stock:
|
||||
'Something is wrong. Indicator was not found. Ending program.', 'white', 'on_red')
|
||||
exit()
|
||||
|
||||
if Functions.strintIsFloat(indicatorValue) == True:
|
||||
if Functions.strintIsFloat(indicatorValue) is True:
|
||||
indicatorValueFound = True
|
||||
return float(indicatorValue)
|
||||
else:
|
||||
@ -820,7 +818,7 @@ def benchmarkInit():
|
||||
benchmark = str(input('Please choose a benchmark from the list: '))
|
||||
# benchmark = 'SPY' # TESTING
|
||||
|
||||
if Functions.stringIsInt(benchmark) == True:
|
||||
if Functions.stringIsInt(benchmark) is True:
|
||||
if int(benchmark) <= len(benchmarks) and int(benchmark) > 0:
|
||||
benchmarkInt = int(benchmark)
|
||||
benchmark = benchmarks[benchmarkInt-1]
|
||||
@ -853,60 +851,60 @@ def stocksInit():
|
||||
print('For simplicity, all of them will be referred to as "stock"')
|
||||
|
||||
found = False
|
||||
while found == False:
|
||||
while found is False:
|
||||
print('\nMethods:')
|
||||
method = 0
|
||||
methods = ['Read from a file', 'Enter manually',
|
||||
'U.S. News popular funds (~35)', 'Kiplinger top-performing funds (50)', 'TheStreet top-rated mutual funds (20)']
|
||||
'Kiplinger top-performing funds (50)',
|
||||
'TheStreet top-rated mutual funds (20)',
|
||||
'Money best mutual funds (50)',
|
||||
'Investors Business Daily best mutual funds (~45)',
|
||||
'Yahoo top mutual funds (25)']
|
||||
|
||||
if Stock.config != 'N/A':
|
||||
methodsConfig = ['Read', 'Manual',
|
||||
'U.S. News', 'Kiplinger', 'TheStreet']
|
||||
for i in range(0, len(methodsConfig), 1):
|
||||
if Stock.config['Method'] == methodsConfig[i]:
|
||||
method = i + 1
|
||||
|
||||
else:
|
||||
for i in range(0, len(methods), 1):
|
||||
print(str(i+1) + '. ' + methods[i])
|
||||
while method == 0 or method > len(methods):
|
||||
method = str(input('Which method? '))
|
||||
if Functions.stringIsInt(method) == True:
|
||||
method = int(method)
|
||||
if method == 0 or method > len(methods):
|
||||
print('Please choose a valid method')
|
||||
else:
|
||||
method = 0
|
||||
print('Please choose a number')
|
||||
for i in range(0, len(methods), 1):
|
||||
print(str(i+1) + '. ' + methods[i])
|
||||
while method == 0 or method > len(methods):
|
||||
method = str(input('Which method? '))
|
||||
if Functions.stringIsInt(method) is True:
|
||||
method = int(method)
|
||||
if method == 0 or method > len(methods):
|
||||
print('Please choose a valid method')
|
||||
else:
|
||||
method = 0
|
||||
print('Please choose a number')
|
||||
|
||||
print('')
|
||||
if method == 1:
|
||||
defaultFiles = ['.gitignore', 'LICENSE', 'main.py', 'Functions.py',
|
||||
'README.md', 'requirements.txt', 'cache.sqlite', 'yahoofinancials.py', 'termcolor.py', 'README.html', 'config.json', '_test_runner.py'] # Added by repl.it for whatever reason
|
||||
'README.md', 'requirements.txt', 'cache.sqlite',
|
||||
'yahoofinancials.py', 'termcolor.py',
|
||||
'README.html', 'config.json',
|
||||
'config.example.json', '_test_runner.py']
|
||||
# Added by repl.it for whatever reason
|
||||
stocksFound = False
|
||||
print('\nFiles in current directory (not including default files): ')
|
||||
print('Files in current directory (without default files): ')
|
||||
listOfFilesTemp = [f for f in os.listdir() if os.path.isfile(f)]
|
||||
listOfFiles = []
|
||||
for files in listOfFilesTemp:
|
||||
if files[0] != '.' and any(x in files for x in defaultFiles) != True:
|
||||
if files[0] != '.' and any(x in files for x in defaultFiles) is not True:
|
||||
listOfFiles.append(files)
|
||||
for i in range(0, len(listOfFiles), 1):
|
||||
if listOfFiles[i][0] != '.':
|
||||
print(str(i+1) + '. ' + listOfFiles[i])
|
||||
while stocksFound == False:
|
||||
while stocksFound is False:
|
||||
fileName = str(input('What is the file number/name? '))
|
||||
if Functions.stringIsInt(fileName) == True:
|
||||
if Functions.stringIsInt(fileName) is True:
|
||||
if int(fileName) < len(listOfFiles)+1 and int(fileName) > 0:
|
||||
fileName = listOfFiles[int(fileName)-1]
|
||||
print(fileName)
|
||||
if Functions.fileExists(fileName) == True:
|
||||
if Functions.fileExists(fileName) is True:
|
||||
listOfStocks = []
|
||||
file = open(fileName, 'r')
|
||||
n = file.read()
|
||||
file.close()
|
||||
s = re.findall(r'[^,;\s]+', n)
|
||||
for i in s:
|
||||
if str(i) != '' and Functions.hasNumbers(str(i)) == False:
|
||||
if str(i) != '' and Functions.hasNumbers(str(i)) is False:
|
||||
listOfStocks.append(str(i).upper())
|
||||
stocksFound = True
|
||||
else:
|
||||
@ -922,10 +920,10 @@ def stocksInit():
|
||||
|
||||
elif method == 2:
|
||||
isInteger = False
|
||||
while isInteger == False:
|
||||
while isInteger is False:
|
||||
temp = input('\nNumber of stocks to analyze (2 minimum): ')
|
||||
isInteger = Functions.stringIsInt(temp)
|
||||
if isInteger == True:
|
||||
if isInteger is True:
|
||||
if int(temp) >= 2:
|
||||
numberOfStocks = int(temp)
|
||||
else:
|
||||
@ -939,7 +937,7 @@ def stocksInit():
|
||||
print('Stock', i + 1, end=' ')
|
||||
stockName = str(input('ticker: '))
|
||||
|
||||
if stockName != '' and Functions.hasNumbers(stockName) == False:
|
||||
if stockName != '' and Functions.hasNumbers(stockName) is False:
|
||||
stockName = stockName.upper()
|
||||
listOfStocks.append(stockName)
|
||||
listOfStocks[i] = Stock()
|
||||
@ -949,34 +947,6 @@ def stocksInit():
|
||||
print('Invalid ticker')
|
||||
|
||||
elif method == 3:
|
||||
listOfStocks = []
|
||||
url = 'https://money.usnews.com/funds/mutual-funds/most-popular'
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get(url, headers=headers)
|
||||
Functions.fromCache(f)
|
||||
raw_html = f.text
|
||||
soup = BeautifulSoup(raw_html, 'html.parser')
|
||||
|
||||
file = open('usnews-stocks.txt', 'w')
|
||||
r = soup.find_all(
|
||||
'span', attrs={'class': 'text-smaller text-muted'})
|
||||
for k in r:
|
||||
print(k.text.strip(), end=' ')
|
||||
listOfStocks.append(k.text.strip())
|
||||
file.write(str(k.text.strip()) + '\n')
|
||||
file.close()
|
||||
|
||||
for i in range(0, len(listOfStocks), 1):
|
||||
stockName = listOfStocks[i].upper()
|
||||
listOfStocks[i] = Stock()
|
||||
listOfStocks[i].setName(stockName)
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
elif method == 4:
|
||||
listOfStocks = []
|
||||
url = 'https://www.kiplinger.com/tool/investing/T041-S001-top-performing-mutual-funds/index.php'
|
||||
headers = {
|
||||
@ -1003,7 +973,7 @@ def stocksInit():
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
elif method == 5:
|
||||
elif method == 4:
|
||||
listOfStocks = []
|
||||
url = 'https://www.thestreet.com/topic/21421/top-rated-mutual-funds.html'
|
||||
headers = {
|
||||
@ -1033,6 +1003,101 @@ def stocksInit():
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
elif method == 5:
|
||||
listOfStocks = []
|
||||
url = 'http://money.com/money/4616747/best-mutual-funds-etfs-money-50/'
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get(url, headers=headers)
|
||||
Functions.fromCache(f)
|
||||
raw_html = f.text
|
||||
soup = BeautifulSoup(raw_html, 'html.parser')
|
||||
|
||||
file = open('money.com-stocks.txt', 'w')
|
||||
r = soup.find_all('td')
|
||||
|
||||
for k in r:
|
||||
t = k.text.strip()
|
||||
if '(' in t and ')' in t:
|
||||
t = t.split('(')[1]
|
||||
t = t.split(')')[0]
|
||||
print(t, end=' ')
|
||||
listOfStocks.append(t)
|
||||
file.write(str(t + '\n'))
|
||||
file.close()
|
||||
|
||||
for i in range(0, len(listOfStocks), 1):
|
||||
stockName = listOfStocks[i].upper()
|
||||
listOfStocks[i] = Stock()
|
||||
listOfStocks[i].setName(stockName)
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
elif method == 6:
|
||||
listOfStocks = []
|
||||
listOfStocksOriginal = []
|
||||
url = 'https://www.investors.com/etfs-and-funds/mutual-funds/best-mutual-funds-beating-sp-500-over-last-1-3-5-10-years/'
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get(url, headers=headers)
|
||||
Functions.fromCache(f)
|
||||
raw_html = f.text
|
||||
soup = BeautifulSoup(raw_html, 'html.parser')
|
||||
|
||||
file = open('investors-stocks.txt', 'w')
|
||||
r = soup.find_all('td')
|
||||
|
||||
for k in r:
|
||||
t = k.text.strip()
|
||||
if len(t) == 5 and Functions.strintIsFloat(t) is False:
|
||||
if t not in listOfStocksOriginal or listOfStocksOriginal == []:
|
||||
listOfStocksOriginal.append(t)
|
||||
print(t, end=' ')
|
||||
listOfStocks.append(k.text.strip())
|
||||
file.write(str(k.text.strip()) + '\n')
|
||||
file.close()
|
||||
|
||||
for i in range(0, len(listOfStocks), 1):
|
||||
stockName = listOfStocks[i].upper()
|
||||
listOfStocks[i] = Stock()
|
||||
listOfStocks[i].setName(stockName)
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
elif method == 7:
|
||||
listOfStocks = []
|
||||
url = 'https://finance.yahoo.com/screener/predefined/top_mutual_funds/'
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'}
|
||||
cprint('Get: ' + url, 'white', attrs=['dark'])
|
||||
with Halo(spinner='dots'):
|
||||
f = requests.get(url, headers=headers)
|
||||
Functions.fromCache(f)
|
||||
raw_html = f.text
|
||||
soup = BeautifulSoup(raw_html, 'html.parser')
|
||||
|
||||
file = open('yahoo-stocks.txt', 'w')
|
||||
r = soup.find_all('a', attrs={'class': 'Fw(600)'})
|
||||
|
||||
for k in r:
|
||||
t = k.text.strip()
|
||||
if len(t) == 5 and t == t.upper():
|
||||
print(t, end=' ')
|
||||
listOfStocks.append(k.text.strip())
|
||||
file.write(str(k.text.strip()) + '\n')
|
||||
file.close()
|
||||
|
||||
for i in range(0, len(listOfStocks), 1):
|
||||
stockName = listOfStocks[i].upper()
|
||||
listOfStocks[i] = Stock()
|
||||
listOfStocks[i].setName(stockName)
|
||||
|
||||
print('\n' + str(len(listOfStocks)) + ' mutual funds total')
|
||||
|
||||
if len(listOfStocks) < 2:
|
||||
print('Please choose another method')
|
||||
else:
|
||||
@ -1089,12 +1154,12 @@ def sendAsync(url):
|
||||
|
||||
def timeFrameInit():
|
||||
isInteger = False
|
||||
while isInteger == False:
|
||||
while isInteger is False:
|
||||
print(
|
||||
'\nPlease enter the time frame in months (<60 months recommended):', end='')
|
||||
temp = input(' ')
|
||||
isInteger = Functions.stringIsInt(temp)
|
||||
if isInteger == True:
|
||||
if isInteger is True:
|
||||
if int(temp) > 1 and int(temp) < 1000:
|
||||
months = int(temp)
|
||||
else:
|
||||
@ -1111,7 +1176,11 @@ def dataMain(listOfStocks):
|
||||
i = 0
|
||||
while i < len(listOfStocks):
|
||||
|
||||
datesAndCloseList = Stock.datesAndClose(listOfStocks[i])
|
||||
try:
|
||||
datesAndCloseList = Stock.datesAndClose(listOfStocks[i])
|
||||
except:
|
||||
print('Error retrieving data')
|
||||
datesAndCloseList = 'N/A'
|
||||
if datesAndCloseList == 'N/A':
|
||||
del listOfStocks[i]
|
||||
if len(listOfStocks) == 0:
|
||||
@ -1229,7 +1298,7 @@ def returnMain(benchmark, listOfStocks):
|
||||
cprint('\nNumber of stocks from original list that fit time frame: ' +
|
||||
str(len(listOfStocks)), 'green')
|
||||
if len(listOfStocks) < 2:
|
||||
#print('Cannot proceed to the next step. Exiting program.')
|
||||
# print('Cannot proceed to the next step. Exiting program.')
|
||||
cprint('Cannot proceed to the next step. Exiting program.',
|
||||
'white', 'on_red')
|
||||
exit()
|
||||
@ -1239,7 +1308,7 @@ def outlierChoice():
|
||||
print('\nWould you like to remove indicator outliers?')
|
||||
print('1. Yes\n2. No')
|
||||
found = False
|
||||
while found == False:
|
||||
while found is False:
|
||||
outlierChoice = str(input('Choice: '))
|
||||
if Functions.stringIsInt(outlierChoice):
|
||||
if int(outlierChoice) == 1:
|
||||
@ -1265,12 +1334,12 @@ def indicatorInit():
|
||||
print('List of indicators:')
|
||||
for i in range(0, len(listOfIndicators), 1):
|
||||
print(str(i + 1) + '. ' + listOfIndicators[i])
|
||||
while indicatorFound == False:
|
||||
while indicatorFound is False:
|
||||
indicator = str(input('Choose an indicator from the list: '))
|
||||
|
||||
# indicator = 'expense ratio' # TESTING
|
||||
|
||||
if Functions.stringIsInt(indicator) == True:
|
||||
if Functions.stringIsInt(indicator) is True:
|
||||
if int(indicator) <= 4 and int(indicator) > 0:
|
||||
indicator = listOfIndicators[int(indicator)-1]
|
||||
indicatorFound = True
|
||||
@ -1284,7 +1353,7 @@ def indicatorInit():
|
||||
indicatorFound = True
|
||||
break
|
||||
|
||||
if indicatorFound == False:
|
||||
if indicatorFound is False:
|
||||
print('Please choose an indicator from the list\n')
|
||||
|
||||
return indicator
|
||||
@ -1374,7 +1443,7 @@ def plot_regression_line(x, y, b, i):
|
||||
plt.pause(1)
|
||||
sys.stdout.flush()
|
||||
sys.stdout.write(
|
||||
' \r')
|
||||
' \r')
|
||||
sys.stdout.flush()
|
||||
plt.close()
|
||||
|
||||
@ -1382,10 +1451,10 @@ def plot_regression_line(x, y, b, i):
|
||||
def persistenceTimeFrame():
|
||||
print('\nTime frame you chose was', Stock.timeFrame, 'months')
|
||||
persTimeFrameFound = False
|
||||
while persTimeFrameFound == False:
|
||||
while persTimeFrameFound is False:
|
||||
persistenceTimeFrame = str(
|
||||
input('Please choose how many months to measure persistence: '))
|
||||
if Functions.stringIsInt(persistenceTimeFrame) == True:
|
||||
if Functions.stringIsInt(persistenceTimeFrame) is True:
|
||||
if int(persistenceTimeFrame) > 0 and int(persistenceTimeFrame) < Stock.timeFrame - 1:
|
||||
persistenceTimeFrame = int(persistenceTimeFrame)
|
||||
persTimeFrameFound = True
|
||||
@ -1409,8 +1478,12 @@ def indicatorMain(listOfStocks):
|
||||
listOfStocks[i].indicatorValue = Stock.calcPersistence(
|
||||
listOfStocks[i])
|
||||
else:
|
||||
listOfStocks[i].indicatorValue = Stock.scrapeYahooFinance(
|
||||
listOfStocks[i])
|
||||
try:
|
||||
listOfStocks[i].indicatorValue = Stock.scrapeYahooFinance(
|
||||
listOfStocks[i])
|
||||
except:
|
||||
print('Error retrieving indicator data')
|
||||
listOfStocks[i].indicatorValue = 'N/A'
|
||||
print('')
|
||||
|
||||
if listOfStocks[i].indicatorValue == 'N/A':
|
||||
@ -1428,11 +1501,11 @@ def indicatorMain(listOfStocks):
|
||||
listOfStocksIndicatorValues.append(listOfStocks[i].indicatorValue)
|
||||
|
||||
# Remove outliers
|
||||
if Stock.removeOutliers == True:
|
||||
if Stock.removeOutliers is True:
|
||||
cprint('\nRemoving outliers\n', 'white', attrs=['underline'])
|
||||
temp = Functions.removeOutliers(listOfStocksIndicatorValues)
|
||||
if temp[0] == listOfStocksIndicatorValues:
|
||||
print('No outliers\n')
|
||||
print('No indicator outliers\n')
|
||||
else:
|
||||
print('First quartile:', temp[2], ', Median:', temp[3],
|
||||
', Third quartile:', temp[4], 'Interquartile range:', temp[5])
|
||||
@ -1502,12 +1575,12 @@ def indicatorMain(listOfStocks):
|
||||
|
||||
|
||||
def checkConfig(fileName):
|
||||
if Functions.fileExists(fileName) == False:
|
||||
if Functions.fileExists(fileName) is False:
|
||||
return 'N/A'
|
||||
file = open(fileName, 'r')
|
||||
n = file.read()
|
||||
file.close()
|
||||
if Functions.validateJson(n) == False:
|
||||
if Functions.validateJson(n) is False:
|
||||
print('Config file is not valid')
|
||||
return 'N/A'
|
||||
t = json.loads(n)
|
||||
@ -1516,12 +1589,14 @@ def checkConfig(fileName):
|
||||
|
||||
|
||||
def main():
|
||||
# Check config file for errors and if not, then use values
|
||||
'''
|
||||
Check config file for errors and if not, then use values
|
||||
#! Only use this if you know it is exactly correct. I haven't spent much time debugging this
|
||||
'''
|
||||
Stock.config = checkConfig('config.json')
|
||||
|
||||
# Check if matplotlib is installed
|
||||
if Functions.checkPackage('matplotlib') == False:
|
||||
if Functions.checkPackage('matplotlib') is False:
|
||||
print(
|
||||
'matplotlib is not installed. This is required for plotting linear regression')
|
||||
|
||||
@ -1545,6 +1620,7 @@ def main():
|
||||
exit()
|
||||
else:
|
||||
Functions.getJoke()
|
||||
# Functions.getWeather()
|
||||
|
||||
# Choose benchmark and makes it class Stock
|
||||
benchmark = benchmarkInit()
|
||||
@ -1567,7 +1643,7 @@ def main():
|
||||
# Choose whether to remove outliers or not
|
||||
Stock.removeOutliers = outlierChoice()
|
||||
else:
|
||||
if Stock.config['Check Packages'] != False:
|
||||
if Stock.config['Check Packages'] is not False:
|
||||
packagesInstalled = Functions.checkPackages(
|
||||
['numpy', 'requests', 'bs4', 'requests_cache', 'halo'])
|
||||
if not packagesInstalled:
|
||||
@ -1575,16 +1651,16 @@ def main():
|
||||
else:
|
||||
print('All required packages are installed')
|
||||
|
||||
if Stock.config['Check Python Version'] != False:
|
||||
if Stock.config['Check Python Version'] is not False:
|
||||
pythonVersionGood = Functions.checkPythonVersion()
|
||||
if not pythonVersionGood:
|
||||
exit()
|
||||
|
||||
if Stock.config['Check Internet Connection'] != False:
|
||||
if Stock.config['Check Internet Connection'] is not False:
|
||||
internetConnection = Functions.isConnected()
|
||||
if not internetConnection:
|
||||
exit()
|
||||
if Stock.config['Get Joke'] != False:
|
||||
if Stock.config['Get Joke'] is not False:
|
||||
Functions.getJoke()
|
||||
|
||||
benchmarksTicker = ['SPY', 'DJIA', 'VTHR', 'EFT']
|
||||
@ -1615,7 +1691,7 @@ def main():
|
||||
Stock.persTimeFrame = persistenceTimeFrame()
|
||||
|
||||
# Choose whether to remove outliers or not
|
||||
if Stock.config['Remove Outliers'] != False:
|
||||
if Stock.config['Remove Outliers'] is not False:
|
||||
Stock.removeOutliers = True
|
||||
else:
|
||||
Stock.removeOutliers = outlierChoice()
|
||||
@ -1640,3 +1716,20 @@ def main():
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
'''
|
||||
Copyright (C) 2019 Andrew Dinh
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
Loading…
Reference in New Issue
Block a user