/// <summary> /// Constructor for the Cox-Ingersoll-Ross Calibration Problem based on caps matrices, /// using an <see cref="InterestRateMarketData"/> to derive the required data. /// </summary> /// <param name="irmd"> /// An <see cref="InterestRateMarketData"/> containing the /// required information for the optimization problem. /// </param> public CapCIROptimizationProblem(InterestRateMarketData irmd) { this.capMaturity = irmd.CapMaturity; this.capRate = irmd.CapRate; this.tau = irmd.CapTenor; PFunction zr = new PFunction(null); zr.m_Function.iType = DVPLUtils.EInterpolationType.LINEAR; double[,] zrval = (double[, ])ArrayHelper.Concat(irmd.ZRMarketDates.ToArray(), irmd.ZRMarket.ToArray()); zr.Expr = zrval; this.r0 = zr.Evaluate(0.0); BlackModel bm = new BlackModel(zr); this.blackCaps = new Matrix(this.capMaturity.Length, this.capRate.Length); for (int i = 0; i < this.capMaturity.Length; i++) { for (int j = 0; j < this.capRate.Length; j++) { if (irmd.CapVolatility[i, j] == 0) { this.blackCaps[i, j] = 0; } else { this.blackCaps[i, j] = bm.Cap(this.capRate[j], irmd.CapVolatility[i, j], this.tau, this.capMaturity[i]); } if (double.IsNaN(this.blackCaps[i, j])) { throw new Exception("Error on cap market price calculation"); } } } }
public void TestCalibration() { InterestRateMarketData IData = InterestRateMarketData.FromFile("../../TestData/IRMD-sample.xml"); CallPriceMarketData HData = CallPriceMarketData.FromFile("../../TestData/CallData-sample.xml"); //InterestRateMarketData IData = InterestRateMarketData.FromFile("../../../EquityModels.Tests/TestData/IRMD-EU-30102012-close.xml"); //CallPriceMarketData HData = CallPriceMarketData.FromFile("../../../EquityModels.Tests/TestData/30102012-SX5E_Index-HestonData.xml"); //CallPriceMarketData HData = ObjectSerialization.ReadFromXMLFile("../../../EquityModels.Tests/TestData/FTSE.xml") as CallPriceMarketData; List <object> l = new List <object>(); l.Add(IData.DiscountingCurve); l.Add(HData); DupireEstimator DE = new DupireEstimator(); DupireCalibrationSettings settings = new DupireCalibrationSettings(); settings.LocalVolatilityCalculation = LocalVolatilityCalculation.Method1; //settings.LocalVolatilityCalculation = LocalVolatilityCalculation.QuantLib; EstimationResult res = DE.Estimate(l, settings); //int nmat = HData.Maturity.Length; //int nstrike = HData.Strike.Length; int i = 5; // Maturity. int j = 4; // Strike. Engine.MultiThread = true; Document doc = new Document(); ProjectROV rov = new ProjectROV(doc); doc.Part.Add(rov); doc.DefaultProject.NMethods.m_UseAntiteticPaths = true; int n_sim = 10000; int n_steps = 500; double strike = HData.Strike[j]; //double volatility = HData.Volatility[i, j]; /* * PFunction2D.PFunction2D impvolfunc = new PFunction2D.PFunction2D(rov); * impvolfunc = res.Objects[3] as PFunction2D.PFunction2D; * impvolfunc.VarName = "impvol"; * rov.Symbols.Add(impvolfunc); * double volatility = impvolfunc.Evaluate(HData.Maturity[i], HData.Strike[j]); */ double volatility = 0.2; double maturity = HData.Maturity[i]; ModelParameter Pstrike = new ModelParameter(strike, string.Empty, "strike"); rov.Symbols.Add(Pstrike); AFunction payoff = new AFunction(rov); payoff.VarName = "payoff"; payoff.m_IndependentVariables = 1; payoff.m_Value = (RightValue)("max(x1 - strike ; 0)"); rov.Symbols.Add(payoff); bool found; double S0 = PopulateHelper.GetValue("S0", res.Names, res.Values, out found); ModelParameter PS0 = new ModelParameter(S0, string.Empty, "S0"); rov.Symbols.Add(PS0); PFunction rfunc = new PFunction(rov); rfunc = res.Objects[0] as PFunction; rfunc.VarName = "r"; rov.Symbols.Add(rfunc); PFunction qfunc = new PFunction(rov); qfunc = res.Objects[1] as PFunction; qfunc.VarName = "q"; rov.Symbols.Add(qfunc); PFunction2D.PFunction2D volfunc = new PFunction2D.PFunction2D(rov); volfunc = res.Objects[2] as PFunction2D.PFunction2D; volfunc.VarName = "localvol"; rov.Symbols.Add(volfunc); DupireProcess process = new DupireProcess(); process.s0 = (ModelParameter)"S0"; process.r = (ModelParameter)"@r"; process.q = (ModelParameter)"@q"; process.localVol = (ModelParameter)"@localvol"; double rate = rfunc.Evaluate(maturity); double dy = qfunc.Evaluate(maturity); StochasticProcessExtendible s = new StochasticProcessExtendible(rov, process); rov.Processes.AddProcess(s); // Set the discounting. RiskFreeInfo rfi = rov.GetDiscountingModel() as RiskFreeInfo; rfi.ActualizationType = EActualizationType.RiskFree; rfi.m_deterministicRF = rate; OptionTree op = new OptionTree(rov); op.PayoffInfo.PayoffExpression = "payoff(v1)"; op.PayoffInfo.Timing.EndingTime.m_Value = (RightValue)maturity; op.PayoffInfo.European = true; rov.Map.Root = op; rov.NMethods.Technology = ETechType.T_SIMULATION; rov.NMethods.PathsNumber = n_sim; rov.NMethods.SimulationSteps = n_steps; ROVSolver solver = new ROVSolver(); solver.BindToProject(rov); solver.DoValuation(-1); if (rov.HasErrors) { rov.DisplayErrors(); } Assert.IsFalse(rov.HasErrors); ResultItem price = rov.m_ResultList[0] as ResultItem; double samplePrice = price.value; double sampleDevSt = price.stdDev / Math.Sqrt((double)n_sim); Console.WriteLine("Surf = " + volfunc.Expr); // Calculation of the theoretical value of the call. double theoreticalPrice = BlackScholes.Call(rate, S0, strike, volatility, maturity, dy); Console.WriteLine("Theoretical Price = " + theoreticalPrice.ToString()); Console.WriteLine("Monte Carlo Price = " + samplePrice); Console.WriteLine("Standard Deviation = " + sampleDevSt.ToString()); double tol = 4.0 * sampleDevSt; doc.WriteToXMLFile("Dupire.fair"); Assert.LessOrEqual(Math.Abs(theoreticalPrice - samplePrice), tol); }
/// <summary> /// Sets several variables used to solve the optimization problem. /// </summary> /// <param name="callMarketPrice">A matrix containing call option market prices.</param> /// <param name="maturity"> /// Vector of call option maturities relative to callMarketPrice matrix. /// </param> /// <param name="strike"> /// Vector of call option strikes relative to callMarketPrice matrix. /// </param> /// <param name="rate"> /// Vector of zero coupon bond rates calculated relative to maturity vector maturities. /// </param> /// <param name="dividendYield"> /// Vector of dividend yield rates calculated relative to maturity vector maturities. /// </param> /// <param name="s0">Index/Equity value at the time of calibration.</param> /// <param name="matBound"> /// A vector containing the minimum and maximum values /// for maturities to be used in calibration. /// </param> /// <param name="strikeBound"> /// A vector containing the minimum and maximum values /// for strikes to be used in calibration.</param> private void SetVariables(Matrix callMarketPrice, Vector maturity, Vector strike, Vector rate, Vector dividendYield, double s0) { this.s0 = s0; //var rf = new PFunction(maturity, rate); var dy = new PFunction(maturity, dividendYield); //var rfF = new Fairmat.Math.Integrate(x => rf.Evaluate(x)); var dyF = new Fairmat.Math.Integrate(x => dy.Evaluate(x)); this.rate = new Vector(maturity.Length); this.dividendYield = new Vector(maturity.Length); for (int z = 0; z < maturity.Length; z++) { //this.rate[z] = rfF.AdaptLobatto(0, maturity[z]) / maturity[z]; this.dividendYield[z] = dyF.AdaptLobatto(0, maturity[z]) / maturity[z]; } this.rate = rate; this.maturity = maturity; //this.drift = this.rate - this.dividendYield; this.strike = strike; this.callMarketPrice = callMarketPrice; this.numCall = 0; callWeight = new Matrix(this.callMarketPrice.R, this.callMarketPrice.C); putWeight = new Matrix(this.callMarketPrice.R, this.callMarketPrice.C); for (int r = 0; r < this.callMarketPrice.R; r++) { if (this.maturity[r] >= matBound[0] && this.maturity[r]<= matBound[1]) { for (int c = 0; c < this.callMarketPrice.C; c++) { if (this.strike[c] >= s0 * strikeBound[0] && this.strike[c] <= s0 * strikeBound[1]) { if (calibrateOnCallOptions) if (this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0) { this.callWeight[r, c] = CalculateWeight(this.cpmd.CallVolume[r, c]); this.numCall++; this.totalVolume += CalculateWeight(this.cpmd.CallVolume[r, c]) ; } if (calibrateOnPutOptions) if (this.cpmd.PutPrice != null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0) { this.putWeight[r, c] = CalculateWeight(this.cpmd.PutVolume[r, c]); this.numPut++; this.totalVolume += CalculateWeight(this.cpmd.PutVolume[r, c]); } } } } } //calibrate minVolatility: actually in this.cpmd.Volatility there is sigma not sigma^2 if (this.cpmd.Volatility != null) { //Rows maturities, columns strikes vLastMin = 0.5 * Math.Pow(this.cpmd.Volatility.Min().Min(), 2); v0Min = 0.99 * Math.Pow(this.cpmd.Volatility[0, Range.All].Min().Min(), 2); v0Max = 1.01 * Math.Pow(this.cpmd.Volatility[0, Range.All].Max().Max(), 2); } Console.WriteLine("Options weighting:\t" + weighting); Console.WriteLine("OptionThreshold:\t" + optionThreshold); Console.WriteLine("Strike Bounds:\t" + strikeBound); Console.WriteLine("Maturity Bounds:\t" + matBound); Console.WriteLine("Lb:\t" + Bounds.Lb); Console.WriteLine("Ub:\t" + Bounds.Ub); if(Engine.Verbose>=2) PutCallTest(); }
/// <summary> /// Calculates call put prices for several strikes using controlled interpolation. /// </summary> /// <param name="context"></param> private void CalculateSingleRowWithInterpolation(object context) { HestonCall hc = context as HestonCall; int r = hc.row; hc.sum = 0; // Finds upper extreme for call and put int max_c =0; for (int c = this.callMarketPrice.C-1; c > 0; c--) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice!=null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition || putCondition) { max_c = c; break; } } var strikes = new List<double>(); var calls = new List<double>(); var puts = new List<double>(); //Evaluates in strategic points for (int c = 0; c < this.callMarketPrice.C; c++) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice!=null&& this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition || putCondition) { hc.K = this.strike[c]; var callPut = hc.HestonCallPutPrice(); strikes.Add(hc.K); calls.Add(callPut[0]); puts.Add(callPut[1]); if (c == max_c) break; c += 1;//skip the subsequent strikes if (c > max_c) c = max_c; } } // Builds interpolated call and put values. var callFun = new PFunction((Vector)strikes.ToArray(), (Vector)calls.ToArray()); callFun.m_Function.iType = DVPLUtils.EInterpolationType.SPLINE; var putFun = new PFunction((Vector)strikes.ToArray(), (Vector)puts.ToArray()); putFun.m_Function.iType = DVPLUtils.EInterpolationType.SPLINE; // Evaluates at the requested strikes for (int c = 0; c < this.callMarketPrice.C; c++) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice!=null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition) { hc.hestonCallPrice[r, c] = callFun.Evaluate(this.strike[c]); if (HestonCallOptimizationProblem.optimizeRelativeError) { double mkt = pricingMin + this.callMarketPrice[r, c]; double model = pricingMin + hc.hestonCallPrice[r, c]; hc.sum += callWeight[r,c] * Math.Pow((model - mkt) / mkt, 2); } else { hc.sum += callWeight[r, c] * Math.Pow(hc.hestonCallPrice[r, c] - this.callMarketPrice[r, c], 2); } } if (putCondition) { hc.hestonPutPrice[r, c] = putFun.Evaluate(this.strike[c]); if (HestonCallOptimizationProblem.optimizeRelativeError) { double mkt = pricingMin + this.cpmd.PutPrice[r, c]; double model = pricingMin + hc.hestonPutPrice[r, c]; hc.sum += putWeight[r, c] * Math.Pow((model - mkt) / mkt, 2); } else { hc.sum += putWeight[r, c] * Math.Pow(hc.hestonPutPrice[r, c] - this.cpmd.PutPrice[r, c], 2); } } } return; }
/// <summary> /// Calculates call put prices for several strikes using controlled interpolation. /// </summary> /// <param name="context"></param> private void CalculateSingleRowWithInterpolation(object context) { HestonCall hc = context as HestonCall; int r = hc.row; hc.sum = 0; // Finds upper extreme for call and put int max_c = 0; for (int c = this.callMarketPrice.C - 1; c > 0; c--) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice != null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition || putCondition) { max_c = c; break; } } var strikes = new List <double>(); var calls = new List <double>(); var puts = new List <double>(); //Evaluates in strategic points for (int c = 0; c < this.callMarketPrice.C; c++) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice != null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition || putCondition) { hc.K = this.strike[c]; var callPut = hc.HestonCallPutPrice(); strikes.Add(hc.K); calls.Add(callPut[0]); puts.Add(callPut[1]); if (c == max_c) { break; } c += 1;//skip the subsequent strikes if (c > max_c) { c = max_c; } } } // Builds interpolated call and put values. var callFun = new PFunction((Vector)strikes.ToArray(), (Vector)calls.ToArray()); callFun.m_Function.iType = DVPLUtils.EInterpolationType.SPLINE; var putFun = new PFunction((Vector)strikes.ToArray(), (Vector)puts.ToArray()); putFun.m_Function.iType = DVPLUtils.EInterpolationType.SPLINE; // Evaluates at the requested strikes for (int c = 0; c < this.callMarketPrice.C; c++) { bool callCondition = this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0; bool putCondition = this.cpmd.PutPrice != null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0; if (callCondition) { hc.hestonCallPrice[r, c] = callFun.Evaluate(this.strike[c]); if (HestonCallOptimizationProblem.optimizeRelativeError) { double mkt = pricingMin + this.callMarketPrice[r, c]; double model = pricingMin + hc.hestonCallPrice[r, c]; hc.sum += callWeight[r, c] * Math.Pow((model - mkt) / mkt, 2); } else { hc.sum += callWeight[r, c] * Math.Pow(hc.hestonCallPrice[r, c] - this.callMarketPrice[r, c], 2); } } if (putCondition) { hc.hestonPutPrice[r, c] = putFun.Evaluate(this.strike[c]); if (HestonCallOptimizationProblem.optimizeRelativeError) { double mkt = pricingMin + this.cpmd.PutPrice[r, c]; double model = pricingMin + hc.hestonPutPrice[r, c]; hc.sum += putWeight[r, c] * Math.Pow((model - mkt) / mkt, 2); } else { hc.sum += putWeight[r, c] * Math.Pow(hc.hestonPutPrice[r, c] - this.cpmd.PutPrice[r, c], 2); } } } return; }
/// <summary> /// Sets several variables used to solve the optimization problem. /// </summary> /// <param name="callMarketPrice">A matrix containing call option market prices.</param> /// <param name="maturity"> /// Vector of call option maturities relative to callMarketPrice matrix. /// </param> /// <param name="strike"> /// Vector of call option strikes relative to callMarketPrice matrix. /// </param> /// <param name="rate"> /// Vector of zero coupon bond rates calculated relative to maturity vector maturities. /// </param> /// <param name="dividendYield"> /// Vector of dividend yield rates calculated relative to maturity vector maturities. /// </param> /// <param name="s0">Index/Equity value at the time of calibration.</param> /// <param name="matBound"> /// A vector containing the minimum and maximum values /// for maturities to be used in calibration. /// </param> /// <param name="strikeBound"> /// A vector containing the minimum and maximum values /// for strikes to be used in calibration.</param> private void SetVariables(Matrix callMarketPrice, Vector maturity, Vector strike, Vector rate, Vector dividendYield, double s0) { this.s0 = s0; //var rf = new PFunction(maturity, rate); var dy = new PFunction(maturity, dividendYield); //var rfF = new Fairmat.Math.Integrate(x => rf.Evaluate(x)); var dyF = new Fairmat.Math.Integrate(x => dy.Evaluate(x)); this.rate = new Vector(maturity.Length); this.dividendYield = new Vector(maturity.Length); for (int z = 0; z < maturity.Length; z++) { //this.rate[z] = rfF.AdaptLobatto(0, maturity[z]) / maturity[z]; this.dividendYield[z] = dyF.AdaptLobatto(0, maturity[z]) / maturity[z]; } this.rate = rate; this.maturity = maturity; //this.drift = this.rate - this.dividendYield; this.strike = strike; this.callMarketPrice = callMarketPrice; this.numCall = 0; callWeight = new Matrix(this.callMarketPrice.R, this.callMarketPrice.C); putWeight = new Matrix(this.callMarketPrice.R, this.callMarketPrice.C); for (int r = 0; r < this.callMarketPrice.R; r++) { if (this.maturity[r] >= matBound[0] && this.maturity[r] <= matBound[1]) { for (int c = 0; c < this.callMarketPrice.C; c++) { if (this.strike[c] >= s0 * strikeBound[0] && this.strike[c] <= s0 * strikeBound[1]) { if (calibrateOnCallOptions) { if (this.callMarketPrice[r, c] > s0 * optionThreshold && this.cpmd.CallVolume[r, c] > 0) { this.callWeight[r, c] = CalculateWeight(this.cpmd.CallVolume[r, c]); this.numCall++; this.totalVolume += CalculateWeight(this.cpmd.CallVolume[r, c]); } } if (calibrateOnPutOptions) { if (this.cpmd.PutPrice != null && this.cpmd.PutPrice[r, c] > s0 * optionThreshold && this.cpmd.PutVolume[r, c] > 0) { this.putWeight[r, c] = CalculateWeight(this.cpmd.PutVolume[r, c]); this.numPut++; this.totalVolume += CalculateWeight(this.cpmd.PutVolume[r, c]); } } } } } } //calibrate minVolatility: actually in this.cpmd.Volatility there is sigma not sigma^2 if (this.cpmd.Volatility != null) { //Rows maturities, columns strikes vLastMin = 0.5 * Math.Pow(this.cpmd.Volatility.Min().Min(), 2); v0Min = 0.99 * Math.Pow(this.cpmd.Volatility[0, Range.All].Min().Min(), 2); v0Max = 1.01 * Math.Pow(this.cpmd.Volatility[0, Range.All].Max().Max(), 2); } Console.WriteLine("Options weighting:\t" + weighting); Console.WriteLine("OptionThreshold:\t" + optionThreshold); Console.WriteLine("Strike Bounds:\t" + strikeBound); Console.WriteLine("Maturity Bounds:\t" + matBound); Console.WriteLine("Lb:\t" + Bounds.Lb); Console.WriteLine("Ub:\t" + Bounds.Ub); if (Engine.Verbose >= 2) { PutCallTest(); } }
/// <summary> /// Constructor for the Cox-Ingersoll-Ross Calibration Problem based on caps matrices, /// using an <see cref="InterestRateMarketData"/> to derive the required data. /// </summary> /// <param name="irmd"> /// An <see cref="InterestRateMarketData"/> containing the /// required information for the optimization problem. /// </param> public CapCIROptimizationProblem(InterestRateMarketData irmd) { this.capMaturity = irmd.CapMaturity; this.capRate = irmd.CapRate; this.tau = irmd.CapTenor; PFunction zr = new PFunction(null); zr.m_Function.iType = DVPLUtils.EInterpolationType.LINEAR; double[,] zrval = (double[,])ArrayHelper.Concat(irmd.ZRMarketDates.ToArray(), irmd.ZRMarket.ToArray()); zr.Expr = zrval; this.r0 = zr.Evaluate(0.0); BlackModel bm = new BlackModel(zr); this.blackCaps = new Matrix(this.capMaturity.Length, this.capRate.Length); for (int i = 0; i < this.capMaturity.Length; i++) { for (int j = 0; j < this.capRate.Length; j++) { if (irmd.CapVolatility[i, j] == 0) this.blackCaps[i, j] = 0; else this.blackCaps[i, j] = bm.Cap(this.capRate[j], irmd.CapVolatility[i, j], this.tau, this.capMaturity[i]); if (double.IsNaN(this.blackCaps[i, j])) throw new Exception("Error on cap market price calculation"); } } }