Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Views: 1856
Image: ubuntu2004
Kernel: Python 3 (system-wide)

Market risk and portfolio theory - MATH0094

In class assesment

November 23 - 2020

Time: 2h 15 minutes. Marks for each question of each part are indicated in square brackets.

Instructions

  • The exam has to be completely done in the corresponding CoCalc folder. Apart from this you only require pen and paper.

  • You can use all the material in the course as well as the Python documentation available from the Help menu in CoCalc.

  • Everything you submit must be your own work. In particular, you must not post questions on online forums, contact your peers, or receive any other form of direct external help.

  • You must keep your Zoom video connection at all times

  • You can only ask to clarify something on the paper. No request for help is accepted unless an unexpected technical problem arises.

  • If you have questions, only ask them by private message in Zoom or through the chat button on the top-right corner of this notebook.

  • For every function you code, make sure to write error management routines to respond to errors when the parameters are not of the expected type or value.

  • You can add as many cells as needed. Bear in mind that some of the cells you start with are 'read only': you cannot modify these cells

  • I have provided some tests to help you. You can run them by clicking on the 'validate' button (no errors, means you pass the pre-delivered tests). You are encouraged to complement with your own tests (the provided tests are only to help and not exhaustive).

  • Explain your code using both comments and Markdown cells as needed.

Important: You must accept to solve this assessment in accordance to the above instructions and UCL's code of honour. Please type the following sentence in the next cell: "I will answer this paper in accordance with the given instructions and UCL's code of honour".

I will answer this paper in accordance with the given instructions and UCL's code of honour

# Some packages you might need in the following from nose.tools import assert_equal, assert_raises # Some functions to produce some validations import numpy as np import matplotlib.pylab as plt import scipy.stats as st from numpy.random import default_rng from scipy.optimize import minimize

Question 1 [60 marks]

Consider a market with two assets: a risk-free asset with constant return Rt0=r>0R^0_t = r>0 for all t=1,,Tt=1, \ldots, T, and an asset with i.i.d. binomial returns such that

Rt1(ω)={r+aωt=0raωt=1.R^1_t(\omega) = \begin{cases} r+a &\omega_t=0 \\ r-a & \omega_t=1 \end{cases}.

Assume further that P[ωt=0]=P[ωt=1]=12\mathbb P[\omega_t=0]=\mathbb P[\omega_t=1]=\frac 1 2.

  • 1.1. Explain if this market is complete and/or arbitrage-free.

[10 marks]

The market is not complete, because there are only two assets in the market, other assets which gross return has other distributions can not be replicated by these two assets. This market is arbitrage-free since we can not construct an arbitrage portfolio with these two assets.

  • 1.2. Let ff be a measurable function. Write a function that, given St1S_t^1 and the model parameters calculates E[f(St+11)Ft]\mathbb E[f(S_{t+1}^1)|\mathcal F_t]

[10 marks]

def cexp(f,st,a,r,t): ''' Calculate the conditional expectation of the function 'f(S^1_{t+1})' given 'S^1_t=st' and 'R^0_t=r' and the parameters 'a'. ''' # YOUR CODE HERE try: st = float(st) except: raise TypeError('st must be a real') try: a = float(a) except: raise TypeError('a must be a real') try: r = float(r) except: raise TypeError('r must be a real') try: t = int(t) except: raise TypeError('t must be an int') if r<=0: raise ValueError('r must be positive') result = 0.5*f((r+a)*st)+0.5*f((r-a)*st) return result
# Some validation tests on the function cexp assert_equal(cexp(lambda x: x, 100,0.1,1,2),100) assert_equal(cexp(lambda x: x, 100,0.1,1.5,2),150)
# Validation tests on errors assert_raises(ValueError, cexp, lambda x:x, 100,0.1,-1,1) assert_raises(TypeError, cexp, lambda x:x, 'a',0.1,1,1)
  • 1.3. Write a function that, given the value of a random variable W(ω),ω{0,1}W(\omega), \omega\in\{0,1\} (expressed as a two-dimensional array) returns a one-period strategy that replicates it (or a value error if it cannot be replicated). The strategy must be given in terms of the amount invested on each asset.

[15 marks]

def hedging_strategy(w,r,a): ''' Calculate the hedging strategy of a wealth w when the arameters of the model are R^0=r and 'a' ''' # YOUR CODE HERE try: w = np.asarray(w) except: raise TypeError('w can not be treated as an array') if w.size!=2: raise TypeError('w can not be treated as an array') if r<=0: raise ValueError('r must be positive') try: a = float(a) except: raise TypeError('a must be a real') A = np.array([[r,r+a], [r,r-a]]) b = w.reshape(2,1) A_hat = np.hstack((A,b)) if np.linalg.matrix_rank(A_hat)!=np.linalg.matrix_rank(A): raise ValueError('w can not be replicated') return np.linalg.solve(A,w)
np.testing.assert_allclose( hedging_strategy(np.ones(2),1,0.1), np.array([1,0]),rtol=1e-6,atol=1e-6) np.testing.assert_allclose( hedging_strategy(np.array([1.1,0.9]),1,0.1), np.array([0,1]),rtol=1e-6,atol=1e-6) assert_raises(TypeError,hedging_strategy,1,1,1) assert_raises(ValueError,hedging_strategy,np.ones(2),-1,1)
  • 1.4. Assume that |a|<r for this market model. Write a function that, given a time tt and the initial value of the asset S01S^1_0 returns two vectors: one with the possible values that St1S^1_t can attain, and a second with their corresponding probabilities.

Hint: Recall that St1=S01=1tR1=S01exp(=1tlog(R1)) S^1_t = S^1_0 \prod_{\ell=1}^t R^1_\ell = S^1_0 \exp\left( \sum_{\ell=1}^t \log(R^1_\ell) \right) it might help to use this expression and use the binomial distribution (st.binom)

[15 marks]

def dist_s1(t,s0,a,r): ''' Calculates the distribution of S_t^1 given that S^1_0=s0, R^0_t = r and 'a' is the parameter of R^1_t ''' # YOUR CODE HERE try: t = int(t) except: raise TypeError('t must be an int') try: s0 = float(s0) except: raise TypeError('st must be a real') if r<=0: raise ValueError('r must be positive') if np.abs(a)>=r: raise ValueError('|a| should be less than r') S_t = np.array([s0*(r-a)**(t-i)*(r+a)**i for i in np.linspace(0,t,t+1)]) p = np.ones_like(S_t)/(2**t) result = np.vstack((S_t,p)) return result
np.testing.assert_allclose( dist_s1(0,1,0.1,1), np.array([[1], [1]])) np.testing.assert_allclose( dist_s1(1,1,0.1,1), np.array([[0.9, 1.1], [0.5, 0.5]])) assert_raises(ValueError,dist_s1,1,1,2,1)
  • 1.5. Explain whether the process (St1/St0)t=0,,T(S^1_t/S^0_t)_{t=0,\ldots,T} is a martingale. Justify your answer either mathematically or with a Python code.

[10 marks]

It is a martingale

Question 2. [40 marks]

A butterfly option centred at x0x_0 and width d>0d>0 is a contingent claim that pays at a given date TT a payoff b(ST1)b(S^1_T) where

b(x)={1xx0dif x[x0d,x0+d]0otherwiseb(x)= \begin{cases}\frac{1- |x-x_0|}{d} & \text{if } x\in [x_0-d,x_0+d]\\ 0 & \text{otherwise} \end{cases}
  • 2.1. Implement the butterfly payoff. Then, plot it for the case when d=0.1, x0=1 for values of xx between 0.8 and 1.2. [10 marks]

def b_payoff(x,x0,d): """ Compute the butterfly payoff having width 'd>0' and center 'x0' for a real 'x' """ # YOUR CODE HERE try: x = float(x) except: raise TypeError('x must be a real') try: x0 = float(x0) except: raise TypeError('x0 must be a real') if d<=0: raise ValueError('d must be positive') if (x>=x0-d)&(x<=x0+d): if np.abs(1-np.abs(x-x0)/d)>0.001: return 1-np.abs(x-x0)/d return 0
# check implementation assert_equal(b_payoff(1,1,0.1),1) assert_equal(b_payoff(0.1,1,0.1),0) assert_equal(b_payoff(1.1,1,0.1),0) # Check that the function raises an error for invalid input assert_raises(TypeError, b_payoff, 'a',1,1) assert_raises(ValueError, b_payoff, 1,1,-1)

Write the code for the plot below:

# YOUR CODE HERE x = np.linspace(0.8,1.2,1000) y = np.array([b_payoff(i,1,0.1) for i in x]) plt.plot(x,y) plt.xlabel('x') plt.ylabel('b(x)') plt.show()
Image in a Jupyter notebook
  • 2.2. We want to obtain the arbitrage-free price of a contingent claim having as payoff a butterfly option centred at x0x_0 and width dd for the market model on Question 1 with S01=s0S^1_0=s0.

To simplify, the function receives the discrete distribution of ST1S^1_T expressed as two vectors (as in the solution of Q.1.4.).

[15 marks]

def price_b(T,r,x0,d,v_st, p_st): ''' Returns the price, for the market of question 1, of a butterfly option with parameters x0,d paying at time T. The parameters v_st and p_st are arrays containing respectively the values and probability of S_T, while r is the constant risk-free rate of return. ''' # YOUR CODE HERE payoff = np.array([b_payoff(i,x0,d) for i in v_st]) return (payoff/r**T)@p_st
np.testing.assert_allclose( price_b(1,1,1,0.05,np.array([0.9, 1.1]), np.array([0.5, 0.5])) , 0 ) np.testing.assert_allclose (price_b(1,1,1,0.2,np.array([0.9, 1.1]), np.array([0.5, 0.5])) , 0.5 )
  • 2.3. Assume we are still working under the market model of Question 1. The following cell contains the definition of a function 'mystery'. Explain what the function does, what are its outputs, and their financial interpretation.

[15 points]

def mystery(T,x0,d,r,a,s0): v,p = dist_s1(T,s0,a,r) f_vec = np.array([b_payoff(x,x0,d) for x in v]) phi=[] for t in range(T,0,-1): phi_new = [] sp = [] for i in range(t): phi_new.append(hedging_strategy(f_vec[i:i+2],r,a)) sp.append(phi_new[-1].sum()) f_vec = sp phi.append(phi_new) return f_vec[0], phi[-1::-1] its outputs are

YOUR ANSWER HERE