Торговая стратегия c прибыльностью 80%

Рейтинг надежности брокеров бинарных опционов за 2020 год:
  • Бинариум
    Бинариум

    1 место! Лидер среди всех брокеров бинарных опционов!
    Бесплатное обучение и демо счет — идеальный вариант для новичков и малоопытных трейдеров!
    Дают существенные бонусы за регистрацию:

Рабочая стратегия бинарных опционов – до 85% прибыльных сделок

Практике мы уделяем особое внимание, торговым системам посвящен отдельный раздел, рекомендуем ознакомиться с ним. Внимание уделяется всем типам ТС: индикаторным, основанным на графическом анализе, свечных паттернах, объединяет их одно – с ними можно зарабатывать. Сегодня рассмотрим 3 торговые системы, которые при грамотном применении обеспечивают до 80-85% прибыльных сигналов.

Универсальная торговая стратегия

Универсальной мы ее называем потому, что она:

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

В этой рабочей стратегии для бинарных опционов используются:

  • трендовые линии;
  • разворотные свечные паттерны;
  • MACD со стандартными параметрами для поиска дивергенций.

В нее можно включить и другие аналитические инструменты, например, уровни Фибоначчи, Стохастик и т. д.

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

Если все условия выполняются, то отскок от поддержки/сопротивления происходит быстро. Поэтому срок экспирации выбирается равным 1-2 свечам. Помните – чем выше таймфрейм, тем более надежным считается сигнал.

На скриншоте выше длинная тень сверху у свечи, коснувшейся уровня сопротивления, дает основания для покупки опциона Put. На MACD дивергенции нет, так что лот следовало бы использовать стандартный.

Следующий пример показывает схожую ситуацию, но в ней ставки делались на рост (т.е. покупался опцион Call). Основания для заключения сделки – касание ценой трендовой линии, и длинная тень снизу у сигнальной свечи.

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

Индикаторная трендовая стратегия

Следующая торговая система подойдет только тем, у кого есть немало свободного времени. Работа будет вестись на 5-минутных графиках с фиксированным сроком истечения в 3 свечи. Для торговли понадобятся 2 пользовательских индикатора:

  • Delta_Trend – отображается «в подвале» в виде гистограммы. Зеленый цвет указывает на силу быков, красный – на падающий рынок;
  • BBTrigger – внешне очень сильно напоминает Стохастик, осциллятор используется как фильтр. На него нужно добавить уровень 0, параметры – 2, 10, 5, 3.

Для ставки на падение нужно, чтобы гистограммы DeltaTrend была красного цвета, а на BBTrigger появилось уверенное пересечение нулевой линии. Для ставок на рост правила обратные.

Если одна из линий индикатора BBTrigger почти сливается с нулевым уровнем, пересечением это не считается и опцион не покупается.

Эта ТС подходит только для БО. Часто после возникновения сигнала цена проходит к моменту истечения срока экспирации в нужном направлении только несколько пунктов. Для БО этого достаточно, для форекс – нет, больше потеряете на спреде.

Метод Сидуса

Торговая система полностью основана на скользящих средних. Используются:

  • линейно-взвешенные мувинги с периодами 5 и 8;
  • экспоненциальные скользящие средние с периодами 18 и 28.

Направление сделки определяется по направлению пересечения быстрыми WМА медленных EMA. Если пересечение происходит по направлению снизу-вверх, то покупается опцион Call, сверху-вниз – делается ставка на падение валютной пары. Срок экспирации рекомендуем выбирать в пределах 2-3 свечей.

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

За период с 9 по 13 октября этого года из 12 сделок на 15-минутном графике прибыльными стали 10. То есть доля успешных сигналов равна примерно 85%.

Если будете по ней работать, то выбирайте валютные пары с максимальной волатильностью. ТС полностью основана на мувингах, а они плохо работают если график движется в узком диапазоне. Оптимальные результаты удается достичь на GBP/JPY, EUR/JPY.

Конечно, такой результат как в примере удается получить не всегда. Но если исключить из работы флетовые участки, то доли успешных сделок в районе 80-85% достичь вполне реально.

Заключение

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

Любую из перечисленных ТС можно изменить, введя дополнительные фильтры. Но с этим нужно быть осторожнее, стратегию можно «сломать» неудачной оптимизацией.

Моя прибыльная торговая стратегия!

Привет, уважаемые посетители блога! Уже кончается февраль, зима подходит к концу! Не люблю холод, поэтому с нетерпением жду прихода весны! Меня не пугает ни слякоть, ни грязь, но радует тёплый ветер и чувство приближающегося пробуждения природы! 🙂 Так, это я замечтался немного, надо о другом рассказать. Моя личная прибыльная торговая стратегия, все правила торговли, не скрою ни одной мелочи! Просто следуйте моей системе, и вы будете зарабатывать! Читайте далее.

А вообще, зачем я рассказываю прибыльную торговую стратегию, ведь это святое для каждого трейдера?! Есть для этого несколько причин. Во-первых, я не считаю это чем-то сверх секретным, ценность прибыльных стратегий раздута на форумах и интернете в целом. Многие считают, что если вашу стратегию узнают другие, то она перестанет работать. Возможно, это имеет смысл для крупных игроков, но даже если моей стратегии начнут следовать все трейдеры России, то это нисколько не изменит расстановку сил на рынке, так как объём средств не позволяет. Во-вторых, у меня есть несколько стратегий, которым следую, и публикация одной из них никак на мне не отразится, уверен, что могу создать хоть 10 прибыльных торговых стратегий (так и сделаю, поделюсь с вами на этом блоге). В-третьих, я ей перестал следовать, так как заменил советником, экономлю время, рассказывал об этом здесь:

Как появилась эта торговая стратегия?

В самом начале своего трейдерского пути глубоко изучал волновой анализ, рассказывал в статье: . Эта система создана именно благодаря волновому принципу, помню, придумал её после прочтения книги Б. Вильямса «Торговый хаос».

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

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

3-ю волну не получалось предугадать, это и не нужно, я торговал уже после неё, 3-я волна это заметный импульс, который не спутаешь с другим, это позволяло однозначно понять в какой стадии я нахожусь. После этого я ждал 4-ю и 5-ю волны, затем открывался против тренда и забирал коррекционное движение. Я заметил, что эффективность данной стратегии повышается в сочетании с сильными уровнями поддержки/сопротивления. Вот примеры сделки и разметки:

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

Как следовать прибыльной торговой стратегии?

Расскажу порядок действий, которому следовал около 2-х лет.

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

На большинстве из них ничего нужного не произойдёт, где-то нет поддержки/сопротивления, где-то картина становится непонятной и др. Но если после 3 волны появляется 4 коррекционная, а за ней 5 по тренду, то это случай, который нужен! Значит, будет сделка. За развитием ситуации следил неотрывно, важно не пропустить момент, часто бывает резкий отскок цены от уровня поддержки/сопротивления. В 5 волне необходимо дождаться завершения, то есть небольшого флэта, которым заканчиваются волны. В этом флэте я открывал сделку. Профит ордер выставлял на вершину 3 волны (часто коррекция доходит именно до неё и продолжается движение по тренду), а стоп выставлял на величину профита, но в другом направлении. Ордера важно выставить сразу, можно не успеть ручками.

Риск в сделке не превышал 2,5%. Как правильно рассчитать риск в сделке, зная цену открытия и уровень стопа рассказывал в статье: . Так как в этой стратегии заранее узнать точный уровень стопа невозможно, то рассчитывал примерно, чтобы излишне не рисковать.

Вот пример, где показан расчёт необходимых уровней:

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

Список правил для следования прибыльной торговой стратегии против тренда.

    1. Уровень поддержки/сопротивления должен быть значительным, не заключать сделки на слабых уровнях.
    2. MACD в третьей волне должен быть больше/меньше соседних максимумов/минимумов.
    3. 5 волна, пересекающая линию поддержки/сопротивления должна быть импульсом (проверить на более мелких таймфреймах), заканчиваться 5 волной в 5 волне или флэтом.
    4. Между 3-й и 5-й волнами должна быть дивергенция на разных .
    5. Расстояние 5-й волны должно быть не менее 0,618 расстояния 3-й волны.
    6. Стоп должен быть равен расстоянию профита, отложенного в другом направлении от цены открытия сделки.
    7. Профит размещать на вершине 3-й волны.
    8. 4-я волна не должна доходить до середины 3-й волны.
    9. Если длина 1-й волны примерно равна длине 3-й, то 5-я волна должна быть растянутой, больше 1-й и 3-й.
    10. 4-я волна должна быть хорошо различимой и не иметь альтернатив.
    11. В случае продолжительного бокового движения после входа, нужно выходить по рынку. Скорее всего коррекция не достигнет вершины 3-й волны.
    12. При сделках с акциями перед открытием позиции, узнать выход важных новостей в ближайшее время. Не открывать сделку, если таковые ожидаются, возможны большие разрывы.

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

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

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

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

P.S. А это вторая часть фильма о начинающих трейдерах, первую публиковал ранее.

Автор: Иван Мочалов.

Критика, благодарность и вопросы в комментариях приветствуются!:))

Торговая стратегия ’80-20′

Введение

’80-20′ — название одной из торговых стратегий (ТС), описанных в книге Street Smarts: High Probability Short-Term Trading Strategies Линды Рашке и Лоуренса Коннорса. Как и пару стратегий, рассмотренных в моей предыдущей статье, авторы отнесли ее к фазе тестирования ценой границ диапазона. Она тоже ориентирована на извлечение прибыли из ложного пробоя или отката от границ. Однако на этот раз для выявления сигнала анализируется движение цены на существенно более коротком отрезке истории — только за предыдущий день. Время жизни полученного сигнала тоже относительно невелико — система предназначена для внутридневной торговли.

Первая из целей этой статьи — описать создание сигнального модуля на языке MQL5, реализующего правила торговой стратегии ’80-20′. Затем мы подключим этот модуль к базовому торговому советнику, созданному в предыдущей статье цикла, немного отредактировав его. Кроме того, этот же модуль без изменений мы используем при создании индикатора для ручной торговли.

Напомню, что создаваемый в этой серии статей код в первую очередь ориентирован на категорию программистов, которую можно определить как «слегка продвинутые новички». Поэтому, кроме основной задачи, программный код призван помочь перейти от процедурного программирования к объектно-ориентированному. В нём не будут создаваться классы, но будут в полном объёме использованы более простые для освоения аналоги — структуры.

Ещё одна цель статьи — создать инструменты, которые позволят проверить, насколько актуальна эта стратегия сегодня, ведь при её создании Рашке и Коннорс исходили из поведения рынка в конце прошлого века. Несколько тестов созданного советника на современных исторических данных будут приведены в конце статьи.

Торговая система ’80-20′

В качестве теоретического обоснования авторы ссылаются на книгу The Taylor Trading Technique Джорджа Тейлора, а также на работы по компьютерному анализу фьючерсных рынков Стива Мура и практический опыт трейдера Дерека Джипсона. Положенную в основу ТС гипотезу кратко можно сформулировать так: если цены открытия и закрытия вчерашнего дня разнесены в противоположные области дневного диапазона, то сегодня с большой вероятностью можно ожидать разворота в сторону открытия вчерашнего дня. При этом важно, чтобы вчерашние цены открытия и закрытия находились достаточно близко к границам диапазона, а разворот начался именно сегодня, а не до закрытия вчерашнего дневного бара. Дополненный собственными поправками авторов набор правил ТС ’80-20′ для входа на покупку можно сформулировать так:

1. Убедитесь, что вчера рынок открылся в верхних 20% дневного диапазона, а закрылся в нижних 20% диапазона

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

3. Разместите отложенный ордер на покупку на нижней границе вчерашнего диапазона

4. Сразу после срабатывания отложенного ордера установите его начальный StopLoss у минимума этого дня

5. Используйте Трейлинг Стоп для защиты полученной прибыли

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

Ещё одна важная деталь появляется в книге при обсуждении иллюстраций к ТС на графиках из истории — авторы обращают внимание на размер закрывшегося дневного бара. По словам Линды Рашке, он должен быть достаточно большим — больше среднего размера дневных баров. Правда, она не уточняет, сколько дней истории следует принимать во внимание при расчёте среднего дневного диапазона.

И не забудем, что ТС предназначена исключительно для внутридневной торговли — показанные в книге примеры используют графики 15-минутного таймфрейма.

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

Результатом анализа этого паттерна должна стать установка отложенного ордера на покупку. Соответствующие торговые уровни лучше видны на минутном таймфрейме:

Аналогичный паттерн с противоположным направлением торговли на пятиминутном таймфрейме:

Его торговые уровни (минутный таймфрейм):

Сигнальный модуль

Чтобы показать пример добавления в авторскую ТС дополнительных опций, добавим расчёт уровня Take Profit. В оригинальной версии этого уровня нет, для закрытия позиции используется только трейлинг уровня Stop Loss. Take Profit сделаем зависимым от заданной пользователем минимальной величины пробоя ( TS_8020_Extremum_Break ) — будем умножать его на пользовательский коэффициент TS_8020_Take_Profit_Ratio .

От основной функции сигнального модуля fe_Get_Entry_Signal нам будут нужны: текущий статус сигнала, рассчитанные уровни входа и выходов (Stop Loss и Take Profit), а также границы диапазона вчерашнего дня. Все уровни мы получим по переданным функции ссылкам на переменные, а возвращённый статус сигнала будет использовать список вариантов из предыдущей статьи:

enum ENUM_ENTRY_SIGNAL < // Список сигналов на вход
ENTRY_BUY, // сигнал на покупку
ENTRY_SELL, // сигнал на продажу
ENTRY_NONE, // нет сигнала
ENTRY_UNKNOWN // статус не определён
>;

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( // Анализ двухсвечного паттерна D1
datetime t_Time, // текущее время
double & d_Entry_Level, // уровень входа (ссылка на переменную)
double & d_SL, // уровень StopLoss (ссылка на переменную)
double & d_TP, // уровень TakeProfit (ссылка на переменную)
double & d_Range_High, // максимум диапазона 1-го бара паттерна (ссылка на переменную)
double & d_Range_Low // минимум диапазона 1-го бара паттерна (ссылка на переменную)
) <>

Для выявления сигнала надо проанализировать два последних бара дневного таймфрейма. Начнём с первого из них — если он не соответствует критериям ТС, второй бар проверять станет незачем. Критериев два:

1. Размах бара (разность цен High и Low) должен быть больше, чем средний за последние XX дней (задаётся пользовательской настройкой TS_8020_D1_Average_Period )

2. Уровни открытия и закрытия бара должны относиться к противоположным 20% диапазона бара

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

// пользовательские настройки
input uint TS_8020_D1_Average_Period = 20 ; // 80-20: Кол-во дней для вычисления среднего дневного диапазона
input uint TS_8020_Extremum_Break = 50 ; // 80-20: Мин. пробой вчерашнего экстремума (в пунктах)

static ENUM_ENTRY_SIGNAL se_Possible_Signal = ENTRY_UNKNOWN; // направление сигнала по 1-му бару паттерна
static double
// переменные для хранения рассчитанных уровней между тиками
sd_Entry_Level = 0 ,
sd_SL = 0 , sd_TP = 0 ,
sd_Range_High = 0 , sd_Range_Low = 0
;

// проверка 1го бара паттерна на D1:
if (se_Possible_Signal == ENTRY_UNKNOWN) < // сегодня ещё не проводилась
st_Last_D1_Bar = t_Curr_D1_Bar; // в этот день 1-й бар больше не изменится

// средний дневной диапазон
double d_Average_Bar_Range = fd_Average_Bar_Range(TS_8020_D1_Average_Period, PERIOD_D1 , t_Time);

if (ma_Rates[ 0 ].high — ma_Rates[ 0 ].low // 1й бар недостаточно велик
se_Possible_Signal = ENTRY_NONE; // значит сегодня сигнала не будет
return (se_Possible_Signal);
>

double d_20_Percents = 0.2 * (ma_Rates[ 0 ].high — ma_Rates[ 0 ].low); // 20% вчерашнего диапазона
if ((
// медвежий бар:
ma_Rates[ 0 ].open > ma_Rates[ 0 ].high — d_20_Percents // бар открылся в верхних 20%
&&
ma_Rates[ 0 ].close 0 ].low + d_20_Percents // а закрылся в нижних 20%
) || (
// бычий:
ma_Rates[ 0 ].close > ma_Rates[ 0 ].high — d_20_Percents // бар закрылся в верхних 20%
&&
ma_Rates[ 0 ].open 0 ].low + d_20_Percents // а открылся в нижних 20%
)) <
// 1-й бар соответствует условиям
// определение направления торговли на сегодня по 1-му бару паттерна:
se_Possible_Signal = ma_Rates[ 0 ].open > ma_Rates[ 0 ].close ? ENTRY_BUY : ENTRY_SELL;
// уровень входа в рынок:
sd_Entry_Level = d_Entry_Level = se_Possible_Signal == ENTRY_BUY ? ma_Rates[ 0 ].low : ma_Rates[ 0 ].high;
// границы диапазона 1-го бара паттерна:
sd_Range_High = d_Range_High = ma_Rates[ 0 ].high;
sd_Range_Low = d_Range_Low = ma_Rates[ 0 ].low;
> else <
// уровни открытия/закрытия 1-го бара не соответствуют условиям
se_Possible_Signal = ENTRY_NONE; // значит, сегодня сигнала не будет
return (se_Possible_Signal);
>
>

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

double fd_Average_Bar_Range( // Расчёт среднего размера бара
int i_Bars_Limit, // сколько баров принимать во внимание
ENUM_TIMEFRAMES e_TF = PERIOD_CURRENT , // таймфрейм баров
datetime t_Time = WRONG_VALUE // с какого времени начинать расчёт
) <
double d_Average_Range = 0 ; // переменная для суммирования значений
if (i_Bars_Limit 1 ) return (d_Average_Range);

MqlRates ma_Rates[]; // массив для информации о барах

// получение информации о барах с заданного уч-ка истории:
if (t_Time == WRONG_VALUE ) t_Time = TimeCurrent ();
int i_Price_Bars = CopyRates ( _Symbol , e_TF, t_Time, i_Bars_Limit, ma_Rates);

if (i_Price_Bars == WRONG_VALUE ) < // обработка ошибки функции CopyRates
if (Log_Level > LOG_LEVEL_NONE) PrintFormat ( «%s: CopyRates: ошибка #%u» , __FUNCTION__ , _LastError );
return (d_Average_Range);
>

if (i_Price_Bars // функция CopyRates извлекла данные не в полном объёме
if (Log_Level > LOG_LEVEL_NONE) PrintFormat ( «%s: CopyRates: скопировано %u баров из %u» , __FUNCTION__ , i_Price_Bars, i_Bars_Limit);
>

// сумма диапазонов:
int i_Bar = i_Price_Bars;
while (i_Bar— > 0 )
d_Average_Range += ma_Rates[i_Bar].high — ma_Rates[i_Bar].low;

// среднее значение:
return (d_Average_Range / double (i_Price_Bars));
>

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

// проверка 2го (текущего) бара паттерна на D1:
if (se_Possible_Signal == ENTRY_BUY) <
sd_SL = d_SL = ma_Rates[ 1 ].low; // StopLoss — на максимум сегодняшней цены
if (TS_8020_Take_Profit_Ratio > 0 ) sd_TP = d_TP = d_Entry_Level + _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
return (
// достаточно ли выражен пробой вниз?
ma_Rates[ 1 ].close 0 ].low — _Point * TS_8020_Extremum_Break ?
ENTRY_BUY : ENTRY_NONE
);
>

if (se_Possible_Signal == ENTRY_SELL) <
sd_SL = d_SL = ma_Rates[ 1 ].high; // StopLoss — на минимум сегодняшней цены
if (TS_8020_Take_Profit_Ratio > 0 ) sd_TP = d_TP = d_Entry_Level — _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; // TakeProfit
return (
// достаточно ли выражен пробой вверх?
ma_Rates[ 1 ].close > ma_Rates[ 0 ].high + _Point * TS_8020_Extremum_Break ?
ENTRY_SELL : ENTRY_NONE
);
>

Описанные выше две функции ( fe_Get_Entry_Signal и fd_Average_Bar_Range ) и пользовательские настройки, относящиеся к получению сигнала, сохраним в файле библиотеки mqh. Полный листинг есть в приложении к этой статье. Назовём файл Signal_80-20.mqh и поместим его в соответствующую папку (MQL5\Include\Expert\Signal) каталога данных терминала.

Индикатор для ручной торговли

Индикатор, как и советник, будет использовать описанный выше сигнальный модуль. Он (индикатор) должен оповестить трейдера о получении сигнала на установку отложенного ордера и сообщить расчётные уровни — уровень установки ордера, уровни Take Profit и Stop Loss. Методы оповещения пользователь может выбрать сам — это может быть стандартное всплывающее окно, сообщение на электронную почту или уведомление на мобильное устройство. Выбрать можно всё сразу или любую удобную комбинацию перечисленных опций.

Другое назначение индикатора — разметка на истории торговли по ТС ’80-20′. Он будет подсвечивать дневные бары, соответствующие критериям системы, и рисовать расчётные торговые уровни. По линиям уровней будет можно оценивать, как развивалась ситуация во времени. Для большей наглядности сделаем так: при касании ценой сигнальной линии она закончится, начнётся линия отложенного ордера, а при срабатывании отложенного ордера закончится его линия и начнутся линии Take Profit и Stop Loss. Линии эти прервутся, когда цена коснется одной из них (ордер закроется). При такой разметке будет проще оценить эффективность правил торговой системы и понять, что именно можно усовершенствовать.

Начнём с объявления буферов и параметров их отображения. Во-первых, нам нужно объявить два буфера с заливкой вертикальной области (DRAW_FILLING). Один будет подсвечивать полный диапазон дневного бара предыдущего дня, другой — только внутреннюю область, чтобы отделить её от задействованных в ТС верхних и нижних 20% диапазона. Затем заявим два буфера для разноцветных сигнальной линии и линии отложенного ордера (DRAW_COLOR_LINE). Их цвет будет зависеть от направления торговли. И ещё две линии (Take Proft и Stop Loss) цвета менять не будут (DRAW_LINE) — они будут использовать те же стандартные цвета, которые им отведены в терминале. Все выбранные типы отображения, кроме простой линии, требуют по два буфера, поэтому код будет выглядеть так:

#property indicator_chart_window
#property indicator_buffers 10
#property indicator_plots 6

#property indicator_label1 «1й бар паттерна»
#property indicator_type1 DRAW_FILLING
#property indicator_color1 clrDeepPink , clrDodgerBlue
#property indicator_width1 1

#property indicator_label2 «1й бар паттерна»
#property indicator_type2 DRAW_FILLING
#property indicator_color2 clrDeepPink , clrDodgerBlue
#property indicator_width2 1

#property indicator_label3 «Сигнальный уровень»
#property indicator_type3 DRAW_COLOR_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_color3 clrDeepPink , clrDodgerBlue
#property indicator_width3 2

#property indicator_label4 «Уровень входа»
#property indicator_type4 DRAW_COLOR_LINE
#property indicator_style4 STYLE_DASHDOT
#property indicator_color4 clrDeepPink , clrDodgerBlue
#property indicator_width4 2

#property indicator_label5 «Stop Loss»
#property indicator_type5 DRAW_LINE
#property indicator_style5 STYLE_DASHDOTDOT
#property indicator_color5 clrCrimson
#property indicator_width5 1

#property indicator_label6 «Take Profit»
#property indicator_type6 DRAW_LINE
#property indicator_style6 STYLE_DASHDOTDOT
#property indicator_color6 clrLime
#property indicator_width6 1

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

#include 20 .mqh> // сигнальный модуль ТС ’80-20′

input bool Show_Outer = true ; // 1-й бар паттерна: Показывать полный диапазон?
input bool Show_Inner = true ; // 1-й бар паттерна: Показывать внутреннюю область?
input bool Alert_Popup = true ; // Алерт: Показывать всплывающее окно?
input bool Alert_Email = false ; // Алерт: Отправлять eMail?
input string Alert_Email_Subj = «» ; // Алерт: Тема eMail-сообщения
input bool Alert_Push = true ; // Алерт: Отправлять push-уведомление?

input uint Bars_Limit = 2000 ; // Глубина разметки истории (в барах текущего ТФ)

ENUM_LOG_LEVEL Log_Level = LOG_LEVEL_NONE; // Режим протоколирования
double
buff_1st_Bar_Outer[], buff_1st_Bar_Outer_Zero[], // буферы для отрисовки полного диапазона 1-го бара паттерна
buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], // буферы для отрисовки внутренних 60% 1-го бара паттерна
buff_Signal[], buff_Signal_Color[], // буферы сигнальной линии
buff_Entry[], buff_Entry_Color[], // буферы линии отложенного ордера
buff_SL[], buff_TP[], // буферы линий StopLoss и TakeProfit
gd_Extremum_Break = 0 // TS_8020_Extremum_Break в ценах инструмента
;
int
gi_D1_Average_Period = 1 , // корректное значение для TS_8020_D1_Average_Period
gi_Min_Bars = WRONG_VALUE // минимальное обязательное кол-во баров для пересчёта
;

int OnInit () <
// проверка введённого параметра TS_8020_D1_Average_Period:
gi_D1_Average_Period = int ( fmin ( 1 , TS_8020_D1_Average_Period));
// перевод пунктов в цены инструмента:
gd_Extremum_Break = TS_8020_Extremum_Break * _Point ;
// минимальное обязательное кол-во баров для пересчёта = кол-ву баров текущего ТФ в сутках
gi_Min_Bars = int ( 86400 / PeriodSeconds ());

// назначение буферов индикатора:

// прямоугольник полного диапазона 1-го бара
SetIndexBuffer ( 0 , buff_1st_Bar_Outer, INDICATOR_DATA );
PlotIndexSetDouble ( 0 , PLOT_EMPTY_VALUE , 0 );
SetIndexBuffer ( 1 , buff_1st_Bar_Outer_Zero, INDICATOR_DATA );

// прямоугольник внутренней области 1-го бара
SetIndexBuffer ( 2 , buff_1st_Bar_Inner, INDICATOR_DATA );
PlotIndexSetDouble ( 1 , PLOT_EMPTY_VALUE , 0 );
SetIndexBuffer ( 3 , buff_1st_Bar_Inner_Zero, INDICATOR_DATA );

// сигнальная линия
SetIndexBuffer ( 4 , buff_Signal, INDICATOR_DATA );
PlotIndexSetDouble ( 2 , PLOT_EMPTY_VALUE , 0 );
SetIndexBuffer ( 5 , buff_Signal_Color, INDICATOR_COLOR_INDEX );

// линия установки отложенного ордера
SetIndexBuffer ( 6 , buff_Entry, INDICATOR_DATA );
PlotIndexSetDouble ( 3 , PLOT_EMPTY_VALUE , 0 );
SetIndexBuffer ( 7 , buff_Entry_Color, INDICATOR_COLOR_INDEX );

// линия SL
SetIndexBuffer ( 8 , buff_SL, INDICATOR_DATA );
PlotIndexSetDouble ( 4 , PLOT_EMPTY_VALUE , 0 );

// линия TP
SetIndexBuffer ( 9 , buff_TP, INDICATOR_DATA );
PlotIndexSetDouble ( 5 , PLOT_EMPTY_VALUE , 0 );

IndicatorSetInteger ( INDICATOR_DIGITS , _Digits );
IndicatorSetString ( INDICATOR_SHORTNAME , «ТС 80-20» );

В штатной функции OnCalculate поместим код основной программы — организуем цикл, который будет перебирать бары текущего таймфрейма в направлении из прошлого в будущее и проверять их на наличие сигнала с помощью функции из сигнального модуля. Предварительно объявим и инициализируем начальными значениями необходимые переменные. Самый старый бар цикла для первого расчёта определим с учётом заданного пользователем ограничения глубины истории ( Bars_Limit ). Для последующих же вызовов пересчитывать мы будем не один лишь последний бар, а все бары текущего дня, ведь двухбарный паттерн на самом деле принадлежит графику D1, независимо от текущего таймфрейма.

Кроме того, придётся принять меры для защиты от так называемых фантомов: если принудительно не очищать буферы индикатора при переинициализации, то при переключении таймфреймов или смене символа на экране останутся неактуальные на новом графике закрашенные области. Это действие (очистку буферов) надо привязать к первому после инициализации индикатора вызову функции OnCalculate. Но определить, что это первый вызов, с помощью одной только стандартной переменной prev_calculated не получится — она содержит ноль не только при первом вызове функции, но и «при изменении контрольной суммы». Потратим некоторое время, чтобы решить эту проблему с запасом — создадим независимую от обнуления переменной prev_calculated структуру, которая будет хранить и обрабатывать часто используемые в индикаторах полезные данные:

— флаг первого запуска функции OnCalculate;

— необнуляемый при изменении контрольной суммы счётчик обсчитанных баров;

— флаг изменения контрольной суммы;

— флаг начала нового бара;

— время начала текущего бара.

Объединяющая все эти данные структура будет объявлена на глобальном уровне и сможет собирать или предоставлять информацию в/из любых штатных или пользовательских функций. Такой программной сущности вполне подойдёт имя ‘Домовой’ (Brownie). Поместить её можно в конце кода индикатора. Там же объявим один глобальный объект-структуру типа с именем go_Brownie :

struct BROWNIE < // Домовой: структура для хранения и обработки данных на глобальном уровне
datetime t_Last_Bar_Time; // время последнего обрабатывавшегося бара
int i_Prew_Calculated; // кол-во посчитанных баров
bool b_First_Run; // флаг первого запуска
bool b_History_Updated; // флаг обновления истории
bool b_Is_New_Bar; // флаг открытия нового бара

BROWNIE() < // конструктор
// значения по умолчанию:
t_Last_Bar_Time = 0 ;
i_Prew_Calculated = WRONG_VALUE ;
b_First_Run = b_Is_New_Bar = true ;
b_History_Updated = false ;
>

void f_Reset( bool b_Reset_First_Run = true ) < // обнуление переменных
// значения по умолчанию:
t_Last_Bar_Time = 0 ;
i_Prew_Calculated = WRONG_VALUE ;
if (b_Reset_First_Run) b_First_Run = true ; // обнуление, если есть разрешение
b_Is_New_Bar = true ;
b_History_Updated = false ;
>

void f_Update( int i_New_Prew_Calculated = WRONG_VALUE ) < // обновление переменных
// флаг первого вызова штатной функции OnCalculate
if (b_First_Run && i_Prew_Calculated > 0 ) b_First_Run = false ;

Самые лучшие платформы для торговли бинарными опционами:
  • Бинариум
    Бинариум

    1 место! Лидер среди всех брокеров бинарных опционов!
    Бесплатное обучение и демо счет — идеальный вариант для новичков и малоопытных трейдеров!
    Дают существенные бонусы за регистрацию:

// новый бар?
datetime t_This_Bar_Time = TimeCurrent () — TimeCurrent () % PeriodSeconds ();
b_Is_New_Bar = t_Last_Bar_Time == t_This_Bar_Time;

// обновить время текущего бара?
if (b_Is_New_Bar) t_Last_Bar_Time = t_This_Bar_Time;

if (i_New_Prew_Calculated > — 1 ) <
// есть какие-то изменения в истории?
b_History_Updated = i_New_Prew_Calculated == 0 && i_Prew_Calculated > WRONG_VALUE ;

// prew_calculated использовать, если 1-й вызов OnCalculate
if (i_Prew_Calculated == WRONG_VALUE ) i_Prew_Calculated = i_New_Prew_Calculated;
// или если не было обновления истории
else if (i_New_Prew_Calculated > 0 ) i_Prew_Calculated = i_New_Prew_Calculated;
>
>
>;
BROWNIE go_Brownie;

Предусмотрим информирование ‘Домового’ и о событии деинициализации индикатора:

При необходимости коллекцию информации, хранимой ‘Домовым’ можно расширить, если пользовательские функции или классы будут нуждаться, например в ценах, объёмах или величине спреда текущего бара (Open, High, Low, Close, tick_volume, volume, spread). Взять готовые данные из функции OnCalculate и передать их через ‘Домового’ удобнее, чем использовать функции копирования таймсерий (CopyOpen, CopyHigh и т.д. или CopyRates) — это будет экономить ресурсы процессора и избавит от необходимости организовывать обработку ошибок этих функций языка.

Вернёмся к основной функции индикатора. Объявление переменных и подготовка массивов с использованием структуры go_Brownie будет выглядеть так:

go_Brownie.f_Update(prev_calculated); // покормить информацией домового

int
i_Period_Bar = 0 , // вспомогательный счётчик
i_Current_TF_Bar = rates_total — int (Bars_Limit) // индекс бара начала цикла текущего ТФ
;
static datetime st_Last_D1_Bar = 0 ; // время последнего из обработанной пары баров D1 (2-го бара паттерна)
static int si_1st_Bar_of_Day = 0 ; // индекс первого бара текущего дня

if (go_Brownie.b_First_Run) < // если это 1й запуск
// очистить буфера при переинициализации:
ArrayInitialize (buff_1st_Bar_Inner, 0 ); ArrayInitialize (buff_1st_Bar_Inner_Zero, 0 );
ArrayInitialize (buff_1st_Bar_Outer, 0 ); ArrayInitialize (buff_1st_Bar_Outer_Zero, 0 );
ArrayInitialize (buff_Entry, 0 ); ArrayInitialize (buff_Entry_Color, 0 );
ArrayInitialize (buff_Signal, 0 ); ArrayInitialize (buff_Signal_Color, 0 );
ArrayInitialize (buff_TP, 0 );
ArrayInitialize (buff_SL, 0 );
st_Last_D1_Bar = 0 ;
si_1st_Bar_of_Day = 0 ;
> else < // это не 1й запуск
datetime t_Time = TimeCurrent ();
// минимальная глубина пересчёта — с прошлого дня:
i_Current_TF_Bar = rates_total — Bars ( _Symbol , PERIOD_CURRENT , t_Time — t_Time % 86400 , t_Time) — 1 ;
>
ENUM_ENTRY_SIGNAL e_Signal = ENTRY_UNKNOWN; // сигнал
double
d_SL = WRONG_VALUE , // уровень SL
d_TP = WRONG_VALUE , // уровень TP
d_Entry_Level = WRONG_VALUE , // уровень входа
d_Range_High = WRONG_VALUE , d_Range_Low = WRONG_VALUE // границы диапазона 1-го бара паттерна
;
datetime
t_Curr_D1_Bar = 0 , // время текущего бара D1 (2-го бара паттерна)
t_D1_Bar_To_Fill = 0 // время бара D1, который надо закрасить (1-го бара паттерна)
;

// проконтролировать, чтобы индекс начального бара пересчёта был в допустимых рамках:
i_Current_TF_Bar = int ( fmax ( 0 , fmin (i_Current_TF_Bar, rates_total — gi_Min_Bars)));

while (++i_Current_TF_Bar IsStopped ()) < // перебор баров текущего ТФ
// здесь будет основной цикл программы
>

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

Если сигнал есть и это первый бар нового дня, то нужно организовать заливку диапазона предыдущего дневного бара. Флагом будет значение переменной t_D1_Bar_To_Fill типа datetime — если ей присвоено значение WRONG_VALUE, то на этом баре заливка не требуется. На этом же первом баре должна начинаться и сигнальная линия, но для лучшего восприятия разметки продлим её до последнего бара предыдущего дня. Так как расчёты сигнального уровня и цвета линий и закрашенных областей для бычьего и медвежьего бара различаются, сделаем два аналогичных друг другу блока:

t_Curr_D1_Bar = Time [i_Current_TF_Bar] — Time [i_Current_TF_Bar] % 86400 ; // начало дня, к которому принадлежит этот бар
if (st_Last_D1_Bar // это бар нового дня
t_D1_Bar_To_Fill = Time [i_Current_TF_Bar — 1 ] — Time [i_Current_TF_Bar — 1 ] % 86400 ;
si_1st_Bar_of_Day = i_Current_TF_Bar;
>
else t_D1_Bar_To_Fill = WRONG_VALUE ; // бар старого дня, новая заливка не требуется
st_Last_D1_Bar = t_Curr_D1_Bar; // запомнить

if (t_D1_Bar_To_Fill != WRONG_VALUE ) < // новый бар D1
// Заливка бара D1 предыдущего дня:
i_Period_Bar = i_Current_TF_Bar;
if (d_Entry_Level // медвежий бар D1
if (Show_Outer) while (—i_Period_Bar > 0 ) < // полный диапазон
if ( Time [i_Period_Bar] break ;
buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_Low;
buff_1st_Bar_Outer[i_Period_Bar] = d_Range_High;
>
if (Show_Inner) < // внутренняя область
i_Period_Bar = i_Current_TF_Bar;
while (—i_Period_Bar > 0 ) <
if ( Time [i_Period_Bar] break ;
buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
buff_1st_Bar_Inner[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
>
>
// начало сигнальной линии — с последнего бара предыдущего дня
buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1 ] = d_Range_Low — gd_Extremum_Break;
buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1 ] = 0 ;
> else < // бычий бар D1
if (Show_Outer) while (—i_Period_Bar > 0 ) < // полный диапазон
if ( Time [i_Period_Bar] break ;
buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_High;
buff_1st_Bar_Outer[i_Period_Bar] = d_Range_Low;
>
if (Show_Inner) < // внутренняя область
i_Period_Bar = i_Current_TF_Bar;
while (—i_Period_Bar > 0 ) <
if ( Time [i_Period_Bar] break ;
buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
buff_1st_Bar_Inner[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
>
>
// начало сигнальной линии — с последнего бара предыдущего дня
buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1 ] = d_Range_High + gd_Extremum_Break;
buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1 ] = 1 ;
>
> else continue ;

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

// Сигнальная линия до пересёкшего её бара:
i_Period_Bar = i_Current_TF_Bar;
if (d_Entry_Level // медвежий бар D1
while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_Signal[i_Period_Bar] = d_Range_Low — gd_Extremum_Break;
buff_Signal_Color[i_Period_Bar] = 0 ;
if (d_Range_Low — gd_Extremum_Break >= Low [i_Period_Bar]) break ;
>
> else < // бычий бар D1
while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_Signal[i_Period_Bar] = d_Range_High + gd_Extremum_Break;
buff_Signal_Color[i_Period_Bar] = 1 ;
if (d_Range_High + gd_Extremum_Break High [i_Period_Bar]) break ;
>
>

// Линия входа до пересёкшего её бара:
if (d_Entry_Level // медвежий бар D1
while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_Entry[i_Period_Bar] = d_Range_Low;
buff_Entry_Color[i_Period_Bar] = 0 ;
if (d_Range_Low High [i_Period_Bar]) <
if (buff_Entry[i_Period_Bar — 1 ] == 0 .) <
// начало и конец на одном баре, продлим на 1 бар в прошлое
buff_Entry[i_Period_Bar — 1 ] = d_Range_Low;
buff_Entry_Color[i_Period_Bar — 1 ] = 0 ;
>
break ;
>
>
> else < // бычий бар D1
while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_Entry[i_Period_Bar] = d_Range_High;
buff_Entry_Color[i_Period_Bar] = 1 ;
if (d_Range_High >= Low [i_Period_Bar]) <
if (buff_Entry[i_Period_Bar — 1 ] == 0 .) <
// начало и конец на одном баре, продлим на 1 бар в прошлое
buff_Entry[i_Period_Bar — 1 ] = d_Range_High;
buff_Entry_Color[i_Period_Bar — 1 ] = 1 ;
>
break ;
>
>
>

// Линии TP и SL до бара, пересёкшего одну из них:
if (d_Entry_Level // медвежий бар D1
// SL равен минимуму с начала дня:
d_SL = Low [ ArrayMinimum ( Low , si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];

while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_SL[i_Period_Bar] = d_SL;
buff_TP[i_Period_Bar] = d_TP;
if (d_TP High [i_Period_Bar] || d_SL >= Low [i_Period_Bar]) <
if (buff_SL[i_Period_Bar — 1 ] == 0 .) <
// начало и конец на одном баре, продлим на 1 бар в прошлое
buff_SL[i_Period_Bar — 1 ] = d_SL;
buff_TP[i_Period_Bar — 1 ] = d_TP;
>
break ;
>
>
> else < // бычий бар D1
// SL равен максимуму с начала дня:
d_SL = High [ ArrayMaximum ( High , si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];

while (++i_Period_Bar if ( Time [i_Period_Bar] > t_Curr_D1_Bar + 86399 ) break ;
buff_SL[i_Period_Bar] = d_SL;
buff_TP[i_Period_Bar] = d_TP;
if (d_SL High [i_Period_Bar] || d_TP >= Low [i_Period_Bar]) <
if (buff_SL[i_Period_Bar — 1 ] == 0 .) <
// начало и конец на одном баре, продлим на 1 бар в прошлое
buff_SL[i_Period_Bar — 1 ] = d_SL;
buff_TP[i_Period_Bar — 1 ] = d_TP;
>
break ;
>
>
>

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

void f_Do_Alert( // Функция рассылки сигналов и оповещений
string s_Message, // текст для алерта
bool b_Alert = true , // показывать всплывающее окно?
bool b_Sound = false , // проигрывать звуковой файл?
bool b_Email = false , // отправлять eMail сообщение?
bool b_Notification = false , // отправлять push-уведомление?
string s_Email_Subject = «» , // тема для eMail сообщения
string s_Sound = «alert.wav» // звуковой файл
) <
static string ss_Prev_Message = «была тишина» ; // текст предыдущего алерта
static datetime st_Prev_Time; // время бара предыдущего алерта
datetime t_This_Bar_Time = TimeCurrent () — PeriodSeconds () % PeriodSeconds (); // время текушего бара

if (ss_Prev_Message != s_Message || st_Prev_Time != t_This_Bar_Time) <
// алерт другой и/или 1-й на этом баре

// запомнить:
ss_Prev_Message = s_Message;
st_Prev_Time = t_This_Bar_Time;

// сформировать строку сообщения:
s_Message = StringFormat ( «%s | %s | %s | %s» ,
TimeToString ( TimeLocal (), TIME_SECONDS ), // локальное время
_Symbol , // символ
StringSubstr ( EnumToString ( ENUM_TIMEFRAMES ( _Period )), 7 ), // ТФ
s_Message // сообщение
);

// подать сигнал оповещения:
if (b_Alert) Alert (s_Message);
if (b_Email) SendMail (s_Email_Subject + » » + _Symbol , s_Message);
if (b_Notification) SendNotification (s_Message);
if (b_Sound) PlaySound (s_Sound);
>
>

Код проверки необходимости вызова этой функции и формирования текста сообщения для неё, размещённый в теле программы, перед завершением обработчика события OnCalculate:

// алерт
i_Period_Bar = rates_total — 1 ; // текущий бар

if (Alert_Popup + Alert_Email + Alert_Push == 0 ) return (rates_total); // всё отключено
if (buff_Signal[i_Period_Bar] == 0 ) return (rates_total); // уже или ещё нечего ловить
if (
buff_Signal[i_Period_Bar] > High [i_Period_Bar]
||
buff_Signal[i_Period_Bar] Low [i_Period_Bar]
) return (rates_total); // нет касания сигнальной линии

// текст сообщения:
string s_Message = StringFormat ( «TS 80-20: нужен %s @ %s, TP: %s, SL: %s» ,
buff_Signal_Color[i_Period_Bar] > 0 ? «BuyStop» : «SellStop» ,
DoubleToString (d_Entry_Level, _Digits ),
DoubleToString (d_TP, _Digits ),
DoubleToString (d_SL, _Digits )
);
// оповещение:
f_Do_Alert(s_Message, Alert_Popup, false , Alert_Email, Alert_Push, Alert_Email_Subj);

return (rates_total); // завершение работы OnCalculate

Весь исходный код индикатора в сборе есть в прикреплённых файлах, его имя — TS_80-20.mq5. Что касается его использования — лучше всего видна разметка торговли по этой системе на минутных графиках.

Есть одно существенное примечание к этой разметке — индикатор использует данные баров, а не последовательности тиков внутри баров. Т.е. если на одном баре цена пересекала несколько линий разметки (например, линии Take Profit и Stop Loss), не всегда можно определить, которая из них была пересечена первой. Другая погрешность связана с тем, что бары начала и окончания линии не могут совпадать, иначе линии из буфера типа DRAW_LINE и DRAW_COLOR_LINE будут просто не видны пользователю. Эти особенности делают разметку не стопроцентно точной, но всё же весьма наглядной.

Советник для тестирования ТС ’80-20′

Базовый советник для тестирования стратегий из книги Street Smarts: High Probability Short-Term Trading Strategies подробно описан в первой статье. Внесём в него два существенных изменения. Первое связано с тем, что сигнальный модуль будет использоваться и в индикаторе тоже, а значит, рационально будет вынести в него расчёт торговых уровней. Это мы уже сделали выше — функция fe_Get_Entry_Signal, помимо статуса сигнала, возвращает уровни установки ордера, Stop Loss и Take Profit. Поэтому уберём из предыдущей версии советника соответствующую часть кода, добавим переменные для приёма уровней из функции и отредактируем сам вызов этой функции. Я не буду приводить здесь листинги старого и нового блоков кода, вы можете посмотреть их в приложенном файле (строки со 128 по 141).

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

Дадим пользователю возможность управлять опцией ‘один вход в день’, а также задавать идентификатор каждой запущенной версии робота — он нужен для использования глобальных переменных терминального уровня:

В блок определения глобальных переменных программы добавим объявление нужных для реализации опции ‘один вход в день’ переменных. В функции OnInit проинициализируем их:

string
gs_Prefix // идентификатор имён (супер)глобальных переменных
;
bool
gb_Position_Today = false ,
gb_Pending_Today = false
;

// Создание префикса имён (супер)глобальных переменных:
gs_Prefix = StringFormat ( «SSB %s %u %s» , _Symbol , Magic_Number, MQLInfoInteger ( MQL_TESTER ) ? «t » : «» );

// Работал ли робот сегодня с рыночными или отложенными ордерами?
gb_Position_Today = int ( GlobalVariableGet (gs_Prefix + «Last_Position_Date» )) == TimeCurrent () — TimeCurrent () % 86400 ;
gb_Pending_Today = int ( GlobalVariableGet (gs_Prefix + «Last_Pending_Date» )) == TimeCurrent () — TimeCurrent () % 86400 ;

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

Второй блок поместим после кода, который определяет свежеоткрытую позицию:

if ( PositionSelect ( _Symbol )) < // есть открытая позиция
if ( PositionGetDouble ( POSITION_SL ) == 0 .) < // новая позиция

if (!gb_Position_Today) < // это 1я позиция сегодня
// обновить флаг:
GlobalVariableSet ( // в глобальных переменных терминала
gs_Prefix + «Last_Position_Date» ,
TimeCurrent () — TimeCurrent () % 86400
);
gb_Position_Today = true ; // в глобальных переменных программы
>
.

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

Тестирование стратегии на исторических данных

Авторы торговой системы в качестве подтверждения её жизнеспособности приводят паттерны на графиках конца прошлого века, а нам нужно проверить её актуальность в условиях современного рынка. Для тестирования я взял наиболее популярную на рынке форекс пару EURUSD, а также более волатильную USDJPY и один из металлов — XAUUSD. Указанные Рашке и Коннорсом отступы я увеличил в 10 раз, так как в те времена использовались четырёхзначные котировки, а я тестировал советник на пятизначных. В отсутствие каких-либо авторских указаний относительно параметров трала я выбрал те, что показались наиболее адекватными дневному таймфрейму и волатильности инструмента. Это же относится и к добавленному к оригинальным правилам алгоритму расчёта Take Profit — коэффициент для его расчёта был выбран произвольно, без глубокой оптимизации.

График изменения баланса при тестировании на пятилетней истории EURUSD с оригинальными правилами (без Take Profit):

С теми же настройками и добавлением Take Profit:

График изменения баланса при тестировании оригинальных правил на пятилетней истории USDJPY:

Тот же инструмент и таймфрейм с теми же настройками, но с добавлением Take Profit:

Оригинальные правила на дневных котировках золота за последние 4 года показывают такой график изменения баланса:

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

Заключение

Запрограммированные в сигнальном модуле правила соответствуют описанию торговой системы 80-20’s из книги Линды Рашке и Лоуренса Коннорса Street Smarts: High Probability Short-Term Trading Strategies. Есть и небольшое расширение авторских правил. Эти инструменты (робот и индикатор) должны помочь желающим сделать самостоятельные выводы об актуальности ТС в условиях современного рынка. По моему скромному мнению, она нуждается в серьёзной модернизации. В статье я постарался подробно прокомментировать создание кода сигнального модуля и использующих его робота и индикатора — надеюсь, это поможет тем, кто решит заняться такой модернизацией. Кроме апгрейда правил, можно попробовать подобрать лучше вписывающиеся в систему торговые инструменты, параметры выявления сигнала и сопровождения позиций.

Рейтинг лучших брокеров БО за 2020 год:
  • Бинариум
    Бинариум

    1 место! Лидер среди всех брокеров бинарных опционов!
    Бесплатное обучение и демо счет — идеальный вариант для новичков и малоопытных трейдеров!
    Дают существенные бонусы за регистрацию:

Добавить комментарий