В этот раз «подкрутим»🔧 стратегию «купи и держи» с помощью скользящих средних на основе этой статьи💡. Там говорится, что при входе выше 200-дневной средней и выходе под ней, мы можем получить аналогичную доходность📈 и сократить просадки📉. Дополнительно появляется возможность припарковать свободный капитал, например, в банк🏦.
Будет приведено несколько алгоритмов:
- пересечение SMA200 и цены;
- пересечение SMA200 и SMA10;
- пересечение SMA200 и SMA50;
- пересечение EMA200 и EMA50;
- пересечение EMA200 и EMA50 плюс покупка облигаций.
Все происходит на платформе Quantopian, о которой я подробнее пишу в этом посте. Период тестирования для всех алгоритмов — с 01.01.2004 до 29.12.2016. Связано это с тем, что у EMA есть нестабильный период, который необходимо пропустить.
«Купи и держи» за выбранный период
Для наглядности размещу здесь результат стратегии «Купи и держи». Можно заметить, что результат хуже бенчмарка на ~3%, что связано с механизмом просадок и комиссий, используемым по умолчанию в платформе. Об этом поговорим в отдельном обзоре.
Внизу видно, что было много транзакций — это связано с реинвестированием дивидендов.
👎Пересечение SMA200 и цены
Справа на картинке не впечатляющие результаты тестирования. Мы входим над 200-дневной скользящей средней и выходим, когда цена опускается под нее. Внизу видно увеличившееся число произведенных транзакций, которые с аппетитом поедали средства на комиссии и проскальзывания. За 50% доходности мы получили сокращение на 22% времени удержания позиции и уменьшение просадки до -22%, что положительно скажется на нервах. Результаты всех измерений доступны в конце статьи, в таблице.
Внизу видно, что транзакции на покупку и продажу сконцентрированы в нескольких местах. Объясняется это тем, что цена очень часто колеблется вокруг 200-дневной средней, являющейся очень сильным уровнем поддержки или сопротивления. Для сокращения колебаний воспользуемся второй короткой средней, SMA(10), и будем входить когда она над длинной, а выходить — наоборот.
Ниже размещен код на Python. Все последующие тесты используют этот же код с минимальными изменениями, которые будут показаны без повторения нетронутых строк.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
import talib # библиотека для технического анализа # функция, выполняемая перед началом тестирования def initialize(context): # сохраняем актив, с которым будем работать context.asset = symbol('SPY') # ставим событие для ребалансировки на открытии рынка schedule_function(rebalance, date_rules.every_day(), time_rules.market_open()) # ежедневная ребалансировка def rebalance(context, data): # текущая цена актива current_price = data.current(context.asset, 'price') # история для расчета средних за последние 500 торговых дней price_hist = data.history(context.asset, 'price', 500, '1d') # средние: длинная и короткая ma_long = talib.SMA(price_hist, timeperiod=200)[-1] #ma_short = talib.SMA(price_hist, timeperiod=10)[-1] # сигнал к открытию или закрытию позиции allow = current_price > ma_long # получаем количество акций в портфеле current_amount = context.portfolio.positions[context.asset].amount # проверяем возможность торговли активом if data.can_trade(context.asset): if allow: # покупаем актив на 100% портфеля, если разрешено order_target_percent(context.asset, 1.00) # закрываем всю позицию, если открыты elif current_amount > 0: order_target(context.asset, 0) |
Для использования этого кода, перейдите на страницу своих алгоритмов, нажмите кнопку «New Algorithm», в названии укажите «Buy and hold SMA200» и нажмите «OK». Скопируйте текущий код и на открывшейся странице вставьте его в левое поле, стерев предварительно все что там было. Для запуска алгоритма нажмите «Build Algorithm».
👎Пересечение SMA200 и SMA10
В этот раз удалось отыграть 20% доходности и сократить просадку до -19%.
В следующем тесте проведем проверку с пересечением SMA(50) и SMA(200). Данное пересечение является фактом долгосрочного бычьего или медвежьего трендов. По этому пересечению многие трейдеры держат консервативную часть своего капитала в индексах широкого рынка, таких как S&P 500 (SPY).
В коде тестирования SMA(200) X SMA(10) были поменяны только три строчки, которые представлены ниже:
1 2 3 4 5 6 7 |
... ma_long = talib.SMA(price_hist, timeperiod=200)[-1] ma_short = talib.SMA(price_hist, timeperiod=10)[-1] # сигнал к открытию или закрытию позиции allow = ma_short > ma_long ... |
👌Пересечение SMA200 и SMA50
Результаты стали значительно лучше. Не хватает только 9% доходности, а на другой чаше весов сокращение времени удержания позиции на 23%, сокращение просадки на 35% и даже сокращение количества сделок.
В следующем тесте попробуем заменить обычные на экспоненциальные скользящие средние. Ведь они быстрее реагируют на изменения цены, что может позволить нам улучшить результаты теста.
Изменения кода SMA(200) X SMA(50):
1 2 3 |
... ma_short = talib.SMA(price_hist, timeperiod=50)[-1] ... |
👌Пересечение EMA200 и EMA50
За последние 12 лет грааль🏆 вроде найден и содержит он простое пересечение EMA(50) и EMA(200). Нам удалось обыграть рынок на 2%, сократить просадку до 18%, сократить количество сделок и сократить время удержания на 18%. И это при учете проскальзываний и комиссий (об этом подробнее в будущих статьях), которые автоматически учитываются на Quantopian.
Изменения кода EMA(200) X EMA(50):
1 2 3 4 |
... ma_long = talib.EMA(price_hist, timeperiod=200)[-1] ma_short = talib.EMA(price_hist, timeperiod=50)[-1] ... |
Помня, что в первый час открытия рынка торговать не рекомендуется из-за повышенной волатильности, мы установим условие, что на рынок будем выходить спустя 1 (один) час после открытия. Данный не хитрый способ позволил нам отыграть еще 4%. Код изменения ниже:
1 2 3 |
... schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1)) ... |
Сейчас мы видим последнюю проблему, которая поражает наш алгоритм — это резкое падение, которое было в августе 2015. Есть несколько возможных решений, например:
- Закрывать позицию при резком падении на определенное кол-во процентов от цены.
- Оставлять допуск на разворот в области пересечения ±2%. То есть закрывать позицию, когда EMA50 на 2% ниже EMA200, а открывать, когда на 2% выше EMA200.
В принципе, улучшать любой алгоритм можно до бесконечности. Попробуйте самостоятельно. На десерт, рассмотрю пример со входом в облигации в момент слабости рынка.
👍Пересечение EMA200 и EMA50, при выходе сидим в TLT
Каждый раз на слабом рынке мы выходили в кэш. А что будет, если при выходе в кэш искать возможность войти в облигации, а именно в ETF на 20-летние облигации TLT? А все будет значительно лучше, что показывают результаты в таблице ниже. Доходность вырастает до 198%, что на 40% выше эталона, а деньги находятся в рынке 94% времени.
Остаются проблемы августа 2015, решая которые можно скорее навредить, подогнав алгоритм под историю, нежели улучшить.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import talib # библиотека для технического анализа # функция, выполняемая перед началом тестирования def initialize(context): # сохраняем актив, с которым будем работать context.asset = symbol('SPY') # добавим облигации context.bond = symbol('TLT') # ставим событие для ребалансировки на открытии рынка schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1)) # ежедневная ребалансировка def rebalance(context, data): # история для расчета средних за последние 500 торговых дней price_hist = data.history(context.asset, 'price', 500, '1d') # средние: длинная и короткая ma_long = talib.EMA(price_hist, timeperiod=200)[-1] ma_short = talib.EMA(price_hist, timeperiod=50)[-1] # сигнал к открытию или закрытию позиции allow = ma_short > ma_long # получаем количество акций в портфеле current_amount = context.portfolio.positions[context.asset].amount # проверяем возможность торговли активом if data.can_trade(context.asset): if allow: # покупаем актив на 100% портфеля, если разрешено order_target_percent(context.asset, 1.00) # закрываем всю позицию, если открыты elif current_amount > 0: order_target(context.asset, 0) # попробуем купить/продать облигации trade_bonds(context, data, asset_allow=allow) def trade_bonds(context, data, asset_allow=False, asset_amount=0): # проверяем возможность торговать облигациями if not data.can_trade(context.bond): return # получаем количество акций в портфеле bond_amount = context.portfolio.positions[context.bond].amount # получаем историю цен price_hist = data.history(context.bond, 'price', 500, '1d') # считаем средние ma_long = talib.EMA(price_hist, timeperiod=100)[-1] ma_short = talib.EMA(price_hist, timeperiod=26)[-1] # проверяем растущий тренд allow = ma_short > ma_long and not asset_allow if allow: order_target_percent(context.bond, 1.) elif bond_amount > 0: order_target(context.bond, 0) |
🥇Результаты
Стратегия | Время торгов | Кол-во сделок | Срок в позиции | Доходность | Просадки |
---|---|---|---|---|---|
Купи-Держи | Open | 53/0 | 99% | 160.1% | -54.9% |
SMA200/ Цена | Open | 91/47 | 77.5% | 113.9% | -22% |
SMA200/ SMA10 | Open | 56/16 | 78% | 129.1% | -19.1% |
SMA200/ SMA50 | Open | 47/7 | 77% | 151.1% | -19.9% |
EMA200/ EMA50 | Open | 46/4 | 82.5% | 162.1% | -18.2% |
EMA200/ EMA50 | Open+1 | 48/4 | 82.5% | 165.9% | -18.3% |
EMA200/ EMA50/ TLT | Open+1 | 71/12 | 94% | 198% | -20.8% |
- Время торгов — время, когда алгоритм начинает торговать: Open — открытие рынка, Open+1 — через 1 час после открытия рынка.
- Кол-во сделок — количество сделок на покупку и продажу. В поле указаны Покупки/Продажи. Реинвестиция дивидендов считается отдельными сделками.
- Срок в позиции — количество дней в позиции. В первый день мы начинаем закупаться, отсюда 99%.
🏁Заключение
Исследование показывает, что стратегия «Купи и держи» с легкостью может быть улучшена обычными скользящими средними. При этом не надо ни ума, ни активного робота. Достаточно заглядывать на графики раз в неделю и уже только это позволит иметь приличные результаты по доходности. В следующий раз я опишу стратегии при использовании индикатора MACD.
💬Напишите в комментариях, что можно улучшить в данных алгоритмах. Или предложите, какие стратегии стоит рассмотреть в будущем.
Автор Quantrum.me
Telegram-канал📣: @quantiki
Подбор и тестирование портфелей. Подключение стратегий к IB.