- PVSM.RU - https://www.pvsm.ru -
Воодушевлённый рекламой структурных продуктов на Хабре [1], адаптировал python-скрипт для их самостоятельного тестирования. Основная идея в том, что подобные продукты предлагают 100% защиту капитала. А учитывая 10 лет бычьего рынка, исторические показатели подобных продуктов одурманивают безрисковым раем.
Данная статья будет интересна начинающим python-программистам, которые интересуются управлением своим капиталом. Ну а кому-то данный инструмент может пригодиться для самостоятельного построения подобных стратегий. Но будьте аккуратны, брокеры пишут, что это не каждому под силу.
Код выложен в GitHub в виде Jupyter-блокнота. Поехали!
Тестировать буду на американских акциях и там доходность будет ниже, чем в рублях. Российский рынок в абсолютных значениях на графиках поинтересней, но и рисков в нём побольше. Суть тестов от этого не меняется.
Данные берём из бесплатного Alpha Advantages, где предварительно нужно получить ключ, поделившись email-адресом. Краткая инструкция в блокноте. Котировки российских бумаг вы можете взять на Финаме.
Кратко, ваш капитал в сохранности, а доходность выше банковского депозита (гособлигаций). Вот только пропущено несколько элементов уравнения:
Рассмотрим самую простую стратегию:
В основе стратегии: казначейские облигации дают 1-3% годовых практически исключая просадку (если доходность есть). 10% от просадки актива, купленного на 10% капитала, как раз будут тем самым риском, который покроют облигации. В периоды бычьего рынка некоторые акции могут вырасти в несколько раз, что и подарит нам счастье.
Для ручного повторения данной стратегии необходимо выполнить следующие действия:
Кратко опишу некоторые решения с выдержками кода, которые позволили сделать тестирование достаточно гибким и удобным.
Производить ребалансировку можно в следующие периоды: неделя, месяц, год. А также в любой день внутри периода: первый, N-ый, последний. За это отвечает класс `Schedule()`:
# датафрейм с индексом из рабочих дней за период
df = pd.DataFrame([], index=pd.date_range(start, end, freq='B'))
# ...
# фильтруем на даты наличия истории цен, при желании
df = df[df.index.isin(dates)].copy()
# ...
# выбираем столбцы группировки
# ...
elif freq == 'week':
groupby = ['year', 'week']
elif freq == 'month':
groupby = ['year', 'month']
elif freq == 'year':
groupby = ['year']
# группировка и пометка дней ребалансировки
grouped = df.groupby(groupby)
for idx, grp in grouped:
if len(grp) >= abs(day):
df.loc[grp.iloc[day].name, 'allow'] = True
StructuredProductMill().run()
Как описано в одной из статей [2], мы можем обходить в цикле только даты ребалансировки и пропустить все остальные дни. Но тогда мы теряем статистику по изменению активов внутри периода, не увидим доходность и просадки за каждый день. Данный скрипт, в ущерб скорости, обходит каждый день, что позволяет видеть рыночную стоимость открытых позиций и применить проверку стоп-приказа.
StructuredProductMill().rebalance()
Здесь активы, которые можно открывать, распределяются на доступный капитал. После сравнения расчёта с открытыми позициями производится исполнение сделок на нужное количество:
# получаем капитал: свободный кэш и рыночную стоимость позиций
balance = self._cash + self.position_balance(day)
# объединяем позиции с текущим днём из истории цен
df = day.merge(self._positions[['quantity']], how='left', left_index=True, right_index=True)
# ...
# объём в процентах от исходной доли в портфеле относительно всего объема доступных активов
day.loc[is_allow, 'size_order'] = day[is_allow]['size'] / day[is_allow]['size'].sum()
# распределяем капитал по активам по цене открытия
day['position_to'] = (balance * day['size_order']) // day['open']
# формируем приказы изменения позиций
day['order'] = day['position_to'] - day['position']
# ...
# исполняем сделки
for symbol, row in day[fltr].iterrows():
self.trade(row['dt'], symbol, row.order, row.open, 'O' if row.order > 0 else 'C')
StructuredProductMill().trade()
И здесь для скорости можно пожертвовать деталями и контролировать только изменение доходности каждой позиции. Но скрипт учитывает комиссии и стоимость активов, а также ведет историю сделок, что позволяет рассчитать транзакции и исполнить стоп-приказ в любой день теста. В этом методе обновляются позиции и размер свободного кэша.
Для запуска необходимо указать набор активов с долями и параметры теста. Мы же будем тестировать структурные продукты за календарный год:
# состав портфеля
portfolio = {'MINT': 0.9, 'AAPL': 0.1,}
# получение цен
SYMBOLS = list(portfolio.keys())
df = prices(SYMBOLS)
params = {
'benchmark': 'SPY', # актив для сравнения доходности
'balance': 100_000, # начальный кэш
'portfolio': portfolio,
'rebalance_day': -1, # ребаланс в последний день периода
'freq': 'year', # ребаланс каждый год
'stop_loss': 0.1, # стоп-приказ в 10%
# обнулять цены открытия позиций при ребалансе для корректных стопов
'reset_position_prices': True,
'allow_method': allow_default,
'start': pd.to_datetime('2011-01-01'), # дата начала
}
# создаем объект, проверяем настройки и готовим данные
pm = StructuredProductMill(params, prices=prices(SYMBOLS + [params['benchmark']]), show_progress=True)
pm.check_params().prepare()
# запускаем тестирование
pm.run()
# показываем результаты
pm.print_results();
# показываем графики
pm.charts()
Внизу блокнота есть графики с доходностью и просадками в даты ребаланса (в конце года), что подтверждает крайне низкие просадки капитала в моменты отчёта и постоянно растущую доходность. Хоть эта доходность и проигрывает широкому индексу американских компаний S&P 500.
В тестах участвовали свободно торгующихся американские инструменты с 2011 года:
Данная конструкция принесла за 8 лет доход в 24% (среднегодовая 2.6%) с просадкой между ребалансировками -6%. Но на стыке лет просадка около нуля. Стопа не коснулись, рынку со 180% дохода порядком проиграли.

Доходность и просадка за каждый день (слева доходность, справа просадка).

Доходность и просадка на стыке лет (слева доходность, справа просадка).
Данная конструкция принесла за 8 лет доход в 26% (среднегодовая 2.75%) с просадкой между ребалансировками -2%. На стыке лет просадка отсутствует.


Данная конструкция принесла за 8 лет доход в 45% (среднегодовая 4.6%) с просадкой между ребалансировками аж -15%. Но всё это в 2013 году, когда Тесла выросла почти в 5 раз. На стыке лет просадка до -2%. Самый беспокойный, но и прибыльный пассажир.


Блокнот позволяет тестировать любые составы портфелей. Это могут быть плечевые фонды или несколько компаний. Хоть вообще без защитного актива.
Репозиторий на GitHub [3].
Автор: Александр Румянцев
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/325271
Ссылки в тексте:
[1] Хабре: https://habr.com/ru/company/iticapital/blog/461421/
[2] статей: https://habr.com/ru/post/419979/
[3] GitHub: https://github.com/iamraa/backtest.structured.product
[4] Источник: https://habr.com/ru/post/461583/?utm_source=habrahabr&utm_medium=rss&utm_campaign=461583
Нажмите здесь для печати.