В прошлой статье я писал про свои ML-модели для оценки отдельных компаний, но вопрос формирования итогового портфеля совсем не затрагивал. В этом посте хочу рассказать о том, как я собираю свой личный портфель, а так же поделиться сайтом, на котором реализую весь описанный в статье функционал http://stocks.ml. Дисклеймер: у автора нет экономического образования и все выводы и суждения в статье делаются на основе житейского опыта и здравого смысла.

Подготовка

Прежде всего хочется сказать, что у меня нет цели зарабатывать на спекуляциях и краткосрочных сделках, я лишь хочу распределять свои деньги по активам, понимая при этом, какие есть риски и перспективы, чего вообще можно ожидать от собранного портфеля с течением времени. В данной статье я хочу рассмотреть часть данного распределения, затрагивающую только акции американских компаний.

Backtesting

Обычно, когда хотят оценить стратегию, то смотрят, как бы она себя вела в прошлом, т.е. на исторических данных, такую процедуру называют бэктестом. Для своих инвестиционных стратегий я долгое время не хотел проводить бэктест, а действовал вслепую, основываясь исключительно на логичности этих самых действий. Дело в том, что грамотно построить такую историческую валидацию - это отдельная сложная задача с кучей подводных камней, легко допустить разные виды переобучений и ликов. В случае неправильно построенного бэктеста можно на истории получить супер-крутые результаты, но в реальности ничего работать не будет. Кроме того, может возникнуть желание начать перебирать различные варианты и параметры, что уже неизбежно приведет к оверфиту под исторические данные. Тем не менее, действовать вслепую - это уж слишком, поэтому я решил все-таки организовать валидацию стратегий, но выбрал заранее небольшое их количество и договорился сам с собой совсем не подстраивать никакие параметры :yearh-sure.

Хорошо, решено проводить тестирование стратегий, какие для этого есть варианты? Например, написать свой код, который будет брать набор всех сделок и считать, сколько такая стратегия бы заработала. Вроде не сложно, но есть готовые решения, имеющие более широкий функционал, который можно впоследствии легко подключать (учет комиссий, проскальзываний и т.п.). Я остановился на библиотеке https://github.com/mementum/backtrader. В ней реализованы в том числе и методы для балансировки портфеля согласно заданным долям (все мои стратегии будут основаны именно на этом принципе).

Метрики

Казалось бы, вот проверили, что если за последние несколько лет одна стратегия зарабатывала, например, по 5% годовых, а другая 30%, то вопрос выбора очевиден. Но на самом деле это совсем не так и нужно учитывать гораздо больше факторов, чем только Profit and Loss. Например, рассмотрим стратегию, в которой на годовом промежутке с вероятностью 10% инвестор удваивает свой капитал, а с вероятностью 90% теряет все. Пусть на бэктесте как раз и реализовался удачный случай, тогда мы увидим, что PnL 100%. Но значит ли, что такая стратегия хорошая? Конечно, нет. Поэтому есть целый ряд метрик, которые полезно измерять для понимания того, как ведёт себя стратегия. Ниже приведены те, которые я решил использовать для оценки своих стратегий:

  • P&L. Собственно, сама доходность.

  • Sharpe ratio. Идея данной метрики в том, что хорошо, когда доходности высокие и стабильные (низкая дисперсия) и плохо, когда в один день мы можем получить +10%, а в следующей день -20%. Рассмотрим некоторые промежутки времени и результаты, которые давала стратегия на данных промежутках. Например, будем считать, сколько она зарабатывала/теряла каждый день. Получится ряд из дневных доходностей (может содержать как положительные, так и отрицательные числа). Далее берется среднее значение ряда (за вычетом безрисковой доходности, но это не так важно) и делится на его стандартное отклонение.

    \frac{\mathop{\mathbb{E}(R-R_f)}}{\sigma}

    Чем выше данный показатель - тем лучше.

  • Max drawdown. Метрика показывает, насколько велика была максимальная просадка за рассматриваемый промежуток времени. Понятно, что чем меньше - тем лучше. Никому не понравится, если его порфтель в какой-то момент может упасть на 90%.

  • Alpha Beta. Идея состоит в том, что рынок (можно рассматривать, например, индекс S&P500) сам по себе иногда растет, иногда падает. И показанная стратегией хорошая доходность может быть связана только с тем, что рынок в среднем тоже рос в данный промежуток времени. Поэтому хорошо было бы связать рост (а может быть и падение) стратегии с ростом рынка. Для этого строят линейную регрессию, которая по доходностям рынка предсказывает доходности стратегии. В нашем случае линейная модель - это по сути просто подобранные коэффициенты alpha и beta, которые по заданной на вход доходности рынка R_m предсказывают доходность стратегии R_s за этот же промежуток:

    R_s = \beta * R_m + \alpha

    В качестве R_s и R_m можно брать, например, дневные доходности. Такие пары точек (R_m, R_s) можно нанести на график и построить соответствующую коэффициентам alpha и beta предсказывающую прямую.

    На данном примере beta приблизительно равна единице, то есть если в какой-то день доходность рынка составила 0.05, то и доходность стратегии тоже дает около 0.05. Таким образом, коэффициент beta можно интерпретировать как то, насколько агрессивно по отношению к рынку ведет себя стратегия. Если beta больше единицы, то на каждое изменение рынка стратегия в среднем показывает ещё большее изменение. Причем это работает не только для доходов, но и для убытков. Поэтому для своих стратегий мне бы не хотелось иметь слишком высокий коэффициент beta.
    Alpha же - это константная добавка, поэтому её можно интерпретировать как раз как дополнительную доходность стратегии по отношению к рынку. Поэтому чем больше alpha - тем лучше. Подробнее про данную метрику можно почитать, например, в статье.

Benchmark

Прежде чем тестировать свои подходы, хорошо бы запустить некоторые базовые варианты и посмотреть на их результаты. Так как в своих стратегиях я буду использовать только акции, то справедливо сравнивать с портфелем, состоящим тоже только из акций, например, с индексом S&P500 (500 самых крупных компаний американского рынка, можно отслеживать динамику, например, по тикеру SPY). Тем не менне, для интереса приведу так же и другой популярный вариант - взвешенный портфель, состоящий из 60% акций в виде индекса S&P500 и 40% облигаций в виде TLT.

По-хорошему, для бэктеста таких инвест.стратегий нужно использовать большие промежутки времени, чтобы затронуть все виды рынка (растущий, падающий), задеть финансовые кризисы, застои и прочие нехорошие штуки. Но все мои модели основаны на фундаментальных данных, которых у меня в наличии имеется только на 12 кварталов назад. Поэтому все бэктесты проводились начиная с 2019 и заканчивая 2021 годом. Как говорится, имеем то, что имеем. Тем не менее, несмотря на то, что данный промежуток довольно короткий, на нём присутствует сильное падение в марте 2020 года, и будет интересно посмотреть, как стратегии будут вести себя на нем. Сразу приведу графики бэнчмарков и соответствующие им метрики:

Стратегия

Среднегодовая доходность, %

Sharpe ratio

Макс. просадка, %

Beta

Alpha

SPY

23.34

2.004

34.05

1.0

0.0

60% SPY + 40% TLT

16.22

1.31

17.93

0.411

0.0002

Как видно, у S&P500 доходность выше, но в смешанном портфеле максимальная просадка в два раза меньше, и beta всего 0.411. Это говорит о том, что смешанный портфель менее рискованный и может быть хорошим выбором для многих. Тем не менее, как я уже писал выше, мои стратегии отбирают только акции, поэтому для меня интереснее смотреть на бэнчмарк чисто с S&P500 (SPY). У него довольно высокая доходность на данном промежутке, но и просадка целых 34%. Для себя я бы не приемлил сильно большей просадки, поэтому все неподходящие по этому параметру стретегии будут отметаться.

Мои стратегии

В этом разделе я бы хотел описать некоторые принципы и основы того, как будет собираться мой портфель и формироваться итоговые стратегии.

Диверсификация

Вроде бы ясная и очевидная мысль, что не нужно держать яйца в одной корзине, а распределять по нескольким. При этом под корзинами можно понимать разные рынки (американский, российский и т.д.), разные инструменты (акции, облигации, металлы и т.д) и многое другое. Я же хочу рассмотреть только диверсификацию в рамках акций американских компаний. Основная идея в том, что портфель должен содержать большое количество компаний, и, в случае падения одних, убыток будет компенсироваться за счет роста других. При этом важно, чтобы компании эти были более-менее разные и не были подвержены одним и тем же рискам. Например, если портфель состоит только из акций компаний, добывающих нефть, и в один момент происходит революционное открытие нового ультимативного источника энергии, то такая диверсификация, понятное дело, не спасет портфель от сильного падения.

Для себя я решил, что будет достаточным распределить активы по рыночным секторам. А именно, существует 11 групп, на которые условно делятся все компании (технологические сырьевые, потребительские и т.д. Отсортировать по этим группам можно, например, в моём скринере http://stocks.ml/screener). При этом доля некоторых секторов на рынке (суммарные капиталы, которые обращаются в этом секторе) больше, а некоторых - меньше. Соответственно, и долю в портфеле у разных секторов логично держать разной. В индексе S&P500 (набор из 500 самых крупных американских компаний), например, доля каждой компании пропорциональна её рыночной капитализации. То есть, если Apple стоит 2 трлн $, а Facebook 1 трлн $, то доля первого в индексе будет в два раза выше. А, значит, и сектора будут тоже сбалансированы пропорционально общей капитализации. По доступным данным нарисовал такое распределение долей секторов:

Как видно, технологический сектор все ещё самый большой, но теперь он сопоставим с остальными. Или, например, Real Estate - как был небольшим, так и остался. В целом, думаю, что конкретные доли не так важны в моих стратегиях, главное, чтобы присутствовали все сектора.

Ml-модели

Если коротко, то я делаю инструменты для оценки компаний с использованием машинного обучения и, соответсвенно, хочу, чтобы они использовались в стратегиях и формировании портфеля. Примеры того, что умеют делать (с некоторой точность, разумеется) эти инструменты:

  • определять "честную" стоимость компании

  • по финансовому отчету говорить хороший он или плохой (и насколько)

  • предсказывать будущий риск для компании

С момента написания прошлой статьи решил оформлять свои поделия в библиотеку для более удобного использования. Кроме того, в качестве источника данных добавил Yahoo Finance, поэтому для первого ознакомления отпала необходимость покупки подписки на платные данные. Теперь, чтобы запустить, например, пайплайн для оценки "честной" капитализации компании, достаточно установить библиотеку и вызвать соответствующую модель:

from ml_investment.applications.fair_marketcap_yahoo import FairMarketcapYahoo

fair_marketcap_yahoo = FairMarketcapYahoo()
fair_marketcap_yahoo.execute(['AAPL', 'FB', 'MSFT'])

Данные и предобученная модель скачаются при этом автоматически, и в качестве результата будет выдана таблица:

ticker

date

fair_marketcap_yahoo

AAPL

2020-12-31

5.173328e+11

FB

2020-12-31

8.442045e+11

MSFT

2020-12-31

4.501329e+11

Из таблицы видно, что, например, согласно модели, компания Apple должна стоить 517 миллиардов $, в то время как реально она стоит в 4 раза дороже.

Итак, в библиотеке/на сайте есть зоопарк моделей (модуль ml_investment.applications), описания к ним можно посмотреть в документации (или аналогично на сайте). Каждая модель выдает какую-то характеристику компании. Теперь возникает логичная задача: как скомпоновать результаты моделей и выбрать, что же конкретно покупать и в каких количествах?

Стратегия 1

Итак, самая базовая идея - взять модель, предсказывающую честную стоимость компании и считать отношение честной стоимости к реальной (рыночной):

fm\_m\_ratio = \frac{fair\_marketcap}{real\_marketcap}

И, если это отношение высокое (желательно выше единицы), значит компания недооценена и её нужно покупать.

Чтобы совместить эту логику с идеей диверсификации, было решено брать топ5 компаний из каждого сектора и задавать им веса пропорционально этому отношению fm_m_ratio. Например, рассмотрим технологический сектор:

Ticker

fm_m_ratio

Доля в портфеле

HEAR

2.947

0.049

NTNX

2.143

0.035

IIVI

2.012

0.033

XRX

1.954

0.032

IAC

1.876

0.031

Из раздела диверсификация, доля данного сектора в порфтеле составляет 18%. Для компании с тикером HEAR отношение fm_m_ratio равно 2.947, значит итоговая доля компании в портфеле будет составлять:

part_{HEAR} = 0.18 * \frac{2.947}{2.947 + 2.143 + 2.012 + 1.954 + 1.876} = 0.049

То есть почти 5%, что неплохо для самой переспективной (по данному отношению) компании самого большого сектора. При этом было решено, что все строки, которые имеют отношение fm_m_ratio больше 3 - выбрасываются, так как это явно какие-то ошибки/выбросы, а не фундаментально хорошие компании. Описанная процедура была проделана для всех секторов. То же самое читатель может осуществить, используя мой скринер. Скринер позволяет выполнять группировку по сектору и сортировку по показателям (не только по fm_m_ratio, но и по другим величинам, описанным в следующих стратегиях).

Таким образом, по построению мы имеем 55 компаний с долями, в сумме дающими единицу, то есть полный портфель. Ребалансировка для такого порфтеля проводилась каждые 30 дней. А именно, в начальный момент пропорционально заданным долям были куплены некоторые компании. Далее, в течение месяца выходили новые отчеты, а, значит, менялся топ5 (и, соответственно, fm_m_ratio) для каждого сектора. И, в связи с этим, осуществлялась покупка акций согласно новым долям (часть компаний могли остаться, часть - выпасть, для части веса поменялись, для части - остались). Ниже представлена динамика стратегии и соответствующие ей метрики:

Стратегия

Среднегодовая доходность, %

Sharpe ratio

Макс. просадка, %

Beta

Alpha

SPY

23.34

2.004

34.05

1.0

0.0

fm_m_ratio portfolio

26.71

2.41

41.39

1.06

0.0001

Несмотря на то, что среднегодовая доходность у портфеля получилась выше, чем у S&P500 (при не очень высокой бэте), большую часть времени он отставал от индекса, а максимальная просадка составила вообще 41.39%, что совсем не допустимо. Вероятно, проблема данного портфеля в том, что модель определения честной стоимости компании не учитывает локальные хайпы и тренды (в такой стратегии акции Теслы, например, никогда бы не купились, хотя в индексе S&P500 их доля высока, а значит и рост компании сильно влиял на индекс).

Стратегия 2

Данная стратегия была разработана ещё до запуска бэктеста для первой стратегии и как раз пыталась избавиться от описанного выше недостатка. А именно, часть компаний всё время переоценена в виду повышенного интереса инвесторов, а непопадание их в портфель может привести к упущению выгоды. Поэтому возникла следующая идея. Да, пусть компания переоценена, но мы знаем, что она переоценена всегда. В случае, если на текущий квартал эта переоцененность меньше, чем обычно, компанию можно взять в портфель в надежде, что скоро она вернется к своей обычной переоцененности. Для этого было решено считать отношение текущего коэффициента fm_m_ratio к его среднему значению за последние 10 кварталов:

fm\_m\_ratio\_rel = \frac{fm\_m\_ratio}{mean(fm\_m\_ratio)}

Аналогично, если fm_m_ratio_rel больше единицы, значит сейчас компания наиболее недооценена, чем это бывает обычно и её стоит добавить в портфель. При этом значение fm_m_ratio может быть как меньше единицы (фундмаентально переоцененная компания), так и больше единицы (фундмаентально недооцененная компания). То есть данная стратегия смотрит только на относительное значение переоцененности и в один из кварталов вполне может купить даже Теслу.

Сама реализация стратегии ничем не отличается от предыдущей: берется топ5 по fm_m_ratio_rel для каждого сектора и взвешивается пропорционально коэффициенту (можно проделать то же самое, используя скринер). Перевзвешивание тоже осуществляется раз в 30 дней. Посмотрим на график и метрики:

Стратегия

Среднегодовая доходность, %

Sharpe ratio

Макс. просадка, %

Beta

Alpha

SPY

23.34

2.004

34.05

1.0

0.0

fm_m_ratio_rel portfolio

30.09

3.634

37.59

0.98

0.0002

Как видно, график ведет себя почти так же, как и индекс, и только в ноябре 2020 года совершает сильный обгон, вследствие чего доходность стратегии становится выше, чем у индекса. Если обратить внимание, то и у первой стратегии наблюдается скачок в этом же месте. Один знакомый попробовал объяснить мне это следующим образом: к ноябрю все крупные и известные компании уже были куплены в достаточных количествах, а деньги у инвесторов остались, поэтому пришлось переключаться и покупать компании второго эшелона. А мои стратегии как раз в основном и отбираеют всякий мусор не очень известные акции. По остальным метрикам - несмотря на бэту меньше единицы, максимальное падение у стратегии 37.59, что хуже показателя S&P500. Тем не менее, считаю, что данная стратегия может иметь место. Более того, до момента проведения бэктестов я полноценно запустил её в своём порфтеле https://www.tinkoff.ru/invest/social/profile/fattakhov_artur?utm_source=share. Но динамику уже не посмотреть, потому что она проработала недолго и сейчас там реализуется последняя, третяя стратегия.

Стратегия 3

Как я писал в предыдущей статье, среди моих моделек присутствуют те, что смотрят на текущий квартальный отчет, сверяют его с предыдущим (и с отчетом год назад тоже) и говорят, насколько согласно таким изменениям фундаментальных показателей (выручки, прибыли, маржинальности и т.д.) должна была измениться стоимость комапнии. Пример модельки на бесплатных данных:

from ml_investment.applications.fair_marketcap_diff_yahoo import FairMarketcapDiffYahoo

diff_model = FairMarketcapDiffYahoo()
diff_model.execute(['AAPL', 'FB', 'MSFT'])

ticker

date

fair_marketcap_diff_yahoo

AAPL

2021-03-27

0.074486

FB

2021-03-31

0.047032

MSFT

2021-03-31

0.081824

Итак, например, для компании Apple модель выдала, что при фундаментальных показателях текущего квартала капитализация должна была вырасти на 7%. Значит мы можем посчитать её значение, умножив капитализацию в предыдущим квартале на 1.07. Получится некоторая другая "честная капитализация" (fair_marketcap_via_diff), которую можно использовать аналогично тому, как это делалось в стратегии 1.

А именно, считаем отношение fair_marketcap_via_diff к реальной рыночной капитализации и отбираем топ5 из каждого сектора:

fmd\_m\_ratio = \frac{fair\_marketcap\_via\_diff}{marketcap}

Итак, идея данной стратегии в чем-то перекликается с предудущей: отбираемые в портфель компании могут быть фундаментально переоценены, но, как правило, переоцененность сохраняется длительное время. Модель же смотрит на локальный промежуток и говорит: да, в этом квартале компания хорошо поработала, нарастила выручку и прибыль, а, значит, должна стоить на столько-то больше, чем в предыдущем квартале. И если реальная рыночная капитализация меньше этого значения - стоит добавить акцию в портфель. Посмотрим на динамику третьей стратегии и её метрики:

Стратегия

Среднегодовая доходность, %

Sharpe ratio

Макс. просадка, %

Beta

Alpha

SPY

23.34

2.004

34.05

1.0

0.0

fmd_m_ratio portfolio

45.41

2.110

36.54

1.038

0.0007

Видна какая-то сумасшедшая доходность в 45%. Визульно кажется, что это из-за того, что график просто сам по себе сильнее скачет по сравнению с S&P500, но при этом бэта не сильно отличается от единицы, что говорит об обратном, да и максимальная просадка почти такая же, как и у индекса. Более того, это единственная стратегия, у которой коэффициент альфа хоть как-то отличен от нуля (целых 0.07%!). Все данные обстоятельства говорят об одном - где-то ошибка)). Я долго проверял и перепроверял, но явного лика или чего-то подобного не нашел, поэтому прошу знающих людей сыграть в угадайку и подсказать, где я дурак какие моменты не были учтены в анализе.

Решил всё-таки немного пошатать параметры стратегии и посмотреть что получится. А именно, совершать ребалансировку не каждые 30 дней, а, например, каждые 14, 7, 1 день. Логично предположить, что чем чаще производится ребалансировка, тем точнее доли в каждый момент времени, а значит и лучше результат. Графики и метрики после проделанных экспериментов:

Стратегия

Среднегодовая доходность, %

Sharpe ratio

Макс. просадка, %

Beta

Alpha

SPY

23.34

2.004

34.05

1.0

0.0

30d

45.41

2.110

36.54

1.038

0.0007

14d

50.44

1.690

35.91

1.063

0.0008

7d

60.45

1.884

35.87

1.082

0.0010

1d

62.58

1.923

36.66

1.092

0.0011

Как видно, метрики с уменьшением интервала ребалансировки становятся еще более дикими, а про среднегодовую доходность в 62% лучше в приличном обществе вообще не заикаться. Тем не менее, у меня был план запустить именно эту стратегию в качестве основной, а, учитывая, что бэта не сильно отличается от единицы, а максимальная просадка и sharpe почти такие же, как у индекса, то нельзя сказать, что я как-то экстремально рискую. Поэтому было решено написать код для автоматической отправки ордеров (каждый день руками покупать по 55 компаний - сомнительное удовольствие) и запустить алгоритм в работу на своих деньгах. Последить за перформансом моего портфеля в реальном времени можно в Тинькофф Пульсе. Надеюсь, что статья окажется кому-то полезной, а так же рассчитываю на конструктивную критику, спасибо за внимание.