Форум » Консультации по программированию » Я в шоке! Подскажите. » Ответить

Я в шоке! Подскажите.

Sergey: Всем привет! Я в шоке! Выпал из рынка на 3,5 месяца, а тут такие перемены. Игорь подскажи, где можно ознакомиться с изменениями в MQL4? Хотел перенести все данные на новый комп, но некоторые индикаторы после компиляции перестают работать. Вот один из них. http://gfile.ru/a8cCP Хотя не перекомпилированные файлы работают. Компиляция ошибок в коде не выявляет. Но при отладке выдается ошибка формирования массива стр.67. Но в чем ошибка не пойму. Буду благодарен, если найдешь время исправить.

Ответов - 203, стр: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 All

Genry: Sergey пишет: Всем привет! Я в шоке! Выпал из рынка на 3,5 месяца, а тут такие перемены. Привет, Сергей! С возвращением ! У Игоря была статья про возможности обновленного MQL, но видимо надо его попросить обновить две базовые статьи: "Как создать советник, не обладая навыками программирования", "Как создать индикатор, не обладая навыками программирования", и шаблоны к ним.

Sergey: Genry пишет: Привет, Сергей! С возвращением Спасибо Genry! Неотложных дел еще на месяц. Затем буду включаться в работу.

Genry: Sergey пишет: Всем привет! Я в шоке! Выпал из рынка на 3,5 месяца, а тут такие перемены. Игорь подскажи, где можно ознакомиться с изменениями в MQL4? Хотел перенести все данные на новый комп, но некоторые индикаторы после компиляции перестают работать. ... Хотя не перекомпилированные файлы работают. Компиляция ошибок в коде не выявляет. Но при отладке выдается ошибка формирования массива стр.67. Но в чем ошибка не пойму. Буду благодарен, если найдешь время исправить. Статья на MQL4 по теме. В статье рассмотрены возможные ошибки, возникающие при компиляции старых программ и методы их устранения. Может что пригодится. 1.Ошибки компиляции ◾1.1. Идентификатор совпадает с зарезервированным словом ◾1.2. Специальные символы в наименованиях переменных и функций ◾1.3. Ошибки использования оператора switch ◾1.4. Возвращаемые значения у функций ◾1.5. Массивы в аргументах функций 2.Ошибки времени выполнения ◾2.1. Выход за пределы массива (Array out of range) ◾2.2. Деление на ноль (Zero divide) ◾2.3. Использование 0 вместо NULL для текущего символа ◾2.4. Строки в формате Unicodе и их использование в DLL ◾2.5. Совместное использование файлов ◾2.6. Особенность преобразования datetime 3.Предупреждения компилятора ◾3.1. Пересечения имен глобальных и локальных переменных ◾3.2. Несоответствие типов ◾3.3. Неиспользуемые переменные


Scriptong: Исправленный индикатор - здесь. Ошибки: 1. Выход за пределы массивов при расчете начального бара (переменная limit). В момент начала работы индикатора выражение limit = Bars - counted_bars; указывает на бар с индексом Bars, которого не существует. Bars - это общее количество баров на графике, которое индексируется с нуля. Поэтому последний бар графика будет иметь индекс Bars - 1. Чтобы подобное не происходило, нужно начинать обработку баров с того бара, для которого будет произведен корректный расчет всех значений индиктаора. В общем случае можно поступить так: limit = (int)MathMin(Bars - counted_bars, Bars - 1); Но в данном случае это тоже неправильно, т. к. необходимо обеспечить расчет средней скользящей с периодом Periods. То есть от конца графика нужно отодвинуться еще на Periods баров: limit = (int)MathMin(Bars - counted_bars, Bars - Periods - 1); 2. Связывание индикаторных буферов с индексами произведено неправильно: if( !SetIndexBuffer(0,e7) && !SetIndexBuffer(1,e2) && !SetIndexBuffer(2,e3) && !SetIndexBuffer(3,e4) && !SetIndexBuffer(4,e5) && !SetIndexBuffer(5,e6) && !SetIndexBuffer(6,e1) ) В новом MQL4 логические выражения обрабатываются по укороченному сценарию. Это означает, что если после вычисления первой части выражения результат всего выражения уже не изменится, то вторая и последующая части не вычисляются. В данном случае при успешном связывании первого буфера становится ясно, что выражение будет иметь результат false. Потому последующие буфера не связываются. Правильно делать так: if( !SetIndexBuffer(0,e7) || !SetIndexBuffer(1,e2) || !SetIndexBuffer(2,e3) || !SetIndexBuffer(3,e4) || !SetIndexBuffer(4,e5) || !SetIndexBuffer(5,e6) || !SetIndexBuffer(6,e1) ) В этом случае результат будет true как только появится первая ошибка связывания. Связывание остальных буферов уже не потребуется, т. к. индикатор и так не должен работать.

Sergey: Scriptong пишет: Исправленный индикатор Игорь, огромное спасибо! Как всегда исчерпывающий ответ.... Выход за пределы массивов при расчете начального бара я исправил, согласно новым функциям и правилам расчета индикаторов. И все равно не мог понять, почему индикатор не отображается. Оказалось проблема не только в этом. Разбор ошибок самый эффективный способ обучения... Очень благодарен за такую ветку. Удачи!

Sergey: В папке Include файл MovingAverages.mqh Функция double ExponentialMA(const int position,const int period,const double prev_value,const double &price[]) Что обозначает параметр prev_value? И каково предназначение символа & ( &price[])?

Scriptong: Sergey пишет: Что обозначает параметр prev_value? Это предыдущее значение экспоненциального среднего. Расчет значения текущего экспоненциального среднего производится на основе предыдущего значения. Подразумевается, что отсутствие предыдущего значения допускается только для самого первого расчета в истории. Для всех последующих значений предыдущие значения уже будут. Sergey пишет: И каково предназначение символа & ( &price[])? В MQL4/5 символ "&", поставленный в объявлении переменной перед ее именем, означает ссылку на переменную. Ссылка в большинстве случаев дает возможность изменять значение переменной внутри локальной функции так, что измененное значение будет доступно после окончания выполнения этой локальной функции. Вот простой пример. В одной функции объявляются переменные a и b, но их значения меняются в другой функции, в которой они не объявлены, а переданы по ссылке: void SwapVars(int &a, int &b) { int c = a; a = b; b = c; } void OnTick() { int a = 5; int b = 10; Print("До изменения: a = ", a, ", b = ", b); SwapVars(a, b); Print("После изменения: a = ", a, ", b = ", b); } В случае с функцией ExponentialMA ссылка имеет другое назначение. Перед типом данных установлен спецификатор const, который указывает на тот факт, что переменная, передаваемая по ссылке, не может быть изменена. Возникает логичный вопрос - а зачем тогда ссылка? Здесь она нужна по той причине, что в функцию требуется передать не одно значение, а целый массив. Если передавать массив по значению, то чем большее количество элементов в массиве, тем дольше он будет передаваться в функцию. Ссылка же дает возможность не передавать все значения массива, а просто указать место в памяти, где все эти значения лежат. При этом передающая функция "может быть спокойна за целостность данных", т. к. функция ExponentialMA не имеет право изменять данные массива price.

Sergey: Всегда имел проблемы с графикой. Вот код: ShowTypeBars(g_typebar, Volume|i|, Time|i|, clrText); //+-------------------------------------------------------------------------------------+ //| Отображение надписи типа сигнала | //+-------------------------------------------------------------------------------------+ void ShowTypeBars(string j_typebar, int j_volume, datetime j_time, color clr) { string name = PREFIX + j_time; ObjectDelete("name"); ObjectCreate(name, OBJ_TEXT, WindowFind(short_name), j_time, j_volume); ObjectSetText(name, j_typebar, 8, "Arial Black", clr); } //================================================= Не могу понять, почему после установки индикатора на нулевом баре не обновляется координата j_volume, хотя текущая гистограмма объема отображается верно.

Scriptong: Sergey пишет: Не могу понять, почему после установки индикатора на нулевом баре не обновляется координата j_volume, хотя текущая гистограмма объема отображается верно. Если руководствоваться только приведенным участком кода, то можно говорить только об одной ошибке. Вместо: ObjectDelete("name"); нужно написать: ObjectDelete(name); Ведь name - это имя переменной, а в коде оно используется как имя объекта. То есть удаляется объект с названием name, а создается потом совершенно другой объект с названием, которое находится в переменной name. При обновлении данных графических объектов лучше использовать не удаление/создание, а создание/модификацию: string name = PREFIX + ...; if (ObjectFind(name) < 0) // объекта нет - можно создавать { ObjectCreate(..); // назначение свойств объекта } else { // модификация объекта - те свойства, которые нужно изменить }

Sergey: Огромное спасибо! Первоначальный вариант бал такой, однако он так же не работал из-за ошибки в названии. void ShowTypeBars(string j_typebar, int j_volume, datetime j_time, color clr) { string name = PREFIX + j_time; ObjectDelete("name"); if (ObjectFind(name) < 0) { ObjectCreate(name, OBJ_TEXT, WindowFind(short_name), j_time, j_volume); ObjectSetText(name, j_typebar, 8, "Arial Black", clr); // текст объекта return; } ObjectMove(name, WindowFind(short_name), j_time, j_volume); ObjectSet(name, OBJ_TEXT, j_typebar); ObjectSet(name, OBJPROP_COLOR, clr); } Вернулся к нему, так как он работает более корректно при смене ТФ.

Scriptong: Да, именно о таком варианте я и говорил... Только не забудьте в нем убрать кавычки вокруг name.

Husanboy: Scriptong пишет: Добрый день уважаемый Игорь! Пожалуйста помогите с кодом?! Надо написать простой советник по этим индикатор(вложил ссылки ниже). Я сам попробовал, но не получилось. Почему то не возможно получить сигнал от буфер индикатора. Есть какой то способ получить сигнал от него? Условии: BUY когда линия индикатора више "0" и SELL когда линия индикатора ниже "0". Очень прощу от вас, пожалуйста помогите?! Жду ваше ответы. http://qclk.ru/kx/iWuC http://qclk.ru/kN/OGO51

Scriptong: Husanboy пишет: Есть какой то способ получить сигнал от него? Да. Причем способ очень простой, т. к. обращение к индикатору вовсе не требуется. Его код, отвечающий за расчет значения, умещается в одну строку. Итак, что нужно сделать: 1. Скопировать в код советника из кода индикатора все настроечные параметры индикатора и тело функции priceSwitch. 2. Получить значения индикатора на двух последних барах: double lastValue = priceSwitch(1) - iMA(NULL, NULL, DPOPeriod, MaShift, MaType, PriceType, 1); double preLastSwitch = priceSwitch(2) - iMA(NULL, NULL, DPOPeriod, MaShift, MaType, PriceType, 2); 3. Написать условие для сигнала Buy: if (lastValue > 0 && preLastValue < 0) { // Открыть Buy } 4. Написать условие для сигнала Sell: if (lastValue < 0 && preLastValue > 0) { // Открыть Sell } Советник готов.

Husanboy: Scriptong пишет: Классно!!! Все получилось. Вы самый лучший MQL программист в мире. Спасибо за помощь! Еще раз скажу Вы самый лучший! Спасибо бесконечно!!!!

Scriptong: Пожалуйста. Обращайтесь.

Husanboy: Scriptong пишет: Пожалуйста. Обращайтесь. Снова приветствую! Уважаемый Игорь есть еще просьба. Вложенный в архиве есть индикатор и советник. С кодом нету ни каких проблему, но не работает в новым билдом метатрейдера(компилируется без ошибки). Помогите пожалуйста?! http://qclk.ru/ks/0IUp

Genry: Husanboy пишет: С кодом нету ни каких проблему, но не работает в новым билдом метатрейдера(компилируется без ошибки). Помогите пожалуйста?! В прежней версии этот индикатор состоял из 2- частей - сам индикатор RSH.mq4 в котором был такой вызов второго индикатора HullMA.mq4 rel=(iCustom(NULL,0,"HullMA",periodoMA,tipoPrecio,tipoPromedio,0,k)-iCustom(NULL,0,"HullMA",periodoMA,tipoPrecio,tipoPromedio,0,i+1))+iCustom(NULL,0,"HullMA",periodoMA,tipoPrecio,tipoPromedio,0,i)-iCustom(NULL,0,"HullMA",periodoMA,tipoPrecio,tipoPromedio,0,k+1); В Вашей версии индикатор HullMA введен в индикатор RSH в виде функции и вызов теперь выглядит так: rel=hma[x]-hma[x+1]; Я взял старую отдельную версию индикатора HullMA и накинул ее на график - она ничего не рисует. Так что проблема в этой части индикатора и функция hma не работает. Если получится найти причину и заставить работать hma, то и RSH начнет выдавать сигналы советнику. Старая версия RSH и HullMA лежит здесь - на FOREX TSD

Genry: Нашел рабочую версию HullMA от Mladen - Здесь Так что можно посмотреть как работает советник со старой версией RSH и новой HullMA

Husanboy: Genry пишет: Нашел рабочую версию HullMA от Mladen - Здесь Так что можно посмотреть как работает советник со старой версией RSH и новой HullMA С уважением! Спасибо за помощь! Я сам исправил и это работает.

Genry: Husanboy пишет: Спасибо за помощь! Я сам исправил и это работает. Ok!

Sergey: Делаю советник, одна из функций которого, при параметре Symbols=false (не учитывать текущий символ) закрывать все открытые ордера, по всем валютным парам. FindOrders(); if (CommonProfit>Profit || CommonProfit<-AccountBalance()/100*Loss) { while (true) { CloseAllOrders(); FindOrders(); if (BuyCount==0 && SellCount==0) break; } } //+-------------------------------------------------------------------------------------+ //| Поиск своих ордеров. | //+-------------------------------------------------------------------------------------+ void FindOrders() { //---- BuyCount = 0; SellCount = 0; CommonProfit = 0; //---- for (int i = 0; i < OrdersTotal(); i++) // Используется весь список ордеров if (OrderSelect(i, SELECT_BY_POS)) // Убедимся, что ордер выбран if ((OrderMagicNumber() == MagicNumber || MagicNumber<0) // Ордер соответствует настройкам MagicNumber && (OrderSymbol() == Symbol() || !Symbols)) // Ордер соответствует настройкам валютной паре { CommonProfit += OrderProfit()+OrderSwap(); // Подсчет совокупного профита if (OrderType() == OP_SELL) SellCount++; if (OrderType() == OP_BUY) BuyCount++; } } //======================================================================================= //+-------------------------------------------------------------------------------------+ //| Закрытие позиций | //+-------------------------------------------------------------------------------------+ void CloseAllOrders() { for (int i = 0; i < OrdersTotal(); i++) // Используется весь список ордеров if (OrderSelect(i, SELECT_BY_POS)) // Убедимся, что ордер выбран if ((OrderMagicNumber() == MagicNumber || MagicNumber<0) // Ордер соответствует настройкам MagicNumber && (OrderSymbol() == Symbol() || !Symbols)) // Ордер соответствует настройкам валютной пары if (WaitForTradeContext()) { if (OrderType()==OP_BUY) if(OrderClose(OrderTicket(), OrderLots(), NT(Bid), Slippeg)) continue; if (OrderType()==OP_SELL) if(OrderClose(OrderTicket(), OrderLots(), NT(Ask), Slippeg)) continue; } } //======================================================================================= Однако, как только параметр устанавливается Symbols=false, советник виснет, когда поступает команда на закрытие позиций, хотя с текущим символом (при Symbols=true) проблем таких нет. В чем причина,понять не могу.

Scriptong: Sergey пишет: В чем причина,понять не могу. Такой код не сможет закрыть рыночные ордера, открытые по символам, отличным от текущего, т. к. цена закрытия указывается в ценах текущего символа. Для мультивалютных программ следует использовать такую конструкцию закрытия: double closePrice = SymbolInfoDouble(OrderSymbol(), SYMBOL_BID); if (OrderType() == OP_SELL) closePrice = SymbolInfoDouble(OrderSymbol(), SYMBOL_ASK); if (OrderClose(OrderTicket(), OrderLots(), closePrice, Slippeg)) continue; Думаю, во время зависаний в журнале эксперта было огромное количество ошибок - неправильные цены. Также при закрытии ордеров необходимо использовать обратный цикл перебора, а то будете закрывать ордера через один. Ну и не лучшее решение делать бесконечные циклы (while(true)). Как минимум, нужно использовать !IsStopped().

Sergey: Scriptong пишет: Такой код не сможет закрыть рыночные ордера, открытые по символам, отличным от текущего, т. к. цена закрытия указывается в ценах текущего символа. Для мультивалютных программ следует использовать такую конструкцию закрытия: Блин! Вот я ...... Спасибо!

Sergey: При компиляции выпадает предупреждение old_color и old_startTime (possible loss of data due to type conversion) Как правильно вывести данные по объекту? double old_y1=ObjectGet(ObjName, OBJPROP_PRICE1); double old_y2=ObjectGet(ObjName, OBJPROP_PRICE2); color old_color=ObjectGet(ObjName, OBJPROP_COLOR); datetime old_startTime=ObjectGet(ObjName, OBJPROP_TIME1); Аналогично для int A=round(B); Как преобразовать данные? В учебнике ничего нет, или не могу найти где смотреть.

Sergey: Возможно мой вопрос был не замечен, а потому просто повторюсь. Sergey пишет: В режиме # property strict При компиляции выпадает предупреждение old_color и old_startTime (possible loss of data due to type conversion) Как правильно теперь вывести данные по объекту? double old_y1=ObjectGet(ObjName, OBJPROP_PRICE1); double old_y2=ObjectGet(ObjName, OBJPROP_PRICE2); color old_color=ObjectGet(ObjName, OBJPROP_COLOR); datetime old_startTime=ObjectGet(ObjName, OBJPROP_TIME1); Аналогично для int A=round(B); Как преобразовать данные? В учебнике ничего нет, или не могу найти где смотреть.

Scriptong: Sergey пишет: Возможно мой вопрос был не замечен, а потому просто повторюсь. Да, действительно, не заметил. Прошу прощения. Sergey пишет: При компиляции выпадает предупреждение old_color и old_startTime (possible loss of data due to type conversion) Как правильно вывести данные по объекту? double old_y1=ObjectGet(ObjName, OBJPROP_PRICE1); double old_y2=ObjectGet(ObjName, OBJPROP_PRICE2); color old_color=ObjectGet(ObjName, OBJPROP_COLOR); datetime old_startTime=ObjectGet(ObjName, OBJPROP_TIME1); Аналогично для int A=round(B); Как преобразовать данные? В учебнике ничего нет, или не могу найти где смотреть. В учебнике этого и не может быть, т. к. учебник был написан для "старого" MQL4, а для нового языка разработчики выпускать учебник не планируют. Суть проблемы заключается в том, что функция ObjectGet возвращает значение типа double, которое в коде присваивается типам color и datetime соответственно. Таким образом, имеем неявное приведение типов данных с возможной потерей точности значения. Именно об этом напоминает компилятор, заботясь о том, чтобы программа оперировала валидными данными. Для решения вопроса есть два пути: 1. Использовать явное приведение типов, указав компилятору о том, что мы осознаем риск приведения типов и считаем, что в данном случае оно никак не скажется на работоспособности программы. Явное приведение типов выглядит так: color old_color=(color)ObjectGet(ObjName, OBJPROP_COLOR); datetime old_startTime=(datetime)ObjectGet(ObjName, OBJPROP_TIME1); int A = (int)round(B); 2. Использовать специальные функции нового MQL4. Правда, для переменных типа color и datetime этот способ не поможет - нет для них специальных функций. А потому все равно придется прибегать к явному приведению: color old_color=(color)ObjectGetInteger(0, ObjName, OBJPROP_COLOR); datetime old_startTime=(datetime)ObjectGetInteger(0, ObjName, OBJPROP_TIME, 1); Тем не менее, второй способ выгоднее тем, что делает код более универсальным. В будущем такой код будет проще портировать в MQL5.

Sergey: Спасибо! Все понятно и все работает!

Sergey: Хочу сделать контроль тестового времени эксперта с использованием глобальных переменных. Проблема в этой строке if (!GlobalVariableGet(ControlTimeExpert, TimeExpertControl)) return; // Вызов гл.пер. Если ставлю &TimeExpertControl идет ошибка, а без & срабатывает return. Возможно есть еще ошибки, не знаю. string ControlTimeExpert; double TimeExpertControl; int j_TimeExpertControl; datetime LastBar; if (LastBar != iTime(NULL,PERIOD_D1,0)) // Если новый день, то делаем проверку { if (!GlobalVariableCheck(ControlTimeExpert)) // Если гл.пер. нет, то создаем GlobalVariableSet(ControlTimeExpert,TimeCurrent()); // Время первого запуска советника if (!GlobalVariableGet(ControlTimeExpert, TimeExpertControl)) return; // Вызов гл.пер. j_TimeExpertControl=(int)TimeExpertControl; if (TimeCurrent()>j_TimeExpertControl+7*24*60*60) // 7 суток на проверку { Alert("Время тестирования советника истекло"); return; } } LastBar = iTime(NULL,PERIOD_D1,0);

Scriptong: На вскидку код верный, если подразумевать, что приведен только участок кода, а не весь код. Если же это весь код, тогда проблема в том, что переменная ControlTimeExpert не инициализирована. В ней нет названия глобальной переменной, отсюда и проблемы. А почему выбран такой странный способ контроля? Ведь глобальную переменную пользователь может удалить вручную, что сведет на нет всю защиту. Лучше прописывать абсолютную дату окончания периода тестирования в самом коде. При отсылке конкретному пользователю просто перекомпилировать эксперт: input datetime i_currentTime = __DATE__; datetime g_testExpirationDate; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- g_testExpirationDate = i_currentTime + PERIOD_D1 * 60; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if (TimeCurrent() > g_testExpirationDate) { Alert("Срок демо-версии истек. Советник отключен!"); ExpertRemove(); return; } }

Sergey: Для отдельных пользователей проблем нет. Но хочу разместить полнофункциональную демо версию на сайте и не заморачиваться под каждого клиента. Scriptong пишет: На вскидку код верный, если подразумевать, что приведен только участок кода, а не весь код. Если же это весь код, тогда проблема в том, что переменная ControlTimeExpert не инициализирована. Вообще то это весь код. Но кажется я понял. string ControlTimeExpert= "ControlTimeEA"; Теперь задана как название гл.переменной в начале. Попробую. Остался вопрос: Чтобы осознать не бесполезность своих действий, подскажите где хранятся глобальные переменные? Я к примеру не знаю, думаю и большинство трейдеров тоже.

Genry: Sergey пишет: Остался вопрос: Чтобы осознать не бесполезность своих действий, подскажите где хранятся глобальные переменные? Я к примеру не знаю, думаю и большинство трейдеров тоже. Сергей, список появится при нажатии в терминале клавиши F3, его можно менять и удалять. Могу добавить, что время тестового периода явно прописанное в программе, по крайней мере в старых билдах интерпретатора, видно при открытии файла в редакторе (типа FAR, например). Т.е получив сообщение о том, что тестовый период истек - нажимается F4, поиск по дате, а потом год меняется с 2014 на 3014... ву а ля. Поэтому после компиляции имел смысл обработать код программой защиты от декомпиляции - программой типа ex4_protector.

Sergey: Хреново! Идея была неплохой, как мне казалось. Развалилась простым нажатием F3. У кого есть идеи по-лучше?

Genry: Sergey пишет: Хреново! Идея была неплохой, как мне казалось. Развалилась простым нажатием F3. У кого есть идеи по-лучше? Перечитай мой пост выше - я его дополнил. Достаточно эффективно - перенос части кода в dll. Хотя в защите, как и во всем, лучше соизмерять затраты на защиту с доходами от продажи.

Sergey: А если в коде прописать создание отдельного файла хранящего информацию о времени. И затем, при необходимости, вызывать эти данные. Такое возможно? Если да, то где об этом почитать?

Genry: Sergey пишет: А если в коде прописать создание отдельного файла хранящего информацию о времени. И затем, при необходимости, вызывать эти данные. Такое возможно? Если да, то где об этом почитать? тогда надо будет шифровать данные в файле или подписывать, иначе их можно просто поменять. А чем плох вариант Игоря? Или есть желание менять только файл лицензии с демо на коммерческий без замены самой программы?

Sergey: Genry пишет: А чем плох вариант Игоря? Этот вариант я знаю и применяю. Но он больше подходит под отдельного пользователя. А хочется не заморачиваться под каждого. Дать возможность пользователям скачивать, но автоматически ограничивать время с начала первого запуска.

Genry: Sergey пишет: Если да, то где об этом почитать? Например здесь: Методы защиты и взлома программ для торговли на Форекс. Если все элементы секрета находятся у злоумышленника - то взлом это вопрос времени, добавит трудностей взломщику перенос части кода в dll. Наиболее стойкая защита - это когда разделен и секрет, и алгоритм работы программы. Здесь повторюсь: надо соизмерять собственные затраты на защиту (и неудобство от ее применения - например, пользователь не понимая значения файла с датой его случайно удалил, получил сбой в торговле большими деньгами и выкатил претензию) с доходами от продажи.

Genry: Sergey пишет: А хочется не заморачиваться под каждого. Дать возможность пользователям скачивать, но автоматически ограничивать время с начала первого запуска. Сергей, это путь сопряжен с различными проблемами. Отследить внешний файл и удалить его трудностей не составит, и снова наступил 1 запуск. Лучше сделать полноценное Демо для какого-то экзотического инструмента, а остальные валюты - в коммерческой версии. Короче дать полное представление о возможностях работы и ограничить по функционалу. Например история берется только за несколько дней назад, а не вся. Или работать будет только на Демо-счете или закрыт прогон в тестере. Вот и будут две версии, одна - демо доступная всем и всегда, вторая - коммерческая, которая берется с закрытого раздела сайта после оплаты или еще как-нибудь

Sergey: Genry пишет: Сергей, это путь сопряжен с различными проблемами. Отследить внешний файл и удалить его трудностей не составит Чисто с психологической точки зрения, не многие поймут, что ограничение можно снять удалив файл или гл.переменную. Именно потому, что это может быть так просто. Одни по незнанию, другие от большого знания. Genry пишет: Лучше сделать полноценное Демо для какого-то экзотического инструмента, а остальные валюты - в коммерческой версии. Короче дать полное представление о возможностях работы и ограничить по функционалу. Например история берется только за несколько дней назад, а не вся. Или работать будет только на Демо-счете или закрыт прогон в тестере. Этот стандартный подход хотелось немного дополнить. А что касается 1% спецов, то любая защита для них не проблема. И вряд ли они могут заинтересоваться моими продуктами.

Scriptong: Наиболее оптимальный способ демо-версии для советников - это пожизненная демо-версия для демо-счетов. В MQL4 тип счета определяется так: ENUM_ACCOUNT_TRADE_MODE accountType = (ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE); switch (accountType) { case ACCOUNT_TRADE_MODE_DEMO: Print("Демо-счет"); break; case ACCOUNT_TRADE_MODE_CONTEST: Print("Конкурсный счет"); break; case ACCOUNT_TRADE_MODE_REAL: Print("Реальный счет"); break; } Для индикаторов и скриптов придется придумывать что-то другое. Как вариант, если есть свой сайт, то можно проверять лицензию через WebRequest. Если же распространение не через сайт, то вполне достаточно того варианта, который я предложил в своем предыдущем посте. Перекомпиляция программы перед отправкой - секундное дело, больше времени потратите на отправку письма.

Sergey: Игорь пишет: Наиболее оптимальный способ демо-версии для советников - это пожизненная демо-версия для демо-счетов. Идея была в защите демо-версии от копировальщиков ордеров. Так или иначе, но Спасибо Всем! Есть вещи о которых не знал, теперь картина немного прояснилась.

Genry: Sergey пишет: Так или иначе, но Спасибо Всем! Есть вещи о которых не знал, теперь картина немного прояснилась.

Scriptong: Sergey пишет: Идея была в защите демо-версии от копировальщиков ордеров. Против лома нет приема, т. е. универсального способа. Поэтому потрудитесь защищать каждую версию индивидуально. В этом нет ничего сложного. Не думаю, что поток запросов на демо-версии больше, чем десяток в день (это огромная цифра!). А для каждого из этих запросов в день у Вас будет одна версия. Новый день - следующая версия. Скомпилили - раскидали по запросам.

Sergey: Столкнулся с такой проблемой. При выводе StopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL)*Point; // текущий уровень стопов через Alert StopLevel=0.0 Однако в реалии брокер не дает устанавливать уровень столосс ближе 30 pip на пятизнаке.(Пробовал в ручную перетащить не получилось.) При этом TP перетащить на Ask для Buy ордеров можно. Получается, что функция проверки расчета уровней SL и TP и корректной установки или модификации ордеров, использующие StopLevel в качестве минимального уровня - бутафория. Как выяснить действительный уровень? И почему они разные для SL и TP. Даже с учетом спреда ни чего не стыкуется. Спред всего 10-15 pip.

Scriptong: Sergey пишет: Столкнулся с такой проблемой. При выводе StopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL)*Point; // текущий уровень стопов через Alert StopLevel=0.0 Однако в реалии брокер не дает устанавливать уровень столосс ближе 30 pip на пятизнаке. Этим грешит Alpari. Возможно, есть другие брокеры. Стоплевел у них завуалированный - устанавливать стопы и отложки не ближе, чем 2 спреда. В своей библиотеке пришлось извратится следующим образом: m_symbolInfo.digits = (int) SymbolInfoInteger(m_symbol, SYMBOL_DIGITS); m_symbolInfo.point = SymbolInfoDouble(m_symbol, SYMBOL_POINT); m_symbolInfo.tickSize = SymbolInfoDouble(m_symbol, SYMBOL_TRADE_TICK_SIZE); m_symbolInfo.ask = SymbolInfoDouble(m_symbol, SYMBOL_ASK); m_symbolInfo.bid = SymbolInfoDouble(m_symbol, SYMBOL_BID); m_symbolInfo.spread = m_symbolInfo.ask - m_symbolInfo.bid; m_symbolInfo.freezeLevel = NormalizeDouble(MarketInfo(m_symbol, MODE_FREEZELEVEL) * m_symbolInfo.point, m_symbolInfo.digits); m_symbolInfo.stopLevel = MarketInfo(m_symbol, MODE_STOPLEVEL) * m_symbolInfo.point; // Коррекция Stop Level ля тех ДЦ, в которых его вроде бы нет (но на самом деле есть - скрытый) и для тех ДЦ, у которых есть. Для последних увеличивается на тик для повышения надежности if (m_symbolInfo.stopLevel == 0) { if (!m_isECN) m_symbolInfo.stopLevel = NormalizeDouble(2 * m_symbolInfo.spread, m_symbolInfo.digits); } else m_symbolInfo.stopLevel = NormalizeDouble(m_symbolInfo.stopLevel + m_symbolInfo.tickSize, m_symbolInfo.digits); Здесь m_isECN указывает программе, что нулевому стоплевелу можно верить. В противном случае используется вариант для Альпари.

Sergey: Scriptong пишет: Этим грешит Alpari. В самую точку. Счета стандарт. Еще слышал об Альфа-банке. Ситуацию понял. Но предложенный подход тоже не идеален. Во-первых потому, что спред плавающий, а m_symbolInfo.stopLevel возможно постоянный. Во-вторых под одну гребенку меряем все ДЦ с m_symbolInfo.stopLevel=0. Созрела такая идея: Первоначальную информацию о m_symbolInfo.stopLevel получаем в int OnInit(). Далее при не удачной установке или модификации ордеров с ошибкой 130, запускаем функцию расчета m_symbolInfo.stopLevel в теле программы. Однако возникает новая проблема: Если m_symbolInfo.stopLevel величина постоянная - увеличение лучше делать по-шагово, скажем + 2pip. Один раз рассчитал и дальше проблем нет. А если плавающая и зависящая от спреда, то как предложено выше, но на каждом тике. И при этом нужно четко знать коэффициент увеличения. Выходит нужен дополнительный механизм получения информации.

Scriptong: Sergey пишет: Но предложенный подход тоже не идеален. Подход не идеален лишь потому, что от пользователя требуется правильная установка параметра m_isECN. Если пользователь укажет, что счет относится к ECN, а это на самом деле не так, то советник не сможет открыть ни одного ордера, если стопы/профиты располагаются слишком близко к базовой цене ордера. Sergey пишет: Во-первых потому, что спред плавающий, а m_symbolInfo.stopLevel возможно постоянный Плавающий спред сейчас у большинства брокеров. Это нормально, и никак не влияет на правильную работу программы. Указанный мною код должен выполняться в начале каждого тика. Таким образом, информация постоянно обновляется. Насчет постоянного стоплевела не видел заверений ни у одного брокера. Такие нюансы почему-то не расписываются даже в договорах на 34-х страницах, а надо бы. Поэтому постоянный стоплевел мною воспринимается как несбыточная мечта, что и заставляет постоянно мониторить его размер (кроме, разве что, среды тестера). Sergey пишет: Во-вторых под одну гребенку меряем все ДЦ с m_symbolInfo.stopLevel=0. Нет. Я ведь указал на параметр m_isECN. Sergey пишет: Далее при не удачной установке или модификации ордеров с ошибкой 130 К сожалению, получение ошибки №130 не дает нам гарантии того, что мы оперируем неправильным стоплевелом. Она, к примеру, может говорить о том, что тип исполнения Market Execution и мы пытаемся установить стопы/профиты в момент открытия рыночного ордера. Правда, это не очень удачный пример, т. к. тип исполнения уже можно узнать в новом MQL4. А вот тип счета (ECN или стандарт) программно узнать невозможно. Отсюда и куча проблем с этим стоплевелом. Sergey пишет: Выходит нужен дополнительный механизм получения информации. Остается лишь надеяться на то, что в язык будет добавлена соответствующая функция, которая позволит программе самой определить тип счета, а значит и способ расчета Stop Level.

Sergey: Scriptong пишет: Нет. Я ведь указал на параметр m_isECN. Я не о счетах ECN. Я о брокерах, которые реально выдерживают Stop Level=0 на других счетах. Scriptong пишет: К сожалению, получение ошибки №130 не дает нам гарантии того, что мы оперируем неправильным стоплевелом. В таком случае мудрить с кодом не буду и как всегда последую опыту и профессионализму. Спасибо!

Scriptong: Sergey пишет: Я не о счетах ECN. Я о брокерах, которые реально выдерживают Stop Level=0 на других счетах. Возможно, я неправильно назвал параметр, ошибочно предполагая, что нулевой стоплевел есть только на счетах типа ECN или STP. То есть речь только о неудачном названии, но не о сути параметра. Смысл в том, чтобы пользователь поставил его в "Да", когда у брокера реально нулевой стоплевел. Посмотрел, как видно этот параметр в списке входных параметров. Получается не так уж и двузначно, вопрос конкретный: "Установить нулевой StopLevel?"

Sergey: Scriptong пишет: Смысл в том, чтобы пользователь поставил его в "Да", когда у брокера реально нулевой стоплевел. Я уже сообразил. Но в место да-нет решил установить коэффициент отступа в спредах, если счета не ECN. Предполагая, что отступ не всегда равен двум спредам, даю возможность настраивать StopLeve пользователю под любого брокера.

Scriptong: Sergey пишет: Но в место да-нет решил установить коэффициент отступа в спредах, если счета не ECN. Достаточно веское замечание. Ведь два спреда советует только Alpari. У других брокеров может быть иное значение. И тут уже разворачиваем фантазию дальше: что, если брокер нам заявит, что отступ в спредах также не постоянный? Хотя это, конечно, уже вообще беспредел, мухлеж чистой воды. От таких "брокеров" нужно уходить сразу же после подобного ответа.

Sergey: Вернусь к старой проблеме. Scriptong пишет: Перекомпиляция программы перед отправкой - секундное дело, больше времени потратите на отправку письма. Genry пишет: Поэтому после компиляции имел смысл обработать код программой защиты от декомпиляции - программой типа ex4_protector. Есть у кого нибудь рабочая версия перекомпилятора (free)? Скачал несколько версий ex4_protector, но все время выскакивают ошибки. Вроде все просто, но не получается.

Scriptong: Sergey пишет: Есть у кого нибудь рабочая версия перекомпилятора (free)? Скачал несколько версий ex4_protector, но все время выскакивают ошибки. Вроде все просто, но не получается. К сожалению, никогда не пользовался подобными "защитниками". Могу лишь сделать предположение о том, что эта версия разработана для старой версии MQL4. Для новой версии MQL4 подобные дополнительные защиты не нужны - новый компилятор на выходе выдает нативный код, который не так уж и легко взломать. Даже если его и взломают, то почему таким профи будет не под силу взломать любую другую защиту, тем более распространяемую в сети? Также учтите следующий момент. В новом MQL4 добавлены возможности ООП. А когда код написан объектно-ориентированным способом, то его декомпиляция в первозданный вид становится практически невозможной - слишком много различных вариантов, ни один современный компьютер не переберет. То есть взломщикам придется иметь дело не с декомпилированным вариантом, а именно с нативным кодом. Это затруднит их работу. Используйте ООП!

Sergey: Scriptong пишет: Используйте ООП "Чем дальше в лес, тем больше дров" Так и у меня. Что такое написание кода объектно-ориентированным способом?

Scriptong: Sergey пишет: Что такое написание кода объектно-ориентированным способом? Использование классов. Для этого любая задача разбивается на две и более подзадачи (как представляется удобнее для конкретного случая). Это подзадачи первого уровня. Они оформляются в виде классов, замкнутых в себе. Для передачи данных между этими классами создаются интерфейсы, которые гарантируют целостность данных в пределах одного класса, т. е. данные класса имеет право изменять только сам класс, и никто другой. Каждая задача первого уровня дробится на более мелкие задачи. В зависимости от степени дробления задачи второго уровня оформляются в виде классов, используемыми классами первого уровня, или в виде функций (методов) существующих классов. Простые задачи решать путем ООП, чаще всего, невыгодно. Но как только возникает задача хотя бы среднего уровня выигрыш от использования ООП становится очевидным.

Sergey: Спасибо. Конечно, с примерами было бы лучше, но в целом понятно.

Scriptong: Sergey пишет: Спасибо. Конечно, с примерами было бы лучше, но в целом понятно. К сожалению, одними примерами не отделаться. На эту тему пишут книги по 300-400 страниц. Классы неплохо описаны у Шилдта, даже несмотря на то, что это справочник, а не полноценный учебник. Читайте с 237-ой страницы. Там, конечно, все про C++, но почти все справедливо и для современного MQL4. Если что-то будет непонятно, спрашивайте.

Sergey: Можно ли объекты типа ARROW привязавать к таким координатам? Ошибка не выдается, однако объект не отображается. Объект нужно отобразить в окне графика справа от него. ObjectCreate("SD_D1_2", OBJ_ARROW, WindowFind(short_name), 10, 35); ObjectSet("SD_D1_2", OBJPROP_ARROWCODE, 110); ObjectSet("SD_D1_2", OBJPROP_WIDTH, 3); ObjectSet("SD_D1_2", OBJPROP_CORNER,1); // угол пивязки ObjectSet("SD_D1_2", OBJPROP_COLOR, UpColor);

Scriptong: Sergey пишет: Можно ли объекты типа ARROW привязавать к таким координатам? Нет, нельзя. Перечень типов объектов, для которых это разрешено, смотрите здесь. Sergey пишет: объект не отображается Он отображается, но на 10-ом баре по цене 35 (где-то вверху, если график EURUSD). Ошибку же проверяйте после вызова: ObjectSet("SD_D1_2", OBJPROP_CORNER,1); // угол пивязки

Sergey: Нужно ограничить демку по валютной паре. Однако некоторые ДЦ используют, как мне сказали, префиксы и суффиксы. Я с этим не сталкивался, а потому проверить правильность кода не могу. Прошу дать квалифицированную оценку. extern string prefix = ""; extern string suffix = ""; if (Symbol()!=prefix + "EURUSD" + suffix) { Alert("Demo version is only for EURUSD"); return(0); }

Scriptong: Делайте проще: if (StringFind(Symbol(), "EURUSD") < 0) { Alert("Demo version is only for EURUSD"); return(0); }

Sergey: Scriptong пишет: Делайте проще: Спасибо!

Sergey: Сейчас в темах затронуты уровни фибоначчи. Я раньше не сталкивался с проекцией. Она рисуется по 3 точкам. Как уровни проекции рассчитать математически для использования в советниках.

Scriptong: Sergey пишет: Я раньше не сталкивался с проекцией. Расширение Фибоначчи?

Sergey: Нет проекция

Scriptong: Sergey пишет: Нет проекция Этот объект называется "Расширение Фибоначчи" Расширение представляет собой совокупность двух объектов "Линии Фибоначчи": На рисунке: 1. Красные линии - линии от расширения Фибо. 2. Зеленая - от линий Фибо, которая совпадает с первой красной линией расширения. 3. Темно-желтая - от линий Фибо, которая совпадает со второй линией расширения. То есть мы взяли первый диапазон цен (зеленые линий Фибо и две первые точки расширения Фибо), потом в произвольном месте определили третью точку расширения (как бы откат), а затем хотим найти, куда будет двигаться цена, если за основу взять высоту волны от первых двух точек. Для этого взяли еще один объект "Линии Фибо" с таким же расстоянием между 0 и 100, как у первого объекта, но за ноль приняли третью точку расширения Фибо.

Sergey: Scriptong пишет: То есть мы взяли первый диапазон цен (зеленые линий Фибо и две первые точки расширения Фибо), потом в произвольном месте определили третью точку расширения (как бы откат), а затем хотим найти, куда будет двигаться цена, если за основу взять высоту волны от первых двух точек. Для этого взяли еще один объект "Линии Фибо" с таким же расстоянием между 0 и 100, как у первого объекта, но за ноль приняли третью точку расширения Все понял. Спасибо. Расчет делаем по первым двум точкам, а отсчитываем от третьей.

Sergey: Scriptong пишет: Этот объект называется "Расширение Фибоначчи" В теории, как я теперь понял, "проекция расширения Фибоначчи". Еще раз спасибо за подробное разъяснение.

Scriptong: Sergey пишет: В теории, как я теперь понял, "проекция расширения Фибоначчи". Так как мы подразумеваем терминал МТ4 (в крайнем случае - МТ5), то я ориентируюсь на его терминологию, чтобы не возникало путаницы.

Sergey: Нужно сделать push-уведомление и уведомление на e-mail. C push-уведомлением вроде бы разобрался: extern bool PushON = true; if (PushON) SendNotification("текст"); С e-mail-ом сложнее, не нашел примеров. Пожалуйста подскажите.

Genry: Sergey пишет: С e-mail-ом сложнее, не нашел примеров. Пожалуйста подскажите. Привет, Сергей! А в доке есть, главное еще настроить в терминале параметры почты. SendMail Посылает электронное письмо по адресу, указанному в окне настроек на закладке "Почта". Пример вызова: if (alertsEmail) SendMail("Message from MQL-Expert ", " Тrend UP"); bool SendMail( string subject, // заголовок string some_text // текст письма ); Параметры subject [in] Заголовок письма. some_text [in] Тело письма. Возвращаемое значение true – если письмо поставлено в очередь на отсылку, иначе возвращает false. Примечание Отсылка может быть запрещена в настройках, также может быть не указан адрес электронной почты. Чтобы получить информацию об ошибке, необходимо вызвать функцию GetLastError(). При работе в тестере стратегий функция SendMail() не выполняется.

Sergey: Спасибо Genry! Все оказалось так же просто.

Scriptong: Sergey пишет: Спасибо Genry! Все оказалось так же просто. Да, только предварительно необходимо настроить параметры почты в терминале. Когда первый раз с этим разбирался, то дня два мучался В итоге все это описал в статье Сигнальные уровни.

Sergey: Scriptong пишет: В итоге все это описал в статье Сигнальные уровни. Спасибо! Взял на заметку нюансы. Я решил написать советник для ручной парной торговли. Но не хочется изобретать велосипед. Тем более, что по первому разу получается не очень ... Игорь, есть ли у вас на примете готовый открытый код, подобный функции "Торговля в один клик" в терминале МТ4. Буду очень благодарен за помощь. Еще интересует такой вопрос - как в кнопке сделать надпись в две строчки. Спасибо.

Scriptong: Sergey пишет: Игорь, есть ли у вас на примете готовый открытый код, подобный функции "Торговля в один клик" в терминале МТ4 То, что есть, уже безнадежно устарело (к примеру, Графическое управление экспертом), т. к. основано на зацикливании экспертов. В новом MQL4 такие вещи решаются без зацикливания, используя события, т. е значительно проще. Sergey пишет: Еще интересует такой вопрос - как в кнопке сделать надпись в две строчки. Кнопка в MQL4 - это потомок класса CChartObjectText, основанном на графическом объекте Label. Поэтому можно наследовать свой объект от кнопки, в котором сделать эту кнопку, состоящей из двух Label, стоящих одна под другой. Если же неохота влезать в дебри ООП, то можно в область отображения кнопки добавить еще один Label, под основной надписью. Но в коде такой вариант будет выглядеть некрасиво, да и пользоваться им будет неудобно - всегда необходимо помнить о том, что при изменении свойств кнопки следует изменить свойства второго Label. При работе с ООП такая необходимость отпадет само собой.

Sergey: Как в объектах типа OBJ_EDIT ограничить ввод текста только форматом double?

Scriptong: Пока приходит в голову только вариант со своим полем ввода. То есть использовать OBJ_LABEL (или OBJ_TEXT), для которого подписаться на события нажатия клавиш (обработчик OnChartEvent, событие CHART_EVENT_KEYDOWN). В обработчике проверяется код нажатой клавиши (из параметра lparam) и, если это цифра или точка, то символ копируется в объект. Иначе- пропускается. Но для такого поля ввода не совсем понятно, как сделать, чтобы был виден курсор, да еще и мигал. Для других вариантов нужно смотреть класс CButton (MQL4\Include\Controls\Button.mqh) и думать, есть ли варианты по наследованию и переделке класса.

Sergey: Scriptong пишет: Пока приходит в голову только вариант со своим полем ввода. Спасибо. В целом идея понятна.

Sergey: Есть ли варианты! Нужно чтобы созданные индикатором объекты (вертикальные линии) не перерисовывались при смене ТФ, но удалялись при удалении индикатора с графика. Хочу задать диапазон расчета индикатора, который бы устанавливался перетаскиванием линий и не менялся при смене ТФ.

Scriptong: Нужно анализировать причину деинициализации: void OnDeinit(const int reason) { if (reason != REASON_CHARTCHANGE) { // Удалить объекты } else { // Не удалять объекты } }

Sergey: Scriptong пишет: Нужно анализировать причину деинициализации: Спасибо! Все получилось. void OnDeinit(const int reason) { //---- if (reason==REASON_REMOVE) { ObjectDelete(SIGN_RLINE); ObjectDelete(SIGN_LLINE); } DeleteAllObjects(); //---- return; }

Scriptong: Sergey пишет: Спасибо! Все получилось. М-м. Этот код удаляет объекты в любом случае. Или у Вас какая-то специальная версия функции DeleteAllObjects?

Sergey: Scriptong пишет: Или у Вас какая-то специальная версия функции DeleteAllObjects? Скорее наоборот. DeleteAllObjects отбирает объекты по префиксу, а у вертикальных линий их нет.

Scriptong: Sergey пишет: Скорее наоборот. DeleteAllObjects отбирает объекты по префиксу, а у вертикальных линий их нет. То есть индикатор создает объекты с префиксом. Эти объекты нужно удалять при каждой деинициализации. Также индикатор создает два объекта без префикса. Такие объекты нужно удалять только в случае удаления индикатора с графика. Такая вот логика выходит.

Sergey: Scriptong пишет: То есть индикатор создает объекты с префиксом. Эти объекты нужно удалять при каждой деинициализации. Также индикатор создает два объекта без префикса. Такие объекты нужно удалять только в случае удаления индикатора с графика. Верно. Вроде все просто, но по функционалу получилось круто. Огромное спасибо за помощь!

Sergey: Иногда сталкиваюсь с проблемами, когда при компиляции ошибок не выдается, но индикатор не работает должным образом или сильно тормозит комп. (при смене ТФ, смене символа, перезагрузки компа). Причины могут быть разные - не верно заданные размеры массивов, нехватка глубины исторических данных и много чего еще. Такие ошибки я ищу с помощью меток (Alert). Существует ли другая технология поиска скрытых ошибок? Как пользоваться "Отладкой" - слово обнадеживающее, а функционал не понятен. Где обо всем об этом можно узнать?

Scriptong: Sergey пишет: Существует ли другая технология поиска скрытых ошибок? Технология одна и та же - как можно подробнее вывести в журнал значения переменных и проследить по этим сообщениям маршрут выполнения программы. Если программа достаточно большая, то сначала распечатываются данные по основным блокам с целью нахождения проблемного блока, а затем переход на более низкий уровень (внутри проблемного блока) распечатки значений. И так до тех пор, пока не будет найдена ошибка. Sergey пишет: Как пользоваться "Отладкой" - слово обнадеживающее, а функционал не понятен. Где обо всем об этом можно узнать? Первоисточник описания всегда следует искать в том месте, где находится функционал, требующий описания. Так как отладчик является частью Meta Editor, то необходимо зайти в пункт меню "Справка" ME, нажать "Вызов справки", а затем просмотреть содержание. В данном случае путь к описанию таков: MetaEditor -> Разработка программ -> Отладка. Там дана первичная информация, так сказать, общее описание. Более подробно пример работы с отладчиком описан в статье "Отладка программ на MQL5". Отладка в МТ4 и МТ5 - процесс практически идентичный. На данный момент проблема отладки - невозможность использования ее в тестере. Поэтому, чаще всего, отладчик оказывается не у дел.

Sergey: Scriptong пишет: Технология одна и та же - как можно подробнее вывести в журнал значения переменных и проследить по этим сообщениям маршрут выполнения программы... Спасибо! Давно собирался задать этот вопрос, но все не решался.

Linker: В МТ5 уже доступна отладка на исторических данных, видимо будет и на МТ4.

Scriptong: Linker пишет: В МТ5 уже доступна отладка на исторических данных, видимо будет и на МТ4. Да, как раз недавно анонсирована. Так что ждем...

Sergey: Как прогнать индикатор в тестере на истории? Теоретически нужно запустить советник в режиме визуализации не выставляющий ордера и к графику прикрепить тестируемый индикатор. Есть ли еще варианты? Просто не все индикаторы ставятся таким образом на график.

Scriptong: Sergey пишет: Как прогнать индикатор в тестере на истории? Теоретически нужно запустить советник в режиме визуализации не выставляющий ордера и к графику прикрепить тестируемый индикатор. Есть ли еще варианты? Просто не все индикаторы ставятся таким образом на график. В МТ5 давно существует возможность полноценного тестирования индикаторов. А вот в МТ4 только-только появилось подобное. 810-й билд. Но пока это только бета-версия. Думаю, через пару-тройку недель состоится релиз и можно будет использовать новшество полноценно.

Sergey: Для экономии ресурсов компьютера расчеты некоторых индикаторов делаю на открытии бара. if (LastBar == Time[0]) return(0); int limit; int counted_bars=IndicatorCounted(); if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; if (limit>ShowBars) limit=ShowBars; if (LastBarDay!=iTime(NULL,PERIOD_D1,0)) { ObDeleteObjectsByPrefix(PREFIX); limit=ShowBars; } int i; for (i = limit-1; i >= 0; i--) Однако столкнулся с такой проблемой: После включения компа на следующий день, индикатор отрисовывается не верно. Как запрограммировать, чтобы при включении компа индикатор пересчитывал всю глубину истории, как при первом запуске. Прием типа if (LastBarDay!=iTime(NULL,PERIOD_D1,0)) { ObDeleteObjectsByPrefix(PREFIX); limit=ShowBars; } Не помогает...

Genry: Sergey пишет: Однако столкнулся с такой проблемой: После включения компа на следующий день, индикатор отрисовывается не верно. Как запрограммировать, чтобы при включении компа индикатор пересчитывал всю глубину истории, как при первом запуске. В этой ветке народ обсуждал вопросы подкачки истории при перезагрузке терминала: http://forum.mql4.com/ru/40689 и ее влияние на работу индикаторов.

Sergey: Genry пишет: В этой ветке народ обсуждал вопросы подкачки истории при перезагрузке терминала Просмотрел. Дело не в подкачке истории, а в новой инициации индикатора при перезагрузке терминала.

Genry: Sergey пишет: Дело не в подкачке истории, а в новой инициации индикатора при перезагрузке терминала. Обсуждение такой проблемы видел для мт5 здесь: https://www.mql5.com/ru/forum/694 Позже попалось еще здесь по мт4: https://www.mql5.com/ru/forum/34432

Sergey: Genry пишет: Обсуждение такой проблемы видел для мт5 здесь: https://www.mql5.com/ru/forum/694 Позже попалось еще здесь по мт4: https://www.mql5.com/ru/forum/34432 Спасибо Генри, все оказалось гораздо проще... int Start() { if (LastBar != Time[0]) { Тело индикатора LastBar = Time[0]; } return(0); } Но я в замешательстве, так как не могу понять, чем это отличается от int Start() { if (LastBar == Time[0]) return(0); Тело индикатора LastBar = Time[0]; return(0); } при перезапуске терминала.

Genry: Sergey пишет: LastBar == Time[0]; Просто у тебя уже усталость от редактирования и "глаз замылился". надо присвоить LastBar = Time[0] , а здесь вместо присвоения - сравнение.

Sergey: Genry пишет: надо присвоить LastBar = Time[0] , а здесь вместо присвоения - сравнение. Это я просто копировал верхнюю строку и ошибся. Сейчас исправил.. Вопрос остался..

Genry: Sergey пишет: .. Вопрос остался.. Тогда причина может быть в части "Тело индикатора"

Scriptong: Sergey пишет: Для экономии ресурсов компьютера расчеты некоторых индикаторов делаю на открытии бара. В индикаторах нет такой необходимости - делать проверку на открытие нового бара. Для этого у него есть IndicatorCounted() или prev_total и rates_total в новой версии MQL4. На основании этих данных необходимо определить, с какого бара следует обновить значения индикатора. В моих индикаторах обычно присутствует специальная функция: int GetRecalcIndex(int &total, const int ratesTotal, const int prevCalculated) { total = ratesTotal - /*здесь период индикатора или то, что его заменяет*/ - 2; if (i_indBarsCount > 0 && i_indBarsCount < total) total = MathMin(i_indBarsCount, total); if (prevCalculated < ratesTotal - 1) { BuffersInitializeAll(); return total; } return (MathMin(ratesTotal - prevCalculated, total)); } А после расчета индекса бара просто производится новый расчет значений индикатора на измененных барах: for (int i = limit; i >= 0; i--) { // Расчет }

Sergey: Scriptong пишет: В индикаторах нет такой необходимости - делать проверку на открытие нового бара. Но мне нужно, чтобы расчет на нулевом баре был произведен только на его открытии. А в случае return (MathMin(ratesTotal - prevCalculated, total)); как я понял, делается постоянный (по тиковый) расчет значений нулевого бара, но сам код возьму на вооружение. Спасибо.

Scriptong: Sergey пишет: Но мне нужно, чтобы расчет на нулевом баре был произведен только на его открытии. А в случае return (MathMin(ratesTotal - prevCalculated, total)); как я понял, делается постоянный (по тиковый) расчет значений нулевого бара, но сам код возьму на вооружение. Спасибо. Нет проблем: for (int i = limit; i > 0; i--) buffer[i - 1] = расчет

Sergey: Кажется наконец нашел в чем проблема. Индикатор установлен на 12 валютных парах. Имя объекта string name = PREFIX + IntegerToString(leftTime); Однако бывает совпадение по времени разных графиков. Предполагаю, что при загрузке терминала обновление данных различных пар идет не синхронно по барам. В результате объекты с одинаковыми именами перерисовываются. Решение - ввел PREFIX, как настраиваемый параметр. Но это не совсем удобно. Можно привязать к символу, но трейдер может открыть несколько одинаковых графиков. Есть ли варианты?

Genry: Sergey пишет: Можно привязать к символу, но трейдер может открыть несколько одинаковых графиков. Есть ли варианты? У каждого графика, даже если они одинаковые, есть его уникальный идентификатор, его возвращает функция ChartID. //--- get handle of the current chart long handle=ChartID();

Scriptong: Sergey пишет: Есть ли варианты? В дополнение к варианту Genry: прибавлять после префикса имя символа и, если нужно, таймфрейм графика.

Sergey: Игорь, в силу ряда реальных обстоятельств пришел к выводу, что брокеры учитывают алгоритм работы прибыльных советников появившихся в сети. Можно ли программно скрыть имя и настройки советника от брокера.

Genry: Sergey пишет: Игорь, в силу ряда реальных обстоятельств пришел к выводу, что брокеры учитывают алгоритм работы прибыльных советников появившихся в сети. Можно ли программно скрыть имя и настройки советника от брокера. Думаю при необходимости (или при желании) брокер может со своей стороны через МТ получить доступ к файлам трейдера и выкачать необходимую информацию. Так что имеет смысл, получив прибыльного советника, с него вообще не торговать, а копировать его сигналы на пустой МТ где стоит только копировальщик сделок - имитируя ручное исполнение команд.

Sergey: Genry пишет: Так что имеет смысл, получив прибыльного советника, с него вообще не торговать, а копировать его сигналы на пустой МТ где стоит только копировальщик сделок - имитируя ручное исполнение команд. Как вариант да, а еще можно выставить 0 магик, сойдет за ручную торговлю. Я торговал 5 месяцев с доходностью 40-80% в месяц и проблем не было, пока не выставил советник на продажу через mql5. Еще ровно месяц торговал, а затем все постепенно свелось к безубытку и что обидно, на демке, как и прежде все в ажуре, по крайней мере, результаты на много лучше. Не хочу отказываться от продаж, вот и ищу варианты.

Genry: Sergey пишет: Еще ровно месяц торговал, а затем все постепенно свелось к безубытку и что обидно, на демке, как и прежде все в ажуре, по крайней мере, результаты на много лучше. Да, по видимому подобрали индивидуальный фильтр

Scriptong: Genry пишет: Думаю при необходимости (или при желании) брокер может со своей стороны через МТ получить доступ к файлам трейдера и выкачать необходимую информацию. Прямо теории заговора Неужели Вы всерьез думаете, что Meta Quotes предоставляют подобный сервис брокерам? Если бы такое было, то утечки информации не удалось избежать и давно появились бы скандалы. Более того, даже сами для себя Meta Quotes этого не сделают, потому как репутация дороже. Разного рода умельцы уже вдоль и поперек прочесали терминалы MT4 и MT5, несмотря на их хваленую защиту (не бывает абсолютной защиты). Если бы там был намек на выуживание информации, то Meta Quotes - не жилец.

Scriptong: Sergey пишет: Игорь, в силу ряда реальных обстоятельств пришел к выводу, что брокеры учитывают алгоритм работы прибыльных советников появившихся в сети. Можно ли программно скрыть имя и настройки советника от брокера. Поверьте, вывод в корне неверный. Рынок - это достаточно сложная система, являющаяся надстройкой над социальными процессами. А социальные процессы - это сложнее, чем термоядерные реакции. Для справки: управляемые термоядерные реакции только разрабатываются, но еще далеки от массового использования. Другое дело, что есть много брокеров, нечистых на руку, которые убивают прибыльные стратегии своим прямым вмешательством. Потому совет достаточно простой: торгуйте у нормальных брокеров (США, Канада, немного Западная Европа). Как только у брокера в регистрации видите какие-нибудь Виргинские о-ва - бегите от него.

Sergey: Scriptong пишет: Другое дело, что есть много брокеров, нечистых на руку, которые убивают прибыльные стратегии своим прямым вмешательством. Все брокеры отслеживают работу своих клиентов - информация от источника в кругах брокера. Однако данные по вмешательству в работу трейдера предположительные по косвенным признакам. Появились советники в которых авторы вводят параметр скрывающий название эксперта от брокера. Идея перестраховаться будет не лишней. Но как это реализовать не знаю.

Scriptong: Sergey пишет: Появились советники в которых авторы вводят параметр скрывающий название эксперта от брокера. Идея перестраховаться будет не лишней. Но как это реализовать не знаю. Тут придется немного попотеть. Схема такова: 1. Стратегия пишется в виде индикатора или неторгующего советника. 2. Все торговые операции выполняет специальный скрипт, который вызывается из программы (п. 1). 3. "Общение" между скриптом и программой производится через глобальные переменные, именованные каналы или специальную DLL. Я такое делал в прошлом году. Подобный подход позволяет увеличить скорость отдачи торговых приказов в 8 раз (по числу доступных торговых потоков в МТ4) за счет их распараллеливания.

Sergey: Scriptong пишет: 3. "Общение" между скриптом и программой производится через глобальные переменные, именованные каналы или специальную DLL. Я предполагал наличие иного варианта, так как в mql5 запрещены dll и обращения к иным программам. Scriptong пишет: Я такое делал в прошлом году. Подобный подход позволяет увеличить скорость отдачи торговых приказов в 8 раз (по числу доступных торговых потоков в МТ4) за счет их распараллеливания. Предложенный вариант тоже заинтересовал. Но для его реализации нужен пример (скажем при реализации советника на диверах) или более подробное описание (может в виде статьи). Есть еще вопрос: Установка TP и SL путем модификации ордера. if (!OrderModify(OrderTicket(), OpPrice, SL, TP,0)) { Alert("Ошибка модификации ордера ",Type," = ",GetLastError()); return(false); } Выскакивает ошибка - 139 Ордер заблокирован и уже обрабатывается Получается, что ордер уходит на модификацию, но не получив ответ о результате модификации советник продолжает работать. И на последующих тиках выскакивают ошибки. Раньше такого не было. Какие существуют варианты решения этой проблемы?

Scriptong: Sergey пишет: Предложенный вариант тоже заинтересовал. Но для его реализации нужен пример (скажем при реализации советника на диверах) или более подробное описание (может в виде статьи). К сожалению, там достаточно много кода получается. В статьях, которые бы читали другие (кратко и лаконично), его не опишешь. Sergey пишет: Есть еще вопрос: Установка TP и SL путем модификации ордера. if (!OrderModify(OrderTicket(), OpPrice, SL, TP,0)) { Alert("Ошибка модификации ордера ",Type," = ",GetLastError()); return(false); } Выскакивает ошибка - 139 Ордер заблокирован и уже обрабатывается Скорее всего здесь речь идет о "заморозке" ордера. Это еще одно ограничение кухонь наряду со Stop Level. Называется Freeze Level. Его значение можно получить через: MarketInfo(_Symbol, MODE_FREEZELEVEL); Если цена открытия отложенного ордера ближе к текущей цене, чем на уровень заморозки, то ничего с ордером уже нельзя будет сделать. То же самое касается, рыночного ордера, когда рынок подходит слишком близко к Stop Loss и Take Profit. В последние годы большинство брокеров устанавливают нулевой размер заморозки. Но, по всей видимости, еще остались динозавры .

Sergey: Scriptong пишет: Скорее всего здесь речь идет о "заморозке" ордера. Что же, придется ввести функцию корректной модификации ордера. и больше с ней не расставаться.

Scriptong: Да, Sergey пишет: Что же, придется ввести функцию корректной модификации ордера. и больше с ней не расставаться. Да, для каждой торговой операции перед ее выполнением следует выполнять целый комплекс проверок на предмет корректности параметров. Если речь идет о модификации отложенного ордера, то список таков: 1. Если изменяется цена открытия ордера, то: 1.1 Проверить расстояние между текущей рыночной ценой и текущей ценой открытия на предмет попадания в коридор Freeze Level. 1.2 Проверить расстояние между рынком и новой ценой открытия на предмет попадания в коридор Stop Level. 1.3 Если не изменяются Stop Loss и Take Profit, то проверить, возможно ли перемещение цены открытия так, чтобы Stop Loss и Take Profit не оказались в коридоре Stop Level. 2. Если изменяется цена Stop Loss, то проверить расстояние от нового Stop Loss до новой цены открытия ордера. Если расстояние менее Stop Level, то модификация невозможна. 3. Если изменяется цена Take Profit, то проверить расстояние от нового Take Profit до новой цены открытия ордера. Если расстояние менее Stop Level, то модификация невозможна. 4. Если изменяется время истечения ордера, то проверить, не будет ли новое время слишком близко к текущему времени. Зазор должен быть не менее 600 секунд.

Sergey: Игорь, помоги, я в тупике. Индикатор рассчитывается по старшему ТФ (WorkTF) на периоде int ShowBars , но должен отрисовываться на графике младшего ТФ. Проблема с проверкой и подкачкой данных. При установке индикатора сразу на младший ТФ, ошибка выдается. А вот при смене ТФ со старшего на младший нет. Индикатор не удаляется, но и не отрисовывается. В чем причина не могу разобраться и, соответственно, исправить ошибку. int OnInit() { datetime New_dtBegin = iTime(NULL,WorkTF,ShowBars); if(!IsAllBarsAvailable(New_dtBegin)) { Alert("Не достаточна глубина истории баров. Индикатор удален."); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //---- bool IsAllBarsAvailable(datetime dtBeginBar) { int lastBar = iBarShift(NULL, 0, dtBeginBar); if (GetLastError() == ERR_HISTORY_WILL_UPDATED) return (false); if(lastBar <= 0) return (false); // Проверка доступности баров for(int i=lastBar; i>=0; i--) if(iTime(NULL,0,i)==0) return (false); // Все бары доступны return (true); }

Scriptong: Sergey пишет: Проблема с проверкой и подкачкой данных. В OnInit еще рано делать выводы о том, закачана история по нужному ТФ или нет. Проверить достаточность баров правильнее так: if (iBars(NULL, WorkTF) <= ShowBars) { Alert("Недостаточная глубина истории. Индикатор отключен."); return INIT_FAILED; } А вот проверять, загружена история по нужному ТФ или нет, нужно уже в OnCalculate: int OnCalculate(...) { iTime(NULL, WorkTF, 1); if (GetLastError() != ERR_NO_ERROR) return prev_calculated; // История по ТФ WorkTF успешно загружена } Чтобы определить, почему индикатор не отображается, нужно посмотреть, код OnCalculate.

Sergey: Scriptong пишет: В OnInit еще рано делать выводы о том, закачана история по нужному ТФ или нет. Проверить достаточность баров правильнее так: На сколько я понял, пытаться подкачивать историю в OnInit не правильно. Тогда ошибка понятна. Исправил все с учетом рекомендаций, вроде работает. Огромное спасибо!

Scriptong: Sergey пишет: пытаться подкачивать историю в OnInit не правильно Как раз правильно. В OnInit всегда стоит начать подкачку данных, просто обратившись в первому бару нужного таймфрейма. Но вот проверять результат там еще рано.

Sergey: Scriptong пишет: Как раз правильно. В OnInit всегда стоит начать подкачку данных, просто обратившись к первому бару нужного таймфрейма. Игорь, а обращение к первому бару нужного таймфрейма запускает подкачку историии и в советниках тоже?

Scriptong: Sergey пишет: Игорь, а обращение к первому бару нужного таймфрейма запускает подкачку историии и в советниках тоже? В MQL4 -да. При обращении к любому бару таймфрейма терминал, по идее, проверяет полноту истории по указанному символу/таймфрейму. Если какого-то бара нет, то запускается процесс подкачки истории, а вызванная функция генерирует ошибку, возвращая 0. Поэтому и используется подобный механизм проверки полноты истории. Но, сразу скажу, он не является идеальным, т. к. изначально не задумывался разработчиками именно таким образом. Поэтому "на все 100" надеяться на него не приходится. В MQL5 для решения этой проблемы есть специально разработанный способ получения данных с сервера.

Sergey: Игорь, подскажи, как определить координату "y" в "окне" у осциллятора ( к примеру Sto), "окна" массштабируются, а координаты нет. Мне нудно отобразить значения показаний индикатора (объектами Wingdings) на всех ТФ в "окне" осциллятора.

Scriptong: Sergey пишет: Игорь, подскажи, как определить координату "y" в "окне" у осциллятора ( к примеру Sto), "окна" массштабируются, а координаты нет. Мне нудно отобразить значения показаний индикатора (объектами Wingdings) на всех ТФ в "окне" осциллятора. Нужно больше информации. Что подразумевается под координатой Y? По моему пониманию, Y в подокнах - это значение индикатора. Также может быть значение в пикселях от одного из четырех углов подокна. И, кстати, уточните, какие исходные данные Вы хотите использовать для определения координат точки?

Sergey: Scriptong пишет: По моему пониманию, Y в подокнах - это значение индикатора. Именно так. Текущее значение индикатора на всех ТФ в виде точки нужно отобразить под соответствующими надписями (М1.....MN) справа от индикатора текущего ТФ. Картинку я дал по Sto, но буду делать WPR . Осциллятор WPR ограничен значениями (-100-0). Я не знаю, как можно перевести значение индикатора в пиксели с учетом масштабирования индикатора. Единственная идея, которая мне пришла - это перевести координату Х надписи соответствующего ТФ в смещение показаний значения индикатора от нулевого бара. (Перевести координиту Х во время и рассчитать смещение в барах). Но я этого ни разу не делал, а возможно есть и другие способы. Нужна Ваша консультация - как грамотнее сделать.

Scriptong: Sergey пишет: Именно так. Текущее значение индикатора на всех ТФ в виде точки нужно отобразить под соответствующими надписями (М1.....MN) справа от индикатора текущего ТФ. Картинку я дал по Sto, но буду делать WPR . Осциллятор WPR ограничен значениями (-100-0). Я не знаю, как можно перевести значение индикатора в пиксели с учетом масштабирования индикатора. Единственная идея, которая мне пришла - это перевести координату Х надписи соответствующего ТФ в смещение показаний значения индикатора от нулевого бара. (Перевести координиту Х во время и рассчитать смещение в барах). Но я этого ни разу не делал, а возможно есть и другие способы. Нужна Ваша консультация - как грамотнее сделать. Тут получается, что Х-координата должна быть в пикселях, а Y - в значениях индикатора. Таким образом, проблема решается преобразованием не Х-координаты, а Y-координаты. Х-координата должна быть известна всегда - это смещение столбца "таблицы" относительно правого верхнего угла подокна. Далее нужно преобразовать значение индикатора в пиксели для подокна. Для этого существует функция ChartTimePriceToXY. Только с ее помощью преобразуйте именно "цену" (так будет представляться значение индикатора) в Y. Время и, соответственно, Х в данном случае преобразовывать не нужно.

Sergey: Scriptong пишет: Для этого существует функция ChartTimePriceToXY. Я уже пробовал делать через нее. ChartTimePriceToXY(0, WindowFind(short_name),TimeCurrent(), SlowWPR, X_SlowWPR, Y_SlowWPR); Но координата У, как выяснилось, отсчитывается от верхней границы графика, а не подокна. Т.е если поднять окно до верхней граници графика, то все отображается, правда все равно со смещением по у. В нормальном же виде, данные даже не попадают в зону видимости подокна. Чтобы использовать данный способ, надо как то определить координату У подокна в системе координат окна основного графика. Я не знаю, как это сделать.

Scriptong: Sergey пишет: Чтобы использовать данный способ, надо как то определить координату У подокна в системе координат окна основного графика. Я не знаю, как это сделать. Эта задача точно такая же: datetime GetAddBarToTime(datetime srcTime, int addBars) { int srcBar = iBarShift(NULL, 0, srcTime); if (srcBar >= addBars) return iTime(NULL, 0, srcBar - addBars); return iTime(NULL, 0, 0) + (addBars - srcBar) * PeriodSeconds(); } ... int subWnd = WindowFind("<имя индикатора>"); if (subWnd < 0) return; int x0, y0; int firstBar = WindowFirstVisibleBar(); int totalBars = WindowBarsPerChart(); datetime lastBarTime = GetAddBarToTime(iTime(NULL, 0, firstBar), totalBars); if (!ChartTimePriceToXY(0, subWnd, lastBarTime, indicator_maximum, x0, y0)) Print("ChartTimePriceToXY error: ", GetLastError()); else Print("x0 = ", x0, ", y0 = ", y0); Таким образом определяете координаты верхнего правого угла подокна относительно левого верхнего угла графика. А далее, получив координаты нужной точки, вычитаете из этих координат "нулевые" координаты.

Sergey: Scriptong пишет: Таким образом определяете координаты верхнего правого угла подокна относительно левого верхнего угла графика. А далее, получив координаты нужной точки, вычитаете из этих координат "нулевые" координаты. Игорь, огромное спасибо! Идея понятна, реализация удалась.

Sergey: Игорь, есть вопрос, но даже не знаю как его сформулировать, чтобы было понятно. Я сделал индикатор и советник на его основе. Обращение к данным индикатора сделал через iCustom. Теперь хочу встроить индикатор в советник. Проблема в том, что индикатор рассчитывается при помощи нескольких циклов, то есть нельзя взять и рассчитать нужные бары без циклов. В индикаторе, чтобы повторно не рассчитывать циклы используем limit = GetRecalcIndex(total, rates_total, prev_calculated); Как это можно реализовать в советнике?

Scriptong: Sergey пишет: Теперь хочу встроить индикатор в советник. Проблема в том, что индикатор рассчитывается при помощи нескольких циклов, то есть нельзя взять и рассчитать нужные бары без циклов. Сегодня уже отвечал на подобный вопрос в другом месте (Недискретные рэндж-бары). Суть в том, что для каждого конкретного индикатора пишется свой собственный алгоритм расчета данных в советнике. В большинстве случаев такой алгоритм будет проще, если все сделать правильно.

Sergey: Scriptong пишет: В большинстве случаев такой алгоритм будет проще, если все сделать правильно. А как правильно? В смысле, какие есть варианты? К примеру, я сейчас просто сокращаю количество проходов по циклам до достаточного и вместо динамического массива использую статический, плюс ограничиваю расчет только открытием нового бара. Была идея перекопирования массива со смещением на одну позицию, тогда в расчет попадает только последний бар. Но запнулся на проверке. Ведь возможны пробелы в следствии, к примеру, разрыва связи с терминалом.

Scriptong: Sergey пишет: А как правильно? В смысле, какие есть варианты? Вариантов настолько много, что все их не перебрать. Проблема в том, что даже если я покажу пример встраивания индикатора в советник, то этот пример подойдет только для одного конкретного индикатора. Сделать по образу и подобию для другого индикатора не получится. Общий смысл встраивания индикаторов, показания которых основаны на своих же предыдущих показаниях, заключается в повторении этой цепочки расчетов в советнике. Для этого не нужно создавать массив, длинной во всю историю котировок. Во многих случаях достаточно всего двух значений: предыдущего и текущего. Эти значения проходят по всей истории, передавая эстафету друг другу. Подобным образом работают индикатор Divergence_Viewer и советник Divergence. Чтобы не мучаться каждый раз со встраиванием индикатора во советник, я разработал единый класс CDivergence, который подключается к любой программе.

Sergey: Scriptong пишет: Чтобы не мучаться каждый раз со встраиванием индикатора во советник, я разработал единый класс CDivergence, который подключается к любой программе. Дааа! Пример явно выше моих познаний в программировании. Не думал, что все так сложно. Придется действовать по-старинке, решать проблемы по мере их поступления. Спасибо!

Scriptong: Sergey пишет: Дааа! Пример явно выше моих познаний в программировании. Не думал, что все так сложно. Придется действовать по-старинке, решать проблемы по мере их поступления. Спасибо! Индикатор дивергенций - сам по себе достаточно сложный инструмент. По этой причине это действительно сложный для понимания пример. Если есть какой-то простенький индикатор, его показания основаны на данных предыдущих баров и индикатор необходимо встроить в советник, то прикрепите его здесь. Я постараюсь показать, как это делается, чтобы желающие делать подобное самостоятельно хотя бы смогли понять сам принцип (в каком направлении думать).

Sergey: Scriptong пишет: По этой причине это действительно сложный для понимания пример. Проблема не в сложности. Раньше ты практически к каждой строчки кода писал комментарий. Я просто учил программирование на примерах. В новом МТ введено много дополнительных параметров - класс, структура и т.д. Справочник не дает развернутого понимания их возможностей применения на примерах, он скорее рассчитан на профессионалов с кратким описанием, да еще с таким, что само описание требует к нему описание. Получается, что не представляя в целом возможностей того или иного параметра и не имея описания при его применении сложно понять, что к чему.

Scriptong: Sergey пишет: Раньше ты практически к каждой строчки кода писал комментарий. Да, потому что раньше (на Адмирале) статьи писал именно для обучения других, т. е. в качестве примеров, т. к. была соответствующая договоренность с администрацией компании. Сейчас же подобных договоренностей ни с кем нет. Программы пишутся именно для использования, а не для примеров. Sergey пишет: В новом МТ введено много дополнительных параметров - класс, структура и т.д. Справочник не дает развернутого понимания их возможностей применения на примерах, он скорее рассчитан на профессионалов с кратким описанием, да еще с таким, что само описание требует к нему описание. Получается, что не представляя в целом возможностей того или иного параметра и не имея описания при его применении сложно понять, что к чему. Да, с обучением языкам MQL4 и MQL5 сейчас достаточно сложно, нет учебника, как было ранее для MQL4. Но возможности ООП в MQL точно такие же, как и в С++. Поэтому для освоения ООП достаточно прочитать соответствующие разделы в учебниках по С++. Также могу порекомендовать неплохую статью Дмитрия Федосеева. В ней он кратко описывает суть ООП. На мой взгляд, достаточно понятно.

Sergey: Scriptong пишет: Поэтому для освоения ООП достаточно прочитать соответствующие разделы в учебниках по С++. Также могу порекомендовать неплохую статью Дмитрия Федосеева. В ней он кратко описывает суть ООП. На мой взгляд, достаточно понятно. Спасибо, посмотрю.

Genry: Привет, Sergey! Я тоже задавал Игорю аналогичные вопросы здесь: http://scriptong.myqip.ru/?1-0-15-00000003-000-15-0 Жаль со временем пропадают файлы и скрины. Sergey, данный пример подойдет к описанной ситуации для решения задачи ? [pre]//+------------------------------------------------------------------+ //| VolumeRSI_v1.mq4 | //| Copyright © 2007, TrendLaboratory Ltd. | //| http://finance.groups.yahoo.com/group/TrendLaboratory | //| E-mail: igorad2003@yahoo.co.uk | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, TrendLaboratory Ltd." #property link "http://finance.groups.yahoo.com/group/TrendLaboratory" #property indicator_separate_window #property indicator_buffers 1 #property indicator_color1 LightBlue #property indicator_width1 2 #property indicator_level1 50 //---- input parameters extern int Price = 0; //Applied Price(0-Close;1-Open;2-High;3-Low;4-Median;5-Typical;6-Weighted) extern int Length = 14; // Period of evaluation extern int Smooth = 3; // Period of smoothing extern int MA_Mode = 2; // Mode of MA //---- buffers double SmRSI[]; double vRSI[]; double vBulls[]; double vBears[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- indicators IndicatorBuffers(4); SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,SmRSI); SetIndexBuffer(1,vRSI); SetIndexBuffer(2,vBulls); SetIndexBuffer(3,vBears); //---- name for DataWindow and indicator subwindow label string short_name="VolumeRSI("+Price+","+Length+","+Smooth+","+MA_Mode+")"; IndicatorShortName(short_name); SetIndexLabel(0,"VolumeRSI"); //---- SetIndexDrawBegin(0,Length+Smooth); return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { int shift, limit, counted_bars=IndicatorCounted(); double Price1, Price2, Bulls, Bears, AvgBulls, AvgBears; //---- if ( counted_bars < 0 ) return(-1); if ( counted_bars ==0 ) limit=Bars-1; if ( counted_bars < 1 ) for(int i=1;i<Length+Smooth-1;i++) {vRSI[Bars-i]=0; SmRSI[Bars-i]=0; vBulls[Bars-i]=0; vBears[Bars-i]=0;} if(counted_bars>0) limit=Bars-counted_bars; limit--; for( shift=limit; shift>=0; shift--) { Price1 = iMA(NULL,0,1,0,0,Price,shift); Price2 = iMA(NULL,0,1,0,0,Price,shift+1); Bulls = 0.5*(MathAbs(Price1-Price2)+(Price1-Price2)); Bears = 0.5*(MathAbs(Price1-Price2)-(Price1-Price2)); if(Bulls > 0) {vBulls[shift] = Volume[shift]; vBears[shift] = 0;} else if(Bears > 0) {vBears[shift] = Volume[shift]; vBulls[shift] = 0;} else if(Bears == Bulls) {vBears[shift]=0; vBulls[shift] = 0;} } for( shift=limit; shift>=0; shift--) { AvgBulls=iMAOnArray(vBulls,0,Length,0,MA_Mode,shift); AvgBears=iMAOnArray(vBears,0,Length,0,MA_Mode,shift); if (AvgBulls+AvgBears != 0) vRSI[shift] = 1.0/(1.0+AvgBears/AvgBulls); else if (AvgBulls+AvgBears==0) vRSI[shift] = 0; } for( shift=limit; shift>=0; shift--) SmRSI[shift] = 100*iMAOnArray(vRSI,0,Smooth,0,MA_Mode,shift); //---- return(0); } //+------------------------------------------------------------------+[/pre]

Sergey: Genry пишет: Sergey, данный пример подойдет к описанной ситуации для решения задачи ? Да вполне. Цикл рассчитанный на предыдущем цикле.

Sergey: Мой мозг сейчас "взорвется"! 1. for (int j = 0; j < OrdersTotal(); j++) // Используется весь список ордеров if (OrderSelect(j, SELECT_BY_POS)) // Убедимся, что ордер выбран 2. for (int j = OrdersTotal(); j > 0; j--) // Используется весь список ордеров if (OrderSelect(j, SELECT_BY_POS)) // Убедимся, что ордер выбран В терминале два отложенных ордера. Почему в первом случае выбирается только один ордер, а во втором как положено два.

Scriptong: Sergey пишет: В терминале два отложенных ордера. Почему в первом случае выбирается только один ордер, а во втором как положено два. Немного путаете: как положено, два ордера, будет выбираться только в первом случае. Потому что, если OrdersTotal() = 2, то будет обращение к ордерам с индексами 0 и 1 (нумерация ведется с нуля). Тут все верно. А вот второй случай описан неправильно. Цикл тоже проходит две итерации (i = 2 и i = 1), но ордера с индексом 2 нет в списке рабочих. Поэтому OrderSelect возвращает false и правильно предостерегает от обращения к ордеру, который не выбран.

Sergey: Scriptong пишет: Немного путаете: как положено, два ордера, будет выбираться только в первом случае. Потому что, если OrdersTotal() = 2, то будет обращение к ордерам с индексами 0 и 1 (нумерация ведется с нуля). Тут все верно. А вот второй случай описан неправильно. Цикл тоже проходит две итерации (i = 2 и i = 1), но ордера с индексом 2 нет в списке рабочих. Поэтому OrderSelect возвращает false и правильно предостерегает от обращения к ордеру, который не выбран. В том то и проблема, что я ничего не путаю. Цикл удаления отложенных ордеров. В первом случае один ордер всегда остается в рынке, а вот во втором удаляются все. Я рассуждаю, как Вы. Вот и не могу понять , почему? Логически правильный первый вариант, а работает второй. Вот я и подумал, что возможно обращение идет не с 0, а 1 ордера. Но цикл с закрытием рыночных ордеров работает правильно именно по первому варианту.

Scriptong: Sergey пишет: Цикл удаления отложенных ордеров. Теперь понятна проблема. Если требуется удалять/закрывать ордера, то следует использовать именно обратный цикл, как во втором случае. Только он должен выглядеть следующим образом: for (int i = OrdersTotal() - 1; i >= 0; i--) Потому как при прямом переборе ордеров удаление любого ордера из середины списка приводит к изменению индексов всех остальных ордеров. Простой пример. Есть три ордера с тикетами А, В и С. Соответственно у ордера А индекс в списке ордеров 0, у В - 1, у С - 2. Если идти прямым перебором, то сначала удаляем ордер А. Сразу после его удаления ордер В получает индекс 0, ордер С - 1. Но в цикле то мы переходим ко второй итерации (i = 1), т. е. к ордеру с индексом 1. В итоге удаляем ордер С и получаем окончание цикла. Выходит, что ордер В так и не будет удален.

Sergey: Scriptong пишет: Потому как при прямом переборе ордеров удаление любого ордера из середины списка приводит к изменению индексов всех остальных ордеров. Игорь, огромное спасибо, наконец разобрался.

Sergey: Кажется я разобрался.... Проблема аналогична той, которую я описывал 03.10.2015. - взаимодействие сервера брокера и терминала. Но возможно я не прав, поэтому приведу код полностью. for (int j = 0; j < OrdersTotal(); j++) // Используется весь список ордеров if (OrderSelect(j, SELECT_BY_POS)) // Убедимся, что ордер выбран { Alert(OrderTicket()); if (OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() // ордер должен принадлежать текущей валютной паре && OrderType()>1) if(!DeleteDeal(OrderTicket()))return; } //---- bool DeleteDeal(int ticket) { if ( OrderSelect(ticket, SELECT_BY_TICKET) && // Существует ордер с заданным.. OrderCloseTime() == 0 && // ..тикетом и ордер не закрыт ... OrderType()>1) // ордер отложенный { if (!WaitForTradeContext()) return(false); // Свободен ли торговый поток? if (!OrderDelete(OrderTicket())) // Если ордер не удалось удалить, то результат { Alert(GetLastError()); return(False); // функции - False } } return(True); // Ордер удален } //---- Ошибки удаления или занятости торгового потока не выскакивают. Я выставил 8 отложенников - удалилось только 4. Alert при отборе ордеров даже не высветил их (не удаленных) тикеты. Этот глюк появился с 600 билдом - сервер еще не обработал ордер, а терминал продолжает отработку кода, при этом не выдает ошибки . С рыночными ордерами я эту проблему решил путем зацикливания функций закрытия и проверки ордеров.

Scriptong: Sergey пишет: Проблема аналогична той, которую я описывал 03.10.2015. - взаимодействие сервера брокера и терминала. Нет. Взаимодействие здесь не при чем. Проблема лишь с нумерацией ордеров в списке. При проведении торговых операций список, конечно же, претерпевает изменения. Не стоит производить торговые операции в цикле, а по большому счету вообще не рекомендую в одном обработчике событий (OnTick, OnChartEvent, OnTimer) совершать более одной торговой операции. То есть вне зависимости от результата выполнения операции следует перемещать выполнение программы на начало обработчика события. Ведь за время выполнение торгового запроса программа "спала" (в компьютерном исчислении пара секунд - это неимоверно много), а ситуация могла кардинально измениться.

Sergey: К стати, проблему взаимодействия сервера и терминала при модернизации ордеров, описанную мной 03.10.2015 (ордер не успевал обрабатываться сервером до следующего тика) пришлось решать так... if (!OrderModify(OrderTicket(), OpPrice, SL, TP,0)) { int Error = GetLastError(); if (Error!=139 && Error!=0) { Alert("Order modification error ",OrderTicket()," = ",Error); return(false); } } Фигня какая то!

Scriptong: Sergey пишет: ордер не успевал обрабатываться сервером до следующего тика Если так, то советник никогда не узнает о приходе этого пропущенного тика. Пропущенные тики для советников теряются безвозвратно. Обработка тика советником происходит только в том случае, если советник закончил обработку предыдущего тика.

Sergey: Scriptong пишет: Если так, то советник никогда не узнает о приходе этого пропущенного тика. Пропущенные тики для советников теряются безвозвратно. Обработка тика советником происходит только в том случае, если советник закончил обработку предыдущего тика. Здесь имелось ввиду, что по приходу следующего тика информация об ордере не поменялась. В результате идет повторная команда на модификацию ордера. И выдается ошибка 139 или 0.

Scriptong: Sergey пишет: Здесь имелось ввиду, что по приходу следующего тика информация об ордере не поменялась. Это более, чем странно. Никогда такого в МТ4 не замечал, т. к. в нем все команды синхронные. Такое может быть только в МТ5, но там для контроля есть соответствующие события. В МТ4, если советник отправляет торговый приказ, то эксперт будет бездействовать до момента получения ответа от сервера. За это время советником не будут восприниматься какие-либо тики, т. к. советник "висит". Если же ответ получен, то вместе с этим будет получено новое состояние ордеров, если они изменились. Ошибка 139 может быть получена, если отправляется приказ на модификацию/удаление/закрытие ордера, который в данный момент обрабатывается сервером в результате достижения ценой уровня его открытия (если отложенный ордер) или уровня Stop Loss/Take Profit. Еще такое может быть, если брокер использует уровень заморозки. Но тогда это брокер-динозавр, т. к. я уже давно не встречал, чтобы Freeze Level отличался от нуля.

Sergey: Scriptong пишет: В МТ4, если советник отправляет торговый приказ, то эксперт будет бездействовать до момента получения ответа от сервера. За это время советником не будут восприниматься какие-либо тики, т. к. советник "висит". Если же ответ получен, то вместе с этим будет получено новое состояние ордеров, если они изменились. Вот и я так думал. И нормального объяснения этому у меня пока нет. Времени прошло много, всего точно не упомнишь. Знаю, что когда исключил ошибку 139, стала появляться ошибка 0 (модификация по тем же параметрам SL и TP). Причем, если исключить Alert, в журнале ошибка не проходит. Думаю, в свете тех рекомендаций, которые ты дал выше, причина может скрываться в сортировке ордеров. Модификация сделана в цикле перебора всех ордеров. А советник допускает наличие нескольких ордеров одновременно. Придется заменить простой перебор функцией слежения за ордерами.

Scriptong: Sergey пишет: стала появляться ошибка 0 Так ведь ошибка 0 - это отсутствие ошибки. Значит, все нормально .

Sergey: Scriptong пишет: Так ведь ошибка 0 - это отсутствие ошибки. Значит, все нормально Не совсем. При модификации 0 означает и модификацию ордера по тем же параметрам. Ошибки с точки зрения торговли нет, а в алгоритме, скорее всего да.

Scriptong: Sergey пишет: При модификации 0 означает и модификацию ордера по тем же параметрам. Ошибки с точки зрения торговли нет, а в алгоритме, скорее всего да. В таком случае получите ошибку 1.

Sergey: Scriptong пишет: В таком случае получите ошибку 1. Учту.

Sergey: Столкнулся с непонятной ситуацией. Вот Журнал модификации ордера... .. из которого следует, что отправлено две заявки на модификацию ордера. Причем конечные значения модификации по ТР на 40 тиков отличаются от расчетных. Ошибок не выдается. Я поставил метки на количество расчетов ТР и модификаций, которые показали, что расчет и заявка на модификацию выполняются, как положено,один раз. У кого нибудь есть разумные объяснения?

Scriptong: Sergey пишет: Столкнулся с непонятной ситуацией. Немного подробнее про ситуацию. Речь идет об общем журнале или о журнале экспертов? Также желательно видеть всю строку сообщения (можно обрезать название эксперта, если речь об автоторговле).

Sergey: Спасибо всем .... разобрался. Оказалось все просто.....Одновременно работали 2 робота на одной паре и с одинаковыми идентификаторами.

Sergey: Нудно посчитать Profit при закрытии нескольких Sell ордеров. На сколько корректен такой алгоритм. for (int i = SellCount-1; i >= 0; i--) if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) if (OrderClose(OrderTicket(), OrderLots(), NT(Ask), 3)) Profit +=OrderProfit(); Или после закрытия нужно заново вытаскивать ордер уже из истории и лишь затем подсчитывать профит.

Scriptong: Sergey пишет: Нудно посчитать Profit при закрытии нескольких Sell ордеров. На сколько корректен такой алгоритм. for (int i = SellCount-1; i >= 0; i--) if (OrderSelect(SellTicket, SELECT_BY_TICKET) && OrderCloseTime() == 0) if (OrderClose(OrderTicket(), OrderLots(), NT(Ask), 3)) Profit +=OrderProfit(); Или после закрытия нужно заново вытаскивать ордер уже из истории и лишь затем подсчитывать профит. Нет, неправильно. Ведь SellCount уже было откуда-то взято. Значит, был цикл, который посчитал количество закрытых ордеров. Зачем тогда второй цикл, если все это можно было посчитать в одном цикле? Также непонятно, для чего представленный цикл нужен, если постоянно идет обращение к одному и тому же ордеру с тикетом SellTicket? Подсчет прибыли/убытка некоторых закрытых ордеров осуществляется следующим образом: double profit = 0.0; for (int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if (!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue; if (OrderType() != OP_SELL) continue; profit += OrderProfit() + OrderSwap() + OrderComission(); }

Sergey: Scriptong пишет: Также непонятно, для чего представленный цикл нужен, если постоянно идет обращение к одному и тому же ордеру с тикетом SellTicket? SellTicket|i| это массив тикетов Sell-ордеров отобранных для закрытия, не заметил, что квадратные скобки удалились. Меня интересует корректность такой последовательности if (OrderClose(OrderTicket(), OrderLots(), NT(Ask), 3)) Profit +=OrderProfit() + OrderSwap() + OrderComission(); или все же так if (OrderClose(OrderTicket(), OrderLots(), NT(Ask), 3)) if (OrderSelect(OrderTicket(), SELECT_BY_TICKET,, MODE_HISTORY) Profit +=OrderProfit() + OrderSwap() + OrderComission();

Scriptong: Sergey пишет: или все же так if (OrderClose(OrderTicket(), OrderLots(), NT(Ask), 3)) if (OrderSelect(OrderTicket(), SELECT_BY_TICKET,, MODE_HISTORY) Profit +=OrderProfit() + OrderSwap() + OrderComission(); Конечно же, так. Ведь в первом случае получаем данные непонятно какого ордера, т. к. перед обращением к функциям OrderXXX обязательно требуется выбор ордера при помощи OrderSelect.

Sergey: Игорь, подскажи, можно ли с помощью обработчика"CHARTEVENT_OBJECT_ENDEDIT — событие окончания редактирования текста в поле ввода графического объекта LabelEdit" задавать советнику значения типа double? Если ДА и если не сложно, напиши пример.

Scriptong: Sergey пишет: Игорь, подскажи, можно ли с помощью обработчика"CHARTEVENT_OBJECT_ENDEDIT — событие окончания редактирования текста в поле ввода графического объекта LabelEdit" задавать советнику значения типа double? Если ДА и если не сложно, напиши пример. В смысле, чтобы в обработчике void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) параметр sparam был не строковый, а типа double? Если так, то нельзя такое делать. Да и не нужно. Ведь строку легко преобразовать в вещественное значение при помощи функции StringToDouble. Таким образом, алгоритм будет следующий: void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { if (sparam != MY_LABEL_EDIT) return; string text = ObjectGetString(0, sparam, OBJPROP_TEXT); double value = StringToDouble(text); }

Sergey: Scriptong пишет: параметр sparam был не строковый, а типа double? Нет. К примеру я хочу изменить лот советнику на поле графика, не вызывая настройки советника. Конкретный пример- встроенная функция "Торговля в один клик". Лот можно менять набирая текст на клавиатуре. То есть мне нужно вывести часть настроек советника в таблицу на графике цены, так чтобы их можно было задавать, а они подхватывались советником, как будто были изменены в настройках.

Scriptong: Sergey пишет: Нет. К примеру я хочу изменить лот советнику на поле графика, не вызывая настройки советника. Конкретный пример- встроенная функция "Торговля в один клик". Лот можно менять набирая текст на клавиатуре. То есть мне нужно вывести часть настроек советника в таблицу на графике цены, так чтобы их можно было задавать, а они подхватывались советником, как будто были изменены в настройках. Посмотрите сам пример. Именно этому случаю он и соответствует.

Sergey: Scriptong пишет: Посмотрите сам пример. Именно этому случаю он и соответствует. Игорь, огромное спасибо! Разобрался. Сделал тестовый советник под это дело, все получилось.

Sergey: Игорь, подскажи в чем проблема.... Вот часть кода из советника #define NumOrd 1 // Количество элементов массива (стратегий) struct OrdInfo { double isPrice; double isSL; double isTP; double isLot; int isTicket; int isType; int isMagic; string isComment; }; OrdInfo g_ordInfo|NumOrd|; //---- void Terminal() { Qnt=0; // Счётчик количества ордеров //---- for(int i=0; i<OrdersTotal(); i++) // По рыночн. и отлож. ордерам { if(OrderSelect(i,SELECT_BY_POS)==true //Если есть следующ. && OrderSymbol()==Symbol() //.. и наша вал.пара && OrderMagicNumber()!=0 && OrderMagicNumber()<=NumOrd) //.. и он открыт экспертом { Alert("1"); g_ordInfo|i|.isPrice = OrderOpenPrice(); // Цена открытия ордера Alert("2"); g_ordInfo|i|.isSL=OrderStopLoss(); // Стоп-лосс ордера g_ordInfo|i|.isTP=OrderTakeProfit(); // Тейк-профит ордера g_ordInfo|i|.isLot=OrderLots(); // Количество лотов g_ordInfo|i|.isTicket=OrderTicket(); // Номер ордера g_ordInfo|i|.isType=OrderType(); // Тип ордера g_ordInfo|i|.isMagic=OrderMagicNumber(); // ID ордера g_ordInfo|i|.isComment=OrderComment(); // Комментарий ордера Qnt++; // Колич. ордеров } } } Советник прекрасно работает в тестере стратегий. Однако в реале зависает между метками 1 и 2. Причем комп не виснет, в журнале ничего. Но тики не проходят.

Scriptong: Sergey пишет: Советник прекрасно работает в тестере стратегий. Однако в реале зависает между метками 1 и 2. Причем комп не виснет, в журнале ничего. Но тики не проходят. Проблема в неправильной записи информации об ордерах в массив. Нумерация ордеров и нумерация элементов массива совпадает, а не должна бы. Поэтому в тестере и работает все прекрасно - там нет "чужих" ордеров, все свои. Правильная организация цикла такая: for(int i=0; i<OrdersTotal(); i++) // По рыночн. и отлож. ордерам { if(OrderSelect(i,SELECT_BY_POS)==true //Если есть следующ. && OrderSymbol()==Symbol() //.. и наша вал.пара && OrderMagicNumber()!=0 && OrderMagicNumber()<=NumOrd) //.. и он открыт экспертом { g_ordInfo[Qnt].isPrice = OrderOpenPrice(); // Цена открытия ордера g_ordInfo[Qnt].isSL=OrderStopLoss(); // Стоп-лосс ордера g_ordInfo[Qnt].isTP=OrderTakeProfit(); // Тейк-профит ордера g_ordInfo[Qnt].isLot=OrderLots(); // Количество лотов g_ordInfo[Qnt].isTicket=OrderTicket(); // Номер ордера g_ordInfo[Qnt].isType=OrderType(); // Тип ордера g_ordInfo[Qnt].isMagic=OrderMagicNumber(); // ID ордера g_ordInfo[Qnt].isComment=OrderComment(); // Комментарий ордера Qnt++; // Колич. ордеров } }

Sergey: Scriptong пишет: Правильная организация цикла такая: Огромное спасибо! Ведь все так явно, но почему-то только когда тебе ткнут пальцем. Понимаю, как не просто разбираться в чужих писульках... Спасибо!

Sergey: Блин! Проверил на реале в Alpfri все работает, а в FXOpen нет. Что за лажа?

Sergey: Нужно сделать описание объекта "кнопка". Рядом с объектом "кнопка" Установил "OBJ_LABEL" - "?". Как сделать, чтобы при наведении на "?" высвечивалось описание , а не имя объекта с ценой? Если выложите готовый блок алгоритма, буду очень признателен.

Scriptong: Sergey пишет: Нужно сделать описание объекта "кнопка". Рядом с объектом "кнопка" Установил "OBJ_LABEL" - "?". Как сделать, чтобы при наведении на "?" высвечивалось описание , а не имя объекта с ценой? Если выложите готовый блок алгоритма, буду очень признателен. Никакого алгоритма там нет. При создании кнопки нужно лишь воспользоваться назначением свойства всплывающей подсказки: ObjectSetInteger(0, name, OBJPROP_TOOLTIP, <текст подсказки>);

Sergey: Scriptong пишет: Никакого алгоритма там нет. При создании кнопки нужно лишь воспользоваться назначением свойства всплывающей подсказки:  цитата: ObjectSetInteger(0, name, OBJPROP_TOOLTIP, <текст подсказки>); Спасибо, на конец разобрался. ObjectSetString(0, name, OBJPROP_TOOLTIP, "текст"); Оказывается упускал первый параметр (chart_id) в результате получал ошибку и не мог понять в чем дело.

Sergey: Игорь, знаком ли ты с программой Forex Tester. Можно ли в ней тестировать индикаторы MQL4?

Scriptong: Sergey пишет: Игорь, знаком ли ты с программой Forex Tester. Можно ли в ней тестировать индикаторы MQL4? Да, и не только по наслышке. В 2009-ом году переводил для Forex Tester один из индикаторов MQL4. Тогда же писал рекламную статью для них в журнале Fortrader.ru (47-й номер) Sergey пишет: Можно ли в ней тестировать индикаторы MQL4? Нет, нельзя. Нужно переводить программы на Delphi или на C++. Но справедливости ради нужно сказать, что API Forex Tester'a намеренно копирует функционал MQL4. Там даже также функции называются. Поэтому чисто алгоритмически перевод не очень сложен.

Sergey: Scriptong пишет: Нужно переводить программы на Delphi или на C++. Спасибо.

Sergey: Игорь, Привет! Я решил попробовать MQL5. Начал с простого. Попробовал раскрасить OsMA используя вызов стандартного индикатора. Привожу код полностью #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 2 #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrGreen #property indicator_width1 2 #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrRed #property indicator_width2 2 #property indicator_level1 0 input int ShowBars = 300; input int FastEMA = 34; input int SlowEMA = 72; input int SignalSMA = 9; input ENUM_APPLIED_PRICE applied_price = PRICE_CLOSE; //--- indicator buffers double Up_OsMA_Buffer[]; double Dn_OsMA_Buffer[]; double OsMA_Buffer[]; //--- OsMA handles int OsMA_Handle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,Up_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(1,Dn_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(2,OsMA_Buffer,INDICATOR_CALCULATIONS); IndicatorSetInteger(INDICATOR_DIGITS,_Digits+3); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); IndicatorSetString(INDICATOR_SHORTNAME,"FW-OsMA("+string(FastEMA)+","+string(SlowEMA)+","+string(SignalSMA)+")"); PlotIndexSetString(0,PLOT_LABEL,"UpOsMA"); PlotIndexSetString(1,PLOT_LABEL,"DnOsMA"); OsMA_Handle = iOsMA(NULL,PERIOD_CURRENT ,FastEMA,SlowEMA,SignalSMA,applied_price); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Функция деинициализации | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(OsMA_Handle); // удаляет хэндл индикатора и освобождает память занимаемую им ArrayFree(Up_OsMA_Buffer); // освобождаем динамический массив от данных ArrayFree(Dn_OsMA_Buffer); ArrayFree(OsMA_Buffer); } //==================================================================== //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { int calculated=BarsCalculated(OsMA_Handle); if(calculated<rates_total) { Alert("Not all data of OsMA_Handle is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } //--- we can copy not all data int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated; to_copy++; } //--- get OsMA_Buffer buffer if(IsStopped()) return(0); //Checking for stop flag if(CopyBuffer(OsMA_Handle,0,0,to_copy,OsMA_Buffer)<=0) { Print("Getting OsMA_Buffer is failed! Error",GetLastError()); return(0); } //--- int k,limit; if(prev_calculated==0) limit=0; else limit=prev_calculated-1; //--- the main loop of calculations for(k=limit;k<rates_total;k++) { //Dn_OsMA_Buffer[k]=0.0; Up_OsMA_Buffer[k]=0.0; //--- calculate OsMA //if(OsMA_Buffer[k]>OsMA_Buffer[k+1]) //Up_OsMA_Buffer[k]=OsMA_Buffer[k]; //else Dn_OsMA_Buffer[k]=OsMA_Buffer[k]; Если вместо выше выделенного текста подставить нижнюю строку и (#property indicator_plots 1), то все работает, но в одном цвете. А так, нет. Отображается абра-кадабра какая-то. Up_OsMA_Buffer[k]=OsMA_Buffer[k]; } //--- return value of prev_calculated for next call return(rates_total); } //==================================================================== Что не правильно в выделенном тексте? А если задать ArraySetAsSeries(OsMA_Buffer,true); , то и этот вариант не работает.

Scriptong: Sergey пишет: Что не правильно в выделенном тексте? А если задать ArraySetAsSeries(OsMA_Buffer,true); , то и этот вариант не работает. Дело в том, что без ArraySetAsSeries нумерация баров в таймсериях ведется слева направо по графику. То есть бар с индексом 0 - где-то глубоко в истории, а текущий бар - это rates_total - 1. Поэтому для такой нумерации нужно: Начинать перебор баров не с бара 0, а с бара 1, т. к. нам нужно взять высоты гистограммы предыдущего бара, которого для бара 0 нет. То есть limit ставится не на 0, а на 1. Понять, что предыдущий бар, это не k + 1, а k - 1. Получаем такой вот работающий код: #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 2 #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrGreen #property indicator_width1 2 #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrRed #property indicator_width2 2 #property indicator_level1 0 input int ShowBars= 300; input int FastEMA = 34; input int SlowEMA = 72; input int SignalSMA=9; input ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE; //--- indicator buffers double Up_OsMA_Buffer[]; double Dn_OsMA_Buffer[]; double OsMA_Buffer[]; //--- OsMA handles int OsMA_Handle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,Up_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(1,Dn_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(2,OsMA_Buffer,INDICATOR_CALCULATIONS); IndicatorSetInteger(INDICATOR_DIGITS,_Digits+3); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); IndicatorSetString(INDICATOR_SHORTNAME,"FW-OsMA("+string(FastEMA)+","+string(SlowEMA)+","+string(SignalSMA)+")"); PlotIndexSetString(0,PLOT_LABEL,"UpOsMA"); PlotIndexSetString(1,PLOT_LABEL,"DnOsMA"); OsMA_Handle=iOsMA(NULL,PERIOD_CURRENT,FastEMA,SlowEMA,SignalSMA,applied_price); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Функция деинициализации | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(OsMA_Handle); // удаляет хэндл индикатора и освобождает память занимаемую им ArrayFree(Up_OsMA_Buffer); // освобождаем динамический массив от данных ArrayFree(Dn_OsMA_Buffer); ArrayFree(OsMA_Buffer); } //==================================================================== //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { int calculated=BarsCalculated(OsMA_Handle); if(calculated<rates_total) { Alert("Not all data of OsMA_Handle is calculated (",calculated,"bars ). Error",GetLastError()); return(0); } //--- we can copy not all data int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy=rates_total; else { to_copy=rates_total-prev_calculated; to_copy++; } //--- get OsMA_Buffer buffer if(IsStopped()) return(0); //Checking for stop flag if(CopyBuffer(OsMA_Handle,0,0,to_copy,OsMA_Buffer)<=0) { Print("Getting OsMA_Buffer is failed! Error",GetLastError()); return(0); } //--- int k,limit; if(prev_calculated==0) limit=1; else limit=prev_calculated-1; //--- the main loop of calculations for(k=limit;k<rates_total;k++) { Dn_OsMA_Buffer[k]=0.0; Up_OsMA_Buffer[k]=0.0; //-- calculate OsMA if(OsMA_Buffer[k]>OsMA_Buffer[k-1]) Up_OsMA_Buffer[k]=OsMA_Buffer[k]; else Dn_OsMA_Buffer[k]=OsMA_Buffer[k]; } //--- return value of prev_calculated for next call return(rates_total); }

Sergey: Спасибо! Но хотелось бы разобраться до конца. Как изменить код с использованием ArraySetAsSeries(OsMA_Buffer,true); Поскольку именно этот вариант в дальнейшем будет использован в советниках и рекомендован во всех статьях, а у меня не вышел.

Scriptong: Тогда нужно всю индексацию развернуть, а не просто использовать ArraySetAsSeries. Т. к. цикл расчета данных для каждого бара в "правильном" индикаторе должен следовать слева направо по графику, то нужно идти от rates_total (limit поддерживать) до 0 с декрементом.

Sergey: Scriptong пишет: Тогда нужно всю индексацию развернуть, а не просто использовать ArraySetAsSeries. Т. к. цикл расчета данных для каждого бара в "правильном" индикаторе должен следовать слева направо по графику, то нужно идти от rates_total (limit поддерживать) до 0 с декрементом. На словах я это понимаю, с практикой туго. Устанавливаем порядок индексации массива как в таймсерии if(CopyBuffer(OsMA_Handle,0,0,to_copy,OsMA_Buffer)<=0) { Print("Getting OsMA_Buffer is failed! Error",GetLastError()); return(0); } ArraySetAsSeries(OsMA_Buffer,true); Отобразим первые 200 баров for(int k=200;k>=0;k--) { Dn_OsMA_Buffer[k]=0.0; Up_OsMA_Buffer[k]=0.0; //-- calculate OsMA if(OsMA_Buffer[k]>OsMA_Buffer[k+1]) Up_OsMA_Buffer[k]=OsMA_Buffer[k]; else Dn_OsMA_Buffer[k]=OsMA_Buffer[k]; } Как не пересчитываю слева-направо или наоборот, получается фигня. В чем не прав не знаю. Я это смогу понять, лишь проанализировав правильный код. Не зачти за лишний труд. Плиз!

Scriptong: Sergey пишет: Как не пересчитываю слева-направо или наоборот, получается фигня. В чем не прав не знаю. Я это смогу понять, лишь проанализировав правильный код. Не зачти за лишний труд. Плиз! Установка признака таймсерии делается один раз, а не на каждом тике. И не для одного буфера, а для всех. Поэтому и проблема: #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 2 #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrGreen #property indicator_width1 2 #property indicator_type2 DRAW_HISTOGRAM #property indicator_color2 clrRed #property indicator_width2 2 #property indicator_level1 0 input int ShowBars= 300; input int FastEMA = 34; input int SlowEMA = 72; input int SignalSMA=9; input ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE; //--- indicator buffers double Up_OsMA_Buffer[]; double Dn_OsMA_Buffer[]; double OsMA_Buffer[]; //--- OsMA handles int OsMA_Handle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,Up_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(1,Dn_OsMA_Buffer,INDICATOR_DATA); SetIndexBuffer(2,OsMA_Buffer,INDICATOR_CALCULATIONS); ArraySetAsSeries(Up_OsMA_Buffer, true); ArraySetAsSeries(Dn_OsMA_Buffer, true); ArraySetAsSeries(OsMA_Buffer, true); IndicatorSetInteger(INDICATOR_DIGITS,_Digits+3); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,SlowEMA+SignalSMA-2); IndicatorSetString(INDICATOR_SHORTNAME,"FW-OsMA("+string(FastEMA)+","+string(SlowEMA)+","+string(SignalSMA)+")"); PlotIndexSetString(0,PLOT_LABEL,"UpOsMA"); PlotIndexSetString(1,PLOT_LABEL,"DnOsMA"); OsMA_Handle=iOsMA(NULL,PERIOD_CURRENT,FastEMA,SlowEMA,SignalSMA,applied_price); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Функция деинициализации | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { IndicatorRelease(OsMA_Handle); // удаляет хэндл индикатора и освобождает память занимаемую им ArrayFree(Up_OsMA_Buffer); // освобождаем динамический массив от данных ArrayFree(Dn_OsMA_Buffer); ArrayFree(OsMA_Buffer); } //==================================================================== //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { int calculated = BarsCalculated(OsMA_Handle); if(calculated < rates_total) { Alert("Not all data of OsMA_Handle is calculated (", calculated, "bars ). Error", GetLastError()); return(0); } //--- we can copy not all data int to_copy; if(prev_calculated>rates_total || prev_calculated<0) to_copy = rates_total; else { to_copy = rates_total-prev_calculated; to_copy++; } //--- get OsMA_Buffer buffer if(CopyBuffer(OsMA_Handle, 0, 0, to_copy, OsMA_Buffer)<=0) { Print("Getting OsMA_Buffer is failed! Error", GetLastError()); return(0); } //--- int k,limit; if(prev_calculated == 0) limit = rates_total - 2; else limit = fmin(rates_total - prev_calculated, rates_total - 2); //--- the main loop of calculations for(k = limit; k >= 0; --k) { Dn_OsMA_Buffer[k] = 0.0; Up_OsMA_Buffer[k] = 0.0; //-- calculate OsMA if (OsMA_Buffer[k] > OsMA_Buffer[k + 1]) Up_OsMA_Buffer[k] = OsMA_Buffer[k]; else Dn_OsMA_Buffer[k] = OsMA_Buffer[k]; } //--- return value of prev_calculated for next call return(rates_total); }

Sergey: Scriptong пишет: Установка признака таймсерии делается один раз, а не на каждом тике. И не для одного буфера, а для всех. Поэтому и проблема: Все точно! В этом проблема. А что с советниками? Вот пример на сайте mql5.com /---- indicator buffers double MA[]; // массив для индикатора iMA //---- handles for indicators int MA_handle; // указатель на индикатор iMA //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- создание указателя на объект - индикатор iMA MA_handle=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- заполнение массива MA[] текущими значениями индикатора iMA //--- в массив будет записано 100 элементов CopyBuffer(MA_handle,0,0,100,MA); //--- задаём порядок индексации массива MA[] как в MQL4 ArraySetAsSeries(MA,true); //--- а дальше делайте с этими данными всё что угодно, например: if(MA[0]>MA[1]) { //--- выполнение каких-то операций } } 1. Почему в советниках порядок индексации делается на каждом тике? 2. Есть ли возможность в советниках обновлять последние бары как в индикаторах, если для анализа нужно большое их количество. 3. И стоит ли использовать динамический массив, если для анализа брать 2 последних бара. Можно ли написать так: double MA[2]; // массив для индикатора iMA //---- handles for indicators int MA_handle; // указатель на индикатор iMA //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- создание указателя на объект - индикатор iMA MA_handle=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE); //--- задаём порядок индексации массива MA[] как в MQL4 ArraySetAsSeries(MA,true); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- заполнение массива MA[] текущими значениями индикатора iMA //--- в массив будет записано 2 элементов CopyBuffer(MA_handle,0,0,2,MA); //--- а дальше делайте с этими данными всё что угодно, например: if(MA[0]>MA[1]) { //--- выполнение каких-то операций } }

Scriptong: Sergey пишет: Можно ли написать так: Статический массив нельзя сделать таймсерией. Но ничего страшного в динамических массивах нет. Ведь функция CopyBuffer сама перераспределит память под массив при первом вызове. При последующих вызовах на это не будет уходить время. Поэтому вполне можно сделать так, проверяя попутно формирование нового бара: double MA[]; int MA_handle; void OnInit() { MA_handle = iMA(NULL, 0, 21, 0, MODE_EMA, PRICE_CLOSE); ArraySetAsSeries(MA, true); } void OnTick() { if (!IsNewBar()) return; if (CopyBuffer(MA_handle, 0, 0, 2, MA) != 2) { Print("Receiving MA values error N", GetLastError()); return; } if (MA[0] > MA[1]) { Print("MA[0] (", MA[0], ") greater than MA[1] (", MA[1], ")"); } else Print("MA[0] (", MA[0], ") less than MA[1] (", MA[1], ")"); } bool IsNewBar() { static datetime dtLastTime = 0; datetime arrdtTime[1]; if (CopyTime(Symbol(), PERIOD_CURRENT, 0, 1, arrdtTime) != 1) { Print("Receiving Time value error N", GetLastError()); return false; } bool bResult = (arrdtTime[0] != dtLastTime); dtLastTime = arrdtTime[0]; return bResult; }

Sergey: Игорь, огромное спасибо! Ситуацию по обращению к индикаторам прояснил.

Sergey: Столкнулся с не пониманием обращения к функции... в mql4. //+------------------------------------------------------------------+ //| Проверяет объем ордера на корректность | //+------------------------------------------------------------------+ bool CheckVolumeValueStart(double volume,string &description) { //--- минимально допустимый объем для торговых операций double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); if(volume<min_volume) { description=StringFormat("Объем меньше минимально допустимого SYMBOL_VOLUME_MIN=%.2f",min_volume); return(false); } //--- максимально допустимый объем для торговых операций double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); if(volume>max_volume) { description=StringFormat("Объем больше максимально допустимого SYMBOL_VOLUME_MAX=%.2f",max_volume); return(false); } //--- получим минимальную градацию объема double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP); int ratio=(int)MathRound(volume/volume_step); if(MathAbs(ratio*volume_step-volume)>0.0000001) { description=StringFormat("Объем не является кратным минимальной градации SYMBOL_VOLUME_STEP=%.2f, ближайший корректный объем %.2f", volume_step,ratio*volume_step); return(false); } description="Корректное значение объема"; return(true); } //============================================================================= Что делать с &description? if (!CheckVolumeValueStart(Lot,................)) return; Конечно, можно его убрать из функции, а в тексте заменить на Print. Но хотелось бы разобраться, что это значит и как правильно применить. Спасибо!

Admin: Sergey пишет: Что делать с &description? if (!CheckVolumeValueStart(Lot,................)) return; Конечно, можно его убрать из функции, а в тексте заменить на Print. Но хотелось бы разобраться, что это значит и как правильно применить. Этот аргумент функции нужно рассматривать как сообщение об ошибке. Пользоваться так: string description = ""; if (!CheckVolumeValueStart(Lot, description)) { Print(description); return; } Могу согласиться с тем, что в качестве примера такой прием вызывает недоумение. Скорее всего взято из какого-то готового проекта и вставлено как есть.

Sergey: Admin пишет: string description = ""; А вот это я и пропустил. Забыл объявить. Спасибо Игорь! Admin пишет: Могу согласиться с тем, что в качестве примера такой прием вызывает недоумение. Скорее всего взято из какого-то готового проекта и вставлено как есть. В маркете на mql5.com при валидации (автоматической проверки на наличие ошибок) это выдано в качестве примера. Авто контроль проверяет каждую из позиций: минимальный и максимальный лот, кратность шагу и достаточность свободных средств для стартового лота в советнике. Причем ошибка обязательно должна быть выведена в журнал (Print) иначе проверку сова на проходит. Пришлось сделать отдельную объединенную функцию на будущее. Замучился, пока понял, что ошибка неправильные объемы относятся к стартовому лоту.

Sergey: Сейчас другая проблема:"Необходимо добавить возможность проверки торговых функций программы на наличие ошибок в Тестере стратегий." В советнике заложен вывод (Alert), если ордер не установлен, при условии, что все параметры заданы правильно. Что еще нужно не пойму. Ведь Alert в журнал тоже выводится. Может кто подскажет, что означает выше указанная ошибка..... И вот это: TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double Lot = CalculatedProfit/TickValue/TakeProfit; TickValue почему-то именно для золота равно 0. Валютные пары тестирование проходят. Бред какой-то. Как обойти не знаю. В тестере ошибок нет.

Scriptong: Sergey пишет: Сейчас другая проблема:"Необходимо добавить возможность проверки торговых функций программы на наличие ошибок в Тестере стратегий." В советнике заложен вывод (Alert), если ордер не установлен, при условии, что все параметры заданы правильно. Что еще нужно не пойму. Ведь Alert в журнал тоже выводится. Может кто подскажет, что означает выше указанная ошибка..... Alert при тестировании выводится в журнал тестера по аналогии с функцией Print. Само окно и звуковой сигнал, конечно же, не отображается ни в одном из режимов тестирования. Sergey пишет: И вот это: TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double Lot = CalculatedProfit/TickValue/TakeProfit; TickValue почему-то именно для золота равно 0. Валютные пары тестирование проходят. Бред какой-то. Как обойти не знаю. В тестере ошибок нет. Проверил для золота на Альпари. Есть значение. А в онлайн что показывает? Еще можно попробовать заменить на более современную форму: SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);

Sergey: Scriptong пишет: ще можно попробовать заменить на более современную форму: За эту подсказку спасибо. Что современнее , то и надо применять. Scriptong пишет: А в онлайн что показывает? Значения есть. Мне видится дело не в этом, а в настойках стартового лота. Проблему решил так: Установил в стартовых настройках вместо расчетного постоянный лот. Таким образом строка в авто проверку не попала. Теперь жду замечания от модератора.....

Scriptong: Sergey пишет: Значения есть. Мне видится дело не в этом, а в настойках стартового лота. Проблему решил так: Установил в стартовых настройках вместо расчетного постоянный лот. Таким образом строка в авто проверку не попала. Теперь жду замечания от модератора Ну это пока Вы решили радикально. Но ведь такая возможность может потребоваться в будущем. Значит, следует разобраться, почему на золоте получены нулевые значения. Кроме того, в том коде, который Вы привели, нет проверки деления на 0. Может просто ввести ее и ждать, пока будет получен не 0? Такое часто бывает при необходимости подкачки данных.

Sergey: Scriptong пишет: Кроме того, в том коде, который Вы привели, нет проверки деления на 0. Может просто ввести ее и ждать, пока будет получен не 0? Такое часто бывает при необходимости подкачки данных. Идея верная... Тем более я допустил еще ряд ошибок..... TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); Я закачиваю при инициации в функции OnInit() вместе с другими параметрами, что в принципе не верно, так как для обратных и кросс курсов эта величина не постоянная. Можно дополнить примерно так: в начале OnTick() TickValue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE); if (TickValue = 0) return; Верно? Или делать запрос при каждом тике не рационально, а лучше внутри функции расчета лота примерно так: while (true) { TickValue = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE); if (TickValue !=0) break; }

Scriptong: Sergey пишет: Верно? Или делать запрос при каждом тике не рационально, а лучше внутри функции расчета лота примерно так: Да, на каждом тике. Ведь рыночное окружение изменяется при изменении цены. Поэтому нужно все данные получать заново. Если какой-то из критически важных параметров (величина пункта, стоимость тика, размер тика и т. п.) равен нулю, то продолжать обработку тика нельзя. Сразу выходить и ждать следующего тика. Посмотрите, как у меня это реализовано на примере любого советника. Допустим, Quantum. В папке Include\Common есть файл Common_GetSymbolInfo.mqh. В нем - реализация класса GetSymbolInfo. Так вот, метод RefreshInfo этого класса вызывается в начале каждого тика. Внутри этого метода собираются все данные рыночного окружения. При этом неважно, что многие из них (величина пункта, к примеру) практически константы. Всякое может случиться, а потому необходимо быть готовым к изменению любого из этих параметров.

Sergey: Scriptong пишет: Посмотрите, как у меня это реализовано на примере любого советника. Огромное спасибо! Я пока сделал так if (!SymbolInfoDouble(Symb, SYMBOL_TRADE_TICK_VALUE,TickValue)) { Comment((isRussianLang)? "Ошибка получения данных SYMBOL_TRADE_TICK_VALUE " : "Error retrieving data SYMBOL_TRADE_TICK_VALUE ",GetLastError()); return; } Но обязательно посмотрю пример. Ваш опыт очень для меня важен. Я именно на ваших примерах научился mql4.

Sergey: Всем Привет! Игорь, подскажи, как определить дыры в истории котировок? Вот пример: индикатор установлен на график Н1, но обсчитывает данные с М1. Последовательность баров без дары 5,4,3,2,1,0 Та же последовательность с дыркой 4,3,_,2,1,0 Проверка типа: //+---------------------------------------------------------------------------+ //| Проверка доступности баров указанного таймфрейма | //+---------------------------------------------------------------------------+ bool IsAllBarsAvailable(int lastBar,int tf) { if(lastBar <= 0) return (false); // Проверка доступности баров for(int i=lastBar; i>=0; i--) if(iTime(Symb,tf,i)==0) return (false); // Все бары доступны return (true); } Эту дырку на М1 не замечает. Почему не пойму.... В результате проверка пройдена, а индикатор на участке с дыркой не рассчитывается. Просьба, если есть готовое решение обнаружения дыр в истории скинь пожалуйста.

Scriptong: Sergey пишет: Игорь, подскажи, как определить дыры в истории котировок? К сожалению, не существует универсального способа автоматической проверки наличия дыр в истории. Сам МТ4 довольно часто не замечает их и, соответственно, не заполняет дыры информацией с сервера. В таких случаях рецепт только один: вручную удалять всю историю и закачивать ее заново. Чаще всего помогает. Почему невозможно создать универсальный алгоритм отслеживания дыр в истории? Да потому что для его создания нужно обладать информацией о том, когда котировки между двумя барами действительно были. То есть нужен эталон, с которым возможно провести сравнение. Но у программы только один источник данных - текущий чарт. Простой пример. Открываем график и видим, что между 27.04.2018 и 30.04.2018 имеется визуальный разрыв в значениях котировок. Логично предположить, что тут дыра. Но потом смотрим календарь и видим, что это пятница и понедельник соответственно. То есть на самом деле не дыра. Этот случай еще поддается автоматизации. Углубимся. Допустим, последняя свеча минутного графика 27.04.2018 - 22:30, хотя официальное закрытие недели значится как 22:55. Что это: недостаток данных длиной в 25 минут (дыра?) или действительно не было котировок до закрытия, потому что рынок под конец недели вялый? Вот такие случаи невозможно решить без наличия эталона. Только вот если эталон у нас имеется, то и сама проблема автоматически исчезает.

Sergey: Scriptong пишет: То есть нужен эталон, с которым возможно провести сравнение. Спасибо. Благодаря твоему разъяснению, возникла идея. Попробую ее изложить и проверить. Суть проблемы: Скажем, Вы не открывали терминал 3 рабочих дня. После открытия терминала на 4 день, данные по всем ТФ обновляются на истории 2048 баров. Для М1 это 1,4 дня, для М5-7,1 дней. Таким образом мы имеем пробел истории котировок только на М1. Теперь, если устроить проверку согласованности графиков для М1 по времени открытия баров старшего тайм-фрейма, мы получим ошибку. Следовательно нужно закачать историю.... Конечно, полностью дыры на М1 таким образом не проверить, но будет понимание в необходимости обновления истории. По крайней мере этого будет достаточно для подбора минимального ТФ с полной закаченной историей. Еще раз спасибо. Бегу реализовывать.....

Scriptong: Sergey пишет: Бегу реализовывать..... Это уже реализовано на основе тиковой истории. Вот скрипт.



полная версия страницы