IC Markets

Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

HI traders, scalpers,

i hope you are all well.
I found out interesting topic, which i have never seen before, its called T3 moving average.

I process a lot of trading algorithms and indicators. I've noticed a common pain point for intermediate traders: you have outgrown the Simple Moving Average (SMA) and Exponential Moving Average (EMA), but you are tired of getting whipsawed by false breakouts during market retracements.

If you understand the basics of momentum and lag, it is time to look at the T3 Moving Average, developed by Tim Tillson in 1998.

The T3 is a multiple exponential moving average designed to be smoother than a traditional EMA, while simultaneously reducing the lag associated with heavy smoothing.

How the T3 Works (The Math)

Tillson achieved this by running price data through a series of six EMAs and applying a unique "Volume Factor" (usually denoted as $v$ or $a$). This factor allows you to adjust how aggressively the indicator responds to recent price changes, effectively acting as a dampener against whipsaws. The volume factor is strictly set between $0$ and $1$, with $0.7$ being the default sweet spot.

Here is the underlying coefficient math that makes the T3 unique:

$c_1 = -v^3$$c_2 = 3v^2 + 3v^3$$c_3 = -6v^2 - 3v - 3v^3$$c_4 = 1 + 3v + v^3 + 3v^2$

Once those coefficients are established based on your chosen volume factor, the indicator plots the final line using this formula (where $e_n$ represents the $n$-th smoothed EMA):

T3 = c_1 \cdot e_6 + c_2 \cdot e_5 + c_3 \cdot e_4 + c_4 \cdot e_3$$

Moving Average ComparisonIf you are wondering how the T3 stacks up against the tools you are currently using, here is a quick breakdown of their characteristics:Indicator TypeSpeed / ResponsivenessSmoothing / Noise FilteringBest Use CaseSMASlow (High Lag)ModerateBroad, long-term trend identification.EMAFast (Low Lag)Poor (Prone to whipsaws)Fast momentum trading, catching early breakouts.T3ModerateExcellentRiding established trends, acting as dynamic support/resistance.

How Intermediate Traders

Use the T3Dynamic Support and Resistance: Because the T3 is heavily smoothed, it acts as a much more reliable dynamic trendline than a standard EMA. Price will often respect a 50-period or 100-period T3 during a strong trend.The Pullback Filter: During an established trend, use the T3 to ignore minor counter-trend spikes. Only look for continuation entries (like a flag breakout) when the price pulls back to, and rejects from, the T3 line.Volume Factor Tuning: If you are trading a highly volatile pair like GBP/JPY, try lowering the $v$ factor to $0.618$ for extra smoothing. If you need it to hug the price closer, increase $v$ to $0.8$.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Here is the code for MT4 traders:

MT4 (MQL4) Version
Save this in your MetaEditor as a Custom Indicator.

Code: Select all

//+------------------------------------------------------------------+
//|                                                      TillsonT3.mq4|
//|                                          AI Generated T3 for MT4 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrDeepSkyBlue
#property indicator_width1 2
#property indicator_style1 STYLE_SOLID

input int Length = 14;         // T3 Period
input double VolumeFactor = 0.7; // Volume Factor (v)

double T3Buffer[];
double e1[], e2[], e3[], e4[], e5[], e6[];

double c1, c2, c3, c4;

int OnInit() {
   SetIndexBuffer(0, T3Buffer);
   SetIndexLabel(0, "T3(" + IntegerToString(Length) + ")");
   
   ArrayResize(e1, Bars); ArrayResize(e2, Bars);
   ArrayResize(e3, Bars); ArrayResize(e4, Bars);
   ArrayResize(e5, Bars); ArrayResize(e6, Bars);
   
   double v = VolumeFactor;
   c1 = -(v * v * v);
   c2 = (3 * v * v) + (3 * v * v * v);
   c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
   c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);

   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
                const double &open[], const double &high[], const double &low[],
                const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) {
                
   int limit = rates_total - prev_calculated;
   if(prev_calculated == 0) limit = rates_total - 1;
   
   double alpha = 2.0 / (Length + 1.0);
   
   for(int i = limit; i >= 0; i--) {
      double price = close[i];
      if(i == rates_total - 1) {
         e1[i] = price; e2[i] = price; e3[i] = price;
         e4[i] = price; e5[i] = price; e6[i] = price;
         T3Buffer[i] = price;
         continue;
      }
      
      e1[i] = alpha * price + (1 - alpha) * e1[i+1];
      e2[i] = alpha * e1[i] + (1 - alpha) * e2[i+1];
      e3[i] = alpha * e2[i] + (1 - alpha) * e3[i+1];
      e4[i] = alpha * e3[i] + (1 - alpha) * e4[i+1];
      e5[i] = alpha * e4[i] + (1 - alpha) * e5[i+1];
      e6[i] = alpha * e5[i] + (1 - alpha) * e6[i+1];
      
      T3Buffer[i] = c1 * e6[i] + c2 * e5[i] + c3 * e4[i] + c4 * e3[i];
   }
   
   return(rates_total);
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Here is version for MT5 traders:

MT5 (MQL5) Version
MetaTrader 5 calculates arrays from left to right (oldest to newest data). Here is the MQL5 equivalent to handle that data structure.

Code: Select all

//+------------------------------------------------------------------+
//|                                                      TillsonT3.mq5|
//|                                          AI Generated T3 for MT5 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 7
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDeepSkyBlue
#property indicator_width1  2

input int Length = 14;           // T3 Period
input double VolumeFactor = 0.7; // Volume Factor (v)

double T3Buffer[];
double e1[], e2[], e3[], e4[], e5[], e6[];

double c1, c2, c3, c4;

int OnInit() {
   SetIndexBuffer(0, T3Buffer, INDICATOR_DATA);
   SetIndexBuffer(1, e1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(2, e2, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, e3, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, e4, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, e5, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, e6, INDICATOR_CALCULATIONS);
   
   double v = VolumeFactor;
   c1 = -(v * v * v);
   c2 = (3 * v * v) + (3 * v * v * v);
   c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
   c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);

   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
                const double &open[], const double &high[], const double &low[],
                const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) {
                
   if(rates_total < Length) return 0;
   
   int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
   double alpha = 2.0 / (Length + 1.0);
   
   for(int i = start; i < rates_total; i++) {
      double price = close[i];
      if(i == 0) {
         e1[i] = price; e2[i] = price; e3[i] = price;
         e4[i] = price; e5[i] = price; e6[i] = price;
         T3Buffer[i] = price;
         continue;
      }
      
      e1[i] = alpha * price + (1 - alpha) * e1[i-1];
      e2[i] = alpha * e1[i] + (1 - alpha) * e2[i-1];
      e3[i] = alpha * e2[i] + (1 - alpha) * e3[i-1];
      e4[i] = alpha * e3[i] + (1 - alpha) * e4[i-1];
      e5[i] = alpha * e4[i] + (1 - alpha) * e5[i-1];
      e6[i] = alpha * e5[i] + (1 - alpha) * e6[i-1];
      
      T3Buffer[i] = c1 * e6[i] + c2 * e5[i] + c3 * e4[i] + c4 * e3[i];
   }
   
   return(rates_total);
}
For those of you trading the 1H or 4H charts, what Volume Factor setting do you find strikes the best balance between smoothness and speed for your specific pairs?

Hope, that it will be helpfull for you.
Please let me know, if you like it or if you have any idea how to make it better etc.
Take a care.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

PTScalper wrote: Thu Aug 20, 2026 11:54 am Here is the code for MT4 traders:

MT4 (MQL4) Version
Save this in your MetaEditor as a Custom Indicator.

Code: Select all

//+------------------------------------------------------------------+
//|                                                      TillsonT3.mq4|
//|                                          AI Generated T3 for MT4 |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrDeepSkyBlue
#property indicator_width1 2
#property indicator_style1 STYLE_SOLID

input int Length = 14;         // T3 Period
input double VolumeFactor = 0.7; // Volume Factor (v)

double T3Buffer[];
double e1[], e2[], e3[], e4[], e5[], e6[];

double c1, c2, c3, c4;

int OnInit() {
   SetIndexBuffer(0, T3Buffer);
   SetIndexLabel(0, "T3(" + IntegerToString(Length) + ")");
   
   ArrayResize(e1, Bars); ArrayResize(e2, Bars);
   ArrayResize(e3, Bars); ArrayResize(e4, Bars);
   ArrayResize(e5, Bars); ArrayResize(e6, Bars);
   
   double v = VolumeFactor;
   c1 = -(v * v * v);
   c2 = (3 * v * v) + (3 * v * v * v);
   c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
   c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);

   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
                const double &open[], const double &high[], const double &low[],
                const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) {
                
   int limit = rates_total - prev_calculated;
   if(prev_calculated == 0) limit = rates_total - 1;
   
   double alpha = 2.0 / (Length + 1.0);
   
   for(int i = limit; i >= 0; i--) {
      double price = close[i];
      if(i == rates_total - 1) {
         e1[i] = price; e2[i] = price; e3[i] = price;
         e4[i] = price; e5[i] = price; e6[i] = price;
         T3Buffer[i] = price;
         continue;
      }
      
      e1[i] = alpha * price + (1 - alpha) * e1[i+1];
      e2[i] = alpha * e1[i] + (1 - alpha) * e2[i+1];
      e3[i] = alpha * e2[i] + (1 - alpha) * e3[i+1];
      e4[i] = alpha * e3[i] + (1 - alpha) * e4[i+1];
      e5[i] = alpha * e4[i] + (1 - alpha) * e5[i+1];
      e6[i] = alpha * e5[i] + (1 - alpha) * e6[i+1];
      
      T3Buffer[i] = c1 * e6[i] + c2 * e5[i] + c3 * e4[i] + c4 * e3[i];
   }
   
   return(rates_total);
}
I thought about it and found out the way how to make it even better.

The mathematical foundation of your T3 indicator is solid, but you are right to want it upgraded. The current code has a few structural flaws typical of older or AI-generated MQL4 scripts—specifically, using ArrayResize for moving average buffers. In MT4, when a new bar arrives, manually resized arrays won't automatically shift their data, which will quickly lead to corrupted calculations or "Array Out of Range" errors.

To make this professional-grade, we need to leverage MT4's native buffer handling, introduce #property strict, and enable dynamic price selection.

Here is the refactored, professional version of your code.

Professional Tillson T3 (MT4)

Code: Select all

//+------------------------------------------------------------------+
//|                                                ProTillsonT3.mq4  |
//|                                     Professional T3 for MT4      |
//+------------------------------------------------------------------+
#property copyright "Optimized Tillson T3"
#property link      ""
#property version   "1.10"
#property strict // Critical for modern MQL4 memory and error handling

#property indicator_chart_window
// Allocate 7 buffers: 1 for the visual line, 6 hidden for internal EMA calculations
#property indicator_buffers 7
#property indicator_color1  clrDeepSkyBlue
#property indicator_width1  2
#property indicator_style1  STYLE_SOLID

//--- Inputs
input int    InpLength       = 14;   // T3 Period
input double InpVolumeFactor = 0.7;  // Volume Factor (v)

// Note: Using the price[] OnCalculate function automatically gives
// the user an "Apply to:" dropdown (Close, Open, High, Typical, etc.)
// in the indicator settings without needing extra inputs!

//--- Indicator Buffers
double T3Buffer[];
double e1[], e2[], e3[], e4[], e5[], e6[];

//--- Global Variables for Optimization
double c1, c2, c3, c4;
double alpha;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   // 1. Validate inputs to prevent zero-divide errors
   if(InpLength < 1)
     {
      Print("Error: T3 Period must be 1 or greater.");
      return(INIT_PARAMETERS_INCORRECT);
     }

   // 2. Map and configure the main visible T3 buffer
   SetIndexBuffer(0, T3Buffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "T3 (" + IntegerToString(InpLength) + ", " + DoubleToString(InpVolumeFactor, 2) + ")");

   // 3. Map hidden buffers for the internal EMA calculations.
   // Mapping them to MT4 handles memory allocation and shifting 
   // automatically when new bars form (no ArrayResize bugs).
   SetIndexBuffer(1, e1); SetIndexStyle(1, DRAW_NONE);
   SetIndexBuffer(2, e2); SetIndexStyle(2, DRAW_NONE);
   SetIndexBuffer(3, e3); SetIndexStyle(3, DRAW_NONE);
   SetIndexBuffer(4, e4); SetIndexStyle(4, DRAW_NONE);
   SetIndexBuffer(5, e5); SetIndexStyle(5, DRAW_NONE);
   SetIndexBuffer(6, e6); SetIndexStyle(6, DRAW_NONE);

   // 4. Pre-calculate constants once (saves CPU resources)
   double v = InpVolumeFactor;
   c1 = -(v * v * v);
   c2 = (3 * v * v) + (3 * v * v * v);
   c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
   c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);
   
   alpha = 2.0 / (InpLength + 1.0);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   // Insufficient data check
   if(rates_total < InpLength) return(0);

   int limit = rates_total - prev_calculated;

   // Initialization on the very first run
   if(prev_calculated == 0)
     {
      limit = rates_total - 1;
      
      // Determine the first price safely based on series indexing
      int firstIdx = ArrayGetAsSeries(price) ? limit : 0;
      double firstPrice = price[firstIdx];
      
      // Prime the internal buffers to prevent lag/distortions at the start
      e1[limit] = firstPrice; e2[limit] = firstPrice; e3[limit] = firstPrice;
      e4[limit] = firstPrice; e5[limit] = firstPrice; e6[limit] = firstPrice;
      T3Buffer[limit] = firstPrice;
      
      limit--; // Move to the next bar
     }

   // MT4 indicator buffers are Series (0 is newest).
   // The price[] array, however, is often standard (0 is oldest).
   bool isSeries = ArrayGetAsSeries(price);

   // Main Calculation Loop
   for(int i = limit; i >= 0 && !IsStopped(); i--)
     {
      // Extract correct price index dynamically
      int pIdx = isSeries ? i : (rates_total - 1 - i);
      double p = price[pIdx];

      // Calculate 6 standard EMAs using the previous bar's data (i+1)
      e1[i] = alpha * p     + (1.0 - alpha) * e1[i+1];
      e2[i] = alpha * e1[i] + (1.0 - alpha) * e2[i+1];
      e3[i] = alpha * e2[i] + (1.0 - alpha) * e3[i+1];
      e4[i] = alpha * e3[i] + (1.0 - alpha) * e4[i+1];
      e5[i] = alpha * e4[i] + (1.0 - alpha) * e5[i+1];
      e6[i] = alpha * e5[i] + (1.0 - alpha) * e6[i+1];

      // Blend the EMAs to form the Tillson T3 line
      T3Buffer[i] = c1 * e6[i] + c2 * e5[i] + c3 * e4[i] + c4 * e3[i];
     }

   // Return processed bar count for the next tick
   return(rates_total);
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

What Makes This "Pro"?
No More ArrayResize memory leaks: The original script utilized native standard arrays and attempted to resize them to Bars. By mapping them out with #property indicator_buffers 7 and hiding them with DRAW_NONE, MQL4's terminal engine now inherently manages the arrays. When a new candlestick forms on the chart, MT4 will smoothly transition e1[0] to e1[1], avoiding the silent miscalculations your previous script would have suffered.

Built-in "Apply to" Dropdown: I switched the OnCalculate signature to the one that uses const double &price[] instead of explicit Open/High/Low/Close. As a result, when you drag this onto your chart, MT4 automatically generates an "Apply to:" dropdown in the indicator settings, letting you choose between Close, Median (HL/2), Typical (HLC/3), etc.

Series vs. Standard Indexing Protection: Native MT4 buffer arrays treat 0 as the newest bar (Series), but the price[] array defaults to 0 being the oldest. I added logic (ArrayGetAsSeries) to seamlessly reconcile this. The older script was prone to reading array bounds backward depending on MT4 build conditions.

Startup Priming (prev_calculated == 0): Moving averages demand initialization. If you don't "prime" e1 through e6 with the oldest known price, they begin at 0, pulling your entire line sharply downward for the first few dozen bars until the math stabilizes. The updated logic catches the first tick properly.

#property strict: Without this, MQL4 compilers forgive implicit conversions and out-of-bounds indexing. Including this forces the script to adhere to modern MQL+ C++ standards.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Transitioning from MT4 to MT5 requires a shift in how the engine handles data. While MQL4 defaults to reading data backward (newest bar to oldest), MQL5 is optimized to process arrays forward (oldest bar to newest).

Furthermore, MT5 strictly separates "Plots" (what you see) from "Buffers" (total memory arrays used), and requires you to define which buffers are for drawing and which are just for hidden math.

Here is the professional, fully optimized MT5 version of your Tillson T3 indicator.

Professional Tillson T3 (MT5)

Code: Select all

//+------------------------------------------------------------------+
//|                                                ProTillsonT3.mq5  |
//|                                     Professional T3 for MT5      |
//+------------------------------------------------------------------+
#property copyright "Optimized Tillson T3"
#property link      ""
#property version   "1.00"
#property indicator_chart_window

// MT5 Paradigm: 1 Plot (visible line), but 7 Buffers total (1 visible + 6 hidden)
#property indicator_plots   1
#property indicator_buffers 7

// Formatting the single visible plot
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDeepSkyBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- Inputs
input int    InpLength       = 14;   // T3 Period
input double InpVolumeFactor = 0.7;  // Volume Factor (v)

//--- Indicator Buffers
double T3Buffer[];
double e1[], e2[], e3[], e4[], e5[], e6[];

//--- Global Variables for Optimization
double c1, c2, c3, c4;
double alpha;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   // 1. Validate inputs
   if(InpLength < 1)
     {
      Print("Error: T3 Period must be 1 or greater.");
      return(INIT_PARAMETERS_INCORRECT);
     }

   // 2. Map the visible T3 buffer as INDICATOR_DATA
   SetIndexBuffer(0, T3Buffer, INDICATOR_DATA);
   PlotIndexSetString(0, PLOT_LABEL, "T3 (" + IntegerToString(InpLength) + ", " + DoubleToString(InpVolumeFactor, 2) + ")");

   // 3. Map the hidden EMA calculations as INDICATOR_CALCULATIONS
   // This saves rendering resources in MT5 since the terminal knows it doesn't need to draw them.
   SetIndexBuffer(1, e1, INDICATOR_CALCULATIONS);
   SetIndexBuffer(2, e2, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, e3, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, e4, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, e5, INDICATOR_CALCULATIONS);
   SetIndexBuffer(6, e6, INDICATOR_CALCULATIONS);

   // 4. Pre-calculate smoothing constants
   double v = InpVolumeFactor;
   c1 = -(v * v * v);
   c2 = (3 * v * v) + (3 * v * v * v);
   c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
   c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);
   
   alpha = 2.0 / (InpLength + 1.0);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   // Wait until we have enough bars on the chart
   if(rates_total < InpLength) return(0);

   int start;

   // 1. Initialization on the very first run
   if(prev_calculated == 0)
     {
      // In MT5 standard arrays, index 0 is the OLDEST bar.
      // We prime the internal buffers with the first known price.
      e1[0] = price[0]; e2[0] = price[0]; e3[0] = price[0];
      e4[0] = price[0]; e5[0] = price[0]; e6[0] = price[0];
      T3Buffer[0] = price[0];
      
      start = 1; // Start calculating from the second bar forward
     }
   else
     {
      // 2. Subsequent runs: recalculate the current forming bar
      start = prev_calculated - 1;
     }

   // 3. MT5 Forward Processing Loop (Oldest to Newest)
   for(int i = start; i < rates_total && !IsStopped(); i++)
     {
      // Calculate 6 EMAs using the PREVIOUS bar's data [i-1] 
      // (Because we are looping forward, not backward like MT4)
      e1[i] = alpha * price[i] + (1.0 - alpha) * e1[i-1];
      e2[i] = alpha * e1[i]    + (1.0 - alpha) * e2[i-1];
      e3[i] = alpha * e2[i]    + (1.0 - alpha) * e3[i-1];
      e4[i] = alpha * e3[i]    + (1.0 - alpha) * e4[i-1];
      e5[i] = alpha * e4[i]    + (1.0 - alpha) * e5[i-1];
      e6[i] = alpha * e5[i]    + (1.0 - alpha) * e6[i-1];

      // Blend the EMAs to form the Tillson T3 line
      T3Buffer[i] = c1 * e6[i] + c2 * e5[i] + c3 * e4[i] + c4 * e3[i];
     }

   // Return processed bar count to be used as prev_calculated on the next tick
   return(rates_total);
  }
//+------------------------------------------------------------------+
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Key Differences in the MT5 Engine Upgrade:

INDICATOR_CALCULATIONS vs INDICATOR_DATA: In MQL4, we used DRAW_NONE to hide the math lines. MT5 is much smarter—by defining buffers 1 through 6 as INDICATOR_CALCULATIONS, MT5 doesn't even try to pass them to the graphics renderer. This dramatically improves backtesting performance and reduces CPU load.

indicator_plots: Even though we have 7 arrays of data, MT5 requires us to declare #property indicator_plots 1. This tells the platform to only look for UI styling rules for one line.

Forward Looping (i++ instead of i--): MQL4 arrays are natively time-series (index 0 is the current flashing price). MQL5 standard arrays are normal (index 0 is the oldest bar in history). While you can force MQL5 to act backward, it is bad practice. I rebuilt the math loop to iterate forward (i = start; i < rates_total), calculating based on the previous bar [i-1]. This makes execution natively lightning-fast on MT5.

"Apply To" Dropdown Preserved: Because we kept the const double &price[] version of OnCalculate, this indicator still generates an "Apply to: Close / Typical / Weighted" dropdown menu out of the box.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Transitioning from MetaTrader to cTrader means moving from C++-style procedural loops (MQL) to modern C# object-oriented programming.

In cTrader, the engine handles the looping for you. Instead of writing a for loop that iterates through all the bars like in MQL4/5, cTrader calls the Calculate(int index) method exactly once for every bar. It inherently knows to process from oldest to newest.

Here is the professional, fully optimized cTrader (cAlgo) version of the Tillson T3.

Professional Tillson T3 (cTrader / C#)

Code: Select all

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProTillsonT3 : Indicator
    {
        // --- Inputs ---
        [Parameter("T3 Period", DefaultValue = 14, MinValue = 1)]
        public int Length { get; set; }

        [Parameter("Volume Factor (v)", DefaultValue = 0.7, MinValue = 0, MaxValue = 1)]
        public double VolumeFactor { get; set; }

        // Automatically creates an "Apply to: Close / High / Low / etc." dropdown
        [Parameter("Source")]
        public DataSeries Source { get; set; }

        // --- Outputs ---
        [Output("T3", LineColor = "DeepSkyBlue", Thickness = 2, PlotType = PlotType.Line)]
        public IndicatorDataSeries Result { get; set; }

        // --- Internal Data Series (Hidden Buffers) ---
        private IndicatorDataSeries _e1, _e2, _e3, _e4, _e5, _e6;

        // --- Global Variables for Math ---
        private double _c1, _c2, _c3, _c4;
        private double _alpha;

        protected override void Initialize()
        {
            // 1. Initialize hidden buffers
            // cTrader manages memory for IndicatorDataSeries automatically
            _e1 = CreateDataSeries();
            _e2 = CreateDataSeries();
            _e3 = CreateDataSeries();
            _e4 = CreateDataSeries();
            _e5 = CreateDataSeries();
            _e6 = CreateDataSeries();

            // 2. Pre-calculate smoothing constants (done once on startup to save CPU)
            double v = VolumeFactor;
            _c1 = -(v * v * v);
            _c2 = (3 * v * v) + (3 * v * v * v);
            _c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v);
            _c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v);
            
            _alpha = 2.0 / (Length + 1.0);
        }

        public override void Calculate(int index)
        {
            // 1. Prime the calculation on the very first bar in history
            if (index == 0)
            {
                _e1[index] = Source[index];
                _e2[index] = Source[index];
                _e3[index] = Source[index];
                _e4[index] = Source[index];
                _e5[index] = Source[index];
                _e6[index] = Source[index];
                
                Result[index] = Source[index];
                return;
            }

            // 2. Calculate the 6 chained EMAs using the previous bar's value [index - 1]
            _e1[index] = _alpha * Source[index] + (1.0 - _alpha) * _e1[index - 1];
            _e2[index] = _alpha * _e1[index]    + (1.0 - _alpha) * _e2[index - 1];
            _e3[index] = _alpha * _e2[index]    + (1.0 - _alpha) * _e3[index - 1];
            _e4[index] = _alpha * _e3[index]    + (1.0 - _alpha) * _e4[index - 1];
            _e5[index] = _alpha * _e4[index]    + (1.0 - _alpha) * _e5[index - 1];
            _e6[index] = _alpha * _e5[index]    + (1.0 - _alpha) * _e6[index - 1];

            // 3. Blend the EMAs to output the final Tillson T3 line
            Result[index] = _c1 * _e6[index] + _c2 * _e5[index] + _c3 * _e4[index] + _c4 * _e3[index];
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Key cTrader Paradigms Leveraged:

No Loops Required: You won't find a for loop in Calculate. cTrader automatically pushes the index integer forward for you bar by bar. When a new tick comes in, it just runs Calculate on the final index again. This makes the code exceptionally clean.

IndicatorDataSeries: Instead of using standard arrays (double[]), cTrader uses IndicatorDataSeries created via CreateDataSeries(). These automatically match the chart's size and handle memory safely, similar to MT5's INDICATOR_CALCULATIONS.

Built-in Data Source Dropdown: By defining public DataSeries Source { get; set; }, cTrader automatically populates an input in the indicator's UI that lets the user choose whether to apply the indicator to Close, Open, High, Low, Median, Typical, or Weighted prices.

Class Architecture: C# enforces defining your math constants (_c1, _alpha) as private fields in the class, initializing them in Initialize(), and utilizing them in Calculate().
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 1114
Joined: Mon Jul 20, 2026 1:28 pm

Re: Level Up Your Trend Trading: The T3 Moving Average (Tillson) + MT4/MT5 Code

Post by PTScalper »

Transitioning to TradingView’s Pine Script is a massive shift. Pine Script is a highly abstracted, declarative language. You do not need to manage memory, map buffers, or manually write moving average algorithms using previous bar indexing ([1]).

Because TradingView already has a highly optimized backend (written in C++) for calculating Exponential Moving Averages, we can leverage the built-in ta.ema() function. This makes the code exceptionally concise and blazingly fast.

Here is the professional, fully optimized Pine Script (v5) version of your Tillson T3.

Professional Tillson T3 (Pine Script v5)

Code: Select all

//@version=5
indicator("Professional Tillson T3", shorttitle="T3", overlay=true, timeframe="", timeframe_gaps=true)

// =========================================================================
// INPUTS
// =========================================================================
length = input.int(14, title="T3 Period", minval=1)
v      = input.float(0.7, title="Volume Factor (v)", minval=0.0, maxval=1.0, step=0.1)
src    = input.source(close, title="Source")

// =========================================================================
// CALCULATION CONSTANTS
// =========================================================================
// The math is evaluated on every bar, but isolating the constants keeps it clean
c1 = -(v * v * v)
c2 = (3 * v * v) + (3 * v * v * v)
c3 = -(6 * v * v) - (3 * v) - (3 * v * v * v)
c4 = 1 + (3 * v) + (v * v * v) + (3 * v * v)

// =========================================================================
// CORE LOGIC (Chained EMAs)
// =========================================================================
// Pine natively handles the alpha = 2/(Length+1) math inside ta.ema()
e1 = ta.ema(src, length)
e2 = ta.ema(e1,  length)
e3 = ta.ema(e2,  length)
e4 = ta.ema(e3,  length)
e5 = ta.ema(e4,  length)
e6 = ta.ema(e5,  length)

// Blend the EMAs to form the Tillson T3 line
t3 = (c1 * e6) + (c2 * e5) + (c3 * e4) + (c4 * e3)

// =========================================================================
// PLOTTING
// =========================================================================
plot(t3, title="Tillson T3", color=#00BFFF, linewidth=2, style=plot.style_line) // #00BFFF is DeepSkyBlue
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply