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