Open Price Only as accurate as Every Tick or OHLC

 

Hi all,

I am using an EA that is using Values from indicators of the last closed candle. So I expected that I can backtest it with using the "Open Pricy Only" Modelling because this is way faster. Specialy in the Optimazion. But the results made by this method are very different to the OHLC or the "every Tick "mode...
So I was wondering if this is typical for the Modelling (OPO not that accurate) or if I did something wrong with my code.


So my Questions:

1. Is it possible to get results as accurate as they are for OHLC/everyTick with the OPO modelling? If yes what has to be done?

2. If the answer for 1. is No: Since I only need the calculations of the last candle. how is it possible to speed up the Backtest/Optimazion when I have to use OHLC/everyTick for accurate results?


Thanks in advance for your help!

Thomas

 

That depends on the strategy you coded.

If that runs on Tick then there could be a rather large difference, but if you coded it on a M1 bar then it would be close to the same.

So nothing can be said about it because it all depends on the way the application has been coded.

 

Hi Marco,


thanks for your reply.


So instead of using the OnTick Handle I should use OnTimer? Would it still be correct to code it with a M1 timer when I use the Daily Chart or should I use a higher Timer that is close to the D1 time. (86400 would be one day I guess)
Or Are you talking about something completely diffrent? There is no Handle like OnNewBar in MQL5...

Oh.. and why is the same Modelling on the Visualisation giving such a huge different result on SingleTest/Optimation? Isnt the only differnence the fact that one runs wit showing the testing and one without showing the test? So generelly both should do the same or do I get something wrong there?


Thanks again and have a great day!

 

No i mean you can code an EA that handles every incoming tick but you can also write an EA that only handles every new M1 bar so the last one will simply check the open price everytime a new bar arrives.

This has got nothing to do with the OnTimer() function but solely with the way it was coded.

For example if you use a trailingstop this could run OnTick() then it will monitor and adjust the stop every incoming tick.

But you can also use and compare the bar time in that case it will only adjust when a new bar opens.

If you look at the bar time and it changes then you will know a new bar has arrived.

The open price only mode is just to quickly test the logic.


Your right about the visualization issue it should be the same with and without so you might want to check the logs on that one.

 

Thanks again Marco. I still have to ask... so the Code bellow should give the same result in Visualisation and in SingleTest/Optimazion. If Not there must be an issue in the Code that I can try to find in the logs?!
Cause I get the same results when I test a shorter amount of time with the RealTicks/OHLC... but when I change to to OPO it differs...


Update: I found the Issue. The CustomIndicator I use to get my LotSize and SL/TP somethimes gave Invalid values. When I use no SL/TP and a fixed LotSize I get the same results in every Modelling... So I guess I have to calculate my Lotsize on my own...I dont know why the Visualisation did ignore this and still gave me the trades but at least I am a little smarter now ;)
Thanks again Marco for your help! Cause of you I watched in the Log and found that issue.

(I posted just parts of the code here... so dont expect it to run^^)

//--- Include standard libraries
#include <Trade\AccountInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>

//--- Classes
CAccountInfo    AccountInfo;
CPositionInfo   PositionInfo;
CSymbolInfo     SymbolInfo;
CTrade          trade;

//--- Number of symbols for each strategy
#define Strategy_A 28

//------------------- External parameters of strategy A
input string          Data_for_Strategy_A="Strategy A -----------------------";
//---Symbole und Handelserlaubnis

string SymbolArray[]={"EURUSD","GBPUSD","USDCHF"
                     ,"USDJPY","USDCAD","AUDUSD","EURGBP","GBPJPY",
                     "AUDJPY","CHFJPY","EURCAD","EURNZD","NZDCAD","NZDCHF","NZDJPY","EURJPY","EURCHF",
                     "AUDCAD","AUDCHF","AUDNZD","CADCHF","CADJPY","EURAUD","GBPAUD","GBPCAD",
                     "GBPCHF","GBPNZD","NZDUSD"};
input bool            myIsTrade_A0     = true;      // Permission for trading

//--- TimeFrame
   input ENUM_TIMEFRAMES Period_A0      = PERIOD_D1;
  


input group "TS und TP Varianten"
input bool            FirstTPCheck     = true;      // Ersten TP nehmen und Position halbieren
input bool            TSCheck          = true;      // TrailingStopp einbauen (sont wird nur SL und TP gesetzt)
input double          PriceMoveFactor  =2;
  
input group "1. Confi"

            input int inpFastLength_A0 = 6; // Fast length

            input int inpSlowLength_A0 = 18; // Slow length

input group "Position Size Calculations"
      input int               InpATRperiod=14;         // ATR Periods
      input double            InpRiskPC=2.0;           // Risk Size %
      input double            InpSLfactor=1.5;         // Stop Loss as a factor of ATR
      input double            InpTPfactor=1.0;         // Take Profit as a factor of ATR
      input int               InpFontSize=9;           // Font size
      input color             InpColor=clrMagenta;     // Color
      input ENUM_BASE_CORNER  InpBaseCorner=CORNER_RIGHT_UPPER; // Corner
      input double            InpFixedATR=0;           // Fixed ATR points
      input bool              InpBack=false;           // Background object
      input bool              InpSelection=false;      // Highlight to move
      input bool              InpHidden=true;          // Hidden in the object list
      input long              InpZOrder=0;             // Priority for mouse click

//------------- Set variables of strategy A -----
//--- Arrays for external parameters
string          mySymbolPair[Strategy_A];
bool            myIsTrade_A[Strategy_A];
//ENUM_TIMEFRAMES TimeFrame_A[Strategy_A];

//--- Indicator handle Array und Variablen für CI1
int          FirstConfiDefinition[Strategy_A];

double          myFirstConfiBufferOneArray[];
double          myFirstConfiBufferTwoArray[];


//--- Indicator handle Array und Variablen für PoseSize
int          PoseSizeDefinition[Strategy_A];

double          myPoseSizeVolumeArray[];
double          myPoseSizeShortSLArray[];
double          myPoseSizeShortTPArray[];
double          myPoseSizeLongSLArray[];
double          myPoseSizeLongTPArray[];

double          Balance;
double          Equity;
double          VolumeOrder;
double          ShortSLOrder; 
double          ShortTPOrder; 
double          LongSLOrder; 
double          LongTPOrder;  



//--- Positionenen Verarbeitung
int  mySignalBeforeArray[Strategy_A];
int myPosTotalBreak[Strategy_A];  
ulong myDealTicketPosition [Strategy_A];
int  myPositionsTotalOld [Strategy_A];
int TotalNumberOfDeals;
ulong TicketNumberDeal;
ulong DealReason;
string MyDealSymbol;
long MyDealType; 

//--- Set global variables for all strategies
long            Leverage;


 datetime old_Time=0;

//---===================== The OnInit function =======================================================
int OnInit()
  {
//--- Set event generation frequency
  EventSetTimer(86400); // 1 second    86400=D1

//--- Get the leverage for the account
   Leverage=AccountInfo.Leverage();

//--- Checks and actions associated with strategy A -------------
//--- Copy external variables to arrays
   for(int i=0; i<Strategy_A; i++)
   {mySymbolPair[i]=SymbolArray[i];
   myIsTrade_A[i]=myIsTrade_A0;  
   //TimeFrame_A[i]=Period_A0;
   }
   
//--- Check for the symbol in the Market Watch
   for(int i=0; i<Strategy_A; i++)
     {
      if(myIsTrade_A[i]==false) continue;
      if(IsSymbolInMarketWatch(mySymbolPair[i])==false)
        {
         Print(mySymbolPair[i]," could not be found on the server!");
         ExpertRemove();
        }
     }

//--- Check whether the symbol is used more than once
   if(Strategy_A>1)
     {
      for(int i=0; i<Strategy_A-1; i++)
        {
         if(myIsTrade_A[i]==false) continue;
         for(int j=i+1; j<Strategy_A; j++)
           {
            if(myIsTrade_A[j]==false) continue;
            if(mySymbolPair[i]==mySymbolPair[j])
              {
               Print(mySymbolPair[i]," is used more than once!");
               ExpertRemove();
              }
           }
        }
     }

//--- General actions
   for(int i=0; i<Strategy_A; i++)
     {
      if(myIsTrade_A[i]==false) continue;

      //--- Set indicator handles            
      FirstConfiDefinition[i] =iCustom(mySymbolPair[i],Period_A0,"Downloads\\Confirmation Indi\\PTL (2)",
                           inpFastLength_A0,inpSlowLength_A0);
      
      PoseSizeDefinition[i] = iCustom (mySymbolPair[i],Period_A0,"Downloads\\SL Calculator\\pos_size_Datawindow extende",InpATRperiod,InpRiskPC,
                                   InpSLfactor,InpTPfactor,InpFontSize,InpColor,InpBaseCorner,InpFixedATR,InpBack,InpSelection,InpHidden,InpZOrder);
   
   ChartIndicatorAdd(i,0,FirstConfiDefinition[i]);

   ChartIndicatorAdd(i,0,PoseSizeDefinition[i]); 
    mySignalBeforeArray[i]=0;

     }

   return(0);
  }

//-===================== The OnTimer function ======================================================
void OnTimer()  
  { 
//--- Check if the terminal is connected to the trade server
   if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return;

//--- Section A: Main loop of the FOR operator for strategy A -----------
for(int A=0; A<Strategy_A; A++)
 {

 datetime bar_Time=iTime(mySymbolPair[A],Period_A0,0);
 if(bar_Time>old_Time)
 {
 old_Time=bar_Time;
//--------------------------------------------------Werte der Indikatoren berechnen------------------------------------------------
   
   //--- 1. CI Werte
   ArraySetAsSeries(myFirstConfiBufferOneArray,true);
   CopyBuffer(FirstConfiDefinition[A],4,0,3,myFirstConfiBufferOneArray);
   double PTLCandleColorValue = NormalizeDouble(myFirstConfiBufferOneArray[1],1);
   double PTLCandleColorVergl = NormalizeDouble(myFirstConfiBufferOneArray[2],1);
     
//--------------------------------------------------Ablauf der Positionen für TS,Exit------------------------------------------------     
         
 //--- prüft ob Positonen für das Symbol vorhanden sind -> wenn ja muss nichts neues berechnet werden
         if (PositionsTotal()>0)
            {
            int cntMyPos=PositionsTotal();
            for(int ti=cntMyPos-1; ti>=0; ti--)
            {
            // skip if there is a position for the current symbol
            if(PositionGetSymbol(ti) == mySymbolPair[A] )
               {
               myPosTotalBreak[A] = 1;
               break;
               }
            else {
                   myPosTotalBreak[A] = 0;
                 }

            }
            }
            else {
                  myPosTotalBreak[A] = 0;
                  }
            
//--- Prüfen ob Bedingungen für eine Positiontseröffnug gegeben sind und wenn ja, dann Position eröffnen           
if (myPosTotalBreak[A]==0)
        {
            ArraySetAsSeries(myPoseSizeVolumeArray,true);
            ArraySetAsSeries(myPoseSizeShortSLArray,true);
            ArraySetAsSeries(myPoseSizeShortTPArray,true);
            ArraySetAsSeries(myPoseSizeLongSLArray,true);
            ArraySetAsSeries(myPoseSizeLongTPArray,true);
            CopyBuffer(PoseSizeDefinition[A],1,0,2,myPoseSizeVolumeArray);
            CopyBuffer(PoseSizeDefinition[A],2,0,2,myPoseSizeShortSLArray);
            CopyBuffer(PoseSizeDefinition[A],3,0,2,myPoseSizeShortTPArray);
            CopyBuffer(PoseSizeDefinition[A],4,0,2,myPoseSizeLongSLArray);
            CopyBuffer(PoseSizeDefinition[A],5,0,2,myPoseSizeLongTPArray);

            VolumeOrder = NormalizeDouble(myPoseSizeVolumeArray[1],2);  
            ShortSLOrder = NormalizeDouble(myPoseSizeShortSLArray[1],_Digits);
            ShortTPOrder = NormalizeDouble(myPoseSizeShortTPArray[1],_Digits);
            LongSLOrder = NormalizeDouble(myPoseSizeLongSLArray[1],_Digits);
            LongTPOrder = NormalizeDouble(myPoseSizeLongTPArray[1],_Digits);

      SymbolInfo.Name(mySymbolPair[A]);
      SymbolInfo.RefreshRates();
      double Ask_price=SymbolInfo.Ask();
      double Bid_price=SymbolInfo.Bid();

 //Abfrage ob Positionen eroeffnet werden koennen
               if(PTLCandleColorValue==0.0 
                  &&(PTLCandleColorVergl==1.0 || PTLCandleColorVergl==-1.0)
                  )
                  {
                     Print(mySymbolPair[A]," : Kaufsignal LONG berechnet V1 ohne TS");
                      trade.PositionOpen(mySymbolPair[A],ORDER_TYPE_BUY,NormalizeDouble((VolumeOrder),2),Ask_price,LongSLOrder,LongTPOrder,NULL);
                      mySignalBeforeArray[A]=1;
                      myPosTotalBreak[A]=1;
                     }
               if(PTLCandleColorValue==0.0                  
                   && (mySignalBeforeArray[A]==-1 ||mySignalBeforeArray[A]== 0)
                   )
                    {
                     Print(mySymbolPair[A]," : Kaufsignal LONG berechnet V2 ohne TS");
                      trade.PositionOpen(mySymbolPair[A],ORDER_TYPE_BUY,NormalizeDouble((VolumeOrder),2),Ask_price,LongSLOrder,LongTPOrder,NULL);
                      mySignalBeforeArray[A]=1;
                      myPosTotalBreak[A]=1;                      
                     } 
         
               if (PTLCandleColorValue == 1.0 
                   && (PTLCandleColorVergl == 0.0 || PTLCandleColorVergl==-1.0)               
                    )
                     {
                     Balance=AccountInfoDouble(ACCOUNT_BALANCE);  
                     Equity=AccountInfoDouble(ACCOUNT_EQUITY); 
                     Print(mySymbolPair[A]," : Kaufsignal SHORT berechnet V1 ohne TS");
                     trade.PositionOpen(mySymbolPair[A],ORDER_TYPE_SELL,NormalizeDouble((VolumeOrder),2),Bid_price,ShortSLOrder,ShortTPOrder,NULL);
                     mySignalBeforeArray[A]=-1;
                     myPosTotalBreak[A]=1;                     
                     }
              if    (PTLCandleColorValue==1.0           
                    &&(mySignalBeforeArray[A]==1 ||mySignalBeforeArray[A]== 0)
                    )                        
                     {
                     Print(mySymbolPair[A]," : Kaufsignal SHORT berechnet V2 ohne TS");
                     trade.PositionOpen(mySymbolPair[A],ORDER_TYPE_SELL,NormalizeDouble((VolumeOrder),2),Bid_price,ShortSLOrder,ShortTPOrder,NULL);
                     mySignalBeforeArray[A]=-1;
                     myPosTotalBreak[A]=1; 
                     }  
      }
  }
  
 } 
}

//-===================== The OnDeinit function ============================
void OnDeinit(const int reason)
 
  {

//--- Termination of event generation
   EventKillTimer();
//--- Delete indicator handles
   for(int i=0; i<Strategy_A; i++)
     {
      IndicatorRelease(FirstConfiDefinition[i]);
      IndicatorRelease(PoseSizeDefinition[i]);
     }
  
  }  
//-===================== Function set ============================

//--- The IsSymbolInMarketWatch() function
bool IsSymbolInMarketWatch(string f_Symbol)
  {
   for(int s=0; s<SymbolsTotal(false); s++)
     {
      if(f_Symbol==SymbolName(s,false))
         return(true);
     }
   return(false);
  }
  

//+-----------------------------------------+
//| END OF PROGRAM                          |
//+-----------------------------------------+
 

That code appears to run OnTimer() which is not OnTick() and not on open prices only.

Theoretically it can open positions without an open price and without any tick.

Open prices means the open price of every canle so that is not OnTick() and not OnTimer() but at every arrival of a new bar.

You could compare that with 1 tick a minute on a M1 time frame and 60 ticks (because 60 new bars thus 60 * a new open price) an Hour.

If you want the same open price only mode but in on tick you would have to filter out all other ticks an keep just the first one that opens a new candle.

You can do that by comparing the bar times.

datetime time_m1;
if(time_m1 != iTime(Symbol(),PERIOD_M1,0))
  {
   // do something....
   Alert("New bar");
   time = iTime(Symbol(),PERIOD_CURRENT,0)
  }
You can also do a combination of both so tat you can still observe and adjust open positions on every tick while only opening positions on a new bar and etc.
The Fundamentals of Testing in MetaTrader 5
The Fundamentals of Testing in MetaTrader 5
  • www.mql5.com
The idea of ​​automated trading is appealing by the fact that the trading robot can work non-stop for 24 hours a day, seven days a week. The robot does not get tired, doubtful or scared, it's is totally free from any psychological problems. It is sufficient enough to clearly formalize the trading rules and implement them in the algorithms, and...
 

Hi Marco,


thanks for your respeonse again! I could fix my EA so that i brings me the same results on Visu and on Single/Opti. Thanks for your help there. What you are writing above is realy interesting but I am a little confused.

I allways thought that I have to implement something like OnTimer or OnTick that my EA actually does something.

Is it possible to let him search for a new bar/for a different time without having it in OnTick/OnTimer or something else?

someting like:


void OnInit()
{
// do something....
}
// can I call a Function just on global? like this?
if(time_m1 != iTime(Symbol(),PERIOD_M1,0))
  { do something....


Otherwise I guess you mean that I can use the OnTick Function as the basic Handle and in the OnTick Function I can call a if Function like above where the EA is only doing something when the nw bar occurs.

Reason: