---
title: "Custom Resolutions"
description: "Learn how to create and handle indicators in custom resolution in the research environment in QuantConnect."
canonical: https://www.quantconnect.com/docs/v2/research-environment/indicators/custom-resolutions
markdown: https://www.quantconnect.com/docs/v2/research-environment/indicators/custom-resolutions.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/04%20Research%20Environment/06%20Indicators/07%20Custom%20Resolutions
modified: 2026-10-29
index: https://www.quantconnect.com/llms.txt
---

# Custom Resolutions

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Research Environment](https://www.quantconnect.com/docs/v2/research-environment.md) > [Indicators](https://www.quantconnect.com/docs/v2/research-environment/indicators.md) > Custom Resolutions

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## 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](https://www.quantconnect.com/docs/v2/research-environment/datasets/us-equity.md#03-Create-Subscriptions) in order to calculate indicator values.

```python
qb = QuantBook()
symbol = qb.add_equity("SPY").symbol
```

## Create Indicator Timeseries

You need to [subscribe to some market data](https://www.quantconnect.com/docs/v2/research-environment/indicators/custom-resolutions.md#03-Create-Subscriptions) 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](https://www.quantconnect.com/docs/v2/research-environment/datasets/key-concepts.md).
   ```python
   # Request historical trading data with the daily resolution.
   history = qb.history[TradeBar](symbol, 70, Resolution.DAILY)
   ```
2. Create a data-point indicator.
   In this example, use a 20-period 2-standard-deviation `BollingerBands` indicator.
   ```python
   bb = BollingerBands(20, 2)
   ```
3. Create a `RollingWindow` for each attribute of the indicator to hold their values.
   ```python
   # 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(50)
   window["bollingerbands"] = RollingWindow(50)
   window["lowerband"] = RollingWindow(50)
   window["middleband"] = RollingWindow(50)
   window["upperband"] = RollingWindow(50)
   window["bandwidth"] = RollingWindow(50)
   window["percentb"] = RollingWindow(50)
   window["standarddeviation"] = RollingWindow(50)
   window["price"] = RollingWindow(50)
   ```
4. Attach a handler method to the indicator that updates the `RollingWindow` objects.
   ```python
   # 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`.
5. Create a `TradeBarConsolidator` to [consolidate data](https://www.quantconnect.com/docs/v2/writing-algorithms/consolidating-data/getting-started.md) into the custom resolution.
   ```python
   consolidator = TradeBarConsolidator(timedelta(days=7))
   ```
6. Attach a handler method to feed data into the consolidator and updates the indicator with the consolidated bars.
   ```python
   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.
7. Iterate through the historical market data and update the indicator.
   ```python
   for bar in history:
       consolidator.update(bar)
   ```
8. Populate a `DataFrame` with the data in the `RollingWindow` objects.
   ```python
   bb_dataframe = pd.DataFrame(window).set_index('time')
   ```
   ![Historical custom bollinger band data](https://cdn.quantconnect.com/i/tu/indicator-custom-resolution-py-classic-bb.png)

## Plot Indicators

Follow these steps to plot the indicator values:

1. Select the columsn to plot.
   ```python
   df = bb_dataframe[['lowerband', 'middleband', 'upperband', 'price']]
   ```
2. Call the `plot` method.
   ```python
   df.plot()
   ```
3. Show the plot.
   ```python
   plt.show()
   ```
   ![Line plot of custom bollinger band properties](https://cdn.quantconnect.com/i/tu/custom-resolution-indicator-plot.jpg)

## 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.

```python
# 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(50)
window["bollingerbands"] = RollingWindow(50)
window["lowerband"] = RollingWindow(50)
window["middleband"] = RollingWindow(50)
window["upperband"] = RollingWindow(50)
window["price"] = RollingWindow(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")
```
