Libraries: MT4Orders

 

MT4Orders:

Parallel use of the MetaTrader 4 and MetaTrader 5 order systems.

Author: fxsaber

 
Very Helpful, Thank you!
 
The latest version is always here.
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Советники: Quantum 103

fxsaber, 2017.09.26 09:55

Cross-platform version of Expert Advisor
// MQL4&5-code

#ifdef __MQL5__

bool RefreshRates( void ) { return(true); }

double MarketInfo( const string Symb, const ENUM_SYMBOL_INFO_DOUBLE Property )  { return(SymbolInfoDouble(Symb, Property)); }
int    MarketInfo( const string Symb, const ENUM_SYMBOL_INFO_INTEGER Property ) { return((int)SymbolInfoInteger(Symb, Property)); }

bool IsTesting(void)            { return(MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION)); }
bool IsTradeContextBusy( void ) { return(false); }
bool IsTradeAllowed(void)       { return(MQLInfoInteger(MQL_TRADE_ALLOWED)); }
bool IsExpertEnabled(void)      { return(AccountInfoInteger(ACCOUNT_TRADE_EXPERT)); }

int    ObjectFind( const string Name )   { return(ObjectFind(0, Name)); }
int    ObjectsTotal( void )              { return(ObjectsTotal(0)); }
bool   ObjectDelete( const string Name ) { return(ObjectDelete(0, Name)); }
string ObjectName( const int Pos )       { return(ObjectName(0, Pos)); }

double AccountFreeMarginCheck(const string Symb,const int Cmd,const double dVolume)
{
 double Margin;

 return(OrderCalcMargin((ENUM_ORDER_TYPE)Cmd, Symb, dVolume,
        SymbolInfoDouble(Symb, (Cmd == ORDER_TYPE_BUY) ? SYMBOL_ASK : SYMBOL_BID), Margin) ?
        AccountInfoDouble(ACCOUNT_MARGIN_FREE) - Margin : -1);
}

#define False false
#define True  true

#define Digits _Digits
#define Point  _Point

#define MODE_BID       SYMBOL_BID
#define MODE_ASK       SYMBOL_ASK
#define MODE_POINT     SYMBOL_POINT
#define MODE_DIGITS    SYMBOL_TRADE_STOPS_LEVEL
#define MODE_STOPLEVEL SYMBOL_TRADE_STOPS_LEVEL
#define MODE_SPREAD    SYMBOL_SPREAD

#define StrToTime    StringToTime
#define StrToInteger StringToInteger
#define TimeToStr    TimeToString
#define DoubleToStr  DoubleToString

#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

#define  MT4_TICKET_TYPE // Обязываем OrderSend и OrderTicket возвращать значение такого же типа, как в MT4 - int.
#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006

// Нужно для графиков в отчетах
// #include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

#define REPORT_TESTER // В тестере будут автоматически записываться отчеты
#include <Report.mqh> // https://www.mql5.com/ru/code/18801

#endif // __MQL5__

#include "Quantum 103.mq4" // https://www.mql5.com/ru/code/19133

Result of backtest on real ticks on MT5

Standard MT5 report

Custom report (MT4 style)


Attached MT4-style report MT5-backtest, because the standard report is read with difficulty - see both in the attach.

You can clearly see the impact on the result of MT5-backtest of this adviser slippage and commission.

 

I'm trying to implement a strategy (which obviously works fine in MT4) but I've hit a brick wall.

The EA begins a new sequence of trades whenever a new signal is generated, so each traded pair will usually have multiple sequences running.

Every trade has an SL & TP, and the EA needs to track what happened (SL or TP) to the most recent trade in each sequence by checking the order history.

Unfortunately, this does not seem to be possible based on the library notes (below) and various attempts to make the system work:

// In MT4 OrderSelect in SELECT_BY_TICKET mode selects a ticket regardless of MODE_TRADES / MODE_HISTORY,
// since "Ticket number is a unique order ID".
// In MT5, the ticket number is NOT unique,
// so OrderSelect in SELECT_BY_TICKET mode has the following selection priorities for matching tickets:
// MODE_TRADES: existing position> existing order> transaction> canceled order
// MODE_HISTORY: transaction> canceled order> existing position> existing order

From experimentation, I have confirmed that the ticket number always changes after an SL/TP event, and that the only common info is the magic number - not even the order comment is retained.

In testing, placing an initial order with an SL, TP, magic number and trade comment returns ticket #2 and displays all of the expected information.

However, when the SL or TP is hit, ticket #2 vanishes and cannot be retrieved using either SELECT_BY_POS or SELECT_BY_TICKET.

Instead, (assuming no other trading activity) there will now be ticket #3, which has no order comment set.

With no consistency regarding ticket number, and no order comment to reference, there seems no simple way to identify the last order in a given sequence :(

Hopefully @fxsaber can find a solution or suggest a workaround :)

 
SysFX:

Unfortunately, I did not understand the problem.

 

Sorry for the late reply :(

The issue is that 'MODE_HISTORY' trade information is basically unusable because:

1) when a trade closes, the ticket number changes ...you can easily test this by opening a buy / sell trade with a TP and SL - if the trade opens as ticket #2, that trade will become ticket #3 in the history pool after the SL or TP triggers

2) the magic number is lost

3) the ticket comment is lost

So, if the EA code needs to check whether a trade closed with a profit or loss, it's impossible because there's no information available to identify the trade.

I'm hoping that you can tweak things to retain the magic number and make the trade comment behave like MT4 - maybe you can find a way to retain the ticket number too.

 
SysFX:

Sorry for the late reply :(

The issue is that 'MODE_HISTORY' trade information is basically unusable because:

1) when a trade closes, the ticket number changes ...you can easily test this by opening a buy / sell trade with a TP and SL - if the trade opens as ticket #2, that trade will become ticket #3 in the history pool after the SL or TP triggers

2) the magic number is lost

3) the ticket comment is lost

So, if the EA code needs to check whether a trade closed with a profit or loss, it's impossible because there's no information available to identify the trade.

I'm hoping that you can tweak things to retain the magic number and make the trade comment behave like MT4 - maybe you can find a way to retain the ticket number too.

Example

#include <MT4Orders.mqh>

#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

#define PRINT(A) Print(#A + " = " + (string)(A))

void OnInit()
{
  long Ticket;
  
  PRINT((Ticket = OrderSend(_Symbol, OP_BUY, 1, Ask, 0, Bid - 100 * _Point, Bid + 100 * _Point, "Hello World!", 12345)));
  
  if (OrderSelect(Ticket, SELECT_BY_TICKET))
    PRINT(OrderClose(OrderTicket(), 0.3, OrderClosePrice(), 0));
}

void OnDeinit( const int )
{
  const int Total = OrdersHistoryTotal();
  
  for (int i = 1; i < Total; i++)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
    {
      OrderPrint();
      
      PRINT(OrderTicket());
      PRINT(OrderMagicNumber());
      PRINT(OrderComment());
      PRINT(OrderTicketOpen());
    }
}


Result

2018.03.25 00:00:00   instant buy 1.00 EURUSD at 1.23527 sl: 1.23414 tp: 1.23614 (1.23514 / 1.23527)
2018.03.25 00:00:00   deal #2 buy 1.00 EURUSD at 1.23527 done (based on order #2)
2018.03.25 00:00:00   deal performed [#2 buy 1.00 EURUSD at 1.23527]
2018.03.25 00:00:00   order performed buy 1.00 at 1.23527 [#2 buy 1.00 EURUSD at 1.23527]
2018.03.25 00:00:00   (Ticket=OrderSend(_Symbol,OP_BUY,1,Ask,0,Bid-100*_Point,Bid+100*_Point,Hello World!,12345)) = 2
2018.03.25 00:00:00   instant sell 0.30 EURUSD at 1.23514, close #2 (1.23514 / 1.23527)
2018.03.25 00:00:00   deal #3 sell 0.30 EURUSD at 1.23514 done (based on order #3)
2018.03.25 00:00:00   deal performed [#3 sell 0.30 EURUSD at 1.23514]
2018.03.25 00:00:00   order performed sell 0.30 at 1.23514 [#3 sell 0.30 EURUSD at 1.23514]
2018.03.25 00:00:00   OrderClose(OrderTicket(),0.3,OrderClosePrice(),0) = true
2018.03.26 01:04:40   take profit triggered #2 buy 0.70 EURUSD 1.23527 sl: 1.23414 tp: 1.23614 [#4 sell 0.70 EURUSD at 1.23614]
2018.03.26 01:04:40   deal #4 sell 0.70 EURUSD at 1.23614 done (based on order #4)
2018.03.26 01:04:40   deal performed [#4 sell 0.70 EURUSD at 1.23614]
2018.03.26 01:04:40   order performed sell 0.70 at 1.23614 [#4 sell 0.70 EURUSD at 1.23614]
final balance 10000046.11 EUR
2018.03.26 23:59:59   #3 2018.03.25 00:00:00 buy 0.30 EURUSD 1.23527 1.23414 1.23614 2018.03.25 00:00:00 1.23514 0.00 0.00 -3.16 Hello World! 12345
2018.03.26 23:59:59   OrderTicket() = 3
2018.03.26 23:59:59   OrderMagicNumber() = 12345
2018.03.26 23:59:59   OrderComment() = Hello World!
2018.03.26 23:59:59   OrderTicketOpen() = 2
2018.03.26 23:59:59   #4 2018.03.25 00:00:00 buy 0.70 EURUSD 1.23527 0.00000 1.23614 2018.03.26 01:04:40 1.23614 0.00 0.00 49.27 tp 1.23614 12345
2018.03.26 23:59:59   OrderTicket() = 4
2018.03.26 23:59:59   OrderMagicNumber() = 12345
2018.03.26 23:59:59   OrderComment() = tp 1.23614
2018.03.26 23:59:59   OrderTicketOpen() = 2

Forum on trading, automated trading systems and testing trading strategies

EA Identification

Carl Schreiber, 2015.11.21 13:46

the broker are free to change the comment. E.g. some replace the comment by [tp] or [sl] if the order has been closed by profit target or stop loss!!


PS Russian discussion thread.

 
TesterEA
TesterEA
  • www.mql5.com
Некоторый выложенный в Кодобазу инструментарий требовал для различных тестов торговый советник в качестве полигона. Данный советник был тем самым подопытным. От идеи до полной реализации, с написанием всего кода с нуля, потребовалось два часа. Особенности Советник канальный - торгует вовнутрь канала, постоянно в рынке.Не используются...
 
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Библиотеки: MT4Orders

fxsaber, 2018.12.05 19:43

// Demonstration of how to manually send trade orders to the Visualizer.

#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006

#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

bool IsModify()
{
  static long PrevTime = 0;
  
  const long NewTime = FileGetInteger(__FILE__, FILE_MODIFY_DATE);
  const bool Res = (PrevTime != NewTime);
  
  if (Res)
    PrevTime = NewTime;
    
  return(Res);  
}

bool CreateFile()
{
  uchar Bytes[];
      
  return(FileSave(__FILE__, Bytes) && IsModify());
}

string GetCommand()
{
  uchar Bytes[];
  FileLoad(__FILE__, Bytes);
  
  return(CharArrayToString(Bytes));
}

bool OrdersScan( const int Type )
{
  for (int i = ::OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS) && (OrderType() == Type))      
      return(true);    
    
  return(false);  
}

bool SendCommand( const string Command, const double Lot = 1, const int Offset = 100 )
{
  bool Res = false;
  
  if (Command == "open buy")  
    Res = (OrderSend(_Symbol, OP_BUY, Lot, Ask, 0, 0, 0) > 0);
  else if (Command == "open sell")  
    Res = (OrderSend(_Symbol, OP_SELL, Lot, Bid, 0, 0, 0) > 0);
  else if (Command == "open buylimit")  
    Res = (OrderSend(_Symbol, OP_BUYLIMIT, Lot, Ask - Offset * _Point, 0, 0, 0) > 0);
  else if (Command == "open selllimit")  
    Res = (OrderSend(_Symbol, OP_SELLLIMIT, Lot, Bid + Offset * _Point, 0, 0, 0) > 0);
  else if (Command == "close buy")  
    Res = OrdersScan(OP_BUY) && OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0);
  else if (Command == "close sell")
    Res = OrdersScan(OP_SELL) && OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0);
  else if (Command == "close buylimit")  
    Res = OrdersScan(OP_BUYLIMIT) && OrderDelete(OrderTicket());
  else if (Command == "close selllimit")
    Res = OrdersScan(OP_SELLLIMIT) && OrderDelete(OrderTicket());
    
  return(Res);
}

bool TesterManual()
{
  static const bool IsVisual = MQLInfoInteger(MQL_VISUAL_MODE) && CreateFile();
  
  return(IsVisual && IsModify() && SendCommand(GetCommand()));
}

void OnTick()
{
  TesterManual();
}


Reason: