这一切开始于当我从 Theoretical Basis of Building Cluster Indicators for FOREX(建立 FOREX 群集指标的理论基础)一文中第一次听到群集指标时。当时我对其非常感兴趣,我决定在多市场分析方面写一点类似的东西。首先,我实施了我自己的指标,代号为 MultiCurrencyIndex,在这个指标中,使用货币指数的计算值来计算经典指标(RSI、MACD、CCI)的值。
现在,我将告诉您我如何在 MQL5 的帮助下将此指标转移到新的平台 MetaTrader 5,除了代替计算 CCI 以外,我将计算随机动量指标 (Stochastic Oscillator),该指标更具前瞻性(我的个人意见)。
让我们从一些定义开始。
美元指数 - 按照 Neutron 提供给我的公式计算的双精度值。
,
其中,USD / YYY 表示所有方向的直接报价,例如 USD / CHF,而 XXX / USD 表示所有反向报价,例如 EUR / USD。
其他指数通过包含美元的货币对收盘价来计算。
主线 - 指标的两条线,反应计算出来的数据,与当前图形直接相关。例如,在 EURUSD 图形中,它将是 EUR 和 USD 的线条。
补充线 - 其他计算出来的指标线,与当前图形无关。例如,对于相同的 EURUSD 图形,它将是 GBP、CHF、JPY、CAD、AUD 和 NZD 货币的线条。
收盘价 - 必要货币对的当前时间框架的收盘价的值。
让我们开始吧。
要开始,我们需要设置问题。
为此,我们需要:
31 个指标缓存:
为了选择指标的目的地,我们将建立一个枚举类型 enum :
enum Indicator_Type { Use_RSI_on_indexes = 1, // RSI 索引 Use_MACD_on_indexes = 2, // MACD 索引 Use_Stochastic_Main_on_indexes = 3 // Stochastic 索引 };接下来,使用 input 命令,在指标参数选择窗口,我们将从这个供用户选择的列表获取值。
input Indicator_Type ind_type=Use_RSI_on_indexes; // 索引对应的指标类型
可以用一种更加用户友好的方式,在 "Inputs"(输入)选项卡中显示输入参数的名称。为此,我们使用紧急备注,该备注必须紧跟着输入参数的说明,在同一行中。这样,输入参数的名称更容易被用户理解。
同样的规则也适用于列举命令 enum。换言之,如果有助于记忆的名称与备注关联在一起,如我们的例子所示,则将显示此备注的内容,而不是有助于记忆的名称。这对编写具有清晰输入参数说明的程序提供了额外的灵活性。
开发人员试图通过让最终用户看到容易理解的参数名称而不是在代码中所写的,向最终用户提供便利的方式来处理 MQL5 程序。可以在此处找到更多信息。
图 1. 指标类型的选择
我们向用户提供呈现指标所必需的货币及其颜色的选择:
input bool USD=true; input bool EUR=true; input bool GBP=true; input bool JPY=true; input bool CHF=true; input bool CAD=true; input bool AUD=true; input bool NZD=true; input color Color_USD = Green; // USD 线颜色 input color Color_EUR = DarkBlue; // EUR 线颜色 input color Color_GBP = Red; // GBP 线颜色 input color Color_CHF = Chocolate; // CHF 线颜色 input color Color_JPY = Maroon; // JPY 线颜色 input color Color_AUD = DarkOrange; // AUD 线颜色 input color Color_CAD = Purple; // CAD 线颜色 input color Color_NZD = Teal; // NZD 线颜色
图 2. 指标线颜色的选择
其他一些可配置的参数:
input string rem000 = ""; // 依据指标类型 input string rem0000 = ""; // 需要的数值 : input int rsi_period = 9; // RSI 周期 input int MACD_fast = 5; // MACD_fast 周期 input int MACD_slow = 34; // MACD_slow 周期 input int stoch_period_k = 8; // Stochastic %K 周期 input int stoch_period_sma = 5; // Stochastics %K 平滑周期 input int shiftbars = 500; // 指标计算柱线数
图 3. 指标参数
指标计算的 500 根柱的限制是人为的,但是足以说明计算的概念。但我们必须记住,每个指标缓存都需要内存,显示非常大的可变规模(数以百万计的柱)可能导致计算机没有足够的内存。
指标缓存:
double EURUSD[], // 报价 GBPUSD[], USDCHF[], USDJPY[], AUDUSD[], USDCAD[], NZDUSD[]; double USDx[], // 指数 EURx[], GBPx[], JPYx[], CHFx[], CADx[], AUDx[], NZDx[]; double USDplot[], // 货币线结果 EURplot[], GBPplot[], JPYplot[], CHFplot[], CADplot[], AUDplot[], NZDplot[]; double USDStoch[], // 无平滑的 schotastics 的收盘价/收盘价类型中间数据缓存区, EURStoch[], GBPStoch[], JPYStoch[], CHFStoch[], CADStoch[], AUDStoch[], NZDStoch[];我们还需要某些全局(指标级别)变量:
int i,ii; int y_pos=0; // 信息对象的 Y 坐标变量 datetime arrTime[7]; // 最后已知时间的零值柱线数组 (需要同步) int bars_tf[7]; // 检查不同货币对的可用柱线数量 int countVal=0; // 可执行数量比率 int index=0; datetime tmp_time[1]; // 柱线时间的中间数组
现在,我们看到一个相当长的函数 OnInit,使用该函数,我们将依据指标的目的分配指标缓存。
因为初始计算采用美元指数,因此对于 USD,我们简单地建立禁用货币指标缓存呈现的可能性。
它看起来如下所示:
if(USD) { countVal++; SetIndexBuffer(0,USDplot,INDICATOR_DATA); // 渲染数组 PlotIndexSetString(0,PLOT_LABEL,"USDplot"); // 指标线名称 (当鼠标选择时) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,shiftbars); // 从哪里,我们开始渲染 PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); // 绘图风格 (线) PlotIndexSetInteger(0,PLOT_LINE_COLOR,Color_USD); // 线的渲染颜色 if(StringFind(Symbol(),"USD",0)!=-1) {PlotIndexSetInteger(0,PLOT_LINE_WIDTH,wid_main);} // 如果交易品种名称包括 USD // 则画适当宽度的线 else {PlotIndexSetInteger(0,PLOT_LINE_STYLE,style_slave);} ArraySetAsSeries(USDplot,true); // 按时间序列检索数组 ArrayInitialize(USDplot,EMPTY_VALUE); // 零值 f_draw("USD",Color_USD); // 在指标信息窗口渲染 } SetIndexBuffer(15,USDx,INDICATOR_CALCULATIONS); // 计算美元指数的数组 // (在指标中不会显示) ArraySetAsSeries(USDx,true); // 按时间序列检索数组 ArrayInitialize(USDx,EMPTY_VALUE); // 零值 if(ind_type==Use_Stochastic_Main_on_indexes) { SetIndexBuffer(23,USDstoch,INDICATOR_CALCULATIONS); // 如果指标是 Use_Stochastic_Main_on_indexes, // 则需要中间数组 ArraySetAsSeries(USDstoch,true); // 按时间序列检索数组 ArrayInitialize(USDstoch,EMPTY_VALUE); // 零值 }对于 EUR 货币,函数代码 OnInit 看起来如下所示:
if(USD) { countVal++; SetIndexBuffer(0,USDplot,INDICATOR_DATA); // 渲染数组 PlotIndexSetString(0,PLOT_LABEL,"USDplot"); // 指标线名称 (当鼠标选择时) PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,shiftbars); // 从哪里,我们开始渲染 PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); // 绘图风格 (线) PlotIndexSetInteger(0,PLOT_LINE_COLOR,Color_USD); // 线的渲染颜色 if(StringFind(Symbol(),"USD",0)!=-1) {PlotIndexSetInteger(0,PLOT_LINE_WIDTH,wid_main);} // 如果交易品种名称包括 USD // 则画适当宽度的线 else {PlotIndexSetInteger(0,PLOT_LINE_STYLE,style_slave);} ArraySetAsSeries(USDplot,true); // 按时间序列检索数组 ArrayInitialize(USDplot,EMPTY_VALUE); // 零值 f_draw("USD",Color_USD); // 在指标信息窗口渲染 } SetIndexBuffer(15,USDx,INDICATOR_CALCULATIONS); // 计算美元指数的数组 // (在指标中不会显示) ArraySetAsSeries(USDx,true); // 按时间序列检索数组 ArrayInitialize(USDx,EMPTY_VALUE); // 零值 if(ind_type==Use_Stochastic_Main_on_indexes) { SetIndexBuffer(23,USDstoch,INDICATOR_CALCULATIONS); // 如果指标是 Use_Stochastic_Main_on_indexes, // 则需要中间数组 ArraySetAsSeries(USDstoch,true); // 按时间序列检索数组 ArrayInitialize(USDstoch,EMPTY_VALUE); // 零值 } if(EUR) { countVal++; SetIndexBuffer(1,EURplot,INDICATOR_DATA); // 渲染数组 PlotIndexSetString(1,PLOT_LABEL,"EURplot"); // 指标线名称 (当鼠标选择时) PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,shiftbars); // 从哪里,我们开始渲染 PlotIndexSetInteger(1,PLOT_DRAW_TYPE,DRAW_LINE); // 绘图风格 (线) PlotIndexSetInteger(1,PLOT_LINE_COLOR,Color_EUR); // 线的渲染颜色 if(StringFind(Symbol(),"EUR",0)!=-1) {PlotIndexSetInteger(1,PLOT_LINE_WIDTH,wid_main);} // 如果交易品种名称包括 EUR // 则画适当宽度的线 else {PlotIndexSetInteger(1,PLOT_LINE_STYLE,style_slave);} // 如果交易品种名称不包括 EUR, // 则画适当风格的线(在上交叉) ArraySetAsSeries(EURplot,true); // 按时间序列检索数组 ArrayInitialize(EURplot,EMPTY_VALUE); // 零值 SetIndexBuffer(8,EURUSD,INDICATOR_CALCULATIONS); // 货币对 EURUSD 的收盘数据 ArraySetAsSeries(EURUSD,true); // 按时间序列检索数组 ArrayInitialize(EURUSD,EMPTY_VALUE); // 零值 SetIndexBuffer(16,EURx,INDICATOR_CALCULATIONS); // EURO 索引数组用于计算 // (在指标中不会显示) ArraySetAsSeries(EURx,true); ArrayInitialize(EURx,EMPTY_VALUE); if(ind_type==Use_Stochastic_Main_on_indexes) { SetIndexBuffer(24,EURstoch,INDICATOR_CALCULATIONS); // 如果指标是 Use_Stochastic_Main_on_indexes, // 则需要中间数组 ArraySetAsSeries(EURstoch,true); // 按时间序列检索数组 ArrayInitialize(EURstoch,EMPTY_VALUE); // 零值 } f_draw("EUR",Color_EUR); // 在指标信息窗口渲染 }与 EUR 类似,对于 GBP、JPY、CHF、CAD、AUD 和 NZD 等货币,代码也是类似的,移位指标缓存的索引。可在附带的指标文件中找到这些货币的代码。
这样就完成了指标初始化的说明。
接下来,我们需要某些自定义的用户功能:
以上各项的简短说明:
输入参数:
double f_RSI(double &buf_in[], int period,int shift),
其中,buf_in[] - 双精度类型的数组(例如时间序列),period - RSI 指标周期,shift - 我们为其计算指标的指标柱。返回一个双精度类型的值。
输入参数:
double f_MACD(double &buf_in[], int period_fast,int period_slow,int shift),
其中,buf_in[] - 双精度类型的数组(例如时间序列),period_fast - 快速移动平均线的周期,period_slow - 慢速移动平均线的周期,shift - 我们为其计算指标的指标柱。返回一个双精度类型的值。
输入参数:
double SimpleMA(const int position,const int period,const double &price[]),
其中,position - 我们为其计算指标的指标柱。period - SMA 指标的周期,price[] - 双精度类型的时间数组(例如时间序列)。返回一个双精度类型的值。
输入参数:
double f_Stoch(double &price[], int period_k, int shift),
其中,price[] - 双精度类型的数组(例如时间序列),period_fast - %K 指标线的周期,shift - 我们为其计算指标的指标柱。返回一个双精度类型的值。
输入参数:
int f_draw(string name, color _color)
其中,name - 对象名称,_color - 对象颜色。该函数仅用于信息显示目的。从窗口的右上角往下,此函数显示受影响的货币的名称。货币文本的颜色和与该货币关联的指标线的颜色相同。
输入参数:
int f_comment(string text)
text - 需要放在指标右下角的文本。一种指标工作状态栏。
最后,结束性和最重要的函数之一:
无输入参数。
在 MetaTrader 5 中,历史记录以针对每一工具的 TF 的分钟数据的形式存储。因此,一旦打开客户端,在启动程序之前,已经依据相同的 TF 分钟数据构建了所有必要的(受影响的)图形。构建还发生在切换当前 TF 或尝试通过 MQL5 程序代码访问 TF 图形期间。
因此:
此任务的第一部分通过内置的 Bars 函数实施,该函数通过与交易品种的对应周期返回历史记录中柱的数量。使用此版本的函数已经足够,如下所示。
int Bars( string symbol_name, // 交易品种名称 ENUM_TIMEFRAMES timeframe // 周期 );
在专为此数组声明的变量中,我们为所有受影响的货币对收集可用柱数。我们为最低限度的必要历史数据量检查每一个值(指标参数中“用于计算指标的柱的数量”)。如果任何工具的历史记录中的可用柱数小于此变量的值,则我们认为构建不成功,并且重新检查可用数据的数量。一旦针对所有货币对的可用历史记录超过用户请求的数量,则我们认为这部分的初始化已成功完成。
初始化的第二部分通过使用 CopyTime 函数来实施。
我们将每个受影响的工具的零柱的建立复制到一个专为此目的创建的数组。如果此数组的所有元素都相同并且不等于 0,则我们认为我们的零柱已经同步,让我们开始计算。要更加详细地理解这是如何实施的,请查看附带指标的代码。
这样就完成了其他函数的说明,我们转到 OnCalculate 函数的实施。因为这是一个多货币指标,我们将需要此函数请求的第二版本。
int OnCalculate(const int rates_total, // 输入时间序列大小 const int prev_calculated, // 前一次请求处理的柱线 const datetime& time[], // 开盘时间 const double& open[], // 开盘价 const double& high[], // 最高价 const double& low[], // 最低价 const double& close[], // 收盘价 const long& tick_volume[], // 即时交易量 const long& volume[], // 实际交易量 const int& spread[] // 点差 );
确定计算所需的柱数:
int limit=shiftbars; if(prev_calculated>0) {limit=1;} else {limit=shiftbars;}
同步货币对的图形:
init_tf();
接下来,使用 CopyClose 函数,我们将所有必要货币对的收盘价数据复制到专为此目的寄存的指标缓存。(欲知有关存取当前工具和/或其他工具的其他 TF 数据的更多信息,请浏览帮助)
如果出于任何原因,函数未复制数据,并且返回 -1,则我们在备注中显示货币对错误消息,并且等待接收当前工具的新价格变动。
if (EUR){copied=CopyClose("EURUSD",PERIOD_CURRENT,0,shiftbars,EURUSD); if (copied==-1){f_comment("等待...EURUSD");return(0);}} if (GBP){copied=CopyClose("GBPUSD",PERIOD_CURRENT,0,shiftbars,GBPUSD); if (copied==-1){f_comment("等待...GBPUSD");return(0);}} if (CHF){copied=CopyClose("USDCHF",PERIOD_CURRENT,0,shiftbars,USDCHF); if (copied==-1){f_comment("等待...USDCHF");return(0);}} if (JPY){copied=CopyClose("USDJPY",PERIOD_CURRENT,0,shiftbars,USDJPY); if (copied==-1){f_comment("等待...USDJPY");return(0);}} if (AUD){copied=CopyClose("AUDUSD",PERIOD_CURRENT,0,shiftbars,AUDUSD); if (copied==-1){f_comment("等待...AUDUSD");return(0);}} if (CAD){copied=CopyClose("USDCAD",PERIOD_CURRENT,0,shiftbars,USDCAD); if (copied==-1){f_comment("等待...USDCAD");return(0);}} if (NZD){copied=CopyClose("NZDUSD",PERIOD_CURRENT,0,shiftbars,NZDUSD); if (copied==-1){f_comment("等待...NZDUSD");return(0);}}
接下来,在循环中(从 0 至限制),我们依据当前柱的收盘价和美元指数进行:
for (i=limit-1;i>=0;i--) { //计算美元指数 USDx[i]=1.0; if (EUR){USDx[i]+=EURUSD[i];} if (GBP){USDx[i]+=GBPUSD[i];} if (CHF){USDx[i]+=1/USDCHF[i];} if (JPY){USDx[i]+=1/USDJPY[i];} if (CAD){USDx[i]+=1/USDCAD[i];} if (AUD){USDx[i]+=AUDUSD[i];} if (NZD){USDx[i]+=NZDUSD[i];} USDx[i]=1/USDx[i]; //计算其它货币值 if (EUR){EURx[i]=EURUSD[i]*USDx[i];} if (GBP){GBPx[i]=GBPUSD[i]*USDx[i];} if (CHF){CHFx[i]=USDx[i]/USDCHF[i];} if (JPY){JPYx[i]=USDx[i]/USDJPY[i];} if (CAD){CADx[i]=USDx[i]/USDCAD[i];} if (AUD){AUDx[i]=AUDUSD[i]*USDx[i];} if (NZD){NZDx[i]=NZDUSD[i]*USDx[i];} }
数据放入相应的指标缓存。检查在初始化期间用户选择了哪类指标,并且依据此基础进行相应的计算。
如果希望查看指数的 RSI 指标,则执行下面的代码:
if (ind_type==Use_RSI_on_indexes) { if (limit>1){ii=limit - rsi_period - 1;} else{ii=limit - 1;} for(i=ii;i>=0;i--) { if (USD){USDplot[i]=f_RSI(USDx,rsi_period,i);} if (EUR){EURplot[i]=f_RSI(EURx,rsi_period,i);} if (GBP){GBPplot[i]=f_RSI(GBPx,rsi_period,i);} if (CHF){CHFplot[i]=f_RSI(CHFx,rsi_period,i);} if (JPY){JPYplot[i]=f_RSI(JPYx,rsi_period,i);} if (CAD){CADplot[i]=f_RSI(CADx,rsi_period,i);} if (AUD){AUDplot[i]=f_RSI(AUDx,rsi_period,i);} if (NZD){NZDplot[i]=f_RSI(NZDx,rsi_period,i);} } }
如果我们想查看按指数统计的 MACD 指标,则我们的代码如下(但是迄今为止仅仅依据 SimpleMA 实施,以后将依据 EMA 实施):
if (ind_type==Use_MACD_on_indexes) { if (limit>1){ii=limit - MACD_slow - 1;} else{ii=limit - 1;} for(i=ii;i>=0;i--) { if (USD){USDplot[i]=f_MACD(USDx,MACD_fast,MACD_slow,i);} if (EUR){EURplot[i]=f_MACD(EURx,MACD_fast,MACD_slow,i);} if (GBP){GBPplot[i]=f_MACD(GBPx,MACD_fast,MACD_slow,i);} if (CHF){CHFplot[i]=f_MACD(CHFx,MACD_fast,MACD_slow,i);} if (JPY){JPYplot[i]=f_MACD(JPYx,MACD_fast,MACD_slow,i);} if (CAD){CADplot[i]=f_MACD(CADx,MACD_fast,MACD_slow,i);} if (AUD){AUDplot[i]=f_MACD(AUDx,MACD_fast,MACD_slow,i);} if (NZD){NZDplot[i]=f_MACD(NZDx,MACD_fast,MACD_slow,i);} } }
如果是随机动量指标,则您必须首先计算 % K 线,然后通过 SimpleMA 方法对其进行平滑。必须在图表上显示最后经过平滑处理的线条。
if (ind_type==Use_Stochastic_Main_on_indexes) { if (limit>1){ii=limit - stoch_period_k - 1;} else{ii=limit - 1;} for(i=ii;i>=0;i--) { if (USD){USDstoch[i]=f_Stoch(USDx,rsi_period,i);} if (EUR){EURstoch[i]=f_stoch(EURx,stoch_period_k,i);} if (GBP){GBPstoch[i]=f_stoch(GBPx,stoch_period_k,i);} if (CHF){CHFstoch[i]=f_stoch(CHFx,stoch_period_k,i);} if (JPY){JPYstoch[i]=f_stoch(JPYx,stoch_period_k,i);} if (CAD){CADstoch[i]=f_stoch(CADx,stoch_period_k,i);} if (AUD){AUDstoch[i]=f_stoch(AUDx,stoch_period_k,i);} if (NZD){NZDstoch[i]=f_stoch(NZDx,stoch_period_k,i);} } if (limit>1){ii=limit - stoch_period_sma - 1;} else{ii=limit - 1;} for(i=ii;i>=0;i--) { if (USD){USDplot[i]=SimpleMA(i,stoch_period_sma,USDstoch);} if (EUR){EURplot[i]=SimpleMA(i,stoch_period_sma,EURstoch);} if (GBP){GBPplot[i]=SimpleMA(i,stoch_period_sma,GBPstoch);} if (CHF){CHFplot[i]=SimpleMA(i,stoch_period_sma,CHFstoch);} if (JPY){JPYplot[i]=SimpleMA(i,stoch_period_sma,JPYstoch);} if (CAD){CADplot[i]=SimpleMA(i,stoch_period_sma,CADstoch);} if (AUD){AUDplot[i]=SimpleMA(i,stoch_period_sma,AUDstoch);} if (NZD){NZDplot[i]=SimpleMA(i,stoch_period_sma,NZDstoch);} } }
这样就完成了指标的计算。图 4-6 以几张图片说明了不同类型的指标。
图 4. 通过指数统计的 RSI 指标
图 5. 通过货币指数统计的 MACD 指标
图 6. 通过货币指数统计的随机动量指标
在实施 MultiCurrencyIndex 指标时,我在 MQL5 中使用没有数量限制的指标缓存,这样大大简化了代码。本文是此类方法的一个例子。为了获得可靠的指标数据,我说明了一种相对于零柱的不同工具的同步算法。我还示范了一种可能算法,该算法可访问来自与指标附加到的交易品种相关联的其他工具的数据。
因为本文旨在说明处理海量指标缓存的可能性,以上按用户的数据数组计算指标的函数并不是让读者毫无负担的最佳方式。但是它足以执行必需的计算。
Forex 市场的群集分析有利也有弊。可以很容易获取依据此方法的交易系统,并且在各个论坛上,包括 MQL4.Community,都有相关讨论。因此,按这个指标进行交易的原理并不在本文的讨论范围之内。
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...