//+------------------------------------------------------------------+ //| e-Trailing.mq4 | //| Ким Игорь В. aka KimIV | //| http://www.kimiv.ru | //| | //| 12.09.2005 Автоматический Trailing Stop всех открытых позиций | //| Вешать только на один график | //+------------------------------------------------------------------+ #property copyright "Ким Игорь В. aka KimIV" #property link "http://www.kimiv.ru" //------- Внешние параметры ------------------------------------------ extern bool ProfitTrailing = True; // Тралить только профит extern int TrailingStop = 8; // Фиксированный размер трала extern int TrailingStep = 2; // Шаг трала extern bool UseSound = True; // Использовать звуковой сигнал extern string NameFileSound = "expert.wav"; // Наименование звукового файла //+------------------------------------------------------------------+ //| expert start function | //+------------------------------------------------------------------+ void start() { for (int i=0; i<OrdersTotal(); i++) { if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { TrailingPositions(); } } } //+------------------------------------------------------------------+ //| Сопровождение позиции простым тралом | //+------------------------------------------------------------------+ void TrailingPositions() { double pBid, pAsk, pp; pp = MarketInfo(OrderSymbol(), MODE_POINT); if (OrderType()==OP_BUY) { pBid = MarketInfo(OrderSymbol(), MODE_BID); if (!ProfitTrailing || (Bid-OrderOpenPrice())>TrailingStop*pp) { if (OrderStopLoss()<Bid-(TrailingStop+TrailingStep-1)*pp) { ModifyStopLoss(Bid-TrailingStop*pp); return; } } } if (OrderType()==OP_SELL) { pAsk = MarketInfo(OrderSymbol(), MODE_ASK); if (!ProfitTrailing || OrderOpenPrice()-Ask>TrailingStop*pp) { if (OrderStopLoss()>Ask+(TrailingStop+TrailingStep-1)*pp || OrderStopLoss()==0) { ModifyStopLoss(Ask+TrailingStop*pp); return; } } } } //+------------------------------------------------------------------+ //| Перенос уровня StopLoss | //| Параметры: | //| ldStopLoss - уровень StopLoss | //+------------------------------------------------------------------+ void ModifyStopLoss(double ldStopLoss) { bool fm; fm=OrderModify(OrderTicket(),OrderOpenPrice(),ldStopLoss,OrderTakeProfit(),0,CLR_NONE); if (fm && UseSound) PlaySound(NameFileSound); } //+------------------------------------------------------------------+
Я в своем эксперте для Чемпионата использовал Трал от Игоря
Кима, только везде (где только можно) добавил NormalizeDouble(), никаких
ошибок нет
Содержание
- Ошибка OrderModify ERROR 1 при изменения уровня стопа функцией трейлингстопа! Как исправить? — страница 3
- Ошибка OrderModify ERROR 1 при изменения уровня стопа функцией трейлингстопа! Как исправить? — страница 2
- Sashken! Тысяча спасибо Вам и Игорю Ким создавшему столь замечательный советник-трал!
- Modify error 1
- Ошибка #1 при модификации ордеров
- Ошибка модификации ордера(error 1)
Ошибка OrderModify ERROR 1 при изменения уровня стопа функцией трейлингстопа! Как исправить? — страница 3
rsi :
Рецепт простой: вместо
всё равно выдаёт ошибку 1
получается что if(.. || .. || ..) не срабатывает
и такое if((<> || <> )|| <>) тоже не срабатывает
всё равно выдаёт ошибку 1
получается что if(.. || .. || ..) не срабатывает
и такое if((<> || <> )|| <>) тоже не срабатывает
Не стОит проверять абсолютное равенсnво или неравенство чисел типа double . Поскольку мы имеем дело с ценами, которые имеют шаг Point, то, мы можем считать, что две цены равны, если абсолютная разность их значений меньше, скажем, чем 0.5*Point. Поэтому, оператор
правильно записать, как
Где переменную lvl следует определить выше, как, например:
что-то зациклились на этой ошибке..
преодолеть данную ошибку может только здравый смысл..
никакие проверки не помогут избежать ее, по той простой причине, что ошибка возникает, если изменение уровней стопов происходит слишком часто, т.е.
изменение цены после последнего изменения уровней стопов незначительно — может быть преодалено одним-двумя тиками..
суть в том, что даже если новые уровни стопов корректны и требуют изменения (по условию трала), то цена может дернуться в сторону уже имеющегося
уровня стопа и изменение будет не по той цене (расчетной на момент изменения), а по цене с учетом реального движения цен..
изменившись в соответствии с реалиями, уровни стопа по факту могут быть стать теми же, что есть на момент изменения уровня и вуаля ошибка 1
Это исправление меняет логическую основу советника, то есть, если раньше на ура Ваш советник брал огромную прибыль и не обращал внимание на этот (своего рода), пустяк с сервером, то когда становится все правильно — прибыль исчезает и приходится его дорабатывать, или отказываться от такого рода затеи. Так что этот атрибут надо исправлять сразу и уже только потом делать какие-либо расчеты из этого, то есть, этой ошибки не должно быть в расчетах. Так что, все что не нужно: нормализуем и умножаем на ноль и скорей всего(это я еще не проверял, но предполагаю), то что не надо менять — умножать на единицу.
И я вот, последний раз очень часто менял тайк-профит и все получалось. Так что от частоты не зависит правильность.
Источник
Ошибка OrderModify ERROR 1 при изменения уровня стопа функцией трейлингстопа! Как исправить? — страница 2
Я в своем эксперте для Чемпионата использовал Трал от Игоря Кима, только везде (где только можно) добавил NormalizeDouble(), никаких ошибок нет
Sashken! Тысяча спасибо Вам и Игорю Ким создавшему столь замечательный советник-трал!
Читаем HELP.
Что значит ошибка с номером 1 при модификации ордера? Читаем:
ERR_NO_RESULT — OrderModify пытается изменить уже установленные значения такими же значениями. Необходимо изменить одно или несколько значений и повторить попытку.
Т.е. при «трале» Вы пытаетесь менять уровень стопа сам на себя. Откуда такое может быть? Из операций предварительного сравнения цен перед проведением модификации ордера!
Вы сравниваете ненормализованные цены и получаете результат — цены неравны. Пытаетесь произвести модификацию (при ней цены нормализуются принудительно) и получаете ответ что новая цена уже и так равна старой — менять нечего.
Отсюда вывод — всегда нормализуйте цены в операциях сравнения.
Точно:) Без нормализации вышеуказанный вариант тоже выдавал эту ошибку.
Читаем HELP.
Что значит ошибка с номером 1 при модификации ордера? Читаем:
ERR_NO_RESULT — OrderModify пытается изменить уже
установленные значения такими же значениями. Необходимо изменить одно или
несколько значений и повторить попытку.
Т.е. при «трале» Вы пытаетесь менять уровень стопа сам на себя. Откуда такое может быть? Из операций предварительного сравнения цен перед проведением модификации ордера!
Вы сравниваете ненормализованные цены и получаете результат — цены неравны. Пытаетесь произвести модификацию (при ней цены нормализуются принудительно) и получаете ответ что новая цена уже и так равна старой — менять нечего.
Отсюда вывод — всегда нормализуйте цены в операциях сравнения.
Читаем HELP.
Что значит ошибка с номером 1 при модификации ордера? Читаем:
ERR_NO_RESULT — OrderModify пытается изменить уже установленные значения такими же значениями. Необходимо изменить одно или несколько значений и повторить попытку.
Т.е. при «трале» Вы пытаетесь менять уровень стопа сам на себя. Откуда такое может быть? Из операций предварительного сравнения цен перед проведением модификации ордера!
Вы сравниваете ненормализованные цены и получаете результат — цены неравны. Пытаетесь произвести модификацию (при ней цены нормализуются принудительно) и получаете ответ что новая цена уже и так равна старой — менять нечего.
Отсюда вывод — всегда нормализуйте цены в операциях сравнения.
Источник
Modify error 1
Продублирую здесь. А то в форум новичков, похоже, профи не заглядывают.
Помогите с тралом плиииз.
Выдает OrderModify error 1 . Две первые сделки модифицирует, третья остается без стопа и сливает депо. Сделки все Buy
Добавлю: вторую сделку тралит правильно, но в какой-то момент OrderModify error 1 очень много раз потом Stack overflow. Видимо поэтому 3 сделка остается без стопа.
Здесь видны две принципиальные ошибки. Одна связана с сравнением на тождество двух вещественных чиел, что неправильно. Это операторы
Правильно проверять так: два числа типа double не равны, если абсолютное значение их разности больше некоторого маленького числа. В вашем случае годится 0.5*Point.
Вторая ошибка связана с использованием ненормализованного числа в операторе OrderModify (). Правильно писать
Если для вас критичны другие мелкие неточности, то стОит пересмотреть сравнения
с учетом приведенного выше предложения по сравнению двух чисел типа double .
Продублирую здесь. А то в форум новичков, похоже, профи не заглядывают.
Помогите с тралом плиииз.
Выдает OrderModify error 1 . Две первые сделки модифицирует, третья остается без стопа и сливает депо. Сделки все Buy
Добавлю: вторую сделку тралит правильно, но в какой-то момент OrderModify error 1 очень много раз потом Stack overflow. Видимо поэтому 3 сделка остается без стопа.
FxRoller :
А самое главное — не нужно осуществлять вызов функции из этой же самой функции. Возникает цикличность, которая и дает Stack overflow.
Если попытка модификации не удалась, не хотелось бы остаться без стопа.
вызов функции из этой же самой функции, по идее не должен переполнять стек, ведь там явная проверка, вызов только в том случае если модификация не удалась.
не может же быть такого большого количества реквотов и т.п.
Если не рекурсия, то как это сделать?
Здесь видны две принципиальные ошибки. Одна связана с сравнением на тождество двух вещественных чиел, что неправильно. Это операторы
Правильно проверять так: два числа типа double не равны, если абсолютное значение их разности больше некоторого маленького числа. В вашем случае годится 0.5*Point.
Вторая ошибка связана с использованием ненормализованного числа в операторе OrderModify (). Правильно писать
Если для вас критичны другие мелкие неточности, то стОит пересмотреть сравнения
с учетом приведенного выше предложения по сравнению двух чисел типа double .
Источник
Ошибка #1 при модификации ордеров
Как убрать эту долбаную ошибку? Она меня уже достала. Проверяю каждый вводный параметр функции. У меня, в данный момент, это так:
Т.е. если параметр не изменён, то изменять его ненужно. Раньше я так не делал, но решил уж.
Дальше у меня параметры передаются в метода модификации ордера так:
Нв выходе в журнале:
Получается, журнал уведомил, типа модификация: ОК, а дальше. ошибка. Ни один параметр при этом не изменился.
Ордер отложенный. Так что менять можно цену открытия , стоп и тейк. И это, как я понимаю, не противоречил документации.
Кто сталкивался с подобным? Как решать?
Как убрать эту долбаную ошибку? Она меня уже достала. Проверяю каждый вводный параметр функции. У меня, в данный момент, это так:
Т.е. если параметр не изменён, то изменять его ненужно. Раньше я так не делал, но решил уж.
Дальше у меня параметры передаются в метода модификации ордера так:
Нв выходе в журнале:
Получается, журнал уведомил, типа модификация: ОК, а дальше. ошибка. Ни один параметр при этом не изменился.
Ордер отложенный. Так что менять можно цену открытия, стоп и тейк. И это, как я понимаю, не противоречил документации.
Кто сталкивался с подобным? Как решать?
Вы неправильно работаете с кодом ошибки, забывая, что код ошибки после ваших многочисленных функций уже переопределен.
Поэтому при возникновении ошибки ее код надо сохранить в локальную переменную и уже потом оттуда, после массы ваших последующих вызовов, распечатать.
Вы неправильно работаете с кодом ошибки, забывая, что код ошибки после ваших многочисленных функций уже переопределен.
Поэтому при возникновении ошибки ее код надо сохранить в локальную переменную и уже потом оттуда, после массы ваших последующих вызовов, распечатать.
А может ещё и массив ошибок завести? Ведь в документации чёрным по белому написано, что переменная _LastError хранит номер последней ошибки. А сброс её производится функцией ResetLastError(). Если между вызовами _LastError не было ошибок больше, то _LastError хранит значение последней ошибки . У меня по ходу кода больше не было ошибок. А значится, вопрос не в этом.
Тем более, заводит переменные для ошибок в каждом методе и потом принтовать эти переменные в экспертах не очень то и логично.
Ведь если есть переменная для сбора ошибок, то зачем их плодить?
Источник
Ошибка модификации ордера(error 1)
Вроде все по уроку делал, может что пропустил?
//| Copyright 2016, MetaQuotes Software Corp. |
#property copyright «Copyright 2016, MetaQuotes Software Corp.»
#property link «https://www.mql5.com»
#property version «1.00»
extern double Lots = 0.1;
extern int TakeProfit = 30;
extern int Step = 50;
extern double Multipler = 2;
extern int Magic = 123;
//extern int tframe = 60;
extern int period = 14;
extern int Slippage = 3;
double tp, price, lastlot;
//| Expert initialization function |
if (Digits == 3 || Digits == 5)
//| Expert deinitialization function |
void OnDeinit(const int reason)
//| Expert tick function |
double ima = iMA(Symbol(), 0, period, 0, MODE_SMA, PRICE_CLOSE, 0);
if (CountTrades() == 0)
tp = NormalizeDouble(Ask + TakeProfit*Point, Digits);
ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, tp, «», Magic, 0, Blue);
tp = NormalizeDouble(Bid — TakeProfit*Point, Digits);
ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, tp, «», Magic, 0, Red);
int order_type = FindLastOrderType();
if(Ask = price + Step*Point)
lastlot = NormalizeDouble(lastlot * Multipler, 2);
ticket = OrderSend(Symbol(), OP_SELL, lastlot, Bid, Slippage, 0, 0, «», Magic, 0, Red);
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == otype)
price += OrderOpenPrice() * OrderLots();
avg_price = NormalizeDouble(price / order_lots, Digits);
if (otype == OP_BUY) tp = NormalizeDouble(avg_price + TakeProfit*Point, Digits);
if (otype == OP_SELL) tp = NormalizeDouble(avg_price — TakeProfit*Point, Digits);
for (int i = OrdersTotal()-1; i>=0; i—)
if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == otype)
if(OrderModify(OrderTicket(), OrderOpenPrice(), 0, tp, 0))
Print(«Ордера успешно модифицированны»);
else Print(«Ошибка модификации ордеров»);
double FindLastLots(int otype)
for(int cnt = OrdersTotal()-1; cnt>=0; cnt—)
if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES))
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == otype)
if (oldticket > ticket)
for(int i=OrdersTotal()-1; i>=0;i—)
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
if (OrderType() == OP_BUY || OrderType() == OP_SELL)
for(int i = OrdersTotal()-1; i>=0; i—)
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
double FindLastOrderPrice(int otype)
for(int cnt = OrdersTotal()-1; cnt>=0; cnt—)
if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES))
if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && OrderType() == otype)
Источник
Разработка торговых экспертов на языке MQL4 является не такой уж простой задачей. Во-первых – алгоритмизация любой сложной торговой системы уже представляет собой проблему, так как нужно учесть очень много деталей, начиная с особенностей ТС и заканчивая спецификой среды MetaTrader 4. Во-вторых, даже наличие детальнейшего алгоритма не избавляет от сложностей, возникающих при переносе разработанного алгоритма на язык программирования MQL4.
Компилятор оказывает некоторую помощь при написании корректных экспертов. После начала компиляции MetaEditor сообщит обо всех синтаксических ошибках в вашем коде. Но, к сожалению, помимо синтаксических ошибок ваш советник может содержать еще и логические ошибки, которые компилятор выловить не может. Поэтому этим нам придется заняться самим. Как это сделать – в нашем сегодняшнем материале.
MQL4 — Ошибки и как их исправить
Самые распространенные ошибки компиляции
При наличии ошибок в коде программа не может быть скомпилирована. Для полного контроля всех ошибок рекомендуется использовать строгий режим компиляции, который устанавливается директивой:
Этот режим значительно упрощает поиск ошибок. Теперь перейдем к самым распространенным ошибкам при компиляции.
Идентификатор совпадает с зарезервированным словом
Если наименование переменной или функции совпадает с одним из зарезервированных слов:
int char[]; // неправильно int char1[]; // правильно int char() // неправильно { return(0); }
то компилятор выводит сообщения об ошибках:
Для исправления данной ошибки нужно исправить имя переменной или функции. Я рекомендую придерживаться следующей системы для именования:
Все функции должны обозначать действие. То есть это должен быть глагол. Например, OpenLongPosition() или ModifyStopLoss(). Ведь функции всегда именно что-то делают, верно?
Кроме того, функции желательно называть в так называемом CamelCase стиле. А переменные в cebab_case стиле. Это общепринятая практика.
Кстати, об именах переменных. Переменные – это существительные. Например, my_stop_loss, day_of_week, current_month. Не так страшно назвать переменную длинным именем, гораздо страшнее назвать ее непонятно. Что такое dow, индекс Dow Jones? Нет, это, оказывается, день недели. Конечно, сегодня вам и так понятно, что это за переменная. Но когда вы откроете код советника месяц спустя, все будет уже не так явно. А это время, упущенное на расшифровку посланий из прошлого – оно вам надо?
Специальные символы в наименованиях переменных и функций
Идем дальше. Если наименования переменных или функций содержат специальные символы ($, @, точка):
int $var1; // неправильно int @var2; // неправильно int var.3; // неправильно void f@() // неправильно { return; }
то компилятор выводит сообщения об ошибках:
Для исправления данной ошибки снова нужно скорректировать имена переменных или функций, ну или сразу называть их по-человечески. В идеале код нужно писать так, чтобы даже человек, не знающий программирование, просто его прочел и понял, что там вообще происходит.
Ошибки использования оператора switch
Старая версия компилятора позволяла использовать любые значения в выражениях и константах оператора switch:
void start() { double n=3.14; switch(n) { case 3.14: Print("Pi"); break; case 2.7: Print("E"); break; } }
В новом компиляторе выражения и константы оператора switch должны быть целыми числами, поэтому при использовании подобных конструкций возникают ошибки:
Поэтому, когда вы разбираете код классики, такой, как WallStreet, Ilan и прочей нетленки (что очень полезно для саморазвития), можно натолкнуться на эту ошибку. Лечится она очень просто, например, при использовании такой вот строки:
switch(MathMod(day_48, 10))
Вот так можно запросто решить проблему:
switch((int)MathMod(day_48, 10))
Возвращаемые значений функций
Все функции, кроме void, должны возвращать значение объявленного типа. Например:
При строгом режиме компиляции (strict) возникает ошибка:
В режиме компиляции по умолчанию компилятор выводит предупреждение:
Если возвращаемое значение функции не соответствует объявлению:
Тогда при строгом режиме компиляции возникает ошибка:
В режиме компиляции по умолчанию компилятор выводит предупреждение:
Для исправления таких ошибок в код функции всего-навсего нужно добавить оператор возврата return c возвращаемым значением соответствующего типа.
Массивы в аргументах функций
Массивы в аргументах функций передаются только по ссылке. Раньше это было не так, поэтому в старых советниках можно встретить эту ошибку. Вот пример:
double ArrayAverage(double a[]) { return(0); }
Данный код при строгом режиме компиляции (strict) приведет к ошибке:
В режиме компиляции по умолчанию компилятор выводит предупреждение:
Для исправления таких ошибок нужно явно указать передачу массива по ссылке, добавив префикс & перед именем массива:
double ArrayAverage(double &a[]) { return(0); }
Кстати, константные массивы (Time[], Open[], High[], Low[], Close[], Volume[]) не могут быть переданы по ссылке. Например, вызов:
вне зависимости от режима компиляции приводит к ошибке:
Для устранения подобных ошибок нужно скопировать необходимые данные из константного массива:
//--- массив для хранения значений цен открытия double OpenPrices[]; //--- копируем значения цен открытия в массив OpenPrices[] ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY); //--- вызываем функцию ArrayAverage(OpenPrices);
Одна из самых распространенных ошибок – потеря советником индикатора. В таких случаях обычно пользователи эксперта на форумах гневно пишут: «Советник не работает!» или «Ставлю советник на график и ничего не происходит!». Решение этого вопроса на самом деле очень простое. Как всегда, достаточно просто заглянуть на вкладку «Журнал» терминала и обнаружить там запись вроде:
2018.07.08 09:15:44.957 2016.01.04 00:51 cannot open file 'C:Users1AppDataRoamingMetaQuotesTerminal MQL4indicatorsKELTNER_F12.ex4' [2]
Говорит это нам о том, что индикатор в папку положить забыли, или же он назван по-другому. Если индикатор отсутствует, нужно добавить его в папку с индикаторами. Если он есть, стоит проверить его название в коде советника – скорее всего там он называется по-другому.
Предупреждения компилятора
Предупреждения компилятора носят информационный характер и не являются сообщениями об ошибках, однако они указывают на возможные источники ошибок и лучше их скорректировать. Чистый код не должен содержать предупреждений.
Пересечения имен глобальных и локальных переменных
Если на глобальном и локальном уровнях присутствуют переменные с одинаковыми именами:
int i; // глобальная переменная void OnStart() { int i=0,j=0; // локальные переменные for (i=0; i<5; i++) { j+=i; } PrintFormat("i=%d, j=%d",i,j); }
то компилятор выводит предупреждение и укажет номер строки, на которой объявлена глобальная переменная:
Для исправления таких предупреждений нужно скорректировать имена глобальных переменных.
Несоответствие типов
В следующем примере:
#property strict void OnStart() { double a=7; float b=a; int c=b; string str=c; Print(c); }
при строгом режиме компиляции при несоответствии типов компилятор выводит предупреждения:
В данном примере компилятор предупреждает о возможной потере точности при присвоении различных типов данных и неявном преобразовании типа int в string.
Для исправления нужно использовать явное приведение типов:
#property strict void OnStart() { double a=7; float b=(float)a; int c=(int)b; string str=(string)c; Print(c); }
Неиспользуемые переменные
Наличие переменных, которые не используются в коде программы (лишние сущности) не является хорошим тоном.
void OnStart() { int i,j=10,k,l,m,n2=1; for(i=0; i<5; i++) {j+=i;} }
Сообщения о таких переменных выводятся вне зависимости от режима компиляции:
Для исправления нужно просто убрать неиспользуемые переменные из кода программы.
Диагностика ошибок при компиляции
Часто после написания программы возникают проблемы при компиляции, вызванные ошибками в коде. Это могут быть самые различные ошибки, но в любом случае возникает необходимость оперативного обнаружения участка кода, где допущена ошибка.
Нередко у людей уходит немало времени и масса нервов на поиски какой-нибудь лишней скобки. Однако, есть способ быстрого обнаружения ошибок, который основан на использовании комментирования.
Написать достаточно большой код без единой ошибки – очень приятно. Но, к сожалению, так получается не часто. Я не рассматриваю здесь ошибки, которые приводят к неверному исполнению кода. Здесь пойдёт речь об ошибках, из-за которых становится невозможной компиляция.
Весьма распространённые ошибки – вставка лишней скобки в сложном условии, нехватка скобки, не выставление двоеточия, запятой при объявлении переменных, опечатка в названии переменной и так далее. Часто при компиляции можно сразу увидеть, в какой строке допущена подобная ошибка. Но бывают и случаи, когда найти такую ошибку не так просто. Ни компилятор, ни зоркий глаз нам не могут помочь сразу найти ошибку. В таких случаях, как правило, начинающие программисты начинают “обходить” весь код, пытаясь визуально определить ошибку. И снова, и снова, пока выдерживают нервы.
Однако MQL, как и другие языки программирования, предлагает отличный инструмент – комментирование. Используя его, можно отключать какие-то участки кода. Обычно комментирование используют именно для вставки каких-то комментариев, или же отключения неиспользуемых участков кода. Комментирование можно также успешно применять и при поиске ошибок.
Поиск ошибок обычно сводится к определению участка кода, где допущена ошибка, а затем, в этом участке, визуально находится ошибка. Думаю, вряд ли кто-то будет сомневаться в том, что исследовать “на глаз” 5-10 строчек кода проще и быстрей, чем 100-500, а то и несколько тысяч.
При использовании комментирования задача предельно проста. Сначала нужно закомментировать различные участки кода (иногда чуть ли не весь код), тем самым “отключив” его. Затем, по очереди, комментирование снимается с этих участков кода. После очередного снятия комментирования совершается попытка компиляции. Если компиляция прошла успешно – ошибка не в этом участке кода. Затем открывается следующий участок кода и так далее. Когда находится проблемный участок кода, визуально ищется ошибка, затем устраняется. Опять происходит попытка компиляции. Если всё прошло успешно, ошибка устранена.
Важно правильно определять участки кода, которые необходимо комментировать. Если это условие (или иная логическая конструкция), то оно должно комментироваться полностью. Если комментируется участок кода, где объявляются переменные, важно, чтобы не был открыт участок, где происходит обращение к этим переменным. Иначе говоря – комментирование должно применяться по логике программирования. Несоблюдения такого подхода приводит к возникновению новых, вводящих в заблуждение, ошибок при компиляции.
Вот отличный пример ошибки, когда неясно, где ее искать и нас может выручить комментирование кода.
Ошибки времени выполнения
Ошибки, возникающие в процессе исполнения кода программы, принято называть ошибками времени выполнения (runtime errors). Такие ошибки обычно зависят от состояния программы и связаны с некорректными значениями переменных.
Например, если переменная используется в качестве индекса элементов массива, то ее отрицательные значения неизбежно приведут к выходу за пределы массива.
Выход за пределы массива (Array out of range)
Эта ошибка часто возникает в индикаторах при обращении к индикаторным буферам. Функция IndicatorCounted() возвращает количество баров, не изменившихся после последнего вызова индикатора. Значения индикаторов на уже рассчитанных ранее барах не нуждаются в пересчете, поэтому для ускорения расчетов достаточно обрабатывать только несколько последних баров.
Большинство индикаторов, в которых используется данный способ оптимизации вычислений, имеют такой вид:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { //--- иногда для расчета требуется не менее N баров (например, 100) // если на графике нет такого количества баров (например, на таймфрейме MN) if (Bars<100) { return(-1); // прекращаем расчет и выходим досрочно } //--- количество баров, не изменившихся с момента последнего вызова индикатора int counted_bars=IndicatorCounted(); //--- в случае ошибки выходим if (counted_bars<0) { return(-1); } //--- позиция бара, с которого начинается расчет в цикле int limit=Bars-counted_bars; //--- если counted_bars=0, то начальную позицию в цикле нужно уменьшить на 1, if (counted_bars==0) { limit--; // чтобы не выйти за пределы массива при counted_bars==0 //--- в расчетах используется смещение на 10 баров вглубь //--- истории, поэтому добавим это смещение при первом расчете limit-=10; } else { //--- индикатор уже рассчитывался ранее, counted_bars>0 //--- при повторных вызовах увеличим limit на 1, чтобы //--- гарантированно обновлять значения индикатора для последнего бара limit++; } //--- основной цикл расчета for (int i=limit; i>0; i--) { // используются значения баров, уходящих вглубь истории на 5 и 10 Buff1[i]=0.5*(Open[i+5]+Close[i+10]) } }
Часто встречается некорректная обработка случая counted_bars==0 (начальную позицию limit нужно уменьшить на значение, равное 1 + максимальный индекс относительно переменной цикла).
Также следует помнить о том, что в момент исполнения функции start() мы можем обращаться к элементам массивов индикаторных буферов от 0 до Bars()-1. Если есть необходимость работы с массивами, которые не являются индикаторными буферами, то их размер следует увеличить при помощи функции ArrayResize() в соответствии с текущим размером индикаторных буферов. Максимальный индекс элемента для адресации также можно получить вызовом ArraySize() с одним из индикаторных буферов в качестве аргумента.
Деление на ноль (Zero divide)
Ошибка “Zero divide” возникает в случае, если при выполнении операции деления делитель оказывается равен нулю:
void OnStart() { int a=0, b=0,c; c=a/b; Print("c=",c); }
При выполнении данного скрипта во вкладке “Эксперты” возникает сообщение об ошибке и завершении работы программы:
Обычно такая ошибка возникает в случаях, когда значение делителя определяется значениями каких-либо внешних данных. Например, если анализируются параметры торговли, то величина задействованной маржи оказывается равна 0, если нет открытых ордеров. Другой пример: если анализируемые данные считываются из файла, то в случае его отсутствия нельзя гарантировать корректную работу. По этой причине желательно стараться учитывать подобные случаи и корректно их обрабатывать.
Самый простой способ – проверять делитель перед операцией деления и выводить сообщение о некорректном значении параметра:
void OnStart() { int a=0, b=0,c; if (b!=0) { c=a/b; Print(c); } else { Print("Error: b=0"); return; } }
В результате критической ошибки не возникает, но выводится сообщение о некорректном значении параметра и работа завершается:
Использование 0 вместо NULL для текущего символа
В старой версии компилятора допускалось использование 0 (нуля) в качестве аргумента в функциях, требующих указания финансового инструмента.
Например, значение технического индикатора Moving Average для текущего символа можно было запрашивать следующим образом:
AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // неправильно
В новом компиляторе для указания текущего символа нужно явно указывать NULL:
AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // правильно
Кроме того, текущий символ и период графика можно указать при помощи функций Symbol() и Period().
AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // правильно
Еще лучше, если вы будете использовать предопределенные переменные _Symbol и _Period – они обрабатываются быстрее:
AlligatorJawsBuffer[i]=iMA(_Symbol,_Period,13,8,MODE_SMMA,PRICE_MEDIAN,i); // правильно
Строки в формате Unicodе и их использование в DLL
Строки представляют собой последовательность символов Unicode. Следует учитывать этот факт и использовать соответствующие функции Windows. Например, при использовании функций библиотеки wininet.dll вместо InternetOpenA() и InternetOpenUrlA() следует вызывать InternetOpenW() и InternetOpenUrlW(). При передаче строк в DLL следует использовать структуру MqlString:
#pragma pack(push,1) struct MqlString { int size; // 32-битное целое, содержит размер распределенного для строки буфера LPWSTR buffer; // 32-разрядный адрес буфера, содержащего строку int reserved; // 32-битное целое, зарезервировано, не использовать }; #pragma pack(pop,1)
Совместное использование файлов
При открытии файлов необходимо явно указывать флаги FILE_SHARE_WRITE и FILE_SHARE_READ для совместного использования.
В случае их отсутствия, файл будет открыт в монопольном режиме, что не позволит больше никому его открывать, пока он не будет закрыт монополистом.
Например, при работе с оффлайновыми графиками требуется явно указывать флаги совместного доступа:
// 1-st change - add share flags ExtHandle=FileOpenHistory(c_symbol+i_period+".hst", FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);
Особенность преобразования datetime
Следует иметь ввиду, что преобразование типа datetime в строку зависит от режима компиляции:
datetime date=D'2014.03.05 15:46:58'; string str="mydate="+date; //--- str="mydate=1394034418" - без директивы #property strict //--- str="mydate=2014.03.05 15:46:58" - с директивой #property strict
Например, попытка работы с файлами, имя которых содержит двоеточие, приведет к ошибке.
Обработка ошибок времени выполнения
Так как без использования встроенных пользовательских функций не сможет обойтись ни один торгующий эксперт, то в первую очередь попытаемся упростить себе жизнь при анализе ошибок, возвращаемых этими функциями.
В наборе “из коробки” доступны некоторые библиотеки для упрощения написания советников, в том числе и для работы с ошибками. Хранятся они в папке MQL4/Include:
Нам понадобятся две библиотеки:
- stderror.mqh – содержит константы для номера каждой ошибки;
- stdlib.mqh – содержит несколько вспомогательных функций, в том числе и функцию возврата описания ошибки в виде строки:
string ErrorDescription(int error_code)
Поэтому подключим в наш проект обе эти библиотеки:
#include <stderror.mqh> #include <stdlib.mqh>
Сами описания ошибок находятся в файле MQL4/Library/stdlib.mql4 и они на английском языке. Поэтому, если вы против иностранных языков, всегда можно переписать описания на свой родной.
Еще одна встроенная необходимая нам функция – GetLastError(). Именно она возвращает коды ошибок в виде целого числа (int), который мы потом будем обрабатывать. Сами коды ошибок и их описания на русском можно посмотреть в руководстве по mql4 от MetaQuotes. Оттуда же можно взять информацию для перевода файла stdlib.mql4 на русский.
Теперь, когда мы подключили необходимые библиотеки, рассмотрим результаты работы функций, непосредственно связанных с торговыми операциями, так как игнорирование сбоев в этих функциях может привести к критическим для бота последствиям.
К сожалению, средствами MQL4 нельзя написать обобщенную библиотеку для обработки всех возможных ошибочных ситуаций. В каждом отдельном случае придется обрабатывать ошибки отдельно. Но не все так плохо, – многие ошибки не нужно обрабатывать, их достаточно исключить на этапе разработки и тестирования эксперта, хотя для этого и нужно вовремя узнать об их наличии.
Рассмотрим для примера две типичные для экспертов на MQL4 ошибки:
- Ошибка 130 – ERR_INVALID_STOPS
- Ошибка 146 – ERR_TRADE_CONTEXT_BUSY
Одним из случаев, когда возникает первая ошибка, является попытка эксперта выставить отложенный ордер слишком близко к рынку. Ее наличие может серьезно ухудшить показатели эксперта в некоторых случаях. Например, допустим эксперт, открыв прибыльную позицию, поджимает прибыль каждые 150 пунктов. Если при очередной такой попытке возникнет ошибка 130, а цена безвозвратно вернется к предыдущему уровню стопа, эксперт может лишить вас законной прибыли. Несмотря на возможность таких последствий, данную ошибку можно исключить в корне, доработав код эксперта так, чтобы он учитывал минимальное допустимое расстояние между ценой и стопами.
Вторую ошибку, связанную с занятостью торгового контекста терминала, полностью исключить не получится. При работе нескольких экспертов в одном терминале всегда возможна ситуация, когда один из экспертов попытается открыть позицию, пока другой все еще делает то же самое. Следовательно, такую ошибку всегда нужно обрабатывать.
Таким образом, мы всегда должны быть в курсе, если какая-то из используемых встроенных функций возвращает ошибку во время работы эксперта. Добиться этого можно, используя следующую нехитрую вспомогательную функцию:
void logError(string functionName, string msg, int errorCode = -1) { Print("ERROR: in " + functionName + "()"); Print("ERROR: " + msg ); int err = GetLastError(); if (errorCode != -1) { err = errorCode; } if (err != ERR_NO_ERROR) { Print("ERROR: code=" + err + " - " + ErrorDescription( err )); } }
Использовать ее мы будем следующим образом:
void openLongTrade() { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
Конечно, это упрощенный пример. Для написания более грамотных функций открытия, закрытия и модификации ордеров смотрите этот урок.
Первым параметром в функцию logError() передается имя функции, в которой была обнаружена ошибка, в нашем примере – в функции openLongTrade(). Если наш эксперт вызывает функцию OrderSend() в нескольких местах, это позволит нам точно установить, в каком из них произошла ошибка. Вторым параметром передается описание ошибки, чтобы можно было понять, где именно внутри функции openLongTrade() была обнаружена ошибка. Это может быть как краткое описание ошибки, так и более развернутое, с перечислением значений всех параметров, переданных во встроенную функцию.
Я предпочитаю последний вариант, так как при возникновении ошибки можно сразу получить всю необходимую для анализа информацию. Для примера допустим, что до вызова OrderSend() текущая цена успела сильно отклониться от последней известной нам цены. В результате при выполнении этого примера произойдет ошибка и в протоколе работы эксперта появятся следующие строки:
ERROR: in openLongTrade() ERROR: could not open order ERROR: code=138 - requote
То есть сразу будет видно:
- в какой функции произошла ошибка;
- к чему она относится (в данном случае – к попытке открыть позицию);
- какая именно ошибка возникла (код ошибки и ее описание).
Теперь рассмотрим третий, необязательный, параметр функции logError(). Он необходим в тех случаях, когда мы хотим обработать конкретный вид ошибки, а об остальных будем отчитываться в протоколе работы эксперта, как и прежде:
void updateStopLoss(double newStopLoss) { bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), OrderExpiration()); if (!modified) { int errorCode = GetLastError(); if (errorCode != ERR_NO_RESULT ) { logError("updateStopLoss", "failed to modify order", errorCode); } } }
Здесь в функции updateStopLoss() вызывается встроенная функция OrderModify(). Эта функция несколько отличается в плане обработки ошибок от OrderSend(). Если ни один из параметров изменяемого ордера не отличается от его текущих параметров, то функция вернет ошибку ERR_NO_RESULT. Если в нашем эксперте такая ситуация допустима, то мы должны игнорировать конкретно эту ошибку. Для этого мы анализируем значение, возвращаемое GetLastError(). Если произошла ошибка с кодом ERR_NO_RESULT, то мы ничего не выводим в протокол.
Однако если произошла другая ошибка, то необходимо полностью отрапортовать о ней, как мы делали это раньше. Именно для этого мы сохраняем результат функции GetLastError() в промежуточной переменной и передаем его третьим параметром в функцию logError(). Дело в том, что встроенная функция GetLastError() автоматически обнуляет код последней ошибки после своего вызова. Если бы мы не передали код ошибки явно в logError(), то в протоколе была бы отражена ошибка с кодом 0 и описанием “no error”.
Похожие действия необходимо совершать и при обработке других ошибок, например, реквотов. Основная идея заключается в том, чтобы обрабатывать только ошибки, требующие обработки, а остальные передавать в функцию logError(). Тогда мы всегда будем в курсе, если во время работы эксперта произошла непредвиденная ошибка. Проанализировав логи, мы сможем решить, требует ли данная ошибка отдельной обработки или же ее можно исключить, доработав код эксперта. Такой подход часто заметно упрощает жизнь и сокращает время, уходящее на борьбу с ошибками.
Диагностика логических ошибок
Логические ошибки в коде эксперта могут доставить много проблем. Отсутствие возможности пошаговой отладки экспертов делают борьбу с такими ошибками не очень приятным занятием. Основным средством для диагностики этого на данный момент является встроенная функция Print(). С ее помощью можно выполнять распечатку текущих значений важных переменных, а также протоколировать ход работы эксперта прямо в терминале во время тестирования. При отладке эксперта во время тестирования с визуализацией также может помочь встроенная функция Comment(), которая выводит сообщения на график. Как правило, убедившись, что эксперт работает не так, как было задумано, приходится добавлять временные вызовы функции Print() и протоколировать внутреннее состояние эксперта в предполагаемых местах возникновения ошибки.
Однако, для обнаружения сложных ошибочных ситуаций порой приходится добавлять десятки таких вызовов функции Print(), а после обнаружения и устранения проблемы их приходится удалять или комментировать, чтобы не загромождался код эксперта и не замедлялось его тестирование. Ситуация ухудшается, если в коде эксперта функция Print() уже используется для периодического протоколирования различных состояний. Тогда удаление временных вызовов Print() не удается выполнить путем простого поиска фразы ‘Print’ в коде эксперта. Приходится задумываться, чтобы не удалить еще и полезные вызовы этой функции.
Например, при протоколировании ошибок функций OrderSend(), OrderModify() и OrderClose() полезным бывает печатать в протокол текущее значение переменных Bid и Ask. Это несколько облегчает распознавание причин таких ошибок, как ERR_INVALID_STOPS и ERR_OFF_QUOTES.
Чтобы выделить такие диагностические выводы в протокол, я рекомендую использовать такую вспомогательную функцию:
void logInfo(string msg) { Print("INFO: " + msg); }
Это желательно сделать по нескольким причинам. Во-первых, теперь такие вызовы не будут попадаться при поиске ‘Print’ в коде эксперта, ведь искать мы будем logInfo. Во-вторых, у этой функции есть еще одна полезная особенность, о которой мы поговорим чуть позже.
Добавление и удаление временных диагностических вызовов функции Print() отнимает у нас драгоценное время. Поэтому я предлагаю рассмотреть еще один подход, который эффективен при обнаружении логических ошибок в коде и позволяет немного сэкономить наше время. Рассмотрим следующую несложную функцию:
void openLongTrade(double stopLoss) { int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
В данном случае, так как мы открываем длинную позицию, совершенно очевидно, что при нормальной работе эксперта значение параметра stopLoss никогда не будет больше или равно текущей цене Bid. То есть, при вызове функции openLongTrade() всегда выполняется условие stopLoss < Bid. Так как мы знаем об этом еще на этапе написания рассматриваемой функции, то мы сразу же можем этим воспользоваться следующим образом:
void openLongTrade( double stopLoss ) { assert("openLongTrade", stopLoss < Bid, "stopLoss < Bid"); int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) { logError("openLongTrade", "could not open order"); } }
То есть мы логируем наше утверждение в коде при помощи новой вспомогательной функции assert(). Сама функция выглядит довольно просто:
void assert(string functionName, bool assertion, string description = "") { if (!assertion) { Print("ASSERT: in " + functionName + "() - " + description); } }
Первым параметром функции является ее имя, в котором проверяется наше условие (по аналогии с функцией logError()). Вторым параметром передается результат проверки этого условия. И третьим параметром передается его описание. В результате, если ожидаемое условие не выполняется, в протокол будет выведена следующая информация:
- название функции, в которой условие было нарушено;
- описание нарушенного условия.
В качестве описания можно передавать, например, само условие, а можно выводить и более подробное описание, содержащее значения контролируемых переменных на момент проверки условия, если это поможет разобраться в причинах ошибки.
Конечно же, рассмотренный пример максимально упрощен. Но, надеюсь, основную идею отражает достаточно хорошо. В процессе наращивания функциональности эксперта мы отдаем себе отчет в том, как он должен работать и какие состояния и входные параметры функций допустимы, а какие нет. Фиксируя это в коде эксперта при помощи функции assert() мы получаем ценную информацию о месте, в котором нарушается логика работы эксперта. Более того, мы частично избавляем себя от необходимости добавлять и удалять временные вызовы функции Print(), так как функция assert() выдает диагностические сообщения в протокол только в момент обнаружения несоответствий в ожидаемых нами условиях.
Еще одним полезным приемом является использование этой функции перед каждой операцией деления. Дело в том, что иногда в результате той или иной логической ошибки иногда происходит деление на ноль. Работа эксперта в этом случае прекращается, а в протоколе появляется одна лишь строка с печальным диагнозом: ‘zero divide’. Узнать, в каком именно месте произошла эта ошибка, если операция деления используется в коде неоднократно, достаточно сложно. Вот здесь и поможет функция assert(). Вставляем соответствующие проверки перед каждой операцией деления:
assert("buildChannel", distance > 0, "distance > 0"); double slope = delta / distance;
И теперь, в случае деления на ноль, достаточно будет лишь взглянуть в логи, чтобы узнать, в каком именно месте произошла ошибка.
Обработка состояний
Во время работы эксперта на вашем счете могут возникнуть некоторые ситуации, которые не являются ошибками – так называемые состояния эксперта. Такие состояния не являются ошибками, но все же их стоит логировать. В этом помогают специальные функции языка mql4.
Функция IsExpertEnabled() возвращает информацию о возможности запуска экспертов. Функция вернет true, если в клиентском терминале разрешен запуск экспертов, иначе возвращает false. В случае возврата false полезно будет известить об этом пользователя с просьбой включить соответствующую настройку. Пример:
void OnStart() { if (!IsExpertEnabled() { //советникам не разрешено торговать Alert("Attention! Please press the "Expert Advisors" button in MT4"); } //рабочий алгоритм советника return; }
Если эксперт использует внешние библиотеки, пригодится функция IsLibrariesAllowed(). Она возвращает true, если эксперт может вызвать библиотечную функцию, иначе возвращает false.
Если библиотека в виде dll файла, пригодится функция IsDllsAllowed(). Также нелишним будет проверить, есть ли вообще возможность торговать при помощи экспертов с помощью функции IsTradeAllowed().
Если вы хотите узнать, является ли счет демонстрационным, или же реальным, можно использовать функцию IsDemo().
Все вышеперечисленные проверки стоит сделать в функции OnInit().
Конечно же, стоит проверять периодически связь с сервером. В этом поможет функция IsConnected().
Следующие три функции помогут определить, в каком режиме находится советник. Если IsOptimisation() возвращает true, проводится оптимизация, если IsTesting(), то тестирование, IsVisualMode() – тестирование в режиме визуализации. Под каждый из этих вариантов в советнике может быть предусмотрена своя логика. Например, для режима визуализации можно что-то выводить на график (и не выводить в других режимах ради экономии ресурсов). В режиме тестирования можно выводить отладочную информацию, в режиме оптимизации максимально облегчить код, чтобы сэкономить время.
И последняя функция – IsTradeContextBusy(). Она вернет true, если поток для выполнения торговых операций занят. Это бывает полезно при совершении экспертом торговых операций. Можно применить функцию Sleep для ожидания некоторого момента и новой попытки.
Еще одна полезная функция – UninitializeReason()
int deinit() { switch(UninitializeReason()) { case REASON_CHARTCLOSE: case REASON_REMOVE: CleanUp(); break; // очистка и освобождение ресурсов. case REASON_RECOMPILE: case REASON_CHARTCHANGE: case REASON_PARAMETERS: case REASON_ACCOUNT: StoreData(); break; // подготовка к рестарту. } //... }
Можно также логировать причину выхода советника.
Коды самых распространенных ошибок и их вероятное решение
№ ошибки | Значение | Проблема | Решение |
4, 146 | Торговый сервер занят | Советник подал слишком много приказов одновременно или не дождавшись ответа от сервера, при выполнении операции – советник пытается отправить новый приказ | Перезагрузка терминала или оптимизация кода советника с помощью функций обработки ошибок |
8, 141 | Слишком частые запросы | Предыдущие причины ошибки, в сильно частом запросе | Аналогичное решение |
129 | Неправильная цена | Цена по которой Вы пытаетесь открыть позицию (BUY или SELL) неправильная | BUY нужно открывать по Ask а закрывать по BID; SELL нужно открывать по BID а закрывать по ASK |
130, 145 | Неправильные стопы | Стоп лосс, тейк профит или уровень открытия отложки или лимитника неверные. Стопы расположены слишком близко к цене. Ваш счет открыт в группе ECN (ЕЦН) или NDD (НДД), что не дает сразу выставлять стопы |
Проверьте значения Ваших стоп лоссов, тейк профитов, уточните минимальный стоп уровень по Вашему инструменту у брокера, при выставлении стопов – соблюдайте уровень минимальной дистанции. В хорошо написанном советнике должны быть функции работы на счетах ECN и NDD – это происходит путем модификации ордера уже после его открытия |
131 | Неправильный объем | Неправильный лот при открытии сделки, или меньше минимального (больше максимального). Разрядность лота тоже может отличаться от разрядности брокера | Проверьте правильность открытия лота, изучите спецификацию контракта и прочтите условия торговли в Вашем ДЦ, проверьте минимальный и максимальный лот в Вашем ДЦ и на Вашем счете |
132 | Рынок закрыт | Рынок закрыт на выходные дни | Пробуйте связаться с рынком после выходных |
133 | Торговля запрещена | В данный момент торговля запрещена | По данной валютной паре запрещено торговать – в конкретный момент времени или вообще. Часто у брокеров есть перерыв в несколько минут в полночь |
134 | Недостаточно денег для совершения операции | Лот, который Вы пытаетесь открыть, слишком большой, на него не хватает маржи | Проверьте уровень свободных средств и рассчитайте средства, которые Вам нужны для открытия лота, следите за уровнем Ваших свободных средств |
135-138 | Цена изменилась | Реквот, слишком быстрый рынок (новости), Брокер или ДЦ не дает Вам поставить позицию по заявленной цене | Не торгуйте в такие моменты, увеличьте уровень проскальзывания, но помните, что это влечет за собой открытие позиций не по заявленной Вами цене. Предусмотрите в советнике функцию обработки ошибок и количество попыток открытия позиций |
147 | Использование даты истечения ордера запрещено брокером | Ваш советник или Вы пытаетесь установить срок истечения отложенного ордера | В советнике, в функции OrderSend в параметре срок истечения поставьте 0 (ноль). Не устанавливайте срок истечения ордера |
148 | Количество открытых и отложенных ордеров достигло предела, установленного брокером | Максимальное количество открытых ордеров и позиций достигло предела, установленного брокером | Удалите или закройте часть позиций. Остановите процесс открытия новых позиций |
4012, 4013 | Остаток от деления на ноль | Вы пытаетесь поделить число на 0 (ноль) | Проверьте код советника на наличие ошибки, или же проверьте все значения из MarketInfo функций на момент возвращения 0, иногда при MarketInfo(Symbol(),MODE_SPREAD) возвращается не спред, а 0 (у брокеров с плавающим спредом) |
4017 | Вызовы DLL не разрешены | В Вашем терминале запрещен вызов DLL | Разрешите вызов DLL через Меню – сервис – Настройки – Советник – Разрешить вызов DLL |
4018, 4019 | Невозможно загрузить библиотеку | Библиотека повреждена или ее вызов завершается с ошибкой, возможно она вообще отсутствует | Проверьте библиотеку DLL |
4020 | Вызовы внешних библиотечных функций не разрешены | В Вашем терминале запрещен вызов функций из внешних экспертов | Разрешите вызов функций через Меню – сервис – Настройки – Советник – Разрешить вызов внешних экспертов |
4103 | Невозможно открыть файл | Данный файл не существует или заблокирован другим процессом | Проверьте наличие указанного файла. Проверьте, не заблокирован ли файл системой антивируса, разрешен ли режим записи-чтения файла |
4106 | Неизвестный символ | Символа нет в обзоре рынка | В обзоре рынка – правой кнопкой мыши – показать все символы. Проверить названия символа в советнике и наличие его в обзоре рынка. Некоторые советники используют четкие названия без суффиксов, а брокеры намеренно ставят суффиксы, например EURUSDx где х – суффикс |
4108 | Неверный номер тикета | Тикет ордера, который выбирает эксперт – не существует. Эксперт пытается выбрать тикет, но данный ордер был закрыт другим советником или руками. При попытке осуществления приказа над ордером, тикет был исполнен и закрыт брокером | Если данная ошибка появляется очень часто, 100-1000 раз за минуту, проверьте функции Вашего советника. Отключите другие советники или настройте их так, чтобы они не конфликтовали, не закрывайте ордер руками, когда эксперт выполняет операцию. Иногда такое случается, когда несколько советников используют одинаковый MagicNumber |
4109 | Торговля не разрешена | Советнику запрещено торговать, на графике грустный смайл или крестик | Включите галочку «Разрешить советнику торговать» во вкладе при установке советника, либо в меню – сервис – настройки – советники |
4110, 4111 | Длинные/короткие позиции не разрешены | В настройках советника, во вкладке Общие не разрешен тип позиций | Во вкладке Общие, при установке советника, есть выбор позиций, разрешенных к открытию |
Заключение
Рассмотренные вспомогательные функции и несложные приемы позволяют заметно упростить и ускорить процесс обнаружения и исправления ошибок в коде торговых экспертов, написанных на языке программирования MQL4. Грамотное написание кода и функций для логирования и сопровождения работы советника существенно ускоряют процесс его разработки.
Тема на форуме
С уважением, Дмитрий аkа Silentspec
TradeLikeaPro.ru
В предыдущих статьях мы узнали, как открывать ордера, закрывать ордера и перебирать существующие ордера. Как вы уже знаете, ордера также могут быть изменены. Например, для отложенных ордеров вы можете изменить цену открытия. Для рыночных ордеров вы можете изменить стоп-лосс или тейк-профит. Для изменения ордеров в MQL4 есть функция OrderModify().
Как вы знаете ECN/STP-брокеры, которые используют MetaTrader, не поддерживают установку стоп-лосса и тейк-профита при открытии сделки. В этом случае нам нужно будет разместить стоп-лосс и тейк-профит после размещения ордера с помощью функции OrderModify(). Это относится только к рыночным ордерам — для отложенных ордеров вы все равно можете разместить стоп-лосс и тейк-профит с помощью функции OrderSend().
Рассмотрим синтаксис функции:
bool OrderModify( int ticket, // номер ордера double price, // цена открытия double stoploss, // stop loss double takeprofit, // take profit datetime expiration, // время истечения ордера color arrow_color // цвет );
Ее параметры:
- ticket — для идентификации тикета ордера.
- price — используется для изменения цены открытия отложенного ордера.
- stoploss — новая цена для стоп-лосса.
- takeprofit — новая цена для тейк-профита.
- expiration — изменить время истечения отложенных ордеров.
- arrow_color — изменить цвет стрелки на графике.
Если изменение ордера выполнено успешно, OrderModify() вернет значение true. Если произошла ошибка, возвращаемое значение будет ложным.
При изменении ордеров мы должны быть уверены, что значения, которые мы передаем функции, действительны. Например, ордер по-прежнему должен быть открытым — мы не можем изменить закрытый ордер. При изменении отложенных ордеров с помощью параметра Price, ордер не должен быть уже исполнен.
Как и при открытии нового ордера, не забывайте учитывать, что стоп-лосс и тейк-профит не могут быть размещены слишком близки к текущей цене, иначе операция изменения ордера может не удастся.
Почему нам может быть необходимо изменить ордер? Вот несколько примеров:
- Трейлинг-стоп, пожалуй, одна из самых популярных причин. Мы перемещаем цену стоп-лосса, когда цена движется в направлении нашего ордера.
- Изменить цену тейк-профита. Обычно это делается вместе с трейлинг-стопом.
- Изменить срок действия отложенного ордера.
- Изменить цену открытия отложенного ордера.
Как добавить стоп-лосс и тейк-профит в существующий ордер?
Во-первых, нам нужно убедиться, что ордер был открыт правильно. Мы делаем это, проверяя значение функции OrderSend(), которая возвращает номером тикета открытого ордера. Если ордер не был открыт из-за ошибки, номер тикета будет равен -1.
Затем мы используем функцию OrderSelect(), чтобы получить информацию о только что размещенном ордере. Мы будем использовать функции OrderOpenPrice(), OrderTakeProfit(), OrderStopLoss() и опционально функции OrderExpiration() при передаче значений в функцию OrderModify(). Наконец, мы будем использовать функцию OrderModify(), чтобы добавить стоп-лосс и тейк-профит.
Вот пример, где мы устанавливаем стоп-лосс и тейк-профит для ордера на покупку, используя функцию OrderModify(). Мы переместили расчет стоп-лосса и тейк-профита после функции OrderSend (), чтобы она вычислялась до того, как мы изменим ордер:
int BuyTicket = OrderSend(Symbol(),OP_BUY,LotSize,Ask,UseSlippage,0,0, "Ордер на покупку",MagicNumber,0,Green); if(BuyTicket > 0) { OrderSelect(BuyTicket,SELECT_BY_TICKET); double OpenPrice = OrderOpenPrice(); if(StopLoss > 0) double BuyStopLoss = OpenPrice – (StopLoss * UsePoint); if(TakeProfit > 0) double BuyTakeProfit = OpenPrice + (TakeProfit * UsePoint); if(BuyStopLoss > 0 || BuyTakeProfit > 0) { bool TicketMod = OrderModify(BuyTicket,OrderOpenPrice(),BuyStopLoss, BuyTakeProfit,0); } }
Функция OrderSend() идентична нашему более раннему примеру, за исключением того, что мы используем значение 0 для параметров стоп-лосса и тейк-профита. Нулевое значение означает, что стоп-лосс или тейк-профит не размещаются вместе с ордером. В переменной BuyTicket будет хранится номер ордера.
Мы используем оператор if, чтобы убедиться, что тикет BuyTicket действителен, то есть больше нуля. Если это так, мы вызываем функцию OrderSelect(), используя тикет BuyTicket. Мы получаем цену открытия для ордера, используя OrderOpenPrice(), и назначаем ее переменной OpenPrice.
Далее мы рассчитываем стоп-лосс и тейк-профит относительно цены открытия ордера, который мы только что разместили. Сначала мы проверяем, являются ли внешние переменные StopLoss и TakeProfit больше нуля. Если это так, мы рассчитываем новые цены для стоп-лосса и тейк-профита.
Наконец, мы вызываем функцию OrderModify(), чтобы добавить наш стоп-лосс и зафиксировать прибыль в ордере. Сначала мы проверяем, чтобы переменные BuyStopLoss или BuyTakeProfit отличались от нуля. Если мы попытаемся изменить порядок с неизменными значениями, мы получим код ошибки 1 из функции OrderModify().
Первым параметром для OrderModify() является наш номер BuyTicket. Мы также можем использовать OrderTicket(). Второй параметр — цена нового ордера. Поскольку мы не изменяем цену ордера, мы используем функцию OrderOpenPrice(), чтобы указать, что цена ордера не изменилась.
Помните, что мы можем изменять цены только для отложенных ордеров. Если мы модифицируем рыночный ордер, мы можем передать любое значение параметра Price, поскольку вы не можете изменить цену для рыночного ордера. Мы не можем предполагать, что мы всегда будем изменять рыночные ордера, поэтому мы всегда будем использовать функцию OrderOpenPrice().
Переменные BuyStopLoss и BuyTakeProfit передают измененный стоп-лосс и значения тейк-профита в функцию OrderModify(). Если вы планируете использовать время истечения ордера для отложенных ордеров, вы можете использовать OrderExpiration(). В противном случае просто используйте 0.
Хотя этот метод добавляет несколько дополнительных шагов, мы рекомендуем вам использовать этот метод размещения стоп-лоссов и фиксации прибыли для рыночных ордеров в ваших советниках, чтобы обеспечить их совместимость со всеми брокерами без влияния проскальзывания.
Как изменять отложенные ордера?
Функция OrderModify() также можно использовать для изменения цены отложенного ордера. Если цена отложенного ордера уже была достигнута и ордер был исполнен, он больше не является отложенным ордером и его цена открытия не может быть изменена.
Мы будем использовать переменную NewPendingPrice для представления новой цены ордера.
OrderSelect(Ticket,SELECT_BY_TICKET); if(NewPendingPrice != OrderOpenPrice()) { bool TicketMod = OrderModify(Ticket,NewPendingPrice,OrderStopLoss(), OrderTakeProfit(),0); }
Как всегда, мы получаем информацию о заказе, используя функцию OrderSelect(). Таким образом, мы можем передать стоп-лосс и зафиксировать цену. Перед изменением ордера мы проверяем, что наша новая цена отложенного ордера не совпадает с текущей ценой.
Для OrderModify() мы указываем наш тикет ордера, новую цену ордера, сохраненную в NewPendingPrice, и неизменные значения стоп-лосс и тейк-профит, представленные OrderStopLoss() и OrderTakeProfit(). Мы не используем время истечения для этого ордера, поэтому мы подставляем 0 в качестве параметра истечения срока действия.
Как разместить стоп-лосс и тейк-профит для каждого открытого ордера?
В примере, который вы увидите, мы установим стоп-лосс и тейк-профит для каждого открытого ордера.
// Допустимое проскальзывание. extern double Slippage=3; //Allowed Slippage extern int TakeProfit=40; //Размер тейк-профита в пунктах. extern int StopLoss=20; //Размер стоп-лосса в пунктах. //В соответствии с рекомендациями нам необходимо нормализовать цену, чтобы правильно рассчитать тейк-профит и стоп-лосс. double CalculateNormalizedDigits() { if(Digits<=3) { return(0.01); } else if(Digits>=4) { return(0.0001); } else return(0); } //Функцию UpdateOpenOrders возвращает количество ордеров, которые должны быть изменены. int UpdateOpenOrders(){ int TotalUpdated=0; //Мы хотим посчитать, сколько ордеров было изменено. double nDigits=CalculateNormalizedDigits(); //Нормализируем проскальзывание. if(Digits==3 || Digits==5){ Slippage=Slippage*10; } //Перебираем все ордера в обратном порядке. for(int i=OrdersTotal()-1; i>=0; i--) { //Мы выбираем ордера по индексу. Если выбор прошел успешно, мы приступаем к их изменению. if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){ // Проверяем, относится ли ордер к той же валютной паре на графике, на котором выполняется скрипт. if(OrderSymbol()==Symbol()){ double OpenPrice, StopLossPrice, TakeProfitPrice; // Получаем цену открытия. OpenPrice=OrderOpenPrice(); // Мы рассчитываем стоп-лосс и тейк-профит в зависимости от типа ордера. if(OrderType()==OP_BUY){ StopLossPrice=NormalizeDouble(OpenPrice-StopLoss*nDigits,Digits); TakeProfitPrice=NormalizeDouble(OpenPrice+TakeProfit*nDigits,Digits); } if(OrderType()==OP_SELL){ StopLossPrice=NormalizeDouble(OpenPrice+StopLoss*nDigits,Digits); TakeProfitPrice=NormalizeDouble(OpenPrice-TakeProfit*nDigits,Digits); } if(OrderModify(OrderTicket(),OpenPrice,StopLossPrice,TakeProfitPrice,CLR_NONE)){ TotalUpdated++; } else{ Print("Не удалось обновить заказ с ошибкой - ",GetLastError()); } } // Если ордер изменяется корректно, мы увеличиваем счетчик измененных ордеров. Если ордер не изменяется, мы выводим ошибку. } else{ Print("Не удалось изменить ордер - ",GetLastError()); } // Устанавливаем небольшую паузу. Sleep(300); } //Если цикл завершается, это означает, что для данного инструмента больше нет открытых ордеров. return(TotalUpdated); } void OnStart() { Print("Сколько ордеров было изменено? ",UpdateOpenOrders()); }
Рассмотрим пример скрипта, который позволит нам устанавливать стоп-лосс и тейк-профит для всех открытых ордеров. Когда вы запускаете скрипт на графике, вы можете установить некоторые параметры, чтобы ограничить, какие ордера будут изменены.
#property copyright "Александр Паркер" #property link "https://traderblog.net/" #property version "1.00" #property strict #property show_inputs //Внешние переменные extern int TakeProfit=40; //Размер тейк-профита в пунктах. extern int StopLoss=20; //Размер стоп-лосса в пунктах. extern bool OnlyMagicNumber=false; //Изменять только ордера с magic number. extern int MagicNumber=0; //Magic number. extern bool OnlyWithComment=false; //Изменять ордера с определенным комментарием. extern string MatchingComment=""; //Комментарий. extern double Slippage=2; //Проскальзывание. extern int Delay=0; //Задержка между модификацией ордеров. //Нормализуем значение цены для рассчета стоп-лосса и тейк-профита. double CalculateNormalizedDigits() { if(Digits<=3){ return(0.01); } else if(Digits>=4){ return(0.0001); } else return(0); } //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- //Счетчик ордеров int TotalModified=0; //Нормализуем проскальзывание. if(Digits==3 || Digits==5){ Slippage=Slippage*10; } double nDigits=CalculateNormalizedDigits(); //Перебираем ордера. for(int i=OrdersTotal()-1; i>=0; i--){ //Выбераем ордер. Если орден не выбран, выводим ошибку и переходим к следующему индексу. if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) { Print("Ошибка. Нельзя выбрать ордер - ",GetLastError()); continue; } //Проверяем, совпадает ли ордер в соответствии с заданными критериями. Если критерии не совпадают, переходим к следующим. if(OrderSymbol()!=Symbol()) continue; if(OnlyMagicNumber && OrderMagicNumber()!=MagicNumber) continue; if(OnlyWithComment && StringCompare(OrderComment(),MatchingComment)!=0) continue; //Цены. double TakeProfitPrice=0; double StopLossPrice=0; double OpenPrice=OrderOpenPrice(); RefreshRates(); if(OrderType()==OP_BUY){ TakeProfitPrice=NormalizeDouble(OpenPrice+TakeProfit*nDigits,Digits); StopLossPrice=NormalizeDouble(OpenPrice-StopLoss*nDigits,Digits); } if(OrderType()==OP_SELL){ TakeProfitPrice=NormalizeDouble(OpenPrice-TakeProfit*nDigits,Digits); StopLossPrice=NormalizeDouble(OpenPrice+StopLoss*nDigits,Digits); } //Модифицируем ордер. if(OrderModify(OrderTicket(),OpenPrice,StopLossPrice,TakeProfitPrice,0,clrNONE)){ TotalModified++; } else{ Print("Ошибка - ",GetLastError()); } //Задержка. Sleep(Delay); } //Выводим количество модифицированных ордеров. Print("Всего ордеров изменено = ",TotalModified); } //+------------------------------------------------------------------+
-
#6 901
Доброго дня!Можно к этому индюку приделать ID,чтобы ставить несколько штук на график с разными ТФ?
-
fs_hor_lvl.mq4
11,9 КБ · Просмотры: 42
-
MetaTrader — EXNESS.png
53 КБ · Просмотры: 212
-
#6 902
GUYS PLEASE HELP REMOVE THE ALERT IN THIS BRILLIANT ARROW INDICATOR . PROGRAMMERS I COUNT ON YOU PLEASE
-
20200804_112748.jpg
3,4 МБ · Просмотры: 171
-
20200804_112723.jpg
3,9 МБ · Просмотры: 178
-
DR ARROW.ex4
7,3 КБ · Просмотры: 74
-
#6 903
Заведите старый индюк 15 года.
Привет!
Проанализировал код и почистил, но он либо не полный, либо должен быть какой-то второй индюк или прога, которая рассчитывает и создает линию, от которой идет дальнейший расчет. В коде индюка этого нет, насколько смог оценить. Чего-то не хватает, но если расчет основной линии был тупо стерт из кода, то я не знаю какой расчет был для отображения этой линии. Короче, нет данных. Восстановить можно, если знать алгоритм расчета основной линии. Или этот индюк был частью нескольких прог, т.к. в нем все расчеты только на основании уже имеющейся какой-то линии. В инете ничего не нашел…
-
#6 904
GUYS PLEASE HELP REMOVE THE ALERT IN THIS BRILLIANT ARROW INDICATOR . PROGRAMMERS I COUNT ON YOU PLEASE
for this purpose it is necessary a format mq4
-
#6 905
Постоянно возникает ошибка OrderModify error 1, хотя вроде бы тут «if(OrderStopLoss()<(DONlow+minstoplevel*pp))» у меня есть проверка на минимальный стоп
Помогите люди добрые, советник и индюк приладываю
double DONhigh, DONlow;(данные с индикатора)
extern bool AllPositions =True; // Управлять всеми позициями
extern bool ProfitTrailing=False; // Тралить только профит
extern double TrailingStep =5; // Шаг трала
extern double TrailingStop =2;
//+------------------------------------------------------------------+
//| Сопровождение позиции простым тралом |
//+------------------------------------------------------------------+
void TrailingPositions()
{
double pBid, pAsk, pp;
double minstoplevel=MarketInfo(Symbol(),MODE_STOPLEVEL);
//----
pp=MarketInfo(OrderSymbol(), MODE_POINT);
if(OrderType()==OP_BUY)
{
pBid=MarketInfo(OrderSymbol(), MODE_BID);
if(!ProfitTrailing || (pBid-OrderOpenPrice())>TrailingStop*pp)
{
//if(OrderStopLoss()<pBid-(minstoplevel+TrailingStop+TrailingStep-1)*pp)
if(OrderStopLoss()<(DONlow+minstoplevel*pp))
{
ModifyStopLoss(DONlow);
return;
}
}
}
if(OrderType()==OP_SELL)
{
pAsk=MarketInfo(OrderSymbol(), MODE_ASK);
if(!ProfitTrailing || OrderOpenPrice()-pAsk>TrailingStop*pp)
{
//if(OrderStopLoss()>pAsk+(minstoplevel+TrailingStop+TrailingStep-1)*pp || OrderStopLoss()==0)
if(OrderStopLoss()>(DONhigh+minstoplevel*pp))
{
ModifyStopLoss(DONhigh);
return;
}
}
}
}
//+------------------------------------------------------------------+
//| Перенос уровня StopLoss |
//| Параметры: |
//| ldStopLoss - уровень StopLoss |
//+------------------------------------------------------------------+
void ModifyStopLoss(double ldStopLoss)
{
bool fm;
fm=OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(ldStopLoss,Digits),OrderTakeProfit(),0,CLR_NONE);
}
-
7.mq4
28,7 КБ · Просмотры: 26
-
Doncian.mq4
3,4 КБ · Просмотры: 22
-
#6 906
Постоянно возникает ошибка OrderModify error 1, хотя вроде бы тут «if(OrderStopLoss()<(DONlow+minstoplevel*pp))» у меня есть проверка на минимальный стоп
Помогите люди добрые, советник и индюк приладываю
ERR_NO_RESULT — OrderModify пытается изменить уже установленные значения такими же значениями. Необходимо изменить одно или несколько значений и повторить попытку…
говоря простыми словами, при модификации происходит попытка уровень стоплосса (тейкпрофита) поменять на точно такое же значение
-
#6 907
Можно исправить робота? Многовалютник. Автор заявил, что установлено ограничение работы до 30 августа 2020 года. Исправить ограничение или продлить лет на «…цать» работу робота.
…
Ставил на демо на месяц, плечо 1:300. Депозит 20$. Робот показал прекрасную устойчивость. Автор рекомендовал депозит от 100$, тем не менее на 20$ просадка не была более 6% за счет оригинального алгоритма работы.
-
SmoothieH.ex4
168 КБ · Просмотры: 82
-
111.jpg
98 КБ · Просмотры: 118
gek
Элитный участник
-
#6 908
Можно исправить робота? Многовалютник. Автор заявил, что установлено ограничение работы до 30 августа 2020 года. Исправить ограничение или продлить лет на «…цать» работу робота.
…
Ставил на демо на месяц, плечо 1:300. Депозит 20$. Робот показал прекрасную устойчивость. Автор рекомендовал депозит от 100$, тем не менее на 20$ просадка не была более 6% за счет оригинального алгоритма работы.
Привет!
Как работал с ним?
Тайм,пары,брокер,преффиксы и т.д.
Хотел бы поставить,глянуть что за зверь.
-
#6 909
Привет!
Как работал с ним?
Тайм,пары,брокер,преффиксы и т.д.
Хотел бы поставить,глянуть что за зверь.
У меня стоит на Робофорекс на демо. Все по дефолту. Поставил на пару евро-доллар, таймфрейм H1. В настройки не лазил, так как интересовала не сама торговля, а именно проверка устойчивости робота. Далее кидаешь робота на график, появляется мультивалютное меню. Нажимаешь кнопу new order, она становится ярко-синей, всё торговля началась. Робот входит в рынок только по ему известному алгоритму, ведет только 1 с валютную пару до ее закрытия. У меня было максимум 3 ордера в сетке. Хеджирования не видел, хотя автор заявляет, что оно возможно. Не мартинил, ордера имели одинаковую лотность. Количество ордеров в день по разному. Бывает 2-3, а бывает с десяток. Торгует нечасто. Я много перепробовал мультивалютников, лучше этого не видел. Робот по всей видимости был выложен в паблик на soehoe.id, там есть тема от автора.
-
robot.jpg
146,2 КБ · Просмотры: 96
Последнее редактирование: 06.08.2020
-
#6 910
Привет!
Проанализировал код и почистил, но он либо не полный, либо должен быть какой-то второй индюк или прога, которая рассчитывает и создает линию, от которой идет дальнейший расчет. В коде индюка этого нет, насколько смог оценить. Чего-то не хватает, но если расчет основной линии был тупо стерт из кода, то я не знаю какой расчет был для отображения этой линии. Короче, нет данных. Восстановить можно, если знать алгоритм расчета основной линии. Или этот индюк был частью нескольких прог, т.к. в нем все расчеты только на основании уже имеющейся какой-то линии. В инете ничего не нашел…
Привет!
Спасибо за ответ! Ответ нашёл за бугром.
Это работает, вам сначала нужно нарисовать вилы Эндрю, затем переименовать их в соответствии с индикатором (по умолчанию это AL1), затем добавить несколько тиков, и вы получите это.
-
#6 911
[QUOTE = «Vaporesso, post: 1564801, member: 170338»]
Is it possible to fix it?
[/ QUOTE]
adjusted
-
RSI-8_CROSS-55 45_ARROW.ex4
11,8 КБ · Просмотры: 19
-
RSI-8_CROSS-55 45_ARROW.mq4
6,7 КБ · Просмотры: 23
-
#6 912
Нда…Я тогда дальше не буду ковырять — обновленный вариант сами попробуете. Если что-то нужно дальше с ним делать — это уже отдельная тема..
-
AML_v21.mq4
29,1 КБ · Просмотры: 60
-
#6 913
Добрый день! Не кодил около года. Прошу помочь. Прописал в коде советника:
if (Bid==TP)
{
SendNotification(Symdol()+» закрой ордер»);
TP=0;
}
Уведомление не приходит… может функцию уведомления еще и в while прописать?
-
#6 914
Прописал в коде советника:
if (Bid==TP)
{
SendNotification(Symdol()+» закрой ордер»);
Привет. Ну, для начала исправьте Symdol() на Symbol()
и в самом терминале есть настройки на отправку пуш уведомлений?
-
#6 915
Привет. Ну, для начала исправьте Symdol() на Symbol()
и в самом терминале есть настройки на отправку пуш уведомлений?
Сори за ошибку) код сюда не копировал, а от руки писал) Терминал настроен, уведомления приходят с сигналом на открытие позиции, а при достижении определенной цены, уведомления не приходят… но иногда случается чудо и уведомление приходит. Вот и думаю может еще в while функцию записать с условием false
-
#6 916
Добрый день! Не кодил около года. Прошу помочь. Прописал в коде советника:
if (Bid==TP)
{
SendNotification(Symdol()+» закрой ордер»);
TP=0;
}
Уведомление не приходит… может функцию уведомления еще и в while прописать?
ну, сравнение 2 чисел double это тема для небольшого, но крайне поучительного разговора…
однако, сейчас не об этом… если ты думаешь, что условие Bid==TP — встречается часто, то тут форменное заблуждение… на 5-знаке цена 2 часа будет скакать около ТР и ни разу это условие не сработает…
пропиши Bid>=TP — и по смыслу вернее будет, и сработает надежнее)
-
#6 917
Вот и думаю может еще в while функцию записать с условием false
Функция while промежуточная (универсальная) и как вы ее и в каком месте используете я понятия не имею — я же не видел код.
if (Bid==TP) — такой вариант очень редкий на совпадение — идеальное совпадение текущего бида с каким-то рассчитанным ТП поймать очень сложно. Нужно проверки делать типа >= для бай или <= для селл, и то в зависимости от момента, когда это должно сработать.
-
#6 918
vladradon, AlexeNP спасибо вам за совет. Просто хотел как раз таки поймать условие Bid==TP т.к. условие писал и для бай и для селл, думал будет работать). Сегодня буду переписывать через <= >=. Еще раз спасибо за оперативную помощь!)
-
#6 919
Хочу ребята, умельцы наши попросить моленько исправить индикатор IRES он не мой говорю сразу и в открытом коде его не найдёте есть только в ex, нужно просто удалить уровня НКЗ с этого индикатора, а то вылазят даже при отключенных настройках. Писал хозяевам индюка а в ответ тишина а эти уровня мне мешают. Помогите пожалуйста.
-
IRES.ex4
2,2 МБ · Просмотры: 51
-
#6 920
Хочу ребята, умельцы наши попросить моленько исправить индикатор IRES он не мой говорю сразу и в открытом коде его не найдёте есть только в ex, нужно просто удалить уровня НКЗ с этого индикатора, а то вылазят даже при отключенных настройках. Писал хозяевам индюка а в ответ тишина а эти уровня мне мешают. Помогите пожалуйста.
без исходника такие вещи не сделать.
Я пытаюсь изменить Заказ, но всегда получаю Error #1
.
Из своего исследования я обнаружил, что ошибка 1 означает, что у меня неправильный входной параметр. Как я могу исправить мою функцию OrderModify()
?
stoploss = NormalizeDouble(Ask - Point * TrailingStop,Digits);
int ticket;
takeprofit = NormalizeDouble(Ask + Point * TrailingStopTP,Digits);
double minstoplevel = MarketInfo( Symbol(), MODE_STOPLEVEL );
if(stoploss > NormalizeDouble(Ask - Point*minstoplevel,Digits)) {
stoploss = NormalizeDouble(Ask - Point*minstoplevel,Digits);
}
if(takeprofit < NormalizeDouble( Ask + Point*minstoplevel2, Digits )) {
takeprofit = NormalizeDouble( Ask + Point*minstoplevel2, Digits );
}
if(AccountFreeMarginCheck(Symbol(),OP_SELL,lotsize)>0) {
ticket=OrderSend(Symbol(),OP_BUY,lotsize,Ask, 0, 0.0, 0.0, "comment", MagicNumber, 0, Lime);
if(ticket<0) {
Print("Order send failed with error #",GetLastError());
} else {
Print("Order send sucesso!! Ticket#", ticket);
res=OrderModify(ticket,OrderOpenPrice(),stoploss,takeprofit,0,Blue);
if(res==false) {
Print("Error modifying order!, error#",GetLastError());
} else {
Print("Order modified successfully, res#", res);
}
}
} else {
Print("Sem dinheiro na conta D=");
}
}
2 ответа
Лучший ответ
Не совсем « неправильно «, OrderModify()
юридически устанавливает _LastError == 1
Это может вызвать небольшое удивление, но OrderModify()
обязан сигнализировать _LastError == 1
в случае, если вызов был синтаксически и семантически правильным, однако , значения, предоставленные для модификации (й), на самом деле были такими же, поскольку идентифицированный ticket#
уже был в базе данных.
Это означает, что изменять было нечего, поскольку все атрибуты уже имели « квази новое » целевое значение (я).
Можно предварительно проверить все поля на предмет потенциальной идентичности, что может позволить нашему коду пропустить вызов OrderModify()
именно в этом случае идентичности- {current | target} значения.
ERR_NO_RESULT == 1 // No error returned, but the result is unknown
GetLastError()
— возвращает последний сгенерированный код ошибки. То же значение доступно через системную переменную с именем _LastError
. Это значение можно сбросить до нуля перед критическим действием, вызвав ResetLastError()
.
Коды ошибок определены в stderror.mqh
.
Чтобы распечатать описание ошибки, вы можете использовать функцию ErrorDescription()
, определенную в файле stdlib.mqh
.
#include <stderror.mqh>
#include <stdlib.mqh>
4
user3666197
5 Окт 2015 в 11:41
Проблема в том, что даже несмотря на то, что параметры цены входа, стоп-лосса и тейк-профита для вызова OrderModify()
кажутся одинаковыми, они, вероятно, различаются на долю единицы (меньше, чем » Цифры »).
Чтобы исправить это,
просто нормализуйте параметры, чтобы убедиться, что они содержат максимум Digits
десятичных знаков.
double entryPrice = NormalizeDouble( entryPrice, Digits );
double stoploss = NormalizeDouble( stoploss, Digits );
double target = NormalizeDouble( target, Digits );
Затем передайте их в вызов OrderModify()
.
1
user3666197
18 Янв 2017 в 07:57
Что значит 2010.02.01 17:00 MyExpert GBPUSD,M15: OrderModify error 130
почему обычно эта ошибка возникает?
Очень много подобных вопросов мне приходит. Поэтому публикую таблицу кодов ошибок. Например посмотрев вышеописанную ошибку error 130 становится понятно, что функция OrderModify пытается изменить стоп-лосс или тейк-профит слишком близко к текущей цене.
Коды ошибок
GetLastError() — функция, возвращающая коды ошибок. Кодовые константы ошибок определены в файле stderror.mqh. Для вывода текстовых сообщений следует использовать функцию ErrorDescription(), определенную в файле stdlib.mqh.
Коды ошибок, возвращаемые торговым сервером или клиентским терминалом:
Значение | Описание |
---|---|
0 | Нет ошибки |
1 | Нет ошибки, но результат неизвестен |
2 | Общая ошибка |
3 | Неправильные параметры |
4 | Торговый сервер занят |
5 | Старая версия клиентского терминала |
6 | Нет связи с торговым сервером |
7 | Недостаточно прав |
8 | Слишком частые запросы |
9 | Недопустимая операция нарушающая функционирование сервера |
64 | Счет заблокирован |
65 | Неправильный номер счета |
128 | Истек срок ожидания совершения сделки |
129 | Неправильная цена |
130 | Неправильные стопы |
131 | Неправильный объем |
132 | Рынок закрыт |
133 | Торговля запрещена |
134 | Недостаточно денег для совершения операции |
135 | Цена изменилась |
136 | Нет цен |
137 | Брокер занят |
138 | Новые цены |
139 | Ордер заблокирован и уже обрабатывается |
140 | Разрешена только покупка |
141 | Слишком много запросов |
145 | Модификация запрещена, так как ордер слишком близок к рынку |
146 | Подсистема торговли занята |
147 | Использование даты истечения ордера запрещено брокером |
148 | Количество открытых и отложенных ордеров достигло предела, установленного брокером. |
Коды ошибок выполнения MQL4 программы:
Значение | Описание |
---|---|
4000 | Нет ошибки |
4001 | Неправильный указатель функции |
4002 | Индекс массива — вне диапазона |
4003 | Нет памяти для стека функций |
4004 | Переполнение стека после рекурсивного вызова |
4005 | На стеке нет памяти для передачи параметров |
4006 | Нет памяти для строкового параметра |
4007 | Нет памяти для временной строки |
4008 | Неинициализированная строка |
4009 | Неинициализированная строка в массиве |
4010 | Нет памяти для строкового массива |
4011 | Слишком длинная строка |
4012 | Остаток от деления на ноль |
4013 | Деление на ноль |
4014 | Неизвестная команда |
4015 | Неправильный переход |
4016 | Неинициализированный массив |
4017 | Вызовы DLL не разрешены |
4018 | Невозможно загрузить библиотеку |
4019 | Невозможно вызвать функцию |
4020 | Вызовы внешних библиотечных функций не разрешены |
4021 | Недостаточно памяти для строки, возвращаемой из функции |
4022 | Система занята |
4050 | Неправильное количество параметров функции |
4051 | Недопустимое значение параметра функции |
4052 | Внутренняя ошибка строковой функции |
4053 | Ошибка массива |
4054 | Неправильное использование массива-таймсерии |
4055 | Ошибка пользовательского индикатора |
4056 | Массивы несовместимы |
4057 | Ошибка обработки глобальныех переменных |
4058 | Глобальная переменная не обнаружена |
4059 | Функция не разрешена в тестовом режиме |
4060 | Функция не подтверждена |
4061 | Ошибка отправки почты |
4062 | Ожидается параметр типа string |
4063 | Ожидается параметр типа integer |
4064 | Ожидается параметр типа double |
4065 | В качестве параметра ожидается массив |
4066 | Запрошенные исторические данные в состоянии обновления |
4067 | Ошибка при выполнении торговой операции |
4099 | Конец файла |
4100 | Ошибка при работе с файлом |
4101 | Неправильное имя файла |
4102 | Слишком много открытых файлов |
4103 | Невозможно открыть файл |
4104 | Несовместимый режим доступа к файлу |
4105 | Ни один ордер не выбран |
4106 | Неизвестный символ |
4107 | Неправильный параметр цены для торговой функции |
4108 | Неверный номер тикета |
4109 | Торговля не разрешена |
4110 | Длинные позиции не разрешены |
4111 | Короткие позиции не разрешены |
4200 | Объект уже существует |
4201 | Запрошено неизвестное свойство объекта |
4202 | Объект не существует |
4203 | Неизвестный тип объекта |
4204 | Нет имени объекта |
4205 | Ошибка координат объекта |
4206 | Не найдено указанное подокно |
4207 | Ошибка при работе с объектом |
Если Вы хотите изучать язык MQL или вам понравилась данная публикация — Вы можете подписаться на получение новых материалов сайта mql4you.ru по
RSS
или по e-mail:
Другие публикации рубрики «FAQ по MQL и Metatrader»:
- Вопрос №9 «Как заставить работать советник при выключенном компьютере?»
- Вопрос №8 «Как рассчитать размер лота в зависимости от размера стоплосса?»
- Можно ли настроить нестандартный тайм-фрейм в MT4? — Вопрос №7
- Вопрос №6 «Как уменьшить размер папки с терминалом MT4?»
- Вопрос №4 — «Выключение компьютера из MQL»
- Вопрос №3
- Вопрос №2
- Вопрос №1