利用模糊逻辑创建指标的简单示例·进阶篇
「在 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 里读不到轨道线值。
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 策略测试器用历史数据验证染色逻辑。
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 这个步长看柱色密度变化,比对着文档空想要直观。
{
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 策略测试器验证。 留个余味:指标写得越薄,价格本身说话的空间才越大。