//+------------------------------------------------------------------+ //| Expert.mq5 | //| Copyright 2024, Yuriy Bykov | //| https://www.mql5.com/ru/users/antekov | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Yuriy Bykov" #property link "https://www.mql5.com/ru/articles/17608" #property version "1.26" #include "../Virtual/VirtualAdvisor.mqh" #include "../Utils/ExpertHistory.mqh" #include "../Utils/ConsoleDialog.mqh" // Если не задана константа с именем итогового советника, то #ifndef __NAME__ // Задаём её равной названию файла советника #define __NAME__ MQLInfoString(MQL_PROGRAM_NAME) //+------------------------------------------------------------------+ //| Функция формирования строки инициализации стратегии | //| из входных параметров по умолчанию (если не было задано имя). | //| Импортирует строку инициализации из базы данных советника | //| по идентификатору группы стратегий | //+------------------------------------------------------------------+ string GetStrategyParams() { // Берём строку инициализации из новой библиотеки для выбранной группы // (из базы данных эксперта) string strategiesParams = CVirtualAdvisor::Import( CVirtualAdvisor::FileName(__NAME__, magic_), groupId_ ); // Если группа стратегий из библиотеки не задана, то прерываем работу if(strategiesParams == NULL && useAutoUpdate_) { strategiesParams = ""; } return strategiesParams; } #endif // Если во внешнем файле, куда подключается этот файл, // есть константа __INPUT_PARAMS__, то входные параметры должны: // - быть полностью объявлены во внешнем файле // - повторять все параметры, которые перечислены ниже // Если константа __INPUT_PARAMS__ не объявлена, // то входные параметры берутся из блока ниже #ifndef __INPUT_PARAMS__ //+------------------------------------------------------------------+ //| Входные параметры | //+------------------------------------------------------------------+ 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; // - Значение общей прибыли для фиксации input group "::: Риск-менеджер" input bool rmIsActive_ = true; // - Активен? input double rmStartBaseBalance_ = 10000; // - Базовый баланс input ENUM_RM_CALC_DAILY_LOSS rmCalcDailyLossLimit_ = RM_CALC_DAILY_LOSS_MONEY_BB; // - Способ расчёта дневного убытка input double rmMaxDailyLossLimit_ = 500; // - Значение дневного убытка input double rmCloseDailyPart_ = 1.0; // - Значение пороговой части дневного убытка input ENUM_RM_CALC_OVERALL_LOSS rmCalcOverallLossLimit_ = RM_CALC_OVERALL_LOSS_MONEY_BB; // - Способ расчёта общего убытка input double rmMaxOverallLossLimit_ = 1000; // - Значение общего убытка input double rmCloseOverallPart_ = 1.0; // - Значение пороговой части общего убытка input ENUM_RM_CALC_OVERALL_PROFIT rmCalcOverallProfitLimit_ = RM_CALC_OVERALL_PROFIT_MONEY_BB; // - Способ расчёта общей прибыли input double rmMaxOverallProfitLimit_ = 1000000; // - Значение общей прибыли input int rmMaxOverallProfitDate_ = 0; // - Предельное время ожидания общей прибыли (дней) input double rmMaxRestoreTime_ = 0; // - Время ожидания лучшего входа на просадке input double rmLastVirtualProfitFactor_ = 1; // - Множитель начальной лучшей просадки input double slPercent_ = 0.03; // - Размер SL одной позиции в % от депозита input group "::: Прочие параметры" input ulong magic_ = 27183; // - Magic input bool useOnlyNewBars_ = true; // - Работать только на открытии бара input bool usePrevState_ = true; // - Загружать предыдущее состояние input string symbolsReplace_ = ""; // - Правила замены символов #endif CVirtualAdvisor *expert; // Объект эксперта CConsoleDialog *dialog; // Диалог для вывода текста с результатами //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Создаём и запускаем диалог для вывода результатов dialog = new CConsoleDialog(); dialog.Create(__NAME__ + " | " + (string) magic_); dialog.Run(); dialog.Text("Initialization..."); // Устанавливаем параметры в классе управления капиталом CMoney::DepoPart(expectedDrawdown_ / 10.0); CMoney::FixedBalance(fixedBalance_); CVirtualReceiver::s_slPercent = slPercent_; // Строка инициализации с наборами параметров стратегий string strategiesParams = NULL; // Берём строку инициализации из новой библиотеки для выбранной группы // (из базы данных эксперта) strategiesParams = GetStrategyParams(); // Если группа стратегий из библиотеки не задана, то прерываем работу if(strategiesParams == NULL) { return INIT_FAILED; } // Подготавливаем строку инициализации для эксперта с группой из нескольких стратегий string expertParams = StringFormat( "class CVirtualAdvisor(\n" " class CVirtualStrategyGroup(\n" " [\n" " %s\n" " ],%f\n" " ),\n" " class CVirtualRiskManager(\n" " %d,%.2f,%d,%.2f,%.2f,%d,%.2f,%.2f,%d,%.2f,%d,%.2f,%.2f" " ),\n" " class CVirtualCloseManager(\n" " %d,%.2f,%d,%.2f,%d,%.2f" " )\n" " ,%d,%s,%d\n" ")", strategiesParams, scale_, rmIsActive_, rmStartBaseBalance_, rmCalcDailyLossLimit_, rmMaxDailyLossLimit_, rmCloseDailyPart_, rmCalcOverallLossLimit_, rmMaxOverallLossLimit_, rmCloseOverallPart_, rmCalcOverallProfitLimit_, rmMaxOverallProfitLimit_, rmMaxOverallProfitDate_, rmMaxRestoreTime_, rmLastVirtualProfitFactor_, cmIsActive_, cmStartBaseBalance_, cmCalcLossLimit_, cmLossLimit_, cmCalcProfitLimit_, cmProfitLimit_, magic_, __NAME__, useOnlyNewBars_ ); PrintFormat(__FUNCTION__" | Expert Params:\n%s", expertParams); // Создаем эксперта, работающего с виртуальными позициями expert = NEW(expertParams); // Если эксперт не создан, то возвращаем ошибку if(!expert) return INIT_FAILED; // Если при замене символов возникла ошибка, то возвращаем ошибку if(!expert.SymbolsReplace(symbolsReplace_)) return INIT_FAILED; // Если требуется восстанавливать состояние, то if(usePrevState_) { // Загружаем прошлое состояние при наличии if(!expert.Load()) return INIT_FAILED; } expert.Tick(); dialog.Text(expert.Text()); // Успешная инициализация return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { expert.Tick(); // Если одновременно выполнено: if(groupId_ == 0 // - не задан конкретный идентификатор группы && useAutoUpdate_ // - разрешено автообновление && IsNewBar(Symbol(), PERIOD_D1) // - наступил новый день && expert.CheckUpdate() // - обнаружена новая группа стратегий ) { // Сохраняем текущее состояние эксперта expert.Save(); // Удаляем объект эксперта OnDeinit(REASON_RECOMPILE); // Вызываем функцию инициализации советника для загрузки новой группы стратегий OnInit(); } if (IsNewBar(Symbol(), PERIOD_M1) && !!dialog) { dialog.Text(expert.Text()); } } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { PrintFormat(__FUNCTION__" : Reason: %d", reason); if(!!expert) delete expert; if(!!dialog) { dialog.Destroy(reason); delete dialog; } } //+------------------------------------------------------------------+ //| Результат тестирования | //+------------------------------------------------------------------+ double OnTester(void) { CExpertHistory::Export(); return expert.Tester(); } //+------------------------------------------------------------------+ //| Обработка событий | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // event ID const long & lparam, // event parameter of the long type const double & dparam, // event parameter of the double type const string & sparam) { // event parameter of the string type if(!!dialog) { dialog.ChartEvent(id, lparam, dparam, sparam); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { CVirtualReceiver *r = CVirtualReceiver::Instance(); r.Changed(); //--- static int counter = 0; // счетчик вызовов OnTradeTransaction() static uint lasttime = 0; // время последнего вызова OnTradeTransaction() //--- uint time = GetTickCount(); //--- если последняя транзакция была больше 1 секунды назад if(time - lasttime > 1000) { counter = 0; // значит, это новая торговая операция и можно сбросить счетчик } lasttime = time; counter++; Print(counter, ". ", __FUNCTION__); //--- результат выполнения торгового запроса ulong lastOrderID = trans.order; ENUM_ORDER_TYPE lastOrderType = trans.order_type; ENUM_ORDER_STATE lastOrderState = trans.order_state; //--- имя символа, по которому произошла транзакция string trans_symbol = trans.symbol; //r[trans_symbol].Changed(); //--- тип транзакции ENUM_TRADE_TRANSACTION_TYPE trans_type = trans.type; switch(trans.type) { case TRADE_TRANSACTION_POSITION: { // изменение позиции ulong pos_ID = trans.position; PrintFormat("MqlTradeTransaction: Position #%I64u %s modified: SL=%.5f TP=%.5f Magic=%I64u", pos_ID, trans_symbol, trans.price_sl, trans.price_tp, request.magic); } break; case TRADE_TRANSACTION_REQUEST: // отправка торгового запроса PrintFormat("MqlTradeTransaction: TRADE_TRANSACTION_REQUEST"); break; case TRADE_TRANSACTION_DEAL_ADD: { // добавление сделки ulong lastDealID = trans.deal; ENUM_DEAL_TYPE lastDealType = trans.deal_type; double lastDealVolume = trans.volume; //--- идентификатор сделки во внешней системе - тикет, присваиваемый биржей string Exchange_ticket = ""; if(HistoryDealSelect(lastDealID)) Exchange_ticket = HistoryDealGetString(lastDealID, DEAL_EXTERNAL_ID); if(Exchange_ticket != "") Exchange_ticket = StringFormat("(Exchange deal=%s)", Exchange_ticket); PrintFormat("MqlTradeTransaction: %s deal #%I64u %s %s %.2f lot %s Magic=%I64u", EnumToString(trans_type), lastDealID, EnumToString(lastDealType), trans_symbol, lastDealVolume, Exchange_ticket, request.magic); } break; case TRADE_TRANSACTION_HISTORY_ADD: { // добавление ордера в историю //--- идентификатор ордера во внешней системе - тикет, присваиваемый биржей string Exchange_ticket = ""; if(lastOrderState == ORDER_STATE_FILLED) { if(HistoryOrderSelect(lastOrderID)) Exchange_ticket = HistoryOrderGetString(lastOrderID, ORDER_EXTERNAL_ID); if(Exchange_ticket != "") Exchange_ticket = StringFormat("(Exchange ticket=%s)", Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s %s", EnumToString(trans_type), lastOrderID, EnumToString(lastOrderType), trans_symbol, EnumToString(lastOrderState), Exchange_ticket); } break; default: { // прочие транзакции //--- идентификатор ордера во внешней системе - тикет, присваиваемый Московской биржей string Exchange_ticket = ""; if(lastOrderState == ORDER_STATE_PLACED) { if(OrderSelect(lastOrderID)) Exchange_ticket = OrderGetString(ORDER_EXTERNAL_ID); if(Exchange_ticket != "") Exchange_ticket = StringFormat("Exchange ticket=%s", Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s", EnumToString(trans_type), lastOrderID, EnumToString(lastOrderType), EnumToString(lastOrderState), Exchange_ticket); } break; } //--- тикет ордера ulong orderID_result = result.order; string retcode_result = GetRetcodeID(result.retcode); if(orderID_result != 0) PrintFormat("MqlTradeResult: order #%d retcode=%s ", orderID_result, retcode_result); //--- } //+------------------------------------------------------------------+ //| переводит числовые коды ответов в строковые мнемокоды | //+------------------------------------------------------------------+ string GetRetcodeID(int retcode) { switch(retcode) { case 10004: return("TRADE_RETCODE_REQUOTE"); break; case 10006: return("TRADE_RETCODE_REJECT"); break; case 10007: return("TRADE_RETCODE_CANCEL"); break; case 10008: return("TRADE_RETCODE_PLACED"); break; case 10009: return("TRADE_RETCODE_DONE"); break; case 10010: return("TRADE_RETCODE_DONE_PARTIAL"); break; case 10011: return("TRADE_RETCODE_ERROR"); break; case 10012: return("TRADE_RETCODE_TIMEOUT"); break; case 10013: return("TRADE_RETCODE_INVALID"); break; case 10014: return("TRADE_RETCODE_INVALID_VOLUME"); break; case 10015: return("TRADE_RETCODE_INVALID_PRICE"); break; case 10016: return("TRADE_RETCODE_INVALID_STOPS"); break; case 10017: return("TRADE_RETCODE_TRADE_DISABLED"); break; case 10018: return("TRADE_RETCODE_MARKET_CLOSED"); break; case 10019: return("TRADE_RETCODE_NO_MONEY"); break; case 10020: return("TRADE_RETCODE_PRICE_CHANGED"); break; case 10021: return("TRADE_RETCODE_PRICE_OFF"); break; case 10022: return("TRADE_RETCODE_INVALID_EXPIRATION"); break; case 10023: return("TRADE_RETCODE_ORDER_CHANGED"); break; case 10024: return("TRADE_RETCODE_TOO_MANY_REQUESTS"); break; case 10025: return("TRADE_RETCODE_NO_CHANGES"); break; case 10026: return("TRADE_RETCODE_SERVER_DISABLES_AT"); break; case 10027: return("TRADE_RETCODE_CLIENT_DISABLES_AT"); break; case 10028: return("TRADE_RETCODE_LOCKED"); break; case 10029: return("TRADE_RETCODE_FROZEN"); break; case 10030: return("TRADE_RETCODE_INVALID_FILL"); break; case 10031: return("TRADE_RETCODE_CONNECTION"); break; case 10032: return("TRADE_RETCODE_ONLY_REAL"); break; case 10033: return("TRADE_RETCODE_LIMIT_ORDERS"); break; case 10034: return("TRADE_RETCODE_LIMIT_VOLUME"); break; case 10035: return("TRADE_RETCODE_INVALID_ORDER"); break; case 10036: return("TRADE_RETCODE_POSITION_CLOSED"); break; default: return("TRADE_RETCODE_UNKNOWN=" + IntegerToString(retcode)); break; } //--- } //+------------------------------------------------------------------+