内容
- 概论
- 第一章. "绳索" 指标创建和计算的原理以及例程代码。
- 第二章. 实际使用 "绳索" 指标并在其基础上构建一款智能交易程序
- 第三章. 优化智能交易程序的输入参数
- 结论
概论
本文和指标均基于 Erik L. Nayman 的书籍 - 交易员小百科 — K . VIRA-R Alfa 资本, 1999。—236 页)。它从所选择的称为 "绳索" 的指标入手, 涵盖了金融市场技术和基本面分析的基础。简言之, 值得注意的是, 指标基于其覆盖周期时间内价格变化速度与变化数量的比率。
在他的书中, 作者给出了分析概述, 提供了用于编写这篇文章的表格和图表。他利用价格变化速度, 买卖数量和变化规模作为分析对象, 在多空交汇处进行市场分析。速度由多笔交易测定, 规模是计算两笔相邻报价 (当前和之前) 间的差值。在书中, 最后的已知价格认定为报价 (使用计算例程的更多信息在第一章内提供)。
市场分析的目的是在多空双方同时影响市场时, 计算力度并判断趋势方向。发现力度值可用来评估哪个方向当前更强。作者将此动作标识为两种对立势力的拉锯战, 正如书中表述的通过计算多空力度的 "绳索" 值:
他还描述了两种方法来分析市场, 以及基于静态和动态的方法进行计算。在他的书中, 计算 "绳索" 值涉及以下步骤:
1. 评估并计算多空力度, 找到方向。
多头力度公式 (如书所述):
BuF = SPCi, 此处:
- BuF — 多头力度;
- SPCi -—覆盖分析周期时间内的正面变化之合。
空头力度公式 (如书所述):<br2/>
BeF = SNCi, 此处:
- BeF — 空头力度;
- SNCi — 覆盖分析周期时间内的负面变化之合。
BuF 和 BeF 的数值进一步相互比较, 谁的数值更高表示谁在市场上占主导地位。
通过比较所分析图表周期内相邻两根柱线的力度值, 我们可得到动态评估。参看源代码中以下分析:
BuF > BeF 和 (BuF1 - BuFО) > (BeF1 - BeFО) = 整体多头力度增长 (当前力度值由索引 1 标记, 前值 - 由索引 0)。
BuF > BeF 和 (BuF1 - BuFО) < (BeF1 - BeFО) = 整体多头力度下降。
BuF < BeF 和 (BuF1 - BuFО) > (BeF1 - BeFО) = 整体空头力度下降。
BuF < BeF 和 (BuF1 - BuFО) < (BeF1 - BeFО) = 整体空头力度增长。
除了静态力度值, 还要分别观察该值在相邻计算点上的动态变化。
2. 评估, 计算和比较多空 "活力" (变化数量)。在源码中的多空活力计算:
BuV = NPCi, 此处:
- BuV — 多头活力;
- NPCi — 覆盖分析周期时间内的正面变化数量。
BeV = NNCi, 此处:
- BeV — 空头活力;
- NNCi — 覆盖分析周期时间内的负面变化数量。
通过与前一个相邻的活力计算值进行比较, 我们得到动态评价。同样方式, 按照前一分析步骤中的比较例程, 将静态值与动态值进行比较得到动态评价。
3. 评估, 计算和比较多空 "技巧" 评价。在源代码中示意如下:
在多空市场政策中的技巧揭示如下:
BuS = SPC1/NPC1;
BeS = SNC1 / NNC1.
这里展示了如何基于第一步的例程计算静态、动态数值。
4. 多头和空头的最终评估。在前三步里计算所有数值并与其数据比较之后, 我们可以针对趋势方向和趋势性质做出结论。根据书中表述, 最终评估基于所有三个参数的比较:
如果 BuF > BeF, BuV > BeV 且 BuS > BeS (上述的动态相关主题), 那么比之空头活力, 多头是首选, 所以只应考虑买单。 两个 "绳索上的相邻计算点" 应添加到静态数据用于动态市场评估。
在这些分析步骤期间, 创建指标的三个主要计算值可以区分:
- 力度 — 覆盖分析周期时间内的变化之合;
- 活力 — 覆盖分析周期时间内的变化量;
- 技巧 = 力度 / 活力。
作者将动态部分标记为整体中最敏感和积极的。本文仅选择了一个静态计算方法。因此, 经过比较三个计算部分 "力度", "活力" 和 "技巧", 作者制定出关于趋势力度和方向的结论, 计算静态和动态数据并介绍了它们的各种不同用法。现在我们继续深入研究使用 Erik L. Nayman 的方法创建 "绳索" 指标。
第一章. "绳索" 指标创建和计算的原理以及例程代码。
选择线性和直方图方法来创建指标。线性方法是计算多空的数值之合。直方图方法分别显示多头和空头的计算值。
我们将要查看的指标代码每步都带注释。让我们从声明变量, 并使用 #property 设置指标显示特性开始。在以下的代码里提供了指标的颜色特征描述以便于数据分析。
//+------------------------------------------------------------------+ //| RopebyEricNaiman.mq5 | //| 版权 2015, MetaQuotes 软件公司| //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "版权 2015, MetaQuotes 软件公司" #property link "https://www.mql5.com" #property description "RopebyEricNaiman by Im_hungry (https://login.mql5.com/en/users/Im_hungry)" #property description "RopebyEricNaiman - 显示市场动作的期望方向和期望力量。" #property version "1.00" #property strict //--- 包含来自 MovingAverages.mqh 文件的均值计算函数 #include <MovingAverages.mqh> //--- #property indicator_separate_window // 设置在单独窗口绘制 #property indicator_buffers 12 // 设置 12 个指标缓存区 #property indicator_plots 5 // 设置 5 个指标绘图区 //--- 多头直方图 #property indicator_label1 "BULL" #property indicator_type1 DRAW_COLOR_HISTOGRAM // 0 - clrDarkGreen - 颜色索引 "仅买入", 买单具有高级别确认 // 1 - clrMediumSeaGreen - 颜色索引 "买单允许", 买单具有平均级别确认 // 2 - clrLightGreen - 颜色索引 "买单允许", 买单具有低级别确认 // 3 - clrGray - 颜色索引 "买单禁止" #property indicator_color1 clrDarkGreen,clrMediumSeaGreen,clrLightGreen,clrGray #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- 空头直方图 #property indicator_label2 "BEAR" #property indicator_type2 DRAW_COLOR_HISTOGRAM // 0 - clrDarkRed - 颜色索引 "仅卖出", 卖单具有高级别确认 // 1 - clrIndianRed - 颜色索引 "卖出允许", 卖单具有平均级别确认 // 2 - clrLightPink - 颜色索引 "卖出允许", 卖单具有低级别确认 // 3 - clrGray - 颜色索引 "卖出禁止" #property indicator_color2 clrDarkRed,clrIndianRed,clrLightPink,clrGray #property indicator_style2 STYLE_SOLID #property indicator_width2 2 //--- 指标主线 #property indicator_label3 "主线" #property indicator_type3 DRAW_COLOR_LINE // 0 - clrDarkGreen - 颜色索引 "仅买入", 买单具有高级别确认 // 1 - clrDarkRed - 颜色索引 "仅卖出", 卖单具有高级别确认 // 2 - clrGray - 颜色索引 "无限制确认" #property indicator_color3 clrDarkGreen,clrDarkRed,clrGray #property indicator_style3 STYLE_SOLID #property indicator_width3 2 //--- 在指标主线数据上绘制 快速均线指标 #property indicator_label4 "快速均线" #property indicator_type4 DRAW_LINE #property indicator_color4 clrAqua #property indicator_style4 STYLE_SOLID #property indicator_width4 1 //--- 在指标主线数据上绘制 慢速均线指标 #property indicator_label5 "慢速均线" #property indicator_type5 DRAW_LINE #property indicator_color5 clrYellow #property indicator_style5 STYLE_SOLID #property indicator_width5 1
作者自己并未提供任何关于颜色特性的描述, 当所有三个指标全部/部分匹配时可分析趋势:
- 多头力度 > 空头力度 — 多头趋势, 逆向符号指示空头霸道。
- 多头活力 > 空头活力 — 多头趋势。
- 多头技巧 > 空头技巧 — 多头趋势。
通过比较这些指标, 我们可以评估趋势的力度。越多指标确定的趋势越强劲。直方图彩色显示的描述, 基于初始化期间三个变量的计算值, 如上所示。这些计算值可以任意组合进行比较。
0 — clrDarkGreen, 颜色索引 "仅买入", 买单具有高级别确认。
1 — clrMediumSeaGreen, 颜色索引 "买单允许", 买单具有平均级别确认。
2 — clrLightGreen, 颜色索引 "买单允许", 买单具有低级别确认。
3 — clrGray, 颜色索引 "买单禁止"。
按照来自书里的原始描述, 当所有三个计算值匹配, 仅有指标主线的彩色显示:
"如果 BuF > BeF, BuV > BeV 和 BuS > BeS (上述的动态相关主题), 那么比之空头活力, 多头是首选, 所以只应考虑买单。 如果 BuF < BeF, BuV < BeV 和 BuS < BeS (动态相关主题), 那么比之多头, 空头是首选。只有卖单是首选。" |
我们将继续声明指标的外部变量。必须注意的是, 依据输入变量 draw_line 和 draw_histogram 显示一条线 和/或 直方图的选项可令指标操作更容易。为达成通用计算, 您可以现在使用 _price 参数选择用于指标计算的价格。均线指标的执行设置选项也可禁用。
//--- 输入参数 input string section_1="___ 主要设置"; //--- 启用绘制多空直方图 input bool draw_histogram=true; //--- 启用 绘制指标主线 input bool draw_line=true; //--- 指标周期 input int _period=76; //--- 用于计算的价格 input ENUM_APPLIED_PRICE _price=PRICE_CLOSE; //--- 限制自当前柱线起始的历史柱线计算深度 input int max_bars=0; //--- 为了指标更清晰, 相对直方图的位移。所有主线和均线数值乘以 line_deviation input double line_deviation=3.0; //--- input string section_2="___ 快速均线"; //--- 启用绘制快速均线 input bool draw_MA_fast=false; //--- 快速均线周期, 计算基于指标 input int period_MA_fast=25; //--- 快速均线的计算方法 input ENUM_MA_METHOD method_MA_fast=MODE_SMA; //--- input string section_3="___ 慢速均线"; //--- 启用绘制快速均线 input bool draw_MA_slow=false; //--- 慢速均线周期, 计算基于指标 input int period_MA_slow=143; //--- 快速均线的计算方法 input ENUM_MA_METHOD method_MA_slow=MODE_SMA; //---- 缓存区 ...
该书包含了一些配合 "绳索" 数值工作的选项。以下是各种的选择:
- 指标与零轴交汇。
- 只在所有三个计算值, 如力度, 活力和技巧匹配时, 才可交易。以主线的颜色实现。
- 指标与均线交汇。
- 两条均线交汇。
МА 指标 (从 MovingAverages.mqh 库里加载) 基于指标缓存区数组 Buffer_calcLINE 内表述的主线数值创建。在将它转移至函数库之前, 已将它添加到实现, 可轻松访问数据来计算均线。
用于计算和创建指标的主要数据在 OnInit 函数里。当图表周期改变以及其它原因令指标重置发生时, 将 prev_rates_total 和 _tm_prev 变量清零是进行计算的一个重要方面。当下一根柱线出现时, _tm_prev 值负责计算开始到最后计算柱线的时间。参数 prev_rates_total 保留前一数值 - rates_total (在前一瞬时报价)。将其与当前的 rates_total (柱线数量或 price[] 数组大小) 比较, 我们可以看到数据已经重计算, 不仅柱线已更新, 重计算还可防止加载历史数据时因缺口而导致图表上显示不正确的情况。
//+------------------------------------------------------------------+ //| 自定义指标初始化函数 | //+------------------------------------------------------------------+ int OnInit() { 指标首次运行的日志消息 Print(__FUNCTION__+"\\ 初始化 | 周期: ",_period); //--- 当前品种小数点后位数 _digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS); //---指标显示精度 IndicatorSetInteger(INDICATOR_DIGITS,_digits); //--- 设置指标名称, 显示在指标窗口 IndicatorSetString(INDICATOR_SHORTNAME,"RopebyEricNaiman"); //--- INDICATOR_DATA - 保存绘图数据 //--- INDICATOR_COLOR_INDEX - 保存颜色索引 //--- INDICATOR_CALCULATIONS - 保存不参与绘图的中间计算数据 SetIndexBuffer(0,Buffer_main_bull,INDICATOR_DATA); // 多头直方图的数据缓存区 SetIndexBuffer(1,Buffer_color_bull,INDICATOR_COLOR_INDEX); // 多头颜色数据缓存区 SetIndexBuffer(2,Buffer_main_bear,INDICATOR_DATA); // 空头直方图的数据缓存区 SetIndexBuffer(3,Buffer_color_bear,INDICATOR_COLOR_INDEX); // 空头颜色数据缓存区 SetIndexBuffer(4,Buffer_mainline,INDICATOR_DATA); // 指标主线数据缓存区 SetIndexBuffer(5,Buffer_mainline_color,INDICATOR_COLOR_INDEX); // 指标主线颜色数据缓存区 SetIndexBuffer(6,Buffer_MAfast,INDICATOR_DATA); // 快速均线数据缓存区 SetIndexBuffer(7,Buffer_MAslow,INDICATOR_DATA); // 慢速均线数据缓存区 SetIndexBuffer(8,Buffer_calc,INDICATOR_CALCULATIONS); // 价格计算数据缓存区 SetIndexBuffer(9,Buffer_calc_bull,INDICATOR_CALCULATIONS); // 多头计算数据缓存区 SetIndexBuffer(10,Buffer_calc_bear,INDICATOR_CALCULATIONS); // 空头计算数据缓存区 SetIndexBuffer(11,Buffer_calcLINE,INDICATOR_CALCULATIONS); // 均线计算数据缓存区 //--- 最后计算过的柱线时间和 prev_rates_total (rates_total 前一调用 // rates_total = 数组大小 price[]) 清零 _tm_prev=0; prev_rates_total=0; //--- return(INIT_SUCCEEDED); }
在 OnCalculate 函数里的进一步计算可在图表上显示数据。考虑书中第 147 页表格的计算部分, 一个正面的变化是两个价格之间的差价是正结果, 而价格是柱线的收盘价 (当使用 price=PRICE_CLOSE)。这个差值可指出市场的看涨 (正面变化) 或空头 (负面变化)。可进一步认为构成 "柱线" 的开盘价等于前一根已计算柱线的收盘价, 以及计算的 bar (i) 收盘价由函数设置:
for(int i=1; i<bars_calc && !IsStopped(); i++) { ...
为了计算三个主要指标, 我们必须根据 _price 设置取价格数组, 并使用 for 操作符计算 _period 变量的值。它需要计算多头的正面价格变化, 空头的负面价格变化, 以及覆盖整个周期的变化数量, 加上查找它们的比率, 即用之合除以数量。
- if(total_bull>0) Buffer_calc_bull[i]=sum_bull/total_bull; — 对于多头。
- if(total_bear>0) Buffer_calc_bear[i]=sum_bear/total_bear; — 对于空头。
查看以下周期 _period=5 的多头, 空头和主线直方图的计算例程:
因此, 多空数值累计的结果, 一根 "绳索" 出现在每根柱线上, 并用于进一步计算。这个已实现, 在指标里通过指标颜色缓存区 - Buffer_mainline_color[] 将其呈现为三种显示颜色的线条。参看以下在 OnCalculate() 函数里的计算。
//+------------------------------------------------------------------+ //| 自定义指标迭代函数 | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, // price[] 数组大小 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[]) { ArraySetAsSeries(time,true); //--- 设置数组索引作为时间序列, 最低索引 0 是历史中最后已知计算值 ArraySetAsSeries(open,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(Buffer_main_bull,true); ArraySetAsSeries(Buffer_color_bull,true); ArraySetAsSeries(Buffer_main_bear,true); ArraySetAsSeries(Buffer_color_bear,true); ArraySetAsSeries(Buffer_mainline,true); ArraySetAsSeries(Buffer_mainline_color,true); ArraySetAsSeries(Buffer_MAfast,true); ArraySetAsSeries(Buffer_MAslow,true); ArraySetAsSeries(Buffer_calc,true); ArraySetAsSeries(Buffer_calc_bull,true); ArraySetAsSeries(Buffer_calc_bear,true); ArraySetAsSeries(Buffer_calcLINE,true); //--- int copy=0; bars_calc=0; //--- 计算指标数据 //--- 当 prev_calculated = 0 所有已计算柱线的所有数据需要再次计算 , =0 历史数据中有缺口则等于零, 例如, 当断网很长一段时间 if(prev_calculated==0) _tm_prev=0; //---如果以前的大小大于当前大小, 它表示失败且所有数据必须重计算 if(prev_rates_total>rates_total) _tm_prev=0; if(_tm_prev<time[0]) // 当新柱线出现时计算 { if(_tm_prev>0) // 如果指标已经有以前计算的数据 { copy=TakeShift_byTime(Symbol(),PERIOD_CURRENT,_tm_prev); // 当前品种柱线沿最后计算的柱线时间移位 if(copy<=0) // 如果移位未发现, 则计算偏离并在下一次瞬时报价重复 { return 0; } bars_calc=copy+1; // 设置自当前指标未收盘柱线开始计算 //--- 首根未收盘柱线清零, 即, 其数据未计算 Buffer_main_bull[0]=0.0; Buffer_color_bull[0]=5; // 设置颜色索引, 号码 5 对应空色, 所以不会绘制颜色 Buffer_main_bear[0]=0.0; Buffer_color_bear[0]=5; // 设置颜色索引, 号码 5 对应空色, 所以不会绘制颜色 Buffer_mainline[0]=0.0; Buffer_mainline_color[0]=5; // 设置颜色索引, 号码 5 对应空色, 所以不会绘制颜色 Buffer_MAfast[0]=0.0; Buffer_MAslow[0]=0.0; Buffer_calc[0]=0.0; Buffer_calc_bull[0]=0.0; Buffer_calc_bear[0]=0.0; Buffer_calcLINE[0]=0.0; } else // 如果指标首次设置且数值尚未计算 { bars_calc=Bars(Symbol(),PERIOD_CURRENT)-1; // 柱线移位到总柱线数 -1 开始计算 //--- 所有柱线的所有数据清零以避免绘图错误 for(int i=0; i<Bars(Symbol(),PERIOD_CURRENT) && !IsStopped(); i++) { Buffer_main_bull[i]=0.0; Buffer_main_bear[i]=0.0; Buffer_mainline[i]=0.0; Buffer_MAfast[i]=0.0; Buffer_MAslow[i]=0.0; Buffer_calc[i]=0.0; Buffer_calc_bull[i]=0.0; Buffer_calc_bear[i]=0.0; Buffer_calcLINE[i]=0.0; } } //--- 如果用于指标计算的柱线数量低于零, 则计算将被放弃 if(bars_calc<0) return 0; //--- 限制计算 max_bars 值的柱线数量 if(bars_calc>max_bars && max_bars!=0) bars_calc=max_bars; for(int i=1; i<bars_calc && !IsStopped(); i++) { //--- 根据 _price 设置, 计算两根柱线报价间的差值 switch(_price) { case PRICE_CLOSE: Buffer_calc[i]=close[i]-close[i+1]; break; case PRICE_OPEN: Buffer_calc[i]=open[i]-open[i+1]; break; case PRICE_HIGH: Buffer_calc[i]=high[i]-high[i+1]; break; case PRICE_LOW: Buffer_calc[i]=low[i]-low[i+1]; break; case PRICE_MEDIAN: Buffer_calc[i]=((high[i]+low[i])/2)-((high[i+1]+low[i+1])/2); break; case PRICE_TYPICAL: Buffer_calc[i]=((high[i]+low[i]+close[i])/3)-((high[i+1]+low[i+1]+close[i+1])/3); break; case PRICE_WEIGHTED: Buffer_calc[i]=((high[i]+low[i]+close[i]+close[i])/4)-((high[i+1]+low[i+1]+close[i+1]+close[i+1])/4); break; default: return 0; } } //--- 运行未计算柱线迭代直到循环结束, 且 IsStopped=true 命令程序结束 (当指标从图表上删除的情况) 没有出现 for(int i=1; i<=bars_calc && !IsStopped(); i++) { //--- 清空数据 sum_bull=0.0; // 覆盖分析周期的正面变化 (对于多头) 累加变量 total_bull=0; // 覆盖分析周期的正面变化数量累加变量 sum_bear=0.0; // 覆盖分析周期的负面变化 (对于空头) 累加变量 total_bear=0; // 覆盖分析周期的负面变化累加变量 Buffer_main_bull[i]=0.0; Buffer_color_bull[i]=5; Buffer_main_bear[i]=0.0; Buffer_color_bear[i]=5; Buffer_mainline[i]=0.0; Buffer_mainline_color[0]=5; Buffer_calc_bull[i]=0.0; Buffer_calc_bear[i]=0.0; Buffer_calcLINE[i]=0.0; //--- 根据分析周期, 如果低于可用数据则停止柱线计算 if(i>=(rates_total-_period)) continue; //--- 从历史数据中的当前柱线, 覆盖分析周期的数据迭代 (_period) for(int i2=i; i2<i+_period; i2++) { //--- 计算多头 if(Buffer_calc[i2]>0) { sum_bull+=Buffer_calc[i2]; // 累计阳线价格 ("覆盖分析周期时间的正面变化累加") total_bull++; // 计算阳线数量 ("盖分析周期时间的正面变化数量") } //--- 计算空头 if(Buffer_calc[i2]<0) { sum_bear+=Buffer_calc[i2]; // 累计阴线价格 ("覆盖分析周期时间的负面变化累加") total_bear++; // 计算阴线数量 ("盖分析周期时间的负面变化数量") } } //--- 计算 "多头的技巧", 用柱线的点数值除以柱线数量 if(total_bull>0) Buffer_calc_bull[i]=sum_bull/total_bull; //--- 计算 "空头的技巧" if(total_bear>0) Buffer_calc_bear[i]=sum_bear/total_bear; //--- 绘制直方图并按照信号确认级别填充颜色 if(draw_histogram) { //--- 绘制多头的直方图, 用柱线的点数值除以柱线数量 if(total_bull>0) Buffer_main_bull[i]=sum_bull/total_bull; //--- 绘制空头的直方图, 用柱线的点数值除以柱线数量 if(total_bear>0) Buffer_main_bear[i]=sum_bear/total_bear; //--- 0 直方图颜色 clrDarkGreen - 颜色索引 "仅买入", 买单具有高级别确认 //--- 确认级别 - 所有指标示意多头趋势 //--- 多头活力 > 空头活力 且 多头力度 > 空头力度 且 多头技巧 > 空头技巧 //--- 活力 - 柱线数量 //--- 力度 - 累计柱线点数大小 //--- 技巧 - 覆盖指示周期的所有柱线的力度/活力计算平均值 if(total_bull>total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i])) Buffer_color_bull[i]=0; else { //--- 直方图颜色 clrMediumSeaGreen - 颜色索引 "买单允许", 买单具有平均级别确认 //--- 确认级别 - 任意两个多头指标和一个空头指标的组合 if((total_bull>total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i])) || (total_bull>total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i])) || (total_bull<total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i]))) Buffer_color_bull[i]=1; else { //--- 直方图颜色 clrLightGreen - 颜色索引 "买单允许", 买单具有低级别确认 //--- 确认级别 - 任意一个多头指标和两个空头指标的组合 if((total_bull>total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i])) || (total_bull<total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i])) || (total_bull<total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i]))) Buffer_color_bull[i]=2; else { //--- 直方图颜色 clrGray - 颜色索引 "买单禁止" //--- 确认级别 - 当多头指标缺失 Buffer_color_bull[i]=3; } } } //--- 直方图颜色 clrDarkRed - 颜色索引 "仅卖出", 卖单具有高级别确认 //--- 确认级别 - 所有指标示意多头趋势 if(total_bull<total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i])) Buffer_color_bear[i]=0; else { //--- 直方图颜色 clrIndianRed - 颜色索引 "卖单允许", 卖单具有平均级别确认 //--- 确认级别 - 任意两个空头指标和一个多头指标的组合 if((total_bull<total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i])) || (total_bull<total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i])) || (total_bull>total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i]))) Buffer_color_bear[i]=1; else { //--- 直方图颜色 clrLightPink - 颜色索引 "卖单允许", 卖单具有低级别确认 //--- 确认级别 - 任意一个多头指标和两个空头指标的组合 if((total_bull<total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i])) || (total_bull>total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])>MathAbs(Buffer_main_bear[i])) || (total_bull>total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_main_bull[i])<MathAbs(Buffer_main_bear[i]))) Buffer_color_bear[i]=2; else { //--- 直方图颜色 clrGray - 颜色索引 "卖单禁止" //--- 确认级别 - 当空头指标缺失 Buffer_color_bear[i]=3; } } } } //--- 计算主线值, 累计空头和多头的技巧 Buffer_calcLINE[i]=(Buffer_calc_bull[i]+Buffer_calc_bear[i])*line_deviation; //--- 通过信号确认级别绘制填充颜色的线条 // 0 - clrDarkGreen - 颜色索引 "仅买入", 买单具有高级别确认 // 1 - clrDarkRed - 颜色索引 "仅卖出", 卖单具有高级别确认 // 2 - clrGray - 颜色索引 "无限制确认" if(draw_line) { //--- 绘制主线值, 累计空头和多头的技巧 Buffer_mainline[i]=(Buffer_calc_bull[i]+Buffer_calc_bear[i])*line_deviation; //--- 设置不确定线条颜色 (2 - clrGray) Buffer_mainline_color[i]=2; //--- 设置多头线条颜色 (0 - clrDarkGreen) 当所有三个多头指标匹配 (多头 > 空头) if(total_bull>total_bear && MathAbs(sum_bull)>MathAbs(sum_bear) && MathAbs(Buffer_calc_bull[i])>MathAbs(Buffer_calc_bear[i])) Buffer_mainline_color[i]=0; //--- 设置多头线条颜色 (1 - clrDarkRed) 当所有三个空头指标匹配 (空头 > 多头) if(total_bull<total_bear && MathAbs(sum_bull)<MathAbs(sum_bear) && MathAbs(Buffer_calc_bull[i])<MathAbs(Buffer_calc_bear[i])) Buffer_mainline_color[i]=1; } } //--- 计算并绘制均线 if(draw_MA_fast || draw_MA_slow) { //--- 禁用索引作为时间序列来转换库 MovingAverages.mqh (索引改到数组结尾的当前历史柱线) ArraySetAsSeries(Buffer_calcLINE,false); ArraySetAsSeries(Buffer_MAfast,false); ArraySetAsSeries(Buffer_MAslow,false); //--- 迭代所有未计算柱线。迭代 = 所有柱线 (rates_total) - 柱线从最后已知计算过的时间 _tm_prev (bars_calc) 移位 for(int i=rates_total-bars_calc; i<rates_total && !IsStopped(); i++) { //--- 在参数 max_bars 里设置计算限制, 计算仅用设置在 max_bars 的数量作为所有柱线 (rates_total) 减设置数量 (max_bars) 的总差值。 if(max_bars>0?(i<(rates_total-max_bars)):false) { //--- 如果当前索引 i 低于许可数值, 我们继续允许索引 i=rates_total-max_bars; continue; } //--- 在未收到均线数据的情况下清空数组 Buffer_MAfast[i]=0.0; Buffer_MAslow[i]=0.0; //--- 基于 method_MA_fast 参数选择快速均线计算方法 if(draw_MA_fast) { switch(method_MA_fast) { case MODE_SMA: // 简单平均 Buffer_MAfast[i]=SimpleMA(i,period_MA_fast,Buffer_calcLINE); // 从库中 Include\\MovingAverages.mqh 获取已计算的 SimpleMA i 索引数据, 基于 Buffer_calcLINE 数据缓存区和 period_MA_fast 周期 break; case MODE_EMA: // 指数平均 Buffer_MAfast[i]=ExponentialMA(i,period_MA_fast,Buffer_MAfast[i-1],Buffer_calcLINE); break; case MODE_SMMA: // 平滑平均 Buffer_MAfast[i]=SmoothedMA(i+period_MA_fast,period_MA_fast,Buffer_MAfast[i-1],Buffer_calcLINE); break; case MODE_LWMA: // 线性加权平均 Buffer_MAfast[i]=LinearWeightedMA(i+period_MA_fast,period_MA_fast,Buffer_calcLINE); break; default: return 0; } } //--- 基于 method_MA_slow 参数选择慢速均线计算方法 if(draw_MA_slow) { switch(method_MA_slow) { case MODE_SMA: // 简单平均 Buffer_MAslow[i]=SimpleMA(i,period_MA_slow,Buffer_calcLINE); break; case MODE_EMA: // 指数平均 Buffer_MAslow[i]=ExponentialMA(i,period_MA_slow,Buffer_MAslow[i-1],Buffer_calcLINE); break; case MODE_SMMA: // 平滑平均 Buffer_MAslow[i]=SmoothedMA(i,period_MA_slow,Buffer_MAslow[i-1],Buffer_calcLINE); break; case MODE_LWMA: // 线性加权平均 Buffer_MAslow[i]=LinearWeightedMA(i,period_MA_slow,Buffer_calcLINE); break; default: return 0; } } } //--- 为绘图, 启用索引作为时间序列 ArraySetAsSeries(Buffer_calcLINE,true); ArraySetAsSeries(Buffer_MAfast,true); ArraySetAsSeries(Buffer_MAslow,true); //--- 当前柱线的均线清零, 即, 未计算 Buffer_MAfast[0]=EMPTY_VALUE; Buffer_MAslow[0]=EMPTY_VALUE; Buffer_calcLINE[0]=EMPTY_VALUE; } //--- 记住最后已计算的柱线时间 _tm_prev=time[0]; } //--- 返回 prev_calculated 值用于下次调用 prev_rates_total=rates_total; return(rates_total); }
当显示线条和直方图时, 已增加了主线值 (主线值 * line_deviation) 以便于低于直方图时的观察。计算柱线数量的限制 (max_bars) 已加入, 提升小周期的计算速度, 例如 М1。最后的 TakeShift_byTime 模块代码用于获取柱线偏离时间, 目的是查找 bars_calc 中所需计算的柱线数量。
第二章. "绳索" 指标的实际实现并创建智能交易程序。
在本章里, 我们将会着眼于创建基于 "绳索" 指标 (指标的源代码名称 — RopebyEricNaiman) 的智能交易程序 EARopebyEricNaiman。基于 EARopebyEricNaiman 智能交易程序例程的指标可作为单独的策略, 并作为过滤器来寻找趋势方向。在本书中, 作者研究了若干种使用此指标计算数据的方法。以下是从它们当中选出的用于智能交易程序:
- 主线与零轴交汇 (主线穿越零轴)。高于交汇处 — 买入交易, 低于交汇处 — 卖出交易。
- 当主线颜色从灰色变为绿色 (买入), 或从灰色变为红色 (卖出) 时开单。
- 当主线与快速均线交汇 (主线穿越 MAfast) 开单。主线上穿均线 — 买单, 下穿 — 卖单。
当两条均线交叉开单。快速均线上穿慢速均线 - 买入, 下穿 — 卖出。
除了主要设置, 智能交易程序提供了一个机会来设置分别对应于开单信号的 4 种平仓类型, 但与开仓的类型方向相反:
//--- 平仓设置 input string section_5="___ 平仓设置"; //--- 当主线下穿零轴时买单平仓 input bool close1=true; // 主线与零轴交叉平仓 //--- 当颜色转为与开仓颜色相反时平仓, 当变为红色时买单平仓 input bool close2=false; // 平仓主线颜色 //--- 在主线与均线交汇时平仓, 当主线下穿均线时买单平仓 input bool close3=false; // 主线与 MAfast 交叉时平仓 //--- 两条均线交汇时平仓, 快速均线下穿慢速均线时买单平仓 input bool close4=true; // 两条均线交叉时平仓
所以, 开单基于 trade_mode 变量里所选的方法之一, 而平仓通过以上的止损/止盈方法之一执行。完整的智能程序和指标源代码可从附带的文件里下载并查阅。在本文中, 我们只分析开仓和平仓信号的主要部分。
- 主要变量和 RopebyEricNaiman 指标将会在 OnInit() 模块里初始化。
- OnTick() 模块包括开仓和平仓的管理模块。
- 信号计算模块 Check_Indi_Open() 用于加载指标数据并搜索开仓信号:
//+------------------------------------------------------------------+ //| Check_Indi_Open | //+------------------------------------------------------------------+ int Check_Indi_Open() { //--- 声明获取指标数据的数组 int copy=0; double _arr_ind_1[]; double mafast[]; double mainline[]; double maslow[]; //--- 使用 switch 函数并基于外部变量参数 trade_mode 来选择开仓方法 switch((int)trade_mode) { case 0: //--- 指标的主线在最后两根柱线上进行比较, 排除当前那根 copy=Copy_indi_Buffer(RbEN_handle,4,1,2,_arr_ind_1,"RbEN"); if(copy!=2) return 4; if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]>0 && _arr_ind_1[1]<=0) { return 0; } if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]<0 && _arr_ind_1[1]>=0) { return 1; } break; case 1: //--- 从指标的第 5 个缓存区获取指标最后两根柱线的颜色索引, 排除当前那根 copy=Copy_indi_Buffer(RbEN_handle,5,1,2,_arr_ind_1,"RbEN"); if(copy!=2) return 5; if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]==0 && _arr_ind_1[1]!=0) { return 0; } if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]==1 && _arr_ind_1[1]!=1) { return 1; } break; case 2: //--- 接收并比较最后两根柱线的 mafast (指标主线的均线) 和 mainline (指标主线) 的数值, 排除当前那根。 copy=Copy_indi_Buffer(RbEN_handle,6,1,2,mafast,"RbEN"); if(copy!=2) return 6; copy=Copy_indi_Buffer(RbEN_handle,4,1,2,mainline,"RbEN"); if(copy!=2) return 7; if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && mainline[0]!=EMPTY_VALUE && mainline[1]!=EMPTY_VALUE && mainline[0]>mafast[0] && mainline[1]<=mafast[1]) { return 0; } if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && mainline[0]!=EMPTY_VALUE && mainline[1]!=EMPTY_VALUE && mainline[0]<mafast[0] && mainline[1]>=mafast[1]) { return 1; } break; case 3: //--- 接收并比较最后两根柱线的 mafast (指标主线的快速均线) 和 maslow (指标主线的快速均线) 的数值, 排除当前那根。 copy=Copy_indi_Buffer(RbEN_handle,6,1,2,mafast,"RbEN"); if(copy!=2) return 8; copy=Copy_indi_Buffer(RbEN_handle,7,1,2,maslow,"RbEN"); if(copy!=2) return 9; if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && maslow[0]!=EMPTY_VALUE && maslow[1]!=EMPTY_VALUE && maslow[0]<mafast[0] && maslow[1]>=mafast[1]) { return 0; } if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && maslow[0]!=EMPTY_VALUE && maslow[1]!=EMPTY_VALUE && maslow[0]>mafast[0] && maslow[1]<=mafast[1]) { return 1; } break; default: return 3; } //--- return 2; }
在 Check_Indi_Close() 模块里根据所有四种平仓方法的输入设置执行迭代:
//+------------------------------------------------------------------+ //| Check_Indi_Close | //+------------------------------------------------------------------+ int Check_Indi_Close() { //--- 声明获取指标数据的数组 int copy=0; double _arr_ind_1[]; double mafast[]; double mainline[]; double maslow[]; _str_close=""; //--- 接收并比较最后两根柱线的零轴和主线数值, 排除当前那根。 if(close1) { copy=Copy_indi_Buffer(RbEN_handle,4,1,2,_arr_ind_1,"RbEN"); if(copy!=2) return 4; _str_close="主线与零轴交叉平仓"; if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]>0 && _arr_ind_1[1]<=0) { return 0; } if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]<0 && _arr_ind_1[1]>=0) { return 1; } } //--- 从指标的第 5 个缓存区获取指标最后两根柱线的颜色索引, 排除当前那根 if(close2) { copy=Copy_indi_Buffer(RbEN_handle,5,1,2,_arr_ind_1,"RbEN"); if(copy!=2) return 5; _str_close="主线平仓颜色"; if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]==0 && _arr_ind_1[1]!=0) { return 0; } if(_arr_ind_1[0]!=EMPTY_VALUE && _arr_ind_1[1]!=EMPTY_VALUE && _arr_ind_1[0]==1 && _arr_ind_1[1]!=1) { return 1; } } //--- 接收并比较最后两根柱线的 mafast (指标主线的均线) 和 mainline (指标主线) 的数值, 排除当前那根。 if(close3) { copy=Copy_indi_Buffer(RbEN_handle,6,1,2,mafast,"RbEN"); if(copy!=2) return 6; copy=Copy_indi_Buffer(RbEN_handle,4,1,2,mainline,"RbEN"); if(copy!=2) return 7; _str_close="主线与 MAfast 交叉平仓"; if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && mainline[0]!=EMPTY_VALUE && mainline[1]!=EMPTY_VALUE && mainline[0]>mafast[0] && mainline[1]<=mafast[1]) { return 0; } if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && mainline[0]!=EMPTY_VALUE && mainline[1]!=EMPTY_VALUE && mainline[0]<mafast[0] && mainline[1]>=mafast[1]) { return 1; } } //--- 接收并比较最后两根柱线的 mafast (指标主线的快速均线) 和 maslow (指标主线的快速均线) 的数值, 排除当前那根。 if(close4) { copy=Copy_indi_Buffer(RbEN_handle,6,1,2,mafast,"RbEN"); if(copy!=2) return 8; copy=Copy_indi_Buffer(RbEN_handle,7,1,2,maslow,"RbEN"); if(copy!=2) return 9; _str_close="两条均线交叉平仓"; if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && maslow[0]!=EMPTY_VALUE && maslow[1]!=EMPTY_VALUE && maslow[0]<mafast[0] && maslow[1]>=mafast[1]) { return 0; } if(mafast[0]!=EMPTY_VALUE && mafast[1]!=EMPTY_VALUE && maslow[0]!=EMPTY_VALUE && maslow[1]!=EMPTY_VALUE && maslow[0]>mafast[0] && maslow[1]<=mafast[1]) { return 1; } } //--- return 2; }
- 创建 Copy_indi_Buffer() 模块使用 CopyBuffer() 函数来接收指标数据。
- TakeLastOpenTime() 模块用来接收最后开仓时间, 并与 (在 OnTick 里) 柱线开盘时间比较以避免在一根柱线上多次开仓。
- OpenPosition() — 开仓函数。
- Copy_Time() 拷贝指定的柱线时间。
- CloseAllPosition() 所有持仓平仓。
在创建智能交易程序之后, 我们继续在下一章中讲述输入参数的优化。
第三章. 优化智能交易程序的输入参数
运行智能交易程序之前, 我们应该优化主要的输入参数。在例中选择了品种 EURUSD H1 2005-2015 (遗传优化算法)。单独执行所有四种开仓类型的优化。
1. trade_mode = 主线与零轴交叉平仓 (主线与零轴交汇: 上穿 — 买入, 下穿 — 卖出)。
优化参数:
数值 开始 步进 停止 步数 StopLoss 0 50 10000 201 TakeProfit 0 50 10000 201 _period 1 1 200 200 close1 false true 2 总计通过 16160400
优化及最佳测试结果可在名为 optimization.zip 的附带档案中的文件夹 "EURUSD H1 2005-2015\test1 - main line cross zero\" 里查阅。
2. trade_mode = 主线颜色平仓 (当主线颜色从灰色变为绿色 (买入), 或从灰色变为红色 (卖出))。
优化参数:
数值 开始 步进 停止 步数 StopLoss 0 50 10000 201 TakeProfit 0 50 10000 201 _period 1 1 200 200 close2 false true 2 总计通过 16160400
优化及最佳测试结果可在名为 optimization.zip 的附带档案中的文件夹 "EURUSD H1 2005-2015\test2 - main line color\" 里查阅。
3. trade_mode = 主线与 MAfast 交叉 (主线与 fast МА 交汇开仓: 主线上穿 MA — 买入, 下穿 — 卖出)。
优化参数:
数值 开始 步进 停止 步数 StopLoss 0 50 10000 201 TakeProfit 0 50 10000 201 _period 1 1 200 200 period_MA_fast 1 1 300 300 close3 false true 2 总计通过 4848120000 优化及最佳测试结果可在名为 optimization.zip 的附带档案中的文件夹 "EURUSD H1 2005-2015\test3 - main line cross MAfast\" 里查阅。
4. trade_mode = 两条均线交叉 (两条均线交汇时开仓: 快速均线上穿慢速均线 — 买单, 下穿 — 卖单)。
优化参数:
数值 开始 步进 停止 步数 StopLoss 0 50 10000 201 TakeProfit 0 50 10000 201 _period 1 1 200 200 period_MA_fast 1 1 300 300 period_MA_slow 1 1 400 400 close4 false true 2 总计通过 1939248000000 优化及最佳测试结果可在名为 optimization.zip 的附带档案中的文件夹 "EURUSD H1 2005-2015\test4 - two MA cross\" 里查阅。
结论
优化结果证明了该策略的可行性, 考虑到智能交易程序是基于指标如何工作的纯逻辑构建, 而未使用任何附加的资金管理功能。纯逻辑是说, 我们意指作者发现趋势的策略是基于在指定周期时间内静态评估多头和空头的活力。借助这个指标, 我们定义核心入场点, 因此该指标用于策略过滤器, 以及作为开仓交易的基础信号均可。
查阅附件文件, 找到指标和智能交易程序的源代码, 它们可在编程和交易领域用于个人开发。此策略已经实施, 且已看到在小范围使用 (入场/离场的基本信号), 在初始阶段已展现出它的工作能力。