Hi everyone,

I'm totally new to QC and C#. My programming background is mostly in Python/R/Matlab (and some C to improve performance when necessary).

As an exercise, I tried to implement an indicator, namely the Vortex Indicator, which I believe could be useful to some of you. The Vortex Indicator is basically an "enhanced" DMI indicator (https://en.wikipedia.org/wiki/Vortex_indicator)).

Nevertheless, I am struggling a bit as I don't fully understand the Architecture of LEAN yet (and lack some C# experience). I took the ATR as a base and tried to build on that. The fact is that I am not very comfortable with the Functional indicators (is it necessary in this case?) and data structure which bothers me a bit to make the code work. Especially, I want to take the previous value of the indicator to sum it but can't help it. The Any help appreciated! Thanks in advance for your help.

Finally, one question/request does QC offer a way to call R functions (with the COM Interface)? It could could be quite useful for ML applications and other stat analysis.

Best,

L

/*

* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.

* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

using System;

using QuantConnect.Data.Market;

namespace QuantConnect.Indicators

{

///

/// SumTrueRange is defined as the maximum of the following:

/// High - Low

/// ABS(High - PreviousClose)

/// ABS(Low - PreviousClose)

/// Summed over a N peridos window

/// VMplus:

///

/// SUM(ABS(High - previous.Low), N periods)

/// VMminus:

///

/// SUM(ABS(High - previous.High), N periods)

/// Vortex Indicator:

/// VMplus / SumTrueRange - VMminus / SumTrueRange

///

///

///

public class Vortex : TradeBarIndicator

{

private Sum _Sum;

private int _period;

///

/// Gets the Sumtrue,VMplus and VMminus range which is the more volatile calculation to be smoothed by this indicator

///

public IndicatorBase SumTrueRange { get; private set; }

public IndicatorBase VMplus { get; private set; }

public IndicatorBase VMminus { get; private set; }

///

/// Gets a flag indicating when this indicator is ready and fully initialized

///

public override bool IsReady

{

get { return Samples > _period; }

}

///

/// Creates a new AverageTrueRange indicator using the specified period and moving average type

///

/// The name of this indicator

/// The smoothing period used to smooth the true range values

public Vortex(string name, int period)

: base(name)

{

_period = period;

TradeBar previous = null;

SumTrueRange = new FunctionalIndicator(name + "_SumTrueRange", currentBar =>

{

// in our ComputeNextValue function we'll just call the ComputeTrueRange

var nextValue = ComputeSumTrueRange(previous, currentBar);

previous = currentBar;

return nextValue;

} // in our IsReady function we just need at least two sample

, trueRangeIndicator => trueRangeIndicator.Samples >= _period

);

VMplus = new FunctionalIndicator(name + "_SumTrueRange", currentBar =>

{

// in our ComputeNextValue function we'll just call the ComputeTrueRange

var nextValue = ComputeVMplus(previous, currentBar);

previous = currentBar;

return nextValue;

} // in our IsReady function we just need at least two sample

, trueRangeIndicator => trueRangeIndicator.Samples >= _period

);

VMminus = new FunctionalIndicator(name + "_SumTrueRange", currentBar =>

{

// in our ComputeNextValue function we'll just call the ComputeTrueRange

var nextValue = ComputeVMminus(previous, currentBar);

previous = currentBar;

return nextValue;

} // in our IsReady function we just need at least two sample

, trueRangeIndicator => trueRangeIndicator.Samples >= _period

);

}

///

/// Creates a new AverageTrueRange indicator using the specified period and moving average type

///

/// The smoothing period used to smooth the true range values

/// The type of smoothing used to smooth the true range values

public Vortex(int period)

: this("Vortex" + period, period)

{

}

public static decimal ComputeSumTrueRange(TradeBar previous, TradeBar current)

{

var range1 = current.High - current.Low;

if (previous == null)

{

return 0m;

}

var range2 = Math.Abs(current.High - previous.Close);

var range3 = Math.Abs(current.Low - previous.Close);

return SumTrueRange.Current + Math.Max(range1, Math.Max(range2, range3));

}

public static decimal ComputeVMplus(TradeBar previous, TradeBar current)

{

if (previous == null)

{

return;

}

var range = Math.Abs(current.High - previous.Low);

return Current + range;

}

public static decimal ComputeVMminus(TradeBar previous, TradeBar current)

{

if (previous == null)

{

return;

}

var range = Math.Abs(current.Low - previous.High);

return Current + range;

}

///

/// Computes the next value of this indicator from the given state

///

/// The input given to the indicator

/// A new value for this indicator

protected override decimal ComputeNextValue(TradeBar input)

{

// compute the true range and then sum it

SumTrueRange.Update(input);

VMplus.Update(input);

VMminus.Update(input);

decimal VIplus= VMplus / SumTrueRange;

decimal VIminus= VMminus / SumTrueRange;

return VIplus - VIminus;

}

///

/// Resets this indicator to its initial state

///

public override void Reset()

{

TrueRange.Reset();

VMplus.Reset();

VMminus.Reset();

base.Reset();

}

}

}

Author