你好,MQL4程序员的同事们!
在今天的教程中,我们将了解如何使用MQL4实现最强大和最可靠的模式之一-发散。 为此,我们将编写一个脚本,相对强度指数(RSI)将从技术指标中帮助我们。 我们还将研究分析酒吧的工作,并编写脚本来标记历史上的分形。
传统上,交易平台上的价格流在某些时间段分组。 在这段时间内收到的所有价格形成一个酒吧,这段时间被称为时间范围。 有几个预定义的 时间范围 从一分钟到一个月。
从广义上讲,酒吧不仅是一个价格周期的图形图像,而且是一个分析元素。 所以, 指标 按比例计算。 有相当流行的交易方法,仅基于分析酒吧:这和 烛光的 分析,和 Price Action ,其中重复的图形和条形组合作为独立的交易信号。 蜡烛组也用于商业建筑 水平 和 技术分析图 .
在 算法交易 使用条形图很容易正式化任务 技术分析 例如,建造 趋势线 或确定 发散 价格和 指示器 .
最常见的图形表现形式是所谓的“日本蜡烛”。 这是我们用来说明的,我们称之为酒吧。
因此,每家酒吧都有一个开盘价——一个时期开始时的价格; 收盘价是该期间收到的最后一个价格; 期间的最高和最低价格。
酒吧阵列 图表 在历史数据的深度称为时间序列。 此数组中的编号从右向左,即数组中的索引0(0)表示当前条的数据,该条对应于给定时间段的未完成时间段。 时间范围 可以通过调用ibars函数或使用预定义的bars变量来获取数组时间序列的大小。
通过以下功能访问特定酒吧的数据:
IOPEN–酒吧开张价格;
ICLOSE–酒吧关闭价格;
Ihigh–酒吧的最高价格;
ILOW:酒吧最低价格;
ITIME–酒吧开放时间;
柚木 体积 或者在酒吧形成期间,价格变化了多少次。
使用计时器的例子
首先,让我们看看一个简单的时间序列示例。 在所有可用的历史中找到一支蜡烛 体积 同时计算蜡烛体的平均大小:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ShowBarsParameters() { Comment(""); long VolumeMax=0; // 最大容积 int VolumeMaxIndex=0;// 最大容量酒吧指数 double BodySum=0; // 烛光体的总和 for(int i=Bars-1; i>=0; i--) { if(VolumeMax<iVolume(NULL,0,i)) { VolumeMax=iVolume(NULL,0,i); VolumeMaxIndex=i; } BodySum+=MathAbs(iOpen(NULL,0,i)-iClose(NULL,0,i)); } Comment(“图表上的所有:”,bars,“bar,” ", “最大体积值”,VolumeMax,“收到” ITIME(Null,0,VolumeMaxindex),“在酒吧”,VolumeMaxindex, ", “蜡烛体的平均值,以点为单位:”,int(bodysum/_point)/bars; }
修改代码,以便可以访问任意字符的时间序列。 为此,我们创建一个函数,将所需的字符和时间范围传递给该函数:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ShowBarsParametersExt(string symbol, int timeframe) { Comment(""); long VolumeMax=0; // 最大容积 int VolumeMaxIndex=0;// 最大容量酒吧指数 double BodySum=0; // 烛光体的总和 for(int i=iBars(symbol,timeframe)-1; i>=0; i--) { if(VolumeMax<iVolume(symbol,timeframe,i)) { VolumeMax=iVolume(symbol,timeframe,i); VolumeMaxIndex=i; } BodySum+=MathAbs(iOpen(symbol,timeframe,i)-iClose(symbol,timeframe,i)); } comment(“在图表上”,symbol,“(“,timeframe,“)总计:”,ibars(symbol,timeframe),“bar,” ", “最大体积值”,VolumeMax,“收到” ITIME(symbol,timeframe,volumemaxindex),“在酒吧”,volumemaxindex, ", “蜡烛体的平均值,以点为单位:”,int(bodysum/_point)/bars; }
例如,获取字符的数据 EURJPY 从一小时的时间范围开始,函数调用如下:
ShowBarsParameters("EURJPY",PERIOD_H1);
比尔·威廉姆斯分形标记
让我们学习如何识别最简单的蜡烛组。 因为我们要寻找 发散 RSI振荡器的价格,我们需要解决在图表上定义局部极值的问题。 这些基准点可以 趋势线 它将显示价格走势的方向。 确定这些点的最简单方法是标记分形 比尔·威廉姆斯 分形是由 5蜡烛 如果高中心蜡烛位于左侧两个蜡烛和右侧两个蜡烛的最高价格之上,则会出现“向上分形”:
如果中央酒吧的低水平低于左侧两个酒吧和右侧两个酒吧的最低水平,则前面是“分形向下”:
终端有一个内置的指示器。 分形 :
我们会写 脚本 ,它将在历史数据上标记分形,就像内置的分形指示器一样。
使用MetaEditor主面板中的按钮创建 脚本 “分形.mq4”:
脚本更易于演示,因为它最初由一个主函数onstart()组成,一旦脚本被添加到图形中,它就会运行 货币对 .onstart()执行一次后,脚本将从内存中卸载。 我们将所有工作代码放在功能中,然后可以轻松地转移到您的顾问和指标中。
//+------------------------------------------------------------------+ //-分形.mq4. //| Copyright 2019, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link " https://www.mql5.com " #property version "1.00" #property strict //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- }
为了显示分形,我们将使用图形对象“箭头”(obju arrow)。 我们需要一个功能,可以在价格图上创建这样的对象。 使用MQL目录中的现成ArrowCreate函数,该函数与终端一起运行:
//+------------------------------------------------------------------+ 他创造了射手。 //+------------------------------------------------------------------+ bool arrowcreate(const long chart_id=0,//图形ID) const string name=“arrow”,//箭头名称 const int subu window=0,//子窗口号 datetime time=0,//绑定点时间 double price=0,//参考点价格 const uchar arrow_code=252,//箭头码 const enum_arrow_anchor_anchor=anchor_bottom,//绑定点的位置 const color clr=clrred,//箭头颜色 const enumu lineu style=styleu solid,//边线样式 const int width=3,//箭头大小 const bool back=false,//后台 const bool selection=false,//选择移动 const bool hidden=true,//隐藏在对象列表中 const long zu order=0)//按鼠标优先
创建一个函数,获取条形索引并确定是否在其上形成分形:
//+------------------------------------------------------------------+ //–检查酒吧是否有索引› 分形。 //| | //isu up-如果已形成,则此变量为true。 //分形向上。 //isu down-如果已形成,则此变量为true。 分形向下。 //如果分形不形成,则两个变量/. //值为false。 //+------------------------------------------------------------------+ void GetFractalType(int index, bool &is_up, bool &is_down) { //重置变量值 is_up=is_down=false; //检查前两个和后两个主蜡烛的最大值是否高于主蜡烛 is_up=(iHigh(_Symbol,_Period,index+1)<iHigh(_Symbol,_Period,index) && iHigh(_Symbol,_Period,index+2)<iHigh(_Symbol,_Period,index) && iHigh(_Symbol,_Period,index-1)<iHigh(_Symbol,_Period,index) && iHigh(_Symbol,_Period,index-2)<iHigh(_Symbol,_Period,index)); //检查前两个和后两个的低(最低)中心蜡烛是否低于 is_down=(iLow(_Symbol,_Period,index+1)>iLow(_Symbol,_Period,index) && iLow(_Symbol,_Period,index+2)>iLow(_Symbol,_Period,index) && iLow(_Symbol,_Period,index-1)>iLow(_Symbol,_Period,index) && iLow(_Symbol,_Period,index-2)>iLow(_Symbol,_Period,index)); }
在这个函数中,除了Bar索引外,还通过链接传递两个逻辑变量,然后可以在调用函数的代码中分析这些变量。 如果在给定的索引上形成了向上的分形,则一个变量获得true,如果分形向下,则另一个变量获得true。
现在,我们需要一个函数来搜索数组的所有元素——时间序列(bar),并使用getfractaltype函数检查它们是否形成分形。 根据在条形图上形成的分形类型,我们将创建所需类型的图形对象,并将其放置在蜡烛的最大或最小值上:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ShowFractals() { //我们在计时赛结束和开始时通过两个酒吧 for(int i=Bars-2; i>1; i--) { string Name;// 对象名称 bool IsUp, IsDown; //如果在给定条上形成分形类型,则得到分形类型 GetFractalType(i,IsUp,IsDown); //形成向上的分形 if(IsUp) { Name="FRCTL_UP_"+_Symbol+"_"+IntegerToString(iTime(_Symbol,_Period,i)); 在高蜡烛上画一个蓝色的点 ArrowCreate(0,Name,0,iTime(_Symbol,_Period,i),iHigh(_Symbol,_Period,i), 159,ANCHOR_BOTTOM,clrBlue,STYLE_SOLID,2,true,false); } //形成向下分形 if(IsDown) { Name="FRCTL_DOWN_"+_Symbol+"_"+IntegerToString(iTime(_Symbol,_Period,i)); //在低蜡烛下画一个红色的点 ArrowCreate(0,Name,0,iTime(_Symbol,_Period,i),iLow(_Symbol,_Period,i), 159,ANCHOR_BOTTOM,clrRed,STYLE_SOLID,2,true,false); } } }
我们还需要一个功能,它将删除图形对象,这些对象可能仍然存在于我们以前运行的脚本中。 此函数将搜索以特定字符集(称为前缀)开头的图形对象,并仅删除它们。 我们以前确保创建的对象具有前缀“frctl”。 在调用showfractals函数之前,我们将删除对象。
//+------------------------------------------------------------------+ //–删除带有PRFX前缀的图形对象› //+------------------------------------------------------------------+ void DeleteObjects(string prfx) { for(int i=ObjectsTotal()-1; i>=0; i--) { string nm=ObjectName(0,i); if(StringSubstr(nm,0,StringLen(prfx))==prfx) ObjectDelete(0,nm); } }
Onstart()函数如下:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { DeleteObjects("FRCTL"); ShowFractals(); }
脚本的结果:
基于RSI振荡器的自动发散搜索
一旦我们学会了使用分形来定义本地价格极值,我们就可以开始我们课程的主要任务,即自动搜索。 发散 指示器 RSI 价格。 首先,我们必须确定术语。 广义上 发散 –这是指标线与价格图表的差异。
看跌和看跌的区别。
看跌趋异是指价格在上涨时形成越来越高的高点的情况。 趋势 ,而指标的每个新顶点 RSI 低于前一个:
看跌发散是一个信号,表明趋势很有可能逆转为下跌(看跌)。
看涨趋异是指价格在下跌时形成越来越低的低点。 趋势 ,RSI指标的每个新凹陷都高于上一个:
牛市发散是一个变化概率高的信号。 趋势 上升(牛市)
很少使用收敛信号或通常称为“隐藏”发散信号。 收敛信号显示现有的继续 趋势 .
隐藏的熊市发散:
隐藏的多头发散:
让我们编写一个脚本,在历史数据中搜索两种发散:通常的发散用实线标记,隐藏的发散用虚线标记。
使用MetaEditor主窗格中的“新建”按钮,我们将创建Diversity.mq4脚本。
在这个脚本中,我们需要输入参数,因此我们将添加scriptu showu inputs指令:
#property script_show_inputs
现在,脚本将不会在添加到图表后立即运行,而是像顾问或指示器一样打开参数窗口。
我们的脚本将使用以下选项:
input int RSIPeriod = 9; // RSI周期 input bool ShowFracrals = false; // 显示分形 input bool ShowHiddenDivergence = false; // 显示隐藏的发散
定义图形对象名称的字符串常量前缀。 我们在寻找要删除它们的对象时需要它:
#define PREFIX "OBJ_DIV_"
描述存储分形数据的结构,并宣布向上分形和向下分形的两组结构:
struct fractal { datetime time; // 分形酒吧的开放时间 double peak; // 根据分形类型,最大或最小条形 double indicator; // 条形图RSI指示器值 } FractalsUp[],FractalsDown[];
我们将以前编写的deleteobjects函数添加到代码中,并在onstart()处理程序中调用它。 这样,我们将从图形中删除以前脚本运行后可能留下的对象。 将分形数组设置为零大小,并定义数组中索引的方向,就像时间序列(从右到左)一样。
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //删除可能是以前脚本运行遗留的对象 DeleteObjects(PREFIX); //将分形数组设置为零 ArrayResize(FractalsUp,0); ArrayResize(FractalsDown,0); //将分形数组中索引的方向定义为时间序列(从右向左) ArraySetAsSeries(FractalsUp,true); ArraySetAsSeries(FractalsDown,true); } //+------------------------------------------------------------------+ //–删除带有PRFX前缀的图形对象› //+------------------------------------------------------------------+ void DeleteObjects(string prfx) { for(int i=ObjectsTotal()-1; i>=0; i--) { string nm=ObjectName(0,i); if(StringSubstr(nm,0,StringLen(prfx))==prfx) ObjectDelete(0,nm); } }
让我们写一个函数,用历史上发现的分形填充fractalsup[]和fractalsdown[]数组。 在这种情况下,如果参数showfracrals=true,则找到的分形将以彩色点显示在图表上。 图形对象的渲染功能来自MQL手册,定义分形类型的getfractaltype函数来自我们以前编写的脚本“分形.mq4”。
//+------------------------------------------------------------------+ //找到历史上所有的分形并填充相应的.. //分形数组。 //根据参数showfracrals显示找到的› //图形上的分形/. //+------------------------------------------------------------------+ void ShowFractalsAndFillArrays() { //我们在计时赛结束和开始时通过两个酒吧 int to=Bars-2; for(int i=2; i<to; i++) { string Name;// 对象名称 bool IsUp, IsDown; double rsi; //如果在给定条上形成分形类型,则得到分形类型 GetFractalType(i,IsUp,IsDown); //形成向上的分形 if(IsUp) { rsi=iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i); //也有RSI的最大值 if(iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i+1)<rsi && iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i-1)<rsi) { //将分形数据添加到数组中 int ind=AddFractalToArray(FractalsUp,iTime(_Symbol,_Period,i), iHigh(_Symbol,_Period,i),rsi); 在高蜡烛上画一个蓝色的点 if(ShowFracrals) { Name=PREFIX+_Symbol+"_FRCTL_UP_"+IntegerToString(FractalsUp[ind].time); ArrowCreate(0,Name,0,FractalsUp[ind].time,FractalsUp[ind].peak,159, ANCHOR_BOTTOM,clrBlue,STYLE_SOLID,2,true,false); } } } //形成向下分形 if(IsDown) { rsi=iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i); //至少有RSI if(iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i+1)>rsi && iRSI(_Symbol,_Period,RSIPeriod,PRICE_CLOSE,i-1)>rsi) { //将分形数据添加到数组中 int ind=AddFractalToArray(FractalsDown,iTime(_Symbol,_Period,i), iLow(_Symbol,_Period,i),rsi); //在低蜡烛下画一个红色的点 if(ShowFracrals) { Name=PREFIX+_Symbol+"_FRCTL_DOWN_"+IntegerToString(FractalsDown[ind].time); ArrowCreate(0,Name,0,FractalsDown[ind].time,FractalsDown[ind].peak,159, ANCHOR_TOP,clrRed,STYLE_SOLID,2,true,false); } } } } }
一旦两个分形数组都被填充,我们需要一个函数来找到价格和RSI振荡器的可能发散并用线标记:
//+------------------------------------------------------------------+ //–搜索并标记趋势发散线› //价格图表和振荡器RSI› //+------------------------------------------------------------------+ void ShowDivergence() { int size; string Name=PREFIX+_Symbol+"TLINE_"; /--------在局部最大值上绘制熊市发散线 size=ArraySize(FractalsUp); for(int i=0; i<size-1; i++) { int Style=EMPTY; //看跌趋异-价格上涨,RSI下跌 if(FractalsUp[i].peak>FractalsUp[i+1].peak && FractalsUp[i].indicator<FractalsUp[i+1].indicator) Style=STYLE_SOLID; else //隐藏的熊市发散(趋同)-价格下跌,RSI上涨 if(ShowHiddenDivergence && FractalsUp[i].peak<FractalsUp[i+1].peak && FractalsUp[i].indicator>FractalsUp[i+1].indicator) Style=STYLE_DOT; if(Style>EMPTY) { TrendCreate(0,Name+"BEAR_BAR_"+IntegerToString(FractalsUp[i].time),0, FractalsUp[i+1].time,FractalsUp[i+1].peak,FractalsUp[i].time,FractalsUp[i].peak, clrMaroon,ENUM_LINE_STYLE(Style)); TrendCreate(0,Name+"BEAR_RSI_"+IntegerToString(FractalsUp[i].time),1, FractalsUp[i+1].time,FractalsUp[i+1].indicator,FractalsUp[i].time, FractalsUp[i].indicator,clrMaroon,ENUM_LINE_STYLE(Style)); } } //--------在局部极小值上绘制牛市发散线 size=ArraySize(FractalsDown); for(int i=0; i<size-1; i++) { int Style=EMPTY; //看涨趋异-价格下跌,RSI上涨 if(FractalsDown[i].peak<FractalsDown[i+1].peak && FractalsDown[i].indicator>FractalsDown[i+1].indicator) Style=STYLE_SOLID; else //隐含多头趋异(趋同)-价格上涨,RSI下跌 if(ShowHiddenDivergence && FractalsDown[i].peak>FractalsDown[i+1].peak && FractalsDown[i].indicator<FractalsDown[i+1].indicator) Style=STYLE_DOT; if(Style>EMPTY) { TrendCreate(0,Name+"BULL_BAR_"+IntegerToString(FractalsDown[i].time),0, FractalsDown[i+1].time,FractalsDown[i+1].peak,FractalsDown[i].time, FractalsDown[i].peak, clrGreen,ENUM_LINE_STYLE(Style)); TrendCreate(0,Name+"BULL_RSI_"+IntegerToString(FractalsDown[i].time),1, FractalsDown[i+1].time,FractalsDown[i+1].indicator,FractalsDown[i].time, FractalsDown[i].indicator,clrGreen,ENUM_LINE_STYLE(Style)); } } }
如您所见,发散搜索算法相当简单:价格运动的方向由两个相邻的分形决定; 如果RSI在这些条形图上形成极值,则比较振荡器的方向和价格。 在发散的情况下,绘制连接蜡烛极值和RSI极值的线段。
将函数调用添加到onstart()处理程序:
//+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //删除可能是以前脚本运行遗留的对象 DeleteObjects(PREFIX); //将分形数组设置为零 ArrayResize(FractalsUp,0); ArrayResize(FractalsDown,0); //将分形数组中索引的方向定义为时间序列(从右向左) ArraySetAsSeries(FractalsUp,true); ArraySetAsSeries(FractalsDown,true); ShowFractalsAndFillArrays(); ShowDivergence(); }
我们的脚本准备好了。 让我们在欧元美元小时图表上运行它,先将RSI指示器附加到图表上,其周期与脚本中的周期相同:
脚本可以在没有RSI指示器的情况下运行。在这种情况下,发散线将仅基于价格图表构建。
启用分形显示(彩色点)和隐藏发散显示(虚线):
结论
在本教程中,我们了解了MQL4如何使用酒吧进行价格流分析。 我们学会了如何找到简单的五条形地层——所谓的分形 比尔·威廉 并用它们来确定局部极值。 因此,我们编写了一个脚本,用于自动搜索价格差异和RSI振荡器。这些解决方案可以用于创建自定义指标和交易。 顾问。
论坛主题
尊敬的尤里·洛塞夫AKA LSV107 Tlap.com