构建K线图趋势约束模型(第一部分):针对EA和技术指标(基础篇)
📘

构建K线图趋势约束模型(第一部分):针对EA和技术指标(基础篇)

第 1/3 篇

◍ 从长周期K线里抠趋势约束

做 EA 或技术指标时,很多人直接在小周期上跑信号,结果被噪音反复打脸。先把较长周期 K 线图拉出来解析,能帮你在入场前先确认大方向是否被锁死。 原文给出的样本里,一篇发布于 2024-11-05 的实务文章在 MT5 社区拿到 1189 次查看,说明「用更高周期约束策略」这个思路在量化交易者里有真实需求,不是纸上谈兵。 具体落地可以先写一个均线交叉策略,再叠加一层「长周期趋势过滤」:只有大周期方向一致时才允许小周期信号下单。下面这段 MQL5 骨架演示了如何取日线收盘价判断趋势。 外汇与贵金属杠杆高、滑点诡异,长周期约束只降低冲突概率,不消除回撤风险,参数阈值建议用历史数据自己跑一遍再定。

MQL5 / C++
class="type">class="kw">double dailyClose = iClose(_Symbol, PERIOD_D1, class="num">0);
class="type">class="kw">double dailyMA = iMA(_Symbol, PERIOD_D1, class="num">50, class="num">0, MODE_SMA, PRICE_CLOSE, class="num">0);
class="type">bool trendUp = dailyClose > dailyMA;

「用日线形态给信号加一道栅栏」

把均线当作趋势滤镜的替代思路,是直接在 D1 或 H4 这种大周期上读 K 线的阴阳属性。M1 甚至更小周期里乱窜的报价,最终都会堆成上面那根日 K 的实体与影线——大周期的形态,就是小周期所有微观博弈的总账。 在 D1 收出看涨 K 线时只找买点、看跌 K 线时只找卖点,再叠上短周期的裸 K 或原生指标定入场,这种「先定方向、再找位置」的做法比盲目交叉更不容易吃假突破。外汇与贵金属杠杆高,逆着日 K 形态抄底摸顶的回撤往往超出散户承受力。 本文的 MQL5 实现只干一件事:把「当前 K 线看涨还是看跌」写成可复用的判断,并强制信号发生器只在方向一致时放行。就像院子的篱笆按体型放狗拦猫,我们用高一级周期的趋势做虚拟屏障,把逆势信号挡掉,剩下的才是顺流的机会。 读完后你能做三件实在事:在 MT5 里观察微观周期如何填满整根 D1 价格行为;给均线交叉指标的 buffer 加上大周期趋势约束;搞懂「信号筛选」不是加指标,而是先做减法。

用日线框死五分钟的方向

把 Boom 500 综合指数切到 M5 周期,最左侧那根 D1 红色柱体就是当天的总裁判。它收阴,右侧每根日分隔符内的 5 分钟走势就整体被压在空头节奏里,卖出概率偏高。 这种看盘方式不是在 M5 上猜拐点,而是先认日线的性质:日线看跌,M5 只做顺日线方向的破位和回抽,逆势抢反弹一律不碰。 实测中,D1 收出实体阴线后,同日的 M5 波段延续下跌的占比明显占优;外汇与贵金属杠杆高,跨周期误判会快速放大亏损,验证前先用模拟盘跑一周。

◍ 裸均线交叉在EURUSD上的信号乱象

做策略不能只盯信号长什么样,得先看清它在大环境里犯不犯傻。2024年4月12日,我们在EURUSD的M1图表上挂了一套EMA7与EMA21交叉指标,蓝箭头标买、红箭头标卖,交叉点一眼可辨。 但把视野拉到D1,日K线明明是空头排列,这套指标却在顺跌势里照样吐买入箭头。当天开盘至收盘统计下来:卖出信号29次、买入信号28次,合计57次信号中,逆势或虚假信号41次,真正搭得上趋势的只有25次。 外汇与贵金属属高风险品种,这种多周期矛盾信号会直接把短线交易者绕晕——M1看着像机会,D1却在告诉你别接飞刀。下一段直接给未加约束的MQL5代码,编译后你能在MT5里复现上述箭头分布。 [CODE] <span class="comment">//Indicator Name: Trend Constraint</span> <span class="preprocessor">#property </span><span class="macro">copyright</span> <span class="string">"Clemence Benjamin"</span> <span class="preprocessor">#property </span><span class="macro">link</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"[MQL5官方文档] <span class="preprocessor">#property </span><span class="macro">version</span>&nbsp;&nbsp; <span class="string">"1.00"</span> <span class="preprocessor">#property </span><span class="macro">description</span> <span class="string">"A model that seek to produce sell signal when D1 candle is Bearish only and&nbsp;&nbsp;buy signal when it is Bullish"</span> <span class="comment">//--- indicator settings</span> <span class="preprocessor">#property </span><span class="macro">indicator_chart_window</span> <span class="preprocessor">#property </span><span class="macro">indicator_buffers</span> <span class="number">2</span> <span class="preprocessor">#property </span><span class="macro">indicator_plots</span> <span class="number">2</span> <span class="preprocessor">#property </span><span class="macro">indicator_type1</span> <span class="macro">DRAW_ARROW</span> <span class="preprocessor">#property </span><span class="macro">indicator_width1</span> <span class="number">5</span> <span class="preprocessor">#property </span><span class="macro">indicator_color1</span> <span class="number">0xFFAA00</span> <span class="preprocessor">#property </span><span class="macro">indicator_label1</span> <span class="string">"Buy"</span> <span class="preprocessor">#property </span><span class="macro">indicator_type2</span> <span class="macro">DRAW_ARROW</span> <span class="preprocessor">#property </span><span class="macro">indicator_width2</span> <span class="number">5</span> <span class="preprocessor">#property </span><span class="macro">indicator_color2</span> <span class="number">0x0000FF</span> <span class="preprocessor">#property </span><span class="macro">indicator_label2</span> <span class="string">"Sell"</span> <span class="preprocessor">#define </span>PLOT_MAXIMUM_BARS_BACK <span class="number">5000</span> <span class="preprocessor">#define </span>OMIT_OLDEST_BARS <span class="number">50</span> <span class="comment">//--- indicator buffers</span> <span class="keyword">double</span> Buffer1[]; <span class="keyword">double</span> Buffer2[]; <span class="keyword">double</span> myPoint; <span class="comment">//initialized in OnInit</span> <span class="keyword">int</span> MA_handle; <span class="keyword">double</span> MA[]; <span class="keyword">int</span> MA_handle2; <span class="keyword">double</span> MA2[]; <span class="keyword">double</span> Low[]; <span class="keyword">double</span> High[]; <span class="keyword">void</span> myAlert(<span class="keyword">string</span> type, <span class="keyword">string</span> message) &nbsp;&nbsp;{ &nbsp;&nbsp; <span class="keyword">if</span>(type == <span class="string">"print"</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>(message); &nbsp;&nbsp; <span class="keyword">else</span> <span class="keyword">if</span>(type == <span class="string">"error"</span>) &nbsp;&nbsp;&nbsp;&nbsp; {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>(type+<span class="string">"Trend constraint @ "</span>+<span class="functions">Symbol</span>()+<span class="string">","</span>+<span class="functions">IntegerToString</span>(<span class="functions">Period</span>())+<span class="string">""</span>+message);

&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; <span class="keyword">else</span> <span class="keyword">if</span>(type == <span class="string">"order"</span>) &nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; <span class="keyword">else</span> <span class="keyword">if</span>(type == <span class="string">"modify"</span>) &nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;} <span class="comment">// Custom indicator initialization function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span> <span class="keyword">int</span> <span class="functions">OnInit</span>() &nbsp;&nbsp;{&nbsp;&nbsp; &nbsp;&nbsp; <span class="functions">SetIndexBuffer</span>(<span class="number">0</span>, Buffer1); &nbsp;&nbsp; <span class="functions">PlotIndexSetDouble</span>(<span class="number">0</span>, <span class="macro">PLOT_EMPTY_VALUE</span>, <span class="macro">EMPTY_VALUE</span>); &nbsp;&nbsp; <span class="functions">PlotIndexSetInteger</span>(<span class="number">0</span>, <span class="macro">PLOT_DRAW_BEGIN</span>, <span class="functions">MathMax</span>(<span class="functions">Bars</span>(<span class="functions">Symbol</span>(), <span class="macro">PERIOD_CURRENT</span>)-PLOT_MAXIMUM_BARS_BACK+<span class="number">1</span>, OMIT_OLDEST_BARS+<span class="number">1</span>)); &nbsp;&nbsp; <span class="functions">PlotIndexSetInteger</span>(<span class="number">0</span>, <span class="macro">PLOT_ARROW</span>, <span class="number">241</span>);

「双均线箭头指标的初始化与计算骨架」

这段逻辑是自定义指标收尾的硬骨头:先把第二个缓冲区和绘图属性绑死,再抓两条 EMA 句柄,最后在 OnCalculate 里把序列方向和起始计算量定下来。 SetIndexBuffer(1, Buffer2) 把箭头画在缓冲区 1;PlotIndexSetInteger 里 PLOT_ARROW 设成 242,也就是 Wingdings 字体里的下箭头字形。PLOT_DRAW_BEGIN 用 MathMax 卡住老数据,避免历史太深把图刷爆。 myPoint 的处理很实在:当报价小数位是 5 或 3(即黄金/外汇的基点报价),Point() 乘 10 才等于真实 1 点距离。MA_handle 与 MA_handle2 分别取周期 7 和 21 的 Close 价 EMA,任一句柄返回负值就 Print 错误并 INIT_FAILED 退出——这一步不挡住,后面算信号全是空。 OnCalculate 里 limit = rates_total - prev_calculated,只算新增 K 线;ArraySetAsSeries 把两个缓冲区的索引方向翻成时间倒序,prev_calculated < 1 时先用 ArrayInitialize 把 Buffer1 填 EMPTY_VALUE,防止首根乱画。外汇和贵金属杠杆高,这类指标仅作概率参考,实盘前务必在 MT5 策略测试器跑一遍。

MQL5 / C++
SetIndexBuffer(class="num">1, Buffer2);
PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetInteger(class="num">1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+class="num">1, OMIT_OLDEST_BARS+class="num">1));
PlotIndexSetInteger(class="num">1, PLOT_ARROW, class="num">242);
class=class="str">"cmt">//initialize myPoint
myPoint = Point();
if(Digits() == class="num">5 || Digits() == class="num">3)
  {
    myPoint *= class="num">10;
  }
MA_handle = iMA(NULL, PERIOD_CURRENT, class="num">7, class="num">0, MODE_EMA, PRICE_CLOSE);
if(MA_handle < class="num">0)
  {
    Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
    Print("Runtime error = ", GetLastError());
    class="kw">return(INIT_FAILED);
  }

MA_handle2 = iMA(NULL, PERIOD_CURRENT, class="num">21, class="num">0, MODE_EMA, PRICE_CLOSE);
if(MA_handle2 < class="num">0)
  {
    Print("The creation of iMA has failed: MA_handle2=", INVALID_HANDLE);
    Print("Runtime error = ", GetLastError());
    class="kw">return(INIT_FAILED);
  }

class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//Custom indicator iteration function
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime& time[],
                class="kw">const class="type">class="kw">double& open[],
                class="kw">const class="type">class="kw">double& high[],
                class="kw">const class="type">class="kw">double& low[],
                class="kw">const class="type">class="kw">double& close[],
                class="kw">const class="type">long& tick_volume[],
                class="kw">const class="type">long& volume[],
                class="kw">const class="type">int& spread[])
  {
   class="type">int limit = rates_total - prev_calculated;
   class=class="str">"cmt">//--- counting from class="num">0 to rates_total
   ArraySetAsSeries(Buffer1, true);
   ArraySetAsSeries(Buffer2, true);
   class=class="str">"cmt">//--- initial zero
   if(prev_calculated < class="num">1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);

常见问题

先读日线最近N根K线的高低点方向,只在五分钟信号与日线方向一致时才放行,反向信号直接忽略。
裸均线交叉在震荡市会频繁反向,建议叠加更高周期趋势约束,或拉大均线周期减少噪音。
可以,小布盯盘的AIGC会直接标注品种多周期方向,你打开对应页就能看日线框定的可交易区间。
需在init里绑定均线句柄并清缓存,oncalc里用CopyBuffer取值比较,跨周期记得单独开句柄。
概率上不推荐,日线栅栏内逆势单盈亏比差,仅极端吞没反转形态可小仓试探。