Elder-Ray (多头力度和空头力度)(基础篇)
用 Elder-Ray 把多空力度拆开看
Elder-Ray 指标由 Alexander Elder 提出,核心是把市场的「多头力度」与「空头力度」分开计算,而不是只给一个综合买卖信号。它的底层依赖一条指数移动平均线(默认周期 13)作为「聪明钱基准线」,再用高低价与这条均线的偏离来量化双方推力。 多头力度 = 最高价 − EMA;空头力度 = EMA − 最低价。两者同时为正,说明价格夹在均线上下方都有余力;若一方转负,往往意味着该方向短期失控。在 MT5 里加载后,你能在副图直接看到两条柱状线,无需自己手算。 一个可验证的现象:在 2018 年 10 月 15 日前后(示例数据来自公开策略贴,浏览量 5855),EURUSD 的 H1 图上多头力度柱连续 3 根收缩而价格未破前高,随后回落概率明显偏高。这只是现象,不是规律,外汇与贵金属杠杆交易高风险,参数和周期需自己回测确认。
「Elder-Ray 系统的表里两层」
Alexander Elder 在《为生活而交易》里给出的 Elder-Ray 框架,核心就三块:EMA 做趋势方向,多头力度与空头力度两个振荡器量买卖双方的压力差。字面规则极简——EMA 向上、空头力度在零轴下方却拐头抬高,就倾向做多。 但把 EMA 和空头力度叠到同一图表细看,会发现这种教科书条件在实盘里出现频率很低,机械照搬很容易长时间空仓。系统「简单在表述、复杂在落地」的矛盾就在这里。 本文会按从简到繁的顺序拆两套玩法:单时间帧内把所有指标塞进一张图;以及接上「三重选择」过滤。需要注意,文中给出的 EA 只跑净持结算账户,锁仓型账户直接编译也用不了。 外汇与贵金属杠杆高、滑点跳空频繁,这类力度振荡信号在极端波动下失效概率明显放大,上 MT5 前先用策略测试器跑历史样本。
◍ 拆开 Elder-Ray 的五个构件
要把 Elder-Ray 系统用顺,先得把它的五个构件逐一拆清:价格、EMA、每根柱线的多空力度高低点,以及多空各自的强度。少看一个,指标信号都会读偏。 价格就是资产价值的当下成交协约。只有急切的买家和卖家同时出现,一笔成交才撮合得成;预期上涨时买入,预期下跌时卖出,这是力度指标的底层逻辑。 EMA 选指数均线而非简单均线,核心在敏感度。以 D1 周期 EMA(13) 为例,它刻画的是过去 13 天资产价值的指数加权平均,对趋势转向的反应比 SMA 快一截。 多头力度最大,是某根柱线上多头推升的极限。价格冲高时多头获利,直到摸顶前都在做多;这个值出现,意味着多头还想更高却已没有后续资金跟进。空头力度最小同理,是空头砸盘的极值,价格探底前空头一直获利,该值出现代表空头想更低却砸不动了。 常规状态下多头力度在零轴上方,一旦跌破零,说明多头陷入恐慌、动能快断;空头力度通常在零轴下方,若上穿零,则是多头过强、空头即将失速的信号。外汇与贵金属杠杆高,这类多空失衡只代表概率倾斜,实盘须严控仓位。
单图堆三指标:D1 上的回滚捕捉法
在 D1 周期里把多头力度、空头力度和 EMA 三个指标叠在同一张图上,均化周期统一设 13。趋势方向只看 EMA 斜率:EMA 向上且空头力度低于零但拐头走高时,挂 BuyStop 在最近两天最高价上方,保护性止损放在最后最低价下方;反过来 EMA 向下、多头力度高于零但走低,则挂 SellStop 在最近两天最低价下方,止损置于最后最高价上方。 这类信号本质是稳定趋势中的回滚入场,在 D1 上触发次数极少。实测若只盯一个品种,可能几周才出一次信号;想把账户交易频率拉起来,就得横向铺开上百个期货或股票品种一起筛。 D1 的好处是每天只在新柱成型时算一次指标,EA 逻辑也照此设计:对每个指定品种等新柱,再查入场条件。净持模式下不能对冲,但能加同向仓。多品种名单可用 Symbols on the specified path.mq5 脚本按路径抓取,写进公用数据文件夹的文本文件,EA 直接读文件名加载。 手数不用搞复杂:脚本遍历同路径品种统计最小交易量,证券和期货两边结果都是 1.0,所以 EA 直接锁最小手数 1.0 即可。 内存方面也不用抠门。1.010 版实测:终端初始 TERMINAL_MEMORY_USED 约 345 MB,加满约百个品种到市场观察后 433 MB,每品种挂三个自定义指标后飙到 5523 MB,任务管理器占 300 MB;测试器非可视化跑约 420 MB、任务管理器 5 GB。正常图表模式内存不超 300 MB,没必要为省资源砍功能。 趋势判定用 EMA 跨多根柱线方向,参数 number of bars for identifying the trend 设 6、minimum percentage of the trend 设 75% 时,新柱出现才重算且不看零号柱。挂单用 #1 柱力度对比 #2 柱来定方向,止损别用 #1 柱价(实测触发太频繁),改搜最近极值是靠谱做法。
「反向挂单触发的三种持仓补偿路径」
EA 在 OnInit 阶段改了可视化逻辑:若策略测试器已启动,且当前加载 EA 的品种出现在文本文件里,就跳过 ChartIndicatorAdd,不再往图表挂指标。OnTimer 里补了信号确认和 BuyStop / SellStop 挂单的交易动作,OnTradeTransaction 则加了仓位反转、部分平仓的补偿机制。 补偿激活时,OnTradeTransaction 不会直接设止损,而是把尾随函数改得更聪明——检查持仓时发现没止损的仓位,就按最近极值点规则补止损。新增的 m_magic_compensation 变量专门标记补偿交易,和原 m_magic 区分开。 反向挂单触发后典型就三种情况:① 持 Sell 1.0、触发 Buy Stop 3.0,成交后反转出 Buy 2.0,原 magic 转 m_magic_compensation,开仓量剩 3.0-2.0=1.0;② 持 Sell 1.0、触发 Buy Stop 1.0,完全平仓后若无持仓再以 1.0 开买;③ 持 Sell 2.0、触发 Buy Stop 1.0,部分平掉 1.0 后剩 Sell 1.0,再开买 1.0 凑成 2.0 反向仓。三种路径都已在净持结算账户和测试器跑过打印验证。 真实账户不进测试器也复现过:市价买 2.0 拆成 20087494、20087495 两笔成交,挂 SellLimit 2.0 止盈,后来分 20088091、20088145 两次吃掉。文本文件测的时候最好只留一个品种,最稳;多品种容易让 EA 在路径判断上出岔子。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Symbols on the specified path.mq5 | class=class="str">"cmt">//| 版权所有 © class="num">2018, Vladimir Karputov | class=class="str">"cmt">//| http://wmua.ru/slesar/ | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "版权所有 © class="num">2018, Vladimir Karputov" class="macro">#class="kw">property link "http:class=class="str">"cmt">//wmua.ru/slesar/" class="macro">#class="kw">property version "class="num">1.002" class="macro">#class="kw">property script_show_inputs class=class="str">"cmt">//--- class="macro">#include <Files\FileTxt.mqh> CFileTxt m_file_txt; class=class="str">"cmt">// 文本文件对象 class=class="str">"cmt">//--- 输入参数 class="kw">input class="type">class="kw">string InpFileName="Enter a unique name.txt"; class=class="str">"cmt">// 文件名 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 脚本程序开始函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//--- 第一步 class="type">class="kw">string current_path=""; if(!SymbolInfoString(Symbol(),SYMBOL_PATH,current_path)) { Print("ERROR: SYMBOL_PATH"); class="kw">return; } class=class="str">"cmt">//--- 第二步 class="type">class="kw">string sep_="\\"; class=class="str">"cmt">// 分隔符 class="type">class="kw">ushort u_sep_; class=class="str">"cmt">// 分隔符的代码 class="type">class="kw">string result_[]; class=class="str">"cmt">// 保存字符串的数组 class=class="str">"cmt">//--- 获取分隔符代码 u_sep_=StringGetCharacter(sep_,class="num">0); class=class="str">"cmt">//--- 将字符串拆分为子字符串 class="type">int k_=StringSplit(current_path,u_sep_,result_); class=class="str">"cmt">//--- 第三步 class=class="str">"cmt">//--- 现在输出所有获得的字符串 if(k_>class="num">0) { current_path="";
◍ 把路径匹配到的品种落盘到公共文件
前面几步已经拼出 current_path 这个品种路径前缀,真正的筛选发生在第四步:先取 SymbolsTotal(false) 拿到当前行情环境里全部可交易品种数,再逐个用 SymbolName(i,false) 取出名称,并通过 SymbolInfoString 拿 SYMBOL_PATH。若路径里找不到 current_path 开头,就直接 continue 跳过,留下的才是同分组品种。 数组扩容用了 ArrayResize(symbols_array,size+1,10),第三个参数 10 代表内存按 10 个元素为步长预留,品种多的时候能少几次重分配。若最终 size 为 0,脚本会 PrintFormat 报错并返回,说明这条路径下实际没有挂任何品种,可能是路径拼错或经纪商分组命名不同。 第六步用 FILE_WRITE|FILE_COMMON 在终端公共数据文件夹开文件,失败同样直接返回并打印提示。第七步把 symbols_array 逐行 WriteString 加 "\r\n" 写进去再 Close,控制台打 Everything is fine 即表示这批同路径品种名已落盘,可供后续批量下单或扫描使用。 外汇与贵金属品种分组常因经纪商而异,公共文件写出后建议手动打开核对数量;路径匹配不到时优先检查 sep_ 分隔符与 SymbolInfoString 返回的原始路径格式是否一致。
class="type">class="kw">string symbols_array[]; class="type">int symbols_total=SymbolsTotal(class="kw">false); for(class="type">int i=class="num">0;i<symbols_total;i++) { class="type">class="kw">string symbol_name=SymbolName(i,class="kw">false); class="type">class="kw">string symbol_path=""; if(!SymbolInfoString(symbol_name,SYMBOL_PATH,symbol_path)) class="kw">continue; if(StringFind(symbol_path,current_path,class="num">0)==-class="num">1) class="kw">continue; class="type">int size=ArraySize(symbols_array); ArrayResize(symbols_array,size+class="num">1,class="num">10); symbols_array[size]=symbol_name; } class="type">int size=ArraySize(symbols_array); if(size==class="num">0) { PrintFormat("ERROR: On path \"%s\" %d symbols",current_path,size); class="kw">return; } PrintFormat("On path \"%s\" %d symbols",current_path,size); if(m_file_txt.Open(InpFileName,FILE_WRITE|FILE_COMMON)==INVALID_HANDLE) { PrintFormat("ERROR: \"%s\" file in the Data Folder Common folder is not created",InpFileName); class="kw">return; } for(class="type">int i=class="num">0;i<size;i++) m_file_txt.WriteString(symbols_array[i]+"\r\n"); m_file_txt.Close(); Print("Everything is fine. There are no errors");