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

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

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

Scriptong: evbut пишет: Вот жеж... Взрыв мозга ))) Во времена первого знакомства с форекс именно так и считал, что нумерация баров начинается с более древней даты... Долго не мог вытравить из себя такой подход... За год наверно избавлися и приучил считать в виде таймсерий... а теперь придется снова приучать себя иногда именно так и считать бары)))) На самом деле это очень полезно - уметь считать и так, и эдак. Избавляет мозг от узкого понимания ситуации и лишний раз напоминает ему, что все в этом мире относительно. Кстати, в трейдинге очень помогает evbut пишет: Функция поиска Внутреннего бара выглядела так: а теперь будет выглядеть вот так, если MqlRates rates[4] вынести на глобальный уровень? Точно не так, т. к. цикл i ничего не дает для функции IsSimpleIB(). Как ей узнать, что нужно обращаться к другим барам? Или же здесь цикл i лишний, а все изменения касаются именно функции IsSimpleIB(). evbut пишет: А вообще не понятно зачем НЕ использовать ArraySetSeries? Пересмотрел кучу индикаторов в MQL5 и львиная доля её использует. Львиная доля индикаторов, написанная на MQL, это очень низкий уровень написания кода. В этой сфере очень мало профессиональных программистов. Потому и подходы далеко не всегда верные. Хотя против ArraySetAsSeries() ничего не имею. Это как раз нельзя считать плохим тоном. То есть да, можно и при помощи ArraySetAsSeries работать, по старинке. Просто мне всегда казалось логичным нумеровать бары именно слева направо по истории, а не наоборот. Поэтому был рад, когда в MQL5 появилась такая нумерация. Ведь любой бар в истории отныне имеет неизменный индекс.

evbut: Пытаюсь разобраться с прямой и обратной индексацией баров и CopyRates Запрашиваю данные 4 свечей с принтом (limit = 20). for (int i = limit; i > 0; i--) { ratesCnt = CopyRates(_Symbol,PERIOD_CURRENT,i,4,rates); for(int j = ArraySize(rates)-1; j >=0; j--) { Print(j + " " + Time[j] + " High " + rates[j].high); } на картинке ценовые метки проставлены по хаям баром. Вертикальная линия стоит на баре = limit и видим, что дата его 22.032017. В принтах же нет такой даты по 4-м запрашиваемым барам ни с использованием ArraySetSeries ни без него. Почему так, я не правильно запросил время бара или это какое-то недоразумение со стороны какой-то функции?

Scriptong: evbut пишет: Почему так, я не правильно запросил время бара Да, неправильно запросили время бара. Ведь Вы оперируете барами, записанными в массив rates, а не барами, записанными в таймсерию Time. Вместо: Print(j + " " + Time[j] + " High " + rates[j].high); нужно написать: Print(j + " " + rates[j].time + " High " + rates[j].high);


evbut: Scriptong пишет: нужно написать: Аха... теперь затыка с границами паттернов, в частности с верхней и нижней в функции FindPatternsAndFillDB double lowPrice = Low[iLowest(NULL,0,MODE_LOW,// Нижняя граница паттерна startBar-endBar+1,endBar)]; double highPrice = High[iHighest(NULL,0,MODE_HIGH,// Верхняя граница паттерна startBar-endBar+1,endBar)]; С правой и левой границами сложности нет: правая всегда равна rates[0].time или rates[3].time в зависимости от установки ArraySetAsSeries. А от нее отсчитываем нужное количество баров влево в зависимости от паттерна. Решил так сделать (ведь универсальности пытаюсь добиться)... поскольку startBar и endBar теперь у нас в виде времени, то получим индексы этих баров на графике int rightbar = iBarShift_(_Symbol,PERIOD_CURRENT,endBar,true); int leftbar = iBarShift_(_Symbol,PERIOD_CURRENT,stratBar,true); В итоге расчет будет вестись вот так с помощью дополнительных функций, аналогов MQL4 double lowPrice = iLow_(_Symbol,PERIOD_CURRENT, iLowest_(_Symbol,PERIOD_CURRENT,MODE_LOW, leftbar-rightbar+1,rightbar)); double highPrice = iHigh_(_Symbol,PERIOD_CURRENT, iHighest_(_Symbol,PERIOD_CURRENT,MODE_HIGH,leftbar-rightbar+1,rightbar)); Громоздко получилось? Пожалуй есть другой вариант более локоничный... Что-нибудь через MathMin и MathMax... Но конструкция кода еще продумал

Scriptong: evbut пишет: Аха... теперь затыка с границами паттернов, в частности с верхней и нижней в функции FindPatternsAndFillDB В данном конкретном случае лучший выход - сделать функцию перебора по барам в поисках нужного экстремума, т. к. баров предполагается немного (4 - это действительно мало): double GetExtremum(const MqlRates &starrRates[], int nBars, int nExtMode) { int nTotal = int(MathMin(nBars, ArraySize(starrRates))); double fExtremum = 0.0; for (int i = 0; i < nTotal; ++i) { if (nExtMode == MODE_HIGH && fExtremum < starrRates[ i ].high) fExtremum = starrRates[ i ].high; if (nExtMode == MODE_LOW && (fExtremum == 0.0 || fExtremum > starrRates[ i ].low)) fExtremum = starrRates[ i ].low; } return fExtremum; } Использование функции: double fLow = GetExtremum(rates, patternBars, MODE_LOW); double fHigh = GetExtremum(rates, patternBars, MODE_HIGH); где patternBars - количество баров для того или иного паттерна. Это вариант для использования без ArraySetAsSeries().

evbut: Scriptong пишет: где patternBars - количество баров для того или иного паттерна. Понятно. Но, чтобы их получить, нужно будет использовать искусственную функцию перевода времени бара в индекс? В MQL5 нету iBarShift.

Scriptong: evbut пишет: Понятно. Но, чтобы их получить, нужно будет использовать искусственную функцию перевода времени бара в индекс? patternsBars - это количество, а не индекс. Поэтому ничего получать не нужно. Вместо patternsBars подставляется 2, 3 или 4 в зависимости от типа паттерна. evbut пишет: В MQL5 нету iBarShift. Зато есть замечательная функция Bars, которая с лихвой покрывает возможности iBarShift от MQL4. Смотрите второй вариант функции: int Bars( string symbol_name, // имя символа ENUM_TIMEFRAMES timeframe, // период datetime start_time, // с какой даты datetime stop_time // по какую дату ); Если использовать ее следующим образом: int nBarIndex = Bars(Symbol(), Period(), time, TimeCurrent()); то получим аналог iBarShift(). Единственный момент - могут быть смещения в +/-1 бар, если time попадает не на время открытия бара.

evbut: Scriptong пишет: Если использовать ее следующим образом: Сделал в виде функции int i_BarShift(datetime time) { return (Bars(_Symbol, PERIOD_CURRENT, time, TimeCurrent())-1); } Scriptong пишет: Единственный момент - могут быть смещения в +/-1 бар, если time попадает не на время открытия бара. Смещение на +1 бар как бы очевидное получается ведь в счет идет и текущий, так называемый "нулевой" бар. А в каких случаях и как часто бывает смещение на -1 бар? Как-то контролировать можно этот момент?

Scriptong: evbut пишет: Смещение на +1 бар как бы очевидное получается ведь в счет идет и текущий, так называемый "нулевой" бар. В принципе, если нужно считать ровно так же, как и в MQL4, то лучше все-таки начинать поиск со времени на 1 секунду ранее, чем нулевой бар: datetime dtTime0Bar[]; CopyTime(Symbol(), PERIOD_CURRENT, 0, 1, dtTime0Bar); int nBarIndex = Bars(Symbol(), PERIOD_CURRENT, time, dtTime0Bar[0] - 1) Это позволит не включать в интервал поиска нулевой бар, индекс которого при счете справа налево нам всегда известен. evbut пишет: А в каких случаях и как часто бывает смещение на -1 бар? Когда третий и четвертый аргумент указывают на время внутри одного и того же бара, к примеру. В документации (см. примечание) этот момент специально описан. Также возможен случай, когда третий аргумент указывает на время правее одного бара, который предполагается включить в расчет, а четвертый - чуть левее. Так и получим -1 от того значения, которое вернула бы функция iBarShift(). Она ведь работает несколько по-другому - использует округление к ближайшему бару. evbut пишет: Как-то контролировать можно этот момент? Сравнить время открытия полученного бара со временем, указанным в третьем аргументе. Если данные равны, то все ОК, если нет, то вычесть или добавить 1. Это уже зависит от поставленной задачи.

evbut: День добрый! Начал по-тихоньку продумывать логику мульти-пульти варианта. Хочется узнать ваше мнение о, так сказать, правильности и локоничности хода моих мыслей. В общем, чтобы находить тот или иной паттерн на разных символах и таймфреймах задумал создать главную (определяющую) структуру по символу(список символов), в состав которой входят данные с таймфреймов (список периодов и данные типа MqlRates для каждого периода): struct TFInfo { ENUM_TIMEFRAMES tf; string text; MqlRates _symbol[]; BAR_TYPE bartype; TFInfo() { Init(); } void Init() { ArraySetAsSeries(_symbol,true); bartype = NONE_BAR; } }; TFInfo g_tfInfo[MAX_PERIOD]; // Periods struct SymbolData { string symbol; TFInfo g_tfInfo[MAX_PERIOD]; SymbolData() { Init(); } void Init() { symbol = ""; } }; SymbolData g_symbdata[MAX_SYMBOLS]; // Symbols Таким образом, чтобы получить данные на каждой свече последних 3 свечей будет вызваться CopyRates() в тройном цикле: for (int k = limit; k > 0; k--) интерации по барам for (int i = 0; i < ArraySize(g_symbdata); i++) интерации по символам for (int j = 0; j < ArraySize(g_tfInfo); j++) интерации по периодам { if(!CopyRates(g_symbdata.symbol,g_symbdata.g_tfInfo[j].tf,k,3,g_symbdata.g_tfInfo[j]._symbol)) return false; } Проверку корректности получаемых данных провожу далее принтом: for(int x = ArraySize(g_symbdata.g_tfInfo[j]._symbol)-1; x >=0; x--) { double high = g_symbdata.g_tfInfo[j]._symbol[x].high; double low = g_symbdata.g_tfInfo[j]._symbol[x].low; datetime time = g_symbdata.g_tfInfo[j]._symbol[x].time; Print(g_symbdata.symbol + " Period [" + g_symbdata.g_tfInfo[j].text+ "] Bar from rates [" + IntegerToString(x) + "][" + TimeToStr(time) + "] High = [" + high + "] Low = [" + low + "] Time = [" + TimeToStr (time, TIME_DATE|TIME_SECONDS) + "]"); Анализ данных с принта и по графикам каждого символа и таймфрейма получается, что все необходимые данные для определения того же паттерна ППР у нас записаны в структуре. Выходит далее к примеру в функции поиска бычьего паттерна ППР (IsBullsPPRPattern()) будет выглядеть так: bool IsBullsPPRPattern(int index) { for (int i = 0; i < ArraySize(g_symbdata); i++) for (int j = 0; j < ArraySize(g_tfInfo); j++) for(int x = 0; x < ArraySize(g_symbdata.g_tfInfo[j]._symbol); x++) { if(g_symbdata.g_tfInfo[j]._symbol[0].close <= g_symbdata.g_tfInfo[j]._symbol[1].high) // Максимум предыдущей свечи не пробит return(false); // Паттерн не сформирован if (g_symbdata.g_tfInfo[j]._symbol[1].low >= g_symbdata.g_tfInfo[j]._symbol[2].low || // Минимум предыдущего бара не.. g_symbdata.g_tfInfo[j]._symbol[1].low >= g_symbdata.g_tfInfo[j]._symbol[0].low) // ..является минимумом паттерна return(false); // Паттерн не сформирован if (g_symbdata.g_tfInfo[j]._symbol[2].close >= g_symbdata.g_tfInfo[j]._symbol[2].open) // Стартовый бар паттерна не является return(false); // ..медвежьим - выход*/ } return(true); // Паттерн сформирован } Что скажите? Громоздко по-моему все получается... Для каждой функции поиска паттерна тройные циклы. Это все же пока набросок. Вызов функций разумеется в дальнейшем будет происходить не в OnCalculate, а через OnTimer... Файл черновика ПРИЛАГАЕТСЯ

Scriptong: Все намного проще. У Вас уже имеется код для работы с одним символом. Далее требуется вызывать его точно также, но с одним фиксом: передать в него имя символа. Так, на сегодня имеется функция ShowIndicatorData. Она работает с текущим символом. Чтобы сделать ее универсальной, требуется всего лишь передать ей имя символа, с которым необходимо работать. И далее внутри этой функции передать имя символа подчиненным функциям: bool ShowIndicatorData(string strSymbol, int limit) { for (int i = limit; i > 0; i--) { if (!FindPatternsAndFillDB(strSymbol, i)) return false; if (ShowWorkedPattern == NO) continue; ProcessSLAndTPOfPatterns(strSymbol, i); } return true; } В итоге для расчета паттернов по нескольким символам потребуется всего лишь один цикл: for (int i = ArraySize(g_symbdata) - 1; i >= 0 ; --i) // итерации по символам ShowIndicatorData(g_symbdata[ i ], limit); Правда в данном случае еще нужно для каждого символа рассчитать свое значение limit.

Scriptong: Кстати, поставленная задача (мультивалютный индикатор) как нельзя лучше решается через ООП. Ведь в данном случае достаточно взять готовый код, сделать из него класс, а уже класс тиражировать для разных символов. Таким образом, отпадает даже необходимость в индивидуальном расчете значения limit, т. к. это уже есть в коде.



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