交易策略中的模糊逻辑·进阶篇
「三输入一输出的模糊变量怎么搭」
做模糊推理模型时,先声明三个输入和一个输出变量,再配几个字典对象方便后续逻辑运算调用。这里用三个不同周期的 RSI 作输入,原始值在 0–100,统一归一化到 0–1 区间,输出也锁在 0–1,维度必须跟输入向量对齐,否则后面模糊运算会直接错位。 每个变量挂三组模糊条例:buy、neutral、sell。输入侧用 Z 形(0.0,0.6)、正态(0.5,0.2)、S 形(0.4,1.0)三种成员函数切分区域;输出侧 neutral 暂用变量 Gposition、Gsigma 占位,留待优化器 later 调。这样振荡器靠近 0 倾向划进买入区,靠近 1 划进卖出区,中间 0.5 附近不确定性最高。 成员函数数量没硬限制,挂 5 个、7 个、15 个都行,只要在常识范围内。选这两种形状主要因为只暴露 2 个可优化参数,极值和中心位置描述得够清楚,你也能换模糊库里别的函式试试。 下面这段是 MT5 里 OnInit 阶段建变量的核心代码,逐行看就是:new 出三个 CFuzzyVariable 命名为 rsi1/rsi2/rsi3 和输出 out,范围全 0–1;三个 CDictionary_Obj_Double 管术语表;随后每个变量 Add 三个 CFuzzyTerm,Z 形管 buy、正态管 neutral、S 形管 sell,最后塞进 OurFuzzy 的 Input/Output。
CFuzzyVariable *firstInput=new CFuzzyVariable("rsi1",class="num">0.0,class="num">1.0); CFuzzyVariable *secondInput=new CFuzzyVariable("rsi2",class="num">0.0,class="num">1.0); CFuzzyVariable *thirdInput=new CFuzzyVariable("rsi3",class="num">0.0,class="num">1.0); CFuzzyVariable *fuzzyOut=new CFuzzyVariable("out",class="num">0.0,class="num">1.0); CDictionary_Obj_Double *firstTerm=new CDictionary_Obj_Double; CDictionary_Obj_Double *secondTerm=new CDictionary_Obj_Double; CDictionary_Obj_Double *thirdTerm=new CDictionary_Obj_Double; CDictionary_Obj_Double *Output; firstInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(class="num">0.0,class="num">0.6))); firstInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(class="num">0.5, class="num">0.2))); firstInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(class="num">0.4,class="num">1.0))); OurFuzzy.Input().Add(firstInput); secondInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(class="num">0.0,class="num">0.6))); secondInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(class="num">0.5, class="num">0.2))); secondInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(class="num">0.4,class="num">1.0))); OurFuzzy.Input().Add(secondInput); thirdInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(class="num">0.0,class="num">0.6))); thirdInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(class="num">0.5, class="num">0.2))); thirdInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(class="num">0.4,class="num">1.0))); OurFuzzy.Input().Add(thirdInput); fuzzyOut.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(class="num">0.0,class="num">0.6))); fuzzyOut.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(Gposition, Gsigma))); fuzzyOut.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(class="num">0.4,class="num">1.0))); OurFuzzy.Output().Add(fuzzyOut);
◍ 用 MT5 把三类模糊隶属函数画出来
在 MT5 里做价格状态模糊化,第一步是把成员函数可视化,确认拐点位置符合你的交易逻辑。下面这段代码直接调用标准库里的 Z形、正态、S形成员函数,并把它们画在同一张图里。 代码先引入模糊数学与图形库,随后用三组参数实例化函数:Z形取(0.0, 0.6),正态取中心0.5、宽度0.2,S形取(0.4, 1.0)。这三个参数决定了隶属度从0升到1的过渡区间,改一个数曲线形状就会变。 OnStart 里建了一个 780×380 的画布,X轴锁死在 0.0~1.0、步长0.1,Y轴 0.0~1.1。三条曲线都用 0.01 的采样步长从 0 扫到 1,标签分别写着对应的参数对,方便你回看时知道哪条线是谁。 把这段塞进一个脚本跑起来,你会在子窗口看到三条平滑过渡的隶属曲线。若想把「趋势强」的判定门槛抬高,就把正态函数的宽度从0.2调小,曲线会变尖、隶属度掉得更快——外汇与贵金属波动剧烈,这类参数务必先在历史数据上验证再上实盘。
class="macro">#include <Math\Fuzzy\membershipfunction.mqh> class="macro">#include <Graphics\Graphic.mqh> class=class="str">"cmt">//--- 创建成员函数 CZ_ShapedMembershipFunction func2(class="num">0.0, class="num">0.6); CNormalMembershipFunction func1(class="num">0.5, class="num">0.2); CS_ShapedMembershipFunction func3(class="num">0.4, class="num">1.0); class=class="str">"cmt">//--- 创建成员函数的包装 class="type">class="kw">double NormalMembershipFunction1(class="type">class="kw">double x) { class="kw">return(func1.GetValue(x)); } class="type">class="kw">double ZShapedMembershipFunction(class="type">class="kw">double x) { class="kw">return(func2.GetValue(x)); } class="type">class="kw">double SShapedMembershipFunction(class="type">class="kw">double x) { class="kw">return(func3.GetValue(x)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 脚本程序启动函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//--- 创建图形 CGraphic graphic; if(!graphic.Create(class="num">0,"Our MembershipFunctions",class="num">0,class="num">30,class="num">30,class="num">780,class="num">380)) { graphic.Attach(class="num">0,"Our MembershipFunctions"); } graphic.HistoryNameWidth(class="num">70); graphic.BackgroundMain("Our MembershipFunctions"); graphic.BackgroundMainSize(class="num">16); class=class="str">"cmt">//--- 创建曲线 graphic.CurveAdd(NormalMembershipFunction1,class="num">0.0,class="num">1.0,class="num">0.01,CURVE_LINES,"[class="num">0.5, class="num">0.2]"); graphic.CurveAdd(ZShapedMembershipFunction,class="num">0.0,class="num">1.0,class="num">0.01,CURVE_LINES,"[class="num">0.0, class="num">0.6]"); graphic.CurveAdd(SShapedMembershipFunction,class="num">0.0,class="num">1.0,class="num">0.01,CURVE_LINES,"[class="num">0.4, class="num">1.0]"); class=class="str">"cmt">//--- 设置 X 轴属性 graphic.XAxis().AutoScale(class="kw">false); graphic.XAxis().Min(class="num">0.0); graphic.XAxis().Max(class="num">1.0); graphic.XAxis().DefaultStep(class="num">0.1); class=class="str">"cmt">//--- 设置 Y 轴属性 graphic.YAxis().AutoScale(class="kw">false); graphic.YAxis().Min(class="num">0.0); graphic.YAxis().Max(class="num">1.1); graphic.YAxis().DefaultStep(class="num">0.1); class=class="str">"cmt">//--- 绘图 graphic.CurvePlotAll(); graphic.Update(); }
给模糊系统喂入规则基准
模糊决策系统要落地,先得往知识基准里塞逻辑条件。最少一条,且不能出现不参与逻辑运算的孤立条例,否则系统直接判不完整;条件数量本身无上限,例子里写了 12 条,全部进逻辑运算,默认权重都是 1,本例没动过。 三条 RSI 类指标若同处买入模糊区,输出就是模糊买入;全卖或全中性同理(规则 1-3)。两买一卖、两卖一买这类对冲组合,输出倾向中性不确定(规则 4-6)。只要有两个同向、第三个中性,同向信号就直接灌进输出(规则 7-12)。 下面这段是 MT5 里实际 ParseRule 的写法,逐行拆一下:rule1 到 rule3 是三指标同态直接定输出;rule4-6 是两同一异判中性;rule7-12 是两同加一中性的偏向输出。最后 12 行 Add 进 Rules 容器,系统才认这批基准。 这套基准只是基于个人对系统该怎么跑的判断,不是唯一解,外汇和贵金属波动剧烈、高风险,拿去跑之前建议自己改几条规则做对比。
rule1 = OurFuzzy.ParseRule("if (rsi1 is buy) and(rsi2 is buy) and(rsi3 is buy) then(out is buy)"); rule2 = OurFuzzy.ParseRule("if (rsi1 is sell) and(rsi2 is sell) and(rsi3 is sell) then(out is sell)"); rule3 = OurFuzzy.ParseRule("if (rsi1 is neutral) and(rsi2 is neutral) and(rsi3 is neutral) then(out is neutral)"); rule4 = OurFuzzy.ParseRule("if (rsi1 is buy) and(rsi2 is sell) and(rsi3 is buy) then(out is neutral)"); rule5 = OurFuzzy.ParseRule("if (rsi1 is sell) and(rsi2 is sell) and(rsi3 is buy) then(out is neutral)"); rule6 = OurFuzzy.ParseRule("if (rsi1 is buy) and(rsi2 is buy) and(rsi3 is sell) then(out is neutral)"); rule7 = OurFuzzy.ParseRule("if (rsi1 is buy) and(rsi2 is buy) and(rsi3 is neutral) then(out is buy)"); rule8 = OurFuzzy.ParseRule("if (rsi1 is sell) and(rsi2 is sell) and(rsi3 is neutral) then(out is sell)"); rule9 = OurFuzzy.ParseRule("if (rsi1 is buy) and(rsi2 is neutral) and(rsi3 is buy) then(out is buy)"); rule10 = OurFuzzy.ParseRule("if (rsi1 is sell) and(rsi2 is neutral) and(rsi3 is sell) then(out is sell)"); rule11 = OurFuzzy.ParseRule("if (rsi1 is neutral) and(rsi2 is buy) and(rsi3 is buy) then(out is buy)"); rule12 = OurFuzzy.ParseRule("if (rsi1 is neutral) and(rsi2 is sell) and(rsi3 is sell) then(out is sell)"); OurFuzzy.Rules().Add(rule1); OurFuzzy.Rules().Add(rule2); OurFuzzy.Rules().Add(rule3); OurFuzzy.Rules().Add(rule4); OurFuzzy.Rules().Add(rule5); OurFuzzy.Rules().Add(rule6); OurFuzzy.Rules().Add(rule7); OurFuzzy.Rules().Add(rule8); OurFuzzy.Rules().Add(rule9); OurFuzzy.Rules().Add(rule10); OurFuzzy.Rules().Add(rule11); OurFuzzy.Rules().Add(rule12);
「去模糊后得到的清晰交易刻度」
这套模型本质上还是计算框架,最终吐出的结果落在 0 到 1 之间。读数的含义很直接:贴近 0 代表偏强的买入倾向,0.5 附近算中性区,靠近 1 则释放偏强的卖出倾向。外汇与贵金属波动剧烈,这种读数只反映概率,不能当作入场保证。 它内部拉取三个不同周期的 RSI 振荡器,先把数值归一化到 0~1(直接除以 100 即可),再用模糊字典把最新指标值更新进列表,送进推理得到输出变量,最后从 res 里取数。下面的函数就是这条链路的实体,复制进 MT5 的 EA 或指标里能直接跑通验证。 别把 0.5 当安全线。实盘里 RSI 多周期共振后,输出在 0.2 以下或 0.8 以上才具备可观测的信号分离度,中间段噪声很大,硬做容易频繁止损。
class="type">class="kw">double CalculateMamdani() { CopyBuffer(hnd1,class="num">0,class="num">0,class="num">1,arr1); NormalizeArrays(arr1); CopyBuffer(hnd2,class="num">0,class="num">0,class="num">1,arr2); NormalizeArrays(arr2); CopyBuffer(hnd3,class="num">0,class="num">0,class="num">1,arr3); NormalizeArrays(arr3); firstTerm.SetAll(firstInput,arr1[class="num">0]); secondTerm.SetAll(secondInput,arr2[class="num">0]); thirdTerm.SetAll(thirdInput,arr2[class="num">0]); Inputs.Clear(); Inputs.Add(firstTerm); Inputs.Add(secondTerm); Inputs.Add(thirdTerm); CList *FuzzResult=OurFuzzy.Calculate(Inputs); Output=FuzzResult.GetNodeAtIndex(class="num">0); class="type">class="kw">double res = Output.Value(); class="kw">delete FuzzResult; class="kw">return(res); }
◍ 把高斯钟形和中性带丢进优化器
把模糊系统里的高斯成员函数参数外置到 input,就能在 MT5 优化器里跑网格。Gsigma 默认 0.5,可设范围 0.05–0.5、步长 0.05;Gposition 默认 0.5,范围 0.0–1.0、步长 0.1。调这两个值等于沿 X 轴挪钟形中心、压扁或拉宽曲线,RSI 的买/卖信号不对称时,能靠这俩把输出掰回对称。 开仓闸门再加一对中性边界:MinNeutralSignal 默认 0.4(0.3–0.5 步 0.1),MaxNeutralSignal 默认 0.6(0.5–0.7 步 0.1)。信号落在这中间一律不开新仓,只在明确偏向多或空时才动手。 计算放在新柱线触发以加速回测;删掉 isNewBar 检查就能改成逐笔报价驱动,看你习惯。持仓时若信号转中性或反向就平仓,越过另一边界直接反手——系统没挂止损,靠信号翻转平掉重开,外汇和贵金属波动剧烈,这种反手逻辑遇跳空可能滑点放大。 下面这段是模糊输出到下单的骨架,用 MT4Orders 库封装,方便以后降回 MQL4。
class="kw">input class="type">class="kw">string Fuzzy_Setings; class=class="str">"cmt">//模糊优化设置 class="kw">input class="type">class="kw">double Gsigma = class="num">0.5; class=class="str">"cmt">//sigma 从 class="num">0.05 至 class="num">0.5 步长 class="num">0.05 class="kw">input class="type">class="kw">double Gposition=class="num">0.5; class=class="str">"cmt">//position 从 class="num">0.0 至 class="num">1.0 步长 class="num">0.1 class="kw">input class="type">class="kw">double MinNeutralSignal=class="num">0.4; class=class="str">"cmt">//MinNeutralSignal 从 class="num">0.3 至 class="num">0.5 步长 class="num">0.1 class="kw">input class="type">class="kw">double MaxNeutralSignal=class="num">0.6; class=class="str">"cmt">//MaxNeutralSignal 从 class="num">0.5 至 class="num">0.7 步长 class="num">0.1 class="type">void OnTick() { class=class="str">"cmt">//--- if(!isNewBar()) { class="kw">return; } class="type">class="kw">double TradeSignal=CalculateMamdani(); if(CountOrders(class="num">0)!=class="num">0 || CountOrders(class="num">1)!=class="num">0) class=class="str">"cmt">// 如果有开仓 { for(class="type">int b=OrdersTotal()-class="num">1; b>=class="num">0; b--) { if(OrderSelect(b,SELECT_BY_POS)==true) { if(OrderSymbol()==_Symbol && OrderMagicNumber()==OrderMagic) { if(OrderType()==OP_BUY && TradeSignal>=MinNeutralSignal) class=class="str">"cmt">// 选择买单且交易信号大于中性信号的左边界 { class=class="str">"cmt">// 也就是说, 有一个中性的信号或卖出的信号 if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),class="num">0,Red)) class=class="str">"cmt">// 然后平多头仓位 { if(TradeSignal>MaxNeutralSignal) class=class="str">"cmt">// 如果订单已平仓且存在卖出信号 (超出中性信号的右边界), 立即开仓 { lots = LotsOptimized(); if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),class="num">0,class="num">0,class="num">0,NULL,OrderMagic,Red)){ }; } } } } } } }