Проанализируем динамику акций аутсайдеров и лидеров
Недавно прочитал статью о том, что акции-аутсайдеры (те, что максимально упали в цене за месяц) индекса Мосбиржи имеют бОльшие перспективы роста, нежели в среднем по индексу.
В данном исследовании рассмотрю
- динамику акций аутсайдеров и лидеров роста (период 30 дней)
- Имеет ли смысл покупать на просадках и играть на понижение после значительного роста
Важно! Я буду рассматривать абсолютно каждый день по всем акциям, соответственно многие просадки будут дивидендными гэпами (идея — рассмотреть все отклонения).
Буду рассматривать акции индекса Мосбиржы (только акции с долей в индексе >0,5%), а именно:
- Газпром
- Лукойл
- Сбербанк
- ГМК Норильский никель
- НОВАТЭК
- Магнит
- Роснефть
- Татнефть
- МТС
- ВТБ
- Сургутнефтегаз
- АЛРОСА
- Сургутнефтегаз п
- Московская Биржа
- НЛМК
- Северсталь
- ЯНДЕКС
- Polymetal International
- Сбербанк России П
- ИНТЕР РАО
- РУСАЛ
- Полюс
- Транснефть п
- РусГидро
- ФосАгро
- ММК
- Аэрофлот
- МегаФон
- Татнефть п
- Ростелеком
Период — 2018-2019 года.
Получение котировок акций
Для начала необходимо получить котировки акций за последние два года. Я скачал их с сайта Финам и просто импортировал csv.
Начнем собирать котировки внутри нашего файла
Импортируем Пандас:
import pandas as pd
Уберем ограничения на отображения в окне (мне необходимо это делать в PyCharm):
pd.set_option('display.max_columns', None)
pd.set_option('display.expand_frame_repr', False)
pd.set_option('max_colwidth', 80)
pd.set_option('max_rows', 60000)
Нужно подобным образом импортировать каждую акцию:
print('GAZP')
GAZP = pd.read_csv("GAZP_180101_191231.csv",sep=';', header=0, index_col='<DATE>', parse_dates=True)
GAZP = GAZP.sort_values(by='<DATE>')
# Добавление столбца изменения с использованием шифтинга
GAZP['30_days_growth']=((GAZP['<CLOSE>']/GAZP['<CLOSE>'].shift(30))-1)*100
GAZP['after_30_days_growth']=((GAZP['<CLOSE>'].shift(-30)/GAZP['<CLOSE>'])-1)*100
GAZP['ticket']='GAZP'
print(GAZP)
- Выводим название акции
- Читаем ее из файла
- Сортируем по дате
- Добавляем столбец с 30 дневным ростом акции в процентах
- Добавляем столбец с изменением стоимости в процентах за следующие 30 дней
- Добавляем столбец с тикетом. Это позволит объединять Датафреймы по столбцу с датой
Просмотр статистики по отдельным акциям
Сейчас посмотрим на отдельные акции. Важно смотреть на акции отдельно так как волатильность может совершенно иначе вести себя в разных случаях (например, просадки по Детскому Миру и Норникелю выкупают быстро, а нефтянку в сложные времена могут вообще игнорировать)
На скринах смотрите именно на столбцы 30_days_growth (изменение в процентах за последние 30 дней) и after_30_days_growth (сколько акция будет стоить спустя 30 дней)
На примере Газпрома посмотрим что произойдет с акциями после 30-дневного снижения больше 10%.
Выведем Газпром с сортировкой по 30-дневным изменениям
print(GAZP.sort_values(by='30_days_growth'))
В данном случае мы видим, что в течение месяца акции будут расти ( В столбце с 30-дневным ростом в основном положительные значения).
А вот с другого конца все не так очевидно, так как новая дивидендная политика все “поломала”.
Откинем 2019 год, чтобы сделать выводы. Для этого выведу только первые 255 строк:
print(GAZP[:254].sort_values(by='30_days_growth'))
Получается, что при росте больше 10% есть смысл продавать
Посмотрим на Лукойл
Тут все уже не так очевидно:
Посмотрим финансовую сферу
Сбербанк
Сильные просадки как правило выкупаются. Но тут волатильность ого-го
Интереснее всего было посмотреть на то, как выкупают просадки у Норникеля
Почти любая просадка Норникеля в последние два года — хорошая инвестиция :)
Вычисление средней доходности за 30 дней
Посмотрим какую в среднем доходность получим за месяц, если войдем в просадку ниже -5%
Создадим переменную с этими строками:
GMKN5 = GMKN[GMKN['30_days_growth']<-5]
print(GMKN5)
Выведем среднее:
print( GMKN5['after_30_days_growth'].mean())
Получим доходность за 30 дней 6.935553432942371%
Также посмотрим как прирастает стоимость за 14 дней
Добавим столбец:
GMKN['after_14_days_growth']=((GMKN['<CLOSE>'].shift(-14)/GMKN['<CLOSE>'])-1)*100
Посмотрим средний прирост за 14 дней:
print(GMKN5['after_14_days_growth'].mean())
Это 4.125%
Вычисление доходности всех акций с просадкой больше 15%
Теперь рассчитаем среднюю доходность за 30 дней по всех акциям, где просадка больше 15%
Нужно объединить все в один Датафрейм.
Для этого создадим лист со всеми акциями:
all_tickets = [GAZP,LKOH,SBER,SBERP,NVTK,MOEX,MGNT,MFON,MTSS,MAGN,GMKN,AFLT,POLY,ROSN,HYDR,RTKM,RUAL,YNDX,ALRS,VTBR,TATN,TATNP,SNGS,SNGSP,PLZL,NLMK,CHMF,IRAO,TRNFP,PHOR]
Объединим:
all_stocks = pd.concat(all_tickets)
Создадим Датафрейм в котором будут только строки с просадками больше 15%:
tickets15 = all_stocks[all_stocks['30_days_growth']<-15]
print(tickets15)
Посчитаем среднюю доходность:
print(tickets15['after_30_days_growth'].mean())
Получим 7.78570670526497%
Довольно неплохо учитывая такой уровень диверсификации.
Аналогично посчитаем доходность 10% просадок
tickets10 = all_stocks[all_stocks['30_days_growth']<-10]
print(tickets10)
print(tickets10['after_30_days_growth'].mean())
Получим 3,1%
А стоит ли играть на понижение акций, которые выросли за 30 дней на 15%+
tickets15plus = all_stocks[all_stocks['30_days_growth']>15]
print(tickets15plus)
print(tickets15plus['after_30_days_growth'].mean())
Получим рост в 2%. Получается что так мы не сможем получить выгоду, но тут видно, что потенциал в этом случае меньше. Но это и не удивительно.
Цифры, конечно, интересные, но стоит учесть, что это в большей степени бенчмарк, так как эту доходность мы получим в случае покупки всех просадок каждый день, пока они находятся в необходимом диапазоне.
Требуется убрать строки, с близкими датами, чтобы не было дублирования одной и той же просадки, но это уже другая история.
Также следует учитывать причины всех отклонений. Очевидно, что, если Газпром вводит новую дивполитику, ждать падений в ближайшее время не стоит.
Автор: Lubiviy_Alexander