Description
Collateralized Mortgage Obligations (CMO) are used to restructure the cashflows from underlying mortgage collateral into a set of high quality bonds with different maturities. In the following mixed-integer programming it is used to restructure a mortgage pool into six normal tranches and one zero tranche. The model is written in a general form to allow experimentation with different periodicity. The following version is annual.
Large Model of Type : MIP
Category : GAMS Model library
Main file : cmo.gms
$title Financial Optimization: Financial Engineering (CMO,SEQ=114)
$onText
Collateralized Mortgage Obligations (CMO) are used to restructure
the cashflows from underlying mortgage collateral into a set of
high quality bonds with different maturities. In the following
mixed-integer programming it is used to restructure a mortgage
pool into six normal tranches and one zero tranche.
The model is written in a general form to allow experimentation
with different periodicity. The following version is annual.
Dahl, H, Meeraus, A, and Zenios, S A, Some Financial Optimization
Models: Risk Management. In Zenios, S A, Ed, Financial Optimization.
Cambridge University Press, New York, NY, 1993.
Keywords: mixed inter linear programming, financial optimization, mortgage
obligations, finance
$offText
Set
i 'tranches' / n1*n6, m /
n(i) 'normal tranches' / n1*n6 /
m(i) 'm tranches' / m /
tp 'time periods' / 0*10 /
ts(tp) 'settlement date' / 0 /
t(tp) 'payment periods' / 1*10 /
tl(tp) 'last payment' / 10 /
zpos(i,tp) 'possible payment periods by tranche';
Alias (i,j), (tp,tau);
* Note: all rates are monthly rates x 12. For aggregation we need to
* compute equivalent rates.
*
* (1 + r/12)**12 == (1 + rd)**d
Scalar
cg 'collateral annual gross coupon' / .10 /
nom 'nominal value of collateral' / 100 /
s 'servicing rate' /.005 /
psa 'pricing speed' / 115 /
d 'periodicity' / 1 /
age 'age of the collateral in years'
minn 'minimum number of normal tranches' / 3 /
minm 'minimum number of m tranches' / 1 /;
Table tranches(*,*) 'tranche data (annual coupon and yield)'
coupon yield low-wal up-wal
n1 .085 .099 1.00 1.24
n2 .085 .0999 2.00 2.44
n3 .085 .0999 3.00 3.44
n4 .0875 .1006 4.00 4.44
n5 .0875 .1009 5.00 5.65
n6 .0875 .1017 7.00 7.94
n7 .0875 .1018 10.00 10.94
n8 .0875 .1025 15.00 20.00
m .0995 .105
r .1285 ;
Parameter
po(tp) 'outstanding collateral principal at zero prepayment'
a 'zero prepayment annuity'
bv(tp) 'temporary factor for bvf calculation'
bvfac(tp) 'temporary factor for bvf calculation - rev sum'
bvf(tp) 'bond value factor'
pe(tp) 'expected outstanding collateral principal'
b(tp) 'prepayment rate'
cflow(tp) 'expected collateral payments'
prin(tp) 'structuring principal payments from collateral'
sump 'sum of prin'
cd 'periodic gross coupon'
sd 'periodic servicing rate'
coupon(i) 'periodic coupon by tranche'
yield(i) 'periodic yield by tranche'
yr 'periodic residual yield'
wallo(i) 'lower weighted average life in periodicity'
walup(i) 'upper weighted average life in periodicity'
bign 'big number'
smalln 'small number'
cmax 'maximum cmo coupon'
tmax 'term to maturity of collateral'
rev(tp) 'reverse order addresses'
psum(tp) 'sum of principal payments on collateral till tp'
tpsum(tp) 'sum of time principal payment products'
walp(tp) 'wal on principal payments from collateral';
rev(tp) = card(tp) - 2*ord(tp) + 1;
tmax = card(t);
age = (360 - tmax*12/d)/12;
b(t) = 0.06*(psa/100)*min((ord(t) + age*d)/(d*2.5),1);
cd = (1 + cg/12)**(12/d) - 1;
sd = (1 + s/12)**(12/d) - 1;
yr = (1 + tranches("r","yield")/12)**(12/d) - 1;
coupon(i) = (1 + tranches(i,"coupon")/12)**(12/d) - 1;
yield(i) = (1 + tranches(i,"yield")/12)**(12/d) - 1;
wallo(n) = tranches(n,"low-wal")*d;
walup(n) = tranches(n,"up-wal")*d;
cmax = smax(i, coupon(i));
a = nom*cd/(1 - power(1 + cd,-tmax));
po(tp) = nom*(1 - power(1 + cd,ord(tp) - 1 - tmax))/(1 - power(1 + cd,-tmax));
bv(tl) = 0;
loop(tp, bv(tp + (rev(tp) - 1)) = (bv(tp + rev(tp)) + (a - sd*po(tp + (rev(tp) - 1))))/(1 + cmax));
bvf(tp)$po(tp) = min(bv(tp)/po(tp),1);
bvf(tl) = 1;
pe(ts) = nom;
loop(t(tp), pe(tp) = pe(tp-1)*(1 - b(tp))**(1/d)*po(tp)/po(tp - 1));
cflow(t(tp)) = (1 + cd - sd)*pe(tp-1) - pe(tp);
prin(t(tp)) = pe(tp-1)*bvf(tp-1) - pe(tp)*bvf(tp);
sump = sum(tp, prin(tp));
walp(ts) = 0;
tpsum(ts) = 0;
psum(ts) = 0;
loop(t(tp),
psum(tp) = psum(tp-1) + prin(tp);
tpsum(tp) = tpsum(tp-1) + (ord(tp) - 1)*prin(tp);
);
walp(t) = tpsum(t)/psum(t);
zpos(i,tp) = yes;
zpos(n,tp)$(walp(tp) > walup(n)) = no;
option age:6, tmax:6, cd:6, sd:6, cmax:6,a:6;
display age, tmax, cd, sd, cmax, a, po, cflow, pe, bv, bvf, prin, walp, zpos;
bign = sump*.7;
smalln = sump*.03;
Variable
x(i,tp) 'outstanding principal in each tranche'
p(i,tp) 'principal payments on tranches'
c(i,tp) 'cashflow in each tranche'
r(tp) 'residual payments'
tpp(i) 'product of time and principal payment'
z(i,tp) 'tranche utilization variable (1 = usage)'
y(i,tp) 'upper triangular structure'
tin(i) 'tranche in the structure'
pv 'def pv tranches'
pvres 'def pv residuals'
proceeds 'gross proceeds';
Positive Variable x, c, r, y;
Binary Variable z, tin;
Equation
obj 'objective function'
defpv 'def pv tranches'
defpvres 'def pv residuals'
pdef(i,tp) 'definition of principal payments'
cdef(i,tp) 'cashflow accounting'
retiren1(tp) 'retirement of normal tranches'
retire(i,tp) 'retirement of tranches'
retirem(m,tp) 'retirement of m tranche'
retirem1(m,tp) 'retirement of m tranche'
cbal(tp) 'cashflow balance'
tppdef(i) 'definition of tpp'
lowal(n) 'lower bound on weighted average life'
upwal(n) 'upper bound on weighted average life'
seq1(tp) 'sequencing constraint 1'
seq2(i,tp) 'sequencing constraint 2'
ydef(i,tp) 'definition of y'
tindef1(i) 'tranche in definition'
tindef2(i) 'tranche in definition'
mcon 'constraint on number of m tranches'
ncon 'constraint on number of tranches';
obj.. proceeds =e= sum(i, pv(i)) + pvres;
defpv(i).. pv(i) =e= sum(zpos(i,t), c(i,t)*(1 + yield(i))**(-ord(t)));
defpvres.. pvres =e= sum(t, r(t)*(1 + yr)**(-ord(t)));
pdef(zpos(i,t(tp))).. p(i,tp) =e= x(i,tp-1) - x(i,tp);
cdef(zpos(i,t(tp))).. c(i,tp) =e= coupon(i)*x(i,tp-1) + p(i,tp);
retiren1(t(tp)).. sum(zpos(n,tp), p(n,tp)) =e= prin(tp) + sum(m, x(m,tp-1)*coupon(m) - c(m,tp));
retire(zpos(n,t)).. p(n,t) =l= cflow(t)*z(n,t);
retirem(m,t).. c(m,t) =l= cflow(t)*z(m,t);
retirem1(m,t).. p(m,t) =l= prin(t)*z(m,t);
cbal(t).. sum(zpos(i,t), c(i,t)) + r(t) =e= cflow(t);
tppdef(n).. tpp(n) =e= sum(zpos(n,t), ord(t)*p(n,t));
lowal(n).. wallo(n)*sum(ts, x(n,ts)) =l= tpp(n);
upwal(n).. walup(n)*sum(ts, x(n,ts)) =g= tpp(n);
seq1(t).. sum(zpos(i,t), z(i,t)) =e= 1;
seq2(zpos(i,t)).. y(i,t) =g= y(i,t+1);
ydef(zpos(i,t)).. y(i,t) =e= y(i-1,t) + z(i,t);
tindef1(i).. sum(ts, x(i,ts)) =l= tin(i)*bign;
tindef2(i).. sum(ts, x(i,ts)) =g= tin(i)*smalln;
ncon.. sum(n, tin(n)) =g= minn;
mcon.. sum(m, tin(m)) =g= minm;
Model cmo 'structuring model' / all /;
p.lo(n,tp) = 0;
x.fx(i,tl) = 0;
x.fx(i,t)$(not zpos(i,t+1)) = 0;
z.fx(i,tp)$(not zpos(i,tp)) = 0;
z.fx(m,tp)$(not sum(n, zpos(n,tp))) = 1;
option optCr = 0.001;
Parameter
xx(tp,i) 'principals'
zs(t,i) 'binary time increments'
cs(t,i) 'cashflows';
solve cmo maximizing proceeds using mip;
xx(tp,i) = x.l(i,tp);
zs(t,i) = z.l(i,t);
cs(t,i) = c.l(i,t);
display zs, xx, cs, r.l;
Parameter report;
report(t,n) = z.l(n,t);
report("value",i) = sum(ts, x.l(i,ts));
report("avl",n)$report("value",n) = tpp.l(n)/report("value",n)/d;
report("pvalue",i) = pv.l(i);
report("pvalue","resid") = pvres.l;
report("pvalue","total") = proceeds.l;
display report;