MQL5 简介(第 17 部分):构建趋势反转 EA 交易·综合运用
用最近四根柱捕捉趋势线的突破与反转
在 MT5 里做趋势线交易,核心不是把整段历史都扫一遍,而是盯住最后四根已收盘的柱。突破和反转大多发生在最近行情里,扫全图反而慢半拍。MQL5 的内置函数能直接按时间取任意趋势线在指定柱的价格水平,这让“价与线互动”的判断可以纯程序化。 需要注意一个细节:我们用 CopyTime() 从索引 1 开始复制,跳过最右边那根还没收盘的 ticking bar。代码里的“第 0 柱”其实是图表上从右数第二根——它已经收盘,数据可靠。把趋势线在第 0、1、2、3 根柱的值分别存进 t_line_value、t1_line_value、t2_line_value、t3_line_value,再用 Comment() 打印到左上角,肉眼就能比对线位变化。 反转不是“碰到线就买”那么简单。影线触线后收阳、阴线触线后下一根阳线确认、连跌几根才出阳线、以及假突破后立刻看涨吞回,都算合格反转;但若是两根阴线已经实穿趋势线,后面再出阳线也只是突破后的回测噪音,不能当作反转信号。算法里要用 prev_touch 类标志锁住第一次有效信号,否则同一波反转会触发多次进场。 突破回测的逻辑正好反过来:价格跌破上升趋势线后,若影线回抽触碰原支撑(已转阻力)但开盘价在线下、随后阴线确认,就下卖单。下降趋势线同理,只是把方向颠倒——重点找看跌反转和看涨突破。外汇与贵金属杠杆高,趋势线被假突破扫损是常态,任何信号都只是概率倾向,实盘前务必用策略测试器跑近 3 个月数据验证。
◍ 用两次摆动低点把上升趋势线画出来
上升趋势线的自动绘制,核心是先抓最近的一个摆动低点,再往前找一个更低的摆动低点。代码里用两次循环完成:第一次从 LookbackBars 扫到 bars_check - LookbackBars,遇到 IsSwingLow 就记 first_low 和 first_low_time 然后 break;第二次同样区间,要求 low_price[i] < first_low 且时间更早,找到即记 second_low。 两个低点确认后,用 ObjectCreate 以 second_low_time/second_low 到 first_low_time/first_low 拉出 OBJ_TREND。先设 OBJPROP_TIMEFRAMES 为 OBJ_NO_PERIODS 暂时全周期隐藏,避免半成品线闪现。 只有当 first_low > second_low 且 second_low > 0 时结构才有效,此时开 OBJPROP_RAY_RIGHT 向右延伸,TIMEFRAMES 改 OBJ_ALL_PERIODS 显示,颜色 clrBlue、宽度 3。外汇与贵金属波动剧烈,此类自动画线在高波动时段可能频繁重绘,需以实盘前回测验证。 画线后立刻取线在最近四根 bar 对应时间的价位:t_line_value 取 time_price[0](当前 bar),t1_line_value 取 time_price[1](一根前),t2_line_value 取 time_price[2](两根前)。这几个值后续可用于判断价格与趋势线的距离关系,建议在 MT5 里打印出来核对与肉眼画线偏差。
class="type">class="kw">double t_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the most recent bar(not the ticking bar) class="type">class="kw">double t1_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the second most recent bar class="type">class="kw">double t2_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the third most recent bar class="type">class="kw">double t3_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the fourth most recent bar if(allow_uptrend) { class=class="str">"cmt">// First loop: Find the most recent swing low(first low) for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check if current point is a swing low if(IsSwingLow(low_price, i, LookbackBars)) { class=class="str">"cmt">// Store price and time of the first(latest) swing low first_low = low_price[i]; first_low_time = time_price[i]; break; class=class="str">"cmt">// Exit loop after finding the first swing low } } class=class="str">"cmt">// Second loop: Find an earlier swing low that is lower than the first low for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check for earlier swing low that is lower and occurs before the first low if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time) { class=class="str">"cmt">// Store price and time of the second(older) swing low second_low = low_price[i]; second_low_time = time_price[i]; break; class=class="str">"cmt">// Exit loop after finding the second swing low } } class=class="str">"cmt">// Create an ascending trend line from the second low to the first low ObjectCreate(chart_id, up_trend, OBJ_TREND, class="num">0, second_low_time, second_low, first_low_time, first_low); ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); class=class="str">"cmt">// Temporarily hide line on all timeframes class=class="str">"cmt">// If the swing structure is valid(i.e., second low is lower than first) if(first_low > second_low && second_low > class="num">0) { class=class="str">"cmt">// Extend the trend line to the right ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true); class=class="str">"cmt">// Show the trend line on all timeframes ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); class=class="str">"cmt">// Set visual properties: class="type">color and thickness ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, class="num">3); class=class="str">"cmt">// Get the price values of the trend line at the corresponding times of the four most recent bars t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">0], class="num">0); class=class="str">"cmt">// Current bar t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">1], class="num">0); class=class="str">"cmt">// One bar ago t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">2], class="num">0); class=class="str">"cmt">// Two bars ago
「参数面板与数据结构怎么摆」
这段脚本把交易开关和风控参数全摊在 input 区,方便在 MT5 属性框里直接调。趋势线绘制、突破/反转下单、手数、止损止盈点数各自独立成变量,改一个不影响其余逻辑。 lookbackBars 设为 5,代表找摆动低点时只回看最近 5 根 K 线;lot_size 给的是 0.6 标准手,sl_points=10、tp_points=50,盈亏比倾向 5:1,但外汇和贵金属杠杆高,实际回撤可能远超预设点数。 下方声明了一组价格数组:close_price、open_price、low_price、high_price 以及 time_price,用来缓存 500 根历史 bars(bars_check=500)的 OHLC 与时间。first_low / second_low 配合各自时间字段,专门接住两个摆动低点,供后面画上升趋势线用。 代码里还顺手取了 chart_id = ChartID(),后续 ObjectGetValueByTime 读趋势线在指定时间的数值就靠它定位图表。
t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">3], class="num">0); class=class="str">"cmt">// Three bars ago Comment("Ascending trend tine value for the last class="num">4 Bars", "\nBar class="num">0: ", DoubleToString(t_line_value, _Digits), "\nBar class="num">1: ", DoubleToString(t1_line_value, _Digits), "\nBar class="num">2: ", DoubleToString(t2_line_value, _Digits), "\nBar class="num">3: ", DoubleToString(t3_line_value, _Digits)); } } class="macro">#include <Trade/Trade.mqh> CTrade trade; class="type">int MagicNumber = class="num">532127; class=class="str">"cmt">// Timeframe to use for retrieving candlestick data(class="kw">default is the current chart timeframe) input ENUM_TIMEFRAMES time_frame = PERIOD_CURRENT; class=class="str">"cmt">// Input to enable or disable drawing of the ascending trend line(true = allow drawing) input class="type">bool allow_uptrend = true; class=class="str">"cmt">// Number of candles to look back when identifying swing lows for drawing the trend line input class="type">int LookbackBars = class="num">5; class=class="str">"cmt">// Input to enable or disable drawing of the descebding trend line(true = allow drawing) input class="type">bool allow_downtrend = true; input class="type">bool allow_break_out = true; class=class="str">"cmt">// Enable or disable trade execution on trend line breakout(true = allow) input class="type">bool allow_reversal = true; class=class="str">"cmt">// Enable or disable trade execution on trend line reversal(true = allow) input class="type">class="kw">double lot_size = class="num">0.6; class=class="str">"cmt">// Lot size for each trade input class="type">class="kw">double sl_points = class="num">10; class=class="str">"cmt">// Stop Loss in points from entry price input class="type">class="kw">double tp_points = class="num">50; class=class="str">"cmt">// Take Profit in points from entry price class=class="str">"cmt">// Number of past bars(candlesticks) to check class="type">int bars_check = class="num">500; class=class="str">"cmt">// Arrays to store candlestick data class="type">class="kw">double close_price[]; class=class="str">"cmt">// Stores close prices class="type">class="kw">double open_price[]; class=class="str">"cmt">// Stores open prices class="type">class="kw">double low_price[]; class=class="str">"cmt">// Stores low prices class="type">class="kw">double high_price[]; class=class="str">"cmt">// Stores high prices class="type">class="kw">datetime time_price[]; class=class="str">"cmt">// Stores time data for each candle class="type">class="kw">double first_low; class=class="str">"cmt">// Price value of the first identified swing low class="type">class="kw">datetime first_low_time; class=class="str">"cmt">// Time when the first swing low occurred class="type">class="kw">double second_low; class=class="str">"cmt">// Price value of the second identified swing low class="type">class="kw">datetime second_low_time; class=class="str">"cmt">// Time when the second swing low occurred class="type">class="kw">string up_trend = "Up Trend"; class=class="str">"cmt">// Label used to name the ascending trend line object on the chart class="type">long chart_id = ChartID(); class=class="str">"cmt">// Stores the current chart ID for referencing during object creation or manipulation class="type">class="kw">double first_high; class=class="str">"cmt">// Price value of the first identified swing high(latest high)
摆动高点与趋势线变量的落地写法
这段声明把下降结构里的两个摆动高点分别存了价格和时间:first_high_time 记最近一次摆动高点发生时刻,second_high 与 second_high_time 则锁住更早那根高点。down_trend 只是给图表下降趋势线对象打的标签,方便后续 ObjectCreate 直接复用。 t_line_value 到 t3_line_value 四个变量,分别取最近一根、倒数第二、第三、第四根已收盘 K 线处的上升趋势线价位。注意这里刻意避开「正在 tick 的当下根」,只用已闭合的 bar 算值,能躲掉实时毛刺造成的假突破信号。 lookbackf_time 用来框定回看边界,lastTradeBarTime 初始为 0,后面靠它防止同一根 bar 重复触发。OnInit 里把 close/open/low/high/time 五个数组全设成系列排列,索引 0 即最新 bar;trade.SetExpertMagicNumber 把魔数绑给交易对象。 OnDeinit 只干一件事:ObjectsDeleteAll 清掉图表上本 EA 画的线,避免切换周期留下垃圾。OnTick 开头用 CopyOpen/CopyClose/CopyLow/CopyHigh/CopyTime 从偏移 1 开始抓 bars_check 根历史数据——跳过第 0 根正是为了只用定型 K 线。最后一句取 SYMBOL_ASK 实时卖价,供后面判突破用。外汇与贵金属波动剧烈,这类结构仅作概率参考,实盘前请在 MT5 策略测试器跑至少 3 个月 tick 数据。
class="type">class="kw">datetime first_high_time; class=class="str">"cmt">// Time when the first swing high occurred class="type">class="kw">double second_high; class=class="str">"cmt">// Price value of the second identified swing high(older high) class="type">class="kw">datetime second_high_time; class=class="str">"cmt">// Time when the second swing high occurred class="type">class="kw">string down_trend = "Down Trend"; class=class="str">"cmt">// Label used to name the descending trend line object on the chart class="type">class="kw">double t_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the most recent bar(not the ticking bar) class="type">class="kw">double t1_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the second most recent bar class="type">class="kw">double t2_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the third most recent bar class="type">class="kw">double t3_line_value; class=class="str">"cmt">// Ascending trend line price level at the time of the fourth most recent bar class=class="str">"cmt">// Time boundary used to limit lookback for valid reversal setups class="type">class="kw">datetime lookbackf_time; class="type">class="kw">datetime lastTradeBarTime = class="num">0; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">// Set arrays as series so the newest bar is index class="num">0 ( start from the latest bar) ArraySetAsSeries(close_price, true); ArraySetAsSeries(open_price, true); ArraySetAsSeries(low_price, true); ArraySetAsSeries(high_price, true); ArraySetAsSeries(time_price, true); trade.SetExpertMagicNumber(MagicNumber); class="kw">return(INIT_SUCCEEDED); class=class="str">"cmt">// Signal that the EA initialized successfully } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { ObjectsDeleteAll(chart_id); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">// Copy the latest candlestick data into the arrays CopyOpen(_Symbol, time_frame, class="num">1, bars_check, open_price); class=class="str">"cmt">// Open prices CopyClose(_Symbol, time_frame, class="num">1, bars_check, close_price); class=class="str">"cmt">// Close prices CopyLow(_Symbol, time_frame, class="num">1, bars_check, low_price); class=class="str">"cmt">// Low prices CopyHigh(_Symbol, time_frame, class="num">1, bars_check, high_price); class=class="str">"cmt">// High prices CopyTime(_Symbol, time_frame, class="num">1, bars_check, time_price); class=class="str">"cmt">// Candle times class="type">class="kw">double ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
◍ 上升趋势线的双低点抓取逻辑
在 MT5 里画一条靠谱的上升趋势线,核心不是连两个点,而是先确认摆动低点(swing low)的结构顺序。下面这段逻辑只处理用户允许绘制上升线的情况,用两次循环分别从近到远找低点。 第一次循环从 LookbackBars 扫到 bars_check - LookbackBars,调用 IsSwingLow 判定当前 Bar 是否为摆动低点,命中就记录 first_low 与 first_low_time,并顺手存了 i-3 的 time 作参考,随后直接 break。第二次循环同样区间,但要求低点时间早于 first_low_time 且价格更低,找到后记为 second_low / second_low_time,这就是趋势线的「远端锚点」。 锚点齐了就用 ObjectCreate 以 OBJ_TREND 画出来,先设 OBJPROP_TIMEFRAMES 为 OBJ_NO_PERIODS 临时隐藏。只有当 first_low > second_low 且 second_low > 0 时,才把 OBJPROP_RAY_RIGHT 开成 true 向右延伸,并改 OBJ_ALL_PERIODS 全周期显示,颜色蓝、线宽 3。 最后用 ObjectGetValueByTime 取当前 Bar 时间对应趋势线价位 t_line_value,后续拿它和实时价比,就能判断价格是否还在线上方。外汇与贵金属波动剧烈,这类画线仅描述已有结构,不代表后续一定沿趋势运行,实盘须自担高风险。
class="type">class="kw">datetime currentBarTime = iTime(_Symbol, time_frame, class="num">0); class=class="str">"cmt">// If the user allows drawing of ascending trend line if(allow_uptrend) { class=class="str">"cmt">// First loop: Find the most recent swing low(first low) for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check if current point is a swing low if(IsSwingLow(low_price, i, LookbackBars)) { class=class="str">"cmt">// Store price and time of the first(latest) swing low first_low = low_price[i]; first_low_time = time_price[i]; lookbackf_time = time_price[i - class="num">3]; break; class=class="str">"cmt">// Exit loop after finding the first swing low } } class=class="str">"cmt">// Second loop: Find an earlier swing low that is lower than the first low for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check for earlier swing low that is lower and occurs before the first low if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time) { class=class="str">"cmt">// Store price and time of the second(older) swing low second_low = low_price[i]; second_low_time = time_price[i]; break; class=class="str">"cmt">// Exit loop after finding the second swing low } } class=class="str">"cmt">// Create an ascending trend line from the second low to the first low ObjectCreate(chart_id, up_trend, OBJ_TREND, class="num">0, second_low_time, second_low, first_low_time, first_low); ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); class=class="str">"cmt">// Temporarily hide line on all timeframes class=class="str">"cmt">// If the swing structure is valid(i.e., second low is lower than first) if(first_low > second_low && second_low > class="num">0) { class=class="str">"cmt">// Extend the trend line to the right ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true); class=class="str">"cmt">// Show the trend line on all timeframes ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); class=class="str">"cmt">// Set visual properties: class="type">color and thickness ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, class="num">3); class=class="str">"cmt">// Get the price values of the trend line at the corresponding times of the four most recent bars t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">0], class="num">0); class=class="str">"cmt">// Current bar }
「回踩趋势线的反转 Wick 判定逻辑」
在上升趋势线策略里,光画好线不够,得确认价格是不是真的在线上方完成了「刺透再收回」的动作。下面这段逻辑专门在最近 4 根 bar 内扫描下影线跌破趋势线、但开盘价仍在线上方的形态,这往往暗示回调抛压被接住,后续存在多头重新控场的可能。 扫描从当前 bar 往前数 i=0 到 3,只要某根 bar 的 low 低于对应时刻趋势线值、且 open 高于该值,就记为一次有效反转区接触;随后从接触点往当前回看,找第一根实体收在趋势线上方的阳线作为确认信号,并用 Bars() 算出它距离现在多少根。外汇与贵金属杠杆高,这类刺透形态失败率不低,需结合更大周期过滤。 为了避免同一处反转区反复发信号,代码末尾还检查了前 1~2 根 bar 是否 already 出现过「下影触线 + 阳线实体」的组合(prev_touch 标记)。若已出现过,则本次不再重复触发,实盘里能少挨几次假突破的巴掌。 开 MT5 把这段塞进 EA 的 OnCalculate 里,把 up_trend 换成你手动或自动画的上升线名,跑 EURUSD 的 M15 就能看到 no_bars 在反转确认后通常落在 0~3 之间。
t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">1], class="num">0); class=class="str">"cmt">// One bar ago t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">2], class="num">0); class=class="str">"cmt">// Two bars ago t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">3], class="num">0); class=class="str">"cmt">// Three bars ago class=class="str">"cmt">// Number of bars between the valid bullish confirmation candle and current time class="type">int no_bars = class="num">0; class=class="str">"cmt">// Loop through the last class="num">4 bars to check for reversal wick touch on the trend line for(class="type">int i = class="num">0; i <= class="num">3; i++) { class=class="str">"cmt">// Condition: Wick of the candle touches below the trend line but opens above it(indicating a potential reversal zone) if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0) && open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0)) { class=class="str">"cmt">// Check if there&class="macro">#x27;s a bullish confirmation candle after the wick touch(within or immediately after) for(class="type">int j = i; j >= class="num">0; j--) { class=class="str">"cmt">// Bullish candle that closed above the trend line if(close_price[j] > open_price[j] && close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], class="num">0)) { class=class="str">"cmt">// Count how many bars ago this confirmation occurred no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()); break; } } break; class=class="str">"cmt">// Exit after first valid reversal zone is found } } class=class="str">"cmt">// Check whether a similar wick touch(reversal) happened recently to avoid repeated signals class="type">bool prev_touch = false; if((low_price[class="num">1] < t1_line_value && close_price[class="num">1] > open_price[class="num">1]) || class=class="str">"cmt">// Bar class="num">1 had reversal wick and bullish body (low_price[class="num">2] < t2_line_value && close_price[class="num">2] > open_price[class="num">2])) class=class="str">"cmt">// Bar class="num">2 had reversal wick and bullish body {
下降趋势线反转买点的条件拼装
这段逻辑只处理下降趋势线启用后的买点判定,核心是先在前文标记出最近的一个摆动高点,再拿它去连更早的高点形成趋势线。代码里用两段循环:第一段从 LookbackBars 扫到 bars_check - LookbackBars,遇到 IsSwingHigh 就存下 first_high 和 first_high_time 并 break,保证只取最新那个摆动高点。 买触发放在另一个 if 块里,要求最近 4 根 K 线中至少有一根下影刺穿趋势线但开盘在线上方(low < t_line_value && open > t_line_value,依次检查 0~3 号 bar 对应的 t/t1/t2/t3 线值),且当前阳线收在趋势线上方。 确认棒必须在触碰后 3 根内(no_bars < 3),同时 prev_touch 为 false 避免重复信号,time_price[3] 要晚于 lookbackf_time 阈值,且 allow_reversal 开启、currentBarTime 不等于 lastTradeBarTime。全部满足才 trade.Buy,并用 lastTradeBarTime 更新防重。 外汇与贵金属杠杆高,这类刺穿反转信号在历史回测中假突破概率不低,实盘前建议在 MT5 策略测试器用 2020—2024 年 XAUUSD 小时图跑一遍,把 sl_points / tp_points 拆开看盈亏比分布。
prev_touch = true; class=class="str">"cmt">// Flag that a recent touch already occurred } class=class="str">"cmt">// Final condition for executing a BUY trade on a reversal setup if( class=class="str">"cmt">// One of the recent class="num">4 bars touched and rejected the trend line(wick below, open above), AND ((low_price[class="num">0] < t_line_value && open_price[class="num">0] > t_line_value) || (low_price[class="num">1] < t1_line_value && open_price[class="num">1] > t1_line_value) || (low_price[class="num">2] < t2_line_value && open_price[class="num">2] > t2_line_value) || (low_price[class="num">3] < t3_line_value && open_price[class="num">3] > t3_line_value)) && class=class="str">"cmt">// Current candle must be bullish and close above the trend line (close_price[class="num">0] > open_price[class="num">0]) && close_price[class="num">0] > t_line_value && class=class="str">"cmt">// The bullish confirmation must occur within class="num">3 bars (no_bars < class="num">3) && class=class="str">"cmt">// No recent wick reversal signal already processed prev_touch == false && class=class="str">"cmt">// The signal must be more recent than the lookback time threshold (time_price[class="num">3] > lookbackf_time) && class=class="str">"cmt">// Reversal signals are allowed and this signal is not duplicated from the same bar (allow_reversal == true && currentBarTime != lastTradeBarTime) ) { class=class="str">"cmt">// Execute BUY trade with defined lot size, SL and TP trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points); lastTradeBarTime = currentBarTime; class=class="str">"cmt">// Update last trade bar time to avoid duplicate signals } } } class=class="str">"cmt">// class=class="str">"cmt">// Only proceed if drawing descending trend lines is enabled if(allow_downtrend) { class=class="str">"cmt">// First loop: Find the most recent swing high(first high) for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check if the current bar is a swing high if(IsSwingHigh(high_price, i, LookbackBars)) { class=class="str">"cmt">// Store the price and time of this latest swing high first_high = high_price[i]; first_high_time = time_price[i]; break; class=class="str">"cmt">// Exit loop once the first swing high is found } } class=class="str">"cmt">// Second loop: Find an earlier swing high that is higher than the first high and occurred before it
◍ 下降趋势线的第二个摆动高点定位
在已捕获第一个摆动高点后,代码从 LookbackBars 位置向前扫到 bars_check - LookbackBars,专门寻找时间更早、价格却更高的摆动高点。一旦通过 IsSwingHigh 判定且 high_price[i] > first_high 且时间更早,就记下 second_high 与 second_high_time 并 break,避免无意义遍历。 找到两个高点后,ObjectCreate 用 OBJ_TREND 在 chart_id 上连出趋势线,但先用 OBJPROP_TIMEFRAMES 设为 OBJ_NO_PERIODS 隐藏,防止在部分周期上画出半截线。 结构校验要求 first_high < second_high 且 second_high > 0,才确认是下降连线(后期高点更低)。通过后把 OBJPROP_RAY_RIGHT 开为真向右延伸,OBJPROP_TIMEFRAMES 改 OBJ_ALL_PERIODS 全周期可见,颜色 clrDarkGreen、宽度 3 像素,便于肉眼跟踪。 IsSwingLow 与 IsSwingHigh 是对称判定:以 index 为中心向左右各看 lookback 根 K 线,只要 low[index] 大于任一侧就非低点。实盘里 lookback 取 2~5 较稳,过大容易漏掉紧凑波段的高风险外汇与贵金属信号。 下面这段是原文里 High 判定的入口,Low 判定同理可对照抄写验证。
for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { class=class="str">"cmt">// Check for earlier swing high that is higher and happened before the first one if(IsSwingHigh(high_price, i, LookbackBars) && high_price[i] > first_high && time_price[i] < first_high_time) { class=class="str">"cmt">// Store the price and time of this older swing high second_high = high_price[i]; second_high_time = time_price[i]; break; class=class="str">"cmt">// Exit loop once the second swing high is found } } class=class="str">"cmt">// Create a trend line object from the second swing high to the first swing high ObjectCreate(chart_id, down_trend, OBJ_TREND, class="num">0, second_high_time, second_high, first_high_time, first_high); class=class="str">"cmt">// Initially hide the trend line across all timeframes to avoid partial drawing ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); class=class="str">"cmt">// Validate the swing structure: class=class="str">"cmt">// The older swing high should be higher than the later swing high to confirm a descending trend line if(first_high < second_high && second_high > class="num">0) { class=class="str">"cmt">// Extend the trend line indefinitely to the right for better visual guidance ObjectSetInteger(chart_id, down_trend, OBJPROP_RAY_RIGHT, true); class=class="str">"cmt">// Make the trend line visible on all chart timeframes ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); class=class="str">"cmt">// Set the trend line class="type">color to dark green for clear distinction ObjectSetInteger(chart_id, down_trend, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the thickness of the trend line to class="num">3 pixels for better visibility ObjectSetInteger(chart_id, down_trend, OBJPROP_WIDTH, class="num">3); } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| FUNCTION FOR LOWS | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool IsSwingLow(const class="type">class="kw">double &low[], class="type">int index, class="type">int lookback) { for(class="type">int i = class="num">1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) class="kw">return false; } class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| FUNCTION FOR HIGHS | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool IsSwingHigh(const class="type">class="kw">double &high[], class="type">int index, class="type">int lookback) {
「回踩趋势线的多条件过滤」
这段逻辑在干一件事:只有当价格从上升趋势线外侧回踩、且近期没有重复触发时,才允许开多。外汇与贵金属杠杆高,这类信号失败概率不低,实盘前必须在 MT5 策略测试器跑一遍。 先看局部高点判定:以当前 K 线为锚,向左右各扩展 lookback 根,只要存在更高高点就返回 false,否则视为局部顶。
for(class="type">int i = class="num">1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) class="kw">return false; } class="kw">return true;
class="type">int no_bars = class="num">0; for(class="type">int i = class="num">0; i <= class="num">3; i++) { if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0) && open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0)) { for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] > open_price[j] && close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], class="num">0)) { no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()); break; } } break; } }
class="type">bool prev_touch = false; if((low_price[class="num">1] < t1_line_value && close_price[class="num">1] > open_price[class="num">1]) || (low_price[class="num">2] < t2_line_value && close_price[class="num">2] > open_price[class="num">2])) { prev_touch = true; }
if( ((low_price[class="num">0] < t_line_value && open_price[class="num">0] > t_line_value) || (low_price[class="num">1] < t1_line_value && open_price[class="num">1] > t1_line_value) || (low_price[class="num">2] < t2_line_value && open_price[class="num">2] > t2_line_value) || (low_price[class="num">3] < t3_line_value && open_price[class="num">3] > t3_line_value)) && (close_price[class="num">0] > open_price[class="num">0]) && close_price[class="num">0] > t_line_value && (no_bars < class="num">3) && prev_touch == false && (time_price[class="num">3] > lookbackf_time) && (allow_reversal == true && currentBarTime != lastTradeBarTime) ) { trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points); lastTradeBarTime = currentBarTime; }
if(allow_uptrend) { for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingLow(low_price, i, LookbackBars)) { first_low = low_price[i]; first_low_time = time_price[i]; lookbackf_time = time_price[i - class="num">3]; break; } } for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time) {
for(class="type">int i = class="num">1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) class="kw">return false; } class="kw">return true; class="type">int no_bars = class="num">0; for(class="type">int i = class="num">0; i <= class="num">3; i++) { if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0) && open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0)) { for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] > open_price[j] && close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], class="num">0)) { no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()); break; } } break; } } class="type">bool prev_touch = false; if((low_price[class="num">1] < t1_line_value && close_price[class="num">1] > open_price[class="num">1]) || (low_price[class="num">2] < t2_line_value && close_price[class="num">2] > open_price[class="num">2])) { prev_touch = true; } if( ((low_price[class="num">0] < t_line_value && open_price[class="num">0] > t_line_value) || (low_price[class="num">1] < t1_line_value && open_price[class="num">1] > t1_line_value) || (low_price[class="num">2] < t2_line_value && open_price[class="num">2] > t2_line_value) || (low_price[class="num">3] < t3_line_value && open_price[class="num">3] > t3_line_value)) && (close_price[class="num">0] > open_price[class="num">0]) && close_price[class="num">0] > t_line_value && (no_bars < class="num">3) && prev_touch == false && (time_price[class="num">3] > lookbackf_time) && (allow_reversal == true && currentBarTime != lastTradeBarTime) ) { trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points); lastTradeBarTime = currentBarTime; } if(allow_uptrend) { for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingLow(low_price, i, LookbackBars)) { first_low = low_price[i]; first_low_time = time_price[i]; lookbackf_time = time_price[i - class="num">3]; break; } } for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingLow(low_price, i, LookbackBars) && low_price[i] < first_low && time_price[i] < first_low_time) {
上升趋势线的下影回踩判定
在找到第二个摆动低点后,代码先把它和第一个低点连成一条上升趋势线,并用 OBJ_TREND 创建、OBJ_NO_PERIODS 暂时隐藏,避免结构未确认前干扰视图。 只有当 first_low > second_low 且 second_low > 0 时,才把线向右延伸(RAY_RIGHT=true)、切到 OBJ_ALL_PERIODS 全周期显示,并设成蓝色 3 像素宽——这是多头结构成立的硬门槛。 随后取最近 4 根 K 线对应趋势线价位:t_line_value 到 t3_line_value 分别绑定 time_price[0]~[3]。循环里判定「下影刺穿但开盘在线上的回踩」:low_price[i] 低于线值且 open_price[i] 高于线值,倾向视为潜在反转区。 紧接着内层循环从 i 往当前扫,若存在 close_price[j] > open_price[j] 且收在线上,才算一根看涨确认蜡烛。外汇与贵金属波动剧烈,这种刺穿+收上行为只是概率信号,实盘前请在 MT5 用历史品种跑一遍验证触发频率。
second_low = low_price[i]; second_low_time = time_price[i]; break; } } ObjectCreate(chart_id, up_trend, OBJ_TREND, class="num">0, second_low_time, second_low, first_low_time, first_low); ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); if(first_low > second_low && second_low > class="num">0) { ObjectSetInteger(chart_id, up_trend, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, up_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); ObjectSetInteger(chart_id, up_trend, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, up_trend, OBJPROP_WIDTH, class="num">3); t_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">0], class="num">0); t1_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">1], class="num">0); t2_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">2], class="num">0); t3_line_value = ObjectGetValueByTime(chart_id, up_trend, time_price[class="num">3], class="num">0); class="type">int no_bars = class="num">0; for(class="type">int i = class="num">0; i <= class="num">3; i++) { if(low_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0) && open_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0)) { for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] > open_price[j] && close_price[j] > ObjectGetValueByTime(chart_id, up_trend, time_price[j], class="num">0))
◍ 反转确认的去重与买点触发
这段逻辑卡的是「趋势线刺透后三日内的看涨反转」买点。它先用 no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()) 算出确认信号距离当前过了多少根 K 线,只在 no_bars < 3 时才算有效,避免迟到信号在 MT5 上误触发。 为了防止同一处刺透反复报警,代码用 prev_touch 标记近期是否已出现过下影线 rejection:若第 1 根或第 2 根 K 线曾跌破 t1/t2 线且收阳,就把 prev_touch 置 true,后续同区信号直接屏蔽。 最终 Buy 条件是一条串起来的与逻辑:近 4 根中至少一根下影刺穿对应趋势线且开盘在线上方、当前 K 线收阳且收盘价站上 t_line_value、确认发生在 3 根内、prev_touch 为 false、信号时间晚于 lookbackf_time、且 allow_reversal 开启并排除同根 K 重复发单。 满足条件后 trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points) 按预设手数、止损、止盈下单,并刷新 lastTradeBarTime。外汇与贵金属杠杆高,这类反转触发可能失效,实盘前请在策略测试器用不同 time_frame 跑一遍 no_bars 阈值。
{
class=class="str">"cmt">// Count how many bars ago this confirmation occurred
no_bars = Bars(_Symbol, time_frame, time_price[j], TimeCurrent());
break;
}
break; class=class="str">"cmt">// Exit after first valid reversal zone is found
}
class=class="str">"cmt">// Check whether a similar wick touch(reversal) happened recently to avoid repeated signals
class="type">bool prev_touch = false;
if((low_price[class="num">1] < t1_line_value && close_price[class="num">1] > open_price[class="num">1]) || class=class="str">"cmt">// Bar class="num">1 had reversal wick and bullish body
(low_price[class="num">2] < t2_line_value && close_price[class="num">2] > open_price[class="num">2])) class=class="str">"cmt">// Bar class="num">2 had reversal wick and bullish body
{
prev_touch = true; class=class="str">"cmt">// Flag that a recent touch already occurred
}
class=class="str">"cmt">// Final condition for executing a BUY trade on a reversal setup
if(
class=class="str">"cmt">// One of the recent class="num">4 bars touched and rejected the trend line(wick below, open above), AND
((low_price[class="num">0] < t_line_value && open_price[class="num">0] > t_line_value) ||
(low_price[class="num">1] < t1_line_value && open_price[class="num">1] > t1_line_value) ||
(low_price[class="num">2] < t2_line_value && open_price[class="num">2] > t2_line_value) ||
(low_price[class="num">3] < t3_line_value && open_price[class="num">3] > t3_line_value))
&&
class=class="str">"cmt">// Current candle must be bullish and close above the trend line
(close_price[class="num">0] > open_price[class="num">0]) && close_price[class="num">0] > t_line_value
&&
class=class="str">"cmt">// The bullish confirmation must occur within class="num">3 bars
(no_bars < class="num">3)
&&
class=class="str">"cmt">// No recent wick reversal signal already processed
prev_touch == false
&&
class=class="str">"cmt">// The signal must be more recent than the lookback time threshold
(time_price[class="num">3] > lookbackf_time)
&&
class=class="str">"cmt">// Reversal signals are allowed and this signal is not duplicated from the same bar
(allow_reversal == true && currentBarTime != lastTradeBarTime)
)
{
class=class="str">"cmt">// Execute BUY trade with defined lot size, SL and TP
trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points);
lastTradeBarTime = currentBarTime; class=class="str">"cmt">// Update last trade bar time to avoid duplicate signals
}
class=class="str">"cmt">//BREAKOUT AND RETEST「上升趋势线假突破的空头确认逻辑」
在 MT5 里做趋势线破位回测,最怕把「刺穿影线」误判成有效跌破。下面这段逻辑专门捕捉上升趋势线被上方影线触碰、但实体仍收在趋势线之下的假突破后空头信号。 先用 prev_touch2 标记最近是否已有一次看跌影线拒绝,避免同一形态反复报警。接着扫最近 2 根 K 线:若第 1 根高点越过 t1_line_value 且收盘低于开盘,或第 2 根高点越过 t2_line_value、收盘低于开盘且开盘本身就在线下方,就置 prev_touch2 = true。 主循环只翻最近 4 根蜡烛。某根 high 大于 ObjectGetValueByTime 取到的趋势线值、但 open 小于该值,说明影线刺穿后回落;再从这根向前找收阴且收盘跌破趋势线的确认棒,用 Bars() 算它距当前多少根。若 no_bars2 落在 3 根内、当前棒收阴跌破线、且 prev_touch2 为假,才认定为可交易回测setup。外汇与贵金属杠杆高,此类假突破反转失败概率不低,实盘前务必在策略测试器跑足样本。
class="type">bool prev_touch2 = false; if((high_price[class="num">1] > t1_line_value && close_price[class="num">1] < open_price[class="num">1]) || (high_price[class="num">2] > t2_line_value && close_price[class="num">2] < open_price[class="num">2] && open_price[class="num">2] < t2_line_value)) { prev_touch2 = true; class=class="str">"cmt">// Set flag to avoid duplicate signals } class="type">int no_bars2 = class="num">0; for(class="type">int i = class="num">0; i <= class="num">3; i++) { if(high_price[i] > ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0) && open_price[i] < ObjectGetValueByTime(chart_id, up_trend, time_price[i], class="num">0)) { for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] < open_price[j] && close_price[j] < ObjectGetValueByTime(chart_id, up_trend, time_price[j], class="num">0)) { no_bars2 = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()); break; } } break; } }
下降趋势线的双顶摆动识别与绘制
在允许绘制下降趋势线时,脚本先用一轮循环从 LookbackBars 扫到 bars_check-LookbackBars,找最近的摆动高点(swing high)作为 first_high,并记录其时间。找到后立即 break,避免无谓遍历。 第二轮的判定更苛刻:必须是更早出现、且价格高于 first_high 的摆动高点,存为 second_high。只有「旧高 > 后来新高」这种结构,才能确认是一条真正向下的趋势线,而不是横盘里的噪声。 画线时用 ObjectCreate 以 second_high 连到 first_high,先设 OBJPROP_TIMEFRAMES 为 OBJ_NO_PERIODS 隐藏,等结构校验通过(first_high < second_high 且 second_high>0)才开 OBJPROP_RAY_RIGHT 向右延伸,并切到 OBJ_ALL_PERIODS 全周期可见。 前面卖单触发段也依赖这类线值:当 2 号或 3 号 K 线最高价刺穿对应 t 线而开盘在线下、当前 K 收盘回落且 no_bars2<3、prev_touch2 为假、允许突破时,才用 trade.Sell 按 ask+sl_points / ask-tp_points 下单,并写 lastTradeBarTime 防同根 K 重复信号。外汇与贵金属杠杆高,此类信号失效概率不低,实盘前务必在 MT5 策略测试器跑历史数据验证。
if(allow_downtrend) { for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingHigh(high_price, i, LookbackBars)) { first_high = high_price[i]; first_high_time = time_price[i]; break; } } for(class="type">int i = LookbackBars; i < bars_check - LookbackBars; i++) { if(IsSwingHigh(high_price, i, LookbackBars) && high_price[i] > first_high && time_price[i] < first_high_time) { second_high = high_price[i]; second_high_time = time_price[i]; break; } } ObjectCreate(chart_id, down_trend, OBJ_TREND, class="num">0, second_high_time, second_high, first_high_time, first_high); ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); if(first_high < second_high && second_high > class="num">0) { ObjectSetInteger(chart_id, down_trend, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, down_trend, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); } }
◍ 下破趋势线的反转触发判定
这段逻辑针对一条已绘制的下降趋势线(down_trend),先把它设为深绿色、线宽 3 像素,保证在 MT5 图表上肉眼可辨。随后用 ObjectGetValueByTime 抓取 time_price[0]~[3] 四个时刻趋势线的价格,分别存入 td_line_value 到 td3_line_value,作为后续刺穿判定的基准线。 核心是一个双重循环:外层遍历最近 4 根 K 线,若某根 high 站上趋势线而 open 在线下(向上刺穿),内层再往前找一根收盘为阴线且收在线下的 K 线,用 Bars() 算出距当前相隔多少根,赋给 no_bars。若 no_bars < 3,说明刺穿后很快被空头反压,倾向视为有效反转信号。 另外用 prev_touch 标记前 1~2 根是否已有刺穿未收破的情况,避免重复触发。最终判定要求:最近 1~3 根任一根向上刺穿、当前根收盘在线下且为阴线、前一根 open 也在线下、no_bars < 3、prev_touch 为假、且允许反转开关打开并避开同根 K 线重复交易——全部满足才进入信号块。外汇与贵金属波动剧烈,该条件仅提高概率,实盘须用小周期回测验证。
ObjectSetInteger(chart_id, down_trend, OBJPROP_COLOR, clrDarkGreen); class=class="str">"cmt">// Set the thickness of the trend line to class="num">3 pixels for better visibility ObjectSetInteger(chart_id, down_trend, OBJPROP_WIDTH, class="num">3); class=class="str">"cmt">//REVERSAL td_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[class="num">0],class="num">0); td1_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[class="num">1],class="num">0); td2_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[class="num">2],class="num">0); td3_line_value = ObjectGetValueByTime(chart_id,down_trend,time_price[class="num">3],class="num">0); class="type">int no_bars = class="num">0; for(class="type">int i = class="num">0; i <= class="num">3; i++) { if(high_price[i] > ObjectGetValueByTime(chart_id,down_trend,time_price[i],class="num">0) && open_price[i] < ObjectGetValueByTime(chart_id,down_trend,time_price[i],class="num">0) ) { for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] < open_price[j] && close_price[j] < ObjectGetValueByTime(chart_id,down_trend,time_price[j],class="num">0)) { no_bars = Bars(_Symbol,time_frame,time_price[j],TimeCurrent()); break; } } break; } } class="type">bool prev_touch = false; if((high_price[class="num">1] > td1_line_value && close_price[class="num">1] < open_price[class="num">1]) || (high_price[class="num">2] > td2_line_value && close_price[class="num">2] < open_price[class="num">2]) ) { prev_touch = true; } if(((high_price[class="num">1] >= td1_line_value && open_price[class="num">1] < td1_line_value) || (high_price[class="num">2] >= td2_line_value && open_price[class="num">2] < td2_line_value) || (high_price[class="num">3] >= td3_line_value && open_price[class="num">3] < td3_line_value) || (high_price[class="num">0] >= td_line_value)) && (close_price[class="num">0] < td_line_value && close_price[class="num">0] < open_price[class="num">0] && open_price[class="num">1] < td1_line_value) && (no_bars < class="num">3) && prev_touch == false && (allow_reversal == true && currentBarTime != lastTradeBarTime) ) {
「下降趋势线的下破回测买点判定」
在下跌趋势线策略里,突破回测的买点不是看价格碰一下线就进场,而是要先确认近期存在“下影线刺穿但实体收在线上方”的拒绝动作。代码用 prev_touch2 标记最近两根 K 线(索引 1、2)是否已出现过此类看涨拒绝,避免同一形态反复发信号。 具体扫描时,对最近 4 根 K 线(i=0 到 3)做外循环:当某根 low 低于趋势线值、但 open 高于趋势线值时,再向内回溯 j 寻找一根收盘大于开盘且收盘站上趋势线的看涨确认 candle,并用 Bars() 算出它距当前有多少根。若 no_bars2 落在最近 3 根以内、当前 candle 为阳线且收在线上、且 prev_touch2 仍为 false,才构成有效的 BUY 回测确认。 外汇与贵金属杠杆高,这类形态失效时反向破位概率不低,实盘前应在 MT5 用历史数据把 td1_line_value / td2_line_value 与循环窗口跑一遍,确认在你的周期上假信号频率可接受。
trade.Sell(lot_size,_Symbol,ask_price,ask_price + sl_points, ask_price - tp_points); lastTradeBarTime = currentBarTime; } class=class="str">"cmt">//BREAKOUT AMD RETEST class=class="str">"cmt">// Flag to track whether a recent bullish wick rejection(touch) already occurred class="type">bool prev_touch2 = false; class=class="str">"cmt">// Check the last class="num">2 candles for bullish rejection from below the descending trend line class=class="str">"cmt">// A bullish rejection occurs when the low goes below the trend line but closes above the open(bullish candle) if((low_price[class="num">1] < td1_line_value && close_price[class="num">1] > open_price[class="num">1]) || (low_price[class="num">2] < td2_line_value && close_price[class="num">2] > open_price[class="num">2] && open_price[class="num">2] > td2_line_value)) { prev_touch2 = true; class=class="str">"cmt">// Set flag to prevent duplicate signals from the same type of setup } class=class="str">"cmt">// Variable to hold how many bars ago a bullish confirmation candle occurred after wick rejection class="type">int no_bars2 = class="num">0; class=class="str">"cmt">// Loop through the last class="num">4 candles to detect a wick rejection of the descending trend line for(class="type">int i = class="num">0; i <= class="num">3; i++) { class=class="str">"cmt">// Condition: Candle wick(low) goes below the trend line, but the open is above it if(low_price[i] < ObjectGetValueByTime(chart_id, down_trend, time_price[i], class="num">0) && open_price[i] > ObjectGetValueByTime(chart_id, down_trend, time_price[i], class="num">0)) { class=class="str">"cmt">// Look backward for a bullish confirmation candle that closes above the trend line for(class="type">int j = i; j >= class="num">0; j--) { if(close_price[j] > open_price[j] && close_price[j] > ObjectGetValueByTime(chart_id, down_trend, time_price[j], class="num">0)) { class=class="str">"cmt">// Count how many bars ago that bullish confirmation happened no_bars2 = Bars(_Symbol, time_frame, time_price[j], TimeCurrent()); break; class=class="str">"cmt">// Exit inner loop once confirmation is found } } break; class=class="str">"cmt">// Exit outer loop after the first valid retest is processed } } class=class="str">"cmt">// Final conditions to confirm a breakout or retest for a BUY setup on descending trend line: class=class="str">"cmt">// class="num">1. One of the last class="num">4 candles had a wick below the trend line but opened above it class=class="str">"cmt">// class="num">2. Current candle is bullish and closed above the trend line class=class="str">"cmt">// class="num">3. A valid bullish confirmation occurred within the last class="num">3 bars class=class="str">"cmt">// class="num">4. No recent similar touch detected(prev_touch2 == false)
下破后回拉的多头触发判定
这段代码是趋势线向下突破失败后的多头入场逻辑,核心看最近 4 根 K 线中是否有任一根开盘在 TD 线之上、最低刺穿该线,且当前 bar 收阳并站回 td_line_value 上方。 条件里用 no_bars2 < 3 限制脱钩计数,prev_touch2 == false 确保前一根未触碰,time_price[3] > lookbackfd_time 卡掉过旧的信号——这几道门槛同时过,才允许发单。 外汇与贵金属杠杆高,刺穿回拉属于概率型反转信号,实盘须用迷你手数验证;MT5 里把 sl_points / tp_points 调成你惯用的 ATR 倍数,再开策略测试器跑 EURUSD 的 M15 看成交密度。 别把刺穿当反转铁证 最低价穿过线但收盘没站回去的 bar,在这套逻辑里直接被拒,说明编写者把“收回”当成确认,而不是“碰线”就动手。
class=class="str">"cmt">// class="num">5. Candle timestamps are valid(not too far back) class=class="str">"cmt">// class="num">6. Breakout trading is allowed, and this bar is not the same as the last trade bar if( ((low_price[class="num">0] < td_line_value && open_price[class="num">0] > td_line_value) || (low_price[class="num">1] < td1_line_value && open_price[class="num">1] > td1_line_value) || (low_price[class="num">2] < td2_line_value && open_price[class="num">2] > td2_line_value) || (low_price[class="num">3] < td3_line_value && open_price[class="num">3] > td3_line_value)) && (close_price[class="num">0] > open_price[class="num">0]) && close_price[class="num">0] > td_line_value && (no_bars2 < class="num">3) && prev_touch2 == false && (time_price[class="num">3] > lookbackfd_time) && (allow_break_out == true && currentBarTime != lastTradeBarTime) ) { class=class="str">"cmt">// All conditions met - place a BUY trade with defined SL and TP trade.Buy(lot_size, _Symbol, ask_price, ask_price - sl_points, ask_price + tp_points); class=class="str">"cmt">// Update the last trade time to avoid repeated trades from the same bar lastTradeBarTime = currentBarTime; }
◍ 把工具请下神坛
趋势线逻辑跑通之后,EA 只是帮你把通道、升降趋势线的突破与反转条件从手动比对变成实时响应,附件里的 Project_11_Trend_Line_EA.mq5 约 47 KB,可直接丢进 MT5 回测验证。 这套检索趋势线数值并比价执行的框架,也能平移到三角形、楔形或双顶双底;但外汇与贵金属杠杆高、滑点跳空频繁,自动交互触发不代表胜率有保证,停产或过度拟合都可能让你在实盘吃亏。 真要落地,先拿英镑兑美元这类低点差品种跑历史数据,看看突破过滤参数在你的 broker 环境里是不是还灵,再谈挂真金白银。