mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 20:42:26 +00:00
resolve conflict from personal changes
This commit is contained in:
53
README.md
53
README.md
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
|
||||
|
||||
## Cryptocurrency [»](cryptocurrency/README.md)
|
||||
|
||||
Command|Description
|
||||
|
||||
@@ -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("")
|
||||
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
219
gamestonk_terminal/due_diligence/dd_controller.py
Normal file
219
gamestonk_terminal/due_diligence/dd_controller.py
Normal 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
|
||||
@@ -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!")
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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",
|
||||
@@ -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"]):
|
||||
@@ -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[
|
||||
@@ -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)
|
||||
@@ -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"))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
463
gamestonk_terminal/options/volume_view.py
Normal file
463
gamestonk_terminal/options/volume_view.py
Normal 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
|
||||
169
gamestonk_terminal/portfolio_optimization/README.md
Normal file
169
gamestonk_terminal/portfolio_optimization/README.md
Normal 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+w_2+...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).
|
||||

|
||||
|
||||
And the pie chart:
|
||||
|
||||

|
||||
|
||||
Note that since `AAPL` had zero allocation, it was omitted from the chart.
|
||||
226
gamestonk_terminal/portfolio_optimization/optimizer_helper.py
Normal file
226
gamestonk_terminal/portfolio_optimization/optimizer_helper.py
Normal 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
|
||||
337
gamestonk_terminal/portfolio_optimization/optimizer_view.py
Normal file
337
gamestonk_terminal/portfolio_optimization/optimizer_view.py
Normal 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("")
|
||||
206
gamestonk_terminal/portfolio_optimization/po_controller.py
Normal file
206
gamestonk_terminal/portfolio_optimization/po_controller.py
Normal 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
|
||||
207
gamestonk_terminal/prediction_techniques/pred_controller.py
Normal file
207
gamestonk_terminal/prediction_techniques/pred_controller.py
Normal 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
|
||||
@@ -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!")
|
||||
@@ -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
|
||||
````
|
||||
@@ -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"""
|
||||
|
||||
@@ -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
173
poetry.lock
generated
@@ -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"},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
24
terminal.py
24
terminal.py
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
9
tests/test_dd_financial_modeling_prep_view.py
Normal file
9
tests/test_dd_financial_modeling_prep_view.py
Normal 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")
|
||||
@@ -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):
|
||||
Reference in New Issue
Block a user