解读经典与隐性背离的新途径·综合运用
(3/3)·从定义到实战,把经典与隐性背离揉进一套能跑的策略,而不是停留在看图说话
挂单怎么吃突破信号
这套打法不挑时间帧,挂单和市价单都能用,核心只有一条:指示线被突破才算数。做挂单时把单子挂在正在突破的烛条高点或低点,烛条收盘方向必须和突破方向一致——线向上破就等阳线收,向下破就等阴线收。 止损放在最近局部极值外侧,止盈看支撑阻力;逆势单的支撑阻力也可以直接画在指示线上,但得留神,行情可能急转,止盈位得跟着调。M1 到 M15 小周期里,价格突破后常回踩指示线走出 1-2-3 形态,等于给信号加保险,所以不够活跃时不妨等破烛条高低点后再进。 AO 振荡器在这套里主要干图形确认的活,不似别的系统那么重,但配合 AO 能明显抬升策略有效率。需要提醒,外汇和贵金属杠杆高,回踩失效或假突破都可能直接扫损,仓位别重。 为了验证,把指标代码嵌进 EA 而不是普通调用(否则指标窗口不画东西),除了 Lots、TP、SL、TrailingStop,还加了 InpOffset 和 InpDellOorder:前者是突破烛条到挂单的距离(点),后者是订单未触发且反向走动超过该点值就删单。 回测选 GBPUSD H1,从 2017 年初跑默认参数,结果偏向「有潜力」而非「已成型」。图形构造类策略只能可视模式人工评估,自动优化做不到,这个过程很耗时间。
<span class="keyword">class="kw">input</span> <span class="keyword">class="type">class="kw">double</span> InpLots =<span class="number">class="num">0.1</span>; <span class="comment">class=class="str">"cmt">// 手数</span> <span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span> InpTakeProfit =<span class="number">class="num">150</span>; <span class="comment">class=class="str">"cmt">// 止盈 (点值)</span> <span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span> InpStopLoss =<span class="number">class="num">60</span>; <span class="comment">class=class="str">"cmt">// 止损 (点值)</span> <span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span> InpTrailingStop =<span class="number">class="num">25</span>; <span class="comment">class=class="str">"cmt">// 尾随停止价位 (点值)</span> <span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span> InpOffset =<span class="number">class="num">5</span>; <span class="comment"> class=class="str">"cmt">// 自价格距离 (点值)</span> <span class="keyword">class="kw">input</span> <span class="keyword">class="type">int</span> InpDellOorder =<span class="number">class="num">30</span>; <span class="comment"> class=class="str">"cmt">// 订单删除距离 (点值)</span> <span class="comment">class=class="str">"cmt">//---</span> <span class="keyword">class="type">int</span> ExtTimeOut=<span class="number">class="num">10</span>; <span class="comment">class=class="str">"cmt">// 交易操作之间的间隔时间</span> <span class="keyword">class="type">int</span> barsCalculated=<span class="number">class="num">1000</span>; <span class="keyword">class="type">class="kw">datetime</span> t=<span class="number">class="num">0</span>; <span class="keyword">class="type">class="kw">datetime</span> time[]; <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| 基本计算 |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">for</span>(<span class="keyword">class="type">int</span> bar=<span class="number">class="num">1</span>; bar><span class="number">class="num">0</span> && !<span class="functions">IsStopped</span>() && t!=time[<span class="number">class="num">0</span>]; bar--) { <span class="keyword">class="type">int</span> l=bar+<span class="number">class="num">1</span>; <span class="keyword">class="type">int</span> p1=<span class="number">class="num">0</span>,p2=<span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">//+----------- 检测向上背离 ------------------------------+</span> <span class="keyword">if</span>(Extremum(m_buff_ind[l+<span class="number">class="num">1</span>],m_buff_ind[l],m_buff_ind[l-<span class="number">class="num">1</span>])<<span class="number">class="num">0</span>) { <span class="keyword">class="type">int</span> i=l; <span class="keyword">class="type">int</span> counted=LastPeak(l,bars,m_buff_ind); <span class="keyword">if</span>(counted!=-<span class="number">class="num">1</span>) { <span class="keyword">if</span>(m_buff_ind[i]<m_buff_ind[counted] && high[i]>high[counted] && !d1) { drawLine(<span class="class="type">class="kw">string">"Buy_1"</span>,time[i],time[counted],m_buff_ind[i],m_buff_ind[counted],Red,<span class="number">class="num">1</span>); d1=<span class="macro">true</span>;} <span class="comment">class=class="str">"cmt">//---</span> <span class="keyword">if</span>(m_buff_ind[i]>m_buff_ind[counted] && high[i]<high[counted] && !d2) { <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">235, class="num">85);">p1=<span class="functions">ArrayMaximum</span>(high,i-<span class="number">class="num">1</span>,<span class="number">class="num">5</span>);p2=<span class="functions">ArrayMaximum</span>(high,counted-<span class="number">class="num">2</span>,<span class="number">class="num">5</span>);</span> drawLine(<span class="class="type">class="kw">string">"Buy_2"</span>,time[p1],time[p2],high[p1],high[p2],Red,<span class="number">class="num">0</span>);d2=<span class="macro">true</span>; } } } <span class="comment">class=class="str">"cmt">//+----------- 检测向下背离 ------------------------------+</span> <span class="keyword">if</span>(Extremum(m_buff_ind[l+<span class="number">class="num">1</span>],m_buff_ind[l],m_buff_ind[l-<span class="number">class="num">1</span>])><span class="number">class="num">0</span>) { <span class="keyword">class="type">int</span> i=l; <span class="keyword">class="type">int</span> counted=LastTrough(l,bars,m_buff_ind); <span class="keyword">if</span>(counted!=-<span class="number">class="num">1</span>) { <span class="keyword">if</span>(m_buff_ind[i]>m_buff_ind[counted] && low[i]<low[counted] && !d3) { drawLine(<span class="class="type">class="kw">string">"Sell_1"</span>,time[i],time[counted],m_buff_ind[i],m_buff_ind[counted],Green,<span class="number">class="num">1</span>);d3=<span class="macro">true</span>;} <span class="comment">class=class="str">"cmt">//---</span>
「空头触发与极值判定的代码骨架」
这段逻辑把「画线下破」和「极值识别」拆成了可复用的成员函数。先说极值函数 Extremum:它要求 a、b、c 三者同号,且 (a-b)*(b-c)<0,也就是中间点 b 是局部拐点;若 c>b 且 b<0 返回 1 判为向下极值,c<b 且 b>0 返回 -1 判为向上极值。 空头开仓检查 ShortOpened 里,先通过 ObjectFind 找 "Sell_2" 和 "Sell_1" 两条趋势线,再用 ValueByTime 取最近 3 根 K 线对应时间点的线价。只要 close[1] 跌破 Sell_2 且前一根收在线上方,或者指标 m_ind_1>m_ind_0 且指标与 Sell_1 线发生穿越,就返回 true,倾向认为空单触发概率升高。 OpenSellStop 中用 ArrayMinimum(low,0,3) 在最近 3 根里抓最低价挂 sell stop。外汇与贵金属杠杆高,这类下破单在震荡市可能被反复扫损,实盘前务必在 MT5 策略测试器用 2020—2023 年 XAUUSD 数据跑一遍。
if(m_buff_ind[i]<m_buff_ind[counted] && low[i]>low[counted] && !d4) { p1=ArrayMinimum(low,i-class="num">1,class="num">5);p2=ArrayMinimum(low,counted-class="num">2,class="num">5); drawLine("Sell_2",time[p1],time[p2],low[p1],low[p2],Green,class="num">0);d4=true; } } } if(d1 && d2 && d3 && d4)break; t=time[class="num">0]; } class=class="str">"cmt">//--- } class=class="str">"cmt">//+-- 搜索极值 -------------------------------------------+ class="type">int Extremum(class="type">class="kw">double a,class="type">class="kw">double b,class="type">class="kw">double c) { if(((a-b)*(b-c)<class="num">0) && ((a>class="num">0 && b>class="num">0 && c>class="num">0) || (a<class="num">0 && b<class="num">0 && c<class="num">0))) { if(c>b && b<class="num">0) class="kw">return(class="num">1); class=class="str">"cmt">//DN extremum if(c<b && b>class="num">0) class="kw">return(-class="num">1);class=class="str">"cmt">//UP extremum } class="kw">return(class="num">0); } class=class="str">"cmt">//+------ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|返回指定对象指定时间处的价格数值 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double CSampleExpert::ValueByTime(class="type">class="kw">string label,class="type">int i) { class="type">class="kw">double p=class="num">0.0; class=class="str">"cmt">//--- p=ObjectGetValueByTime(class="num">0,label,time[i],class="num">0); class="kw">return(p); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 检查开空单的条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::ShortOpened(class="type">void) { class="type">bool res=false; class=class="str">"cmt">//--- class="type">class="kw">double pp1=EMPTY_VALUE,pp2=EMPTY_VALUE,pp3=EMPTY_VALUE, pp4=EMPTY_VALUE,pp5=EMPTY_VALUE,pp6=EMPTY_VALUE; class=class="str">"cmt">//--- if(ObjectFind(class="num">0,"Sell_2")!=-class="num">1) { pp1=ValueByTime("Sell_2",class="num">1); pp2=ValueByTime("Sell_2",class="num">2); pp3=ValueByTime("Sell_2",class="num">3); } if(ObjectFind(class="num">0,"Sell_1")!=-class="num">1) { pp4=ValueByTime("Sell_1",class="num">1); pp5=ValueByTime("Sell_1",class="num">2); pp6=ValueByTime("Sell_1",class="num">3); } class=class="str">"cmt">//--- 检查空头仓位的可能性 (卖出) if((pp1!=EMPTY_VALUE && close[class="num">1]<pp1 && close[class="num">2]>pp2&&close[class="num">0]<close[class="num">1])|| (pp4!=EMPTY_VALUE && m_ind_1>m_ind_0 && ((m_ind_1<pp4 && m_ind_2>pp5) ||(m_ind_2<pp5 && m_ind_3>pp6)))) { class=class="str">"cmt">//--- 任何情况下, 我们都需要退出智能交易系统 res=true; } class=class="str">"cmt">//--- 结果 class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 开一笔向下突破卖出单 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::OpenSellStop(class="type">void) { class="type">bool res=false; class=class="str">"cmt">//--- 搜索最近烛条中的最低柱线 class="type">int i=ArrayMinimum(low,class="num">0,class="num">3); class=class="str">"cmt">//--- if(ShortOpened()) {
◍ 挂单前先卡住保证金红线
这段逻辑干的事很直接:在挂 SELL STOP 之前,先用 FreeMarginCheck 探一下账户扛不扛得住。若返回值小于 0.0,说明保证金不够,直接 printf 打出当前可用保证金,不再往下走,避免盲目挂单把账户拖进强平区。外汇和贵金属杠杆高,这种前置检查不是可选项,是风控底线。 订单参数里,limit_price 取当前 Bid,price 则是前一根低点减去 offset 个点值,止盈在 price 下方、止损在 price 上方,典型的逆势接刀结构。开仓成功打印品种名,失败则把错误描述和价格参数都吐出来,方便你回 MT5 日志里对照。 DelOrder 这块有个反直觉的坑:删除成功时没置 res=true,失败反而 res=true 表示“还没删掉”。如果你拿这个函数写批量清理,得反过来读返回值,否则会以为删失败了。 [CODE]double offset=InpOffset; // 距烛台低点放置订单的距离点值 double limit_price=m_symbol.Bid(); double price=low[i]-offset*m_adjusted_point;; double tp =price-m_take_profit; double sl =price+m_stop_losse; //--- check the account balance if(m_account.FreeMarginCheck(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,price)<0.0) printf("资金不足。可用保证金 = %f",m_account.FreeMargin()); else { //--- 开仓 if(m_trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,limit_price,price,sl,tp)) {res=true; printf("已开仓品种 %s",Symbol());} else { printf("SELL STOP %s 开仓错误: '%s'",Symbol(),m_trade.ResultComment()); printf("开仓参数: 价格=%f, 止盈=%f",price,tp); } } } //--- 结果 return(res); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+
| // | 删除不必要的订单 |
|---|
//+------------------------------------------------------------------+ bool CSampleExpert::DelOrder(ulong ticket,string type) { bool res=false; if(m_trade.OrderDelete(ticket)) printf("已开仓品种 %s",Symbol()); else { res=true;// 设置标志, 表示订单尚未被删除 printf("删除订单错误"+type+" %s : '%s'",Symbol(),m_trade.ResultComment()); } return(res); } //+------------------------------------------------------------------+[/CODE]
class="type">class="kw">double offset=InpOffset; class=class="str">"cmt">// 距烛台低点放置订单的距离点值 class="type">class="kw">double limit_price=m_symbol.Bid(); class="type">class="kw">double price=low[i]-offset*m_adjusted_point;; class="type">class="kw">double tp =price-m_take_profit; class="type">class="kw">double sl =price+m_stop_losse; class=class="str">"cmt">//--- check the account balance if(m_account.FreeMarginCheck(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,price)<class="num">0.0) printf("资金不足。可用保证金 = %f",m_account.FreeMargin()); else { class=class="str">"cmt">//--- 开仓 if(m_trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP,InpLots,limit_price,price,sl,tp)) {res=true; printf("已开仓品种 %s",Symbol());} else { printf("SELL STOP %s 开仓错误: &class="macro">#x27;%s&class="macro">#x27;",Symbol(),m_trade.ResultComment()); printf("开仓参数: 价格=%f, 止盈=%f",price,tp); } } } class=class="str">"cmt">//--- 结果 class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 删除不必要的订单 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::DelOrder(class="type">class="kw">ulong ticket,class="type">class="kw">string type) { class="type">bool res=false; if(m_trade.OrderDelete(ticket)) printf("已开仓品种 %s",Symbol()); else { res=true;class=class="str">"cmt">// 设置标志, 表示订单尚未被删除 printf("删除订单错误"+type+" %s : &class="macro">#x27;%s&class="macro">#x27;",Symbol(),m_trade.ResultComment()); } class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+
策略能跑通但别指望全自动
这套基于加速器指标识别显性与隐性背离/趋合的思路,实测能拼出一个可上 MT5 跑的策略骨架,且留了口子接别的工具。但它有个硬伤:指标线偶尔会画错,得人工去修,直接导致难以全自动,回测统计也因此不好做干净。 附带的两个程序里,Accelerator_Div 指标负责背离判定,TestExpert 智能交易系统只用来跑策略测试,不是实盘 EA。外汇和贵金属波动大、滑点凶,手工干预环节多意味着实盘风险偏高,信号错过或画错都可能发生。 研究价值在于证明了经典战略之外还有活路,但别把它当免维护系统。真要验证,先把 ZIP 里的指标拖进 MT5 看画线错误频率,再决定值不值得自己写自动化外层。