English 中文 Español Deutsch 日本語 Português
Наивный байесовский классификатор для сигналов набора индикаторов

Наивный байесовский классификатор для сигналов набора индикаторов

MetaTrader 5Индикаторы | 12 мая 2017, 17:15
10 433 29
Stanislav Korotky
Stanislav Korotky

Хотим мы того или нет, но статистика в трейдинге играет заметную роль. Начиная с фундаментальных новостей, пестрящих цифрами, и заканчивая торговыми отчетами или отчетами тестирования, от статистических показателей никуда не деться. Вместе с тем, тезис о применимости статистики в принятии торговых решений остается одной из самых дискуссионных тем. Случаен ли рынок, стационарны ли котировки, применим ли вероятностный подход к их анализу? Спорить об этом можно бесконечно. На просторах интернета, да и на сайте mql5.com легко найти материалы и обсуждения с самыми разными точками зрения, строгими научными выкладками и впечатляющими графиками. Однако трейдеров, как правило, интересует прикладной аспект — как это все работает на деле, в торговом терминале. Эта статья — попытка продемонстрировать прагматичный подход к вероятностной модели принятия торговых решений с помощью набора технических индикаторов. Минимум теории, максимум практики.

Идея заключается в том, чтобы оценить потенциал различных индикаторов с позиции теории вероятности и проверить способность комитета индикаторов повышать процент выигрышей торговой системы.

Это потребует создания фреймворка для обработки сигналов произвольных индикаторов и простейшего эксперта на его основе для тестирования.

В качестве рабочих индикаторов предполагается использовать стандартные индикаторы, однако фреймворк позволит самостоятельно подключать и анализировать и другие, пользовательские индикаторы.

Но прежде чем приступать к проектированию и реализации алгоритмов, нам все-таки придется немного окунуться в теорию.


Введение в модель условной вероятности

В названии статьи упомянут наивный байесовский классификатор. Он основан на известной формуле Байеса, которая будет здесь вкратце рассмотрена, а "наивным" назван из-за необходимого предположения о независимости случайных величин, описываемых формулой. По поводу независимости индикаторов мы еще поговорим, а пока — сама формула.

   (1)

где H — некая гипотеза о внутреннем состоянии системы (в нашем случае это гипотеза о состоянии рынка и торговой системы), E — наблюдаемое событие (в нашем случае это сигналы индикаторов), а также описывающие их вероятности:
  • P(H) — априорная, известная из истории наблюдений, вероятность состояния H;
  • P(E) — полная вероятность события E с учетом всех существующих гипотез, которых обычно несколько (тут следовало бы отметить, что гипотезы должны быть несовместными, т.е. состояние системы в каждый момент — только одно, но для желающих углубиться в теорию приведены ссылки);
  • P(E|H) — вероятность наступления события E при истинности гипотезы (состояния) H;
  • P(H|E) — апостериорная вероятность гипотезы (состояния) H при наблюдении события E.

Если взять в качестве примера простейшую торговую систему, то в качестве гипотез H обычно рассматривают такие состояния рынка, как движение вверх (покупка), вниз (продажа) и боковые колебания (ожидание). В качестве событий E, описывающих вероятное состояние рынка, используются сигналы индикаторов.

Для сигналов конкретного индикатора несложно рассчитать вероятности из правой части формулы (1) на доступной истории и затем выяснить наиболее вероятное состояние рынка P(H|E).

Однако для расчета необходимо более четко определить гипотезы и методику сбора статистики, на основе которой и будут получены вероятности.

Прежде всего, предположим, что торговля ведется по барам (не по тикам). Результативность торговли можно оценивать размером прибыли, профит-фактором или другими характеристиками, но для простоты изложения возьмем количество выигрышных и проигрышных входов в рынок. Это напрямую свяжет оценку системы с вероятностью успешных сделок (отработанных сигналов).

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

В довершение темы упрощений сделаем еще два кардинальных хода.

Выше говорилось, что в качестве торговых гипотез обычно берутся "покупка", "продажа" и "ожидание". Отбросив "ожидание", мы без потери общности изложения заметно сократили бы вычисления. Может показаться, что подобные упрощения негативно скажутся на применимости полученного результата, и отчасти это действительно так. Однако, если вы обратите внимание на то, какой объем материала еще осталось прочесть даже при таких упрощениях, то, возможно, согласитесь, что для начала неплохо бы просто получить работающую модель, а дополнить её деталями можно и позднее, постепенно. Желающие строить более сложные модели, учитывающие плотности вероятностей, могут найти соответствующие работы в интернете, в том числе и на английском языке, такие как Reasoning Methods for Merging Financial Technical Indicators, где описана гибридная вероятностная система принятия решений.

Наконец, второй и завершающий кардинальный ход — объединение статусов "покупка" и "продажа" в один, но с универсальным значением — "вход в рынок". Обычно мы используем разнонаправленные сигналы индикатора симметрично, аналогичным образом, т.е., например, перекупленность согласно индикатору становится сигналом для продажи, а перепроданность — сигналом для покупки.

Иными словами, гипотеза H звучит теперь как успешный вход в рынок в любом из двух направлений (покупка или продажа).

При данных условиях расчет вероятностей из правой части формулы (1) можно выполнить на выбранной истории котировок следующим образом.


т.к. на любом баре есть возможность успешно войти в рынок — одно из направлений окажется прибыльным (мы здесь пренебрегаем спредом, потому что рабочим таймфреймом будет D1, о чем более подробно сказано далее).

P(E) = количество баров с сигналами индикатора / общее количество баров

P(E|H) = количество баров с сигналами индикатора, совпавшими с прибыльным направлением торговли / общее количество баров

После упрощения получим формулу расчета на истории вероятности того, что сигнал выбранного индикатора указывает на условия для открытия успешной сделки:

   (2)

где Nok — количество правильных сигналов, Ntotal — общее количество сигналов.

Чуть позже мы реализуем фреймворк, который позволит вычислять эту вероятность для любого индикатора. Как мы увидим, эта вероятность обычно близка к 0.5, и нужно произвести некоторые изыскания для того, чтобы найти условия, где она стабильно больше 0.5. Однако индикаторы с большим показателем — редкость. Для стандартных индикаторов, которые мы и будем исследовать в первую очередь, данная вероятность колеблется в пределах 0.51-0.55. Понятно, что такие значения слишком малы и скорее позволят "остаться при своих", нежели стабильно увеличивать депозит.

Для решения этой проблемы необходимо использовать не один индикатор, а несколько. Само по себе это решение — не новость, оно применяется большинством трейдеров. Но теория вероятности позволит провести количественный анализ действенности индикаторов в различных сочетаниях и оценить потенциальный эффект.

Формула (1) для случая трех индикаторов (A, B, C) будет выглядеть так:

  (3)

Нам необходимо привести её к виду, удобному для алгоритмического расчета. К счастью, байесовская теория применяется во многих отраслях, и потому в них можно найти готовый рецепт для нашего случая.

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

Тогда формулу (3) можно раскрыть через вероятности отдельных индикаторов следующим образом (выкладки см. по вышеприведенной ссылке):

   (4)

Вычисления P(H|A), P(H|B), P(H|C) выполняются по формуле (2) для каждого индикатора в отдельности.

Разумеется, при необходимости формулу (4) легко расширить на любое количество индикаторов. Чтобы примерно понять, как количество индикаторов влияет на вероятность правильного торгового решения, предположим, что все индикаторы имеют равное значение вероятности:



Тогда формула (4) получит вид:

   (5)

где N — количество индикаторов.

График этой функции для различных значений N приведен на рисунке 1.

Вид совместной вероятности при различном количестве случайных величин

Рис. 1 Вид совместной вероятности при различном количестве случайных величин

Так, при p = 0.51 получим P(3) = 0.53, что не особо впечатляет, но при p = 0.55 - P(3) = 0.65, а это уже заметное улучшение.


Независимость показателей

Рассмотренные выше формулы основываются на предположении о независимости анализируемых случайных процессов, коими являются в нашем случае сигналы индикаторов. Но выполняется ли это условие?

Очевидно, что некоторые индикаторы, среди которых и многие из списка стандартных, имеют много общего. В качестве наглядной иллюстрации на рисунке 2 приведены некоторые из встроенных индикаторов.

Группы подобных стандартных индикаторов

Рис. 2 Группы подобных стандартных индикаторов

Легко заметить, что индикаторы Stochastic и WPR для одного периода, наложенные друг на друга в последнем окне, фактически повторяют друг друга. Это неудивительно, поскольку их формулы арифметически эквиваленты.

Показанные чуть выше на скриншоте индикаторы MACD и Awesome Oscillator идентичны с поправкой на тип скользящих средних. Кроме того, поскольку оба строятся на скользящих средних (MA), то их никак не назовешь независимыми от самих MA.

RSI, RVI, CCI также сильно коррелированы. Следует отметить, что практически все стандартные осцилляторы подобны, коэффициенты корреляции будут близки к 1.

Среди индикаторов волатильности, в частности, ATR и StdDev, также наблюдается заметное совпадение.

Все это важно учитывать при формировании набора индикаторов для торговой системы, поскольку реальный эффект от комитета зависимых индикаторов будет на практике гораздо ниже, чем ожидаемый теоретический.

Кстати говоря, подобная ситуация возникает и при обучении нейронных сетей. С их помощью трейдеры часто пытаются обработать данные от многих волюнтаристски выбранных индикаторов. Однако подача на вход сетей зависимых векторов существенно снижает эффективность обучения, поскольку вычислительные мощности сети тратятся вхолостую. Объем анализируемых данных может казаться большим, но содержащаяся в них информация дублируется, не имеет смысла.

Строгий подход к данной проблеме требует расчета корреляции между индикаторами и составления наборов с наименьшими попарными значениями. Это отдельная большая область исследования. Желающие могут найти в интернете статьи на эту тему. Здесь же мы будем руководствоваться общими соображениями на основе изложенных выше наблюдений. Например, один из наборов может выглядеть так: Stochastic, ATR, AC (Acceleration/Deceleration) или WPR, Bollinger Bands, Momentum.

Здесь следует пояснить, что индикатор Ускорения/Замедления (AC) по сути представляет собой производную осциллятора. Почему он подходит для включения в группу?

Представим ряд котировок (или производный от них осциллятор) в упрощенном виде как периодические колебания, например, косинус или синус. Вспомним, что производные этих функций равны соответственно:

   (6)



Корреляции этих функций и их производных равны нулю.

    (7)


Поэтому использование первой производной от индикатора является в общем случае хорошим кандидатом на рассмотрение в качестве дополнительного независимого индикатора.

Вторая производная — кандидат уже сомнительный в подобных колебательных процессах, потому что велики шансы получить реплику исходного сигнала.

Завершая разговор о независимости индикаторов, имеет смысл остановиться на вопросе, можно ли считать независимыми копии индикатора, посчитанные с разными периодами.

Можно предположить, что ответ зависит от соотношения периодов. Незначительная разница, очевидно, сохраняет зависимость показателей, а потому требуется заметное различие. Это отчасти согласуется с классическими методами, таким как метод трех экранов Элдера, где таймфреймы, отличающиеся, как правило, не менее чем в 5 раз, являются эквивалентами анализа индикаторов с различными временными периодами.

Следует отметить, что в рассматриваемой системе независимыми величинами на самом деле должны быть не показания индикаторов, а непосредственно торговые сигналы, генерируемые ими. Однако для большинства индикаторов одного типа, например, осцилляторов, принципы формирования торговых сигналов схожи, и потому сильная или слабая зависимость временных рядов эквивалентна сильной или слабой зависимости сигналов.


Проектирование

Итак, мы разобрались с теорией и готовы заняться вопросом, что и как кодировать.

Статистику торговых сигналов индикаторов будем собирать в специальном эксперте. Чтобы эксперт мог торговать на основе показаний произвольных индикаторов, потребуется разработать фреймворк (фактически, заголовочный mqh-файл), который получит описание используемых индикаторов и способы формирования сигналов на их основе через входные параметры. Например, у нас должна быть возможность задать в параметрах две скользящие средние разных периодов и сформировать сигналы на покупку и продажу, когда быстрая МА пересекает более медленную вверх и вниз, соответственно.

Эксперт будет с явным контролем открытия баров и торговать только по ценам открытия. Это не реальный эксперт, а инструмент для вычисления вероятностей и проверки гипотез. Нам важно, чтобы проверка проходила быстро, потому что вариантов наборов индикаторов можно придумать неограниченно много.

В качестве рабочего таймфрейма по умолчанию будет использоваться D1. Разумеется, никто не запретит в дальнейшем проводить анализ на любом другом таймфрейме, однако D1 наименее подвержен случайному шуму, а анализ закономерностей, существующих на протяжении нескольких лет, наиболее полно отвечает специфике вероятностного подхода. Кроме того, для торговых стратегий на D1 можно обычно пренебречь спредом, что нивелирует наш отказ от поддержки промежуточного состояния системы "ждать". Для внутридневной торговли, конечно, нельзя было бы сделать такое допущение, и потребовалось бы просчитывать вероятность большего количества гипотез.

Как уже было сказано ранее, эксперт будет открывать позиции по сигналам индикаторов, а закрывать через предопределенный период времени. Для этого введем соответствующий входной параметр. Его значение по умолчанию будет равно 5 дням. Это характеристический период для таймфрейма D1, он используется во многих исследовательских работах по трейдингу, также использующих D1.

Эксперт и фреймворк будут кроссплатформенными, то есть будут компилироваться и выполняться как в MetaTrader 4, так и в MetaTrader 5. Эта возможность будет обеспечена за счет уже имеющихся, публично доступных заголовочных файлов-оберток, которые позволяют использовать синтаксис MQL API MetaTrader 4 в среде MetaTrader 5, а кроме того, мы воспользуемся в некоторых случаях условной компиляцией: специфические части кодов будут обернуты в директивы препроцессора #ifdef __MQL4__ и #ifdef __MQL5__.


Реализация на MQL

Фреймворк для индикаторов

Обзор фреймворка для обработки сигналов индикаторов начнем с обсуждения того, какие типы индикаторов нам понадобятся. Самое очевидное перечисление включает все встроенные индикаторы, а также пункт для пользовательских индикаторов iCustom. Перечисление будет необходимо для выбора индикаторов с помощью входных параметров фреймворка.

enum IndicatorType
{
  iCustom,

  iAC,
  iAD,
  tADX_period_price,
  tAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price,
  iAO,
  iATR_period,
  tBands_period_deviation_shift_price,
  iBearsPower_period_price,
  iBullsPower_period_price,
  iBWMFI,
  iCCI_period_price,
  iDeMarker_period,
  tEnvelopes_period_method_shift_price_deviation,
  iForce_period_method_price,
  dFractals,
  dGator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price,
  fIchimoku_tenkan_kijun_senkou,
  iMomentum_period_price,
  iMFI_period,
  iMA_period_shift_method_price,
  dMACD_fast_slow_signal_price,
  iOBV_price,
  iOsMA_fast_slow_signal_price,
  iRSI_period_price,
  dRVI_period,
  iSAR_step_maximum,
  iStdDev_period_shift_method_price,
  dStochastic_K_D_slowing_method_price,
  iWPR_period

};

Название каждого встроенного индикатора содержит суффикс с информацией о параметрах самого индикатора. Первый символ элемента означает количество доступных буферов, например, i — один буфер, d — два, t — три. Все это — всего лишь подсказки для пользователя. Если он укажет неверное количество параметров или индекс несуществующего буфера, фреймворк выведет ошибку в лог.

Разумеется, во входных параметрах нужно будет указать для каждого индикатора не только его тип, но и фактические параметры в виде строки, номер буфера и номер бара, откуда будут читаться данные.

На основе показаний индикаторов необходимо сформировать сигналы. В принципе, их может быть много разных, но основные варианты сведем воедино в другом перечислении.

enum SignalCondition
{
  Disabled,
  NotEmptyIndicatorX,
  SignOfValueIndicatorX,
  IndicatorXcrossesIndicatorY,
  IndicatorXcrossesLevelX,
  IndicatorXrelatesToIndicatorY,
  IndicatorXrelatesToLevelX
};

Таким образом, сигналы могут формироваться:

  • если значение индикатора не пустое;
  • значение индикатора имеет требуемый знак (положительное или отрицательное);
  • индикатор пересекает другой индикатор, и здесь следует отметить, что при описании сигнала мы должны предоставить возможность задать 2 индикатора;
  • индикатор пересекает некий уровень, и здесь становится ясно, что должно быть поле для ввода уровня;
  • индикатор расположен требуемым образом относительно другого индикатора (например, выше или ниже);
  • индикатор расположен требуемым образом относительно заданного уровня;

Первый элемент — Disabled — позволяет отключить любое условие генерации сигналов. Мы предоставим несколько идентичных групп входных параметров для описания сигналов, и каждый сигнал будет по умолчанию выключен.

Из названий пунктов предыдущего перечисления можно предположить, что необходимо каким-то образом задавать требуемый знак значений и положение линий относительно друг друга. Для этой цели добавим еще одно перечисление.

enum UpZeroDown
{
  EqualOrNone,
  UpSideOrAboveOrPositve,
  DownSideOrBelowOrNegative,
  NotEqual
};

EqualOrNone позволяет проверить:

  • пустое значение в комбинации с SignOfValueIndicatorX
  • равенство уровню в комбинации с IndicatorXrelatesToLevelX

UpSideOrAboveOrPositve позволяет проверить:

  • пересечение снизу вверх с помощью IndicatorXcrossesIndicatorY
  • положительность значения с помощью SignOfValueIndicatorX
  • пересечение уровня снизу вверх с помощью IndicatorXcrossesLevelX
  • рост значений индикатора на последовательных барах с помощью IndicatorXrelatesToIndicatorY, если X и Y — один и тот же индикатор
  • расположение X над Y с помощью IndicatorXrelatesToIndicatorY, если X и Y — разные индикаторы
  • расположение индикатора над уровнем с помощью IndicatorXrelatesToLevelX

DownSideOrBelowOrNegative позволяет проверить:

  • пересечение сверху вниз с помощью IndicatorXcrossesIndicatorY
  • на отрицательное значение с помощью SignOfValueIndicatorX
  • пересечение уровня сверху вниз с помощью IndicatorXcrossesLevelX
  • падение значений индикатора на последовательных барах с помощью IndicatorXrelatesToIndicatorY, если X и Y — один и тот же индикатор
  • расположение X под Y с помощью IndicatorXrelatesToIndicatorY, если X и Y — разные индикаторы
  • расположение индикатора под уровнем с помощью IndicatorXrelatesToLevelX

NotEqual позволяет проверить:

  • неравенство уровню (значению) с помощью IndicatorXrelatesToLevelX

Когда сигнал сработает, необходимо его обработать. Для этого определим специальное перечисление.

enum SignalType
{
  Alert,
  Buy,
  Sell,
  CloseBuy,
  CloseSell,
  CloseAll,
  BuyAndCloseSell,
  SellAndCloseBuy,
  ModifyStopLoss,
  ModifyTakeProfit,
  ProceedToNextCondition
};

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

Можно заметить, что в списке действий нет установки отложенных ордеров. Это оставлено за рамками данной работы. Желающие могут расширить фреймворк.

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

input IndicatorType Indicator1Selector = iCustom; // ·     Selector
input string Indicator1Name = ""; // ·     Name
input string Parameter1List = "" /*1.0,value:t,value:t*/; // ·     Parameters
input string Indicator1Buffer = ""; // ·     Buffer
input int Indicator1Bar = 1; // ·     Bar

Параметр Indicator1Name предназначен для задания имени пользовательского индикатора, когда в Indicator1Selector стоит iCustom.

Параметр Parameter1List позволяет задать в виде строки параметры индикатора, разделенные запятыми. Тип каждого входного параметра будет распознан автоматически, например, 11.0 — double, 11 — int, 2015.01.01 20:00 — date/time, true/false — bool, "text" — string. Некоторые параметры — например, типы скользящих средних или типы цен — могут задаваться не числом, а строкой без кавычек (sma, ema, smma, lwma, close, open, high, low, median, typical, weighted, lowhigh, closeclose).

Indicator1Buffer — номер или название буфера без кавычек. Поддерживаемые названия буферов - main, signal, upper, lower, jaw, teeth, lips, tenkan, kijun, senkouA, senkouB, chikou, +di, -di.

Indicator1Bar — номер бара, по умолчанию равен 1.

После того как определены индикаторы, на их основе можно формировать сигналы, т.е. условия срабатывания событий. Каждый сигнал задается группой входных параметров.

input string __SIGNAL_A = "";
input SignalCondition ConditionA = Disabled; // ·     Condition A
input string IndicatorA1 = ""; // ·     Indicator X for signal A
input string IndicatorA2 = ""; // ·     Indicator Y for signal A
input double LevelA1 = 0; // ·     Level X for signal A
input double LevelA2 = 0; // ·     Level Y for signal A
input UpZeroDown DirectionA = EqualOrNone; // ·     Direction or sign A
input SignalType ExecutionA = Alert; // ·     Action A

Для каждого сигнала можно указать идентификатор в параметре __SIGNAL_.

С помощью Condition выбирается условие проверки сигнала. Далее задаются один или два индикатора и одно или два значения уровней (второй уровень зарезервирован на будущее и не будет использоваться в этом эксперименте). Индикаторы в параметрах Indicator — это или номер индикатора из соответствующей группы атрибутов, или прототип индикатора в виде:

indicatorName@buffer(param1,param2,...)[bar]

Эта форма записи позволяет быстро определить используемый индикатор без его подробного описания с помощью группы атрибутов. Например,

iMA@0(1,0,sma,high)[1]

дает значения цен high, причем на каждом текущем баре работающего эксперта берется бар номер 1 (самый последний завершенный, для которого цена high известна окончательно).

Таким образом, индикаторы можно задавать как в выделенных группах атрибутов (для последующей ссылки на них из сигналов по номеру), так и непосредственно в сигналах в параметре Indicator (X или Y). Первый способ удобен, если один и тот же индикатор нужно использовать в разных сигналах или в качестве X и Y внутри одного сигнала.

Параметр Direction указывает направление или знак значения для срабатывания условия. В соответствии с Execution выполняется то или иное действие при активации сигнала.

Далее мы увидим примеры определения индикаторов и сигналов на их основе.

Внутри фреймворка сейчас определено, что индикатор не может иметь более 20 параметров, максимальное количество выделенных групп с атрибутами индикаторов равно 6 (но, как мы помним, индикаторы можно дополнительно задавать непосредственно в сигнале), а сигналов - 8. Все это можно поменять в исходном коде. Файл IndicatN.mqh приложен в конце статьи.

Также в этом файле в виде нескольких классов реализована вся логика распарсивания параметров индикаторов, их вызова, проверки условий и возвращения результатов проверки в вызывающий код (которым будет наш эксперт).

В частности, для передачи инструкций о необходимости выполнить какое-либо действие из перечисления SignalType, рассмотренного выше, используется простой публичный класс TradeSignals с логическими полями, соответствующими пунктам перечисления:

class TradeSignals
{
  public:
    bool alert;
    bool buy;
    bool sell;
    bool buyExit;
    bool sellExit;
    bool ModifySL;
    bool ModifyTP;
    
    int index;
    double value;
    
    string message;
  
    TradeSignals(): alert(false), buy(false), sell(false), buyExit(false), sellExit(false), ModifySL(false), ModifyTP(false), value(EMPTY_VALUE), message(""){}
};

При выполнении необходимых условий поля устанавливаются в true. Например, если выбрано действие CloseAll, в объекте TradeSignals взводятся флаги buyExit и sellExit.

Поле index содержит порядковый номер сработавшего условия.

С помощью поля value можно передать произвольное значение, например, новый уровень стоп-лосса, полученный из значений индикатора.

Наконец, поле message содержит сообщение для пользователя с описанием ситуации.

С подробностями реализации всех классов можно ознакомиться в исходном коде. Он использует вспомогательные заголовочные файлы fmtprnt2.mqh (форматированный вывод в лог) и RubbArray.mqh ("резиновый" массив), которые также приложены.

Заголовочный файл фреймворка IndicatN.mqh следует включать в код эксперта с помощью директивы #include. В результате после компиляции в диалоге настройки параметров эксперта мы получим группы входных параметров с атрибутами индикаторов:

Настройка индикаторов

Рис.3 Настройка индикаторов

и с определениями сигналов:

Настройка торговых сигналов

Рис.4 Настройка торговых сигналов

На скриншотах показаны уже заполненные свойства. Мы рассмотрим их подробно, когда перейдем к концепции эксперта и начнем настраивать конкретные торговые стратегии. Здесь же стоит отметить, что при задании атрибутов индикаторов можно любые числовые параметры заменять на выражение вида =var1, =var2 и т.д. до 9. Они ссылаются на специальные одноименные входные параметры фреймворка var1, var2 и т.д., предназначенные для оптимизации. Например, запись:

iMACD@main(=var4,=var5,=var6,open)[0]

означает, что параметры периодов быстрой, медленной и сигнальной скользящих средних MACD могут оптимизироваться через входные параметры var4, var5 и var6 соответственно. И даже при отключенной оптимизации, во время одиночного тестирования, значения соответствующих атрибутов индикатора будут считываться из указанных входных параметров фреймворка.

Тестовый эксперт

Для облегчения кодирования вынесем все торговые функции в специальный класс и оформим его как отдельный заголовочный файл Expert0.mqh. Поскольку мы собираемся проверять достаточно простые торговые системы, класс позволит только открывать и закрывать позиции.

Таким образом, все рутинные операции с индикаторами и связанные с торговлей вынесены в заголовочные файлы.

#include <IndicatN.mqh>
#include <Expert0.mqh>

Непосредственно в файле эксперта indstats.mq4 будет совсем немного строк кода и простая логика.

Так как эксперт после смены расширения на mq5 должен компилироваться и работать в MetaTrader 5, добавим заголовочные файлы, обеспечивающие перенос кодов в новую среду.

#ifdef __MQL5__
  #include <MarketMQL4.mqh>
  #include <ind4to5.mqh>
  #include <mt4orders.mqh>
#endif

Теперь обратимся к входным параметрам эксперта.

input int ConsistentSignalNumber = 1;
input int Magic = 0;
input float Lot = 0.01f;
input int TradeDuration = 1;

Magic и Lot необходимы для создания объекта Expert из файла Expert0.mqh.

Expert e(Magic, Lot);

Параметр ConsistentSignalNumber будет содержать количество торговых сигналов, которые мы пытаемся объединить для повышения надежности.

Параметр TradeDuration задает количество баров, в течение которых будет удерживаться открытая позиция. Как говорилось выше, мы будет открывать сделки по сигналам и выходить из них через 5 баров, т.е. дней, поскольку используется таймфрейм D1.

В обработчике события OnInit проинициализируем индикаторный фреймворк.

int OnInit()
{
  return IndicatN::handleInit();
}

В обработчике OnTick обеспечим контроль за открытием бара.

void OnTick()
{
  static datetime lastBar;
  
  if(lastBar != Time[0])
  {
    const RubbArray<TradeSignals> *ts = IndicatN::handleStart();
    ...
    lastBar = Time[0];
  }
}

При формировании нового бара проверим все индикаторы и связанные с ними условия, вызвав опять индикаторный фреймворк. В результате получим массив сработавших сигналов — объектов TradeSignals.

Теперь настало время поговорить о накоплении статистики.

Каждое условие (событие) фреймворка в случае его наступления генерирует по-умолчанию сигнал с флагом alert. Мы будем использовать это для подсчета количества сигналов от индикаторов, а также количества реализовавшихся состояний системы, т.е. случаев (баров), когда покупка или продажа были бы успешными.

Для подсчета статистики опишем массивы.

int bars = 0; // total count of bars/samples
int bull = 0, bear = 0; // number of bars/samples per trade type
int buy[MAX_SIGNAL_NUM] = {0}, sell[MAX_SIGNAL_NUM] = {0};  // unconditional signals arrays
int buyOnBull[MAX_SIGNAL_NUM] = {0}, sellOnBear[MAX_SIGNAL_NUM] = {0}; // conditional (successful) signals arrays

В нашем случае побаровой торговли каждый бар — это потенциальный новый вход в сделку длительностью 5 баров. Каждый такой отрезок характеризуется ростом или падением котировок и помечается как бычий или медвежий соответственно.

Все сигналы покупки и продажи будут суммироваться в массивах buy и sell, а в том случае, если соответствующий сигнал совпал с "бычностью" или "медвежностью" отрезка, то есть является успешным, он также аккумулируется в массиве buyOnBull или sellOnBear, в зависимости от типа.

Для заполнения массивов напишем следующий код внутри OnTick.

    const RubbArray<TradeSignals> *ts = IndicatN::handleStart();
    bool up = false, down = false;
    int buySignalCount = 0, sellSignalCount = 0;
    
    for(int i = 0; i < ts.size(); i++)
    {
      // alerts are used to collect statistics
      if(ts[i].alert)
      {
        // while setting up events, enumerated by i,
        // hypothesis H_xxx should come first, before signals S_xxx,
        // because we assign up or down marks here
        if(IndicatN::GetSignal(ts[i].index) == "H_BULL")
        {
          bull++;
          buy[ts[i].index]++;
          up = true;
        }
        else if(IndicatN::GetSignal(ts[i].index) == "H_BEAR")
        {
          bear++;
          sell[ts[i].index]++;
          down = true;
        }
        else if(StringFind(IndicatN::GetSignal(ts[i].index), "S_BUY") == 0)
        {
          buy[ts[i].index]++;
          if(up)
          {
            if(PrintDetails) Print("buyOk ", IndicatN::GetSignal(ts[i].index));
            buyOnBull[ts[i].index]++;
          }
        }
        else if(StringFind(IndicatN::GetSignal(ts[i].index), "S_SELL") == 0)
        {
          sell[ts[i].index]++;
          if(down)
          {
            if(PrintDetails) Print("sellOk ", IndicatN::GetSignal(ts[i].index));
            sellOnBear[ts[i].index]++;
          }
        }
        
        if(PrintDetails) Print(ts[i].message);
      }
    }

Получив массив сработавших сигналов, проходим по его элементам в цикле. Если взведен флаг alert, это сбор статистики.

Прежде чем анализировать код более глубоко, введем специальное соглашение о наименовании сигналов (событий). Гипотезы о бычьем или медвежьем состоянии рынка будем помечать идентификаторами H_BULL и H_BEAR. Эти события должны определяться с помощью входных параметров фреймворка самыми первыми — до прочих событий (сигналов индикаторов). Это необходимо для того, чтобы на основании подтвержденных гипотез устанавливать соответствующие признаки — логические переменные up и down.

Сигналы индикаторов должны иметь идентификаторы, начинающиеся с S_BUY или S_SELL.

Как видно, с помощью ссылки на номер активированного событий ts[i].index, мы получаем его идентификатор через вызов функции GetSignal. В случае реализации гипотез обновляем общие счетчики бычьих или медвежьих участков. В случае генерации сигналов, ведем их общий подсчет для каждого типа сигнала, а также индекс их успешности, то есть количество совпадений с текущими гипотезами.

Напомним, что либо гипотеза H_BULL, либо гипотеза H_BEAR является истинной на каждом баре.

Помимо сбора статистики, эксперт должен поддерживать торговлю по сигналам. Для этой цели дополним тело цикла проверкой на флаги buy и sell.

      if(ts[i].buy)
      {
        buySignalCount++;
      }
      else
      if(ts[i].sell)
      {
        sellSignalCount++;
      }

После цикла реализуем торговый функционал. Прежде всего, закроем открытые позиции (если они есть) по истечении заданного периода.

    if(e.getLastOrderBar() >= TradeDuration)
    {
      e.closeMarketOrders();
    }

Далее осуществим покупку или продажу в зависимости от совместных сигналов.

    if(buySignalCount >= ConsistentSignalNumber
    && sellSignalCount >= ConsistentSignalNumber)
    {
      Print("Signal collision");
    }
    else
    if(buySignalCount >= ConsistentSignalNumber)
    {
      e.closeMarketOrders(e.mask(OP_SELL));
      
      if(e.getOrderCount(e.mask(OP_BUY)) == 0)
      {
        e.placeMarketOrder(OP_BUY);
      }
    }
    else
    if(sellSignalCount >= ConsistentSignalNumber)
    {
      e.closeMarketOrders(e.mask(OP_BUY));
      
      if(e.getOrderCount(e.mask(OP_SELL)) == 0)
      {
        e.placeMarketOrder(OP_SELL);
      }
    }

Если сигналы к покупке и продаже противоречат друг другу, пропускаем такое состояние. Если количество сигналов на покупку или продажу равно или больше предопределенному количеству ConsistentSignalNumber, открываем соответствующий ордер.

Следует отметить, что установив ConsistentSignalNumber на меньшее значение, чем количество настроенных сигналов, можно будет протестировать торговлю системы в режиме объединения всех или большинства стратегий. В штатном режиме работы эксперт будет использовать пересечение, а не объединение, поскольку для нахождения совместных событий ConsistentSignalNumber должно быть точно равно количеству сигналов. Например, при настроенных 3 сигналах и ConsistentSignalNumber равном 3, торговля будет вестись только по наступлению всех трех событий одновременно. Если же ConsistentSignalNumber установить в 1, то сделки будут открываться при поступлении любого (хотя бы одного) из 3 сигналов.

В обработчике OnDeinit выведем собранную статистику по алертам или по истории ордеров в лог.

Полностью исходный код эксперта можно посмотреть в файле indstats.mq4.


Настройка торговых сигналов

Все сигналы должны проверяться относительно двух гипотез о покупке или продаже. Для этого настроим сигналы H_BULL и H_BEAR, а также их индикаторы.

Для получения цен баров используем индикатор iMA с периодом 1. В группе __INDICATOR_1 установим:

Selector = iMA_period_shift_method_price

Parameters = 1,0,sma,open

Buffer = 0

Bar = 0

В группе __INDICATOR_2 сделаем аналогичные настройки за исключением номера бара — там следует ввести 5, количество баров, которые мы будем использовать в параметре TradeDuration.

Иными словами, в режиме сбора статистики эксперт не торгует, а анализирует изменение котировок между 5-м и 0-м баром, а также сигналы индикаторов на 5 или 6 баре, в зависимости от используемого типа цены: для индикаторов, работающих по ценам open, можно брать значения с 5 бара, а для всех остальных — с 6. В режиме сбора статистики бар номер 5 — это виртуальный текущий бар, а все последующие предоставляют информацию о "будущей" реализации гипотез бычьего или медвежьего рынка.

Сразу оговоримся, что в режиме торговли мы будем брать сигналы с бара 0 (если индикатор строится по цене open) или бара 1 (в остальных случаях). Если бы эксперт работал не по ценам открытия и анализировал тики, нужно было бы в этом режиме смотреть значения индикаторов на 0 баре.

Наличие этих двух режимов — сбора статистики и торговли — подразумевает необходимость создания различных наборов параметров, отличающихся номерами рабочих баров. Мы начнем с набора для сбора статистики, а затем легко преобразуем его в реальный, торговый.

С помощью этих двух копий индикатора MA настроим гипотезы. В группе __SIGNAL_A введем:

__SIGNAL_A = H_BULL
Condition = IndicatorXrelatesToIndicatorY Indicator X = 1 Indicator Y = 2 Direction or sign = UpSideOrAboveOrPositve Action = Alert

Группу __SIGNAL_B настроим аналогично, за исключением направления:

__SIGNAL_B = H_BEAR
Direction or sign = DownSideOrBelowOrNegative

Для проверки вероятностной модели торговли будем использовать 3 стандартных стратегии, основанных на индикаторах:

  • Stochastic
  • MACD
  • BollingerBands

Заранее отметим, что параметры всех индикаторов были оптимизированы, причем некоторые специально оставлены в виде ссылок на входные переменные var1, var2 и т.д. для демонстрации этой возможности фреймворка. Для воспроизведения положительных результатов на данных вашего провайдера каждую стратегию, скорее всего, потребуется переоптимизировать.

Стратегия Stochastic заключается в покупке при пересечении индикатором уровня 20 снизу вверх и продажи при пересечении уровня 80 сверху вниз. Для этого определим группу __INDICATOR_3:

Selector = dStochastic_K_D_slowing_method_price
Parameters = 14,3,3,sma,lowhigh Buffer = main Bar = 6

Поскольку для индикатора используются цены high и low, необходимо брать бар номер 6 — последний из полностью сформированных перед баром 5, на котором начинается виртуальная торговля, если сигнал сработал.

На основе индикатора Stochastic настроим сигналы покупки и продажи. Группа для покупки:

__SIGNAL_C = S_BUY stochastic
Condition = IndicatorXcrossesLevelX Level X = 20 Direction or sign = UpSideOrAboveOrPositve

Группа для продажи:

__SIGNAL_D = S_SELL stochastic
Condition = IndicatorXcrossesLevelX Level X = 80 Direction or sign = DownSideOrBelowOrNegative

Стратегия по MACD заключается в покупке при пересечении линией main сигнальной линии вверх и продаже — при пересечении вниз.

Настроим группу индикатора __INDICATOR_4:

Selector = dMACD_fast_slow_signal_price
Parameters = =var4,=var5,=var6,open Buffer = signal Bar = 5

Периоды fast, slow, signal будем считывать из параметров var4, var5, var6, доступных для оптимизации. Там сейчас стоят 6, 21, 6 соответственно. Мы используем бар 5, т.к. индикатор строим по цене open.

Поскольку количество групп для настройки индикаторов ограничено, буфер main опишем непосредственно в сигналах. Группа для покупки:

__SIGNAL_E = S_BUY macd
Condition = IndicatorXcrossesIndicatorY Indicator X = iMACD@main(=var4,=var5,=var6,open)[5] Indicator Y = 4 Direction or sign = UpSideOrAboveOrPositve

Группа для продажи: 

__SIGNAL_F = S_SELL macd
Condition = IndicatorXcrossesIndicatorY Indicator X = iMACD@main(=var4,=var5,=var6,open)[5] Indicator Y = 4 Direction or sign = DownSideOrBelowOrNegative

Стратегия на основе BollingerBands заключается в покупке, когда high предыдущего бара пробивает верхнюю линию индикатора, смещенного вправо на 2 бара, и продаже, когда low предыдущего бара пробивает нижнюю линию индикатора, смещенного вправо на 2 бара. Ниже приведены настройки двух линий индикатора.

__INDICATOR_5:

Selector = tBands_period_deviation_shift_price

Parameters = =var1,=var2,2,typical
Buffer = upper Bar = 5

__INDICATOR_6:

Selector = tBands_period_deviation_shift_price
Parameters = =var1,=var2,2,typical Buffer = lower Bar = 5

Период и девиация заданы в var1 и var2 как 7 и 1 соответственно. В обоих случаях может использоваться бар 5, несмотря на тип цены typical, потому что линии индикатора сдвинуты на 2 бара вправо, т.е. фактически рассчитываются на прошлых данных.

Наконец, группы настройки сигналов выглядят следующим образом.

__SIGNAL_G = S_BUY bands
Condition = IndicatorXcrossesIndicatorY Indicator X = iMA@0(1,0,sma,high)[6] Indicator Y = 5 Direction or sign = UpSideOrAboveOrPositve
__SIGNAL_H = S_SELL bands
Condition = IndicatorXcrossesIndicatorY Indicator X = iMA@0(1,0,sma,low)[6] Indicator Y = 6 Direction or sign = DownSideOrBelowOrNegative

Все настройки в виде set-файлов приложены в конце статьи.


Результаты

Статистика по индикаторам

Для расчета вероятностей используем статистику за период 2014.01.01-2017.01.01 для пары EURUSD D1. Настройки эксперта для режима сбора статистики содержатся в файле indstats-stats-all.set.

Собранные данные выводятся в лог. Ниже приведен пример:

: bars=778
: bull=328 bear=449
:    buy:    328      0     30      0     50      0     58      0 
:  buyOk:      0      0     18      0     29      0     30      0 
:   sell:      0    449      0     22      0     49      0     67 
: sellOk:      0      0      0     14      0     28      0     41 
: totals:   0.00   0.00   0.60   0.64   0.58   0.57   0.52   0.61 
: Stats by name:
:  macd=0.576 [57/99]
:  bands=0.568 [71/125]
:  stochastic=0.615 [32/52]

Общее число баров — 778, из них 328 подходило для успешной 5-дневной сделки на покупку, и 449 — для успешной 5-дневной продажи. Первые 2 колонки содержат счетчики гипотез — те же 2 числа, а следующие пары колонок относятся к соответствующим торговым стратегиям, каждая из которых представлена колонкой для покупки и колонкой для продажи. Например, стратегия на основе стохастика дала 30 сигналов на покупку, и 18 из них были прибыльными, а также 22 — на продажу, 14 из которых были прибыльными. Если суммировать для каждой стратегии общее количество успешных сигналов и разделить на количество сгенерированных сигналов, получим эффективность (вероятность успеха на основе исторических данных) каждой из них:

  • Stochastic — 0.615
  • MACD — 0.576
  • Bands — 0.568

Тестовая торговля

Чтобы убедиться, что статистика посчитана правильно, необходимо запустить эксперт в режиме торговли. Для этого нужно отредактировать в настройках номера баров, заменив 5 на 0, 6 — на 1. Кроме того, следует последовательно включать одну за другой торговые стратегии путем установки параметров Action в Buy и Sell вместо Alert. Например, для проверки торговли по стохастику, в группе __SIGNAL_C (S_BUY stochastic) заменим в параметре Action значение Alert на значение Buy, а в группе __SIGNAL_D (S_SELL stochastic) — значение Alert на Sell.

Соответствующие настройки для всех 3 стратегий приведены, соответственно, в файлах indstats-trade-stoch.set, indstats-trade-macd.set, indstats-trade-bands.set.

Запустив эксперт 3 раза с этими наборами параметров, получим 3 лога с краткими отчетами о торговле. Статистика — в самом конце. Например, для стохастика получим строчку:

: Buys: 18/29 0.62 Sells: 14/22 0.64 Total: 0.63

Это цифры о реальных сделках: 18 покупок из 29 прибыльны, 14 продаж из 22 прибыльны, общая эффективность сигнала — 0.63.

Результаты стратегий по MACD и BollingerBands приведены ниже.

: Buys: 29/49 0.59 Sells: 28/49 0.57 Total: 0.58

: Buys: 29/51 0.57 Sells: 34/59 0.58 Totals: 0.57

Сведем показатели всех стратегий в один список.

  • Stochastic — 0.63
  • MACD — 0.58
  • Bands — 0.57

Здесь мы видим почти полное соответствие с теорией из предыдущего подраздела. Некоторое различие объясняется тем, что торговые сигналы могут накладываться друг на друга, если находятся в пределах 5 баров, и тогда повторная сделка не открывается.

Разумеется, мы можем проанализировать торговые отчеты для каждой стратегии.

Отчет по стратегии на основе индикатора Stochastic

Рис.5 Отчет по стратегии на основе индикатора Stochastic


Отчет по стратегии на основе индикатора MACD

Рис.6 Отчет по стратегии на основе индикатора MACD


Отчет по стратегии на основе индикатора BollingerBands

Рис.7 Отчет по стратегии на основе индикатора BollingerBands

По формуле (4) рассчитаем теоретическую вероятность того, что сделка будет успешной при входе по синхронным сигналам всех трех индикаторов.

P(H|ABC) = 0.63 * 0.58 * 0.57 / (0.63 * 0.58 * 0.57 + 0.37 * 0.42 * 0.43) = 0.208278 / (0.208278 + 0.066822) = 0.208278 / 0.2751 = 0.757

Для тестирования этой ситуации мы должны включить в работу все три сигнала, а также поменять значение параметра ConsistentSignalNumber с 1 на 3. Соответствующие настройки находятся в файле indstats-trade-all.set.

Согласно проверке торговли в тестере, итоговая эффективность такой системы на практике равна 0.75:

: Buys: 4/7 0.57 Sells: 5/5 1.00 Total: 0.75

Вот отчет тестирования:

Отчет по комбинации стратегий на основе 3-х индикаторов

Рис.8 Отчет по комбинации стратегий на основе 3-х индикаторов

Ниже приведена таблица показателей торговли по каждому из индикаторов в отдельности и по их суперпозиции.


Profit,$ PF N DD,$
Stochastic 204 2.36 51 41
MACD 159 1.39 98 76
Bands 132 1.29 110 64
Total 68 3.18 12 30

Как мы видим, увеличение вероятности выигрыша достигается за счет более редких, но более точных входов — количество сделок и общая прибыль снизились, однако профит-фактор и максимальная просадка улучшились как минимум на 35%, а в некоторых случаях — в 2 с лишним раза.


Заключение

В статье рассмотрен самый простой вариант реализации вероятностного подхода к принятию торговых решений на основе сигналов индикаторов. С помощью специального эксперта было показано, что теоретические расчеты повышения вероятности успешных сделок по формуле Байеса соответствуют результатам, получаемым на практике.

Поскольку генерация сигналов дискретна, сигналы разных индикаторов могут не совпадать и потенциально возможна ситуация, когда суперпозиция индикаторов не дает общих сигналов, подтвержденных всеми индикаторами. Одно из возможных решений этой проблемы — введение временного допуска между сигналами.

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

Ниже приложены файлы:

  • indstats.mq4 (он же indstats.mq5) — эксперт;
  • common-includes.zip — архив с общими использующимися заголовочными mqh-файлами;
  • additional-mt5-includes.zip — архив с дополнительными заголовочными файлами для МТ5;
  • instats-tester-sets.zip — архив с set-файлами настроек;


Прикрепленные файлы |
indstats.mq4 (7.81 KB)
common-includes.zip (14.28 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (29)
Stanislav Korotky
Stanislav Korotky | 1 сент. 2017 в 15:12
Rashid Umarov:

Спасибо, что заметили опечатку. Там должен быть "+", а не минус "-"  https://ru.wikipedia.org/wiki/%D0%91%D0%B0%D0%B9%D0%B5%D1%81%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F_%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D0%B0%D1%86%D0%B8%D1%8F_%D1%81%D0%BF%D0%B0%D0%BC%D0%B0

Исправим


Это опечатка, внесенная редактором между 3-ей и 4-ей ревизией. В 3-ей формулы написаны еще мной текстом - там все правильно, а в 4-й - формула уже в виде картинки и с ошибкой.

Maxim Dmitrievsky
Maxim Dmitrievsky | 5 апр. 2018 в 11:34

Как пофиксить что бы что-нибудь заработало? ) не компилируется эксперт, МТ5

code generation error           1 

и зачем так писать код с кучей ворнингов?

'indstats.mq5'  indstats.mq5    1       1
'MarketMQL4.mqh'        MarketMQL4.mqh  1       1
'ind4to5.mqh'   ind4to5.mqh     1       1
macro redefinition      ind4to5.mqh     170     9
macro redefinition      ind4to5.mqh     171     9
macro redefinition      ind4to5.mqh     174     9
'mt4orders.mqh' mt4orders.mqh   1       1
macro redefinition      mt4orders.mqh   412     9
macro redefinition      mt4orders.mqh   413     9
'IndicatN.mqh'  IndicatN.mqh    1       1
'fmtprnt2.mqh'  fmtprnt2.mqh    1       1
'RubbArray.mqh' RubbArray.mqh   1       1
'Expert0fix.mqh'        Expert0fix.mqh  1       1
'HashMapTemplate.mqh'   HashMapTemplate.mqh     1       1
possible loss of data due to type conversion    MarketMQL4.mqh  49      8
possible loss of data due to type conversion    MarketMQL4.mqh  61      8
possible loss of data due to type conversion    MarketMQL4.mqh  63      8
possible loss of data due to type conversion    MarketMQL4.mqh  65      8
possible loss of data due to type conversion    MarketMQL4.mqh  77      8
possible loss of data due to type conversion    MarketMQL4.mqh  79      8
possible loss of data due to type conversion    MarketMQL4.mqh  89      8
possible loss of data due to type conversion    MarketMQL4.mqh  91      8
possible loss of data due to type conversion    MarketMQL4.mqh  103     8
declaration of 'line' hides member declaration at line 14       fmtprnt2.mqh    21      39
   see previous declaration of 'line'   fmtprnt2.mqh    14      12
possible loss of data due to type conversion    IndicatN.mqh    945     45
possible loss of data due to type conversion    IndicatN.mqh    947     51
possible loss of data due to type conversion    IndicatN.mqh    947     73
possible loss of data due to type conversion    IndicatN.mqh    947     95
possible loss of data due to type conversion    IndicatN.mqh    947     117
possible loss of data due to type conversion    IndicatN.mqh    947     139
possible loss of data due to type conversion    IndicatN.mqh    947     161
possible loss of data due to type conversion    IndicatN.mqh    951     45
possible loss of data due to type conversion    IndicatN.mqh    953     47
possible loss of data due to type conversion    IndicatN.mqh    953     69
possible loss of data due to type conversion    IndicatN.mqh    953     113
possible loss of data due to type conversion    IndicatN.mqh    955     52
possible loss of data due to type conversion    IndicatN.mqh    957     52
possible loss of data due to type conversion    IndicatN.mqh    961     45
possible loss of data due to type conversion    IndicatN.mqh    961     67
possible loss of data due to type conversion    IndicatN.mqh    963     50
possible loss of data due to type conversion    IndicatN.mqh    965     51
possible loss of data due to type conversion    IndicatN.mqh    965     73
possible loss of data due to type conversion    IndicatN.mqh    967     47
possible loss of data due to type conversion    IndicatN.mqh    971     47
possible loss of data due to type conversion    IndicatN.mqh    971     69
possible loss of data due to type conversion    IndicatN.mqh    971     91
possible loss of data due to type conversion    IndicatN.mqh    971     113
possible loss of data due to type conversion    IndicatN.mqh    971     135
possible loss of data due to type conversion    IndicatN.mqh    971     157
possible loss of data due to type conversion    IndicatN.mqh    973     50
possible loss of data due to type conversion    IndicatN.mqh    973     72
possible loss of data due to type conversion    IndicatN.mqh    973     94
possible loss of data due to type conversion    IndicatN.mqh    975     50
possible loss of data due to type conversion    IndicatN.mqh    975     72
possible loss of data due to type conversion    IndicatN.mqh    977     45
possible loss of data due to type conversion    IndicatN.mqh    979     44
possible loss of data due to type conversion    IndicatN.mqh    979     66
possible loss of data due to type conversion    IndicatN.mqh    981     46
possible loss of data due to type conversion    IndicatN.mqh    981     68
possible loss of data due to type conversion    IndicatN.mqh    981     90
possible loss of data due to type conversion    IndicatN.mqh    981     112
possible loss of data due to type conversion    IndicatN.mqh    985     46
possible loss of data due to type conversion    IndicatN.mqh    985     68
possible loss of data due to type conversion    IndicatN.mqh    985     90
possible loss of data due to type conversion    IndicatN.mqh    985     112
possible loss of data due to type conversion    IndicatN.mqh    987     45
possible loss of data due to type conversion    IndicatN.mqh    987     67
possible loss of data due to type conversion    IndicatN.mqh    989     45
possible loss of data due to type conversion    IndicatN.mqh    994     48
possible loss of data due to type conversion    IndicatN.mqh    994     70
possible loss of data due to type conversion    IndicatN.mqh    996     52
possible loss of data due to type conversion    IndicatN.mqh    996     74
possible loss of data due to type conversion    IndicatN.mqh    996     96
possible loss of data due to type conversion    IndicatN.mqh    998     45
possible loss of data due to type conversion    IndicatN.mqh    1011    42
possible loss of data due to type conversion    IndicatN.mqh    1419    54
possible loss of data due to type conversion    IndicatN.mqh    1538    15
declaration of 'e' hides global declaration in file 'indstats.mq5' at line 50   IndicatN.mqh    1582    160
   see previous declaration of 'e'      indstats.mq5    50      8
possible loss of data due to type conversion    IndicatN.mqh    1635    75
possible loss of data due to type conversion    IndicatN.mqh    1849    49
possible loss of data due to type conversion    Expert0fix.mqh  254     18
possible loss of data due to type conversion    IndicatN.mqh    707     9
possible loss of data due to type conversion    IndicatN.mqh    707     9
possible loss of data due to type conversion    IndicatN.mqh    707     9
possible loss of data due to type conversion    IndicatN.mqh    707     9
implicit conversion from 'number' to 'string'   fmtprnt2.mqh    62      44
implicit conversion from 'number' to 'string'   fmtprnt2.mqh    77      17
implicit conversion from 'number' to 'string'   fmtprnt2.mqh    93      17
code generation error           1       1
1 error(s), 79 warning(s)               2       80
Stanislav Korotky
Stanislav Korotky | 7 апр. 2018 в 19:28

По поводу code generation error - как всегда - в СД.

Предупреждения можно убрать, специально я их не плодил, просто куча унаследованного кода, но сейчас я от MQL отошел - желающие могу оптимизировать, исходник есть.

Stanislav Korotky
Stanislav Korotky | 17 дек. 2018 в 14:14
Выкладываю подправленные коды (без предупреждений). На старых билдах MT5 без поддержки серийных функций а-ля MT4, без iBars и iBarShift - их реализации включаются путем макроса MT5_BUILD_IS_LESS_THAN_1860.
Stanislav Korotky
Stanislav Korotky | 12 нояб. 2020 в 15:28
Правки кода под текущие версии MT5/MT4.
Углы в трейдинге и необходимость их изучения Углы в трейдинге и необходимость их изучения
Статья посвящена анализу трейдинга с помощью измерения углов в терминале MetaTrader 4. В ней изложен как общий план использования углов для анализа движения тренда, так и нестандартные подходы к практическому применению анализа углов в трейдинге. Описаны сделанные выводы, полезные для торговли.
Сколько длится тренд? Сколько длится тренд?
В статье выбираются несколько способов идентификации тренда с целью определения его длительности по отношению к флэтовому состоянию рынка. В теории считается, что соотношение тренда к флэту составляет 30% на 70%. Это нам предстоит и проверить.
Создание пользовательских индикаторов с использованием класса CCanvas Создание пользовательских индикаторов с использованием класса CCanvas
В статье рассмотрен пример создания рисованных пользовательских индикаторов с помощью графических примитивов класса CCanvas.
Торговая система ДиНаполи Торговая система ДиНаполи
В статье подробно рассматривается торговая система с использованием уровней Фибоначчи, которую разработал и описал Джо ДиНаполи. Разъясняются основные понятия и суть системы, дается иллюстрация на примере несложного индикатора.