/// <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");
                    }
                }
            }
        }
Пример #2
0
        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");
                }
            }
        }