Автор: Александр Кургузкин (mehanizator).
Неоднократно видел стратегии ловли отскоков на S&P 500 и продуктах, обычно сигналом на покупку считается пробитие N-дневного LOW рынка и закрытие близкое к минимуму дня. Я решил для начала посмотреть более общую картинку — как зависит изменение будущего дня от изменения текущего дня.
Изменением считаем логарифм закрытия к закрытию, оба изменения — прошлое и будущее — нормированы на волатильность, посчитанную в окне 21 день (число торговых дней в месяце). Данные — дневки SPY с 2003 года.
Опасаясь влияния выбросов, я разделил все данные на 20 примерно равных по числу дней групп и усреднил данные внутри каждой группы.
Вот что получилось:
По оси X — изменение за день, нормированное на месячную волатильность. По оси Y — будущее изменение, тоже нормированное. Красная линия — аппроксимация квадратичным полиномом.
Видно, что в левом хвосте получаем матожидание в районе 0.01 от нормировки. Что такое 0.01 по оси Y, если пересчитать в проценты? Волатильность приведена к году, значит в среднем составляет где-то в районе 0.2, то есть 0.01 для Y в процентах будет в среднем 0.2%. Транзакционные издержки на SPY вполне перепрыгиваем.
Для сравнения посчитал то же самое, но прошлые и будущие изменения берутся за 5 дней (торговая неделя). Зависимость еще более выражена:
Если кому вдруг интересно, как это исследование у меня выглядит на языке Kotlin, которым я стал в последнее время активно пользоваться:
fun main(args: Array<String>) {
val spy = Sec(yahooFileName = «spy», since = Date(103, 0, 1))
val normDays = 21
val norms = spy.getNormalization(normDays, NormalizationType.ABS)!!
val changesPast = spy.getPastLogChanges(5)!!
val changesFuture = spy.getFutureLogChanges(5)!!
val xyList = Util.commonDates(norms, changesPast, changesFuture)!!.map { d ->
val norm = norms[d]!!
val past = changesPast[d]!!
val future = changesFuture[d]!!
doubleArray(past / norm, future / norm)
} sort comparator { a, b -> a[0].compareTo(b[0]) }
val sz = xyList.size
val nGroup = 20
val groupSize = sz.toDouble() / nGroup
val groupMap = (0..sz — 1).groupBy { Math.floor(it / groupSize) }.toSortedMap()
val groupedList = groupMap.values().map { indexes ->
val xs = indexes.map { xyList[it][0] }
val ys = indexes.map { xyList[it][1] }
val xMean = xs.statistics().getMean()
val yMean = ys.statistics().getMean()
doubleArray(xMean, yMean)
}
val groupPastChange = groupedList.map { it[0] }
val groupFutureChange = groupedList.map { it[1] }
val extrapolation = Util.fitPolynomial(groupPastChange, groupFutureChange)!!
val extrapolated = groupPastChange.map { extrapolation.value(it) }
Chart.showCharts(«», groupPastChange, groupFutureChange, extrapolated, 5.0)
}
Автор: mehanizator