Форум » Консультации по программированию » Паттерны ПрайсЭкшен через структуру » Ответить

Паттерны ПрайсЭкшен через структуру

evbut: Игорь добрый день! Перенесу сюда свои мытарства с перепрограммированием индикатора по паттернам под свои нужды. В той ветке я показал, что кое-какие сдвиги к получению желаемого мной результата есть. В текущем варианте как вы видели по картинкам он уже отрисовывает паттерны и их цели, способен перекрасить паттерны, если его таргет пробит. Вопрос по удалению перекрытых паттернов пока откладываю на потом, поскольку возник более актуальный. Повторю здесь кусок кода, где происходят главные события с паттернами: [more] [quote]void FindAndShowPatterns(int index) { int startBar, patternDir,patternName; GetData(index,startBar,patternDir,patternName); double lowPrice = ND(Low[iLowest(NULL, 0, MODE_LOW, // Нижняя граница паттерна startBar - index + 1, index)]); double highPrice = ND(High[iHighest(NULL, 0, MODE_HIGH, // Верхняя граница паттерна startBar - index + 1, index)]); double patternHeight = highPrice - lowPrice; double BuyTP = ND(lowPrice + patternHeight * FiboTarget / 100.0); double SellTP = ND(highPrice - patternHeight * FiboTarget / 100.0); // string nameofpattern = PatternName(patternName); int patternArray = ArraySize(g_patterns); if(patternDir !=0) { if( ArrayResize(g_patterns, patternArray+1, ARRAY_RESERVE_SIZE) < 0) { Alert(WindowExpertName(),(g_isRussianLang)? ": Недостаточно памяти для продолжения работы программы. Индикатор отключен.": ": Not enough memory to continue work the program. Indicator is turned off."); } g_patterns[patternArray].patternType = patternDir; g_patterns[patternArray].patternName = patternName; g_patterns[patternArray].leftBarIndex = startBar; g_patterns[patternArray].rightBarIndex = index; g_patterns[patternArray].patternHighPrice = highPrice; g_patterns[patternArray].patternLowPrice = lowPrice; if(patternDir == BEAR_TYPE) g_patterns[patternArray].patternColor = RegcolorBears; else g_patterns[patternArray].patternColor = RegcolorBulls; if(patternDir == BULL_TYPE) g_patterns[patternArray].patternTarget = BuyTP; else g_patterns[patternArray].patternTarget = SellTP; //Проверка паттерна на отработку таргета if(IsShowWorked) for(int i = ArraySize(g_patterns)-1; i >= 0; i--) { if(g_patterns.patternType == BEAR_TYPE) { for(int k = g_patterns.rightBarIndex-1; k >= 0; k--) { if(iLow(NULL, 0, k) < g_patterns.patternTarget) g_patterns.patternColor = WorkColor; } } if(g_patterns.patternType == BULL_TYPE) { for(int k = g_patterns.rightBarIndex-1; k >= 0; k--) { if(iHigh(NULL, 0, k) > g_patterns.patternTarget) g_patterns.patternColor = WorkColor; } } } // == Запишем факт отработки паттерна ====================== int getTarget = IsActualPattern(g_patterns[patternArray]); g_patterns[patternArray].getTarget = getTarget; // === Отрисуем паттерны =========== /*if(!IsShowALLPatterns) DeleteAnyPattern(g_patterns[patternArray], index); */ // Удаление перекрытых паттернов if(IsRectangleShow) { string name = GetUniqueID(g_patterns[patternArray], FIRST_PART); /*string name = GetObjectName(g_patterns[patternArray].rightBarIndex, g_patterns[patternArray].patternType, FIRST_PART);// Имя объекта паттерна*/ ShowRectangle(name, Time[g_patterns[patternArray].leftBarIndex], g_patterns[patternArray].patternLowPrice, Time[g_patterns[patternArray].rightBarIndex],g_patterns[patternArray].patternHighPrice, "",g_patterns[patternArray].patternColor); } /*string name = GetObjectName(g_patterns[patternArray].rightBarIndex, g_patterns[patternArray].patternType, SECOND_PART);// Имя объекта паттерна */ string name = GetUniqueID(g_patterns[patternArray], SECOND_PART); ShowTarget(name, Time[g_patterns[patternArray].leftBarIndex], g_patterns[patternArray].patternTarget, Time[g_patterns[patternArray].rightBarIndex], g_patterns[patternArray].patternTarget, "",g_patterns[patternArray].patternColor); [/quote][/more] Отображает он все как мне хотелось бы. При наведении мышки на паттерн отображает информацию из функции GetUniqueID: [quote]string GetUniqueID(const Pattern & pattern, string middlePart) { return(PREFIX + EnumToString(pattern.patternName)+EnumToString(pattern.getTarget)+"_" +EnumToString(pattern.patternType)+ middlePart + string(Time[pattern.rightBarIndex])); } [/quote] Но тут где-то собака зарылась, не пойму относительно отображения EnumToString(pattern.getTarget), которая определяется отдельной функцией (кстати подобными строками перекрашиваются паттерны): [quote] int IsActualPattern(Pattern &pattern) { for(int i = ArraySize(g_patterns)-1; i >= 0; i--) { if(g_patterns.patternType == BEAR_TYPE) { for(int k = g_patterns.rightBarIndex-1; k >= 0; k--) { if(iLow(NULL, 0, k) < g_patterns.patternTarget) g_patterns.getTarget = WORKED; else g_patterns.getTarget = ACTUAL; } } if(g_patterns.patternType == BULL_TYPE) { for(int k = g_patterns.rightBarIndex-1; k >= 0; k--) { if(iHigh(NULL, 0, k) > g_patterns.patternTarget) g_patterns.getTarget = WORKED; else g_patterns.getTarget = ACTUAL; } } } return (g_patterns[ArraySize(g_patterns)-1].getTarget); } [/quote] Так вот... Eсли медвежий паттерн отработан (WORKED) - то окрашивается он как отработанный, и в подписи к нему EnumToString(pattern.getTarget) пишет WORKED, а вот с бычьими почему-то не пишет... окрашивает как отработанный, но в подписи и в принте пишет что он актуальный (см. рис). Как побороть это недоразумение? Сдается мне что тут вина в implicit enum conversion в строке g_patterns[patternArray].getTarget = getTarget, его энум [more][quote] enum ENUM_GET_TARGET { NONE , ACTUAL, //Actual pattern WORKED //Worked pattern }; [/quote][/more] Кроме этого таких предупреждений еще два штуки есть по коду в строках: g_patterns[patternArray].patternType = patternDir, его Энум выглядит так [more][quote] enum ENUM_PATTERN_TYPE { NONE_TYPE, //0 - No pattern BULL_TYPE, //1 - Bull pattern BEAR_TYPE, //2 - bear pattern }; [/quote][/more] и g_patterns[patternArray].patternName = patternName, его энум [more][quote] enum ENUM_PATTERN_NAME { NONE_INDEX = -1, RAILS_ = 0, OVB_ = 1, PPR_ = 2 }; [/quote][/more] Ну и все они в структуре объявлены и инициализированы вот так patternType = NONE_TYPE; patternName = NONE_INDEX; getTarget = NONE; И на всякий случай функция GetData [more][quote] void GetData(int index, int& startBar, int &type, int &patternName) { //- 1 - == Определим тип найденного паттерна по направлению ===================== if(IsBullsRailsPattern(index)|| IsBUOVBPattern(index) || IsBullsPPRPattern(index)) type = BULL_TYPE; if(IsBearsRailsPattern(index) || IsBEOVBPattern(index)|| IsBearsPPRPattern(index)) type = BEAR_TYPE; //- 1 - == Конец =============================================================== //- 2 - == Определим левую границу найденного паттерна ========================= if(IsBullsPPRPattern(index) || IsBearsPPRPattern(index)) startBar = index+2; if(IsBUOVBPattern(index) || IsBEOVBPattern(index)|| IsBearsRailsPattern(index) || IsBullsRailsPattern(index)) startBar = index+1; //- 2 - == Конец =============================================================== //- 3 - == Ообозначим паттерн по названию======================================= if(IsBullsRailsPattern(index) || IsBearsRailsPattern(index)) patternName = RAILS_; if(IsBUOVBPattern(index) || IsBEOVBPattern(index)) patternName = OVB_; if(IsBullsPPRPattern(index)|| IsBearsPPRPattern(index)) patternName = PPR_; //- 3 - == Конец =============================================================== } [/quote][/more] Ткните, плиз носом, как победить этот недуг Просмотрев немного по истории, точной закономерности не выявил, но вот покажу участок, где стрелками показаны два по рассраске отработанных бычьих паттерна, между ними 8 свечей (две стрелки указывают на один и тот паттерн). Но подписи у них разные... тот что ниже слева - пишет WORKED, а тот что через 8 свечей справа - ACTUAL. Чо к чему... не пойму А главное как так-то? Может в коде какого дополнительного условия не хватает в функции IsActualPattern? Или это не понятные примудрусти какие-то в функции строки подписи? [more][quote] //+-------------------------------------------------------------------------------------+ string GetUniqueID(const Pattern & pattern, string middlePart) { return(PREFIX + EnumToString(pattern.patternName)+EnumToString(pattern.getTarget)+"_" +EnumToString(pattern.patternType)+ "\n"+ middlePart +"\n" + "TARGET = " + DoubleToString(pattern.patternTarget,_Digits)); } [/quote][/more]

Ответов - 87, стр: 1 2 3 4 5 6 All

evbut: Гран мерси. Теперь можно и за кросс-платформенный вариант приниматься ))) а уж потом мастрить мульти-пульти версию Чтобы написать в стиле МТ5, используйте функции типа Copy (CopyRates, CopyHigh, CopyLow и т. д.) вместо iLow, iHigh И т. д. Правда придется немного поменять подход к их использованию, готовя данные заранее, а не беря их в том месте, где потребовались. Тут наверно лучше использоваться CopyRates, вместо 4 других Copy... А вот в OnCalculate есть уже массивы: high, low и проч... Их Никак нельзя использовать? PS. Попробовал прикрутить MqlRates rates[]. Прописал ее в глобальных переменных. В int GetRecalcIndex добавил строку ArrayResize(rates,0.0) - типа обнулять массив при смене таймфрейма. Затем в ShowIndicatorData прописал for (int i = limit; i > 0; i--) { int copied = CopyRates(NULL,0,0,i,rates); Print(copied); if (!FindPatternsAndFillDB(i)) return false; Принт мне перечислил значения copied от 210 до 1. Но когда в OnCalculate int limit = GetRecalcIndex(total, rates_total, prev_calculated); //GetRecalcIndex(total); int copied = CopyRates(NULL,0,0,total,rates); Print(copied); Принт выдает 1000. Именно такое по умолчанию стоит значение indBarsCount. Почему так? Ну и разумеется при замене Low на rates[index].low вылазит ошибка выхода за пределы массива (( Чот видать не туда шагать начал ((

Scriptong: evbut пишет: Тут наверно лучше использоваться CopyRates, вместо 4 других Copy... Это зависит от того, какого рода информация нужна. Бывает так, что информация по всей свече не требуется. evbut пишет: А вот в OnCalculate есть уже массивы: high, low и проч... Можно. Но придется тащить их передачу практически во все используемые функции, что далеко не всегда удобно. Да и лишнее заполнение стека, к размеру которого чувствительна любая программа. evbut пишет: Принт мне перечислил значения copied от 210 до 1. Но когда в OnCalculate Если смотрели в журнале, который в закладке "Эксперты", то вполне такое может быть. Этот журнал при массовом выводе информации режет ее, чтобы не было переполнения. В таких случаях журнал нужно смотреть через контекстное меню закладки логов "Открыть". Если открыть полную версию журнала, то получим те же 1000 значений. evbut пишет: Ну и разумеется при замене Low на rates[index].low вылазит ошибка выхода за пределы массива (( Чот видать не туда шагать начал (( Имейте в виду, что нумерация баров после использования функций типа Copy изменяется на противоположную. Причем в достаточно сложном сочетании, т. к. нулевой элемент всякий раз указывает на разный бар. С этим фактом всегда ломается огромное количество копий.

evbut: Scriptong пишет: Имейте в виду, что нумерация баров после использования функций типа Copy изменяется на противоположную. Причем в достаточно сложном сочетании, т. к. нулевой элемент всякий раз указывает на разный бар. С этим фактом всегда ломается огромное количество копий. ArraySetAsSeries(rates,true) не помогает в этом случае? И как обычно борются с этой напастью?


Scriptong: evbut пишет: ArraySetAsSeries(rates,true) не помогает в этом случае? И как обычно борются с этой напастью? Не советую. И не напасть это, а обычная работа с данными. Просто привыкнуть нужно и научиться ориентироваться в получаемых массивах.

evbut: Решил проблему. Не знаю на сколько оправдано, тем не менее работает и в МТ4 и в МТ5. CopyRates(NULL,0,0,rates_total,rates) Можно приниматься за мультивсяковость ))

Scriptong: evbut пишет: Решил проблему. Не знаю на сколько оправдано, тем не менее работает и в МТ4 и в МТ5. CopyRates(NULL,0,0,rates_total,rates) Не-не, очень плохо. Зачем Вам весь массив данных, если максимум четыре бара для поиска паттерна требуется? Просто на каждом баре запрашивайте четыре последних свечи: MqlRates stRates[]; if (CopyRates(Symbol(), PERIOD_CURRENT, nBarIndex, 4, stRates) < 0) { // ошибка return; } Имеется в виду, что nBarIndex - это текущий обрабатываемый бар (тот, который в главном цикле задается). От него, включая его, запрашивается всего 4 бара. Это достаточно быстрая операция. Если выполнение функции успешно завершено, то в элементе 0 массива stRates будут данные о баре (nBarIndex + 3), в элементе 1 - данные от (nBarIndex + 2), в элементе 2 - данные (nBarIndex + 1) и в элементе 3 - от бара nBarIndex. P. S. Кстати, в MQL5 конструкция CopyRates(NULL, 0...) работать не будет. Обязательно требуется CopyRates(Symbol(), PERIOD_CURRENT...).

evbut: Scriptong пишет: Не советую. И не напасть это, а обычная работа с данными. Без ArraySetAsSeries не отображаются паттерны, а с ним отображаются Кстати, в MQL5 конструкция CopyRates(NULL, 0...) работать не будет. Хм... а ведь работает

Scriptong: evbut пишет: Без ArraySetAsSeries не отображаются паттерны, а с ним отображаются Я же говорю - массив переворачивается. А ArraySetAsSeries переворачивает его обратно, в таймсерию. Если не хочется переписывать функции поиска паттернов, то да, нужен ArraySetAsSeries. Но в этом случае код получается не адаптированным, а именно переведенным (как Google Translate переводит с одного языка на другой) на MQL5. То есть по-хорошему для универсального кода нужно переписать функции поиска паттернов, развернув в них нумерацию баров. evbut пишет: Хм... а ведь работает Сейчас работает, но нет гарантий, что будет работать в будущем. Это недокументированная возможность, перешедшая с MQL4. Поменяют MetaQuotes значения перечислений, и тогда все подобные коды полетят в тартарары.

evbut: Scriptong пишет: То есть по-хорошему для универсального кода нужно переписать функции поиска паттернов, развернув в них нумерацию баров. Ну да... поглядел несколько кодов на MQL5 и в них наоборот пишется, т.е. если в MQL4 пишем к примеру if(Close[index] >= Low[index+1]) // Минимум предыдущей свечи не пробит return(false); то в MQL5 это будет кажется вот так if(Close[index-1] >= Low[index]) // Минимум предыдущей свечи не пробит return(false); Поразмыслил над вашим что nBarIndex - это текущий обрабатываемый бар (тот, который в главном цикле задается). От него, включая его, запрашивается всего 4 бара. Это достаточно быстрая операция. Если выполнение функции успешно завершено, то в элементе 0 массива stRates будут данные о баре (nBarIndex + 3), в элементе 1 - данные от (nBarIndex + 2), в элементе 2 - данные (nBarIndex + 1) и в элементе 3 - от бара nBarIndex. и получается, что на каждом новом баре бар nBarIndex + 3 будет запрашиваться 1 раз, бар nBarIndex + 2 - два раза и nBarIndex + 1 - три раза... как вы говорите двойная работа и даже тройная... Или нет? Такое необходимо только 1 раз сделать при первом обращении в функцию OnCalculate, а далее записывать в массив MQLrates данные только первого бара.

Scriptong: evbut пишет: то в MQL5 это будет кажется вот так Ни в коем случае. В MQL5 другой подход к сбору данных: через функции CopyXXX. Такой же способ сбора данных имеется в MQL4. Это и дает возможность написания кроссплатформенных кодов. То есть даже в MQL4 при использовании способа получения данных через CopyXXX придется разворачивать логику. evbut пишет: и получается, что на каждом новом баре бар nBarIndex + 3 будет запрашиваться 1 раз, бар nBarIndex + 2 - два раза и nBarIndex + 1 - три раза... как вы говорите двойная работа и даже тройная... Или нет? Такое необходимо только 1 раз сделать при первом обращении в функцию OnCalculate, а далее записывать в массив MQLrates данные только первого бара. Для чего тянуть весь массив данных (если пополнять MQLRates)? Это не нужно, т. к. потребует лишнюю память. Просто на каждой итерации запрашиваются данные о 4 барах и к следующей итерации они благополучно теряются. Ведь все равно на каждой итерации необходимо делать запрос данных, хотя бы одного (нового) бара. А 1 бар будет запрошен или 4 бара - разницы практически никакой. Другое дело, если бы речь шла о тысячах баров. Тогда нужно было бы что-то придумывать.

evbut: То есть даже в MQL4 при использовании способа получения данных через CopyXXX придется разворачивать логику. Покумекал в общем и вот что на мой взгляд подойдет кроссплаформенности: Создадим еще одну структуру "Candle", struct Candle { ENUM_BARTYPE type; double open,high,low,close, body, shadow, height; datetime time; //Время Candle() { Init(); } void Init() { type = NONE_BAR; open = 0.0; close = 0.0; high = 0.0; low = 0.0; body = 0.0; shadow = 0.0; height = 0.0; time = -1; } }; Candle g_candles[]; //Свечи которую заполним данными: if(!CopyRates(symbol,period,time,4,rates)) return false; for(int i = 0; i < 4; i++) { candle.close = rates.close; candle.open = rates.open; candle.high = rates.high; candle.low = rates.low; candle.time = rates.time; candle.type = (rates.close < rates.open)? BEAR_BAR : BULL_BAR; candle.shadow = (candle.type == BULL_BAR)? MathMin(rates.open, rates.close) - rates.low : rates.high - MathMax(rates.open, rates.close); candle.height = rates.high - rates.low; candle.body = MathAbs(rates.close - rates.open); } Ну и далее в каждой функции поиска паттерна через цикл for(int i = 0; i < ArraySize(g_candles); i++) исправляем всякие Close, High и Low и проч на данные из этой структуры Не тот маршрут выбрал? )))

Scriptong: evbut пишет: Создадим еще одну структуру "Candle", Пока не понял преимущество такой надстройки. В моих кодах для таких целей пока хватало стандартной структуры MqlRates. То есть вместо того, чтобы использовать стандартные таймсерии, достаточно один раз на каждой итерации цикла запросить данные о последних 4-х барах и передать эту структуру всем функциям, которые используют данные для свечей.

evbut: Это понятно, что копировать на каждой i данные только 4х свечей включая саму i. Не поятен мне вопрос по смене логики, чтобы и в mql4и mql5 работало без ArraySetSeries.

Scriptong: evbut пишет: Это понятно, что копировать на каждой i данные только 4х свечей включая саму i. Не поятен мне вопрос по смене логики, чтобы и в mql4и mql5 работало без ArraySetSeries. К примеру, в оригинальной версии поиск бычьего паттерна PPR выглядит так: bool IsBullsPPRPattern(int index) { if(Close[index] <= High[index+1]) // Максимум предыдущей свечи не пробит return(false); // Паттерн не сформирован if (Low[index+1] >= Low[index+2] || // Минимум предыдущего бара не.. Low[index+1] >= Low[index]) // ..является минимумом паттерна return(false); // Паттерн не сформирован if (Close[index+2] >= Open[index+2]) // Стартовый бар паттерна не является return(false); // ..медвежьим - выход*/ return(true); // Паттерн сформирован } Здесь index - бар, находящийся по графику правее, чем бар (index + 1). Если же получен массив rates[4] при помощи CopyRates без использования ArraySetAsSeries, то функция будет выглядеть следующим образом: bool IsBullsPPRPattern(const MqlRates &rates[4]) { if (rates[3].close <= rates[2].high) // Максимум предыдущей свечи не пробит return(false); // Паттерн не сформирован if (rates[2].low >= rates[1].low || // Минимум предыдущего бара не.. rates[2].low >= rates[3].low) // ..является минимумом паттерна return(false); // Паттерн не сформирован if (rates[1].close >= rates[1].open) // Стартовый бар паттерна не является return(false); // ..медвежьим - выход*/ return(true); // Паттерн сформирован } Здесь не используется элемент массива rates с индексом 0, т. к. это эквивалент бара (index + 3). Индекс 1 массива rates - это эквивалент бара (index + 2), элемент 2 - эквивалент (index + 1), а элемент 3 - эквивалент index. То есть все индексы "перевернуты".

evbut: Вот жеж... Взрыв мозга ))) Во времена первого знакомства с форекс именно так и считал, что нумерация баров начинается с более древней даты... Долго не мог вытравить из себя такой подход... За год наверно избавлися и приучил считать в виде таймсерий... а теперь придется снова приучать себя иногда именно так и считать бары)))) Функция поиска Внутреннего бара выглядела так: bool IsIBPattern(int index, int total, int& patternStart) { while(IsSimpleIB(patternStart) && // Поиск элементарных паттернов подряд patternStart < total) patternStart++; if (patternStart == index) // Ни один паттерн не найден return(false); return(true); // Паттерн найден } а теперь будет выглядеть вот так, если MqlRates rates[4] вынести на глобальный уровень? bool IsIBPattern(int total, int& patternStart) { total = ArraySize(rates)-1; for (int i = 0; i < total; i++) { while(IsSimpleIB() && // Поиск элементарных паттернов подряд patternStart < total) patternStart++; if (patternStart == i) // Ни один паттерн не найден return(false); } return(true); // Паттерн найден } А вообще не понятно зачем НЕ использовать ArraySetSeries? Пересмотрел кучу индикаторов в MQL5 и львиная доля её использует.



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