Разработчик и трейдер Йоан Кристиан Лоттер, создатель блога Financial Hacker, написал интересный материал, в котором рассказал о своем эксперименте, призванном выяснить, имеет ли смысл торговля с использованием коротких и сверхкоротких интервалов для совершения сделок. Мы представляем вашему вниманию главные мысли этой заметки.
Начинающие трейдеры часто хотят работать на очень коротких временных интервалах. Некоторые из них вдохновляются историями о том, как кто-то заработал $2000 за пять минут — такого добра очень много на финансовых фороумах. Другие услышали о высокочастотной торговле и теперь уверены, что чем выше частота совершения сделок, тем лучше итоговый результат. Полностью ли это бесполезное занятие или у такой краткосрочной торговли есть какие-то количественные преимущества? Эксперимент, целью которого было это выяснить, показал неожиданные результаты.
Соблазн получать доходы в течение каких-то минут очень велик. Кроме того, при торговле на коротких временных интервалах (таймфреймах) совершается больше сделок и есть больше баров для анализа и улучшения стратегии. Качество тестов и обучения зависит от объёма доступных данных. И тем не менее скальпинг — то есть открытие и закрытие сделок за минуты или даже секунды — в среде алготрейдеров многими считается ерундой и чем-то иррациональным. Этому есть несколько причин:
- Короткие таймфреймы приводят к росту издержек — проскользывание, спред, комиссии — по отношению к ожидаемому доходу.
- При коротких интервалах в кривой спроса больше шума, случайностей и артефактов — все это снижает доход и увеличивает риск.
- Каждый торговый алгоритм нужно адаптировать для работы с конкретным брокером или поставщиком финансовых данных, поскольку для успешной торговли крайне важна качественная работа с потоком данных о ценах (price feed).
- Алгоритмические стратегии обычно просто прекращают работать на таймфреймах ниже определенного значения.
Высокие издержки, меньшая выгода, больший риск, зависимость от дата-фида, неработающие алгостратегии — неплохие аргументы против скальпинга (HFT — совсем другая история). Несмотря на то, что изложенное выше может являться правдой, и к примеру, эксперименты показывают, что при бэктестинге стратегий на таймфреймах меньше 10 минут на данных от разных брокеров результаты сильно разнятся. Но это не значит, что такой способ торговли на бирже не имеет никакого смысла, может быть работа с небольшими таймфреймами просто требует особого подхода?
Чтобы это выяснить, Лоттер провел эксперимент, который включал создание реальной стратегии скальпинга.
Влияние сопутствующих издержек
Первая часть эксперимента заключалась в анализе статистическом влиянии на итоговый финансовый результат сопутствующих издержек. Логично, что более высокие издержки требуют большего дохода, чтобы как-то их компенсировать. Сколько успешных сделок нужно совершить, чтобы прибыль превысила расходы на торговлю на разных таймфреймах? Небольшой скрипт ниже отвечает на этот вопрос:
function run()
{
BarPeriod = 1;
LookBack = 1440;
Commission = 0.60;
Spread = 0.5*PIP;
int duration = 1, i = 0;
if(!is(LOOKBACK))
while(duration <= 1440)
{
var Return = abs(priceClose(0)-priceClose(duration))*PIPCost/PIP;
var Cost = Commission*LotAmount/10000. + Spread*PIPCost/PIP;
var Rate = ifelse(Return > Cost, Cost/(2*Return) + 0.5, 1.);
plotBar("Min Rate",i++,duration,100*Rate,AVG+BARS,RED);
if(duration < 10) duration += 1;
else if(duration < 60) duration += 5;
else if(duration < 180) duration += 30;
else duration += 60;
}
Bar += 100; // hack!
}
Скрипт вычисляет минимальную степень успешности сделок, которая нужна для того, чтобы компенсировать издержки при торговле на разных временных интервалах. Предполагается, что существует спред размером в 0,5 пипса (минимальной величины изменения цены валютной пары) и общая комиссия в 60 центов на 10000 контрактов — средняя стоимость сделок на финансовом рынке. PIPCost/PIP в коде выше — это фактор конверсии из ценовой разницы в прибавку или потерю денег на счете. Также предполагается отсутствие влияния психологических факторов на торговлю: все трейдеры должны в среднем получать прибыль или проигрывать одинаковым образом. Это позволяет разбить величину Return сделки на ее прибыльность или убыточность, которые определяются показателем WinRate. То есть, если сделка прибыльна, то мы получим WinRate * Return, а если убыточна, то (1-WinRate) * Return. Для выхода на окупаемость нужно, чтобы разница между успешными сделками и убыточными покрывала издержки. Степень успешности в этом случае должна быть такой:
WinRate = Cost/(2*Return) + 0.5
Этот показатель усреднен по всем барам и изображен в виде гистограммы длительности сделок от 1 минуты до 1 дня. Шаг увеличения таймфрейма составляет 1, 5, 30 и 60 минут. Сделка для любой длительности совершается каждые 101 минут (Bar +=100 в скрипте — это хитрость, чтобы запустить симуляцию на шаге, начиная со 101-го, при этом сохраняя период бара равный 1 минуте).
Скрипту требуется несколько секунд, а затем он выдает вот такую гистограмму для валютной пары EUR/USD в 2015 году:
Для покрытия издержек при торговле на интервале в 1 день (крайний правый бар) нужен показатель успешности 53%, а для 1-минутных сделок он вырастает уже до 90%. То есть соотношения вознаграждения к риску составляет 9:1 для показателя успешности в 50%. Это значительно превышает реальный доход, генерируемый торговыми системами. Казалось бы, подтверждается первый пунт из списка выше, и становится ясно, что рассказы «успешных скальперов» с форумов о трейдинге нужно воспринимать с изредной долей скептицизма.
Но как насчет второго тезиса о том, что на коротких таймфреймах больше шума и случайностей? Или есть какой-то способ работы с ними, который позволит добиться большей предсказуемости. Выяснить это уже посложнее.
Измерение случайности
«Шум» часто описывают с помощью компонента высокочастотной торговли под названием сигнал. Обычно короткие временные интервалы генерируют больше hft-компонентов, чем более длинные таймфреймы. Их можно находить с помощью фильтра высоких частот и устранять с помощью фильтра нижних частот. Но есть проблема: шум кривой спроса не всегда связан с высокими частотами. Шум это просто часть кривой, которая не несет в себе информации о торговых сигналах. Для цикличной торговли, высокие частоты — это сигналы, а низкочастотные тренды являются шумом. Иными словами, то, что является шумом, зависит от конкретной стратегии — нет какого-то «универсального» шума.
Таким образом для определения «торгуемости» кривой спроса нужен какой-то другой критерий. Это случайность (randomness). Ее можно измерить с помощью информационной наполненности кривой спроса. Хорошая мера информационной наполненности — это энтропия Шеннона. Она определяется так:
Эта формула измеряет неупорядоченность. В случае упорядоченного, предсказуемого сигнала энтропия будет низкой. Случайный, непредсказуемый сигнал обладает высокой энтропией. В формуле P(si) — относительная частота возникновения конкретного паттерна si в сигнале s. Энтропия будет максимальной, когда все паттерны равномерно распределены, а все P(si) примерно равны. Если какие-то паттерны возникают чаще других, энтропия снижается. В таком случае сигнал будет менее случайным и более предсказуемым. Энтропия Шеннона измеряется в битах.
Ниже представлен код индикатора энтропии Шеннона, изображаемой в виде последовательности символов:
var ShannonEntropy(char *S,int Length)
{
static var Hist[256];
memset(Hist,0,256*sizeof(var));
var Step = 1./Length;
int i;
for(i=0; i<Length; i++)
Hist[S[i]] += Step;
var H = 0;
for(i=0;i<256;i++) {
if(Hist[i] > 0.)
H -= Hist[i]*log2(Hist[i]);
}
return H;
}
В символе 8 бит, так что в строке может быть 28 = 256 символов. Частота каждого символа вычисляется и хранится в массиве Hist. Этот массив содержит P(si) из формулы энтропии выше. Затем эти значения умножаются на двоичный логарифм и суммируются. В результате получается H(S) — это энтропия Шеннона.
В коде выше символ является паттеном сигнала. Таким образом нужно конвертировать кривую спроса в символьные паттерны. Это можно сделать с помощью функции
ShannonEntropy
, которая вызывает предыдущую:var ShannonEntropy(var *Data,int Length,int PatternSize)
{
static char S[1024]; // hack!
int i,j;
int Size = min(Length-PatternSize-1,1024);
for(i=0; i<Size; i++) {
int C = 0;
for(j=0; j<PatternSize; j++) {
if(Data[i+j] > Data[i+j+1])
C += 1<<j;
}
S[i] = C;
}
return ShannonEntropy(S,Size);
}
PatternSize
определяет сегментированность кривой спроса. Паттерн определяется числом изменений цены. Каждая новая цена либо выше предыдущей либо нет — это двоичная информация, описывающая один бит паттерна. Паттерн может включать до 8 битов, что эквивалентно 256 комбинациям изменений цены. Паттерны также хранятся в символьной строке. Их энтропия определяется с помощью вызова функции ShannonEntropy
для конкретной строки (как видно, у обеих функций одно и то же название, но компилятор их различает по параметрам). Паттерны генерируются на основе цены и последующих ценах PatternSize
. Далее процедура повторяется для последующей цены, таким образом происходит наложение паттернов.Неожиданный результат
Теперь остается лишь нарисовать гистограмму энтропии Шеннона, как мы сделали выше с показателем успешности:
function run()
{
BarPeriod = 1;
LookBack = 1440*300;
StartWeek = 10000;
int Duration = 1, i = 0;
while(Duration <= 1440)
{
TimeFrame = frameSync(Duration);
var *Prices = series(price(),300);
if(!is(LOOKBACK) && 0 == (Bar%101)) {
var H = ShannonEntropy(Prices,300,3);
plotBar("Randomness",i++,Duration,H,AVG+BARS,BLUE);
}
if(Duration < 10) Duration += 1;
else if(Duration < 60) Duration += 5;
else if(Duration < 240) Duration += 30;
else if(Duration < 720) Duration += 120;
else Duration += 720;
}
}
Энтропия вычисляется для всех таймфреймов на каждом 101-м баре (такое число выбрано, чтобы избежать проблем с синхронизацией). Здесь нельзя просто пропустить сто баров, как в предыдущем скрипте, поскольку это не позволит адекватно обрабатывать ценовые ряды. Поэтому скрипту требуется изучить каждую торговую минуту за последние три года, на что уходит несколько минут.
В коде есть две строки, которые важно объяснить — они важны для измерения энтропии дневных свечей с использованием баров для перидов менее дня:
StartWeek = 10000;
Неделя начинается в полночь понедельника ( 1 = понедельник, 00 00 = полночь), вместо 11 вечера воскресенья. Если выбрать последнее значение, то скрипт будет рассматривать этот один воскресный час, как полный день, что увеличивает случайность дневных свечей.
TimeFrame = frameSync(Duration);
Эта строка нужна для синхронизации таймфрейма с часами в ходе дня. Если ее убрать, то энтропия Шеннона дневных свечей будет слишком велика.
Энтропия Шеннона вычисляется для паттерна размером 3 изменений цены, что приводит к наличию 8 разных паттернов. Максимальная энтропия для 8 паттернов составляет 3 бита. Поскольку изменения цены не полностью случайны, можно было бы предположить, что энтропия будет меньше 3, возрастая с уменьшением таймфрейма. Однако для данных по торгах валютной пары EUR/USD в 2013-2015 годах, получилась следующая гистограмма:
Энтропия составляет почти 3 бита. Это подтверждает тезис о не полной случайности цены. Как видно, энтропия Шеннона будет наименьшей для таймфрейма 1440 размером минут — она составляет около 2,9. Это ожидаемый результат, поскольку дневные циклы серьезно влияют на кривую спроса, поэтому дневные свечи более предсказуемы. Поэтому алгоритмы ценовых паттернов часто используют дневные свечи. Энтропия возрастает с уменьшением таймфрейма, но так происходит лишь до таймфреймов размером десять минут. А еще более короткие временные интервалы на самом деле менее случайны.
И вот это уже неожиданность. Чем меньше таймфрейм, тем меньше котировок цены он в себе содержит, это должно увеличивать элемент случайности. Но происходит наоборот — так происходит для разных временных интервалов (даже сверхкоротких 2, 5, 10, 15, 30, 45 и 60 секунд) и даже для разных активов.
Энтропия vs таймфрейм (в секундах)
Теперь ось x — это секунды, а не минуты. И все равно случайность снижается вместе с уменьшением таймфрейма.
Этому может быть несколько объяснений. Гранулярность цены выше на небольших таймфреймах, поскольку в них содержится меньшее количество тиков (минимальных колебаний цены). Крупные сделки часто разбивают на много небольших частей (“Iceberg-приказы”), что может приводить к появлению последовательностей похожих ценовых котировок на коротких временных интервалах. Все это снижает ценовую энтропию коротких таймфреймов, но совсем необязательно приводит к появлению возможностей для торговли.
Последовательность одинаковых котировок имеет нулевую энтропию, они на 100% предсказуемы, но их нельзя использовать для торговли. Сделки айсберг представляют определенную неэффективность рынка, которую теоретически можно использовать — но этому мешает высокие издержки на совершение сделки. Если бы не было брокерских комиссий, то это было бы возможно, но не в текущей ситуации.
Выводы
- Скальпинг — это не полное сумасшествие. Даже у очень небольших таймфреймов есть какая-то регулярность.
- Тем не менее, эту регулярность тяжело использовать частному трейдеру из-за высоких издержек на совершение сделок.
- Для таймфреймов выше 60 минут цены становятся менее случайными и более предсказуемыми. Это значит, что такие таймфреймы хорошо использовать для алгоритмической торговли.
- Наиболее предсказуемы цены для однодневных баров. При торговле с их использованием также будут минимальные издержки.