交易策略中的模糊逻辑·进阶篇
📘

交易策略中的模糊逻辑·进阶篇

第 2/3 篇

「三输入一输出的模糊变量怎么搭」

做模糊推理模型时,先声明三个输入和一个输出变量,再配几个字典对象方便后续逻辑运算调用。这里用三个不同周期的 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。

MQL5 / C++
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调小,曲线会变尖、隶属度掉得更快——外汇与贵金属波动剧烈,这类参数务必先在历史数据上验证再上实盘。

MQL5 / C++
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 容器,系统才认这批基准。 这套基准只是基于个人对系统该怎么跑的判断,不是唯一解,外汇和贵金属波动剧烈、高风险,拿去跑之前建议自己改几条规则做对比。

MQL5 / C++
  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 以上才具备可观测的信号分离度,中间段噪声很大,硬做容易频繁止损。

MQL5 / C++
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。

MQL5 / C++
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)){
                         };
                       }
                     }
                 }
              }
         }
     }
  }

常见问题

把三个输入(如趋势强度、波动率、动量)归一化到0-1区间,输出设为仓位刻度,用独立隶属函数分隔语义区,避免重叠过多导致规则冲突。
在图表工具里分别用分段线性画三角/梯形,用指数公式画高斯,叠加到同一坐标轴对比宽度与峰值,确认中性带留白合理。
小布可读取你的变量定义自动生成初始规则矩阵,并标出重叠或空覆盖区,你只需在品种页确认调整,不必手算真值表。
将重心法输出的0-1刻度映射到手数或挂单距离,例如>0.7偏多仓、<0.3偏空,中间观望,外汇贵金属波动大需严控风控。
有可能。建议用 walk-forward 分段验证,中性带宽度单独约束上下限,样本外回测衰减明显就收窄钟形标准差。