Portfolio Optimization with Python and PyPortfolioOpt

Markowitz portfolio theory

The Markowitz portfolio theory (hereinafter referred to as PTM) (Modern portfolio theory) is a methodology for the formation of an investment portfolio, developed by Harry Markowitz, aimed at the optimal choice of assets, based on the required return / risk ratio. The ideas he formulated in the 1950s form the basis of modern portfolio theory.





The main provisions of the portfolio theory were formulated by Garry Markowitz during the preparation of his doctoral dissertation in 1950-1951.





The birth of Markowitz's portfolio theory is considered the article “Portfolio selection” published in the Financial Journal in 1952. In it, he first proposed a mathematical model for the formation of an optimal portfolio and presented methods for constructing portfolios under certain conditions. The main merit of Markowitz was in proposing a probabilistic formalization of the concepts of "profitability" and "risk", which made it possible to translate the problem of choosing the optimal portfolio into a formal mathematical language. It should be noted that during the years of the creation of the theory, Markowitz worked at RAND Corp., together with one of the founders of linear and nonlinear optimization, George Danzig, and he himself participated in solving these problems. Therefore, my own theory, after the necessary formalization, fell well into the indicated channel.





Markowitz is constantly improving his theory and in 1959 publishes the first dedicated monograph "Portfolio Selection: Effective Investment Diversification".





Model base

1.  Portfolio Expected Return





The expected return on the portfolio will depend on the expected return on each of the assets included in it. This approach allows you to reduce risk through diversification and at the same time maximize the investor's income, since losses on one investment will be offset by income on others.





The expected return on a portfolio is the total expected return on its constituent securities, weighted by their share in the portfolio.





E (R_ {p}) = \ sum_ {i = 1} ^ nw_ {i} E (R_ {i})

2. Portfolio Variance





— , , . , , , , .





\ sigma_ {p} ^ {2} = \ sum_ {i} ^ {} \ omega_ {i} ^ {2} \ sigma_ {i} ^ {2} + \ sum_ {i} ^ {} \ sum_ {j \ neq i} ^ {} \ omega_ {i} ^ {} \ omega_ {j} ^ {} \ sigma_ {i} ^ {} \ sigma_ {j} ^ {} \ rho_ {ij}

3. (Sharpe Ratio)





\ frac {R_ {p} - R_ {f}} {\ sigma_ {p}}

4.  (The Efficient Frontier)





:





(. Efficient frontier) — , . , , , . 1952 .





«», ( ). , , . () , .





, , , ( ) . , .





Python





:





import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pandas_datareader as web
from matplotlib.ticker import FuncFormatter
      
      



PyPortfolioOpt. , .





!pip install PyPortfolioOpt
      
      



:





from pypfopt.efficient_frontier import EfficientFrontier 
from pypfopt import risk_models 
from pypfopt import expected_returns
from pypfopt.cla import CLA
import pypfopt.plotting as pplt
from matplotlib.ticker import FuncFormatter
      
      







, . yahoo.





, — .





nullin_df = pd.DataFrame(df_stocks,columns=tickers)
print(nullin_df.isnull().sum())
      
      







. .





# 
mu = expected_returns.mean_historical_return(df_stocks) 
# 
Sigma = risk_models.sample_cov(df_stocks)
#  
ef = EfficientFrontier(mu, Sigma, weight_bounds=(0,1)) #weight bounds in negative allows shorting of stocks
sharpe_pfolio=ef.max_sharpe() #May use add objective to ensure minimum zero weighting to individual stocks
sharpe_pwt=ef.clean_weights()
print(sharpe_pwt)

OrderedDict([('AFLT.ME', 0.0), ('DSKY.ME', 0.22606), ('GMKN.ME', 0.48796), ('IRAO.ME', 0.0), ('LKOH.ME', 0.0), ('MTSS.ME', 0.02953), ('NKNC.ME', 0.25645), ('SBER.ME', 0.0)])
      
      



, weight_bounds=(0,1) weight_bounds=(-1,1), .





.





ef.portfolio_performance(verbose=True)

Expected annual return: 37.1%
Annual volatility: 20.7%
Sharpe Ratio: 1.70
(0.37123023494063007, 0.20717177784552962, 1.695357536597058)
      
      



, :





ef1 = EfficientFrontier(mu, Sigma, weight_bounds=(0,1)) 
minvol=ef1.min_volatility()
minvol_pwt=ef1.clean_weights()
print(minvol_pwt)

OrderedDict([('AFLT.ME', 0.02876), ('DSKY.ME', 0.24503), ('GMKN.ME', 0.10403), ('IRAO.ME', 0.0938), ('LKOH.ME', 0.01168), ('MTSS.ME', 0.41967), ('NKNC.ME', 0.09704), ('SBER.ME', 0.0)])

ef1.portfolio_performance(verbose=True, risk_free_rate = 0.27)

Expected annual return: 24.0%
Annual volatility: 16.9%
Sharpe Ratio: -0.18(0.239915644698749, 0.16885732511472468, -0.17816434839774456)
      
      







.





100 000 .





cl_obj = CLA(mu, Sigma)
ax = pplt.plot_efficient_frontier(cl_obj, showfig = False)
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: '{:.0%}'.format(x)))
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:.0%}'.format(y)))
      
      



:





latest_prices = get_latest_prices(df_stocks)
allocation_minv, rem_minv = DiscreteAllocation(minvol_pwt, latest_prices, total_portfolio_value=100000).lp_portfolio() 
print(allocation_minv)
print("         - {:.2f} ".format(rem_minv))
print()

{'AFLT.ME': 41, 'DSKY.ME': 181, 'IRAO.ME': 1765, 'LKOH.ME': 1, 'MTSS.ME': 127, 'NKNC.ME': 107}
         - 6152.03 
      
      



:





latest_prices1 = get_latest_prices(df_stocks)
allocation_shp, rem_shp = DiscreteAllocation(sharpe_pwt, latest_prices1, total_portfolio_value=100000).lp_portfolio() 
print(allocation_shp)
print("          {:.2f} ".format(rem_shp))

{'DSKY.ME': 167, 'GMKN.ME': 2, 'MTSS.ME': 9, 'NKNC.ME': 283} 
          1319.05 
      
      



167 , 2 , 9 283 . 1319 .





, , .








All Articles