迷你行情模拟器或手动策略测试器·进阶篇
(2/3)· 全职没空盯盘、回测漂亮实盘拉胯?自己写个轻量模拟器在 MT5 里跑策略
指标初始化与定时器驱动的回放骨架
做历史回放类指标,第一步是把模拟起点从图表空白处拉到真实历史里。OnInit() 里先读全局变量 time_end 作为仿真停止时间,再开定时器按节奏吐出分笔或成根蜡烛;没有这个偏移量,策略检验会直接从图表左侧虚无区开始,毫无意义。 [CODE] //--- 设置指标绘制的时间 if(GlobalVariableCheck(time_end))end_time_indicator=datetime(GlobalVariableGet(time_end)); //+------------------------------------------------------------------+
| // | Timer 函数 |
|---|
//+------------------------------------------------------------------+ void OnTimer() { //--- if(button_play) { end_bar_indicator=Bars(_Symbol,_Period,end_time_indicator,TimeCurrent()); // 从最早到现在的柱线数量 ChartNavigate(0,CHART_END,-end_bar_indicator); // 移动图表 (指标) 到当前建模的柱线 number_now_rates=(Bars(_Symbol,_Period,real_start,end_time_indicator)-1); // 当前用于建模的柱线 bars_now_rates=(Bars(_Symbol,_Period,real_start,stop)-1); // 从来自历史记录的当前周期使用的柱线数 all_bars_indicator=(Bars(_Symbol,_Period,real_start,TimeCurrent()))-1; // 从模拟开始到当前时间的柱线数 if(end_time_indicator<stop) // 检查模拟时间 { func_merger(); ObjectSetDouble(0,line_bid,OBJPROP_PRICE,price_bid_now); if(ObjectFind(0,line_ask)>=0) {ObjectSetDouble(0,line_ask,OBJPROP_PRICE,price_ask_now);} //--- 订单的当前值 int point_now=0; double vol_now=0; [/CODE] 上面这段代码就是定时器入口的骨架。button_play 没按下时整段跳过;按下后先用 Bars() 算 end_bar_indicator,再用 ChartNavigate(0,CHART_END,-end_bar_indicator) 把可视区钉在正在建模的柱线——周六周日图表不动,但定时器仍在跑,所以每秒重算一次端点才能跟住实时位移。 number_now_rates / bars_now_rates / all_bars_indicator 三个计数把“已建模 / 取自历史 / 总跨度”拆开,end_time_indicator<stop 才进 func_merger() 继续生成行情。外汇与贵金属杠杆高、滑点跳空频繁,这类离线重播仅用于验证逻辑,实盘表现可能有明显偏差。 COrder 类管的是回放里的挂单生命周期:Placed 用 switch 分买卖写入全局变量并画止盈止损线,Delete 在触线或手动删线时清零 order_sell/order_buy;利润累加到 info_point_all 等全局变量,靠 small_concatenation 做类似 += 的拼接。鼠标拖价改线走 Small_mod,禁止移动时按订单类型重画止盈或止损并弹回原线。 蜡烛生成分三档速度合在 func_merger():func_candle_per_seconds() 每秒填一根走“第七速”;func_of_form_candle() 按 M1 数据慢慢养出当前周期蜡烛,管第二到第六速;func_of_form_jeweler_candle() 调分笔报价发生器逐 tick 逼近真实影线。切换周期会重初始化,所以 func_filling() 负责把“尾巴”重铺一遍,返回 work_status 让定时器知道何时回头补根。
class=class="str">"cmt">//--- 设置指标绘制的时间 if(GlobalVariableCheck(time_end))end_time_indicator=class="type">class="kw">datetime(GlobalVariableGet(time_end)); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Timer 函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTimer() { class=class="str">"cmt">//--- if(button_play) { end_bar_indicator=Bars(_Symbol,_Period,end_time_indicator,TimeCurrent()); class=class="str">"cmt">// 从最早到现在的柱线数量 ChartNavigate(class="num">0,CHART_END,-end_bar_indicator); class=class="str">"cmt">// 移动图表 (指标) 到当前建模的柱线 number_now_rates=(Bars(_Symbol,_Period,real_start,end_time_indicator)-class="num">1); class=class="str">"cmt">// 当前用于建模的柱线 bars_now_rates=(Bars(_Symbol,_Period,real_start,stop)-class="num">1); class=class="str">"cmt">// 从来自历史记录的当前周期使用的柱线数 all_bars_indicator=(Bars(_Symbol,_Period,real_start,TimeCurrent()))-class="num">1; class=class="str">"cmt">// 从模拟开始到当前时间的柱线数 if(end_time_indicator<stop) class=class="str">"cmt">// 检查模拟时间 { func_merger(); ObjectSetDouble(class="num">0,line_bid,OBJPROP_PRICE,price_bid_now); if(ObjectFind(class="num">0,line_ask)>=class="num">0) {ObjectSetDouble(class="num">0,line_ask,OBJPROP_PRICE,price_ask_now);} class=class="str">"cmt">//--- 订单的当前值 class="type">int point_now=class="num">0; class="type">class="kw">double vol_now=class="num">0;
「浮盈与仓位信息的实时聚合」
这段逻辑在每次 tick 里把当前持仓的浮动点数和金额算出来,再写回全局变量供面板读取。注意买入单用 bid 减开仓价、卖出单用开仓价减 ask,乘上 dig_pow 转成整数点差,再乘Volume*10得到美元浮盈——这是标准外汇合约每点 10 美元/手的近似。 [CODE] double money_now=0; if(ObjectFind(0,order_buy)>=0 && GlobalVariableGet(order_buy)>0) // 存在买入订单 { int p_now=int((price_bid_now-GlobalVariableGet(order_buy))*dig_pow); // 用当前买价减去买入开仓价,乘精度系数转成整数点 // 例:EURUSD dig_pow=10000,价差0.0005 => p_now=5点 double v_now=GlobalVariableGet(vol_buy); // 读取买入单的手数全局变量 double m_now=p_now*v_now*10; // 浮盈=点数*手数*10(每标准手每点约10美元) point_now+=p_now; vol_now+=v_now; money_now+=m_now; } if(ObjectFind(0,order_sell)>=0 && GlobalVariableGet(order_sell)>0) // 存在卖出订单 { int p_now=int((GlobalVariableGet(order_sell)-price_ask_now)*dig_pow); // 用卖出开仓价减当前卖价,转整数点 double v_now=GlobalVariableGet(vol_sell); double m_now=p_now*v_now*10; point_now+=p_now; vol_now+=v_now; money_now+=m_now; } GlobalVariableSet(info_point_now,point_now); GlobalVariableSet(info_vol_now,vol_now); GlobalVariableSet(info_money_now,money_now); } [/CODE] 算完之后 COrder 对象做删除与校验:Delete 清掉失效挂单,Check 比对止盈止损和指标结束时间。随后 func_info_print 把 All/Last/Now 三组 Money、Volume、Point 打到日志,小数位分别是2、2、0。 隐藏按钮靠全局变量 "hide" 切换:x==1 时把对象"20"状态置 false,并把"14"~"19"的 YDISTANCE 设为正24像素(下移);x==2 则状态 true、YDISTANCE 为负24(上移)。外汇与贵金属波动剧烈,这类浮盈计算仅反映当前概率性敞口,实盘前请在 MT5 策略测试器用真实点差验证。
class="type">class="kw">double money_now=class="num">0; if(ObjectFind(class="num">0,order_buy)>=class="num">0 && GlobalVariableGet(order_buy)>class="num">0) class=class="str">"cmt">// 存在买入订单 { class="type">int p_now=class="type">int((price_bid_now-GlobalVariableGet(order_buy))*dig_pow); class="type">class="kw">double v_now=GlobalVariableGet(vol_buy); class="type">class="kw">double m_now=p_now*v_now*class="num">10; point_now+=p_now; vol_now+=v_now; money_now+=m_now; } if(ObjectFind(class="num">0,order_sell)>=class="num">0 && GlobalVariableGet(order_sell)>class="num">0) class=class="str">"cmt">// 存在卖出订单 { class="type">int p_now=class="type">int((GlobalVariableGet(order_sell)-price_ask_now)*dig_pow); class="type">class="kw">double v_now=GlobalVariableGet(vol_sell); class="type">class="kw">double m_now=p_now*v_now*class="num">10; point_now+=p_now; vol_now+=v_now; money_now+=m_now; } GlobalVariableSet(info_point_now,point_now); GlobalVariableSet(info_vol_now,vol_now); GlobalVariableSet(info_money_now,money_now); } COrder position; class=class="str">"cmt">//"COrder" 类的对象 position.Delete(price_bid_now,price_ask_now,(-class="num">1)); position.Check(end_time_indicator,GlobalVariableGet(order_buy),GlobalVariableGet(tp_buy),GlobalVariableGet(sl_buy), GlobalVariableGet(order_sell),GlobalVariableGet(tp_sell),GlobalVariableGet(sl_sell)); func_info_print("Money All: ",info_money_all,class="num">2); func_info_print("Money Last: ",info_money_last,class="num">2); func_info_print("Money Now: ",info_money_now,class="num">2); func_info_print("Volume All: ",info_vol_all,class="num">2); func_info_print("Volume Last: ",info_vol_last,class="num">2); func_info_print("Volume Now: ",info_vol_now,class="num">2); func_info_print("Point All: ",info_point_all,class="num">0); func_info_print("Point Last: ",info_point_last,class="num">0); func_info_print("Point Now: ",info_point_now,class="num">0); position.Modify(); } class=class="str">"cmt">//--- 管理隐藏按钮 class="type">char x=class="type">char(GlobalVariableGet("hide")); if(x==class="num">1) { ObjectSetInteger(class="num">0,"class="num">20",OBJPROP_STATE,false); ObjectSetInteger(class="num">0,"class="num">14",OBJPROP_YDISTANCE,class="num">24); ObjectSetInteger(class="num">0,"class="num">15",OBJPROP_YDISTANCE,class="num">24); ObjectSetInteger(class="num">0,"class="num">16",OBJPROP_YDISTANCE,class="num">24); ObjectSetInteger(class="num">0,"class="num">17",OBJPROP_YDISTANCE,class="num">24); ObjectSetInteger(class="num">0,"class="num">18",OBJPROP_YDISTANCE,class="num">24); ObjectSetInteger(class="num">0,"class="num">19",OBJPROP_YDISTANCE,class="num">24); } if(x==class="num">2) { ObjectSetInteger(class="num">0,"class="num">20",OBJPROP_STATE,true); ObjectSetInteger(class="num">0,"class="num">14",OBJPROP_YDISTANCE,-class="num">24); ObjectSetInteger(class="num">0,"class="num">15",OBJPROP_YDISTANCE,-class="num">24); ObjectSetInteger(class="num">0,"class="num">16",OBJPROP_YDISTANCE,-class="num">24); ObjectSetInteger(class="num">0,"class="num">17",OBJPROP_YDISTANCE,-class="num">24); ObjectSetInteger(class="num">0,"class="num">18",OBJPROP_YDISTANCE,-class="num">24);
◍ 用类封装挂单的买卖线与损盈线
把下单逻辑收进 COrder 类,比在 EA 主循环里散写更容易维护。Placed() 接收订单类型、买卖价和止损止盈点数,按类型 1(买)或 2(卖)分支处理,用全局变量存价格再调 Line() 画水平线。 买入时止盈线画在 ask 加点数×_Point 的位置,止损线在 ask 减对应距离;卖出则反向,止盈在 bid 减、止损在 bid 加。这样黄金和欧美盘里,肉眼能直接看到挂单位置与损盈边界。 删除分支里有个细节:m_del_manual 为 -1 时是自动清理,只删那些对象已不在图表、但全局变量还大于 0 的残留线。手动删(1 或 2)则先 ObjectDelete 主线,再调 Small_del_sell / Small_del_buy 清掉附属的 TP、SL 虚线。外汇与贵金属波动大,这类对象残留可能导致误判,建议开 MT5 跑一遍看图表是否干净。
class COrder { class="kw">public: class="type">void Placed( class="type">char m_type,class=class="str">"cmt">// 订单类型 (class="num">1-买入, class="num">2-卖出) class="type">class="kw">double m_price_bid, class=class="str">"cmt">// 竞买价 class="type">class="kw">double m_price_ask, class=class="str">"cmt">// 竞卖价 class="type">int m_take_profit,class=class="str">"cmt">// 止盈点数 class="type">int m_stop_loss class=class="str">"cmt">// 止损点数 ) { class="kw">switch(m_type) { case class="num">1: { GlobalVariableSet(order_buy,m_price_ask); Line(GlobalVariableGet(order_buy),order_buy,col_buy,STYLE_SOLID,class="num">1,true); if(m_take_profit>class="num">0) { GlobalVariableSet(tp_buy,(m_price_ask+(_Point*m_take_profit))); Line(GlobalVariableGet(tp_buy),tp_buy,col_tp,STYLE_DASH,class="num">1,true); } if(m_stop_loss>class="num">0) { GlobalVariableSet(sl_buy,(m_price_ask-(_Point*m_stop_loss))); Line(GlobalVariableGet(sl_buy),sl_buy,col_sl,STYLE_DASH,class="num">1,true); } } break; case class="num">2: { GlobalVariableSet(order_sell,m_price_bid); Line(GlobalVariableGet(order_sell),order_sell,col_sell,STYLE_SOLID,class="num">1,true); if(m_take_profit>class="num">0) { GlobalVariableSet(tp_sell,(m_price_bid-(_Point*m_take_profit))); Line(GlobalVariableGet(tp_sell),tp_sell,col_tp,STYLE_DASH,class="num">1,true); } if(m_stop_loss>class="num">0) { GlobalVariableSet(sl_sell,(m_price_bid+(_Point*m_stop_loss))); Line(GlobalVariableGet(sl_sell),sl_sell,col_sl,STYLE_DASH,class="num">1,true); } } break; } } class="type">void Delete( class="type">class="kw">double m_price_bid, class=class="str">"cmt">// 竞买价 class="type">class="kw">double m_price_ask, class=class="str">"cmt">// 竞卖价 class="type">char m_del_manual class=class="str">"cmt">// 删除类型 (-class="num">1 - 自动, class="num">1 - 买入, class="num">2 - 卖出) ) { class="kw">switch(m_del_manual) { case(-class="num">1): if(ObjectFind(class="num">0,order_buy)<class="num">0 && GlobalVariableGet(order_buy)>class="num">0) {Small_del_buy(m_price_bid);} if(ObjectFind(class="num">0,order_sell)<class="num">0 && GlobalVariableGet(order_sell)>class="num">0) {Small_del_sell(m_price_ask);} break; case class="num">1: if(ObjectFind(class="num">0,order_buy)>=class="num">0) { ObjectDelete(class="num">0,order_buy); Small_del_buy(m_price_bid); } break; case class="num">2: if(ObjectFind(class="num">0,order_sell)>=class="num">0) { ObjectDelete(class="num">0,order_sell); Small_del_sell(m_price_ask); } break; } } class="type">void Small_del_sell(class="type">class="kw">double m_price_ask) { if(ObjectFind(class="num">0,tp_sell)>=class="num">0)ObjectDelete(class="num">0,tp_sell); class=class="str">"cmt">// 删除止盈指示线 if(ObjectFind(class="num">0,sl_sell)>=class="num">0)ObjectDelete(class="num">0,sl_sell); class=class="str">"cmt">// 删除止损指示线
用全局变量把图形拖拽同步进EA状态
订单平仓后,先把卖单挂单价与当前 ask 的差值折算成点数:MathRound((GlobalVariableGet(order_sell)-m_price_ask)/_Point) 得出 point_plus,这一步把浮亏浮盈固化成可累加的整数点。随后把 order_sell 等变量清零,并把本笔的成交量、点数、金额(点数×成交量×10)写入 info_*_last,再喂给 Small_concatenation 做历史序列拼接。 Small_mod 负责把图表上的手动线对象价格,与同名的全局变量对齐。它先用 ObjectFind 确认对象存在,再分别取对象价 price_obj 与全局变量价 price_glo(均乘 dig_pow 转整避免浮点误差)。若两者不等且 m_mode=true,就允许把对象价写回全局变量;若 m_mode=false,则按 m_type 区分买卖,对象价高于原值对买单为止盈、对卖单为止损,反向则反之,并调用 Line 重绘虚线。 Check 函数用 Bars(_Symbol,PERIOD_M1,real_start,time_end_check) 算出两次调用间新生成的 M1 柱数量(start_of_z 与 end_of_z),以 time_end_order_check 这个全局变量做游标,避免重复扫描历史。外汇与贵金属杠杆高,这类手动线同步逻辑若 dig_pow 设错,可能把止损画偏好几个点。
class="type">int point_plus=class="type">int(MathRound((GlobalVariableGet(order_sell)-m_price_ask)/_Point)); class=class="str">"cmt">// 计算交易的利润 GlobalVariableSet(order_sell,class="num">0); class=class="str">"cmt">// 将所下订单的价格变量清零 GlobalVariableSet(info_vol_last,GlobalVariableGet(vol_sell)); GlobalVariableSet(vol_sell,class="num">0); GlobalVariableSet(info_point_last,point_plus); GlobalVariableSet(info_money_last,(GlobalVariableGet(info_point_last)*GlobalVariableGet(info_vol_last)*class="num">10)); Small_concatenation(info_point_all,info_point_last); Small_concatenation(info_vol_all,info_vol_last); Small_concatenation(info_money_all,info_money_last); } class="type">void Small_mod(class="type">class="kw">string m_name, class=class="str">"cmt">// 对象的名称和全局变量 class="type">bool m_mode, class=class="str">"cmt">// 改变位置的许可 class="type">char m_type class=class="str">"cmt">// class="num">1 — 买入, class="num">2 — 卖出 ) { if(ObjectFind(class="num">0,m_name)>=class="num">0) { class="type">class="kw">double price_obj_double=ObjectGetDouble(class="num">0,m_name,OBJPROP_PRICE); class="type">int price_obj=class="type">int(price_obj_double*dig_pow); class="type">class="kw">double price_glo_double=GlobalVariableGet(m_name); class="type">int price_glo=class="type">int(price_glo_double*dig_pow); if(price_obj!=price_glo && m_mode==true) { GlobalVariableSet(m_name,(class="type">class="kw">double(price_obj)/class="type">class="kw">double(dig_pow))); } if(price_obj!=price_glo && m_mode==false) { class="kw">switch(m_type) { case class="num">1: class=class="str">"cmt">// 买入订单 if(price_obj>price_glo) class=class="str">"cmt">// 止盈 { GlobalVariableSet(tp_buy,(class="type">class="kw">double(price_obj)/class="type">class="kw">double(dig_pow))); Line(GlobalVariableGet(tp_buy),tp_buy,col_tp,STYLE_DASH,class="num">1,true); } if(price_obj<price_glo) class=class="str">"cmt">// 止损 { GlobalVariableSet(sl_buy,(class="type">class="kw">double(price_obj)/class="type">class="kw">double(dig_pow))); Line(GlobalVariableGet(sl_buy),sl_buy,col_sl,STYLE_DASH,class="num">1,true); } break; case class="num">2: class=class="str">"cmt">// 卖出订单 if(price_obj>price_glo) class=class="str">"cmt">// 止损 { GlobalVariableSet(sl_sell,(class="type">class="kw">double(price_obj)/class="type">class="kw">double(dig_pow))); Line(GlobalVariableGet(sl_sell),sl_sell,col_sl,STYLE_DASH,class="num">1,true); } if(price_obj<price_glo) class=class="str">"cmt">// 止盈 { GlobalVariableSet(tp_sell,(class="type">class="kw">double(price_obj)/class="type">class="kw">double(dig_pow))); Line(GlobalVariableGet(tp_sell),tp_sell,col_tp,STYLE_DASH,class="num">1,true); } break; } ObjectSetDouble(class="num">0,m_name,OBJPROP_PRICE,(class="type">class="kw">double(price_glo)/class="type">class="kw">double(dig_pow))); } } } class="type">void Check( class="type">class="kw">datetime m_time, class="type">class="kw">double m_price_buy, class="type">class="kw">double m_price_tp_buy, class="type">class="kw">double m_price_sl_buy, class="type">class="kw">double m_price_sell, class="type">class="kw">double m_price_tp_sell, class="type">class="kw">double m_price_sl_sell ) { class="type">int start_of_z=class="num">0; class="type">int end_of_z=class="num">0; class="type">class="kw">datetime time_end_check=class="type">class="kw">datetime(GlobalVariableGet(time_end_order_check)); if(time_end_check<=class="num">0){time_end_check=m_time;} GlobalVariableSet(time_end_order_check,m_time); start_of_z=Bars(_Symbol,PERIOD_M1,real_start,time_end_check); end_of_z=Bars(_Symbol,PERIOD_M1,real_start,m_time);
「逐根回放里的挂单清算与数据回填」
这段逻辑干了两件事:一是把 M1 每根 K 线的高低价加上点差扩成 ask/bid 区间,再拿已有买入、卖出仓的 TP/SL 去比对,命中就调 del.Delete 平仓。 回看循环头部,z 从 start_of_z 走到 end_of_z,period_m1[z].high 与 .low 直接当作 bid 高低,p_ask_high = p_bid_high + spread*_Point,说明点差是以 _Point 为单位的浮点倍数,黄金在常规点差 20 点(_Point=0.01)下 ask 会比 bid 高 0.20。 买入单的止盈看 bid 区间(m_price_tp_buy 落在 p_bid_high 与 p_bid_low 之间),止损同理;卖出单反过来用 ask 区间判止损、用 ask 区间判止盈,del.Delete 的第三个参数 1 代表 buy、2 代表 sell,平仓价直接传 TP 或 SL 值。 func_filling 则是把当前周期 rates 往输出数组倒填:从最新一根往回扫,遇到 time 小于指标时间就写 open/high/low/close,阴线 output_col 置 0、阳线置 1;最后把指标当前柱的开高低收全压成 open 值,work_status 置 -1 表示填完。 让小布替你跑这套:把 spread 打印出来,看黄金在亚盘低点差时 TP 命中率是否比美盘高,外汇与贵金属自带高杠杆高风险,回测结论只代表历史概率。
for(class="type">int z=start_of_z; z<end_of_z; z++) { COrder del; class="type">class="kw">double p_bid_high=period_m1[z].high; class="type">class="kw">double p_bid_low=period_m1[z].low; class="type">class="kw">double p_ask_high=p_bid_high+(spread*_Point); class="type">class="kw">double p_ask_low=p_bid_low+(spread*_Point); if(m_price_buy>class="num">0) class=class="str">"cmt">// 这是一笔买入订单 { if(ObjectFind(class="num">0,tp_buy)>=class="num">0) { if(m_price_tp_buy<=p_bid_high && m_price_tp_buy>=p_bid_low) class=class="str">"cmt">// 止盈触发 {del.Delete(m_price_tp_buy,class="num">0,class="num">1);} class=class="str">"cmt">// 在止盈价平仓 } if(ObjectFind(class="num">0,sl_buy)>=class="num">0) { if(m_price_sl_buy>=p_bid_low && m_price_sl_buy<=p_bid_high) class=class="str">"cmt">// 止损触发 {del.Delete(m_price_sl_buy,class="num">0,class="num">1);} class=class="str">"cmt">// 在止损价平仓 } } if(m_price_sell>class="num">0) class=class="str">"cmt">// 这是一笔卖出订单 { if(ObjectFind(class="num">0,tp_sell)>=class="num">0) { if(m_price_sl_sell<=p_ask_high && m_price_sl_sell>=p_ask_low) class=class="str">"cmt">// 止损触发 {del.Delete(class="num">0,m_price_sl_sell,class="num">2);} class=class="str">"cmt">// 在止损价平仓 } if(ObjectFind(class="num">0,sl_sell)>=class="num">0) { if(m_price_tp_sell>=p_ask_low && m_price_tp_sell<=p_ask_high) class=class="str">"cmt">// 止盈触发 {del.Delete(class="num">0,m_price_tp_sell,class="num">2);} class=class="str">"cmt">// 在止盈价平仓 } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Filling 函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void func_filling(class="type">MqlRates &input_rates[], class=class="str">"cmt">// 输入 (当前周期) 数据来填充 class="type">class="kw">datetime input_end_time_indicator, class=class="str">"cmt">// 指标的当前时间 class="type">int input_all_bars_indicator, class=class="str">"cmt">// 指标的所有柱线数量 class="type">class="kw">datetime &output_time_end_filling, class=class="str">"cmt">// 最后一根柱线的开盘时间 class="type">class="kw">datetime &output_time_next_filling, class=class="str">"cmt">// 下一根柱线的开盘时间 class="type">int input_end_bar_indicator, class=class="str">"cmt">// 指标的当前 (绘制) 柱线 class="type">class="kw">double &output_o[], class="type">class="kw">double &output_h[], class="type">class="kw">double &output_l[], class="type">class="kw">double &output_c[], class="type">class="kw">double &output_col[], class="type">char &work_status) class=class="str">"cmt">// 操作状态 { if(work_status==class="num">1) { class="type">int stopped_rates_bar; for(class="type">int x=input_all_bars_indicator,y=class="num">0;x>class="num">0;x--,y++) { if(input_rates[y].time<input_end_time_indicator) { output_o[x]=input_rates[y].open; output_h[x]=input_rates[y].high; output_l[x]=input_rates[y].low; output_c[x]=input_rates[y].close; if(output_o[x]>output_c[x])output_col[x]=class="num">0; else output_col[x]=class="num">1; output_time_end_filling=input_rates[y].time; output_time_next_filling=input_rates[y+class="num">1].time; input_end_bar_indicator=x; stopped_rates_bar=y; } else break; } output_o[input_end_bar_indicator]=input_rates[stopped_rates_bar].open; output_h[input_end_bar_indicator]=output_o[input_end_bar_indicator]; output_l[input_end_bar_indicator]=output_o[input_end_bar_indicator]; output_c[input_end_bar_indicator]=output_o[input_end_bar_indicator]; work_status=-class="num">1; } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 每秒蜡烛函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void func_candle_per_seconds(class="type">MqlRates &input_rates[], class="type">class="kw">datetime &input_end_time_indicator, class="type">int input_bars_now_rates, class="type">int input_number_now_rates, class="type">int &input_end_bar_indicator, class="type">class="kw">double &output_o[], class="type">class="kw">double &output_h[],
◍ 用 M1 分笔回填重塑指标蜡烛
指标在非实时合成阶段,靠 MqlRates 数组逐根回填输出缓冲。work_status 为 -1 时代表仍处于快速填充态,此时先把 input_number_now_rates 指向的那根开高低收写进 output_o/h/l/c,并用收线对比开盘决定 output_col:阴线记 0,阳线记 1。 func_of_form_candle 里用 Bars(_Symbol,PERIOD_M1,real_start,input_time_end_filling) 和 Bars(...,input_end_time_indicator) 算出起止 M1 根数,循环把 high/low 向外扩、close 持续更新。当 input_end_time_indicator 越过 input_time_next_filling,work_status 置 1,填充结束。 珠宝蜡烛函数(func_of_form_jeweler_candle)参数结构一致,多带 input_time_next_filling 与 current_of_z 局部量,用于在更细粒度上接续模拟。开 MT5 把这段挂到自定义指标里,改 PERIOD_M1 为 PERIOD_M5 可观察不同基准周期下回填密度的差异,外汇与贵金属品种波动跳空频繁,此类合成逻辑在高波动时段可能偏离真实成交。
class="type">class="kw">double &output_l[], class="type">class="kw">double &output_c[], class="type">class="kw">double &output_col[], class="type">char &work_status) { if(work_status==-class="num">1) { if(input_number_now_rates<input_bars_now_rates) { if(input_number_now_rates!=class="num">0) { output_o[input_end_bar_indicator]=input_rates[input_number_now_rates-class="num">1].open; output_h[input_end_bar_indicator]=input_rates[input_number_now_rates-class="num">1].high; output_l[input_end_bar_indicator]=input_rates[input_number_now_rates-class="num">1].low; output_c[input_end_bar_indicator]=input_rates[input_number_now_rates-class="num">1].close; if(output_o[input_end_bar_indicator]>output_c[input_end_bar_indicator])output_col[input_end_bar_indicator]=class="num">0; else output_col[input_end_bar_indicator]=class="num">1; } input_end_bar_indicator--; output_o[input_end_bar_indicator]=input_rates[input_number_now_rates].open; output_h[input_end_bar_indicator]=input_rates[input_number_now_rates].high; output_l[input_end_bar_indicator]=input_rates[input_number_now_rates].low; output_c[input_end_bar_indicator]=input_rates[input_number_now_rates].close; if(output_o[input_end_bar_indicator]>output_c[input_end_bar_indicator])output_col[input_end_bar_indicator]=class="num">0; else output_col[input_end_bar_indicator]=class="num">1; input_end_time_indicator=input_rates[input_number_now_rates+class="num">1].time; } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 形成蜡烛的函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void func_of_form_candle(class="type">MqlRates &input_rates[], class="type">int input_bars, class="type">class="kw">datetime &input_time_end_filling, class="type">class="kw">datetime &input_end_time_indicator, class="type">class="kw">datetime &input_time_next_filling, class="type">int input_end_bar_indicator, class="type">class="kw">double &output_o[], class="type">class="kw">double &output_h[], class="type">class="kw">double &output_l[], class="type">class="kw">double &output_c[], class="type">class="kw">double &output_col[], class="type">char &work_status) { if(work_status==-class="num">1) { class="type">int start_of_z=class="num">0; class="type">int end_of_z=class="num">0; start_of_z=Bars(_Symbol,PERIOD_M1,real_start,input_time_end_filling); end_of_z=Bars(_Symbol,PERIOD_M1,real_start,input_end_time_indicator); for(class="type">int z=start_of_z; z<end_of_z; z++) { output_c[input_end_bar_indicator]=input_rates[z].close; if(output_h[input_end_bar_indicator]<input_rates[z].high)output_h[input_end_bar_indicator]=input_rates[z].high; if(output_l[input_end_bar_indicator]>input_rates[z].low)output_l[input_end_bar_indicator]=input_rates[z].low; if(output_o[input_end_bar_indicator]>output_c[input_end_bar_indicator])output_col[input_end_bar_indicator]=class="num">0; else output_col[input_end_bar_indicator]=class="num">1; } if(input_end_time_indicator>=input_time_next_filling)work_status=class="num">1; } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 形成珠宝蜡烛的函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void func_of_form_jeweler_candle(class="type">MqlRates &input_rates[], class="type">int input_bars, class="type">class="kw">datetime &input_time_end_filling, class="type">class="kw">datetime &input_end_time_indicator, class="type">class="kw">datetime &input_time_next_filling, class="type">int input_end_bar_indicator, class="type">class="kw">double &output_o[], class="type">class="kw">double &output_h[], class="type">class="kw">double &output_l[], class="type">class="kw">double &output_c[], class="type">class="kw">double &output_col[], class="type">char &work_status) { if(work_status==-class="num">1) { class="type">int start_of_z=class="num">0; class="type">int current_of_z=class="num">0;