$TITLE Put/Call efficient frontier model * MAD.gms: Put/Call efficient frontier model. * Consiglio, Nielsen and Zenios. * PRACTICAL FINANCIAL OPTIMIZATION: A Library of GAMS Models, Section 5.7 * Last modified: Apr 2008. $IF NOT set out $SET out PutCallModel $INCLUDE "WorldIndices.inc" OPTION LIMROW=0,LIMCOL=0,SOLVELINK=2; SCALARS Budget Nominal investment budget Omega Bound on the expected shortfalls; Budget = 100.0; PARAMETERS pr(l) Scenario probability P(i,l) Final values EP(i) Expected final values; pr(l) = 1.0 / CARD(l); P(i,l) = 1 + AssetReturns ( i, l ); EP(i) = SUM(l, pr(l) * P(i,l)); PARAMETER TargetIndex(l) Target index returns; * To test the model with a market index, uncomment the following line two lines. * Note that, this index is consistent only when using WorldIndexes.inc. $INCLUDE "Index.inc" TargetIndex(l) = Index(l); * Primal of the UnConstrained Put/Call model PARAMETERS LowerBounds(i) UpperBounds(i); POSITIVE VARIABLES yPos(l) Positive deviations yNeg(l) Negative deviations; VARIABLES x(i) Holdings of assets in monetary units (not proportions) z Objective function value; EQUATIONS BudgetCon Equation defining the budget contraint ObjDef Objective function definition for MAD TargetDevDef(l) Equations defining the positive and negative deviations PutCon Constraint to bound the expected value of the negative deviations ; BudgetCon .. SUM(i, x(i)) =E= Budget; PutCon .. SUM(l, pr(l) * yNeg(l) ) =L= Omega; TargetDevDef(l).. SUM(i, ( P(i,l) - TargetIndex(l) ) * x(i) ) =E= yPos(l) - yNeg(l); ObjDef .. z =E= SUM(l, pr(l) * yPos(l)); MODEL UnConPutCallModel 'Model PFO 5.7.1' / PutCon, TargetDevDef, ObjDef /; * Set the average level of downside (risk) allowed Omega = 0.1; SOLVE UnConPutCallModel MAXIMIZING z USING LP; * Dual of the UnConstrained Put/Call model POSITIVE VARIABLES Pi(l) PiOmega; EQUATIONS DualObjDef DualTrackingDef(i) MeasureDef(l); DualObjDef .. z =E= Omega * PiOmega; DualTrackingDef(i).. SUM(l, (P(i,l) - TargetIndex(l)) * Pi(l)) =E= 0.0; MeasureDef(l).. pr(l) * PiOmega - Pi(l) =G= 0; Pi.LO(l) = pr(l); MODEL UnConDualPutCallModel 'Model PFO 5.7.2' / DualObjDef, DualTrackingDef, MeasureDef /; SOLVE UnConDualPutCallModel MINIMIZING z USING LP; * Display PiOmega.l and Pi.l and check that they are, respectively, equal * to TargetDevDef.m and PutCon.m * GAMS provides the dual prices directly, so it is not * really necessary to build explicitly the dual model. PARAMETER PrimalDual(l,*) Compare primal and dual soultions; PrimalDual(l,'pi.l') = - pi.l(l); PrimalDual(l,'TargetDevDef.m') = TargetDevDef.m(l); PrimalDual(l,'Difference') = TargetDevDef.m(l)+pi.l(l); DISPLAY z.l,PiOmega.l,PutCon.m,PrimalDual; * We propose an alternative way to build a frontier using * the loop statement. Such a structure is suitable for the * GDX utility (for details, see gdxutility.pdf included in the doc folder ) OPTION SOLPRINT = OFF; SET FrontierPoints /P_1 * P_50/; * Number of points in the frontier ALIAS ( FrontierPoints, j ); PARAMETER FrontierPortfolios(j,i) Frontier portfolios CallValues(j,*) Call values DualPrices(j,*) Dual prices PutCall(j,*) Put and Call values OmegaLevels(j) Risk levels (Omega); FILE temp file handle / temp.txt /; * We assign to each point a risk level Omega OmegaLevels('P_1') = 0.01; LOOP (j$(ORD(j) > 1), OmegaLevels(j) = OmegaLevels(j-1) + (0.01)$(ORD(j) <= 10) + (0.025)$(ORD(j) > 10) ); Dualprices(j,'Omega') = OmegaLevels(j); CallValues(j,'Omega') = OmegaLevels(j); * Set some liquidity constraints x.LO(i) = -100.0; x.UP(i) = 100.0; LOOP (j, Omega = OmegaLevels(j); SOLVE UnConPutCallModel MAXIMIZING z USING LP; FrontierPortfolios(j,i) = x.L(i); CallValues(j,'Mild Constraint') = z.L; Dualprices(j,'Mild Constraint') = PutCon.M ); embeddedCode Connect: - GAMSReader: symbols: [ { name: FrontierPortfolios } ] - ExcelWriter: file: %out%.xlsx symbols: [ { name: FrontierPortfolios, range: MildPortfolios!A1 } ] endEmbeddedCode * Set tight liquidity constraints x.LO(i) = -20.0; x.UP(i) = 20.0; LOOP (j, Omega = OmegaLevels(j); SOLVE UnConPutCallModel MAXIMIZING z USING LP; FrontierPortfolios(j,i) = x.L(i); CallValues(j,'Tight Constraint') = z.L; Dualprices(j,'Tight Constraint') = PutCon.M ); embeddedCode Connect: - GAMSReader: symbols: [ { name: DualPrices }, { name: CallValues }, { name: FrontierPortfolios } ] - ExcelWriter: file: %out%.xlsx symbols: - { name: DualPrices, range: DualPrices!A1 } - { name: CallValues, range: CallValues!A1 } - { name: FrontierPortfolios, range: TightPortfolios!A1 } endEmbeddedCode * Determine the liquidity and discount premium * for one put/call efficient portfolio. SCALAR Df; PARAMETER Price(i) Discount(i) Premium(i) BenchMarkNeutralPrice(i) Psi(l); Omega = 0.475; SOLVE UnConPutCallModel MAXIMIZING z USING LP; Discount(i) = 0.0; Premium(i) = 0.0; Df = SUM(l, -TargetDevDef.M(l) ); Psi(l) = -TargetDevDef.M(l) / Df; BenchMarkNeutralPrice(i) = SUM(l, Psi(l) * P(i,l)) / SUM(l, Psi(l) * TargetIndex(l)); Discount(i)$(x.M(i) > 0) = x.M(i) / SUM(l, (-TargetDevDef.M(l)) * TargetIndex(l)); Premium(i)$(x.M(i) < 0) = (-x.M(i)) / SUM(l, (-TargetDevDef.M(l)) * TargetIndex(l)); Price(i) = BenchMarkNeutralPrice(i) + Premium(i) - Discount(i); PARAMETER liquidity(i,*) Liquidity report; liquidity(i,'Premium') = Premium(i); liquidity(i,'Discount') = Discount(i); embeddedCode Connect: - GAMSReader: symbols: [ { name: liquidity } ] - ExcelWriter: file: %out%.xlsx symbols: [ { name: liquidity, range: Liquidity!A1 } ] endEmbeddedCode DISPLAY Df,Psi,BenchMarkNeutralPrice,Discount,Premium,Price; * Put/call model with balance constraint MODEL PutCallModel 'Model PFO 5.7.3' / BudgetCon, PutCon, TargetDevDef, ObjDef /; LOOP (j, Omega = OmegaLevels(j); SOLVE PutCallModel MAXIMIZING z USING LP; FrontierPortfolios(j,i) = x.L(i); putcall(j,'Put side') = PutCon.L; putcall(j,'Call side')= z.L ); embeddedCode Connect: - GAMSReader: symbols: [ { name: PutCall }, { name: FrontierPortfolios } ] - ExcelWriter: file: %out%.xlsx symbols: - { name: PutCall, range: Frontiers!A1 } - { name: FrontierPortfolios, range: Portfolios!A1 } endEmbeddedCode