大家好!今天我要和大家分享一个非常实用的指标——理想的ZigZag。这是一个简单而快速的ZigZag指标,能有效帮助我们识别市场的高低点。
此指标没有悬挂或错误的峰值,峰值的获取过程经过了时间优化。

优点:
- 计算中最耗时的功能是iBarShift。它完全替代了获取峰值所需的所有循环,使用ArrayBSearch替代,因此这个指标的效率比MQL4版本更高。
- 每个K线所需的数据不仅可以实时访问,还可以供EA在历史数据中随时调用。
- 没有悬挂的峰值。
- 高效的方法找到峰值,无需搜索指标值。
- 运行非常快速。
- 在历史数据插入和切换时间框架时均能正确工作。
- 非常适合在EA中使用。
缺点:
- 内存需求较高。ZigZag需要两个缓冲区(一个不足以绘制正确的图形),而这里使用了五个缓冲区。在我看来,这个缺点完全被第六个优点所掩盖,因为没有快速ZigZag能够在两个缓冲区上正确处理历史插入。
- 额外的线条是可用的。这是为了使数据能够被EA可见,但这些线条不应该被显示。
原理:
ZigZag是通过通道原理绘制的,通道的宽度可以用点(IdealZZ)或百分比(IdealZZP)来定义。
峰值获取:
input int ChannelWidth=100; #property indicator_chart_window datetime LastTime; int ZZHandle; //+------------------------------------------------------------------+ //| 自定义指标初始化函数 | //+------------------------------------------------------------------+ void OnInit() { LastTime = 0; ZZHandle = iCustom(_Symbol, Period(), "IdealZZ", ChannelWidth); } //+------------------------------------------------------------------+ //| 获取值 | //+------------------------------------------------------------------+ bool GetValue(double dir,int bar,int prevBar,double &peak, int &peakBar,datetime &peakTime,const datetime &T[]) { if(dir<0) { double t[1]; if(0>=CopyBuffer(ZZHandle,2,bar,1,t)) return false; int i= ArrayBsearch(T, (datetime)t[0]); if(i==prevBar) { if(0>=CopyBuffer(ZZHandle,2,bar+1,1,t)) return false; i=ArrayBsearch(T,(datetime)t[0]); } double v[1]; if(0>=CopyBuffer(ZZHandle,1,i,1,v)) return false; if(v[0]==EMPTY_VALUE) { if(0>=CopyBuffer(ZZHandle,2,bar+1,1,t)) return false; i=ArrayBsearch(T,(datetime)t[0]); if(0>=CopyBuffer(ZZHandle,1,i,1,v)) return false; } peak=v[0]; peakBar=i; peakTime=(datetime)t[0]; } else if(dir>0) { double t[1]; if(0>=CopyBuffer(ZZHandle,3,bar,1,t)) return false; int i= ArrayBsearch(T, (datetime)t[0]); if(i==prevBar) { if(0>=CopyBuffer(ZZHandle,3,bar+1,1,t)) return false; i=ArrayBsearch(T,(datetime)t[0]); } double v[1]; if(0>=CopyBuffer(ZZHandle,0,i,1,v)) return false; if(v[0]==EMPTY_VALUE) { if(0>=CopyBuffer(ZZHandle,3,bar+1,1,t)) return false; i=ArrayBsearch(T,(datetime)t[0]); if(0>=CopyBuffer(ZZHandle,0,i,1,v)) return false; } peak=v[0]; peakBar=i; peakTime=(datetime)t[0]; } else { return(false); } return(true); } //+------------------------------------------------------------------+ //| 设置点 | //+------------------------------------------------------------------+ void SetPt(string name,double price,datetime time) { ObjectCreate(0,name,OBJ_ARROW,0,time,price); ObjectSetInteger(0,name,OBJPROP_ARROWCODE,108); ObjectSetDouble(0,name,OBJPROP_PRICE,price); ObjectSetInteger(0,name,OBJPROP_TIME,time); } //+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &T[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(LastTime==T[0]) return(rates_total); LastTime=T[0]; ArraySetAsSeries(T,true); double dir_[1]; if(0>=CopyBuffer(ZZHandle,4,1,1,dir_)) return rates_total; double dir=dir_[0]; double rdir=-dir; if(dir==EMPTY_VALUE) return(rates_total); double v1,v2,v3,v4,v5; int i1,i2,i3,i4,i5; datetime t1,t2,t3,t4,t5; if( GetValue(dir,1,0,v1,i1,t1,T) && GetValue(rdir,i1,0,v2,i2,t2,T) && GetValue(dir,i2,i1,v3,i3,t3,T) && GetValue(rdir,i3,i2,v4,i4,t4,T) && GetValue(dir,i4,i3,v5,i5,t5,T) ) { SetPt("1",v1,t1); SetPt("2",v2,t2); SetPt("3",v3,t3); SetPt("4",v4,t4); SetPt("5",v5,t5); Print(v1," ",v2," ",v3," ",v4," ",v5," ",i1," ",i2," ",i3," ",i4," ",i5); } else { Print("似乎出现了错误..."); } return(rates_total); } //+------------------------------------------------------------------+
这个例子是一个指标,每次K线形成时会标记前五个峰值(包括当前正在形成的峰值)。
注意!如果启用了零K线模式,代码可能会出现不正确的情况。
零K线模式:
该模式可以在DrawZeroBar变量代码中启用,默认情况下是禁用的。特别是在使用EA时,不建议启用此模式。
希望大家能愉快地使用这个指标!如果遇到任何问题,请告诉我!