Объяснение того, как создать эффективные инвестиционные портфели от начала до конца
Одним из основных этапов разработки приложения для управления инвестициями было внедрение комплексного решения, которое начинается с получения данных о ценах на акции компании и создания набора эффективных и оптимальных портфелей с использованием процедур оптимизации.
Эта статья продемонстрирует, как этого добиться.
Пожалуйста, прочтите Заявление об ответственности с объяснениями FinTech. Это приложение основано на моем мнении, и они могут ошибаться. Прежде чем вкладывать деньги, всегда обращайтесь за советом к профессиональному финансовому консультанту. Эта статья призвана объяснить концепцию оптимизации портфеля на абстрактном уровне, и ее не следует рассматривать как инвестиционный совет.
1. Цель статьи
В этой статье будут задокументированы следующие ключевые моменты:
- Теория эффективных границ
- Процесс сквозного решения
- Реализация на Python
- Путь полного кода
- Следующие шаги
2. Теория эффективных границ
Мы довольно часто слышим термин «эффективный рубеж», но что именно он означает? А в чем суть современной теории портфеля?
Бертон Дж. Малкиел прекрасно выразил это:
«Теория дает прочную основу для интуиции, что вы не должны класть все яйца в одну корзину, и показывает инвесторам, как комбинировать ценные бумаги, чтобы минимизировать риск» (Бертон Г. Малкиел).
Основное примечание: никогда не кладите все яйца в одну корзину. Убедитесь, что вы диверсифицируете!
- Предположим, у вас есть наличные в размере 10 000 долларов и вы заинтересованы в их вложении.
- Ваша цель - вложить деньги на год.
- Как и любой рациональный инвестор, вы ожидаете, что окончательная сумма через несколько лет будет выше, чем сумма в 10 000 долларов, которую вы хотите вложить.
- Меньше всего вам хочется потерять 10 000 долларов, на которые вы начали инвестировать.
Пока все хорошо!
Ключевое примечание: я твердо верю в сохранение концентрации, когда мы пытаемся достичь цели. И ключ к достижению этой цели - быстро адаптироваться к меняющейся среде.
Прочтите эту статью, если хотите получить представление об управлении инвестициями:
2.1 Понимание взаимосвязи риска и доходности
Доступно множество вариантов инвестирования, таких как покупка казначейских векселей или акций компании и т. Д. Некоторые из вариантов инвестирования более рискованны, чем другие, потому что они привлекают нас для получения более высокой прибыли.
Следовательно, следует отметить, что существует компромисс между риском и доходностью.
Наша единственная цель как инвестора может заключаться в том, чтобы вложить средства в наименее рискованный вариант инвестирования, который принесет наибольшую прибыль.
Если мы покупаем несколько активов, таких как акции разных компаний, то общий риск портфеля может быть уменьшен за счет диверсификации. Это означает, что инвестор может снизить общий риск и увеличить доход, смешивая активы с разными пропорциями. Это связано с тем, что активы можно соотносить друг с другом, и их объединение может снизить риск. Следовательно, это позволяет нам создавать хорошее портфолио.
Возникает вопрос: что такое хорошее портфолио?
Хороший портфель - это больше, чем длинный список хороших акций и облигаций. Это сбалансированное целое, обеспечивающее инвестору защиту и возможности в отношении широкого спектра непредвиденных обстоятельств. - Гарри Марковиц
Современная теория портфеля лауреата Нобелевской премии Гарри Марковица может быть реализована с помощью процедур оптимизации.
3. 7 шагов эффективного пограничного процесса
Прежде чем я представлю сквозную реализацию решения, я хотел бы выделить здесь одну ключевую техническую заметку. Ядро архитектуры этого приложения вращается вокруг одной концепции:
Когда приложение запускается, оно производит большой объем данных, таких как цены акций, их доходность, ковариационная матрица, портфели и их распределение, а также их риск, доходность и коэффициент Шарпа. Код Python сохраняет все необходимые данные в электронной таблице Excel. Его местонахождение указано в файле настроек.
Нам необходимо выполнить следующие 7 шагов:
Три этапа по семь шагов
- Первый этап (шаги 1–3) - рассчитать дневную доходность наших акций.
- Второй этап - это этап 4, на котором нам нужно оптимизировать распределение, чтобы получить лучший портфель для каждого из наших целевых показателей доходности.
- На последнем этапе используется распределение для расчета риска и доходности и построения портфелей.
4. Запуск внедрения в 3 этапа
В этом разделе будет документирован код Python с объяснением того, как я реализовал решение.
Для поддержки приложения требуются следующие пакеты:
- Панды: для манипулирования данными
- Numpy: для расчетов
- Matplotlib: для построения графиков
- SciPy: для оптимизации
4.1 Первый шаг - получить цены на акции
Мы создали компонент, который извлекает исторические цены на акции:
end_date = settings.get_end_date() start_date = settings.get_start_date(end_date) closing_prices = price_extractor.get_prices(settings.PriceEvent, start_date, end_date) #plot stock prices & save data to a file cp.plot_prices(closing_prices) fr.save_to_file(closing_prices, 'StockPrices')
Курсы акций представляют собой данные временного ряда.
Это снимок данных. Код сохранил данные в листе StockPrices электронной таблицы Excel, как показано ниже.
Как видите, мы не можем сравнивать ZM с SYMC, потому что цены на акции различаются по величине. Следовательно, нам нужно вычислить доходность, чтобы стандартизировать цены на акции.
4.2 Нам необходимо получить прибыль от цен
Нам нужно вычислить их геометрическую отдачу, вычислив следующее уравнение:
Возврат генерируется таким образом, чтобы мы могли стандартизировать цены на акции и сравнивать их.
returns = settings.DailyAssetsReturnsFunction(closing_prices, settings.ReturnType) #plot stock prices & save data to a file cp.plot_returns(returns) fr.save_to_file(returns, 'Returns')
Доходность - это данные временного ряда. Это моментальный снимок данных, которые хранятся на листе «Возврат» электронной таблицы Excel, как показано ниже.
4.3 Ожидаемая средняя доходность активов и матрица ковариации
По сути, это два шага:
expected_returns = settings.AssetsExpectedReturnsFunction(returns) covariance = settings.AssetsCovarianceFunction(returns) #Plot & Save covariance to file cp.plot_correlation_matrix(returns) fr.save_to_file(covariance, 'Covariances')
- Первым шагом является вычисление единственного числа, представляющего доходность активов.
Один из наиболее распространенных способов представления доходности в виде единственного числа - вычисление среднего значения доходности, которое известно как ожидаемая средняя доходность.
Чтобы вычислить ожидаемую среднюю доходность актива, нам понадобится среднее доходности каждой акции, рассчитанное по следующей формуле:
По доходности активов мы можем вычислить доходность портфеля. Активы в портфеле распределены пропорционально общей сумме инвестиций. Например, портфель может содержать 40% активов ABC и 60% активов DEF.
Примечание. Правильный ли выбор - историческая доходность? Должны ли мы вместо этого генерировать другие меры? В будущем я буду реализовывать превосходное приложение, которое продемонстрирует, как вместо этого мы можем реализовать возврат, взвешенный по времени.
2. На втором этапе создается матрица ковариации на основе доходности активов.
Затем код подготавливает ковариационную матрицу:
Ковариационная матрица хранится в таблице ковариаций электронной таблицы Excel, как показано ниже:
Волатильность портфеля - это риск портфеля. Волатильность вычисляется путем вычисления стандартного отклонения доходности каждой акции вместе с ковариацией между каждой парой акций по следующей формуле:
Волатильность в данном случае - это стандартное отклонение, то есть общий риск портфеля.
Стандартное отклонение измеряет разброс значений вокруг среднего.
4.4 Теперь важный момент: моделирование Монте-Карло и линия эффективных портфелей
Давайте сначала сгенерируем более 100000 портфелей с помощью моделирования Монте-Карло. Распределения будут генерироваться случайным образом.
Этот метод основан на теории о том, что, генерируя портфель много раз, мы в конечном итоге достигнем формирования истинно оптимального портфеля. Помните, что здесь генерация портфеля подразумевает случайную генерацию распределения (весов).
Эта статья подробно демонстрирует моделирование Монте-Карло:
Если мы нанесем на график риск и доходность для каждого портфеля, то мы увидим дугообразную линию вверху портфелей.
Эта желтая линия, по сути, указывает на наиболее эффективные портфели. Эта линия известна как эффективная граница.
Граница эффективности - это набор портфелей, которые обеспечивают максимальную доходность при минимально возможном риске.
Любой другой портфель, который не находится на границе эффективности, не так эффективен, потому что он предлагает ту же доходность, что и портфель на границе эффективности, но за счет более высокого риска.
Следовательно, любой другой портфель уступает портфелям на границе эффективности. В результате мы можем буквально игнорировать портфели, которые не находятся на эффективной границе.
Запуск симулятора Монте-Карло:
portfolios_allocations_df = mcs.generate_portfolios(expected_returns, covariance, settings.RiskFreeRate) portfolio_risk_return_ratio_df = portfolios_allocation_mapper.map_to_risk_return_ratios(portfolios_allocations_df)
Портфолио хранятся на листе MonteCarloPortfolios электронной таблицы Excel, как показано ниже:
5. Ключевой шаг - оптимизация портфеля
Теперь давайте создадим оптимальный портфель, применив методы оптимизации.
Мы собираемся использовать пакет SciPy. Модуль scipy.optimize в пакете SciPy предлагает множество алгоритмов оптимизации. Модуль включает неограниченные алгоритмы, процедуры глобальной оптимизации, минимизацию методом наименьших квадратов, скалярные и многомерные минимизаторы и т. Д.
Он предлагает, среди прочего, алгоритмы Ньютона-Крылова, сопряженного градиента Ньютона, SLSQP, подгонки кривой и двойного отжига.
5.1 Функция сворачивания SciPy
В модуле scipy.optimize есть функция минимизации, которая выполняет минимизацию скалярной функции одной или нескольких переменных. Функция минимизации упрощает выполнение требуемого алгоритма для целевой функции.
Сигнатура метода:
scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)[source]
Ключевые аргументы:
- забава: Это целевая функция, которую мы хотим минимизировать. В нашем случае наш портфель состоит из ряда активов. Мы хотим найти такое распределение каждого актива, которое дает нам наименьший риск. Первый параметр функции - это одномерный массив распределений. Мы также можем передать в функцию набор других параметров.
- x0: это первоначальное предположение. Мы можем присвоить значение 1 / количество активов в качестве начального значения x.
- args: это кортеж, который может содержать дополнительные необязательные аргументы. Например, если наша целевая функция риска принимает ковариационную матрицу, мы можем передать ее в кортеже args.
- метод: это строковый аргумент, в который мы можем передать имя алгоритма, например SLSQP.
- ограничения: сюда мы передадим ограничения, например, сумма выделенных ресурсов должна быть равна 1.
- границы: по сути, это минимальная и максимальная пары каждого элемента в x. Например, мы можем указать, что мы не хотим, чтобы ценность распределения была отрицательной, подразумевая, что мы не хотим продавать актив.
5.2 Процедура оптимизации портфеля
Чтобы выполнить минимизацию с ограничениями для многомерных скалярных функций, мы можем использовать функцию минимизации с алгоритмом SLSQP.
SLSQP расшифровывается как «Последовательное программирование по наименьшему количеству SQuares».
def solve(self, x0, constraints, bounds, covariance): return minimize(self.__risk_function, x0, args=(covariance), method='SLSQP', #prints covergence msgs options={'disp': True}, constraints=constraints, bounds=bounds)
5.3 Начальные предположения и границы
Мы указали начальное предположение равнопропорционального портфеля и нижнюю границу, равную 0, вместе с верхней границей, равной 1, для каждого из распределений.
x0 = np.ones(self.__portfolio_size) * (1.0 / self.__portfolio_size) bounds = ((0, 1),) * (self.__portfolio_size)
5.4 Ограничения
Мы также указали ограничения. Идея состоит в том, чтобы минимизировать функцию x с учетом функций ограничений. Ограничения могут быть линейными и нелинейными. Они определены как словари.
Есть два ограничения:
- Добавлены ограничения на распределение, которые гарантируют максимальную отдачу.
- Добавляются ограничения, чтобы гарантировать, что сумма выделений равна 1.
Теперь оптимизатор должен создать портфель с минимальным риском в рамках этих ограничений.
Ключи словаря каждого ограничения включают:
- type: указывает тип ограничений, например, ineq (неравенство) или eq (равенство). Если у вас есть ограничение равенства, например:
A + B = C, тогда он будет представлен как A + B-C
def con():
return A + B - C
Это будет ограничение равенства (type='eq'
), когда вы создаете функцию, которая должна быть равна нулю:
def con(t):
return A + B - C
Если у вас есть ограничение неравенства, например:
A + B ≥ C, тогда он будет представлен как A + B-C
def con():
return A + B - C
Это будет ограничение неравенства (type='ineq'
), когда вы создаете функцию, которая должна быть больше, чем C:
def con(t):
return A + B - C
- забава: функция ограничения, такая как сумма распределения активов в портфеле, должна быть равна 1.
constraints=[] constraints.append({'type': 'eq', 'fun': lambda inputs: 1.0 - np.sum(inputs)}) constraints.append({'type': 'eq', 'args': (returns,), 'fun': lambda allocations, returns: my_return - self.__return_function(returns, allocations)})
5.5 Выполнение оптимизатора портфеля
optimiser = obj_factory.get_optimiser(targets, len(expected_returns.index)) portfolios_allocations_df = optimiser.generate_portfolios(expected_returns, covariance, settings.RiskFreeRate) portfolio_risk_return_ratio_df = portfolios_allocation_mapper.map_to_risk_return_ratios(portfolios_allocations_df) #plot efficient frontiers cp.plot_efficient_frontier(portfolio_risk_return_ratio_df) cp.show_plots() #save data print('7. Saving Data') fr.save_to_file(portfolios_allocations_df, 'OptimisationPortfolios') fr.close()
Мы указали список наших целевых доходов.
def get_my_targets(): return np.arange(0, 1.5, 0.05)
Для каждого дохода оптимизатор запускал процедуру оптимизации и создавал наиболее оптимальные и лучшие портфели, которые известны как граница эффективности.
Для каждой цели было получено наиболее оптимальное портфолио. Массив распределения - это веса (пропорции) активов, которые составляют портфель для каждого нашего целевого дохода
Красная линия выше - это эффективный рубеж. Это оптимизированные целевые портфели.
6. Полный код
Полный код загружен на GitHub.
7. Следующие шаги:
Нам предстоит долгий путь, но мы приближаемся к нему! Следующий набор шагов касается:
- Добавление превосходных показателей для расчета прибыли
- Расширенные метрики риска для расчета риска портфеля
- Реализация перспективной корреляционной матрицы
- Привязка весов к фактической сумме
- Инструмент для извлечения компаний, чтобы найти нам нужные компании и обогатить дополнительную информацию, такую как сектор, объем, рейтинги и т. Д.
- Добавление дополнительных ограничений на сектор, валюту, рейтинги и т. Д.
- Соответствующее распределение паев акций
8. Примечания, которые стоит упомянуть
- Нам нужно учитывать транзакционные издержки.
- Кроме того, мы должны помнить, что прошлое не всегда точно определяет будущее.
- Меры риска и доходности не являются лучшими и имеют много недостатков. Нам необходимо изучить другие меры, такие как средневзвешенная доходность и меры ожидаемого риска дефицита, а также прогнозную ковариационную матрицу.
- Нам нужно приступить к рассмотрению факторных моделей.
9. Резюме
В этой статье задокументированы следующие разделы:
- Теория эффективных границ
- Процесс сквозного решения
- Реализация на Python
- Путь полного кода
- Следующие шаги
Если вас интересуют следующие этапы, дайте мне знать. Кроме того, дайте мне знать, если у вас есть другие идеи.