Discussing the article: "Developing a multi-currency Expert Advisor (Part 17): Further preparation for real trading"

 

Check out the new article: Developing a multi-currency Expert Advisor (Part 17): Further preparation for real trading.

Currently, our EA uses the database to obtain initialization strings for single instances of trading strategies. However, the database is quite large and contains a lot of information that is not needed for the actual EA operation. Let's try to ensure the EA's functionality without a mandatory connection to the database.

In one of the previous articles, we already turned our attention to the EA improvements necessary for working on real accounts. Until now, our efforts have been focused mainly on getting acceptable EA results in the strategy tester. Real trading requires much more preparations.

In addition to restoring the EA operation after restarting the terminal, the ability to use slightly different names of trading instruments and auto completion of trading when the specified indicators are reached, we also face the following issue: in order to form the initialization string, we use information obtained directly from the database, which stores all the results of optimizations of trading strategy instances and their groups.

To run the EA, we must have a file with the database in the shared terminal folder. The size of the database is already several gigabytes, and it will only grow in the future. So making the database an integral part of the EA is not rational - only a very small part of the information stored there is needed for launch. Therefore, it is necessary to implement a mechanism for extracting and using this information in the EA.

Разрабатываем мультивалютный советник (Часть 17): Дальнейшая подготовка к реальной торговле 

Author: Yuriy Bykov

 

Yuri hello! I'm studying your code and I don't understand a bit.... In the file SimpleVolumesStrategy.mqh in the constructor you have a parameter:

         // Регистрируем обработчик события нового бара на минимальном таймфрейме
         IsNewBar(m_symbol, PERIOD_M1);

Why do you do this? Why exactly the period M1, and not the current one, on which this strategy instance is launched? As I understand in this case, the Expert Advisor will work not by bar openings of a given TF, but simply by m1. Or do I understand something wrong?

And the second point - in the SignalForOpen() function here:

      // Если текущий объем превысил заданный уровень, то
      if(m_volumes[0] > avrVolume * (1 + m_signalDeviation + m_ordersTotal * m_signaAddlDeviation)) {
         // если цена открытия свечи меньше текущей цены (закрытия), то
         if(iOpen(m_symbol, m_timeframe, 0) < iClose(m_symbol, m_timeframe, 0)) {
            signal = 1; // сигнал на покупку
         } else {
            signal = -1; // иначе - сигнал на продажу
         }
      }

You have indicated bar 0 (current) in the calculations, although it seems to me there should be bar 1 (last closed). It's just that bar 0 on the first tick (if we work on opening prices) has no body, and therefore the opening price is always equal to the closing price. And it can't have any volume because it hasn't been formed yet. I may be misunderstanding something, but it works somehow.... I'd like to understand why it's like this?

And also if I want to throw some parameter (new) into this strategy, I need to create it in the SimpleVolumesStage1.mq5 Expert Advisor in the settings and pass it to a string variable there, and then in this class (SimpleVolumesStrategy.mqh) in the constructor to parse in the correct order and that's it? Or do I need to prescribe it somewhere else?

 

Hello, Victor.

Зачем Вы это делаете? Почему именно период М1, а не текущий на котором запускается этот экземпляр стратегии? Как я понимаю в этом случае советник будет работать не по открытиям баров заданного ТФ а просто по м1. Или я что-то не правильно понимаю?

The work of the Expert Advisor consists of two parts: opening virtual positions and synchronisation of open virtual positions with real ones. The set TF is used only in the first part to determine the opening signal. And synchronisation should ideally be performed on every tick or at least on every new bar of the minimum timeframe M1, because at any moment a virtual position can reach TP or SL.

In the VirtualAdvisor::Tick() method, there is a check at the beginning for the occurrence of a new bar on all monitored symbols and timeframes, including M1. If it has not occurred, the Expert Advisor does not perform any more actions. It will do something else only when a new bar occurs on M1. In this case, you can optimise in OHLC mode on M1 and get almost the same results when the EA works on the chart (where there are all ticks). And optimisation is much faster this way. The line of code you mentioned is just a safety net in case we don't need to track a new bar on M1 in the strategy. This way it is guaranteed to be tracked at least on one symbol.

If you want, you can, of course, disable this mode of operation through the variable useOnlyNewBars_ = false. Then the Expert Advisor will check and synchronise positions on every available tick.

It's just that bar 0 on the first tick (if we work onopening prices) has no body, so the opening price is always equal to the closing price. And it can't have any volume because it hasn't been formed yet. I may be misunderstanding something, but it works somehow...

The opening of a new M1 bar can occur inside a bar of a higher timeframe. Note that SignalForOpen() uses the current timeframe, which is usually H1, M30 or M15. Therefore, there will no longer be a coincidence of the opening and closing prices of the current timeframe. In addition, this check comes only when the tick volume of the current bar on the current timeframe has significantly exceeded the typical tick volume of one bar. This cannot happen on the first tick, when the tick volume is only 1.

And also if I want to throw some parameter (new) into this strategy, I need to create it in the SimpleVolumesStage1.mq5 Expert Advisor in the settings and pass it to a string variable there, and then in this class (SimpleVolumesStrategy.mqh) in the constructor to parse in the correct order and that's it? Or is there somewhere else you have to spell it out?

Absolutely.

 
Yuriy Bykov #:

The work of the Expert Advisor consists of two parts: opening virtual positions and synchronisation of open virtual positions with real ones. The set TF is used only in the first part to determine the opening signal. And synchronisation should ideally be performed on each tick or at least on each new bar of the minimum timeframe M1, because at any moment the virtual position may reach TP or SL.

In the VirtualAdvisor::Tick() method, there is a check at the beginning for the occurrence of a new bar on all monitored symbols and timeframes, including M1. If it has not occurred, the Expert Advisor does not perform any more actions. It will do something else only when a new bar occurs on M1. In this case, you can optimise in OHLC mode on M1 and get almost the same results when the EA works on the chart (where there are all ticks). And optimisation is much faster this way. The line of code you mentioned is just a safety net in case we don't need to track a new bar on M1 in the strategy. This way it is guaranteed to be tracked at least on one symbol.

If you want, you can, of course, disable this mode of operation through the variable useOnlyNewBars_ = false. Then the Expert Advisor will check and synchronise positions on every available tick.

I see. But for example, can we make synchronisation of positions work on every tick, and opening of virtual (new) positions occurs when a new bar occurs on the TF specified in the strategy (m15,m30,h1)?

Yuriy Bykov #:
Opening of a new M1 bar can occur inside a bar of a higher timeframe. Note that SignalForOpen() uses the current timeframe, which is usually H1, M30 or M15. Therefore, there will no longer be a coincidence of the opening and closing prices of the current timeframe. In addition, this check comes only when the tick volume of the current bar on the current timeframe has significantly exceeded the typical tick volume of one bar. This cannot happen on the first tick, when the tick volume is only 1.

I don't understand you a little bit here. Yes, SignalForOpen() uses the TF set in the settings of the current virtual strategy instance, I can see that. But for example, if I want the EA to work strictly on the closed last bars, then here I have to specify units instead of zeros.

      if(m_volumes[0] > avrVolume * (1 + m_signalDeviation + m_ordersTotal * m_signaAddlDeviation)) {
         // если цена открытия свечи меньше текущей цены (закрытия), то
         if(iOpen(m_symbol, m_timeframe, 0) < iClose(m_symbol, m_timeframe, 0)) {

I should specify units instead of zeros ? Do I understand correctly ?

 
Viktor Kudriavtsev #:
For example, can we make position synchronisation work on every tick, and opening of virtual (new) positions occurs when a new bar occurs on the TF specified in the strategy (m15,m30,h1)?

Yes, this will be the case if useOnlyNewBars_ = false. This variable is not used by strategies, they themselves determine when to check for an opening signal and when to open positions when a signal has been received earlier. For example, only when a new bar occurs on H1. In this case, you must then modify the code so that the signal received in the middle of the bar survives until the beginning of the next bar. Now the received signal is used immediately (leads to the opening of virtual positions), so it is not saved anywhere.

I don't understand you a bit here. Yes, SignalForOpen() uses the TF set in the settings of the current instance of the virtual strategy, I can see that. But for example, if I want the EA to work strictly on the closed last bars, then here I should specify units instead of zeros ? Do I understand correctly ?

If by the words"EA worked strictly on closed last bars" you mean that when the tick volume exceeds the threshold value on the current bar to determine the direction of the signal to open, we will take the previous bar and look at its direction, then you have understood everything correctly.

 

Yuri hello. I have an error when executing the Expert Advisor SimpleVolumesStage3.mq5 and saving information to the database:

2024.09.11 21:02:09.909 Core 1  2024.09.06 23:54:59   
2024.09.11 21:02:09.909 Core 1  2024.09.06 23:54:59   database error, FOREIGN KEY constraint failed
2024.09.11 21:02:09.909 Core 1  2024.09.06 23:54:59   CDatabase::Execute | ERROR: 5619 in query
2024.09.11 21:02:09.909 Core 1  2024.09.06 23:54:59   INSERT INTO strategy_groups VALUES(0, 'EA_EG_EU (H1, M30, M15, 9x16 items)')
2024.09.11 21:02:09.909 Core 1  final balance 24603.99 USD

What does it mean and how to fix it? The table was added to the database using your query from the article.

 

Yuri, I have been looking through your code and I see that the error occurs a little earlier in the CDatabase::Insert function, the log writes this:

2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59   CDatabase::Insert | ERROR: Reading row for request 
2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59   INSERT INTO passes VALUES (NULL, 0, 0, 10000.00,0.00,11096.20,21542.31,-10446.11,92.51,-63.35,630.89,39.00,444.04,53.00,-376.27,52.00,-376.27,52.00,9430.69,569.31,5.69,5.69,569.31,9325.11,683.96,6.83,6.83,683.96,2.15,2.06,16.22,3.44,3736.76,8435.00,5170.00,3042.00,2128.00,2766.00,2404.00,1706.00,1336.00,6.00,4.00,99.11,8122.90,'class CVirtualStrategyGroup([
2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59           class CVirtualStrategyGroup([
2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59           class CVirtualStrategyGroup([
2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59           class CSimpleVolumesStrategy("CADCHF",16385,220,1.40,1.70,150,2200.00,200.00,46000,24)
2024.09.12 20:14:11.248 Core 1  2024.09.06 23:54:59          ],66.401062),class CVirtualStrategyGroup([

.....

2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59          ],13.365410),class CVirtualStrategyGroup([
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59           class CSimpleVolumesStrategy("CADJPY",15,132,0.40,1.90,0,7200.00,600.00,45000,27)
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59          ],13.365410),
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59          ],2.970797),
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59          ],1.462074)',
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59   '',
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59   '2024.09.06 23:54:59') RETURNING rowid;
2024.09.12 20:38:26.905	Core 1	2024.09.06 23:54:59   failed with code 5039

Cannot execute

      if(DatabaseReadBind(request, row)) {

What can this be related to? The second stage is passed and the test itself passes (the Expert Advisor trades and passes from the database are loaded).

 

Hello Victor.

I will be back soon to continue working on this project and will try to sort out the errors I found. Thank you for finding them. I managed to reproduce some of the errors you wrote about earlier. They turned out to be related to the fact that in later parts, edits were made that were aimed at one thing, but in addition had an impact on other things that were not considered in the next article. This influence created errors. In the next article we will go through all the steps of automated optimisation again, eliminating all the errors that were detected.