Русский Português
preview
Desarrollamos un asesor experto multidivisas (Parte 28): Añadimos un gestor de cierre de posiciones

Desarrollamos un asesor experto multidivisas (Parte 28): Añadimos un gestor de cierre de posiciones

MetaTrader 5Ejemplos |
185 0
Yuriy Bykov
Yuriy Bykov

Introducción

En la Parte 12, ya añadimos un módulo de gestión de riesgos al asesor multidivisa para limitar las pérdidas diarias y totales. Este no aumenta las ganancias, pero resulta fundamental para proteger el capital en condiciones adversas. Está basado en las reglas de prop-trading, con ajustes flexibles: reducción de capital en la divisa, como porcentaje del saldo o desde el inicio del día.

El módulo está implementado como una clase CVirtualRiskManager con métodos para realizar un seguimiento del saldo, las ganancias y la verificación de las restricciones. También se incluye una función de fijación de ganancias: una vez alcanzado el objetivo, todas las posiciones se cierran y la negociación se detiene.

Para las cuentas regulares, querríamos que la negociación se reiniciara automáticamente después de obtener ganancias. Actualmente esto requiere de intervención manual. Ha llegado el momento de automatizar también este proceso.

Para reiniciar las estrategias comerciales una vez alcanzadas las ganancias objetivo, se consideraron dos opciones:

  • ampliar el gestor de riesgos actual,
  • crear un módulo aparte.

Nos hemos decantado por la segunda opción porque el gestor de riesgos actual opera independientemente de las estrategias: solo cierra posiciones reales sin afectar a las virtuales. Modificar esta lógica complicaría la arquitectura y violaría la independencia modular.

El gestor de riesgos también genera una carga de pruebas adicional, por lo que lo mejor es trasladar la nueva funcionalidad a un módulo independiente; este se puede usar incluso sin que el gestor de riesgos esté en funcionamiento.

El nuevo objetivo es un módulo que puede reiniciar todas las estrategias cuando se cumplen las condiciones especificadas (ganancias, pérdidas, tiempo, etc.), sin depender de la historia y sin intervención manual. Llamaremos al nuevo módulo gestor de cierre, ya que es un módulo independiente cuya presencia no es obligatoria, pero su incorporación puede mejorar los resultados, y gestiona el proceso de cierre completo de todas las posiciones, tanto reales como virtuales.


Requisitos iniciales

Vamos a definir con mayor claridad cuáles serán las responsabilidades del gestor de cierre y qué parámetros necesitará para ello. 

El gestor de cierre debe:

  1. Fijar las ganancias, es decir, cerrar todas las posiciones virtuales cuando se alcancen las ganancias especificadas. En este caso, las posiciones reales también se cerrarán automáticamente. Para controlar este proceso, vamos a introducir tres parámetros:
    • El saldo básico. La cantidad de fondos en una cuenta comercial a partir de la cual se calcularán las ganancias o las pérdidas.
    • El método de cálculo de ganancias. Puede tomar uno de varios valores posibles, por ejemplo, como un porcentaje del saldo básico o una cantidad fija en la divisa del depósito.
    • El valor de ganancias. El número utilizado para calcular las ganancias mediante el método elegido.
  2. Fijar las pérdidas, es decir, cerrar todas las posiciones virtuales cuando se alcancen las pérdidas especificadas. Este proceso también requiere tres parámetros, uno o dos de los cuales pueden ser compartidos con los parámetros de fijación de ganancias:
    • El saldo básico. La cantidad de fondos en una cuenta comercial a partir de la cual se calcularán las ganancias o las pérdidas.
    • El método de cálculo de pérdidas. También puede tomar uno de varios valores posibles, al igual que el método de cálculo de ganancias.
    • El valor de las pérdidas. El número utilizado para calcular las pérdidas mediante el método seleccionado.
  3. Habilitar el trailing de ganancias : cuando se alcanzan unas ganancias específicas, las posiciones virtuales no se cierran, sino que se recuerda un nivel de ganancias inferior, en el que las posiciones se cerrarán directamente. Si los ganancias aumentan, este nivel también debería incrementarse. El aumento puede producirse de manera continua o por etapas con cierto incremento. A este proceso se pueden añadir los siguientes parámetros:
    • Habilitar trailing (Sí / No).
    • Método para ajustar el nivel. En este parámetro podemos seleccionar el método preferido para establecer el nivel de habilitación de trailing. Por ejemplo, el nivel se puede establecer como un porcentaje de ganancias fijas o como un valor absoluto en la divisa de la cuenta comercial.
    • Nivel de inicio de trailing. El número utilizado para calcular el nivel de inicio de trailing para el método seleccionado.
    • Tamaño del pasoEl número usado para calcular el tamaño del paso en el que se restablece el nivel de trailing. Para el cálculo, podemos utilizar el mismo método que para el nivel de inicio de trailing.
  4. Habilitar el nivel de ausencia de pérdidas: cuando las ganancias alcanzan este valor, se recuerda un pequeño nivel de ganancia positivo, en el que se cerrarán las posiciones. Con un mayor aumento de las ganancias, este nivel, a diferencia del trailing, no aumentará. Los parámetros que controlan este proceso pueden ser los siguientes:
    • Habilitar ausencia de pérdidas (Sí/No).
    • Método para ajustar el nivel. Este parámetro es similar al parámetro homónimo para el trailing, es decir, también puede ser relativo o absoluto.
    • Nivel de activación de ausencia de pérdidasNúmero utilizado para calcular el punto de ausencia de pérdidas del método seleccionado.
Por ahora nos centraremos en esta funcionalidad básica, que se puede desarrollar más adelante. Es posible que durante la implementación necesitemos añadir algo a los parámetros o cambiar su composición. Pero inicialmente nos centraremos en esta descripción de la tarea.


Repositorio del proyecto

En la Parte 25, añadimos una nueva estrategia y analizamos cómo crear un proyecto para optimizar automáticamente la estrategia seleccionada y crear un asesor final que incluya muchas instancias de estrategias comerciales con diferentes parámetros. Todo el código se dividió en dos partes: la biblioteca y el proyecto. Para la parte de la biblioteca, ya hemos creado en la Parte 26 un repositorio de código público Adwizard en el repositorio MQL5 Algo Forge. Sin embargo, en lo que respecta a al parte del proyecto, esto aún no se ha hecho.

Vamos a solucionar esto y crear un nuevo repositorio   SimpleCandles. Este repositorio contendrá la parte del proyecto para crear el asesor final usando estrategias homónimas. Más allá de la rama principal main, también haremos una rama de desarrollo con el nombre develop. Si este proyecto es objeto de varios artículos, las ediciones relacionadas con los diferentes artículos se dividirán en distintas ramas derivadas de la rama develop. Cuando estén listas, se integrarán de nuevo en las ramas develop y main.

Primero creamos una carpeta local para contener la carpeta del proyecto, por ejemplo, MQL5/Experts/Articles/17608. Luego clonamos este repositorio en la carpeta seleccionada y creamos una carpeta dentro de ella, Include. En esta carpeta colocaremos el repositorio de la biblioteca de la que depende este proyecto. Después clonamos en la carpeta Include el repositorio de la biblioteca Adwizard.

Tras estas operaciones, obtendremos aproximadamente la siguiente estructura de carpetas en la carpeta de la terminal:

Fig. 1. Estructura de carpetas en el repositorio del proyecto después de clonar las partes del proyecto y la biblioteca.

En la carpeta del repositorio clonado Adwizard pasamos a la rama develop. Será común a todos los artículos. Sin embargo, a medida que avancemos en este proyecto, realizaremos cambios en la biblioteca Adwizard, en este repositorio crearemos una nueva rama, generada a partir de la rama develop.

Después de esto, también crearemos una rama separada en el repositorio del proyecto SimpleCandles para trabajar en este artículo y empezar el desarrollo en ella.


Preparando el código de la biblioteca

Antes de comenzar a implementar el gestor de cierre, prepararemos el terreno para su implementación. En primer lugar, observamos que las últimas versiones de MetaTrader han añadido una comprobación de tipos de variables más estricta, razón por la cual el código compilado anteriormente ahora produce errores del tipo:

parameter convertion type 'short[260]' to 'ushort[] &' is not allowed   MTTester.mqh    
   int user32::GetClassNameW(long,ushort&[],int)        winuser.mqh     

Por suerte, en el código que utilizamos, esto ocurre solo en un lugar y lo hemos solucionado cambiando el tipo de array: 

static string GetClassName( const HANDLE Handle )
  {
    string Str = NULL;

    ushort Buffer[MAX_PATH] = {0};

    if (user32::GetClassNameW(Handle, Buffer, ::ArraySize(Buffer)))
      Str = ::ShortArrayToString(Buffer);

    return(Str);
  }

Sin embargo, tras actualizaciones posteriores del terminal, este archivo ha sido reemplazado por completo con la última versión de la biblioteca MultiTester para corregir un comportamiento incorrecto que tenía otra causa.

El siguiente cambio está relacionado con la necesidad de que el gestor de cierre inicie el cierre de todas las posiciones. Así, añadimos a la clase de asesor CVirtualAdvisor un método independiente para cerrar todas las posiciones, de modo que el gestor de cierres pueda llamarlo si es necesario.

Para implementar este método, ya tenemos todo lo necesario: cada estrategia heredada de CVirtualStrategy posee un método para cerrar todas sus posiciones virtuales. Por ello, en la clase asesor, solo necesitamos llamar a este método para cada estrategia:

//+------------------------------------------------------------------+
//| Закрытие позиций всех стратегий                                  |
//+------------------------------------------------------------------+
void CVirtualAdvisor::Close(void) {
// Для всех стратегий вызываем метод закрытия виртуальныхпозиций
   FOREACH(m_strategies) ((CVirtualStrategy *)m_strategies[i]).Close();
}

En la Parte 27, nosotros creamos un componente para mostrar un texto de varias líneas en una ventana que se expande para cubrir todo el gráfico al que está vinculado el asesor. Lo elaboramos como parte de otro proyecto, pero también nos será útil aquí. Por consiguiente, lo transferiremos a la biblioteca Adwizard, colocando el archivo con la claseCConsoleDialog en la carpeta Adwizard/Utils. Para usarlo, agregaremos la creación de un objeto de esta clase en el asesor final en el archivo Adwizard/Experts/Expert.mqh :

CConsoleDialog      *dialog;             // Диалог для вывода текста с результатами

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
// ...

// Создаём и запускаем диалог для вывода результатов
   dialog = new CConsoleDialog();
   dialog.Create(__NAME__ + ":" + (string) magic_);
   dialog.Run();

// Успешная инициализация
   return(INIT_SUCCEEDED);
}

En la función para procesar un nuevo tick en el mismo archivo, agregaremos la configuración de nuevo texto para este objeto. Obtendremos el texto en sí del objeto de clase CVirtualAdvisor llamando a su método Text(), que escribiremos a continuación:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   expert.Tick();

// ...

// Выводим текст с информацией о работе советника
   if (IsNewBar(Symbol(), PERIOD_M1)) {
      dialog.Text(expert.Text());
   }
}

Para evitar que las líneas de apertura de posición virtual se dibujen sobre el fondo del texto, deshabilitaremos temporalmente su visualización dejando vacío el método CVirtualChartOrder::Show():

//+------------------------------------------------------------------+
//| Показ виртуальной позиции (ордера)                               |
//+------------------------------------------------------------------+
void CVirtualChartOrder::Show() {
   return;

   // ...
}


Propiedad IsActive para todos los descendientes de CFactorable

Cuando realizamos la optimización en instrumentos comerciales que incluyen criptomonedas, y el inicio se efectúa en un bróker que no admite criptomonedas, puede producirse un error al iniciar el asesor final. Este consiste en intentar obtener la historia de negociación y las propiedades de un símbolo que no está incluido en la descripción general del mercado. En este caso, si el asesor final contiene una gran cantidad de instancias de estrategias comerciales que operan con los símbolos disponibles, simplemente podemos desactivar las estrategias para aquellos instrumentos que no estén incluidos en la descripción general del mercado.

Actualmente, todas las estrategias comerciales son descendientes de la clase CFactorable, lo cual permite crear objetos de estas estrategias a partir de la cadena de inicialización. Esta clase contempla la posibilidad de que la cadena de inicialización no sea del todo correcta. Entonces, este objeto y todos los objetos anteriores de la cadena de inicialización común se considerarán no válidos. En esta situación, el asesor no podrá inicializarse ni continuar trabajando.

Lo que nos gustaría es que cierto tipo de "error" en la cadena de inicialización nos permitiera simplemente ignorar parte de la cadena de inicialización, creando finalmente un objeto experto a partir de la cadena de inicialización completa. Para ello, vamos a añadir una nueva propiedad a la clase CFactorable, que llamaremos m_isActive, y un método para leer su valor, IsActive() :

//+------------------------------------------------------------------+
//| Базовый класс объектов, создаваемых из строки                    |
//+------------------------------------------------------------------+
class CFactorable {
private:
   // ...

protected:
   // ...
   
   bool              m_isActive; // Объект активен?

   // ...

public:
   // ...

   bool              IsActive();                         // Объект активный?

   // ...
};

En algunas clases, como la clase gestora de riesgos CVirtualRiskManager, dicha propiedad ya existía, por lo que en estas clases eliminaremos su declaración, ya que se hará en la clase básica. Esto también se aplica a la futura clase de gestor de cierre, que también usará esta propiedad para comprobar si está activa.

En el proceso, hemos hecho opcional especificar el gestor de riesgos y el gestor de cierre en la cadena de inicialización agregando una verificación de su presencia al iniciar el asesor en el constructor de la clase CVirtualAdvisor:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CVirtualAdvisor::CVirtualAdvisor(string p_params) {
// Запоминаем строку инициализации
   m_params = p_params;

// Читаем строку инициализации объекта группы стратегий
   string groupParams = ReadObject(p_params);

// Читаем строку инициализации объекта риск-менеджера
   string riskManagerParams = NULL;

   if(IsObjectOf(p_params, "CVirtualRiskManager")) {
      riskManagerParams = ReadObject(p_params);
   }

// Читаем строку инициализации объекта менеджера закрытия
   string closeManagerParams = NULL;
   if(IsObjectOf(p_params, "CVirtualCloseManager")) {
      closeManagerParams = ReadObject(p_params);
   }

// Читаем магический номер
   ulong p_magic = ReadLong(p_params);

// Читаем название эксперта
   string p_name = ReadString(p_params);

// Читаем признак работы на только на открытии бара
   m_useOnlyNewBar = (bool) ReadLong(p_params);

// Если нет ошибок чтения, то
   if(IsValid()) {
// Создаём группу стратегий
      CREATE(CVirtualStrategyGroup, p_group, groupParams);

      // Инициализируем монитор символов статическим монитором символов
      m_symbols = CSymbolsMonitor::Instance();

      // Инициализируем получателя статическим получателем
      m_receiver = CVirtualReceiver::Instance(p_magic);

      // Инициализируем интерфейс статическим интерфейсом
      m_interface = CVirtualInterface::Instance(p_magic);

      // Формируем из имени эксперта и параметров имя файла базы данных эксперта для сохранения состояния
      m_fileName = FileName(p_name, p_magic);

      // Запоминаем время начала работы (тестирования)
      m_fromDate = TimeCurrent();

      // Сбрасываем время последнего сохранения
      m_lastSaveTime = 0;

      // Добавляем к эксперту содержимое группы
      Add(p_group);

      // Удаляем объект группы
      delete p_group;

      // Создаём объект риск-менеджера
      if(riskManagerParams != NULL) {
         m_riskManager = NEW(riskManagerParams);
      }

      // Создаём объект менеджера закрытия
      if(closeManagerParams != NULL) {
         m_closeManager = NEW(closeManagerParams);
         m_closeManager.Expert(&this);
      }
   }
}

Una vez realizados los cambios, podemos pasar a la parte principal: la creación de un gestor de cierre.


Creando un gestor de cierre

Para empezar, debemos destacar varios estados posibles en los que puede encontrarse el gestor encargado del cierre. En condiciones normales, ni las ganancias previstas ni las pérdidas máximas se han alcanzado todavía. Mientras se encuentra en este estado, el gestor de cierre solo tiene que esperar la transición a uno de los siguientes estados. Al alcanzar unas ganancias o unas pérdidas determinadas, se llevará a cabo una transición a dos estados correspondientes. En estos estados, el gestor de cierre debe cerrar todas las posiciones, recordar los nuevos niveles de ganancias y pérdidas especificados y volver al estado normal.

Si la opción de trailing está habilitada, cuando se alcance el nivel de ganancias especificado, el gestor de cierre pasará a otro estado. La transición de vuelta a la normalidad requerirá acciones más complejas, por lo que no las describiremos con detalle por ahora.

Implementaremos todos los estados como un tipo enumerado ENUM_CM_STATE.

Para definir los métodos de cálculo de las ganancias y pérdidas previstas, también crearemos dos tipos de enumeración separados: ENUM_CM_CALC_LOSS y ENUM_CM_CALC_PROFIT. Contemplaremos dos opciones: un valor fijo en términos monetarios y un valor relativo como porcentaje de un saldo básico determinado.

// Возможные состояния менеджера закрытия
enum ENUM_CM_STATE {
   CM_STATE_OK,            // Лимиты не превышены
   CM_STATE_LOSS,          // Превышен общий лимит
   CM_STATE_PROFIT,        // Достигнута общая прибыль
   CM_STATE_TRAIL_PROFIT   // Трейлинг прибыли
};

// Возможные способы расчёта общего убытка
enum ENUM_CM_CALC_LOSS {
   CM_CALC_LOSS_MONEY_BB,           // [$] Fixed Money
   CM_CALC_LOSS_PERCENT_BB,         // [%] of Base Balance
};

// Возможные способы расчёта общей прибыли
enum ENUM_CM_CALC_PROFIT {
   CM_CALC_PROFIT_MONEY_BB,           // [$] Fixed Money
   CM_CALC_PROFIT_PERCENT_BB,         // [%] of Base Balance
};

La clase gestora de cierre heredará de la clase básica CFactorable para proporcionar la capacidad de crear un objeto gestor de cierre a partir de la cadena de inicialización. Al mismo tiempo, contará inmediatamente con una propiedad de actividad heredada para habilitar o deshabilitar fácilmente el gestor de cierre.

Para poder desempeñar su función, el gestor de cierre deberá recordar el nivel de saldo básico a partir del cual se calcularán las ganancias o pérdidas resultantes. Al fijar ganancias o pérdidas, este nivel también debe cambiar al valor del saldo actual de la cuenta alcanzado después de cerrar todas las posiciones. Esta es la diferencia entre este parámetro y el parámetro homónimo en el gestor de riesgos. Allí, el nivel del saldo básico permanece siempre inalterado.

El siguiente grupo de propiedades se usará para seleccionar el método de cálculo y el cálculo en sí de las ganancias y pérdidas previstas. Se utilizarán en los métodos de cálculo LossMoney() y ProfitMoney(), que retornan un valor en términos monetarios.

Para cerrar las posiciones, el gestor de cierre debe poder dirigirse al experto para solicitar el cierre. Por consiguiente, añadiremos un puntero al objeto experto y un método para asignarlo.

Agregaremos además una propiedad más para almacenar el estado actual del objeto gestor de cierre.

La derivación de CFactorable requiere colocar el constructor en un ámbito no público y agregar dos macros especiales, como se describe en la Parte 24.

Como resultado, obtenemos una descripción de la clase del gestor de cierres que se parece a esto:

//+------------------------------------------------------------------+
//| Класс менеджера закрытия (фиксации прибыли и убытков)            |
//+------------------------------------------------------------------+
class CVirtualCloseManager : public CFactorable {
protected:
// Основные параметры конструктора
   double            m_baseBalance;          // Базовый баланс

   ENUM_CM_CALC_LOSS m_calcLossLimit;        // Способ расчёта максимального общего убытка
   double            m_maxLossLimit;         // Параметр расчёта максимального общего убытка

   ENUM_CM_CALC_PROFIT m_calcProfitLimit;    // Способ расчёта максимальной общей прибыли
   double            m_maxProfitLimit;       // Параметр расчёта максимальной общей прибыли

   CVirtualAdvisor*  m_expert;               // Указатель на объект эксперта

// Текущее состояние
   ENUM_CM_STATE     m_state;                // Состояние

// Обновляемые значения
   double            m_balance;              // Текущий баланс
   double            m_equity;               // Текущие средства
   double            m_profit;               // Текущая плавающая прибыль
   double            m_overallProfit;        // Текущая общая прибыль относительно базового баланса

// Защищённые методы
   double            LossMoney();            // Максимальный общий убыток
   double            ProfitMoney();          // Максимальная прибыль

   void              UpdateProfit();         // Обновление текущих значений прибыли
   void              CheckLimits();          // Проверка достижения допустимых уровней прибыли/убытка
  
   CVirtualCloseManager(string p_params);    // Закрытый конструктор

public:
   STATIC_CONSTRUCTOR(CVirtualCloseManager); // Статический метод создания объекта
   virtual void      Tick();                 // Обработка тика в менеджере закрытия

   virtual string    Text();                 // Информация о текущем состоянии

   // Привязка эксперта к менеджеру закрытия
   void              Expert(CVirtualAdvisor* p_expert);

   virtual bool      Save();      // Сохранение состояния
   virtual bool      Load();      // Загрузка состояния

   virtual string    operator~() override;   // Преобразование объекта в строку
};

REGISTER_FACTORABLE_CLASS(CVirtualCloseManager); // Регистрация нового потомка CFactorable

Vamos a analizar los dos métodos principales de esta clase: el constructor y el método de procesamiento de ticks.

En el constructor, como de costumbre, leemos los valores de los parámetros secuencialmente desde la cadena de inicialización y los asignamos a las propiedades correspondientes, establecemos el estado actual como normal, actualizamos los valores de ganancias actuales y recordamos el valor del saldo actual como valor básico si no se ha establecido directamente:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CVirtualCloseManager::CVirtualCloseManager(string p_params) {
// Запоминаем строку инициализации
   m_params = p_params;

// Читаем строку инициализации и устанавливаем значения свойств
   m_isActive = (bool) ReadLong(p_params);
   m_baseBalance = ReadDouble(p_params);
   m_calcLossLimit = (ENUM_CM_CALC_LOSS) ReadLong(p_params);
   m_maxLossLimit = ReadDouble(p_params);
   m_calcProfitLimit = (ENUM_CM_CALC_PROFIT) ReadLong(p_params);
   m_maxProfitLimit = ReadDouble(p_params);

// Устанавливаем состояние: Лимиты не превышены
   m_state = CM_STATE_OK;

// Обновляем текущие значения прибыли
   UpdateProfit();

// Корректируем базовый баланс, если он не задан
   if(m_baseBalance == 0) {
      m_baseBalance = m_balance;
   }
}

En el método básico de procesamiento de ticks, analizamos el estado actual y, dependiendo de él, o bien comprobamos si se han alcanzado los niveles objetivo de ganancias o pérdidas si el gestor se encontraba en un estado normal, o bien iniciamos el cierre de todas las posiciones y luego pasamos a un estado normal:

//+------------------------------------------------------------------+
//| Обработка тика в риск-менеджере                                  |
//+------------------------------------------------------------------+
void CVirtualCloseManager::Tick() {
// Если риск-менеджер неактивен, то выходим
   if(!m_isActive) {
      return;
   }

// Обновляем текущие значения прибыли
   UpdateProfit();

// Если менеджер в состоянии трейлинга, то
   if(m_state == CM_STATE_TRAIL_PROFIT) {
      // Пока просто будем сразу фиксировать прибыль,
      // переводя менеджер в соответсвующее состояние
      if(true) {
         m_state = CM_STATE_PROFIT;
      }
   }

// Если менеджер в нормальном состоянии, то
   if(m_state == CM_STATE_OK) {
      // Проверяем превышение пределов убытка и прибыли
      CheckLimits();
   }

// Если менеджер в состоянии достигнутого убытка или прибыли, то
   if(m_state == CM_STATE_LOSS || m_state == CM_STATE_PROFIT) {
      // Закрываем все позиции
      m_expert.Close();

      // Если все позиции закрыты, то
      if(PositionsTotal() == 0) {
         // Переходим в нормальное состояние
         m_state = CM_STATE_OK;
         
         // Обновляем значение базового баланса
         m_baseBalance = m_balance;
      } else {
         // Ждём закрытия всех позиций
      }

      // Сохраняем состояние эксперта
      m_expert.Save();
   }
}

Para empezar, hemos decidido limitarnos a esta funcionalidad del gestor de cierre, por lo que, por ahora, no utilizaremos el estado de trailing de ganancias.


Transmisión de los parámetros de entrada

Después de crear la clase del gestor de cierres, necesitamos conectarla al asesor. Para ello, deberemos añadir parámetros de entrada para controlar la creación de la cadena de inicialización para el gestor de cierre. Esto debe hacerse en el archivo Adwizard/Experts/Expert.mqh :

// ...

//+------------------------------------------------------------------+
//| Входные параметры                                                |
//+------------------------------------------------------------------+
input group "::: Использовать группу стратегий"
sinput int        groupId_       = 0;     // - ID группы из новой библиотеки (0 - последняя)
sinput bool       useAutoUpdate_ = true;  // - Использовать автообновление?

input group "::: Управление капиталом"
sinput double expectedDrawdown_  = 10;    // - Максимальный риск (%)
sinput double fixedBalance_      = 10000; // - Используемый депозит (0 - использовать весь) в валюте счета
input  double scale_             = 1.00;  // - Масштабирующий множитель для группы

input group ":::  Менеджер закрытия"
input bool        cmIsActive_                = true;  // - Активен?
input double      cmStartBaseBalance_        = 0;     // - Базовый баланс
input ENUM_CM_CALC_LOSS
cmCalcLossLimit_           = CM_CALC_LOSS_MONEY_BB;   // - Способ расчёта убытка
input double      cmLossLimit_       = 100;           // - Значение убытка для фиксации
input ENUM_CM_CALC_PROFIT
cmCalcProfitLimit_                    = CM_CALC_PROFIT_MONEY_BB;  // - Способ расчёта общей прибыли
input double      cmProfitLimit_   = 1000000;                     // - Значение общей прибыли для фиксации

// ...

Este archivo se incluye al compilar el asesor final, por lo que los parámetros de entrada añadidos estarán disponibles en él. Por defecto, establecemos los valores de ganancias y pérdidas fijas en dinero, en la divisa del depósito. Todavía debemos seleccionar los valores nosotros mismos, así que por ahora no importa lo que especifiquemos en los valores predeterminados.


Pruebas iniciales

Veamos qué tenemos. En primer lugar, comprobamos si funciona correctamente el mecanismo para cerrar posiciones sin tener en cuenta las ganancias obtenidas. Si es así, en la siguiente etapa podremos comenzar a optimizar las ganancias obtenidas.

Para ejecutar el asesor final en el simulador, usaremos la base de datos del asesor final obtenida en la Parte 25 Entonces realizamos una optimización acelerada durante varios intervalos de un año de duración y obtuvimos doce grupos de estrategias almacenados en la tabla strategy_groups :

El archivo de la base de datos se llamaba SimpleCandles-27183.test.db.sqlitePara que el asesor experto final pueda utilizar esta base de datos, este archivo debe estar ubicado en la carpeta de datos comunes de los terminales MeteTrader 5, en la subcarpeta Files. Además, el nombre del asesor final debe ser SimpleCandles.ex5 y el valor del número mágico en los parámetros de entrada debe dejarse igual a 27183.

Primero ejecutamos el asesor sin usar el gestor de cierre con el primer grupo de estrategias con id_group=20. Para ello, establecemos los siguientes valores para los parámetros de entrada:

Usaremos el mismo intervalo durante el cual se llevó a cabo la optimización automática como intervalo de prueba, es decir, todo el año 2022. Obtenemos los siguientes resultados:

Fig. 2. Resultados del asesor final con id_group=20 sin un gestor de cierre para 2022

Como podemos observar, la optimización ha encontrado combinaciones de parámetros bastante buenas para diferentes instancias de estrategias comerciales simples, lo que ha permitido obtener ganancias significativas durante un intervalo determinado, manteniéndose dentro del límite de pérdidas del 10%.

Ahora vamos a activar el gestor de cierre y a establecer un valor pequeño, por ejemplo 10 USD, como ganancias esperadas por la fijación:


Luego ejecutaremos el asesor en modo de prueba visual. Como hemos añadido la visualización de información sobre el trabajo al asesor final, en este modo podemos ver qué símbolos y cuántas estrategias se utilizan en el grupo con ID 20 de la base de datos del asesor (tres símbolos GBPUSD, EURUSD, EURGBP y 48 estrategias), cuál es el valor del saldo básico del gestor de cierre, el nivel objetivo de ganancias y pérdidas para el cierre.

Fig. 3. Iniciando la prueba visual del asesor con el gestor de cierre habilitado

La figura 3 muestra que el saldo básico del gestor de cierre ya ha alcanzado los 10009,89 dólares, lo que significa que todas las posiciones se han cerrado una vez que se ha alcanzado las ganancias objetivo de 10 dólares.

En el registro vemos la siguiente línea:

2022.01.03 02:31:00   CVirtualCloseManager::CheckLimits | CLOSE PROFIT Profit = 12.94 | OverallProfit = 10.54 (10.00)

El gestor de cierre se ha activado cuando las ganancias totales (ganancias totales = 10,54) en relación con el saldo básico inicial de 10.000 dólares han superado los 10 dólares. Como el modo de prueba solo se ejecuta al comienzo de cada barra de un minuto (OHLC de 1 minuto), el proceso de cierre de todas las posiciones abiertas se ha extendido durante dos minutos consecutivos, por lo que el nuevo nivel básico registrado ha resultado ligeramente inferior a 10010 dólares. Cuando se activa el modo de visualización de todos los ticks, ya no se observan dichas discrepancias.

Ahora vamos a probar el funcionamiento del gestor de cierre con limitación de pérdidas. Luego establecemos un valor pequeño para fijar las pérdidas, por ejemplo 20 dólares, mientras que el valor para fijar las ganancias será más significativo, de modo que haya una alta probabilidad de que se produzcan pérdidas, pero no ganancias.

En los demás parámetros prohibiremos trabajar solo en la apertura de la barra, para que al activar el modo de simulación de todos los tics (Every tick) el asesor experto realice todas las acciones requeridas en cada tick, y no solo al principio de la barra de un minuto:


Iniciaremos las pruebas con un intervalo de un día (03/01/2022). Al filtrar los mensajes de registro, seleccionaremos solo aquellas líneas que se generan cuando se alcanza la pérdida especificada de $20:

2022.01.03 17:11:33   CVirtualCloseManager::CheckLimits | CLOSE LOSS Profit = -33.13 | OverallProfit = -20.06 (-20.00)
2022.01.03 17:30:39   CVirtualCloseManager::CheckLimits | CLOSE LOSS Profit = -20.51 | OverallProfit = -20.51 (-20.00)
2022.01.03 19:13:31   CVirtualCloseManager::CheckLimits | CLOSE LOSS Profit = -21.20 | OverallProfit = -20.11 (-20.00)

Vemos que esto ha ocurrido tres veces durante el día de prueba, y en el modo de todos los ticks, las ganancias totales (OverallProfit), en las que se activa el cierre de posiciones al alcanzar las pérdidas especificadas, están mucho más cerca del valor especificado en los parámetros. 

Tenga en cuenta que la primera entrada del registro anterior contiene la siguiente parte:

Profit = -33.13

Este es el valor de las ganancias actuales en las posiciones abiertas (una ganancia negativa es una pérdida). En este caso, difiere del valor de -$20 porque inicialmente se han cerrado varias posiciones con unas ganancias de aproximadamente $13. Por ello, hemos obtenido unas pérdidas de 20 dólares con respecto al saldo inicial de la cuenta básica, con exactamente este valor de ganancias en las posiciones abiertas.

Así pues, las pruebas iniciales han demostrado que el gestor de cierre desarrollado ya puede realizar la parte básica de su trabajo.


Conclusión

Haremos una breve pausa aquí y continuaremos desarrollando el rol del gestor de cierre en una de las próximas partes. Los planes para el desarrollo futuro de su funcionalidad incluyen principalmente la adición de una función de seguimiento de ganancias para posiciones abiertas y la posibilidad de establecer un nivel de ausencia de pérdidas.

Las mejoras no terminan ahí. Por ejemplo, el gestor de cierre actualmente comprueba si se han cerrado todas las posiciones simplemente esperando a que el número de posiciones abiertas llegue a cero. Pero esto también puede ocurrir si hay posiciones virtuales abiertas, así que veamos si necesitamos utilizar un método de verificación más fiable en este caso. También podríamos tener que organizar la interacción entre el gestor de riesgos y el gestor de cierre: al cerrar, se debe actualizar el estado del gestor de riesgos y viceversa.

Sin embargo, la primera versión ya está hecha y el siguiente paso no se hará desde cero.

Gracias por su atención, ¡hasta pronto!


Advertencia importante

Todos los resultados expuestos en este artículo y en todos los artículos anteriores de la serie se basan únicamente en datos de pruebas históricas y no ofrecen ninguna garantía de lograr ganancias en el futuro. El trabajo de este proyecto es de carácter exploratorio. Todos los resultados publicados pueden ser usados por cualquiera bajo su propia responsabilidad.

Contenido del archivo

#
 Nombre
Versión  Descripción  Cambios recientes
  SimpleCandles     Carpeta de trabajo del proyecto (debe estar dentro de MQL5/Experts)  
SimpleCandles.mq5
1.01
Asesor final para el funcionamiento paralelo de varios grupos de estrategias de modelo. Los parámetros se tomarán de la biblioteca de grupos integrada.
Parte 25
  └ Optimization
  Carpeta de asesores para la optimización de proyectos  
2    CreateProject.mq5 1.02 Script asesor para crear un proyecto con etapas, actividades y tareas de optimización.
Parte 25
3    Optimization.mq5 1.00
Asesor experto para la optimización automática de proyectos
 
4    Stage1.mq5 1.02
Asesor experto para optimizar una única instancia de una estrategia comercial (Etapa 1)
Parte 25
5    Stage2.mq5 1.01
Asesor experto para optimizar un grupo de instancias de estrategias comerciales (Etapa 2)
Parte 25
6 Stage3.mq5 1.01
Asesor experto que guarda un grupo normalizado formado de estrategias usando como base los datos del asesor experto con el nombre especificado.  Parte 25
  └ Strategies   Carpeta de estrategias del proyecto
Parte 25
7    SimpleCandlesStrategy.mqh
1.01
Clase de la estrategia comercial SimpleCandles
Parte 25
  └ Include/Adwizard   Carpeta de la biblioteca de Adwizard  
    └ Base
  Clases básicas de las que heredan otras clases del proyecto    
8       Advisor.mqh 1.04. Clase básica del experto Parte 10
9       Factorable.mqh
1.06
Clase básica de objetos creados a partir de una cadena (string)
Parte 28
10       FactorableCreator.mqh
1.00 Clase creadora que enlaza nombres y constructores estáticos de clases herederas CFactorable Parte 24
11       Interface.mqh 1.01
Clase básica de visualización de diversos objetos
Parte 4
12       Receiver.mqh
1.04.  Clase básica de transferencia de volúmenes abiertos a posiciones de mercado
Parte 12
13       Strategy.mqh
1.04.
Clase básica de estrategia comercial
Parte 10
     └ Database
  Archivos para trabajar con todo tipo de bases de datos utilizadas por los asesores de proyectos
 
14       Database.mqh 1.12 Clase para trabajar con la base de datos Parte 25
15       db.adv.schema.sql 1.00
Esquema de la base de datos del asesor final Parte 22
16       db.cut.schema.sql
1.00 Esquema de una base de datos de optimización truncada
Parte 22
17       db.opt.schema.sql
1.05  Esquema de la base de datos de optimización
Parte 22
18       Storage.mqh   1.01
Clase de trabajo con almacenamiento Key-Value para el asesor experto final en la base de datos del asesor experto.
Parte 23
     └ Experts
  Archivos con partes comunes de asesores usados de distintos tipos
 
19       Expert.mqh  1.24 Archivo de biblioteca para el asesor final. Los parámetros del grupo pueden tomarse de la base de datos del asesor experto
Parte 28
20       Optimization.mqh  1.04. Archivo de biblioteca para el asesor experto que controla el inicio de las tareas de optimización
Parte 23
21       Stage1.mqh
1.19 Archivo de biblioteca del asesor experto para optimizar una única instancia de una estrategia comercial (Etapa 1)
Parte 23
22       Stage2.mqh 1.04. Archivo de biblioteca del asesor experto para optimizar un grupo de instancias de estrategias comerciales (Etapa 2)   Parte 23
23       Stage3.mqh
1.04. Archivo de biblioteca para el asesor experto que guarda el grupo normalizado generado de estrategias en la base de datos del asesor experto con un nombre especificado. Parte 23
     └ Optimization
  Clases responsables del trabajo de optimización automática
 
24       OptimizationJob.mqh 1.00 Clase para el funcionamiento de la etapa de proyecto de optimización
Parte 25
25       OptimizationProject.mqh 1.00 Clase para el proyecto de optimización Parte 25
26       OptimizationStage.mqh 1.00 Clase para la fase del proyecto de optimización Parte 25
27       OptimizationTask.mqh 1.00 Clase para la tarea de optimización (para la creación) Parte 25
28       Optimizer.mqh
1.03  Clase para el gestor de optimización automática de proyectos
Parte 22
29       OptimizerTask.mqh
1.03
Clase para la tarea de optimización (para el pipeline)
Parte 22
     └ Strategies    Ejemplos de estrategias comerciales usadas para demostrar el funcionamiento del proyecto
 
24       HistoryStrategy.mqh 
1.00 Clase de estrategia comercial para reproducir la historia de transacciones
Parte 16
25       SimpleVolumesStrategy.mqh
1.11
Clase de estrategia comercial con uso de volúmenes de ticks
Parte 22
     └ Utils
  Utilidades auxiliares, macros para la reducción del código

26        ConsoleDialog.mqh 1.01 Clase para mostrar información de texto en un gráfico Parte 28
26       ExpertHistory.mqh 1.00 Clase para exportar la historia de transacciones a un archivo Parte 16
27       Macros.mqh 1.07 Macros útiles para operaciones con arrays Parte 26
28        MTTester.mqh 
Archivo para trabajar con el simulador de estrategias de la biblioteca MultiTester
Parte 28
29       NewBarEvent.mqh 1.00  Clase de definición de una nueva barra para un símbolo específico  Parte 8
30       SymbolsMonitor.mqh  1.01 Clase de obtención de información sobre instrumentos comerciales (símbolos) Parte 28
     └ Virtual
  Clases para crear diversos objetos unidos mediante un sistema de órdenes y posiciones comerciales virtuales

31       Money.mqh 1.01  Clase básica de gestión de capital
Parte 12
32       TesterHandler.mqh  1.07 Clase para gestionar los eventos de optimización  Parte 23
33       VirtualAdvisor.mqh  1.12  Clase de asesor experto para trabajar con posiciones (órdenes) virtuales Parte 28
34       VirtualChartOrder.mqh  1.02  Clase de posición virtual gráfica Parte 28
35       VirtualCloseManager.mqh 1.00 Clase de gestor de cierre Parte 28
36       VirtualHistoryAdvisor.mqh 1.00  Clase experta para reproducir la historia de transacciones  Parte 16
37       VirtualInterface.mqh  1.00  Clase de GUI del asesor  Parte 4
38       VirtualOrder.mqh 1.09  Clase de órdenes y posiciones virtuales  Parte 22
39       VirtualReceiver.mqh 1.04. Clase de transferencia de los volúmenes abiertos a las posiciones de mercado (receptor)  Parte 23
40       VirtualRiskManager.mqh  1.06 Clase de gestión de riesgos (gestor de riesgos)  Parte 28
41       VirtualStrategy.mqh 1.09  Clase de estrategia comercial con posiciones virtuales  Parte 23
42       VirtualStrategyGroup.mqh  1.04.  Clase de grupo o grupos de estrategias comerciales Parte 28
43       VirtualSymbolReceiver.mqh  1.00 Clase de receptor simbólico  Parte 3
  Common/Files   Carpeta compartida de datos del terminal MetaTrader 5  
44 SimpleCandles-27183.test.db.sqlite Base de datos del asesor final Parte 25

El código fuente también está disponible en los repositorios públicos SimpleCandles y Adwizard


Cómo utilizar los repositorios de código abierto

A medida que avancemos gradualmente hacia el nuevo sistema de almacenamiento Algo Forge, seguiremos trabajando en la mejor y más conveniente manera de utilizarlo. La restricción del MetaEditor a utilizar un único repositorio correspondiente a la carpeta raíz de MQL5 no resulta especialmente cómoda. Los demás repositorios solo estarán disponibles como subcarpetas de la carpeta Shared Projects, lo que facilita un poco la gestión, pero no lo suficiente como para justificar el uso inmediato de este método.

Además, durante la transición, el único repositorio principal se ha creado dos veces en nombre del superadministrador, y la segunda vez, por alguna razón, su creación ha destruido otros repositorios adicionales creados previamente por el usuario. Afortunadamente, la copia local del repositorio permite volver a subirlo al servidor, pero este tipo de acción forzada no es particularmente deseable. Por consiguiente, por ahora esperaremos a que se desarrollen más las funcionalidades del editor MetaEditor para trabajar con repositorios sin utilizarlos directamente.

Esto no tiene ninguna complicación: mientras continuamos trabajando en MetaEditor, simplemente trasladaremos por el momento todas las operaciones relacionadas con el almacenamiento a aplicaciones externas.

Por ejemplo, se puede obtener una copia de todos los archivos con el código de este artículo en la computadora local ejecutando el siguiente script en la consola, habiendo establecido previamente como carpeta actual alguna carpeta dentro de la carpeta MQL5 del terminal MetaTrader correspondiente:

# Creamos una carpeta para el proyecto
mkdir SimpleCandles

# Entramos en la carpeta del proyecto
cd SimpleCandles

# Clonamos el repositorio del proyecto en la carpeta actual
git clone https://forge.mql5.io/antekov/SimpleCandles.git .

# Cambiamos el repositorio a la rama deseada (para este artículo: "article-17608-close-manager")
git checkout article-17608-close-manager

# Comprobamos que hemos cambiado a la rama
git status

# Creamos una carpeta para la parte de la biblioteca
mkdir Include

# Pasamos a ella
cd Include

# Clonamos el repositorio de Adwizard en la carpeta de la biblioteca
git clone https://forge.mql5.io/antekov/Adwizard.git 

# Pasamos a la carpeta creada
cd Adwizard

# Cambiamos el repositorio a la rama deseada (para este artículo: " article-17608-close-manager" )
git checkout article-17608-close-manager

# Comprobamos que hemos cambiado a la rama
git status

# Subimos dos niveles hasta la carpeta del proyecto original
cd ./../..

Lo único que falta en estos repositorios es un archivo con la base de datos del asesor final, ya que el código en el repositorio permite obtenerla mediante la realización de una optimización automática. Pero si es necesario, este archivo se puede extraer del archivo del artículo y colocarlo en la carpeta común del terminal, dentro de la carpeta Files.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/17608

Archivos adjuntos |
SimpleCandles.zip (494.95 KB)
Redes neuronales en el trading: Previsión probabilística de series temporales (K2VAE) Redes neuronales en el trading: Previsión probabilística de series temporales (K2VAE)
Le invitamos a explorar la implementación original del framework K²VAE, un modelo flexible capaz de aproximar linealmente dinámicas complejas en el espacio latente. Este artículo le mostraremos cómo implementar componentes clave en MQL5, incluidas las matrices parametrizadas y su gestión fuera de las capas estándar de redes neuronales. Este material resultará útil a quienes busquen un enfoque práctico para crear modelos de series temporales interpretables.
De novato a experto: Noticias animadas utilizando MQL5 (IV) Análisis de mercado sobre modelos de IA alojados localmente De novato a experto: Noticias animadas utilizando MQL5 (IV) Análisis de mercado sobre modelos de IA alojados localmente
En esta discusión, analizaremos cómo autoalojar modelos de IA de código abierto y utilizarlos para obtener información sobre el mercado. Esto forma parte de nuestro esfuerzo continuo por ampliar el News Headline EA, con la introducción de una franja «AI Insights» que lo convierte en una herramienta de asistencia con múltiples integraciones. La versión mejorada del Asesor Experto (EA) tiene como objetivo mantener informados a los operadores a través de eventos del calendario, noticias financieras de última hora, indicadores técnicos y, ahora, perspectivas de mercado generadas por IA, ofreciendo así un apoyo oportuno, variado e inteligente para la toma de decisiones de trading. Únete a la conversación mientras exploramos estrategias prácticas de integración y cómo MQL5 puede colaborar con recursos externos para crear un terminal de trabajo para trading potente e inteligente.
Introducción a MQL5 (Parte 18): Introducción al patrón de onda de Wolfe Introducción a MQL5 (Parte 18): Introducción al patrón de onda de Wolfe
En este artículo se explica en detalle el patrón de la onda de Wolfe, abordando tanto la variante bajista como la alcista. Además, desglosa paso a paso la lógica utilizada para identificar configuraciones válidas de compra y venta basadas en este patrón gráfico avanzado.
Redes neuronales en el trading: Segmentación periódica adaptativa (Final) Redes neuronales en el trading: Segmentación periódica adaptativa (Final)
Le propongo sumergirse en el apasionante mundo de LightGTS, un framework de predicción de series temporales ligero pero potente que combina la convolución adaptativa y la codificación RoPE con métodos de atención innovadores. En el artículo de hoy, encontrará una descripción detallada de todos los componentes, desde la creación de parches hasta una compleja combinación de asesores expertos en un decodificador, listo para su integración en proyectos MQL5. ¡Descubra cómo LightGTS lleva el trading automatizado al siguiente nivel!