交易策略中的模糊逻辑·综合运用
📘

交易策略中的模糊逻辑·综合运用

第 3/3 篇

反手与裸开仓的信号分支

这段逻辑处理的是持仓反转与无仓直接开仓两类情形。当检测到一笔卖单且 TradeSignal 降至 MaxNeutralSignal 以下时,先按红价平掉该卖单,随后若信号进一步跌破 MinNeutralSignal,则通过 LotsOptimized() 重算手数并以 OP_BUY 在 Ask 价反向开多。 无持仓时的入口更直白:信号低于 MinNeutralSignal 且 CheckMoneyForTrade 校验通过,就买;高于 MaxNeutralSignal 且资金校验通过,就卖。两个 OrderSend 的滑点、止损、止盈参数都传了 0,说明这套框架默认不挂防护单,外汇与贵金属的高波动下可能迅速放大回撤。 镜像处理卖单的部分,原文注释明确写的是「与多头仓位一样,但所有条件都是镜像的」。你在 MT5 里跑这段代码时,可以把 MinNeutralSignal / MaxNeutralSignal 两个阈值打印到日志,观察实盘信号在中性带里的停留时长。

MQL5 / C++
   }
      }
         }
         if(OrderType()==OP_SELL && TradeSignal<=MaxNeutralSignal)      class=class="str">"cmt">// 选择一笔卖单....///.... 与多头仓位一样, 但所有条件都是镜像的
         {
            if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),class="num">0,Red))
            {
               if(TradeSignal<MinNeutralSignal)
               {
                 lots = LotsOptimized();
                 if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),class="num">0,class="num">0,class="num">0,NULL,OrderMagic,Green)){
                 };
               }
            }
         }
      }
   }
   class="kw">return;
            class=class="str">"cmt">// 如果没有持仓, 根据信号开仓
   lots = LotsOptimized();
   if(TradeSignal<MinNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_BUY))
    {
      if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),class="num">0,class="num">0,class="num">0,NULL,OrderMagic,Green)){
      };
    }
   else if(TradeSignal>MaxNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_SELL))
    {
      if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),class="num">0,class="num">0,class="num">0,NULL,OrderMagic,Red)){
      };
    }
   class="kw">return;

「M15与M5双周期下的高斯优化实测」

系统先用 M15 周期、跨度 8 个月的历史数据跑优化,价格取开盘价,遗传算法选快速模式,优化准则设为余额加最大利润。从遗传输出里挑出最佳参数组,再丢进严格模型回测做对照,观察成员函数输出在优化前后的偏移。 同样设置换到 M5 重跑一遍,只是周期缩到五分钟,其余不变。两轮结果都出现一个现象:高斯中性区明显偏向买入侧,多头持仓数压过空头,说明这段历史里买卖信号本身不对称,不跑这组实验根本看不出来。 由三个 RSI 拼成的系统,超卖区(区域1)的出现频次可能高于超买区(区域0),而高斯优化能把这种倾斜抹平。即便只动了 4 个参数,8 个月内 equity 曲线也还算稳,其中 Gsigma 和 Gposition 是核心,另两个总在 0.5 附近晃,基本可砍掉只留两个。 对比严格规则系统动辄要建一堆优化标准、参数越加越多,模糊逻辑这条路反而能把待调维度压下来。当然这只是一个粗样:底层是裸 RSI 策略、没接止损,外汇和贵金属这种高波动品种直接照搬风险很大,但用来摸清模糊模块怎么嵌进 EA 足够了。

◍ 把模糊系统从单输出扩成三路委员会

原文收尾处提到一个可自学的改进方向:用多个模糊模型组成委员会,分别负责交易系统的不同层面。给出的代码片段正是一个把 RSI、CCI、随机指标各拆成独立模糊系统的雏形——九个输入术语分三组塞进 Inputs 列表,但 Output 仍只取 FuzzResult 的第 0 个节点,注释里明写「需要更新,以考虑三个而不是一个输出」。 这段实现暴露了单输出架构的硬限制:当您想并行跑三套模糊逻辑(例如 RSI 管超买超卖、CCI 管趋势强度、Stochastic 管反转概率),去模糊化后必须自己加权汇总,而不是直接拿一个清晰值下单。外汇与贵金属市场的高波动下,这种多模型投票比单模型更抗过拟合,但权重分配本身又引入新参数维度。 实操上,您可以把三路输出各自归一到 0–10,再按历史样本回测分配权重(比如 RSI 路 0.4、CCI 路 0.35、Stochastic 路 0.25),总分明低于 3 倾向观望、高于 7 才触发。注意 MT5 编译前需自备 MT4Orders 与 Fuzzy 函数库,否则 hnd1–hnd9 的句柄会全部失效。 别把正态当圣经 原文用「在家看电影/聚会/打怪」比喻模糊推论,本质是说人类决策本就是概率化的。直接搬进 EA 时,记得把「绝对不」这类清晰边界改成隶属度截断,否则小样本里容易误杀有效信号。

MQL5 / C++
class="type">class="kw">double CalculateMamdani()
{
class=class="str">"cmt">// 缓冲区和数组正常化
class="type">class="kw">double CalculateMamdani()
{
class=class="str">"cmt">// 缓冲区和数组正常化
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);
CopyBuffer(hnd4,class="num">0,class="num">0,class="num">1,arr4);
NormalizeArrays(arr4);
  
CopyBuffer(hnd5,class="num">0,class="num">0,class="num">1,arr5);
NormalizeArrays(arr5);
    
CopyBuffer(hnd6,class="num">0,class="num">0,class="num">1,arr6);
NormalizeArrays(arr6);
CopyBuffer(hnd7,class="num">0,class="num">0,class="num">1,arr7);
NormalizeArrays(arr7);
  
CopyBuffer(hnd8,class="num">0,class="num">0,class="num">1,arr8);
NormalizeArrays(arr8);
    
CopyBuffer(hnd9,class="num">0,class="num">0,class="num">1,arr9);
NormalizeArrays(arr9);
class=class="str">"cmt">// 输入 - 第一个模糊系统 [RSI] 
firstTerm.SetAll(firstInput,arr1[class="num">0]);
secondTerm.SetAll(secondInput,arr2[class="num">0]);
thirdTerm.SetAll(thirdInput,arr2[class="num">0]);
class=class="str">"cmt">// 输入 - 第二模糊系统 [CCI]]
fourthTerm.SetAll(fourthInput,arr4[class="num">0]);
fifthTerm.SetAll(fifthInput,arr5[class="num">0]);
sixthTerm.SetAll(sixthInput,arr6[class="num">0]);
class=class="str">"cmt">// 输入 - 第三模糊系统 [随机]]
seventhTerm.SetAll(seventhInput,arr7[class="num">0]);
eighthTerm.SetAll(eighthInput,arr8[class="num">0]);
ninthTerm.SetAll(ninthInput,arr9[class="num">0]);
Inputs.Clear();
class=class="str">"cmt">// 添加术语 - 第一个模糊系统 [RSI]]
Inputs.Add(firstTerm);
Inputs.Add(secondTerm);
Inputs.Add(thirdTerm);
class=class="str">"cmt">// 增加术语 - 第二模糊系统 [CCI} 
Inputs.Add(fourthTerm);
Inputs.Add(fifthTerm);
Inputs.Add(sixthTerm);
class=class="str">"cmt">// 增加术语 - 第三模糊系统 [Stockastic]
Inputs.Add(seventhTerm);
Inputs.Add(eighthTerm);
Inputs.Add(ninthTerm);
CList *FuzzResult=OurFuzzy.Calculate(Inputs);
Output=FuzzResult.GetNodeAtIndex(class="num">0); class=class="str">"cmt">// 需要更新,以考虑三个而不是一个输出 !!!!!!!!
}

最后一句大实话

把三个模糊输出的加权汇总塞进 Output.Value(),再 delete 掉 FuzzResult 释放内存,这个 return 就是整个推理链的出口。你在 MT5 里改权重或换隶属度函数,res 的曲线会直接变样,贵金属和外汇这类高波动品种尤其敏感,回测里常见的过拟合就藏在这几行。 跑通之后别急着上实盘,先用历史 tick 把 res 和价格拐点对齐看胜率,概率上能过滤掉一半假信号就已经值回票价。

MQL5 / C++
class="type">class="kw">double res = Output.Value();
class=class="str">"cmt">// 使用权重为三个输出的汇总方法添加代码
class="kw">delete FuzzResult;
class="kw">return(res);
}

常见问题

重点看高斯隶属度函数的宽度与中心偏移,先用M15定趋势门限,再用M5收窄入场区间,样本外留30%数据验证。
用模糊输出值切分:高于0.8走反手,0.4到0.6之间且无持仓才允许裸开,其余情况观望。
小布可加载你的三路委员会规则,在对应品种页实时标注信号分支并提示反手或裸开条件,省去手动盯双周期。
趋势路占0.5、动量路0.3、波动率路0.2,回测中这种分配在贵金属上回撤相对可控。
是,无对冲裸开在外汇和贵金属属高风险动作,建议仅在小周期确认背离且止损明确时轻仓尝试。