Files
OpenBB/terminal.py
2021-07-25 21:29:53 -04:00

566 lines
17 KiB
Python

#!/usr/bin/env python
"""Main Terminal Module"""
__docformat__ = "numpy"
import argparse
import os
import sys
from datetime import datetime
from typing import List
import requests
import pandas as pd
import yfinance as yf
from alpha_vantage.timeseries import TimeSeries
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal.helper_funcs import (
b_is_stock_market_open,
get_flair,
)
from gamestonk_terminal.main_helper import (
clear,
load,
view,
candle,
print_goodbye,
quote,
update_terminal,
about_us,
bootup,
reset,
check_api_keys,
)
from gamestonk_terminal.menu import session
from gamestonk_terminal import config_terminal as cfg
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.discovery import disc_controller
from gamestonk_terminal.due_diligence import dd_controller
from gamestonk_terminal.fundamental_analysis import fa_controller
from gamestonk_terminal.papermill import papermill_controller as mill
from gamestonk_terminal.behavioural_analysis import ba_controller
from gamestonk_terminal.technical_analysis import ta_controller
from gamestonk_terminal.comparison_analysis import ca_controller
from gamestonk_terminal.exploratory_data_analysis import eda_controller
from gamestonk_terminal.options import op_controller
from gamestonk_terminal.economy import econ_controller
from gamestonk_terminal.residuals_analysis import ra_controller
from gamestonk_terminal.portfolio_analysis import pa_controller
from gamestonk_terminal.brokers import bro_controller
from gamestonk_terminal.cryptocurrency import crypto_controller
from gamestonk_terminal.screener import screener_controller
from gamestonk_terminal.portfolio_optimization import po_controller
from gamestonk_terminal.forex import fx_controller
from gamestonk_terminal.backtesting import bt_controller
from gamestonk_terminal.resource_collection import rc_controller
from gamestonk_terminal.research import res_controller
from gamestonk_terminal.government import gov_controller
from gamestonk_terminal.etf import etf_controller
from gamestonk_terminal.insider import insider_controller
# pylint: disable=too-many-public-methods
class TerminalController:
"""Terminal Controller class"""
# To hold suffix for Yahoo Finance
suffix = ""
# Command choices
CHOICES_TICKER_DEPENDENT = [
"ba",
"res",
"fa",
"ta",
"bt",
"dd",
"eda",
"pred",
"ca",
"ra",
]
CHOICES = [
"cls",
"?",
"help",
"quit",
"q",
"reset",
"update",
"clear",
"load",
"quote",
"candle",
"view",
"disc",
"scr",
"mill",
"econ",
"pa",
"crypto",
"po",
"ins",
"fx",
"rc",
"op",
"gov",
"etf",
"about",
"bro",
"ins",
"keys",
]
CHOICES += CHOICES_TICKER_DEPENDENT
def __init__(
self,
stock: pd.DataFrame,
ticker: str,
start: datetime,
interval: str,
):
"""Constructor"""
self.stock = stock
self.ticker = ticker
self.start = start
self.interval = interval
self.update_succcess = False
self.t_parser = argparse.ArgumentParser(add_help=False, prog="terminal")
self.t_parser.add_argument(
"cmd",
choices=self.CHOICES,
)
self.completer = NestedCompleter.from_nested_dict(
{c: None for c in self.CHOICES}
)
def print_help(self):
"""Print help"""
help_text = """
What do you want to do?
cls clear screen
?/help show this menu again
update update terminal from remote
keys check for defined api keys
reset reset terminal and reload configs
about about us
q(uit) to abandon the program
Contexts:
> mill papermill menu, \t\t menu to generate notebook reports
> econ economic data, \t\t\t e.g.: events, FRED data, GDP, VIXCLS
> pa portfolio analysis, \t\t analyses your custom portfolio
> bro brokers holdings, \t\t supports: robinhood, alpaca, ally
> crypto cryptocurrencies, \t\t from: coingecko, coinmarketcap, binance
> po portfolio optimization, \t optimal portfolio weights from pyportfolioopt
> gov government menu, \t\t house trading, contracts, corporate lobbying
> etf etf menu, \t\t\t from: StockAnalysis.com
> fx forex menu, \t\t\t forex support through Oanda
> rc resource collection, \t\t e.g. hf letters, arXiv, EDGAR, FINRA
> op options info, \t e.g.: volume, open interest, chains, volatility
> ins insider trading, \t e.g.: latest penny stock buys, insider sales
"""
s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"]
if self.ticker and self.start:
help_text += f"\n{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})\n"
elif self.ticker:
help_text += f"\n{s_intraday} Stock: {self.ticker}\n"
else:
help_text += "\nStock: ?\n"
help_text += f"Market {('CLOSED', 'OPEN')[b_is_stock_market_open()]}.\n"
help_text += """
clear clear a specific stock ticker from analysis
load load a specific stock ticker for analysis
"""
if self.ticker:
help_text += """quote view the current price for a specific stock ticker
candle view a candle chart for a specific stock ticker
view view and load a specific stock ticker for technical analysis
> dd in-depth due-diligence, \t e.g.: news, analyst, shorts, insider, sec
> ba behavioural analysis, \t from: reddit, stocktwits, twitter, google
> ta technical analysis, \t e.g.: ema, macd, rsi, adx, bbands, obv
> fa fundamental analysis, \t e.g.: income, balance, cash, earnings
> res research web page, \t e.g.: macroaxis, yahoo finance, fool
> ca comparison analysis, \t e.g.: historical, correlation, financials
> eda exploratory data analysis,\t e.g.: decompose, cusum, residuals analysis
> ra residuals analysis, \t e.g.: model fit, qqplot, hypothesis test
> bt strategy backtester, \t e.g.: simple ema, ema cross, rsi strategies
> pred prediction techniques, \t e.g.: regression, arima, rnn, lstm"""
help_text += """\n> disc discover trending stocks, \t e.g. map, sectors, high short interest
> scr screener stocks, \t\t e.g. overview/performance, using preset filters
"""
print(help_text)
def switch(self, an_input: str):
"""Process and dispatch input
Returns
-------
True, False or None
False - quit the menu
True - quit the program
None - continue in the menu
"""
# Empty command
if not an_input:
print("")
return None
if not self.ticker and an_input in self.CHOICES_TICKER_DEPENDENT:
print("No ticker selected. Use 'load <ticker>'.\n")
return None
(known_args, other_args) = self.t_parser.parse_known_args(an_input.split())
# Help menu again
if known_args.cmd == "?":
self.print_help()
return None
# Clear screen
if known_args.cmd == "cls":
os.system("cls||clear")
return None
return getattr(
self, "call_" + known_args.cmd, lambda: "Command not recognized!"
)(other_args)
def call_help(self, _):
"""Process Help command"""
self.print_help()
def call_quit(self, _):
"""Process Quit command - quit the program"""
return True
def call_q(self, _):
"""Process Quit command - quit the program"""
return True
def call_reset(self, _):
"""Process reset command"""
return True
def call_clear(self, other_args: List[str]):
"""Process clear command"""
self.ticker, self.start, self.interval, self.stock = clear(
other_args, self.ticker, self.start, self.interval, self.stock
)
def call_load(self, other_args: List[str]):
"""Process load command"""
self.ticker, self.start, self.interval, self.stock = load(
other_args, self.ticker, self.start, self.interval, self.stock
)
if "." in self.ticker:
self.ticker, self.suffix = self.ticker.split(".")
else:
self.suffix = ""
def call_quote(self, other_args: List[str]):
"""Process quote command"""
quote(
other_args, self.ticker + "." + self.suffix if self.suffix else self.ticker
)
def call_candle(self, other_args: List[str]):
"""Process candle command"""
candle(
self.ticker + "." + self.suffix if self.suffix else self.ticker,
other_args,
)
def call_view(self, other_args: List[str]):
"""Process view command"""
view(other_args, self.ticker, self.interval, self.stock)
def call_disc(self, _):
"""Process disc command"""
return disc_controller.menu()
def call_mill(self, _):
"""Process mill command"""
return mill.papermill_menu()
def call_fx(self, _):
"""Process fx command"""
return fx_controller.menu()
def call_econ(self, _):
"""Process econ command"""
return econ_controller.menu()
def call_pa(self, _):
"""Process pa command"""
return pa_controller.menu()
def call_bro(self, _):
"""Process bro command"""
return bro_controller.menu()
def call_crypto(self, _):
"""Process crypto command"""
return crypto_controller.menu()
def call_scr(self, _):
"""Process scr command"""
return screener_controller.menu()
def call_rc(self, _):
"""Process rc command"""
return rc_controller.menu()
def call_etf(self, _):
"""Process etf command"""
return etf_controller.menu()
def call_ins(self, _):
"""Process ins command"""
return insider_controller.menu()
def call_gov(self, _):
"""Process gov command"""
return gov_controller.menu(self.ticker)
def call_po(self, _):
"""Process po command"""
return po_controller.menu([self.ticker])
def call_update(self, _):
"""Process update command"""
self.update_succcess = not update_terminal()
return True
def call_keys(self, _):
"""Process keys command"""
check_api_keys()
def call_about(self, _):
"""Process about command"""
about_us()
def call_ba(self, _):
"""Process ba command"""
return ba_controller.menu(
self.ticker,
self.start,
)
def call_res(self, _):
"""Process res command"""
return res_controller.menu(
self.ticker,
self.start,
self.interval,
)
def call_ca(self, _):
"""Process ca command"""
return ca_controller.menu(self.ticker, self.start, self.interval, self.stock)
def call_fa(self, _):
"""Process fa command"""
return fa_controller.menu(
self.ticker,
self.start,
self.interval,
)
def call_ta(self, _):
"""Process ta command"""
return ta_controller.menu(
self.ticker,
self.start,
self.interval,
self.stock,
)
def call_dd(self, _):
"""Process dd command"""
return dd_controller.menu(
self.ticker,
self.start,
self.interval,
self.stock,
)
def call_ra(self, _):
"""Process ra command"""
return ra_controller.menu(
self.ticker,
self.start,
self.interval,
self.stock,
)
def call_bt(self, _):
"""Process bt command"""
return bt_controller.menu(
self.ticker,
self.start,
)
def call_eda(self, _):
"""Process eda command"""
if self.interval == "1440min":
return eda_controller.menu(
self.ticker,
self.start,
self.interval,
self.stock,
)
df_stock = yf.download(self.ticker, start=self.start, progress=False)
df_stock.index.name = "date"
s_interval = "1440min"
return eda_controller.menu(
self.ticker,
self.start,
s_interval,
df_stock,
)
def call_op(self, _):
"""Process op command"""
return op_controller.menu(self.ticker)
def call_pred(self, _):
"""Process pred command"""
if not gtff.ENABLE_PREDICT:
print(
"Predict is disabled. Check ENABLE_PREDICT flag on feature_flags.py",
"\n",
)
return None
try:
# pylint: disable=import-outside-toplevel
from gamestonk_terminal.prediction_techniques import pred_controller
except ModuleNotFoundError as e:
print("One of the optional packages seems to be missing: ", e, "\n")
return None
except Exception as e:
print(e, "\n")
return None
if self.interval == "1440min":
return pred_controller.menu(
self.ticker,
self.start,
self.interval,
self.stock,
)
# If stock data is intradaily, we need to get data again as prediction
# techniques work on daily adjusted data. By default we load data from
# Alpha Vantage because the historical data loaded gives a larger
# dataset than the one provided by quandl
try:
ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas")
# pylint: disable=unbalanced-tuple-unpacking
df_stock_pred, _ = ts.get_daily_adjusted(
symbol=self.ticker, outputsize="full"
)
# pylint: disable=no-member
df_stock_pred = df_stock_pred.sort_index(ascending=True)
df_stock_pred = df_stock_pred[self.start :]
return pred_controller.menu(
self.ticker,
self.start,
"1440min",
df_stock_pred,
)
except Exception as e:
print(e)
print("Either the ticker or the API_KEY are invalids. Try again!")
return None
def terminal():
"""Terminal Menu"""
# Check if the user has internet connection
if requests.get("https://www.google.com/").status_code != 200:
print("Internet connection is needed to use Gamestonk Terminal.", "\n")
return
bootup()
ticker = "" # "GME"
start = "" # "2021-01-01"
stock = pd.DataFrame() # yf.download(ticker, start, progress=False)
interval = "1440min"
t_controller = TerminalController(stock, ticker, start, interval)
t_controller.print_help()
parsed_stdin = False
while True:
if gtff.ENABLE_QUICK_EXIT:
print("Quick exit enabled")
break
# Get input command from stdin or user
if not parsed_stdin and len(sys.argv) > 1:
an_input = " ".join(sys.argv[1:])
print(f"{get_flair()}> {an_input}")
parsed_stdin = True
elif session and gtff.USE_PROMPT_TOOLKIT:
an_input = session.prompt(
f"{get_flair()}> ", completer=t_controller.completer
)
else:
an_input = input(f"{get_flair()}> ")
# Is command empty
if not an_input:
print("")
continue
# Process list of commands selected by user
try:
process_input = t_controller.switch(an_input)
# None - Keep loop
# True - Quit or Reset based on flag
# False - Keep loop and show help menu
if process_input is not None:
# Quit terminal
if process_input:
break
t_controller.print_help()
except SystemExit:
print("The command selected doesn't exist\n")
continue
if not gtff.ENABLE_QUICK_EXIT:
# Check if the user wants to reset application
if an_input == "reset" or t_controller.update_succcess:
ret_code = reset()
if ret_code != 0:
print_goodbye()
else:
print_goodbye()
if __name__ == "__main__":
terminal()