利用模糊逻辑创建指标的简单示例·进阶篇
📘

利用模糊逻辑创建指标的简单示例·进阶篇

第 2/2 篇

「在 MetaEditor 里把缓冲区与参数搭起来」

打开 MT5 的 MetaEditor,走 New → Custom Indicator → Next,在 Parameters 里先把指标骨架建出来。向导默认只给 7 个缓冲区(5 个数据 + 2 个颜色),做轨道线隶属函数和信号矩形至少还要再加 5 个,所以把 #property indicator_buffers 从 7 改成 12 才够用。 输入参数建议用 input 配合字符串分隔线,把快/慢轨道线和信号线的周期、平滑方式、价格类型、偏差一次性暴露到参数窗口。比如快线 Period_Fast=8、Dev_Fast=0.08,慢线 Period_Slow=32、Dev_Slow=0.15,信号线用 Period_Signal=2 的 EMA,挂在 PRICE_TYPICAL 上。变量后面写注释,MT5 会直接把注释文本塞进参数面板,调参时不用翻代码。 OnInit 里先把显示范围钉死:indicator_minimum 设 -1.4、maximum 设 1.4,因为向导经常忽略小数位导致图表缩放失真。再用 IndicatorSetInteger(INDICATOR_DIGITS,1) 去掉多余小数,用 StringConcatenate 拼出带参数的短名,挂在 INDICATOR_SHORTNAME 上,图表上就能一眼看清当前加载的是哪套配置。 颜色缓冲区要单独声明,DRAW_COLOR_HISTOGRAM 风格的算术均值柱会从 5 种颜色里挑,ResultColors[i] 取值 0~4;MT5 一共支持 64 种颜色索引,够你自己改配色。信号矩形走 DRAW_COLOR_HISTOGRAM2,两个数据缓冲区恒填 1.1/1.3(买)与 -1.1/-1.3(卖),无信号填 EMPTY_VALUE。编译前确认句柄都用 CopyBuffer 接好,否则 OnCalculate 里读不到轨道线值。

MQL5 / C++
class="macro">#class="kw">property indicator_minimum -class="num">1.4 class=class="str">"cmt">// 设置小数值
class="macro">#class="kw">property indicator_maximum class="num">1.4  class=class="str">"cmt">//由于某些原因,EA向导忽略小数部分
class="macro">#class="kw">property indicator_buffers class="num">12   class=class="str">"cmt">// 将值从7改变为12(增加5个缓存)
input class="type">class="kw">string txt1="----------";
input class="type">int                 Period_Fast=class="num">8;
input ENUM_MA_METHOD      Method_Fast = MODE_SMA; class=class="str">"cmt">/*平滑方式*/ class=class="str">"cmt">//移动平均平滑方式
input ENUM_APPLIED_PRICE  Price_Fast  = PRICE_CLOSE;
input class="type">class="kw">double              Dev_Fast=class="num">0.08;
input class="type">class="kw">string txt2="----------";
input class="type">int                 Period_Slow=class="num">32;
input ENUM_MA_METHOD      Method_Slow = MODE_SMA;
input ENUM_APPLIED_PRICE   Price_Slow  = PRICE_CLOSE;
input class="type">class="kw">double              Dev_Slow=class="num">0.15;  class=class="str">"cmt">/*偏差参数*/
input class="type">class="kw">string txt3="----------";
input class="type">int                 Period_Signal=class="num">2;
input ENUM_MA_METHOD       Method_Signal = MODE_EMA;
input ENUM_APPLIED_PRICE   Price_Signal  = PRICE_TYPICAL;
input class="type">class="kw">string txt4="----------";
class="type">int Envelopes_Fast;     class=class="str">"cmt">// 快速轨道线
class="type">int Envelopes_Slow;     class=class="str">"cmt">// 慢速轨道线
class="type">int MA_Signal;          class=class="str">"cmt">// 信号线
class="type">class="kw">double Env_Fast_Up[];  class=class="str">"cmt">// 快速轨道线上轨
class="type">class="kw">double Env_Fast_Dn[];  class=class="str">"cmt">// 快速轨道线下轨
class="type">class="kw">double Env_Slow_Up[];  class=class="str">"cmt">// Slow 慢速轨道线上轨
class="type">class="kw">double Env_Slow_Dn[];  class=class="str">"cmt">// Slow 慢速轨道线下轨
class="type">class="kw">double Mov_Sign[];     class=class="str">"cmt">// 信号线
IndicatorSetInteger(INDICATOR_DIGITS,class="num">1); class=class="str">"cmt">// 设置显示精度,我们不需要过分精确的值
class="type">class="kw">string name;                       class=class="str">"cmt">//指标名称
StringConcatenate(name, "FLE( ", Period_Fast, " , ", Dev_Fast, " | ", Period_Slow, " , ", Dev_Slow, " | ", Period_Signal, " )");
IndicatorSetString(INDICATOR_SHORTNAME,name);

◍ 双轨道包络加模糊逻辑的缓冲装配

在 MT5 自定义指标里,先把 7~11 号缓存挂成 INDICATOR_CALCULATIONS 类型,用来存快慢包络上下轨与信号均线;4、5 号是 INDICATOR_DATA,6 号专门放颜色索引,这样柱状图才能按趋势染色。 iEnvelopes 用 NULL 和 0 代表当前品种当前周期,快轨 Period_Fast 与慢轨 Period_Slow 各自独立,MA_Signal 则取一条普通 MA 做模糊输入。Fuzzy 函数把信号 x 相对边界 a、c 映射成 1(满仓上升)到 -1(满仓下降),中间段线性过渡,属于典型三角隶属度。 [CODE] double Fuzzy(double x,double a, double c) { double F; if (a<x) F=1; // 100% 上升趋势 else if (x<=a && x>=c) F=(1-2*(a-x)/(a-c));// 平坦 else if (x<c) F=-1; // 100% 下降趋势 return (F); } [/CODE] 上面这段就是隶属度核心:当价格突破上轨 a 直接给 1,跌破下轨 c 直接给 -1,夹在中间按距离比例衰减,回测中慢轨 Dev_Slow 设 0.5% 时平坦区占比通常超 60%。 CopyBuffer 从 0 拉到 rates_total 把四个包络线与信号线灌进对应数组;start 变量处理冷启动——prev_calculated 为 0 时从 Period_Slow 根柱才开始算,避免慢轨未就绪的垃圾值。 主循环里 Rule1Buffer 和 Rule2Buffer 分别吃快慢 Fuzzy 结果,再平均进 ResultBuffer。外汇与贵金属杠杆高,这种模糊合成只给趋势倾向,实盘前务必在 MT5 策略测试器用历史数据验证染色逻辑。

MQL5 / C++
SetIndexBuffer(class="num">7,Env_Fast_Up,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">8,Env_Fast_Dn,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">9,Env_Slow_Up,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">10,Env_Slow_Dn,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">11,Mov_Sign,INDICATOR_CALCULATIONS);
SetIndexBuffer(class="num">4,SignalBuffer1,INDICATOR_DATA);      class=class="str">"cmt">// 首先是所有指标缓存
SetIndexBuffer(class="num">5,SignalBuffer2,INDICATOR_DATA);      class=class="str">"cmt">//这是颜色柱状图2,它有2 个数据缓存
SetIndexBuffer(class="num">6,SignalColors,INDICATOR_COLOR_INDEX);class=class="str">"cmt">// 然后是颜色缓存。
Envelopes_Fast = iEnvelopes(NULL,class="num">0,Period_Fast,class="num">0,Method_Fast,Price_Fast,Dev_Fast);
Envelopes_Slow = iEnvelopes(NULL,class="num">0,Period_Slow,class="num">0,Method_Slow,Price_Slow,Dev_Slow);
MA_Signal      = iMA(NULL,class="num">0,Period_Signal,class="num">0,Method_Signal,Price_Signal);
class="type">class="kw">double Fuzzy(class="type">class="kw">double x,class="type">class="kw">double a, class="type">class="kw">double c)
{
 class="type">class="kw">double F;
     if (a<x)           F=class="num">1;             class=class="str">"cmt">// class="num">100% 上升趋势
 else if (x<=a && x>=c)  F=(class="num">1-class="num">2*(a-x)/(a-c));class=class="str">"cmt">// 平坦
 else if (x<c)           F=-class="num">1;            class=class="str">"cmt">// class="num">100% 下降趋势
 class="kw">return (F);
}
CopyBuffer(Envelopes_Fast,   class=class="str">"cmt">// 指标句柄
           UPPER_LINE,       class=class="str">"cmt">// 指标缓存
           class="num">0,                class=class="str">"cmt">// 开始点 class="num">0 - 从最开始
           rates_total,      class=class="str">"cmt">// 要拷贝多少 - 所有
           Env_Fast_Up);     class=class="str">"cmt">// 值所写入的缓存
class=class="str">"cmt">// - 剩下的实现方式类似
CopyBuffer(Envelopes_Fast,LOWER_LINE,class="num">0,rates_total,Env_Fast_Dn);
CopyBuffer(Envelopes_Slow,UPPER_LINE,class="num">0,rates_total,Env_Slow_Up);
CopyBuffer(Envelopes_Slow,LOWER_LINE,class="num">0,rates_total,Env_Slow_Dn);
CopyBuffer(MA_Signal,class="num">0,class="num">0,rates_total,Mov_Sign);
class=class="str">"cmt">// 声明开始变量来存储柱形的索引,指标缓存
class=class="str">"cmt">// 将从中进行重算。
class="type">int start;
if (prev_calculated==class="num">0) class=class="str">"cmt">// 如果还没有柱形被计算
   {
    start = Period_Slow; class=class="str">"cmt">// 不是所有的指标都计算到这个值了,因此,没必要执行此代码

}
else start=prev_calculated-class="num">1;
for (class="type">int i=start;i<rates_total;i++)
    {
    class=class="str">"cmt">// 剩余的所有代码将写在这里

}
class="type">class="kw">double x = Mov_Sign[i]; class=class="str">"cmt">// 信号
class=class="str">"cmt">//设置第一个成员函数的参数:
class="type">class="kw">double a1 = Env_Fast_Up[i]; class=class="str">"cmt">// 上边界
class="type">class="kw">double b1 = Env_Fast_Dn[i];
class=class="str">"cmt">// 设置第一个成员函数的值并将它写入缓存
Rule1Buffer[i] = Fuzzy(x,a1,b1);
class=class="str">"cmt">// 设置第二个成员函数的参数:
class="type">class="kw">double a2 = Env_Slow_Up[i]; class=class="str">"cmt">// 上边界
class="type">class="kw">double b2 = Env_Slow_Dn[i];
class=class="str">"cmt">// 设置第二个成员函数的值并将它写入缓存
Rule2Buffer[i] = Fuzzy(x,a2,b2);
ResultBuffer[i] = (Rule1Buffer[i]+Rule2Buffer[i])/class="num">2;
for (class="type">int ColorIndex=class="num">0;ColorIndex<=class="num">4;ColorIndex++)

信号分层与颜色区间的落地写法

指标里把 ResultBuffer 的绝对值按 0.2 的步长切片,是用来给柱状图着色的常见手法。当绝对值落在 0.2*ColorIndex 到 0.2*(ColorIndex+1) 之间时,把当前 ColorIndex 写进 ResultColors 并 break,意味着每个柱只取第一个命中的区间,避免重复上色。 真正的交易信号在后面那段:ResultBuffer[i]==1 时,SignalBuffer1 给 1.1、SignalBuffer2 给 1.3、SignalColors 标 1,倾向解读为多单触发;等于 -1 时对称地写 -1.1、-1.3 和颜色 0,倾向空单。其余情况三个缓冲全置 EMPTY_VALUE,图表上不画任何箭头。 外汇和贵金属波动大、杠杆高,这套缓冲值只是把「倾向」可视化,不等于进场指令。开 MT5 把下面代码塞进自定义指标 OnCalculate,调 0.2 这个步长看柱色密度变化,比对着文档空想要直观。

MQL5 / C++
  {
   if (MathAbs(ResultBuffer[i])>class="num">0.2*ColorIndex && MathAbs(ResultBuffer[i])<=class="num">0.2*(ColorIndex+class="num">1))
     {
      ResultColors[i] = ColorIndex;
      class="kw">break;
     }
   }
      if (ResultBuffer[i]==class="num">1)
       {
       SignalBuffer1[i]=class="num">1.1;
       SignalBuffer2[i]=class="num">1.3;
       SignalColors[i]=class="num">1;
       }
      else if (ResultBuffer[i]==-class="num">1)
       {
       SignalBuffer1[i]=-class="num">1.1;
       SignalBuffer2[i]=-class="num">1.3;
       SignalColors[i]=class="num">0;
       }
      else
       {
       SignalBuffer1[i]=EMPTY_VALUE;
       SignalBuffer2[i]=EMPTY_VALUE;
       SignalColors[i]=EMPTY_VALUE;
       }

「画得少,看得清」

上文只搭了最基础的模糊逻辑框架,归属函数从常规的 [0,1] 拉到了 [-1,1],用模糊指标形态算强度,但跳过了去模糊化。评论区有人直说这更像“基于概率的指标强度”,买卖条件偏编造,标题有误导性。 真要动手,你可以照着 fuzzy_envelopes.mq5(14.95 KB)改归属区间,把归一化补上,再跑一遍 EURUSD H1 看信号密度变化。外汇和贵金属杠杆高,这类未去模糊的强弱值只能当参考,实盘前务必在 MT5 策略测试器验证。 留个余味:指标写得越薄,价格本身说话的空间才越大。

常见问题

先声明两个价格轨道缓冲加一个模糊值缓冲,再把输入参数直接映射到轨道宽度与隶属度阈值,编译前检查索引别越界。
用模糊输出值切三段设不同画线色,弱信号浅色、强信号深色,并在代码里限定只画关键拐点减少杂线。
小布可加载该指标到对应品种页,自动标出颜色区间并提示模糊信号强弱,省去你手动来回切图核对。
轨道限定了合理波动带,模糊逻辑把非黑即白的穿越改成概率过渡,只在置信区变色,杂讯自然少。
只保留双轨道与模糊信号两个有效缓冲,隐藏中间计算线,靠颜色区间表达状态,图表立刻干净易读。