The Schaff Trend Cycle (STC) is a sophisticated technical analysis tool designed to identify and visualize market trends by filtering out "noise" from price movements. Instead of simply looking at raw price changes, this script processes data through multiple layers of calculation to determine the underlying momentum and direction of the market.
Here is a breakdown of the logic used in this script:
1. Momentum Foundation (MACD)
The process begins by calculating the difference between two moving averages—a fast one and a slow one. This identifies the immediate strength of the price movement. By comparing these two averages, the script determines whether the market is accelerating upwards or downwards.
2. Double Smoothing and Percentages
Raw momentum data can be erratic, so the script applies a "Double Stochastic" logic to it. It looks at the high and low values of the momentum over a specific period (the Schaff Period) and converts them into a percentage scale. This is then smoothed out again using a specific mathematical formula to ensure that the resulting line moves fluidly rather than jumping erratically from bar to bar.
3. Trend Identification
The core logic revolves around detecting the "Cycle." The script monitors the slope of the processed data:
- Upward Trend: When the value increases compared to the previous point, it identifies a bullish trend.
- Downward Trend: When the value decreases, it identifies a bearish trend.
4. Visual Refinement (Plotting Logic)
A unique feature of this script is how it handles visual transitions. Rather than just showing a continuous line that might look jagged or confusing at turning points, the logic includes specific "Clean" and "Plot" functions. These ensure that the lines representing upward and downward movements are distinct and clearly defined, helping traders see exactly where a trend shift occurs without unnecessary visual clutter.
5. Multi-Timeframe Compatibility
The script is built to be versatile. It includes logic that allows it to pull data from different timeframes (for example, viewing a higher timeframe trend while looking at a lower timeframe chart). If you choose to use a higher timeframe, the script features an "Interpolation" logic—this automatically fills in the gaps between larger data points to create a smooth, continuous line on your current chart.
Summary for the User
In plain English: This tool takes complex price movements, strips away the minor fluctuations, calculates the underlying momentum percentage, and smooths it out twice. It then draws a clear visual representation of the cycle, making it easier for a trader to see the primary direction of the market and identify when the trend is potentially reversing.
//+------------------------------------------------------------------+
//| Schaff Trend Cycle.mq4 |
//| mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link "mladenfx@gmail.com"
#property link "www.forex-station.com"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1 clrDodgerBlue
#property indicator_color2 clrSandyBrown
#property indicator_color3 clrSandyBrown
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 2
#property strict
//
//
//
//
//
extern ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT; // Time frame
extern int STCPeriod = 10; // Schaff period
extern int FastMAPeriod = 23; // Fast macd period
extern int SlowMAPeriod = 50; // Slow macd period
extern double SmoothPeriod = 3; // Smoothing period
extern bool Interpolate = true; // Interpolate in multi time frame mode?
double stcBuffer[],stcBufferUA[],stcBufferUB[],macdBuffer[],fastKBuffer[],fastDBuffer[],fastKKBuffer[],slope[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_y) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,STCPeriod,FastMAPeriod,SlowMAPeriod,SmoothPeriod,_buff,_y)
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
int init()
{
IndicatorBuffers(9);
SetIndexBuffer(0,stcBuffer);
SetIndexBuffer(1,stcBufferUA);
SetIndexBuffer(2,stcBufferUB);
SetIndexBuffer(3,macdBuffer);
SetIndexBuffer(4,fastKBuffer);
SetIndexBuffer(5,fastDBuffer);
SetIndexBuffer(6,fastKKBuffer);
SetIndexBuffer(7,slope);
SetIndexBuffer(8,count);
indicatorFileName = WindowExpertName();
TimeFrame = MathMax(TimeFrame,_Period);
IndicatorShortName(timeFrameToString(TimeFrame)+" Schaff Trend Cycle ("+(string)STCPeriod+","+(string)FastMAPeriod+","+(string)SlowMAPeriod+","+(string)SmoothPeriod+")");
return(0);
}
int deinit()
{
return(0);
}
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
int start()
{
int counted_bars=IndicatorCounted();
if(counted_bars < 0) return(-1);
if(counted_bars>0) counted_bars--;
int limit = MathMin(Bars-counted_bars,Bars-1); count[0] = limit;
if (TimeFrame!=_Period)
{
limit = (int)MathMax(limit,MathMin(Bars-1,_mtfCall(8,0)*TimeFrame/Period()));
if (slope[limit] == 1) CleanPoint(limit,stcBufferUA,stcBufferUB);
for(int i=limit; i>=0; i--)
{
int y = iBarShift(NULL,TimeFrame,Time[i]);
slope[i] = _mtfCall(7,y);
stcBuffer[i] = _mtfCall(0,y);
stcBufferUA[i] = EMPTY_VALUE;
stcBufferUB[i] = EMPTY_VALUE;
//
//
//
//
//
if (!Interpolate || (i>0 && y==iBarShift(NULL,TimeFrame,Time[i-1]))) continue;
#define _interpolate(buff,i,k,n) buff[i+k] = buff[i]+(buff[i+n]-buff[i])*k/n
int n,k; datetime time = iTime(NULL,TimeFrame,y);
for(n = 1; (i+n)<Bars && Time[i+n] >= time; n++) continue;
for(k = 1; k<n && (i+n)<Bars && (i+k)<Bars; k++) _interpolate(stcBuffer,i,k,n);
}
for(int i=limit; i>=0; i--) if (slope[i]==-1) PlotPoint(i,stcBufferUA,stcBufferUB,stcBuffer);
return(0);
}
//
//
//
//
//
double alpha = 2.0/(1.0+SmoothPeriod);
if (slope[limit] == 1) CleanPoint(limit,stcBufferUA,stcBufferUB);
for(int i = limit; i >= 0 && !_StopFlag; i--)
{
macdBuffer[i] = iMA(NULL,0,FastMAPeriod,0,MODE_EMA,PRICE_CLOSE,i)-iMA(NULL,0,SlowMAPeriod,0,MODE_EMA,PRICE_CLOSE,i);
//
//
//
//
//
double lowMacd = macdBuffer[ArrayMinimum(macdBuffer,STCPeriod,i)];
double highMacd = macdBuffer[ArrayMaximum(macdBuffer,STCPeriod,i)]-lowMacd;
fastKBuffer[i] = (highMacd > 0) ? 100*((macdBuffer[i]-lowMacd)/highMacd) : (i<Bars-1) ? fastKBuffer[i+1] : 0;
fastDBuffer[i] = (i<Bars-1) ? fastDBuffer[i+1]+alpha*(fastKBuffer[i]-fastDBuffer[i+1]) : fastKBuffer[i];
double lowStoch = fastDBuffer[ArrayMinimum(fastDBuffer,STCPeriod,i)];
double highStoch = fastDBuffer[ArrayMaximum(fastDBuffer,STCPeriod,i)]-lowStoch;
fastKKBuffer[i] = (highStoch > 0) ? 100*((fastDBuffer[i]-lowStoch)/highStoch) : (i<Bars-1) ? fastKKBuffer[i+1] : 0;
stcBuffer[i] = (i<Bars-1) ? stcBuffer[i+1]+alpha*(fastKKBuffer[i]-stcBuffer[i+1]) : fastKKBuffer[i];
stcBufferUA[i] = EMPTY_VALUE;
stcBufferUB[i] = EMPTY_VALUE;
slope[i] = (i<Bars-1) ? (stcBuffer[i] > stcBuffer[i+1]) ? 1 : (stcBuffer[i] < stcBuffer[i+1]) ? -1 : slope[i+1] : 0;
if (slope[i]==-1) PlotPoint(i,stcBufferUA,stcBufferUB,stcBuffer);
}
return(0);
}
//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//
void CleanPoint(int i,double& first[],double& second[])
{
if (i>=Bars-3) return;
if ((second[i] != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
second[i+1] = EMPTY_VALUE;
else
if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
first[i+1] = EMPTY_VALUE;
}
void PlotPoint(int i,double& first[],double& second[],double& from[])
{
if (i>=Bars-2) return;
if (first[i+1] == EMPTY_VALUE)
if (first[i+2] == EMPTY_VALUE)
{ first[i] = from[i]; first[i+1] = from[i+1]; second[i] = EMPTY_VALUE; }
else { second[i] = from[i]; second[i+1] = from[i+1]; first[i] = EMPTY_VALUE; }
else { first[i] = from[i]; second[i] = EMPTY_VALUE; }
}
//
//
//
//
//
string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};
string timeFrameToString(int tf)
{
for (int i=ArraySize(iTfTable)-1; i>=0; i--)
if (tf==iTfTable[i]) return(sTfTable[i]);
return("");
}
Comments