The VHF was included in WL6 basic indicators. Is it possible to get it added to WL8?
Rename
You probably already have this, but if not, here's the equivalent popped into a TimeSeries. Of course, adjust the period and variable scope to your needs. Maybe I'll write the full-fledged indicator soon.
CODE:
const int vhfPeriod = 8; TimeSeries vhf = (Highest.Series(bars.Close, vhfPeriod) - Lowest.Series(bars.Close, vhfPeriod)) / (bars.Close - (bars.Close >> 1)).Abs().Sum(vhfPeriod);
paul1986,
Thanks! Your code is MUCH cleaner than what I have been using.
Thanks! Your code is MUCH cleaner than what I have been using.
QUOTE:
Maybe I'll write the full-fledged indicator soon.
I had a couple WL6 indicators I had to redevelop for WL8. If the indicator is just for one strategy, what you can do instead is write a private static method (see the VHF function below) for that one indicator as shown below.
CODE:If you run the above code, you'll see both approaches come up with the same TimeSeries. I'll have to confess, the approach in Post #1 might be slightly more efficient on garbage collection since more work can be done in stack storage than heap storage.
using WealthLab.Backtest; using WealthLab.Core; using WealthLab.Indicators; namespace WealthScript3 { public class MyStrategy : UserStrategyBase { static TimeSeries VHF(TimeSeries source, int vhfPeriod = 8) { TimeSeries numerator = Highest.Series(source, vhfPeriod) - Lowest.Series(source, vhfPeriod); TimeSeries denominator = Momentum.Series(source, 1).Abs().Sum(vhfPeriod); return numerator / denominator; } public override void Initialize(BarHistory bars) { const int vhfPeriod = 8; TimeSeries vhf = (Highest.Series(bars.Close, vhfPeriod) - Lowest.Series(bars.Close, vhfPeriod)) / (bars.Close - (bars.Close >> 1)).Abs().Sum(vhfPeriod); PlotTimeSeriesLine(vhf, "VHF", "VHF", WLColor.Orange, 6); TimeSeries vhfMethod = VHF(bars.Close); PlotTimeSeriesLine(vhfMethod, "VHF method", "VHF"); } public override void Execute(BarHistory bars, int idx) { } } }
superticker,
Thanks for your code approach. I appreciate the great help from this Board!
Thanks for your code approach. I appreciate the great help from this Board!
Here's the VHF indicator. I made it a bit more flexible. You can use any TimeSeries for each of Highest and Lowest instead of just plain-old Close. It defaults to the Close for both Highest and Lowest. I only did some simple testing. This should be a tad bit speedier and less memory intensive than the TimeSeries of my prior post.
CODE:
using System; using WealthLab.Core; using WealthLab.Indicators; namespace WLUtility.Indicators { /// <summary> /// Vertical Horizontal Filter (VHF) measures the trend strength by comparing the net price movement /// over a period to the sum of absolute price changes. Allows custom TimeSeries for Highest and Lowest. /// </summary> public sealed class VerticalHorizontalFilter : IndicatorBase { public VerticalHorizontalFilter() { // for WL8 - do not remove! } public VerticalHorizontalFilter(BarHistory bars, int period = 14) : this(bars.Close, bars.Close, period) { } public VerticalHorizontalFilter(TimeSeries highSeries, TimeSeries lowSeries, int period = 14) { Parameters[0].Value = highSeries; Parameters[1].Value = lowSeries; Parameters[2].Value = period; Populate(); } public override string Name => "Vertical Horizontal Filter"; public override string Abbreviation => "VHF"; public override string HelpDescription => "Vertical Horizontal Filter"; public override string PaneTag => "VHF"; public override bool IsSmoother => false; public override bool IsCalculationLengthy => false; public override bool PeekAheadFlag => false; public override PlotStyle DefaultPlotStyle => PlotStyle.Line; public override WLColor DefaultColor => WLColor.Orange; public override void Populate() { var highSeries = Parameters[0].AsTimeSeries; var lowSeries = Parameters[1].AsTimeSeries; var period = Parameters[2].AsInt; DateTimes = highSeries.DateTimes; if (period <= 0 || highSeries.Count == 0 || lowSeries.Count == 0) { return; } var highest = new Highest(highSeries, period); var lowest = new Lowest(lowSeries, period); for (var i = 0; i < period; i++) { Values[i] = double.NaN; } var sumChangeAbs = 0.0; // Sum the first window for (var i = 1; i < period; i++) { sumChangeAbs += Math.Abs(highSeries[i] - highSeries[i - 1]); } for (var idx = period; idx < highSeries.Count; idx++) { sumChangeAbs += Math.Abs(highSeries[idx] - highSeries[idx - 1]); sumChangeAbs -= Math.Abs(highSeries[idx - period + 1] - highSeries[idx - period]); Values[idx] = sumChangeAbs == 0 ? 0 : (highest[idx] - lowest[idx]) / sumChangeAbs; } } protected override void GenerateParameters() { AddParameter("High Series", ParameterType.TimeSeries, PriceComponent.Close); AddParameter("Low Series", ParameterType.TimeSeries, PriceComponent.Close); AddParameter("Period", ParameterType.Int32, 14); } public static VerticalHorizontalFilter Series(TimeSeries highSeries, TimeSeries lowSeries, int period = 14) { var key = CacheKey("VerticalHorizontalFilter", highSeries, lowSeries, period); if (highSeries.Cache.TryGetValue(key, out var obj)) { return (VerticalHorizontalFilter) obj; } var indicator = new VerticalHorizontalFilter(highSeries, lowSeries, period); highSeries.Cache[key] = indicator; return indicator; } // Convenience overload for BarHistory public static VerticalHorizontalFilter Series(BarHistory bars, int period = 14) => Series(bars.Close, bars.Close, period); } }
I will add this to the next Build so we have it as a native, core indicator once again.
Your Response
Post
Edit Post
Login is required