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

# Custom Indicators

[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 Indicators

> 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 custom indicators.

## 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-indicators.md#03-Create-Subscriptions) 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. Define a custom indicator class. Note the `PythonIndicator` superclass inheritance, `value` attribute, and `update` method are mandatory.
   In this tutorial, create an `ExpectedShortfallPercent` indicator that uses Monte Carlo to calculate the expected shortfall of returns.
   ```python
   class ExpectedShortfallPercent(PythonIndicator):
       import math, numpy as np

       def __init__(self, period, alpha):
           self.value = None   # Attribute represents the indicator value
           self.value_at_risk = None

           self.alpha = alpha

           self._window = RollingWindow(period)

       # Override the IsReady attribute to flag all attributes values are ready.
       @property
       def is_ready(self) -> bool:
           return self.value and self.value_at_risk

       # Method to update the indicator values. Note that it only receives 1 IBaseData object (Tick, TradeBar, QuoteBar) argument.
       def update(self, input: BaseData) -> bool:
           count = self._window.count

           self._window.add(input.close)

           # Update the Value and other attributes as the indicator current value.
           if count >= 2:
               cutoff = math.ceil(self.alpha * count)

               ret = [ (self._window[i] - self._window[i+1]) / self._window[i+1] for i in range(count-1) ]
               lowest = sorted(ret)[:cutoff]

               self.value = np.mean(lowest)
               self.value_at_risk = lowest[-1]

           # return a boolean to indicate IsReady.
           return count >= 2
   ```
3. Initialize a new instance of the custom indicator.
   ```python
   custom = ExpectedShortfallPercent(50, 0.05)
   ```
4. Create a `RollingWindow` for each attribute of the indicator to hold their values.
   In this example, save 20 data points.
   ```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(20)
   window['expectedshortfall'] = RollingWindow(20)
   window['valueatrisk'] = RollingWindow(20)
   ```
5. Attach a handler method to the indicator that updates the `RollingWindow` objects.
   When the indicator receives new data, the preceding handler method adds the new `IndicatorDataPoint` values into the respective `RollingWindow`.
6. Iterate through the historical market data and update the indicator.
   ```python
   for bar in history:
       custom.update(bar)

       # The Updated event handler is not available for custom indicator in Python, RollingWindows are needed to be updated in here.
       if custom.is_ready:
           window['time'].add(bar.end_time)
           window['expectedshortfall'].add(custom.value)
           window['valueatrisk'].add(custom.value_at_risk)
   ```
7. Populate a `DataFrame` with the data in the `RollingWindow` objects.
   ```python
   custom_dataframe = pd.DataFrame(window).set_index('time')
   ```
   ![Historical expected shortfall and value at risk](https://cdn.quantconnect.com/i/tu/indicator-custom-indicator-py-classic.png)

## Plot Indicators

Follow these steps to plot the indicator values:

1. Call the `plot` method.
   ```python
   custom_dataframe.plot()
   ```
2. Show the plot.
   ```python
   plt.show()
   ```
   ![Line plot of expected shortfall and value at risk](https://cdn.quantconnect.com/i/tu/custom-indicators-plot.jpg)

## Examples

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

### Example 1: Expected Shortfall

The following example demonstrates researching using a custom-constructed Expected Shortfall indicator. Expected Shortfall refers to the average of the N% worst-case return.

```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 daily resolution.
history = qb.history[TradeBar](symbol, 70, Resolution.DAILY)

class ExpectedShortfallPercent(PythonIndicator):
    import math, numpy as np

    def __init__(self, period, alpha):
        self.value = None   # Attribute represents the indicator value
        self.value_at_risk = None

        self.alpha = alpha

        self._window = RollingWindow(period)

    # Override the IsReady attribute to flag all attributes values are ready.
    @property
    def is_ready(self) -> bool:
        return self.value and self.value_at_risk

    # Method to update the indicator values. Note that it only receives 1 IBaseData object (Tick, TradeBar, QuoteBar) argument.
    def update(self, input: BaseData) -> bool:
        count = self._window.count

        self._window.add(input.close)

        # Update the Value and other attributes as the indicator current value.
        if count >= 2:
            cutoff = math.ceil(self.alpha * count)

            ret = [ (self._window[i] - self._window[i+1]) / self._window[i+1] for i in range(count-1) ]
            lowest = sorted(ret)[:cutoff]

            self.value = np.mean(lowest)
            self.value_at_risk = lowest[-1]

        # return a boolean to indicate IsReady.
        return count >= 2

# Initialize a new instance of the custom indicator.
custom = ExpectedShortfallPercent(50, 0.05)

# 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(20)
window['expectedshortfall'] = RollingWindow(20)
window['valueatrisk'] = RollingWindow(20)

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

    # The Updated event handler is not available for custom indicator in Python, RollingWindows are needed to be updated in here.
    if custom.is_ready:
        window['time'].add(bar.end_time)
        window['expectedshortfall'].add(custom.value)
        window['valueatrisk'].add(custom.value_at_risk)

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

# Create line chart of the Expected Shortfall Percent.
custom_dataframe.plot(title=f"Expected Shortfall Percent of {symbol}", xlabel="Time", ylabel="%")
plt.show()
```
