validate MT4 EA

 
how do I pass validation when my EA only works with EURCHF?
 
ZORAN RAJKOV:
how do I pass validation when my EA only works with EURCHF?
You don’t 
Read the rules you cannot restrict a product in anyway it must be able to run on any symbol timeframe etc 
 
ZORAN RAJKOV:
how do I pass validation when my EA only works with EURCHF?

You can create the EA for all symbols and suggest in the descriptions to apply it to EURCHF.

 
Paul Anscombe #:
You don’t 
Read the rules you cannot restrict a product in anyway it must be able to run on any symbol timeframe etc 

There are no restrictions except for the currency pair EURCHF

 

I also canceled that restriction, but it doesn't go through again with the message

test on EURUSD, H1

there are no trading operations
 

Try using this code , it checks everything 

Forum on trading, automated trading systems and testing trading strategies

Automatic validation for my EA

Lorentzos Roussos, 2022.12.29 15:47

Okay i think some checks changed again . I just passed the below code .

If i'm not mistaken the tester may fail in SymbolInfo requests so it should have an alternative for MarketInfo on lots.(that or i should use Symbol() , or they are testing on old terminals too)

The following code may have issues in the margin calculation , does not account for ECN types and does not handle Freeze Level which i'm not experienced with. 

The rest is okay and passed in a hidden project that i have in the market for testing .

here is the all in one trade code in its test form (this ea passed as is).

#property version   "33.00"
#property strict
datetime barstamp=0;
int clock=0,clock_limit=7;
int OnInit()
  {
  barstamp=0;
  clock=0;
  return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   
  }

void OnTick()
  {
  if(Time[0]>barstamp){
  barstamp=Time[0];
  clock++;
  if(clock==clock_limit){
  clock=0;
  if(MathRand()>16000){
    trade_result result=SafeMarketTrade(OP_BUY,0.01,10,false,10,false,false,1.5,10,333,1000,23,NULL,clrBlue);
    if(!result.success){
      Print(result.error_message);
      }
    }
  else{
    trade_result result=SafeMarketTrade(OP_SELL,0.01,Ask+(10*_Point),true,0.0,false,false,1.5,10,333,1000,23,NULL,clrRed);
    if(!result.success){
      Print(result.error_message);
      }
    }
  }}
  }

struct trade_result{
bool     success;
int      ticket;
double   open_price;
double   initial_price,ask,bid,spread;
double   stop_loss;
double   take_profit;
double   lots;
double   margin_used;
datetime open_time;
int      slippage;
int      attempts;
long     time_to_open_ms;
string   error_message;
         trade_result(void){reset();}
        ~trade_result(void){reset();}
    void reset(){
         success=false;
         ticket=-1;
         attempts=0;
         error_message="";
         } 
};

trade_result SafeMarketTrade(ENUM_ORDER_TYPE direction,//buy or sell
                             double          lots,//the volume of the trade
                             double          sl,//stop loss price or distance from entry price 
                             bool            sl_is_price,//true if stop loss is not distance 
                             double          tp,//take profit price or distance from entry price
                             bool            tp_is_price,//true if take profit is not distance
                             bool            reject_if_stops_too_close,//reject trade if stops are too close for broker
                             double          stops_expansion_multiple_of_limit,//if not rejecting trade for stop level , expand by this much (off of the limit)
                             int             max_attempts,//max attempts to open
                             uint            timeout,//timeout between attempts
                             int             max_slippage,//max allowed slippage
                             int             magic_number,
                             string          order_comment,
                             color           clr){
trade_result result;
//log the start ms 
  result.time_to_open_ms=GetTickCount();
  //create id for the order attempt 
    string id="{"+_Symbol+" "+EnumToString(direction)+" "+DoubleToString(lots,2)+" #Magic("+IntegerToString(magic_number)+")'"+order_comment+"'}";
  //Proper Type 
  if(direction==OP_BUY||direction==OP_SELL)
  {
  //Context and Connection
  if(!IsTradeContextBusy()&&IsConnected())
  {
  //total orders and account limits
    ResetLastError();
    int account_orders_limit=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);
    if(GetLastError()==0&&OrdersTotal()<account_orders_limit){
    //Trade allowance 
      ResetLastError();
      if(IsTradeAllowed(_Symbol,TimeCurrent())&&(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)==1)&&GetLastError()==0){
      //minlot , maxlot , step , total max
        ResetLastError();
        bool volumes_acquired=true;
        double volume_min=(double)SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
               if(GetLastError()!=0){
               ResetLastError();
               volume_min=(double)MarketInfo(_Symbol,MODE_MINLOT);
               if(GetLastError()!=0){volumes_acquired=false;}
               }
               ResetLastError();
        double volume_max=(double)SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
               if(GetLastError()!=0&&volumes_acquired){
               ResetLastError();
               volume_max=(double)MarketInfo(_Symbol,MODE_MAXLOT);
               if(GetLastError()!=0){volumes_acquired=false;}
               }
               ResetLastError();    
        double volume_step=(double)SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
               if(GetLastError()!=0&&volumes_acquired){
               ResetLastError();
               volume_step=(double)MarketInfo(_Symbol,MODE_LOTSTEP);
               if(GetLastError()!=0){volumes_acquired=false;}
               }
               ResetLastError();
        double volume_limit=(double)SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
               if(GetLastError()!=0){volume_limit=0.0;}
        if(volumes_acquired){
        //check against step 
          int steps=(int)(MathFloor(lots/volume_step));
          lots=((double)steps)*volume_step;
          if(lots<volume_min){lots=volume_min;}
          else if(lots>volume_max){lots=volume_max;}
         //Total symbol volume 
           double total_symbol_volume=get_symbol_volume(_Symbol,true)+lots;
           if(total_symbol_volume<volume_limit||volume_limit<=0.0){
           //get max available margin with margin of error on top
             double max_available_margin=get_max_available_margin_with_error_margin(0.2);//20% buffer
             //free margin check 
               //the free margin remaining must be above free margin - max available free margin
               double margin_min=AccountFreeMargin()-max_available_margin;
               ResetLastError();
               if(AccountFreeMarginCheck(_Symbol,direction,lots)>margin_min&&GetLastError()==0){
               //turn all stops to distance if they are not and catch the first price 
                 double sl_ticks=0.0,tp_ticks=0.0;
                 RefreshRates();
                 result.ask=Ask;
                 result.bid=Bid;
                 if(direction==OP_BUY){result.initial_price=Ask;}
                 else{result.initial_price=Bid;}
                 //get stop level limit 
                 ResetLastError();
                 double stop_limit=((double)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))*_Point;
                 if(GetLastError()==0){
                 //stop loss
                 if(sl!=0.0){
                 if(sl_is_price){
                   if(direction==OP_BUY){
                     sl_ticks=result.bid-sl;
                     }else{
                     sl_ticks=sl-result.ask;
                     }
                 }
                 else{
                     sl_ticks=sl*_Point;
                 }
                 //if the stop loss is below the limit and the limit is not zero
                   if(stop_limit>0.0&&sl_ticks<=stop_limit){
                   //adjust or reject
                     if(reject_if_stops_too_close){
                       result.error_message="SL is too close , rejecting trade "+id;
                       return(result);
                       }
                     else{
                       sl_ticks=NormalizeDouble(stop_limit*stops_expansion_multiple_of_limit,_Digits);
                       }
                   }
                 }
                 //stop loss ends here
                 //take profit 
                 if(tp!=0.0){
                 if(tp_is_price){
                   if(direction==OP_BUY){
                     tp_ticks=tp-result.ask;
                     }else{
                     tp_ticks=result.bid-tp;
                     }
                 }
                 else{
                     tp_ticks=tp*_Point;
                 }
                 //if the take profit is below the limit and the limit is not zero 
                   if(stop_limit>0.0&&tp_ticks<=stop_limit){
                   //adjust or reject
                     if(reject_if_stops_too_close){
                       result.error_message="TP is too close , rejecting trade "+id;
                       return(result);
                       }
                     else{
                       tp_ticks=NormalizeDouble(stop_limit*stops_expansion_multiple_of_limit,_Digits);
                       }
                   }
                 }
                 //take profit ends here   
                 /*
                 At this point we have everything we need now we will start 
                 attempting to place the trade 
                 */              
                 //TRADE 
                   double fm_snap=0.0;
                   while(result.ticket==-1&&result.attempts<max_attempts){
                   result.attempts++;
                   RefreshRates();
                   result.ask=Ask;
                   result.bid=Bid;
                   fm_snap=AccountFreeMargin();
                   if(direction==OP_BUY){result.open_price=Ask;}else{result.open_price=Bid;}
                   //format sl + tp ,we have the ticks so we can use sl , tp variables
                     if(sl_ticks!=0.0){
                     if(direction==OP_BUY){
                       sl=result.bid-sl_ticks;
                       }else{
                       sl=result.ask+sl_ticks;
                       }
                     }
                     if(tp_ticks!=0.0){
                     if(direction==OP_BUY){
                       tp=result.ask+tp_ticks;
                       }else{
                       tp=result.bid-tp_ticks;
                       }
                     }
                    ResetLastError();
                    result.ticket=OrderSend(_Symbol,direction,lots,result.open_price,max_slippage,sl,tp,order_comment,magic_number,0,clr);
                   Sleep(timeout);
                   }
                 //TRADE ENDS HERE
                   //if trade failed 
                     if(result.ticket==-1){
                     result.error_message="Trade failed error#"+IntegerToString(GetLastError())+" ";
                     }
                   //if trade succeeded
                     else{
                     result.success=true;
                     //time to open 
                       long endms=GetTickCount();
                       if(endms<result.time_to_open_ms){
                       result.time_to_open_ms=UINT_MAX-result.time_to_open_ms+endms;
                       }else{
                       result.time_to_open_ms=endms-result.time_to_open_ms;
                       }
                     //spread 
                       result.spread=result.ask-result.bid; 
                     //margin used 
                       result.margin_used=fm_snap-AccountFreeMargin();
                     //select for more info 
                       if(OrderSelect(result.ticket,SELECT_BY_TICKET)){
                       result.open_price=OrderOpenPrice();
                       result.open_time=OrderOpenTime();
                       result.lots=OrderLots();
                       result.stop_loss=OrderStopLoss();
                       result.take_profit=OrderTakeProfit();
                       //slippage 
                         if(direction==OP_BUY){
                         result.slippage=(int)((result.initial_price-result.open_price)/_Point);
                         }else{
                         result.slippage=(int)((result.open_price-result.initial_price)/_Point);
                         }
                       }
                     }
                   //if trade succeeded ends here 
                 }
                 else{
                 result.error_message="Cannot get stops level ";
                 }
               //get stop level limit ends here
               }else{
               result.error_message="Not enough margin for lots ";
               }
             //free margin check ends here 
           }else{
           result.error_message="Volume will exceed total limit ";
           }
         //Total symbol volume ends here
        }else{
         result.error_message="Cannot acquire symbol volume info ";
         }
      //minlot , maxlot , step ,total max ends here
      }else{
       if(!IsTradeAllowed(_Symbol,TimeCurrent())){result.error_message="Trade not possible for symbol ";}
       if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)==0){result.error_message+="Trade switch is off ";}
       }
    //Trade allowance ends here 
    }else{
     result.error_message="Total orders account limit reached ";
     }
  //total orders and account limits ends here
  }else{
      if(IsTradeContextBusy()){result.error_message="Trade Context Busy ";}
      if(!IsConnected()){result.error_message+="No Connection ";}
      }
  //Context and Connection Ends Here
  }else{
      result.error_message="Wrong trade type.Market orders only please ";
      }
  //Proper Type Ends Here
result.error_message+=id;
return(result);
}


double get_symbol_volume(string _symbol,bool only_live_orders){
double sum=0.0;
for(int i=0;i<OrdersTotal();i++){
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)){
if(!only_live_orders||(OrderType()==OP_BUY||OrderType()==OP_SELL)){
sum+=OrderLots();
}}}
return(sum);
}

double get_max_available_margin_with_error_margin(double error_margin_buffer_percent){
double avail_margin=0.0;
ResetLastError();
int margin_call_mode=AccountStopoutMode();//https://docs.mql4.com/account/accountstopoutmode

  if(GetLastError()==0){
  //if absolute value mode 
    if(margin_call_mode==1){
    avail_margin=AccountFreeMargin()-AccountStopoutLevel();
    }
  //if % mode 
    else if(margin_call_mode==0){ 
    double min_ratio=AccountStopoutLevel();
    //at min ratio the margin must be 
      double margin_at_min=AccountEquity()/(min_ratio/100.0);
      avail_margin=MathMin(margin_at_min-AccountMargin(),AccountFreeMargin());
    }
  //and margin of error 
    avail_margin-=avail_margin*error_margin_buffer_percent;
  }
return(avail_margin);
}
Edit : Volume limit should not reject the acquisition switch of lots in case the broker does not support it , or its not supported at all i guess . That means maybe the symbol info doubles are working too . This works however .

 

I check on the strategy tester and everything is fine

Strategy Tester Report
ExpertEURCHF
RoboForex-Pro (Build 1367)

Symbol EURCHF (Euro vs Swiss Franc)
Period 1 Hour (H1) 2022.12.01 00:00 - 2023.01.03 23:00 (2022.12.01 - 2023.01.04)
Model Every tick (the most precise method based on all available least timeframes)
Parameters Lot=0.01; distance=50; doubleDistance=100; takeProfit=100; stopLoss=200; increaseLot=0.01; setTarget=false; closeFriday=false;
Bars in test 1565 Ticks modelled 2072034 Modelling quality 90.00%
Mismatched charts errors 0
Initial deposit 1000.00 Spread Current (26)
Total net profit 82.53 Gross profit 98.33 Gross loss -15.80
Profit factor 6.23 Expected payoff 1.38
Absolute drawdown 1.94 Maximal drawdown 9.86 (0.96%) Relative drawdown 0.96% (9.86)
Total trades 60 Short positions (won %) 29 (93.10%) Long positions (won %) 31 (90.32%)
Profit trades (% of total) 55 (91.67%) Loss trades (% of total) 5 (8.33%)
Largest profit trade 7.45 loss trade -3.93
Average profit trade 1.79 loss trade -3.16
Maximum consecutive wins (profit in money) 22 (39.77) consecutive losses (loss in money) 1 (-3.93)
Maximal consecutive profit (count of wins) 39.77 (22) consecutive loss (count of losses) -3.93 (1)
Average consecutive wins 9 consecutive losses 1
Graph
 
ZORAN RAJKOV #: I check on the strategy tester and everything is fine Strategy Tester Report ExpertEURCHF

Test it on other symbols in the Strategy Tester, not just EURCHF. It must be able to trade on ANY symbol, even if it is unprofitable.

Those are the conditions and rules for Market products. It MUST be able to trade on any and all symbols.

Reason: