Отправлено: 16.12.16 05:39. Заголовок: Паттерны ПрайсЭкшен через структуру
Игорь добрый день! Перенесу сюда свои мытарства с перепрограммированием индикатора по паттернам под свои нужды. В той ветке я показал, что кое-какие сдвиги к получению желаемого мной результата есть. В текущем варианте как вы видели по картинкам он уже отрисовывает паттерны и их цели, способен перекрасить паттерны, если его таргет пробит. Вопрос по удалению перекрытых паттернов пока откладываю на потом, поскольку возник более актуальный. Повторю здесь кусок кода, где происходят главные события с паттернами: Скрытый текст
цитата:
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;
Но тут где-то собака зарылась, не пойму относительно отображения EnumToString(pattern.getTarget), которая определяется отдельной функцией (кстати подобными строками перекрашиваются паттерны):
Так вот... Eсли медвежий паттерн отработан (WORKED) - то окрашивается он как отработанный, и в подписи к нему EnumToString(pattern.getTarget) пишет WORKED, а вот с бычьими почему-то не пишет... окрашивает как отработанный, но в подписи и в принте пишет что он актуальный (см. рис). Как побороть это недоразумение? Сдается мне что тут вина в implicit enum conversion в строке g_patterns[patternArray].getTarget = getTarget, его энум Скрытый текст
Кроме этого таких предупреждений еще два штуки есть по коду в строках: g_patterns[patternArray].patternType = patternDir, его Энум выглядит так Скрытый текст
Ну и все они в структуре объявлены и инициализированы вот так patternType = NONE_TYPE; patternName = NONE_INDEX; getTarget = NONE; И на всякий случай функция GetData Скрытый текст
цитата:
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 - == Конец ===============================================================
Просмотрев немного по истории, точной закономерности не выявил, но вот покажу участок, где стрелками показаны два по рассраске отработанных бычьих паттерна, между ними 8 свечей (две стрелки указывают на один и тот паттерн). Но подписи у них разные... тот что ниже слева - пишет WORKED, а тот что через 8 свечей справа - ACTUAL.
Чо к чему... не пойму А главное как так-то? Может в коде какого дополнительного условия не хватает в функции IsActualPattern? Или это не понятные примудрусти какие-то в функции строки подписи? Скрытый текст
Отправлено: 20.04.17 16:18. Заголовок: evbut пишет: Вот же..
evbut пишет:
цитата:
Вот жеж... Взрыв мозга ))) Во времена первого знакомства с форекс именно так и считал, что нумерация баров начинается с более древней даты... Долго не мог вытравить из себя такой подход... За год наверно избавлися и приучил считать в виде таймсерий... а теперь придется снова приучать себя иногда именно так и считать бары))))
На самом деле это очень полезно - уметь считать и так, и эдак. Избавляет мозг от узкого понимания ситуации и лишний раз напоминает ему, что все в этом мире относительно. Кстати, в трейдинге очень помогает
evbut пишет:
цитата:
Функция поиска Внутреннего бара выглядела так: а теперь будет выглядеть вот так, если MqlRates rates[4] вынести на глобальный уровень?
Точно не так, т. к. цикл i ничего не дает для функции IsSimpleIB(). Как ей узнать, что нужно обращаться к другим барам? Или же здесь цикл i лишний, а все изменения касаются именно функции IsSimpleIB().
evbut пишет:
цитата:
А вообще не понятно зачем НЕ использовать ArraySetSeries? Пересмотрел кучу индикаторов в MQL5 и львиная доля её использует.
Львиная доля индикаторов, написанная на MQL, это очень низкий уровень написания кода. В этой сфере очень мало профессиональных программистов. Потому и подходы далеко не всегда верные. Хотя против ArraySetAsSeries() ничего не имею. Это как раз нельзя считать плохим тоном. То есть да, можно и при помощи ArraySetAsSeries работать, по старинке. Просто мне всегда казалось логичным нумеровать бары именно слева направо по истории, а не наоборот. Поэтому был рад, когда в MQL5 появилась такая нумерация. Ведь любой бар в истории отныне имеет неизменный индекс.
Пытаюсь разобраться с прямой и обратной индексацией баров и 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 ни без него. Почему так, я не правильно запросил время бара или это какое-то недоразумение со стороны какой-то функции?
Аха... теперь затыка с границами паттернов, в частности с верхней и нижней в функции 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
Аха... теперь затыка с границами паттернов, в частности с верхней и нижней в функции 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; }
Понятно. Но, чтобы их получить, нужно будет использовать искусственную функцию перевода времени бара в индекс?
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 попадает не на время открытия бара.
int i_BarShift(datetime time) { return (Bars(_Symbol, PERIOD_CURRENT, time, TimeCurrent())-1); }
Scriptong пишет:
цитата:
Единственный момент - могут быть смещения в +/-1 бар, если time попадает не на время открытия бара.
Смещение на +1 бар как бы очевидное получается ведь в счет идет и текущий, так называемый "нулевой" бар. А в каких случаях и как часто бывает смещение на -1 бар? Как-то контролировать можно этот момент?
Смещение на +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. Это уже зависит от поставленной задачи.
Отправлено: 12.05.17 09:18. Заголовок: День добрый! Начал ..
День добрый! Начал по-тихоньку продумывать логику мульти-пульти варианта. Хочется узнать ваше мнение о, так сказать, правильности и локоничности хода моих мыслей. В общем, чтобы находить тот или иной паттерн на разных символах и таймфреймах задумал создать главную (определяющую) структуру по символу(список символов), в состав которой входят данные с таймфреймов (список периодов и данные типа MqlRates для каждого периода):
Таким образом, чтобы получить данные на каждой свече последних 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... Файл черновика ПРИЛАГАЕТСЯ
Отправлено: 16.05.17 18:18. Заголовок: Все намного проще. У..
Все намного проще. У Вас уже имеется код для работы с одним символом. Далее требуется вызывать его точно также, но с одним фиксом: передать в него имя символа. Так, на сегодня имеется функция 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.
Кстати, поставленная задача (мультивалютный индикатор) как нельзя лучше решается через ООП. Ведь в данном случае достаточно взять готовый код, сделать из него класс, а уже класс тиражировать для разных символов. Таким образом, отпадает даже необходимость в индивидуальном расчете значения limit, т. к. это уже есть в коде.
Все даты в формате GMT
2 час. Хитов сегодня: 1
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет