//+------------------------------------------------------------------+ //| OnTesterInit_Sample.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property description "Sample EA with the OnTesterInit() handler," #property description "in which values and limitations of " #property description "inputs during optimization are set" input double lots=0. 1; // 交易量手数 input double kATR=3; // ATR中的信号蜡烛长度 input int ATRperiod=20; // ATR指标周期 input int holdbars=8; // 保持持仓的柱形图数量 input int slippage=10; // 可允许滑移 input bool revers=false; //反向信号? input ulong EXPERT_MAGIC=0; // EA幻数 //--- 用于存储ATR指标句柄 int atr_handle; //--- 在这里,我们将存储ATR最后值和蜡烛体 double last_atr,last_body; datetime lastbar_timeopen; double trade_lot; //--- 请记住优化开始时间 datetime optimization_start; //--- 用于在优化结束后在图表上显示持续时间 string report; //+------------------------------------------------------------------+ //| TesterInit 函数 | //+------------------------------------------------------------------+ void OnTesterInit() { //--- 设置优化的输入值 ParameterSetRange("lots",false,0.1,0,0,0); ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0); ParameterSetRange("ATRperiod",true,10,15,1,30); ParameterSetRange("holdbars",true,5,3,1,15); ParameterSetRange("slippage",false,10,0,0,0); ParameterSetRange("revers",true,false,false,1,true); ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0); Print("Initial values and optimization parameter limitations are set"); //--- 记得开始优化 optimization_start=TimeLocal(); report=StringFormat("%s: optimization launched at %s", __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS)); //--- 显示图表上和程序端日志中的消息 Print(report); Comment(report); //--- } //+------------------------------------------------------------------+ //| TesterDeinit函数 | //+------------------------------------------------------------------+ void OnTesterDeinit() { //--- 优化持续时间 string log_message=StringFormat("%s: optimization took %d seconds", __FUNCTION__,TimeLocal()-optimization_start); PrintFormat(log_message); report=report+"\r\n"+log_message; Comment(report); } //+------------------------------------------------------------------+ //| EA交易初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- 初始化全局变量 last_atr=0; last_body=0; //--- 设置正确的交易量 double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); trade_lot=lots>min_lot? lots:min_lot; //--- 创建ATR指标句柄 atr_handle=iATR(_Symbol,_Period,ATRperiod); if(atr_handle==INVALID_HANDLE) { PrintFormat("%s: failed to create iATR, error code %d",__FUNCTION__,GetLastError()); return(INIT_FAILED); } //--- EA成功初始化 return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| EA报价函数 | //+------------------------------------------------------------------+ void OnTick() { //--- 交易信号 static int signal=0; // +1表示买入信号, -1 表示卖出信号 //--- 检查和关闭‘holdbars’柱形图之前开仓的旧持仓 ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC); //--- 检查新柱形图 if(isNewBar()) { //--- 检查信号是否存在 signal=CheckSignal(); } //--- 如果持有单边持仓,跳过信号-等候直至关闭 if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING) { signal=0; return; // 退出NewTick事件处理程序,并在新柱形图出现之前不要进入市场 } //--- 对于锁仓账户,每个持仓都是单独持仓和平仓 if(signal!=0) { //--- 买入信号 if(signal>0) { PrintFormat("%s: Buy signal! Revers=%s",__FUNCTION__,string(revers)); if(Buy(trade_lot,slippage,EXPERT_MAGIC)) signal=0; } //--- 卖出信号 if(signal<0) { PrintFormat("%s: Sell signal! Revers=%s",__FUNCTION__,string(revers)); if(Sell(trade_lot,slippage,EXPERT_MAGIC)) signal=0; } } //--- OnTick函数结束 } //+------------------------------------------------------------------+ //| 检查新的交易信号 | //+------------------------------------------------------------------+ int CheckSignal() { //--- 0 意味着没有信号 int res=0; //--- 获得倒数第二个完整柱形图的ATR值(柱形图索引是2) double atr_value[1]; if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1) { last_atr=atr_value[0]; //--- 获得最后关闭的柱形图的MqlRates类型数组数据 MqlRates bar[1]; if(CopyRates(_Symbol,_Period,1,1,bar)!=-1) { //--- 计算最后完整柱形图的柱体大小 last_body=bar[0].close-bar[0].open; //--- 如果最后柱形图的柱体(索引为1)超过的之前ATR的值(柱形图索引为2),那么会收到一个交易信号 if(MathAbs(last_body)>kATR*last_atr) res=last_body>0?1:-1; // 向上蜡烛图的正值 } else PrintFormat("%s: Failed to receive the last bar! Error",__FUNCTION__,GetLastError()); } else PrintFormat("%s: Failed to receive ATR indicator value! Error",__FUNCTION__,GetLastError()); //--- 如果启用了反向交易模式 res=revers?-res:res; // 在必要时反转信号(返回-1而不是1,反之亦然) //--- 返回一个交易信号值 return (res); } //+------------------------------------------------------------------+ //| 当新柱形图出现时返回'true' | //+------------------------------------------------------------------+ bool isNewBar(const bool print_log=true) { static datetime bartime=0; //存储当前柱形图的开盘时间 //--- 获得零柱的开盘时间 datetime currbar_time=iTime(_Symbol,_Period,0); //--- 如果开盘时间更改,则新柱形图出现 if(bartime!=currbar_time) { bartime=currbar_time; lastbar_timeopen=bartime; //--- 在日志中显示新柱形图开盘时间的数据 if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER))) { //--- 显示新柱形图开盘时间的信息 PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol, StringSubstr(EnumToString(_Period),7), TimeToString(TimeCurrent(),TIME_SECONDS)); //--- 获取关于最后报价的数据 MqlTick last_tick; if(!SymbolInfoTick(Symbol(),last_tick)) Print("SymbolInfoTick() failed, error = ",GetLastError()); //--- 显示最后报价的时间,精确至毫秒 PrintFormat("Last tick was at %s.%03d", TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000); } //--- 我们有一个新柱形图 return (true); } //--- 没有新柱形图 return (false); } //+------------------------------------------------------------------+ //| 以指定交易量和市场价买入 | //+------------------------------------------------------------------+ bool Buy(double volume,ulong deviation=10,ulong magicnumber=0) { //--- 以市场价买入 return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber)); } //+------------------------------------------------------------------+ //| 以指定交易量和市场价卖出 | //+------------------------------------------------------------------+ bool Sell(double volume,ulong deviation=10,ulong magicnumber=0) { //--- 以市场价卖出 return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber)); } //+------------------------------------------------------------------+ //| 在柱形图中根据持有时间平仓 | //+------------------------------------------------------------------+ void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0) { int total=PositionsTotal(); // 持仓数量 //--- 重复持仓 for(int i=total-1; i>=0; i--) { //--- 持仓参数 ulong position_ticket=PositionGetTicket(i); // 持仓单号 string position_symbol=PositionGetString(POSITION_SYMBOL); //交易品种 ulong magic=PositionGetInteger(POSITION_MAGIC); // 持仓幻数 datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // 持仓开盘时间 int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // 开仓之前有多少柱形图 //--- 如果持仓的生命周期很长,则幻数和交易品种匹配 if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol) { int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小数位数 double volume=PositionGetDouble(POSITION_VOLUME); // 持仓交易量 ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // 持仓类型 string str_type=StringSubstr(EnumToString(type),14); StringToLower(str_type); //纠正消息格式的小写文本案例 PrintFormat("Close position #%I64u %s %s %.2f", position_ticket,position_symbol,str_type,volume); //--- 设置一个订单类型并发送交易请求 if(type==POSITION_TYPE_BUY) MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket); else MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket); } } } //+------------------------------------------------------------------+ //| 准备和发送交易请求 | //+------------------------------------------------------------------+ bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0) { //--- 声明和初始化结构 MqlTradeRequest request={}; MqlTradeResult result={}; double price=SymbolInfoDouble(Symbol(),SYMBOL_BID); if(type==ORDER_TYPE_BUY) price=SymbolInfoDouble(Symbol(),SYMBOL_ASK); //--- 请求参数 request.action =TRADE_ACTION_DEAL; // 交易操作类型 request.position =pos_ticket; // 关闭情况下的持仓单号 request.symbol =Symbol(); // 交易品种 request.volume =volume; // 交易量 request.type =type; // 订单类型 request.price =price; // 交易价格 request.deviation=slip; // 可允许的价格偏差 request.magic =magicnumber; // 订单幻数 //--- 发送请求 if(!OrderSend(request,result)) { //--- 显示数据失败 PrintFormat("OrderSend %s %s %.2f at %.5f error %d", request.symbol,EnumToString(type),volume,request.price,GetLastError()); return (false); } //--- 通知成功操作 PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); return (true); } |