Asset Allocation for Tangent Portfolio with Risk-Free Asset in Python

Studying a new programming language is an adventurous journey. It takes time in case of Python. Even for me. Not every concept is easy to grasp and testing a piece of code with its endless modifications may take a longer while. We have two choices: either to get a good book like Python for Quants. Vol I. or sit down and apply a copy-and-paste-and-test method. The latter approach is much longer but rewarding in the middle of our learning process. Hit and try. So, why not to try?

You cannot write the code which performs complex operations omitting necessary set of functions. There is no need to reinvent the wheel. In Python, the community works hard to deliver best programming solutions. In this lesson, we will accelerate by conducting an investigation of Python code aimed at finding optimised weights for a tangent portfolio problem.




In QaR ebook on Applied Portfolio Optimization with Risk Management using Matlab we discussed in great detail the theory and practical calculations for various cases of portfolios with different objective functions. Markowitz in 1952 underlined that the goal of portfolio choice was either to look for such portfolio which could deliver maximum return at a given level of risk or minimum risk for a given level of return. Based on the theoretical works of Sharpe in 1964, Lintner in 1965 and Tobin in 1958, the importance of the risk-free asset in the portfolio has been proved to equip the investor with a better control over risk.

We can split the budget into fractions of our capital designated for an investment in the risk-free option (e.g. the savings account in a bank) while the rest will be delegated to other assets with diversified risk levels. It was shown that for any portfolio with the risk-free component, the expected return is:
$$
R_P = mw^T + (1+{\bf 1}w^T)r_f \ \ \ \mbox{at} \ \ \ \sigma_P = wCw^T
$$ where by $C$, $m$, and $w$ the covariance matrix, individual asset expected return matrix, and asset weighting have been denoted accordingly. If we aim at variance, $\sigma_P$, to be minimised:
$$
\min_{w} \ \ wCw^T
$$ subject to $mw^T + (1+{\bf 1}^T)r_f = r_{\rm target}$, we formulate the minimum variance portfolio optimization problem. It occurs that all minimum variance portfolios are a combination of the risk-free asset and a given risky portfolio. The latter is often called the tangent portfolio and has been shown that it must contain of all assets available to investors (held in quantity to its market value relative to the total market value of all assets). That makes the second name of the tangent portfolio: the market portfolio. The objective function of the form:
$$
\max_{w} \ \ \frac{mw^T-r_f}{\sqrt{wCw^T}}
$$ subject to ${\bf 1}w^T=1$ called the Sharpe ratio corresponding to the market portfolio directly. The risk-free asset is connected with the tangent portfolio by the straight line therefore provides an investor with a good blend of risk-controlled portfolios. Modern Portfolio Theory tells us that the tangent portfolio is given by:
$$
{\bf w} = C^{-1}({\bf m} – {\bf 1}r_f)
$$ where the vector ${\bf w}$ stores the computed weights for each asset in portfolio $P$. Since finding the tangent portfolio given $N$ assets is, luckily, an analytical problem (i.e. without employment of the solvers), that makes this task a straightforward problem to be coded in Python as follows.

# Asset Allocation for Tangent Portfolio with Risk-Free Asset in Python
# Accelerated Python for Quants Tutorial, Lesson 4
# (c) 2014 QuantAtRisk

from numpy import matrix, power
from math import sqrt

def TangentPortfolio(m,C,rf):
    # find number of rows and columns for the covariance matrix 
    (nr,nc)=C.shape
    A=matrix([[0.0] for r in xrange(nr)])
    A=(1/C)*(m-rf)
    (nr,nc)=A.shape
    A=A/sum(A[r,0] for r in xrange(nr))
    w=[A[r,0] for r in xrange(nr)]
    pret=mu.T*A
    prsk=power(A.T*(C*A),0.5)
    return matrix(w),pret,prsk

Here, we can see a new element of Python language which is a definition and usage of a function. We start its syntax with def, next write a desired function name with input parameters in the brackets, and end it with a colon. The body of the function must be always indented (min 4 space signs; don’t use tab!) and if some results are intended to be sent out of the function, return function should be specified at the end, listing all variables of interest.

In addition we make use of numpy module from which we import only two functions that we will be using. The first one is matrix that allows us to implement matrix or vector notation explicitly (we avoid Python’s lists or arrays at this stage). The dimensions of any matrix M can be return into tuple as shown in line #10.

Please note how Python eases our life. In line #15 we create a new one-row vector (matrix) referring directly to certain elements of other matrix by putting for…in loop inside the matrix of w itself. How brilliant it is! Lastly, using a function of power we take its first argument to the power of 1/2, i.e. we compute a square root.

To see some action, let us first define an exemplary covariance matrix and vector with expected returns corresponding to 3-assets in the portfolio:

cov=matrix([[0.04, 0.004, 0.02],[0.004, 0.09, 0.09],[0.02,0.09,0.16]])
mu=matrix([[0.13],[0.11],[0.19]])
rf=0.05

w,ret,rsk=TangentPortfolio(mu,cov,rf)

where investing at the risk-free rate of 5% has been added to complete the grand picture of the problem we discuss here. Line #24 reveals the way how we call our function and assign calculated values within the function to outer variables (their names can be different). We display the results on the screen by typing:

print("Portfolio weights")
print(w.T)

print("Expected Portfolio Return and Risk")
print ret,rsk

what returns:

Portfolio weights
[[ 0.46364368]
 [ 0.4292997 ]
 [ 0.10705661]]

Expected Portfolio Return and Risk
[[ 0.1278374]] [[ 0.19715402]]

what simply communicates that the expected portfolio return equals 12.8% at 19.7% of risk if we allocate 46%, 42%, and 10% in asset number 1, 2, and 3, respectively.

We find that Sharpe ratio,

sharpe=(ret-rf)/rsk
print(sharpe)
</pre>
equals 0.3948 which corresponds to the Sharpe ratio of the first asset:
[code lang="python" firstline="35"]
for r in xrange(3): print((mu[r,0]-rf)/sqrt(cov[r,r]))
0.4
0.2
0.35

Finally, if we denote by $\zeta$ a fraction of capital we want to invest in risky assets, leaving $(1-\zeta)$ in the bank at $r_f=5\%$ rate, then the expected portfolio return will be:
$$
\zeta wm+(1-\zeta)r_r \ \ \ \mbox{at} \ \ \ \zeta\sqrt{wCw^T}
$$ of risk, therefore for two different cases, for example:

alpha=0.7
print(((matrix(alpha)*w)*mu)+(1-alpha)*rf)
print(matrix(alpha)*power(w*cov*w.T,1))

alpha=0.25
print(((matrix(alpha)*w)*mu)+(1-alpha)*rf)
print(matrix(alpha)*power(w*cov*w.T,1))

we get

[[ 0.10448618]]
[[ 0.0272088]]

[[ 0.06945935]]
[[ 0.00971743]]

what confirms that by putting 70% of our capital, for instance, into three stocks should result in 10.4% gain at 2.7% rate of risk, while an allocation of 75% of the capital in the bank promises 6.9% return, i.e. approaching earlier defined risk-free rate of 5% pa.



Leave a Reply

Your email address will not be published. Required fields are marked *