(*Description...
Peaks&Troughs Indicator (PTBar) and Swing Indicator (SI) - as described in November 2006 Stocks&Commodities (Siligardos' article on "Active Trend Lines")
function PTBarSeries builds a series containing bar numbers of most recent peaks/troughs, by measuring percent changes from lowest values, that is: using (100*Threshold)/(100+Threshold) when looking for peaks
[this is to ensure that any two consecutive peaks (resp. troughs) will have exactly one trough (resp. peak) between them]
function SISeries builds a series that interpolates through those peaks and troughs
N.B.: USES FUTURE INFORMATION!!!
*)
function PTBarSeries(Series: integer; Threshold: float): integer;
begin
var Name: string = 'PTBar(' + GetDescription(Series) +
',' + FloatToStr(Threshold) + ')';
Result := FindNamedSeries(Name);
if Result >= 0 then exit;
Result := CreateNamedSeries(Name);
var Bar1, Bar2, Bar: integer;
var PeakReversalFactor: float = 100 / (100 + Threshold);
var TroughReversalFactor: float = (100 + Threshold) / 100;
// locate Bar2 as the earliest bar that goes "far enough" from first bar
for Bar2 := 1 to BarCount - 1 do
if (@Series[Bar2] >= @Series[Bar1] * TroughReversalFactor)
or (@Series[Bar2] <= @Series[Bar1] * PeakReversalFactor) then
break;
// go on, alternating peaks and troughs
for Bar := Bar2 to BarCount - 1 do
begin
if (@Series[Bar2] > @Series[Bar1]) then // looking for a new peak
begin
if (@Series[Bar] >= @Series[Bar2]) then Bar2 := Bar // found higher high
else if (@Series[Bar] <= @Series[Bar2] * PeakReversalFactor) then
begin // reversal triggered at Bar, peak at Bar2 is confirmed
Bar1 := Bar2;
Bar2 := Bar;
end;
end
else // @Series[Bar2] < @Series[Bar1], looking for a new trough
begin
if (@Series[Bar] <= @Series[Bar2]) then Bar2 := Bar // found lower low
else if (@Series[Bar] >= @Series[Bar2] * TroughReversalFactor) then
begin // reversal triggered at Bar, trough at Bar2 is confirmed
Bar1 := Bar2;
Bar2 := Bar;
end;
end;
// Bar1 is last confirmed peak/trough bar (as seen at Bar)
SetSeriesValue(Bar, Result, Bar1);
end;
end;
function PTBar(Bar, Series: integer; Threshold: float): integer;
begin
Result := trunc(GetSeriesValue(Bar, PTBarSeries(Series, Threshold)));
end;
function SISeries(Series: integer; Threshold: float): integer;
begin
var Name: string = 'SwingIndicator(' + GetDescription(Series) +
',' + FloatToStr(Threshold) + ')';
Result := FindNamedSeries(Name);
if Result >= 0 then exit;
Result := CreateNamedSeries(Name);
var Bar, Bar1, Bar2: integer;
var PTBs: integer = PTBarSeries(Series, Threshold);
Bar1 := BarCount - 1;
for Bar := Bar1 downto 0 do
begin
if Bar = Bar1 then
begin
Bar2 := Bar1;
Bar1 := trunc(@PTBs[Bar2]);
end;
SetSeriesValue(Bar, Result, LineExtendY(Bar1, @Series[Bar1], Bar2, @Series[Bar2], Bar));
end;
end;
function SI(Bar, Series: integer; Threshold: float): float;
begin
Result := GetSeriesValue(Bar, SISeries(Series, Threshold));
end;