Оптимизация быстродеятельности эксперта. Подскажите, что можно усовершенствовать на рабочем примере.
- Создание готового советника - Мастер MQL4/MQL5
- Общие принципы - Торговые операции
- MetaTrader 5 для ваших менеджеров
Например, заголовок цикла
for(int i=0;i<OrdersTotal();i++)
Сколько итераций, столько и обращений к функции OrdersTotal, плюс ещё 1 обращение
int total=OrdersTotal(); for(int i=0;i<total;i++)
2. То же самое касается вызова функции Symbol(). Заведите глобальную строковую переменную и присваивайте ей значение при инициализации эксперта
string symbol; ... int init() { symbol=Symbol(); } ... if(OrderSymbol()!=symbol) continue; ...
Самая главная проблема - в функции start
Вот отработает удачно функция CheckForOpen() и значение, возвращаемое функцией OrdersTotal() станет не 0, а 1. Сразу же вызовутся CheckForCloseByTS и CheckForCloseByPips. А оно надо? Похоже, что нет, лишняя работа
Возможно это не совсем в тему про Ваш советник, но всё равно Вам может быть полезно. Вы наверное просто идёте по пути научного тыка, который формулируется следующим образом:"А давай как я возьму десяток параметров и найду в n-мерном пространстве глобальный максимум загнав тестер сразу на оптимизацию по всем параметрам с мелким шагом, чтобы чего не пропустить. Наверняка что-то получится?" Действительно что-то получится. Правда если Вам нужен только ТАКОЙ способ решения, то сразу скажу, что никакие двуядерные супер-пупергигагерцовые системы Вашу задачу не решат поскольку технические средства не могут соревноваться с геометрической прогрессией (и могу сказать для справки - и НИКОГДА не смогут!). И дело тут совершенно не в том во сколько раз MQL4 медленнее, чем C++. Даже если разработчикам МТ4 удастся ускорить работу тестера в 10 раз это Вам в лучшем случае сможет сэкономить лишь считанные проценты (и не более того!) времени до того самого момента когда Вы станете миллионером ;o)! Я же со своей стороны могу Вам лишь предложить то, чем сам пользуюсь постоянно:
Я например по своему опыту провожу оптимизацию каждого параметра по отдельности используя метод последовательных приближений (для информации один прогон в тестере моего эксперта занимает 3мин на P4 2,4ГГц – период М1(все тики) за 1,5 года). То есть делю диапазон значений например на 20 интервалов, далее в области максимума делю пару прилегающих к максимуму интервалов из предыдущей оптимизации ещё на 20 интервалов и делаю ещё один такой же шаг. Обычно 3-4 таких итерации оказывается вполне достаточно. Таким образом мы получаем в итоге результат по точности сопоставимый с прогоном сразу по 8000 интервалам с самым мелким шагом (20 в 3 степени). При этом мы имеем экономию в 8000-3*20=7940 итераций. То есть в идеале тратим время в 133 раза меньше, чем при тупом прогоне сразу на мелком шаге. При этом нужно отметить, что если у нас имеется ограниченное количество самого времени, за которое мы хотим получить результат (например мы можем подождать результат до вечера рабочего дня), то мы можем взять в 133 раза более длительный период для тестирования (например вместо 1 недели можно будет посчитать по 133 неделям) и в итоге мы будем иметь результат который будет хоть чего-то стоить, потратив на это тоже самое количество расчётного времени! А результаты быстрых прогонов численностью в 2,7 млн. штук, которых человек в состоянии дождаться, у меня вызывают серьёзные сомнения в плане применимости полученных результатов.
Конечно же мне можно возразить в плане того, что при просчёте всех мелких интервалов можно получить какой-то новый максимум, который будет упущен при расчёте по методике последовательных приближений, описанной выше. Но на это можно сказать, что в таком случае можно поймать лишь максимум, отражающий особенности выборки, и которых не будет в будущем! То есть при оптимизации должны выбираться параметры, изменение которых в небольших пределах, вызывает небольшое изменение в конечном результате. Ну а если у вас на мелком шаге окажется супер-пупер максимум, который мы можем не отследить на более крупном шаге, то можно быть точно уверенным, что данный максимум отражает ТОЛЬКО особенности конкретной выборки и его лучше не принимать во внимание вообще.
Запустил представленного эксперта как есть на оптимизацию (чтобы вывод в логи не мешался).
36 прогонов за 4 минуты 2 секунды. На 1 прогон - 6.7 секунды
Устранил по собственному совету все лишние вызовы функций.
Те же 36 прогонов за 3 минуты 7 секунд. 1 прогон 5.2 секунды
Дополнительно вставил элементарнейшую проверку на наличие денег
2 минуты 55 секунд или 4.9 секунды на прогон
А вы говорите, что это даст.
Просто не вызывается функция CheckForOpen
код пожалуйста:
extern double SL = 58; extern double TP = 310; extern double MA_Type = 2; extern double fast = 3; extern double slow = 23; extern double shiftfast = 0; extern double shiftslow = 0; extern double hour_work_from = 11; extern double hour_work_to = 15; extern double Lots = 1; extern double TS_enabled = 0; extern double TS_PipsZeroStop = 40; extern double TS_PipsToMoveStop = 110; extern double TS_PipsStop = 80; extern double StopsEnabled = 2; extern double Target1 = 180; extern double Stop1 = 140; extern double Target2 = 300; extern double Stop2 = 245; extern double Target3 = 80; extern double Stop3 = 60; double fast2 = 0; double fast1 = 0; double slow2 = 0; double slow1 = 0; double TS_value = 0; int i = 0; string symbol; int init() { symbol=Symbol(); return(0); } int deinit() { return(0); } void CheckForOpen() { fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast); fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast); slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast); slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast); if ((fast1>slow1) && (fast2<slow2)) { OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); } else { if ((fast1<slow1) && (fast2>slow2)) OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green); } } void CheckForCloseByTS() { int total=OrdersTotal(); for(int i=0;i<total;i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderSymbol()!=symbol) continue; int type=OrderType(); if(type==OP_BUY) { if ((Bid-OrderOpenPrice()) > (TS_PipsZeroStop*Point)) { TS_value = 0; if (OrderStopLoss() < OrderOpenPrice()) { TS_value=OrderOpenPrice(); } else { if ((Bid - OrderStopLoss()) > (TS_PipsToMoveStop*Point)) { TS_value = OrderStopLoss() + (TS_PipsStop*Point); } } if (TS_value > 0) { OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Blue); } } break; } if(type==OP_SELL) { if ((OrderOpenPrice()-Ask) > (TS_PipsZeroStop*Point)) { TS_value = 0; if (OrderStopLoss() > OrderOpenPrice()) { TS_value=OrderOpenPrice(); } else { if ((OrderStopLoss()-Ask) > (TS_PipsToMoveStop*Point)) { TS_value = OrderStopLoss() - TS_PipsStop*Point; } } if (TS_value > 0) { OrderModify(OrderTicket(),OrderOpenPrice(), TS_value, OrderTakeProfit(),0,Green); } } break; } } } void CheckForCloseByPips() { int total=OrdersTotal(); for(int i=0;i<total;i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break; if(OrderSymbol()!=symbol) continue; int type=OrderType(); if(type==OP_BUY) { if ((StopsEnabled > 0) && ((Bid-OrderOpenPrice()) > (Target1*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop1*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop1*Point), OrderTakeProfit(),0,Blue); } if ((StopsEnabled > 1) && ((Bid-OrderOpenPrice()) > (Target2*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop2*Point), OrderTakeProfit(),0,Blue); } if ((StopsEnabled > 2) && ((Bid-OrderOpenPrice()) > (Target3*Point)) && ((OrderStopLoss() - OrderOpenPrice()) < (Stop3*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()+(Stop3*Point), OrderTakeProfit(),0,Blue); } break; } if(type==OP_SELL) { if ((StopsEnabled > 0) && ((OrderOpenPrice()-Ask) > (Target1*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop1*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop1*Point), OrderTakeProfit(),0,Blue); } if ((StopsEnabled > 1) && ((OrderOpenPrice()-Ask) > (Target2*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop2*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop2*Point), OrderTakeProfit(),0,Blue); } if ((StopsEnabled > 2) && ((OrderOpenPrice()-Ask) > (Target3*Point)) && ((OrderOpenPrice() - OrderStopLoss()) < (Stop3*Point))) { OrderModify(OrderTicket(),OrderOpenPrice(), OrderOpenPrice()-(Stop3*Point), OrderTakeProfit(),0,Blue); } break; } } } int start() { static bool money=true; if (OrdersTotal() < 1) { int hour=Hour(); if ((hour >= hour_work_from) && (hour < hour_work_to) && money) { CheckForOpen(); if(GetLastError()==134) money=false; } } else { if (TS_enabled > 0) CheckForCloseByTS(); if (StopsEnabled > 0) CheckForCloseByPips(); } return(0); } //+------------------------------------------------------------------+
ИМХО : код еще можно намного ускорить - сама функция
void CheckForOpen() { fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast); fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast); slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast); slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast); if ((fast1>slow1) && (fast2<slow2)) { OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); } else { if ((fast1<slow1) && (fast2>slow2)) OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green); } }
топ "тормозного искусства" :). Незачем на каждом тике пересчитывать значения мувингов на предыдущих барах, а это происходит, если нет открытых ордеров. Код лень писать - ИМХО - и так очевидно: эти значения не изменяются - считаем один раз, заносим в массив и используем. Причем, пересчет нужен только по последнему бару, один раз на момент открытия этого бара, все остальные элементы массива просто смещаем на один.
Удачи и попутных трендов.
ИМХО : код еще можно намного ускорить - сама функция
void CheckForOpen() { fast1=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,1+shiftfast); fast2=iMA(NULL,0,fast,0,MA_Type,PRICE_CLOSE,2+shiftfast); slow1=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,1+shiftfast); slow2=iMA(NULL,0,slow,0,MA_Type,PRICE_CLOSE,2+shiftfast); if ((fast1>slow1) && (fast2<slow2)) { OrderSend(symbol,OP_BUY,Lots,Ask,3,Ask-SL*Point,Ask+TP*Point,"",0,0,Green); } else { if ((fast1<slow1) && (fast2>slow2)) OrderSend(symbol,OP_SELL,Lots,Bid,3,Bid+SL*Point,Bid-TP*Point,"",0,0,Green); } }
топ "тормозного искусства" :). Незачем на каждом тике пересчитывать значения мувингов на предыдущих барах, а это происходит, если нет открытых ордеров. Код лень писать - ИМХО - и так очевидно: эти значения не изменяются - считаем один раз, заносим в массив и используем. Причем, пересчет нужен только по последнему бару, один раз на момент открытия этого бара, все остальные элементы массива просто смещаем на один.
Удачи и попутных трендов.
Так это и есть Ваш эксперт. Я в нём сделал только те изменения, о которых говорил раньше. Из самодеятельности я просто добавил один break (похоже, Вы его просто забыли, в функции CheckForCloseByTS первый break).
Добавил переменные symbol, total, type, money. Поиском по тексту эти переменные ищите.
Переписал функцию start - при визуальном сравнении Вашего и моего текста сразу увидите.
Возможна проблема с тем, что открывающие фигурные скобки я ставил в новой строке.
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования