The phrase "zero lag" is common indicator shorthand, but no causal smoother using historical prices is literally lag-free. This note examines an Alligator-style NinjaTrader 8 study that replaces the classic smoothing with a recursive ITrend calculation. The example is educational and unvalidated; it does not establish earlier entries, better exits, or improved trading results.
Provenance and attribution
The initial design supplied for this article was described as derived from a ThinkScript community implementation of a near-zero-lag Williams Alligator. No original author name, source URL, version, or license was supplied, and none is invented here. That provenance and permission therefore remain unverified. The recurrence is commonly attributed in technical-analysis literature to John F. Ehlers' Instantaneous Trendline, while the Jaw, Teeth, and Lips framing derives from Bill Williams' Alligator. Anyone redistributing a verified derivative should locate the original ThinkScript source and comply with its actual terms.
The C# below is rewritten as a self-contained expression of the disclosed recurrence. This wording is attribution, not a claim that an unidentified community source granted a license.
What this variant computes
The classic Alligator convention uses smoothed moving averages of median price, generally with Jaw, Teeth, and Lips lengths of 13, 8, and 5, then displays those lines forward by 8, 5, and 3 bars. This implementation is not mathematically equivalent. It applies the same ITrend recurrence separately to the current median-price series for each length and uses no display displacement.
For median price P_t = (High_t + Low_t) / 2 and alpha = 2 / (length + 1), the implemented recurrence is:
ITrend_t = (alpha - alpha² / 4)P_t + (alpha² / 2)P_(t-1) - (alpha - 3alpha² / 4)P_(t-2) + 2(1 - alpha)ITrend_(t-1) - (1 - alpha)²ITrend_(t-2)
Each line uses a coherent sequence: current, one-bar-old, and two-bar-old median prices feed the same output series. The first two output values are seeded directly from available median prices before the recurrence begins. This avoids mixing an eight-bar-old Jaw input with unrelated one- and two-bar-old inputs, which was a defect in the earlier draft.
Removing the classic forward display shifts does not prove that the filter leads the classic indicator. It changes both the smoothing rule and visual alignment. Any lag comparison must define a benchmark and measurement method before examining charts.
NinjaTrader 8 implementation
// ITrend-smoothed Alligator-style indicator for NinjaTrader 8.
// The concept supplied for this note was described as community-derived
// ThinkScript. Original author, URL, version, and license were unavailable.
#region Using declarations
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.NinjaScript;
#endregion
namespace NinjaTrader.NinjaScript.Indicators.OranselIndustries
{
public class ITrendAlligatorVariant : Indicator
{
private Series<double> _medianPrice;
private Series<double> _jawTrend;
private Series<double> _teethTrend;
private Series<double> _lipsTrend;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"Alligator-style lines using an ITrend recurrence without display displacement.";
Name = "ITrendAlligatorVariant";
Calculate = Calculate.OnBarClose;
IsOverlay = true;
DisplayInDataBox = true;
DrawOnPricePanel = true;
PaintPriceMarkers = true;
ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
IsSuspendedWhileInactive = true;
JawLength = 13;
TeethLength = 8;
LipsLength = 5;
AddPlot(Brushes.Blue, "JAW");
AddPlot(Brushes.Red, "TEETH");
AddPlot(Brushes.Lime, "LIPS");
}
else if (State == State.DataLoaded)
{
_medianPrice = new Series<double>(this);
_jawTrend = new Series<double>(this);
_teethTrend = new Series<double>(this);
_lipsTrend = new Series<double>(this);
}
}
protected override void OnBarUpdate()
{
_medianPrice[0] = (High[0] + Low[0]) / 2.0;
if (CurrentBar < 2)
{
double seed = CurrentBar == 0
? _medianPrice[0]
: (_medianPrice[0] + _medianPrice[1]) / 2.0;
_jawTrend[0] = seed;
_teethTrend[0] = seed;
_lipsTrend[0] = seed;
}
else
{
_jawTrend[0] = CalculateITrend(JawLength, _jawTrend);
_teethTrend[0] = CalculateITrend(TeethLength, _teethTrend);
_lipsTrend[0] = CalculateITrend(LipsLength, _lipsTrend);
}
Values[0][0] = _jawTrend[0];
Values[1][0] = _teethTrend[0];
Values[2][0] = _lipsTrend[0];
}
private double CalculateITrend(int length, Series<double> output)
{
double alpha = 2.0 / (length + 1.0);
double alphaSquared = alpha * alpha;
return (alpha - alphaSquared / 4.0) * _medianPrice[0]
+ (alphaSquared / 2.0) * _medianPrice[1]
- (alpha - 0.75 * alphaSquared) * _medianPrice[2]
+ 2.0 * (1.0 - alpha) * output[1]
- (1.0 - alpha) * (1.0 - alpha) * output[2];
}
#region Properties
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name = "Jaw Length", Description = "Jaw ITrend length", Order = 1, GroupName = "Parameters")]
public int JawLength { get; set; }
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name = "Teeth Length", Description = "Teeth ITrend length", Order = 2, GroupName = "Parameters")]
public int TeethLength { get; set; }
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name = "Lips Length", Description = "Lips ITrend length", Order = 3, GroupName = "Parameters")]
public int LipsLength { get; set; }
[Browsable(false)]
[XmlIgnore]
public Series<double> JAW { get { return Values[0]; } }
[Browsable(false)]
[XmlIgnore]
public Series<double> TEETH { get { return Values[1]; } }
[Browsable(false)]
[XmlIgnore]
public Series<double> LIPS { get { return Values[2]; } }
#endregion
}
}
Parameter and display ledger
| Line | ITrend length | Classic display shift | Shift in this code | Plot color |
|---|---|---|---|---|
| Lips | 5 | 3 bars forward | 0 | Lime |
| Teeth | 8 | 5 bars forward | 0 | Red |
| Jaw | 13 | 8 bars forward | 0 | Blue |
The classic shifts in this table are reference conventions, not historical-index inputs. Reading High[8] and Low[8] at the current bar would delay the source by eight bars; it would not reproduce a line displayed eight bars forward.
Calculate.OnBarClose means the plots update after a completed bar. Switching to intrabar calculation would change signal timing and resource use and would require a separate validation design.
Observable states, not trading rules
The three lines can be described without implying a tested strategy:
- Compression: the distance among the lines is small relative to a stated scale.
- Bullish ordering:
Lips > Teeth > Jaw. - Bearish ordering:
Jaw > Teeth > Lips. - Ordering change: one or more inequalities change after a completed bar.
A chart description such as "price is above all three lines while bullish ordering holds" is reproducible, but it is not an entry rule until the researcher defines price input, crossing logic, bar timing, spread and cost assumptions, and what happens on gaps. The code only plots lines; it submits no entries or exits.
The informal idea that a longer compression precedes a larger breakout is a hypothesis. To test it, define compression numerically—for example, maximum line separation divided by ATR(14)—choose the lookback and forward horizon in advance, and compare the conditional forward-move distribution with an unconditional baseline. No such result is supplied here.
Comparison protocol
A defensible comparison with a classic Alligator should implement both indicators on the same median-price input and data set. Before evaluation, choose a lag metric such as bar delay at identified turning points or phase delay at selected frequencies. Also report false ordering changes, turnover under any derived rule, and sensitivity to neighboring lengths.
Visual overlays alone are vulnerable to display-shift confusion and selection of favorable chart regions. Keep the classic forward displacement separate from the smoothing calculation, align timestamps before measuring, and include startup bars in the implementation audit even if they are excluded from performance statistics.
Limitations
The recurrence depends on historical input and prior outputs, so it can lag and its startup seed affects early values. The fixed alpha = 2 / (length + 1) mapping is an assumption. Lengths 13, 8, and 5, median-price input, zero displacement, and on-close evaluation are illustrative and unvalidated. Different instruments, sessions, bar constructions, and feeds can produce different behavior.
Using volume or a momentum oscillator alongside these plots adds parameters; it does not automatically validate the combined signal. Treat each combination as a separately registered hypothesis, include realistic costs if orders are modeled, and retain out-of-sample data for final evaluation.