Боб: «На прошлой неделе я случайно встретил Уоррена Баффета. Я, конечно, спросил его, как лучше торговать. Он сказал так: „Будь жадным там, где другие боятся“».
Алиса: «Интересно. И что это значит?»
Боб: «Разве он не сказал, что должен уйти. Но я думаю, он хотел, чтобы я торговал против тренда».
Алиса: «Разве это не полная противоположность последней стратегии?»
Боб: «Да. Но на самом деле это логично: покупай дешево, продавай дорого. Я хочу покупать сейчас, когда цена высокая, и продавать в короткую, когда она дорогая. Автоматизируйте это, пожалуйста».
Алиса: «Насколько дешево это дешево?»
Боб: «Зависит от рынка».
Алиса: «Конечно. Я должна была подумать об этом сама.
Боб: „Цены часто совершают регулярные колебания вверх и вниз. Просто выясните, происходит ли такое колебание прямо сейчас. Тогда, когда цена падает, мы знаем, что вскоре она снова поднимется. Поэтому мы покупаем длинные позиции, когда цена достигает определенного минимума. При коротких сделках мы делаем все наоборот“.
Алиса: „Хм. Я могла бы использовать полосовой фильтр, чтобы отфильтровать медленный тренд и быстрые колебания от кривой цен. Остаются средние колебания“.
Боб: „Звучит неплохо“.
Алиса: „Затем я нормализую кривую так, чтобы колебания в среднем всегда имели одинаковую высоту. Затем, когда кривая пересекает определенный порог вверх, я продаю в шорт, от порога вниз — покупаю. Это то, что вы хотите?“
Боб: „Все еще звучит хорошо“.
Алиса: „Перед сравнением с порогом я могу применить преобразование Фишера. Это придает кривой гауссово распределение с резкими и отчетливыми колебаниями.
Боб: “Я понятия не имею, о чем вы говорите. Но резкий и отчетливый звучит хорошо».
Алиса: «Конечно, это сложнее, чем система следования за трендом. Это становится немного дороже».
Боб: «Это звучит не так хорошо».
Алиса: «Но за эту цену я могу дополнительно провести оптимизацию параметров. И, прежде всего, тест вне выборки».
Боб: «Это принесет больше прибыли?»
Алиса: «Не обязательно. Но прибыль легче предсказать. Я хочу убедиться, что я тебе по карману».
Это первая версия контртрендового сценария Алисы (Alice2a, гонорар: 8 000 евро):
function run() { BarPeriod = 240; vars Price = series(price()); vars Filtered = series(BandPass(Price,30,0.5)); vars Signal = series(FisherN(Filtered,500)); var Threshold = 1.0; Stop = 4*ATR(100); Trail = 4*ATR(100); if(crossUnder(Signal,-Threshold)) enterLong(); else if(crossOver(Signal,Threshold)) enterShort(); set(LOGFILE); }
Контртрендовые системы чувствительны к рыночным циклам и поэтому более зависимы от периода бара, чем системы следования за трендом. Боб рассказал Алисе, что периоды работы бара, совпадающие с фиксированным временем на мировых рынках — например, 4 или 8 часов — являются особенно прибыльными. Поэтому она установила период бара равным 4 часам, т.е. 240 минут:
BarPeriod = 240;
BarPeriod — это одна из предопределенных переменных Zorro, которые могут быть использованы для определения поведения стратегии и оценки. Все эти переменные описаны в руководстве по Zorro (нажмите на [Help] в окне Zorro и откройте раздел «Системные переменные»).
Следующие строки содержат правила стратегии. В первой строке снова формируется ценовой ряд, как и в предыдущей стратегии следования за трендом:
vars Price = series(price());
В следующей строке цены подаются на полосовой фильтр:
vars Filtered = series(BandPass(Price,30,0.5));
Поведение полосового фильтра можно увидеть на рис. 15 — Некоторые спектральные фильтры предыдущей главы во второй кривой сверху. Он в основном пропускает определенный цикл и затухает все более длинные и более короткие циклы кривой цен. Этот пропускает цикл из 30 баров и имеет ширину 0,5 (чем шире фильтр, тем больше отклоняющихся циклов пропускается). Таким образом, из кривой цен удаляется как тренд (цикл с очень длинным периодом), так и случайный шум (очень короткие циклы). В результате получается чистая кривая максимумов и минимумов 30-барного цикла, что соответствует примерно одной неделе (30*4 часа = 5 рабочих дней). Он хранится в новой серии под названием Filtered.
Как и исходная кривая цен, отфильтрованная кривая также может принимать все возможные значения. Чтобы иметь возможность сравнивать ее с фиксированными пороговыми значениями для генерации торговых сигналов, кривая должна быть «нормализована», т.е. преобразована в фиксированный диапазон значений. В традиционном техническом анализе существует индикатор «стохастик» для нормализации кривой. Алиса предпочитает трансформацию Фишера. Это относительно простая операция, которая преобразует значения данных в гауссово распределение — знаменитую «кривую колокола», где большинство значений лежит в середине и только несколько значений лежат вне диапазона 1 ...- 1. Нормализация и преобразование Фишера выполняются с помощью функции FisherN. Она преобразует отфильтрованную серию в серию сигналов с гауссовским распределением. Последние 500 баров используются для определения диапазона значений для нормализации:
vars Signal = series(FisherN(Filtered,500));
Теперь текущее значение серии сигналов может быть сравнено с верхним и нижним пороговым значением для генерации торговых сигналов. Пороговое значение задается в следующей строке:
var Threshold = 1.0;
Эта строка определяет новую переменную Threshold со значением 1. Алиса хочет начать торговлю, как только Signal превысит пороговое значение 1 или упадет ниже -1. Это происходит в следующей части кода. Однако перед этим Алиса устанавливает стоп-лосс таким же образом, как и раньше в сценарии следования за трендом. Функция ATR снова используется для определения средней высоты 4 свечей в качестве расстояния стоп-лосса:
Stop = 4*ATR(100);
В дополнение к стоп-лоссу Алиса также установила трейлинг-триггер, также на расстоянии 4 свечей от текущей цены:
Trail = 4*ATR(100);
Trail автоматически подтягивает стоп при положительном развитии сделки. При прибыли более 4 средних высот свечи, стоп-лосс автоматически начинает следовать за ценой. Если цена движется в направлении прибыли, стоп следует на расстоянии 8 свечей (4 свечи расстояние стопа и 4 свечи расстояние следа). Если она движется в убыточном направлении, стоп останавливается. Такое «сужение стопа» гарантирует, что все сделки, которые растут более чем на 8 свечей, гарантированно принесут прибыль — независимо от того, как цена будет развиваться дальше. Как только цена проходит более 8 свечей в неправильном направлении, срабатывает стоп. Поскольку стоп уже превысил цену входа в сделку, сделка завершается с прибылью. Механизм следа часто — но не всегда — повышает прибыль стратегии.
if(crossUnder(Signal,-Threshold)) reverseLong(1); else if(crossOver(Signal,Threshold)) reverseShort(1);
Мы знаем об открытии сделок с помощью вызовов enterLong/enterShort из предыдущей главы, но что делают reverseLong и reverseShort? В принципе то же самое, но в то же время они ограничивают максимальное количество открытых сделок указанным числом (здесь 1). Таким образом, одновременно открыта только одна сделка, даже если последовательно срабатывает несколько сигналов. Это снижает риск, и кривая капитала становится более плавной. В скрипте торговли по тренду в этом не было необходимости, так как открытие двух сделок в одном направлении там никогда не могло произойти (если только смена тренда не произошла в выходные дни, когда ни одна сделка не может быть закрыта или открыта). reverseLong/reverseShort — это вспомогательные функции, которые «запрограммированы» для удобства. Они, в свою очередь, вызывают enterLong/enterShort и делают некоторые другие полезные вещи, например, устанавливают стоп-расстояния всех уже открытых сделок на текущее значение стопа. Исходный код reverseLong/reverseShort находится в файле default.c в каталоге include. Этот файл автоматически считывается каждым скриптом, и его функции становятся доступными в скрипте.
Функции crossUnder и crossOver работают аналогично функциям peak и valley. Они проверяют, пересекаются ли две кривые друг с другом или кривая опускается ниже или превышает пороговое значение. Как только кривая сигнала пересекает отрицательное пороговое значение (-threshold) сверху вниз — т.е. когда сигнал падает ниже -1 — через reverseLong запускается длинная сделка. Поскольку в гауссовом распределении только несколько значений меньше -1, если цена падает ниже этого порога, она должна находиться вблизи нижней точки колебания и, следовательно, вскоре снова вырасти. И наоборот, если положительный порог пробит снизу вверх, то есть сигнал поднимается выше значения 1, цена находится вблизи максимума и вызывает короткую сделку. Поэтому данная система ведет себя противоположно стратегии следования за трендом. Поэтому, вероятно, можно ожидать, что он будет приносить прибыль, когда следование за трендом терпит неудачу, и наоборот.