From 28ef84eebf9552d96e43e2f667f443a115fadce9 Mon Sep 17 00:00:00 2001 From: Artem Veremey Date: Sat, 27 Feb 2021 23:24:26 -0800 Subject: [PATCH] Adding more terminal color and more pylint --- gamestonk_terminal/discovery/fidelity_api.py | 42 ++++++++-- gamestonk_terminal/helper_funcs.py | 81 +++++++++++++++++-- .../tests/test_discovery_fidelity_api.py | 24 +++++- terminal.py | 36 ++++----- 4 files changed, 152 insertions(+), 31 deletions(-) diff --git a/gamestonk_terminal/discovery/fidelity_api.py b/gamestonk_terminal/discovery/fidelity_api.py index fc17369ebf0..c55db33429b 100644 --- a/gamestonk_terminal/discovery/fidelity_api.py +++ b/gamestonk_terminal/discovery/fidelity_api.py @@ -1,15 +1,38 @@ import argparse -from gamestonk_terminal.config_terminal import USE_COLOR +import re import requests from colorama import Fore, Style from bs4 import BeautifulSoup import pandas as pd -from gamestonk_terminal.helper_funcs import check_positive, get_user_agent + +from gamestonk_terminal.helper_funcs import ( + check_positive, + get_user_agent, + patch_pandas_text_adjustment, +) +from gamestonk_terminal.config_terminal import USE_COLOR -def color_red_green(val): - vval = float(val.split(" ")[0]) - if vval < 0: +def buy_sell_ratio_color_red_green(val: str) -> str: + buy_sell_match = re.match(r"(\d+)% Buys, (\d+)% Sells", val, re.M | re.I) + + if not buy_sell_match: + return val + + buys = int(buy_sell_match.group(1)) + sells = int(buy_sell_match.group(2)) + + if buys >= sells: + return "{}{}%{} Buys, {}% Sells".format( + Fore.GREEN, buys, Style.RESET_ALL, sells + ) + + return "{}% Buys, {}{}%{} Sells".format(buys, Fore.RED, sells, Style.RESET_ALL) + + +def price_change_color_red_green(val: str) -> str: + val_float = float(val.split(" ")[0]) + if val_float > 0: color = Fore.GREEN else: color = Fore.RED @@ -129,7 +152,14 @@ def orders(l_args): pd.set_option("display.max_colwidth", -1) if USE_COLOR: - df_orders["Price Change"] = df_orders["Price Change"].apply(color_red_green) + df_orders["Buy / Sell Ratio"] = df_orders["Buy / Sell Ratio"].apply( + buy_sell_ratio_color_red_green + ) + df_orders["Price Change"] = df_orders["Price Change"].apply( + price_change_color_red_green + ) + + patch_pandas_text_adjustment() print(df_orders.head(n=ns_parser.n_num).iloc[:, :-1].to_string(index=False)) print("") diff --git a/gamestonk_terminal/helper_funcs.py b/gamestonk_terminal/helper_funcs.py index bd674603907..80c6d825200 100644 --- a/gamestonk_terminal/helper_funcs.py +++ b/gamestonk_terminal/helper_funcs.py @@ -5,6 +5,8 @@ import re import sys import iso8601 import matplotlib.pyplot as plt +import pandas.io.formats.format +from pandas._config.config import get_option from pytz import timezone from holidays import US as holidaysUS @@ -28,17 +30,17 @@ def check_positive(value) -> int: def valid_date(s: str) -> datetime: try: return datetime.strptime(s, "%Y-%m-%d") - except ValueError: - raise argparse.ArgumentTypeError("Not a valid date: {s}") + except ValueError as value_error: + raise argparse.ArgumentTypeError("Not a valid date: {s}") from value_error # ----------------------------------------------------------------------------------------------------------------------- def plot_view_stock(df, symbol): df.sort_index(ascending=True, inplace=True) - pfig, axVolume = plt.subplots() + _, axVolume = plt.subplots() plt.bar(df.index, df.iloc[:, -1], color="k", alpha=0.8, width=0.3) plt.ylabel("Volume") - axPrice = axVolume.twinx() + _ = axVolume.twinx() plt.plot(df.index, df.iloc[:, :-1]) plt.title(symbol + " (Time Series)") plt.xlim(df.index[0], df.index[-1]) @@ -77,7 +79,7 @@ def plot_stock_ta(df_stock, s_ticker, df_ta, s_ta): # ----------------------------------------------------------------------------------------------------------------------- def plot_stock_and_ta(df_stock, s_ticker, df_ta, s_ta): - pfig, axPrice = plt.subplots() + _, axPrice = plt.subplots() plt.title(f"{s_ta} on {s_ticker}") plt.plot(df_stock.index, df_stock.values, "k", lw=3) plt.xlim(df_stock.index[0], df_stock.index[-1]) @@ -277,3 +279,72 @@ def get_user_agent() -> str: ] return random.choice(user_agent_strings) + + +# monkey patch Pandas +def text_adjustment_init(self): + self.ansi_regx = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]") + self.encoding = get_option("display.encoding") + + +def text_adjustment_len(self, text): + # return compat.strlen(self.ansi_regx.sub("", text), encoding=self.encoding) + return len(self.ansi_regx.sub("", text)) + + +def text_adjustment_justify(self, texts, max_len, mode="right"): + jfunc = ( + str.ljust + if (mode == "left") + else str.rjust + if (mode == "right") + else str.center + ) + out = [] + for s in texts: + escapes = self.ansi_regx.findall(s) + if len(escapes) == 2: + out.append( + escapes[0].strip() + + jfunc(self.ansi_regx.sub("", s), max_len) + + escapes[1].strip() + ) + else: + out.append(jfunc(s, max_len)) + return out + + +# pylint: disable=unused-argument +def text_adjustment_join_unicode(self, lines, sep=""): + try: + return sep.join(lines) + except UnicodeDecodeError: + # sep = compat.text_type(sep) + return sep.join([x.decode("utf-8") if isinstance(x, str) else x for x in lines]) + + +# pylint: disable=unused-argument +def text_adjustment_adjoin(self, space, *lists, **kwargs): + # Add space for all but the last column: + pads = ([space] * (len(lists) - 1)) + [0] + max_col_len = max([len(col) for col in lists]) + new_cols = [] + for col, pad in zip(lists, pads): + width = max([self.len(s) for s in col]) + pad + c = self.justify(col, width, mode="left") + # Add blank cells to end of col if needed for different col lens: + if len(col) < max_col_len: + c.extend([" " * width] * (max_col_len - len(col))) + new_cols.append(c) + + rows = [self.join_unicode(row_tup) for row_tup in zip(*new_cols)] + return self.join_unicode(rows, sep="\n") + + +# https://github.com/pandas-dev/pandas/issues/18066#issuecomment-522192922 +def patch_pandas_text_adjustment(): + pandas.io.formats.format.TextAdjustment.__init__ = text_adjustment_init + pandas.io.formats.format.TextAdjustment.len = text_adjustment_len + pandas.io.formats.format.TextAdjustment.justify = text_adjustment_justify + pandas.io.formats.format.TextAdjustment.join_unicode = text_adjustment_join_unicode + pandas.io.formats.format.TextAdjustment.adjoin = text_adjustment_adjoin diff --git a/gamestonk_terminal/tests/test_discovery_fidelity_api.py b/gamestonk_terminal/tests/test_discovery_fidelity_api.py index bf8c60ad344..5f97844a351 100755 --- a/gamestonk_terminal/tests/test_discovery_fidelity_api.py +++ b/gamestonk_terminal/tests/test_discovery_fidelity_api.py @@ -5,9 +5,31 @@ import unittest # pylint: disable=unused-import import pytest -from gamestonk_terminal.discovery.fidelity_api import orders +from gamestonk_terminal.discovery.fidelity_api import ( + buy_sell_ratio_color_red_green, + price_change_color_red_green, + orders, +) class TestDiscoveryFidelityApi(unittest.TestCase): def test_orders(self): orders([]) + + def test_buy_sell_ratio_color_red_green(self): + res = buy_sell_ratio_color_red_green("56% Buys, 44% Sells") + + assert res == "\x1b[32m56%\x1b[0m Buys, 44% Sells" + + res = buy_sell_ratio_color_red_green("44% Buys, 56% Sells") + + assert res == "44% Buys, \x1b[31m56%\x1b[0m Sells" + + def test_price_change_color_red_green(self): + res = price_change_color_red_green("-6.99 (-6.4288%)") + + assert res == "\x1b[31m-6.99 (-6.4288%)\x1b[0m" + + res = price_change_color_red_green("2.40 (+124.3523%)") + + assert res == "\x1b[32m2.40 (+124.3523%)\x1b[0m" diff --git a/terminal.py b/terminal.py index d83c1076472..9b7f953c702 100644 --- a/terminal.py +++ b/terminal.py @@ -72,12 +72,12 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock): ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas") # Daily if s_interval == "1440min": - df_stock, d_stock_metadata = ts.get_daily_adjusted( + df_stock, _, _ = ts.get_daily_adjusted( symbol=ns_parser.s_ticker, outputsize="full" ) # Intraday else: - df_stock, d_stock_metadata = ts.get_intraday( + df_stock, _, _ = ts.get_intraday( symbol=ns_parser.s_ticker, outputsize="full", interval=s_interval ) @@ -173,12 +173,10 @@ def view(l_args, s_ticker, s_start, s_interval, df_stock): ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas") # Daily if s_interval == "1440min": - df_stock, d_stock_metadata = ts.get_daily_adjusted( - symbol=s_ticker, outputsize="full" - ) + df_stock, _, _ = ts.get_daily_adjusted(symbol=s_ticker, outputsize="full") # Intraday else: - df_stock, d_stock_metadata = ts.get_intraday( + df_stock, _, _ = ts.get_intraday( symbol=s_ticker, outputsize="full", interval=s_interval ) @@ -270,24 +268,23 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): # ----------------------------------------------------------------------------------------------------------------------- def main(): - """Gamestonk Terminal is an awesome stock market terminal that has been developed for fun, - while I saw my GME shares tanking. But hey, I like the stock.""" + """ + Gamestonk Terminal is an awesome stock market terminal that has been developed for fun, + while I saw my GME shares tanking. But hey, I like the stock. + """ s_ticker = "" s_start = "" df_stock = pd.DataFrame() - b_intraday = False s_interval = "1440min" - """ # Set stock by default to speed up testing - s_ticker = "BB" - ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') - df_stock, d_stock_metadata = ts.get_daily_adjusted(symbol=s_ticker, outputsize='full') - df_stock.sort_index(ascending=True, inplace=True) - s_start = datetime.strptime("2020-06-04", "%Y-%m-%d") - df_stock = df_stock[s_start:] - """ + # s_ticker = "BB" + # ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format='pandas') + # df_stock, d_stock_metadata = ts.get_daily_adjusted(symbol=s_ticker, outputsize='full') + # df_stock.sort_index(ascending=True, inplace=True) + # s_start = datetime.strptime("2020-06-04", "%Y-%m-%d") + # df_stock = df_stock[s_start:] # Add list of arguments that the main parser accepts menu_parser = argparse.ArgumentParser(prog="gamestonk_terminal", add_help=False) @@ -320,7 +317,7 @@ def main(): as_input = input("> ") # Is command empty - if not len(as_input): + if not as_input: print("") continue @@ -433,9 +430,10 @@ def main(): else: ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas") - df_stock_pred, _ = ts.get_daily_adjusted( + df_stock_pred, _, _ = ts.get_daily_adjusted( symbol=s_ticker, outputsize="full" ) + # pylint: disable=no-member df_stock_pred = df_stock_pred.sort_index(ascending=True) df_stock_pred = df_stock_pred[s_start:] b_quit = pm.pred_menu(