PeakTroughCalculator
Namespace: WealthLab.Core
Parent: Object

The PeakTroughCalculator is a utility class the calculates peaks and troughs in the source time series data. Peaks and troughs are calculated by observing when the data moves by a certain reversal amount, which can be expressed as either percent or point value. The result is a list of PeakTrough objects available via the PeakTroughs property.

Due to the nature of this calculation, the detection of peaks and troughs always happens a little after the fact. The resulting PeakTrough instances provide both the point at which the peak trough began (the PeakTroughIndex property) as well as the point at which it was detected (the DetectedAtIndex property).

Constructor
PeakTroughCalculator
public PeakTroughCalculator(BarHistory source, double reversal, PeakTroughReversalTypes reversalType = PeakTroughReversalTypes.Percent)
public PeakTroughCalculator(TimeSeries highs, TimeSeries lows, double reversalAmount, PeakTroughReversalTypes reversalType = PeakTroughReversalTypes.Percent)
public PeakTroughCalculator(TimeSeries source, double reversal, PeakTroughReversalTypes reversalType = PeakTroughReversalTypes.Percent)

The class supports three constructors.

  • The first takes a single BarHistory object, and the peaks and troughs are calculated based on high and low prices
  • The second takes two TimeSeries objects, which should be the desired highs and lows
  • The third takes a single TimeSeries object on which the peaks and troughs are calculated
Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using System.Drawing;

namespace WealthLab
{
	public class MyStrategy1 : UserStrategyBase
	{
		//compare two methods of determining peak/troughs, using closing price only vs using highs/lows
		public override void Initialize(BarHistory bars)
		{
			PeakTroughCalculator ptClose = new PeakTroughCalculator(bars.Close, 5.0, PeakTroughReversalTypes.Percent);
			RenderPeakTroughs(ptClose, Color.Red);
			PeakTroughCalculator ptHighLow = new PeakTroughCalculator(bars, 5.0, PeakTroughReversalTypes.Percent);
			RenderPeakTroughs(ptHighLow, Color.Blue);
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
		}

		//render most recent peak/trough
		private void RenderPeakTroughs(PeakTroughCalculator ptc, Color color)
		{
			if (ptc.PeakTroughs.Count < 2)
				return;
			PeakTrough pt = ptc.PeakTroughs[ptc.PeakTroughs.Count - 1];
			PeakTrough pt2 = ptc.GetPeakTrough(pt.PeakTroughIndex);
			DrawLine(pt.PeakTroughIndex, pt.Value, pt2.PeakTroughIndex, pt2.Value, color, 2, LineStyles.Solid, "Price");
		}
	}
}


Divergences
HasFallingPeaks
public bool HasFallingPeaks(int idx)

Returns true if the peak detected as of the specified index (idx) in the source data has a lower value than the previous peak.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript3
{
	public class MyStrategy : UserStrategyBase
	{
		PeakTroughCalculator _ptc;
		PeakTroughCalculator _ptcRsi;
		IndicatorBase _rsi;

		public override void Initialize(BarHistory bars)
		{
			StartIndex = 100;

			//control variables
			double swing = 10.0;

			//calculate peaks and troughs based on closes
			_ptc = new PeakTroughCalculator(bars.Close, swing, PeakTroughReversalTypes.Percent);

			//calculate peaks and troughs based on RSI
			_rsi = RSI.Series(bars.Close, 14);
			PlotIndicatorLine(_rsi);
			_ptcRsi = new PeakTroughCalculator(_rsi, swing, PeakTroughReversalTypes.Point);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			//shade sell zones red based on divergence
			if (_ptc.HasRisingPeaks(idx) && _ptcRsi.HasFallingPeaks(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Red));

			//shade buy zones green based on divergence
			if (_ptc.HasFallingTroughs(idx) && _ptcRsi.HasRisingTroughs(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Green));
		}
	}
}

HasFallingTroughs
public bool HasFallingTroughs(int idx)

Returns true if the trough detected as of the specified index (idx) in the source data has a lower value than the previous trough.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthScript
{
	public class GetTroughExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 5.0;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			//connect the troughs
			//if trough is rising, use a green line
			int idx = bars.Count - 1;
			PeakTrough lastpt = null;
			do
			{
				PeakTrough pt = _ptc.GetTrough(idx);
				if (pt == null) break;

				if (lastpt == null)
				{
					DrawLine(idx, pt.Value, pt.XIndex, pt.Value, Color.Blue, 2);
				}
				else
				{
					Color clr = _ptc.HasFallingTroughs(lastpt.DetectedAtIndex) ? Color.Fuchsia : Color.Green;
					DrawLine(lastpt.XIndex, lastpt.Value, pt.XIndex, pt.Value, clr, 2);
				}

				lastpt = pt;
				idx = pt.XIndex - 1;

			} while (idx > 10);

		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			//color background light pink while the last detected trough was lower than the previous one
			if (_ptc.HasRisingTroughs(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(20, Color.Blue));
		}
	}
}

HasRisingPeaks
public bool HasRisingPeaks(int idx)

Returns true if the peak detected as of the specified index (idx) in the source data has a higher value than the previous peak.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript3
{
	public class MyStrategy : UserStrategyBase
	{
		PeakTroughCalculator _ptc;
		PeakTroughCalculator _ptcRsi;
		IndicatorBase _rsi;

		public override void Initialize(BarHistory bars)
		{
			StartIndex = 100;

			//control variables
			double swing = 10.0;

			//calculate peaks and troughs based on closes
			_ptc = new PeakTroughCalculator(bars.Close, swing, PeakTroughReversalTypes.Percent);

			//calculate peaks and troughs based on RSI
			_rsi = RSI.Series(bars.Close, 14);
			PlotIndicatorLine(_rsi);
			_ptcRsi = new PeakTroughCalculator(_rsi, swing, PeakTroughReversalTypes.Point);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			//shade sell zones red based on divergence
			if (_ptc.HasRisingPeaks(idx) && _ptcRsi.HasFallingPeaks(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Red));

			//shade buy zones green based on divergence
			if (_ptc.HasFallingTroughs(idx) && _ptcRsi.HasRisingTroughs(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Green));
		}
	}
}

HasRisingTroughs
public bool HasRisingTroughs(int idx)

Returns true if the trough detected as of the specified index (idx) in the source data has a higher value than the previous trough.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthScript
{
	public class GetTroughExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 5.0;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			//connect the troughs
			//if trough is rising, use a green line
			int idx = bars.Count - 1;
			PeakTrough lastpt = null;
			do
			{
				PeakTrough pt = _ptc.GetTrough(idx);
				if (pt == null) break;

				if (lastpt == null)
				{
					DrawLine(idx, pt.Value, pt.XIndex, pt.Value, Color.Blue, 2);
				}
				else
				{
					Color clr = _ptc.HasFallingTroughs(lastpt.DetectedAtIndex) ? Color.Fuchsia : Color.Green;
					DrawLine(lastpt.XIndex, lastpt.Value, pt.XIndex, pt.Value, clr, 2);
				}

				lastpt = pt;
				idx = pt.XIndex - 1;

			} while (idx > 10);

		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			//color background light pink while the last detected trough was lower than the previous one
			if (_ptc.HasRisingTroughs(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(20, Color.Blue));
		}
	}
}

PeakState
public int PeakState(int idx)

As of the specified index (idx) in the source data, returns 1 if the most recently detected peak has a higher value than the previous peak, and -1 if it has a lower value. If the values are equal, or one or both peaks are not available, returns 0.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript3
{
	public class PeakTroughStateExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;
		PeakTroughCalculator _ptcRsi;
		IndicatorBase _rsi;

		public override void Initialize(BarHistory bars)
		{
			StartIndex = 100;

			//control variables
			double swing = 10.0;

			//calculate peaks and troughs based on closes
			_ptc = new PeakTroughCalculator(bars.Close, swing, PeakTroughReversalTypes.Percent);

			//calculate peaks and troughs based on RSI
			_rsi = RSI.Series(bars.Close, 14);
			PlotIndicatorLine(_rsi);
			_ptcRsi = new PeakTroughCalculator(_rsi, swing, PeakTroughReversalTypes.Point);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			//shade areas where PeakStates and TroughStates don't match
			if (_ptc.PeakState(idx) != _ptcRsi.PeakState(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Red));

			//shade buy zones green based on divergence
			if (_ptc.TroughState(idx) != _ptcRsi.TroughState(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Green));
		}
	}
}

TroughState
public int TroughState(int idx)

As of the specified index (idx) in the source data, returns 1 if the most recently detected trough has a higher value than the previous trough, and -1 if it has a lower value. If the values are equal, or one or both troughs are not available, returns 0.

Remarks

  • This method returns a value based on confirmed PeakTroughs.
  • It may be obvious that the value should change based on an unconfirmed PeakTrough long before the confirmation occurs.
Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript3
{
	public class PeakTroughStateExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;
		PeakTroughCalculator _ptcRsi;
		IndicatorBase _rsi;

		public override void Initialize(BarHistory bars)
		{
			StartIndex = 100;

			//control variables
			double swing = 10.0;

			//calculate peaks and troughs based on closes
			_ptc = new PeakTroughCalculator(bars.Close, swing, PeakTroughReversalTypes.Percent);

			//calculate peaks and troughs based on RSI
			_rsi = RSI.Series(bars.Close, 14);
			PlotIndicatorLine(_rsi);
			_ptcRsi = new PeakTroughCalculator(_rsi, swing, PeakTroughReversalTypes.Point);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			//shade areas where PeakStates and TroughStates don't match
			if (_ptc.PeakState(idx) != _ptcRsi.PeakState(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Red));

			//shade buy zones green based on divergence
			if (_ptc.TroughState(idx) != _ptcRsi.TroughState(idx))
				SetBackgroundColor(bars, idx, Color.FromArgb(40, Color.Green));
		}
	}
}


Peaks & Troughs
GetPeak
public PeakTrough GetPeak(int idx)

Returns the most recent peak (instance of the PeakTrough class) detected as of the specified index in the source data, or null if not available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthScript
{
	public class GetPeakExample : UserStrategyBase
	{

		PeakTroughCalculator _ptc;

		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 5.0;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			//connect the troughs
			int idx = bars.Count - 1;
			PeakTrough lastpt = null;
			do
			{
				PeakTrough pt = _ptc.GetPeak(idx);
				if (pt == null) break;

				if (lastpt == null)
				{
					DrawLine(idx, pt.Value, pt.XIndex, pt.Value, Color.Red, 2);
				}
				else
				{
					DrawLine(lastpt.XIndex, lastpt.Value, pt.XIndex, pt.Value, Color.Green, 1);
				}

				lastpt = pt;
				idx = pt.XIndex - 1;

			} while (idx > 10);

		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{

		}
	}
}

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

Access the list of peaks (only) that were generated as of the specified idx and that are not older than maxAgeInDays calendar days. These are instances of the PeakTrough class.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class GetTroughsAsOfExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 10.0;
			StartIndex = 20;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			List<PeakTrough> peaks = _ptc.GetPeaksAsOf(bars.Count - 1);

			//connect the peaks
			PeakTrough lastpt = null;
			foreach (PeakTrough pt in peaks)
			{
				if (lastpt == null)
				{
					lastpt = pt;
					continue;
				}
				DrawLine(lastpt.XIndex, lastpt.YValue, pt.XIndex, pt.YValue, Color.Blue, 2);
				lastpt = pt;
			}
		}


		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

GetPeakTrough
public PeakTrough GetPeakTrough(int idx)

Returns the most recent peak or trough (instance of the PeakTrough class) detected as of the specified index in the source data, or null if not available.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript3
{
	public class MyStrategy : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swing = 5.0;

			//calculate peaks and troughs based on High/Low
			_ptc = new PeakTroughCalculator(bars, swing, PeakTroughReversalTypes.Percent);

			//was the most recent PeakTrough a peak or a trough?
			PeakTrough pt = _ptc.GetPeakTrough(bars.Count - 1);

			if (pt != null)
			{
				string pttype = pt.Type == PeakTroughTypes.Peak ? "Peak" : "Trough";
				string txt = string.Format("Last detected a {0:N1}% {1} on {2:d}", swing, pttype, bars.DateTimes[pt.PeakTroughIndex]);
				DrawHeaderText(txt, Color.Red, 12);
				SetBackgroundColor(bars, pt.PeakTroughIndex, Color.FromArgb(40, Color.Blue));
				DrawLine(pt.PeakTroughIndex, pt.Value, bars.Count - 1, pt.Value, Color.Blue, 2);
			}
			else
			{
				string txt = string.Format("Could not find a {0:N1}% PeakTrough given these data!", swing);
				DrawHeaderText(txt, Color.Red, 12);
			}
		}

		public override void Execute(BarHistory bars, int idx)
		{

		}
	}
}

GetTrough
public PeakTrough GetTrough(int idx)

Returns the most recent trough (instance of the PeakTrough class) detected as of the specified index in the source data, or null if not available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthScript2
{
	public class GetTroughExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 5.0;
			StartIndex = 100;

			//show the limit prices, usually the PeakTrough values
			PlotStopsAndLimits(4);

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);
		}


		public override void Execute(BarHistory bars, int idx)
		{
			//buy at the previous trough and sell at the previous peak
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				PeakTrough tgh = _ptc.GetTrough(idx);
				if (tgh == null) return;
				PlaceTrade(bars, TransactionType.Buy, OrderType.Limit, tgh.Value);
			}
			else
			{
				Position p = LastPosition;
				PeakTrough peak = _ptc.GetPeak(p.EntryBar);
				double tgt = peak.Value > p.EntryPrice ? peak.Value : p.EntryPrice * 1.05;

				PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, tgt);
			}
		}
	}
}

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

Access the list of troughs (only) that were generated as of the specified idx and that are not older than maxAgeInDays calendar days. These are instances of the PeakTrough class.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript123
{
	public class GetTroughsAsOfExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 10.0;
			StartIndex = 20;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			List<PeakTrough> troughs = _ptc.GetTroughsAsOf(bars.Count - 1);

			//connect the troughs
			PeakTrough lastpt = null;
			foreach (PeakTrough pt in troughs)
			{
				if (lastpt == null)
				{
					lastpt = pt;
					continue;
				}
				DrawLine(lastpt.XIndex, lastpt.YValue, pt.XIndex, pt.YValue, Color.Blue, 2);
				lastpt = pt;
			}
		}


		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

PeakTroughs
public List<PeakTrough> PeakTroughs

Access the list of peaks and troughs that were generated. These are instances of the PeakTrough class.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthScript3
{
	public class PeakTroughIndexExample : UserStrategyBase
	{
		PeakTroughCalculator _ptc;

		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 5.0;

			//calculate peaks and troughs based on high/lows
			_ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			foreach (PeakTrough pt in _ptc.PeakTroughs)
			{
				//highlight where the peaks and troughs occurred
				SetBackgroundColor(bars, pt.PeakTroughIndex, Color.FromArgb(40, Color.Blue));

				//add a line to detection bar
				DrawLine(pt.PeakTroughIndex, pt.Value, pt.DetectedAtIndex, pt.Value, Color.Purple, 2);
			}
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}


Previous Peaks & Troughs
GetPrevPeak
public PeakTrough GetPrevPeak(PeakTrough pt)

Returns the first peak that occurred prior to the PeakTrough specified in the pt parameter, or null if not available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript
{
	public class DivergenceExample : UserStrategyBase
	{

		IndicatorBase _rsi;
		PeakTroughCalculator _ptcRsi;
		List<string> _test = new List<string>();


		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			StartIndex = 100;

			//control variables
			double swingPts = 13.0;

			//calculate peaks and troughs of the RSI
			_rsi = RSI.Series(bars.Close, 14);
			_ptcRsi = new PeakTroughCalculator(_rsi, swingPts, PeakTroughReversalTypes.Point);

			//plot ZigZagHL indicator, it's based on the peak/troughs we're using
			//ZigZag zz = new ZigZag(_rsi, swingPts, PeakTroughReversalTypes.Point, false);
			//PlotIndicator(zz);

			PlotIndicatorLine(_rsi, Color.Blue);
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			//get the last 2 peaks
			PeakTrough pt = _ptcRsi.GetPeak(idx);
			if (pt == null) return;
			PeakTrough prept = _ptcRsi.GetPrevPeak(pt);
			if (prept == null) return;
			
			//save a key so we know these were processed
			string key = pt.XIndex + "," + prept.XIndex;

			//if the key was already processed, we're done
			if (_test.Contains(key))
				return;

			//new set of peaks, save it
			_test.Add(key);
			WriteToDebugLog(key);

			//divergence test
			if (_ptcRsi.HasFallingPeaks(idx))
			{
				if (bars.High[pt.XIndex] > bars.High[prept.XIndex])
				{
					DrawLine(pt.XIndex, bars.High[pt.XIndex], prept.XIndex, bars.High[prept.XIndex], Color.Green, 2);
					DrawLine(pt.XIndex, _rsi[pt.XIndex], prept.XIndex, _rsi[prept.XIndex], Color.Red, 2, LineStyles.Solid, _rsi.PaneTag);
				}
			}			
		}
	}
}

GetPrevTrough
public PeakTrough GetPrevTrough(PeakTrough pt)

Returns the first trough that occurred prior to the PeakTrough specified in the pt parameter, or null if not available.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript1
{
	public class DivergenceExample2 : UserStrategyBase
	{

		IndicatorBase _rsi;
		IndicatorBase _sma;
		PeakTroughCalculator _ptcRsi;
		List<string> _test = new List<string>();


		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			PlotStopsAndLimits(4);
			StartIndex = 100;

			//control variables
			double swingPts = 13.0;

			//calculate peaks and troughs of the RSI
			_rsi = RSI.Series(bars.Close, 14);
			_ptcRsi = new PeakTroughCalculator(_rsi, swingPts, PeakTroughReversalTypes.Point);
			PlotIndicatorLine(_rsi, Color.Blue);

			_sma = SMA.Series(bars.Close, 50);
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				//get the last 2 troughs
				PeakTrough pt = _ptcRsi.GetTrough(idx);
				if (pt == null) return;
				PeakTrough prept = _ptcRsi.GetPrevTrough(pt);
				if (prept == null) return;

				//save a key so we know these were processed
				string key = pt.XIndex + "," + prept.XIndex;

				//if the key was already processed, we're done
				if (_test.Contains(key))
					return;

				//new set of peaks, save it
				_test.Add(key);

				//divergence test
				if (_ptcRsi.HasRisingTroughs(idx))
				{
					if (bars.Low[pt.XIndex] < bars.Low[prept.XIndex])
					{
						//buy this divergence
						PlaceTrade(bars, TransactionType.Buy, OrderType.Market);
						DrawLine(pt.XIndex, bars.High[pt.XIndex], prept.XIndex, bars.High[prept.XIndex], Color.Green, 2);
						DrawLine(pt.XIndex, _rsi[pt.XIndex], prept.XIndex, _rsi[prept.XIndex], Color.Red, 2, LineStyles.Solid, _rsi.PaneTag);
					}
				}
			}
			else
			{
				Position p = LastPosition;
				if (p.MFEPctAsOf(idx) > 25)
				{
					PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, _sma.GetHighest(idx, idx - p.EntryBar));
				}
				else
				{
					double tgt = p.EntryPrice * 1.30;   //30% gain
					double stop = p.EntryPrice * 0.85;  //-15% stop

					PlaceTrade(bars, TransactionType.Sell, OrderType.Stop, stop);
					PlaceTrade(bars, TransactionType.Sell, OrderType.Limit, tgt);
				}
			}
		}
	}
}


TrendLines
GetLowerTrendLine
public TrendLine GetLowerTrendLine(int idx, int numTroughs)

Returns the lower TrendLine at the specified index (idx), calculated using the most recent numTroughs Troughs detected at that index. A trendline with more than 2 points is calculated using linear regression.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthLab
{
	public class GetLowerTrendLineExample : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 3.0;

			//plot ZigZagHL indicator, it's based on the peak/troughs we're using
			ZigZagHL zz = new ZigZagHL(bars, swingPct, PeakTroughReversalTypes.Percent, false);
			PlotIndicator(zz);

			//calculate peaks and troughs based on high/lows
			PeakTroughCalculator ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			//get bottom trendline
			TrendLine bottom = ptc.GetLowerTrendLine(bars.Count - 1, 4);
			if (bottom == null)
				return;
			DrawLine(bottom.Index1, bottom.Value1, bottom.Index2, bottom.Value2, Color.Red, 2, LineStyles.Dashed);

			//get upper trendline
			TrendLine top = ptc.GetUpperTrendLine(bars.Count - 1, 4);
			if (top == null)
				return;
			DrawLine(top.Index1, top.Value1, top.Index2, top.Value2, Color.Green, 2, LineStyles.Dashed);

			//display deviations
			DrawHeaderText("Top Trendline Deviation: " + top.Deviation.ToString("N2"), Color.Black, 12);
			DrawHeaderText("Bottom Trendline Deviation: " + bottom.Deviation.ToString("N2"), Color.Black, 12);
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

GetUpperTrendLine
public TrendLine GetUpperTrendLine(int idx, int numPeaks)

Returns the upper TrendLine at the specified index (idx), calculated using the most recent numPeaks Peaks detected at that index. A trendline with more than 2 points is calculated using linear regression.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;

namespace WealthLab
{
	public class GetUpperTrendLineExample : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//control variables
			double swingPct = 3.0;

			//plot ZigZagHL indicator, it's based on the peak/troughs we're using
			ZigZagHL zz = new ZigZagHL(bars, swingPct, PeakTroughReversalTypes.Percent, false);
			PlotIndicator(zz);

			//calculate peaks and troughs based on high/lows
			PeakTroughCalculator ptc = new PeakTroughCalculator(bars, swingPct, PeakTroughReversalTypes.Percent);

			//get bottom trendline
			TrendLine bottom = ptc.GetLowerTrendLine(bars.Count - 1, 4);
			if (bottom == null)
				return;
			DrawLine(bottom.Index1, bottom.Value1, bottom.Index2, bottom.Value2, Color.Red, 2, LineStyles.Dashed);

			//get upper trendline
			TrendLine top = ptc.GetUpperTrendLine(bars.Count - 1, 4);
			if (top == null)
				return;
			DrawLine(top.Index1, top.Value1, top.Index2, top.Value2, Color.Green, 2, LineStyles.Dashed);

			//display deviations
			DrawHeaderText("Top Trendline Deviation: " + top.Deviation.ToString("N2"), Color.Black, 12);
			DrawHeaderText("Bottom Trendline Deviation: " + bottom.Deviation.ToString("N2"), Color.Black, 12);
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

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

TrendlineEnvelope is the method used by the Trend Line Break Advanced Building Block condition. It returns a TrendLine for the specified parameters as follows:

  • 2-Point Lines - Returns the trendline with the least (most negative) slope for rising or falling Peaks. Likewise, the rule returns the trendline with the greatest (most positive) slope for rising or falling Troughs.
  • Multi-point Lines - Since several active trends may be found, returns the trendline with the least percentage deviation from the composing peaks/troughs.

Remarks

  • TrendlineEnvelope discovers TrendLines that you would recognize visually.
  • Consecutive Peaks/Troughs are not required, i.e., a two peak line could "envelope" several lower peaks, hence the name TrendLineEnvelope.
  • Internally, the method allows up to 2 closing prices to cross the line formed by and between 2 peaks/troughs, but a TrendLine is "de-activated" when closing price crosses the TrendLine's extension.

Usage

  1. Create an instance of the PeakTroughCalculator specifying the BarHistory and reversal points or percentages for finding peaks and troughs.
  2. Call TrendlineEnvelope method specifying:
  • the BarHistory, bars
  • the current index idx
  • to use PeakTroughTypes.Peak or PeakTroughTypes.Trough
  • number of points required to form a line
  • a percentage allowed that an intervening peak or trough may cross the line. 0 may be specified.
  • pass false for trendlines on a linear chart or true for a log chart
  • specify maxLookbackDays (calendar days) to discard old peaks or troughs looking back from the current idx
  1. Using an instance of the TrendLine returned, check if the deviation is within your specification. TrendLine.Deviation is the average percent distance from each peak/trough that creates the line returned by the linear least squares method.
  2. Test for price crossing the TrendLine extension and draw the line (see example).
Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

/* Buy when price closes above a linear descending trendline that is formed by at least four 10% peaks 
*  in a line with less than 2% deviation.  Allow an intervening peak of up to 3% to cross this line.
*/
namespace WealthScript6
{
	public class DescendingTrendlineBreak : UserStrategyBase
	{
		public override void Initialize(BarHistory bars)
		{
			StartIndex = 20;
			_pt = new PeakTroughCalculator(bars, 10.00, PeakTroughReversalTypes.Percent);
		}

		public override void Execute(BarHistory bars, int idx)
		{
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				bool tlFlag = false;
				TrendLine tl = _pt.TrendlineEnvelope(bars, idx, PeakTroughTypes.Peak, 4, _findDescending, 3.00, _useLog, 1500);
				if (tl != null)
				{
					double val = tl.ExtendTo(idx, _useLog);
					if (tl.Deviation <= 2.00)
					{
						if (bars.Close[idx] > val)
							tlFlag = bars.Close[idx - 1] < tl.ExtendTo(idx - 1, _useLog);

						//draw the line on a crossing or if the last bar
						if (tlFlag || idx == bars.Count - 1)
						{
							//extend the line up to 5 bars past the crossing
							int toIdx = idx + Math.Min(bars.Count - 1 - idx, 5);
							DrawLine(tl.Index1, tl.Value1, toIdx, tl.ExtendTo(toIdx, _useLog), Color.Green, 2, LineStyles.Solid, "Price", false, false);

							//trendline start and end points to debug
							WriteToDebugLog("idx = " + idx + "\tTrendline: [" + tl.Index1 + ", " + tl.Index2 + "]\tValue @idx: " + val.ToString("N2"));
						}
					}
				}
				if (tlFlag)
					PlaceTrade(bars, TransactionType.Buy, OrderType.Market, 0, 0, "Buy At Market (1)");

			}
			else
			{
				//sell after 5 bars
				Position p = LastPosition;
				if (idx + 1 - p.EntryBar >= 5)
				{
					PlaceTrade(bars, TransactionType.Sell, OrderType.Market, 0, 0, "TimeBased");
				}
			}

		}

		PeakTroughCalculator _pt;
		bool _findDescending = true;
		bool _useLog = false;
	}
}