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();
}
}
}