book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Indicators

Custom Resolutions

Introduction

This page explains how to create and update indicators with data of a custom resolution.

Create Subscriptions

You need to subscribe to some market data in order to calculate indicator values.

Select Language:
qb = QuantBook()
symbol = qb.add_equity("SPY").symbol

Create Indicator Timeseries

You need to subscribe to some market data and create an indicator in order to calculate a timeseries of indicator values.

Follow these steps to create an indicator timeseries:

  1. Get some historical data.
  2. Select Language:
    # Request historical trading data with the daily resolution.
    history = qb.history[TradeBar](symbol, 70, Resolution.DAILY)
  3. Create a data-point indicator.
  4. In this example, use a 20-period 2-standard-deviation BollingerBands indicator.

    Select Language:
    bb = BollingerBands(20, 2)
  5. Create a RollingWindow for each attribute of the indicator to hold their values.
  6. Select Language:
    # Create a window dictionary to store RollingWindow objects.
    window = {}
    # Store the RollingWindow objects, index by key is the property of the indicator.
    window['time'] = RollingWindow[DateTime](50)
    window["bollingerbands"] = RollingWindow[float](50)
    window["lowerband"] = RollingWindow[float](50)
    window["middleband"] = RollingWindow[float](50)
    window["upperband"] = RollingWindow[float](50)
    window["bandwidth"] = RollingWindow[float](50)
    window["percentb"] = RollingWindow[float](50)
    window["standarddeviation"] = RollingWindow[float](50)
    window["price"] = RollingWindow[float](50)
  7. Attach a handler method to the indicator that updates the RollingWindow objects.
  8. Select Language:
    # Define an update function to add the indicator values to the RollingWindow object.
    def update_bollinger_band_window(sender: object, updated: IndicatorDataPoint) -> None:
        indicator = sender
        window['time'].add(updated.end_time)
        window["bollingerbands"].add(updated.value)
        window["lowerband"].add(indicator.lower_band.current.value)
        window["middleband"].add(indicator.middle_band.current.value)
        window["upperband"].add(indicator.upper_band.current.value)
        window["bandwidth"].add(indicator.band_width.current.value)
        window["percentb"].add(indicator.percent_b.current.value)
        window["standarddeviation"].add(indicator.standard_deviation.current.value)
        window["price"].add(indicator.price.current.value)
    
    bb.updated += update_bollinger_band_window

    When the indicator receives new data, the preceding handler method adds the new IndicatorDataPoint values into the respective RollingWindow.

  9. Create a TradeBarConsolidator to consolidate data into the custom resolution.
  10. Select Language:
    consolidator = TradeBarConsolidator(timedelta(days=7))
  11. Attach a handler method to feed data into the consolidator and updates the indicator with the consolidated bars.
  12. Select Language:
    def on_data_consolidated(sender, consolidated):
        bb.update(consolidated.end_time, consolidated.close)
    consolidator.data_consolidated += on_data_consolidated

    When the consolidator receives 7 days of data, the handler generates a 7-day TradeBar and update the indicator.

  13. Iterate through the historical market data and update the indicator.
  14. Select Language:
    for bar in history:
        consolidator.update(bar)
  15. Populate a DataFrame with the data in the RollingWindow objects.
  16. bb_dataframe = pd.DataFrame(window).set_index('time')
    Historical custom bollinger band data

Plot Indicators

Follow these steps to plot the indicator values:

  1. Select the columsn to plot.
  2. df = bb_dataframe[['lowerband', 'middleband', 'upperband', 'price']]
  3. Call the plot method.
  4. df.plot()
  5. Show the plot.
  6. plt.show()
    Line plot of custom bollinger band properties

Examples

The following examples demonstrate some common practices for researching with data point indicators.

Example 1: Quick Backtest On Bollinger Band

The following example demonstrates a quick backtest to testify the effectiveness of a Bollinger Band mean-reversal, using 5-miunte bar under the research enviornment.

Select Language:
# Instantiate the QuantBook instance for researching.
qb = QuantBook()
# Request SPY data to work with the indicator.
symbol = qb.add_equity("SPY").symbol

# Request historical trading data with the minute resolution.
history = qb.history[TradeBar](symbol, 1000, Resolution.MINUTE)

# Create a window dictionary to store RollingWindow objects.
window = {}
# Store the RollingWindow objects, index by key is the property of the indicator.
window['time'] = RollingWindow[DateTime](50)
window["bollingerbands"] = RollingWindow[float](50)
window["lowerband"] = RollingWindow[float](50)
window["middleband"] = RollingWindow[float](50)
window["upperband"] = RollingWindow[float](50)
window["price"] = RollingWindow[float](50)

# Define an update function to add the indicator values to the RollingWindow object.
def update_bollinger_band_window(sender: object, updated: IndicatorDataPoint) -> None:
    indicator = sender
    window['time'].add(updated.end_time)
    window["bollingerbands"].add(updated.value)
    window["lowerband"].add(indicator.lower_band.current.value)
    window["middleband"].add(indicator.middle_band.current.value)
    window["upperband"].add(indicator.upper_band.current.value)
    window["price"].add(indicator.price.current.value)

# Create the Bollinger Band indicator with parameters to be studied.
bb = BollingerBands(20, 2)
bb.updated += update_bollinger_band_window

# Create a TradeBarConsolidator to consolidate data into the custom resolution.
consolidator = TradeBarConsolidator(timedelta(minutes=5))
# Attach a handler method to feed data into the consolidator and updates the indicator with the consolidated bars.
def on_data_consolidated(sender, consolidated):
    bb.update(consolidated.end_time, consolidated.close)
consolidator.data_consolidated += on_data_consolidated

# Iterate through the historical market data and update the indicator.
for bar in history:
    consolidator.update(bar)

# Populate a DataFrame with the data in the RollingWindow objects.
bb_dataframe = pd.DataFrame(window).set_index('time')

# Create a order record and return column.
# Buy if the asset is underprice (below the lower band), sell if overpriced (above the upper band)
bb_dataframe["position"] = bb_dataframe.apply(lambda x: 1 if x.price < x.lowerband else -1 if x.price > x.upperband else 0, axis=1)
# Get the 1-day forward return.
bb_dataframe["return"] = bb_dataframe["price"].pct_change().shift(-1).fillna(0)
bb_dataframe["return"] = bb_dataframe["position"] * bb_dataframe["return"]

# Obtain the cumulative return curve as a mini-backtest.
equity_curve = (bb_dataframe["return"] + 1).cumprod()
equity_curve.plot(title="Equity Curve on BBand Mean Reversal", ylabel="Equity", xlabel="time")

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: