Traders' Tip text
We’ve implemented the “Perceptually Important Points” (PIPs) method in a script study that uses a recursive call to find the PIP having the maximum absolute value of the vertical distance from the line connecting two PIPs previously found. The price movement plot is based on a user-specified percentage. As suggested by the article, for a DataSeries plotted in the arithmetic scale the minimum vertical distance required to find a PIP is the percentage of the DataSeries’ entire range, whereas a fixed vertical distance in a log plot is inherently represented equally by the same percentage. For example, on a log chart the distance between 1 and 10 is the same as that between 10 and 100 (or for any other 1000% price change). Finally note that due to the manner in which the indicator is constructed, the zzTOP indicator “must not” be used for backtesting, but rather could be useful for digitally scanning many charts for patterns.
Figure 1. Wealth-Lab’s zzTopAuto routine automatically adjusts for the chart pane’s log or arithmetic scale.
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;
using Community.Components;
namespace WealthLab.Strategies
{
internal struct PipInterval
{
public int X1;
public int X2;
}
public class zzTOPAutoStudy : WealthScript
{
StrategyParameter _pct;
StrategyParameter _thickness;
List _pipList; // list of PIP bar numbers
public zzTOPAutoStudy()
{
_pct = CreateParameter("PIP Percent", 20, 2, 50, 1);
_thickness = CreateParameter("Line Width", 2, 1, 3, 1);
}
int getPipBar(DataSeries ds, PipInterval pi, double minMove, bool useLog)
{
int pip = 0;
double maxDiff = 0;
double delta, y;
for(int bar = pi.X1; bar <= pi.X2; bar++)
{
if (useLog)
{
y = LineExtendYLog( pi.X1, dspi.X1, pi.X2, dspi.X2, bar );
delta = Math.Abs(Math.Log(dsbar / y));
}
else
{
y = LineExtendY( pi.X1, dspi.X1, pi.X2, dspi.X2, bar );
delta = Math.Abs(dsbar - y);
}
if( delta > maxDiff )
{
maxDiff = delta;
pip = bar;
}
}
if (maxDiff < minMove)
pip = -1; // invalid; no PIP in specified PipInterval
if (pip > 0)
_pipList.Add(pip);
return pip;
}
/* Find the new PIP for each PipInterval in the List and return the new PipIntervals */
internal List zzTopAuto(List piList, DataSeries ds, double minmove, bool useLog)
{
List nextList = new List();
foreach (PipInterval pi in piList)
{
int bar = getPipBar(ds, pi, minmove, useLog);
if (bar == -1)
continue;
else
{
PipInterval newinvl = new PipInterval();
newinvl.X1 = pi.X1;
newinvl.X2 = bar;
nextList.Add(newinvl);
PipInterval newinvl2 = new PipInterval();
newinvl2.X1 = bar;
newinvl2.X2 = pi.X2;
nextList.Add(newinvl2);
}
}
if (nextList.Count != 0)
zzTopAuto(nextList, ds, minmove, useLog);
return nextList;
}
void ZZTOP(DataSeries ds, ChartPane cp, double minPercent)
{
// Minimum vertical move for the arithmetic and log cases
double minV = 0;
if (cp.LogScale)
minV = Math.Log(1 + minPercent/100d);
else
{
int bc = Bars.Count;
minV = minPercent / 100d * (Highest.Value(bc-1, ds, bc) - Lowest.Value(bc-1, ds, bc));
}
// Initialize _pipList with the first and last bar numbers
int nbars = Bars.Count - 1;
_pipList = new List();
_pipList.Add(0);
_pipList.Add(nbars);
// Initialize the first list to pass to zzTopAuto
PipInterval interval = new PipInterval();
interval.X1 = 0;
interval.X2 = nbars;
List aList = new List();
aList.Add(interval);
// Let the recursion begin!
zzTopAuto(aList, ds, minV, cp.LogScale);
// Sort the result to plot lines between the PIPs
_pipList.Sort();
int lastpip = 0;
foreach (int pip in _pipList)
{
if (pip == 0) continue;
DrawLine(cp, lastpip, dslastpip, pip, dspip, Color.Blue, LineStyle.Solid, _thickness.ValueInt);
lastpip = pip;
}
int segments = _pipList.Count - 1;
DrawLabel(cp, "Log Scale: " + cp.LogScale.ToString());
DrawLabel(cp, "Segment Count: " + segments.ToString());
DrawLabel(cp, "PIP: " + minPercent + "%");
//DrawLabel(cp, "Press Go! after switch between Log and Arithmetic scales!", Color.Red);
}
protected override void Execute()
{
ZZTOP(Close, PricePane, _pct.Value);
DataSeries rsi = RSI.Series(Close, 14);
ChartPane rsiPane = CreatePane(40, true, true);
PlotSeries(rsiPane, rsi, Color.Black, LineStyle.Solid, 1);
ZZTOP(rsi, rsiPane, _pct.Value);
}
}
}
Robert Sucher
www.wealth-lab.com