With what to replace OnTradeTransaction() in mql4? - page 8

 
Vitaly Muzichenko:

That's it, the solution is simple: we introduce another check to change the history, that way nothing will be lost and it will work 100%

This can even be used as an incomplete OnTrade()

void OnTrade()
 {
  ...
 }

static __OTotal = -1;
static __HTotal = -1;
int OT=OrdersTotal();
int HT=OrdersHistoryTotal();
  if(OT!=__OTotal || HT!=__HTotal) // если изменилось - выполняем
   {
     OnTrade(); // здесь дёргаем текущую ситуацию на счёте и заполняем структуры
     __OTotal=OrdersTotal(); // запомним текущее количество
     __HTotal=OrdersHistoryTotal(); // запомним количество в истории
   }
 
Vitaly Muzichenko:

I guess I'm not smart enough.)

How do I apply this?

There's only one problem and it's extremely rare, found it today for the first time in a couple of years, may have been before, just didn't notice it


I was talking about hash sum calculation - how you can add a character name to the hash sum value - calculate the values of the letter codes that make up the character name.

 
Vitaly Muzichenko:

That's it, the solution is simple: introduce another history change check, that way nothing will be lost and it will work 100%

Here's an excerpt from my article #3:

-------

Let's focus more on the hash amount as part of the structure.
It is not enough to know the number of orders and positions to be able to accurately determine what has changed in the account - a pending order can be deleted, and in this case the total number of orders and positions in the account will change. But... A pending order may trigger and become a position. In this case, the total amount of orders and positions will not change (for hedge accounts and MQL4) - the number of positions has increased, but the number of orders has decreased, so the total amount is still the same. This does not work for us.

Let's consider a ticket. Adding/removing a pending order will change the total amount of ticks on the account, triggering a pending order will not change the total amount of ticks on the account.

Let's look at total volume. You placed or deleted a pending order - total amount in the account has changed; we opened or closed it, or changed the position - total amount in the account has changed. It seems to be suitable but once again, activating a pending order will not change the total volume.

So let's view one more position property - time of its change in milliseconds: opening a new position will change total position change time, partial closing will change position change time, adding volume to a netting account will change total position change time.

Which of these methods is suitable for determining the precise location of changes on the account? Ticket + time of position change. Let's check:

  • Opened a position - amount of tickets has changed + amount of time of position change has changed - there is a change
  • We closed a position - the amount of ticks has changed + the amount of time the position was changed - there is a change.
  • Placed a pending order - Ticket + time of position change has not changed - - there is a change
  • Deleted pending order - Ticket amount changed + Position change time amount remained unchanged - there is a change
  • Pending order activated - Ticket amount has not changed + position change time amount has changed - - there is a change
  • Partial position closing - Ticket amount changed + Position change time changed - - There is a change
  • Add volume to position - Ticket amount remained unchanged + position change time changed - - there is a change
Thus, we will use the ticket + time of change of a position in milliseconds for hash sum.

Here, though, I didn't add a symbol to the hash sum - there were no precedents for that. But I have it works in conjunction with the check history. Therefore - should work without errors. However, I will check it sometime.

Библиотека для простого и быстрого создания программ для MetaTrader (Часть III): Коллекция рыночных ордеров и позиций, поиск и фильтрация
Библиотека для простого и быстрого создания программ для MetaTrader (Часть III): Коллекция рыночных ордеров и позиций, поиск и фильтрация
  • www.mql5.com
В первой части данного цикла статей мы начали создавать большую кроссплатформенную библиотеку, целью которой является облегчение создания программ для платформы MetaTrader 5 и MetaTrader 4. Во второй части продолжили развитие библиотеки и сделали коллекцию исторических ордеров и сделок. В данной части повествования создадим класс для удобного...
 
Vitaly Muzichenko:

The solution is simple: introduce another history change check, that way nothing will be lost and it will work 100%

And yes, it will. If the number of open orders does not change, then the number in the history will change. (I don't care about pending orders - I don't use them) And we won't have to go through the entire day to catch only one event.

And if the user has received a text message and decided to display the history in the terminal instead of all of it only for the last month, it will be an additional check with trying all orders, which is not fatal.

It seems to be a good solution. And without reference to anything but what we have, which is a trading account and a terminal.

 
Artyom Trishkin:

Here is an extract from my article #3:

-------

Let's elaborate on the hash amount as a part of the structure.
It is not enough to know the amount of orders and positions that have changed in the account to be able to precisely determine this change - a pending order can be deleted and in this case, the total amount of orders and positions in the account will change. But... A pending order may trigger and become a position. In this case, the total amount of orders and positions will not change (for hedge accounts and MQL4) - the number of positions has increased, but the number of orders has decreased, so the total amount is still the same. This does not work for us.

Consider a ticket. Adding/removing a pending order will change the total amount of tickets on the account, triggering a pending order will not change the total amount of tickets on the account.

Consider total volume. You placed or deleted a pending order - total volume on the account changed, opened, closed, or changed position - total volume on the account changed. It seems to be suitable but once again, activating a pending order will not change the total volume.

So let's look at one more position property - time of its change in milliseconds: opening a new position will change total position change time, partial closing will change position change time, adding volume to a netting account will change total position change time.

Which of these methods is suitable for determining the precise location of changes on the account? Ticket + time of position change. Let's check:

  • Opened a position - amount of tickets changed + amount of time of position change - there is a change
  • We closed a position - the amount of ticks has changed + the amount of time the position was changed - there is a change.
  • Placed a pending order - Ticket + time of position change has not changed - - there is a change
  • Deleted pending order - Ticket amount changed + Position change time amount remained unchanged - there is a change
  • Pending order activated - Ticket amount has not changed + position change time amount has changed - - there is a change
  • Partial position closing - Ticket amount changed + Position change time changed - - There is a change
  • Add volume to position - Ticket amount remained unchanged + position change time changed - - there is a change
Thus, we will use the ticket + time of change of a position in milliseconds for hash sum.

Here, though, I didn't add a symbol to the hash sum - there were no precedents for that. But I have it works in conjunction with the check history. Therefore - should work without errors. However, I will check it sometime.

Fat solution, no need for such a variant yet.

Thank you!

 
Vitaly Muzichenko:

Fat decision, no need for that option yet.

Thank you!

"Bold" because it wasn't made for a local solution, but as one part of a full-fledged replacement for OnTradeXXXX. There's more work with history in it.

And this is a great advantage - we have ready data for search and sorting of any orders and positions in the program (we do not need to search for orders and positions again to meet the program needs - everything is already in the lists). Another plus is that the program knows which order originated the position in MQL4, what cannot be done in the versions mentioned above.

I would repeat, the library is made to make things easier for those who have too much time and money to do it all by themselves. I don't insist on anything, of course :)

 
Aleksandr Volotko:

And so it is. If the number of open orders does not change, the number in history will.(I do not care about pending orders - I do not use them). And you don't have to bother your grandmother with going through the orders all day long to catch only one event.

And if the user has received a text message and decided to display the history in the terminal instead of all of it only for the last month, it will be an additional check with trying all orders, which is not fatal.

It seems to be a good solution. If you have a trading account and terminal, you may use only what you have without being bound to anything.

In order not to go through the whole day, you can check only when the conditions that can lead to changes, opening, closing positions, ie focus on achieving the prices that the Expert Advisor uses to open, TP, SL. Well, it depends on the Expert Advisor's logic, you know what's cheaper.

 
Aleksey Mavrin:

To avoid going through the whole day, you can check only when the conditions that can lead to a change, opening, closing a position have been met, i.e. to focus on achieving the prices that the EA uses to open, TP, SL. Well, yes, it depends on the logic of the Expert Advisor, you know what's cheaper.

One EA (on one computer, on one continent) trades. The other (on another computer, on another continent) deals with its functions. A solution has already been found.

 
fxsaber:

I'd be grateful if you could provide some reproducible example (without polling the trading history).

Here's what came up (although offtopic here, but asked for here).

Cut to live. No MT4 compatibility (of course), no error handling etc.

Trading is primitive, opens BUY, closes on SL/TP. The only point is to demonstrate OnTradeTransaction() vs "polling the server".

For me it took 2.34s vs 3.06s to pass in the given timeframe. The difference is small due to low load on server functions (only one position, no check for magik and symbol). In real EA the difference will be much bigger. The difference will be compensated slightly by calculation of signals and adding trailing stops, but they do not need to be done on every tick. My ATR is calculated on M1, but for 6 hours (i.e. there is space to enlarge TF). And trailing and Breakeven is calculated on H3. It all depends on the strategy.

// Test OnTradeTransaction.mqh
#property version   "1.00"
#property copyright "(c)2020 Edgar Akhmadeev"
#property link      "https://www.mql5.com/en/users/dali"
// 2020.01.27

//      Primitive expert to test OnTradeTransaction().
//      BUY only one position a time. There's no any signals.
//      Try OHLC M1 EURUSD 2016.12.19-2018.04.14 (bull trend) with default SL/TP.



//#define TESTER



#define  MT4_TICKET_TYPE
#include <MT4Orders.mqh>
#include <mql4_to_mql5.mqh>



struct PosInfoBase {
        double                  price, sl, tp;
        datetime                time;
        int                     ticket;
        ENUM_ORDER_TYPE 	 type;

        #ifndef  TESTER
                int             mn;
                string          sym;
        #endif 
};

struct PosInfo {
        PosInfoBase             base;
        int                     ticket;
};



input int       SL              = 6;
input int       TP              = 900;



MqlTick         tick;
PosInfo         posInfo         = { 0 };



#ifdef  TESTER

//*************************************************************************************************************
void 
OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) {
        if (trans.type == TRADE_TRANSACTION_ORDER_DELETE && trans.order_state == ORDER_STATE_PLACED) {
                // No money
                //errFatal = true;
                return;
        }
        
        static long lastTick;

        if (trans.type == TRADE_TRANSACTION_DEAL_ADD && trans.order_state == ORDER_STATE_STARTED) {
                // Open on market step1, SL/Order triggered step1, SL/Order triggered step4
                
                if (!HistoryDealSelect(trans.deal)) {
                        //errFatal = true;
                        return;
                }
                
                ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
                if (entry != DEAL_ENTRY_OUT)
                        return;
                
                ENUM_DEAL_REASON reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON);
                if (reason != DEAL_REASON_SL && reason != DEAL_REASON_TP)
                        return;
                
                if (lastTick == tick.time_msc)
                        return;

                posInfo.base.ticket = 0;
                InitPos();

                lastTick = tick.time_msc;
                return;
        }
}



#else // TESTER



//*************************************************************************************************************
bool
Support() {
        posInfo.base.ticket = 0;
        
        int cnt = PosTotal();
        if (cnt > 1)
                return false;
        
        PosInfo _posInfo;
        
        if (cnt == 0)
                _posInfo.base.ticket = 0;
        else {
                PosInfoFill(_posInfo);
                _posInfo.ticket = _posInfo.base.ticket;
        }

        //-------------------------------------------------------------------------------------------------------------
        // Position: process
        
        if (_posInfo.base.ticket != 0 && posInfo.ticket != 0 && _posInfo.base.ticket == posInfo.ticket) {
                // Ничего не произошло, та же позиция
                
                posInfo.base.ticket = _posInfo.base.ticket;
                posInfo.base.time = _posInfo.base.time;
                //posInfo.base.price = _posInfo.base.price;
                posInfo.base.sl = _posInfo.base.sl;
                posInfo.base.tp = _posInfo.base.tp;
        }

        //-------------------------------------------------------------------------------------------------------------
        // Инициализация новой позиции
        
        if (posInfo.base.ticket > 0)
                return true;
        
        return InitPos();
}

#endif // TESTER



//*************************************************************************************************************
bool
InitPos() {
        if (!SymbolInfoTick(_Symbol, tick))
                return false;
        
        posInfo.base.type = ORDER_TYPE_BUY;
        posInfo.base.sl = NormalizeDouble(tick.bid - SL * Point, Digits); 
        posInfo.base.tp = NormalizeDouble(tick.bid + TP * Point, Digits);       

        ResetLastError();
        int order = OrderSend(_Symbol, posInfo.base.type, 0.01, tick.ask, 99, posInfo.base.sl, posInfo.base.tp); 
        if (order < 0)
                return false;
        
        if (!OrderSelect(order, SELECT_BY_TICKET))
                return false;

        posInfo.base.price = OrderOpenPrice();
        posInfo.ticket = posInfo.base.ticket = order;
        posInfo.base.time = tick.time;
        return true;
}



//*************************************************************************************************************
int
PosTotal() {
        int posTotal = OrdersTotal();
        int cnt = 0;
        for (int i = posTotal - 1; i >= 0; --i) {
                if (!OrderSelect(i, SELECT_BY_POS)) {
                        //errFatal = true;
                        return -1;
                }

                if (OrderType() > OP_SELL)
                        continue;

                /*
                #ifndef TESTER
                        if (OrderMagicNumber() != MagicNum)
                                continue;
                        if (OrderSymbol() != symInfo.sym)
                                continue;
                #endif 
                */
                        
                ++cnt;
        } // for
        
        return cnt;
}


        
//*************************************************************************************************************
void
PosInfoFill(PosInfo& _posInfo) {
        ZeroMemory(_posInfo);

        _posInfo.base.ticket = (int)PositionGetInteger(POSITION_TICKET);
        _posInfo.base.type = (ENUM_ORDER_TYPE)PositionGetInteger(POSITION_TYPE);
        _posInfo.base.price = PositionGetDouble(POSITION_PRICE_OPEN);
        _posInfo.base.time = (datetime)PositionGetInteger(POSITION_TIME);
        _posInfo.base.sl = PositionGetDouble(POSITION_SL);
        _posInfo.base.tp = PositionGetDouble(POSITION_TP);

        #ifndef  TESTER
                _posInfo.base.mn = (int)PositionGetInteger(POSITION_MAGIC);
                _posInfo.base.sym = PositionGetString(POSITION_SYMBOL);
        #endif 
}



//*************************************************************************************************************
void 
OnTick() {
        ResetLastError();
        if (!SymbolInfoTick(_Symbol, tick)) {
                int LE = GetLastError();
                //errFatal = true;
                return;
        }
        
        #ifdef  TESTER

                if (posInfo.base.ticket > 0)
                        return;
                
                if (!InitPos()) {
                        //errFatal = true;
                        return;
                }

        #else  // TESTER
                
                if (!Support()) {
                        //errFatal = true;
                        return;
                }
        
        #endif // TESTER
}



//*************************************************************************************************************
int 
OnInit() {
        return INIT_SUCCEEDED;
}



//*************************************************************************************************************
void 
OnDeinit(const int reason) {
}
 
prostotrader:

You are hopelessly behind the times!

These events have been guaranteed for a long time!

Suppose an event occurred in OnTradeTransaction() after which some action must be performed, but an error occurred at the first attempt to perform this action. What to do? Obviously, it must be repeated, and for this it is necessary to save somewhere data about the necessity of repeating this action - most likely, this data is saved in the usual global variables of Expert Advisor or in static functions. And suddenly I had to restart the terminal... the data is gone.

And when you analyze the current situation and history - nothing goes anywhere.