From fa63412ac35d117f3a7ec102ccd2868e36a8e16f Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 21 Apr 2021 12:20:37 -0400 Subject: [PATCH 01/10] Add coinmarketcap top 100 ability + small refactor --- gamestonk_terminal/config_terminal.py | 3 + gamestonk_terminal/cryptocurrency/README.md | 16 +++ .../cryptocurrency/coinmarketcap_view.py | 100 ++++++++++++++++++ .../cryptocurrency/crypto_controller.py | 14 ++- .../{coin_api.py => pycoingecko_view.py} | 0 pyproject.toml | 1 + requirements-full.txt | 1 + requirements-old.txt | 1 + requirements-win.txt | 1 + requirements.txt | 1 + tests/test_coingecko_crypto.py | 2 +- 11 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 gamestonk_terminal/cryptocurrency/coinmarketcap_view.py rename gamestonk_terminal/cryptocurrency/{coin_api.py => pycoingecko_view.py} (100%) diff --git a/gamestonk_terminal/config_terminal.py b/gamestonk_terminal/config_terminal.py index ea2aab6b960..bed32fd3644 100644 --- a/gamestonk_terminal/config_terminal.py +++ b/gamestonk_terminal/config_terminal.py @@ -45,3 +45,6 @@ TRADIER_TOKEN = os.getenv("GT_TRADIER_TOKEN") or "REPLACE_ME" # Selenium Webbrowser drivers can be found at https://selenium-python.readthedocs.io/installation.html WEBDRIVER_TO_USE = "chrome" PATH_TO_SELENIUM_DRIVER = None # Replace with "PATH" + +# https://coinmarketcap.com/api/ +COINMARKETCAP_KEY = os.getenv("GT_CMC_API_KEY") or "REPLACE_ME" diff --git a/gamestonk_terminal/cryptocurrency/README.md b/gamestonk_terminal/cryptocurrency/README.md index fd5ac92c10e..78a1167ffbf 100644 --- a/gamestonk_terminal/cryptocurrency/README.md +++ b/gamestonk_terminal/cryptocurrency/README.md @@ -9,6 +9,7 @@ Current functionality is aimed to be similar to loading a stock ticker. * [load](#load) * [view](#view) +* [top](#top) ## load Load a given coin vs a given currency: @@ -39,3 +40,18 @@ usage: view After loading the above 1 day dogecoin/eur. Running view plots the data: ![doge](https://user-images.githubusercontent.com/18151143/112690617-7832f500-8e52-11eb-84c4-253ab222a918.png) + + +## top +This command displays the top n cryptocurrencies from coinmarketcap.com. The list appears to be sorted by Mar + +```` +top [-n] [-s --sort SORTBY] [--descend] +```` +* -n: Number of coins to look at (after sort). Defaults to 10. +* -s/--sort : Column to sort by. One of {Symbol,CMC_Rank,LastPrice,DayPctChange,MarketCap}. Defaults to sorting by the CMC rank. +* --descend : Flag to change the sorting order. Sorts in ascending order (since default order is the Rank which starts at 1). + +Example usage to get the CMC top 100, then show the top 4 highest Percent Day Change + +![im](https://user-images.githubusercontent.com/18151143/115587223-6bf85700-a29b-11eb-9d96-cb4239b7c2d0.png) diff --git a/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py b/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py new file mode 100644 index 00000000000..a8820889d33 --- /dev/null +++ b/gamestonk_terminal/cryptocurrency/coinmarketcap_view.py @@ -0,0 +1,100 @@ +"""CoinMarketCap API""" +__docformat__ = "numpy" + +import argparse +from typing import List +import pandas as pd +from tabulate import tabulate +from coinmarketcapapi import CoinMarketCapAPI +from gamestonk_terminal.config_terminal import COINMARKETCAP_KEY +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + +sort_options = ["Symbol", "CMC_Rank", "LastPrice", "DayPctChange", "MarketCap"] + +sort_map = { + "Symbol": "Symbol", + "CMC_Rank": "CMC_Rank", + "LastPrice": "Last Price", + "DayPctChange": "1 Day Pct Change", + "MarketCap": "Market Cap ($B)", +} + + +def get_cmc_top_n(other_args: List[str]): + """ + Shows top n coins from coinmarketcap.com + Parameters + ---------- + other_args: List[str] + Arguments to pass to argparse + + """ + parser = argparse.ArgumentParser( + prog="cmc_top_n", + add_help=False, + description="This gets the top ranked coins from coinmarketcap.com", + ) + + parser.add_argument( + "-n", default=10, dest="n_to_get", type=int, help="number of coins to display" + ) + + parser.add_argument( + "-s", + "--sort", + dest="sortby", + type=str, + help="column to sort data by.", + default="CMC_Rank", + choices=sort_options, + ) + + parser.add_argument( + "--descend", + action="store_false", + help="Flag to sort in descending order (lowest first)", + dest="descend", + default=True, + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + + cmc = CoinMarketCapAPI(COINMARKETCAP_KEY) + ratings = cmc.cryptocurrency_listings_latest().data + + symbol, rank, price, pchange1d, mkt_cap = [], [], [], [], [] + for coin in ratings: + symbol.append(coin["symbol"]) + rank.append(coin["cmc_rank"]) + price.append(coin["quote"]["USD"]["price"]) + pchange1d.append(coin["quote"]["USD"]["percent_change_24h"]) + mkt_cap.append(coin["quote"]["USD"]["market_cap"] / (10 ** 9)) + + df = pd.DataFrame(data=[symbol, rank, price, pchange1d, mkt_cap]).transpose() + df.columns = [ + "Symbol", + "CMC_Rank", + "Last Price", + "1 Day Pct Change", + "Market Cap ($B)", + ] + + df = df.sort_values(by=sort_map[ns_parser.sortby], ascending=ns_parser.descend) + + print( + tabulate( + df.iloc[: ns_parser.n_to_get, :], + headers=df.columns, + showindex=False, + tablefmt="fancy_grid", + floatfmt=".2f", + ) + ) + print("") + + except Exception as e: + print(e) + print("") diff --git a/gamestonk_terminal/cryptocurrency/crypto_controller.py b/gamestonk_terminal/cryptocurrency/crypto_controller.py index 4a1508e11f8..e78323b473b 100644 --- a/gamestonk_terminal/cryptocurrency/crypto_controller.py +++ b/gamestonk_terminal/cryptocurrency/crypto_controller.py @@ -7,12 +7,13 @@ from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session -from gamestonk_terminal.cryptocurrency import coin_api +from gamestonk_terminal.cryptocurrency import pycoingecko_view +from gamestonk_terminal.cryptocurrency import coinmarketcap_view as cmc_view class CryptoController: - CHOICES = ["help", "q", "quit", "load", "view"] + CHOICES = ["help", "q", "quit", "load", "view", "top"] def __init__(self): """ CONSTRUCTOR """ @@ -34,6 +35,8 @@ class CryptoController: print(" load load cryptocurrency data") print(" view load and view cryptocurrency data") print("") + print(" top view top coins from coinmarketcap") + print("") def switch(self, an_input: str): """Process and dispatch input @@ -64,16 +67,19 @@ class CryptoController: return True def call_load(self, other_args): - self.current_coin, self.current_df = coin_api.load(other_args) + self.current_coin, self.current_df = pycoingecko_view.load(other_args) def call_view(self, other_args): if self.current_coin: - coin_api.view(self.current_coin, self.current_df, other_args) + pycoingecko_view.view(self.current_coin, self.current_df, other_args) else: print("No coin selected. Use 'load' to load the coin you want to look at.") print("") + def call_top(self, other_args): + cmc_view.get_cmc_top_n(other_args) + def menu(): crypto_controller = CryptoController() diff --git a/gamestonk_terminal/cryptocurrency/coin_api.py b/gamestonk_terminal/cryptocurrency/pycoingecko_view.py similarity index 100% rename from gamestonk_terminal/cryptocurrency/coin_api.py rename to gamestonk_terminal/cryptocurrency/pycoingecko_view.py diff --git a/pyproject.toml b/pyproject.toml index 53f0f468699..a3bbf6762c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ statsmodels = "^0.12.2" tabulate = "^0.8.9" pyportfolioopt = "^1.4.1" selenium = "^3.141.0" +python-coinmarketcap = "^0.2" [tool.poetry.dev-dependencies] pytest = "^6.2.2" diff --git a/requirements-full.txt b/requirements-full.txt index 39247ebb9c6..1dce549665e 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -146,6 +146,7 @@ pyportfolioopt==1.4.1; python_full_version >= "3.6.1" and python_full_version < pyrsistent==0.14.11 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.19.1.1 +python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") python-dotenv==0.17.0; python_version >= "3" pytrends==4.7.3 diff --git a/requirements-old.txt b/requirements-old.txt index 3dd16fb2a3f..6dcb6d00577 100644 --- a/requirements-old.txt +++ b/requirements-old.txt @@ -93,6 +93,7 @@ pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" pyportfolioopt==1.4.1 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.18 +python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") pytest==6.2.2; python_version >= "3.6" pytrends==4.7.3 diff --git a/requirements-win.txt b/requirements-win.txt index d5b413e1ca6..399b80c6f67 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -95,6 +95,7 @@ pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" pyportfolioopt==1.4.1 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pystan==2.19.1.1 +python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0") pytest==6.2.2; python_version >= "3.6" pytrends==4.7.3 diff --git a/requirements.txt b/requirements.txt index 31e6b0412cc..bf0985ecfa5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -110,6 +110,7 @@ pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or p pyportfolioopt==1.4.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyrsistent==0.14.11 pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +python-coinmarketcap==0.2 python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") python-dotenv==0.17.0; python_version >= "3" pytrends==4.7.3 diff --git a/tests/test_coingecko_crypto.py b/tests/test_coingecko_crypto.py index 5f20e2e04d1..003b5927f8b 100644 --- a/tests/test_coingecko_crypto.py +++ b/tests/test_coingecko_crypto.py @@ -1,7 +1,7 @@ from unittest import mock, TestCase import json from pycoingecko import CoinGeckoAPI -from gamestonk_terminal.cryptocurrency.coin_api import load +from gamestonk_terminal.cryptocurrency.pycoingecko_view import load # pylint: disable=unused-import From c3838e21995bc21b2fbf3956560f6bb5e24e0805 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 21 Apr 2021 12:23:58 -0400 Subject: [PATCH 02/10] Add coinmarketcap top 100 ability + small refactor --- poetry.lock | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 582cfdfc228..1c9dc7b56d1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2046,6 +2046,14 @@ toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "python-coinmarketcap" +version = "0.2" +description = "CoinMarketCap Python API Wrapper" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "python-dateutil" version = "2.8.1" @@ -2946,7 +2954,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t [metadata] lock-version = "1.1" python-versions = "^3.6.8" -content-hash = "2f3963dac96a208dfe5157d593c45f946d9b4c829b16302f2b297250390367cb" +content-hash = "fa7c02ebec2d14a1c42f76f8e1328134975893c5f98afa879f2bc672675d6a7f" [metadata.files] absl-py = [ @@ -4275,6 +4283,11 @@ pytest = [ {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] +python-coinmarketcap = [ + {file = "python-coinmarketcap-0.2.tar.gz", hash = "sha256:b1e0e8b098fc910a5ec943a06774d5ee3b190e915123366e2f16238ef0a8a433"}, + {file = "python_coinmarketcap-0.2-py2-none-any.whl", hash = "sha256:54c278154ee11ca78837fb73eae8c322a60af93dc0ce75b515fecf895d181cf5"}, + {file = "python_coinmarketcap-0.2-py3-none-any.whl", hash = "sha256:3b69a7a92cd4f2b4726a2982d166c0b587fa4911cf366286f798e1e6f9d94933"}, +] python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, From 2e3e9c6bb4bc3729c41bb289aac5c0c170cde9be Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 20 Apr 2021 20:05:52 -0400 Subject: [PATCH 03/10] updated permanent discord link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a23fb298cbb..d3d2f8966fb 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ Distributed under the MIT License. See [LICENSE](https://github.com/DidierRLopes [James Maslek](https://www.linkedin.com/in/james-maslek-b6810186/) - jmaslek11@gmail.com -Feel free to share loss porn, memes or any questions at https://discord.gg/BsfJk4MQ +Feel free to share loss porn, memes or any questions at https://discord.gg/Up2QGbMKHY ## Acknowledgments From 9b6bef5b37de2005c8225a409643611574650e9e Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 13:21:49 -0700 Subject: [PATCH 04/10] Refactoring comparison analysis --- .../comparison_analysis/ca_controller.py | 91 +++++++++++----- .../{finbrain_api.py => finbrain_view.py} | 6 ++ ...rket_watch_api.py => market_watch_view.py} | 3 + ...o_finance_api.py => yahoo_finance_view.py} | 100 ++++++++++++++---- 4 files changed, 155 insertions(+), 45 deletions(-) rename gamestonk_terminal/comparison_analysis/{finbrain_api.py => finbrain_view.py} (99%) rename gamestonk_terminal/comparison_analysis/{market_watch_api.py => market_watch_view.py} (99%) rename gamestonk_terminal/comparison_analysis/{yahoo_finance_api.py => yahoo_finance_view.py} (77%) diff --git a/gamestonk_terminal/comparison_analysis/ca_controller.py b/gamestonk_terminal/comparison_analysis/ca_controller.py index 499b6c9cd39..3772807b71a 100644 --- a/gamestonk_terminal/comparison_analysis/ca_controller.py +++ b/gamestonk_terminal/comparison_analysis/ca_controller.py @@ -2,23 +2,24 @@ __docformat__ = "numpy" import argparse -import requests import random from typing import List from datetime import datetime +import requests import pandas as pd from matplotlib import pyplot as plt +from finvizfinance.screener.overview import Overview +from prompt_toolkit.completion import NestedCompleter + from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal import config_terminal as cfg -from finvizfinance.screener.overview import Overview from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn -from gamestonk_terminal.comparison_analysis import yahoo_finance_api as yf_api -from gamestonk_terminal.comparison_analysis import market_watch_api as mw_api -from gamestonk_terminal.comparison_analysis import finbrain_api as f_api +from gamestonk_terminal.comparison_analysis import yahoo_finance_view +from gamestonk_terminal.comparison_analysis import market_watch_view +from gamestonk_terminal.comparison_analysis import finbrain_view from gamestonk_terminal.comparison_analysis import finviz_compare_view from gamestonk_terminal.portfolio_optimization import po_controller from gamestonk_terminal.menu import session -from prompt_toolkit.completion import NestedCompleter class ComparisonAnalysisController: @@ -54,9 +55,23 @@ class ComparisonAnalysisController: start: datetime, interval: str, similar: List[str], - user: bool, ): - """Constructor""" + """Constructor + + Parameters + ---------- + stock : pd.DataFrame + Stock data + ticker : str + Stock ticker + start : datetime + Start time + interval : str + Time interval + similar : List[str] + List of similar tickers + """ + self.similar = similar self.user = "" self.stock = stock @@ -69,7 +84,6 @@ class ComparisonAnalysisController: choices=self.CHOICES, ) - @staticmethod def print_help(self): """Print help""" @@ -108,14 +122,21 @@ class ComparisonAnalysisController: print(" performance brief performance comparison") print(" technical brief technical comparison") print("") + if self.similar: print(" > po portfolio optimization for selected tickers") print("") return - @staticmethod def get_similar_companies(self, other_args: List[str]): - """ Get similar companies. [Source: Polygon API] """ + """Get similar companies. [Source: Polygon API] + + Parameters + ---------- + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, prog="get", @@ -189,9 +210,15 @@ class ComparisonAnalysisController: print("") return - @staticmethod def select_similar_companies(self, other_args: List[str]): - """ Select similar companies, e.g. NIO,XPEV,LI""" + """Select similar companies, e.g. NIO,XPEV,LI + + Parameters + ---------- + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, prog="select", @@ -242,7 +269,7 @@ class ComparisonAnalysisController: def call_help(self, _): """Process Help command""" - self.print_help(self) + self.print_help() def call_q(self, _): """Process Q command - quit the menu""" @@ -254,43 +281,43 @@ class ComparisonAnalysisController: def call_get(self, other_args: List[str]): """Process get command""" - self.get_similar_companies(self, other_args) + self.get_similar_companies(other_args) def call_select(self, other_args: List[str]): """Process select command""" - self.select_similar_companies(self, other_args) + self.select_similar_companies(other_args) def call_historical(self, other_args: List[str]): """Process historical command""" - yf_api.historical( + yahoo_finance_view.historical( other_args, self.stock, self.ticker, self.start, self.interval, self.similar ) def call_hcorr(self, other_args: List[str]): """Process historical correlation command""" - yf_api.correlation( + yahoo_finance_view.correlation( other_args, self.stock, self.ticker, self.start, self.interval, self.similar ) def call_income(self, other_args: List[str]): """Process income command""" - mw_api.compare_income(other_args, self.ticker, self.similar) + market_watch_view.compare_income(other_args, self.ticker, self.similar) def call_balance(self, other_args: List[str]): """Process balance command""" - mw_api.compare_balance(other_args, self.ticker, self.similar) + market_watch_view.compare_balance(other_args, self.ticker, self.similar) def call_cashflow(self, other_args: List[str]): """Process cashflow command""" - mw_api.compare_cashflow(other_args, self.ticker, self.similar) + market_watch_view.compare_cashflow(other_args, self.ticker, self.similar) def call_sentiment(self, other_args: List[str]): """Process sentiment command""" - f_api.sentiment_compare(other_args, self.ticker, self.similar) + finbrain_view.sentiment_compare(other_args, self.ticker, self.similar) def call_scorr(self, other_args: List[str]): """Process sentiment correlation command""" - f_api.sentiment_correlation(other_args, self.ticker, self.similar) + finbrain_view.sentiment_correlation(other_args, self.ticker, self.similar) def call_overview(self, other_args: List[str]): """Process overview command""" @@ -324,11 +351,21 @@ class ComparisonAnalysisController: def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str): - """Comparison Analysis Menu""" + """Comparison Analysis Menu - ca_controller = ComparisonAnalysisController( - stock, ticker, start, interval, [], False - ) + Parameters + ---------- + stock : pd.DataFrame + Stock data + ticker : str + Stock ticker + start : datetime + Time start + interval : str + Time interval + """ + + ca_controller = ComparisonAnalysisController(stock, ticker, start, interval, []) ca_controller.call_help(None) while True: diff --git a/gamestonk_terminal/comparison_analysis/finbrain_api.py b/gamestonk_terminal/comparison_analysis/finbrain_view.py similarity index 99% rename from gamestonk_terminal/comparison_analysis/finbrain_api.py rename to gamestonk_terminal/comparison_analysis/finbrain_view.py index c1cb629d893..cc0ee139d64 100644 --- a/gamestonk_terminal/comparison_analysis/finbrain_api.py +++ b/gamestonk_terminal/comparison_analysis/finbrain_view.py @@ -10,6 +10,7 @@ import pandas as pd from pandas.plotting import register_matplotlib_converters import numpy as np import seaborn as sns + from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, plot_autoscale, @@ -33,6 +34,7 @@ def sentiment_compare(other_args: List[str], ticker: str, similar: List[str]): similar : List[str] Similar companies to compare income with """ + parser = argparse.ArgumentParser( add_help=False, prog="sentiment_compare", @@ -98,6 +100,7 @@ def sentiment_correlation(other_args: List[str], ticker: str, similar: List[str] similar : List[str] Similar companies to compare income with """ + parser = argparse.ArgumentParser( add_help=False, prog="sentiment_compare", @@ -164,6 +167,7 @@ def get_sentiments(similar: List[str]) -> pd.DataFrame: DataFrame() Contains sentiment analysis from several tickers """ + df_sentiment = pd.DataFrame() for ticker in similar: result = requests.get(f"https://api.finbrain.tech/v0/sentiments/{ticker}") @@ -196,6 +200,7 @@ def plot_sentiments(df_sentiment: pd.DataFrame, similar: List[str]): similar : List[str] List of similar companies """ + plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) for idx, ticker in enumerate(similar): @@ -251,6 +256,7 @@ def plot_correlation(df_sentiment: pd.DataFrame, similar: List[str]): similar : List[str] List of similar companies """ + plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) mask = np.zeros((len(similar), len(similar)), dtype=bool) diff --git a/gamestonk_terminal/comparison_analysis/market_watch_api.py b/gamestonk_terminal/comparison_analysis/market_watch_view.py similarity index 99% rename from gamestonk_terminal/comparison_analysis/market_watch_api.py rename to gamestonk_terminal/comparison_analysis/market_watch_view.py index 5067cdeae38..4cef54e2b79 100644 --- a/gamestonk_terminal/comparison_analysis/market_watch_api.py +++ b/gamestonk_terminal/comparison_analysis/market_watch_view.py @@ -27,6 +27,7 @@ def compare_income(other_args: List[str], ticker: str, similar: List[str]): similar : List[str] Similar companies to compare income with """ + parser = argparse.ArgumentParser( add_help=False, prog="income", @@ -132,6 +133,7 @@ def compare_balance(other_args: List[str], ticker: str, similar: List[str]): similar : List[str] Similar companies to compare income with """ + parser = argparse.ArgumentParser( add_help=False, prog="compare_balance", @@ -237,6 +239,7 @@ def compare_cashflow(other_args: List[str], ticker: str, similar: List[str]): similar : List[str] Similar companies to compare income with """ + parser = argparse.ArgumentParser( add_help=False, prog="compare_cashflow", diff --git a/gamestonk_terminal/comparison_analysis/yahoo_finance_api.py b/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py similarity index 77% rename from gamestonk_terminal/comparison_analysis/yahoo_finance_api.py rename to gamestonk_terminal/comparison_analysis/yahoo_finance_view.py index 72df9f825ad..095a506b4f6 100644 --- a/gamestonk_terminal/comparison_analysis/yahoo_finance_api.py +++ b/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py @@ -1,4 +1,9 @@ +""" Comparison Analysis Yahoo Finance View """ +__docformat__ = "numpy" + import argparse +from typing import List +from datetime import datetime import numpy as np import pandas as pd from pandas.plotting import register_matplotlib_converters @@ -21,6 +26,24 @@ d_candle_types = { def check_one_of_ohlca(type_candles: str) -> str: + """Convert a candle type + + Parameters + ---------- + type_candles : str + OHLCA candle type + + Returns + ------- + str + Converted candle type + + Raises + ------ + argparse.ArgumentTypeError + Unknown candle type + """ + if ( type_candles == "o" or type_candles == "h" @@ -32,7 +55,32 @@ def check_one_of_ohlca(type_candles: str) -> str: raise argparse.ArgumentTypeError("The type of candles specified is not recognized") -def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar): +def historical( + other_args: List[str], + df_stock: pd.DataFrame, + ticker: str, + start: datetime, + interval: str, + similar: List[str], +): + """Display historical data from Yahoo Finance + + Parameters + ---------- + other_args : List[str] + Command line arguments to be processed with argparse + df_stock : pd.DataFrame + Stock data + ticker : str + Ticker symbol + start : datetime + Time start + interval : str + Time interval + similar : List[str] + List of similar tickers + """ + parser = argparse.ArgumentParser( add_help=False, prog="historical", @@ -66,11 +114,11 @@ def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar): ) try: - if s_interval != "1440min": + if interval != "1440min": print("Intraday historical data analysis comparison is not yet available.") # Alpha Vantage only supports 5 calls per minute, we need another API to get intraday data else: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -80,16 +128,14 @@ def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar): similar += ns_parser.l_also plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI) - plt.title(f"Similar companies to {s_ticker}") - df_stock = yf.download( - s_ticker, start=s_start, progress=False, threads=False - ) + plt.title(f"Similar companies to {ticker}") + df_stock = yf.download(ticker, start=start, progress=False, threads=False) plt.plot( df_stock.index, df_stock[d_candle_types[ns_parser.type_candle]].values ) # plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=2) l_min = [df_stock.index[0]] - l_leg = [s_ticker] + l_leg = [ticker] l_stocks = similar[:] @@ -98,7 +144,7 @@ def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar): for symbol in l_stocks: try: df_similar_stock = yf.download( - symbol, start=s_start, progress=False, threads=False + symbol, start=start, progress=False, threads=False ) if not df_similar_stock.empty: plt.plot( @@ -141,7 +187,25 @@ def historical(l_args, df_stock, s_ticker, s_start, s_interval, similar): print(e, "\n") -def correlation(l_args, df_stock, s_ticker, s_start, s_interval, similar): +def correlation(other_args, df_stock, ticker, start, interval, similar): + """[summary] + + Parameters + ---------- + other_args : [type] + Command line arguments to be processed with argparse + df_stock : pd.DataFrame + Stock data + ticker : str + Ticker symbol + start : datetime + Time start + interval : str + Time interval + similar : [type] + List of similar tickers + """ + parser = argparse.ArgumentParser( add_help=False, prog="corr", @@ -176,11 +240,11 @@ def correlation(l_args, df_stock, s_ticker, s_start, s_interval, similar): ) try: - if s_interval != "1440min": + if interval != "1440min": print("Intraday historical data analysis comparison is not yet available.") # Alpha Vantage only supports 5 calls per minute, we need another API to get intraday data else: - ns_parser = parse_known_args_and_warn(parser, l_args) + ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: return @@ -193,21 +257,21 @@ def correlation(l_args, df_stock, s_ticker, s_start, s_interval, similar): print("Provide at least a similar company for correlation") else: d_stock = {} - d_stock[s_ticker] = yf.download(s_ticker, start=s_start, progress=False) - l_min = [d_stock[s_ticker].index[0]] + d_stock[ticker] = yf.download(ticker, start=start, progress=False) + l_min = [d_stock[ticker].index[0]] for symbol in similar: - d_stock[symbol] = yf.download(symbol, start=s_start, progress=False) + d_stock[symbol] = yf.download(symbol, start=start, progress=False) if not d_stock[symbol].empty: l_min.append(d_stock[symbol].index[0]) min_start_date = max(l_min) - df_stock = d_stock[s_ticker][ + df_stock = d_stock[ticker][ d_candle_types[ns_parser.type_candle] - ].rename(s_ticker) + ].rename(ticker) for symbol in d_stock: - if symbol != s_ticker: + if symbol != ticker: if not d_stock[symbol].empty: df_stock = pd.concat( [ From 4355dcaaf697c62cf6b02ed76eaff76922ad78e1 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 13:25:51 -0700 Subject: [PATCH 05/10] Adding __init__.py --- gamestonk_terminal/comparison_analysis/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gamestonk_terminal/comparison_analysis/__init__.py diff --git a/gamestonk_terminal/comparison_analysis/__init__.py b/gamestonk_terminal/comparison_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 7ecc5f2feacc494beeb3ccc826c2807cd67d4460 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 13:37:56 -0700 Subject: [PATCH 06/10] Try a different way to pylint --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e85716d7882..a6e166da0c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,8 @@ jobs: - run: mypy --ignore-missing-imports . - run: shopt -s globstar && pyupgrade --py36-plus **/*.py - run: safety check - - run: pylint **/*.py + - run: | + find . -name '*.py' -exec pylint {} \; test: name: ubuntu-latest - Python 3.8 needs: linting From 67c31b1b035d424c39c0813a0c653d9a2ff696d9 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 14:15:45 -0700 Subject: [PATCH 07/10] Updating pylint to actually run --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6e166da0c5..fceaff60738 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: - run: shopt -s globstar && pyupgrade --py36-plus **/*.py - run: safety check - run: | - find . -name '*.py' -exec pylint {} \; + pylint terminal.py gamestonk_terminal tests test: name: ubuntu-latest - Python 3.8 needs: linting From 5718b49a843d6550ff35626401598cdc5021c1b7 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 22:25:29 -0700 Subject: [PATCH 08/10] Fixing pylint errors and warnings --- .pylintrc | 6 +- .../behavioural_analysis/ba_controller.py | 2 +- .../behavioural_analysis/finbrain_view.py | 3 +- .../behavioural_analysis/reddit_view.py | 22 ----- .../comparison_analysis/yahoo_finance_view.py | 9 +- gamestonk_terminal/discovery/finviz_view.py | 15 ++- .../discovery/marketbeat_view.py | 4 +- .../discovery/seeking_alpha_model.py | 4 +- .../discovery/seeking_alpha_view.py | 2 +- .../due_diligence/finra_view.py | 3 +- .../portfolio/port_controller.py | 7 +- .../optimizer_helper.py | 23 +++-- .../portfolio_optimization/optimizer_view.py | 99 +++++++++---------- .../portfolio_optimization/po_controller.py | 9 +- .../prediction_techniques/ets.py | 12 +-- .../prediction_techniques/neural_networks.py | 21 ---- .../prediction_techniques/pred_controller.py | 9 +- .../prediction_techniques/pred_helper.py | 3 +- .../technical_analysis/finviz_view.py | 3 +- .../technical_analysis/overlap.py | 2 +- .../technical_analysis/ta_controller.py | 10 +- .../technical_analysis/tradingview_view.py | 3 +- terminal.py | 14 +-- 23 files changed, 119 insertions(+), 166 deletions(-) diff --git a/.pylintrc b/.pylintrc index ceaa81904dc..c8806cea49c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -54,7 +54,7 @@ confidence= # disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call # disable=too-many-instance-attributes,len-as-condition,too-few-public-methods,anomalous-backslash-in-string,no-else-return,simplifiable-if-statement,too-many-arguments,duplicate-code,no-name-in-module,no-member,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,missing-docstring,invalid-name,bad-whitespace,consider-using-enumerate,unexpected-keyword-arg -disable=useless-return,missing-docstring,invalid-name,import-error,bad-continuation,broad-except,unnecessary-lambda,no-self-use,duplicate-code,consider-using-generator +disable=useless-return,missing-docstring,invalid-name,import-error,bad-continuation,broad-except,unnecessary-lambda,no-self-use,duplicate-code,consider-using-generator,unbalanced-tuple-unpacking # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -397,7 +397,7 @@ valid-metaclass-classmethod-first-arg=mcs [DESIGN] # Maximum number of arguments for function / method -max-args=5 +max-args=8 # Maximum number of attributes for a class (see R0902). # max-attributes=7 @@ -416,7 +416,7 @@ max-locals=40 max-parents=7 # Maximum number of public methods for a class (see R0904). -max-public-methods=20 +max-public-methods=30 # Maximum number of return / yield for function / method body max-returns=9 diff --git a/gamestonk_terminal/behavioural_analysis/ba_controller.py b/gamestonk_terminal/behavioural_analysis/ba_controller.py index 57e9a66cbe6..e8ae8f60aee 100644 --- a/gamestonk_terminal/behavioural_analysis/ba_controller.py +++ b/gamestonk_terminal/behavioural_analysis/ba_controller.py @@ -4,6 +4,7 @@ __docformat__ = "numpy" import argparse from typing import List from datetime import datetime +from prompt_toolkit.completion import NestedCompleter from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair @@ -14,7 +15,6 @@ from gamestonk_terminal.behavioural_analysis import ( stocktwits_view, finbrain_view, ) -from prompt_toolkit.completion import NestedCompleter class BehaviouralAnalysisController: diff --git a/gamestonk_terminal/behavioural_analysis/finbrain_view.py b/gamestonk_terminal/behavioural_analysis/finbrain_view.py index 090327aff73..e5d1907e485 100644 --- a/gamestonk_terminal/behavioural_analysis/finbrain_view.py +++ b/gamestonk_terminal/behavioural_analysis/finbrain_view.py @@ -9,13 +9,14 @@ import matplotlib.dates as mdates import pandas as pd from pandas.plotting import register_matplotlib_converters import numpy as np +from colorama import Fore, Style + from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, plot_autoscale, ) from gamestonk_terminal.config_plot import PLOT_DPI from gamestonk_terminal import feature_flags as gtff -from colorama import Fore, Style register_matplotlib_converters() diff --git a/gamestonk_terminal/behavioural_analysis/reddit_view.py b/gamestonk_terminal/behavioural_analysis/reddit_view.py index fdf6b1396d1..231f1728276 100644 --- a/gamestonk_terminal/behavioural_analysis/reddit_view.py +++ b/gamestonk_terminal/behavioural_analysis/reddit_view.py @@ -189,17 +189,6 @@ def popular_tickers(l_args): wallstreetbets """, ) - """ - parser.add_argument( - "-d", - "--days", - action="store", - dest="n_days", - type=check_positive, - default=1, - help="look for the tickers from those n past days.", - ) - """ try: ns_parser = parse_known_args_and_warn(parser, l_args) @@ -502,17 +491,6 @@ def spac(l_args): default=5, help="limit of posts with SPACs retrieved.", ) - """ - parser.add_argument( - "-d", - "--days", - action="store", - dest="n_days", - type=check_positive, - default=5, - help="look for the tickers from those n past days.", - ) - """ try: ns_parser = parse_known_args_and_warn(parser, l_args) diff --git a/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py b/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py index 095a506b4f6..d2552845e61 100644 --- a/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py +++ b/gamestonk_terminal/comparison_analysis/yahoo_finance_view.py @@ -44,14 +44,9 @@ def check_one_of_ohlca(type_candles: str) -> str: Unknown candle type """ - if ( - type_candles == "o" - or type_candles == "h" - or type_candles == "l" - or type_candles == "c" - or type_candles == "a" - ): + if type_candles in ("o", "h", "l", "c", "a"): return type_candles + raise argparse.ArgumentTypeError("The type of candles specified is not recognized") diff --git a/gamestonk_terminal/discovery/finviz_view.py b/gamestonk_terminal/discovery/finviz_view.py index 3bd3e06e8f6..284779960c4 100644 --- a/gamestonk_terminal/discovery/finviz_view.py +++ b/gamestonk_terminal/discovery/finviz_view.py @@ -4,10 +4,11 @@ __docformat__ = "numpy" import argparse from typing import List import webbrowser -from gamestonk_terminal.helper_funcs import parse_known_args_and_warn from finvizfinance.group import valuation, performance, spectrum from PIL import Image +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + l_GROUPS = [ "Sector", "Industry", @@ -97,6 +98,7 @@ def get_valuation_performance_data(group: str, data_type: str): pd.DataFrame dataframe with valuation/performance data """ + if data_type == "valuation": df_group = valuation.Valuation().ScreenerView(group=group) elif data_type == "performance": @@ -117,6 +119,7 @@ def get_spectrum_data(group: str): data_type : str select data type to see data between valuation or performance """ + spectrum.Spectrum().ScreenerView(group=group) @@ -130,6 +133,7 @@ def view_group_data(other_args: List[str], data_type: str): data_type : str select data type to see data between valuation, performance and spectrum """ + parser = argparse.ArgumentParser( add_help=False, prog="group_data", @@ -156,12 +160,12 @@ def view_group_data(other_args: List[str], data_type: str): ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return + return None if isinstance(ns_parser.group, List): ns_parser.group = ns_parser.group[0] - if data_type == "valuation" or data_type == "performance": + if data_type in ("valuation", "performance"): df_group = get_valuation_performance_data(ns_parser.group, data_type) print(df_group.to_string()) @@ -172,7 +176,7 @@ def view_group_data(other_args: List[str], data_type: str): img.show() print("") - return ns_parser.group + return img else: print( @@ -180,7 +184,8 @@ def view_group_data(other_args: List[str], data_type: str): ) print("") - return "" + return None except SystemExit: print("") + return None diff --git a/gamestonk_terminal/discovery/marketbeat_view.py b/gamestonk_terminal/discovery/marketbeat_view.py index 13ec048ee5c..c2f3c58007d 100644 --- a/gamestonk_terminal/discovery/marketbeat_view.py +++ b/gamestonk_terminal/discovery/marketbeat_view.py @@ -2,14 +2,14 @@ __docformat__ = "numpy" import argparse +from typing import List import numpy as np import pandas as pd -from typing import List + from gamestonk_terminal.helper_funcs import ( check_positive, parse_known_args_and_warn, ) - from gamestonk_terminal.discovery import marketbeat_model diff --git a/gamestonk_terminal/discovery/seeking_alpha_model.py b/gamestonk_terminal/discovery/seeking_alpha_model.py index 6041eb63f67..1a2df5f03a3 100644 --- a/gamestonk_terminal/discovery/seeking_alpha_model.py +++ b/gamestonk_terminal/discovery/seeking_alpha_model.py @@ -2,11 +2,11 @@ __docformat__ = "numpy" from typing import List +from datetime import datetime import requests from bs4 import BeautifulSoup import pandas as pd from pandas.core.frame import DataFrame -from datetime import datetime from gamestonk_terminal.helper_funcs import get_user_agent @@ -180,7 +180,7 @@ def get_trending_list(num: int) -> list: } ) - return articles + return articles[:num] def get_article_data(article_id: int) -> dict: diff --git a/gamestonk_terminal/discovery/seeking_alpha_view.py b/gamestonk_terminal/discovery/seeking_alpha_view.py index 1670cc3e084..ee65fae29d5 100644 --- a/gamestonk_terminal/discovery/seeking_alpha_view.py +++ b/gamestonk_terminal/discovery/seeking_alpha_view.py @@ -3,8 +3,8 @@ __docformat__ = "numpy" import argparse from typing import List -import pandas as pd from datetime import datetime +import pandas as pd from gamestonk_terminal.helper_funcs import ( check_positive, parse_known_args_and_warn, diff --git a/gamestonk_terminal/due_diligence/finra_view.py b/gamestonk_terminal/due_diligence/finra_view.py index baa7884dcea..8182e376cb1 100644 --- a/gamestonk_terminal/due_diligence/finra_view.py +++ b/gamestonk_terminal/due_diligence/finra_view.py @@ -200,7 +200,8 @@ def plot_dark_pools(ticker: str, ats: pd.DataFrame, otc: pd.DataFrame): otc : pd.DataFrame OTC (Non-ATS) Data """ - _, axData = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + + _, _ = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.subplot(3, 1, (1, 2)) if not ats.empty and not otc.empty: diff --git a/gamestonk_terminal/portfolio/port_controller.py b/gamestonk_terminal/portfolio/port_controller.py index d6d5265789e..e9d6b0c1fea 100644 --- a/gamestonk_terminal/portfolio/port_controller.py +++ b/gamestonk_terminal/portfolio/port_controller.py @@ -4,6 +4,7 @@ import argparse from typing import List import pandas as pd from prompt_toolkit.completion import NestedCompleter + from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session @@ -15,6 +16,7 @@ from gamestonk_terminal.portfolio.portfolio_helpers import ( class PortfolioController: + """ Portfolio Controller """ CHOICES = [ "help", @@ -129,6 +131,7 @@ class PortfolioController: if broker in self.BROKERS: api = broker + "_api" try: + # pylint: disable=eval-used eval(api + ".login()") self.broker_list.add(broker) logged_in = True @@ -179,7 +182,9 @@ class PortfolioController: print("Login to desired brokers\n") for broker in self.broker_list: holdings = pd.concat( - [holdings, eval(broker + "_api.return_holdings()")], axis=0 + # pylint: disable=eval-used + [holdings, eval(broker + "_api.return_holdings()")], + axis=0, ) self.merged_holdings = merge_portfolios(holdings) print_portfolio(self.merged_holdings) diff --git a/gamestonk_terminal/portfolio_optimization/optimizer_helper.py b/gamestonk_terminal/portfolio_optimization/optimizer_helper.py index f88ad80463c..8236d01b789 100644 --- a/gamestonk_terminal/portfolio_optimization/optimizer_helper.py +++ b/gamestonk_terminal/portfolio_optimization/optimizer_helper.py @@ -1,4 +1,4 @@ -""" Portfolio Optimization Helper Functions""" +""" Portfolio Optimization Helper Functions """ __docformat__ = "numpy" import argparse @@ -110,12 +110,12 @@ l_valid_property_infos = [ ] -def check_valid_property_type(property: str) -> str: +def check_valid_property_type(aproperty: str) -> str: """ Check that the property selected is valid """ - if property in l_valid_property_infos: - return property - else: - raise argparse.ArgumentTypeError(f"{property} is not a valid info") + if aproperty in l_valid_property_infos: + return aproperty + + raise argparse.ArgumentTypeError(f"{aproperty} is not a valid info") def process_stocks(list_of_stocks: List[str], period: str = "3mo") -> pd.DataFrame: @@ -133,6 +133,7 @@ def process_stocks(list_of_stocks: List[str], period: str = "3mo") -> pd.DataFra stock_closes: DataFrame DataFrame containing daily (adjusted) close prices for each stock in list """ + stock_prices = yf.download( list_of_stocks, period=period, progress=False, group_by="ticker" ) @@ -155,6 +156,7 @@ def prepare_efficient_frontier(stock_prices: pd.DataFrame): ef: EfficientFrontier EfficientFrontier object """ + mu = expected_returns.mean_historical_return(stock_prices) S = risk_models.sample_cov(stock_prices) ef = EfficientFrontier(mu, S) @@ -162,13 +164,14 @@ def prepare_efficient_frontier(stock_prices: pd.DataFrame): def display_weights(weights: dict): - """ - Print weights in a nice format + """Print weights in a nice format + Parameters ---------- weights: dict weights to display. Keys are stocks. Values are either weights or values if -v specified """ + if not weights: return weight_df = pd.DataFrame.from_dict(data=weights, orient="index", columns=["value"]) @@ -185,8 +188,8 @@ def my_autopct(x): """Function for autopct of plt.pie. This results in values not being printed in the pie if they are 'too small'""" if x > 4: return f"{x:.2f} %" - else: - return "" + + return "" def pie_chart_weights(weights: dict, title_opt: str): diff --git a/gamestonk_terminal/portfolio_optimization/optimizer_view.py b/gamestonk_terminal/portfolio_optimization/optimizer_view.py index 6c61666d5bf..884bea7c578 100644 --- a/gamestonk_terminal/portfolio_optimization/optimizer_view.py +++ b/gamestonk_terminal/portfolio_optimization/optimizer_view.py @@ -122,12 +122,8 @@ def property_weighting(stocks: List[str], other_args: List[str]): List of tickers to be included in optimization other_args : List[str] Command line arguments to be processed with argparse - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers """ + parser = argparse.ArgumentParser( add_help=False, prog="property", @@ -136,7 +132,7 @@ def property_weighting(stocks: List[str], other_args: List[str]): parser.add_argument( "-p", "--property", - required=True if "-h" not in other_args else False, + required=bool("-h" not in other_args), type=check_valid_property_type, dest="property", help="""Property info to weigh. Use one of: @@ -217,19 +213,16 @@ def property_weighting(stocks: List[str], other_args: List[str]): def max_sharpe(stocks: List[str], other_args: List[str]): - """ - Return a portfolio that maximises the Sharpe Ratio + """Return a portfolio that maximises the Sharpe Ratio Parameters ---------- - stocks: List[str] + stocks : List[str] List of the stocks to be included in the weights - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers. + other_args : List[str] + argparse other args """ + parser = argparse.ArgumentParser( add_help=False, prog="maxsharpe", description="Maximise the Sharpe Ratio" ) @@ -273,7 +266,7 @@ def max_sharpe(stocks: List[str], other_args: List[str]): try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return None + return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") @@ -309,19 +302,16 @@ def max_sharpe(stocks: List[str], other_args: List[str]): def min_volatility(stocks: List[str], other_args: List[str]): - """ - Return a portfolio that optimizes for minimum volatility + """Return a portfolio that optimizes for minimum volatility Parameters ---------- - stocks: List[str] + stocks : List[str] List of the stocks to be included in the weights - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers. + other_args : List[str] + argparse other args """ + parser = argparse.ArgumentParser( add_help=False, prog="min_volatility", @@ -354,7 +344,7 @@ def min_volatility(stocks: List[str], other_args: List[str]): try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return None + return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") @@ -390,19 +380,16 @@ def min_volatility(stocks: List[str], other_args: List[str]): def max_quadratic_utility(stocks: List[str], other_args: List[str]): - """ - Return a portfolio that maximises the quadratic utility, given some risk aversion + """Return a portfolio that maximises the quadratic utility, given some risk aversion Parameters ---------- - stocks: List[str] + stocks : List[str] List of the stocks to be included in the weights - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers. + other_args : List[str] + argparse other args """ + parser = argparse.ArgumentParser( add_help=False, prog="max_quadratic_utility", @@ -458,7 +445,7 @@ def max_quadratic_utility(stocks: List[str], other_args: List[str]): try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return None + return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") @@ -499,20 +486,15 @@ def max_quadratic_utility(stocks: List[str], other_args: List[str]): def efficient_risk(stocks: List[str], other_args: List[str]): - """ - Return a portfolio that maximises return for a target risk. The resulting portfolio will have - a volatility less than the target (but not guaranteed to be equal) + """Return a portfolio that maximises return for a target risk. The resulting portfolio will have + a volatility less than the target (but not guaranteed to be equal) - Parameters - ---------- - stocks: List[str] + stocks : List[str] List of the stocks to be included in the weights - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers. + other_args : List[str] + argparse other args """ + parser = argparse.ArgumentParser( add_help=False, prog="effrisk", @@ -568,7 +550,7 @@ def efficient_risk(stocks: List[str], other_args: List[str]): try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return None + return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") @@ -609,19 +591,16 @@ def efficient_risk(stocks: List[str], other_args: List[str]): def efficient_return(stocks: List[str], other_args: List[str]): - """ - Return a portfolio that minimises volatility for a given target return ('Markowitz portfolio') + """Displays a portfolio that minimises volatility for a given target return ('Markowitz portfolio') Parameters ---------- - stocks: List[str] + stocks : List[str] List of the stocks to be included in the weights - - Returns - ------- - weights: dict - Dictionary of weights where keys are the tickers. + other_args : List[str] + argparse other args """ + parser = argparse.ArgumentParser( add_help=False, prog="effret", @@ -676,7 +655,7 @@ def efficient_return(stocks: List[str], other_args: List[str]): try: ns_parser = parse_known_args_and_warn(parser, other_args) if not ns_parser: - return None + return if len(stocks) < 2: print("Please have at least 2 loaded tickers to calculate weights.\n") @@ -717,6 +696,16 @@ def efficient_return(stocks: List[str], other_args: List[str]): def show_ef(stocks: List[str], other_args: List[str]): + """Display efficient frontier + + Parameters + ---------- + stocks : List[str] + List of the stocks to be included in the weights + other_args : List[str] + argparse other args + """ + parser = argparse.ArgumentParser( add_help=False, prog="ef", @@ -756,7 +745,7 @@ def show_ef(stocks: List[str], other_args: List[str]): mu = expected_returns.mean_historical_return(stock_prices) S = risk_models.sample_cov(stock_prices) ef = EfficientFrontier(mu, S) - fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) # Generate random portfolios n_samples = ns_parser.n_port diff --git a/gamestonk_terminal/portfolio_optimization/po_controller.py b/gamestonk_terminal/portfolio_optimization/po_controller.py index c66ca63e846..c4345e8b386 100644 --- a/gamestonk_terminal/portfolio_optimization/po_controller.py +++ b/gamestonk_terminal/portfolio_optimization/po_controller.py @@ -1,7 +1,6 @@ -"""Portfolio Optimization Controller Module""" +""" Portfolio Optimization Controller Module """ __docformat__ = "numpy" - import argparse from typing import List import matplotlib.pyplot as plt @@ -34,6 +33,7 @@ class PortfolioOptimization: "yolo", ] + # pylint: disable=dangerous-default-value def __init__(self, tickers: List[str] = []): """ Construct Portfolio Optimization @@ -113,12 +113,12 @@ class PortfolioOptimization: def call_add(self, other_args: List[str]): """Process add command""" - self.add_stocks(self, other_args) + self.add_stocks(other_args) def call_select(self, other_args: List[str]): """Process select command""" self.tickers = [] - self.add_stocks(self, other_args) + self.add_stocks(other_args) def call_equal(self, other_args: List[str]): """Process equal command""" @@ -168,7 +168,6 @@ class PortfolioOptimization: print({"GME": 200}) print("") - @staticmethod def add_stocks(self, other_args: List[str]): """ Add ticker or Select tickes for portfolio to be optimized """ parser = argparse.ArgumentParser( diff --git a/gamestonk_terminal/prediction_techniques/ets.py b/gamestonk_terminal/prediction_techniques/ets.py index 96e17306d78..c26e6f28a5a 100644 --- a/gamestonk_terminal/prediction_techniques/ets.py +++ b/gamestonk_terminal/prediction_techniques/ets.py @@ -5,6 +5,7 @@ import numpy as np import pandas as pd from pandas.plotting import register_matplotlib_converters from statsmodels.tsa.holtwinters import ExponentialSmoothing + from gamestonk_terminal.helper_funcs import ( check_positive, parse_known_args_and_warn, @@ -18,19 +19,18 @@ from gamestonk_terminal.prediction_techniques.pred_helper import ( price_prediction_backtesting_color, print_prediction_kpis, ) - from gamestonk_terminal.config_plot import PLOT_DPI from gamestonk_terminal import feature_flags as gtff register_matplotlib_converters() -trend = "Ad" # Additive damped, A Additive, N None -seasonal = "N" # None, A Additive, M Multiplicative -seasonal_periods = 5 +# trend = "Ad" # Additive damped, A Additive, N None +# seasonal = "N" # None, A Additive, M Multiplicative +# seasonal_periods = 5 def check_valid_trend(trend: str) -> str: - if trend == "N" or trend == "A" or trend == "Ad": + if trend in ("N", "A", "Ad"): return trend raise argparse.ArgumentTypeError( "Invalid trend selected. Choose between 'N', 'A', and 'Ad'" @@ -38,7 +38,7 @@ def check_valid_trend(trend: str) -> str: def check_valid_seasonal(seasonal: str) -> str: - if seasonal == "N" or seasonal == "A" or seasonal == "M": + if seasonal in ("N", "A", "M"): return seasonal raise argparse.ArgumentTypeError( "Invalid seasonal selected. Choose between 'N', 'A', and 'M'" diff --git a/gamestonk_terminal/prediction_techniques/neural_networks.py b/gamestonk_terminal/prediction_techniques/neural_networks.py index 2e7d63e5db6..d0489619576 100644 --- a/gamestonk_terminal/prediction_techniques/neural_networks.py +++ b/gamestonk_terminal/prediction_techniques/neural_networks.py @@ -408,27 +408,6 @@ def _plot_and_print_results( ) plt.plot(df_pred.index, df_pred[df_pred.columns[0]], lw=2, c="tab:green") else: - """ - # Looks too noisy to output all loops predictions - for idx_loop in range(ns_parser.n_loops): - plt.plot( - [df_stock.index[-1], df_pred.index[0]], - [ - df_stock["5. adjusted close"].values[-1], - df_pred[df_pred.columns[idx_loop]].values[0], - ], - lw=0.5, - c="tab:green", - linestyle="--", - ) - plt.plot( - df_pred.index, - df_pred[df_pred.columns[idx_loop]], - lw=1, - ls="--", - c="tab:green", - ) - """ df_quantiles = pd.DataFrame() df_quantiles["Quantile 10%"] = df_pred.quantile(0.1, axis=1) df_quantiles["Median"] = df_pred.quantile(0.5, axis=1) diff --git a/gamestonk_terminal/prediction_techniques/pred_controller.py b/gamestonk_terminal/prediction_techniques/pred_controller.py index 8d2bdb4b2be..2ca6c8e6443 100644 --- a/gamestonk_terminal/prediction_techniques/pred_controller.py +++ b/gamestonk_terminal/prediction_techniques/pred_controller.py @@ -1,8 +1,10 @@ import argparse from typing import List -import pandas as pd from datetime import datetime +import pandas as pd import matplotlib.pyplot as plt +from prompt_toolkit.completion import NestedCompleter + from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session @@ -14,7 +16,6 @@ from gamestonk_terminal.prediction_techniques import ( regression, sma, ) -from prompt_toolkit.completion import NestedCompleter class PredictionTechniquesController: @@ -59,7 +60,6 @@ class PredictionTechniquesController: choices=self.CHOICES, ) - @staticmethod def print_help(self): """ Print help """ @@ -110,7 +110,7 @@ class PredictionTechniquesController: def call_help(self, _): """Process Help command""" - self.print_help(self) + self.print_help() def call_q(self, _): """Process Q command - quit the menu""" @@ -170,6 +170,7 @@ class PredictionTechniquesController: def call_prophet(self, other_args: List[str]): """Process prophet command""" + # pylint: disable=import-outside-toplevel from gamestonk_terminal.prediction_techniques import fbprophet fbprophet.fbprophet(other_args, self.ticker, self.stock) diff --git a/gamestonk_terminal/prediction_techniques/pred_helper.py b/gamestonk_terminal/prediction_techniques/pred_helper.py index 43aba435f39..c69ac5f0616 100644 --- a/gamestonk_terminal/prediction_techniques/pred_helper.py +++ b/gamestonk_terminal/prediction_techniques/pred_helper.py @@ -1,13 +1,14 @@ import numpy as np import pandas as pd from colorama import Fore, Style -from gamestonk_terminal import feature_flags as gtff from sklearn.metrics import ( mean_absolute_error, r2_score, mean_squared_error, ) +from gamestonk_terminal import feature_flags as gtff + def price_prediction_color(val: float, last_val: float) -> str: if float(val) > last_val: diff --git a/gamestonk_terminal/technical_analysis/finviz_view.py b/gamestonk_terminal/technical_analysis/finviz_view.py index 0fc3f794356..1e74144ca48 100644 --- a/gamestonk_terminal/technical_analysis/finviz_view.py +++ b/gamestonk_terminal/technical_analysis/finviz_view.py @@ -3,10 +3,11 @@ __docformat__ = "numpy" import argparse from typing import List -from gamestonk_terminal.helper_funcs import parse_known_args_and_warn from finvizfinance.quote import finvizfinance from PIL import Image +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn + def view(other_args: List[str], ticker: str): """View historical price with trendlines. [Source: Finviz] diff --git a/gamestonk_terminal/technical_analysis/overlap.py b/gamestonk_terminal/technical_analysis/overlap.py index 7509c0ce2f3..a6c4b5d971a 100644 --- a/gamestonk_terminal/technical_analysis/overlap.py +++ b/gamestonk_terminal/technical_analysis/overlap.py @@ -69,7 +69,7 @@ def ema(l_args, s_ticker, s_interval, df_stock): offset=ns_parser.n_offset, ).dropna() - _, axPrice = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + _, _ = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) plt.title(f"{ns_parser.n_length} EMA on {s_ticker}") if s_interval == "1440min": plt.plot( diff --git a/gamestonk_terminal/technical_analysis/ta_controller.py b/gamestonk_terminal/technical_analysis/ta_controller.py index 7362b2edb26..620e13ab55f 100644 --- a/gamestonk_terminal/technical_analysis/ta_controller.py +++ b/gamestonk_terminal/technical_analysis/ta_controller.py @@ -3,10 +3,12 @@ __docformat__ = "numpy" import argparse import os -import pandas as pd from typing import List from datetime import datetime +import pandas as pd import matplotlib.pyplot as plt +from prompt_toolkit.completion import NestedCompleter + from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal.helper_funcs import get_flair from gamestonk_terminal.menu import session @@ -18,7 +20,6 @@ from gamestonk_terminal.technical_analysis import volume as ta_volume from gamestonk_terminal.technical_analysis import finbrain_view from gamestonk_terminal.technical_analysis import tradingview_view from gamestonk_terminal.technical_analysis import finviz_view -from prompt_toolkit.completion import NestedCompleter class TechnicalAnalysisController: @@ -65,7 +66,6 @@ class TechnicalAnalysisController: choices=self.CHOICES, ) - @staticmethod def print_help(self): """Print help""" @@ -106,7 +106,6 @@ class TechnicalAnalysisController: print(" ad chaikin accumulation/distribution line values") print(" obv on balance volume") print("") - return def switch(self, an_input: str): """Process and dispatch input @@ -118,6 +117,7 @@ class TechnicalAnalysisController: True - quit the program None - continue in the menu """ + (known_args, other_args) = self.ta_parser.parse_known_args(an_input.split()) # Due to Finviz implementation of Spectrum, we delete the generated spectrum figure @@ -134,7 +134,7 @@ class TechnicalAnalysisController: def call_help(self, _): """Process Help command""" - self.print_help(self) + self.print_help() def call_q(self, _): """Process Q command - quit the menu""" diff --git a/gamestonk_terminal/technical_analysis/tradingview_view.py b/gamestonk_terminal/technical_analysis/tradingview_view.py index 61d2faf0723..b2f1668dfb4 100644 --- a/gamestonk_terminal/technical_analysis/tradingview_view.py +++ b/gamestonk_terminal/technical_analysis/tradingview_view.py @@ -2,8 +2,9 @@ import argparse from typing import List import requests import pandas as pd -from gamestonk_terminal import config_terminal as cfg from tradingview_ta import TA_Handler + +from gamestonk_terminal import config_terminal as cfg from gamestonk_terminal.helper_funcs import ( parse_known_args_and_warn, ) diff --git a/terminal.py b/terminal.py index 28c7d7c641c..ed0ff2cbbfe 100644 --- a/terminal.py +++ b/terminal.py @@ -33,13 +33,7 @@ from gamestonk_terminal.cryptocurrency import crypto_controller from gamestonk_terminal.screener import screener_controller from gamestonk_terminal.portfolio_optimization import po_controller -# import warnings -# warnings.simplefilter("always") - - -# pylint: disable=too-many-branches - - +# pylint: disable=too-many-statements,too-many-branches def main(): """ Gamestonk Terminal is an awesome stock market terminal that has been developed for fun, @@ -361,9 +355,9 @@ def main(): if b_quit: break - else: - if not main_cmd: - should_print_help = True + + if not main_cmd: + should_print_help = True print( "Hope you enjoyed the terminal. Remember that stonks only go up. Diamond hands.\n" From 31076e9c4ab34a0e5802ab6f1ea0f86dfe577fbc Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Tue, 20 Apr 2021 22:30:30 -0700 Subject: [PATCH 09/10] Adding extra line --- terminal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/terminal.py b/terminal.py index ed0ff2cbbfe..78e912c761d 100644 --- a/terminal.py +++ b/terminal.py @@ -33,6 +33,7 @@ from gamestonk_terminal.cryptocurrency import crypto_controller from gamestonk_terminal.screener import screener_controller from gamestonk_terminal.portfolio_optimization import po_controller + # pylint: disable=too-many-statements,too-many-branches def main(): """ From 0265a4f78c3a4b0954dac1d4cc8c0d00f90e73f3 Mon Sep 17 00:00:00 2001 From: Jamie Date: Wed, 21 Apr 2021 15:35:19 -0400 Subject: [PATCH 10/10] fix test --- tests/test_coingecko_crypto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_coingecko_crypto.py b/tests/test_coingecko_crypto.py index 003b5927f8b..e0eed2eed55 100644 --- a/tests/test_coingecko_crypto.py +++ b/tests/test_coingecko_crypto.py @@ -8,7 +8,7 @@ from gamestonk_terminal.cryptocurrency.pycoingecko_view import load class TestCoinGeckoAPI(TestCase): @mock.patch( - "gamestonk_terminal.cryptocurrency.coin_api.CoinGeckoAPI.get_coin_market_chart_by_id" + "gamestonk_terminal.cryptocurrency.pycoingecko_view.CoinGeckoAPI.get_coin_market_chart_by_id" ) def test_coin_api_load(self, mock_load): """