Channel ZZ_v2_en
Download scriptThe Channel ZZ indicator is designed to visually map out and analyze market trends by identifying significant price movements and establishing "channels" based on specific price extremes. Instead of just showing raw price action, it focuses on the relationship between peaks and troughs to determine the prevailing market direction.
Here is a breakdown of how the logic works:
Trend Identification & Extremums
The script constantly monitors the high and low prices of the market to find "extremums" (the highest points in an upward move or the lowest points in a downward move).
- Upward Movement: When the price moves higher, the script identifies the new peak.
- Downward Movement: When the price moves lower, it identifies the new trough.
By tracking these specific points, the script can distinguish between a minor price fluctuation and a significant trend change.
Channel Construction
Once an extremum is identified, the tool calculates a dynamic "channel." This channel represents a corridor of price action:
- Width Options: You can define the width of this corridor in two ways: either by a fixed number of points (a specific distance) or as a percentage relative to the current price.
- Visual Projection: The script draws a boundary line that follows the slope between the start and end of a trend. It then projects a second parallel line a set distance away, creating a "tunnel" or channel. This helps traders see the "fair value" zone of the current move.
Breakout Detection & Alerts
One of the core functions of this logic is identifying when the price breaks out of its established territory.
- Directional Shifts: If the price moves far enough outside of the current channel, the script recognizes this as a trend reversal (e.g., from UP to DOWN).
- Notifications: When such a shift occurs, the system can trigger an automated alert to notify you that the market direction has changed.
Statistical Tracking
For traders who prefer data over just visuals, the script includes a statistics feature:
- Movement Measurement: It calculates the distance of each new extremum relative to the previous one.
- On-Chart Labels: It can automatically place text on your chart showing the size of these movements (in points) and a percentage comparison to the previous move. This allows you to see at a glance if the market is accelerating or losing momentum over time.
Summary for the User
In simple terms, this tool acts as a "trend tracker." It draws corridors around price movements to show you how much room the market has to breathe. If the price stays within the corridor, the trend is considered intact; if it breaks out significantly, the script identifies it as a new direction and alerts you accordingly.
//+------------------------------------------------------------------+
//| Channel ZZ.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, TheXpert"
#property link "theforexpert@gmail.com"
extern string _1 = "Channel width in points";
extern int ChannelPoints = 150;
extern string _2 = "Channel width in percent";
extern double ChannelPercent = 1.5;
extern string _3 = "If 0, channel is counted using ChannelPoints , if 1 -- using ChannelPercent";
extern int ChannelType = 1;
extern string _4 = "Font size";
extern int FontSize = 10;
extern string _5 = "Font name";
extern string FontName = "Arial Black";
extern string _6 = "Font color";
extern color FontColor = Yellow;
extern string _7 = "Draw or not objects containing ZZ statistics";
extern bool DrawStatistics = true;
extern string _8 = "Offset for stats objects in points";
extern int StatisticsOffsetPoints = 20;
extern string _9 = "Alert or not when the direction has been changed";
extern bool AlertBreakout = true;
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 LightGray
#property indicator_width1 3
#property indicator_color2 LightGreen
#property indicator_color3 Orange
#property indicator_style2 STYLE_DASH
#property indicator_style3 STYLE_DASH
#define ID "Channel ZZ Stats "
//---- buffers
double ZZ[];
double UpChannel[];
double DnChannel[];
string symbol;
#define UP 1
#define DN -1
#define NONE 0
int Direction;
int LastDirection;
datetime StartMax;
datetime EndMax;
datetime StartMin;
datetime EndMin;
// Channel Variables
datetime StartChannel;
datetime EndChannel;
// Object Variables
int Counter;
int Length;
int LastLength;
double LastExtrValue;
double ExtrValue;
int ExtrDirection;
datetime ExtrTime;
int ChannelWidth;
void DrawInfo(datetime endDraw)
{
if (!DrawStatistics) return;
Length = MathAbs(MathRound((ExtrValue - LastExtrValue)/Point));
double pos = ExtrValue;
if (Direction == UP) pos += StatisticsOffsetPoints*Point;
string id = ID + Counter;
string text;
if (LastExtrValue != 0)
{
text = "(" + Length + ")";
if (LastLength != 0)
{
text = DoubleToStr(Length/(1.0*LastLength), 2) + text;
}
}
if (ObjectFind(id) == -1)
{
ObjectCreate(id, OBJ_TEXT, 0, endDraw, pos);
ObjectSet(id, OBJPROP_COLOR, FontColor);
}
ObjectMove(id, 0, endDraw, pos);
ObjectSetText(id, text, FontSize, FontName);
}
void NotifyDirectionChange()
{
if (!AlertBreakout) return;
if (ExtrTime < Time[1]) return;
string direction = "UP";
if (Direction != UP) direction = "DOWN";
Alert("Channel ZZ ", Symbol(), " changed direction to ", direction);
}
void SetExtremum(double value, int direction, datetime time)
{
if (direction == ExtrDirection && ExtrTime != 0)
{
if (time == ExtrTime)
{
ExtrValue = value;
DrawInfo(time);
}
else
{
ZZ[iBarShift(NULL, 0, ExtrTime)] = EMPTY_VALUE;
ExtrValue = value;
ExtrTime = time;
DrawInfo(time);
}
}
else
{
LastExtrValue = ExtrValue;
ExtrValue = value;
ExtrDirection = direction;
ExtrTime = time;
if (ChannelType == 1)
{
ChannelWidth = ChannelPercent*value/Point/100;
}
LastLength = Length;
Counter++;
DrawInfo(time);
NotifyDirectionChange();
}
ZZ[iBarShift(NULL, 0, ExtrTime)] = ExtrValue;
}
int init()
{
IndicatorShortName("-=<Channel ZZ>=-");
IndicatorBuffers(3);
SetIndexBuffer(0, ZZ);
SetIndexBuffer(1, UpChannel);
SetIndexBuffer(2, DnChannel);
SetIndexStyle(0, DRAW_SECTION);
SetIndexStyle(1, DRAW_SECTION, STYLE_DASH);
SetIndexStyle(2, DRAW_SECTION, STYLE_DASH);
symbol = Symbol();
Direction = NONE;
Counter = 0;
StartMax = 0;
EndMax = 0;
StartMin = 0;
EndMin = 0;
Length = 0;
LastLength = EMPTY_VALUE;
LastExtrValue = 0;
if (ChannelType == 0)
{
ChannelWidth = ChannelPoints;
}
return(0);
}
int deinit()
{
for (int i = 0; i <= Counter; i++)
{
ObjectDelete(ID + i);
}
}
int start()
{
int ToCount = Bars - IndicatorCounted();
if (ToCount > 2)
{
ToCount = Bars;
ArrayInitialize(ZZ, EMPTY_VALUE);
ArrayInitialize(UpChannel, EMPTY_VALUE);
ArrayInitialize(DnChannel, EMPTY_VALUE);
}
for (int i = ToCount - 1; i >= 1; i--)
{
if (Direction == NONE)
{
CheckInit(i);
continue;
}
if (Direction == UP)
{
CheckUp(i);
}
else
{
CheckDown(i);
}
DrawChannel(i);
}
return(0);
}
void CheckInit(int i)
{
if (StartMax == 0 || StartMin == 0)
{
if (StartMax == 0) StartMax = Time[i];
if (StartMin == 0) StartMin = Time[i];
ExtrTime = 0;
return;
}
if (Direction == NONE)
{
double maxValue = High[iBarShift(symbol, 0, StartMax)];
double minValue = Low[iBarShift(symbol, 0, StartMin)];
double nowMax = High[i];
double nowMin = Low[i];
if (nowMax > maxValue && Time[i] > StartMax)
{
EndMax = Time[i];
StartMin = Time[i];
Direction = UP;
SetExtremum(nowMax, UP, EndMax);
}
else if (nowMin > minValue && Time[i] > StartMin)
{
EndMin = Time[i];
StartMax = Time[i];
Direction = DN;
SetExtremum(nowMin, DN, EndMin);
}
}
}
void CheckUp(int i)
{
int startIndex = iBarShift(symbol, 0, StartMax);
int endIndex = iBarShift(symbol, 0, EndMax);
double endMaxValue = High[endIndex];
if (endMaxValue < High[i])
{
endMaxValue = High[i];
EndMax = Time[i];
SetExtremum(High[i], UP, EndMax);
}
else
{
double startMaxValue = High[startIndex];
double startMinValue = Low[iBarShift(symbol, 0, StartMin)];
double nowMaxValue = endMaxValue;
if (startIndex - endIndex != 0)
{
nowMaxValue += (endMaxValue - startMaxValue)/(startIndex - endIndex)*(endIndex - i);
}
double nowMinValue = Low[i];
if (nowMaxValue - nowMinValue > ChannelWidth*Point)
{
if (EndMax != i)
{
StartMin = Time[i];
EndMin = Time[i];
Direction = DN;
SetExtremum(Low[i], DN, EndMin);
}
}
}
}
void CheckDown(int i)
{
int startIndex = iBarShift(symbol, 0, StartMin);
int endIndex = iBarShift(symbol, 0, EndMin);
double endMinValue = Low[endIndex];
if (endMinValue > Low[i])
{
endMinValue = Low[i];
EndMin = Time[i];
SetExtremum(Low[i], DN, EndMin);
}
else
{
double startMinValue = Low[startIndex];
double startMaxValue = High[iBarShift(symbol, 0, StartMax)];
double nowMinValue = endMinValue;
if (startIndex - endIndex != 0)
{
nowMinValue += (endMinValue - startMinValue)/(startIndex - endIndex)*(endIndex - i);
}
double nowMaxValue = High[i];
if (nowMaxValue - nowMinValue > ChannelWidth*Point)
{
if (EndMin != i)
{
EndMax = Time[i];
StartMax = Time[i];
Direction = UP;
SetExtremum(High[i], UP, EndMax);
}
}
}
}
void DrawChannel(int i)
{
switch (Direction)
{
case UP:
DrawUp(i);
break;
case DN:
DrawDn(i);
break;
}
}
void DrawUp(int i)
{
int startIdx = iBarShift(symbol, 0, StartMax);
int endIdx = iBarShift(symbol, 0, EndMax);
if (startIdx == i)
{
UpChannel[i] = High[i];
DnChannel[i] = UpChannel[i] - ChannelWidth*Point;
return;
}
if (startIdx - i > 1)
{
UpChannel[i + 1] = EMPTY_VALUE;
DnChannel[i + 1] = EMPTY_VALUE;
}
if (startIdx == endIdx)
{
UpChannel[i] = UpChannel[startIdx];
DnChannel[i] = DnChannel[startIdx];
return;
}
double upValue =
High[startIdx] + (High[endIdx] - High[startIdx])/(endIdx - startIdx)*(i - startIdx);
UpChannel[i] = upValue;
DnChannel[i] = UpChannel[i] - ChannelWidth*Point;
}
void DrawDn(int i)
{
int startIdx = iBarShift(symbol, 0, StartMin);
int endIdx = iBarShift(symbol, 0, EndMin);
if (startIdx == i)
{
UpChannel[i] = Low[i];
DnChannel[i] = UpChannel[i] + ChannelWidth*Point;
return;
}
if (startIdx - i > 1)
{
UpChannel[i + 1] = EMPTY_VALUE;
DnChannel[i + 1] = EMPTY_VALUE;
}
if (startIdx == endIdx)
{
UpChannel[i] = UpChannel[startIdx];
DnChannel[i] = DnChannel[startIdx];
return;
}
double dnValue =
Low[startIdx] + (Low[endIdx] - Low[startIdx])/(endIdx - startIdx)*(i - startIdx);
UpChannel[i] = dnValue;
DnChannel[i] = UpChannel[i] + ChannelWidth*Point;
}
Comments