diff --git a/README.md b/README.md index 5149a61d105..2809b6a6025 100644 --- a/README.md +++ b/README.md @@ -224,10 +224,11 @@ These are the ones where a key is necessary: * News API: https://newsapi.org * Tradier: https://developer.tradier.com/getting_started * Oanda API: https://developer.oanda.com + * CoinMarketCap API: https://coinmarketcap.com/api/ When these are obtained, don't forget to update [config_terminal.py](/gamestonk_terminal/config_terminal.py). -Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_OANDA_TOKEN, GT_OANDA_ACCOUNT, GT_TRADIER_TOKEN. +Alternatively, you can also set them to the following environment variables: GT_API_KEY_ALPHAVANTAGE, GT_API_KEY_FINANCIALMODELINGPREP, GT_API_KEY_QUANDL, GT_API_REDDIT_CLIENT_ID, GT_API_REDDIT_CLIENT_SECRET, GT_API_REDDIT_USERNAME, GT_API_REDDIT_USER_AGENT, GT_API_REDDIT_PASSWORD, GT_API_TWITTER_KEY, GT_API_TWITTER_SECRET_KEY, GT_API_TWITTER_BEARER_TOKEN, GT_API_POLYGON_KEY, GT_FRED_API_KEY, GT_API_NEWS_TOKEN, GT_OANDA_TOKEN, GT_OANDA_ACCOUNT, GT_TRADIER_TOKEN, GT_CMC_API_KEY. Example: ``` @@ -334,10 +335,7 @@ Distributed under the MIT License. See [LICENSE](https://github.com/DidierRLopes [James Maslek](https://www.linkedin.com/in/james-maslek-b6810186/) - jmaslek11@gmail.com -Feel free to share loss porn, memes or any questions: - -![Discord Banner 2](https://discordapp.com/api/guilds/831165782750789672/widget.png?style=banner2) - +Feel free to share loss porn, memes or any questions: https://discord.gg/Up2QGbMKHY ## Acknowledgments diff --git a/ROADMAP.md b/ROADMAP.md index 5242e32bfb3..64a4900b9e2 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -21,6 +21,7 @@ * [Cryptocurrencies](#Cryptocurrencies) * [Screener](#Screener) * [Forex](#Forex) +* [Backtesting](#Backtesting) ## Additional @@ -217,6 +218,11 @@ ___ **NEXT** +## Backtesting +* [x] Add simple backtest menu with simple strategies and benchmarks (@jmaslek) [PR #415](https://github.com/DidierRLopes/GamestonkTerminal/pull/415) + +**NEXT** + --- ## Backend diff --git a/gamestonk_terminal/backtesting/README.md b/gamestonk_terminal/backtesting/README.md new file mode 100644 index 00000000000..6a567e027e4 --- /dev/null +++ b/gamestonk_terminal/backtesting/README.md @@ -0,0 +1,59 @@ +# Backtesting +The goal of this menu is to allow super simple research into potential strategies. + +We currently provides a couple basic options for starting with backtesting strategies. Note that this is currently just a +simple logic (i.e price comparisons). Obviously these strategies should not be used immediately and should be researched, +improved and further tested on your own. + +The current implementation uses the [bt](#http://pmorissette.github.io/bt/index.html) library. + +Current strategies are: +* [Simple EMA](#SimpleEMA) + * Buys the stock when `price > EMA(l)` + +* [EMA Cross](#EMA Cross) + * Buys when EMA(short) > EMA(long) and sells when the opposite is true + +* [RSI](#RSI) + * Momentum strategy that longs when RSI < threshold (oversold) and shorts when + +### SimpleEMA +This strategy buys when price > EMA(l) +```` +usage: ema [-l] [--spy] [--no_bench] +```` +* -l: Period of EMA to use. Default = 20. +* [--spy]: Flag that overlays the results if you buy and hold SPY +* [--no_bench]: Flag that removes the benchmark comparison of just buy and hold the stock. + +im: + +### EMA Cross +This strategy goes long when EMA(short) > EMA(long) and goes short when +EMA(short) < EMA(long). I provide the option to go long only through the --no_short flag + +```` +usage: ema_cross [-l] [-s] [--spy] [--no_bench] [--no_short] +```` +* -l/--long: Long period of EMA to use. Default = 50. +* -s/--short: Short period of EMA to use. Default = 20. +* [--spy]: Flag that overlays the results if you buy and hold SPY +* [--no_bench]: Flag that removes the benchmark comparison of just buy and hold the stock. +* [--no_short]: Flag that removes the shortable option. Will just be long positions when short > long EMAs. + +im +### RSI +This strategy goes long when the RSI is "oversold" - defined as the low parameter. It goes short when +the RSI is "overbought" - defined as the high (upper) parameter +```` +usage: rsi [-u] [-l] [-p] [--spy] [--no_bench] [--no_short] +```` +* -u/--high: High (upper) value to consider overbought. Default = 70 +* -s/--short: Short period of EMA to use. Default = 30 +* -p/--periods: Periods to use for RSI calculation. Default = 14. +* [--spy]: Flag that overlays the results if you buy and hold SPY +* [--no_bench]: Flag that removes the benchmark comparison of just buy and hold the stock. +* [--no_short]: Flag that removes the shortable option. Will just be long positions when short > long EMAs. + + + diff --git a/gamestonk_terminal/backtesting/__init__.py b/gamestonk_terminal/backtesting/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/gamestonk_terminal/backtesting/bt_controller.py b/gamestonk_terminal/backtesting/bt_controller.py new file mode 100644 index 00000000000..efeaefd6226 --- /dev/null +++ b/gamestonk_terminal/backtesting/bt_controller.py @@ -0,0 +1,125 @@ +"""Backtesting Controller Module""" +__docformat__ = "numpy" + +import argparse +from typing import List, Union +from datetime import datetime +import matplotlib as mpl +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 +from gamestonk_terminal.menu import session + +# This code below aims to fix an issue with the fnn module, used by bt module +# which forces matplotlib backend to be 'agg' which doesn't allow to plot +# Save current matplotlib backend +default_backend = mpl.get_backend() +# pylint: disable=wrong-import-position +from gamestonk_terminal.backtesting import bt_view # noqa: E402 + +# Restore backend matplotlib used +mpl.use(default_backend) + + +class BacktestingController: + """Backtesting Class""" + + CHOICES = ["help", "q", "quit", "ema", "ema_cross", "rsi"] + + def __init__( + self, + ticker: str, + start: Union[datetime, str], + ): + self.ticker = ticker + self.start = start + self.bt_parser = argparse.ArgumentParser(add_help=False, prog="bt") + self.bt_parser.add_argument( + "cmd", + choices=self.CHOICES, + ) + + def print_help(self): + """Print help""" + + print("Back Testing Menu\n") + print(" help show this backtesting menu again") + print(" q quit this menu, and shows back to main menu") + print(" quit quit to abandon program") + print("") + print(" ema buy when price exceeds EMA(l)") + print(" ema_cross buy when EMA(short) > EMA(long) ") + print(" rsi buy when RSI < low and sell when RSI > high") + 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.bt_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_ema(self, other_args: List[str]): + """Call EMA strategy""" + bt_view.simple_ema(self.ticker, self.start, other_args) + + def call_ema_cross(self, other_args: List[str]): + """Call EMA Cross strategy""" + bt_view.ema_cross(self.ticker, self.start, other_args) + + def call_rsi(self, other_args: List[str]): + """Call RSI Strategy""" + bt_view.rsi_strat(self.ticker, self.start, other_args) + + +def menu(ticker: str, start: Union[str, datetime]): + """Backtesting Menu""" + plt.close("all") + bt_controller = BacktestingController(ticker, start) + bt_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 bt_controller.CHOICES} + ) + an_input = session.prompt( + f"{get_flair()} (bt)> ", + completer=completer, + ) + else: + an_input = input(f"{get_flair()} (bt)> ") + + try: + plt.close("all") + process_input = bt_controller.switch(an_input) + if process_input is not None: + return process_input + + except SystemExit: + print("The command selected doesn't exist\n") + continue diff --git a/gamestonk_terminal/backtesting/bt_helper.py b/gamestonk_terminal/backtesting/bt_helper.py new file mode 100644 index 00000000000..38e835c0c84 --- /dev/null +++ b/gamestonk_terminal/backtesting/bt_helper.py @@ -0,0 +1,65 @@ +"""Helper functions for backtesting""" +__docformat__ = "numpy" + + +from datetime import datetime +from typing import Union +from matplotlib import pyplot as plt +from pandas.plotting import register_matplotlib_converters +import bt +from gamestonk_terminal.helper_funcs import plot_autoscale +from gamestonk_terminal.config_plot import PLOT_DPI +from gamestonk_terminal import feature_flags as gtff + +register_matplotlib_converters() + + +def buy_and_hold(ticker: str, start: Union[str, datetime], name: str): + """ + Generates a backtest object for the given ticker + Parameters + ---------- + ticker: str + Stock to test + start: Union[str, datetime] + Backtest start date. Can be either string or datetime + name: + Name of the backtest (for labeling purposes) + + Returns + ------- + bt.Backtest object for buy and hold strategy + """ + prices = bt.get(ticker, start=start) + bt_strategy = bt.Strategy( + name, + [ + bt.algos.RunOnce(), + bt.algos.SelectAll(), + bt.algos.WeighEqually(), + bt.algos.Rebalance(), + ], + ) + return bt.Backtest(bt_strategy, prices) + + +def plot_bt(res: bt.backtest.Result, plot_title: str): + """ + Plot the bt result + Parameters + ---------- + res: bt.backtest.Result + Result after a bt backtest is run + plot_title: str + Title of plot + + """ + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + res.plot(title=plot_title, ax=ax) + 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() diff --git a/gamestonk_terminal/backtesting/bt_view.py b/gamestonk_terminal/backtesting/bt_view.py new file mode 100644 index 00000000000..b2cab11fd33 --- /dev/null +++ b/gamestonk_terminal/backtesting/bt_view.py @@ -0,0 +1,299 @@ +"""bt view module""" +__docformat__ = "numpy" +import argparse +from typing import List, Union +from datetime import datetime +import pandas as pd +import bt +import pandas_ta as ta +from gamestonk_terminal.helper_funcs import parse_known_args_and_warn +from gamestonk_terminal.backtesting.bt_helper import buy_and_hold, plot_bt + + +def simple_ema(ticker: str, start_date: Union[str, datetime], other_args: List[str]): + """ + Strategy where stock is bought when Price > EMA(l) + Parameters + ---------- + ticker: str + Stock to test + start: Union[str, datetime] + Backtest start date. Can be either string or datetime + other_args: List[str] + List of argparse arguments + + Returns + ------- + Plot and a printout of backtest + """ + parser = argparse.ArgumentParser(add_help=False, prog="ema") + parser.add_argument( + "-l", default=20, dest="length", type=int, help="EMA period to consider" + ) + + parser.add_argument( + "--spy", + action="store_true", + default=False, + help="Flag to add spy hold comparison", + dest="spy", + ) + + parser.add_argument( + "--no_bench", + action="store_true", + default=False, + help="Flag to not show buy and hold comparison", + dest="no_bench", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + ticker = ticker.lower() + ema = pd.DataFrame() + prices = bt.get(ticker, start=start_date) + ema[ticker] = ta.ema(prices[ticker], ns_parser.length) + bt_strategy = bt.Strategy( + "AboveEMA", + [ + bt.algos.SelectWhere(prices >= ema), + bt.algos.WeighEqually(), + bt.algos.Rebalance(), + ], + ) + bt_backtest = bt.Backtest(bt_strategy, prices) + + if ns_parser.spy: + spy_bt = buy_and_hold("spy", start_date, "SPY Hold") + if ns_parser.no_bench: + res = bt.run(bt_backtest, spy_bt) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, spy_bt, stock_bt) + else: + if ns_parser.no_bench: + res = bt.run(bt_backtest) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, stock_bt) + + plot_bt(res, f"Equity for EMA({ns_parser.length})") + + print(res.display()) + print("") + + except Exception as e: + print(e) + print("") + return + + +def ema_cross(ticker: str, start_date: Union[str, datetime], other_args: List[str]): + """ + Strategy where we go long/short when EMA(short) is greater than/less than EMA(short) + Parameters + ---------- + ticker: str + Stock to test + start: Union[str, datetime] + Backtest start date. Can be either string or datetime + other_args: List[str] + List of argparse arguments + + Returns + ------- + Plot and a printout of backtest + """ + parser = argparse.ArgumentParser(add_help=False, prog="ema_cross") + parser.add_argument( + "-l", "--long", default=50, dest="long", type=int, help="Long EMA period" + ) + parser.add_argument( + "-s", "--short", default=20, dest="short", type=int, help="Short EMA period" + ) + + parser.add_argument( + "--spy", + action="store_true", + default=False, + help="Flag to add spy hold comparison", + dest="spy", + ) + + parser.add_argument( + "--no_bench", + action="store_true", + default=False, + help="Flag to not show buy and hold comparison", + dest="no_bench", + ) + + parser.add_argument( + "--no_short", + action="store_false", + default=True, + dest="shortable", + help="Flag that disables the short sell", + ) + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + if ns_parser.long < ns_parser.short: + print("Short EMA period is longer than Long EMA period\n") + return + ticker = ticker.lower() + prices = bt.get(ticker, start=start_date) + short_ema = pd.DataFrame(ta.ema(prices[ticker], ns_parser.short)) + short_ema.columns = [ticker] + long_ema = pd.DataFrame(ta.ema(prices[ticker], ns_parser.long)) + long_ema.columns = [ticker] + + # signals + signals = long_ema.copy() + signals[short_ema > long_ema] = 1.0 + signals[short_ema <= long_ema] = -1.0 * ns_parser.shortable + signals[long_ema.isnull()] = 0.0 + + combined_data = bt.merge(signals, prices, short_ema, long_ema) + combined_data.columns = ["signal", "price", "ema_short", "ema_long"] + bt_strategy = bt.Strategy( + "EMA_Cross", + [ + bt.algos.WeighTarget(signals), + bt.algos.Rebalance(), + ], + ) + bt_backtest = bt.Backtest(bt_strategy, prices) + + if ns_parser.spy: + spy_bt = buy_and_hold("spy", start_date, "SPY Hold") + if ns_parser.no_bench: + res = bt.run(bt_backtest, spy_bt) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, spy_bt, stock_bt) + else: + if ns_parser.no_bench: + res = bt.run(bt_backtest) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, stock_bt) + + plot_bt(res, f"EMA Cross for EMA({ns_parser.short})/EMA({ns_parser.long})") + print(res.display()) + print("") + + except Exception as e: + print(e) + print("") + return + + +def rsi_strat(ticker: str, start_date: Union[datetime, str], other_args: List[str]): + """ + Strategy that buys when the stock is less than a threshold and shorts when it exceeds a threshold. + Parameters + ---------- + ticker: str + Stock to test + start: Union[str, datetime] + Backtest start date. Can be either string or datetime + other_args: List[str] + List of argparse arguments + + Returns + ------- + Plot and a printout of backtest + """ + parser = argparse.ArgumentParser(add_help=False, prog="rsi_strat") + parser.add_argument( + "-p", + "--periods", + dest="periods", + help="Number of periods for RSI calculation", + type=int, + default=14, + ) + + parser.add_argument( + "-u", "--high", default=70, dest="high", type=int, help="High (upper) RSI Level" + ) + parser.add_argument( + "-l", "--low", default=30, dest="low", type=int, help="Low RSI Level" + ) + + parser.add_argument( + "--spy", + action="store_true", + default=False, + help="Flag to add spy hold comparison", + dest="spy", + ) + + parser.add_argument( + "--no_bench", + action="store_true", + default=False, + help="Flag to not show buy and hold comparison", + dest="no_bench", + ) + + parser.add_argument( + "--no_short", + action="store_false", + default=True, + dest="shortable", + help="Flag that disables the short sell", + ) + + try: + ns_parser = parse_known_args_and_warn(parser, other_args) + if not ns_parser: + return + if ns_parser.high < ns_parser.low: + print("Low RSI value is higher than Low RSI value\n") + return + ticker = ticker.lower() + prices = bt.get(ticker, start=start_date) + + rsi = pd.DataFrame(ta.rsi(prices[ticker], ns_parser.periods)) + rsi.columns = [ticker] + + signal = 0 * rsi.copy() + signal[rsi > ns_parser.high] = -1 + signal[rsi < ns_parser.low] = 1 + signal[rsi.isnull()] = 0 + + merged_data = bt.merge(signal, prices) + merged_data.columns = ["signal", "price"] + + bt_strategy = bt.Strategy( + "RSI Reversion", [bt.algos.WeighTarget(signal), bt.algos.Rebalance()] + ) + bt_backtest = bt.Backtest(bt_strategy, prices) + + if ns_parser.spy: + spy_bt = buy_and_hold("spy", start_date, "SPY Hold") + if ns_parser.no_bench: + res = bt.run(bt_backtest, spy_bt) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, spy_bt, stock_bt) + else: + if ns_parser.no_bench: + res = bt.run(bt_backtest) + else: + stock_bt = buy_and_hold(ticker, start_date, ticker.upper() + " Hold") + res = bt.run(bt_backtest, stock_bt) + + plot_bt(res, f"RSI Strategy between ({ns_parser.low}, {ns_parser.high})") + print(res.display()) + print("") + + except Exception as e: + print(e) + print("") + return diff --git a/gamestonk_terminal/config_plot.py b/gamestonk_terminal/config_plot.py index d6b34c03719..a38c987f559 100644 --- a/gamestonk_terminal/config_plot.py +++ b/gamestonk_terminal/config_plot.py @@ -1,5 +1,14 @@ PLOT_DPI = 100 +# Backend to use for plotting +BACKEND = None +# Examples: +# "tkAgg" - This uses the tkinter library. If unsure, set to this +# "module://backend_interagg" - This is what pycharm defaults to in Scientific Mode +# "MacOSX" - Mac default. Does not work with backtesting +# "Qt5Agg" - This requires the PyQt5 package is installed +# See more: https://matplotlib.org/stable/tutorials/introductory/usage.html#the-builtin-backends + # Used when USE_PLOT_AUTOSCALING is set to False PLOT_HEIGHT = 500 PLOT_WIDTH = 800 diff --git a/gamestonk_terminal/helper_funcs.py b/gamestonk_terminal/helper_funcs.py index 49d4171bb3f..05e575dca0b 100644 --- a/gamestonk_terminal/helper_funcs.py +++ b/gamestonk_terminal/helper_funcs.py @@ -6,6 +6,7 @@ import re import sys from pytz import timezone import iso8601 +import matplotlib import matplotlib.pyplot as plt from holidays import US as holidaysUS from colorama import Fore, Style @@ -17,6 +18,8 @@ from gamestonk_terminal import feature_flags as gtff from gamestonk_terminal import config_plot as cfgPlot register_matplotlib_converters() +if cfgPlot.BACKEND is not None: + matplotlib.use(cfgPlot.BACKEND) def check_non_negative(value) -> int: diff --git a/gamestonk_terminal/main_helper.py b/gamestonk_terminal/main_helper.py index e42fb2ad1f3..2c813d24a46 100644 --- a/gamestonk_terminal/main_helper.py +++ b/gamestonk_terminal/main_helper.py @@ -79,6 +79,9 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open): print( " ta technical analysis, \t e.g.: ema, macd, rsi, adx, bbands, obv" ) + print( + " bt strategy backtester, \t e.g.: simple ema, ema cross, rsi strategies" + ) print( " dd in-depth due-diligence, \t e.g.: news, analyst, shorts, insider, sec" ) diff --git a/poetry.lock b/poetry.lock index 0281cf47085..3b481c673f8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -104,11 +104,11 @@ tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] [[package]] name = "astroid" -version = "2.5.3" +version = "2.5.6" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = "~=3.6" [package.dependencies] lazy-object-proxy = ">=1.4.0" @@ -249,9 +249,24 @@ python-versions = "*" [package.dependencies] beautifulsoup4 = "*" +[[package]] +name = "bt" +version = "0.2.9" +description = "A flexible backtesting framework for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +ffn = ">=0.3.5" +pyprind = ">=2.11" + +[package.extras] +dev = ["black (>=20.8b1)", "codecov", "coverage", "cython (>=0.25)", "flake8", "flake8-black", "future", "mock", "nose", "numpy (>=1)", "pandas (>=0.19)", "matplotlib (>=2)", "ffn (>=0.3.5)", "pyprind (>=2.11)"] + [[package]] name = "cachetools" -version = "4.2.1" +version = "4.2.2" description = "Extensible memoizing collections and decorators" category = "main" optional = true @@ -435,11 +450,11 @@ pytz = "*" [[package]] name = "decorator" -version = "4.4.2" +version = "5.0.7" description = "Decorators for Humans" category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.5" [[package]] name = "defusedxml" @@ -528,6 +543,28 @@ pystan = ">=2.14" python-dateutil = ">=2.8.0" setuptools-git = ">=1.2" +[[package]] +name = "ffn" +version = "0.3.6" +description = "Financial functions for Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +decorator = ">=4" +future = ">=0.15" +matplotlib = ">=1" +numpy = ">=1.5" +pandas = ">=0.19" +pandas-datareader = ">=0.2" +scikit-learn = ">=0.15" +scipy = ">=0.15" +tabulate = ">=0.7.5" + +[package.extras] +dev = ["black (>=20.8b1)", "codecov", "coverage", "flake8", "flake8-black", "future", "mock", "nose"] + [[package]] name = "filelock" version = "3.0.12" @@ -557,7 +594,7 @@ user_agent = "*" [[package]] name = "finvizfinance" -version = "0.9.6" +version = "0.9.7" description = "Finviz Finance. Information downloader." category = "main" optional = false @@ -662,7 +699,7 @@ name = "future" version = "0.18.2" description = "Clean single-source support for Python 3 and 2" category = "main" -optional = true +optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] @@ -710,7 +747,7 @@ test-win = ["pytest", "pytest-rerunfailures", "mock", "cython", "nmslib", "pyemd [[package]] name = "google-auth" -version = "1.29.0" +version = "1.30.0" description = "Google Authentication Library" category = "main" optional = true @@ -1448,14 +1485,14 @@ python-versions = ">=3.5" [[package]] name = "networkx" -version = "2.5.1" +version = "2.5" description = "Python package for creating and manipulating graphs and networks" category = "main" optional = true python-versions = ">=3.6" [package.dependencies] -decorator = ">=4.3,<5" +decorator = ">=4.3.0" [package.extras] all = ["numpy", "scipy", "pandas", "matplotlib", "pygraphviz", "pydot", "pyyaml", "lxml", "pytest"] @@ -1591,6 +1628,19 @@ pytz = ">=2017.2" [package.extras] test = ["pytest (>=4.0.2)", "pytest-xdist", "hypothesis (>=3.58)"] +[[package]] +name = "pandas-datareader" +version = "0.9.0" +description = "Data readers extracted from the pandas codebase,should be compatible with recent pandas versions" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +lxml = "*" +pandas = ">=0.23" +requests = ">=2.19.0" + [[package]] name = "pandas-ta" version = "0.2.45b" @@ -1936,22 +1986,19 @@ python-versions = ">=3.5" [[package]] name = "pylint" -version = "2.7.4" +version = "2.8.2" description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] -astroid = ">=2.5.2,<2.7" +astroid = ">=2.5.6,<2.7" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" toml = ">=0.7.1" -[package.extras] -docs = ["sphinx (==3.5.1)", "python-docs-theme (==2020.12)"] - [[package]] name = "pymeeus" version = "0.5.11" @@ -2012,6 +2059,14 @@ 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 = "pyprind" +version = "2.11.3" +description = "Python Progress Bar and Percent Indicator Utility" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pyrsistent" version = "0.14.11" @@ -2086,7 +2141,7 @@ six = ">=1.5" [[package]] name = "python-dotenv" -version = "0.17.0" +version = "0.17.1" description = "Read key-value pairs from a .env file and set them as environment variables" category = "main" optional = false @@ -2137,8 +2192,8 @@ python-versions = "*" [[package]] name = "pywinpty" -version = "0.5.7" -description = "Python bindings for the winpty library" +version = "1.0.1" +description = "Pseudo terminal support for Windows from Python." category = "main" optional = false python-versions = "*" @@ -2325,7 +2380,7 @@ tqdm = "*" [[package]] name = "scikit-learn" -version = "0.24.1" +version = "0.24.2" description = "A set of python modules for machine learning and data mining" category = "main" optional = false @@ -2981,7 +3036,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t [metadata] lock-version = "1.1" python-versions = "^3.6.8" -content-hash = "24dd5a8aac56488e2fa9b754a5b71e6cbcf51006166910e4e683bc09533a1d21" +content-hash = "51e9191a579c58d229a6a32b419f3f8d891d2e7ce12decb6f74a283fb46a851a" [metadata.files] absl-py = [ @@ -3068,8 +3123,8 @@ argon2-cffi = [ {file = "argon2_cffi-20.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a84934bd818e14a17943de8099d41160da4a336bcc699bb4c394bbb9b94bd32"}, ] astroid = [ - {file = "astroid-2.5.3-py3-none-any.whl", hash = "sha256:bea3f32799fbb8581f58431c12591bc20ce11cbc90ad82e2ea5717d94f2080d5"}, - {file = "astroid-2.5.3.tar.gz", hash = "sha256:ad63b8552c70939568966811a088ef0bc880f99a24a00834abd0e3681b514f91"}, + {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"}, + {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"}, ] astunparse = [ {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, @@ -3114,9 +3169,20 @@ bpemb = [ bs4 = [ {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, ] +bt = [ + {file = "bt-0.2.9-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:5654e5639ffd0778714d567ae14fb06d485e43becb720a0dc5a38b405251485e"}, + {file = "bt-0.2.9-cp36-cp36m-win_amd64.whl", hash = "sha256:5d6dd92fd6aa1efbade3efee980add709fc591a8b007643016f2e0cbf7372daf"}, + {file = "bt-0.2.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:637fd917f57d0302b17968025650be7d8b2b6da32c2c7daea9ae48f6a66cbb8c"}, + {file = "bt-0.2.9-cp37-cp37m-win_amd64.whl", hash = "sha256:55a9cf1ca3aa2c425a48fc1e8d324cd5959d4d189e221f7744c1c6e30149c61b"}, + {file = "bt-0.2.9-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5dbf1ced108a6f2086b927dcf17ecd1deff7d98266d7f52ab3ff43be4a349ec3"}, + {file = "bt-0.2.9-cp38-cp38-win_amd64.whl", hash = "sha256:e77a56adc7bcac285df2c70e714b8724fc92fa1beb8de4b1fd991ab3862f84b0"}, + {file = "bt-0.2.9-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a1aed6e0ee3fa3b6f5bcef1f26d29e2a96c8329ea54894362014e20012de053b"}, + {file = "bt-0.2.9-cp39-cp39-win_amd64.whl", hash = "sha256:399bedbf48026899e85536ea8cbd21459461346638ae20f439a09bc75fa131e9"}, + {file = "bt-0.2.9.tar.gz", hash = "sha256:d162d71aaaaf7265a848d1fc0040f503add32dac2f9f3127bece0d74c22efb9b"}, +] cachetools = [ - {file = "cachetools-4.2.1-py3-none-any.whl", hash = "sha256:1d9d5f567be80f7c07d765e21b814326d78c61eb0c3a637dffc0e5d1796cb2e2"}, - {file = "cachetools-4.2.1.tar.gz", hash = "sha256:f469e29e7aa4cff64d8de4aad95ce76de8ea1125a16c68e0d93f65c3c3dc92e9"}, + {file = "cachetools-4.2.2-py3-none-any.whl", hash = "sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001"}, + {file = "cachetools-4.2.2.tar.gz", hash = "sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff"}, ] certifi = [ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, @@ -3268,8 +3334,8 @@ datetime = [ {file = "DateTime-4.3.tar.gz", hash = "sha256:5cef605bab8259ff61281762cdf3290e459fbf0b4719951d5fab967d5f2ea0ea"}, ] decorator = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, + {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"}, + {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"}, ] defusedxml = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, @@ -3346,6 +3412,10 @@ ephem = [ fbprophet = [ {file = "fbprophet-0.6.tar.gz", hash = "sha256:de7eaa16500e4dba69a0706906775cbe96668a6fe48354ee2fff820d9e6a899b"}, ] +ffn = [ + {file = "ffn-0.3.6-py2.py3-none-any.whl", hash = "sha256:1e55e8caf6b63dce2f164cc4d3b8a02b678be7ed12990cafb2006b818a9a09e7"}, + {file = "ffn-0.3.6.tar.gz", hash = "sha256:4a79e72e06ff328e333ffe97010b1ce110bcd694fcd03be7351bf5065cd273e8"}, +] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, @@ -3354,8 +3424,8 @@ finviz = [ {file = "finviz-1.4.1.tar.gz", hash = "sha256:7b1a2a730ce8e09dfe64aad30b499a3adaf0fdf2d52cba6dd3ef9813248182be"}, ] finvizfinance = [ - {file = "finvizfinance-0.9.6-py3-none-any.whl", hash = "sha256:18db3403bac6c3e6dae9d2c8d555df10d228e34daf02e4d50bb69c46e578cb7d"}, - {file = "finvizfinance-0.9.6.tar.gz", hash = "sha256:9a5f5a1a1e67e810e4f6819cda0e3966f95313ff27d06466ae8a91e5a0e0cdbe"}, + {file = "finvizfinance-0.9.7-py3-none-any.whl", hash = "sha256:f4e944ba28bb715d31dbe4c738572a6f7fad8feddbd5223a693d81eb31dd5365"}, + {file = "finvizfinance-0.9.7.tar.gz", hash = "sha256:92e6454686ed736164e0252137bb491b77f0dd6a32ea82677082de880afc6bfb"}, ] flair = [ {file = "flair-0.7-py3-none-any.whl", hash = "sha256:42c7e8074f67eeac722d68f4d5a9ad602377a76f5c4a2a190b916645b9be6ff2"}, @@ -3421,8 +3491,8 @@ gensim = [ {file = "gensim-3.8.3.tar.gz", hash = "sha256:786adb0571f75114e9c5f7a31dd2e6eb39a9791f22c8757621545e2ded3ea367"}, ] google-auth = [ - {file = "google-auth-1.29.0.tar.gz", hash = "sha256:010f011c4e27d3d5eb01106fba6aac39d164842dfcd8709955c4638f5b11ccf8"}, - {file = "google_auth-1.29.0-py2.py3-none-any.whl", hash = "sha256:f30a672a64d91cc2e3137765d088c5deec26416246f7a9e956eaf69a8d7ed49c"}, + {file = "google-auth-1.30.0.tar.gz", hash = "sha256:9ad25fba07f46a628ad4d0ca09f38dcb262830df2ac95b217f9b0129c9e42206"}, + {file = "google_auth-1.30.0-py2.py3-none-any.whl", hash = "sha256:588bdb03a41ecb4978472b847881e5518b5d9ec6153d3d679aa127a55e13b39f"}, ] google-auth-oauthlib = [ {file = "google-auth-oauthlib-0.4.4.tar.gz", hash = "sha256:09832c6e75032f93818edf1affe4746121d640c625a5bef9b5c96af676e98eee"}, @@ -3948,8 +4018,8 @@ nest-asyncio = [ {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"}, ] networkx = [ - {file = "networkx-2.5.1-py3-none-any.whl", hash = "sha256:0635858ed7e989f4c574c2328380b452df892ae85084144c73d8cd819f0c4e06"}, - {file = "networkx-2.5.1.tar.gz", hash = "sha256:109cd585cac41297f71103c3c42ac6ef7379f29788eb54cb751be5a663bb235a"}, + {file = "networkx-2.5-py3-none-any.whl", hash = "sha256:8c5812e9f798d37c50570d15c4a69d5710a18d77bafc903ee9c5fba7454c616c"}, + {file = "networkx-2.5.tar.gz", hash = "sha256:7978955423fbc9639c10498878be59caf99b44dc304c2286162fd24b458c1602"}, ] notebook = [ {file = "notebook-6.3.0-py3-none-any.whl", hash = "sha256:cb271af1e8134e3d6fc6d458bdc79c40cbfc84c1eb036a493f216d58f0880e92"}, @@ -4058,6 +4128,10 @@ pandas = [ {file = "pandas-1.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:edda9bacc3843dfbeebaf7a701763e68e741b08fccb889c003b0a52f0ee95782"}, {file = "pandas-1.1.5.tar.gz", hash = "sha256:f10fc41ee3c75a474d3bdf68d396f10782d013d7f67db99c0efbfd0acb99701b"}, ] +pandas-datareader = [ + {file = "pandas-datareader-0.9.0.tar.gz", hash = "sha256:b2cbc1e16a6ab9ff1ed167ae2ea92839beab9a20823bd00bdfb78155fa04f891"}, + {file = "pandas_datareader-0.9.0-py3-none-any.whl", hash = "sha256:fd9ce1238114dd199c32662a9d788271032580960bcf029e64b814d33d17477c"}, +] pandas-ta = [ {file = "pandas_ta-0.2.45b.tar.gz", hash = "sha256:2adde7d3ecec5d96b032b6d80491fb2c9b7952626a3c2cf01fe7ca78f35ebe38"}, ] @@ -4254,8 +4328,8 @@ pygments = [ {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, ] pylint = [ - {file = "pylint-2.7.4-py3-none-any.whl", hash = "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a"}, - {file = "pylint-2.7.4.tar.gz", hash = "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"}, + {file = "pylint-2.8.2-py3-none-any.whl", hash = "sha256:f7e2072654a6b6afdf5e2fb38147d3e2d2d43c89f648637baab63e026481279b"}, + {file = "pylint-2.8.2.tar.gz", hash = "sha256:586d8fa9b1891f4b725f587ef267abe2a1bad89d6b184520c7f07a253dd6e217"}, ] pymeeus = [ {file = "PyMeeus-0.5.11.tar.gz", hash = "sha256:bb9d670818d8b0594317b48a7dadea02a0594e5344263bf2054e1a011c8fed55"}, @@ -4288,6 +4362,10 @@ pyportfolioopt = [ {file = "PyPortfolioOpt-1.4.1-py3-none-any.whl", hash = "sha256:73f0b4ac2a5d879879a7a91af038b4d7ff82e9998fb2cad7c249302294e8c09e"}, {file = "PyPortfolioOpt-1.4.1.tar.gz", hash = "sha256:e6c8e983db92cd13caf41d87fb4dcd945d37053f28b75dd19b5be699b6b1d19e"}, ] +pyprind = [ + {file = "PyPrind-2.11.3-py2.py3-none-any.whl", hash = "sha256:cc8edb66aabb18f25f7a3cce65312b3bc64b49992ddc93ba4cf511e5e25c1be3"}, + {file = "PyPrind-2.11.3.tar.gz", hash = "sha256:e37dcab6e1a9c8e0a7f0fce65fde7a79e2deda1c75aa015910a49e2137b54cbf"}, +] pyrsistent = [ {file = "pyrsistent-0.14.11.tar.gz", hash = "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"}, ] @@ -4332,8 +4410,8 @@ python-dateutil = [ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] python-dotenv = [ - {file = "python-dotenv-0.17.0.tar.gz", hash = "sha256:471b782da0af10da1a80341e8438fca5fadeba2881c54360d5fd8d03d03a4f4a"}, - {file = "python_dotenv-0.17.0-py2.py3-none-any.whl", hash = "sha256:49782a97c9d641e8a09ae1d9af0856cc587c8d2474919342d5104d85be9890b2"}, + {file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"}, + {file = "python_dotenv-0.17.1-py2.py3-none-any.whl", hash = "sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544"}, ] pytrends = [ {file = "pytrends-4.7.3-py3-none-any.whl", hash = "sha256:88d92e398b9408c79fd92946ddc2b03fa622301233bc49f16804042eb2897328"}, @@ -4360,16 +4438,11 @@ pywin32 = [ {file = "pywin32-300-cp39-cp39-win_amd64.whl", hash = "sha256:b1609ce9bd5c411b81f941b246d683d6508992093203d4eb7f278f4ed1085c3f"}, ] pywinpty = [ - {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, - {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"}, - {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"}, - {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"}, - {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"}, - {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"}, - {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"}, - {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"}, - {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, - {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, + {file = "pywinpty-1.0.1-cp36-none-win_amd64.whl", hash = "sha256:739094e8d0d685a64c92ff91424cf43da9296110349036161ab294268e444d05"}, + {file = "pywinpty-1.0.1-cp37-none-win_amd64.whl", hash = "sha256:5447b8c158e5807237f80ea4e14262f0c05ff7c4d39f1c4b697ea6e8920786b2"}, + {file = "pywinpty-1.0.1-cp38-none-win_amd64.whl", hash = "sha256:aa3e4178503ff6be3e8a1d9ae4ce77de9058308562dbf26b505a51583be9f02d"}, + {file = "pywinpty-1.0.1-cp39-none-win_amd64.whl", hash = "sha256:58e23d59891e624d478ec7bcc42ced0ecfbf0a4e7cb0217de714f785f71c2461"}, + {file = "pywinpty-1.0.1.tar.gz", hash = "sha256:b3512d4a964a0abae1b77b6908917c62ea0ad7d8178696e4e973877fe9e820f9"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -4596,39 +4669,39 @@ sacremoses = [ {file = "sacremoses-0.0.45.tar.gz", hash = "sha256:58176cc28391830789b763641d0f458819bebe88681dac72b41a19c0aedc07e9"}, ] scikit-learn = [ - {file = "scikit-learn-0.24.1.tar.gz", hash = "sha256:a0334a1802e64d656022c3bfab56a73fbd6bf4b1298343f3688af2151810bbdf"}, - {file = "scikit_learn-0.24.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:9bed8a1ef133c8e2f13966a542cb8125eac7f4b67dcd234197c827ba9c7dd3e0"}, - {file = "scikit_learn-0.24.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a36e159a0521e13bbe15ca8c8d038b3a1dd4c7dad18d276d76992e03b92cf643"}, - {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"}, - {file = "scikit_learn-0.24.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a29460499c1e62b7a830bb57ca42e615375a6ab1bcad053cd25b493588348ea8"}, - {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"}, - {file = "scikit_learn-0.24.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c13ebac42236b1c46397162471ea1c46af68413000e28b9309f8c05722c65a09"}, - {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"}, - {file = "scikit_learn-0.24.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c7f4eb77504ac586d8ac1bde1b0c04b504487210f95297235311a0ab7edd7e38"}, - {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"}, + {file = "scikit-learn-0.24.2.tar.gz", hash = "sha256:d14701a12417930392cd3898e9646cf5670c190b933625ebe7511b1f7d7b8736"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:d5bf9c863ba4717b3917b5227463ee06860fc43931dc9026747de416c0a10fee"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5beaeb091071625e83f5905192d8aecde65ba2f26f8b6719845bbf586f7a04a1"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:06ffdcaaf81e2a3b1b50c3ac6842cfb13df2d8b737d61f64643ed61da7389cde"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:fec42690a2eb646b384eafb021c425fab48991587edb412d4db77acc358b27ce"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:5ff3e4e4cf7592d36541edec434e09fb8ab9ba6b47608c4ffe30c9038d301897"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3cbd734e1aefc7c5080e6b6973fe062f97c26a1cdf1a991037ca196ce1c8f427"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-win32.whl", hash = "sha256:f74429a07fedb36a03c159332b914e6de757176064f9fed94b5f79ebac07d913"}, + {file = "scikit_learn-0.24.2-cp36-cp36m-win_amd64.whl", hash = "sha256:dd968a174aa82f3341a615a033fa6a8169e9320cbb46130686562db132d7f1f0"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:49ec0b1361da328da9bb7f1a162836028e72556356adeb53342f8fae6b450d47"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f18c3ed484eeeaa43a0d45dc2efb4d00fc6542ccdcfa2c45d7b635096a2ae534"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cdf24c1b9bbeb4936456b42ac5bd32c60bb194a344951acb6bfb0cddee5439a4"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d177fe1ff47cc235942d628d41ee5b1c6930d8f009f1a451c39b5411e8d0d4cf"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f3ec00f023d84526381ad0c0f2cff982852d035c921bbf8ceb994f4886c00c64"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:ae19ac105cf7ce8c205a46166992fdec88081d6e783ab6e38ecfbe45729f3c39"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-win32.whl", hash = "sha256:f0ed4483c258fb23150e31b91ea7d25ff8495dba108aea0b0d4206a777705350"}, + {file = "scikit_learn-0.24.2-cp37-cp37m-win_amd64.whl", hash = "sha256:39b7e3b71bcb1fe46397185d6c1a5db1c441e71c23c91a31e7ad8cc3f7305f9a"}, + {file = "scikit_learn-0.24.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:90a297330f608adeb4d2e9786c6fda395d3150739deb3d42a86d9a4c2d15bc1d"}, + {file = "scikit_learn-0.24.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f1d2108e770907540b5248977e4cff9ffaf0f73d0d13445ee938df06ca7579c6"}, + {file = "scikit_learn-0.24.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1eec963fe9ffc827442c2e9333227c4d49749a44e592f305398c1db5c1563393"}, + {file = "scikit_learn-0.24.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2db429090b98045d71218a9ba913cc9b3fe78e0ba0b6b647d8748bc6d5a44080"}, + {file = "scikit_learn-0.24.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:62214d2954377fcf3f31ec867dd4e436df80121e7a32947a0b3244f58f45e455"}, + {file = "scikit_learn-0.24.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8fac72b9688176922f9f54fda1ba5f7ffd28cbeb9aad282760186e8ceba9139a"}, + {file = "scikit_learn-0.24.2-cp38-cp38-win32.whl", hash = "sha256:ae426e3a52842c6b6d77d00f906b6031c8c2cfdfabd6af7511bb4bc9a68d720e"}, + {file = "scikit_learn-0.24.2-cp38-cp38-win_amd64.whl", hash = "sha256:038f4e9d6ef10e1f3fe82addc3a14735c299866eb10f2c77c090410904828312"}, + {file = "scikit_learn-0.24.2-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:48f273836e19901ba2beecd919f7b352f09310ce67c762f6e53bc6b81cacf1f0"}, + {file = "scikit_learn-0.24.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a2a47449093dcf70babc930beba2ca0423cb7df2fa5fd76be5260703d67fa574"}, + {file = "scikit_learn-0.24.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0e71ce9c7cbc20f6f8b860107ce15114da26e8675238b4b82b7e7cd37ca0c087"}, + {file = "scikit_learn-0.24.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2754c85b2287333f9719db7f23fb7e357f436deed512db3417a02bf6f2830aa5"}, + {file = "scikit_learn-0.24.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:7be1b88c23cfac46e06404582215a917017cd2edaa2e4d40abe6aaff5458f24b"}, + {file = "scikit_learn-0.24.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4e6198675a6f9d333774671bd536668680eea78e2e81c0b19e57224f58d17f37"}, + {file = "scikit_learn-0.24.2-cp39-cp39-win32.whl", hash = "sha256:cbdb0b3db99dd1d5f69d31b4234367d55475add31df4d84a3bd690ef017b55e2"}, + {file = "scikit_learn-0.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:40556bea1ef26ef54bc678d00cf138a63069144a0b5f3a436eecd8f3468b903e"}, ] scipy = [ {file = "scipy-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4f12d13ffbc16e988fa40809cbbd7a8b45bc05ff6ea0ba8e3e41f6f4db3a9e47"}, diff --git a/pyproject.toml b/pyproject.toml index 8aa0ad91f60..65ac1931eb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,7 @@ python-coinmarketcap = "^0.2" oandapyV20 = "^0.6.3" degiroapi = "^0.9.5" valinvest = "^0.0.2" +bt = "^0.2.9" [tool.poetry.dev-dependencies] pytest = "^6.2.2" diff --git a/requirements-full.txt b/requirements-full.txt index 9e7ae93c693..a8c07a921e4 100644 --- a/requirements-full.txt +++ b/requirements-full.txt @@ -16,6 +16,7 @@ black==20.8b1; python_version >= "3.6" bleach==3.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" bpemb==0.3.3; python_version >= "3.6" bs4==0.0.1 +bt==0.2.9 cachetools==4.2.1; python_version >= "3.5" and python_version < "4.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") certifi==2020.12.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" cffi==1.14.5; implementation_name == "pypy" and python_version >= "3.6" diff --git a/requirements-old.txt b/requirements-old.txt index 6dcb6d00577..77b359de143 100644 --- a/requirements-old.txt +++ b/requirements-old.txt @@ -9,6 +9,7 @@ beautifulsoup4==4.9.3 black==20.8b1; python_version >= "3.6" bpemb==0.3.2; python_version >= "3.6" bs4==0.0.1 +bt==0.2.9 cachetools==4.2.1; python_version >= "3.5" and python_version < "4.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") certifi==2020.12.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" chardet==3.0.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" diff --git a/requirements-win.txt b/requirements-win.txt index 399b80c6f67..1f658e78cc9 100644 --- a/requirements-win.txt +++ b/requirements-win.txt @@ -9,6 +9,7 @@ beautifulsoup4==4.9.3 black==20.8b1; python_version >= "3.6" bpemb==0.3.2; python_version >= "3.6" bs4==0.0.1 +bt==0.2.9 cachetools==4.2.1; python_version >= "3.5" and python_version < "4.0" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0") certifi==2020.12.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" chardet==3.0.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" diff --git a/requirements.txt b/requirements.txt index 122170ff40a..4a189ab8556 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,7 @@ beautifulsoup4==4.9.3; python_version >= "3.5" black==20.8b1; python_version >= "3.6" bleach==3.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" bs4==0.0.1 +bt==0.2.9 certifi==2020.12.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" cffi==1.14.5; implementation_name == "pypy" and python_version >= "3.6" 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" diff --git a/terminal.py b/terminal.py index 5afbfad080b..6765b0f68f5 100644 --- a/terminal.py +++ b/terminal.py @@ -33,6 +33,7 @@ from gamestonk_terminal.cryptocurrency import crypto_controller from gamestonk_terminal.screener import screener_controller from gamestonk_terminal.portfolio_optimization import po_controller from gamestonk_terminal.forex import fx_controller +from gamestonk_terminal.backtesting import bt_controller # pylint: disable=too-many-statements,too-many-branches @@ -76,6 +77,7 @@ def main(): "res", "fa", "ta", + "bt", "dd", "eda", "pred", @@ -358,6 +360,11 @@ def main(): elif ns_known_args.opt == "scr": b_quit = screener_controller.menu() + elif ns_known_args.opt == "bt": + b_quit = bt_controller.menu( + s_ticker.split(".")[0] if "." in s_ticker else s_ticker, s_start + ) + else: print("Shouldn't see this command!") continue