Одной из
характеристик торговой системы является периодичность принятия решений
о совершении сделок. Другими словами - срочность торговли. По срочности торговлю
разделяют на такие типы:
Долгосрочная - торговые решения принимаются на основании
анализа больших временных периодов (дней, недель, месяцев). Соответственно,
время удержания таких сделок исчисляется неделями и месяцами. По крайней
мере, до тех пор, пока не будут достигнуты намеченные грандиозные цели.
Среднесрочная - принятие торговых решений производится на
основании движения цен за дни, реже за недели. Здесь в расчет могут
приниматься и данные с графиков ниже дневного. Такие позиции живут уже не
так долго, как долгосрочные. В основном они рассчитаны на сроки не больше
месяца.
Краткосрочная - торговля в пределах одного дня. Еще ее
называют внутридневной торговлей. Здесь в расчет могут приниматься
данные даже с минутного периода.
Большинство отечественных трейдеров (к
коим я и себя отношу) использует именно краткосрочную торговлю, так как она
очень близка нам по менталитету, да и что греха таить, по средствам. О
правильности такой тактики торговли можно много спорить, но факт остается фактом
- всем нам хочется побыстрее закрыть накопившуюся прибыль, а бывалым - и
появившийся убыток. В результате, мало кто из нас может похвастать большим
количеством позиций, закрытых с прибылями более 500 пунктов.
Исходя из таких сформировавшихся
предпочтений трейдеров (понятно, что без исключений не обходится), рассмотрим
торговую стратегию, которая оперирует сделками в течение одной торговой сессии.
Под торговой сессией понимается деление суток на три условные зоны, которые
называют азиатской сессией (центры - Токио, Веллингтон и Сидней), европейской
сессией (центры - Франкфурт, Цюрих, Париж, Лондон) и американской (центр -
Нью-Йорк).
Стратегия сессионной торговли основана на
совершении сделок внутри канала, образованного предыдущей сессией. Исключение
составляет азиатская сессия, приходящаяся на начало недели. Технически выходит,
что предшествующая ей американская сессия относится к прошлой неделе. Поэтому
в течение азиатской сессии понедельника сделки совершаться не будут.
Рис.
1. - Совершение сделок при сессионной торговле.
Схематично суть сессионной торговли
представлена на рис. 1. Каждая из сессий выделена цветом: азиатская сессия -
желтым, европейская - синим, американская - красным (цвета выбраны, исходя из
цветов олимпийских колец, символизирующих части света). Все сделки производятся
отложенными ордерами Buy Stop и
Sell Stop.
Сигналом для установки ордера
Buy Stop является закрепление цены ниже нижней границы
канала. Под закреплением имеется в виду нахождение цен открытия и закрытия свечи
за границами канала. Ценой открытия ордера Buy Stop
будет назначена цена, соответствующая нижней границе канала (так как
ордера Buy Stop открываются по
цене Ask, то к нижней границе необходимо будет
прибавить спрэд).Уровень стоп-приказа ордера
устанавливается на минимум текущей сессии, из которого вычтена ширина канала.
Уровень профита ордера устанавливается на верхнюю границу канала. Если в течение
сессии ордер не сработал, то он отменяется.
Соответственно,
сигналом для установки ордера Sell Stop является
закрепление цены выше верхней границы канала. Ценой открытия ордера будет цена,
соответствующая верхней границе канала. Уровень стоп-приказа устанавливается на
максимум текущей сессии, к которому прибавляется ширина канала (закрытие
Sell производится по цене Ask,
поэтому прибавляется спрэд). Уровень профита ордера устанавливается на нижнюю
границу канала (плюс спрэд). Как и в случае с Buy Stop -
ордером, если Sell Stop
не сработал до конца сессии, то он отменяется.
По традиции все
расчеты, связанные с получением ценовых уровней открытия и закрытия сделок,
произведем в функции GetSignal, хотя никакой генерации
сигнала здесь не будет:
//+-------------------------------------------------------------------------------------+
//| Расчет границ канала |
//+-------------------------------------------------------------------------------------+
void GetSignal()
{
// - 1 - ==== Если канал не будет рассчитан, то все значения останутся нулевыми =========
LowV = 0;
HighV = 0;
CurSession = -1;
int DayBar = 0;
int k = 0;
// - 1 - ============================= Окончание блока ==================================
// - 2 - ==== Определение текущей торговой сесиии =======================================
for (k = 0; k < 3; k++)
if (Hour() >= Start[k] && Hour() < Finish[k])
{
if (k == 0)
{
DayBar++;
int j = 2;
}
else
j = k - 1;
// - 2 - ============================= Окончание блока ==================================
// - 3 - ==== Расчет уровней ============================================================
datetime BeginDay = iTime(Symbol(), PERIOD_D1, DayBar); // Время начала дня
if (!(TimeDayOfWeek(BeginDay) == 5 && k == 0)) // Исключаем азиатскую сессию
{ // понедельника
// Бар, соответствующий началу предыдущей сесии
int StartBar = iBarShift(Symbol(), 0, BeginDay+Start[j]*3600);
// Бар, соответствующий окончанию предыдущей сесии
int FinishBar = iBarShift(Symbol(), 0, BeginDay+Finish[j]*3600)+1;
// Нижняя граница
LowV = Low[iLowest(Symbol(), 0, MODE_LOW, StartBar-FinishBar+1, FinishBar)];
// Верхняя граница
HighV = High[iHighest(Symbol(), 0, MODE_HIGH, StartBar-FinishBar+1, FinishBar)];
// Минимум текущей сессии
SessionLow = Low[iLowest(Symbol(), 0, MODE_LOW, FinishBar+1)];
// Максимум текущей сессии
SessionHigh = High[iHighest(Symbol(), 0, MODE_HIGH, FinishBar+1)];
}
CurSession = k;// номер текущей сессии (0-азиатская, 1-европейская, 2-американская)
break;
}
// - 3 - ============================= Окончание блока ==================================
}
Алгоритм не совсем очевиден, поэтому требует пояснений. В
первом блоке производится инициализация всех участвующих в расчетах переменных.По окончании расчета в переменных LowV и
HighV будут находиться соответственноцены нижней и верхней границ канала. В переменной
CurSession - номер текущей сессии (соответствие описано в конце третьего
блока и в таком виде используется во всем эксперте). Переменные
DayBar и k - служебные и
поэтому никакой полезной информации для других функций не несут.
Для определения границ канала необходимо определить
принадлежность текущего времени какой-либо торговой сессии, что производится во
втором блоке. В одномерных массивах Start и
Finish до вызова функции GetSignal
должны быть сформированы часы начала и окончания каждой сессии.
Количество элементов каждого массива равно количеству сессий, то есть 3. Именно
поэтому максимальное число итераций цикла for равно
трем. Если текущий час принадлежит одной из трех сессий, то окончательно
определяются переменные DayBar и j.
Переменная j должна указывать на предыдущую
торговую сессию, поэтому ее приравниваем к значению k,
уменьшенного на единицу. Соответственно, значение
DayBar должно указывать на порядковый номер дневного
бара, которому принадлежит предыдущая торговая сессия. В результате, если
k равно нулю (азиатская сессия), то предыдущая для нее
сессия будет американская сессия прошедшего дня. Это соответствует значению
j, равного двум. А порядковый номер дневного бара
просто увеличивается на единицу.
В третьем блоке по порядковому номеру дня, указанного в
DayBar, определяется время открытия дня(BeginDay), которому принадлежит предыдущая
торговая сессия.Затем исключается расчет границ
канала для азиатской сессии понедельника. В этом случае
BeginDay будет указывать на пятницу, как на предыдущий день, а
k - на азиатскую сессию. Дальше происходит простой
арифметический расчет. Для вычисления границ канала требуется знать номера
баров начала (StartBar) и окончания сессии
(FinishBar) текущего временного периода.Для
этого к времени начала дня прибавляется количество часов, умноженное на
количество секунд в часе (3600). По рассчитанным StartBar
и FinishBar вычисляются границы канала. Также,
кроме границ канала, требуется всегда знать текущий максимум и минимум торговой
сессии. Для этого подобным образом рассчитываются значения переменных
SessionLow и SessionHigh. И
в конце блока глобальной переменной CurSession
присваивается номер текущей торговой сессии.
Код функции start
приобрел на редкость лаконичный вид:
//+-------------------------------------------------------------------------------------+
//| Функция START эксперта |
//+-------------------------------------------------------------------------------------+
int start()
{
// - 1 - === Разрешено ли советнику работать? ===========================================
if (!Activate || FatalError) // Отключается работа советника, если функция
return(); // init завершилась с ошибкой или имела место фатальная ошибка
// - 1 - === Окончание блока ============================================================
// - 2 - == Контроль образования нового бара для ускорения работы эксперта ==============
if (LastBar == Time[])
return();
// - 2 - === Окончание блока ============================================================
// - 3 - == Сбор информации об условиях торговли ========================================
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов
// - 3 - === Окончание блока ============================================================
// - 4 - == Определение времени начала текущего дня и расчет уровней ====================
NowDay = iTime(Symbol(), PERIOD_D1, 0);
GetSignal();
// - 4 - === Окончание блока ============================================================
// - 5 - == Установка отложенного ордера, если определены уровни ========================
if (LowV != 0 && HighV != 0 && CurSession != -1)
DoTransactions();
// - 5 - === Окончание блока ============================================================
//----
return();
}
Все сведено к вызовам функций. И если функцию
GetSignal мы уже рассмотрели, то функция
DoTransactions еще только ожидает своей очереди. Но
перед ее рассмотрением стоит заметить один факт, который может привести к
неправильному тестированию эксперта. Речь идет о содержимом второго блока,
который отвечает за исполнение функций GetSignal и
DoTransactions один раз в течение одного бара. Но это
не совсем так. Иногда случается, что расчетная цена открытия
отложенного ордера находится слишком близко к текущей цене, что не позволяет
произвести установку ордера. В таких случаях эксперт будет ждать подходящего
момента для установки ордера. Поэтому очередная попытка установки ордера будет
произведена на следующем тике. Этот момент касается не только работы эксперта
онлайн, но и в тестере. Ведь, если тестировать такой эксперт на модели "По ценам
открытия", то следующего тика для текущего бар не будет и произойдет переход к
следующему бару. Таким образом, будет пропущено довольно много сделок. В связи с
этим, несмотря на присутствие блока контроля открытия бара, тестирование
советника необходимо производить только на модели "Все тики".
А вот теперь рассмотрим функцию
DoTransactions, которая осуществляет установку отложенного ордера:
//+-------------------------------------------------------------------------------------+
//| Установка отложенных ордеров |
//+-------------------------------------------------------------------------------------+
void DoTransactions()
{
// - 1 - == Установка ордера Buy Stop ===================================================
if (LastOpen[CurSession] < NowDay) // Устанавливался ли ордер за время текущей сессии?
if (Close[1] < LowV && Open[1] < LowV) // Цена закрепилась за нижней границей канала
{
RefreshRates();
double Price = ND(LowV + Spread + Delta*Tick); // Цена открытия Buy Stop - нижняя
// граница канала
double SL = ND(SessionLow - (HighV - LowV) - Tick); // Стоп для Buy Stop
double TP = ND(HighV); // профит для Buy Stop - противоположная граница канала
if (OpenOrderCorrect(OP_BUYSTOP, Lots, Price, SL, TP, False) == 0)
LastOpen[CurSession] = TimeCurrent(); // Ордер успешно открыт, записываем время
else
return;
}
// - 1 - === Окончание блока ============================================================
// - 2 - === Установка ордера Sell Stop =================================================
if (LastOpen[CurSession] < NowDay) // Устанавливался ли ордер за время текущей сессии?
if (Close[1] > HighV && Open[1] > HighV)// Цена закрепилась за верхней границей канала
{
RefreshRates();
Price = ND(HighV - Delta*Tick); // Цена открытия Sell Stop - верхняя граница канала
SL = ND(SessionHigh + (HighV - LowV) + Spread + Tick); // Стоп для Sell Stop
TP = ND(LowV + Spread); // профит для Sell Stop - противоположная граница канала
if (OpenOrderCorrect(OP_SELLSTOP, Lots, Price, SL, TP, False) == 0)
LastOpen[CurSession] = TimeCurrent(); // Ордер успешно открыт, записываем время
else
return;
}
// - 2 - === Окончание блока ============================================================
LastBar = Time[]; // Больше на текущем баре не работаем
}
Функция состоит из двух подобных блоков. Различие лишь в
методике расчета уровней цены открытия, стоп-приказа и профита.
Обязательным условием стратегии является открытие всего
одного ордера за одну торговую сессию. Это условие возможно выполнить, записывая
время открытия последнего ордера для соответствующей торговой сессии. Как вы
помните, сессии у нас пронумерованы от 0 до 2. Поэтому достаточно использовать
массив из трех элементов, в котором будет сохраняться время каждого успешно
открытого ордера. В эксперте таким массивом выступает
LastOpen, значение которого сравнивается со временем открытия текущего
дня.
При работе в тестере значение массива сохраняется. А вот в
реальной работеэти значения теряются. Поэтому при
каждом новом запуске эксперта необходимо поэлементно восстанавливать массив. Для
этого в функцию init нужно вставить блок поиска
последних ордеров. Принадлежность ордеров торговой сессии можно отметить разными
значениями поля Magic Number, которые будут
устанавливаться еще при открытии отложенного ордера в функции
OpenOrderCorrect (см. предыдущую статью
"Ловец трендов").
Блок восстановления значений массива
LastOpen будет выглядеть так:
// - 4 - === Восстановление значений элементов массива LastOpen =========================
for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) // поиск в истории счета
if (OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber)
{
int ID = MathMod(OrderMagicNumber(), 10);
LastOpen[ID] = MathMax(LastOpen[ID], OrderOpenTime());
}
for (i = OrdersTotal() - 1; i >= 0; i--)
if (OrderSelect(i, SELECT_BY_POS)) // поиск в окне терминала
if (OrderSymbol() == Symbol() && MathFloor(OrderMagicNumber()/10) == MagicNumber)
{
ID = MathMod(OrderMagicNumber(), 10);
LastOpen[ID] = MathMax(LastOpen[ID], OrderOpenTime());
}
// - 4 - === Окончание блока ============================================================
В получившемся эксперте
SessionTrade_Expert входных параметров немного. Здесь присутствуют уже
привычные Lots, OrderOpenSound и
MagicNumber. К ним добавился параметр Delta,
который указывает, на сколько пунктов от расчетной цены открытия должен быть
установлен ордер. Для Buy Stop значение
Delta прибавляется, для Sell Stop -
вычитается.
Остальные шесть параметров по смыслу идентичны. Это время в
часах, определяющее начало (AzianFlatStart, EuropeanFlatStart
и AmericanFlatStart) и окончание(AzianFlatEnd, EuropeanFlatEnd и
AmericanFlatEnd) каждой из торговых сессий. Если
пользователю не не подходят установленные по умолчанию значениями начала и
окончания сессий, то он всегда может их изменить. Можно даже уменьшить или
увеличить продолжительность каждой сессии. Главное, чтобы получившиеся сессии не
накладывались друг на друга.
Как всегда после разработки советника, приступим к тестированию
стратегии.
Эксперт SessionTrade_Expert тестируется
на промежутке 01.01.2008 - 26.09.2009, таймфрейм M15, значения входных параметров
- по умолчанию. Результаты приведены на рис. 2 - 5.
Рис. 2. - График кривой баланса,
получаемый при тестировании советника на валютной паре
EURUSD.
Рис. 3.
- График кривой баланса, получаемый при тестировании советника на валютной паре
USDCHF.
Рис. 4.
- График кривой баланса, получаемый при тестировании советника на валютной паре
GBPUSD.
Рис. 5.
- График кривой баланса, получаемый при тестировании советника на валютной паре
USDJPY.
Из всех
результатов удачным можно назвать только один - по EURUSD,даже, несмотря на то, что на
паре USDCHF показана прибыль. Ведь прибыль
показана мизерная - 313.52, а просадка - 1747.31 долларов, что к
рассмотрению просто не допускается.
Поэтому рассмотрим
результаты только валютной пары EURUSD. Из-за
уменьшения тестируемого таймфрейма (с привычного Н1 до более мелкого М15) количество сделок получилось
довольно большим - 569. Чистая прибыль 4059.70 против максимальной просадки
1821.91 долларов. Несмотря на большое значение максимальной просадки, фактор
восстановления оказался больше двух - 2.23, что делает результат приемлемым. Из
других статистических показателей следует отметить процент прибыльных сделок -
61.8 (прямо как одно из любимых чисел приверженцев волновой теории Эллиота).
Вселяет оптимизм превалирование средней серии прибыльных сделок над убыточной
3/2. Еще больше этот разрыв в показателях максимальных серий прибыльных и
убыточных сделок - 12/5.
И напоследок
несколько слов о версии советника для приложения AutoGraf.
Для установки стратегии файл, на который
указывает ссылка "Исполняемый файл стратегии для AutoGraf 4.0",
необходимо поместить в папку МТ4\experts\libraries.
Затем запустить AutoGraf, настроить значение
объема сделок (Lots установить на 0.1 для соответствия
значениям по умолчанию). Эквивалент входного параметра
Delta в AutoGraf будет
дистанция (Ds поставить равной 1).
Только после окончания произведения
настроек можно перевести значок AT в верхнее
положение, что приведет к активизации функции автоматической торговли.
Использование полученного
советника рекомендуется только в полуавтоматическом режиме под присмотром
трейдера и после всестороннего изучения слабых и сильных сторон стратегии.
Извените что пишу тут. почему может не работать индикатор sesions flat? терминал виснит ничего немогу сделать. Ставлю индикатор и всё зависон!
Хотя он откомпелировался, всё гуд.
Комментарии
Отправить комментарий