mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 05:21:48 +00:00
300 lines
8.9 KiB
Python
300 lines
8.9 KiB
Python
"""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
|