一个基于不同大陆不同时区的交易策略实例(基础篇)
◍ 拿时区差做跨大陆日内波段
同一品种在亚洲、欧洲、美洲三个盘面接力交易,价格行为会出现可重复的节奏差。以 EURUSD 为例,东京时段(北京时间 8:00–11:00)波幅常低于 30 点,而伦敦开盘后一小时(16:00–17:00 北京时)平均波幅可拉到 45–60 点,这种结构性缺口本身就能成为入场依据。 思路不复杂:在流动性最薄的大陆时段尾盘挂突破单,赌下一大陆开盘带来的重定价。2013 年 12 月 3 日那篇旧帖里,作者 Vasily 用 MT5 跑了这个框架,帖子浏览 3386 次、零跟帖,说明冷门但能跑——你开 MT5 把时区切到 broker 时间对照本地钟,就能复现。 外汇和贵金属杠杆高、滑点凶,跨时段单子遇到假期流动性断档可能瞬间反向,先用模拟盘验节奏再上实盘。
从时区错位看欧美盘资产重叠
把地球按时区摊开看,规律比想象中硬:日本、澳大利亚、中国、俄罗斯依次开市,等俄罗斯职员坐进交易台,亚洲早已入夜;欧洲一开,亚洲又收了。这种错位不是背景噪音,而是流动性切换的物理骨架。 伦敦与芝加哥差 8 个时区。若按工作日 8 小时加 45 分钟到 1 小时休息、实际常延后 1.5–2 小时算,欧洲时段未结束时芝加哥黎明已至。两地面向证券市场的经理人几乎同时碰头,资产在重叠窗口里被双向搬运。 该重叠段的价格现象很直接:波动边界先扩张、再缩窄,随后沿主趋势继续走。外汇与贵金属在此窗口成交密度陡增,滑点与反向扫单概率明显偏高,属典型高风险时段。 市场价格可涨可跌,但时间轴始终向右——重叠窗口提供的不是方向预言,而是一段可验证的流动性样本。
「用流程图搭 MQL5 算法骨架」
画算法别急着写代码,先用流程图把主干钉死。最基础的 Start-Stop 块只干一件事:标出程序或某个函数(含 OnInit/OnDeinit 这类初始化与反初始化流程)的起止边界,相当于给 MT5 脚本一个明确的入口和出口。 Data 块放的是程序启动时就要确定的参数。落到 MQL5 上,它就是对外暴露的输入变量(input),同时也充当全局变量的目标容器。周期块是另一块重头——MQL 里约 99% 的程序都靠它分两截描述,明确标出一次 tick 或一根 K 线处理的起止,块内再塞赋值、计算等操作(见图 6 那类运算单元)。 逻辑块(Solution)负责分支判断;若它处在“按配售数量切换”这类 switch 运算里,就会长出对应数量的输出口,不再是单进单出。函数块则用来引 iMACD、iRSI 这类预定义指标函数,或库里自定义的封装函数;最后的注释与报错块只是服务性辅料。 这几类块覆盖了给机器写程序的基本表达。早期用它们梳理,系统弱点会直接暴露出来,也逼你想出解法。我不建议照图死搬,但搞清这套初始流程图语义,足够你读懂不少现成计算方法的来路,也能把脑子里一闪而过的策略念头快速落成一个可验证的框架。
◍ 把时段判定和风控封装成可复用函数
美国开市后两小时是欧洲收盘、美洲流动性叠加的敏感窗口,本策略把这段等待逻辑做成了独立函数,而不是全堆在 OnTick 里。输入参数里 America=16 代表美洲盘基准小时,加 2 之后才是真正盯盘的时刻;MagicNumber=665 用来隔离 EA 自己的订单,避免和手动单或其他程序打架。 封装的价值在于变量名可以重名却不串台。Get 方法只从分配给该函数的存储单元取值,Set 方法也只在自己领地内写变量,主程序和函数之间靠这种屏障互不污染。这样后面要调 Lot、TakeProfit 或 TrailingStop,都不会把时间判断逻辑搞乱。 风控参数里 Limited=600 是单日订单有效性的秒级时限类设定,TrailingStop=100 则以最小价格变动为单位拉开保护止损。外汇和贵金属波动受新闻冲击大,这类参数留足优化空间,实盘前务必在 MT5 策略测试器里按品种重新标定值。 下方代码里 time2trade 函数先取服务器交易时间,小时不符直接返回 false;随后清零时分秒,遍历当前单与历史单比对幻数,同一 MagicNumber 当天已开过就拒单。OnTick 中调用时传入 America+2 与 MagicNumber,命中后才取最近 9 根 15 分钟柱,扫最高最低构造区间。
class=class="str">"cmt">//--- 输入参数 class="kw">input class="type">int America=class="num">16; class="kw">input class="type">class="kw">double Lots=class="num">0.1; class="kw">input class="type">int TakeProfit=class="num">500; class="kw">input class="type">long MagicNumber=class="num">665; class="kw">input class="type">int Limited=class="num">600; class="kw">input class="type">int TrailingStop=class="num">100; class="type">bool time2trade(class="type">int TradeHour,class="type">int Number) { class="type">MqlDateTime time2trade; TimeTradeServer(time2trade); if(time2trade.hour!=TradeHour) class="kw">return(false); time2trade.hour= class="num">0; time2trade.min = class="num">0; time2trade.sec = class="num">1; for(class="type">int ii=OrdersTotal()-class="num">1;ii>=class="num">0;ii--) { OrderGetTicket(ii); class="type">long ordmagic=OrderGetInteger(ORDER_MAGIC); if(Number==ordmagic) class="kw">return(false); } HistorySelect(StructToTime(time2trade),TimeTradeServer()); for(class="type">int ii=HistoryOrdersTotal()-class="num">1;ii>=class="num">0;ii--) { class="type">long HistMagic=HistoryOrderGetInteger(HistoryOrderGetTicket(ii),ORDER_MAGIC); if(Number==HistMagic) class="kw">return(false); } class="kw">return(true); } class="type">void OnTick() { class=class="str">"cmt">//--- if(time2trade(America+class="num">2,MagicNumber)) { class="type">int i; class="type">class="kw">double Highest = class="num">0; class="type">class="kw">double Lowest = class="num">0; class="type">MqlRates Range[]; CopyRates(Symbol(),class="num">15,class="num">0,class="num">9,Range); Lowest=Range[class="num">1].low; for(i=class="num">0; i<class="num">9;i++) { if(Highest<Range[i].high) Highest=Range[i].high;class=class="str">"cmt">//MathMax(,Highest); if(Lowest>Range[i].low) Lowest=Range[i].low; }