Contributions

Indicators

Introduction

LEAN currently supports over 100 indicators. This page explains how to contribute a new indicator to the open-source project by making a pull request to Lean. Before you get started, familiarize yourself with our contributing guidelines. If you don't already have a new indicator in mind that you want to contribute, see the GitHub Issues in the Lean repository for a list of indicators that community members have requested.

Get Third-Party Values

As a quantitative algorithmic trading engine, accuracy and reliability are very important to LEAN. When you submit a new indicator to the LEAN, you must include third-party source values are required as reference points in your pull request to contrast the values output by your indicator implementation. This requirement validates that your indicator implementation is correct. The following sections explain some examples of acceptable third-party sources.

Renowned Open-source Projects

Developed and maintained by expert teams, these sources undergo rigorous testing and optimization, ensuring accurate calculations. The transparent nature of open-source projects allows for community scrutiny, resulting in bug fixes and continuous improvements. Open-source projects provide thorough information on how the indicator values are calculated, which provides excellent reproducibility. Thus, we accept values from these projects with high confidence. Example projects include TA-Lib and QuantLib.

Highly Credible Websites

Similar reasons apply to these websites as well. The site should be either the original source or a very popular trading data provider, such that we have confidence in their accuracy and reliability. These sources might provide structured data samples, like a JSON response, CSV/Excel file, or scripts for calculating the indicator values.

Define the Class

To add a new indicator to Lean, add a class file to the Lean / Indicators directory. Indicators are classified as either a data point, bar, or TradeBar indicator. Their classification depends on the class they inherit and the type of data they receive. The following sections explain how to implement each type. Regardless of the indicator type, the class must define the following properties:

PropertyTypeDescription
WarmUpPeriodintThe minimum number of data entries required to calculate an accurate indicator value.
IsReadyboolA flag that states whether the indicator has sufficient data to generate values.

The class must also define a ComputeNextValue method, which accepts some data and returns the indicator value. As shown in the following sections, the data/arguments that this method receives depends on the indicator type.

On rare occassions, some indicators can produce invalid values. For example, a moving average can produce unexpected values due to extreme quotes. In cases like these, override the ValidateAndComputeNextValue method to return an IndicatorResult with an IndicatorStatus enumeration. If the IndicatorStatus states the value is invalid, it won't be passed to the main algorithm. The IndicatorStatus enumeration has the following members:

To enable the algorithm to warm up the indicator with the WarmUpIndicator method, inherit the IIndicatorWarmUpPeriodProvider interface.

Data Point Indicators

Data point indicators use IndicatorDataPoint objects to compute their value. These types of indicators can inherit the IndicatorBase<IndicatorDataPoint> or WindowIndicator<IndicatorDataPoint> class. The WindowIndicator<IndicatorDataPoint> class has several members to help you compute indicator values over multiple periods.

public class CustomPointIndicator : IndicatorBase<IndicatorDataPoint>, IIndicatorWarmUpPeriodProvider
{
    public int WarmUpPeriod = 2;
    public override bool IsReady => Samples >= WarmUpPeriod;

    protected override decimal ComputeNextValue(IndicatorDataPoint input)
    {
        return 1m;
    }

    protected virtual IndicatorResult ValidateAndComputeNextValue(IndicatorDataPoint input)
    {
        var indicatorValue = ComputeNextValue(input);
        return IsReady ?
            new IndicatorResult(indicatorValue) :
            new IndicatorResult(indicatorValue, IndicatorStatus.ValueNotReady);
    }
}

To view some example data point indicators that inherit the IndicatorBase<IndicatorDataPoint> class, see the implementation of the following indicators in the LEAN repository:

public class CustomWindowIndicator : WindowIndicator<IndicatorDataPoint>
{
    public int WarmUpPeriod => base.WarmUpPeriod;
    public override bool IsReady => base.IsReady;

    protected override decimal ComputeNextValue(IReadOnlyWindow<T> window, IndicatorDataPoint input)
    {
        return window.Average();
    }

    protected virtual IndicatorResult ValidateAndComputeNextValue(IndicatorDataPoint input)
    {
        var indicatorValue = ComputeNextValue(input);
        return IsReady ?
            new IndicatorResult(indicatorValue) :
            new IndicatorResult(indicatorValue, IndicatorStatus.InvalidInput);
    }
}

To view some example data point indicators that inherit the WindowIndicator<IndicatorDataPoint> class, see the implementation of the following indicators in the LEAN repository:

Bar Indicators

Bar indicators use QuoteBar or TradeBar objects to compute their value. Since Forex and CFD securities don't have TradeBar data, they use bar indicators. Candlestick patterns are examples of bar indicators.

public class CustomBarIndicator : BarIndicator, IIndicatorWarmUpPeriodProvider
{
    public int WarmUpPeriod = 2;
    public override bool IsReady => Samples >= WarmUpPeriod;

    protected override decimal ComputeNextValue(IBaseDataBar input)
    {
        return 1m;
    }

    protected virtual IndicatorResult ValidateAndComputeNextValue(IBaseDataBar input)
    {
        var indicatorValue = ComputeNextValue(input);
        return IsReady ?
            new IndicatorResult(indicatorValue) :
            new IndicatorResult(indicatorValue, IndicatorStatus.ValueNotReady);
    }
}

To view some example bar indicators, see the implementation of the following indicators in the LEAN repository:

TradeBar Indicators

TradeBar indicators use TradeBar objects to compute their value. Some TradeBar indicators use the volume property of the TradeBar to compute their value.

public class CustomTradeBarIndicator : TradeBarIndicator, IIndicatorWarmUpPeriodProvider
{
    public int WarmUpPeriod = 2;
    public override bool IsReady => Samples >= WarmUpPeriod;

    protected override decimal ComputeNextValue(TradeBar input)
    {
        return 1m;
    }

    protected virtual IndicatorResult ValidateAndComputeNextValue(TradeBar input)
    {
        var indicatorValue = ComputeNextValue(input);
        return IsReady ?
            new IndicatorResult(indicatorValue) :
            new IndicatorResult(indicatorValue, IndicatorStatus.ValueNotReady);
    }
}

To view some example TradeBar indicators, see the implementation of the following indicators in the LEAN repository:

Define the Helper Method

The preceding indicator class is sufficient to instatiate a manual version of the indicator. To enable users to create an automatic version of the indicator, add a new method to the Lean / Algorithm / QCAlgorithm.Indicators.cs file. Name the method a short abbreviation of the indicator's full name. In the method definition, call the InitializeIndicator method to create a consolidator and register the indicator for automatic updates with the consolidated data.

public CustomIndicator CI(Symbol symbol, Resolution? resolution = null, Func<IBaseData, IBaseDataBar> selector = null)
{
    var name = CreateIndicatorName(symbol, $"CI()", resolution);
    var ci = new CustomIndicator(name, symbol);
    InitializeIndicator(symbol, ci, resolution, selector);
    return ci;
}

Add Unit Tests

Unit tests ensure your indicator functions correctly and produces accurate values. Follow these steps to add unit tests for your indicator:

  1. Save the third-party values in the Lean / Tests / TestData directory as a CSV file.
  2. In the Lean / Tests / QuantConnect.Tests.csproj file, reference the new data file.
    <Content Include="TestData\<filePath>.csv">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  3. Create a Lean / Tests / Indicators / <IndicatorName>Tests.cs file with the following content:
    namespace QuantConnect.Tests.Indicators
    {
        [TestFixture]
        public class CustomIndicatorTests : CommonIndicatorTests<T>
        {
            protected override IndicatorBase<T> CreateIndicator()
            {
                return new CustomIndicator();
            }
    
            protected override string TestFileName => "custom_3rd_party_data.csv";
    
            protected override string TestColumnName => "CustomIndicatorValueColumn";
    
            // How do you compare the values
            protected override Action<IndicatorBase<T>, double> Assertion
            {
                get { return (indicator, expected) => Assert.AreEqual(expected, (double)indicator.Current.Value, 1e-4); }        // allow 0.0001 error margin of indicator values
            }
        }
    }
  4. Set the values of the TestFileName and TestColumnName attributes to the CSV file name and the column name of the testing values in the CSV file of third-party values, respectively.
  5. Add test cases.
  6. Test if the constructor, IsReady flag, and Reset method work. If there are other custom calculation methods in your indicator class, add a tests for them.

The following example shows the testing class structure:

namespace QuantConnect.Tests.Indicators
{
    [TestFixture]
    public class CustomIndicatorTests : CommonIndicatorTests<T>
    {
        protected override IndicatorBase<T> CreateIndicator()
        {
            return new CustomIndicator();
        }

        protected override string TestFileName => "custom_3rd_party_data.csv";

        protected override string TestColumnName => "CustomIndicatorValueColumn";

        // How do you compare the values
        protected override Action<IndicatorBase<T>, double> Assertion
        {
            get { return (indicator, expected) => Assert.AreEqual(expected, (double)indicator.Current.Value, 1e-4); }        // allow 0.0001 error margin of indicator values
        }

        [Test]
        public void IsReadyAfterPeriodUpdates()
        {
            var ci = CreateIndicator();

            Assert.IsFalse(ci.IsReady);
            ci.Update(DateTime.UtcNow, 1m);
            Assert.IsTrue(ci.IsReady);
        }

        [Test]
        public override void ResetsProperly()
        {
            var ci = CreateIndicator();

            ci.Update(DateTime.UtcNow, 1m);
            Assert.IsTrue(ci.IsReady);
            
            ci.Reset();

            TestHelper.AssertIndicatorIsInDefaultState(ci);
        }
    }
}

For a full example, see SimpleMovingAverageTests.cs in the LEAN repository.

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: