resolve conflict from personal changes

This commit is contained in:
DJ Nieto
2021-04-15 21:43:14 -06:00
parent 2ea0c0c61e
commit d58f8f4caf
40 changed files with 2520 additions and 641 deletions

View File

@@ -58,6 +58,9 @@
<ul>
<li><a href="#install">Install</a></li>
<li><a href="#advanced-user-install---machine-learning">Advanced User Install - Machine Learning</a></li>
<li><a href="#other-issues">Other Issues</a></li>
<li><a href="#modulenotfounderror">ModuleNotFoundError</a></li>
<li><a href="#update-terminal">Update Terminal</a></li>
<li><a href="#api-keys">API Keys</a></li>
<li><a href="#usage">Usage</a></li>
</ul>
@@ -120,6 +123,14 @@ Navigate into the folder with: `cd GamestonkTerminal/`
conda install poetry
```
5.5. If installing python 3.8
```
conda deactivate
conda activate gst
```
*The `conda deactivate` -> `conda activate` in the middle is on purpose, this is sometimes required to avoid issues with poetry*
6. Install poetry dependencies
```
poetry install
@@ -132,21 +143,6 @@ This is a library for package management, and ensures a smoother experience than
python terminal.py
```
### Advanced User Install - Python 3.8
*Note that the `conda deactivate` -> `conda activate` in the middle is on purpose, this is sometimes required to avoid issues with poetry*
```
conda create -n gst python=3.8.8
conda activate gst
conda install poetry
conda deactivate
conda activate gst
poetry install
conda install -c conda-forge fbprophet numpy=1.19.5 hdf5=1.10.5
poetry install -E prediction
```
### Advanced User Install - Machine Learning
If you are an advanced user and use other Python distributions, we have several requirements.txt documents that you can pick from to download project dependencies.
@@ -185,7 +181,7 @@ Note: The problem with docker is that it won't output matplotlib figures.
* `pip install pystan --upgrade`
* `poetry update --lock`
### Other issues
### Other Issues
If you run into trouble with poetry and the advice above did not help, your best bet is to try
@@ -215,6 +211,7 @@ If you run into trouble with poetry and the advice above did not help, your best
7. Submit a ticket on github
### ModuleNotFoundError
In the case when you run into an error of the form `ModuleNotFoundError: No module named '_______'`. The solution is to
install the missing package via pip.
@@ -223,6 +220,30 @@ If you get the error that `statsmodels` is not found, you would run
Then please submit an issue so that we can address why that was not imported.
Please note that the package `pmdarima` needs to installed through `pip install` and not through `conda install`.
### Update Terminal
The terminal is constantly being updated with new features and bug fixes, hence, for your terminal to be update, you can run:
```
git pull
```
to get the latest changes.
If this fails due to the fact that you had modified some python files, and there's a conflict with the updates, you can use:
```
git stash
```
Then, re-run `poetry install` or `pip install -r requirements.txt` to get any new dependencies.
Once installation is finished, you're ready to gamestonk.
If you `stashed` your changes previously, you can un-stash them with:
```
git stash pop
```
### API Keys
The project is build around several different API calls, whether it is to access historical data or financials.

View File

@@ -142,8 +142,13 @@
* [ ] Add Brokers (td, webull, etc) (@jmaslek)
* [ ] Refactoring
* [ ] Summaries / tear sheets (@jmaslek)
* [ ] Portfolio Optimization (@jmaslek)
___
##Portfolio Optimization
* [x] Basic Optimization through PyPortFolioOpt(@jmaslek) - [PR #329](https://github.com/DidierRLopes/GamestonkTerminal/pull/329)
**NEXT**
* [ ] Decide what features should be present
* [ ] Allow for more custom optimization constrains
---
## FRED

View File

@@ -23,9 +23,11 @@ The main menu allows the following commands:
```
load -t S_TICKER [-s S_START_DATE] [-i {1,5,15,30,60}]
```
* Load stock ticker to perform analysis on
* Load stock ticker to perform analysis on. When the data source is 'yf', an Indian ticker can be loaded by using '.NS' at the end, e.g. 'SBIN.NS'. See available market in https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html.
* -s : The starting date (format YYYY-MM-DD) of the stock
* -i : Intraday stock minutes
* --source : Source of historical data. 'yf' and 'av' available. Default 'yf'
* -p : Pre/After market hours. Only works for 'yf' source, and intraday data
**Note:** Until a ticker is loaded, the menu will only show *disc* and *sen* menu, as the others require a ticker being provided.
@@ -33,6 +35,7 @@ load -t S_TICKER [-s S_START_DATE] [-i {1,5,15,30,60}]
clear
```
* Clear previously loaded stock ticker.
```
view -t S_TICKER [-s S_START_DATE] [-i {1,5,15,30,60}] [--type N_TYPE]
```
@@ -238,6 +241,21 @@ Command|Description|Brokers
`hold` | view net holdings across all logins
&nbsp;
## Portfolio Optimization [»](portfolio_optimization/README.md)
command|desciription
------|------
`equal_weight`|Returns equal weights for all selected stocks
`mkt_cap`| Returns weights that are weighted by market cap
`div_yield`|Returns weights that are weighted by dividend yield
`max_sharpe`| Returns portfolio of Efficient Frontier with maximum sharpe ratio
`min_vol`| Returns portfolio on Efficient Frontier with minimum volatility
`eff_risk`| Returns portfolio on Efficient Frontier that maximizes returns at a given risk
`eff_ret`| Returns portfolio on Efficient Frontier that minimizes risk at a given return level
`show_eff`|Plots random portfolios and shows the Efficient Frontier
&nbsp;
## Cryptocurrency [»](cryptocurrency/README.md)
Command|Description

View File

@@ -99,7 +99,7 @@ def regions(l_args, s_ticker):
plt.bar(df_interest_region.index, df_interest_region[s_ticker], width=0.8)
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.ylabel("Interest [%]")
plt.xlabel("Time")
plt.xlabel("Region")
plt.show()
print("")

View File

@@ -3,6 +3,7 @@ __docformat__ = "numpy"
import argparse
import requests
import random
from typing import List
from datetime import datetime
import pandas as pd
@@ -15,6 +16,7 @@ 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 finviz_compare_view
from gamestonk_terminal.portfolio_optimization import po_controller
from gamestonk_terminal.menu import session
from prompt_toolkit.completion import NestedCompleter
@@ -42,6 +44,7 @@ class ComparisonAnalysisController:
"ownership",
"performance",
"technical",
"po",
]
def __init__(
@@ -107,6 +110,8 @@ class ComparisonAnalysisController:
print(" performance brief performance comparison")
print(" technical brief technical comparison")
print("")
print(" > po portfolio optimization for selected tickers")
print("")
return
@staticmethod
@@ -172,9 +177,11 @@ class ComparisonAnalysisController:
if len(self.similar) > 10:
print(
"\nThe limit of stocks to compare with are 10. Hence, the similar stocks list will be:"
"\nThe limit of stocks to compare with are 10. Hence, 10 random similar stocks will be displayed.",
"\nThe selected list will be:",
)
self.similar = self.similar[:10]
random.shuffle(self.similar)
self.similar = sorted(self.similar[:10])
print(", ".join(self.similar))
except Exception as e:
@@ -312,6 +319,10 @@ class ComparisonAnalysisController:
"""Process technical command"""
finviz_compare_view.screener(other_args, "technical", self.ticker, self.similar)
def call_po(self, _):
"""Open Portfolio Optimization menu with ticker and similar"""
return po_controller.menu([self.ticker] + self.similar)
def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str):
"""Comparison Analysis Menu"""

View File

@@ -4,6 +4,7 @@ __docformat__ = "numpy"
import argparse
import os
from typing import List
from matplotlib import 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
@@ -209,6 +210,8 @@ def menu():
an_input = input(f"{get_flair()} (disc)> ")
try:
plt.close("all")
process_input = disc_controller.switch(an_input)
if process_input is not None:

View File

@@ -1,6 +1,11 @@
""" Business Insider View """
__docformat__ = "numpy"
import argparse
from typing import List
import json
import re
from pandas.core.frame import DataFrame
import requests
import matplotlib.pyplot as plt
import pandas as pd
@@ -12,11 +17,30 @@ from gamestonk_terminal.helper_funcs import (
get_user_agent,
parse_known_args_and_warn,
)
from gamestonk_terminal import feature_flags as gtff
register_matplotlib_converters()
def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
def price_target_from_analysts(
other_args: List[str], stock: DataFrame, ticker: str, start: str, interval: str
):
"""Display analysts' price targets for a given stock
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
stock : DataFrame
Due diligence stock dataframe
ticker : str
Due diligence ticker symbol
start : str
Start date of the stock data
interval : str
Stock data interval
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="pt",
@@ -34,12 +58,12 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
)
try:
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
url_market_business_insider = (
f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock"
f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock"
)
text_soup_market_business_insider = BeautifulSoup(
requests.get(
@@ -59,8 +83,7 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
d_analyst_data = json.loads(s_analyst_data)
break
# pprint.pprint(d_analyst_data)
df_analyst_data = pd.DataFrame.from_dict(d_analyst_data["Markers"])
df_analyst_data = pd.DataFrame.from_dict(d_analyst_data["Markers"]) # type: ignore
df_analyst_data = df_analyst_data[
["DateLabel", "Company", "InternalRating", "PriceTarget"]
]
@@ -73,17 +96,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
df_analyst_data = df_analyst_data.set_index("Date")
# Slice start of ratings
if s_start:
df_analyst_data = df_analyst_data[s_start:]
if start:
df_analyst_data = df_analyst_data[start:] # type: ignore
if s_interval == "1440min":
plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=3)
if interval == "1440min":
plt.plot(stock.index, stock["5. adjusted close"].values, lw=3)
# Intraday
else:
plt.plot(df_stock.index, df_stock["4. close"].values, lw=3)
plt.plot(stock.index, stock["4. close"].values, lw=3)
if s_start:
plt.plot(df_analyst_data.groupby(by=["Date"]).mean()[s_start:])
if start:
plt.plot(df_analyst_data.groupby(by=["Date"]).mean()[start:]) # type: ignore
else:
plt.plot(df_analyst_data.groupby(by=["Date"]).mean())
@@ -91,13 +114,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
plt.legend(["Closing Price", "Average Price Target", "Price Target"])
plt.title(f"{s_ticker} (Time Series) and Price Target")
plt.xlim(df_stock.index[0], df_stock.index[-1])
plt.title(f"{ticker} (Time Series) and Price Target")
plt.xlim(stock.index[0], stock.index[-1])
plt.xlabel("Time")
plt.ylabel("Share Price ($)")
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.minorticks_on()
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
@@ -115,7 +142,17 @@ def price_target_from_analysts(l_args, df_stock, s_ticker, s_start, s_interval):
return
def estimates(l_args, s_ticker):
def estimates(other_args: List[str], ticker: str):
"""Display analysts' estimates for a given ticker
Parameters
----------
other_args : List[str]
argparse other args
ticker : str
Due diligence ticker symbol
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="est",
@@ -123,12 +160,12 @@ def estimates(l_args, s_ticker):
)
try:
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
url_market_business_insider = (
f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock"
f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock"
)
text_soup_market_business_insider = BeautifulSoup(
requests.get(
@@ -274,7 +311,24 @@ def estimates(l_args, s_ticker):
return
def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
def insider_activity(
other_args: List[str], stock: DataFrame, ticker: str, start: str, interval: str
):
"""Display insider activity
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
stock : DataFrame
Due diligence stock dataframe
ticker : str
Due diligence ticker symbol
start : str
Start date of the stock data
interval : str
Stock data interval
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="ins",
@@ -291,12 +345,12 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
)
try:
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
url_market_business_insider = (
f"https://markets.businessinsider.com/stocks/{s_ticker.lower()}-stock"
f"https://markets.businessinsider.com/stocks/{ticker.lower()}-stock"
)
text_soup_market_business_insider = BeautifulSoup(
requests.get(
@@ -334,17 +388,17 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
df_insider = df_insider.set_index("Date")
df_insider = df_insider.sort_index(ascending=True)
if s_start:
df_insider = df_insider[s_start:]
if start:
df_insider = df_insider[start:] # type: ignore
_, ax = plt.subplots()
if s_interval == "1440min":
plt.plot(df_stock.index, df_stock["5. adjusted close"].values, lw=3)
if interval == "1440min":
plt.plot(stock.index, stock["5. adjusted close"].values, lw=3)
else: # Intraday
plt.plot(df_stock.index, df_stock["4. close"].values, lw=3)
plt.plot(stock.index, stock["4. close"].values, lw=3)
plt.title(f"{s_ticker.upper()} (Time Series) and Price Target")
plt.title(f"{ticker.upper()} (Time Series) and Price Target")
plt.xlabel("Time")
plt.ylabel("Share Price ($)")
@@ -354,7 +408,7 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
* float(row["Shares Traded"].replace(",", "")),
axis=1,
)
plt.xlim(df_insider.index[0], df_stock.index[-1])
plt.xlim(df_insider.index[0], stock.index[-1])
min_price, max_price = ax.get_ylim()
price_range = max_price - min_price
@@ -373,16 +427,16 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
for ind in (
df_insider[df_insider["Type"] == "Sell"].groupby(by=["Date"]).sum().index
):
if ind in df_stock.index:
if ind in stock.index:
ind_dt = ind
else:
ind_dt = get_next_stock_market_days(ind, 1)[0]
n_stock_price = 0
if s_interval == "1440min":
n_stock_price = df_stock["5. adjusted close"][ind_dt]
if interval == "1440min":
n_stock_price = stock["5. adjusted close"][ind_dt]
else:
n_stock_price = df_stock["4. close"][ind_dt]
n_stock_price = stock["4. close"][ind_dt]
plt.vlines(
x=ind_dt,
@@ -402,16 +456,16 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
for ind in (
df_insider[df_insider["Type"] == "Buy"].groupby(by=["Date"]).sum().index
):
if ind in df_stock.index:
if ind in stock.index:
ind_dt = ind
else:
ind_dt = get_next_stock_market_days(ind, 1)[0]
n_stock_price = 0
if s_interval == "1440min":
n_stock_price = df_stock["5. adjusted close"][ind_dt]
if interval == "1440min":
n_stock_price = stock["5. adjusted close"][ind_dt]
else:
n_stock_price = df_stock["4. close"][ind_dt]
n_stock_price = stock["4. close"][ind_dt]
plt.vlines(
x=ind_dt,
@@ -431,6 +485,10 @@ def insider_activity(l_args, df_stock, s_ticker, s_start, s_interval):
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.minorticks_on()
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
if gtff.USE_ION:
plt.ion()
plt.show()
l_names = list()

View File

@@ -0,0 +1,219 @@
""" Due Diligence Controller """
__docformat__ = "numpy"
import argparse
from typing import List
from pandas.core.frame import DataFrame
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal.due_diligence import business_insider_view as bi_view
from gamestonk_terminal.due_diligence import financial_modeling_prep_view as fmp_view
from gamestonk_terminal.due_diligence import finviz_view as fvz_view
from gamestonk_terminal.due_diligence import market_watch_view as mw_view
from gamestonk_terminal.due_diligence import quandl_view as q_view
from gamestonk_terminal.due_diligence import reddit_view as r_view
from gamestonk_terminal.due_diligence import news_view
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import get_flair
from gamestonk_terminal.menu import session
class DueDiligenceController:
""" Due Diligence Controller """
# Command choices
CHOICES = [
"help",
"q",
"quit",
"red",
"short",
"rating",
"pt",
"est",
"ins",
"insider",
"news",
"analyst",
"warnings",
"sec",
]
def __init__(self, stock: DataFrame, ticker: str, start: str, interval: str):
"""Constructor
Parameters
----------
stock : DataFrame
Due diligence stock dataframe
ticker : str
Due diligence ticker symbol
start : str
Start date of the stock data
interval : str
Stock data interval
"""
self.stock = stock
self.ticker = ticker
self.start = start
self.interval = interval
self.dd_parser = argparse.ArgumentParser(add_help=False, prog="dd")
self.dd_parser.add_argument(
"cmd",
choices=self.CHOICES,
)
def print_help(self):
""" Print help """
intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"]
if self.start:
print(
f"\n{intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
)
else:
print(f"\n{intraday} Stock: {self.ticker}")
print("\nDue Diligence:")
print(" help show this fundamental analysis menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print("")
print(" news latest news of the company [News API]")
print(" red gets due diligence from another user's post [Reddit]")
print(" analyst analyst prices and ratings of the company [Finviz]")
print(
" rating rating of the company from strong sell to strong buy [FMP]"
)
print(" pt price targets over time [Business Insider]")
print(
" est quarter and year analysts earnings estimates [Business Insider]"
)
print(" ins insider activity over time [Business Insider]")
print(" insider insider trading of the company [Finviz]")
print(" sec SEC filings [Market Watch]")
print(" short short interest [Quandl]")
print(
" warnings company warnings according to Sean Seah book [Market Watch]"
)
print("")
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.dd_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()
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_red(self, other_args: List[str]):
""" Process red command """
r_view.due_diligence(other_args, self.ticker)
def call_insider(self, other_args: List[str]):
""" Process insider command """
fvz_view.insider(other_args, self.ticker)
def call_news(self, other_args: List[str]):
""" Process news command """
news_view.news(other_args, self.ticker)
def call_analyst(self, other_args: List[str]):
""" Process analyst command """
fvz_view.analyst(other_args, self.ticker)
def call_pt(self, other_args: List[str]):
""" Process pt command """
bi_view.price_target_from_analysts(
other_args, self.stock, self.ticker, self.start, self.interval
)
def call_est(self, other_args: List[str]):
""" Process est command """
bi_view.estimates(other_args, self.ticker)
def call_ins(self, other_args: List[str]):
""" Process ins command """
bi_view.insider_activity(
other_args, self.stock, self.ticker, self.start, self.interval
)
def call_rating(self, other_args: List[str]):
""" Process rating command """
fmp_view.rating(other_args, self.ticker)
def call_warnings(self, other_args: List[str]):
""" Process rating command """
mw_view.sean_seah_warnings(other_args, self.ticker)
def call_sec(self, other_args: List[str]):
""" Process sec command """
mw_view.sec_fillings(other_args, self.ticker)
def call_short(self, other_args: List[str]):
""" Process short command """
q_view.short_interest(other_args, self.ticker, self.start)
def menu(stock: DataFrame, ticker: str, start: str, interval: str):
"""Due Diligence Menu
Parameters
----------
stock : DataFrame
Due diligence stock dataframe
ticker : str
Due diligence ticker symbol
start : str
Start date of the stock data
interval : str
Stock data interval
"""
dd_controller = DueDiligenceController(stock, ticker, start, interval)
dd_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 dd_controller.CHOICES}
)
an_input = session.prompt(
f"{get_flair()} (dd)> ",
completer=completer,
)
else:
an_input = input(f"{get_flair()} (dd)> ")
try:
process_input = dd_controller.switch(an_input)
if process_input is not None:
return process_input
except SystemExit:
print("The command selected doesn't exist\n")
continue

View File

@@ -1,148 +0,0 @@
import argparse
from gamestonk_terminal.due_diligence import business_insider_api as bi_api
from gamestonk_terminal.due_diligence import financial_modeling_prep_api as fmp_api
from gamestonk_terminal.due_diligence import finviz_api as fvz_api
from gamestonk_terminal.due_diligence import market_watch_api as mw_api
from gamestonk_terminal.due_diligence import quandl_api as q_api
from gamestonk_terminal.due_diligence import reddit_api as r_api
from gamestonk_terminal.due_diligence import news_api
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
def print_due_diligence(s_ticker, s_start, s_interval):
""" Print help """
s_intraday = (f"Intraday {s_interval}", "Daily")[s_interval == "1440min"]
if s_start:
print(f"\n{s_intraday} Stock: {s_ticker} (from {s_start.strftime('%Y-%m-%d')})")
else:
print(f"\n{s_intraday} Stock: {s_ticker}")
print("\nDue Diligence:")
print(" help show this fundamental analysis menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print("")
print(" news latest news of the company [News API]")
print(" red gets due diligence from another user's post [Reddit]")
print(" analyst analyst prices and ratings of the company [Finviz]")
print(" rating rating of the company from strong sell to strong buy [FMP]")
print(" pt price targets over time [Business Insider]")
print(
" est quarter and year analysts earnings estimates [Business Insider]"
)
print(" ins insider activity over time [Business Insider]")
print(" insider insider trading of the company [Finviz]")
print(" sec SEC filings [Market Watch]")
print(" short short interest [Quandl]")
print(
" warnings company warnings according to Sean Seah book [Market Watch]"
)
print("")
return
def dd_menu(df_stock, s_ticker, s_start, s_interval):
# Add list of arguments that the due diligence parser accepts
dd_parser = argparse.ArgumentParser(prog="dd", add_help=False)
choices = [
"info",
"help",
"q",
"quit",
"red",
"short",
"rating",
"pt",
"est",
"ins",
"insider",
"news",
"analyst",
"warnings",
"sec",
]
dd_parser.add_argument("cmd", choices=choices)
completer = NestedCompleter.from_nested_dict({c: None for c in choices})
print_due_diligence(s_ticker, s_start, s_interval)
# Loop forever and ever
while True:
# Get input command from user
if session and gtff.USE_PROMPT_TOOLKIT:
as_input = session.prompt(
f"{get_flair()} (dd)> ",
completer=completer,
)
else:
as_input = input(f"{get_flair()} (dd)> ")
# Parse due diligence command of the list of possible commands
try:
(ns_known_args, l_args) = dd_parser.parse_known_args(as_input.split())
except SystemExit:
print("The command selected doesn't exist\n")
continue
if ns_known_args.cmd == "help":
print_due_diligence(s_ticker, s_start, s_interval)
elif ns_known_args.cmd == "q":
# Just leave the DD menu
return False
elif ns_known_args.cmd == "quit":
# Abandon the program
return True
# REDDIT API
elif ns_known_args.cmd == "red":
r_api.due_diligence(l_args, s_ticker)
# FINVIZ API
elif ns_known_args.cmd == "insider":
fvz_api.insider(l_args, s_ticker)
elif ns_known_args.cmd == "news":
news_api.news(l_args, s_ticker)
elif ns_known_args.cmd == "analyst":
fvz_api.analyst(l_args, s_ticker)
# BUSINESS INSIDER API
elif ns_known_args.cmd == "pt":
bi_api.price_target_from_analysts(
l_args, df_stock, s_ticker, s_start, s_interval
)
elif ns_known_args.cmd == "est":
bi_api.estimates(l_args, s_ticker)
elif ns_known_args.cmd == "ins":
bi_api.insider_activity(l_args, df_stock, s_ticker, s_start, s_interval)
# FINANCIAL MODELING PREP API
elif ns_known_args.cmd == "rating":
fmp_api.rating(l_args, s_ticker)
# MARKET WATCH API
elif ns_known_args.cmd == "sec":
mw_api.sec_fillings(l_args, s_ticker)
elif ns_known_args.cmd == "warnings":
mw_api.sean_seah_warnings(l_args, s_ticker)
# QUANDL API
elif ns_known_args.cmd == "short":
q_api.short_interest(l_args, s_ticker, s_start)
else:
print("Command not recognized!")

View File

@@ -1,10 +1,24 @@
""" Financial Modeling Prep View """
__docformat__ = "numpy"
import argparse
from typing import List
import FundamentalAnalysis as fa # Financial Modeling Prep
from gamestonk_terminal import config_terminal as cfg
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
def rating(l_args, s_ticker):
def rating(other_args: List[str], ticker: str):
"""Display ratings for a given ticker
Parameters
----------
other_args : List[str]
argparse other args
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="rating",
@@ -16,18 +30,18 @@ def rating(l_args, s_ticker):
)
try:
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
df_fa = fa.rating(s_ticker, cfg.API_KEY_FINANCIALMODELINGPREP)
df_fa = fa.rating(ticker, cfg.API_KEY_FINANCIALMODELINGPREP)
print(df_fa)
print("")
except KeyError:
print(
f"Financialmodelingprep.com is returning empty response the ticker {s_ticker}."
f"Financialmodelingprep.com is returning empty response the ticker {ticker}."
)
print("")
return

View File

@@ -1,4 +1,8 @@
""" FinViz View """
__docformat__ = "numpy"
import argparse
from typing import List
from colorama import Fore, Style
import finviz
import pandas as pd
@@ -11,6 +15,19 @@ from gamestonk_terminal.helper_funcs import (
def category_color_red_green(val: str) -> str:
"""Add color to analyst rating
Parameters
----------
val : str
Analyst rating - Upgrade/Downgrade
Returns
-------
str
Analyst rating with color
"""
if val == "Upgrade":
return Fore.GREEN + val + Style.RESET_ALL
if val == "Downgrade":
@@ -18,7 +35,16 @@ def category_color_red_green(val: str) -> str:
return val
def insider(l_args, s_ticker):
def insider(other_args: List[str], ticker: str):
"""Display insider activity for a given stock ticker
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="insider",
@@ -39,11 +65,11 @@ def insider(l_args, s_ticker):
)
try:
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
d_finviz_insider = finviz.get_insider(s_ticker)
d_finviz_insider = finviz.get_insider(ticker)
df_fa = pd.DataFrame.from_dict(d_finviz_insider)
df_fa.set_index("Date", inplace=True)
df_fa = df_fa[
@@ -68,7 +94,17 @@ def insider(l_args, s_ticker):
return
def news(l_args, s_ticker):
def news(other_args: List[str], ticker: str):
"""Display news for a given stock ticker
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="news",
@@ -88,11 +124,11 @@ def news(l_args, s_ticker):
)
try:
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
d_finviz_news = finviz.get_news(s_ticker)
d_finviz_news = finviz.get_news(ticker)
i = 0
for s_news_title, s_news_link in {*d_finviz_news}:
print(f"-> {s_news_title}")
@@ -110,9 +146,22 @@ def news(l_args, s_ticker):
return
def analyst_df(s_ticker: str) -> DataFrame:
def analyst_df(ticker: str) -> DataFrame:
"""[summary]
Parameters
----------
ticker : str
Stock ticker
Returns
-------
DataFrame
[description]
"""
try:
d_finviz_analyst_price = finviz.get_analyst_price_targets(s_ticker)
d_finviz_analyst_price = finviz.get_analyst_price_targets(ticker)
df_fa = pd.DataFrame.from_dict(d_finviz_analyst_price)
df_fa.set_index("date", inplace=True)
except Exception as e:
@@ -122,7 +171,17 @@ def analyst_df(s_ticker: str) -> DataFrame:
return df_fa
def analyst(l_args, s_ticker):
def analyst(other_args, ticker):
"""Display analyst ratings
Parameters
----------
other_args : [type]
argparse other args
ticker : [type]
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="analyst",
@@ -144,11 +203,11 @@ def analyst(l_args, s_ticker):
)
try:
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
df_fa = analyst_df(s_ticker)
df_fa = analyst_df(ticker)
if ns_parser.n_color == 1:
df_fa["category"] = df_fa["category"].apply(category_color_red_green)

View File

@@ -1,4 +1,8 @@
""" Market Watch View """
__docformat__ = "numpy"
import argparse
from typing import List
import re
import requests
import pandas as pd
@@ -13,7 +17,17 @@ from gamestonk_terminal.helper_funcs import (
)
def sec_fillings(l_args, s_ticker):
def sec_fillings(other_args: List[str], ticker: str):
"""Display SEC filings for a given stock ticker
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="sec",
@@ -34,13 +48,13 @@ def sec_fillings(l_args, s_ticker):
)
try:
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
pd.set_option("display.max_colwidth", None)
url_financials = f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/secfilings"
url_financials = f"https://www.marketwatch.com/investing/stock/{ticker}/financials/secfilings"
text_soup_financials = BeautifulSoup(
requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text,
@@ -60,7 +74,7 @@ def sec_fillings(l_args, s_ticker):
l_financials_info.extend(a_financials[5:-1])
l_financials_info.append(financials_info.a["href"])
# Append data values to financials
df_financials.loc[len(df_financials.index)] = l_financials_info
df_financials.loc[len(df_financials.index)] = l_financials_info # type: ignore
if "Filing Date" in a_financials:
l_financials_header = [a_financials[2]]
@@ -72,7 +86,7 @@ def sec_fillings(l_args, s_ticker):
b_ready_to_process_info = True
# Set Filing Date as index
df_financials = df_financials.set_index("Filing Date")
df_financials = df_financials.set_index("Filing Date") # type: ignore
print(df_financials.head(n=ns_parser.n_num).to_string())
print("")
@@ -83,7 +97,17 @@ def sec_fillings(l_args, s_ticker):
return
def sean_seah_warnings(l_args, s_ticker):
def sean_seah_warnings(other_args: List[str], ticker: str):
"""Display Sean Seah warnings
Parameters
----------
other_args : List[str]
argparse other args
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="warnings",
@@ -114,7 +138,7 @@ def sean_seah_warnings(l_args, s_ticker):
)
try:
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
@@ -129,7 +153,7 @@ def sean_seah_warnings(l_args, s_ticker):
# From INCOME STATEMENT, get: 'EPS (Basic)', 'Net Income', 'Interest Expense', 'EBITDA'
url_financials = (
f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/income"
f"https://www.marketwatch.com/investing/stock/{ticker}/financials/income"
)
text_soup_financials = BeautifulSoup(
requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text,
@@ -173,7 +197,7 @@ def sean_seah_warnings(l_args, s_ticker):
]
# From BALANCE SHEET, get: 'Liabilities & Shareholders\' Equity', 'Long-Term Debt'
url_financials = f"https://www.marketwatch.com/investing/stock/{s_ticker}/financials/balance-sheet"
url_financials = f"https://www.marketwatch.com/investing/stock/{ticker}/financials/balance-sheet"
text_soup_financials = BeautifulSoup(
requests.get(url_financials, headers={"User-Agent": get_user_agent()}).text,
"lxml",

View File

@@ -1,4 +1,8 @@
""" News View """
__docformat__ = "numpy"
import argparse
from typing import List
from datetime import datetime, timedelta
import requests
from gamestonk_terminal import config_terminal as cfg
@@ -8,7 +12,17 @@ from gamestonk_terminal.helper_funcs import (
)
def news(l_args, s_ticker):
def news(other_args: List[str], ticker: str):
"""Display news for a given ticker
Parameters
----------
other_args : List[str]
argparse other args - ["-n", "10"]
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="news",
@@ -28,14 +42,14 @@ def news(l_args, s_ticker):
)
try:
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
s_from = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
response = requests.get(
f"https://newsapi.org/v2/everything?q={s_ticker}&from={s_from}"
f"https://newsapi.org/v2/everything?q={ticker}&from={s_from}"
f"&sortBy=publishedAt&language=en&apiKey={cfg.API_NEWS_TOKEN}",
)
@@ -45,7 +59,7 @@ def news(l_args, s_ticker):
else:
print(
f"{response.json()['totalResults']} news articles from {s_ticker} were found since {s_from}\n"
f"{response.json()['totalResults']} news articles from {ticker} were found since {s_from}\n"
)
for idx, article in enumerate(response.json()["articles"]):

View File

@@ -1,7 +1,11 @@
""" Quandl View """
__docformat__ = "numpy"
import argparse
from typing import List
import quandl
from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.ticker
import pandas as pd
from gamestonk_terminal.helper_funcs import (
check_positive,
@@ -11,7 +15,19 @@ from gamestonk_terminal.helper_funcs import (
from gamestonk_terminal import config_terminal as cfg
def short_interest(l_args, s_ticker, s_start):
def short_interest(other_args: List[str], ticker: str, start: str):
"""Display short interest for a given ticker and a given start date
Parameters
----------
other_args : List[str]
argparse other args - ["-d", "10"]
ticker : str
Stock ticker
start : str
Start date of the stock data
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="short",
@@ -40,18 +56,18 @@ def short_interest(l_args, s_ticker, s_start):
)
try:
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
quandl.ApiConfig.api_key = cfg.API_KEY_QUANDL
if ns_parser.b_nyse:
df_short_interest = quandl.get(f"FINRA/FNYX_{s_ticker}")
df_short_interest = quandl.get(f"FINRA/FNYX_{ticker}")
else:
df_short_interest = quandl.get(f"FINRA/FNSQ_{s_ticker}")
df_short_interest = quandl.get(f"FINRA/FNSQ_{ticker}")
df_short_interest = df_short_interest[s_start:]
df_short_interest = df_short_interest[start:] # type: ignore
df_short_interest.columns = [
"".join(
" " + char if char.isupper() else char.strip() for char in idx
@@ -77,18 +93,18 @@ def short_interest(l_args, s_ticker, s_start):
ax.set_ylabel("Shares")
ax.set_xlabel("Date")
if s_start:
if start:
ax.set_title(
f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {s_ticker} from {s_start.date()}"
f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {ticker} from {start.date()}" # type: ignore
)
else:
ax.set_title(
f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {s_ticker}"
f"{('NASDAQ', 'NYSE')[ns_parser.b_nyse]} Short Interest on {ticker}"
)
ax.legend(labels=["Short Volume", "Total Volume"])
ax.tick_params(axis="both", which="major")
ax.yaxis.set_major_formatter(ticker.EngFormatter())
ax.yaxis.set_major_formatter(matplotlib.ticker.EngFormatter())
ax_twin = ax.twinx()
ax_twin.tick_params(axis="y", colors="green")
ax_twin.set_ylabel("Percentage of Volume Shorted", color="green")
@@ -98,7 +114,9 @@ def short_interest(l_args, s_ticker, s_start):
color="green",
)
ax_twin.tick_params(axis="y", which="major", color="green")
ax_twin.yaxis.set_major_formatter(ticker.FormatStrFormatter("%.0f%%"))
ax_twin.yaxis.set_major_formatter(
matplotlib.ticker.FormatStrFormatter("%.0f%%")
)
plt.xlim([df_short_interest.index[0], df_short_interest.index[-1]])
df_short_interest["% of Volume Shorted"] = df_short_interest[

View File

@@ -1,4 +1,8 @@
""" Reddit View """
__docformat__ = "numpy"
import argparse
from typing import List
from datetime import datetime, timedelta
from psaw import PushshiftAPI
import praw
@@ -7,7 +11,17 @@ from gamestonk_terminal import config_terminal as cfg
from gamestonk_terminal.reddit_helpers import print_and_record_reddit_post
def due_diligence(l_args, s_ticker):
def due_diligence(other_args: List[str], ticker: str):
"""Display Reddit due diligence data for a given ticker
Parameters
----------
other_args : List[str]
argparse other args - ["-l", "5"]
ticker : str
Stock ticker
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="red",
@@ -47,7 +61,7 @@ def due_diligence(l_args, s_ticker):
)
try:
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
@@ -83,9 +97,9 @@ def due_diligence(l_args, s_ticker):
]
submissions = psaw_api.search_submissions(
after=int(n_ts_after), subreddit=l_sub_reddits, q=s_ticker, filter=["id"]
after=int(n_ts_after), subreddit=l_sub_reddits, q=ticker, filter=["id"]
)
d_submission = {}
d_submission = {} # type: ignore
n_flair_posts_found = 0
while True:
submission = next(submissions, None)

View File

@@ -16,6 +16,9 @@ USE_PROMPT_TOOLKIT = strtobool(os.getenv("GTFF_USE_PROMPT_TOOLKIT", "False"))
# Enable Prediction features
ENABLE_PREDICT = strtobool(os.getenv("GTFF_ENABLE_PREDICT", "False"))
# Enable FB Prophet
ENABLE_FBPROPHET = strtobool(os.getenv("GTFF_ENABLE_FBPROPHET", "False"))
# Enable plot autoscaling
USE_PLOT_AUTOSCALING = strtobool(os.getenv("GTFF_USE_PLOT_AUTOSCALING", "False"))

View File

@@ -1,10 +1,12 @@
import argparse
from sys import stdout
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import pandas as pd
from alpha_vantage.timeseries import TimeSeries
import mplfinance as mpf
import yfinance as yf
import pytz
from gamestonk_terminal.helper_funcs import (
valid_date,
@@ -58,6 +60,9 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
)
print(" pa portfolio analysis, \t\t supports: robinhood, alpaca, ally ")
print(" crypto cryptocurrencies, \t\t uses coingecko api")
print(
" po portfolio optimization, \t\t optimal portfolio weights from pyportfolioopt"
)
if s_ticker:
print(
@@ -117,7 +122,9 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock):
parser = argparse.ArgumentParser(
add_help=False,
prog="load",
description=""" Load a stock in order to perform analysis.""",
description="Load stock ticker to perform analysis on. When the data source is 'yf', an Indian ticker can be"
" loaded by using '.NS' at the end, e.g. 'SBIN.NS'. See available market in"
" https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html.",
)
parser.add_argument(
"-t",
@@ -151,8 +158,15 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock):
dest="source",
type=check_sources,
default="yf",
help="Source of historical data. Intraday: Only 'av' available."
"Daily: 'yf' and 'av' available.",
help="Source of historical data. 'yf' and 'av' available.",
)
parser.add_argument(
"-p",
"--prepost",
action="store_true",
default=False,
dest="b_prepost",
help="Pre/After market hours. Only works for 'yf' source, and intraday data",
)
try:
@@ -229,9 +243,54 @@ def load(l_args, s_ticker, s_start, s_interval, df_stock):
df_stock = df_stock[ns_parser.s_start_date :]
elif ns_parser.source == "yf":
print(
"Unfortunately, yahoo finance historical data doesn't contain intraday prices"
s_int = s_interval[:-2]
d_granularity = {"1m": 6, "5m": 59, "15m": 59, "30m": 59, "60m": 729}
s_start_dt = datetime.utcnow() - timedelta(days=d_granularity[s_int])
s_date_start = s_start_dt.strftime("%Y-%m-%d")
s_start = pytz.utc.localize(s_start_dt)
if s_start_dt > ns_parser.s_start_date:
print(
f"Using Yahoo Finance with granularity {s_int} the starting date is set to: {s_date_start}\n"
)
df_stock = yf.download(
ns_parser.s_ticker,
start=s_date_start,
progress=False,
interval=s_int,
prepost=ns_parser.b_prepost,
)
else:
df_stock = yf.download(
ns_parser.s_ticker,
start=ns_parser.s_start_date.strftime("%Y-%m-%d"),
progress=False,
interval=s_int,
prepost=ns_parser.b_prepost,
)
# Check that a stock was successfully retrieved
if df_stock.empty:
print("")
return [s_ticker, s_start, s_interval, df_stock]
df_stock = df_stock.rename(
columns={
"Open": "1. open",
"High": "2. high",
"Low": "3. low",
"Close": "4. close",
"Adj Close": "5. adjusted close",
"Volume": "6. volume",
}
)
df_stock.index.name = "date"
return [s_ticker, s_start, s_interval, df_stock]
except Exception as e:
@@ -369,24 +428,6 @@ def view(l_args, s_ticker, s_start, s_interval, df_stock):
type_candles = lett_to_num(ns_parser.type)
try:
ts = TimeSeries(key=cfg.API_KEY_ALPHAVANTAGE, output_format="pandas")
# Daily
if s_interval == "1440min":
# pylint: disable=unbalanced-tuple-unpacking
df_stock, _ = ts.get_daily_adjusted(symbol=s_ticker, outputsize="full")
# Intraday
else:
# pylint: disable=unbalanced-tuple-unpacking
df_stock, _ = ts.get_intraday(
symbol=s_ticker, outputsize="full", interval=s_interval
)
except Exception as e:
print(e)
print("Either the ticker or the API_KEY are invalids. Try again!")
return
df_stock.sort_index(ascending=True, inplace=True)
# Slice dataframe from the starting date YYYY-MM-DD selected

View File

@@ -9,7 +9,7 @@ import yfinance as yf
from prompt_toolkit.completion import NestedCompleter
from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.options import volume
from gamestonk_terminal.options import volume_view
from gamestonk_terminal.menu import session
@@ -17,16 +17,15 @@ class OptionsController:
"""Options Controller class."""
# Command choices
CHOICES = ["help", "q", "quit", "exp", "volume"]
CHOICES = ["help", "q", "quit", "exp", "voi", "vcalls", "vputs"]
def __init__(
self,
ticker: str,
):
def __init__(self, ticker: str, last_adj_close_price: float):
"""Construct data."""
self.ticker = ticker
self.raw_data_options = yf.Ticker(self.ticker)
self.expiry_date = self.raw_data_options.options[0]
self.yf_ticker_data = yf.Ticker(self.ticker)
self.expiry_date = self.yf_ticker_data.options[0]
self.options = self.yf_ticker_data.option_chain(self.expiry_date)
self.last_adj_close_price = last_adj_close_price
self.op_parser = argparse.ArgumentParser(add_help=False, prog="op")
self.op_parser.add_argument(
"cmd",
@@ -48,7 +47,7 @@ class OptionsController:
action="store",
type=int,
default=-1,
choices=range(len(self.raw_data_options.options)),
choices=range(len(self.yf_ticker_data.options)),
help=f"Expiry date index for {self.ticker}.",
)
@@ -64,12 +63,13 @@ class OptionsController:
# Print possible expiry dates
if ns_parser.n_date == -1:
print("\nAvailable expiry dates:")
for i, d in enumerate(self.raw_data_options.options):
for i, d in enumerate(self.yf_ticker_data.options):
print(f" {(2-len(str(i)))*' '}{i}. {d}")
# It means an expiry date was correctly selected
else:
self.expiry_date = self.raw_data_options.options[ns_parser.n_date]
self.expiry_date = self.yf_ticker_data.options[ns_parser.n_date]
self.options = self.yf_ticker_data.option_chain(self.expiry_date)
print(f"\nSelected expiry date : {self.expiry_date}")
except Exception as e:
@@ -89,7 +89,9 @@ class OptionsController:
print(f"Selected expiry date: {expiry_date}")
print("")
print(" exp see/set expiry date")
print(" volume plot options trading volume / open interest")
print(" voi volume + open interest options trading plot")
print(" vcalls calls volume + open interest plot")
print(" vputs puts volume + open interest plot")
print("")
return
@@ -121,26 +123,47 @@ class OptionsController:
"""Process Quit command - quit the program."""
return True
def call_volume(self, _):
"""Process volume command."""
volume.volume_graph(
self.raw_data_options,
self.ticker,
self.expiry_date,
volume_percentile_threshold=60,
)
print("")
def call_exp(self, other_args: List[str]):
"""Process exp command."""
self.expiry_dates(self, other_args)
def call_voi(self, other_args: List[str]):
"""Process voi command."""
volume_view.plot_volume_open_interest(
other_args,
self.ticker,
self.expiry_date,
self.last_adj_close_price,
self.options.calls,
self.options.puts,
)
def menu(ticker: str):
"""Options info Menu."""
def call_vcalls(self, other_args: List[str]):
"""Process vcalls command."""
volume_view.plot_calls_volume_open_interest(
other_args,
self.ticker,
self.expiry_date,
self.last_adj_close_price,
self.options.calls,
)
def call_vputs(self, other_args: List[str]):
"""Process vcalls command."""
volume_view.plot_puts_volume_open_interest(
other_args,
self.ticker,
self.expiry_date,
self.last_adj_close_price,
self.options.puts,
)
def menu(ticker: str, last_adj_close_price: float):
""" Options Menu. """
try:
op_controller = OptionsController(ticker)
op_controller = OptionsController(ticker, last_adj_close_price)
op_controller.call_help(None)
except IndexError:
print("No options found for " + ticker)

View File

@@ -1,176 +0,0 @@
"""options info. [Source: Yahoo Finance]."""
from bisect import bisect_left
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from gamestonk_terminal.helper_funcs import plot_autoscale
from gamestonk_terminal import config_plot as cfgPlot
def volume_graph(raw_data, ticker_name, exp_date, volume_percentile_threshold=50):
"""Docstring make linter hap."""
# SET VOLUME TO BE FILTERED, default = 50
PERCENTILE_THRESHOLD = volume_percentile_threshold
TICKER_NAME = ticker_name
raw_data_options = raw_data
EXP_DATE = exp_date
# current stock price
spot = __get_current_spot(raw_data_options)
calls = __parse_opt_data(raw_data_options, EXP_DATE)
puts = __parse_opt_data(raw_data_options, EXP_DATE, is_calls=False)
calls = __add_max_pain_data(calls, spot)
puts = __add_max_pain_data(puts, spot, is_calls=False)
max_pain = __calc_max_pain(calls, puts)
# Initialize the matplotlib figure
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
# _, ax = plt.subplots(figsize=(12, 10))
# make x axis symmetric
axis_origin = max(abs(max(puts["oi+v"])), abs(max(calls["oi+v"])))
ax.set_xlim(-axis_origin, +axis_origin)
VOLUME_THRESHOLD = np.percentile(calls["oi+v"], PERCENTILE_THRESHOLD)
sns.set_style(style="darkgrid")
g = sns.barplot(
x="oi+v",
y="strike",
data=calls[calls["oi+v"] > VOLUME_THRESHOLD],
label="Calls: Open Interest",
color="lightgreen",
orient="h",
)
g = sns.barplot(
x="volume",
y="strike",
data=calls[calls["oi+v"] > VOLUME_THRESHOLD],
label="Calls: Volume",
color="green",
orient="h",
)
g = sns.barplot(
x="oi+v",
y="strike",
data=puts[puts["oi+v"] < -VOLUME_THRESHOLD],
label="Puts: Open Interest",
color="pink",
orient="h",
)
g = sns.barplot(
x="volume",
y="strike",
data=puts[puts["oi+v"] < -VOLUME_THRESHOLD],
label="Puts: Volume",
color="red",
orient="h",
)
# draw spot line
s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
spot_index = bisect_left(s, spot) # find where the spot is on the graph
spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3)
# draw max pain line
max_pain_index = bisect_left(s, max_pain)
max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3)
max_pain_line.set_linewidth(5)
# ax.axhline(max_pain_index, ls='--')
# format ticklabels without - for puts
g.set_xticks(g.get_xticks())
xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
g.set_xticklabels(xlabels)
plt.title(
f"{TICKER_NAME.upper()} volumes for {EXP_DATE} (open interest displayed only during market hours)"
)
ax.invert_yaxis()
# ax.spines['left'].set_position('center')
_ = ax.legend()
handles, _ = ax.get_legend_handles_labels()
handles.append(spot_line)
handles.append(max_pain_line)
# create legend labels + add to graph
labels = [
"Calls open interest",
"Calls volume ",
"Puts open interest",
"Puts volume",
"Current stock price",
f"Max pain = {max_pain}",
]
plt.legend(handles=handles[:], labels=labels)
sns.despine(left=True, bottom=True)
plt.show()
def __add_max_pain_data(df, spot, is_calls=True):
# max pain parsing + calculation
df["spot"] = round(spot, 2)
if is_calls:
df["dv"] = spot - df.index
else:
df["dv"] = df.index - spot
df["dv"] = df["dv"].apply(lambda x: max(0, x))
df["dv"] = abs(df["dv"] * df["volume"])
return df
def __calc_max_pain(calls, puts):
df = pd.merge(calls, puts, left_index=True, right_index=True)
df["dv"] = round(df["dv_x"] + df["dv_y"], 2)
max_pain = df["dv"].idxmax()
return max_pain
def __parse_opt_data(raw_data_options, exp_date, is_calls=True):
# get option chain for specific expiration
opt = raw_data_options.option_chain(exp_date)
# PARSE DATA
if is_calls:
option_data = opt.calls
flag = "calls"
else:
option_data = opt.puts
flag = "puts"
data = option_data.pivot_table(
index="strike", values=["volume", "openInterest"], aggfunc="sum"
).reindex()
data["strike"] = data.index
data["type"] = flag
if is_calls:
data["openInterest"] = data["openInterest"]
data["volume"] = data["volume"]
else:
data["openInterest"] = -data["openInterest"]
data["volume"] = -data["volume"]
data["oi+v"] = data["openInterest"] + data["volume"]
return data
def __get_current_spot(raw_data_options):
return raw_data_options.history().tail(1)["Close"].iloc[0]

View File

@@ -0,0 +1,463 @@
""" Volume view """
__docformat__ = "numpy"
from bisect import bisect_left
import seaborn as sns
from typing import List
import argparse
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from gamestonk_terminal.helper_funcs import (
plot_autoscale,
check_non_negative,
parse_known_args_and_warn,
)
from gamestonk_terminal import config_plot as cfgPlot
from gamestonk_terminal import feature_flags as gtff
def plot_volume_open_interest(
other_args: List[str],
ticker: str,
exp_date: str,
last_adj_close_price: float,
op_calls: pd.DataFrame,
op_puts: pd.DataFrame,
):
"""Options volume and open interest
Parameters
----------
other_args : List[str]
Command line arguments to be processed with argparse
ticker : str
Main ticker to compare income
exp_date : str
Expiry date of the option
last_adj_close_price: float
Last adjusted closing price
op_calls: pd.DataFrame
Option data calls
op_puts: pd.DataFrame
Option data puts
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="voi",
description="""
Plots Volume + Open Interest of calls vs puts. [Source: Yahoo Finance]
""",
)
parser.add_argument(
"-v",
"--minv",
dest="min_vol",
type=check_non_negative,
default=-1,
help="minimum volume (considering open interest) threshold of the plot.",
)
parser.add_argument(
"-m",
"--min",
dest="min_sp",
type=check_non_negative,
default=-1,
help="minimum strike price to consider in the plot.",
)
parser.add_argument(
"-M",
"--max",
dest="max_sp",
type=check_non_negative,
default=-1,
help="maximum strike price to consider in the plot.",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
df_calls, df_puts, max_pain = get_calls_puts_maxpain(
op_calls, op_puts, last_adj_close_price
)
if (
ns_parser.min_vol == -1
and ns_parser.min_sp == -1
and ns_parser.max_sp == -1
):
# If no argument provided, we use the percentile 50 to get 50% of upper volume data
volume_percentile_threshold = 50
min_vol_calls = np.percentile(df_calls["oi+v"], volume_percentile_threshold)
min_vol_puts = np.percentile(df_puts["oi+v"], volume_percentile_threshold)
df_calls = df_calls[df_calls["oi+v"] > min_vol_calls]
df_puts = df_puts[df_puts["oi+v"] < min_vol_puts]
else:
if ns_parser.min_vol > -1:
df_calls = df_calls[df_calls["oi+v"] > ns_parser.min_vol]
df_puts = df_puts[df_puts["oi+v"] < -ns_parser.min_vol]
if ns_parser.min_sp > -1:
df_calls = df_calls[df_calls["strike"] > ns_parser.min_sp]
df_puts = df_puts[df_puts["strike"] > ns_parser.min_sp]
if ns_parser.max_sp > -1:
df_calls = df_calls[df_calls["strike"] < ns_parser.max_sp]
df_puts = df_puts[df_puts["strike"] < ns_parser.max_sp]
if df_calls.empty and df_puts.empty:
print(
"The filtering applied is too strong, there is no data available for such conditions.\n"
)
return
# Initialize the matplotlib figure
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
# make x axis symmetric
axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"])))
ax.set_xlim(-axis_origin, +axis_origin)
sns.set_style(style="darkgrid")
g = sns.barplot(
x="oi+v",
y="strike",
data=df_calls,
label="Calls: Open Interest",
color="lightgreen",
orient="h",
)
g = sns.barplot(
x="volume",
y="strike",
data=df_calls,
label="Calls: Volume",
color="green",
orient="h",
)
g = sns.barplot(
x="oi+v",
y="strike",
data=df_puts,
label="Puts: Open Interest",
color="pink",
orient="h",
)
g = sns.barplot(
x="volume",
y="strike",
data=df_puts,
label="Puts: Volume",
color="red",
orient="h",
)
# draw spot line
s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
spot_index = bisect_left(
s, last_adj_close_price
) # find where the spot is on the graph
spot_line = ax.axhline(spot_index, ls="--", color="dodgerblue", alpha=0.3)
# draw max pain line
max_pain_index = bisect_left(s, max_pain)
max_pain_line = ax.axhline(max_pain_index, ls="-", color="black", alpha=0.3)
max_pain_line.set_linewidth(5)
# format ticklabels without - for puts
g.set_xticks(g.get_xticks())
xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
g.set_xticklabels(xlabels)
plt.title(
f"{ticker} volumes for {exp_date} (open interest displayed only during market hours)"
)
ax.invert_yaxis()
_ = ax.legend()
handles, _ = ax.get_legend_handles_labels()
handles.append(spot_line)
handles.append(max_pain_line)
# create legend labels + add to graph
labels = [
"Calls open interest",
"Calls volume ",
"Puts open interest",
"Puts volume",
"Current stock price",
f"Max pain = {max_pain}",
]
plt.legend(handles=handles[:], labels=labels)
sns.despine(left=True, bottom=True)
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
except Exception as e:
print(e, "\n")
return
def get_calls_puts_maxpain(
op_calls: pd.DataFrame, op_puts: pd.DataFrame, last_adj_close_price: float
):
"""Get calls and puts dataframes, and max pain
Parameters
----------
op_calls: pd.DataFrame
Option data calls
op_puts: pd.DataFrame
Option data puts
last_adj_close_price: float
Last adjusted closing price
Returns
----------
pd.DataFrame
Processed calls dataframe
pd.DataFrame
Processed puts dataframe
float
Max pain
"""
df_calls = pd.DataFrame()
df_puts = pd.DataFrame()
max_pain = 0
if not op_calls.empty:
# Process Calls Data
df_calls = op_calls.pivot_table(
index="strike", values=["volume", "openInterest"], aggfunc="sum"
).reindex()
df_calls["strike"] = df_calls.index
df_calls["type"] = "calls"
df_calls["openInterest"] = df_calls["openInterest"]
df_calls["volume"] = df_calls["volume"]
df_calls["oi+v"] = df_calls["openInterest"] + df_calls["volume"]
df_calls["spot"] = round(last_adj_close_price, 2)
df_calls["dv"] = last_adj_close_price - df_calls.index
df_calls["dv"] = df_calls["dv"].apply(lambda x: max(0, x))
df_calls["dv"] = abs(df_calls["dv"] * df_calls["volume"])
if not op_puts.empty:
# Process Puts Data
df_puts = op_puts.pivot_table(
index="strike", values=["volume", "openInterest"], aggfunc="sum"
).reindex()
df_puts["strike"] = df_puts.index
df_puts["type"] = "puts"
df_puts["openInterest"] = df_puts["openInterest"]
df_puts["volume"] = -df_puts["volume"]
df_puts["openInterest"] = -df_puts["openInterest"]
df_puts["oi+v"] = df_puts["openInterest"] + df_puts["volume"]
df_puts["spot"] = round(last_adj_close_price, 2)
df_puts["dv"] = df_puts.index - last_adj_close_price
df_puts["dv"] = df_puts["dv"].apply(lambda x: max(0, x))
df_puts["dv"] = abs(df_puts["dv"] * df_puts["volume"])
if not op_calls.empty and not op_puts.empty:
# Get max pain
df_opt = pd.merge(df_calls, df_puts, left_index=True, right_index=True)
df_opt["dv"] = round(df_opt["dv_x"] + df_opt["dv_y"], 2)
max_pain = df_opt["dv"].idxmax()
return df_calls, df_puts, max_pain
def plot_calls_volume_open_interest(
other_args: List[str],
ticker: str,
exp_date: str,
last_adj_close_price: float,
op_calls: pd.DataFrame,
):
"""Options volume and open interest
Parameters
----------
other_args : List[str]
Command line arguments to be processed with argparse
ticker : str
Main ticker to compare income
exp_date : str
Expiry date of the option
last_adj_close_price: float
Last adjusted closing price
op_calls: pd.DataFrame
Option data calls
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="vcalls",
description="""
Plots Calls Volume + Open Interest. [Source: Yahoo Finance]
""",
)
parser.add_argument(
"-m",
"--min",
dest="min_sp",
type=check_non_negative,
default=-1,
help="minimum strike price to consider in the plot.",
)
parser.add_argument(
"-M",
"--max",
dest="max_sp",
type=check_non_negative,
default=-1,
help="maximum strike price to consider in the plot.",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
df_calls, _, _ = get_calls_puts_maxpain(
op_calls, pd.DataFrame(), last_adj_close_price
)
if ns_parser.min_sp == -1:
min_strike = 0.75 * last_adj_close_price
else:
min_strike = ns_parser.min_sp
if ns_parser.max_sp == -1:
max_strike = 1.25 * last_adj_close_price
else:
max_strike = ns_parser.max_sp
plt.figure(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
plt.axvline(last_adj_close_price, lw=2)
plt.bar(
df_calls["strike"],
df_calls["volume"] + df_calls["openInterest"],
color="lightgreen",
)
plt.bar(df_calls["strike"], df_calls["volume"], color="green")
plt.title(f"{ticker} calls volumes for {exp_date} ")
plt.legend(["Stock Price", "Open Interest", "Volume"])
plt.xlabel("Strike Price")
plt.ylabel("Volume")
plt.xlim([min_strike, max_strike])
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
except Exception as e:
print(e, "\n")
return
def plot_puts_volume_open_interest(
other_args: List[str],
ticker: str,
exp_date: str,
last_adj_close_price: float,
op_puts: pd.DataFrame,
):
"""Options volume and open interest
Parameters
----------
other_args : List[str]
Command line arguments to be processed with argparse
ticker : str
Main ticker to compare income
exp_date : str
Expiry date of the option
last_adj_close_price: float
Last adjusted closing price
op_puts: pd.DataFrame
Option data puts
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="vputs",
description="""
Plots Puts Volume + Open Interest. [Source: Yahoo Finance]
""",
)
parser.add_argument(
"-m",
"--min",
dest="min_sp",
type=check_non_negative,
default=-1,
help="minimum strike price to consider in the plot.",
)
parser.add_argument(
"-M",
"--max",
dest="max_sp",
type=check_non_negative,
default=-1,
help="maximum strike price to consider in the plot.",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
_, df_puts, _ = get_calls_puts_maxpain(
pd.DataFrame(), op_puts, last_adj_close_price
)
if ns_parser.min_sp == -1:
min_strike = 0.75 * last_adj_close_price
else:
min_strike = ns_parser.min_sp
if ns_parser.max_sp == -1:
max_strike = 1.25 * last_adj_close_price
else:
max_strike = ns_parser.max_sp
plt.figure(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
plt.axvline(last_adj_close_price, lw=2)
plt.bar(
df_puts["strike"],
-df_puts["volume"] - df_puts["openInterest"],
color="pink",
)
plt.bar(df_puts["strike"], -df_puts["volume"], color="red")
plt.title(f"{ticker} puts volumes for {exp_date} ")
plt.legend(["Stock Price", "Open Interest", "Volume"])
plt.xlabel("Strike Price")
plt.ylabel("Volume")
plt.xlim([min_strike, max_strike])
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
except Exception as e:
print(e, "\n")
return

View File

@@ -0,0 +1,169 @@
# Portfolio Optimization
This menu aims to discover optimal portfolios for selected stocks.
Currently we support two methods:
* [Property Weighting](#weighting)
* Equal Weights
* Market Cap Weighting
* Divident Yield Weighting
* [Mean-Variance-Optimization](#eff_front)
* Max sharpe ratio
* Minimum volatility
* Maximum returns at given risk level
* Minimum risk level at a given return
## Procedure
There are three ways to load stocks to be analyzed.
* add
* select
* from ca menu
###add
Adds selected tickers to the menu to be considered
````
usage: add ticker1,ticker2,ticker3,...
````
###select
Clears current list and loads selected ticker
````
usage: select ticker1,ticker2,ticker3,...
````
### ca menu
From the ca menu, the loaded ticker and the selected similar tickers can be loaded by entering the `> po` menu.
From this `po` menu, you can return to `ca` using `ca`, which will keep your loaded stock, but reset the selected stocks from that menu.
Once your stocks are listed, you can select one of the options.
## Property weighted <a name="weighting"></a>
* equal weights
* market cap weighted
* dividend yield weighted
### equal weights
Returns an equally weighted portfolio where the weights are 1/number of stocks.
````
equal_weight [-v --value VALUE] [--pie]
````
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
### market cap weighted
Returns portfolio values that are weighted by their relative Market Cap.
````
mkt_cap [-v --value VALUE] [--pie]
````
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
###dividend yield weighted
Returns portfolio values that are weighted by relative Dividend Yield.
````
div_yield [-v --value VALUE] [--pie]
````
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
## Mean Variance Optimization<a name="eff_front"></a>
These approaches are based off of the efficient frontier approach, which is meant to solve the following optimization problem.
<img src="https://latex.codecogs.com/svg.image?w^TS&space;w" title="w^TS w" />
With constraints:
<img src="https://latex.codecogs.com/svg.image?w^TR&space;>&space;R^*" title="w^TR > R^*" />
\
<img src="https://latex.codecogs.com/svg.image?w_1&plus;w_2&plus;...w_n&space;=&space;1" title="w_1+w_2+...w_n = 1" />
Where S is the covariance matrix between stocks and R is the expected returns. The condition that all weights add up to 1
just implies that you want to have a net long portfolio (with no margin).
A long-short portfolio can have negative weights and usually wants to have everything add up to 0 for a market-neutral strategy.
Currently, we do not allow for changing risk models or adding constraints. If there is something specific, please submit a feature request, or if you can
write it, feel free to add a PR!
All of our current implementations use the [PyPortFolioOpt](#https://pyportfolioopt.readthedocs.io/en/latest/index.html) package.
### max_sharpe
The sharpe ratio is defined as
(Mean Returns - Risk Free Rate)/(Standard Deviation of Returns)
The usage is:
````
max_sharpe [-p PERIOD] [-v --value VALUE] [--pie]
````
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
### min_vol
This portfolio minimizes the total volatility, which also means it has the smallest returns among the efficient frontier.
The usage is:
````
min_vol [-p PERIOD] [-v --value VALUE] [--pie]
````
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
### eff_risk
This portfolio maximizes the returns at a given risk tolerance
The usage is:
````
eff_risk [-p PERIOD] [-r --risk RISK_LEVEL] [-v --value VALUE] [--pie]
````
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
* -r/--risk Risk tolerance. Default is 0.1 (10%)
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
### eff_ret
This portfolio minimizes the risk at a given return level
The usage is:
````
eff_ret [-p PERIOD] [-r --return] [-v --value VALUE] [--pie]
````
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
* -r/--return. Desired return. Default is 0.1 (10%)
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
* --pie Flag that displays a pie chart of the allocations.
### show_eff
This function plots random portfolios based on their risk and returns and shows the efficient frontier.
The usage is:
````
show_eff [-p PERIOD] [-n N_PORTFOLIOS]
````
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
* -n Number of portfolios to simulate.
##Sample Usage
In this example, we generate weights for a list of 6 stocks using the eff_ret command. This optimization looks to maximize returns
at a given risk level. We start by adding the stocks we want to analyze:
````
select aapl,amzn,msft,f,gm,ge
````
Which shows:
````
Current Tickers: GE, GM, AMZN, AAPL, F, MSFT
````
To perform the optimization, we will set a target return of 25% (.25). Given how stocks performed during COVID,
there is a lot of volatility, so many optimizations may not get low volatility. The module also returns annualized volatility,
so the number is your portfolio volatility * `sqrt(252`. This optimization will be (including a pie chart!). Note we could also supply a different
time period, which changes the expected returns and historical volatility, which changes the optimization. We could also specify a dollar
amount that you wish to allocate using the `-v` flag.
````
eff_ret -r .25 --pie
````
The console will show (numbers will vary based on when this is done).
![console](https://user-images.githubusercontent.com/18151143/114740311-bd429c80-9d17-11eb-90e2-97430781431a.png)
And the pie chart:
![yummypie](https://user-images.githubusercontent.com/18151143/114740289-b9167f00-9d17-11eb-9c29-470785b21d09.png)
Note that since `AAPL` had zero allocation, it was omitted from the chart.

View File

@@ -0,0 +1,226 @@
""" Portfolio Optimization Helper Functions"""
__docformat__ = "numpy"
import argparse
from typing import List
import math
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import yfinance as yf
from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt import risk_models
from pypfopt import expected_returns
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal.helper_funcs import plot_autoscale, parse_known_args_and_warn
title_maps = {
"max_sharpe": "Maximum Sharpe Portfolio",
"min_volatility": "Minimum Volatility Portfolio",
"eff_risk": "Maximum Return Portfolio at Risk = {:1f} %",
"eff_ret": "Minimum Volatility Portfolio at Target Return = {:.1f} %",
"equal": "Equally Weighted Portfolio",
"marketCap": "MarketCap Weighted Portfolio",
"dividendYield": "Dividend Yield Weighted Portfolio",
}
def process_stocks(list_of_stocks: List[str], period: str = "3mo") -> pd.DataFrame:
"""
Parameters
----------
list_of_stocks: List[str]
List of tickers to get historical data for
period: str
Period to get data from yfinance
Returns
-------
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"
)
stock_closes = pd.DataFrame(index=stock_prices.index)
for stock in list_of_stocks:
stock_closes[stock] = stock_prices[stock]["Adj Close"]
return stock_closes
def prepare_efficient_frontier(stock_prices: pd.DataFrame):
"""
Take in a dataframe of prices and return an efficient frontier object
Parameters
----------
stock_prices : DataFrame
DataFrame where indices are DateTime and columns are stocks
Returns
-------
ef: EfficientFrontier
EfficientFrontier object
"""
mu = expected_returns.mean_historical_return(stock_prices)
S = risk_models.sample_cov(stock_prices)
ef = EfficientFrontier(mu, S)
return ef
def display_weights(weights: dict):
"""
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"])
if math.isclose(weight_df.sum()["value"], 1, rel_tol=0.1):
weight_df["weight"] = (weight_df["value"] * 100).astype(str).apply(
lambda s: s[:6]
) + " %"
print(pd.DataFrame(weight_df["weight"]))
else:
print(weight_df)
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 ""
def pie_chart_weights(weights: dict, optimizer: str, value: float):
"""
Show a pie chart of holdings
Parameters
----------
weights: dict
weights to display. Keys are stocks. Values are either weights or values if -v specified
optimzer: str
Optimization technique used for title
"""
plt.close("all")
if not weights:
return
init_stocks = list(weights.keys())
init_sizes = list(weights.values())
stocks = []
sizes = []
for stock, size in zip(init_stocks, init_sizes):
if size > 0:
stocks.append(stock)
sizes.append(size)
total_size = np.sum(sizes)
leg_labels = [
f"{str(a)}: {str(round(100*b/total_size,3))[:4]}%"
for a, b in zip(stocks, sizes)
]
plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
if math.isclose(sum(sizes), 1, rel_tol=0.1):
wedges, _, autotexts = plt.pie(
sizes,
labels=stocks,
autopct=my_autopct,
textprops=dict(color="k"),
explode=[s / (5 * total_size) for s in sizes],
normalize=True,
shadow=True,
)
else:
wedges, _, autotexts = plt.pie(
sizes,
labels=stocks,
autopct="",
textprops=dict(color="k"),
explode=[s / (5 * total_size) for s in sizes],
normalize=True,
shadow=True,
)
for i, a in enumerate(autotexts):
if sizes[i] / total_size > 0.05:
a.set_text(f"{sizes[i]:.2f}")
else:
a.set_text("")
plt.axis("equal")
plt.legend(
wedges,
leg_labels,
title="Stocks",
loc="center left",
bbox_to_anchor=(0.85, 0, 0.5, 1),
)
plt.setp(autotexts, size=8, weight="bold")
if optimizer in ["eff_ret", "eff_risk"]:
plt.title(title_maps[optimizer].format(100 * value))
else:
plt.title(title_maps[optimizer])
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
def parse_from_port_type(
parser_in: argparse.ArgumentParser, port_type: str, other_args: List[str]
):
"""
Parameters
----------
parser_in: ArgumentParser
parser to get data from
port_type: str
Type of optimization that will be done. One of ["max_sharpe","min_vol", "eff_risk", "eff_ret"]
other_args: List[str]
Arguments passed to function
Returns
-------
ns_parser:
Parsed arguments
"""
if port_type in ["max_sharpe", "min_volatility"]:
ns_parser = parse_known_args_and_warn(parser_in, other_args)
if not ns_parser:
return None
return ns_parser
elif port_type == "eff_risk":
parser_in.add_argument(
"-r", "--risk", type=float, dest="risk_level", default=0.1
)
ns_parser = parse_known_args_and_warn(parser_in, other_args)
if not ns_parser:
return None
return ns_parser
elif port_type == "eff_ret":
parser_in.add_argument(
"-r", "--return", type=float, dest="target_return", default=0.1
)
ns_parser = parse_known_args_and_warn(parser_in, other_args)
if not ns_parser:
return None
return ns_parser
else:
print("Incorrect portfolio optimizer type\n")
return None

View File

@@ -0,0 +1,337 @@
""" Portfolio Optimization Functions"""
__docformat__ = "numpy"
import argparse
from typing import List
import matplotlib.pyplot as plt
import numpy as np
import yfinance as yf
from pypfopt import plotting
from pypfopt import risk_models
from pypfopt import expected_returns
from pypfopt import EfficientFrontier
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale
from gamestonk_terminal.portfolio_optimization.optimizer_helper import (
process_stocks,
prepare_efficient_frontier,
pie_chart_weights,
display_weights,
parse_from_port_type,
)
from gamestonk_terminal.config_plot import PLOT_DPI
from gamestonk_terminal import feature_flags as gtff
period_choices = [
"1d",
"5d",
"1mo",
"3mo",
"6mo",
"1y",
"2y",
"5y",
"10y",
"ytd",
"max",
]
def equal_weight(stocks: List[str], other_args: List[str]):
"""
Equally weighted portfolio, where weight = 1/# of stocks
Parameters
----------
stocks: List[str]
List of tickers to be included in optimization
Returns
-------
weights : dict
Dictionary of weights where keys are the tickers
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="equal_weight",
description="Return equally weighted portfolio holdings",
)
parser.add_argument(
"-v",
"--value",
default=1,
type=float,
dest="value",
help="Amount to allocate to portfolio",
)
parser.add_argument(
"--pie",
action="store_true",
dest="pie",
default=False,
help="Display a pie chart for weights",
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
values = {}
n_stocks = len(stocks)
for stock in stocks:
values[stock] = ns_parser.value * round(1 / n_stocks, 5)
if ns_parser.pie:
pie_chart_weights(values, "equal", 0)
if n_stocks >= 1:
print("Equal Weight Portfolio: ")
display_weights(values)
print("")
except Exception as e:
print(e)
print("")
def property_weighting(stocks: List[str], property_type: str, other_args: List[str]):
"""
Property weighted portfolio where each weight is the relative fraction.
Parameters
----------
stocks: List[str]
List of tickers to be included in optimization
property_type: str
Property to weight by. Can be anything in yfinance.Ticker().info. Examples:
"marketCap", "dividendYield", etc
Returns
-------
weights: dict
Dictionary of weights where keys are the tickers
"""
parser = argparse.ArgumentParser(
add_help=False,
prog="market_cap_weighted",
description="Return portfolio weights/values that are weighted by marketcap",
)
parser.add_argument(
"-v",
"--value",
default=1,
type=float,
dest="value",
help="Amount to allocate to portfolio",
)
parser.add_argument(
"--pie",
action="store_true",
dest="pie",
default=False,
help="Display a pie chart for weights",
)
weights = {}
prop = {}
prop_sum = 0
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
for stock in stocks:
stock_prop = yf.Ticker(stock).info[property_type]
if stock_prop is None:
stock_prop = 0
prop[stock] = stock_prop
prop_sum += stock_prop
for k, v in prop.items():
weights[k] = round(v / prop_sum, 5) * ns_parser.value
if ns_parser.pie:
pie_chart_weights(weights, property_type, 0)
if property_type == "marketCap":
print("Market Cap Weighted Portfolio: ")
elif property_type == "dividendYield":
print("Dividend Yield Weighted Portfolio: ")
if len(stocks) >= 1:
display_weights(weights)
print("")
except Exception as e:
print(e)
print("")
return
def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
"""
Return a portfolio based on condition in port_type Defaults to 3m of historical data
Parameters
----------
stocks: List[str]
List of the stocks to be included in the weights
port_type: str
Method to be used on ef object (example: max_sharpe, min_volatility)
Returns
-------
weights: dict
Dictionary of weights where keys are the tickers.
"""
parser = argparse.ArgumentParser(add_help=False, prog=port_type)
parser.add_argument(
"-p",
"--period",
default="3mo",
dest="period",
help="period to get yfinance data from",
choices=period_choices,
)
parser.add_argument(
"-v",
"--value",
dest="value",
help="Amount to allocate to portfolio",
type=float,
default=1.0,
)
parser.add_argument(
"--pie",
action="store_true",
dest="pie",
default=False,
help="Display a pie chart for weights",
)
try:
ns_parser = parse_from_port_type(parser, port_type, other_args)
if not ns_parser:
return
if len(stocks) < 2:
print("Please have at least 2 loaded tickers to calculate weights.\n")
return
period = ns_parser.period
stock_prices = process_stocks(stocks, period)
ef = prepare_efficient_frontier(stock_prices)
if port_type == "max_sharpe":
ef_sharpe = dict(ef.max_sharpe())
weights = {
key: ns_parser.value * round(value, 5)
for key, value in ef_sharpe.items()
}
val = 0
print("Weights that maximize Sharpe Ratio:")
elif port_type == "min_volatility":
ef_min_vol = dict(ef.min_volatility())
weights = {
key: ns_parser.value * round(value, 5)
for key, value in ef_min_vol.items()
}
val = 0
print("Weights that minimize volatility")
elif port_type == "eff_risk":
ef_eff_risk = dict(ef.efficient_risk(ns_parser.risk_level))
weights = {
key: ns_parser.value * round(value, 5)
for key, value in ef_eff_risk.items()
}
val = ns_parser.risk_level
print(f"Weights for maximizing returns at risk = {100*val:.1f} %")
elif port_type == "eff_ret":
ef_eff_risk = dict(ef.efficient_return(ns_parser.target_return))
weights = {
key: ns_parser.value * round(value, 5)
for key, value in ef_eff_risk.items()
}
val = ns_parser.target_return
print(f"Weights for minimizing risk at target return = {100*val:.1f} %")
else:
raise ValueError("EF Method not found")
if ns_parser.pie:
pie_chart_weights(weights, port_type, val)
print("")
ef.portfolio_performance(verbose=True)
print("")
display_weights(weights)
print("")
except Exception as e:
print(e)
print("")
return
def show_ef(stocks: List[str], other_args: List[str]):
parser = argparse.ArgumentParser(add_help=False, prog="ef")
parser.add_argument(
"-p",
"--period",
default="3mo",
dest="period",
help="period to get yfinance data from",
choices=period_choices,
)
parser.add_argument(
"-n", default=300, dest="n_port", help="number of portfolios to simulate"
)
try:
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
if len(stocks) < 2:
print("Please have at least 2 loaded tickers to calculate weights.\n")
return
stock_prices = process_stocks(stocks, ns_parser.period)
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)
# Generate random portfolios
n_samples = ns_parser.n_port
w = np.random.dirichlet(np.ones(len(mu)), n_samples)
rets = w.dot(mu)
stds = np.sqrt(np.diag(w @ S @ w.T))
sharpes = rets / stds
ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")
plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True)
# Find the tangency portfolio
ef.max_sharpe()
ret_sharpe, std_sharpe, _ = ef.portfolio_performance()
ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe")
ax.set_title("Efficient Frontier")
ax.legend()
plt.tight_layout()
plt.grid(b=True, which="major", color="#666666", linestyle="-")
plt.minorticks_on()
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
if gtff.USE_ION:
plt.ion()
plt.show()
print("")
except Exception as e:
print(e)
print("")

View File

@@ -0,0 +1,206 @@
"""Portfolio Optimization Controller Module"""
__docformat__ = "numpy"
import argparse
from typing import List
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, parse_known_args_and_warn
from gamestonk_terminal.menu import session
from gamestonk_terminal.portfolio_optimization import optimizer_view as po_api
class PortfolioOptimization:
CHOICES = [
"help",
"q",
"quit",
"select",
"add",
"equal_weight",
"mkt_cap",
"div_yield",
"max_sharpe",
"min_vol",
"eff_risk",
"eff_ret",
"show_ef",
"yolo",
]
def __init__(self, tickers: List[str] = []):
"""
Construct Portfolio Optimization
"""
self.po_parser = argparse.ArgumentParser(add_help=False, prog="po")
self.po_parser.add_argument("cmd", choices=self.CHOICES)
self.tickers = list(set(tickers))
# These will allow the ca menu to be re-access
self.ca_ticker = None
self.ca_similar = None
@staticmethod
def print_help(tickers: List[str]):
"""Print help"""
print("\nPortfolio Optimization:")
print(" help show this menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print(f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}")
print("")
print(" add add ticker to optimize")
print(" select overwrite current tickers with new tickers")
print("")
print("Optimization:")
print("")
print(" equal_weight equally weighted portfolio")
print(" mkt_cap marketcap weighted portfolio")
print(" div_yield dividend weighted portfolio")
print(" max_sharpe portfolio with maximum sharpe ratio")
print(" min_vol portfolio with minimum volatility")
print(" eff_risk portfolio that maximizes returns at given risk")
print(" eff_ret portfolio that minimizes risk at given return")
print(" show_ef show the efficient frontier")
print("")
plt.close("all")
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.po_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.tickers)
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_add(self, other_args: List[str]):
self.add_stocks(self, other_args)
def call_select(self, other_args: List[str]):
self.tickers = []
self.add_stocks(self, other_args)
def call_equal_weight(self, other_args: List[str]):
po_api.equal_weight(self.tickers, other_args)
def call_mkt_cap(self, other_args: List[str]):
po_api.property_weighting(self.tickers, "marketCap", other_args)
def call_div_yield(self, other_args: List[str]):
po_api.property_weighting(self.tickers, "dividendYield", other_args)
def call_max_sharpe(self, other_args: List[str]):
po_api.ef_portfolio(self.tickers, "max_sharpe", other_args)
def call_min_vol(self, other_args: List[str]):
po_api.ef_portfolio(self.tickers, "min_volatility", other_args)
def call_eff_risk(self, other_args: List[str]):
po_api.ef_portfolio(self.tickers, "eff_risk", other_args)
def call_eff_ret(self, other_args: List[str]):
po_api.ef_portfolio(self.tickers, "eff_ret", other_args)
def call_show_ef(self, other_args):
po_api.show_ef(self.tickers, other_args)
print("")
def call_yolo(self, _):
# Easter egg :)
print("DFV YOLO")
print({"GME": 200})
print("")
@staticmethod
def add_stocks(self, other_args: List[str]):
""" Add ticker to current list for optimization"""
parser = argparse.ArgumentParser(
add_help=False,
prog="add",
description="""Add tickers for optimizing.""",
)
parser.add_argument(
"-t",
"--tickers",
dest="add_tickers",
type=lambda s: [str(item).upper() for item in s.split(",")],
default=[],
help="add tickers to optimzation.",
)
try:
if other_args:
if "-" not in other_args[0]:
other_args.insert(0, "-t")
ns_parser = parse_known_args_and_warn(parser, other_args)
if not ns_parser:
return
tickers = set(self.tickers)
for ticker in ns_parser.add_tickers:
tickers.add(ticker)
self.tickers = list(tickers)
print(
f"\nCurrent Tickers: {('None', ', '.join(self.tickers))[bool(self.tickers)]}"
)
print("")
except Exception as e:
print(e)
print("")
def menu(tickers: List[str]):
"""Portfolio Optimization Menu"""
if tickers == [""]:
tickers = []
plt.close("all")
po_controller = PortfolioOptimization(tickers)
po_controller.call_help(tickers)
while True:
# Get input command from user
if session and gtff.USE_PROMPT_TOOLKIT:
completer = NestedCompleter.from_nested_dict(
{c: None for c in po_controller.CHOICES}
)
an_input = session.prompt(
f"{get_flair()} (po)> ",
completer=completer,
)
else:
an_input = input(f"{get_flair()} (po)> ")
try:
process_input = po_controller.switch(an_input)
if process_input is not None:
return process_input
except SystemExit:
print("The command selected doesn't exist\n")
continue

View File

@@ -0,0 +1,207 @@
import argparse
from typing import List
import pandas as pd
from datetime import datetime
import matplotlib.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 gamestonk_terminal.prediction_techniques import (
arima,
ets,
knn,
neural_networks,
regression,
sma,
)
from prompt_toolkit.completion import NestedCompleter
class PredictionTechniquesController:
"""Prediction Techniques Controller class"""
# Command choices
CHOICES = [
"help",
"q",
"quit",
"sma",
"ets",
"knn",
"linear",
"quadratic",
"cubic",
"regression",
"arima",
"mlp",
"rnn",
"lstm",
]
if gtff.ENABLE_FBPROPHET:
CHOICES.append("prophet")
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.pred_parser = argparse.ArgumentParser(add_help=False, prog="pred")
self.pred_parser.add_argument(
"cmd",
choices=self.CHOICES,
)
@staticmethod
def print_help(self):
""" Print help """
s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"]
if self.start:
print(
f"\n{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
)
else:
print(f"\n{s_intraday} Stock: {self.ticker}")
print("\nPrediction Techniques:")
print(" help show this prediction techniques menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print("")
print(" sma simple moving average")
print(" ets exponential smoothing (e.g. Holt-Winters)")
print(" knn k-Nearest Neighbors")
print(" linear linear regression (polynomial 1)")
print(" quadratic quadratic regression (polynomial 2)")
print(" cubic cubic regression (polynomial 3)")
print(" regression regression (other polynomial)")
print(" arima autoregressive integrated moving average")
print(" mlp MultiLayer Perceptron")
print(" rnn Recurrent Neural Network")
print(" lstm Long-Short Term Memory")
if gtff.ENABLE_FBPROPHET:
print(" prophet Facebook's prophet prediction")
print("")
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.pred_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_sma(self, other_args: List[str]):
"""Process sma command"""
sma.simple_moving_average(other_args, self.ticker, self.stock)
def call_ets(self, other_args: List[str]):
"""Process ets command"""
ets.exponential_smoothing(other_args, self.ticker, self.stock)
def call_knn(self, other_args: List[str]):
"""Process knn command"""
knn.k_nearest_neighbors(other_args, self.ticker, self.stock)
def call_linear(self, other_args: List[str]):
"""Process linear command"""
regression.regression(other_args, self.ticker, self.stock, regression.LINEAR)
def call_quadratic(self, other_args: List[str]):
"""Process quadratic command"""
regression.regression(other_args, self.ticker, self.stock, regression.QUADRATIC)
def call_cubic(self, other_args: List[str]):
"""Process cubic command"""
regression.regression(other_args, self.ticker, self.stock, regression.CUBIC)
def call_regression(self, other_args: List[str]):
"""Process regression command"""
regression.regression(
other_args, self.ticker, self.stock, regression.USER_INPUT
)
def call_arima(self, other_args: List[str]):
"""Process arima command"""
arima.arima(other_args, self.ticker, self.stock)
def call_mlp(self, other_args: List[str]):
"""Process mlp command"""
neural_networks.mlp(other_args, self.ticker, self.stock)
def call_rnn(self, other_args: List[str]):
"""Process rnn command"""
neural_networks.rnn(other_args, self.ticker, self.stock)
def call_lstm(self, other_args: List[str]):
"""Process lstm command"""
neural_networks.lstm(other_args, self.ticker, self.stock)
if gtff.ENABLE_FBPROPHET:
def call_prophet(self, other_args: List[str]):
"""Process prophet command"""
from gamestonk_terminal.prediction_techniques import fbprophet
fbprophet.fbprophet(other_args, self.ticker, self.stock)
def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str):
"""Comparison Analysis Menu"""
pred_controller = PredictionTechniquesController(stock, ticker, start, interval)
pred_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 pred_controller.CHOICES}
)
an_input = session.prompt(
f"{get_flair()} (pred)> ",
completer=completer,
)
else:
an_input = input(f"{get_flair()} (pred)> ")
try:
plt.close("all")
process_input = pred_controller.switch(an_input)
if process_input is not None:
return process_input
except SystemExit:
print("The command selected doesn't exist\n")
continue

View File

@@ -1,145 +0,0 @@
import argparse
import matplotlib.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 gamestonk_terminal.prediction_techniques import (
arima,
ets,
fbprophet,
knn,
neural_networks,
regression,
sma,
)
from prompt_toolkit.completion import NestedCompleter
def print_prediction(s_ticker, s_start, s_interval):
""" Print help """
s_intraday = (f"Intraday {s_interval}", "Daily")[s_interval == "1440min"]
if s_start:
print(f"\n{s_intraday} Stock: {s_ticker} (from {s_start.strftime('%Y-%m-%d')})")
else:
print(f"\n{s_intraday} Stock: {s_ticker}")
print("\nPrediction Techniques:")
print(" help show this prediction techniques menu again")
print(" q quit this menu, and shows back to main menu")
print(" quit quit to abandon program")
print("")
print(" sma simple moving average")
print(" ets exponential smoothing (e.g. Holt-Winters)")
print(" knn k-Nearest Neighbors")
print(" linear linear regression (polynomial 1)")
print(" quadratic quadratic regression (polynomial 2)")
print(" cubic cubic regression (polynomial 3)")
print(" regression regression (other polynomial)")
print(" arima autoregressive integrated moving average")
print(" prophet Facebook's prophet prediction")
print(" mlp MultiLayer Perceptron")
print(" rnn Recurrent Neural Network")
print(" lstm Long-Short Term Memory")
print("")
def pred_menu(df_stock, s_ticker, s_start, s_interval):
# Add list of arguments that the prediction techniques parser accepts
pred_parser = argparse.ArgumentParser(prog="pred", add_help=False)
choices = [
"help",
"q",
"quit",
"sma",
"ets",
"knn",
"linear",
"quadratic",
"cubic",
"regression",
"arima",
"prophet",
"mlp",
"rnn",
"lstm",
]
pred_parser.add_argument("cmd", choices=choices)
completer = NestedCompleter.from_nested_dict({c: None for c in choices})
print_prediction(s_ticker, s_start, s_interval)
# Loop forever and ever
while True:
# Get input command from user
if session and gtff.USE_PROMPT_TOOLKIT:
as_input = session.prompt(
f"{get_flair()} (pred)> ",
completer=completer,
)
else:
as_input = input(f"{get_flair()} (pred)> ")
# Images are non blocking - allows to close them if we type other command
plt.close("all")
# Parse prediction techniques command of the list of possible commands
try:
(ns_known_args, l_args) = pred_parser.parse_known_args(as_input.split())
except SystemExit:
print("The command selected doesn't exist\n")
continue
if ns_known_args.cmd == "help":
print_prediction(s_ticker, s_start, s_interval)
elif ns_known_args.cmd == "q":
# Just leave the FA menu
return False
elif ns_known_args.cmd == "quit":
# Abandon the program
return True
elif ns_known_args.cmd == "sma":
sma.simple_moving_average(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "ets":
ets.exponential_smoothing(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "knn":
knn.k_nearest_neighbors(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "linear":
regression.regression(l_args, s_ticker, df_stock, regression.LINEAR)
elif ns_known_args.cmd == "quadratic":
regression.regression(l_args, s_ticker, df_stock, regression.QUADRATIC)
elif ns_known_args.cmd == "cubic":
regression.regression(l_args, s_ticker, df_stock, regression.CUBIC)
elif ns_known_args.cmd == "regression":
regression.regression(l_args, s_ticker, df_stock, regression.USER_INPUT)
elif ns_known_args.cmd == "arima":
arima.arima(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "prophet":
fbprophet.fbprophet(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "mlp":
neural_networks.mlp(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "rnn":
neural_networks.rnn(l_args, s_ticker, df_stock)
elif ns_known_args.cmd == "lstm":
neural_networks.lstm(l_args, s_ticker, df_stock)
else:
print("Command not recognized!")

View File

@@ -23,7 +23,8 @@ This menu aims to filter stocks based on pre-specified preset filters, and the u
* contains [Oversold (-s) signal example](#signal-oversold)
* [signals](#signals)
* view filter signals (e.g. -s top_gainers) [Finviz]
* [po](#port_opt)
* go to the portfolio optimization menu
## view <a name="view"></a>
@@ -180,3 +181,13 @@ Prints list of available signals. [Source: Finviz]
<img width="937" alt="Captura de ecrã 2021-04-05, às 20 25 13" src="https://user-images.githubusercontent.com/25267873/113616495-0ece9580-964d-11eb-97af-4150f928a170.png">
## po <a name="port_opt"></a>
Goes to the portfolio menu with list of passed stocks. In order to pass the stocks, just type the tickers of interest, no commas.
```
usage: po ticker1 ticker2 ...
```
````
example: po aapl msft tsla gme
````

View File

@@ -14,6 +14,7 @@ from gamestonk_terminal.helper_funcs import (
)
from gamestonk_terminal.screener import finviz_view
from gamestonk_terminal.screener import yahoo_finance_view
from gamestonk_terminal.portfolio_optimization import po_controller
class ScreenerController:
@@ -34,6 +35,7 @@ class ScreenerController:
"performance",
"technical",
"signals",
"po",
]
def __init__(self):
@@ -69,6 +71,8 @@ class ScreenerController:
print("")
print(" signals view filter signals (e.g. -s top_gainers)")
print("")
print(" > po go to the portfolio optimization menu")
print("")
@staticmethod
def view_available_presets(other_args: List[str]):
@@ -217,6 +221,10 @@ class ScreenerController:
"""Process signals command"""
finviz_view.view_signals(other_args)
def call_po(self, other_args: List[str]):
"""Call the portfolio optimization menu"""
return po_controller.menu(other_args)
def menu():
"""Screener Menu"""

View File

@@ -68,7 +68,7 @@
"outputs": [],
"source": [
"from gamestonk_terminal.technical_analysis import trendline_api as trend\n",
"from gamestonk_terminal.due_diligence import finviz_api as finviz"
"from gamestonk_terminal.due_diligence import finviz_view as finviz"
]
},
{

173
poetry.lock generated
View File

@@ -379,6 +379,21 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "cvxpy"
version = "1.1.12"
description = "A domain-specific language for modeling convex optimization problems in Python."
category = "main"
optional = false
python-versions = ">=3.5"
[package.dependencies]
ecos = ">=2"
numpy = ">=1.15"
osqp = ">=0.4.1"
scipy = ">=1.1.0"
scs = ">=1.1.6"
[[package]]
name = "cycler"
version = "0.10.0"
@@ -456,6 +471,18 @@ category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "ecos"
version = "2.0.7.post1"
description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
numpy = ">=1.6"
scipy = ">=0.9"
[[package]]
name = "entrypoints"
version = "0.3"
@@ -1491,6 +1518,19 @@ numpy = ">=1.7"
docs = ["sphinx (==1.2.3)", "sphinxcontrib-napoleon", "sphinx-rtd-theme", "numpydoc"]
tests = ["pytest", "pytest-cov", "pytest-pep8"]
[[package]]
name = "osqp"
version = "0.6.2.post0"
description = "OSQP: The Operator Splitting QP Solver"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
numpy = ">=1.7"
qdldl = "*"
scipy = ">=0.13.2"
[[package]]
name = "overrides"
version = "3.1.0"
@@ -1930,6 +1970,23 @@ category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "pyportfolioopt"
version = "1.4.1"
description = "Financial portfolio optimization in python"
category = "main"
optional = false
python-versions = ">=3.6.1,<4.0.0"
[package.dependencies]
cvxpy = ">=1.1.10,<2.0.0"
numpy = ">=1.12,<2.0"
pandas = ">=0.19"
scipy = ">=1.3,<2.0"
[package.extras]
optionals = ["cvxopt (>=1.2,!=1.2.5.post1,<2.0)", "scikit-learn (>=0.24.1,<0.25.0)", "matplotlib (>=3.2.0,<4.0.0)"]
[[package]]
name = "pyrsistent"
version = "0.14.11"
@@ -2073,6 +2130,18 @@ python-versions = ">=3.6"
cffi = {version = "*", markers = "implementation_name == \"pypy\""}
py = {version = "*", markers = "implementation_name == \"pypy\""}
[[package]]
name = "qdldl"
version = "0.1.5.post0"
description = "QDLDL, a free LDL factorization routine."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
numpy = ">=1.7"
scipy = ">=0.13.2"
[[package]]
name = "qtconsole"
version = "5.0.3"
@@ -2265,6 +2334,18 @@ Cython = {version = "*", markers = "sys_platform == \"darwin\""}
dataclasses = {version = "*", markers = "python_version < \"3.7\""}
pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""}
[[package]]
name = "scs"
version = "2.1.2"
description = "scs: splitting conic solver"
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
numpy = ">=1.7"
scipy = ">=0.13.2"
[[package]]
name = "seaborn"
version = "0.11.1"
@@ -2837,7 +2918,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t
[metadata]
lock-version = "1.1"
python-versions = "^3.6.8"
content-hash = "144771641ef753b1e937c0db16496c409905f6f692ef81a03d9ba716d9207bb0"
content-hash = "5b3abe79834fc75092d1d9e8e3a809252f56eddc170fb61a93c36ffef9328b73"
[metadata.files]
absl-py = [
@@ -3063,6 +3144,20 @@ cssselect = [
{file = "cssselect-1.1.0-py2.py3-none-any.whl", hash = "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf"},
{file = "cssselect-1.1.0.tar.gz", hash = "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"},
]
cvxpy = [
{file = "cvxpy-1.1.12-cp36-cp36m-win_amd64.whl", hash = "sha256:86f8e4aa1a79482431b89aa526249d9fda04c74e04a3ec224d1175d281643d71"},
{file = "cvxpy-1.1.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:90b09a7361b9c614166fa673e1fe9321317df6939135330e1621b9a0bf10e3a8"},
{file = "cvxpy-1.1.12-cp37-cp37m-win_amd64.whl", hash = "sha256:084e5292f2a13133a1cd28d68f5af1df33a5119b0f1aec7cecf834eea3e2d11d"},
{file = "cvxpy-1.1.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:575f6e1c70274008961edff97a7494701faed20b40fbd49327010c0e7d186256"},
{file = "cvxpy-1.1.12-cp38-cp38-win_amd64.whl", hash = "sha256:76d04444cb50ad2840bbc72a625ac246b5eeeeb580ee20c71bec057822667738"},
{file = "cvxpy-1.1.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2de5e0e75316839bc71b5df2c2db0131cee7ef06dac566c6c44dc852c091502b"},
{file = "cvxpy-1.1.12-cp39-cp39-win_amd64.whl", hash = "sha256:9fa4d033ab31855aea090e4b358017b36880390a7d0e6e97ecc5226c77eab489"},
{file = "cvxpy-1.1.12-py3.6-win-amd64.egg", hash = "sha256:d2c28e0eea1b6dc70262c9ed8b63c5c1747d85c28b3ec24cbaab07583b3ad729"},
{file = "cvxpy-1.1.12-py3.7-win-amd64.egg", hash = "sha256:44d0e3d60693831ac70bdc9b2e23faf84401347bfcdef4eb5da5ae22ac57f5ff"},
{file = "cvxpy-1.1.12-py3.8-win-amd64.egg", hash = "sha256:c86f8de9a63ccbd45844a4482df8099824688b5329060f089aad1c3beb939190"},
{file = "cvxpy-1.1.12-py3.9-win-amd64.egg", hash = "sha256:08c720706abd94c25903a4db5dd2171232e72d0969b874a5de9a8181f96357d5"},
{file = "cvxpy-1.1.12.tar.gz", hash = "sha256:b499ebfae4fc645e9523621573ffcb1c5b6854a1b5c0ce1d66a8a9a458605807"},
]
cycler = [
{file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"},
{file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"},
@@ -3126,6 +3221,30 @@ detecta = [
{file = "detecta-0.0.5-py3-none-any.whl", hash = "sha256:dbaf1938e5d785386c904034e2553824e2aa31793e967984ca65983aca06d23e"},
{file = "detecta-0.0.5.tar.gz", hash = "sha256:d2ea7d13dfbbc994d6ce385a7f8dc0a85fe675a8a8e712a64ec56e54c40603ed"},
]
ecos = [
{file = "ecos-2.0.7.post1-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:dd9f01e28fe58894fb394931804884122606fb4e2a59d4514b803e9cd11b7d2b"},
{file = "ecos-2.0.7.post1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:db7433051f6072d4821ebc582e9ff853d7d631ed98770550d248eae70b29dd26"},
{file = "ecos-2.0.7.post1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:96ddc1c4e440820bb343c44785da480a64a9c468ec467b997e40f0c7d3236226"},
{file = "ecos-2.0.7.post1-cp27-cp27m-win32.whl", hash = "sha256:574fa26661d192e48551a30e217296875020fdb7eebf9a072a14d68a0b9de03a"},
{file = "ecos-2.0.7.post1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7c871b7a49a5855e7df0d60a06d0e2148a4b183d612f7db5e155988629c3ec21"},
{file = "ecos-2.0.7.post1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:845455f99cd579ee0cdfbcea675b4e4f7674b563e6a54a225977fc0819cda7e0"},
{file = "ecos-2.0.7.post1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:e002df0f4b6777be68c73756e60f3cf76cb5f2f7d36c4f1a482c5538aac1a287"},
{file = "ecos-2.0.7.post1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:feda86ddd191b1ae34d5ea615743894d1baa800f2dbb552cb7c7095a87037831"},
{file = "ecos-2.0.7.post1-cp34-cp34m-win32.whl", hash = "sha256:6b0829b76ba49f6ebf8b9512673a0b702b756704927cbb106043e1b2273dadea"},
{file = "ecos-2.0.7.post1-cp34-cp34m-win_amd64.whl", hash = "sha256:54f7c480029fbfa738ddb8e538388868b2a17c9189a426d924ca0c36e4cbbbd5"},
{file = "ecos-2.0.7.post1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1f811e7244a58a7037474b3dead85c7ffd524cf631c90584cc544e438f114cf9"},
{file = "ecos-2.0.7.post1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:72657189f71dbde01d1df841d2139e04da17071c8d3270919c8501f239d8c8e4"},
{file = "ecos-2.0.7.post1-cp35-cp35m-win32.whl", hash = "sha256:30e7e9c5ad8a012ba1f69aa6827beb99f208323f678a82fcccc30e4ab8090aad"},
{file = "ecos-2.0.7.post1-cp35-cp35m-win_amd64.whl", hash = "sha256:94dd0f82a18550232e4924e6c42730c46d7cdc03c4e2dc889e98ec97c0f24061"},
{file = "ecos-2.0.7.post1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:361758a3568eb5a3a9b37c00a22b36f9548bbd0cd21f1da904a3627285bb4274"},
{file = "ecos-2.0.7.post1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:831acb6bac205025ffe87002d8f425d2764a70db3d9d053a1f7e0e50bc2a18b9"},
{file = "ecos-2.0.7.post1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:912b17f72476aff33b32e92decd7c02ddb929db28227563b3750948783cff6f4"},
{file = "ecos-2.0.7.post1-cp36-cp36m-win32.whl", hash = "sha256:4fdfee011853cd07d494ef58a9299b1f0ccf0268c375109cb2b60727f746ea74"},
{file = "ecos-2.0.7.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:fb64fb29aef26474f807df4f0c198a6d192291edc9faa3bb05e3bc9b1e2b960a"},
{file = "ecos-2.0.7.post1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4b3068ef023c4f39f6c0d6fd73d27d8a8008f8b14e1f4ddd8d0f4876e841b986"},
{file = "ecos-2.0.7.post1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4f775caf828597d094cddae54f7ed12b4a5aa760f535a92ccabec47741e4d61c"},
{file = "ecos-2.0.7.post1.tar.gz", hash = "sha256:83e90f42b3f32e2a93f255c3cfad2da78dbd859119e93844c45d2fca20bdc758"},
]
entrypoints = [
{file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
{file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
@@ -3810,6 +3929,29 @@ opt-einsum = [
{file = "opt_einsum-3.3.0-py3-none-any.whl", hash = "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147"},
{file = "opt_einsum-3.3.0.tar.gz", hash = "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"},
]
osqp = [
{file = "osqp-0.6.2.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:21047e9ceb4c2224ba15dd95f5125e2dcabd178aa93d8efa407e67216ea4fe62"},
{file = "osqp-0.6.2.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:598a7bb1534a0ab69bcaf22d6e6c4ec042a41184f007c01b7fa863b528c6b61d"},
{file = "osqp-0.6.2.post0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:6b745ba55add8be63eaee059858b7c0cf59925074fcc8355c17c3863fad01a95"},
{file = "osqp-0.6.2.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:1104dd56d6bfe5ac5e5e076e1cf9e2be76932cadae4e0cfbdf495f3f43e4f8fa"},
{file = "osqp-0.6.2.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6f89feaf7c30dfb5031b370e8c45aed23a60d6471606bad9b7d23506ddf807e6"},
{file = "osqp-0.6.2.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:198b66cbe9326ef181f7598856aae20dcc5b7824b119cc119b412206e858bc28"},
{file = "osqp-0.6.2.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9aaecd3ed00a99d063e5bd45ebce965d1ebc8f4754d6c05ab5d31c3577a68513"},
{file = "osqp-0.6.2.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:2d150d880dc5bee015aadac6cff32ab0c2721cd4bb8a3f5b1f0cec819fe70b39"},
{file = "osqp-0.6.2.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:20cd6a352694cc182bb79a8e19e4e03b7970ecb4b95d6abf86e3c578a45783a9"},
{file = "osqp-0.6.2.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:96e42524dd156f62ac8e35d71831588acb2919b5ba27e0e695c757539708dfa5"},
{file = "osqp-0.6.2.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:d6dd2b70ffcee6089758518620087a5909a10621a59493be8ce0ef2a74502a27"},
{file = "osqp-0.6.2.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:7c9f7e661fe2da984eab6533a56b5366f10a7360a28b8dd18c222fcf6655e247"},
{file = "osqp-0.6.2.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b102c26b2017b039032797a388548dcfe11dbd3d06b3294721fa65976fe1065e"},
{file = "osqp-0.6.2.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a835746179c81944e51a6a2e0efb0aca66402be351af912bfd682527f15a35a7"},
{file = "osqp-0.6.2.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:57f5026b91bb8f39059576c5ee50c0a1c2705d775a776e2e24be2aa47e63d8b0"},
{file = "osqp-0.6.2.post0-cp38-cp38-win_amd64.whl", hash = "sha256:fb0d77e634362fe59ab2bedba3dde40d5357922e9719bcb4d16a99766cefd5a2"},
{file = "osqp-0.6.2.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e21463d97bcab58350a41a975e9e5e4a685087b6357899a58813d2238795315"},
{file = "osqp-0.6.2.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02fdbabfffd72a8118328d117a83998800ca015182053fced9d5e8be6ffd702a"},
{file = "osqp-0.6.2.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:b238b9049a29f6955f08567b285b0734dd578b529a3780d617854ffd91e5e4ed"},
{file = "osqp-0.6.2.post0-cp39-cp39-win_amd64.whl", hash = "sha256:2c6cb14fd1365004d0e7a01f6101e16414a0348db736ee45aac8865fac165e57"},
{file = "osqp-0.6.2.post0.tar.gz", hash = "sha256:5f0695f26a3bef0fae91254bc283fab790dcca0064bfe0f425167f9c9e8b4cbc"},
]
overrides = [
{file = "overrides-3.1.0.tar.gz", hash = "sha256:30f761124579e59884b018758c4d7794914ef02a6c038621123fec49ea7599c6"},
]
@@ -4069,6 +4211,10 @@ pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
]
pyportfolioopt = [
{file = "PyPortfolioOpt-1.4.1-py3-none-any.whl", hash = "sha256:73f0b4ac2a5d879879a7a91af038b4d7ff82e9998fb2cad7c249302294e8c09e"},
{file = "PyPortfolioOpt-1.4.1.tar.gz", hash = "sha256:e6c8e983db92cd13caf41d87fb4dcd945d37053f28b75dd19b5be699b6b1d19e"},
]
pyrsistent = [
{file = "pyrsistent-0.14.11.tar.gz", hash = "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"},
]
@@ -4212,6 +4358,24 @@ pyzmq = [
{file = "pyzmq-22.0.3-pp37-pypy37_pp73-win32.whl", hash = "sha256:279cc9b51db48bec2db146f38e336049ac5a59e5f12fb3a8ad864e238c1c62e3"},
{file = "pyzmq-22.0.3.tar.gz", hash = "sha256:f7f63ce127980d40f3e6a5fdb87abf17ce1a7c2bd8bf2c7560e1bbce8ab1f92d"},
]
qdldl = [
{file = "qdldl-0.1.5.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:787d59b4301608e96bdf32ab3a572d9f41b3ea08581774826720986e18da261e"},
{file = "qdldl-0.1.5.post0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:223dd49f4aa625c727c3053e9f62814fab2f929e0575b34dd1f38e66b536a849"},
{file = "qdldl-0.1.5.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:cd0090d5512a8e386534a755f9ebd8be82087d401746cebbfb9f9faa21073c39"},
{file = "qdldl-0.1.5.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3e5e2ee6b45dd655120ca8d6645331c800c494ce765bc0674714ba86e1ebec7d"},
{file = "qdldl-0.1.5.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:a2ab0d4618d5516101fc0ce99c873df61a085c3d7cb4d8347d5eca818795e48f"},
{file = "qdldl-0.1.5.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:ac7a2891628554de6260132fa090c5b00cf32ae083ece4991a43b887ec6f2114"},
{file = "qdldl-0.1.5.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:817a226b2450e56c50f61a92b727c69f50a4d043876e5f1bcdff096c5a82bc37"},
{file = "qdldl-0.1.5.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:c10f91dda6a422a6d879668fd293132c8939431fffe64abd9700dce170a3aec4"},
{file = "qdldl-0.1.5.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:e70c57ea65d0bb09708bc9ecafd4fb4b797e64af4da475404e6bcf5a2cc92596"},
{file = "qdldl-0.1.5.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:64f531ef2a12cc77be85045cdffdb2fe339244c73fc07aebe49977541bfc7e79"},
{file = "qdldl-0.1.5.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:cff34b89258a3c9b1b398f1c8eeb480a9c4ae2ba14c4ffc8c78cdb123dab11c6"},
{file = "qdldl-0.1.5.post0-cp38-cp38-win_amd64.whl", hash = "sha256:3a7ec584aaddff7036d22013c911132572f3d59c65ec6bf7e32beb01765accc4"},
{file = "qdldl-0.1.5.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a7c086ae21a2b2e43b31ac6a5024c79dadf16e7c3152eda9e3263cefe1675e26"},
{file = "qdldl-0.1.5.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:2c09f4b1a1c6f3a0579af004443417e084491e7c844ff9fb73170bb5d43f70b5"},
{file = "qdldl-0.1.5.post0-cp39-cp39-win_amd64.whl", hash = "sha256:640cab781f87c2f1c04ada65615b3d1d136d319c4595176f37e2e3c99149108f"},
{file = "qdldl-0.1.5.post0.tar.gz", hash = "sha256:c392c7427651d8b226423c7aba4a0f2338a1f38a4bbdabac6bc4afd8bc934f06"},
]
qtconsole = [
{file = "qtconsole-5.0.3-py3-none-any.whl", hash = "sha256:4a38053993ca2da058f76f8d75b3d8906efbf9183de516f92f222ac8e37d9614"},
{file = "qtconsole-5.0.3.tar.gz", hash = "sha256:c091a35607d2a2432e004c4a112d241ce908086570cf68594176dd52ccaa212d"},
@@ -4359,7 +4523,6 @@ scikit-learn = [
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c658432d8a20e95398f6bb95ff9731ce9dfa343fdf21eea7ec6a7edfacd4b4d9"},
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9dfa564ef27e8e674aa1cc74378416d580ac4ede1136c13dd555a87996e13422"},
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:9c6097b6a9b2bafc5e0f31f659e6ab5e131383209c30c9e978c5b8abdac5ed2a"},
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5580eba7345a4d3b097be2f067cc71a306c44bab19e8717a30361f279c929bea"},
{file = "scikit_learn-0.24.1-cp36-cp36m-win32.whl", hash = "sha256:7b04691eb2f41d2c68dbda8d1bd3cb4ef421bdc43aaa56aeb6c762224552dfb6"},
{file = "scikit_learn-0.24.1-cp36-cp36m-win_amd64.whl", hash = "sha256:1adf483e91007a87171d7ce58c34b058eb5dab01b5fee6052f15841778a8ecd8"},
{file = "scikit_learn-0.24.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:ddb52d088889f5596bc4d1de981f2eca106b58243b6679e4782f3ba5096fd645"},
@@ -4367,7 +4530,6 @@ scikit-learn = [
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0567a2d29ad08af98653300c623bd8477b448fe66ced7198bef4ed195925f082"},
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:99349d77f54e11f962d608d94dfda08f0c9e5720d97132233ebdf35be2858b2d"},
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:83b21ff053b1ff1c018a2d24db6dd3ea339b1acfbaa4d9c881731f43748d8b3b"},
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:abe835a851610f87201819cb315f8d554e1a3e8128912783a31e87264ba5ffb7"},
{file = "scikit_learn-0.24.1-cp37-cp37m-win32.whl", hash = "sha256:c3deb3b19dd9806acf00cf0d400e84562c227723013c33abefbbc3cf906596e9"},
{file = "scikit_learn-0.24.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d54dbaadeb1425b7d6a66bf44bee2bb2b899fe3e8850b8e94cfb9c904dcb46d0"},
{file = "scikit_learn-0.24.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:3c4f07f47c04e81b134424d53c3f5e16dfd7f494e44fd7584ba9ce9de2c5e6c1"},
@@ -4375,7 +4537,6 @@ scikit-learn = [
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4ddd2b6f7449a5d539ff754fa92d75da22de261fd8fdcfb3596799fadf255101"},
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:826b92bf45b8ad80444814e5f4ac032156dd481e48d7da33d611f8fe96d5f08b"},
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:259ec35201e82e2db1ae2496f229e63f46d7f1695ae68eef9350b00dc74ba52f"},
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9599a3f3bf33f73fed0fe06d1dfa4e6081365a58c1c807acb07271be0dce9733"},
{file = "scikit_learn-0.24.1-cp38-cp38-win32.whl", hash = "sha256:8772b99d683be8f67fcc04789032f1b949022a0e6880ee7b75a7ec97dbbb5d0b"},
{file = "scikit_learn-0.24.1-cp38-cp38-win_amd64.whl", hash = "sha256:ed9d65594948678827f4ff0e7ae23344e2f2b4cabbca057ccaed3118fdc392ca"},
{file = "scikit_learn-0.24.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:8aa1b3ac46b80eaa552b637eeadbbce3be5931e4b5002b964698e33a1b589e1e"},
@@ -4383,7 +4544,6 @@ scikit-learn = [
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:087dfede39efb06ab30618f9ab55a0397f29c38d63cd0ab88d12b500b7d65fd7"},
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:895dbf2030aa7337649e36a83a007df3c9811396b4e2fa672a851160f36ce90c"},
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9a24d1ccec2a34d4cd3f2a1f86409f3f5954cc23d4d2270ba0d03cf018aa4780"},
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:54be0a60a5a35005ad69c75902e0f5c9f699db4547ead427e97ef881c3242e6f"},
{file = "scikit_learn-0.24.1-cp39-cp39-win32.whl", hash = "sha256:fab31f48282ebf54dd69f6663cd2d9800096bad1bb67bbc9c9ac84eb77b41972"},
{file = "scikit_learn-0.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:4562dcf4793e61c5d0f89836d07bc37521c3a1889da8f651e2c326463c4bd697"},
]
@@ -4417,6 +4577,9 @@ scipy = [
screeninfo = [
{file = "screeninfo-0.6.7.tar.gz", hash = "sha256:1c4bac1ca329da3f68cbc4d2fbc92256aa9bb8ff8583ee3e14f91f0a7baa69cb"},
]
scs = [
{file = "scs-2.1.2.tar.gz", hash = "sha256:667ed6019bb4e2f925bd9291161d2c61cc0077443094437b703ea905333fd585"},
]
seaborn = [
{file = "seaborn-0.11.1-py3-none-any.whl", hash = "sha256:4e1cce9489449a1c6ff3c567f2113cdb41122f727e27a984950d004a88ef3c5c"},
{file = "seaborn-0.11.1.tar.gz", hash = "sha256:44e78eaed937c5a87fc7a892c329a7cc091060b67ebd1d0d306b446a74ba01ad"},

View File

@@ -54,6 +54,7 @@ detecta = "^0.0.5"
tradingview-ta = "^3.2.3"
finvizfinance = "^0.9.3"
Pillow = "^8.2.0"
pyportfolioopt = "^1.4.1"
[tool.poetry.dev-dependencies]
pytest = "^6.2.2"

View File

@@ -133,6 +133,7 @@ pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or py
pygments==2.8.1; python_version >= "3.6"
pymeeus==0.5.9; python_version >= "3.5"
pyparsing==2.4.7; python_full_version >= "3.6.0" 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")
pyportfolioopt==1.4.1
pyrsistent==0.17.3; python_version >= "3.6"
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

View File

@@ -90,6 +90,7 @@ pyasn1==0.4.8; python_version >= "3.5" and python_full_version < "3.0.0" and pyt
pycoingecko==1.4.0
pymeeus==0.4.3; python_version >= "3.5"
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-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")

View File

@@ -92,6 +92,7 @@ pyasn1==0.4.8; python_version >= "3.5" and python_full_version < "3.0.0" and pyt
pycoingecko==1.4.0
pymeeus==0.4.3; python_version >= "3.5"
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-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")

View File

@@ -92,6 +92,7 @@ pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or py
pygments==2.8.1; python_version >= "3.6"
pymeeus==0.5.9; python_version >= "3.5"
pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pyportfolioopt==1.4.1
pyrsistent==0.17.3; python_version >= "3.6"
pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
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")

View File

@@ -15,7 +15,7 @@ from gamestonk_terminal import feature_flags as gtff
from gamestonk_terminal import thought_of_the_day as thought
from gamestonk_terminal import res_menu as rm
from gamestonk_terminal.discovery import disc_controller
from gamestonk_terminal.due_diligence import dd_menu as ddm
from gamestonk_terminal.due_diligence import dd_controller
from gamestonk_terminal.fundamental_analysis import fa_menu as fam
from gamestonk_terminal.helper_funcs import b_is_stock_market_open, get_flair
from gamestonk_terminal.main_helper import clear, export, load, print_help, view, candle
@@ -32,7 +32,7 @@ from gamestonk_terminal.portfolio import port_controller
from gamestonk_terminal.cryptocurrency import crypto_controller
from gamestonk_terminal.screener import screener_controller
from gamestonk_terminal.oanda import oanda_controller
from gamestonk_terminal.portfolio_optimization import po_controller
# import warnings
# warnings.simplefilter("always")
@@ -91,7 +91,9 @@ def main():
"crypto",
"ra",
"fx",
"po",
]
menu_parser.add_argument("opt", choices=choices)
completer = NestedCompleter.from_nested_dict({c: None for c in choices})
@@ -180,7 +182,8 @@ def main():
else:
print(
"No ticker selected. Use 'load ticker' to load the ticker you want to look at."
"No ticker selected. Use 'load ticker' to load the ticker you want to look at.",
"\n",
)
main_cmd = True
@@ -213,7 +216,7 @@ def main():
b_quit = ta_controller.menu(df_stock, s_ticker, s_start, s_interval)
elif ns_known_args.opt == "dd":
b_quit = ddm.dd_menu(df_stock, s_ticker, s_start, s_interval)
b_quit = dd_controller.menu(df_stock, s_ticker, s_start, s_interval)
elif ns_known_args.opt == "eda":
if s_interval == "1440min":
@@ -236,7 +239,9 @@ def main():
b_quit = eda_controller.menu(df_stock, s_ticker, s_start, s_interval)
elif ns_known_args.opt == "op":
b_quit = op_controller.menu(s_ticker)
b_quit = op_controller.menu(
s_ticker, df_stock["5. adjusted close"].values[-1]
)
elif ns_known_args.opt == "fred":
b_quit = fred_controller.menu()
@@ -247,6 +252,9 @@ def main():
elif ns_known_args.opt == "crypto":
b_quit = crypto_controller.menu()
elif ns_known_args.opt == "po":
b_quit = po_controller.menu([s_ticker])
elif ns_known_args.opt == "pred":
if not gtff.ENABLE_PREDICT:
@@ -257,7 +265,7 @@ def main():
try:
# pylint: disable=import-outside-toplevel
from gamestonk_terminal.prediction_techniques import pred_menu as pm
from gamestonk_terminal.prediction_techniques import pred_controller
except ModuleNotFoundError as e:
print("One of the optional packages seems to be missing")
print("Optional packages need to be installed")
@@ -270,7 +278,7 @@ def main():
continue
if s_interval == "1440min":
b_quit = pm.pred_menu(df_stock, s_ticker, s_start, s_interval)
b_quit = pred_controller.menu(df_stock, s_ticker, s_start, s_interval)
# 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
@@ -287,7 +295,7 @@ def main():
# 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(
b_quit = pred_controller.menu(
df_stock_pred, s_ticker, s_start, s_interval="1440min"
)
except Exception as e:

View File

@@ -1,9 +0,0 @@
""" fundamental_analysis/business_insider_api.py tests """
import unittest
from gamestonk_terminal.due_diligence.financial_modeling_prep_api import rating
class TestDdFinancialModelingPrepApi(unittest.TestCase):
def test_rating(self):
rating([], "PLTR")

View File

@@ -0,0 +1,9 @@
""" fundamental_analysis/business_insider_api.py tests """
import unittest
from gamestonk_terminal.due_diligence.financial_modeling_prep_view import rating
class TestDdFinancialModelingPrepView(unittest.TestCase):
def test_rating(self):
rating([], "PLTR")

View File

@@ -1,7 +1,7 @@
""" due_diligence/finviz_api.py tests """
import unittest
from gamestonk_terminal.due_diligence.finviz_api import analyst
from gamestonk_terminal.due_diligence.finviz_view import analyst
class TestDdFinvizApi(unittest.TestCase):