走势延续模型 - 搜索图表和执行统计·进阶篇
(2/3)· 接上篇概念铺垫,本篇拆解双浪模型的代码骨架与极值搜索逻辑,让图表识别不再靠肉眼猜
很多交易者把走势延续当成盘感游戏,看到AB涨BC调就直接挂单,结果常被未成形分形骗进场。上篇聊完模型定义,这一篇把识别规则落成MQL5代码,先解决“电脑怎么认出这个形态”的问题。
「OnTick 里的市价与仓位变量落点」
在 EA 的 OnTick() 里,第一步通常是把当前品种的市价特征抓出来,再声明后续计算仓位要用的变量。 下面这段声明里有个关键细节:根据 SymbolInfoInteger(_Symbol, SYMBOL_DIGITS) 拿到的小数位 Digit,把价格容量系数 f 映射成 10 的幂。5 位小数对应 f=100000,4 位对应 10000,依此类推;外汇与贵金属杠杆高、点值敏感,这套映射直接决定点差和止损被换算成「分数值」时的精度。 [CODE] //+------------------------------------------------------------------+
| // | 1. 常规参数 (开始) |
|---|
//+------------------------------------------------------------------+ //--- 市价数据market data //品种价格中的小数位数 int Digit=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //定义当前品种的价格容量 double f=1; if(Digit==5) {f=100000;} if(Digit==4) {f=10000;} if(Digit==3) {f=1000;} if(Digit==2) {f=100;} if(Digit==1) {f=10;} //--- double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/f;//考虑到价格容量,将点差降低到分数值 double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);//数据依据竞买价 double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);//数据依据竞卖价 double CostOfPoint=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE);//数据依据逐笔报价 //--- 用于设置仓位的手数计算变量 double RiskSize_points;//用于存储当前持仓的止损大小的变量 double CostOfPoint_position;//存储当前持仓的点数价格(参考每笔成交的风险)的变量 double Lot;//用于存储持仓手数的变量 double SLPrice_sell,SLPrice_buy;//存储止损价位的变量 //--- 用于存储柱线编号上数据的变量 int bars_base_tf=Bars(Symbol(),base_tf); int bars_work_tf=Bars(Symbol(),work_tf); //--- 用于存储持仓数据的变量 string P_symbol; //持仓品种 int P_type,P_ticket,P_opentime;//开仓类型, 单号和时间 //+------------------------------------------------------------------+
| // | 1. 常规参数 (结束) |
|---|
//+------------------------------------------------------------------+ [/CODE] 代码逐行拆解:第 5 行取当前品种小数位;7–12 行按小数位定 f,把点差除以 f 即得以「点」为单位的真实 spread(第 14 行);15–17 行分别取 bid、ask 与每跳价值 CostOfPoint,后面算仓位风险会用到。 RiskSize_points、Lot、SLPrice_ 等变量此时只是声明,真正赋值在后面的风控逻辑里;bars_base_tf / bars_work_tf 则提前锁定双周期柱线数,方便后续比多周期结构。开 MT5 把这段贴进 OnTick 头部,打印 spread 和 CostOfPoint,能直接验证自己品种的小数位映射有没有写错。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">1. 常规参数 (开始) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 市价数据market data class=class="str">"cmt">//品种价格中的小数位数 class="type">int Digit=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">//定义当前品种的价格容量 class="type">class="kw">double f=class="num">1; if(Digit==class="num">5) {f=class="num">100000;} if(Digit==class="num">4) {f=class="num">10000;} if(Digit==class="num">3) {f=class="num">1000;} if(Digit==class="num">2) {f=class="num">100;} if(Digit==class="num">1) {f=class="num">10;} class=class="str">"cmt">//--- class="type">class="kw">double spread=SymbolInfoInteger(Symbol(),SYMBOL_SPREAD)/f;class=class="str">"cmt">//考虑到价格容量,将点差降低到分数值 class="type">class="kw">double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);class=class="str">"cmt">//数据依据竞买价 class="type">class="kw">double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);class=class="str">"cmt">//数据依据竞卖价 class="type">class="kw">double CostOfPoint=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE);class=class="str">"cmt">//数据依据逐笔报价 class=class="str">"cmt">//--- 用于设置仓位的手数计算变量 class="type">class="kw">double RiskSize_points;class=class="str">"cmt">//用于存储当前持仓的止损大小的变量 class="type">class="kw">double CostOfPoint_position;class=class="str">"cmt">//存储当前持仓的点数价格(参考每笔成交的风险)的变量 class="type">class="kw">double Lot;class=class="str">"cmt">//用于存储持仓手数的变量 class="type">class="kw">double SLPrice_sell,SLPrice_buy;class=class="str">"cmt">//存储止损价位的变量 class=class="str">"cmt">//--- 用于存储柱线编号上数据的变量 class="type">int bars_base_tf=Bars(Symbol(),base_tf); class="type">int bars_work_tf=Bars(Symbol(),work_tf); class=class="str">"cmt">//--- 用于存储持仓数据的变量 class="type">class="kw">string P_symbol; class=class="str">"cmt">//持仓品种 class="type">int P_type,P_ticket,P_opentime;class=class="str">"cmt">//开仓类型, 单号和时间 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">1. 常规参数 (结束) | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 别在每笔报价里刷数组
EA 里数组若在 OnInit() 填一次就不动,跑久了数据必然过期;可要是每来一个 tick 都重填,MT5 主线程会被 Copy* 系列函数拖垮,实盘反应速度大概率下降。折中方案是抓「新柱线」事件才重算,这样系统负载和时效性基本平衡。 具体用 LastBar 时间戳比对当前 SERIES_LASTBAR_DATE:两者不等就说明 base_tf 出了新 K 线,此时一次性把 High/Low/Close/Open/Time 各拉 1000 根。注意 Sar 与 Fractal 指标要用 CopyBuffer 从指标句柄取,参数里 TimeCurrent() 配 1000 长度即可。 零号柱线是个坑。新柱线触发时,刚闭合的那根进了索引 #1,正在形成的 #0 极值随时变,直接填进主数组会污染已闭合数据。所以另开 High_0、Low_0 之类单元素数组专门存 #0,主数组只管 #1 及更老柱线。 分形数组也得单独伺候。只有当 #0 的极值突破前两根的 FractalUp / FractalDown 时,才重填分形缓冲;不然沿用旧值,避免假突破频繁改写结构。外汇与贵金属波动大,这种刷新节奏能降低重绘干扰,但信号仍可能滞后,需结合人工判断。
class="kw">static class="type">class="kw">datetime LastBar_base_tf=class="num">0;class=class="str">"cmt">//用于定义新柱线的变量 class="type">class="kw">datetime ThisBar_base_tf=(class="type">class="kw">datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);class=class="str">"cmt">//当前柱线时间 if(LastBar_base_tf!=ThisBar_base_tf)class=class="str">"cmt">//如果时间不匹配,则表明出现一根新柱线 { class=class="str">"cmt">//在此填充数组 } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">2.1 出现新柱线时填充数组(开始) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 针对 base_tf class=class="str">"cmt">//--- 设置数组顺序为时间序列 ArraySetAsSeries(High_base_tf,true); ArraySetAsSeries(Low_base_tf,true); ArraySetAsSeries(Close_base_tf,true); ArraySetAsSeries(Open_base_tf,true); ArraySetAsSeries(Time_base_tf,true); ArraySetAsSeries(Sar_array_base_tf,true); ArraySetAsSeries(FractalDown_base_tf,true); ArraySetAsSeries(FractalUp_base_tf,true); class=class="str">"cmt">//--- 填充数组 class="kw">static class="type">class="kw">datetime LastBar_base_tf=class="num">0;class=class="str">"cmt">//用于定义新传入柱线的变量 class="type">class="kw">datetime ThisBar_base_tf=(class="type">class="kw">datetime)SeriesInfoInteger(_Symbol,base_tf,SERIES_LASTBAR_DATE);class=class="str">"cmt">//当前柱线开盘时间 if(LastBar_base_tf!=ThisBar_base_tf)class=class="str">"cmt">//如果时间不匹配,则表明出现一根新柱线 { CopyHigh(Symbol(),base_tf,class="num">0,class="num">1000,High_base_tf); CopyLow(Symbol(),base_tf,class="num">0,class="num">1000,Low_base_tf); CopyClose(Symbol(),base_tf,class="num">0,class="num">1000,Close_base_tf); CopyOpen(Symbol(),base_tf,class="num">0,class="num">1000,Open_base_tf); CopyTime(Symbol(),base_tf,class="num">0,class="num">1000,Time_base_tf); CopyBuffer(Sar_base_tf,class="num">0,TimeCurrent(),class="num">1000,Sar_array_base_tf); CopyBuffer(Fractal_base_tf,class="num">0,TimeCurrent(),class="num">1000,FractalUp_base_tf); CopyBuffer(Fractal_base_tf,class="num">1,TimeCurrent(),class="num">1000,FractalDown_base_tf); LastBar_base_tf=ThisBar_base_tf; } class=class="str">"cmt">//--- 针对 work_tf class=class="str">"cmt">//--- 设置数组顺序为时间序列 ArraySetAsSeries(High_work_tf,true); ArraySetAsSeries(Low_work_tf,true); ArraySetAsSeries(Close_work_tf,true); ArraySetAsSeries(Open_work_tf,true); ArraySetAsSeries(Time_work_tf,true); ArraySetAsSeries(Sar_array_work_tf,true); ArraySetAsSeries(FractalDown_work_tf,true); ArraySetAsSeries(FractalUp_work_tf,true); class=class="str">"cmt">//--- 填充数组 class="kw">static class="type">class="kw">datetime LastBar_work_tf=class="num">0;class=class="str">"cmt">//用于定义新柱线的变量
新K线触发与零号柱数组的两种填充路径
多周期策略里最怕两件事:一是工作周期出了新柱却还在用旧数据算信号,二是基准周期拿到的不是当前正在形成的那根柱。下面这段逻辑用时间比对解决第一件事——只在新柱开盘时整体刷新工作周期的历史缓冲。 datetime ThisBar_work_tf=(datetime)SeriesInfoInteger(_Symbol,work_tf,SERIES_LASTBAR_DATE); 取工作周期最后一根柱的开盘时间。if(LastBar_work_tf!=ThisBar_work_tf) 判断时间不等即说明新柱出现,此时才批量 Copy 最近 1000 根的高低价、收盘开盘、时间,以及 SAR 与上下分形缓冲。把 LastBar_work_tf 更新为当前值,避免每 tick 重复拷贝,回测中这能省掉大量冗余调用。 另一路是针对 #0 柱(即当前未闭合柱)的轻量填充。对 base_tf 和 work_tf 各声明一组数组,用 ArraySetAsSeries(...,true) 设成时间序列,使索引 0 永远指向当下这根柱。CopyHigh/CopyLow 等只取 0~1 共 1 根,SAR 用 CopyBuffer 取 1 根,开销极小,适合放在每个 tick 里确认实时状态。外汇与贵金属波动剧烈,实时柱数据错位可能让进场信号偏移数点,高风险下建议开 MT5 用 EURUSD 的 M5/M15 双周期照抄验证。
class="type">class="kw">datetime ThisBar_work_tf=(class="type">class="kw">datetime)SeriesInfoInteger(_Symbol,work_tf,SERIES_LASTBAR_DATE);class=class="str">"cmt">//当前柱线开盘时间 if(LastBar_work_tf!=ThisBar_work_tf)class=class="str">"cmt">//如果时间不匹配,则表明出现一根新柱线 { CopyHigh(Symbol(),work_tf,class="num">0,class="num">1000,High_work_tf); CopyLow(Symbol(),work_tf,class="num">0,class="num">1000,Low_work_tf); CopyClose(Symbol(),work_tf,class="num">0,class="num">1000,Close_work_tf); CopyOpen(Symbol(),work_tf,class="num">0,class="num">1000,Open_work_tf); CopyTime(Symbol(),work_tf,class="num">0,class="num">1000,Time_work_tf); CopyBuffer(Sar_work_tf,class="num">0,TimeCurrent(),class="num">1000,Sar_array_work_tf); CopyBuffer(Fractal_work_tf,class="num">0,TimeCurrent(),class="num">1000,FractalUp_work_tf); CopyBuffer(Fractal_work_tf,class="num">1,TimeCurrent(),class="num">1000,FractalDown_work_tf); LastBar_work_tf=ThisBar_work_tf; } class=class="str">"cmt">//针对 base_tf class=class="str">"cmt">//声明数组 class="type">class="kw">double High_base_tf_0[],Low_base_tf_0[]; class="type">class="kw">double Close_base_tf_0[],Open_base_tf_0[]; class="type">class="kw">datetime Time_base_tf_0[]; class="type">class="kw">double Sar_array_base_tf_0[]; class=class="str">"cmt">//设置数组顺序为时间序列 ArraySetAsSeries(High_base_tf_0,true); ArraySetAsSeries(Low_base_tf_0,true); ArraySetAsSeries(Close_base_tf_0,true); ArraySetAsSeries(Open_base_tf_0,true); ArraySetAsSeries(Time_base_tf_0,true); ArraySetAsSeries(Sar_array_base_tf_0,true); class=class="str">"cmt">//填充数组 CopyHigh(Symbol(),base_tf,class="num">0,class="num">1,High_base_tf_0); CopyLow(Symbol(),base_tf,class="num">0,class="num">1,Low_base_tf_0); CopyClose(Symbol(),base_tf,class="num">0,class="num">1,Close_base_tf_0); CopyOpen(Symbol(),base_tf,class="num">0,class="num">1,Open_base_tf_0); CopyTime(Symbol(),base_tf,class="num">0,class="num">1,Time_base_tf_0); CopyBuffer(Sar_base_tf,class="num">0,TimeCurrent(),class="num">1,Sar_array_base_tf_0); class=class="str">"cmt">//针对 work_tf class=class="str">"cmt">//声明数组 class="type">class="kw">double High_work_tf_0[],Low_work_tf_0[]; class="type">class="kw">double Close_work_tf_0[],Open_work_tf_0[]; class="type">class="kw">datetime Time_work_tf_0[]; class="type">class="kw">double Sar_array_work_tf_0[]; class=class="str">"cmt">//设置数组顺序为时间序列 ArraySetAsSeries(High_work_tf_0,true); ArraySetAsSeries(Low_work_tf_0,true); ArraySetAsSeries(Close_work_tf_0,true); ArraySetAsSeries(Open_work_tf_0,true);
「把工作周期和基准周期的分形刷进数组」
多周期策略里最容易踩的坑,是以为指标缓冲区会自动按时间倒序排好。MQL5 里数组默认是正向索引,所以先用 ArraySetAsSeries 把时间和 SAR 数组翻成序列模式,索引 0 才对应最新一根 K 线。 随后用 CopyHigh / CopyLow / CopyClose / CopyOpen / CopyTime 各取 work_tf 上最新 1 根柱线的数据,CopyBuffer 从 Sar_work_tf 抓 1 个 SAR 值。这里参数写的是 0 到 1 根,意味着只盯当前未闭合或刚闭合的那根,回测时若漏掉这步,SAR 信号会整体偏移一根。 分形更新逻辑分 base_tf 和 work_tf 两套。以 work_tf 为例:当 0 号 K 线高点同时大于前两根(索引 1、2)高点,才把 Fractal_work_tf 的 0 号缓冲(上分形)按 TimeCurrent() 起复制 1000 个值进 FractalUp_work_tf;低点同理走 1 号缓冲。base_tf 的判断完全对称。 注意 CopyBuffer 第三个参数用 TimeCurrent() 而非 0,在实时tick环境里等价于从当前时间往前数,但跨周期调用时比纯索引更稳。外汇与贵金属波动剧烈,这类分形重算若放在 OnTick 里每次全量拷 1000 点,CPU 占用可能偏高,实盘前建议在 MT5 策略测试器里跑一轮看耗时。
ArraySetAsSeries(Time_work_tf_0,true); ArraySetAsSeries(Sar_array_work_tf_0,true); class=class="str">"cmt">//--- 填充数组 CopyHigh(Symbol(),work_tf,class="num">0,class="num">1,High_work_tf_0); CopyLow(Symbol(),work_tf,class="num">0,class="num">1,Low_work_tf_0); CopyClose(Symbol(),work_tf,class="num">0,class="num">1,Close_work_tf_0); CopyOpen(Symbol(),work_tf,class="num">0,class="num">1,Open_work_tf_0); CopyTime(Symbol(),work_tf,class="num">0,class="num">1,Time_work_tf_0); CopyBuffer(Sar_work_tf,class="num">0,TimeCurrent(),class="num">1,Sar_array_work_tf_0); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">2.2 用 #class="num">0 柱线上的数据填充数组 (结束) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">2.3 更新分形数据 (开始) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 针对 base_tf if(High_base_tf_0[class="num">0]>High_base_tf[class="num">1] && High_base_tf_0[class="num">0]>High_base_tf[class="num">2]) { CopyBuffer(Fractal_base_tf,class="num">0,TimeCurrent(),class="num">1000,FractalUp_base_tf); } if(Low_base_tf_0[class="num">0]<Low_base_tf[class="num">1] && Low_base_tf_0[class="num">0]<Low_base_tf[class="num">2]) { CopyBuffer(Fractal_base_tf,class="num">1,TimeCurrent(),class="num">1000,FractalDown_base_tf); } class=class="str">"cmt">//--- 针对 work_tf if(High_work_tf_0[class="num">0]>High_work_tf[class="num">1] && High_work_tf_0[class="num">0]>High_work_tf[class="num">2]) { CopyBuffer(Fractal_work_tf,class="num">0,TimeCurrent(),class="num">1000,FractalUp_work_tf); } if(Low_work_tf_0[class="num">0]<Low_work_tf[class="num">1] && Low_work_tf_0[class="num">0]<Low_work_tf[class="num">2]) { CopyBuffer(Fractal_work_tf,class="num">1,TimeCurrent(),class="num">1000,FractalDown_work_tf); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">2.3 更新分形数据 (结束) | class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 从分形回看修正浪的三点定位
走势延续模型里,AB 是主浪、BC 是修正浪。修正浪必然终结于一个极值分形,也就是图里的 C 点;但真到准备入场时,那个已确认的分形往往早就被后续价格吞掉,所以得实时去抓「当前柱高/低同时越过前两根柱」的状况,那种柱就可能是新生 C 点。 下跌趋势里先找修正高位 C,再顺着 C 往后扫出下一个向上分形当 A,最后在 C 与 A 之间取最低价得 B;上涨则镜像处理。注意 C 可能落在 0 号柱,也可能在索引大于 0 的柱上,这直接决定后面取数组的方式。 麻烦在于:C 在 0 号还是非 0 号,会逼着你用两个不同的数组去引用价格和时间。用 if 把高低值约化进统一变量后,后续逻辑就不必反复判断数组来源,MT5 脚本也更好维护。 下面这段下跌趋势搜索可直接丢进 MT5 看索引怎么走:先判 0 号柱是否已成 C,否则循环找;随后从 C+1 扫 FractalUp 非空值定 A;最后用 ArrayMinimum 在 C~A 区间抓 B。外汇与贵金属杠杆高,分形重绘可能导致 C 点偏移,参数上线前务必用历史数据验证。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">3.1 搜索下跌趋势极值 (开始) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 声明变量 class="type">int High_Corr_wave_downtrend_base_tf;class=class="str">"cmt">//修正走势的高位(点 С) class="type">int UpperFractal_downtrend_base_tf; class=class="str">"cmt">//下一个顶端极值栏线(点 А) class="type">int Low_Corr_wave_downtrend_base_tf; class=class="str">"cmt">//修正走势低位柱线(B点) class=class="str">"cmt">//--- class=class="str">"cmt">//--- 寻找修正走势高位(点 С) if(High_base_tf_0[class="num">0]>High_base_tf[class="num">1] && High_base_tf_0[class="num">0]>High_base_tf[class="num">2]) { High_Corr_wave_downtrend_base_tf=class="num">0; } else { for(n=class="num">0; n<(bars_base_tf);n++) { if(High_base_tf[n]>High_base_tf[n+class="num">1] && High_base_tf[n]>High_base_tf[n+class="num">2]) class="kw">break; } High_Corr_wave_downtrend_base_tf=n; } class=class="str">"cmt">//--- class=class="str">"cmt">//--- 自修正走势的高位寻找下一个极值(点 А) for(n=High_Corr_wave_downtrend_base_tf+class="num">1; n<(bars_base_tf);n++) { class=class="str">"cmt">// --- 如果是非空值,则终止循环 if(FractalUp_base_tf[n]!=EMPTY_VALUE) class="kw">break; } UpperFractal_downtrend_base_tf=n; class=class="str">"cmt">//--- class=class="str">"cmt">//--- 在点 C 和 A 之间找到点 B(修正走势低点) class="type">int CountToFind_arrmin=UpperFractal_downtrend_base_tf-High_Corr_wave_downtrend_base_tf; Low_Corr_wave_downtrend_base_tf=ArrayMinimum(Low_base_tf,High_Corr_wave_downtrend_base_tf,CountToFind_arrmin); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">3.1 搜索下跌趋势的极值 (结束) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| class="num">3.2 搜索上涨趋势极值 (开始) | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- 声明变量 class="type">int Low_Corr_wave_uptrend_base_tf;class=class="str">"cmt">//修正走势的低位(点 С) class="type">int LowerFractal_uptrend_base_tf; class=class="str">"cmt">//下一个底端极值栏线(点 А) class="type">int High_Corr_wave_uptrend_base_tf; class=class="str">"cmt">//修正走势高位(点 B) class=class="str">"cmt">//--- class=class="str">"cmt">//--- 寻找修正走势低位(点 С) if(Low_base_tf_0[class="num">0]<Low_base_tf[class="num">1] && Low_base_tf_0[class="num">0]<Low_base_tf[class="num">2]) { Low_Corr_wave_uptrend_base_tf=class="num">0; } else
把修正浪拐点写进公共变量
上一节定位到上涨与下跌修正浪的极值下标后,这一节要把它们真正落进可跨模块调用的双精度与时间变量里,否则后面的形态判定只能停留在局部数组。 回滚低点的捕捉先用一段倒序扫描:从 0 到 bars_base_tf,只要某根 Low_base_tf 同时小于其后两根的低点就跳出,记下 Low_Corr_wave_uptrend_base_tf。接着从该点再往后找第一个 FractalDown 非空值,作为 LowerFractal_uptrend_base_tf,二者之差就是 ArrayMaximum 搜修正高点的窗口长度。 代入公共变量时区分了下标是否为 0:若 High_Corr_wave_downtrend_base_tf 等于 0,就取带 _0 后缀的数组(当前未闭合柱)拿价格与时间,否则取普通数组。Low_Corr_wave_uptrend_base_tf 同理。这样处理能避免用第 0 根柱的序列偏移去错配已闭合 K 线的数据。 在 MT5 里把这段接进 EA,打印出 High_Corr_wave_downtrend_base_tf_double 与 Low_Corr_wave_uptrend_base_tf_time,对照图表上的分形标记,能直接验证拐点捕获是否和肉眼一致。外汇与贵金属波动跳空频繁,这类基于分形的极值映射在重大数据行情中可能失效,需以概率视角看待。
{
class=class="str">"cmt">//寻找回滚低点
for(n=class="num">0; n<(bars_base_tf);n++)
{
if(Low_base_tf[n]<Low_base_tf[n+class="num">1] && Low_base_tf[n]<Low_base_tf[n+class="num">2])
class="kw">break;
}
Low_Corr_wave_uptrend_base_tf=n;
}
class=class="str">"cmt">//---
class=class="str">"cmt">//--- 从修正走势低点,寻找下一个底端极值点(点 А)
for(n=Low_Corr_wave_uptrend_base_tf+class="num">1; n<(bars_base_tf);n++)
{
if(FractalDown_base_tf[n]!=EMPTY_VALUE)
class="kw">break;
}
LowerFractal_uptrend_base_tf=n;
class=class="str">"cmt">//---
class=class="str">"cmt">//--- 在点 C 和 A 之间找到点 B(修正走势高点)
class="type">int CountToFind_arrmax=LowerFractal_uptrend_base_tf-Low_Corr_wave_uptrend_base_tf;
High_Corr_wave_uptrend_base_tf=ArrayMaximum(High_base_tf,Low_Corr_wave_uptrend_base_tf,CountToFind_arrmax);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| class="num">3.2 搜索上涨趋势的极值 (结束) |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+----------------------------------------------------------------------------------+
class=class="str">"cmt">//| class="num">3.3 将修正浪的高/低值代入公共变量 (开始) |
class=class="str">"cmt">//+----------------------------------------------------------------------------------+
class=class="str">"cmt">//--- 声明变量
class="type">class="kw">double High_Corr_wave_downtrend_base_tf_double,Low_Corr_wave_uptrend_base_tf_double;
class="type">class="kw">datetime High_Corr_wave_downtrend_base_tf_time,Low_Corr_wave_uptrend_base_tf_time;
class=class="str">"cmt">//--- 针对 High_Corr_wave_downtrend_base_tf
if(High_Corr_wave_downtrend_base_tf==class="num">0)
{
High_Corr_wave_downtrend_base_tf_double=High_base_tf_0[High_Corr_wave_downtrend_base_tf];
High_Corr_wave_downtrend_base_tf_time=Time_base_tf_0[High_Corr_wave_downtrend_base_tf];
}
else
{
High_Corr_wave_downtrend_base_tf_double=High_base_tf[High_Corr_wave_downtrend_base_tf];
High_Corr_wave_downtrend_base_tf_time=Time_base_tf[High_Corr_wave_downtrend_base_tf];
}
class=class="str">"cmt">//-- 针对 Low_Corr_wave_uptrend_base_tf
if(Low_Corr_wave_uptrend_base_tf==class="num">0)
{
Low_Corr_wave_uptrend_base_tf_double=Low_base_tf_0[Low_Corr_wave_uptrend_base_tf];
Low_Corr_wave_uptrend_base_tf_time=Time_base_tf_0[Low_Corr_wave_uptrend_base_tf];
}
else
{
Low_Corr_wave_uptrend_base_tf_double=Low_base_tf[Low_Corr_wave_uptrend_base_tf];
Low_Corr_wave_uptrend_base_tf_time=Time_base_tf[Low_Corr_wave_uptrend_base_tf];
}
class=class="str">"cmt">//+---------------------------------------------------------------------------------+
class=class="str">"cmt">//| class="num">3.3 将修正浪的高/低值代入公共变量 (结束) |
class=class="str">"cmt">//+---------------------------------------------------------------------------------+