diff --git a/gamestonk_terminal/backtesting/bt_controller.py b/gamestonk_terminal/backtesting/bt_controller.py index 152ab3c3c57..ebec9a06457 100644 --- a/gamestonk_terminal/backtesting/bt_controller.py +++ b/gamestonk_terminal/backtesting/bt_controller.py @@ -73,18 +73,21 @@ class BacktestingController: 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 startegy""" 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]): """Technical Analysis Menu""" - + plt.close("all") bt_controller = BacktestingController(ticker, start) bt_controller.call_help(None) diff --git a/gamestonk_terminal/backtesting/bt_helper.py b/gamestonk_terminal/backtesting/bt_helper.py index 1b03c0d0790..8cc1c3b9f5e 100644 --- a/gamestonk_terminal/backtesting/bt_helper.py +++ b/gamestonk_terminal/backtesting/bt_helper.py @@ -4,18 +4,43 @@ __docformat__ = "numpy" from datetime import datetime from typing import Union +import matplotlib 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.config_plot import PLOT_DPI, backend from gamestonk_terminal import feature_flags as gtff register_matplotlib_converters() +if backend is not None: + print(f"Setting matplotlib backend to {backend}") + matplotlib.use(backend) + +if matplotlib.get_backend() in ["Agg", "MacOSX"]: + print( + "You may encounter errors due to your MatplotLib backend. " + " Please configure in config_plot.py." + ) + def buy_and_hold(ticker: str, start: Union[str, datetime], name: str): - """Generates a backtest object for the given ticker""" + """ + 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 teh backtest (for labeling purposes) + + Returns + ------- + bt.Backtest object for buy and hold strategy + """ prices = bt.get(ticker, start=start) bt_strategy = bt.Strategy( name, @@ -40,6 +65,7 @@ def plot_bt(res: bt.backtest.Result, plot_title: str): Title of plot """ + matplotlib.use("TkAgg") _, 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="-") diff --git a/gamestonk_terminal/backtesting/bt_view.py b/gamestonk_terminal/backtesting/bt_view.py index 7377577c9bb..b2cab11fd33 100644 --- a/gamestonk_terminal/backtesting/bt_view.py +++ b/gamestonk_terminal/backtesting/bt_view.py @@ -11,7 +11,21 @@ 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" @@ -77,6 +91,21 @@ def simple_ema(ticker: str, start_date: Union[str, datetime], other_args: List[s 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" @@ -164,6 +193,21 @@ def ema_cross(ticker: str, start_date: Union[str, datetime], other_args: List[st 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", diff --git a/gamestonk_terminal/config_plot.py b/gamestonk_terminal/config_plot.py index d6b34c03719..7761e79dd20 100644 --- a/gamestonk_terminal/config_plot.py +++ b/gamestonk_terminal/config_plot.py @@ -1,5 +1,19 @@ PLOT_DPI = 100 +# Backend to use for plotting. After using the bt library, which somehow changes the +# backend to 'agg' it raises an error. Set this to avoid. +""" +More information: +https://matplotlib.org/stable/tutorials/introductory/usage.html#the-builtin-backends + +Common ones: +module://backend_interagg : This is what pycharm defaults to in Scientific Mode +MacOSX : Mac default. Does not work with backtesting +tkAgg : This uses the tkinter library. +Qt5Agg : This requires the PyQt5 package is installed +""" +backend = None + # Used when USE_PLOT_AUTOSCALING is set to False PLOT_HEIGHT = 500 PLOT_WIDTH = 800