Search Framework:
PeakTroughCalculator
Namespace: WealthLab.Indicators
Parent: Object

The PeakTroughCalculator class calculates peaks and troughs in source time series data. Peaks and troughs are detected when the data reverses by a specified amount, which can be expressed as a percentage, point value, ATR multiple, or ATR percentage. The resulting PeakTrough instances are available through the PeakTroughs property. Because a reversal must occur before a peak or trough can be confirmed, detection always occurs after the actual peak or trough. Each PeakTrough contains both the index where the peak or trough occurred (PeakTroughIndex) and the index where it was confirmed (DetectedAtIndex).

Chart Rendering
DrawPeakLines
public void DrawPeakLines(
UserStrategyBase usb,
WLColor color = null,
int lineWidth = 1,
LineStyle lineStyle = LineStyle.Solid,
string paneTag = "Price",
bool behindBars = false)

Draws lines connecting confirmed peaks. Pass this for usb when calling the method from a C# Coded Strategy. If the most recently confirmed PeakTrough is a trough, the method also draws a dotted gray line from the latest confirmed peak to the current provisional peak.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class DrawPeakLinesExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                10.0,
                PeakTroughReversalType.Percent);
            _ptc.DrawPeakLines(
                this,
                WLColor.Red,
                2,
                LineStyle.Solid);
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

DrawTroughLines
public void DrawTroughLines(
UserStrategyBase usb,
WLColor color = null,
int lineWidth = 1,
LineStyle lineStyle = LineStyle.Solid,
string paneTag = "Price",
bool behindBars = false)

Draws lines connecting confirmed troughs. Pass this for usb when calling the method from a C# Coded Strategy. If the most recently confirmed PeakTrough is a peak, the method also draws a dotted gray line from the latest confirmed trough to the current provisional trough.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class DrawTroughLinesExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                10.0,
                PeakTroughReversalType.Percent);
            _ptc.DrawTroughLines(
                this,
                WLColor.Green,
                2,
                LineStyle.Solid);
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}


Constructors
PeakTroughCalculator
public PeakTroughCalculator(
TimeSeries highs,
TimeSeries lows,
double reversalAmount,
PeakTroughReversalType reversalType = PeakTroughReversalType.Percent)
public PeakTroughCalculator(
BarHistory source,
double reversalAmount,
PeakTroughReversalType reversalType = PeakTroughReversalType.Percent,
int atrPeriod = 10)
public PeakTroughCalculator(
TimeSeries source,
double reversalAmount,
PeakTroughReversalType reversalType = PeakTroughReversalType.Percent)
public PeakTroughCalculator(List<PeakTrough> pts)

The first constructor calculates peaks and troughs using separate highs and lows TimeSeries. The second constructor accepts a BarHistory and calculates peaks and troughs from its High and Low price series. The optional atrPeriod specifies the period used when reversalType is PeakTroughReversalType.ATR or PeakTroughReversalType.ATRPercent. The third constructor calculates peaks and troughs from a single TimeSeries. The fourth constructor initializes the calculator with an existing List<PeakTrough>. The supported PeakTroughReversalType values are:

  • Percent - A peak or trough is detected after the source reverses by reversalAmount percent.
  • Point - A peak or trough is detected after the source reverses by reversalAmount points.
  • ATR - The reversal amount is determined by multiplying ATR by reversalAmount.
  • ATRPercent - The reversal percentage is determined by multiplying ATRP by reversalAmount.

Note: Percentage-based reversals can produce unexpected results when applied to TimeSeries containing negative values. Use PeakTroughReversalType.Point for such series.

using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class PeakTroughCalculatorExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            foreach (PeakTrough pt in _ptc.PeakTroughs)
            {
                WLColor color = pt.Type == PeakTroughType.Peak
                    ? WLColor.Red
                    : WLColor.Green;
                DrawDot(pt.XIndex, pt.YValue, color, 4);
            }
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}


Peak and Trough State
HasEqualPeaks
public bool HasEqualPeaks(
int idx,
double maxPercentDiff = 0.1,
bool useProvisional = false)

Returns true if the two applicable peaks as of idx are within maxPercentDiff percent of one another. When useProvisional is false, only confirmed peaks are considered. When useProvisional is true and the most recent confirmed PeakTrough is a trough, the current provisional peak is compared with the most recent confirmed peak. Because the provisional peak can continue to change, the result can also change until the peak is confirmed. For example, with the default maxPercentDiff of 0.1 and a previous peak of $20.00, another peak between $19.98 and $20.02, inclusive, is considered approximately equal. Unlike PeakState, which returns 0 only when two confirmed peaks have exactly the same value, HasEqualPeaks permits a percentage tolerance.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class EqualPeaksExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        private SMA _sma;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            _sma = SMA.Series(bars.Close, 200);
            PlotIndicator(_sma);
            ZigZagHL zz = new ZigZagHL(
                bars,
                5.0,
                PeakTroughReversalType.Percent,
                false);
            PlotIndicator(zz, WLColor.Gray);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            PeakTrough pt = _ptc.GetPeakTrough(idx);
            if (pt == null)
                return;
            if (bars.Close[idx] < _sma[idx] &&
                pt.Type == PeakTroughType.Trough &&
                _ptc.HasEqualPeaks(idx, 0.25, true))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Red));
            }
        }
    }
}

HasEqualTroughs
public bool HasEqualTroughs(
int idx,
double maxPercentDiff = 0.1,
bool useProvisional = false)

Returns true if the two applicable troughs as of idx are within maxPercentDiff percent of one another. When useProvisional is false, only confirmed troughs are considered. When useProvisional is true and the most recent confirmed PeakTrough is a peak, the current provisional trough is compared with the most recent confirmed trough. For example, with the default maxPercentDiff of 0.1 and a previous trough of $20.00, another trough between $19.98 and $20.02, inclusive, is considered approximately equal. Unlike TroughState, which returns 0 only when two confirmed troughs have exactly the same value, HasEqualTroughs permits a percentage tolerance.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class EqualTroughsExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        private SMA _sma;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            _sma = SMA.Series(bars.Close, 200);
            PlotIndicator(_sma);
            ZigZagHL zz = new ZigZagHL(
                bars,
                5.0,
                PeakTroughReversalType.Percent,
                false);
            PlotIndicator(zz, WLColor.Gray);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            PeakTrough pt = _ptc.GetPeakTrough(idx);
            if (pt == null)
                return;
            if (bars.Close[idx] > _sma[idx] &&
                pt.Type == PeakTroughType.Peak &&
                _ptc.HasEqualTroughs(idx, 0.25, true))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Green));
            }
        }
    }
}

HasFallingPeaks
public bool HasFallingPeaks(
int idx,
bool useProvisional = false)

Returns true if the most recent peak as of idx has a lower value than the previous peak. When useProvisional is false, only confirmed peaks are considered. When useProvisional is true, a currently falling-peaks condition can become false following a trough if the provisional peak rises above the previous confirmed peak.


HasFallingTroughs
public bool HasFallingTroughs(
int idx,
bool useProvisional = false)

Returns true if the most recent trough as of idx has a lower value than the previous trough. When useProvisional is false, only confirmed troughs are considered. When useProvisional is true, a falling-troughs condition can become true following a peak if the provisional trough falls below the previous confirmed trough.


HasRisingPeaks
public bool HasRisingPeaks(
int idx,
bool useProvisional = false)

Returns true if the most recent peak as of idx has a higher value than the previous peak. When useProvisional is false, only confirmed peaks are considered. When useProvisional is true, a rising-peaks condition can become true following a trough if the provisional peak rises above the previous confirmed peak.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class RisingFallingExample : UserStrategyBase
    {
        private PeakTroughCalculator _pricePTC;
        private PeakTroughCalculator _rsiPTC;
        private RSI _rsi;
        public override void Initialize(BarHistory bars)
        {
            StartIndex = 100;
            _pricePTC = new PeakTroughCalculator(
                bars.Close,
                10.0,
                PeakTroughReversalType.Percent);
            _rsi = RSI.Series(bars.Close, 14);
            PlotIndicator(_rsi);
            _rsiPTC = new PeakTroughCalculator(
                _rsi,
                10.0,
                PeakTroughReversalType.Point);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            if (_pricePTC.HasRisingPeaks(idx) &&
                _rsiPTC.HasFallingPeaks(idx))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Red));
            }
            if (_pricePTC.HasFallingTroughs(idx) &&
                _rsiPTC.HasRisingTroughs(idx))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Green));
            }
        }
    }
}

HasRisingTroughs
public bool HasRisingTroughs(
int idx,
bool useProvisional = false)

Returns true if the most recent trough as of idx has a higher value than the previous trough. When useProvisional is false, only confirmed troughs are considered. When useProvisional is true, a rising-troughs condition can become false following a peak if the provisional trough falls below the previous confirmed trough.


PeakState
public int PeakState(int idx)

Compares the two most recent confirmed peaks as of idx. Returns:

  • 1 if the most recent peak is higher than the previous peak.
  • -1 if the most recent peak is lower than the previous peak.
  • 0 if the peaks are equal or insufficient confirmed peaks are available. PeakState considers only confirmed PeakTroughs.

TroughState
public int TroughState(int idx)

Compares the two most recent confirmed troughs as of idx. Returns:

  • 1 if the most recent trough is higher than the previous trough.
  • -1 if the most recent trough is lower than the previous trough.
  • 0 if the troughs are equal or insufficient confirmed troughs are available. TroughState considers only confirmed PeakTroughs.
Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class PeakTroughStateExample : UserStrategyBase
    {
        private PeakTroughCalculator _pricePTC;
        private PeakTroughCalculator _rsiPTC;
        private RSI _rsi;
        public override void Initialize(BarHistory bars)
        {
            StartIndex = 100;
            _pricePTC = new PeakTroughCalculator(
                bars.Close,
                10.0,
                PeakTroughReversalType.Percent);
            _rsi = RSI.Series(bars.Close, 14);
            PlotIndicator(_rsi);
            _rsiPTC = new PeakTroughCalculator(
                _rsi,
                10.0,
                PeakTroughReversalType.Point);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            if (_pricePTC.PeakState(idx) != _rsiPTC.PeakState(idx))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Red));
            }
            if (_pricePTC.TroughState(idx) != _rsiPTC.TroughState(idx))
            {
                SetBackgroundColor(
                    bars,
                    idx,
                    WLColor.FromArgb(40, WLColor.Green));
            }
        }
    }
}


Peaks and Troughs
GetPeak
public PeakTrough GetPeak(int idx)

Returns the most recently confirmed peak as of idx, or null if no peak is available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class GetPeakExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            int idx = bars.Count - 1;
            PeakTrough lastPeak = null;
            do
            {
                PeakTrough peak = _ptc.GetPeak(idx);
                if (peak == null)
                    break;
                if (lastPeak != null)
                {
                    DrawLine(
                        lastPeak.XIndex,
                        lastPeak.YValue,
                        peak.XIndex,
                        peak.YValue,
                        WLColor.Red,
                        2);
                }
                lastPeak = peak;
                idx = peak.XIndex - 1;
            }
            while (idx > 10);
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

GetPeaksAsOf
public List<PeakTrough> GetPeaksAsOf(
int idx,
int maxAgeInDays = Int32.MaxValue)

Returns a List<PeakTrough> containing peaks that had been confirmed as of idx. If maxAgeInDays is specified, peaks whose apex is more than that number of calendar days older than the date at idx are excluded. The returned List is ordered from the most recent qualifying peak backward in time.

Example Code
using System.Collections.Generic;
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class GetPeaksAsOfExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                10.0,
                PeakTroughReversalType.Percent);
            List<PeakTrough> peaks =
                _ptc.GetPeaksAsOf(bars.Count - 1);
            PeakTrough previous = null;
            foreach (PeakTrough peak in peaks)
            {
                if (previous != null)
                {
                    DrawLine(
                        previous.XIndex,
                        previous.YValue,
                        peak.XIndex,
                        peak.YValue,
                        WLColor.Blue,
                        2);
                }
                previous = peak;
            }
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

GetPeakTrough
public PeakTrough GetPeakTrough(int idx)

Returns the most recently confirmed peak or trough as of idx, or null if none is available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class GetPeakTroughExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            PeakTrough pt = _ptc.GetPeakTrough(idx);
            if (pt == null)
                return;
            WLColor color = pt.Type == PeakTroughType.Peak
                ? WLColor.Red
                : WLColor.Green;
            SetBarColor(bars, idx, color);
        }
    }
}

GetTrough
public PeakTrough GetTrough(int idx)

Returns the most recently confirmed trough as of idx, or null if no trough is available.


GetTroughsAsOf
public List<PeakTrough> GetTroughsAsOf(
int idx,
int maxAgeInDays = Int32.MaxValue)

Returns a List<PeakTrough> containing troughs that had been confirmed as of idx. If maxAgeInDays is specified, troughs whose nadir is more than that number of calendar days older than the date at idx are excluded. The returned List is ordered from the most recent qualifying trough backward in time.


Peaks
public List<PeakTrough> Peaks

Returns a List<PeakTrough> containing only the peaks generated by the calculator. The List is derived from PeakTroughs and contains PeakTrough instances whose Type is PeakTroughType.Peak.


PeakTroughs
public List<PeakTrough> PeakTroughs

Returns the complete List<PeakTrough> generated by the calculator, containing both peaks and troughs in chronological order.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class PeakTroughsExample : UserStrategyBase
    {
        public override void Initialize(BarHistory bars)
        {
            PeakTroughCalculator ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            foreach (PeakTrough pt in ptc.PeakTroughs)
            {
                WLColor color = pt.Type == PeakTroughType.Peak
                    ? WLColor.Red
                    : WLColor.Green;
                DrawDot(
                    pt.PeakTroughIndex,
                    pt.Value,
                    color,
                    4);
            }
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

Troughs
public List<PeakTrough> Troughs

Returns a List<PeakTrough> containing only the troughs generated by the calculator. The List is derived from PeakTroughs and contains PeakTrough instances whose Type is PeakTroughType.Trough.



PeakTrough Navigation
GetNextPeak
public PeakTrough GetNextPeak(PeakTrough pt)

Returns the peak following the specified pt, or null if no subsequent peak is available. If pt is itself a peak, the following peak is returned. If pt is a trough, the peak immediately following that trough is returned.


GetNextPeakTrough
public PeakTrough GetNextPeakTrough(PeakTrough pt)

Returns the PeakTrough immediately following pt in the alternating sequence of peaks and troughs. If pt is a peak, this method returns the following trough. If pt is a trough, it returns the following peak.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class NextPeakTroughExample : UserStrategyBase
    {
        public override void Initialize(BarHistory bars)
        {
            PeakTroughCalculator ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            foreach (PeakTrough pt in ptc.PeakTroughs)
            {
                PeakTrough next = ptc.GetNextPeakTrough(pt);
                if (next == null)
                    continue;
                DrawLine(
                    pt.XIndex,
                    pt.YValue,
                    next.XIndex,
                    next.YValue,
                    WLColor.Blue,
                    2);
            }
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

GetNextTrough
public PeakTrough GetNextTrough(PeakTrough pt)

Returns the trough following the specified pt, or null if no subsequent trough is available. If pt is itself a trough, the following trough is returned. If pt is a peak, the trough immediately following that peak is returned.


GetPrevPeak
public PeakTrough GetPrevPeak(PeakTrough pt)

Returns the confirmed peak preceding the specified pt, or null if a previous peak is not available.


GetPrevPeakTrough
public PeakTrough GetPrevPeakTrough(PeakTrough pt)

Returns the PeakTrough immediately preceding pt in the alternating sequence of peaks and troughs. If pt is a peak, this method returns the preceding trough. If pt is a trough, it returns the preceding peak.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class PreviousPeakTroughExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            _ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            PeakTrough current = _ptc.GetPeakTrough(idx);
            if (current == null)
                return;
            PeakTrough previous =
                _ptc.GetPrevPeakTrough(current);
            if (previous == null)
                return;
            if (current.DetectedAtIndex == idx)
            {
                DrawLine(
                    previous.XIndex,
                    previous.YValue,
                    current.XIndex,
                    current.YValue,
                    WLColor.Blue,
                    2);
            }
        }
    }
}

GetPrevTrough
public PeakTrough GetPrevTrough(PeakTrough pt)

Returns the confirmed trough preceding the specified pt, or null if a previous trough is not available.



Provisional Peaks and Troughs
ProvisionalPeakTroughIndex
public int ProvisionalPeakTroughIndex(int idx)

Returns the index of the provisional, not-yet-confirmed peak or trough as of idx. After a confirmed trough, this is the index of the highest value encountered while searching for the next peak. After a confirmed peak, it is the index of the lowest value encountered while searching for the next trough. Before the first confirmed PeakTrough is available, the method returns -1.


ProvisionalPeakTroughValue
public double ProvisionalPeakTroughValue(int idx)

Returns the value of the provisional, not-yet-confirmed peak or trough as of idx. After a confirmed trough, the method returns the provisional peak value. After a confirmed peak, it returns the provisional trough value. Returns Double.NaN if a provisional value is not available.

Example Code
using System;
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class ProvisionalPeakTroughExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            double reversal = 2.0;
            _ptc = new PeakTroughCalculator(
                bars,
                reversal,
                PeakTroughReversalType.Percent);
            ZigZagHL zz = new ZigZagHL(
                bars,
                reversal,
                PeakTroughReversalType.Percent,
                true);
            PlotIndicator(zz);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            int provisionalIndex =
                _ptc.ProvisionalPeakTroughIndex(idx);
            double provisionalValue =
                _ptc.ProvisionalPeakTroughValue(idx);
            if (provisionalIndex >= 0 &&
                !Double.IsNaN(provisionalValue))
            {
                DrawDot(
                    provisionalIndex,
                    provisionalValue,
                    WLColor.Blue,
                    3);
            }
        }
    }
}


Trendlines
GetLowerTrendLine
public TrendLine GetLowerTrendLine(
int idx,
int numTroughs,
bool useLog = false)

Returns a TrendLine calculated from the specified number of confirmed troughs as of idx. The method returns null if there are not enough troughs available. Set useLog to true to calculate the TrendLine using logarithmic values.


GetUpperTrendLine
public TrendLine GetUpperTrendLine(
int idx,
int numPeaks,
bool useLog = false)

Returns a TrendLine calculated from the specified number of confirmed peaks as of idx. The method returns null if there are not enough peaks available. Set useLog to true to calculate the TrendLine using logarithmic values.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class PeakTroughTrendLineExample : UserStrategyBase
    {
        public override void Initialize(BarHistory bars)
        {
            PeakTroughCalculator ptc = new PeakTroughCalculator(
                bars,
                5.0,
                PeakTroughReversalType.Percent);
            TrendLine lower =
                ptc.GetLowerTrendLine(bars.Count - 1, 4);
            if (lower != null)
            {
                DrawLine(
                    lower.Index1,
                    lower.Value1,
                    lower.Index2,
                    lower.Value2,
                    WLColor.Red,
                    2,
                    LineStyle.Dashed);
            }
            TrendLine upper =
                ptc.GetUpperTrendLine(bars.Count - 1, 4);
            if (upper != null)
            {
                DrawLine(
                    upper.Index1,
                    upper.Value1,
                    upper.Index2,
                    upper.Value2,
                    WLColor.Green,
                    2,
                    LineStyle.Dashed);
            }
        }
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

TrendlineEnvelope
public TrendLine TrendlineEnvelope(
BarHistory bars,
int idx,
PeakTroughType ptType,
int points,
bool useDescendingPeakTroughs,
double allowableIncursionPct = 3,
bool useLog = false,
int maxLookbackDays = 1000)

Calculates and returns the preferred active trendline envelope formed from peaks or troughs as of idx. Specify PeakTroughType.Peak for an upper trendline based on peaks, or PeakTroughType.Trough for a lower trendline based on troughs. points determines how many PeakTroughs are used to form the line. useDescendingPeakTroughs determines whether descending or ascending PeakTroughs are considered. allowableIncursionPct specifies how far another PeakTrough can penetrate the candidate trendline before that line is rejected. Set useLog to true to calculate logarithmic trendlines. maxLookbackDays limits the age of PeakTroughs considered. For a two-point trendline, the method selects the candidate based on slope. For a multi-point trendline, it returns the candidate with the lowest TrendLine.Deviation. Returns null if an appropriate trendline cannot be calculated.

Example Code
using System;
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class DescendingTrendlineBreak : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            StartIndex = 20;
            _ptc = new PeakTroughCalculator(
                bars,
                10.0,
                PeakTroughReversalType.Percent);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            TrendLine tl = _ptc.TrendlineEnvelope(
                bars,
                idx,
                PeakTroughType.Peak,
                4,
                true,
                3.0,
                false,
                1500);
            if (tl == null)
                return;
            double value = tl.ExtendTo(idx, false);
            if (idx == bars.Count - 1)
            {
                DrawLine(
                    tl.Index1,
                    tl.Value1,
                    idx,
                    value,
                    WLColor.Green,
                    2);
            }
            if (!HasOpenPosition(bars, PositionType.Long) &&
                tl.Deviation <= 2.0 &&
                bars.Close[idx] > value &&
                bars.Close[idx - 1] <= tl.ExtendTo(idx - 1, false))
            {
                PlaceTrade(
                    bars,
                    TransactionType.Buy,
                    OrderType.Market);
            }
        }
    }
}

TrendlineEnvelopes
public List<TrendLine> TrendlineEnvelopes(
BarHistory bars,
int idx,
PeakTroughType ptType,
int points,
bool useDescendingPeakTroughs,
double allowableIncursionPct = 3,
bool useLog = false,
int maxLookbackDays = 1000)

Returns the List<TrendLine> of active trendline-envelope candidates that satisfy the supplied criteria. The parameters have the same meanings as those of TrendlineEnvelope. For two-point lines, the returned List is ordered by slope. Peak trendlines are ordered from the most negative to the most positive slope, while trough trendlines are ordered from the most positive to the most negative slope. For multi-point lines, the method returns all qualifying TrendLines. Use each TrendLine's Deviation property to compare how closely its constituent PeakTroughs conform to the line. The method can return null when insufficient qualifying PeakTroughs are available, or an empty List when no active trendlines satisfy the criteria.

Example Code
using System.Collections.Generic;
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
namespace WealthScript
{
    public class TrendlineEnvelopesExample : UserStrategyBase
    {
        private PeakTroughCalculator _ptc;
        public override void Initialize(BarHistory bars)
        {
            StartIndex = 20;
            _ptc = new PeakTroughCalculator(
                bars,
                10.0,
                PeakTroughReversalType.Percent);
        }
        public override void Execute(BarHistory bars, int idx)
        {
            if (idx != bars.Count - 1)
                return;
            List<TrendLine> lines = _ptc.TrendlineEnvelopes(
                bars,
                idx,
                PeakTroughType.Peak,
                2,
                true);
            if (lines == null)
                return;
            foreach (TrendLine line in lines)
            {
                DrawLine(
                    line.Index1,
                    line.Value1,
                    idx,
                    line.ExtendTo(idx),
                    WLColor.Red,
                    1);
            }
        }
    }
}