diff --git a/gamestonk_terminal/portfolio/portfolio_controller.py b/gamestonk_terminal/portfolio/portfolio_controller.py index 42324a27b5b..16585f272e9 100644 --- a/gamestonk_terminal/portfolio/portfolio_controller.py +++ b/gamestonk_terminal/portfolio/portfolio_controller.py @@ -17,6 +17,7 @@ from gamestonk_terminal.helper_funcs import ( valid_date, check_positive_float, check_positive, + EXPORT_ONLY_FIGURES_ALLOWED, ) from gamestonk_terminal.menu import session @@ -35,16 +36,7 @@ from gamestonk_terminal.helper_funcs import parse_known_args_and_warn class PortfolioController(BaseController): """Portfolio Controller class""" - CHOICES_COMMANDS = [ - "load", - "save", - "show", - "add", - "rmv", - "ar", - "rmr", - "al", - ] + CHOICES_COMMANDS = ["load", "save", "show", "add", "rmv", "ar", "rmr", "al", "mdd"] CHOICES_MENUS = [ "bro", "po", @@ -93,6 +85,7 @@ class PortfolioController(BaseController): [info]Graphs:[/info][cmds] rmr graph your returns versus the market's returns al displays the allocation of the portfolio + mdd display portfolio drawdown[/cmds] """ console.print(text=help_text, menu="Portfolio") @@ -402,3 +395,24 @@ class PortfolioController(BaseController): console.print("Cannot generate a graph from an empty dataframe\n") else: console.print("Please add items to the portfolio\n") + + def call_mdd(self, other_args: List[str]): + """Process mdd command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="mdd", + description="Show portfolio drawdown", + ) + ns_parser = parse_known_args_and_warn( + parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED + ) + if ns_parser: + if not self.portfolio.empty: + val, _ = portfolio_model.convert_df(self.portfolio) + if not val.empty: + df_m = yfinance_model.get_market(val.index[0], "SPY") + returns, _ = portfolio_model.get_return(val, df_m, 365) + portfolio_view.display_drawdown(returns[["return"]].dropna()) + else: + console.print("[red]No portfolio loaded.\n[/red]") diff --git a/gamestonk_terminal/portfolio/portfolio_model.py b/gamestonk_terminal/portfolio/portfolio_model.py index 8a2de2e396a..248bc0ca52a 100644 --- a/gamestonk_terminal/portfolio/portfolio_model.py +++ b/gamestonk_terminal/portfolio/portfolio_model.py @@ -40,7 +40,7 @@ def save_df(df: pd.DataFrame, name: str) -> None: elif ".json" in name: df.to_json(path, index=False) elif ".xlsx" in name: - df = df.to_excel(path, index=False, engine="openpyxl") + df.to_excel(path, index=False, engine="openpyxl") def load_df(name: str) -> pd.DataFrame: @@ -597,3 +597,28 @@ performance_text = ( ". The information ratio is the excess return on systematic risk. An information ratio of" " 0.4 to 0.6 is considered good." ) + + +def calculate_drawdown(input_series: pd.Series, is_returns: bool = False) -> pd.Series: + """Calculate the drawdown (MDD) of historical series. Note that the calculation is done + on cumulative returns (or prices). The definition of drawdown is + + DD = (current value - rolling maximum) / rolling maximum + + Parameters + ---------- + input_series: pd.DataFrame + Dataframe of input values + is_returns: bool + Flag to indicate inputs are returns + + Returns + pd.Series + Drawdown series + ------- + """ + if is_returns: + input_series = (1 + input_series).cumprod() + rolling_max = input_series.cummax() + drawdown = (input_series - rolling_max) / rolling_max + return drawdown diff --git a/gamestonk_terminal/portfolio/portfolio_view.py b/gamestonk_terminal/portfolio/portfolio_view.py index 65001f2ab4c..48293500b08 100644 --- a/gamestonk_terminal/portfolio/portfolio_view.py +++ b/gamestonk_terminal/portfolio/portfolio_view.py @@ -5,6 +5,7 @@ from typing import List from datetime import datetime from io import BytesIO from os import path +import os import numpy as np import pandas as pd @@ -24,7 +25,7 @@ from gamestonk_terminal.portfolio import ( yfinance_model, ) from gamestonk_terminal.portfolio import reportlab_helpers -from gamestonk_terminal.helper_funcs import get_rf +from gamestonk_terminal.helper_funcs import get_rf, plot_autoscale, export_data from gamestonk_terminal.portfolio.portfolio_optimization import optimizer_model from gamestonk_terminal.rich_config import console @@ -295,6 +296,40 @@ def display_allocation(data: pd.DataFrame, graph: bool): plt.show() +def display_drawdown(holdings: pd.DataFrame, export: str = ""): + """Display drawdown curve + + Parameters + ---------- + holdings: pd.DataFrame + Dataframe of holdings vs time + export: str + Format to export data + + """ + drawdown = portfolio_model.calculate_drawdown(holdings) + fig, ax = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI) + ax[0].plot(holdings.index, holdings) + ax[0].set_title("Cumulative Returns") + + ax[1].plot(holdings.index, drawdown) + ax[1].fill_between(holdings.index, np.asarray(drawdown["return"]), alpha=0.4) + ax[1].set_title("Portfolio Drawdown") + ax[0].grid() + ax[1].grid() + fig.autofmt_xdate() + fig.tight_layout() + if gtff.USE_ION: + plt.ion() + console.print() + plt.show() + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "mdd", + ) + + class Report: def __init__(self, df: pd.DataFrame, hist: pd.DataFrame, m_tick: str, n: int): """Generate financial reports. diff --git a/gamestonk_terminal/stocks/prediction_techniques/pred_controller.py b/gamestonk_terminal/stocks/prediction_techniques/pred_controller.py index f6ecdbdf669..91374daf0f5 100644 --- a/gamestonk_terminal/stocks/prediction_techniques/pred_controller.py +++ b/gamestonk_terminal/stocks/prediction_techniques/pred_controller.py @@ -761,6 +761,7 @@ class PredictionTechniquesController(BaseController): help="Number of simulations to perform", dest="n_sims", default=100, + type=check_positive, ) parser.add_argument( "--dist", diff --git a/poetry.lock b/poetry.lock index 05af43de22d..b65271eadde 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1957,23 +1957,23 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pandas" -version = "1.4.0" +version = "1.3.5" description = "Powerful data structures for data analysis, time series, and statistics" category = "main" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7.1" [package.dependencies] numpy = [ - {version = ">=1.18.5", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.17.3", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""}, {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""}, ] -python-dateutil = ">=2.8.1" -pytz = ">=2020.1" +python-dateutil = ">=2.7.3" +pytz = ">=2017.3" [package.extras] -test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] +test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"] [[package]] name = "pandas-datareader" @@ -3836,7 +3836,7 @@ prediction = ["tensorflow"] [metadata] lock-version = "1.1" python-versions = "^3.8,<3.10" -content-hash = "65028ccaf95086a7576f5910cb4b439e7626f73c04879c4b4c4fc7ba66bbc935" +content-hash = "67f5d4c5a7cf6088cd5a77ff1fadb13af8a765c2231e7d8bd5eae9bb90292516" [metadata.files] absl-py = [ @@ -4200,7 +4200,6 @@ cvxpy = [ {file = "cvxpy-1.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f82ca609e34577062940d5c32bc1f687fcf83d39ca92ec0d04fb1f290b43355"}, {file = "cvxpy-1.1.18-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:569f7298539a2bd8704209628af11ecf1bca0cff2d5e27c46b57ffea921a01cf"}, {file = "cvxpy-1.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:2eb079877d23f03494141102d6cf9d3139edf5e1dc07bfa3f767f4c79e837ecb"}, - {file = "cvxpy-1.1.18.tar.gz", hash = "sha256:5baefe1f2ed6937749b293586c6ce4f42ed30e7890223f7661ccb2bb7df70bef"}, ] cycler = [ {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, @@ -5159,27 +5158,31 @@ packaging = [ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] pandas = [ - {file = "pandas-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de62cf699122dcef175988f0714678e59c453dc234c5b47b7136bfd7641e3c8c"}, - {file = "pandas-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46a18572f3e1cb75db59d9461940e9ba7ee38967fa48dd58f4139197f6e32280"}, - {file = "pandas-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:73f7da2ccc38cc988b74e5400b430b7905db5f2c413ff215506bea034eaf832d"}, - {file = "pandas-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5229c95db3a907451dacebc551492db6f7d01743e49bbc862f4a6010c227d187"}, - {file = "pandas-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe454180ad31bbbe1e5d111b44443258730467f035e26b4e354655ab59405871"}, - {file = "pandas-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:784cca3f69cfd7f6bd7c7fdb44f2bbab17e6de55725e9ff36d6f382510dfefb5"}, - {file = "pandas-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:de8f8999864399529e8514a2e6bfe00fd161f0a667903655552ed12e583ae3cb"}, - {file = "pandas-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0f19504f2783526fb5b4de675ea69d68974e21c1624f4b92295d057a31d5ec5f"}, - {file = "pandas-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f045bb5c6bfaba536089573bf97d6b8ccc7159d951fe63904c395a5e486fbe14"}, - {file = "pandas-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280d057ddae06fe4a3cd6aa79040b8c205cd6dd21743004cf8635f39ed01712"}, - {file = "pandas-1.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f3b74335390dda49f5d5089fab71958812bf56f42aa27663ee4c16d19f4f1c5"}, - {file = "pandas-1.4.0-cp38-cp38-win32.whl", hash = "sha256:51e5da3802aaee1aa4254108ffaf1129a15fb3810b7ce8da1ec217c655b418f5"}, - {file = "pandas-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:f103a5cdcd66cb18882ccdc18a130c31c3cfe3529732e7f10a8ab3559164819c"}, - {file = "pandas-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4a8d5a200f8685e7ea562b2f022c77ab7cb82c1ca5b240e6965faa6f84e5c1e9"}, - {file = "pandas-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b5af258c7b090cca7b742cf2bd67ad1919aa9e4e681007366c9edad2d6a3d42b"}, - {file = "pandas-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:156aac90dd7b303bf0b91bae96c0503212777f86c731e41929c571125d26c8e9"}, - {file = "pandas-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dad075089e17a72391de33021ad93720aff258c3c4b68c78e1cafce7e447045"}, - {file = "pandas-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d59c958d6b8f96fdf850c7821571782168d5acfe75ccf78cd8d1ac15fb921df"}, - {file = "pandas-1.4.0-cp39-cp39-win32.whl", hash = "sha256:55ec0e192eefa26d823fc25a1f213d6c304a3592915f368e360652994cdb8d9a"}, - {file = "pandas-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:23c04dab11f3c6359cfa7afa83d3d054a8f8c283d773451184d98119ef54da97"}, - {file = "pandas-1.4.0.tar.gz", hash = "sha256:cdd76254c7f0a1583bd4e4781fb450d0ebf392e10d3f12e92c95575942e37df5"}, + {file = "pandas-1.3.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62d5b5ce965bae78f12c1c0df0d387899dd4211ec0bdc52822373f13a3a022b9"}, + {file = "pandas-1.3.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:adfeb11be2d54f275142c8ba9bf67acee771b7186a5745249c7d5a06c670136b"}, + {file = "pandas-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:60a8c055d58873ad81cae290d974d13dd479b82cbb975c3e1fa2cf1920715296"}, + {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd541ab09e1f80a2a1760032d665f6e032d8e44055d602d65eeea6e6e85498cb"}, + {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2651d75b9a167cc8cc572cf787ab512d16e316ae00ba81874b560586fa1325e0"}, + {file = "pandas-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:aaf183a615ad790801fa3cf2fa450e5b6d23a54684fe386f7e3208f8b9bfbef6"}, + {file = "pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:344295811e67f8200de2390093aeb3c8309f5648951b684d8db7eee7d1c81fb7"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:552020bf83b7f9033b57cbae65589c01e7ef1544416122da0c79140c93288f56"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cce0c6bbeb266b0e39e35176ee615ce3585233092f685b6a82362523e59e5b4"}, + {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d28a3c65463fd0d0ba8bbb7696b23073efee0510783340a44b08f5e96ffce0c"}, + {file = "pandas-1.3.5-cp37-cp37m-win32.whl", hash = "sha256:a62949c626dd0ef7de11de34b44c6475db76995c2064e2d99c6498c3dba7fe58"}, + {file = "pandas-1.3.5-cp37-cp37m-win_amd64.whl", hash = "sha256:8025750767e138320b15ca16d70d5cdc1886e8f9cc56652d89735c016cd8aea6"}, + {file = "pandas-1.3.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fe95bae4e2d579812865db2212bb733144e34d0c6785c0685329e5b60fcb85dd"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f261553a1e9c65b7a310302b9dbac31cf0049a51695c14ebe04e4bfd4a96f02"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b6dbec5f3e6d5dc80dcfee250e0a2a652b3f28663492f7dab9a24416a48ac39"}, + {file = "pandas-1.3.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3bc49af96cd6285030a64779de5b3688633a07eb75c124b0747134a63f4c05f"}, + {file = "pandas-1.3.5-cp38-cp38-win32.whl", hash = "sha256:b6b87b2fb39e6383ca28e2829cddef1d9fc9e27e55ad91ca9c435572cdba51bf"}, + {file = "pandas-1.3.5-cp38-cp38-win_amd64.whl", hash = "sha256:a395692046fd8ce1edb4c6295c35184ae0c2bbe787ecbe384251da609e27edcb"}, + {file = "pandas-1.3.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd971a3f08b745a75a86c00b97f3007c2ea175951286cdda6abe543e687e5f2f"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37f06b59e5bc05711a518aa10beaec10942188dccb48918bb5ae602ccbc9f1a0"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c21778a688d3712d35710501f8001cdbf96eb70a7c587a3d5613573299fdca6"}, + {file = "pandas-1.3.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3345343206546545bc26a05b4602b6a24385b5ec7c75cb6059599e3d56831da2"}, + {file = "pandas-1.3.5-cp39-cp39-win32.whl", hash = "sha256:c69406a2808ba6cf580c2255bcf260b3f214d2664a3a4197d0e640f573b46fd3"}, + {file = "pandas-1.3.5-cp39-cp39-win_amd64.whl", hash = "sha256:32e1a26d5ade11b547721a72f9bfc4bd113396947606e00d5b4a5b79b3dcb006"}, + {file = "pandas-1.3.5.tar.gz", hash = "sha256:1e4285f5de1012de20ca46b188ccf33521bff61ba5c5ebd78b4fb28e5416a9f1"}, ] pandas-datareader = [ {file = "pandas-datareader-0.10.0.tar.gz", hash = "sha256:9fc3c63d39bc0c10c2683f1c6d503ff625020383e38f6cbe14134826b454d5a6"}, diff --git a/pyproject.toml b/pyproject.toml index e14d1bcf814..2630b467949 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ pmdarima = "^1.8.4" pyrsistent = "^0.18.0" openpyxl = "^3.0.9" jsonschema = "3.2.0" -pandas = "^1.3.4" +pandas = "1.3.5" ipympl = "^0.8.2" voila = "^0.3.0" rich = "^10.16.1" diff --git a/requirements-full.txt b/requirements-full.txt index 8eb03896e02..04f66c8bfb6 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -6,40 +6,40 @@ ansiwrap==0.8.4; python_version >= "3.6" anyio==3.5.0; python_full_version >= "3.6.2" and python_version >= "3.7" appdirs==1.4.4; python_full_version >= "3.6.2" and python_version >= "3.6" appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.8" and sys_platform == "darwin" -argon2-cffi-bindings==21.2.0; python_version >= "3.6" +argon2-cffi-bindings==21.2.0; python_version >= "3.7" argon2-cffi==21.3.0; python_version >= "3.7" astroid==2.9.3; python_full_version >= "3.6.2" asttokens==2.0.5; python_version >= "3.8" astunparse==1.6.3 async-timeout==3.0.1; python_full_version >= "3.5.3" and python_version >= "3.6" atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0" -attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_full_version >= "3.5.0" and python_version >= "3.6" and python_version < "4.0" +attrs==21.4.0; python_full_version >= "3.7.0" and python_version >= "3.6" and python_version < "4.0" babel==2.9.1; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") backcall==0.2.0; python_version >= "3.8" backports.zoneinfo==0.2.1; python_version < "3.9" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") bandit==1.7.1; python_version >= "3.5" beartype==0.7.1; python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0" -beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.5" +beautifulsoup4==4.10.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.5" black==21.7b0; python_full_version >= "3.6.2" bleach==4.1.0; python_version >= "3.7" bs4==0.0.1 bt==0.2.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") cachetools==5.0.0; python_version >= "3.7" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") -certifi==2021.10.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" -cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.6" +certifi==2021.10.8; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") +cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" cfgv==3.3.1; python_full_version >= "3.6.1" chardet==4.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3" +charset-normalizer==2.0.10; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") click==8.0.3; python_full_version >= "3.6.2" and python_version >= "3.6" codespell==2.1.0; python_version >= "3.5" -colorama==0.4.4; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.5" and python_full_version >= "3.5.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows") and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.6" and python_full_version >= "3.5.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") +colorama==0.4.4; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and (python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0") and sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.5" and python_full_version >= "3.5.0") and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows" and python_version >= "3.6") and platform_system == "Windows" and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") commonmark==0.9.1; python_full_version >= "3.6.2" and python_full_version < "4.0.0" convertdate==2.4.0; python_version >= "3.7" and python_version < "4" coverage==6.2; python_version >= "3.6" cryptography==36.0.1; python_version >= "3.6" cssselect==1.1.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" cvxpy==1.1.18; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -cycler==0.11.0; python_version >= "3.7" +cycler==0.11.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" cython==0.29.26; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "darwin" or python_full_version >= "3.3.0" and sys_platform == "darwin" and python_version >= "3.6" dateparser==1.1.0; python_version >= "3.5" datetime==4.3; python_version >= "3.5" @@ -53,7 +53,7 @@ distlib==0.3.4; python_full_version >= "3.6.1" dnspython==2.2.0; python_version >= "3.6" and python_version < "4.0" docutils==0.16; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6") ecos==2.0.10; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -entrypoints==0.3; python_full_version >= "3.6.1" and python_version >= "3.7" +entrypoints==0.3; python_full_version >= "3.7.0" and python_version >= "3.7" et-xmlfile==1.1.0; python_version >= "3.6" exchange-calendars==3.5.1; python_version >= "3.7" and python_full_version >= "3.7.0" executing==0.8.2; python_version >= "3.8" @@ -65,7 +65,7 @@ finviz==1.4.3 finvizfinance==0.9.10; python_version >= "3.5" flake8==3.9.2; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") flatbuffers==2.0 -fonttools==4.28.5; python_version >= "3.7" +fonttools==4.28.5; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" fred==3.1 fredapi==0.4.3 frozendict==2.2.0; python_version >= "3.6" @@ -82,7 +82,7 @@ h5py==3.6.0; python_version >= "3.7" hijri-converter==2.2.2; python_version >= "3.6" holidays==0.11.3.1; python_version >= "3.6" identify==2.4.4; python_full_version >= "3.6.1" -idna==3.3; python_full_version >= "3.6.2" and python_version >= "3.7" +idna==3.3; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") imagesize==1.3.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" importlib-metadata==4.10.1; python_version < "3.10" and python_version >= "3.7" inflection==0.5.1; python_version >= "3.6" @@ -90,19 +90,19 @@ iniconfig==1.1.1; python_version >= "3.6" investpy==1.0.7; python_version >= "3" ipykernel==6.7.0; python_version >= "3.7" ipympl==0.8.7 -ipython-genutils==0.2.0; python_version >= "3.7" +ipython-genutils==0.2.0; python_full_version >= "3.7.0" and python_version >= "3.7" ipython==8.0.1; python_version >= "3.8" ipywidgets==7.6.5 iso8601==0.1.16 isort==5.10.1; python_full_version >= "3.6.2" and python_version < "4.0" jedi==0.18.1; python_version >= "3.8" jinja2==3.0.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" -joblib==1.1.0; python_version >= "3.7" +joblib==1.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" json5==0.9.6; python_version >= "3.6" jsonschema==3.2.0 jupyter-client==7.1.2; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-console==6.4.0; python_version >= "3.6" -jupyter-core==4.9.1; python_full_version >= "3.6.1" and python_version >= "3.7" +jupyter-core==4.9.1; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-server==1.13.4; python_version >= "3.7" jupyter==1.0.0 jupyterlab-pygments==0.1.2; python_version >= "3.7" @@ -111,7 +111,7 @@ jupyterlab-widgets==1.0.2; python_version >= "3.6" jupyterlab==3.2.8; python_version >= "3.6" keras-preprocessing==1.1.2 keras==2.7.0 -kiwisolver==1.3.2; python_version >= "3.7" +kiwisolver==1.3.2; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" korean-lunar-calendar==0.2.1; python_version >= "3.7" and python_full_version >= "3.7.0" lazy-object-proxy==1.7.1; python_version >= "3.6" and python_full_version >= "3.6.2" libclang==11.1.0 @@ -119,7 +119,7 @@ lxml==4.7.1; python_full_version >= "3.6.8" and python_version >= "3.5" and pyth m2r2==0.2.8; python_full_version >= "3.6.8" and python_full_version < "4.0.0" markdown-it-py==1.1.0; python_version >= "3.6" and python_version < "4.0" markdown==3.3.6; python_version >= "3.6" -markupsafe==2.0.1; python_version >= "3.6" +markupsafe==2.0.1; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" matplotlib-inline==0.1.3; python_version >= "3.8" matplotlib==3.5.1; python_version >= "3.7" mccabe==0.6.1; python_full_version >= "3.6.2" @@ -151,7 +151,7 @@ packaging==21.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0 pandas-datareader==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" pandas-market-calendars==3.2; python_full_version >= "3.7.0" pandas-ta==0.3.14b -pandas==1.4.0; python_version >= "3.8" +pandas==1.3.5; python_full_version >= "3.7.1" pandocfilters==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" papermill==2.3.3; python_version >= "3.6" parso==0.8.3; python_version >= "3.8" @@ -174,13 +174,13 @@ protobuf==3.19.3; python_full_version >= "3.7.1" and python_full_version < "4.0. psaw==0.0.12; python_version >= "3" ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.8" and sys_platform != "win32" pure-eval==0.2.2; python_version >= "3.8" -py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" and implementation_name == "pypy" or python_full_version >= "3.5.0" and python_version >= "3.6" and implementation_name == "pypy" +py==1.11.0; python_full_version >= "3.7.0" and python_version >= "3.7" and implementation_name == "pypy" pyally==1.1.2 pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" pyasn1==0.4.8; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") or python_full_version >= "3.6.0" and python_version >= "3.6" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") pycodestyle==2.7.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pycoingecko==2.2.0 -pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" +pycparser==2.21; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" pyex==0.5.0 pyflakes==2.3.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" pygments==2.11.2; python_version >= "3.5" @@ -191,7 +191,7 @@ pymongo==3.11.0; (python_version >= "2.7" and python_full_version < "3.0.0") or pyobjc-core==8.1; python_version >= "3.6" and sys_platform == "darwin" pyobjc-framework-cocoa==8.1; python_version >= "3.6" and sys_platform == "darwin" pyotp==2.6.0; python_version >= "3" -pyparsing==3.0.7; python_version >= "3.7" +pyparsing==3.0.7; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" pyportfolioopt==1.5.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyprind==2.11.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" pyrsistent==0.18.1; python_version >= "3.7" @@ -200,16 +200,16 @@ pytest-recording==0.12.0; python_version >= "3.5" pytest==6.2.5; python_version >= "3.6" python-binance==1.0.15 python-coinmarketcap==0.2 -python-dateutil==2.8.2; python_full_version >= "3.7.0" and python_version >= "3.8" and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") +python-dateutil==2.8.2; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") python-dotenv==0.19.2; python_version >= "3.5" pytrends==4.7.3 pytz-deprecation-shim==0.1.0.post0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -pytz==2021.3; python_version >= "3.8" and python_full_version >= "3.7.0" and python_full_version < "4.0.0" +pytz==2021.3; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") pyupgrade==2.31.0; python_full_version >= "3.6.1" -pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.6" -pywinpty==1.1.6; os_name == "nt" and python_version >= "3.6" +pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.7" and python_full_version >= "3.7.0" +pywinpty==1.1.6; os_name == "nt" and python_version >= "3.7" pyyaml==6.0; python_version >= "3.6" and python_full_version >= "3.6.1" -pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.7" +pyzmq==22.3.0; python_full_version >= "3.7.0" and python_version >= "3.7" qdldl==0.1.5.post0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" qtconsole==5.2.2; python_version >= "3.6" qtpy==2.0.0; python_version >= "3.6" @@ -223,15 +223,15 @@ rich==10.16.2; python_full_version >= "3.6.2" and python_full_version < "4.0.0" robin-stocks==2.1.0; python_version >= "3" rsa==4.8; python_version >= "3.6" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") scikit-learn==1.0.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" -scipy==1.7.3; python_version >= "3.7" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") +scipy==1.7.3; python_version >= "3.7" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") screeninfo==0.6.7 scs==3.1.0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" seaborn==0.11.2; python_version >= "3.6" selenium==3.141.0 send2trash==1.8.0; python_version >= "3.7" sentiment-investor==2.1.0; python_version >= "3.8" and python_version < "4.0" -setuptools-scm==6.4.2; python_version >= "3.7" -six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" 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 >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") +setuptools-scm==6.4.2; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" +six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" 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 >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") smmap==5.0.0; python_version >= "3.7" sniffio==1.2.0; python_full_version >= "3.6.2" and python_version >= "3.7" snowballstemmer==2.2.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" @@ -263,12 +263,12 @@ terminado==0.12.1; python_version >= "3.7" testpath==0.5.0; python_version >= "3.7" textwrap3==0.9.2; python_version >= "3.6" thepassiveinvestor==1.0.10 -threadpoolctl==3.0.0; python_version >= "3.7" +threadpoolctl==3.0.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" tokenize-rt==4.2.1; python_full_version >= "3.6.1" toml==0.10.2; python_full_version >= "3.6.2" 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") tomli==1.2.3; python_full_version >= "3.6.2" and python_version >= "3.7" toolz==0.11.2; python_version >= "3.7" and python_full_version >= "3.7.0" -tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.7" +tornado==6.1; python_full_version >= "3.7.0" and python_version >= "3.7" tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" tradingview-ta==3.2.10; python_version >= "3.6" traitlets==5.1.1; python_full_version >= "3.7.0" and python_version >= "3.8" @@ -285,14 +285,14 @@ tzlocal==4.1; python_version >= "3.6" ujson==5.1.0; python_version >= "3.7" unidecode==1.3.2; python_version >= "3.5" update-checker==0.18.0; python_version >= "3.6" and python_version < "4.0" -urllib3==1.26.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" +urllib3==1.26.8; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") user-agent==0.1.10 vadersentiment==3.3.2 valinvest==0.0.2; python_version >= "3.6" vcrpy==4.1.1; python_version >= "3.5" virtualenv==20.13.0; python_full_version >= "3.6.1" voila==0.3.0; python_version >= "3.6" -wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.6" +wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.8" webencodings==0.5.1; python_version >= "3.7" websocket-client==1.2.3; python_version >= "3.8" and python_version < "4.0" websockets==9.1; python_full_version >= "3.6.1" and python_version >= "3.6" diff --git a/requirements.txt b/requirements.txt index 3c35347e730..46437f8e091 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,32 +5,32 @@ ansiwrap==0.8.4; python_version >= "3.6" anyio==3.5.0; python_full_version >= "3.6.2" and python_version >= "3.7" appdirs==1.4.4; python_full_version >= "3.6.2" and python_version >= "3.6" appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.8" and sys_platform == "darwin" -argon2-cffi-bindings==21.2.0; python_version >= "3.6" +argon2-cffi-bindings==21.2.0; python_version >= "3.7" argon2-cffi==21.3.0; python_version >= "3.7" asttokens==2.0.5; python_version >= "3.8" async-timeout==3.0.1; python_full_version >= "3.5.3" and python_version >= "3.6" -attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +attrs==21.4.0; python_full_version >= "3.7.0" and python_version >= "3.6" babel==2.9.1; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") backcall==0.2.0; python_version >= "3.8" backports.zoneinfo==0.2.1; python_version < "3.9" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") beartype==0.7.1; python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0" -beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.5" +beautifulsoup4==4.10.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.5" black==21.7b0; python_full_version >= "3.6.2" bleach==4.1.0; python_version >= "3.7" bs4==0.0.1 bt==0.2.9; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0") -certifi==2021.10.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" -cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.6" +certifi==2021.10.8; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") +cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" chardet==4.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3" +charset-normalizer==2.0.10; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") click==8.0.3; python_full_version >= "3.6.2" and python_version >= "3.6" -colorama==0.4.4; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and sys_platform == "win32" and python_version >= "3.6" and (python_version >= "2.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows") and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or platform_system == "Windows" and python_version >= "3.6" and python_full_version >= "3.5.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") +colorama==0.4.4; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_full_version >= "3.5.0" and platform_system == "Windows" and python_version >= "3.6") and platform_system == "Windows" and (python_version >= "3.8" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.8" and python_full_version >= "3.5.0") commonmark==0.9.1; python_full_version >= "3.6.2" and python_full_version < "4.0.0" convertdate==2.4.0; python_version >= "3.7" and python_version < "4" cryptography==36.0.1; python_version >= "3.6" cssselect==1.1.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" cvxpy==1.1.18; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -cycler==0.11.0; python_version >= "3.7" +cycler==0.11.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" cython==0.29.26; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "darwin" or python_full_version >= "3.3.0" and sys_platform == "darwin" and python_version >= "3.6" dateparser==1.1.0; python_version >= "3.5" datetime==4.3; python_version >= "3.5" @@ -43,7 +43,7 @@ detecta==0.0.5; python_version >= "3.6" dnspython==2.2.0; python_version >= "3.6" and python_version < "4.0" docutils==0.16; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" ecos==2.0.10; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" -entrypoints==0.3; python_full_version >= "3.6.1" and python_version >= "3.7" +entrypoints==0.3; python_full_version >= "3.7.0" and python_version >= "3.7" et-xmlfile==1.1.0; python_version >= "3.6" executing==0.8.2; python_version >= "3.8" fear-greed-index==0.1.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0" @@ -51,7 +51,7 @@ ffn==0.3.6; python_version >= "2.7" and python_full_version < "3.0.0" or python_ financedatabase==1.0.2 finviz==1.4.3 finvizfinance==0.9.10; python_version >= "3.5" -fonttools==4.28.5; python_version >= "3.7" +fonttools==4.28.5; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" fred==3.1 fredapi==0.4.3 frozendict==2.2.0; python_version >= "3.6" @@ -62,35 +62,35 @@ gitpython==3.1.26; python_version >= "3.7" grpcio==1.43.0; python_full_version >= "3.7.1" and python_full_version < "4.0.0" and python_version >= "3.6" hijri-converter==2.2.2; python_version >= "3.6" holidays==0.11.3.1; python_version >= "3.6" -idna==3.3; python_full_version >= "3.6.2" and python_version >= "3.7" +idna==3.3; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") imagesize==1.3.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" inflection==0.5.1; python_version >= "3.6" investpy==1.0.7; python_version >= "3" ipykernel==6.7.0; python_version >= "3.7" ipympl==0.8.7 -ipython-genutils==0.2.0; python_version >= "3.7" +ipython-genutils==0.2.0; python_full_version >= "3.7.0" and python_version >= "3.7" ipython==8.0.1; python_version >= "3.8" ipywidgets==7.6.5 iso8601==0.1.16 jedi==0.18.1; python_version >= "3.8" jinja2==3.0.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" -joblib==1.1.0; python_version >= "3.7" +joblib==1.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" json5==0.9.6; python_version >= "3.6" jsonschema==3.2.0 jupyter-client==7.1.2; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-console==6.4.0; python_version >= "3.6" -jupyter-core==4.9.1; python_full_version >= "3.6.1" and python_version >= "3.7" +jupyter-core==4.9.1; python_full_version >= "3.7.0" and python_version >= "3.7" jupyter-server==1.13.4; python_version >= "3.7" jupyter==1.0.0 jupyterlab-pygments==0.1.2; python_version >= "3.7" jupyterlab-server==2.10.3; python_version >= "3.6" jupyterlab-widgets==1.0.2; python_version >= "3.6" jupyterlab==3.2.8; python_version >= "3.6" -kiwisolver==1.3.2; python_version >= "3.7" +kiwisolver==1.3.2; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" korean-lunar-calendar==0.2.1; python_version >= "3.6" lxml==4.7.1; python_full_version >= "3.6.8" and python_version >= "3.5" and python_full_version < "4.0.0" and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3") and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6") m2r2==0.2.8; python_full_version >= "3.6.8" and python_full_version < "4.0.0" -markupsafe==2.0.1; python_version >= "3.6" +markupsafe==2.0.1; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" matplotlib-inline==0.1.3; python_version >= "3.8" matplotlib==3.5.1; python_version >= "3.7" mistune==0.8.4; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" @@ -115,7 +115,7 @@ osqp==0.6.2.post5; python_full_version >= "3.6.1" and python_full_version < "4.0 packaging==21.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" pandas-datareader==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" pandas-ta==0.3.14b -pandas==1.4.0; python_version >= "3.8" +pandas==1.3.5; python_full_version >= "3.7.1" pandocfilters==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" papermill==2.3.3; python_version >= "3.6" parso==0.8.3; python_version >= "3.8" @@ -134,10 +134,10 @@ protobuf==3.19.3; python_full_version >= "3.7.1" and python_full_version < "4.0. psaw==0.0.12; python_version >= "3" ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.8" and sys_platform != "win32" pure-eval==0.2.2; python_version >= "3.8" -py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" and implementation_name == "pypy" or implementation_name == "pypy" and python_version >= "3.6" and python_full_version >= "3.5.0" +py==1.11.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" pyally==1.1.2 pycoingecko==2.2.0 -pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" +pycparser==2.21; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.7.0" pyex==0.5.0 pygments==2.11.2; python_version >= "3.5" pymeeus==0.5.11; python_version >= "3.7" and python_version < "4" @@ -145,21 +145,21 @@ pymongo==3.11.0; (python_version >= "2.7" and python_full_version < "3.0.0") or pyobjc-core==8.1; python_version >= "3.6" and sys_platform == "darwin" pyobjc-framework-cocoa==8.1; python_version >= "3.6" and sys_platform == "darwin" pyotp==2.6.0; python_version >= "3" -pyparsing==3.0.7; python_version >= "3.7" +pyparsing==3.0.7; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" pyportfolioopt==1.5.1; python_full_version >= "3.6.1" and python_full_version < "4.0.0" pyprind==2.11.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" pyrsistent==0.18.1; python_version >= "3.7" python-binance==1.0.15 python-coinmarketcap==0.2 -python-dateutil==2.8.2; python_full_version >= "3.6.1" and python_version >= "3.8" +python-dateutil==2.8.2; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5") python-dotenv==0.19.2; python_version >= "3.5" pytrends==4.7.3 pytz-deprecation-shim==0.1.0.post0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" -pytz==2021.3; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.8" -pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.6" -pywinpty==1.1.6; os_name == "nt" and python_version >= "3.6" +pytz==2021.3; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") +pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.7" and python_full_version >= "3.7.0" +pywinpty==1.1.6; os_name == "nt" and python_version >= "3.7" pyyaml==6.0; python_version >= "3.6" -pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.7" +pyzmq==22.3.0; python_full_version >= "3.7.0" and python_version >= "3.7" qdldl==0.1.5.post0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" qtconsole==5.2.2; python_version >= "3.6" qtpy==2.0.0; python_version >= "3.6" @@ -172,15 +172,15 @@ requests==2.27.1; (python_version >= "2.7" and python_full_version < "3.0.0") or rich==10.16.2; python_full_version >= "3.6.2" and python_full_version < "4.0.0" robin-stocks==2.1.0; python_version >= "3" scikit-learn==1.0.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" -scipy==1.7.3; python_version >= "3.7" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") +scipy==1.7.3; python_version >= "3.7" and python_version < "3.11" and python_full_version >= "3.6.1" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7") screeninfo==0.6.7 scs==3.1.0; python_full_version >= "3.6.1" and python_full_version < "4.0.0" and python_version >= "3.6" seaborn==0.11.2; python_version >= "3.6" selenium==3.141.0 send2trash==1.8.0; python_version >= "3.7" sentiment-investor==2.1.0; python_version >= "3.8" and python_version < "4.0" -setuptools-scm==6.4.2; python_version >= "3.7" -six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.6" and python_full_version < "4.0.0" 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 >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") +setuptools-scm==6.4.2; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.7" +six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.7" and python_full_version < "4.0.0" 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 >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") and (python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8") smmap==5.0.0; python_version >= "3.7" sniffio==1.2.0; python_full_version >= "3.6.2" and python_version >= "3.7" snowballstemmer==2.2.0; python_full_version >= "3.6.8" and python_full_version < "4.0.0" and python_version >= "3.6" @@ -204,9 +204,9 @@ terminado==0.12.1; python_version >= "3.7" testpath==0.5.0; python_version >= "3.7" textwrap3==0.9.2; python_version >= "3.6" thepassiveinvestor==1.0.10 -threadpoolctl==3.0.0; python_version >= "3.7" +threadpoolctl==3.0.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" tomli==1.2.3; python_full_version >= "3.6.2" and python_version >= "3.7" -tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.7" +tornado==6.1; python_full_version >= "3.7.0" and python_version >= "3.7" tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" tradingview-ta==3.2.10; python_version >= "3.6" traitlets==5.1.1; python_full_version >= "3.7.0" and python_version >= "3.8" @@ -216,12 +216,12 @@ tzlocal==4.1; python_version >= "3.6" ujson==5.1.0; python_version >= "3.7" unidecode==1.3.2; python_version >= "3.5" update-checker==0.18.0; python_version >= "3.6" and python_version < "4.0" -urllib3==1.26.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" +urllib3==1.26.8; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6" and python_full_version < "4.0.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") and (python_version >= "3.8" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.8" and python_version < "4.0" and python_full_version >= "3.6.0") and (python_version >= "3" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3") user-agent==0.1.10 vadersentiment==3.3.2 valinvest==0.0.2; python_version >= "3.6" voila==0.3.0; python_version >= "3.6" -wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.6" +wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.8" webencodings==0.5.1; python_version >= "3.7" websocket-client==1.2.3; python_version >= "3.8" and python_version < "4.0" websockets==9.1; python_full_version >= "3.6.1" and python_version >= "3.6" diff --git a/tests/gamestonk_terminal/portfolio/test_portfolio_controller.py b/tests/gamestonk_terminal/portfolio/test_portfolio_controller.py new file mode 100644 index 00000000000..d8f64d25ecf --- /dev/null +++ b/tests/gamestonk_terminal/portfolio/test_portfolio_controller.py @@ -0,0 +1,193 @@ +import os +import pytest +from gamestonk_terminal.portfolio import portfolio_controller + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.parametrize( + "queue, expected", + [ + (["load", "help"], []), + (["quit", "help"], ["help"]), + ], +) +def test_menu_with_queue(expected, mocker, queue): + path_controller = "gamestonk_terminal.portfolio.portfolio_controller" + + # MOCK SWITCH + mocker.patch( + target=f"{path_controller}.PortfolioController.switch", + return_value=["quit"], + ) + result_menu = portfolio_controller.PortfolioController(queue=queue).menu() + + assert result_menu == expected + + +@pytest.mark.vcr(record_mode="none") +def test_menu_without_queue_completion(mocker): + path_controller = "gamestonk_terminal.portfolio.portfolio_controller" + + # ENABLE AUTO-COMPLETION : HELPER_FUNCS.MENU + mocker.patch( + target="gamestonk_terminal.feature_flags.USE_PROMPT_TOOLKIT", + new=True, + ) + mocker.patch( + target="gamestonk_terminal.parent_classes.session", + ) + mocker.patch( + target="gamestonk_terminal.parent_classes.session.prompt", + return_value="quit", + ) + + # DISABLE AUTO-COMPLETION : CONTROLLER.COMPLETER + mocker.patch.object( + target=portfolio_controller.gtff, + attribute="USE_PROMPT_TOOLKIT", + new=True, + ) + mocker.patch( + target=f"{path_controller}.session", + ) + mocker.patch( + target=f"{path_controller}.session.prompt", + return_value="quit", + ) + + result_menu = portfolio_controller.PortfolioController(queue=None).menu() + + assert result_menu == [] + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.parametrize( + "mock_input", + ["help", "homee help", "home help", "mock"], +) +def test_menu_without_queue_sys_exit(mock_input, mocker): + path_controller = "gamestonk_terminal.portfolio.portfolio_controller" + + # DISABLE AUTO-COMPLETION + mocker.patch.object( + target=portfolio_controller.gtff, + attribute="USE_PROMPT_TOOLKIT", + new=False, + ) + mocker.patch( + target=f"{path_controller}.session", + return_value=None, + ) + + # MOCK USER INPUT + mocker.patch("builtins.input", return_value=mock_input) + + # MOCK SWITCH + class SystemExitSideEffect: + def __init__(self): + self.first_call = True + + def __call__(self, *args, **kwargs): + if self.first_call: + self.first_call = False + raise SystemExit() + return ["quit"] + + mock_switch = mocker.Mock(side_effect=SystemExitSideEffect()) + mocker.patch( + target=f"{path_controller}.PortfolioController.switch", + new=mock_switch, + ) + + result_menu = portfolio_controller.PortfolioController(queue=None).menu() + + assert result_menu == [] + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.record_stdout +def test_print_help(): + + controller = portfolio_controller.PortfolioController(queue=None) + controller.print_help() + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.parametrize( + "an_input, expected_queue", + [ + ("", []), + ("/help", ["quit", "help"]), + ("help/help", ["help"]), + ("q", ["quit"]), + ("h", []), + ( + "r", + [ + "quit", + "reset", + "portfolio", + ], + ), + ], +) +def test_switch(an_input, expected_queue): + + controller = portfolio_controller.PortfolioController(queue=None) + queue = controller.switch(an_input=an_input) + + assert queue == expected_queue + + +@pytest.mark.vcr(record_mode="none") +def test_call_cls(mocker): + mocker.patch("os.system") + + controller = portfolio_controller.PortfolioController(queue=None) + controller.call_cls([]) + + assert controller.queue == [] + os.system.assert_called_once_with("cls||clear") + + +@pytest.mark.vcr(record_mode="none") +@pytest.mark.parametrize( + "func, queue, expected_queue", + [ + ( + "call_exit", + [], + ["quit", "quit"], + ), + ("call_exit", ["help"], ["quit", "quit", "help"]), + ("call_home", [], ["quit"]), + ("call_help", [], []), + ("call_quit", [], ["quit"]), + ("call_quit", ["help"], ["quit", "help"]), + ( + "call_reset", + [], + [ + "quit", + "reset", + "portfolio", + ], + ), + ( + "call_reset", + ["help"], + [ + "quit", + "reset", + "portfolio", + "help", + ], + ), + ], +) +def test_call_func_expect_queue(expected_queue, func, queue): + controller = portfolio_controller.PortfolioController(queue=queue) + result = getattr(controller, func)([]) + + assert result is None + assert controller.queue == expected_queue diff --git a/tests/gamestonk_terminal/portfolio/txt/test_portfolio_controller/test_print_help.txt b/tests/gamestonk_terminal/portfolio/txt/test_portfolio_controller/test_print_help.txt new file mode 100644 index 00000000000..ba79982304c --- /dev/null +++ b/tests/gamestonk_terminal/portfolio/txt/test_portfolio_controller/test_print_help.txt @@ -0,0 +1,20 @@ + +> bro brokers holdings, supports: robinhood, ally, degiro, coinbase +> po portfolio optimization, optimal portfolio weights from pyportfolioopt +> pa portfolio analysis, analyse portfolios + +Portfolio: + load load data into the portfolio + save save your portfolio for future use + show show existing transactions + add add a security to your portfolio + rmv remove a security from your portfolio + +Reports: + ar annual report for performance of a given portfolio + +Graphs: + rmr graph your returns versus the market's returns + al displays the allocation of the portfolio + mdd display portfolio drawdown + diff --git a/website/content/portfolio/mdd/_index.md b/website/content/portfolio/mdd/_index.md new file mode 100644 index 00000000000..d7f1faa319a --- /dev/null +++ b/website/content/portfolio/mdd/_index.md @@ -0,0 +1,10 @@ +``` +usage: mdd [-h] [--export {png,jpg,pdf,svg}] +``` +Show portfolio drawdown + +```optional arguments: + -h, --help show this help message (default: False) + --export {png,jpg,pdf,svg} + Export figure into png, jpg, pdf, svg (default: ) +``` \ No newline at end of file diff --git a/website/data/menu/main.yml b/website/data/menu/main.yml index 89b3a696600..ad0112f1b80 100755 --- a/website/data/menu/main.yml +++ b/website/data/menu/main.yml @@ -1126,6 +1126,8 @@ main: ref: "/portfolio/ar" - name: rmr ref: "/portfolio/rmr" + - name: mdd + ref: "portfolio/mdd" - name: forex ref: "/forex" sub: