Bollinger Bands Example¶
This example shows how to write an example bollinger bands strategy, and execute a backtest of it
Python source code: ../../examples/bollinger_bands.py
from boatwright import Strategy, Backtest
from boatwright.Data import CSVdatabase
from boatwright.Brokers import Broker, BacktestBroker
from boatwright.Orders import MarketOrder, TrailingOrder
from boatwright.Indicators import bollinger_bands, crossover
from boatwright.Visualization import plot_backtest
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
class BollingerBands(Strategy):
"""
An example strategy using Bollinger Bands (BB) as a technical indicator and Trailing Orders
"""
def __init__(self, period:int=7, std_devs:int=2, symbol:str="BTC", strategy_id="BollingerBands"):
super().__init__(symbol, strategy_id)
self.p["period"] = period
self.p["std_devs"] = std_devs
self.position = False
def calculate_signals(self, df):
self.df = df
self.df["avg"], self.df["lower"], self.df["upper"] = bollinger_bands(df["close"], period=self.p["period"], n_std_dev=self.p["std_devs"])
self.df["buy_trigger"] = crossover(self.df["close"], self.df["lower"])
self.df["sell_trigger"] = crossover(self.df["close"], self.df["upper"])
return self.df
def calc_prerequisite_data_length(self):
return self.p['period']
def step(self, row):
buy_trigger = row["buy_trigger"]
sell_trigger = row["sell_trigger"]
datetime = row["datetime"]
price = row["close"]
if buy_trigger == -1:
market_buy = MarketOrder(symbol=self.symbol, side="BUY", time_opened=datetime, frac=0.25)
# This trailing orders will execute the market order when the price increases 1% with respect to the minimum price (since order placement)
# This should help avoid entering a trade if the price is continueing to swing down
buy_order = TrailingOrder(market_buy, price=price, time_opened=datetime, pct_greater_than_min=0.25, time_in_force=datetime+timedelta(minutes=30))
self.broker.place_order(buy_order)
if sell_trigger == 1:
market_sell = MarketOrder(symbol=self.symbol, side="SELL", time_opened=datetime, frac=0.5)
# This trailing orders will execute the market order when the price decreases 1% with respect to the maximum price (since order placement)
# This should help avoid exiting a trade if the price is continueing to swing up
sell_order = TrailingOrder(market_sell, price=price, time_opened=datetime, pct_less_than_max=-0.25, time_in_force=datetime+timedelta(minutes=30))
self.broker.place_order(sell_order)
def plot_info(self):
info = {
"avg": {"ax_n": 2, "color":"C0", "linestyle":"-"},
"lower": {"ax_n": 2, "color":"C0", "linestyle":"--"},
"upper": {"ax_n": 2, "color":"C0", "linestyle":"--"},
"buy_trigger": {"ax_n": 3, "color":"lime"},
"sell_trigger": {"ax_n": 3, "color":"red"},
}
return info
# -------------------------------------------------------
# 1. define the strategy
strategy = BollingerBands(period=60, std_devs=2, symbol="BTC")
# 2. load data for the backtet (including prequisite data for necessary for signal generation)
database = CSVdatabase(source="QUICKSTART", debug=False, dir="quickstart_data/")
start = datetime(year=2024, month=1, day=1, hour=1, minute=0)
end = datetime(year=2024, month=1, day=20, hour=12, minute=0)
data = database.load(symbol=strategy.symbol, start=start, end=end, prerequisite_data_length=strategy.calc_prerequisite_data_length(), granularity=1, granularity_unit="MINUTE", verbose=True)
# 3. declare and run the backtest
broker_model = BacktestBroker(taker_fee=0, maker_fee=0, slippage=0, quote_symbol="USD")
backtest = Backtest(strategy=strategy, data=data, broker=broker_model, debug=False)
backtest.run(verbose=True)
# 4. analyze results
plot_backtest(backtest)
plt.show()


