Traders' Tip text
The article was on “trading divergences”, so we looked back to our Traders’ Tip code for the July 2004 issue of Stocks & Commodities on “VFI Divergence” and modified it for the all-new Wealth-Lab Version 6 (.NET). The Strategy’s PeakDivergence method, which automatically highlights and draws the lines on the chart, can be used to detect any indicator’s divergence from a specified price DataSeries. We’ve included a trading strategy that shorts on “Oscillator lower-top divergence” and exits after a specified number of bars, which can readily be changed using the parameter sliders in the lower-left corner.
Figure 1. When using peaks and troughs, keep in mind that price must retrace by some amount before a peak/trough can be seen. Highlighting the detection bars adds realism to what a strategy can achieve.
Strategy Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
namespace WealthLab.Strategies
{
public class DivergenceDetector : WealthScript
{
private StrategyParameter peakPctPrice;
private StrategyParameter peakPctIndicator;
private StrategyParameter exitBars;
public DivergenceDetector()
{
peakPctPrice = CreateParameter("Price Peak%", 4, 2, 10, 1);
peakPctIndicator = CreateParameter("Indctr Peak%", 20, 5, 30, 1);
exitBars = CreateParameter("Exit bars", 5, 1, 30, 1);
}
// Strategy logic
protected override void Execute()
{
int pkPctPrice = peakPctPrice.ValueInt;
int pkPctIndicator = peakPctIndicator.ValueInt;
int barsToHold = exitBars.ValueInt;
int lastDetectedBar = 0;
_barLastChecked = 0;
DataSeries rsi = RSI.Series(Close, 14);
ChartPane rsiPane = CreatePane(40, true, true);
PlotSeries(rsiPane, rsi, Color.Brown, WealthLab.LineStyle.Solid, 2);
for(int bar = 20; bar < Bars.Count; bar++)
{
if (IsLastPositionActive) {
Position p = LastPosition;
if (bar + 1 - p.EntryBar >= barsToHold)
CoverAtMarket(bar + 1, p);
}
else if (PeakDivergence(bar, Close, pkPctPrice, rsi, pkPctIndicator, rsiPane))
ShortAtMarket(bar + 1);
}
}
// Returns true if divergence with indicator detected on the specified bar
public bool PeakDivergence(int bar, DataSeries price, double pctRev1, DataSeries ind, double pctRev2, ChartPane indPane)
{
bool divergeDetected = false;
PeakTroughMode mode = PeakTroughMode.Percent;
int pb1 = (int)PeakBar.Value(bar, price, pctRev1, mode);
if (pb1 > _barLastChecked) {
_barLastChecked = pb1;
int pb2 = (int)PeakBar.Value(pb1, price, pctRev1, mode);
if (pb2 > -1) {
int testBar = Math.Min(bar, pb1 + proxBars);
int ibar1 = (int)PeakBar.Value( testBar, ind, pctRev2, mode);
// test peak proximity
if (Math.Abs(pb1 - ibar1) > proxBars) ibar1 = pb1;
int span = Math.Min(pb1 - pb2 - 1, proxBars);
testBar = Math.Min(ibar1 - 1, pb2 + span);
int ibar2 = (int)PeakBar.Value( testBar, ind, pctRev2, mode);
if (ibar2 < 0) ibar2 = 0;
if (Math.Abs(pb2 - ibar2) > proxBars) ibar2 = pb2;
divergeDetected = Math.Sign(pricepb1 - pricepb2) != Math.Sign(indibar1 - indibar2);
if (divergeDetected) {
// Highlight bar detected
SetPaneBackgroundColor(PricePane, bar, Color.FromArgb(40, Color.Blue));
DrawLine(PricePane, pb1, pricepb1 * 1.01, pb2, pricepb2 * 1.01, Color.Blue, LineStyle.Solid, 2);
DrawLine(indPane, ibar1, indibar1 * 1.01, ibar2, indibar2 * 1.01, Color.Red, LineStyle.Solid, 2);
if (Math.Sign(pricepb1 - pricepb2) == 1)
for (int b = pb2; b <= pb1; b++)
SetPaneBackgroundColor(indPane, b, Color.FromArgb(40, Color.Red));
}
}
}
return divergeDetected;
}
private const int proxBars = 6;
private int _barLastChecked = 0;
}
}