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

Indicators

Combining Indicators

Introduction

This page explains how to create, update, and visualize LEAN Composite indicators.

Create Subscriptions

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

var qb = new QuantBook();
var symbol = qb.AddEquity("SPY").Symbol;
qb = QuantBook()
symbol = qb.add_equity("SPY").symbol

Create Indicator Timeseries

You need to subscribe to some market data and create a composite indicator in order to calculate a timeseries of indicator values. In this example, use a 10-period SimpleMovingAverage of a 10-period RelativeStrengthIndex indicator.

// Create 10-period RSI and 10-period SMA indicator objects.
var rsi = new RelativeStrengthIndex(10);
var sma = new SimpleMovingAverage(10);
// Create a composite indicator by feeding the value of 10-period RSI to the 10-period SMA indicator.
var smaOfRsi = IndicatorExtensions.Of(sma, rsi);
# Create 10-period RSI and 10-period SMA indicator objects.
rsi = RelativeStrengthIndex(10)
sma = SimpleMovingAverage(10)
# Create a composite indicator by feeding the value of 10-period RSI to the 10-period SMA indicator.
sma_of_rsi = IndicatorExtensions.of(sma, rsi)

Follow these steps to create an indicator timeseries:

  1. Get some historical data.
  2. // Request historical trading data with the daily resolution.
    var history = qb.History(symbol, 70, Resolution.Daily);
    # Request historical trading data with the daily resolution.
    history = qb.history[TradeBar](symbol, 70, Resolution.DAILY)
  3. Create a RollingWindow for each attribute of the indicator to hold their values.
  4. In this example, save 50 data points.

    // Create a window dictionary to store RollingWindow objects.
    var window = new Dictionary<string, RollingWindow<decimal>>();
    // Store the RollingWindow objects, index by key is the property of the indicator.
    var time = new RollingWindow<DateTime>(50);
    window["SMA Of RSI"] = new RollingWindow<decimal>(50);
    window["rollingsum"] = new RollingWindow<decimal>(50);
    
    # 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["SMA Of RSI"] = RollingWindow[float](50)
    window["rollingsum"] = RollingWindow[float](50)
    
  5. Attach a handler method to the indicator that updates the RollingWindow objects.
  6. // Define an update function to add the indicator values to the RollingWindow object.
    smaOfRsi.Updated += (sender, updated) => 
    {
        var indicator = (SimpleMovingAverage)sender;    // Use terminal indicator class.
        time.Add(updated.EndTime);
        window["SMA Of RSI"].Add(updated);
        window["rollingsum"].Add(indicator.RollingSum);
    };
    # Define an update function to add the indicator values to the RollingWindow object.
    def update_sma_of_rsi_window(sender: object, updated: IndicatorDataPoint) -> None:
        indicator = sender
        window['time'].add(updated.end_time)
        window["SMA Of RSI"].add(updated.value)
        window["rollingsum"].add(indicator.rolling_sum.current.value)
    
    sma_of_rsi.updated += UpdateSmaOfRsiWindow

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

  7. Iterate the historical market data to update the indicators and the RollingWindows.
  8. foreach(var bar in history){
        // Update the base indicators, the composite indicator will update automatically when the base indicator is updated.
        rsi.Update(bar.EndTime, bar.Close);
    }
    for bar in history:
        # Update the base indicators, the composite indicator will update automatically when the base indicator is updated.
        rsi.update(bar.end_time, bar.close)
  9. Display the data.
  10. Console.WriteLine($"time,{string.Join(',', window.Select(kvp => kvp.Key))}");
    foreach (var i in Enumerable.Range(0, 5).Reverse())
    {
        var data = string.Join(", ", window.Select(kvp => Math.Round(kvp.Value[i],6)));
        Console.WriteLine($"{time[i]:yyyyMMdd}, {data}");
    }
    Historical data of 10-day SMA of 10-day RSI
  11. Populate a DataFrame with the data in the RollingWindow objects.
  12. sma_of_rsi_dataframe = pd.DataFrame(window).set_index('time')
    Historical data of 10-day SMA of 10-day RSI

Plot Indicators

Jupyter Notebooks don't currently support libraries to plot historical data, but we are working on adding the functionality. Until the functionality is added, use Python to plot composite indicators.

Follow these steps to plot the indicator values:

  1. Select the columns/features to plot.
  2. sma_of_rsi_plot = sma_of_rsi_dataframe[["SMA Of RSI"]]
  3. Call the plot method.
  4. sma_of_rsi_plot.plot(title="SPY SMA(10) of RSI(10)", figsize=(15, 10))
  5. Show the plots.
  6. plt.show()
    Line plot of 10-day SMA of 10-day RSI

Examples

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

Example 1: Plot Standard Deviation Of Return

The following example demonstrates a quick backtest to testify the effectiveness of a Standard Deviation On Return mean-reversal under the research enviornment.

// Load the assembly files and data types in their own cell.
#load "../Initialize.csx"

// Load the necessary assembly files.
#load "../QuantConnect.csx"
#r "../Plotly.NET.dll"
#r "../Plotly.NET.Interactive.dll"

// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
using QuantConnect;
using QuantConnect.Indicators;
using QuantConnect.Research;
            
using Plotly.NET;
using Plotly.NET.Interactive;
using Plotly.NET.LayoutObjects;

// Instantiate the QuantBook instance for researching.
var qb = new QuantBook();
// Request SPY data to work with the indicator.
var symbol = qb.AddEquity("SPY").Symbol;

// Create the SD on Return indicator with parameters to be studied.
var roc = new RateOfChange(1);
var sd = new StandardDeviation(252);
var indicator = IndicatorExtensions.Of(sd, roc);

// Get the history of SPY to update the indicator and trade.
var history = qb.History<TradeBar>(symbol, 500, Resolution.Daily).ToList();

var indicatorValues = new List<decimal>();
var time = new List<DateTime>();
for (int i = 0; i < history.Count - 1; i++)
{
    // Update the indicator value.
    var bar = history[i];
    roc.Update(bar);
    
    indicatorValues.Add(indicator.Current.Value);
    time.Add(bar.EndTime);
}

// Create line chart of the SD of Return.
var chart = Chart2D.Chart.Line<DateTime, decimal, string>(
    time,
    indicatorValues
);

// Create a Layout as the plot settings.
LinearAxis xAxis = new LinearAxis();
xAxis.SetValue("title", "Time");
LinearAxis yAxis = new LinearAxis();
yAxis.SetValue("title", "%");
Title title = Title.init($"Return SD of {symbol}");

Layout layout = new Layout();
layout.SetValue("xaxis", xAxis);
layout.SetValue("yaxis", yAxis);
layout.SetValue("title", title);
// Assign the Layout to the chart.
chart.WithLayout(layout);

// Display the plot.
HTML(GenericChart.toChartHTML(chart))
# Instantiate the QuantBook instance for researching.
qb = QuantBook()
# Request SPY data to work with the indicator.
symbol = qb.add_equity("SPY").symbol

# Get the historical data for trading.
history = qb.history(symbol, 500, Resolution.DAILY).close.unstack(0)
history = history.groupby(history.index.date).sum()

# Create the SD on Return with parameters to be studied.
roc = RateOfChange(1)
sd = StandardDeviation(252)
indicator = IndicatorExtensions.of(sd, roc)

# Update and obtain the indicator value
def update(row):
    roc.update(row.name, row.iloc[0])
    return indicator.current.value
indicator_dataframe = history.apply(update, axis=1).iloc[252:]

indicator_dataframe.plot(title=f"Return SD of {symbol}", ylabel="%", 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: