Автор: Александр Кургузкин (mehanizator).
В недавней статье я показал, что изменение SPY на следующий день после сильных падений в среднем имеет положительное матожидание. То есть — сильные падения в основном выкупаются. Попробуем собрать торговую стратегию, которая будет использовать эту рыночную закономерность.
Как обычно, будем рассматривать ценовые изменения в нормированном виде, где в качестве нормы возьмем волатильность предыдущего месяца, т.е. в скользящем окне 21 торговых дней.
Если сегодня SPY упал, будем заходить в позицию, пропорциональную этому падению, нормированному на волатильность. Коэффициент пропорциональности определим из формулы для коэффициента Келли, с понижающим коэффициентом 0.33.
Смотрим что получилось, шкала логарифмическая:
Результат 40 за 11 лет это 3.6 в год в среднем, то есть +260% без реинвестирования. Просадки, конечно, будут нечеловеческими, поэтому как это обычно бывает, вряд ли кто решится торговать с «плечами по келли».
Шарп 1.03
Оборот стратегии за все время 50000, если оценить торговые издержки на SPY 0.01% в одну сторону, получается 5, т.е. где-то 8% доходности будут уходить в издержки.
В общем, как первое приближение пойдет, но для реальной жизни хорошо бы добавить еще один параметр. Напрашивается очевидный кандидат — само значение локальной волатильности. Из-за эффекта плеча можно ожидать, что стратегия будет лучше работать, когда волатильность низкая.
Код рисеча на Котлине:
fun main(args: Array<String>) {
val spy = Sec(«spy», Date(103, 0, 1))
val normDays = 21
val norms = spy.getNormalization(normDays, NormalizationType.ABS)!!
val changesPast = spy.getPastLogChanges(1)!!
val changesFuture = spy.getFutureLogChanges(1)!!
// KELLY
val normalizedChangesFuture = Util.commonDates(norms, changesFuture)!!.map { date ->
date to changesFuture[date]!! / norms[date]!!
}.toSortedMap()
val variation = normalizedChangesFuture.values().fold(0.0, { v, x -> v + x * x }) / normalizedChangesFuture.size
val kelly = 0.33 / variation
// POSITIONS
val positions = Util.commonDates(norms, changesFuture, changesPast)!!.map { date ->
val normalizedPastChange = changesPast[date]!! / norms[date]!!
val position = if (normalizedPastChange < 0) Math.abs(kelly * normalizedPastChange) else 0.0
date to position
}.toSortedMap()
// TURNOVER
var lastPosition = 0.0
var turnover = 0.0
for ((d, p) in positions) {
val norm = norms[d]!!
val realPosition = p / norm
turnover += Math.abs(realPosition — lastPosition)
lastPosition = realPosition
}
// EQUITY RETURNS
val equityLogReturns = positions.mapValues { e ->
val (d, p) = e
p * changesFuture[d]!! / norms[d]!!
}.toSortedMap()
val eq = Util.cumulativeLogChanges(equityLogReturns)
val sharpe = Equity.getSharpe(equityLogReturns, 0.0)
«turnover: %7.4f, sharpe: %7.4f».f(turnover, sharpe).p()
Chart.showCharts(«», eq, 2.0)
}
Комментарии:
Andrew Kartashov: Отличная стратегия. И без добавок. А локальная волатильность … ну если смотреть по дневкам — не знаю. А для внутри дня мне ничего не давала — хватало и грубой прикидки, более точные и локальные — давали только ухудшение.
Так что и на дневках, имхо, будет хуже. Хотя не ручаюсь.
Конв: Код рисеча на Котлине:
fun main(args: Array) {
я правильно понимаю, что главный исполняемый метод программы возвращает fun? )))
mehanizator: fun это обозначение функции. функция main ничего не возвращает.
Салимжан Бижанов: ___Результат 40 за 11 лет это 3.6 в год в среднем, то есть +260% без реинвестирования. Просадки, конечно, будут нечеловеческими, поэтому как это обычно бывает, вряд ли кто решится торговать с «плечами по келли». ___
Александр, подскажи, пожалуйста, почему 260% без реинвестирования?? Как я понял 40 общая доходность — это логарифм доходности? то есть 3,6 в год — это не 260% годовых. нужно обратное преобразование сделать из логарифмической шкалы в обычную?
mehanizator: если с реинвестирование идет торговля, то нужно делать. а если без реинвестирования, то капитал без реинвестирования он как раз и будет равен логарифму капитала с реинвестированием.
Салимжан Бижанов: ещё вопрос: так как каждое значение исходного ряда логарифмов вы нормируете на волатильность, то единица измерения у вас становится именно волатильность. те доходность измеряется теперь не в логарифмах, не в %, а в волатильностях.
тогда почему вы говорите, что доходность 260%? ведь доходность получается 3,6 волатильностей в год? или я что-то не так понял?
mehanizator: здесь она уже переведена обратно из волатильностей в проценты/логарифмы