Discussing the article: "Developing a multi-currency Expert Advisor (Part 3): Architecture revision"

 

Check out the new article: Developing a multi-currency Expert Advisor (Part 3): Architecture revision.

We have already made some progress in developing a multi-currency EA with several strategies working in parallel. Considering the accumulated experience, let's review the architecture of our solution and try to improve it before we go too far ahead.

We have allocated an EA object (of the CAdvisor class or its descendants), which is an aggregator of trading strategy objects (CStrategy class or its descendants). At the beginning of the EA operation, the following happens in the OnInit() handler:

  • EA object is created.
  • Objects of trading strategies are created and added to the EA in its array for trading strategies.

The following happens in the OnTick() event handler:

  • The CAdvisor::Tick() method is called for the EA object.
  • This method iterates through all strategies and calls their CStrategy::Tick() method.
  • The strategies within CStrategy::Tick() perform all necessary operations to open and close market positions.

This can be represented schematically like this:



Fig. 1. Operation mode from the first article

Fig. 1. Operation mode from the first article

The advantage of this mode was that, it was possible to make the EA work with other instances of trading strategies via a number of relatively simple operations provided that you had the source code of an EA following a certain trading strategy.

However, the main drawback quickly emerged: when combining several strategies, we have to reduce the size of the positions opened by each instance of the strategy to one degree or another. This may lead to the complete exclusion of some or even all strategy instances from trading. The more strategy instances we include in parallel work or the smaller the initial deposit, the more likely such an outcome is, since the minimum size of open market positions is fixed.

Author: Yuriy Bykov

 
FOREACH(m_orders, if(m_orders[i].IsOpen()) { m_ordersTotal += 1; })

Sometimes something inside spurs you to write like that.

FOREACH(m_orders, m_ordersTotal += m_orders[i].IsOpen())
 
Haven't read all the way to the end. But the feeling is that the work with the pending (not in the Tester) doesn't really fit into this, indeed, much improved architecture.
 

It would be good to add a mask of switching on/off a strategy to account for the volumetrician (recipient of virtual volumes).

For example, you need to switch off some TS from the portfolio for a while: it continues to trade virtually, but does not affect the real environment. Similarly with its reverse switching on.

 
I can't think of a way to refine this architecture right off the bat. This is the only way to do it now.
//+------------------------------------------------------------------+
//| Базовый класс эксперта                                           |
//+------------------------------------------------------------------+
class CAdvisor {
protected:
   CStrategy         *m_strategies[];  // Массив торговых стратегий

But somewhere there must be a competent embedding of something similar.

 CAdvisor *m_advisors[];  // Массив виртуальных портфелей

with its own mages.

 
Оповещение получателя и стратегии должно происходить только при открытии или закрытии виртуальной позиции.

You may get difficulty with this even in the Tester if the quote session is out of the trading session.

The flag that volume synchronisation was successful can save you.

 
//+------------------------------------------------------------------+
//| Класс виртуальных ордеров и позиций                              |
//+------------------------------------------------------------------+
class CVirtualOrder {
private:
//--- Статические поля ...
   
//--- Связанные объекты получателя и стратегии
   CVirtualReceiver  *m_receiver;
   CVirtualStrategy  *m_strategy;

It is a questionable solution to insert a completely different entity into a virtual order.

It seems that when creating the architecture, the performance problem was solved in parallel. I have such a sin myself.


Probably, it is still correct to design without looking at performance. And then think how you can speed it up.

 
fxsaber #:
Haven't read all the way to the end. But I have a feeling that working with pending orders (not in the Tester) does not really fit into this, indeed, much improved architecture.

I don't work with real pending orders, it seems to be enough just to open market positions when virtual pending orders are triggered. I realise that this can be fraught with not quite accurate entries due to slippage and requotes in real trading. But in practice I have not encountered it yet. Support for virtual pending orders will be in the next part.

If you still want to bother with real pending orders, then, in principle, the architecture allows it: you will have to write another implementation of the CVirtualSymbolReceiver class. The current implementation simply ignores virtual pending orders.

 
fxsaber #:

It would be a good idea to add an on/off strategy mask to account for the volumetrician (recipient of virtual volumes).

For example, you need to switch off some TS from the portfolio for a while: it continues to trade virtually, but does not affect the real environment. Similarly with its reverse switching on.

It is not difficult to realise such a thing, but without using it, you will need clear on/off criteria for each strategy. This is a more complex task, I haven't approached it yet; probably I won't.

 
fxsaber #:

And there must be a competent incorporation of something like this somewhere.

CAdvisor *m_advisors[];  // Массив виртуальных портфелей

with its own mages.

There are no plans for that. Merging into portfolios will happen at an intermediate level between CAdvisor and CStrategy. There is a draft solution, but it is likely to change a lot during the ongoing refactoring.

 
fxsaber trading session.

The flag that volume synchronisation was successful can save you.

It seems to be already there:

class CVirtualSymbolReceiver : public CReceiver {
  ...
   bool              m_isChanged;      // Есть ли изменения в составе виртуальных позиций

It is reset only when the required real volume is successfully opened for each symbol.