448 lines
20 KiB
MQL5
448 lines
20 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
//---
|
|
}
|
|
//+------------------------------------------------------------------+
|