Using the Monte Carlo Method to Create a Portfolio

Beginning (and not only) investors often wonder how to choose the ideal ratio of assets included in the portfolio. Often (or not very, but I know about two for sure) for some brokers, this function is performed by a trading robot. But the algorithms embedded in them are not disclosed.

This post will cover how to optimize a portfolio using Python and Monte Carlo simulations. Portfolio optimization is understood as such a ratio of weights that will satisfy one of the conditions:

  • A portfolio with a minimum level of risk with the desired profitability;
  • Portfolio with maximum profitability at established risk;
  • Portfolio with maximum yield

For the calculation we’ll take nine stocks recommended by the trading robot of one of the brokers at the beginning of January 2020 and also set the optimal weights for them in the portfolio: 'ATVI', 'BA', 'CNP', 'CMA', 'STZ', 'GPN', 'MPC', 'NEM' and 'PKI'. For analysis, we will take stock data for the last three years.

# 

import pandas as pd
import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt

#    
ticker = ['ATVI','BA','CNP','CMA', 'STZ','GPN','MPC','NEM', 'PKI']

stock = yf.download(ticker,'2017-01-01', '2019-01-31') 


If you add up the share of all the shares included in the portfolio, then the amount should tend to one (or rather be equal). Then, as usual, we will prepare the data for the calculations:

#    
all_adj_close = stock[['Adj Close']]

#  
all_returns = all_adj_close.pct_change()

#       
mean_returns = all_returns.mean()
cov_matrix = all_returns.cov()

Now you can calculate for the weights offered by the trading robot and find out the profitability of this portfolio for the last three years and the standard deviation.

#    
robot = np.array([0.0441, 0.1030, 0.1086, 0.2070, 0.1525, 0.0714, 0.0647, 0.1828, 0.0661])

# ,     
portfolio_return_robot = np.sum(mean_returns * robot)
portfolio_std_dev_robot = np.sqrt(np.dot(robot.T,np.dot(cov_matrix, robot)))
sharpo_robot = portfolio_return_robot/portfolio_std_dev_robot

#         
robot_result = np.array([portfolio_return_robot, portfolio_std_dev_robot, sharpo_robot])
robot_result = np.array([portfolio_return_robot, portfolio_std_dev_robot, sharpo_robot])
robot_result = np.concatenate((robot_result, robot), axis=0)
robot_sim_result = pd.DataFrame(robot_result, columns=['Robot'], index=['ret','stdev','sharpe',ticker[0],ticker[1],ticker[2],ticker[3],ticker[4],ticker[5],ticker[6],ticker[7],ticker[8]])

print(robot_sim_result) 

image

Monte Carlo simulation


Initially, a small introduction to how the Monte Carlo method is used to optimize the portfolio.

First, random weights are given to the shares, after which the yield and standard deviation are calculated. The resulting values ​​are saved. The next step is to randomly change the weights (the main thing is not to forget that their sum should be one) and everything repeats - calculating and saving the obtained value. The number of iterations depends on the time, computer capacities for calculation, and risks that the investor is ready to accept. This time we’ll try to do 10,000 calculations to identify a portfolio with a minimum loss and a maximum Sharpe ratio.

#   
num_iterations = 10000
simulation_res = np.zeros((4+len(ticker)-1,num_iterations))

#  
for i in range(num_iterations):
        #    ,    1
        weights = np.array(np.random.random(9))
        weights /= np.sum(weights)
        
        #    
        portfolio_return = np.sum(mean_returns * weights)
        portfolio_std_dev = np.sqrt(np.dot(weights.T,np.dot(cov_matrix, weights)))
        
        #     
        simulation_res[0,i] = portfolio_return
        simulation_res[1,i] = portfolio_std_dev
        
        #    
        simulation_res[2,i] = simulation_res[0,i] / simulation_res[1,i]
        
        # 
        for j in range(len(weights)):
                simulation_res[j+3,i] = weights[j]

#     DataFrame     .
sim_frame = pd.DataFrame(simulation_res.T,columns=['ret','stdev','sharpe',ticker[0],ticker[1],ticker[2],ticker[3],ticker[4],ticker[5],ticker[6],ticker[7],ticker[8]])

Now you can calculate the portfolio with the maximum Sharpe ratio or minimum risk.

#   Sharpe Ratio
max_sharpe = sim_frame.iloc[sim_frame['sharpe'].idxmax()]

#    
min_std = sim_frame.iloc[sim_frame['stdev'].idxmin()]

print ("The portfolio for max Sharpe Ratio:\n", max_sharpe)
print ("The portfolio for min risk:\n", min_std)

image

image

Well, the most important idea can be obtained when you visualize the data:

fig, ax = plt.subplots(figsize=(10, 10))

#    scatter plot        x      y

plt.scatter(sim_frame.stdev,sim_frame.ret,c=sim_frame.sharpe,cmap='RdYlBu')
plt.xlabel('Standard Deviation')
plt.ylabel('Returns')
plt.ylim(0,.0015)
plt.xlim(0.007,0.012)

plt.scatter(max_sharpe[1],max_sharpe[0],marker=(5,1,0),color='r',s=600)

plt.scatter(min_std[1],min_std[0],marker=(5,1,0),color='b',s=600)

plt.scatter(portfolio_std_dev_robot, portfolio_return_robot,marker=(5,1,0),color='g',s=600)

plt.show()

image

The portfolio with the maximum Sharpe ratio is shown by a red star, blue - with a minimum standard deviation and green - proposed by the robot. As you can see, the portfolio proposed by the robot does not coincide with these indicators, but the investor is left with the choice of portfolio. And I will try at the end of the year to return to comparing portfolios. And now all three portfolios are in drawdown.

All Articles