Files
OpenBB/openbb_terminal/stocks/backtesting/bt_controller.py
Chavithra 9e1a58e2db Here we merge all API Refactor related branches (#2236)
* Update api.py

* Updated forex menu

* refactor ycrv command

* refactor ycrv command black

* refactor ecocal command

* Minh changes

* Adding space to test pushing

* title fix ecocal df

* get economic calendar annotation

* fix investingcom tests

* refactor index command

* refactor overview command

* give defaults to wsj view function args

* rename date args investincom

* refacto bigmac command

* fix ecocal typo

* refactor rtps command

* alphavantage gdp

* alphavantage gdp per capita

* alphavantage cpi

* alphavantage tyld

* alphavantage inf

* refactor macro command

* refactor macro command w helpers

* refactor treasury command

* fix macro on terminal

* treasury labels

* refactor maturities

* update treasury maturities doc strings

* refactor get economic calendar finhub

* refactor map command api

* display map filter choices

* route economy api to performance map

* route economy api to performance map

* display group choices on valuation command

* refactor performance and valuation commands

* refactor spectrum model and view

* add choices to spectrum controller

* delete image after view

* fix model tests finviz

* fix finciz view tests

* refactor futures

* fix some tests

* fix more tests

* fix controller test

* refactor fred series notes

* update fred notes docstring

* refacto fred series ids

* fix pred and qa when empty datasets

* refactor fred

* uncomment stuff

* refacto get series data

* fix some tests

* set defaults on args

* refactor fred yield curve

* black

* fix spell and remove ecocal names

* fix linting

* linting

* pylint fix

* change dangerous defaults

* Working through crypto fixes (#2256)

* Working through crypto fixes

* Continued adding crypto stuff

* Added crypto overview

* Added test fixes

* Added fixtures

* Fixed tests

* Fixed charting issue

* Removed broken APIs

* Final adjustments

* Added test fixes

* map get groups and get ycrv countries into old api

* exposed econdb helper funcs

* remove helpers

* refactor search indices

* linting

* refactor arg currency

* pylint from currency

* Started switching crpyto ascending to ascend

* Merging

* Portfolio model arguements, params, and docstring

* Refactored for etf commands (#2292)

* Refactored for etf commands

* Fixed tests

* Added load command

* Fixed menu

* Portfolio logic fixes

* Added econometrics (#2260)

* Added econometrics

* Fixed tests

* Simplified API

* Added test fixes

* Added test csv

* Allowed examples to be loaded

* Fund refactor (#2291)

* Fund refactor

* Changed fund_name and fund to name

* Changed ascending to ascend

* Stock menu refactoring for easier API usage (#2194)

* Stocks  refactoring for easier API usage

* Linting

* Refactor newly added features

* Linting

* Fixing tests

* Refactor common files used by stocks menu

* Fixing flake8

* Fix linting and tests

* Linting

* Fix flake8

* refactor insider_data

* refactor mentions

* refactor watchlist

* refactor sentiment

* refactor sentiment

* fix yahoofinance tests

* refactor load and candle

* refactor get_news and display_news

* refactor stocks.ins.act

* candle default matplotlib

* fix yahoofinance_view tests

* fix ark model tests

* fix ark view tests

* fix business insider model

* fix business insider view

* refactor csimarket model

* fix tests csi market model

* update dd controller

* fix get suppliers tests

* fix dd controller tests

* fix finhub tests

* fix finviz tests

* fix fmp tests

* fix marketwatch tests

* corrected argument keywords in test_bt_model

* corrected argument keywords in test_bt_view

* refactor fa controller

* refactor marketwatch view

* refactor gov controller

* fix tests fa av

* fix tests elect

* fix dcf tests

* fix polygon tests

* fix fmp tests

* fix quiverquant tests

* fix yahoofinance fa tests

* fix more fa tests

* fix insider tests

* fix more tests

* fix more tests

* fix options tests

* fix stock gov tests

* fix tests test_ba_controller

* fix tests for test_finviz_compare_model.py

* fixed 2 tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* fix final tests

* fixed tests

* fixed tests

* Fix tests

* black

* forgot to black tests

* fixed tests

* fixed tests

* fixed tests

* fixed tests

* flakefix

* Tests + code : Stocks / Discovery

* fix tests

* added recorder

* fixed tests

* fixed tests

* black

* black

* remove unused imports

* refactor display raw

* sia dicts fix

* pylint

* linting

* remove dangerous default

* fix tests

* fix beta model test

* black

* skip screener qa test

* change sector path to sectors

* update tests readme

* fix metric defaults

* black

* substitute lost ticker

* defaults cpic

* another round on sia

* refactor cramer

* reduce default tweets on sentiment

* refactor yf hist, corr, volume

* arkorders default

* refactor income, balance, cashflow

* refacto scorr, screener, getfinnhub

* refactor stockgrid

* ibkr refactor

* another round on stockgrid

* add dividens end point

* refactor discovery endpoints

* update docstrings with similar input

* refactor messages

* refactor ba

* refactor regioons

* refactor twitter sentiment

* refactor hist

* refactor regions

* give default to timeframe

* refactor bunch of defaults and arg names

* remove leftover imports

* refactor vwap

* let tests run

* fix tests

* fix stock tests

* fix stockanalysis tests

* flake

* MYPY

* Made important changes

* added fixes

* Fixed big issue

* Added fixes to tests

* fix qa tests

* fix tests

* fix 1 more test

* last stocks failing

* fix crypto test

Co-authored-by: Chavithra PARANA <chavithra@gmail.com>
Co-authored-by: montezdesousa <montezdesousa@gmail.com>
Co-authored-by: hjoaquim <h.joaquim@campus.fct.unl.pt>
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
Co-authored-by: colin99d <colin99delahunty@gmail.com>

* fix portfolio tests

* change period to window

* update ca docstrings

* refactor get_similar_companies func

* Fixed

* Update CI

* Update CI 2

* Update CI 3

* Update dependencies

Co-authored-by: colin99d <colin99delahunty@gmail.com>
Co-authored-by: Colin Delahunty <72827203+colin99d@users.noreply.github.com>
Co-authored-by: montezdesousa <montezdesousa@gmail.com>
Co-authored-by: James Simmons <simmonsj330@gmail.com>
Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com>
Co-authored-by: minhhoang1023 <40023817+minhhoang1023@users.noreply.github.com>
Co-authored-by: jose-donato <43375532+jose-donato@users.noreply.github.com>
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
Co-authored-by: northern-64bit <75195383+northern-64bit@users.noreply.github.com>
Co-authored-by: hjoaquim <h.joaquim@campus.fct.unl.pt>
2022-08-17 15:28:54 +01:00

290 lines
9.1 KiB
Python

"""Backtesting Controller Module"""
__docformat__ = "numpy"
import argparse
from typing import List
import logging
import matplotlib as mpl
import pandas as pd
from prompt_toolkit.completion import NestedCompleter
from openbb_terminal import feature_flags as obbff
from openbb_terminal.helper_funcs import (
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_non_negative_float,
check_positive,
valid_date,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.rich_config import console, MenuText
from openbb_terminal.decorators import log_start_end
# 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()
# Restore backend matplotlib used
# pylint: disable=wrong-import-position
from openbb_terminal.stocks.backtesting import bt_view # noqa: E402
logger = logging.getLogger(__name__)
mpl.use(default_backend)
class BacktestingController(BaseController):
"""Backtesting Controller class"""
CHOICES_COMMANDS = ["ema", "ema_cross", "rsi", "whatif"]
PATH = "/stocks/bt/"
def __init__(self, ticker: str, stock: pd.DataFrame, queue: List[str] = None):
"""Constructor"""
super().__init__(queue)
self.ticker = ticker
self.stock = stock
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
self.completer = NestedCompleter.from_nested_dict(choices)
def print_help(self):
"""Print help"""
mt = MenuText("stocks/bt/")
mt.add_param("_ticker", self.ticker.upper())
mt.add_raw("\n")
mt.add_cmd("whatif")
mt.add_raw("\n")
mt.add_cmd("ema")
mt.add_cmd("ema_cross")
mt.add_cmd("rsi")
console.print(text=mt.menu_text, menu="Stocks - Backtesting")
def custom_reset(self):
"""Class specific component of reset command"""
if self.ticker:
return ["stocks", f"load {self.ticker}", "bt"]
return []
@log_start_end(log=logger)
def call_whatif(self, other_args: List[str]):
"""Call whatif"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="whatif",
description="Displays what if scenario of having bought X shares at date Y",
)
parser.add_argument(
"-d",
"--date",
default=None,
dest="date_shares_acquired",
type=valid_date,
help="Date at which the shares were acquired",
)
parser.add_argument(
"-n",
"--number",
default=1.0,
type=check_non_negative_float,
help="Number of shares acquired",
dest="num_shares_acquired",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-d")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
bt_view.display_whatif_scenario(
symbol=self.ticker,
num_shares_acquired=ns_parser.num_shares_acquired,
date_shares_acquired=ns_parser.date_shares_acquired,
)
@log_start_end(log=logger)
def call_ema(self, other_args: List[str]):
"""Call EMA strategy"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="ema",
description="Strategy where stock is bought when Price > EMA(l)",
)
parser.add_argument(
"-l",
default=20,
dest="length",
type=check_positive,
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",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
bt_view.display_simple_ema(
symbol=self.ticker,
data=self.stock,
ema_length=ns_parser.length,
spy_bt=ns_parser.spy,
no_bench=ns_parser.no_bench,
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_ema_cross(self, other_args: List[str]):
"""Call EMA Cross strategy"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="ema_cross",
description="Cross between a long and a short Exponential Moving Average.",
)
parser.add_argument(
"-l",
"--long",
default=50,
dest="long",
type=check_positive,
help="Long EMA period",
)
parser.add_argument(
"-s",
"--short",
default=20,
dest="short",
type=check_positive,
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",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
if ns_parser.long < ns_parser.short:
console.print("Short EMA period is longer than Long EMA period\n")
bt_view.display_ema_cross(
symbol=self.ticker,
data=self.stock,
short_ema=ns_parser.short,
long_ema=ns_parser.long,
spy_bt=ns_parser.spy,
no_bench=ns_parser.no_bench,
shortable=ns_parser.shortable,
export=ns_parser.export,
)
@log_start_end(log=logger)
def call_rsi(self, other_args: List[str]):
"""Call RSI Strategy"""
parser = argparse.ArgumentParser(
add_help=False,
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
prog="rsi",
description="""Strategy that buys when the stock is less than a threshold
and shorts when it exceeds a threshold.""",
)
parser.add_argument(
"-p",
"--periods",
dest="periods",
help="Number of periods for RSI calculation",
type=check_positive,
default=14,
)
parser.add_argument(
"-u",
"--high",
default=70,
dest="high",
type=check_positive,
help="High (upper) RSI Level",
)
parser.add_argument(
"-l",
"--low",
default=30,
dest="low",
type=check_positive,
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",
)
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
)
if ns_parser:
if ns_parser.high < ns_parser.low:
console.print("Low RSI value is higher than Low RSI value\n")
bt_view.display_rsi_strategy(
symbol=self.ticker,
data=self.stock,
periods=ns_parser.periods,
low_rsi=ns_parser.low,
high_rsi=ns_parser.high,
spy_bt=ns_parser.spy,
no_bench=ns_parser.no_bench,
shortable=ns_parser.shortable,
export=ns_parser.export,
)