Esempio n. 1
0
        public override IPricingResult Calculate(BondFutures bondFuture, IMarketCondition market, PricingRequest request)
        {
            var beginValuation = DateTime.Now;
            var pricingRequest = CheckParameterCondition(bondFuture, market, request);

            //if (result.IsRequested(PricingRequest.Dv01))
            //{
            //	result.Dv01 = CalcDv01(bondFuture, market);
            //}

            var result = new PricingResult(market.ValuationDate, pricingRequest);

            //if (result.IsRequested(PricingRequest.Pv))
            //{
            //    result.Pv = CalcPv(bondFuture, market);
            //}

            if (result.IsRequested(PricingRequest.DirtyPrice))
            {
                result.DirtyPrice = market.MktQuote.Value.Where(x => x.Key == bondFuture.Id).Select(x => x.Value.Item2).First() * bondFuture.Notional / 100;
            }

            if (result.IsRequested(PricingRequest.ConvertFactors))
            {
                result.ConvertFactors = CalcConvertFactors(bondFuture, market);
            }

            if (result.IsRequested(PricingRequest.Irr) || result.IsRequested(PricingRequest.Pv01) ||
                result.IsRequested(PricingRequest.KeyRateDv01) ||
                result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.ZeroSpreadDelta) ||
                result.IsRequested(PricingRequest.UnderlyingPv) || result.IsRequested(PricingRequest.Basis) ||
                result.IsRequested(PricingRequest.Convexity) || result.IsRequested(PricingRequest.Ytm) ||
                result.IsRequested(PricingRequest.CheapestToDeliver) ||
                result.IsRequested(PricingRequest.ModifiedDuration) || result.IsRequested(PricingRequest.MacDuration)
                )
            {
                //TODO:  wierd update logic, why bother?
                var mktQuote     = market.MktQuote.Value.Keys.Where(quoteKey => !quoteKey.Contains(bondFuture.Id + "_")).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]);
                var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote));
                var yieldPricer  = new BondFuturesYieldPricer(bondFuture, updateMarket);

                //calculate convert factors
                if (!result.ConvertFactors.Any())
                {
                    result.ConvertFactors = CalcConvertFactors(bondFuture, market);
                }
                var convertFactors = result.ConvertFactors;

                //calculate IRR
                result.ProductSpecific = yieldPricer.CalcEquation("FromFuturesPriceAndBondPrice");

                //calculate pv01
                var maxIrr    = result.ProductSpecific["Irr"].Values.Select(x => x.Rate).Max();
                var ctdBondId = result.ProductSpecific["Irr"].First(x => x.Value.Rate == maxIrr).Key;
                var ctdBond   = bondFuture.Deliverables.First(x => x.Id == ctdBondId);
                var cf        = convertFactors[ctdBondId];
                var scaling   = bondFuture.Notional / (100.0 * cf);

                var engine = new BondEngineCn();

                result.CheapestToDeliver = ctdBondId;

                //two risks here are CTD risk, not bond futures risk
                var resultCTD = engine.Calculate(ctdBond, market, PricingRequest.All);
                result.ZeroSpread   = resultCTD.ZeroSpread;
                result.UnderlyingPv = resultCTD.Pv * scaling;
                result.Basis        = yieldPricer.CalcFutureCtdBasis(ctdBond, cf);
                result.Ytm          = resultCTD.Ytm;
                result.MacDuration  = resultCTD.MacDuration;

                result.ModifiedDuration       = resultCTD.ModifiedDuration;
                result.Convexity              = resultCTD.Convexity;
                result.DollarConvexity        = resultCTD.DollarConvexity * scaling;        // 1% price impact
                result.DollarModifiedDuration = resultCTD.DollarModifiedDuration * scaling; // same order of magnitutude of CTD dollar modifiedDuration, good for pnl attribution

                //convert to bond futures risk
                result.Pv01 = resultCTD.Pv01 * scaling;   // underlying pv01 is
                foreach (var kvp in resultCTD.KeyRateDv01)
                {
                    foreach (var risk in kvp.Value)
                    {
                        risk.Risk *= scaling;
                    }
                }
                result.KeyRateDv01     = resultCTD.KeyRateDv01;
                result.ZeroSpreadDelta = resultCTD.ZeroSpreadDelta * scaling;
            }
            if (result.IsRequested(PricingRequest.FairQuote))
            {
                var mktQuote     = market.MktQuote.Value.Keys.Where(quoteKey => !quoteKey.Equals(bondFuture.Id)).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]);
                var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote));
                var yieldPricer  = new BondFuturesYieldPricer(bondFuture, updateMarket);
                result.ProductSpecific = yieldPricer.CalcEquation("FromBondPriceAndIrr");
            }
            if (result.IsRequested(PricingRequest.MktQuote))
            {
                result.ProductSpecific = CalcMktFuturePrice(bondFuture, market);
            }
            if (result.IsRequested(PricingRequest.UnderlyingFairQuote))
            {
                var mktQuote     = market.MktQuote.Value.Keys.Where(quoteKey => quoteKey.Contains(bondFuture.Id)).ToDictionary(quoteKey => quoteKey, quoteKey => market.MktQuote.Value[quoteKey]);
                var updateMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, mktQuote));
                var yieldPricer  = new BondFuturesYieldPricer(bondFuture, updateMarket);
                result.ProductSpecific = yieldPricer.CalcEquation("FromFuturesPriceAndIrr");
            }


            var endValuation = DateTime.Now;

            result.CalcTimeInMilliSecond = (endValuation - beginValuation).TotalMilliseconds;
            return(result);
        }
Esempio n. 2
0
        private Dictionary <string, Dictionary <string, RateRecord> > CalcMktFuturePrice(BondFutures bondFuture, IMarketCondition market)
        {
            var psDict       = new Dictionary <string, Dictionary <string, RateRecord> >();
            var fundingCurve = market.RiskfreeCurve.HasValue
                                ? market.RiskfreeCurve.Value
                                : YieldCurve.GetConstRateCurve(market.DiscountCurve.Value, 0.0);
            var reinvestmentCurve = market.DiscountCurve.HasValue
                                ? market.DiscountCurve.Value
                                : YieldCurve.GetConstRateCurve(market.RiskfreeCurve.Value, 0.0);

            var bonds  = bondFuture.Deliverables;
            var length = bonds.Length;

            var aiAtStart = bonds.Select(x => x.GetAccruedInterest(market.ValuationDate, market, false)).ToArray();
            var aiAtEnd   = bonds.Select(x => x.GetAccruedInterest(bondFuture.UnderlyingMaturityDate, market, false)).ToArray();
            var cf        = bonds.Select(x => bondFuture.GetConversionFactor(x, market)).ToArray();

            var coupons                      = new double[length];
            var timeWeightedCoupon           = new double[length];
            var couponsAccruedByReinvestment = new double[length];

            for (var i = 0; i < length; ++i)
            {
                var bond      = bonds[i];
                var cashflows = bond.GetCashflows(market, false).Where(x => x.PaymentDate > market.ValuationDate && x.PaymentDate <= bondFuture.UnderlyingMaturityDate).ToArray();
                if (cashflows.Any())
                {
                    coupons[i]                      = cashflows.Sum(x => x.PaymentAmount);
                    timeWeightedCoupon[i]           = cashflows.Sum(x => x.PaymentAmount * bondFuture.DayCount.CalcDayCountFraction(x.PaymentDate, bondFuture.UnderlyingMaturityDate));
                    couponsAccruedByReinvestment[i] = cashflows.Sum(x => x.PaymentAmount * (reinvestmentCurve.GetCompoundedRate2(x.PaymentDate, bondFuture.UnderlyingMaturityDate) - 1.0));
                }
                else
                {
                    coupons[i]                      = 0.0;
                    timeWeightedCoupon[i]           = 0.0;
                    couponsAccruedByReinvestment[i] = 0.0;
                }
            }

            var interestIncome = bonds.Select((x, i) => aiAtEnd[i] - aiAtStart[i] + coupons[i] + couponsAccruedByReinvestment[i]).ToArray();
            //var interestIncome = bonds.Select((x, i) => aiAtEnd[i] - aiAtStart[i] + coupons[i]).ToArray();

            var dirtyPrice       = new double[length];
            var cleanPrice       = new double[length];
            var ytm              = new double[length];
            var modifiedDuration = new double[length];

            var bondEngine = new BondEngine();

            for (var i = 0; i < length; ++i)
            {
                var bResult = bondEngine.Calculate(bonds[i], market, PricingRequest.Ytm | PricingRequest.ModifiedDuration);
                dirtyPrice[i] = bResult.DirtyPrice;
                cleanPrice[i] = bResult.CleanPrice;
                ytm[i]        = bResult.Ytm;
            }

            var interestCostRate = fundingCurve.GetCompoundedRate2(market.ValuationDate, bondFuture.UnderlyingMaturityDate) - 1.0;
            var interestCost     = dirtyPrice.Select(x => x * interestCostRate).ToArray();

            var futurePrice  = new double[length];
            var irr          = new double[length];
            var basis        = new double[length];
            var pnl          = new double[length];
            var netBasis     = new double[length];
            var invoicePrice = new double[length];
            var margin       = new double[length];
            var spread       = new double[length];

            var fundingRate = fundingCurve.GetSpotRate(0.0);

            var compoundedRate = fundingCurve.GetCompoundedRate2(market.ValuationDate, bondFuture.UnderlyingMaturityDate);
            var bondPricesCompounedByFunding = bonds.Select((x, i) => dirtyPrice[i] * compoundedRate).ToArray();

            for (var i = 0; i < bonds.Length; ++i)
            {
                futurePrice[i] = (bondPricesCompounedByFunding[i] - aiAtEnd[i] - coupons[i]) / cf[i];

                var newMarket =
                    market.UpdateCondition(
                        new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote,
                                                                                                           market.MktQuote.Value.UpdateKey(bondFuture.Id, Tuple.Create(PriceQuoteType.Dirty, futurePrice[i]))));

                var yieldPricer = new BondFuturesYieldPricer(bondFuture, newMarket);
                var tmpResult   = yieldPricer.CalcEquation("FromFuturesPriceAndBondPrice");

                invoicePrice[i] = tmpResult["InvoicePrice"][bonds[i].Id].Rate;
                margin[i]       = tmpResult["Margin"][bonds[i].Id].Rate;

                basis[i]    = tmpResult["Basis"][bonds[i].Id].Rate;
                pnl[i]      = tmpResult["PnL"][bonds[i].Id].Rate;
                netBasis[i] = tmpResult["NetBasis"][bonds[i].Id].Rate;

                irr[i]    = tmpResult["Irr"][bonds[i].Id].Rate;
                spread[i] = irr[i] - fundingRate;
            }

            psDict["FuturesPrice"] = futurePrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["BondDirtyPrice"] = dirtyPrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["BondCleanPrice"] = cleanPrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["BondYieldToMaturity"] = ytm.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["ConversionFactor"] = cf.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["AiStart"] = aiAtStart.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["AiEnd"] = aiAtEnd.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["Coupon"] = coupons.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["InterestIncome"] = interestIncome.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["InterestCost"] = interestCost.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["PnL"] = pnl.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["InvoicePrice"] = invoicePrice.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["Margin"] = margin.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["Spread"] = spread.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["Irr"] = irr.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["Basis"] = basis.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["NetBasis"] = netBasis.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["ModifiedDuration"] = modifiedDuration.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);
            psDict["TimeWeightedCoupon"] = timeWeightedCoupon.Select((x, i) => Tuple.Create(bonds[i].Id, new RateRecord {
                Rate = x
            })).ToDictionary(x => x.Item1, x => x.Item2);

            return(psDict);
        }
Esempio n. 3
0
        public override IPricingResult Calculate(Bond bond, IMarketCondition market, PricingRequest request)
        {
            var beginValuation    = DateTime.Now;
            var result            = new PricingResult(market.ValuationDate, request);
            var isCleanPriceRound = bond.RoundCleanPrice;

            var            bondQuote        = market.MktQuote.Value.ContainsKey(bond.Id) ? market.MktQuote.Value[bond.Id] : null;
            var            bMktQuote        = new Dictionary <string, Tuple <PriceQuoteType, double> >();
            IPricingResult resultOptionBond = new PricingResult(market.ValuationDate, request);
            IPricingResult resultSimpleBond;

            if (bondQuote != null && (bondQuote.Item1 == PriceQuoteType.YtmExecution || bondQuote.Item1 == PriceQuoteType.YtmCallExecution || bondQuote.Item1 == PriceQuoteType.YtmPutExecution))
            {
                bMktQuote[bond.Id] = Tuple.Create(PriceQuoteType.Ytm, bondQuote.Item2);
                var ytmMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMktQuote));
                // Get Call or Put cleanprice
                var pricingRequest = PricingRequest.CleanPrice;
                if (result.IsRequested(PricingRequest.AiEod))
                {
                    pricingRequest = pricingRequest | PricingRequest.AiEod;
                }
                resultOptionBond = CalcOptionBond(bond, ytmMarket, pricingRequest, bondQuote.Item1);
                // Parse market
                var cleanMarket = UpdateCleanPriceMarket(bond.Id, resultOptionBond.CleanPrice, isCleanPriceRound, market);
                resultSimpleBond = _bondEngine.Calculate(bond, cleanMarket, request);
            }
            else
            {
                if (isCleanPriceRound && bondQuote != null)
                {
                    if (bondQuote.Item1 == PriceQuoteType.Clean)
                    {
                        var cleanPriceMarket = UpdateCleanPriceMarket(bond.Id, bondQuote.Item2, isCleanPriceRound,
                                                                      market);
                        resultSimpleBond = _bondEngine.Calculate(bond, cleanPriceMarket, request);
                    }
                    else
                    {
                        resultSimpleBond = _bondEngine.Calculate(bond, market, request);
                        var cleanPriceMarket = UpdateCleanPriceMarket(bond.Id, resultSimpleBond.CleanPrice, isCleanPriceRound, market);
                        resultSimpleBond = _bondEngine.Calculate(bond, cleanPriceMarket, request);
                    }
                }
                else
                {
                    resultSimpleBond = _bondEngine.Calculate(bond, market, request);
                }
                // Parse market
                bMktQuote[bond.Id] = Tuple.Create(PriceQuoteType.Clean, double.IsNaN(resultSimpleBond.CleanPrice) ? 0.0 : resultSimpleBond.CleanPrice);
                var executionYieldPricingRequest = PricingRequest.Ytm;
                if (result.IsRequested(PricingRequest.AiEod))
                {
                    executionYieldPricingRequest = PricingRequest.Ytm | PricingRequest.AiEod;
                }
                var newMarket = market.UpdateCondition(new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMktQuote));
                if (result.IsRequested(PricingRequest.YtmExecution))
                {
                    resultOptionBond = CalcOptionBond(bond, newMarket, executionYieldPricingRequest, PriceQuoteType.YtmExecution);
                }
            }
            result             = (PricingResult)resultSimpleBond;
            result.YieldToCall = resultOptionBond.YieldToCall;
            result.YieldToPut  = resultOptionBond.YieldToPut;
            result.CallDate    = resultOptionBond.CallDate;
            result.PutDate     = resultOptionBond.PutDate;

            var endValuation = DateTime.Now;

            result.CalcTimeInMilliSecond = (endValuation - beginValuation).TotalMilliseconds;
            return(result);
        }
Esempio n. 4
0
        public override IPricingResult GetRisks(Forward <Bond> trade, IMarketCondition market, PricingRequest pricingRequest)
        {
            var result     = new PricingResult(market.ValuationDate, pricingRequest);
            var bondEngine = new BondEngine();
            var bMarket    = market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.UnderlyingDiscountCurve.Value));

            if (result.IsRequested(PricingRequest.Dv01))
            {
                var bondZeroSpread = BondPricingFunctions.ZeroSpread(trade.Underlying, bMarket);
                IMarketCondition bondMktUp;
                IMarketCondition bondMktDown;
                if (market.FixingCurve.HasValue)
                {
                    bondMktUp = bMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread)))
                        );
                    bondMktDown = bMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread)))
                        );
                }
                else
                {
                    bondMktUp = bMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread)))
                        );
                    bondMktDown = bMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1).GetSpreadedCurve(new ZeroSpread(bondZeroSpread)))
                        );
                }

                var fwdMarket = market.UpdateCondition(new UpdateMktConditionPack <ISpread>(x => x.CreditSpread, new ZeroSpread(bondZeroSpread)));
                var upPv      = bondEngine.Calculate(trade.Underlying, bondMktUp, PricingRequest.Pv).Pv;
                var downPv    = bondEngine.Calculate(trade.Underlying, bondMktDown, PricingRequest.Pv).Pv;

                if (fwdMarket.FixingCurve.HasValue)
                {
                    var fwdMktUp = fwdMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, upPv)))
                        );
                    var fwdMktDown = fwdMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, bMarket.FixingCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, downPv)))
                        );
                    result.Dv01 = (CalcPv(trade, fwdMktDown) - CalcPv(trade, fwdMktUp)) / 2.0;
                }
                else
                {
                    var fwdMktUp = fwdMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(1)),
                        new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, upPv)))
                        );
                    var fwdMktDown = fwdMarket.UpdateCondition(
                        new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, bMarket.DiscountCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <IYieldCurve>(x => x.UnderlyingDiscountCurve, bMarket.UnderlyingDiscountCurve.Value.Shift(-1)),
                        new UpdateMktConditionPack <Dictionary <string, Tuple <PriceQuoteType, double> > >(x => x.MktQuote, bMarket.MktQuote.Value.UpdateKey(trade.Underlying.Id, Tuple.Create(PriceQuoteType.Dirty, downPv)))
                        );
                    result.Dv01 = (CalcPv(trade, fwdMktDown) - CalcPv(trade, fwdMktUp)) / 2.0;
                }
            }

            if (result.IsRequested(PricingRequest.Dv01Underlying))
            {
                var factor = trade.Notional / trade.Underlying.Notional;
                result.Dv01Underlying = bondEngine.Calculate(trade.Underlying, bMarket, PricingRequest.Dv01).Dv01 *factor;
            }

            return(result);
        }
Esempio n. 5
0
        /// <summary>
        /// 计算期权
        /// </summary>
        /// <param name="option">期权</param>
        /// <param name="market">市场</param>
        /// <param name="request">计算请求</param>
        /// <returns>计算结果</returns>
        public override IPricingResult Calculate(IOption option, IMarketCondition market, PricingRequest request)
        {
            var  result            = new PricingResult(market.ValuationDate, request);
            bool isExpired         = option.ExerciseDates.Last() < market.ValuationDate;
            bool isExpiredforTheta = option.ExerciseDates.Last() <= market.ValuationDate;
            bool onMaturityDate    = (option.ExerciseDates.Last() == market.ValuationDate);

            if (isExpired)
            {
                result.Pv         = result.Delta = result.DeltaCash = result.Gamma = result.GammaCash = result.Theta = 0.0;
                result.ThetaPnL   = result.CalenderThetaPnL = result.Vega = result.Rho = result.StoppingTime = 0.0;
                result.DVegaDvol  = result.DVegaDt = result.DDeltaDvol = result.DDeltaDt = 0.0;
                result.PricingVol = 0.0;
                return(result);
            }

            else
            {
                var now = DateTime.Now;
                var expiryDayCutoffTime = new DateTime(now.Year, now.Month, now.Day, 15, 0, 0);
                var expiryDayStartTime  = new DateTime(now.Year, now.Month, now.Day, 9, 30, 0);
                var T = (expiryDayCutoffTime - now).TotalSeconds / (expiryDayCutoffTime - expiryDayStartTime).TotalSeconds / 365.0;

                SetPricingVol(result, option, market);

                //Calc delta/pv differently on expiry day
                if (onMaturityDate && T > 0.0)
                {
                    if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv))
                    {
                        result.Pv = DoCalcExpiryPv(option, market, T);
                    }

                    if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta))
                    {
                        result.Delta     = DoCalcExpiryDelta(option, market, T);
                        result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First();
                    }
                }
                else if (onMaturityDate && T <= 0.0)
                {
                    if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv))
                    {
                        var pv = CalcPvs(option, market.ToArrayT());
                        result.Pv         = pv[0];
                        result.PricingVol = 0.0;
                    }

                    if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta))
                    {
                        var pv = CalcPvs(option, new[] { market })[0];
                        if (option.OptionType == OptionType.Call)
                        {
                            result.Delta     = (pv > 0) ? 1 * option.Notional : 0;
                            result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First();
                        }
                        else
                        {
                            result.Delta     = (pv > 0) ? -1 * option.Notional: 0;
                            result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First();
                        }
                    }
                }

                //on other days

                if (result.IsRequested(PricingRequest.Pv) && double.IsNaN(result.Pv))
                {
                    var pv = CalcPvs(option, market.ToArrayT());
                    result.Pv = pv[0];
                }

                if (result.IsRequested(PricingRequest.Delta) && double.IsNaN(result.Delta))
                {
                    result.Delta     = CalcDelta(option, market);
                    result.DeltaCash = result.Delta * market.SpotPrices.Value.Values.First();
                }

                if (result.IsRequested(PricingRequest.Gamma) && double.IsNaN(result.Gamma))
                {
                    result.Gamma     = CalcGamma(option, market);
                    result.GammaCash = result.Gamma * market.SpotPrices.Value.Values.First() * market.SpotPrices.Value.Values.First() / 100;
                }

                if (result.IsRequested(PricingRequest.Vega) && double.IsNaN(result.Vega))
                {
                    result.Vega        = CalcVega(option, market);
                    result.FwdDiffVega = CalcFwdDiffVega(option, market);
                }

                if (result.IsRequested(PricingRequest.Rho) && double.IsNaN(result.Rho))
                {
                    var markets = new[]
                    {
                        market,
                        market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(10))),
                    };
                    var pvs = CalcPvs(option, markets);
                    result.Rho = (pvs[1] - pvs[0]) / 10.0;
                }

                if (result.IsRequested(PricingRequest.DVegaDvol) || result.IsRequested(PricingRequest.DVegaDt) ||
                    result.IsRequested(PricingRequest.DDeltaDvol) || result.IsRequested(PricingRequest.DDeltaDt))
                {
                    CalcHighOrder(option, market, result, isExpiredforTheta);
                }

                if (result.IsRequested(PricingRequest.StoppingTime) && double.IsNaN(result.StoppingTime))
                {
                    var stoppingTime = StoppingTime(option, market.ToArrayT());
                    result.StoppingTime = stoppingTime;
                }

                if (result.IsRequested(PricingRequest.Theta))
                {
                    if (isExpiredforTheta)
                    {
                        result.ThetaPnL = result.CalenderThetaPnL = result.Theta = 0.0;
                        return(result);
                    }
                    else
                    {
                        var thetaResults = CalcTheta(option, market);
                        result.Theta            = thetaResults[0]; // this theta is for time pnl
                        result.ThetaPnL         = thetaResults[1];
                        result.CalenderThetaPnL = thetaResults[2]; //in trading day mode, we cannot calc calendar theta
                    }
                }
            }

            if (option.IsMoneynessOption && result.IsRequested(PricingRequest.Pv) && (option.InitialSpotPrice != 0.0))
            {
                result.PctPv = result.Pv / option.InitialSpotPrice;
            }

            return(result);
        }
Esempio n. 6
0
        /// <summary>
        /// 计算一个债券的估值和基本风险指标
        /// </summary>
        /// <param name="bond">债券</param>
        /// <param name="market">市场数据对象</param>
        /// <param name="request">计算请求类型</param>
        /// <returns>计算结果</returns>
        public override IPricingResult Calculate(Bond bond, IMarketCondition market, PricingRequest request)
        {
            var result = new PricingResult(market.ValuationDate, request);

            var cfs   = bond.GetCashflows(market, true);
            var cfAis = bond.GetAiCashflows(market, false);

            //Ai and AiEod are mutually exclusive requests
            var isEod = result.IsRequested(PricingRequest.AiEod);

            if (result.IsRequested(PricingRequest.Ai) || result.IsRequested(PricingRequest.AiEod))
            {
                result.Ai     = bond.GetAccruedInterest(market.ValuationDate, cfAis, isEod);
                result.AiDays = bond.GetAccruedInterestDays(market.ValuationDate, cfAis, isEod);
            }

            if (result.IsRequested(PricingRequest.Ytm) || result.IsRequested(PricingRequest.DirtyPrice) || result.IsRequested(PricingRequest.CleanPrice) ||
                result.IsRequested(PricingRequest.ModifiedDuration) || result.IsRequested(PricingRequest.Convexity) ||
                (result.IsRequested(PricingRequest.DollarDuration)) || result.IsRequested(PricingRequest.DollarConvexity) ||
                result.IsRequested(PricingRequest.MacDuration) || result.IsRequested(PricingRequest.Pv01) ||
                result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.Dv01) ||
                result.IsRequested(PricingRequest.KeyRateDv01))
            {
                double unitDirtyPrice = 0;
                double unitCleanPrice = 0;
                if (double.IsNaN(result.Ai))
                {
                    result.Ai     = bond.GetAccruedInterest(market.ValuationDate, cfAis, isEod);
                    result.AiDays = bond.GetAccruedInterestDays(market.ValuationDate, cfAis, isEod);
                }
                var bondQuote = market.MktQuote.Value[bond.Id];
                if (bondQuote.Item1 == PriceQuoteType.Dirty)
                {
                    result.DirtyPrice = bondQuote.Item2 * bond.Notional / 100.0;
                    result.Ytm        = _bondYieldPricer.YieldFromFullPrice(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.DirtyPrice, bond.BondTradeingMarket, bond.IrregularPayment);
                    result.CleanPrice = result.DirtyPrice - result.Ai;
                    unitDirtyPrice    = bondQuote.Item2;
                    unitCleanPrice    = result.CleanPrice / bond.Notional * 100.0;
                }
                else if (bondQuote.Item1 == PriceQuoteType.Clean)
                {
                    result.CleanPrice = bondQuote.Item2 * bond.Notional / 100.0;
                    result.DirtyPrice = result.CleanPrice + result.Ai;

                    unitCleanPrice = bondQuote.Item2;
                    unitDirtyPrice = result.DirtyPrice / bond.Notional * 100;
                    result.Ytm     = _bondYieldPricer.YieldFromFullPrice(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.DirtyPrice, bond.BondTradeingMarket, bond.IrregularPayment);
                }
                else
                {
                    result.Ytm        = bondQuote.Item2;
                    result.DirtyPrice = _bondYieldPricer.FullPriceFromYield(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket, bond.IrregularPayment);
                    result.CleanPrice = result.DirtyPrice - result.Ai;
                    unitCleanPrice    = result.CleanPrice / 100.0;
                    unitDirtyPrice    = result.DirtyPrice / 100.0;
                }

                if (result.IsRequested(PricingRequest.Convexity) || result.IsRequested(PricingRequest.ModifiedDuration) ||
                    result.IsRequested(PricingRequest.DollarConvexity) || result.IsRequested(PricingRequest.DollarDuration) ||
                    result.IsRequested(PricingRequest.MacDuration) || result.IsRequested(PricingRequest.Pv01))
                {
                    result.MacDuration = _bondYieldPricer.GetMacDuration(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket);

                    //modified duration here = 1% move, price change, per 100 notional dollar bond
                    result.ModifiedDuration = BondPricingFunctions.GetModifiedDuration(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm, bond.BondTradeingMarket, bond.IrregularPayment, _bondYieldPricer);

                    //1% convexity here, modified duration move, further multiplied by 10000
                    result.Convexity = BondPricingFunctions.GetConvexity(cfs, bond.PaymentDayCount, bond.PaymentFreq, bond.StartDate, market.ValuationDate, result.Ytm,
                                                                         bond.BondTradeingMarket, bond.IrregularPayment, _bondYieldPricer);

                    //1% impact, dollar duration is modified duration in dollar term, but scales with notional, for calculating bond book avg portfolio duration, and display purpose
                    result.DollarModifiedDuration = result.ModifiedDuration * unitDirtyPrice * 0.0001 * bond.Notional;

                    //1% impact, dollar convexity is for calculating both pnl and avg book convexity
                    result.DollarConvexity = result.Convexity / 100 * unitDirtyPrice * bond.Notional;

                    //1bp dollar impact, scales with Notional
                    result.Pv01 = (-result.DollarModifiedDuration + 0.5 * result.DollarConvexity * 0.0001 * 0.0001) / 100.0;
                }
            }

            if (result.IsRequested(PricingRequest.Pv))
            {
                result.Pv = cfs.Where(x => x.PaymentDate > market.ValuationDate)
                            .Sum(x => x.PaymentAmount * market.DiscountCurve.Value.GetDf(market.ValuationDate, x.PaymentDate));
            }

            if (result.IsRequested(PricingRequest.ZeroSpread) || result.IsRequested(PricingRequest.Dv01) || result.IsRequested(PricingRequest.KeyRateDv01) || result.IsRequested(PricingRequest.ZeroSpreadDelta))
            {
                result.ZeroSpread      = BondPricingFunctions.ZeroSpread(cfs, market.DiscountCurve.Value, market.ValuationDate, result.DirtyPrice);
                result.ZeroSpreadDelta = BondPricingFunctions.ZeroSpreadRisk(cfs, market.DiscountCurve.Value, market.ValuationDate, result.ZeroSpread);
                if (market.CreditSpread.HasValue)
                {
                    market =
                        market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve,
                                                                                        market.DiscountCurve.Value.GetSpreadedCurve(market.CreditSpread.Value)));
                }
                else
                {
                    market =
                        market.UpdateCondition(new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve,
                                                                                        market.DiscountCurve.Value.GetSpreadedCurve(new ZeroSpread(result.ZeroSpread))));
                }
            }

            if (result.IsRequested(PricingRequest.Dv01))
            {
                result.Dv01 = base.Calculate(bond, market, PricingRequest.Dv01).Dv01;
            }

            if (result.IsRequested(PricingRequest.KeyRateDv01))
            {
                result.KeyRateDv01 = base.Calculate(bond, market, PricingRequest.KeyRateDv01).KeyRateDv01;
            }

            if (result.IsRequested(PricingRequest.Cashflow))
            {
                result.Cashflows    = bond.GetCashflows(market, false);
                result.CashflowDict = result.Cashflows.ToDictionary(x => x.ToCfKey(), x => x.PaymentAmount);
            }

            //if (bond.Coupon is FloatingCoupon)
            //{
            //	var primeRateDate = market.ValuationDate;
            //	//var primeRateCashflow = cfAis.FirstOrDefault(x => x.AccrualEndDate > market.ValuationDate && x.AccrualStartDate <= market.ValuationDate);
            //	//if (primeRateCashflow != null)
            //	//{
            //	//	primeRateDate = primeRateCashflow.AccrualStartDate;
            //	//}
            //	var fixingCurve = market.FixingCurve ?? market.DiscountCurve ?? market.RiskfreeCurve;
            //	var fixingTuple = bond.Coupon.GetPrimeCoupon(market.HistoricalIndexRates, fixingCurve.Value, primeRateDate);
            //	if (fixingTuple != null && fixingTuple.Item1 != null)
            //		result.ProductSpecific = new Dictionary<string, Dictionary<string, RateRecord>>
            //		{
            //			{
            //				"currentPrimeRate",
            //				new Dictionary<string, RateRecord>
            //				{
            //					{
            //						bond.Id,
            //						new RateRecord
            //						{
            //							Rate = fixingTuple.Item2,
            //							Date = fixingTuple.Item1.ToString()
            //						}
            //					}
            //				}
            //			}
            //		};
            //}

            return(result);
        }
Esempio n. 7
0
        /// <summary>
        /// 计算一个金融衍生品交易的定价和风险指标
        /// </summary>
        /// <param name="trade">交易</param>
        /// <param name="market">市场数据对象</param>
        /// <param name="request">计算请求类型</param>
        /// <returns>计算结果</returns>
        public override IPricingResult Calculate(TTrade trade, IMarketCondition market, PricingRequest request)
        {
            var result = new PricingResult(market.ValuationDate, request);

            if (result.IsRequested(PricingRequest.Pv))
            {
                result.Pv = CalcPv(trade, market);
            }

            if (result.IsRequested(PricingRequest.Carry))
            {
                result.Carry = CalcCarry(trade, market);
            }

            if (result.IsRequested(PricingRequest.Dv01))
            {
                if (double.IsNaN(result.Pv))
                {
                    result.Pv = CalcPv(trade, market);
                }
                var mktDown = market.FixingCurve.HasValue
                                        ? market.UpdateCondition(
                    new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1)),
                    new UpdateMktConditionPack <IYieldCurve>(x => x.FixingCurve, market.FixingCurve.Value.Shift(1)))
                                        : market.UpdateCondition(
                    new UpdateMktConditionPack <IYieldCurve>(x => x.DiscountCurve, market.DiscountCurve.Value.Shift(1)));
                result.Dv01 = CalcPv(trade, mktDown) - result.Pv;
            }

            if (result.IsRequested(PricingRequest.Cashflow))
            {
                result.Cashflows = trade.GetCashflows(market, false);
            }

            //Ai and AiEod are mutually exclusive requests
            var isEod = result.IsRequested(PricingRequest.AiEod);

            if (result.IsRequested(PricingRequest.Ai) || result.IsRequested(PricingRequest.AiEod))
            {
                if (result.Cashflows == null || result.Cashflows.Length == 0)
                {
                    result.Cashflows = trade.GetCashflows(market, false);
                }

                result.Ai = trade.GetAccruedInterest(market.ValuationDate, market, isEod);
            }

            if (result.IsRequested(PricingRequest.KeyRateDv01))
            {
                if (double.IsNaN(result.Pv))
                {
                    result.Pv = CalcPv(trade, market);
                }
                var dc = new Dictionary <string, CurveRisk[]>();
                var fc = new Dictionary <string, CurveRisk[]>();

                Parallel.Invoke(
                    () => CalcDiscountDv01(trade, market, result.Pv, ref dc),
                    () => CalcResetDv01(trade, market, result.Pv, ref fc)
                    );


                result.KeyRateDv01 = PricingResultExtension.Aggregate(dc, fc);
            }

            if (result.IsRequested(PricingRequest.FairQuote))
            {
                result.FairQuote = GetFairQuote(trade, market);
            }

            if (result.IsRequested(PricingRequest.MacDuration))
            {
                if (result.Cashflows == null || result.Cashflows.Length == 0)
                {
                    result.Cashflows = trade.GetCashflows(market, false);
                }
                var weightedCf = 0.0;
                var totalCf    = 0.0;
                foreach (var cashflow in result.Cashflows)
                {
                    if (cashflow.PaymentDate > market.ValuationDate)
                    {
                        var t  = market.DiscountCurve.Value.DayCount.CalcDayCountFraction(market.ValuationDate, cashflow.PaymentDate);
                        var df = market.DiscountCurve.Value.GetDf(market.ValuationDate, cashflow.PaymentDate);

                        weightedCf += cashflow.PaymentAmount * df * t;
                        totalCf    += cashflow.PaymentAmount * df;
                    }
                }
                result.MacDuration = weightedCf / totalCf;
            }

            if (result.IsRequested(PricingRequest.Pv01))
            {
                result.Pv01 = CalcPv01(trade, market, result.Pv);
            }

            Date valueDate = result.ValuationDate;

            if (result.IsRequested(PricingRequest.ProductSpecific))
            {
                var yieldCurve = market.DiscountCurve.Value;

                #region

                var psDict = new Dictionary <string, Dictionary <string, RateRecord> >();
                var dayGap = new DayGap("+0BD");
                var T      = (trade is InterestRateSwap) ? (trade as InterestRateSwap).FloatingLeg : trade as SwapLeg;

                //forward rate points
                var tenors           = new[] { "1D", "7D", "3M", "1Y" };
                var fwdStartInTenors = new List <string> {
                    "1D", "1W", "2W", "1M", "2M", "3M", "4M", "5M", "6M", "7M", "8M", "9M", "10M", "11M", "1Y"
                };
                var totalMonths = Convert.ToInt16((yieldCurve.KeyPoints.Last().Item1 - yieldCurve.KeyPoints.First().Item1) / 30.0) + 1;
                for (var i = 15; i <= totalMonths; i += 3)
                {
                    fwdStartInTenors.Add(i + "M");
                }
                foreach (var tenor in tenors)
                {
                    var fwdRates = new Dictionary <string, RateRecord>();
                    var fwdTerm  = new Term(tenor);
                    foreach (var fwdStartInTenor in fwdStartInTenors)
                    {
                        var fwdStartDate = dayGap.Get(T.Calendar, new Term(fwdStartInTenor).Next(valueDate));
                        var fwdEndDate   = dayGap.Get(T.Calendar, fwdTerm.Next(fwdStartDate));
                        if (fwdEndDate < yieldCurve.KeyPoints.Last().Item1)
                        {
                            fwdRates[fwdStartInTenor] = new RateRecord()
                            {
                                Date = fwdStartDate.ToString(),
                                Rate = yieldCurve.GetForwardRate(fwdStartDate, fwdTerm)
                            };
                        }
                    }

                    psDict["forwardrates" + tenor] = fwdRates;
                }

                //spot rate
                var spotRates    = new Dictionary <string, RateRecord>();
                var spotInTenors = fwdStartInTenors;

                foreach (var spotInTenor in spotInTenors)
                {
                    var spotDate = dayGap.Get(T.Calendar, new Term(spotInTenor).Next(valueDate));
                    if (spotDate <= yieldCurve.KeyPoints.Last().Item1)
                    {
                        spotRates[spotInTenor] = new RateRecord
                        {
                            Date = spotDate.ToString(),
                            Rate = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple)
                        };
                    }
                }
                psDict["spotRates"] = spotRates;

                //key rates
                var rates          = new Dictionary <string, RateRecord>();
                var ccTenors       = yieldCurve.GetKeyTenors().ToArray();
                var mktInstruments = yieldCurve.MarketInstruments;
                if (mktInstruments != null)
                {
                    if (mktInstruments.Length != ccTenors.Length)
                    {
                        throw new PricingBaseException("Number of calibration instruments mismatches number of calibrated points!");
                    }
                }
                for (var i = 0; i < ccTenors.Count(); ++i)
                {
                    //var spotDate = mktInstruments != null ? mktInstruments[i].Instrument.GetClibrationDate() : dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate));
                    var spotDate = dayGap.Get(T.Calendar, new Term(ccTenors[i]).Next(valueDate));
                    rates[ccTenors[i]] = new RateRecord()
                    {
                        ContinuousRate                           = yieldCurve.ZeroRate(valueDate, spotDate),
                        Date                                     = spotDate.ToString(),
                        DiscountFactor                           = yieldCurve.GetDf(valueDate, spotDate),
                        Rate                                     = mktInstruments == null?yieldCurve.GetSpotRate(spotDate) : mktInstruments[i].TargetValue,
                                                     ProductType = mktInstruments == null ? "None" : (mktInstruments[i].Instrument is Deposit) ? "Index" : "Swap",
                                                     ZeroRate    = yieldCurve.ZeroRate(valueDate, spotDate, Compound.Simple),
                                                     Term        = ccTenors[i]
                    };
                }
                psDict["rates"] = rates;

                //discount at cash flow dates
                var dfs   = new Dictionary <string, RateRecord>();
                var dates = result.Cashflows.Select(x => x.PaymentDate);
                foreach (var date in dates)
                {
                    dfs[date.ToString()] = new RateRecord
                    {
                        DiscountFactor = yieldCurve.GetDf(date)
                    };
                }

                psDict["discountfactor"] = dfs;
                //qb rate return
                result.ProductSpecific = psDict;

                #endregion
            }

            return(result);
        }
Esempio n. 8
0
        public override IPricingResult Calculate(AsianOption trade, IMarketCondition market, PricingRequest request)
        {
            var result = new PricingResult(market.ValuationDate, request);

            var exerciseDate      = trade.ExerciseDates.Last();
            var remainingObsDates = trade.ObservationDates.Where(x => x > market.ValuationDate).ToArray();
            var n  = trade.ObservationDates.Count();
            var n2 = remainingObsDates.Count();
            var m  = n - n2;

            if (trade.Fixings.Count != m)
            {
                throw   new PricingLibraryException("AsianOption: number of fixings does not match!");
            }

            var fixingAverage = trade.Fixings.Any() ? trade.Fixings.Average(x => x.Value) : 0.0;


            if (!remainingObsDates.Any())
            {
                //already fix on all observation days
                var cfs = trade.GetPayoff(new[] { fixingAverage });
                result.Pv        = cfs.Sum(x => x.PaymentAmount * market.DiscountCurve.Value.GetDf(market.ValuationDate, x.PaymentDate));
                result.Delta     = 0.0;
                result.DeltaCash = 0.0;
                result.Gamma     = 0.0;
                result.GammaCash = 0.0;
                result.Vega      = 0.0;
                result.Theta     = 0.0;
                result.Rho       = 0.0;
                return(result);
            }

            //TODO: do it properly
            var newSpot = market.SpotPrices.Value.Values.First();

            var sigma = market.VolSurfaces.Value.Values.First().GetValue(exerciseDate, trade.Strike, newSpot);

            var    newSigma = market.VolSurfaces.Value.Values.First().GetValue(exerciseDate, trade.Strike, newSpot);
            double newStrike;
            var    factor = 1.0;

            if (remainingObsDates.Count() == 1)
            {
                newStrike = trade.ObservationDates.Count() * trade.Strike - (n - 1) * trade.Fixings.Average(x => x.Value);
                factor    = 1.0 / n;
            }
            else
            {
                var T  = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, exerciseDate, trade);
                var T2 = AnalyticalOptionPricerUtil.timeToMaturityFraction(remainingObsDates[0], remainingObsDates.Last(), trade);
                var t1 = AnalyticalOptionPricerUtil.timeToMaturityFraction(market.ValuationDate, remainingObsDates[0], trade);

                var h            = T2 / (n2 - 1);
                var S            = market.SpotPrices.Value.Values.First();
                var riskFreeRate = market.DiscountCurve.Value.ZeroRate(market.ValuationDate, exerciseDate);
                var dividendRate = market.DividendCurves.Value.Values.First().ZeroRate(market.ValuationDate, exerciseDate);
                var b            = riskFreeRate - dividendRate;
                var sigma2       = sigma * sigma;

                double eat, eat2;
                if (riskFreeRate.AlmostEqual(dividendRate))
                {
                    eat = market.SpotPrices.Value.Values.First();

                    eat2 = S * S * Math.Exp(sigma2 * t1) / n / n *
                           (
                        (1 - Math.Exp(sigma2 * h * n)) / (1 - Math.Exp(sigma2 * h)) +
                        2.0 / (1 - Math.Exp(sigma2 * h)) *
                        (n - (1 - Math.Exp(sigma2 * h * n)) / (1 - Math.Exp(sigma2 * h)))
                           );
                }
                else
                {
                    var o = 2 * b + sigma2;
                    eat  = S / n2 * Math.Exp(b * t1) * (1 - Math.Exp(b * h * n2)) / (1 - Math.Exp(b * h));
                    eat2 = S * S * Math.Exp(o * t1) / n / n *
                           (
                        (1 - Math.Exp(o * h * n)) / (1 - Math.Exp(o * h)) +
                        2.0 / (1 - Math.Exp((b + sigma2) * h)) *
                        ((1 - Math.Exp(b * h * n)) / (1 - Math.Exp(b * h)) - (1 - Math.Exp(o * h * n)) / (1 - Math.Exp(o * h)))
                           );
                }

                newSpot   = eat;
                newSigma  = Math.Sqrt(Math.Log(eat2 / eat / eat) / T);
                newStrike = (n * trade.Strike - m * fixingAverage) / (n - m) - m / (n - m);

                factor = 1.0 * (n - m) / n;
            }

            var newTrade = new VanillaOption(trade.StartDate,
                                             trade.UnderlyingMaturityDate,
                                             trade.Exercise,
                                             trade.OptionType,
                                             newStrike,
                                             trade.UnderlyingProductType,
                                             trade.Calendar,
                                             trade.DayCount,
                                             trade.PayoffCcy,
                                             trade.SettlementCcy,
                                             trade.ExerciseDates,
                                             trade.ObservationDates,
                                             trade.Notional,
                                             trade.SettlmentGap,
                                             trade.OptionPremiumPaymentDate,
                                             trade.OptionPremium);

            var newVol    = market.VolSurfaces.Value.Values.First().BumpVolSurf(newSigma - sigma);
            var newMarket = market.UpdateCondition(
                new UpdateMktConditionPack <Dictionary <string, double> >(x => x.SpotPrices, new Dictionary <string, double> {
                { "", newSpot }
            }),
                new UpdateMktConditionPack <Dictionary <string, IVolSurface> >(x => x.VolSurfaces, new Dictionary <string, IVolSurface> {
                { "", newVol }
            })
                );

            var bsEngine = new AnalyticalVanillaEuropeanOptionEngine();

            result        = (PricingResult)bsEngine.Calculate(newTrade, newMarket, request);
            result.Pv    *= factor;
            result.Delta *= factor;
            result.Gamma *= factor;
            result.Vega  *= factor;
            result.Theta *= factor;
            result.Rho   *= factor;

            return(result);
        }
Esempio n. 9
0
        public static Tuple <Date, double>[] Calibrate(
            string name,
            Date referenceDate,
            IMarketInstrument[] marketInstruments,
            BusinessDayConvention bda,
            IDayCount daycount,
            ICalendar calendar,
            Compound compound,
            Interpolation interpolation,
            YieldCurveTrait trait,
            CurrencyCode currency,
            IMarketCondition baseMarket = null,
            Expression <Func <IMarketCondition, object> >[] expression = null,
            double initialValue = double.NaN,
            double initialGuess = 0.05,
            //double xmin = -0.9999,
            double xmin = -0.5,
            double xmax = 1.0
            )
        {
            var accuracy = 1.0e-14;

            if (marketInstruments.Any(x => x.Instrument is CreditDefaultSwap) || trait == YieldCurveTrait.DiscountCurve)
            {
                initialValue = 1.0;
                initialGuess = 1.0;
                xmin         = 1.0e-64;
            }

            var len    = marketInstruments.Length;
            var points = marketInstruments.Select(x => Tuple.Create(x.Instrument.GetCalibrationDate(), initialGuess)).ToList();

            var calibrationStartIndex = 0;

            if (!double.IsNaN(initialValue))
            {
                points.Insert(0, Tuple.Create(referenceDate, initialValue));
                calibrationStartIndex = 1;
            }

            if (interpolation == Interpolation.ForwardFlat)
            {
                //forward flat interpolation, the final instrument is not needed in calibration
                points = points.Take(len - 1).ToList();
                points.Insert(0, Tuple.Create(referenceDate, 0.05));
                calibrationStartIndex = 0;
            }
            else if (trait == YieldCurveTrait.ForwardCurve)
            {
                points.Insert(0, Tuple.Create(referenceDate, 0.05));
                calibrationStartIndex = 1;
            }

            baseMarket = baseMarket ?? new MarketCondition(x => x.ValuationDate.Value = referenceDate);
            expression = expression ?? new Expression <Func <IMarketCondition, object> >[]
            {
                x => x.DiscountCurve,
                x => x.FixingCurve
            };

            IYieldCurve calibratedCurve = new YieldCurve(name, referenceDate, points.ToArray(), bda, daycount, calendar, currency, compound, interpolation, trait, baseMarket);
            var         finalInstrument = marketInstruments.Last();
            var         smooth          = (finalInstrument.Instrument is InterestRateSwap);

            double preCalibrationValue = 0.0;

            if (smooth)
            {
                preCalibrationValue = finalInstrument.Instrument.ModelValue(
                    baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()),
                    finalInstrument.CalibMethod);
            }

            while (true)
            {
                for (var i = 0; i < len; ++i)
                {
                    var calibrateFunc = new CalibrateMarketInstrument(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression);
                    //Note: alternative brent solver gives same result, but slower convergence,
                    //var calibrateFunc = new CalibrateMarketInstrument2(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression);
                    double finalR;
                    try
                    {
                        finalR = BrentZero.Solve(calibrateFunc, xmin, xmax, accuracy);
                        //finalR = BrentZero2<IUnivariateFunction>.DoSolve(calibrateFunc, xmin, xmax, 0.05, 0);
                    }
                    catch (Exception ex)
                    {
                        throw new PricingLibraryException(string.Format("Error when bootstrapping {0}th point", i));
                    }
                    calibratedCurve = UpdateKeyRate(calibratedCurve, i + calibrationStartIndex, finalR);
                }

                if (!smooth)
                {
                    break;
                }
                else
                {
                    var postCalibrationValue = finalInstrument.Instrument.ModelValue(
                        baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()),
                        finalInstrument.CalibMethod);
                    if (Math.Abs(postCalibrationValue - preCalibrationValue) < 1e-12)
                    {
                        break;
                    }
                    preCalibrationValue = postCalibrationValue;
                }
            }

            //if initial value is provided for 0D, 不插0D
            if (!double.IsNaN(initialValue))
            {
                return(calibratedCurve.KeyPoints.ToArray());
            }

            //NEVER EVER insert points at the end, inset at MOST ONE point at the beginning
            return(new[] { Tuple.Create(referenceDate, calibratedCurve.KeyPoints[0].Item2) }            // Insert 0D point to avoid interpolation jump at the beginning
                   .Union(calibratedCurve.KeyPoints)
                   .ToArray());
        }