Implementation
The following Mosel model implements the mathematical model from the previous section. Notice how the ellipsoid uncertainty set is added to the original problem to create a robust optimization problem.
Also, take a look at how the Monte-Carlo simulation method is applied to quantify the quality of the solution: in NMC=5000 iterations we draw random values for the expected value of every asset by applying a normal distribution centered around its price (mean value) and using its known variance. We count the occurrence of objective function values (or more precisely, which value range the resulting solution value belongs to) to determine the probabilities of different solution qualities. This method is particularly helpful to gain some insight about the solution quality when it is difficult to determine the exact shape of the distribution function of a random variable.
model "Robust portfolio optimization" uses "mmrobust", "random" uses "mmsystem" parameters ZEROTOL=1e-6 SEED=12345 NMC=500000 N=1.5 ! Worst case metric end-parameters declarations Problems: set of mpproblem ProtectLevel: set of real mp_problemA: mpproblem ! Worst case optimization mp_problemB: array(ProtectLevel) of mpproblem ! Robust optimization NSHARES = 25 ! Max number of shares Shares = 1..NSHARES ! Set of shares PRICE: array(Shares) of real ! Expected return on investment VAR: array(Shares) of real ! Uncertainty measure (deviation) per SHARE expReturn: mpvar ! Expected portfolio value wstReturn: mpvar ! Worst case portfolio value frac: array(Shares) of mpvar ! Fraction of capital used per share frac_sol: array(Shares,Problems) of real obj: mpvar e: array(Shares) of uncertain ! Deviation of share values PRICEthr: array(Problems,Shares) of real ! The price threshold end-declarations !***************************Configuration************************* setparam("XPRS_LOADNAMES",true) !***************************Subroutines*************************** !**** Create the nominal model **** procedure create_nominalmodel ! Nominal model expReturn = (sum(s in Shares) PRICE(s)*frac(s)) wstReturn = sum(s in Shares) ( PRICE(s) - N*VAR(s) )*frac(s) ! Spend all the budget sum(s in Shares) frac(s) = 1 end-procedure !**** Optimize for the worst case realization **** procedure solve_det obj = wstReturn maximize(obj) end-procedure !**** Optimize for a given protection level **** procedure solve_rob(k: real) ! The value variation domain assert(k>=0 and k<=1) sum(s in Shares) (e(s) / (N*VAR(s)))^2 <= (k)^2 ! The robust constraint obj <= sum(s in Shares) (PRICE(s) + e(s)) * frac(s) maximize(obj) end-procedure !***************************Main model*************************** !**** Input data generation **** setmtrandseed(12345) cnt := 0 forall(s in Shares, cnt as counter) do PRICE(s) := round((100*(NSHARES-cnt+1)/NSHARES)) c := (1+sqrt((NSHARES-s)/NSHARES))/3 VAR(s) := round(PRICE(s)*c*100)/100 end-do writeln("\n *** Input Data *** ") writeln writeln("Shares | Mean | S.Dev. | Worst value") forall(s in Shares) writeln(strfmt(s,6), " |", strfmt(PRICE(s),5), " |", strfmt(VAR(s),7,2), " |", strfmt(PRICE(s)-N*VAR(s),6,3)) writeln !**** Optimize the worst case **** with mp_problemA do create_nominalmodel solve_det forall(s in Shares) frac_sol(s,mp_problemA) := frac(s).sol*100 ! Price set by opponent is worst case price forall(s in Shares) PRICEthr(mp_problemA,s) := PRICE(s) - N*VAR(s) end-do !**** Optimize the 'budgeted' worst case **** Ks := [0, ! No variation on average 0.01, ! +/- 1% variation on the list 0.10, ! +/- 10% variation on the list 0.25, ! +/- 25% 1] ! +/-100% variation on the list forall(k in Ks) do create(mp_problemB(k)) with mp_problemB(k) do create_nominalmodel solve_rob(k) forall(s in Shares) frac_sol(s,mp_problemB(k)) := frac(s).sol*100 forall(s in Shares) PRICEthr(mp_problemB(k),s) := (PRICE(s) + getsol(e(s))) end-do end-do !**** Print results **** writeln("\n *** Portfolio allocation results *** ") write("\n | protection level") write("\nShares | Wst price | Price | A |") forall(k in Ks) write(" ", strfmt(k*100,3), "% |" ) ; writeln forall(s in Shares) do write(strfmt(s,6), " |", strfmt(PRICE(s)-N*VAR(s),10,0), " |", strfmt(PRICE(s),6), " | ") forall(mp in Problems) do if (frac_sol(s,mp)>1) then write(strfmt(frac_sol(s,mp),3,0), "% | ") else write(" | ") end-if end-do writeln end-do writeln("\n *** Worst case price *** ") write("\n | protection level") write("\nShares | Wst price | Price | A |") forall(k in Ks) write(" ", strfmt(k*100,3), "% |" ) ; writeln forall(s in Shares) do write(strfmt(s,6), " |", strfmt(PRICE(s)-N*VAR(s),10,0), " |", strfmt(PRICE(s),6), " | ") forall(mp in Problems) do if (frac_sol(s,mp)>1) then write(strfmt(PRICEthr(mp,s),3,0), "* | ") else write(strfmt(PRICEthr(mp,s),4,0), " | ") ! write(" | ") end-if end-do writeln end-do !**** Simulate results (Monte-Carlo simulation) **** writeln("\nRunning Monte-Carlo simulation...") Values := {0,20,40,60,80,100} declarations cntV: dynamic array(Problems,Values) of real s1: real s2: real end-declarations forall(mp in Problems) do expRev(mp) := 0.0 ; wstRev(mp) := 0.0 ; expDev(mp) := 0.0 forall(v in Values) cntV(mp,v) := 0 c := 0.0 ! Number of draws s1 := 0 ; s2 := 0; forall(i in 1..NMC, c as counter) do totalVal := 0.0 ; totalRisk := 0.0 forall(s in Shares | frac_sol(s,mp)>0) do ! draw a price p := normal(PRICE(s),VAR(s)) ! calculate share revenue r := frac_sol(s,mp)*p/100 ! calculate total revenue totalVal += r ! summing up revenue end-do forall(v in Values) do if (totalVal>v) then cntV(mp,v) += 1 end-if end-do expRev(mp) += totalVal s1 += totalVal s2 += totalVal^2 end-do expRev(mp) := expRev(mp) / c expDev(mp) := sqrt(NMC*s2 - s1^2)/(NMC) forall(v in Values) cntV(mp,v) := cntV(mp,v) / c end-do !**** Print simulation results **** write("\n | protection level") write("\n | A |") forall(k in Ks) write(" ", strfmt(k*100,3), "% |" ) write("\n Expected value |") forall(mp in Problems) write(strfmt(round(expRev(mp)),5), " |") write("\n Standard deviation |") forall(mp in Problems) write(strfmt(round(expDev(mp)),5), " |") writeln forall(v in Values) do write("\n P (value>"+textfmt(v,3)+" |") forall(mp in Problems) write(strfmt(cntV(mp,v),5,2) , " |") end-do end-model
© 2001-2020 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.