Toy Model
Various stocks with rewards and risks on a scale of 0-3 (0 low risk, 3 high risk) are shown below.
Stock Name |
Reward |
Risk
|
AAA |
15% |
3
|
BBB |
12% |
2
|
CCC |
4% |
0.5
|
Decision Variables
Optimization Function
Constraints
Slack Variables
Worked Solution
Dual Problem
Objective Function
Constraints
Slack Variables
Worked Solution
Interpretation of Results
In both the primal and the dual the optimal solutions, z and w respectively, are 9. This tells us that using the investment distribution given by the primal we should see a return of 9% on our initial investment.
Now, consider the variables from the optimal solution to the dual problem.
: this variable refers to the first constraint. The first constraint puts an upper bound on the amount of total risk we are willing to take on. This means
gives the increase in output for an additional unit of risk we're willing to take on. So in this case, increasing the risk we'd take by 1 increases out total return by 4%.
: this variable refers to the second constraint. Here,
. Lets consider what happens if you loosen the constraint by 0.1. In this case the increase in return would go up by 0.1%.
and
: these variables correspond with the constraints derived from
. These would be the changes in return percentage based on borrowing additional money; however, borrowing is outside the scope of this toy problem so we will not discuss this any further.
Actual Portfolio
We will use these options from TD bank.
Various BMO ETFs with rewards (3 year annualized return), risks on a scale of 1-5 (1 low risk, 5 high risk) and volatility (3 year volatility) are shown below.
Variable |
Symbol |
Stock Name |
Reward |
Risk |
Volatility
|
![{\displaystyle x_{1}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/a8788bf85d532fa88d1fb25eff6ae382a601c308) |
ZXB |
BMO 2015 Corporate Bond Target Mat ETF |
-0.23% |
3 |
6.55
|
![{\displaystyle x_{2}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/d7af1b928f06e4c7e3e8ebfd60704656719bd766) |
ZXC |
BMO 2020 Corporate Bond Target Mat ETF |
2.63% |
3 |
7.81
|
![{\displaystyle x_{3}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/766d09a498699be10e276ad49145c921f8cbe335) |
ZXD |
BMO 2025 Corporate Bond Target Mat ETF |
0.91% |
4 |
7.55
|
![{\displaystyle x_{4}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/fb828766e82e496666b179ff70d8e2fd24a79e5f) |
ZAG |
BMO Aggregate Bond ETF |
2.50% |
3 |
9.31
|
![{\displaystyle x_{5}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/61b358d0e5e47692d8e6bf61c74130b612a7f919) |
ZDV |
BMO Canadian Dividend ETF |
6.43% |
3 |
9.46
|
![{\displaystyle x_{6}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/315276aae6e84d8cbb6f4e165a29dbe51f323307) |
ZCH |
BMO China Equity ETF |
26.73% |
3 |
13.60
|
![{\displaystyle x_{7}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/954c46116945ae4026c359eec235f65b6c408df8) |
ZWB |
BMO Covered Call Canadian Banks ETF |
11.47% |
4 |
12.23
|
![{\displaystyle x_{8}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/a631ca7c0ec07097af0806f818d3ace2f06429b5) |
ZWA |
BMO Covered Call DJIA Hedged to CAD ETF |
11.76% |
3 |
11.18
|
![{\displaystyle x_{9}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/c315560c3dacd659df30f39c951b82f45fa461f9) |
ZDJ |
BMO Dow Jones Ind Avg Hdgd CAD ETF |
13.63% |
3 |
11.96
|
![{\displaystyle x_{10}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/c6f81393481a94be9cca20430d468620e63cdb75) |
ZEF |
BMO Emerging Market Bnd Hdgd to CAD ETF |
3.88% |
5 |
10.85
|
![{\displaystyle x_{11}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/df6e8ee8613c2c8514687bd2db19c01df4d068e5) |
ZRE |
BMO Equal Weight REITs ETF |
1.99% |
2 |
12.10
|
![{\displaystyle x_{12}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/fbe6f173e4f67cb34854ec175b0f7bce5389ab87) |
ZUB |
BMO Eq Weight US Banks Hdgd to CAD ETF |
22.05% |
4 |
15.73
|
![{\displaystyle x_{13}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/b1333e0e82d096bf4af243163de18e9a94f59709) |
ZGI |
BMO Global Infrastructure ETF |
17.37% |
3 |
10.66
|
![{\displaystyle x_{14}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/12d760388da0fb0801c1edbd0f274f23fc798a74) |
ZHY |
BMO High Yld US Corp Bd Hdgd to CAD ETF |
5.00% |
5 |
8.14
|
![{\displaystyle x_{15}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/6dfa6dae34b170a08140d98b9070c2bc4877a898) |
ZJN |
BMO Junior Gas ETF |
9.75% |
4 |
21.80
|
![{\displaystyle x_{16}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/4bdd623f6569717b9e136c0c22db7ecb6ca6be74) |
ZJG |
BMO Junior Gold ETF |
-27.79% |
5 |
42.71
|
![{\displaystyle x_{17}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/aacaacb28282f6b799e70722ba92ef6f7deb6275) |
ZJO |
BMO Junior Oil ETF |
1.46% |
4 |
22.12
|
![{\displaystyle x_{18}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/ec1aadd82a070d7530dec7a1484511b47fa13f65) |
ZLC |
BMO Long Corporate Bond ETF |
2.92% |
2 |
9.72
|
![{\displaystyle x_{19}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/5b624d29e8fb870ffe3cc7f385bc2e9265078362) |
ZFL |
BMO Long Federal Bond ETF |
-0.20% |
3 |
10.59
|
![{\displaystyle x_{20}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/ddc6719cce56f76d220cf48a842cb3f089736065) |
ZLB |
BMO Low Volatility Canadian Equity ETF |
19.23% |
1 |
8.52
|
![{\displaystyle x_{21}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/4cf1e9982af3397d19d863a2df1c09116ccfa80c) |
ZCM |
BMO Mid Corporate Bond ETF |
2.67% |
5 |
7.53
|
![{\displaystyle x_{22}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/1cecad28c7e9d99b287ca2fcc656d754da8420bc) |
ZFM |
BMO Mid Federal Bond ETF |
0.20% |
5 |
7.92
|
![{\displaystyle x_{23}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/b6f4d635a1795b498d6abb8e137c28f216a6afbc) |
ZMI |
BMO Monthly Income ETF |
4.60% |
4 |
7.96
|
![{\displaystyle x_{24}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/1ebcad2b886679e07c32bde75bb6549daaa1f5f8) |
ZDM |
BMO MSCI EAFE Hdg to CAD ETF |
18.95% |
4 |
10.56
|
![{\displaystyle x_{25}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/1de7be6a50e34be2effd98467d11dafb5963b13c) |
ZEM |
BMO MSCI Emerging Markets ETF |
8.86% |
3 |
10.97
|
![{\displaystyle x_{26}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/d11bb3c3128dc54c134dad875b533a8b9b9209cd) |
ZQQ |
BMO NASDAQ 100 Equity Hedged to CAD ETF |
20.36% |
5 |
12.61
|
![{\displaystyle x_{27}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/3634ce718762daae05c8c2c9557def94e7c30e3d) |
ZRR |
BMO Real Return Bond ETF |
-2.31% |
4 |
11.77
|
![{\displaystyle x_{28}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/650efadc2a24deb201c523f24068f1d3e64b9039) |
ZUE |
BMO S&P 500 Hedged to CAD ETF |
17.36% |
4 |
11.07
|
![{\displaystyle x_{29}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/e259eaca1f27fe9000b4edcbc9e05701599c1621) |
ZCN |
BMO S&P/TSX Capped Composite ETF |
9.92% |
3 |
9.16
|
![{\displaystyle x_{30}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/4df6d4fbe838305fc4b6a69289f49b23c218349e) |
ZEB |
BMO S&P/TSX Equal Weight Banks ETF |
13.06% |
3 |
13.34
|
![{\displaystyle x_{31}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/156f9c72b7399396e5c2d2887272f5ff10f271ab) |
ZMT |
BMO S&P/TSX EqWt Glb BM Hdgd to CAD ETF |
-4.23% |
4 |
20.63
|
![{\displaystyle x_{32}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/7bd3b513e7a88151d7a6185e42a9ace7e3878ec6) |
ZEO |
BMO S&P/TSX Equal Weight Oil&Gas ETF |
0.27% |
1 |
16.66
|
![{\displaystyle x_{33}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/1b9598bdb0765d38c8b1ca46f6b0b66f7464d029) |
ZCS |
BMO Short Corporate Bond ETF |
0.74% |
4 |
6.44
|
![{\displaystyle x_{34}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/81a62bfc62163e4ea28ff1564d19f4757005f8f4) |
ZFS |
BMO Short Federal Bond ETF |
-0.61% |
4 |
6.41
|
![{\displaystyle x_{35}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/eec0c9494f53102fa82aa98f239fcebfc76ce061) |
ZPS |
BMO Short Provincial Bond ETF |
0.08% |
5 |
6.27
|
![{\displaystyle x_{36}}](https://wiki.ubc.ca/api/rest_v1/media/math/render/svg/dd152075c44ea85bb762deb8f6ce54048777cc40) |
ZST |
BMO Ultra Short-Term Bond ETF |
-0.97% |
3 |
6.78
|
LP Problem
Results
Z = 15.9009
Symbol |
Percentage Invested
|
ZXB |
0
|
ZXC |
0
|
ZXD |
0
|
ZAG |
0
|
ZDV |
0
|
ZCH |
0.15
|
ZWB |
0
|
ZWA |
0
|
ZDJ |
0
|
ZEF |
0
|
ZRE |
0
|
ZUB |
0
|
ZGI |
0.15
|
ZHY |
0
|
ZJN |
0
|
ZJG |
0
|
ZJO |
0
|
ZLC |
0
|
ZFL |
0
|
ZLB |
0.15
|
ZCM |
0
|
ZFM |
0
|
ZMI |
0
|
ZDM |
0.15
|
ZEM |
0
|
ZQQ |
0
|
ZRR |
0
|
ZUE |
0.15
|
ZCN |
0.0849481
|
ZEB |
0
|
ZMT |
0
|
ZEO |
0
|
ZCS |
0.15
|
ZFS |
0
|
ZPS |
0.0150519
|
ZST |
0
|
Solver Code
Here is the code to solve the LP program using python.
Operating System: Ubuntu 14.04 LTS
This uses a modified version PyGLPK found at https://github.com/bradfordboyle/pyglpk
Ran on python version 2.7
Both the code, called solve.py, and the datafile it uses, data.txt are shown below. Note, data.txt must be in the same directory as solve.py.
solve.py
import glpk
max_percent_per_stock = 0.15
max_volatility = 10
data_filename = 'data.txt'
the_file = open(data_filename, 'r')
lines = []
for line in the_file:
l = line.strip()
l = line.split()
lines.append(l)
# Drop first line. It's column labels.
stocks = lines[1:]
n = len(stocks)
names = [stock[0] for stock in stocks]
rewards = [float(stock[1].replace("%", "")) for stock in stocks]
risks = [stock[2] for stock in stocks]
volatilities = [float(stock[3]) for stock in stocks]
# Referencing solver code from http://tfinley.net/software/pyglpk/ex_ref.html
lp = glpk.LPX()
lp.name = '340project'
lp.obj.maximize = True
# add rows
# 1 row for first constraint
# 1 row for second constraint
# n rows for last constraint
lp.rows.add(n+2)
for row in lp.rows:
row.name = 's{0}'.format(row.index)
# put bounds on constraints
# constraint 1: 0 \leq x_n \leq 0.15 \text{ for }n=1, 2, ..., n
for row in lp.rows[:n]:
# TODO ISSUE: 0 \leq x_n \leq 0.15? Can I use None or should I use 0?
row.bounds = None, max_percent_per_stock
# constraint 2: \sum\limits{n=1}_{36} Volatility_n*x_n \leq 10
lp.rows[n].bounds = None, max_volatility
# constraint 3: \sum\limits{n=1}_{36} x_n = 1
lp.rows[n+1].bounds = 1.0, 1.0
# initial dictionary has n non-basic variables so we need n columns
lp.cols.add(n)
for col in lp.cols:
col.name = '{0}'.format(names[col.index])
col.bounds = 0, None
# set the objective function
lp.obj[:] = rewards
# build matrix of coeffs. We refer to this as matrix A in class.
matrix = []
# constraint 1: 0 \leq x_n \leq 0.15 \text{ for }n=1, 2, ..., n
for stock_num in xrange(len(stocks)):
row_of_zeros = [0 for x in xrange(n)]
row_of_zeros[stock_num] = 1
matrix += row_of_zeros
# constraint 2: \sum\limits{n=1}_{36} Volatility_n*x_n \leq 10
matrix += volatilities
# constraint 3: \sum\limits{n=1}_{36} x_n = 1
matrix += [1 for x in xrange(n)]
lp.matrix = matrix
lp.simplex()
print 'Z = %g\n' % lp.obj.value,
print '{| class="wikitable" style="text-align: center;"'
print '|-'
print '! Symbol !! Percentage Invested'
print '|-'
print '\n|-\n'.join('| %s || %g' % (c.name, c.primal) for c in lp.cols)
print '|-'
print '|}'
data.txt
Symbol Reward Risk Volatility
ZXB -0.23% 3 6.55
ZXC 2.63% 3 7.81
ZXD 0.91% 4 7.55
ZAG 2.50% 3 9.31
ZDV 6.43% 3 9.46
ZCH 26.73% 3 13.60
ZWB 11.47% 4 12.23
ZWA 11.76% 3 11.18
ZDJ 13.63% 3 11.96
ZEF 3.88% 5 10.85
ZRE 1.99% 2 12.10
ZUB 22.05% 4 15.73
ZGI 17.37% 3 10.66
ZHY 5.00% 5 8.14
ZJN 9.75% 4 21.80
ZJG -27.79% 5 42.71
ZJO 1.46% 4 22.12
ZLC 2.92% 2 9.72
ZFL -0.20% 3 10.59
ZLB 19.23% 1 8.52
ZCM 2.67% 5 7.53
ZFM 0.20% 5 7.92
ZMI 4.60% 4 7.96
ZDM 18.95% 4 10.56
ZEM 8.86% 3 10.97
ZQQ 20.36% 5 12.61
ZRR -2.31% 4 11.77
ZUE 17.36% 4 11.07
ZCN 9.92% 3 9.16
ZEB 13.06% 3 13.34
ZMT -4.23% 4 20.63
ZEO 0.27% 1 16.66
ZCS 0.74% 4 6.44
ZFS -0.61% 4 6.41
ZPS 0.08% 5 6.27
ZST -0.97% 3 6.78