diff --git a/README.md b/README.md
index 5d6ddd3a4e6..582fca4a5f3 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@
Table of Contents
+
## select
diff --git a/gamestonk_terminal/comparison_analysis/ca_controller.py b/gamestonk_terminal/comparison_analysis/ca_controller.py
index 3772807b71a..74fda2f90c9 100644
--- a/gamestonk_terminal/comparison_analysis/ca_controller.py
+++ b/gamestonk_terminal/comparison_analysis/ca_controller.py
@@ -136,25 +136,25 @@ class ComparisonAnalysisController:
other_args : List[str]
argparse other args
"""
-
parser = argparse.ArgumentParser(
add_help=False,
prog="get",
description="""Get similar companies to compare with.""",
)
parser.add_argument(
- "-p",
- "--polygon",
- action="store_true",
- default=False,
- dest="b_polygon",
- help="Polygon data source flag.",
+ "-s",
+ "--source",
+ action="store",
+ default="finviz",
+ dest="source",
+ choices=["polygon", "finnhub", "finviz"],
+ help="source that provides similar companies",
)
- # If polygon source not selected, the user may want to get
+ # If source is finviz the user may want to get
# similar companies based on Industry and Sector only, and not
# on the fact that they are based on the same country
- if "-p" not in other_args and "--polygon" not in other_args:
+ if "finviz" in other_args or not other_args:
parser.add_argument(
"--nocountry",
action="store_true",
@@ -164,11 +164,15 @@ class ComparisonAnalysisController:
)
try:
+ if other_args:
+ if "-" not in other_args[0]:
+ other_args.insert(0, "-s")
+
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
- if ns_parser.b_polygon:
+ if ns_parser.source == "polygon":
result = requests.get(
f"https://api.polygon.io/v1/meta/symbols/{self.ticker.upper()}/company?&apiKey={cfg.API_POLYGON_KEY}"
)
@@ -179,6 +183,20 @@ class ComparisonAnalysisController:
else:
print(result.json()["error"])
+ elif ns_parser.source == "finnhub":
+ result = requests.get(
+ f"https://finnhub.io/api/v1/stock/peers?symbol={self.ticker}&token={cfg.API_FINNHUB_KEY}"
+ )
+
+ if result.status_code == 200:
+ d_peers = result.json()
+
+ if d_peers:
+ self.similar = d_peers
+ self.user = "Finnhub"
+ else:
+ print("Similar companies not found.")
+
else:
if ns_parser.b_no_country:
compare_list = ["Sector", "Industry"]
@@ -192,17 +210,18 @@ class ComparisonAnalysisController:
)
self.user = "Finviz"
- if self.similar:
- print(f"\n[{self.user}] Similar Companies: {', '.join(self.similar)}")
+ if self.ticker.upper() in self.similar:
+ self.similar.remove(self.ticker.upper())
if len(self.similar) > 10:
- print(
- "\nThe limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed.",
- "\nThe selected list will be:",
- )
random.shuffle(self.similar)
self.similar = sorted(self.similar[:10])
- print(", ".join(self.similar))
+ print(
+ "The limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed.\n",
+ )
+
+ if self.similar:
+ print(f"[{self.user}] Similar Companies: {', '.join(self.similar)}")
except Exception as e:
print(e)
diff --git a/gamestonk_terminal/government/README.md b/gamestonk_terminal/government/README.md
new file mode 100644
index 00000000000..4792d404426
--- /dev/null
+++ b/gamestonk_terminal/government/README.md
@@ -0,0 +1,64 @@
+# GOVERNMENT
+
+This menu aims to get some insights regarding government data, and the usage of the following commands along with an example will be exploited below.
+This data has been provided by [quiverquant](https://www.quiverquant.com).
+
+* [last_congress](#last_congress)
+ * last congress trading
+* [buy_congress](#buy_congress)
+ * top buy congress tickers
+* [sell_congress](#sell_congress)
+ * top sell congress tickers
+
+#### WITH TICKER PROVIDED
+
+* [congress](#congress)
+ * congress trades on the ticker
+
+
+## last_congress
+```text
+usage: last_congress [-p PAST_TRANSACTIONS_DAYS]
+```
+Last congress trading. [Source: www.quiverquant.com]
+
+* -p : Past transaction days. Default: 5.
+* -r : Congress representative.
+
+
+
+
+## buy_congress
+```text
+usage: buy_congress [-p PAST_TRANSACTIONS_MONTHS] [-t TOP_NUM]
+```
+Top buy congress trading. [Source: www.quiverquant.com]
+
+* -p : Past transaction months. Default: 6.
+* -t : Number of top tickers. Default: 10.
+
+
+
+
+## sell_congress
+```text
+usage: sell_congress [-p PAST_TRANSACTIONS_MONTHS] [-t TOP_NUM]
+```
+Top sell congress trading. [Source: www.quiverquant.com]
+
+* -p : Past transaction months. Default: 6.
+* -t : Number of top tickers. Default: 10.
+
+
+
+
+## congress
+```text
+usage: congress [-p PAST_TRANSACTIONS_MONTHS]
+```
+Congress trading. [Source: www.quiverquant.com]
+
+* -p : Past transaction months. Default: 6.
+
+
+
diff --git a/gamestonk_terminal/government/gov_controller.py b/gamestonk_terminal/government/gov_controller.py
new file mode 100644
index 00000000000..5084cefb981
--- /dev/null
+++ b/gamestonk_terminal/government/gov_controller.py
@@ -0,0 +1,134 @@
+"""Government Controller Module"""
+__docformat__ = "numpy"
+
+import argparse
+from typing import List
+from matplotlib import pyplot as plt
+from gamestonk_terminal import feature_flags as gtff
+from gamestonk_terminal.helper_funcs import get_flair
+from gamestonk_terminal.menu import session
+from prompt_toolkit.completion import NestedCompleter
+from gamestonk_terminal.government import quiverquant_view
+
+
+class GovController:
+ """Gov Controller class"""
+
+ # Command choices
+ CHOICES = [
+ "help",
+ "q",
+ "quit",
+ "last_congress",
+ "buy_congress",
+ "sell_congress",
+ "congress",
+ ]
+
+ def __init__(
+ self,
+ ticker: str,
+ ):
+ """Constructor"""
+ self.ticker = ticker
+ self.gov_parser = argparse.ArgumentParser(add_help=False, prog="gov")
+ self.gov_parser.add_argument(
+ "cmd",
+ choices=self.CHOICES,
+ )
+
+ @staticmethod
+ def print_help(self):
+ """Print help"""
+
+ print("\nExploratory Data Analysis:")
+ print(" help show this comparison analysis menu again")
+ print(" q quit this menu, and shows back to main menu")
+ print(" quit quit to abandon program")
+ print("")
+ print("Explore:")
+ print(" last_congress last congress trading")
+ print(" buy_congress top buy congress tickers")
+ print(" sell_congress top sell congress tickers")
+ print("")
+ if self.ticker:
+ print(f"Ticker: {self.ticker}")
+ print(" congress congress trades on the ticker")
+ print("")
+ return
+
+ 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
+ """
+ (known_args, other_args) = self.gov_parser.parse_known_args(an_input.split())
+
+ return getattr(
+ self, "call_" + known_args.cmd, lambda: "Command not recognized!"
+ )(other_args)
+
+ def call_help(self, _):
+ """Process Help command"""
+ self.print_help(self)
+
+ def call_q(self, _):
+ """Process Q command - quit the menu"""
+ return False
+
+ def call_quit(self, _):
+ """Process Quit command - quit the program"""
+ return True
+
+ def call_last_congress(self, other_args: List[str]):
+ """Process last_congress command"""
+ quiverquant_view.last_congress(other_args)
+
+ def call_buy_congress(self, other_args: List[str]):
+ """Process buy_congress command"""
+ quiverquant_view.buy_congress(other_args)
+
+ def call_sell_congress(self, other_args: List[str]):
+ """Process sell_congress command"""
+ quiverquant_view.sell_congress(other_args)
+
+ def call_congress(self, other_args: List[str]):
+ """Process congress command"""
+ quiverquant_view.congress(other_args, self.ticker)
+
+
+def menu(ticker: str):
+ """Government Menu"""
+
+ gov_controller = GovController(ticker)
+ gov_controller.call_help(None)
+
+ while True:
+ # Get input command from user
+ if session and gtff.USE_PROMPT_TOOLKIT:
+ completer = NestedCompleter.from_nested_dict(
+ {c: None for c in gov_controller.CHOICES}
+ )
+ an_input = session.prompt(
+ f"{get_flair()} (gov)> ",
+ completer=completer,
+ )
+ else:
+ an_input = input(f"{get_flair()} (gov)> ")
+
+ try:
+ plt.close("all")
+
+ process_input = gov_controller.switch(an_input)
+
+ if process_input is not None:
+ return process_input
+
+ except SystemExit:
+ print("The command selected doesn't exist\n")
+ continue
diff --git a/gamestonk_terminal/government/quiverquant_model.py b/gamestonk_terminal/government/quiverquant_model.py
new file mode 100644
index 00000000000..3bf5527108a
--- /dev/null
+++ b/gamestonk_terminal/government/quiverquant_model.py
@@ -0,0 +1,36 @@
+import requests
+import pandas as pd
+
+# Provided by Quiverquant guys to GST users
+API_QUIVERQUANT_KEY = "5cd2a65e96d0486efbe926a7cdbc1e8d8ab6c7b3"
+
+
+def get_congress_trading(ticker: str = "") -> pd.DataFrame:
+ """Returns the most recent transactions by members of U.S. Congress
+
+ Parameters
+ ----------
+ ticker : str
+ Ticker to get congress trading data from
+
+ Returns
+ -------
+ pd.DataFrame
+ Most recent transactions by members of U.S. Congress
+ """
+
+ if ticker:
+ url = f"https://api.quiverquant.com/beta/historical/congresstrading/{ticker}"
+ else:
+ url = "https://api.quiverquant.com/beta/live/congresstrading"
+
+ headers = {
+ "accept": "application/json",
+ "X-CSRFToken": "TyTJwjuEC7VV7mOqZ622haRaaUr0x0Ng4nrwSRFKQs7vdoBcJlK9qjAS69ghzhFu",
+ "Authorization": f"Token {API_QUIVERQUANT_KEY}",
+ }
+ response = requests.get(url, headers=headers)
+ if response.status_code == 200:
+ return pd.DataFrame(response.json())
+
+ return pd.DataFrame()
diff --git a/gamestonk_terminal/government/quiverquant_view.py b/gamestonk_terminal/government/quiverquant_view.py
new file mode 100644
index 00000000000..88f314fa72e
--- /dev/null
+++ b/gamestonk_terminal/government/quiverquant_view.py
@@ -0,0 +1,429 @@
+import argparse
+from typing import List
+import pandas as pd
+from datetime import datetime, timedelta
+from gamestonk_terminal.government import quiverquant_model
+from matplotlib import pyplot as plt
+import matplotlib.dates as mdates
+from gamestonk_terminal.helper_funcs import (
+ parse_known_args_and_warn,
+ check_positive,
+ plot_autoscale,
+)
+from gamestonk_terminal.config_plot import PLOT_DPI
+from gamestonk_terminal import feature_flags as gtff
+
+
+def last_congress(other_args: List[str]):
+ """Last congress trading
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ """
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="last_congress",
+ description="""
+ Last congress trading. [Source: www.quiverquant.com]
+ """,
+ )
+ parser.add_argument(
+ "-p",
+ "--past_transactions_days",
+ action="store",
+ dest="past_transactions_days",
+ type=check_positive,
+ default=5,
+ help="Past transaction days",
+ )
+ parser.add_argument(
+ "-r",
+ "--representative",
+ action="store",
+ dest="representative",
+ type=str,
+ default="",
+ help="Congress representative",
+ )
+
+ try:
+ if other_args:
+ if "-" not in other_args[0]:
+ other_args.insert(0, "-p")
+
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ df_congress = quiverquant_model.get_congress_trading()
+
+ if df_congress.empty:
+ print("No congress trading data found\n")
+ return
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=False)
+
+ df_congress = df_congress[
+ df_congress["TransactionDate"].isin(
+ df_congress["TransactionDate"].unique()[
+ : ns_parser.past_transactions_days
+ ]
+ )
+ ]
+
+ df_congress = df_congress[
+ [
+ "TransactionDate",
+ "Ticker",
+ "Representative",
+ "Transaction",
+ "House",
+ "Range",
+ "ReportDate",
+ ]
+ ].rename(
+ columns={"TransactionDate": "Transaction Date", "ReportDate": "Report Date"}
+ )
+
+ if ns_parser.representative:
+ df_congress_rep = df_congress[
+ df_congress["Representative"].str.split().str[0]
+ == ns_parser.representative
+ ]
+
+ if df_congress_rep.empty:
+ print(
+ f"No representative {ns_parser.representative} found in the past {ns_parser.past_transactions_days}"
+ f" days. The following are available: "
+ f"{', '.join(df_congress['Representative'].str.split().str[0].unique())}"
+ )
+ else:
+ print(df_congress_rep.to_string(index=False))
+ else:
+ print(df_congress.to_string(index=False))
+ print("")
+
+ except Exception as e:
+ print(e, "\n")
+
+
+def buy_congress(other_args: List[str]):
+ """Top buy congress trading
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ """
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="buy_congress",
+ description="""
+ Top buy congress trading. [Source: www.quiverquant.com]
+ """,
+ )
+ parser.add_argument(
+ "-p",
+ "--past_transactions_months",
+ action="store",
+ dest="past_transactions_months",
+ type=check_positive,
+ default=6,
+ help="Past transaction months",
+ )
+ parser.add_argument(
+ "-t",
+ "--top",
+ action="store",
+ dest="top_num",
+ type=check_positive,
+ default=10,
+ help="Number of top tickers",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ df_congress = quiverquant_model.get_congress_trading()
+
+ if df_congress.empty:
+ print("No congress trading data found\n")
+ return
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=False)
+
+ start_date = datetime.now() - timedelta(
+ days=ns_parser.past_transactions_months * 30
+ )
+
+ df_congress["TransactionDate"] = pd.to_datetime(df_congress["TransactionDate"])
+
+ df_congress = df_congress[df_congress["TransactionDate"] > start_date].dropna()
+
+ df_congress["min"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
+ )
+ df_congress["max"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
+ if "-" in x
+ else x.strip("$").replace(",", "")
+ )
+
+ df_congress["lower"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["min"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["max"]),
+ axis=1,
+ )
+ df_congress["upper"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["max"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["min"]),
+ axis=1,
+ )
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=True)
+
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ df_congress.groupby("Ticker")["upper"].sum().sort_values(ascending=False).head(
+ n=ns_parser.top_num
+ ).plot(kind="bar", rot=0)
+ plt.ylabel("Amount [$]")
+ plt.title(
+ f"Top {ns_parser.top_num} most bought stocks since last {ns_parser.past_transactions_months} "
+ "months (upper bound)"
+ )
+ plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
+ if gtff.USE_ION:
+ plt.ion()
+
+ plt.show()
+ print("")
+
+ except Exception as e:
+ print(e, "\n")
+
+
+def sell_congress(other_args: List[str]):
+ """Top sell congress trading
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ """
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="sell_congress",
+ description="""
+ Top sell congress trading. [Source: www.quiverquant.com]
+ """,
+ )
+ parser.add_argument(
+ "-p",
+ "--past_transactions_months",
+ action="store",
+ dest="past_transactions_months",
+ type=check_positive,
+ default=6,
+ help="Past transaction months",
+ )
+ parser.add_argument(
+ "-t",
+ "--top",
+ action="store",
+ dest="top_num",
+ type=check_positive,
+ default=10,
+ help="Number of top tickers",
+ )
+
+ try:
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ df_congress = quiverquant_model.get_congress_trading()
+
+ if df_congress.empty:
+ print("No congress trading data found\n")
+ return
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=False)
+
+ start_date = datetime.now() - timedelta(
+ days=ns_parser.past_transactions_months * 30
+ )
+
+ df_congress["TransactionDate"] = pd.to_datetime(df_congress["TransactionDate"])
+
+ df_congress = df_congress[df_congress["TransactionDate"] > start_date].dropna()
+
+ df_congress["min"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
+ )
+ df_congress["max"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
+ if "-" in x
+ else x.strip("$").replace(",", "")
+ )
+
+ df_congress["lower"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["min"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["max"]),
+ axis=1,
+ )
+ df_congress["upper"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: float(x["max"])
+ if x["Transaction"] == "Purchase"
+ else -float(x["min"]),
+ axis=1,
+ )
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=True)
+
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ df_congress.groupby("Ticker")["lower"].sum().sort_values().abs().head(
+ n=ns_parser.top_num
+ ).plot(kind="bar", rot=0)
+ plt.ylabel("Amount [$]")
+ plt.title(
+ f"Top {ns_parser.top_num} most sold stocks since last {ns_parser.past_transactions_months} months"
+ " (upper bound)"
+ )
+ plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
+ if gtff.USE_ION:
+ plt.ion()
+
+ plt.show()
+
+ print("")
+
+ except Exception as e:
+ print(e, "\n")
+
+
+def plot_congress(congress: pd.DataFrame, ticker: str):
+ """Plot congress trading
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ ticker: str
+ Ticker to plot congress trading
+ """
+ plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
+
+ plt.gca().fill_between(
+ congress["TransactionDate"].unique(),
+ congress.groupby("TransactionDate")["lower"].sum().values / 1000,
+ congress.groupby("TransactionDate")["upper"].sum().values / 1000,
+ )
+
+ plt.xlim(
+ [congress["TransactionDate"].values[0], congress["TransactionDate"].values[-1]]
+ )
+ plt.grid()
+ plt.title(f"Congress trading on {ticker}")
+ plt.xlabel("Date")
+ plt.ylabel("Amount [1k $]")
+ plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
+ plt.gcf().autofmt_xdate()
+
+ if gtff.USE_ION:
+ plt.ion()
+
+ plt.show()
+
+
+def congress(other_args: List[str], ticker: str):
+ """Congress trading
+
+ Parameters
+ ----------
+ other_args : List[str]
+ Command line arguments to be processed with argparse
+ ticker: str
+ Ticker to get congress trading data from
+ """
+ parser = argparse.ArgumentParser(
+ add_help=False,
+ prog="congress",
+ description="""
+ Congress trading. [Source: www.quiverquant.com]
+ """,
+ )
+ parser.add_argument(
+ "-p",
+ "--past_transactions_months",
+ action="store",
+ dest="past_transactions_months",
+ type=check_positive,
+ default=6,
+ help="Past transaction months",
+ )
+
+ try:
+
+ if other_args:
+ if "-" not in other_args[0]:
+ other_args.insert(0, "-p")
+
+ ns_parser = parse_known_args_and_warn(parser, other_args)
+ if not ns_parser:
+ return
+
+ df_congress = quiverquant_model.get_congress_trading(ticker)
+
+ if df_congress.empty:
+ print("No congress trading data found\n")
+ return
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=False)
+
+ start_date = datetime.now() - timedelta(
+ days=ns_parser.past_transactions_months * 30
+ )
+
+ df_congress["TransactionDate"] = pd.to_datetime(df_congress["TransactionDate"])
+
+ df_congress = df_congress[df_congress["TransactionDate"] > start_date]
+
+ df_congress["min"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[0].strip("$").replace(",", "").strip()
+ )
+ df_congress["max"] = df_congress["Range"].apply(
+ lambda x: x.split("-")[1].replace(",", "").strip().strip("$")
+ if "-" in x
+ else x.strip("$").replace(",", "")
+ )
+
+ df_congress["lower"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: int(x["min"])
+ if x["Transaction"] == "Purchase"
+ else -int(x["max"]),
+ axis=1,
+ )
+ df_congress["upper"] = df_congress[["min", "max", "Transaction"]].apply(
+ lambda x: int(x["max"])
+ if x["Transaction"] == "Purchase"
+ else -int(x["min"]),
+ axis=1,
+ )
+
+ df_congress = df_congress.sort_values("TransactionDate", ascending=True)
+
+ plot_congress(df_congress, ticker)
+
+ print("")
+
+ except Exception as e:
+ print(e, "\n")
diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py
index 8cd282145f6..51fcf179b0b 100644
--- a/gamestonk_terminal/main_helper.py
+++ b/gamestonk_terminal/main_helper.py
@@ -65,6 +65,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
print(
" > po portfolio optimization, \t optimal portfolio weights from pyportfolioopt"
)
+ print(" > gov government menu, \t\t congress, senate, house trading")
print(" > fx forex menu, \t\t\t forex support through Oanda")
print(" > rc resource collection, \t\t e.g. hf letters")
@@ -100,7 +101,7 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
" > ra residuals analysis, \t e.g.: model fit, qqplot, hypothesis test"
)
print(
- " > op options info , \t e.g.: volume and open interest"
+ " > op options info, \t e.g.: volume and open interest"
)
print("")
diff --git a/gamestonk_terminal/portfolio_optimization/README.md b/gamestonk_terminal/portfolio_optimization/README.md
index 3e9e44d4603..9aa9f6551a5 100644
--- a/gamestonk_terminal/portfolio_optimization/README.md
+++ b/gamestonk_terminal/portfolio_optimization/README.md
@@ -1,4 +1,4 @@
-# Portfolio Optimization
+# PORTFOLIO OPTIMIZATION
This menu aims to optimize a portfolio of pre-loaded stocks, and the usage of the following commands along with an example will be exploited below.
@@ -6,6 +6,9 @@ This menu aims to optimize a portfolio of pre-loaded stocks, and the usage of th
* add ticker to optimize
* [select](#select)
* overwrite current tickers with new tickers
+
+[Optimization](#Optimization)
+
* [equal](#equal)
* equally weighted
* [mktcap](#mktcap)
@@ -30,6 +33,10 @@ This menu aims to optimize a portfolio of pre-loaded stocks, and the usage of th
* [ef](#ef)
* show the efficient frontier
+
+* [> ca](comparison_analysis/README.md)
+ * **comparison analysis for selected tickers**
+
### add
@@ -53,6 +60,8 @@ Add/Select tickers for portfolio to be optimized.
* -t : Tickers to be used in the portfolio to optimize
+## Optimization
+
### equal
```text
diff --git a/gamestonk_terminal/portfolio_optimization/po_controller.py b/gamestonk_terminal/portfolio_optimization/po_controller.py
index 5940d0b969e..87e6513d98a 100644
--- a/gamestonk_terminal/portfolio_optimization/po_controller.py
+++ b/gamestonk_terminal/portfolio_optimization/po_controller.py
@@ -79,7 +79,7 @@ class PortfolioOptimization:
print("")
print(" ef show the efficient frontier")
print("")
- if tickers:
+ if len(tickers) > 1:
print(" > ca comparison analysis for selected tickers")
print("")
diff --git a/gamestonk_terminal/resource_collection/README.md b/gamestonk_terminal/resource_collection/README.md
index 80a2ba94580..f065660eee1 100644
--- a/gamestonk_terminal/resource_collection/README.md
+++ b/gamestonk_terminal/resource_collection/README.md
@@ -4,6 +4,14 @@ This menu aims to store a collection of useful resources that can't be scraped,
* [hfletters](#hfletters)
* hedge fund letters or reports
+* [arxiv](#arxiv)
+ * open-access archive for academic articles
+* [finra](#finra)
+ * self-regulatory organization
+* [edgar](#edgar)
+ * online public database from SEC
+* [fred](#fred)
+ * economic research data
* [learn](#learn)
* trading analysis, tips and resources
@@ -18,6 +26,42 @@ usage: hfletters
+## arxiv
+
+```text
+usage: arxiv
+```
+
+
+
+
+## finra
+
+```text
+usage: finra
+```
+
+
+
+
+## edgar
+
+```text
+usage: edgar
+```
+
+
+
+
+## fred
+
+```text
+usage: fred
+```
+
+
+
+
## learn
```text
diff --git a/gamestonk_terminal/resource_collection/rc_controller.py b/gamestonk_terminal/resource_collection/rc_controller.py
index 4ba30e38107..81366356460 100644
--- a/gamestonk_terminal/resource_collection/rc_controller.py
+++ b/gamestonk_terminal/resource_collection/rc_controller.py
@@ -14,7 +14,17 @@ class ResourceCollectionController:
"""Resources Controller class"""
# Command choices
- CHOICES = ["help", "q", "quit", "hfletters", "learn"]
+ CHOICES = [
+ "help",
+ "q",
+ "quit",
+ "hfletters",
+ "arxiv",
+ "finra",
+ "edgar",
+ "fred",
+ "learn",
+ ]
def __init__(self):
"""Constructor"""
@@ -34,6 +44,10 @@ class ResourceCollectionController:
print(" quit quit to abandon program")
print("")
print(" hfletters hedge fund letters or reports")
+ print(" arxiv open-access archive for academic articles")
+ print(" finra self-regulatory organization")
+ print(" edgar online public database from SEC")
+ print(" fred economic research data")
print(" learn trading analysis, tips and resources")
print("")
@@ -73,6 +87,26 @@ class ResourceCollectionController:
webbrowser.open("https://miltonfmr.com/hedge-fund-letters/")
print("")
+ def call_arxiv(self, other_args: List[str]):
+ """Process arxiv command"""
+ webbrowser.open("https://arxiv.org")
+ print("")
+
+ def call_finra(self, other_args: List[str]):
+ """Process finra command"""
+ webbrowser.open("https://www.finra.org/#/")
+ print("")
+
+ def call_edgar(self, other_args: List[str]):
+ """Process edgar command"""
+ webbrowser.open("https://www.sec.gov/edgar.shtml")
+ print("")
+
+ def call_fred(self, other_args: List[str]):
+ """Process fred command"""
+ webbrowser.open("https://fred.stlouisfed.org")
+ print("")
+
def call_learn(self, other_args: List[str]):
"""Process learn command"""
webbrowser.open("https://moongangcapital.com/free-stock-market-resources/")
diff --git a/gamestonk_terminal/screener/README.md b/gamestonk_terminal/screener/README.md
index af2a3feeab2..23ede646ac7 100644
--- a/gamestonk_terminal/screener/README.md
+++ b/gamestonk_terminal/screener/README.md
@@ -24,17 +24,19 @@ This menu aims to filter stocks based on pre-specified preset filters, and the u
* [signals](#signals)
* view filter signals (e.g. -s top_gainers) [Finviz]
* [> po](portfolio_optimization/README.md)
- * portfolio optimization for last screened tickers
+ * **portfolio optimization for last screened tickers**
## view
```text
-view
+usage: view [-p {template,sexy_year}]
```
-
View available presets under presets folder.
-
+* -p : View specific preset filters.
+
+
+
## set
diff --git a/gamestonk_terminal/screener/screener_controller.py b/gamestonk_terminal/screener/screener_controller.py
index ae7a07ab050..9ca084e180b 100644
--- a/gamestonk_terminal/screener/screener_controller.py
+++ b/gamestonk_terminal/screener/screener_controller.py
@@ -3,6 +3,7 @@ __docformat__ = "numpy"
import os
import argparse
+import configparser
from typing import List
import matplotlib.pyplot as plt
from gamestonk_terminal import feature_flags as gtff
@@ -86,37 +87,73 @@ class ScreenerController:
prog="view",
description="""View available presets under presets folder.""",
)
+ parser.add_argument(
+ "-p",
+ "--preset",
+ action="store",
+ dest="preset",
+ type=str,
+ help="View specific preset",
+ default="",
+ choices=[
+ preset.split(".")[0]
+ for preset in os.listdir("gamestonk_terminal/screener/presets")
+ if preset[-4:] == ".ini"
+ ],
+ )
try:
+ if other_args:
+ if "-" not in other_args[0]:
+ other_args.insert(0, "-p")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
- presets = [
- preset.split(".")[0]
- for preset in os.listdir("gamestonk_terminal/screener/presets")
- if preset[-4:] == ".ini"
- ]
+ if ns_parser.preset:
+ preset_filter = configparser.RawConfigParser()
+ preset_filter.optionxform = str # type: ignore
+ preset_filter.read(
+ "gamestonk_terminal/screener/presets/" + ns_parser.preset + ".ini"
+ )
- for preset in presets:
- with open(
- "gamestonk_terminal/screener/presets/" + preset + ".ini",
- encoding="utf8",
- ) as f:
- description = ""
- for line in f:
- if "[General]" == line.strip():
- break
- description += line.strip()
- print(f"\nPRESET: {preset}")
- print(description.split("Description: ")[1].replace("#", ""))
+ filters_headers = ["General", "Descriptive", "Fundamental", "Technical"]
+
+ print("")
+ for filter_header in filters_headers:
+ print(f" - {filter_header} -")
+ d_filters = {**preset_filter[filter_header]}
+ d_filters = {k: v for k, v in d_filters.items() if v}
+ if d_filters:
+ max_len = len(max(d_filters, key=len))
+ for key, value in d_filters.items():
+ print(f"{key}{(max_len-len(key))*' '}: {value}")
+ print("")
+
+ else:
+ presets = [
+ preset.split(".")[0]
+ for preset in os.listdir("gamestonk_terminal/screener/presets")
+ if preset[-4:] == ".ini"
+ ]
+
+ for preset in presets:
+ with open(
+ "gamestonk_terminal/screener/presets/" + preset + ".ini",
+ encoding="utf8",
+ ) as f:
+ description = ""
+ for line in f:
+ if "[General]" == line.strip():
+ break
+ description += line.strip()
+ print(f"\nPRESET: {preset}")
+ print(description.split("Description: ")[1].replace("#", ""))
+ print("")
except Exception as e:
print(e)
- print("")
- return
-
@staticmethod
def set_preset(self, other_args: List[str]):
"""Set preset"""
diff --git a/terminal.py b/terminal.py
index 69e6e6f5919..03189c3b1fc 100644
--- a/terminal.py
+++ b/terminal.py
@@ -43,6 +43,7 @@ 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
# pylint: disable=too-many-statements,too-many-branches
@@ -99,6 +100,7 @@ def main():
"po",
"fx",
"rc",
+ "gov",
]
menu_parser.add_argument("opt", choices=choices)
@@ -378,6 +380,9 @@ def main():
elif ns_known_args.opt == "rc":
b_quit = rc_controller.menu()
+ elif ns_known_args.opt == "gov":
+ b_quit = gov_controller.menu(s_ticker)
+
else:
print("Shouldn't see this command!")
continue