Обсуждение статьи "Разрабатываем мультивалютный советник (Часть 3): Ревизия архитектуры"

 

Опубликована статья Разрабатываем мультивалютный советник (Часть 3): Ревизия архитектуры:

Мы уже несколько продвинулись в разработке мультивалютного советника с несколькими параллельно работающими стратегиями. С учетом накопленного опыта проведем ревизию архитектуры нашего решения и попробуем ее улучшить, пока не ушли слишком далеко вперед.

Мы выделили объект эксперта (класса CAdvisor или его потомков), который является агрегатором объектов торговых стратегий (класса CStrategy или его потомков). В начале работы советника в обработчике OnInit() происходит следующее:

  • Создается объект эксперта.
  • Создаются объекты торговых стратегий и добавляются к эксперту в его массив для торговых стратегий.

В советнике в обработчике события OnTick() происходит следующее:

  • Вызывается метод CAdvisor::Tick() для объекта эксперта.
  • Этот метод перебирает все стратегии и вызывает их метод CStrategy::Tick().
  • Стратегии в рамках работы CStrategy::Tick() выполняют все необходимые операции для открытия и закрытия рыночных позиций.

Схематично это можно изобразить так:



Рис. 1. Схема работы из первой статьи

Рис. 1. Схема работы из первой статьи

Достоинством такой схемы было то, что имея исходный код советника, работающего по какой-то одной торговой стратегии, можно было путем не сильно сложных операций переделать его для совместной работы с другими экземплярами торговых стратегий.

Но быстро обнаружился и главный недостаток: при объединении нескольких стратегий нам приходится уменьшать в той или иной степени размеры позиций, открываемых каждым экземпляром стратегии. Это может привести к полному исключению из торговли некоторых или даже всех экземпляров стратегий. Чем больше экземпляров стратегий мы включаем в параллельную работу или чем меньший начальный депозит выбирается для торговли, тем вероятнее подобный исход, так как минимальный размер открываемых рыночных позиций фиксирован.

Автор: Yuriy Bykov

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

Иногда что-то внутри подстегивает так написать.

FOREACH(m_orders, m_ordersTotal += m_orders[i].IsOpen())
 
Не дочитал до конца. Но ощущение, что работа с отложками (не в Тестере) не очень вписывается в эту, действительно, значительно улучшенную архитектуру.
 

Хорошо бы добавить маску включения/выключения стратегии для учета объемника (получатель виртуальных объемов).

Например, нужно какую-то ТС из портфеля отключить на время: виртуально продолжает торговать, но при этом не влияет на реальное окружение. Аналогично и с обратным ее включением.

 
Сходу не приходит в голову доработка данной архитектуры. Сейчас только так.
//+------------------------------------------------------------------+
//| Базовый класс эксперта                                           |
//+------------------------------------------------------------------+
class CAdvisor {
protected:
   CStrategy         *m_strategies[];  // Массив торговых стратегий

А ведь где-то должно быть грамотное встраивание нечто подобного.

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

со своими мэджиками.

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

Вы можете получит сложность с этим даже в Тестере, если котировочная сессия выходит за торговую сессию.

Спасти может флаг, что синхронизация объемов прошла успешно.

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

В виртуальный ордер вставлять совсем иную сущность - сомнительное решение.

Складывается впечатление, что при создании архитектуры параллельно решалась проблема производительности. Сам подобным грешу.


Наверное, все же правильно сперва ваять без оглядки на производительность. А потом уже думать, как можно ускорить.

 
fxsaber #:
Не дочитал до конца. Но ощущение, что работа с отложками (не в Тестере) не очень вписывается в эту, действительно, значительно улучшенную архитектуру.

С реальными отложенными ордерами не работаю, вроде бы достаточно пока просто открывать рыночные позиции при срабатывании виртуальных отложенных ордеров. Понимаю, что это может быть чревато не совсем точными входами из-за проскальзывания и реквот при реальной торговли. Но на практике пока с этим не столкнулся. Поддержка виртуальных отложенных ордеров будет в следующей части.

Если всё-таки заморачиваться реальным выставлением отложенных ордеров, то, в принципе, архитектура это позволяет: надо будет написать другую реализацию класса CVirtualSymbolReceiver. Сейчас реализация просто игнорирует виртуальные отложенные ордера.

 
fxsaber #:

Хорошо бы добавить маску включения/выключения стратегии для учета объемника (получатель виртуальных объемов).

Например, нужно какую-то ТС из портфеля отключить на время: виртуально продолжает торговать, но при этом не влияет на реальное окружение. Аналогично и с обратным ее включением.

Такое реализовать несложно, но без для использования понадобятся чёткие критерии включения/выключения для каждой стратегии. Это уже более сложная задача, пока к ней не подступался; возможно, что и не подступлюсь.

 
fxsaber #:

А ведь где-то должно быть грамотное встраивание нечто подобного.

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

со своими мэджиками.

Такого как раз не планируется. Объединение в портфели будет происходить на промежуточном уровне между CAdvisor и CStrategy. Черновое решение есть, но оно, скорее всего, сильно изменится при проводимом рефакторинге.

 
fxsaber #:

Вы можете получит сложность с этим даже в Тестере, если котировочная сессия выходит за торговую сессию.

Спасти может флаг, что синхронизация объемов прошла успешно.

Вроде бы он уже есть:

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

Сбрасывается только при успешном открытии нужного реального объёма по каждому символу.

Причина обращения: