This script is a sophisticated financial analysis tool designed to display the Stochastic Oscillator—a common momentum indicator—across different timeframes simultaneously. While standard indicators are usually locked to the timeframe you are currently viewing, this tool allows you to "peek" into higher or lower timeframes without switching your main chart view.
Core Functionality
The primary purpose of this script is Multi-Timeframe (MTF) Analysis. It enables a trader to see the market momentum of, for example, a Daily or 4-hour chart while they are actively trading on a much smaller timeframe, like a 15-minute chart. This helps in identifying the "big picture" trend while making precise entries on small price movements.
Key Technical Features
- Multi-Timeframe Synchronization: The tool calculates Stochastic values from various timeframes (ranging from 1 minute up to monthly charts). It automatically handles the heavy lifting of retrieving this data and aligning it with your current view.
- Linear Interpolation: When viewing a higher timeframe on a lower timeframe chart, the data points can sometimes look "choppy" or disconnected because one high-timeframe candle covers many small ones. To solve this, the script uses a mathematical technique called Linear Interpolation. This smoothly fills in the gaps between those large data points, creating a fluid, continuous line that is much easier for the human eye to read.
- Customizable Smoothing and Pricing: The logic allows you to choose different methods for calculating the averages (such as Simple Moving Average or Exponential Moving Average) and decide whether the indicator should be based on the "High/Low" prices or the "Close" prices of the candles.
- Real-Time Countdown Clock: The script includes a visual "Clock" feature. It calculates the remaining time until the next candle closes on the selected higher timeframe and displays this as a live countdown text on your chart. This is highly useful for traders who need to know exactly when a high-timeframe candle will flip or finish forming.
- Dynamic Visual Buffers: The tool manages four separate data "buffers." It distinguishes between historical (closed) candles and the current live candle, ensuring that the lines on your screen accurately represent both past history and real-time price action without overlapping or confusing the two.
Summary for the User
Essentially, this script acts as a multi-lens telescope. It takes the momentum data from any timeframe you choose, smoothes it out so it flows naturally across your current chart, and provides a live countdown timer so you can stay perfectly in sync with the broader market trends.
//+------------------------------------------------------------------+
//| MTF Stochastic.mq4 |
//| 2007, Christof Risch (iya) |
//| Stochastic indicator from any timeframe. |
//+------------------------------------------------------------------+
#property link "http://www.forexfactory.com/showthread.php?t=30109"
#property indicator_separate_window
//----
#property indicator_buffers 4
#property indicator_color1 Red // %K line
#property indicator_color2 CLR_NONE // %D line
#property indicator_color3 LightPink // %K line of the current candle
#property indicator_color4 CLR_NONE // %D line of the current candle
#property indicator_maximum 100
#property indicator_minimum 0
//---- input parameters
extern string note1="Chart Time Frame";
extern string note2="0=current time frame";
extern string note3="1=M1, 5=M5, 15=M15, 30=M30";
extern string note4="60=H1, 240=H4, 1440=D1";
extern string note5="10080=W1, 43200=MN1";
extern int TimeFrame =0; // {1=M1, 5=M5, 15=M15, ..., 1440=D1, 10080=W1, 43200=MN1}
extern string note6="Stochastic1";
extern int KPeriod =5,
DPeriod =3,
Slowing =3;
extern string note7="0=SMA, 1=EMA, 2=SMMA, 3=LWMA";
extern int MAMethod =0; // {0=SMA, 1=EMA, 2=SMMA, 3=LWMA}
extern string note8="0=Hi/Low, 1=Close/Close";
extern int PriceField =0; // {0=Hi/Low, 1=Close/Close}
extern bool ShowClock =true; // display time to candle close countdown
extern bool ShowStochInfo =true;
extern color ClockColor =Red;
extern string ClockFont ="Arial";
extern int ClockSize =8;
//---- indicator buffers
double BufferK[],
BufferD[],
BufferK_Curr[],
BufferD_Curr[];
//----
string IndicatorName="", IndicatorName2="", StochName="",
TimeLabelName="";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
{
//---- name for DataWindow and indicator subwindow label
switch(TimeFrame)
{
case 1: IndicatorName2="M1"; break;
case 5: IndicatorName2="M5"; break;
case 10: IndicatorName2="M10"; break;
case 15: IndicatorName2="M15"; break;
case 30: IndicatorName2="M30"; break;
case 60: IndicatorName2="H1"; break;
case 120: IndicatorName2="H2"; break;
case 240: IndicatorName2="H4"; break;
case 480: IndicatorName2="H8"; break;
case 1440: IndicatorName2="D1"; break;
case 10080: IndicatorName2="W1"; break;
case 43200: IndicatorName2="MN1"; break;
default: {TimeFrame=Period(); init(); return(0);}
}
StochName=" Stoch("+KPeriod+","+DPeriod+","+Slowing+")";
IndicatorName=IndicatorName2+StochName;
IndicatorShortName(IndicatorName);
IndicatorDigits(1);
//---- indicator lines
SetIndexBuffer(0,BufferK);
SetIndexBuffer(1,BufferD);
SetIndexBuffer(2,BufferK_Curr);
SetIndexBuffer(3,BufferD_Curr);
SetIndexStyle(0,DRAW_LINE);
SetIndexStyle(1,DRAW_LINE);
SetIndexStyle(2,DRAW_LINE);
SetIndexStyle(3,DRAW_LINE);
SetIndexLabel(0,IndicatorName+" %K line");
SetIndexLabel(1,IndicatorName+" %D Signal");
SetIndexLabel(2,IndicatorName+" %K current candle");
SetIndexLabel(3,IndicatorName+" %D current candle");
}
//+------------------------------------------------------------------+
int deinit()
{
if(TimeLabelName!="")
if(ObjectFind (TimeLabelName)!=-1)
ObjectDelete(TimeLabelName);
}
//+------------------------------------------------------------------+
//| MTF Stochastic |
//+------------------------------------------------------------------+
int start()
{
//----
// counted bars from indicator time frame
static int countedBars1=0;
//----
// counted bars from display time frame
if(Bars-1-IndicatorCounted() > 1 && countedBars1!=0)
countedBars1=0;
//----
int bars1=iBars(NULL,TimeFrame),
start1=bars1-1-countedBars1,
limit1=iBarShift(NULL,TimeFrame,Time[Bars-1]);
//----
if(countedBars1!=bars1-1)
{
countedBars1 =bars1-1;
ArrayInitialize(BufferK_Curr,EMPTY_VALUE);
ArrayInitialize(BufferD_Curr,EMPTY_VALUE);
if(TimeLabelName!="")
if(ObjectFind (TimeLabelName)!=-1)
ObjectDelete(TimeLabelName);
}
if(start1 > limit1 && limit1!=-1)
start1=limit1;
//----
// 3... 2... 1... GO!
for(int i=start1; i>=0; i--)
{
int shift1=i;
//----
if(TimeFrame < Period())
shift1=iBarShift(NULL,TimeFrame,Time[i]);
//----
int time1 =iTime (NULL,TimeFrame,shift1),
shift2=iBarShift(NULL,0,time1);
double stochK, stochD;
if ((TimeFrame!=10) && (TimeFrame!=120) && (TimeFrame!=480))
{
stochK=iStochastic(NULL,TimeFrame,KPeriod,DPeriod,Slowing,MAMethod,PriceField,0,shift1);
stochD=iStochastic(NULL,TimeFrame,KPeriod,DPeriod,Slowing,MAMethod,PriceField,1,shift1);
}
else
{
switch(TimeFrame)
{
case 10:
{
stochK=2*iStochastic(NULL,5,KPeriod,DPeriod,Slowing,MAMethod,PriceField,0,shift1);
stochD=2*iStochastic(NULL,5,KPeriod,DPeriod,Slowing,MAMethod,PriceField,1,shift1);
break;
}
case 120:
{
stochK=2*iStochastic(NULL,60,KPeriod,DPeriod,Slowing,MAMethod,PriceField,0,shift1);
stochD=2*iStochastic(NULL,60,KPeriod,DPeriod,Slowing,MAMethod,PriceField,1,shift1);
break;
}
case 480:
{
stochK=2*iStochastic(NULL,240,KPeriod,DPeriod,Slowing,MAMethod,PriceField,0,shift1);
stochD=2*iStochastic(NULL,240,KPeriod,DPeriod,Slowing,MAMethod,PriceField,1,shift1);
break;
}
}
}
// old (closed) candles
if(shift1>=1)
{
BufferK[shift2]=stochK;
BufferD[shift2]=stochD;
}
//----
// current candle
if((TimeFrame >=Period() && shift1<=1)
|| (TimeFrame < Period() &&(shift1==0||shift2==1)))
{
BufferK_Curr[shift2]=stochK;
BufferD_Curr[shift2]=stochD;
}
//----
// linear interpolatior for the number of intermediate bars, between two higher timeframe candles.
int n=1;
if(TimeFrame > Period())
{
if ((TimeFrame!=10) && (TimeFrame!=120) && (TimeFrame!=480))
{
int shift2prev=iBarShift(NULL,0,iTime(NULL,TimeFrame,shift1+1));
}
else
{
switch(TimeFrame)
{
case 10:
{
shift2prev=2*iBarShift(NULL,0,iTime(NULL,5,shift1+1));
break;
}
case 120:
{
shift2prev=2*iBarShift(NULL,0,iTime(NULL,60,shift1+1));
break;
}
case 480:
{
shift2prev=2*iBarShift(NULL,0,iTime(NULL,240,shift1+1));
break;
}
}
}
if(shift2prev!=-1 && shift2prev!=shift2)
n=shift2prev - shift2;
}
// apply interpolation
double factor=1.0/n;
if(shift1>=1)
if(BufferK[shift2+n]!=EMPTY_VALUE && BufferK[shift2]!=EMPTY_VALUE)
{
for(int k=1; k < n; k++)
{
BufferK[shift2+k]=k*factor*BufferK[shift2+n] + (1.0-k*factor)*BufferK[shift2];
BufferD[shift2+k]=k*factor*BufferD[shift2+n] + (1.0-k*factor)*BufferD[shift2];
}
}
// current candle
if(shift1==0)
if(BufferK_Curr[shift2+n]!=EMPTY_VALUE && BufferK_Curr[shift2]!=EMPTY_VALUE)
{
for(k=1; k < n; k++)
{
BufferK_Curr[shift2+k]=k*factor*BufferK_Curr[shift2+n] + (1.0-k*factor)*BufferK_Curr[shift2];
BufferD_Curr[shift2+k]=k*factor*BufferD_Curr[shift2+n] + (1.0-k*factor)*BufferD_Curr[shift2];
}
// candle time countdown
if(ShowClock)
{
int m,s;
//----
s=iTime(NULL,TimeFrame,0)+TimeFrame*60 - TimeCurrent();
m=(s-s%60)/60;
s=s%60;
//----
string text;
if(s<10) text="0"+s;
else text=""+s;
if (ShowStochInfo)
text=" "+IndicatorName2+StochName+" - "+m+":"+text;
else
text=" "+IndicatorName2+" - "+m+":"+text;
int window=WindowFind(IndicatorName);
if(window==-1)
window=WindowOnDropped() ;
TimeLabelName=IndicatorName+" Time Counter "+window;
if(ObjectFind (TimeLabelName)==-1)
ObjectCreate(TimeLabelName, OBJ_TEXT, window, Time[shift2], BufferK_Curr[shift2]+3);
else
ObjectMove (TimeLabelName, 0, Time[shift2], BufferK_Curr[shift2]+3);
ObjectSetText (TimeLabelName, text, ClockSize, ClockFont, ClockColor);
}
else if(ShowStochInfo)
{
text=" "+IndicatorName2+StochName+text;
window=WindowFind(IndicatorName);
if(window==-1)
window=WindowOnDropped() ;
//----
TimeLabelName=IndicatorName+" Time Counter "+window;
if(ObjectFind (TimeLabelName)==-1)
ObjectCreate(TimeLabelName, OBJ_TEXT, window, Time[shift2], BufferK_Curr[shift2]+3);
else
ObjectMove (TimeLabelName, 0, Time[shift2], BufferK_Curr[shift2]+3);
ObjectSetText (TimeLabelName, text, ClockSize, ClockFont, ClockColor);
}
}
}
//----
return(0);
}
//+------------------------------------------------------------------+
Comments