Adwizard/Virtual/VirtualCloseManager.mqh

286 lines
22 KiB
MQL5

//+------------------------------------------------------------------+
//| VirtualCloseManager.mqh |
//| Copyright 2022-2025, Yuriy Bykov |
//| https://www.mql5.com/ru/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022-2025, Yuriy Bykov"
#property link "https://www.mql5.com/ru/users/antekov"
#property version "1.00"
class CVirtualAdvisor;
#include "../Database/Storage.mqh"
// Возможные состояния менеджера закрытия
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
};
//+------------------------------------------------------------------+
//| Класс менеджера закрытия (фиксации прибыли и убытков) |
//+------------------------------------------------------------------+
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
//+------------------------------------------------------------------+
//| Конструктор |
//+------------------------------------------------------------------+
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;
}
}
//+------------------------------------------------------------------+
//| Обработка тика в риск-менеджере |
//+------------------------------------------------------------------+
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();
}
}
//+------------------------------------------------------------------+
//| Информация о текущем состоянии |
//+------------------------------------------------------------------+
string CVirtualCloseManager::Text() {
string s = "=== Close Manager ===\n";
s += StringFormat("BL: %8.2f | Target: %8.2f (%8.2f)\n",
m_baseBalance,
m_baseBalance + ProfitMoney(),
m_baseBalance - LossMoney());
return s;
}
//+------------------------------------------------------------------+
//| Привязка эксперта к менеджеру закрытия |
//+------------------------------------------------------------------+
void CVirtualCloseManager::Expert(CVirtualAdvisor* p_expert) {
m_expert = p_expert;
}
//+------------------------------------------------------------------+
//| Сохранение состояния |
//+------------------------------------------------------------------+
bool CVirtualCloseManager::Save() {
CStorage::Set("CVirtualCloseManager::m_state", m_state);
CStorage::Set("CVirtualCloseManager::m_baseBalance", m_baseBalance);
return true;
}
//+------------------------------------------------------------------+
//| Загрузка состояния |
//+------------------------------------------------------------------+
bool CVirtualCloseManager::Load() {
bool res = true;
res &= CStorage::Get("CVirtualCloseManager::m_state", m_state);
res &= CStorage::Get("CVirtualCloseManager::m_baseBalance", m_baseBalance);
return res;
}
//+------------------------------------------------------------------+
//| Максимальный общий убыток |
//+------------------------------------------------------------------+
double CVirtualCloseManager::LossMoney() {
if(m_calcLossLimit == CM_CALC_LOSS_PERCENT_BB) {
// Для заданного процента от базового баланса вычисляем его
return m_baseBalance * m_maxLossLimit / 100;
} else {
// Для фиксированного значения просто возвращаем его
// CM_CALC_LOSS_MONEY_BB
return m_maxLossLimit;
}
}
//+------------------------------------------------------------------+
//| Максимальная общая прибыль |
//+------------------------------------------------------------------+
double CVirtualCloseManager::ProfitMoney() {
if(m_calcProfitLimit == CM_CALC_PROFIT_PERCENT_BB) {
// Для заданного процента от базового баланса вычисляем его
return m_baseBalance * m_maxProfitLimit / 100;
} else {
// Для фиксированного значения просто возвращаем его
// CM_CALC_PROFIT_MONEY_BB
return m_maxProfitLimit;
}
}
//+------------------------------------------------------------------+
//| Обновление текущих значений прибыли |
//+------------------------------------------------------------------+
void CVirtualCloseManager::UpdateProfit() {
// Текущие средства
m_equity = AccountInfoDouble(ACCOUNT_EQUITY);
// Текущий баланс
m_balance = AccountInfoDouble(ACCOUNT_BALANCE);
// Текущая плавающая прибыль
m_profit = m_equity - m_balance;
// Текущая общая прибыль относительно базового баланса
m_overallProfit = m_equity - m_baseBalance;
// Раз в час выводим значения в лог
if(IsNewBar(Symbol(), PERIOD_H1)) {
PrintFormat(__FUNCTION__" | Profit = %.2f | Overall = %.2f",
m_profit, m_overallProfit);
}
}
//+------------------------------------------------------------------+
//| Проверка лимитов убытка |
//+------------------------------------------------------------------+
void CVirtualCloseManager::CheckLimits() {
// Если достигнут общий убыток
if(m_overallProfit <= -LossMoney()) {
// Устанавливаем риск-менеджер в состояние достигнутого общего убытка
m_state = CM_STATE_LOSS;
PrintFormat(__FUNCTION__" | CLOSE LOSS Profit = %.2f | OverallProfit = %.2f (%.2f)",
m_profit, m_overallProfit, -LossMoney());
}
// Если достигнут общий убыток и позиции ещё открыты
else if(m_overallProfit >= ProfitMoney()) {
// Устанавливаем риск-менеджер в состояние достигнутой общей прибыли
m_state = CM_STATE_PROFIT;
PrintFormat(__FUNCTION__" | CLOSE PROFIT Profit = %.2f | OverallProfit = %.2f (%.2f)",
m_profit, m_overallProfit, ProfitMoney());
}
}
//+------------------------------------------------------------------+
//| Преобразование объекта в строку |
//+------------------------------------------------------------------+
string CVirtualCloseManager::operator~() {
return StringFormat("%s(%s)", typename(this), m_params);
}
//+------------------------------------------------------------------+