MQL5 中的交易策略自动化(第十五部分):可视化价格行为的谐波形态模式·综合运用
📘

MQL5 中的交易策略自动化(第十五部分):可视化价格行为的谐波形态模式·综合运用

第 3/3 篇

◍ 把赛弗形态画进图表的那几行

识别完 X-A-B-C-D 五点后,先调用两次 DrawTrendLine 把 XB 与 BD 两段连线画出来,线宽固定 2、纯黑实线,视觉上能把形态骨架直接钉在 K 线上。 标签偏移量用 15 倍 SYMBOL_POINT 计算,摆文字时按该摆动点是高点还是低点决定往上或往下推,五个字母标签字体大小统一 11,避免和价格线糊在一起。 中心标签取 X 与 B 的时间中点、挂在 D 的价格位,OBJ_TEXT 写死 "Cypher" 并用 Arial Bold 居中,扫一眼就知道这张图被判定成啥形态。 交易线从 D 的时间向右延 2 根当前周期 K 线(PeriodSeconds(_Period)*2),多头时入场价抓 SYMBOL_ASK、止损落在 X 的摆动价;外汇与贵金属杠杆高,这类自动标线只作参考,实际触发前务必在 MT5 里核对点值和点差。 想验证就直接把下面片段塞进你的指标 OnCalculate 末尾,改 ObjectPrefix 看不同品种不串对象。

MQL5 / C++
DrawTrendLine(ObjectPrefix + "_Line_XB", PointX.TimeOfSwing, PointX.PriceAtSwing, PointB.TimeOfSwing, PointB.PriceAtSwing, clrBlack, class="num">2, STYLE_SOLID);
DrawTrendLine(ObjectPrefix + "_Line_BD", PointB.TimeOfSwing, PointB.PriceAtSwing, PointD.TimeOfSwing, PointD.PriceAtSwing, clrBlack, class="num">2, STYLE_SOLID);
class=class="str">"cmt">//--- class="num">3. Draw labels for each swing point(X, A, B, C, D)
class="type">class="kw">double LabelOffset = class="num">15 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); class=class="str">"cmt">//--- Offset in points for label placement
DrawTextLabel(ObjectPrefix + "_Label_X", "X", PointX.TimeOfSwing, PointX.PriceAtSwing + (PointX.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, class="num">11, PointX.IsSwingHigh);
DrawTextLabel(ObjectPrefix + "_Label_A", "A", PointA.TimeOfSwing, PointA.PriceAtSwing + (PointA.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, class="num">11, PointA.IsSwingHigh);
DrawTextLabel(ObjectPrefix + "_Label_B", "B", PointB.TimeOfSwing, PointB.PriceAtSwing + (PointB.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, class="num">11, PointB.IsSwingHigh);
DrawTextLabel(ObjectPrefix + "_Label_C", "C", PointC.TimeOfSwing, PointC.PriceAtSwing + (PointC.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, class="num">11, PointC.IsSwingHigh);
DrawTextLabel(ObjectPrefix + "_Label_D", "D", PointD.TimeOfSwing, PointD.PriceAtSwing + (PointD.IsSwingHigh ? LabelOffset : -LabelOffset), clrBlack, class="num">11, PointD.IsSwingHigh);
class=class="str">"cmt">//--- class="num">4. Draw a central label to identify the pattern
class="type">class="kw">datetime CenterTime = (PointX.TimeOfSwing + PointB.TimeOfSwing) / class="num">2;  class=class="str">"cmt">//--- Middle time between X and B
class="type">class="kw">double CenterPrice = PointD.PriceAtSwing;                            class=class="str">"cmt">//--- Place it at D’s price level
if(ObjectCreate(class="num">0, ObjectPrefix + "_Label_Center", OBJ_TEXT, class="num">0, CenterTime, CenterPrice)) {
   ObjectSetString(class="num">0, ObjectPrefix + "_Label_Center", OBJPROP_TEXT, "Cypher"); class=class="str">"cmt">//--- Label as "Cypher"
   ObjectSetInteger(class="num">0, ObjectPrefix + "_Label_Center", OBJPROP_COLOR, clrBlack);
   ObjectSetInteger(class="num">0, ObjectPrefix + "_Label_Center", OBJPROP_FONTSIZE, class="num">11);
   ObjectSetString(class="num">0, ObjectPrefix + "_Label_Center", OBJPROP_FONT, "Arial Bold");
   ObjectSetInteger(class="num">0, ObjectPrefix + "_Label_Center", OBJPROP_ALIGN, ALIGN_CENTER);
}
class=class="str">"cmt">//--- class="num">5. Draw trade levels(entry, take-profits) as dotted lines
class="type">class="kw">datetime LineStartTime = PointD.TimeOfSwing;                        class=class="str">"cmt">//--- Start at D’s time
class="type">class="kw">datetime LineEndTime = PointD.TimeOfSwing + PeriodSeconds(_Period) * class="num">2; class=class="str">"cmt">//--- Extend class="num">2 bars to the right
class="type">class="kw">double EntryPrice, StopLossPrice, TakeProfitPrice, TakeProfit1Level, TakeProfit2Level, TakeProfit3Level, TradeDistance;
if(PatternDirection == "Bullish") {
   EntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); class=class="str">"cmt">//--- Buy at current ask price
   StopLossPrice = PointX.PriceAtSwing;                class=class="str">"cmt">//--- Stop-loss at X(below entry for bullish)

三级止盈与图形标注的落地写法

在谐波或摆动点模型里,把 C 点 swing 价格直接设为第三级止盈,是常见收口做法。多头情形下 TradeDistance 等于 TP3 减入场价,再按 1/3、2/3 切出 TP1、TP2;空头则反向用 EntryPrice 减 TP3 算距离,层级价格随方向自然镜像。 下面这段逻辑把多空两套都覆盖了: TakeProfitPrice = PointC.PriceAtSwing; //--- Take-profit at C (target level) TakeProfit3Level = PointC.PriceAtSwing; //--- Highest TP at C TradeDistance = TakeProfit3Level - EntryPrice; //--- Distance to TP3 TakeProfit1Level = EntryPrice + TradeDistance / 3; //--- First TP at 1/3 of the distance TakeProfit2Level = EntryPrice + 2 * TradeDistance / 3; //--- Second TP at 2/3 } else { EntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Sell at current bid price StopLossPrice = PointX.PriceAtSwing; //--- Stop-loss at X (above entry for bearish) TakeProfitPrice = PointC.PriceAtSwing; //--- Take-profit at C TakeProfit3Level = PointC.PriceAtSwing; //--- Lowest TP at C TradeDistance = EntryPrice - TakeProfit3Level; //--- Distance to TP3 TakeProfit1Level = EntryPrice - TradeDistance / 3; //--- First TP at 1/3 TakeProfit2Level = EntryPrice - 2 * TradeDistance / 3; //--- Second TP at 2/3 } 画线是验证层级对不对的最快办法。用 DrawDottedLine 把 Entry、TP1~TP3 分别用品红和三种绿画成虚线,颜色深浅对应层级远近,一眼能看出间距是否按 1:2:3 分布。 标注文字放在 LineEndTime 加半个周期的位置,避免压到 K 线。LabelTime = LineEndTime + PeriodSeconds(_Period) / 2 这一行决定标签右移量;DoubleToString 配 _Digits 能保证 XAUUSD 这类小数位多的品种价格不丢精度。外汇与贵金属波动剧烈,层级只是概率目标,实盘须以止损为前提。

MQL5 / C++
TakeProfitPrice = PointC.PriceAtSwing;              class=class="str">"cmt">//--- Take-profit at C(target level)
TakeProfit3Level = PointC.PriceAtSwing;             class=class="str">"cmt">//--- Highest TP at C
TradeDistance = TakeProfit3Level - EntryPrice;       class=class="str">"cmt">//--- Distance to TP3
TakeProfit1Level = EntryPrice + TradeDistance / class="num">3;   class=class="str">"cmt">//--- First TP at class="num">1/class="num">3 of the distance
TakeProfit2Level = EntryPrice + class="num">2 * TradeDistance / class="num">3; class=class="str">"cmt">//--- Second TP at class="num">2/class="num">3
} else {
  EntryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); class=class="str">"cmt">//--- Sell at current bid price
  StopLossPrice = PointX.PriceAtSwing;                class=class="str">"cmt">//--- Stop-loss at X(above entry for bearish)
  TakeProfitPrice = PointC.PriceAtSwing;              class=class="str">"cmt">//--- Take-profit at C
  TakeProfit3Level = PointC.PriceAtSwing;             class=class="str">"cmt">//--- Lowest TP at C
  TradeDistance = EntryPrice - TakeProfit3Level;      class=class="str">"cmt">//--- Distance to TP3
  TakeProfit1Level = EntryPrice - TradeDistance / class="num">3;  class=class="str">"cmt">//--- First TP at class="num">1/class="num">3
  TakeProfit2Level = EntryPrice - class="num">2 * TradeDistance / class="num">3; class=class="str">"cmt">//--- Second TP at class="num">2/class="num">3
}
DrawDottedLine(ObjectPrefix + "_EntryLine", LineStartTime, EntryPrice, LineEndTime, clrMagenta); class=class="str">"cmt">//--- Entry line
DrawDottedLine(ObjectPrefix + "_TP1Line", LineStartTime, TakeProfit1Level, LineEndTime, clrForestGreen); class=class="str">"cmt">//--- TP1 line
DrawDottedLine(ObjectPrefix + "_TP2Line", LineStartTime, TakeProfit2Level, LineEndTime, clrGreen);     class=class="str">"cmt">//--- TP2 line
DrawDottedLine(ObjectPrefix + "_TP3Line", LineStartTime, TakeProfit3Level, LineEndTime, clrDarkGreen); class=class="str">"cmt">//--- TP3 line
class=class="str">"cmt">//--- class="num">6. Draw labels for trade levels
class="type">class="kw">datetime LabelTime = LineEndTime + PeriodSeconds(_Period) / class="num">2; class=class="str">"cmt">//--- Place labels further right
class="type">class="kw">string EntryLabelText = (PatternDirection == "Bullish") ? "BUY(" : "SELL(";  
EntryLabelText += DoubleToString(EntryPrice, _Digits) + ")"; class=class="str">"cmt">//--- Add price to label
DrawTextLabel(ObjectPrefix + "_EntryLabel", EntryLabelText, LabelTime, EntryPrice, clrMagenta, class="num">11, true);  
class="type">class="kw">string TP1LabelText = "TP1(" + DoubleToString(TakeProfit1Level, _Digits) + ")";  
DrawTextLabel(ObjectPrefix + "_TP1Label", TP1LabelText, LabelTime, TakeProfit1Level, clrForestGreen, class="num">11, true);  
class="type">class="kw">string TP2LabelText = "TP2(" + DoubleToString(TakeProfit2Level, _Digits) + ")";  
DrawTextLabel(ObjectPrefix + "_TP2Label", TP2LabelText, LabelTime, TakeProfit2Level, clrGreen, class="num">11, true);  
class="type">class="kw">string TP3LabelText = "TP3(" + DoubleToString(TakeProfit3Level, _Digits) + ")";  
DrawTextLabel(ObjectPrefix + "_TP3Label", TP3LabelText, LabelTime, TakeProfit3Level, clrDarkGreen, class="num">11, true);  
class=class="str">"cmt">//--- **Trading Logic**
class=class="str">"cmt">//--- Check if trading is allowed and no position is already open
if(TradingEnabled && !PositionSelect(_Symbol)) {  
  class=class="str">"cmt">//--- Place a buy or sell order based on pattern type
  class="type">bool TradeSuccessful = (PatternDirection == "Bullish") ?

「塞弗形态触发后的下单与清理动作」

当价格走到塞弗形态的潜在反转区,EA 会按方向调用买入或卖出接口,同时把止损、止盈和标识名一并带进去。下面这段代码就是实际发单与日志输出的核心片段。 obj_Trade.Buy(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Buy"); obj_Trade.Sell(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Sell"); if(TradeSuccessful) Print(PatternDirection, " order opened successfully."); else Print(PatternDirection, " order failed: ", obj_Trade.ResultRetcodeDescription()); 发单后无论成败都用 Print 把结果写进日志:成功仅提示方向,失败则附带 ResultRetcodeDescription() 返回的具体错误描述,便于在 MT5 终端直接排查 rejects 原因。 图表对象不会自动刷新,所以每次绘制或发单后都该补一句 ChartRedraw() 强制重绘,否则形态连线可能滞后一两秒才显示。外汇与贵金属杠杆高,这类基于形态的市价单滑点概率不低,实盘前建议在策略测试器用 2023 年 XAUUSD 的 M15 数据跑一遍观察回码。 EA 卸载时 OnDeinit 里用 ObjectsDeleteAll(0, "CY_") 按前缀清空所有塞弗相关图形,避免换周期或重加载时旧对象堆叠干扰识别。

MQL5 / C++
  obj_Trade.Buy(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Buy") :
  obj_Trade.Sell(TradeVolume, _Symbol, EntryPrice, StopLossPrice, TakeProfitPrice, "Cypher Sell");
  class=class="str">"cmt">//--- Log the result of the trade attempt
  if(TradeSuccessful)
     Print(PatternDirection, " order opened successfully.");
  else
     Print(PatternDirection, " order failed: ", obj_Trade.ResultRetcodeDescription());
}
class=class="str">"cmt">//--- Force the chart to update and show all drawn objects
ChartRedraw();
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Runs when the EA stops(e.g., removed from chart)
class="type">void OnDeinit(class="kw">const class="type">int reason) {
  class=class="str">"cmt">//--- Remove all objects starting with "CY_" (our Cypher pattern objects)
  ObjectsDeleteAll(class="num">0, "CY_");
}

◍ 用形态锁定干掉重绘误报

初始回测里最刺眼的问题是密码形态重绘:某根 K 线看似有效,新数据进来后摆动点偏移甚至整形态消失,信号直接不可信。这类误报会让系统按后来被证伪的形态下单,回测权益曲线被无谓磨损。 修复思路不复杂——用全局变量 g_patternFormationBar 与 g_lockedPatternX 在探测到形态当根锁住 X 摆动点,下一根 K 线再做一致性确认。只有 X 点时间对齐才放行交易,等于强制形态多活一根 bar 才算数。 加上这段逻辑后,重绘触发次数明显下降,无效成交减少;后续多轮回测显示形态检测稳定性改善,误报率倾向收敛。外汇与贵金属波动跳空多,重绘风险本就偏高,锁定机制是必要护栏而非可选项。 下面这段就是锁定更新的核心:发现形态变动就重写锁存并打日志等待确认,不急着进场。

MQL5 / C++
class=class="str">"cmt">//--- If the pattern has changed, update the lock
g_patternFormationBar = CurrentBarIndex;
g_lockedPatternX = PointX.TimeOfSwing;
Print("Cypher pattern has changed; updating lock on bar ", CurrentBarIndex, ". Waiting for confirmation.");
class="kw">return;

把这条线请下神坛

这套密码谐波 EA 把摆动点检测、斐波那契比率校验和形态锁定串成了一条线,46.26 KB 的 mq5 文件在 MT5 里能直接编译跑起来,但它在肯尼亚作者那篇里本质仍是教学样品——讨论区里 Kyle 问过源码、Muhammad 说拿来改别的形态有用,说明它最大的价值是可改,不是可盲跟。 外汇和贵金属杠杆高、跳空频繁,任何形态识别都只是把概率结构化,重绘屏蔽了也只是少踩一个坑,不代表信号胜率越过 50% 这道坎。 真要验证,自己下载 ZIP 把 Cypher_Harmonic_Pattern_EA.mq5 拖进策略测试器,用 2023—2024 年 XAUUSD 的 M15 跑一遍回测,看形态锁定后的成交分布再决定加不加仓位系数。

常见问题

用折线对象按 X-A-B-C-D 五个点依次连接,并在每点旁写文字标签;代码里用 ObjectCreate 建 OBJ_POLYLINE 再填坐标即可。
按 D 点回调比例分三批平仓:例如 38.2%、61.8%、100% 各设一档;每档成交后删对应线段避免干扰。
小布盯盘的 AIGC 已内置谐波扫描,打开对应品种页即可看到形态标注与触发提醒,不用自己写代码。
未锁定形态导致每根新 K 线重算;应在形态确认后把点坐标写死并用标志位阻止重绘。
不存在无脑跟;外汇贵金属高风险,形态仅提高概率,须配合止损与仓位管理再决策。