diff --git a/ROADMAP.md b/ROADMAP.md index 92ac7a66635..4080bece07e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -21,92 +21,78 @@ ## Discovery ### Short term -* Add Cathy's ARK trading activity (@aia) * Top Losers (@didier) -### Medium term -* Add screeners (@didier) ### Long term +* Add screeners (@didier) ## Behavioral Analysis ### Short term * Implement a better Twitter client (@aia) -### Medium term: -* Add Stocktwits (@aia) ### Long term +* Add Stocktwits (@aia) ## Sell-side Equity Research ### Short term -### Medium term ### Long term ## Fundamental Analysis ### Short term * Multi-ticker comparison (@didier) -### Medium term +* Rearrange FA menu to have AV and FMP as submenus (@didier) ### Long term ## Technical Analysis ### Short term -### Medium term * Add auto-recognition of major TA patterns (@didier) ### Long term ## Due Diligence ### Short term -### Medium term ### Long term ## Prediction Techniques ### Short term -* Implement several exponential smoothing methods (@didier) -### Medium term ### Long term -### Reports +## Reports ### Short term * Expand Due Diligence report * Add multi-ticker comparison report -### Medium term ### Long term ## Portfolio Analysis ### Short term -### Medium term ### Long term ## Credit Analysis ### Short term -### Medium term ### Long term ## Cryptocurrencies ### Short term -### Medium term ### Long term ## Backend ### Short term -### Medium term +### Long term * Add caching * APIs -### Long term * GraphQL ## Frontend ### Short term -### Medium term ### Long term ## Developer Experience @@ -114,13 +100,10 @@ ### Short term * Add Windows CI (@aia) * Write a developer guide -### Medium term ### Long term ## User Experience ### Short term -### Medium term -* Add a WHL installation ### Long term - +* Add a WHL installation diff --git a/gamestonk_terminal/discovery/README.md b/gamestonk_terminal/discovery/README.md index a8de67a3bf4..c97af26a238 100644 --- a/gamestonk_terminal/discovery/README.md +++ b/gamestonk_terminal/discovery/README.md @@ -10,6 +10,8 @@ This menu aims to discover new stocks, and the usage of the following commands a * show latest top gainers [Yahoo Finance] * [orders](#orders) * orders by Fidelity Customers [Fidelity] +* [ark_orders](#ark_orders) + * orders by ARK Investment Management LLC [https://cathiesark.com] * [up_earnings](#up_earnings) * upcoming earnings release dates [Seeking Alpha] * [high_short](#high_short) @@ -70,6 +72,19 @@ Orders by Fidelity customers. Information shown in the table below is based on t Captura de ecrã 2021-02-20, às 11 45 43 +## ark_orders + +```shell +usage: ark_orders [-n N_NUM] +``` + + Orders by ARK Investment Management LLC - https://ark-funds.com/. [Source: https://cathiesark.com] + +* -n : Last N orders to be printed. Default 20. + +Captura de ecrã 2021-03-10, às 21 36 38 + + ## up_earnings ```shell diff --git a/gamestonk_terminal/discovery/ark_api.py b/gamestonk_terminal/discovery/ark_api.py new file mode 100644 index 00000000000..7dd5fb4d665 --- /dev/null +++ b/gamestonk_terminal/discovery/ark_api.py @@ -0,0 +1,133 @@ +import argparse +from datetime import timedelta +import json +import requests +from colorama import Fore, Style +from bs4 import BeautifulSoup +import numpy as np +import pandas as pd +from pandas.core.frame import DataFrame +import yfinance as yf + +from gamestonk_terminal.helper_funcs import ( + check_positive, + get_user_agent, + patch_pandas_text_adjustment, + parse_known_args_and_warn, +) +from gamestonk_terminal.config_terminal import USE_COLOR + + +def direction_color_red_green(val: str) -> str: + if val == "Buy": + ret = Fore.GREEN + val + Style.RESET_ALL + elif val == "Sell": + ret = Fore.RED + val + Style.RESET_ALL + else: + ret = val + + return ret + + +def get_ark_orders() -> DataFrame: + url_orders = "https://cathiesark.com/ark-funds-combined/trades" + + raw_page = requests.get(url_orders, headers={"User-Agent": get_user_agent()}).text + + parsed_script = BeautifulSoup(raw_page, "lxml").find( + "script", {"id": "__NEXT_DATA__"} + ) + + parsed_json = json.loads(parsed_script.string) + + df_orders = pd.json_normalize(parsed_json["props"]["pageProps"]["arkTrades"]) + df_orders.drop( + [ + "everything", + "everything.profile.customThumbnail", + "hidden", + "images.thumbnail", + ], + axis=1, + inplace=True, + ) + + df_orders["date"] = pd.to_datetime(df_orders["date"], format="%Y-%m-%dZ").dt.date + + return df_orders + + +def add_order_total(df_orders: DataFrame) -> DataFrame: + start_date = df_orders["date"].iloc[-1] - timedelta(days=1) + + tickers = " ".join(df_orders["ticker"].unique()) + + print("") + + prices = yf.download(tickers, start=start_date, progress=False) + + for i, candle in enumerate(["Volume", "Open", "Close", "High", "Low", "Total"]): + df_orders.insert(i + 3, candle.lower(), 0) + + for i, _ in df_orders.iterrows(): + if np.isnan( + prices["Open"][df_orders.loc[i, "ticker"]][ + df_orders.loc[i, "date"].strftime("%Y-%m-%d") + ] + ): + for candle in ["Volume", "Open", "Close", "High", "Low", "Total"]: + df_orders.loc[i, candle.lower()] = 0 + continue + + for candle in ["Volume", "Open", "Close", "High", "Low"]: + df_orders.loc[i, candle.lower()] = prices[candle][ + df_orders.loc[i, "ticker"] + ][df_orders.loc[i, "date"].strftime("%Y-%m-%d")] + + df_orders.loc[i, "total"] = ( + df_orders.loc[i, "close"] * df_orders.loc[i, "shares"] + ) + + return df_orders + + +def ark_orders(l_args): + parser = argparse.ArgumentParser( + prog="ARK Orders", + description=""" + Orders by ARK Investment Management LLC - https://ark-funds.com/. [Source: https://cathiesark.com] + """, + ) + + parser.add_argument( + "-n", + "--num", + action="store", + dest="n_num", + type=check_positive, + default=20, + help="Last N orders.", + ) + + ns_parser = parse_known_args_and_warn(parser, l_args) + + df_orders = get_ark_orders() + + pd.set_option("mode.chained_assignment", None) + df_orders = add_order_total(df_orders.head(ns_parser.n_num)) + + if USE_COLOR: + df_orders["direction"] = df_orders["direction"].apply(direction_color_red_green) + + patch_pandas_text_adjustment() + + df_orders["link"] = "https://finviz.com/quote.ashx?t=" + df_orders["ticker"] + + pd.set_option("display.max_colwidth", None) + pd.set_option("display.max_rows", None) + pd.set_option("display.float_format", "{:,.2f}".format) + print("") + print("Orders by ARK Investment Management LLC") + print("") + print(df_orders.to_string(index=False)) + print("") diff --git a/gamestonk_terminal/discovery/disc_menu.py b/gamestonk_terminal/discovery/disc_menu.py index bfabf65877b..bc6c3717efc 100644 --- a/gamestonk_terminal/discovery/disc_menu.py +++ b/gamestonk_terminal/discovery/disc_menu.py @@ -5,12 +5,16 @@ from gamestonk_terminal.discovery import finviz_api from gamestonk_terminal.discovery import short_interest_api from gamestonk_terminal.discovery import seeking_alpha_api from gamestonk_terminal.discovery import fidelity_api +from gamestonk_terminal.discovery import ark_api from gamestonk_terminal.discovery import simply_wallst_api from gamestonk_terminal.discovery import spachero_api from gamestonk_terminal.discovery import unusual_whales_api from gamestonk_terminal.helper_funcs import get_flair +from gamestonk_terminal.menu import session +from prompt_toolkit.completion import WordCompleter + def print_discovery(): """ Print help """ @@ -24,6 +28,7 @@ def print_discovery(): print(" sectors show sectors performance [Alpha Vantage]") print(" gainers show latest top gainers [Yahoo Finance]") print(" orders orders by Fidelity Customers [Fidelity]") + print(" ark_orders orders by ARK Investment Management LLC") print(" up_earnings upcoming earnings release dates [Seeking Alpha]") print( " high_short show top high short interest stocks of over 20% ratio [www.highshortinterest.com]" @@ -42,33 +47,36 @@ def disc_menu(): # Add list of arguments that the discovery parser accepts disc_parser = argparse.ArgumentParser(prog="discovery", add_help=False) - disc_parser.add_argument( - "cmd", - choices=[ - "help", - "q", - "quit", - "map", - "sectors", - "gainers", - "spacs", - "orders", - "spachero", - "high_short", - "low_float", - "up_earnings", - "simply_wallst", - "uwhales", - "mill", - ], - ) + choices = [ + "help", + "q", + "quit", + "map", + "sectors", + "gainers", + "spacs", + "orders", + "ark_orders", + "spachero", + "high_short", + "low_float", + "up_earnings", + "simply_wallst", + "uwhales", + "mill", + ] + disc_parser.add_argument("cmd", choices=choices) + word_completer = WordCompleter(choices) print_discovery() # Loop forever and ever while True: # Get input command from user - as_input = input(f"{get_flair()} (disc)> ") + as_input = session.prompt( + f"{get_flair()} (disc)> ", + completer=word_completer, + ) # Parse fundamental analysis command of the list of possible commands try: @@ -107,6 +115,9 @@ def disc_menu(): elif ns_known_args.cmd == "orders": fidelity_api.orders(l_args) + elif ns_known_args.cmd == "ark_orders": + ark_api.ark_orders(l_args) + elif ns_known_args.cmd == "simply_wallst": simply_wallst_api.simply_wallst(l_args) diff --git a/gamestonk_terminal/menu.py b/gamestonk_terminal/menu.py new file mode 100644 index 00000000000..4c898945042 --- /dev/null +++ b/gamestonk_terminal/menu.py @@ -0,0 +1,7 @@ +import os + +from prompt_toolkit import PromptSession +from prompt_toolkit.history import FileHistory + +history_file = os.path.join(os.path.expanduser("~"), ".gamestonk_terminal.his") +session = PromptSession(history=FileHistory(history_file)) diff --git a/gamestonk_terminal/sentiment/reddit_api.py b/gamestonk_terminal/sentiment/reddit_api.py index ee29e03d1a3..a29a8863e23 100644 --- a/gamestonk_terminal/sentiment/reddit_api.py +++ b/gamestonk_terminal/sentiment/reddit_api.py @@ -308,7 +308,7 @@ def popular_tickers(l_args): n_top_stocks += 1 except HTTPError as e: if e.response.status_code != 404: - print(f"Unexpected exception from Fiviz: {e}") + print(f"Unexpected exception from Finviz: {e}") except Exception as e: print(e) diff --git a/notebooks/templates/due_diligence.ipynb b/notebooks/templates/due_diligence.ipynb index 460924d725e..139a074132b 100644 --- a/notebooks/templates/due_diligence.ipynb +++ b/notebooks/templates/due_diligence.ipynb @@ -102,7 +102,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.8.8" } }, "nbformat": 4, diff --git a/requirements-full.txt b/requirements-full.txt index 4ee3b88a656..2618656e3f1 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -156,3 +156,4 @@ wrapt==1.12.1; python_version >= "3.6" and python_full_version < "3.0.0" or pyth yarl==1.6.3; python_version >= "3.6" yfinance==0.1.55 zipp==3.4.1; python_version < "3.8" and python_version >= "3.6" +prompt-toolkit==3.0.16; python_version >= "3.6" diff --git a/requirements-old.txt b/requirements-old.txt index 01857adc3ce..a6b15ffebcc 100644 --- a/requirements-old.txt +++ b/requirements-old.txt @@ -133,3 +133,4 @@ wrapt==1.12.1; python_version >= "3.6" and python_full_version < "3.0.0" or pyth yarl==1.6.3; python_version >= "3.6" yfinance==0.1.55 zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.2.0" and python_version >= "3.6" and python_version < "3.8" +prompt-toolkit==3.0.16; python_version >= "3.6" diff --git a/requirements-win.txt b/requirements-win.txt index b77624f1fc1..6049dad70ca 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -133,3 +133,4 @@ wrapt==1.12.1; python_version >= "3.6" and python_full_version < "3.0.0" or pyth yarl==1.6.3; python_version >= "3.6" yfinance==0.1.55 zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.2.0" and python_version >= "3.6" and python_version < "3.8" +prompt-toolkit==3.0.16; python_version >= "3.6" diff --git a/requirements.txt b/requirements.txt index 81e052a160e..c08fd5316b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -90,3 +90,4 @@ websocket-client==0.58.0; python_version >= "3.6" and python_full_version < "3.0 yarl==1.6.3; python_version >= "3.6" yfinance==0.1.55 zipp==3.4.1; python_version < "3.8" and python_version >= "3.6" +prompt-toolkit==3.0.16; python_version >= "3.6" diff --git a/terminal.py b/terminal.py index 8ac585ebe99..a4e13d7a71f 100644 --- a/terminal.py +++ b/terminal.py @@ -16,6 +16,9 @@ from gamestonk_terminal.papermill import papermill_menu as mill from gamestonk_terminal.comparison_analysis import ca_menu as cam from gamestonk_terminal import res_menu as rm from gamestonk_terminal import config_terminal as cfg +from gamestonk_terminal.menu import session +from prompt_toolkit.completion import WordCompleter + # import warnings # warnings.simplefilter("always") @@ -43,27 +46,25 @@ def main(): # Add list of arguments that the main parser accepts menu_parser = argparse.ArgumentParser(prog="gamestonk_terminal", add_help=False) - menu_parser.add_argument( - "opt", - choices=[ - "help", - "quit", - "q", - "clear", - "load", - "view", - "export", - "disc", - "mill", - "sen", - "res", - "ca", - "fa", - "ta", - "dd", - "pred", - ], - ) + choices = [ + "help", + "quit", + "q", + "clear", + "load", + "view", + "export", + "disc", + "mill", + "sen", + "res", + "fa", + "ta", + "dd", + "pred", + ] + menu_parser.add_argument("opt", choices=choices) + word_completer = WordCompleter(choices) # Print first welcome message and help print("\nWelcome to Gamestonk Terminal 🚀\n") @@ -77,7 +78,10 @@ def main(): should_print_help = False # Get input command from user - as_input = input(f"{get_flair()}> ") + as_input = session.prompt( + f"{get_flair()}> ", + completer=word_completer, + ) # Is command empty if not as_input: diff --git a/tests/test_discovery_ark_api.py b/tests/test_discovery_ark_api.py new file mode 100644 index 00000000000..c9589f13dc2 --- /dev/null +++ b/tests/test_discovery_ark_api.py @@ -0,0 +1,10 @@ +import unittest + +from gamestonk_terminal.discovery.ark_api import ( + ark_orders, +) + + +class TestDiscoveryArkApi(unittest.TestCase): + def test_ark_orders(self): + ark_orders(["-n", "2"])