自动构造支撑和阻力线·进阶篇
(2/3)·手画支撑阻力总被主观线干扰?这篇把ZigZag极点筛选和线有效性标准拆成可复用的代码逻辑
◍ 下跌趋势线的交叉判定与绘图落地
这段逻辑干的事很直接:在已框定的 a、b 两点区间内,倒序扫 K 线,统计收盘价由上穿下一条斜率为 coef 的基准线的次数,计数存进 cross_bc。当 cross_bc 同时不大于 Intersection_bc 与 Intersection_ab 时,才认为满足“下降通道未被破坏”的条件 2 和 3,进而把该段写进 DownTrend 矩阵。 矩阵字段填的是 a/b 柱索引、ax/bx 价格坐标、deviation 绝对值以及 coef 斜率,y 作为自增下标。这意味着同一轮扫描可能识别出多条互不冲突的下降段,全部暂存,不等绘图。 真正画线在后面独立循环里发生:遍历 DownTrend,要求 a、b 均大于 0 且间距非 0,才调用 TrendCreate 在图表上生成名为 “DownTrend j” 的斜线对象,随后 ChartRedraw 强制重绘。外汇与贵金属波动剧烈,这类自动趋势线仅描述已发生结构,后续破位概率随消息面显著变化,实盘须手动复核。 把下面代码直接丢进 MT5 脚本段,改 Intersection_bc / Intersection_ab 两个阈值,就能看不同宽松度下画出的下降线数量差异。
for(class="type">int n=b-class="num">1; n>=class="num">0; n--) if(close[n]>(bx+coef*(b-n)) && close[n+class="num">1]<(bx+coef*(b-n+class="num">1))) cross_bc++; if(cross_bc<=Intersection_bc && cross_bc<=Intersection_ab)class=class="str">"cmt">// 定义是否满足条件 class="num">2 和 class="num">3 { class=class="str">"cmt">// 填写下降趋势矩阵 ArrayResize(DownTrend,y+class="num">1); DownTrend[y].a=a; DownTrend[y].b=b; DownTrend[y].ax=ax; DownTrend[y].bx=bx; DownTrend[y].dst=MathAbs(deviation); DownTrend[y].coef=coef; y++; } class=class="str">"cmt">// 使用获得的矩阵显示图表上的下跌趋势线 for(j=class="num">0; j<ArraySize(DownTrend); j++) { a=DownTrend[j].a; b=DownTrend[j].b; ax=DownTrend[j].ax; bx=DownTrend[j].bx; coef=DownTrend[j].coef; if(a>class="num">0 && b>class="num">0 && MathAbs(a-b)>class="num">0) { if(a>class="num">0 && b>class="num">0 && MathAbs(a-b)>class="num">0) { class=class="str">"cmt">//--- 创建趋势线 TrendCreate(class="num">0,"DownTrend "+class="type">class="kw">string(j),class="num">0,time[a],ax,time[b],bx,DColor,DStyle,DWidth,DBack,DSelection,DRayLeft,DRayRight,DHidden,DZOrder); ChartRedraw(); } } }
「顺着趋势在支撑阻力附近找进场点」
上升趋势里贴近支撑线买入、下跌趋势里贴近阻力线卖出,这是支撑阻力最基础的用法。横盘或形态整理阶段也一样适用,比如下跌转横盘后,阻力线附近卖出比支撑位买入成功概率更高;若向上趋势中出现三角形,则优先在三角形支撑线附近买。 价格靠近支撑阻力不代表一定会弹开,突破是常态。所以顺势交易前必须等确认:要么价格在该价位附近横住走稳,要么明确从该价位反弹再动手,避免假突破扫损。 从支撑线买入时,等价格突破整理区高点并回踩站稳再进,说明该位确实在牵引价格向上;阻力线卖出则反过来,等阻力区下方低点被跌破后开仓。 平仓要提前想好:支撑线买入的,在碰到大一级强阻力前了结,或次一级阻力位出场;若价格冲破该阻力,可持有博更大空间。例如三角形支撑买入后,持仓到价格突破三角形上沿续涨,再在下一条阻力线离场。外汇与贵金属杠杆高,假突破造成的连续扫损风险不可忽视。
指标趋势线驱动的 EA 三种交易逻辑
这套 EA 直接吃同图表上指标画出的趋势线,实时跟线操作。它本身带止损止盈、最大多空单数、尾随止损、盈亏平衡,以及反向信号平掉旧单的功能;高周期滤波器给了 MACD、RSI、WPR、MA 四种可选,EA 里的指标参数只用于回测,实盘以指标端配置为准。 开单逻辑分三档:mode 0 只在价格触碰趋势线后未破位、下一根收线仍守住时做回滚;mode 1 专做突破,要求刺穿线但收线未破、次根收线破位;mode 2 两种都吃。回测看,回滚单的盈利能力倾向低于突破单。 2015–2017 年 EURUSD H1 上,用 MACD 过滤(值小于 0 买、大于 0 卖),单方向每次只开 0.1 手,反向信号先平再反手。默认设置跑下来,只有 EURUSD 和 USDCHF 表现出尚可的结果——说明不能无脑参考所有趋势线,还要叠文章开头列的其他因子。 外汇和贵金属属高风险品种,支撑阻力线在重要新闻后常被秒破。实务上可给 EA 加新闻过滤器,或切到 RENKO 图表回避噪音。下面这段 signal() 是信号判定的核心,逐行拆一下。 int res=0 等是各滤波器与结果暂存;Use_macd 等开关为真才调对应函数拿信号。CopyOpen/High/Low/Close 取最近 3 根 K 线的开高低收进数组。ObjectsTotal 扫图表上所有 OBJ_TREND 对象,只认 UpTrend/DownTrend 前缀的线。 取线的两端价与时间,iBarShift 转成柱索引,kkk 算斜率,lvl/plvl 推当前与次根价位。mode 0 或 2 时:上升线且低刺破次根线、两根收线均站回线上给买信号 1;下降线对称给卖信号 2。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class="type">int</span> signal() { <span class="keyword">class="type">int</span> res=<span class="number">class="num">0</span>; <span class="keyword">class="type">int</span> macd=<span class="number">class="num">0</span>; <span class="keyword">class="type">int</span> rsi=<span class="number">class="num">0</span>; <span class="keyword">class="type">int</span> wpr=<span class="number">class="num">0</span>; <span class="keyword">class="type">int</span> ma=<span class="number">class="num">0</span>; <span class="keyword">if</span>(Use_macd==<span class="macro">true</span>)macd=macdS(); <span class="keyword">if</span>(Use_rsi==<span class="macro">true</span>)rsi=rsiS(); <span class="keyword">if</span>(Use_wpr==<span class="macro">true</span>)wpr=wprS(); <span class="keyword">if</span>(Use_ma==<span class="macro">true</span>)ma=maS(); <span class="functions">CopyOpen</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,O); <span class="functions">CopyHigh</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,H); <span class="functions">CopyLow</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,L); <span class="functions">CopyClose</span>(<span class="macro">NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,<span class="number">class="num">3</span>,C); Signals=<span class="number">class="num">0</span>; <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i=<span class="number">class="num">0</span>;i<<span class="functions">ObjectsTotal</span>(<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="macro">OBJ_TREND</span>);i++) { <span class="keyword">class="type">class="kw">string</span> sName=<span class="functions">ObjectName</span>(<span class="number">class="num">0</span>,i,<span class="number">class="num">0</span>,<span class="macro">OBJ_TREND</span>); <span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"UpTrend"</span>)==<span class="number">class="num">0</span> || <span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"DownTrend"</span>)==<span class="number">class="num">0</span>) { ax=<span class="functions">ObjectGetDouble</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_PRICE</span>,<span class="number">class="num">0</span>); bx=<span class="functions">ObjectGetDouble</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_PRICE</span>,<span class="number">class="num">1</span>); p1=(<span class="keyword">class="type">int</span>)<span class="functions">ObjectGetInteger</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_TIME</span>,<span class="number">class="num">0</span>); p2=(<span class="keyword">class="type">int</span>)<span class="functions">ObjectGetInteger</span>(<span class="number">class="num">0</span>,sName,<span class="macro">OBJPROP_TIME</span>,<span class="number">class="num">1</span>); a=<span class="functions">iBarShift</span>(p1); b=<span class="functions">iBarShift</span>(p2); kkk=(bx-ax)/(a-b); lvl=bx+kkk*b; plvl=bx+kkk*(b-<span class="number">class="num">1</span>); <span class="keyword">if</span>(mode==<span class="number">class="num">0</span> || mode==<span class="number">class="num">2</span>) { <span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"UpTrend"</span>)==<span class="number">class="num">0</span> && L[<span class="number">class="num">1</span>]<=plvl && C[<span class="number">class="num">1</span>]>plvl && C[<span class="number">class="num">0</span>]>lvl)Signals=<span class="number">class="num">1</span>; <span class="keyword">if</span>(<span class="functions">StringFind</span>(sName,<span class="class="type">class="kw">string">"DownTrend"</span>)==<span class="number">class="num">0</span> && H[<span class="number">class="num">1</span>]>=plvl && C[<span class="number">class="num">1</span>]<plvl && C[<span class="number">class="num">0</span>]<lvl)Signals=<span class="number">class="num">2</span>; }
◍ 信号合成与参数入口的衔接
趋势字串匹配只是第一步,真正下单前还要过一道过滤器。下面这段逻辑里,mode 为 1 或 2 时才启用 ZigZag 趋势名识别:前一根 K 线低点刺穿 plvl 但收盘收回其上、当前收盘跌回 lvl 下方,判为 UpTrend 反转空信号(Signals=2);反之高点刺穿 plvl 后收破、当前收过 lvl,判为 DownTrend 反转多信号(Signals=1)。 信号值出来后并不立刻成交,而是要和 MACD、RSI、WPR、MA 四个子指标确认位对齐。任意一项 Use_xxx 设为 false 就跳过该项;全部对应上才把 res 置为 1 或 2 返回。这种「与」逻辑意味着默认全开时,漏掉一个指标就不会出单。 参数区直接决定风险敞口。LotSize 默认 0.1 标准手,Slippage 给 30 点,StopLoss 与 TakeProfit 均为 0 表示不挂止损止盈——外汇与贵金属波动剧烈,0 止损在实盘里可能瞬间扩大亏损,务必手动填点数。 ZigZag 的三参数 _ExtDepth=12、_ExtDeviation=5、_ExtBackstep=3 控制拐点灵敏度;_fibo=30 是扩展位比率(百分比),_tolerance=200 作冗余容差。复制下面 input 块到 EA 头部,改 _ExtDepth 到 8 或 20 能在 MT5 上直观比对拐点密度变化。
if(mode==class="num">1 || mode==class="num">2) { if(StringFind(sName,"UpTrend")==class="num">0 && L[class="num">1]<=plvl && C[class="num">1]>plvl && C[class="num">0]<lvl)Signals=class="num">2; if(StringFind(sName,"DownTrend")==class="num">0 && H[class="num">1]>=plvl && C[class="num">1]<plvl && C[class="num">0]>lvl)Signals=class="num">1; } } } if(Signals==class="num">1 &&(macd==class="num">1 || Use_macd==false) && (rsi==class="num">1 || Use_rsi==false) && (wpr==class="num">1 || Use_wpr==false) && (ma==class="num">1 || Use_ma==false))res=class="num">1; if(Signals==class="num">2 &&(macd==class="num">2 || Use_macd==false) && (rsi==class="num">2 || Use_rsi==false) && (wpr==class="num">2 || Use_wpr==false) && (ma==class="num">2 || Use_ma==false))res=class="num">2; class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">class="kw">string s="-------------------------------------------"; class=class="str">"cmt">// 主设置 class="kw">input class="type">int Magic=class="num">12345; class="kw">input class="type">class="kw">double LotSize=class="num">0.1; class="kw">input class="type">int Slippage=class="num">30; class=class="str">"cmt">//滑点, 点数 class="kw">input class="type">int StopLoss=class="num">0; class=class="str">"cmt">//止损, 点数 class="kw">input class="type">int TakeProfit=class="num">0; class=class="str">"cmt">//止盈, 点数 class="kw">input class="type">int TrailingStart=class="num">0; class=class="str">"cmt">//为谁开始, 点数 class="kw">input class="type">int TrailingStop= class="num">0; class=class="str">"cmt">//尾随停止, 点数 class="kw">input class="type">int TrailingStep= class="num">0; class=class="str">"cmt">//尾随步长, 点数 class="kw">input class="type">int SL_prof=class="num">0; class=class="str">"cmt">//开始 BE, 点数 class="kw">input class="type">int SL_lev=class="num">0; class=class="str">"cmt">//BE 价位, 点数 class="kw">input class="type">int Buy_max=class="num">1; class=class="str">"cmt">//最大买单数量 class="kw">input class="type">int Sell_max=class="num">1; class=class="str">"cmt">//最大卖单数量 class="kw">input class="type">bool Sig_close=true; class=class="str">"cmt">//逆势交易平仓 class="kw">input tip mode=class="num">0; class="kw">input class="type">class="kw">string s0="-------------------------------------------"; class=class="str">"cmt">// 指标设置 class="kw">input class="type">int _ExtDepth=class="num">12; class="kw">input class="type">int _ExtDeviation=class="num">5; class="kw">input class="type">int _ExtBackstep=class="num">3; class="kw">input class="type">int _Min_dist=class="num">0; class=class="str">"cmt">// 最小距离 class="kw">input class="type">int _fibo=class="num">30; class=class="str">"cmt">// 菲波纳奇比率 class="kw">input class="type">int _tolerance=class="num">200; class=class="str">"cmt">// 冗余
「多指标滤波器的输入参数拆解」
这段输入声明把形态识别与指标过滤拆成了四组可独立调控的块,先在 MT5 里把 _Intersection_ab 和 _Intersection_bc 都设为 1,意味着 a→b、b→c 两段只允许出现 1 个交汇点,过严会漏掉次优形态,过松则噪声激增。
MACD 块默认快线 12、慢线 26、信号 9,与经典参数一致;Use_macd=true 时它作为主滤波器参与信号判定。RSI 块周期 14,卖区设 70–100、买区设 0–30,但 Use_rsi=false 说明默认不启用,想叠加超买超卖约束时再手动开。
WPR 与 MA 块给出另一套阈值:WPR(14) 卖区 -20~0、买区 -100~-80,MA 则用周期 10 的收盘价 SMA 做趋势衬垫。外汇与贵金属波动属性不同,直接套同一组数可能放大假信号,建议先在 XAUUSD 的 M15 上只开 MACD 滤波回测,再逐步加 RSI 观察命中率变化。
class="kw">input class="type">int _Intersection_ab=class="num">1; class=class="str">"cmt">// 从 a 点到 b 点允许的交汇点数量 class="kw">input class="type">int _Intersection_bc=class="num">1; class=class="str">"cmt">// 从 b 点到 c 点允许的交汇点数量 class="kw">input class="type">class="kw">string s1="-------------------------------------------"; class=class="str">"cmt">// MACD 设置 class="kw">input ENUM_TIMEFRAMES macd_tf=PERIOD_CURRENT; class=class="str">"cmt">// 周期 class="kw">input class="type">int fast_ema_period=class="num">12; class=class="str">"cmt">// 快速均线周期 class="kw">input class="type">int slow_ema_period=class="num">26; class=class="str">"cmt">// 慢速均线周期 class="kw">input class="type">int signal_period=class="num">9; class=class="str">"cmt">// 差值的均化周期 class="kw">input ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE; class=class="str">"cmt">// 价格类型 class="kw">input class="type">class="kw">string s2="-------------------------------------------"; class=class="str">"cmt">// RSI 设置 class="kw">input ENUM_TIMEFRAMES rsi_tf=PERIOD_CURRENT; class=class="str">"cmt">// 周期 class="kw">input class="type">int rsi_period=class="num">14; class=class="str">"cmt">// 周期 class="kw">input ENUM_APPLIED_PRICE rsi_applied_price=PRICE_CLOSE; class=class="str">"cmt">// 价格类型 class="kw">input class="type">class="kw">double rsi_max_s=class="num">100; class=class="str">"cmt">// 卖出的最大价格 class="kw">input class="type">class="kw">double rsi_min_s=class="num">70; class=class="str">"cmt">// 卖出的最小价格 class="kw">input class="type">class="kw">double rsi_max_b=class="num">30; class=class="str">"cmt">// 买入的最大价格 class="kw">input class="type">class="kw">double rsi_min_b=class="num">0; class=class="str">"cmt">// 买入的最小价格 class="kw">input class="type">class="kw">string s3="-------------------------------------------"; class=class="str">"cmt">// WPR 设置 class="kw">input ENUM_TIMEFRAMES wpr_tf=PERIOD_CURRENT; class=class="str">"cmt">// 周期 class="kw">input class="type">int calc_period=class="num">14; class=class="str">"cmt">// 周期 class="kw">input class="type">class="kw">double wpr_max_s=class="num">0; class=class="str">"cmt">// 卖出的最大价格 class="kw">input class="type">class="kw">double wpr_min_s=-class="num">20; class=class="str">"cmt">// 卖出的最小价格 class="kw">input class="type">class="kw">double wpr_max_b=-class="num">80; class=class="str">"cmt">// 买入的最大价格 class="kw">input class="type">class="kw">double wpr_min_b=-class="num">100; class=class="str">"cmt">// 买入的最小价格 class="kw">input class="type">class="kw">string s4="-------------------------------------------"; class=class="str">"cmt">// MA 设置 class="kw">input ENUM_TIMEFRAMES ma_tf=PERIOD_CURRENT; class=class="str">"cmt">// 周期 class="kw">input class="type">int ma_period=class="num">10; class=class="str">"cmt">// MA 周期 class="kw">input class="type">int ma_shift=class="num">0; class=class="str">"cmt">// 平移 class="kw">input ENUM_MA_METHOD ma_method=MODE_SMA; class=class="str">"cmt">// 平滑类型 class="kw">input ENUM_APPLIED_PRICE ma_applied_price=PRICE_CLOSE; class=class="str">"cmt">// 价格类型 class="kw">input class="type">bool Use_macd=true; class=class="str">"cmt">// 使用 MACD 作为滤波器 class="kw">input class="type">bool Use_rsi=false; class=class="str">"cmt">// 使用 RSI 作为滤波器