protected RateCurveBase(
      string name_,
      CurveIDs forecastCurve_,
      CurveIDs discountCurve_,
      string otHolidayCode_,
      int daysToSpot_,
      int maxTenor_,
      long fixedFreq_,
      long floatFreq_,
      BondAnalytics.DayCountType fixedDC_,
      BondAnalytics.DayCountType floatDC_
      )
    {
      CurveName = name_;

      OTHolidayCode = otHolidayCode_;

      ForecastCurve = forecastCurve_;
      DiscountCurve = discountCurve_;

      DaysToSpot = daysToSpot_;
      MaxTenor = maxTenor_;

      FixedFreq = fixedFreq_;
      FloatFreq = floatFreq_;

      FixedDayCount = fixedDC_;
      FloatDayCount = floatDC_;
    }
        public void SubscribeAndCompute(List<ChartComponent> components, BondCurves curve, BondAnalytics.Country country, string guid)
        {
            var curveName = EnumDescriptionAttribute.GetDescription(curve).ToUpper();

            var newComponentList = new List<ChartComponent>();
            foreach (var chartComponent in components)
            {
                newComponentList.Add(chartComponent.Clone());
            }

            workitems[guid] = new LiveBondSpreadWorkItem
                                    {
                                        Guid = guid,
                                        Components = newComponentList,
                                        Curve = curve,
                                        Country = country,
                                        ComputeAction = (com, ccy, c, g) => ComputeLiveBondSpread(com, ccy, c, g),
                                    };

            // get curve update stream 
            var disp = CarbonSubscriber.Instance().LiveCurveObservable()
                .Where(c => c.CarbonCurveName == curveName)
                .Sample(TimeSpan.FromMilliseconds(500))                          
                //.SubscribeOn(Scheduler.ThreadPool)
                .ObserveOn(Scheduler.CurrentThread)
                .Subscribe(_ => workitems[guid].Compute());
            disps.Add(disp);
        }
        public void ComputeLiveBondSpread(List<ChartComponent> components, BondCurves curve, BondAnalytics.Country country, string guid)
        {
            try
            {
                Logger.Info("Computing Live Bond Spread.....", typeof(LiveBondSpreadModel));
                // live 
                var liveCurve = GetLiveCurve(EnumDescriptionAttribute.GetDescription(curve)).GetValues();

                var finalResultList = new List<double>();
                foreach (var chartComponent in components)
                {
                    // look up the current live ctd bond isin
                    var isin = _liveDataModel.GetCusipFromStructure(chartComponent.Component);


                    if (string.IsNullOrEmpty(isin)) return;

                    var bondPrice = (_liveDataModel.GetRealTimeBondData(isin).Bid + _liveDataModel.GetRealTimeBondData(isin).Ask) / 2;
                    var settleDate = CarbonModel.RollDate(DateTime.Today, 1, DateUnit.Bd, BusinessDayConvention.Following, "USNY");
                    var maturity = _bond[chartComponent.Component, CTDValueGeneration.Current].Maturity ?? DateTime.MinValue;
                    var coupon = _bond[chartComponent.Component, CTDValueGeneration.Current].Coupon;

                    var bondSpread = BondAnalytics.SolveZSpread(country, settleDate, bondPrice,
                        DateTime.MinValue,
                        DateTime.MinValue,
                        maturity, coupon, 6,
                        liveCurve.Item1,
                        liveCurve.Item2, new List<DateTime>());


                    // for smoothing, we also need the current obond isin.
                    // if obond is different from current live ctd bond, we also compute the live bond spread for the obond ctd.


                    if (bondSpread == -1.0) bondSpread = double.NaN;

                    var finalResult = -1 * bondSpread * chartComponent.Multiplier * 10000d;
                    finalResultList.Add(finalResult);
                }


                var result = finalResultList.Sum();
      
                bsResultSubject.OnNext(new BondSpreadResult()
                {
                    Guid = guid,
                    Components = components,
                    Country = country,
                    Curve = curve,
                    BondSpread = result,
                    // also set the obond spread here
                    //oBondSpread = 
                });
            }
            catch(Exception e)
            {
                Logger.Error("Exception while computing bond spread", typeof(LiveBondSpreadModel), e);
            }
               
        }
        //  Newton-Raphson to solve for Hazard rate that equates PV to zero. Constant hazard rate.
        public static double SolveHazardRateFromSpread(DateTime startDate, DateTime endDate, int freq, double coupon, double spread, double upfront, BondAnalytics.DayCountType dct, DateTime[] discCurveDates, double[] discDfs, double recovery, List<DateTime> holidays, double creditStartProtDf = 1.0, double creditStartPremDf = 1.0)
        {
            double guess = spread > 0.0 ? spread : (coupon + upfront * 365.25 / (endDate - startDate).TotalDays) / (1.0 - recovery);

            for (int i = 0; i < 50; i++)
            {
                double[] error = PriceCdsFlatHazard(startDate, endDate, freq, coupon, guess, dct, discCurveDates, discDfs, recovery, holidays, creditStartProtDf, creditStartPremDf);

                if (Math.Abs(error[0] - upfront) < 1.0e-14)
                    return guess;

                if (Math.Abs(error[1]) < 1.0e-10)
                    return -1.0;

                guess -= (error[0] - upfront) / error[1];
            }

            return -1.0;
        }
        //  Price premium leg of a CDS with a term structure of interest rates and credit survival probabilities. Assumes flat forward interpolation in both to allow analytic integrals.
        public static double CalcPremiumPv(DateTime startDate, DateTime endDate, int freq, double coupon, BondAnalytics.DayCountType dct, DateTime[] discCurveDates, double[] discDfs, DateTime[] creditCurveDates, double[] creditDfs, double recovery, List<DateTime> holidays, bool bAdjLast = true)
        {
            List<DateTime> schedule = BondAnalytics.GenerateSchedule(startDate, endDate, freq);
            List<DateTime> adjSchedule = BondAnalytics.GenerateAdjustedSchedule(schedule, holidays, "MF");

            //  Avoid extrapolation
            if (schedule[schedule.Count - 1] > creditCurveDates[creditCurveDates.Length - 1] || adjSchedule[adjSchedule.Count - 1] > discCurveDates[discCurveDates.Length - 1])
                return -1.0;

            //  Accrual
            int i = 1;
            while (i < schedule.Count - 1 && adjSchedule[i] <= discCurveDates[0].Date)
                i++;
            double result = -coupon * BondAnalytics.CalcDcf(adjSchedule[i - 1], adjSchedule[i], discCurveDates[0].AddDays(1).Date, dct);

            for (; i < schedule.Count; i++)
            {
                //  End date is not adjusted.
                DateTime tmpDate = bAdjLast && i == adjSchedule.Count - 1 ? schedule[i].AddDays(1) : adjSchedule[i];
                result += coupon * PriceSinglePremiumFlow(adjSchedule[i - 1], tmpDate, adjSchedule[i], dct, discCurveDates, discDfs, creditCurveDates, creditDfs);
            }

            return result;
        }
        //  Float leg pricer
        public static double[] PriceFloatLeg(DateTime startDate, DateTime endDate, double spread, BondAnalytics.DayCountType dctType, long freq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool bIncPrin = false)
        {
            List<DateTime> schedule = BondAnalytics.GenerateSchedule(startDate.Date, endDate.Date, freq);
            List<DateTime> adjSchedule = BondAnalytics.GenerateAdjustedSchedule(schedule, holidays, "MF");
            DateTime todayDate = discCurveDates[0].Date;
            DateTime settleDate = BondAnalytics.NextBusDay(BondAnalytics.NextBusDay(todayDate.AddDays(1), holidays).AddDays(1), holidays);
            double[] result = new double[2];
            double flow;
            int i, startIndex = 0;

            if (settleDate > endDate)
            {
                return result;
            }

            //  Ignore historic flows.
            if (adjSchedule[0] < startDate)
                adjSchedule[0] = startDate;
            while (startIndex < adjSchedule.Count && settleDate > adjSchedule[startIndex]) startIndex++;
            double[] interpTimes = new double[adjSchedule.Count - startIndex];

            // Convert dates to times for spline interpolator. Use adjusted schedule dates for payments.
            for (i = startIndex; i < adjSchedule.Count; i++)
            {
                interpTimes[i - startIndex] = (adjSchedule[i] - todayDate).TotalDays;
            }

            //  Interpolate discount factors. Allow for change in interpolation from piecewise flat to spline at blendIndex for discount curve.
            List<double> flowDiscDfs = (blendIndexD > 0 ? BondAnalytics.InterpDfBlend(interpTimes, discCurveDates, discDfs, blendIndexD) : BondAnalytics.InterpDfSpline(interpTimes, discCurveDates, discDfs));
            List<double> flowFcstDfs = (blendIndexF > 0 ? BondAnalytics.InterpDfBlend(interpTimes, fcstCurveDates, fcstDfs, blendIndexF) : BondAnalytics.InterpDfSpline(interpTimes, fcstCurveDates, fcstDfs));
            double pv = (startDate.Date >= settleDate.Date && bIncPrin ? -flowDiscDfs[0] : 0.0);
            double margin01 = 0.0;

            double dayCountAdjustment = dctType == BondAnalytics.DayCountType.ActActIcma ? (double)freq / 12 : 1.0;

            // If the start index is non-zero, start from index 0 to include the coupon up to adjSchedule[startIndex].
            i = startIndex == 0 ? 1 : 0;
            // First float period is unusual: if first flow date < settle date then fixing known.
            if (startDate.Date <= settleDate.Date)
            {
                double dcf = BondAnalytics.CalcDcf(adjSchedule[startIndex + i - 1], adjSchedule[startIndex + i], adjSchedule[startIndex + i], dctType) * dayCountAdjustment;
                //  Don't need startIndex offset for Dfs, only for dates.
                if (Math.Abs(lastFixing) < 1.0e-8)
                {
                    // If i == 0 we don't have a flowFcstDfs[i - 1] so use 1;
                    flow = ((i == 0 ? 1.0 : flowFcstDfs[i - 1]) / flowFcstDfs[i] - 1.0) + spread / 10000.0*dcf;
                }
                else
                {
                    flow = lastFixing * dcf / 100.0 + spread / 10000.0 * dcf;
                }
                pv += flow * flowDiscDfs[i];
                margin01 += dcf * flowDiscDfs[i];
                ++i; // Skip cashflow in the loop
            }
            for (/* i >= 1 by construction */; i < interpTimes.Length; i++)
            {
                double dcf = BondAnalytics.CalcDcf(adjSchedule[startIndex + i - 1], adjSchedule[startIndex + i], adjSchedule[startIndex + i], dctType) * dayCountAdjustment;
                flow = (flowFcstDfs[i - 1] / flowFcstDfs[i] - 1.0) + spread / 10000.0 * dcf;
                pv += flow * flowDiscDfs[i];
                margin01 += dcf * flowDiscDfs[i];
            }
            if (bIncPrin)
                pv += flowDiscDfs[i - 1];

            result[0] = pv;
            result[1] = margin01;

            return result;
        }
        //  Price standard swap from settleDate and tenor string. Assumes mod foll roll convention.
        public static double PriceStdSwap(DateTime settleDate, String tenor, int payRec, double fixedRate, BondAnalytics.DayCountType fixedDctType, long fixedFreq, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool bIncPrin = false)
        {
            DateTime endDate = TenorToDate(settleDate, tenor.ToLower(), holidays, "MF");

            double fixedPv = PriceFixedLeg(settleDate, endDate, fixedRate, fixedDctType, fixedFreq, discCurveDates, discDfs, holidays, blendIndexD, bIncPrin)[0];
            double floatPv = PriceFloatLeg(settleDate, endDate, 0.0, fixedDctType, floatFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndexD, blendIndexF, bIncPrin)[0];

            return payRec * (fixedPv - floatPv);
        }
        //  Equivalent float leg margin calculator.
        public static double CalcSwapMargin(DateTime startDate, DateTime endDate, double upfront, BondAnalytics.DayCountType floatDctType, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool incPrin = false)
        {
            double[] floatPv = PriceFloatLeg(startDate, endDate, 0.0, floatDctType, floatFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndexD, blendIndexF, incPrin);

            return -(upfront + floatPv[0]) / floatPv[1];
        }
        //  Swap pricer.
        public static double PriceSwap(DateTime startDate, DateTime endDate, int payRec, double fixedRate, BondAnalytics.DayCountType fixedDctType, long fixedFreq, double floatSpread, BondAnalytics.DayCountType floatDctType, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool incPrin = false)
        {
            double fixedPv = PriceFixedLeg(startDate, endDate, fixedRate, fixedDctType, fixedFreq, discCurveDates, discDfs, holidays, blendIndexD, incPrin)[0];
            double floatPv = PriceFloatLeg(startDate, endDate, floatSpread, floatDctType, floatFreq, discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndexD, blendIndexF, incPrin)[0];

            return payRec * (fixedPv - floatPv);
        }
        private BondSpreadResult ComputeCloseBondSpreadResult(string bond, BondAnalytics.Country eCountry,
           double optStartDate, double optFirstCpnDate, long Freq, Dictionary<DateTime, double> closeLiborInterestCurve,
           Dictionary<DateTime, double> closeOisInterestCurve)
        {
            try
            {
                //var bondStatic = bondSpreadServiceModel.GetSingleSecurityMetadata(b);
                BondStatic bondStatic;
                if (!bondStaticCache.TryGetValue(bond, out bondStatic))
                    return null;
                double closePrice;
                if (!histPriceCache.TryGetValue(bond, out closePrice))
                {
                    var histPriceObj = bondSpreadServiceModel.GetHistoryPrice(bondStatic.MLP, futureBuilder.AsOfDate, "NYK");
                    closePrice = histPriceObj != null ? histPriceObj.Price : double.NaN;
                    //closePrice = bondSpreadServiceModel.GetCarbonHistoryPrice(bondStatic.MLP, futureBuilder.AsOfDate);
                    if(!double.IsNaN(closePrice))
                        histPriceCache[bond] = closePrice;
                }
                else
                    closePrice = histPriceCache[bond];
                
                DateTime settleDate = futureBuilder.CashSettleClose;

                var bsResult = BondSpreadResult(eCountry, settleDate, optStartDate, optFirstCpnDate, Freq,
                    closeLiborInterestCurve, closeOisInterestCurve, closePrice, bondStatic);
                if (bsResult != null)
                    bsResult.IsLive = true;
                return bsResult;
            }
            catch (Exception e)
            {
                Log.Error("Exception occurs in computing close bond spread", e);
                return null;
            }
        }
Beispiel #11
0
        //----------------------------------------------------------------------------------------
        public static double calcMeasure(   string Measure, 
                                            double price, 
                                            BondAnalytics.Country eCountry,
                                            DateTime asof,
                                            DateTime settle,
                                            DiscountCurve discCurve, 
                                            DiscountCurve fcstCurve,
                                            BondStatic bondStatic,
                                            SpreadTimeSeriesConfigs configs,
                                            List<DateTime> hols)
         //----------------------------------------------------------------------------------------
        {

            DateTime effectiveDate  = DateTime.FromOADate( bondStatic.EffectiveDate);
            DateTime maturity       = DateTime.FromOADate( bondStatic.Maturity);
            DateTime firstCpnDate   = DateTime.FromOADate(bondStatic.FirstCoupon);
            double coupon           = bondStatic.Coupon;


            long bondCpnFreq                = configs.bndCouponFreq;
            bool bOIS                       = !configs.bForecastCurve;
            BondAnalytics.DayCountType dct  = configs.dct;
            long fixedFreq                  = configs.swpfixfreq;
            long floatFreq                  = configs.swpfloatfreq; 



            double output = double.NaN;
            switch (Measure)
            {
                case "Price":
                    {
                        output= price;
                        break;
                    }
                case "Yield":
                    {
                        output= 10000.0 * BondAnalytics.SolveYield(eCountry, settle, price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0];
                        break;
                    }
                case "TrueSpread":
                    {
                            DateTime[] dfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                            double[] dfs = (bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                            //  Check. Sometimes (holidays) the first date on the curve can be repeated, which will cause an exception in the pricer.
                            if (dfDates[0] == dfDates[1])
                            {
                                dfDates = dfDates.GetSubArray(1, dfDates.GetLength(0)).ToArray();
                          
                                dfs = dfs.GetSubArray(1, dfs.GetLength(0)).ToArray();
                            }
                            output= -10000.0 * BondAnalytics.SolveZSpread(eCountry, settle, price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq, dfDates, dfs,hols );

                        break;
                    }
                case "MMS":
                {
                    DateTime[] discDfDates = discCurve.AsTuples().Select(x => x.Item1).ToArray();
                    DateTime[] fcstDfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                    double[] discDfs = discCurve.AsTuples().Select(x => x.Item2).ToArray();
                    double[] fcstDfs =(bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                    double mms = BondAnalytics.CalcMMS(settle, maturity, dct, fixedFreq, floatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, null, null, null, null, null, firstCpnDate, (bOIS ? 5 : 0));
                    output= 10000.0 * mms;
                    
                    break;
                    }
                case "Spread":
                    {

                        DateTime[] discDfDates = discCurve.AsTuples().Select(x => x.Item1).ToArray();
                        DateTime[] fcstDfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                        double[] discDfs = discCurve.AsTuples().Select(x => x.Item2).ToArray();
                        double[] fcstDfs = (bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                        double mms = BondAnalytics.CalcMMS(settle, maturity, dct, fixedFreq, floatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, null, null, null, null, null, firstCpnDate, (bOIS ? 5 : 0));
                        double yield = BondAnalytics.SolveYield(eCountry,settle ,price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0];
                        output= 10000.0 * (mms - yield);


                        break;
                    }
            }
            return output;
        }
    /// <summary>
    /// Calculate the bondspreads for the given bond over the given swap curve
    /// </summary>
    /// <param name="bond_"></param>
    /// <param name="priceType_">either yield or price</param>
    /// <param name="priceValue_">if price, then pass 100.3 rather than 1.003</param>
    /// <param name="curveType_">the swap curve</param>
    /// <param name="country_">JB's country definitions</param>
    /// <param name="asOf_">the date of the price. will determine the date of the curves that are used</param>
    /// <param name="client_">carbon client to get holidays</param>
    /// <param name="quoteSource_">mlp/sym</param>
    /// <param name="snapCode_">mlp/nyk/ldn</param>
    /// <param name="behavior_">whether you want exceptions to be swallowed or not</param>
    /// <returns></returns>
    public static async Task<BondSpreadResult> GetSpreads(
      Symmetry.Data.FIBond bond_, 
      QuoteValueType priceType_, 
      double priceValue_, 
      SwapCurveType curveType_,
      BondAnalytics.Country country_,
      DateTime asOf_, 
      CarbonClient client_,
      string quoteSource_,
      string snapCode_,
      ThrowBehavior behavior_ = ThrowBehavior.DontThrow)
    {
      try
      {
        var mapping = CurveMappings.GetMapping(curveType_);

        var settleDate = await client_.RollDateAsync(
          date: asOf_.Date.ToNodaLocalDate(),
          count: country_.SettleDateDays(),
          unit: DateUnit.Bd,
          convention: BusinessDayConvention.Following,
          calendar: country_.OTHolidayCode());

        var holidays = CalendarRetriever.GetCalendar(country_.OTHolidayCode(), client_).ToList();

        var startDate = bond_.EffectiveDate.Value;

        var firstCpnDate = bond_.Coupon==0 ? bond_.IssueDate.Value : bond_.FirstCouponDate.Value;

        var maturity = bond_.FI.Maturity;

        var coupon = Convert.ToDouble(bond_.Coupon);

        // get the persisted discount curves for the fixed and floating legs
        var discCurve = SObjectManager.Instance().LoadSObject<DiscountCurve>(new Moniker()
        {
          Close = snapCode_,
          Source = quoteSource_,
          Name = KnownCurveHelpers.GetKnownCurveCode(mapping.DiscountCurve),
          Type = "discountcurve",
          Date = asOf_.Date
        });

        if (discCurve == null)
        {
          SLog.log.ErrorFormat("Could not load {0} discount curve from database for {1}", mapping.DiscountCurve, asOf_.Date);
          return null;
        }

        var fcstCurve = SObjectManager.Instance().LoadSObject<DiscountCurve>(new Moniker()
        {
          Close = snapCode_,
          Source = quoteSource_,
          Name = KnownCurveHelpers.GetKnownCurveCode(mapping.ForecastCurve),
          Type = "discountcurve",
          Date = asOf_.Date
        });

        if (fcstCurve == null)
        {
          SLog.log.ErrorFormat("Could not load {0} discount curve from database for {1}", mapping.ForecastCurve, asOf_.Date);
          return null;
        }

        double price=0, yield=0;

        switch (priceType_)
        {
          case QuoteValueType.Price:
            price = priceValue_;

            yield = BondAnalytics.SolveYield(
              country: country_,
              settleDate: settleDate.ToDateTime(),
              cleanPrice: price,
              startDate: startDate,
              firstCpnDate: firstCpnDate,
              maturityDate: maturity.Value,
              coupon: coupon,
              freq: country_.BondFreq())[0];

            break;
          case QuoteValueType.Yield:
            yield = priceValue_;
            price = BondAnalytics.PriceFromYield(
              country: country_,
              settleDate: settleDate.ToDateTime(),
              yield: priceValue_,
              startDate: startDate,
              firstCpnDate: firstCpnDate,
              maturityDate: maturity.Value,
              coupon: coupon,
              freq: country_.BondFreq())[0];
              break;
        }

        var mms = BondAnalytics.CalcMMS(
          startDate: settleDate.ToDateTime(),
          maturityDate: maturity.Value,
          dctType: mapping.DayCountType,
          fixedFreq: (long) mapping.FixedFreq,
          floatFreq: (long) mapping.FloatFreq,
          discCurveDates: discCurve.AsDoubleArray().SliceColumn(0).Select(DateTime.FromOADate).ToArray(),
          discDfs: discCurve.AsDoubleArray().SliceColumn(1),
          fcstCurveDates: fcstCurve.AsDoubleArray().SliceColumn(0).Select(DateTime.FromOADate).ToArray(),
          fcstDfs: fcstCurve.AsDoubleArray().SliceColumn(1),
          holidays: holidays,
          stubExpiries: null,
          stubTenors: null,
          stubValues: null,
          fixingTenors: null,
          fixings: null,
          firstCpnDate: firstCpnDate
          );

        var truespread = BondAnalytics.SolveZSpread(
          country: country_,
          settleDate: settleDate.ToDateTime(),
          cleanPrice: price,
          startDate: startDate,
          firstCpnDate: firstCpnDate,
          maturityDate: maturity.Value,
          coupon: coupon,
          freq: country_.BondFreq(),
          curveDates: fcstCurve.AsDoubleArray().SliceColumn(0).Select(DateTime.FromOADate).ToArray(),
          dfs: fcstCurve.AsDoubleArray().SliceColumn(1),
          holidays: holidays);

        return new BondSpreadResult
        {
          Price = price,
          Spreads = new BondSpread
          {
            Yield=yield,
            MMS=mms,
            TrueSpread=truespread * -1d,
            Spread = mms-yield,
          }
        };
      }
      catch (Exception ex_)
      {
        Exceptions.Rethrow("Error calculating bondspread", behavior_, ex_);
        return null;
      }
    }
        //  Written so as to allow pricing of forward starting CDS with a single hazard rate to facilitate bootstrapping. This function returns a price and the hazardRate 01, so can work efficiently in a Newton-Raphson loop.
        //  creditStartProtDf is the survival probability at the startDate, which is known in the bootstrap case. If startDate in the past then this = 1 by default.
        //  creditStartPremDf is the survival probability at the premium accrual startDate, typically one day prior to startDate. Note this is not usually the end date of the know hazardRates.
        public static double[] PriceCdsFlatHazard(DateTime startDate, DateTime endDate, int freq, double coupon, double hazardRate, BondAnalytics.DayCountType dct, DateTime[] discCurveDates, double[] discDfs, double recovery, List<DateTime> holidays, double creditStartProtDf = 1.0, double creditStartPremDf = 1.0)
        {
            List<DateTime> schedule = BondAnalytics.GenerateSchedule(startDate, endDate, freq);
            List<DateTime> adjSchedule = BondAnalytics.GenerateAdjustedSchedule(schedule, holidays, "F");
            double[] result = new double[3];

            DateTime newDate = startDate < discCurveDates[0] ? discCurveDates[0] : startDate;
            DateTime adjStartDate = newDate;
            double oldRiskyDiscDf = creditStartProtDf * InterpDf(newDate, discCurveDates, discDfs);
            int i = 1;
            // Protection leg
            double protectionPv = 0.0;
            double protection01 = 0.0;
            while (newDate <= endDate)
            {
                while (i < discCurveDates.Length - 1 && discCurveDates[i].Date <= newDate)
                    i++;
                DateTime oldDate = newDate;
                newDate = discCurveDates[i].Date < endDate ? discCurveDates[i].Date : endDate;
                double newRiskyDiscDf = InterpDf(newDate, discCurveDates, discDfs) * creditStartProtDf * Math.Exp(-hazardRate * (newDate - adjStartDate).TotalDays / 365.0);
                double logRiskyDiscDf = Math.Log(newRiskyDiscDf / oldRiskyDiscDf);
                double dt = (newDate - oldDate).TotalDays / 365.0;
                double logCreditDf = -hazardRate * dt;

                if (Math.Abs(logRiskyDiscDf) < 5.0e-4)
                {
                    protectionPv -= (1.0 - recovery) * logCreditDf * oldRiskyDiscDf * (1.0 + 0.5 * logRiskyDiscDf * (1.0 + logRiskyDiscDf / 3.0 * (1.0 + 0.25 * logRiskyDiscDf)));
                    protection01 += (1.0 - recovery) * dt * oldRiskyDiscDf * (1.0 + 0.5 * logRiskyDiscDf * (1.0 + logRiskyDiscDf / 3.0 * (1.0 + 0.25 * logRiskyDiscDf)));
                    protection01 += (1.0 - recovery) * logCreditDf * (oldDate - adjStartDate).TotalDays / 365.0 * oldRiskyDiscDf * (1.0 - 0.5 * logRiskyDiscDf * (1.0 + logRiskyDiscDf / 3.0 * (1.0 + 0.25 * logRiskyDiscDf)));
                    protection01 += (1.0 - recovery) * logCreditDf * oldRiskyDiscDf * dt * (0.5 + logRiskyDiscDf * (1.0 / 3.0 + logRiskyDiscDf * (0.125 + logRiskyDiscDf / 30.0)));
                }
                else
                {
                    protectionPv += (1.0 - recovery) * logCreditDf / logRiskyDiscDf * (oldRiskyDiscDf - newRiskyDiscDf);
                    protection01 -= (1.0 - recovery) * dt * (logRiskyDiscDf - logCreditDf) / logRiskyDiscDf / logRiskyDiscDf * (oldRiskyDiscDf - newRiskyDiscDf);
                    protection01 += (1.0 - recovery) * logCreditDf / logRiskyDiscDf / 365.0 * (-(oldDate - adjStartDate).TotalDays * oldRiskyDiscDf + (newDate - adjStartDate).TotalDays * newRiskyDiscDf);
                }
                oldRiskyDiscDf = newRiskyDiscDf;
                if (newDate == endDate)
                    break;
            }

            //Premium leg:
            adjStartDate = adjSchedule[0].AddDays(-1).Date > discCurveDates[0] ? adjSchedule[0].AddDays(-1).Date : discCurveDates[0];
            oldRiskyDiscDf = creditStartPremDf * InterpDf(adjStartDate, discCurveDates, discDfs);
            i = 1;
            //  Accrual
            int j = 1;
            while (j < schedule.Count - 1 && adjSchedule[j] <= discCurveDates[0].Date)
                j++;
            double premiumPv = -coupon * BondAnalytics.CalcDcf(adjSchedule[j - 1], adjSchedule[j], discCurveDates[0].AddDays(1).Date, dct);
            double premium01 = 0.0;
            //  creditStartProtDf is the survival probability to the end of the known hazardRate curve.
            if (adjStartDate > discCurveDates[0] && Math.Abs(creditStartProtDf - creditStartPremDf) > 1.0e-8)
                adjStartDate = adjStartDate.AddDays(1);
            for (; j < adjSchedule.Count; j++)
            {
                DateTime accStartDate = adjSchedule[j - 1].AddDays(-1).Date;
                DateTime accEndDate = j == adjSchedule.Count - 1 ? schedule[j].Date : adjSchedule[j].AddDays(-1).Date;
                double flow = coupon * BondAnalytics.CalcDcf(accStartDate, accEndDate, accEndDate, dct) * InterpDf(adjSchedule[j], discCurveDates, discDfs) * creditStartProtDf * Math.Exp(-hazardRate * (accEndDate - adjStartDate).TotalDays / 365.0);
                //  Full Coupon contingent on survival
                premiumPv += flow;
                premium01 -= flow * (accEndDate - adjStartDate).TotalDays / 365.0;
                newDate = accStartDate > discCurveDates[0] ? accStartDate : discCurveDates[0];
                while (newDate <= accEndDate)
                {
                    while (i < discCurveDates.Length - 1 && discCurveDates[i].Date <= newDate)
                        i++;
                    DateTime oldDate = newDate;
                    newDate = discCurveDates[i].Date < accEndDate ? discCurveDates[i].Date : accEndDate;
                    double newRiskyDiscDf = InterpDf(newDate, discCurveDates, discDfs) * creditStartProtDf * Math.Exp(-hazardRate * (newDate - adjStartDate).TotalDays / 365.0);
                    double logRiskyDiscDf = Math.Log(newRiskyDiscDf / oldRiskyDiscDf);
                    double dt = (newDate - oldDate).TotalDays / 365.0;
                    double logCreditDf = -hazardRate * dt;

                    if (Math.Abs(logRiskyDiscDf) < 5.0e-4)
                    {
                        double tmp = ((oldDate - accStartDate).TotalDays + 0.5) * (1.0 + 0.5 * logRiskyDiscDf * (1.0 + logRiskyDiscDf / 3.0 * (1.0 + 0.125 * logRiskyDiscDf)));
                        tmp += (newDate - oldDate).TotalDays * (0.5 + logRiskyDiscDf * (1.0 / 3.0 + logRiskyDiscDf * (0.125 + logRiskyDiscDf / 30.0)));
                        premiumPv -= coupon / 360.0 * logCreditDf * oldRiskyDiscDf * tmp;
                        premium01 += coupon / 360.0 * (dt * oldRiskyDiscDf + logCreditDf * (oldDate - adjStartDate).TotalDays / 365.0) * tmp;
                        //  Differentiate tmp
                        tmp = ((oldDate - accStartDate).TotalDays + 0.5) * dt * (0.5 + logRiskyDiscDf * (1.0 / 3.0 + logRiskyDiscDf * (0.125 + logRiskyDiscDf / 30.0)));
                        tmp += (newDate - oldDate).TotalDays * dt * (1.0 / 3.0 + logRiskyDiscDf * (0.25 + logRiskyDiscDf * (0.1 + logRiskyDiscDf / 36.0)));
                        premium01 += coupon / 360.0 * logCreditDf * oldRiskyDiscDf * tmp;
                    }
                    else
                    {
                        premiumPv += coupon / 360.0 * logCreditDf / logRiskyDiscDf * (365.0 * dt * (-(oldRiskyDiscDf - newRiskyDiscDf) / logRiskyDiscDf - newRiskyDiscDf) + ((oldDate - accStartDate).TotalDays + 0.5) * (oldRiskyDiscDf - newRiskyDiscDf));
                        premium01 -= coupon / 360.0 * dt * (logRiskyDiscDf - logCreditDf) / logRiskyDiscDf / logRiskyDiscDf * (365.0 * dt * (-(oldRiskyDiscDf - newRiskyDiscDf) / logRiskyDiscDf - newRiskyDiscDf) + ((oldDate - accStartDate).TotalDays + 0.5) * (oldRiskyDiscDf - newRiskyDiscDf));
                        premium01 += coupon / 360.0 * logCreditDf / logRiskyDiscDf * (dt * (-(-(oldDate - adjStartDate).TotalDays * oldRiskyDiscDf + (newDate - adjStartDate).TotalDays * newRiskyDiscDf) / logRiskyDiscDf + (newDate - adjStartDate).TotalDays * newRiskyDiscDf) + ((oldDate - accStartDate).TotalDays + 0.5) * (-(oldDate - adjStartDate).TotalDays * oldRiskyDiscDf + (newDate - adjStartDate).TotalDays * newRiskyDiscDf) / 365.0);
                        premium01 -= coupon / 360.0 * logCreditDf / logRiskyDiscDf * 365.0 * dt * dt * (oldRiskyDiscDf - newRiskyDiscDf) / logRiskyDiscDf / logRiskyDiscDf;
                    }
                    oldRiskyDiscDf = newRiskyDiscDf;
                    if (newDate == accEndDate)
                        break;
                }
            }

            result[0] = protectionPv - premiumPv;
            result[1] = protection01 - premium01;
            result[2] = premiumPv;
            return result;
        }
        //  Analytic calculation of integrals in single premium flow assuming flat forward interpolation for both log(DF) and hazard rate.
        private static double PriceSinglePremiumFlow(DateTime startDate, DateTime endDate, DateTime pmtDate, BondAnalytics.DayCountType dct, DateTime[] discCurveDates, double[] discDfs, DateTime[] creditCurveDates, double[] creditDfs)
        {
            DateTime accStartDate = startDate.AddDays(-1), accEndDate = endDate.AddDays(-1);
            DateTime newDate = accStartDate > discCurveDates[0] ? accStartDate : discCurveDates[0];
            int i = 1, j = 1;
            double dcf = BondAnalytics.CalcDcf(accStartDate, accEndDate, accEndDate, dct);

            //  Risky flow: log-linear interpolation.
            double result = dcf * InterpDf(pmtDate, discCurveDates, discDfs) * InterpDf(accEndDate, creditCurveDates, creditDfs);
            //  Accrual on default
            double oldDiscDf = InterpDf(newDate, discCurveDates, discDfs);
            double oldCreditDf = InterpDf(newDate, creditCurveDates, creditDfs);
            while (newDate <= accEndDate)
            {
                while (i < discCurveDates.Length - 1 && discCurveDates[i].Date <= newDate)
                    i++;
                while (j < creditCurveDates.Length - 1 && creditCurveDates[j].Date <= newDate)
                    j++;
                DateTime oldDate = newDate;
                newDate = discCurveDates[i].Date < creditCurveDates[j].Date ? discCurveDates[i].Date : creditCurveDates[j].Date;
                if (newDate > accEndDate)
                    newDate = accEndDate;
                double newDiscDf = InterpDf(newDate, discCurveDates, discDfs);
                double logDiscDf = Math.Log(newDiscDf / oldDiscDf);
                double newCreditDf = InterpDf(newDate, creditCurveDates, creditDfs);
                double logCreditDf = Math.Log(newCreditDf / oldCreditDf);

                if (Math.Abs(logCreditDf + logDiscDf) < 1.0e-6)
                {
                    double tmp = ((oldDate - accStartDate).TotalDays + 0.5) * (1 - 0.5 * (logCreditDf + logDiscDf) * (1.0 - (logCreditDf + logDiscDf) / 3.0 * (1.0 - (logCreditDf + logDiscDf) / 8.0)));
                    tmp += (newDate - oldDate).TotalDays * (0.5 - (logCreditDf + logDiscDf) * (1.0 / 3.0 - (logCreditDf + logDiscDf) * (1.0 / 8.0 - (logCreditDf + logDiscDf) / 30.0)));
                    result += 1.0 / 360.0 * logCreditDf * oldDiscDf * oldCreditDf * tmp;
                }
                else
                    result += 1.0 / 360.0 * logCreditDf / (logCreditDf + logDiscDf) * ((newDate - oldDate).TotalDays * (-(oldDiscDf * oldCreditDf - newDiscDf * newCreditDf) / (logCreditDf + logDiscDf) - newDiscDf * newCreditDf) + ((oldDate - accStartDate).TotalDays + 0.5) * (oldDiscDf * oldCreditDf - newDiscDf * newCreditDf));
                if (newDate == accEndDate)
                    break;
                oldDiscDf = newDiscDf;
                oldCreditDf = newCreditDf;
            }

            return result;
        }
        private BondSpreadResult ComputeCloseFutureBondSpread(BondAnalytics.Country eCountry, Future f, long Freq,
          Dictionary<DateTime, double> liborInterestCurveDict, Dictionary<DateTime, double> oisInterestCurveDict)
        {
            try
            {
                if (liborInterestCurveDict == null || oisInterestCurveDict == null)
                    return null;

                var floas = BondAnalytics.SolveZSpread(eCountry, f.Delivery, f.BondPriceClose,
                    DateTime.MinValue,
                    DateTime.MinValue,
                    f.Maturity, f.Coupon, Freq,
                    liborInterestCurveDict.Keys.ToArray(),
                    liborInterestCurveDict.Values.ToArray(), new List<DateTime>());
                var foisoas = BondAnalytics.SolveZSpread(eCountry, f.Delivery, f.BondPriceClose,
                    DateTime.MinValue,
                    DateTime.MinValue,
                    f.Maturity, f.Coupon, Freq,
                    oisInterestCurveDict.Keys.ToArray(),
                    oisInterestCurveDict.Values.ToArray(), new List<DateTime>());

                floas = (floas == -1.0) ? double.NaN : -1 * floas * 10000;
                foisoas = (foisoas == -1.0) ? double.NaN : -1 * foisoas * 10000;
                var isFront = GetCTDFlagForBond(f.Contract) == CTDFlag.Front;
                var isBack = GetCTDFlagForBond(f.Contract) == CTDFlag.Back;


                return new BondSpreadResult
                {
                    Ticker = f.Contract,
                    Coupon = f.Coupon,
                    IssueDate = f.IssueDate,
                    EffectiveDate = f.Delivery,
                    Maturity = f.Maturity,
                    LOAS = floas,
                    OISOAS = foisoas,
                    FwdLOASFront = isFront ? floas : double.NaN,
                    FwdLOASBack = isBack ? floas : double.NaN,
                    FwdOISOASFront = isFront ? foisoas : double.NaN,
                    FwdOISOASBack = isBack ? foisoas : double.NaN,
                    Price = f.ClosePrice,
                    BondPriceUsed = f.BondPriceClose,
                    CtdBondCusipUsed = f.CTDReuters,
                    InstrumentType = InstrumentType.BondFuture,                    
                    Series = f.Header,
                    IsLive = false,
                };
            }
            catch (Exception e)
            {
                Log.Error(string.Format("Failed to compute future spread for {0}", f.Contract), e);
                return null;                
            }
           
        }
        private BondSpreadResult ComputeLiveBondSpreadResult(string bond, BondAnalytics.Country eCountry,
            double optStartDate, double optFirstCpnDate, long Freq, Dictionary<DateTime, double> liveLiborInterestCurve,
            Dictionary<DateTime, double> liveOisInterestCurve)
        {
            //var bondStatic = bondSpreadServiceModel.GetSingleSecurityMetadata(b);
            BondStatic bondStatic;
            if (!bondStaticCache.TryGetValue(bond, out bondStatic))
                return null;
            if (!bondPriceCache.ContainsKey(bond))
                return null;
            double livePrice = (bondPriceCache[bond].PRIMACT + bondPriceCache[bond].SEC_ACT)/2;
            DateTime settleDate = futureBuilder.CashSettleLive;

            var bsResult = BondSpreadResult(eCountry, settleDate, optStartDate, optFirstCpnDate, Freq, liveLiborInterestCurve, liveOisInterestCurve, livePrice, bondStatic);
            if (bsResult != null)
            {
                bsResult.IsLive = true;
                bsResult.LivePriceTimeUtc = bondPriceCache[bond].UtcTime;
            }
            return bsResult;
        }
 //------------------------------------------------------------------------------------------------
 public SpreadConventions(BondAnalytics.Country country, BondAnalytics.DayCountType dct, 
                         long swapfixed, long swapfloat, long bondcpnfreq,
                         bool bforecastcurve, int blendIndex = 0)
 //------------------------------------------------------------------------------------------------
 {
     Country = country;
     DayCount = dct;
     SwapFixedFrequency  = swapfixed;
     SwapFloatFrequency  = swapfloat;
     BondCouponFrequency = bondcpnfreq;
     bForecastCurve      = bforecastcurve;
     BlendIndex          = blendIndex; // 5 for OIS, 0 for libor
 }
        private BondSpreadResult BondSpreadResult(BondAnalytics.Country eCountry, DateTime settleDate, double optStartDate,
            double optFirstCpnDate, long Freq, Dictionary<DateTime, double> liborInterestCurveDict, Dictionary<DateTime, double> oisInterestCurveDict,
            double cleanPrice, BondStatic bondStatic)
        {
            try
            {


                if (liborInterestCurveDict == null || oisInterestCurveDict == null)
                    return null;

                var yield = BondAnalytics.SolveYield(eCountry, settleDate, cleanPrice, DateTime.MinValue, DateTime.MinValue, DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq)[0];

                var loas = BondAnalytics.SolveZSpread(eCountry, settleDate, cleanPrice,
                    (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                    (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                    DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                    liborInterestCurveDict.Keys.ToArray(),
                    liborInterestCurveDict.Values.ToArray(), new List<DateTime>());

                var oisoas = BondAnalytics.SolveZSpread(eCountry, settleDate, cleanPrice,
                    (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                    (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                    DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                    oisInterestCurveDict.Keys.ToArray(),
                    oisInterestCurveDict.Values.ToArray(), new List<DateTime>());
                if (loas == -1.0) loas = double.NaN;
                if (oisoas == -1.0) oisoas = double.NaN;

                // Forward
                var frontDate = forwardBuilder.GetFrontDateForBond(DateTime.FromOADate(bondStatic.Maturity));
                var fwdPriceFrontResult = forwardBuilder.ComputeBondForwardPrice(bondStatic.MLP, cleanPrice,
                    DateTime.FromOADate(bondStatic.IssueDate), DateTime.FromOADate(bondStatic.Maturity),
                    bondStatic.Coupon, true);
                var fwdPriceFront = fwdPriceFrontResult.Item1;

                double fwdLoasFront=Double.NaN;
                double fwdOisoasFront=Double.NaN;                
                double fwdOisoasBack = Double.NaN;
                double fwdLoasBack = Double.NaN;
                if (fwdPriceFront != 0.0)
                {
                    fwdLoasFront = BondAnalytics.SolveZSpread(eCountry, frontDate, fwdPriceFront,
                        (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                        (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                        DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                        liborInterestCurveDict.Keys.ToArray(),
                        liborInterestCurveDict.Values.ToArray(), new List<DateTime>());
                    fwdOisoasFront = BondAnalytics.SolveZSpread(eCountry, frontDate, fwdPriceFront,
                        (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                        (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                        DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                        oisInterestCurveDict.Keys.ToArray(),
                        oisInterestCurveDict.Values.ToArray(), new List<DateTime>());
                    if (fwdLoasFront == -1.0) fwdLoasFront = double.NaN;
                    if (fwdOisoasFront == -1.0) fwdOisoasFront = double.NaN;

                }

                var backDate = forwardBuilder.GetBackDateForBond(DateTime.FromOADate(bondStatic.Maturity));
                var fwdPriceBackResult = forwardBuilder.ComputeBondForwardPrice(bondStatic.MLP, cleanPrice,
                    DateTime.FromOADate(bondStatic.IssueDate), DateTime.FromOADate(bondStatic.Maturity),
                    bondStatic.Coupon, false);
                var fwdPriceBack = fwdPriceBackResult.Item1;
                if (fwdPriceBack != 0.0)
                {
                    fwdLoasBack = BondAnalytics.SolveZSpread(eCountry, backDate, fwdPriceBack,
                        (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                        (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                        DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                        liborInterestCurveDict.Keys.ToArray(),
                        liborInterestCurveDict.Values.ToArray(), new List<DateTime>());
                    fwdOisoasBack = BondAnalytics.SolveZSpread(eCountry, backDate, fwdPriceBack,
                        (optStartDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optStartDate)),
                        (optFirstCpnDate == 0.0 ? DateTime.MinValue : DateTime.FromOADate(optFirstCpnDate)),
                        DateTime.FromOADate(bondStatic.Maturity), bondStatic.Coupon, Freq,
                        oisInterestCurveDict.Keys.ToArray(),
                        oisInterestCurveDict.Values.ToArray(), new List<DateTime>());

                    if (fwdLoasBack == -1.0) fwdLoasBack = double.NaN;
                    if (fwdOisoasBack == -1.0) fwdOisoasBack = double.NaN;
                }

                // forward price would not be 0.0, we change it to NaN.....
                if (fwdPriceFront == 0.0) fwdPriceFront = double.NaN;
                if (fwdPriceBack == 0.0) fwdPriceBack = double.NaN;

                // find out if the bond is a ctd bond
                Future ctdFuture = null;
                var ctdreuters = FutureCache.Where(f => f.CTDReuters != null).ToArray();
                if (ctdreuters.Any())
                {
                    if (ctdreuters.Select(f => f.CTDReuters.TrimEnd('=')).Contains(bondStatic.CUSIP))
                    {
                        ctdFuture = ctdreuters.First(f => f.CTDReuters.TrimEnd('=') == bondStatic.CUSIP);
                    }
                }

                return new BondSpreadResult
                {
                    Ticker = bondStatic.MLP,
                    Cusip = bondStatic.CUSIP,
                    ISIN = bondStatic.ISIN,
                    Coupon = bondStatic.Coupon,
                    IssueDate = DateTime.FromOADate(bondStatic.IssueDate),
                    Maturity = DateTime.FromOADate(bondStatic.Maturity),
                    LOAS = -1*loas*10000,
                    OISOAS = -1*oisoas*10000,
                    Price = cleanPrice,
                    Yield = yield,
                    ForwardPriceFront = fwdPriceFront,
                    FwdLOASFront = -1*fwdLoasFront*10000,
                    FwdOISOASFront = -1 * fwdOisoasFront * 10000,
                    ForwardPriceBack = fwdPriceBack,
                    FwdLOASBack = -1*fwdLoasBack * 10000,
                    FwdOISOASBack = -1*fwdOisoasBack * 10000,
                    FrontUsedRepo = fwdPriceFrontResult.Item2,
                    BackUsedRepo = fwdPriceBackResult.Item2,
                    InstrumentType = InstrumentType.Bond,
                    CtFlag = forwardBuilder.GetCTFlagForBond(bondStatic.MLP),
                    CtdFlag = ctdFuture == null? CTDFlag.None : GetCTDFlagForBond(ctdFuture.Contract),
                    Series =
                        ((int)
                            Math.Round(
                                (DateTime.FromOADate(bondStatic.Maturity) -
                                 DateTime.FromOADate(bondStatic.IssueDate)).TotalDays/360.0)).ToString(),
                };
            }
            catch (Exception e)
            {
                Log.Error(string.Format("Failed to compute bond spread for {0}", bondStatic.MLP), e);
                return null;
            }
        }
Beispiel #19
0
        public static double CalcMeasure(CalcMeasure Measure, double price, BondAnalytics.Country eCountry,
            //DateTime asof,
            DateTime settle, DiscountCurve discCurve, DiscountCurve fcstCurve, DateTime effectiveDate, DateTime maturity, DateTime firstCpnDate, double coupon, long bondCpnFreq, bool bOIS, BondAnalytics.DayCountType dct, long swapFixedFreq, long swapFloatFreq, List<DateTime> hols)
            //----------------------------------------------------------------------------------------
        {
            double output = double.NaN;
            switch (Measure)
            {
                case Analytics.CalcMeasure.Price:
                {
                    output = price;
                    break;
                }
                case Analytics.CalcMeasure.Yield:
                {
                    output = 10000.0*BondAnalytics.SolveYield(eCountry, settle, price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0];
                    break;
                }
                case Analytics.CalcMeasure.TrueSpread:
                {
                    DateTime[] dfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                    double[] dfs = (bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                    //  Check. Sometimes (holidays) the first date on the curve can be repeated, which will cause an exception in the pricer.
                    if (dfDates[0] == dfDates[1])
                    {
                        dfDates = dfDates.GetSubArray(1, dfDates.GetLength(0)).ToArray();

                        dfs = dfs.GetSubArray(1, dfs.GetLength(0)).ToArray();
                    }
                    output = -10000.0*BondAnalytics.SolveZSpread(eCountry, settle, price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq, dfDates, dfs, hols);

                    break;
                }
                case Analytics.CalcMeasure.MMS:
                {
                    DateTime[] discDfDates = discCurve.AsTuples().Select(x => x.Item1).ToArray();
                    DateTime[] fcstDfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                    double[] discDfs = discCurve.AsTuples().Select(x => x.Item2).ToArray();
                    double[] fcstDfs = (bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                    double mms = BondAnalytics.CalcMMS(settle, maturity, dct, swapFixedFreq, swapFloatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, null, null, null, null, null, firstCpnDate, (bOIS ? 5 : 0));
                    output = 10000.0*mms;

                    break;
                }
                case Analytics.CalcMeasure.Spread:
                {
                    DateTime[] discDfDates = discCurve.AsTuples().Select(x => x.Item1).ToArray();
                    DateTime[] fcstDfDates = (bOIS ? discCurve.AsTuples().Select(x => x.Item1).ToArray() : fcstCurve.AsTuples().Select(x => x.Item1).ToArray());
                    double[] discDfs = discCurve.AsTuples().Select(x => x.Item2).ToArray();
                    double[] fcstDfs = (bOIS ? discCurve.AsTuples().Select(x => x.Item2).ToArray() : fcstCurve.AsTuples().Select(x => x.Item2).ToArray());

                    double mms = BondAnalytics.CalcMMS(settle, maturity, dct, swapFixedFreq, swapFloatFreq, discDfDates, discDfs, fcstDfDates, fcstDfs, hols, null, null, null, null, null, firstCpnDate, (bOIS ? 5 : 0));
                    double yield = BondAnalytics.SolveYield(eCountry, settle, price, effectiveDate, firstCpnDate, maturity, coupon, bondCpnFreq)[0];
                    output = 10000.0*(mms - yield);


                    break;
                }
            }
            return output;
        }
Beispiel #20
0
 public string SubscribeAndCompute(List<ChartComponent> components, BondCurves curve, BondAnalytics.Country country)
 {
     var guid = Guid.NewGuid().ToString();
     livebsmodel.SubscribeAndCompute(components, curve, country, guid);
     return guid;
 }
Beispiel #21
0
        public static double Calculate(CalcMeasure Measure, 
                                        DateTime settle,
                                        double price,
                                        Bond bondStatic, 
                                        BondAnalytics.Country eCountry, 
                                        DateTime asOfDate,                                         
                                        List<DateTime> hols)
        {
            
            // byran lim config coded in symmetry.net. we use this until we have a centralised place for the curve static
            // different country has different setting of using forcast and discount curve
            bool bForecastCurve;
            switch (eCountry)
            {                
                case BondAnalytics.Country.DE:
                    bForecastCurve = false;
                    break;                
                default:
                    bForecastCurve = true;
                    break;
            }

            var fcstCfg = new SpreadTimeSeriesConfigs(eCountry.ToString(), bForecastCurve);            

            int trial = 0;
            DiscountCurve forCurve = null;
            DiscountCurve disCurve = null;
            if (discountCurveCache.ContainsKey(fcstCfg.SymForecastCurveName))
            {
                var forTup = discountCurveCache[fcstCfg.SymForecastCurveName].FirstOrDefault(i => i.Item1 == asOfDate);
                if (forTup != null) forCurve = forTup.Item2;
            }
            if (discountCurveCache.ContainsKey(fcstCfg.SymDiscountCurveName))
            {
                var disTup = discountCurveCache[fcstCfg.SymDiscountCurveName].FirstOrDefault(i => i.Item1 == asOfDate);
                if (disTup != null) disCurve = disTup.Item2;
            }
            if (forCurve == null || disCurve == null)
            {
                while (trial < 3)
                {
                    try
                    {
                        forCurve = SymmetryDataModel.GetDiscountCurve(fcstCfg.SymForecastCurveName, asOfDate);
                        disCurve = SymmetryDataModel.GetDiscountCurve(fcstCfg.SymDiscountCurveName, asOfDate);

                        trial = 5;
                        break;
                    }
                    catch (Exception)
                    {
                        trial++;
                        continue;
                    }
                }

                if (forCurve != null && disCurve != null)
                {
                    var forTup = new Tuple<DateTime, DiscountCurve>(asOfDate, forCurve);
                    var disTup = new Tuple<DateTime, DiscountCurve>(asOfDate, disCurve);
                    if (discountCurveCache.ContainsKey(fcstCfg.SymForecastCurveName))
                        discountCurveCache[fcstCfg.SymForecastCurveName].Add(forTup);
                    else
                        discountCurveCache[fcstCfg.SymForecastCurveName] = new[] {forTup}.ToList();
                    if (discountCurveCache.ContainsKey(fcstCfg.SymDiscountCurveName))
                        discountCurveCache[fcstCfg.SymDiscountCurveName].Add(disTup);
                    else
                        discountCurveCache[fcstCfg.SymDiscountCurveName] = new[] { disTup }.ToList();
                }
            }

            if (forCurve == null || disCurve == null)
                return double.NaN;

            DateTime effectiveDate = bondStatic.EffectiveDate ?? DateTime.MinValue;
            DateTime maturity = bondStatic.Maturity ?? DateTime.MinValue;
            DateTime firstCpnDate = bondStatic.FirstCouponDate ?? DateTime.MinValue;
            double coupon = bondStatic.Coupon;


            long bondCpnFreq = fcstCfg.bndCouponFreq;
            bool bOIS = !fcstCfg.bForecastCurve;
            BondAnalytics.DayCountType dct = fcstCfg.dct;
            long swapFixedFreq = fcstCfg.swpfixfreq;
            long swapFloatFreq = fcstCfg.swpfloatfreq;

            return CalcMeasure(Measure, price, eCountry, settle, disCurve, forCurve, effectiveDate, maturity, firstCpnDate, coupon, bondCpnFreq, bOIS, dct, swapFixedFreq, swapFloatFreq, hols);
        }
Beispiel #22
0
        //  Calculate the DV01 of a standard swap. Assumes mod foll roll covention. 01s in Notional Terms
        public static double CalcSwapDv01(DateTime settleDate, DateTime endDate, int payRec, double fixedRate, BondAnalytics.DayCountType fixedDctType, BondAnalytics.DayCountType floatDctType, long fixedFreq, long floatFreq, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool bIncPrin = false)
        {
            double bumpUp = PriceSwap(settleDate, endDate, payRec, fixedRate + 0.01, fixedDctType, fixedFreq, 0.0, floatDctType, floatFreq, 
                discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndexD, blendIndexF,
                bIncPrin);

            double bumpDown = PriceSwap(settleDate, endDate, payRec, fixedRate - 0.01, fixedDctType, fixedFreq, 0.0, floatDctType, floatFreq,
                discCurveDates, discDfs, fcstCurveDates, fcstDfs, holidays, lastFixing, blendIndexD, blendIndexF,
                bIncPrin);


            return (bumpUp - bumpDown) / 2;
        }
Beispiel #23
0
        //----------------------------------------------------------------------------------------
        public static double[] GetRepoRates(BondAnalytics.Country eCountry, List<DateTime> dates )
        //----------------------------------------------------------------------------------------
        {
            List<double> reporates = new List<double>();
            
            foreach (var dte in dates)
            {
                double repo = double.NaN;
                switch (eCountry)
                {
                    case BondAnalytics.Country.US:

                        GenericPrice prc = GetHistoryPrice(Constants.NomGcMidRate, dte, "NOM", "NOM");
                        repo = prc == null ? double.NaN : prc.GcMidRate / 100.0;

                        break;
                    case BondAnalytics.Country.DE:
                        var o = GetCbnTimeSeriesDataCell("GC_GERMANY_SUB_10YR", "eod-icaprepo", dte, "wtdRate", new object[] { "term~S-N" });
                        if (o is double)
                        {
                            repo = (double) o / 100.0;
                        }
                        else
                            repo = double.NaN;
                        break;

                   /* case BondAnalytics.Country.UK:
                        break;*/

                    default:
                        throw new ArgumentException("Invalid input country " + eCountry.ToString());
                }

                reporates.Add(repo);
            }

            return reporates.ToArray();
        }
Beispiel #24
0
        //  Basis Swap pricer. FX resets handled by flag bFx = true, in which case float2Pv is the resetting leg.
        public static double PriceBasisSwap(DateTime startDate, DateTime endDate, double sizing, double fx, int payRec, BondAnalytics.DayCountType float1DctType, long float1Freq, double float1Spread, BondAnalytics.DayCountType float2DctType, long float2Freq, double float2Spread, DateTime[] discCurve1Dates, double[] disc1Dfs, DateTime[] fcstCurve1Dates, double[] fcst1Dfs, DateTime[] discCurve2Dates, double[] disc2Dfs, DateTime[] fcstCurve2Dates, double[] fcst2Dfs, List<DateTime> holidays, double lastFixing1 = 0.0, double lastFixing2 = 0.0, int blendIndexD = 0, int blendIndexF = 0, bool bIncPrin = false, bool bFx = false)
        {
            double float1Pv = PriceFloatLeg(startDate, endDate, float1Spread, float1DctType, float1Freq, discCurve1Dates, disc1Dfs, fcstCurve1Dates, fcst1Dfs, holidays, lastFixing1, blendIndexD, blendIndexF, bIncPrin)[0];
            double float2Pv = bFx ?
                PriceFxResetFloatLeg(startDate, endDate, float2Spread, float2DctType, float2Freq, discCurve2Dates, disc2Dfs, fcstCurve2Dates, fcst2Dfs, discCurve1Dates, disc1Dfs, holidays, lastFixing2, blendIndexD, blendIndexF)[0] :
                PriceFloatLeg(startDate, endDate, float2Spread, float2DctType, float2Freq, discCurve2Dates, disc2Dfs, fcstCurve2Dates, fcst2Dfs, holidays, lastFixing2, blendIndexD, blendIndexF, bIncPrin)[0];

            return payRec * (float1Pv - sizing / fx * float2Pv);
        }
Beispiel #25
0
 //----------------------------------------------------------------------------------------
 public static Tuple<string /*closeSnap*/, string /*source*/> GetCloseSnap(DateTime dte, BondAnalytics.Country eCountry)
 //----------------------------------------------------------------------------------------
 {
     if (dte.Year < 2015)
     {
         return new Tuple<string, string>("MLP", "MLP");
     }
     else
     {
         if (eCountry == BondAnalytics.Country.US)
         {
                return new Tuple<string, string>("NYK", "SYM");
         }
         else
         {
             return new Tuple<string, string>("LDN", "SYM");
         }
        
     }
 }
Beispiel #26
0
        //  Price standard FRA from settleDate and tenor strings. Assumes mod foll roll convention.
        public static double PriceStdFra(DateTime settleDate, String startTenor, string endTenor, int payRec, double fixedRate, BondAnalytics.DayCountType fixedDctType, DateTime[] discCurveDates, double[] discDfs, DateTime[] fcstCurveDates, double[] fcstDfs, List<DateTime> holidays, double lastFixing = 0.0, int blendIndexD = 0, int blendIndexF = 0)
        {
            DateTime startDate = TenorToDate(settleDate, startTenor.ToLower(), holidays, "MF");
            DateTime endDate = TenorToDate(settleDate, endTenor.ToLower(), holidays, "MF");

            double dcf = BondAnalytics.CalcDcf(startDate, endDate, endDate, fixedDctType);
            // Convert dates to times for spline interpolator. Use adjusted schedule dates for payments.
            double[] interpTimes = new double[2];
            DateTime todayDate = discCurveDates[0].Date;
            interpTimes[0] = (startDate - todayDate).TotalDays;
            interpTimes[1] = (endDate - todayDate).TotalDays;

            //  Interpolate discount factors. Allow for change in interpolation from piecewise flat to spline at blendIndex for discount curve.
            List<double> flowDiscDfs = (blendIndexD > 0 ? BondAnalytics.InterpDfBlend(interpTimes, discCurveDates, discDfs, blendIndexD) : BondAnalytics.InterpDfSpline(interpTimes, discCurveDates, discDfs));
            List<double> flowFcstDfs = (blendIndexF > 0 ? BondAnalytics.InterpDfBlend(interpTimes, fcstCurveDates, fcstDfs, blendIndexF) : BondAnalytics.InterpDfSpline(interpTimes, fcstCurveDates, fcstDfs));

            return payRec * (fixedRate * dcf - (flowFcstDfs[0] / flowFcstDfs[1] - 1.0)) * flowFcstDfs[1] / flowFcstDfs[0] * flowDiscDfs[0];
        }
Beispiel #27
0
        //----------------------------------------------------------------------------------------
        public async static  Task<Frame<DateTime, string>> GetBondPricesAsync(List<string> bonds, List<DateTime> dates, 
                                                            BondAnalytics.Country eCountry )
        //----------------------------------------------------------------------------------------
        {
            var opMap = new ConcurrentDictionary<DateTime, Series<string, double>>();


            // Let's trigger all the data to start coming back
            ConcurrentDictionary<string, Task<GenericPrice>> asyncTable = new ConcurrentDictionary<string, Task<GenericPrice>>();


            List<Tuple<DateTime, string>>  dateBondList = new List<Tuple<DateTime, string>>();

            foreach (var dte in dates)
                foreach (string bond in bonds)
                {
                    dateBondList.Add(new Tuple<DateTime, string>(dte, bond));
                }

            Parallel.ForEach(dateBondList, item =>
            {
                DateTime dte = item.Item1;
                string bond = item.Item2;
                Console.WriteLine("Triggering async for {0}", bond + "_" + dte.ToShortDateString());
                var closeSnapTup = GetCloseSnap(dte, eCountry);

                string closeSnap = closeSnapTup.Item1;
                string source = closeSnapTup.Item2;
                asyncTable[bond + "_" + dte.ToShortDateString()] = GetHistoryPriceAsync(bond, dte, closeSnap, source);
            });

            // Wait for 10 secs to do its thing
            Thread.Sleep(PauseDuration);

            foreach(var dte in dates)
            {
                Console.WriteLine("Getting prices on {0}", dte.ToShortDateString());
                var sb = new SeriesBuilder<string, double>();
                
                foreach (var bond in bonds)
                {
                    GenericPrice res = await asyncTable[bond + "_" + dte.ToShortDateString()];

                    if (res == null)
                    {
                        sb.Add(bond, double.NaN);
                    }
                    else
                        sb.Add(bond, res.Price);
                }

                opMap[dte] = sb.Series;
            }

            var df = Frame.FromRows(opMap);
            df = df.SortRowsByKey();
            return df;
        }
Beispiel #28
0
        //  Fixed leg pricer. Allows inflation indexing through optional arguments
        public static double[] PriceFixedLeg(DateTime startDate, DateTime endDate, double fixedRate, BondAnalytics.DayCountType dctType, long freq, DateTime[] discCurveDates, double[] discDfs, List<DateTime> holidays, int blendIndex = 0, bool bIncPrin = false, bool bLongFirstCpn = false, BondAnalytics.InflationCurve infCurve = null, double infSettleFactor = 1.0)
        {
            List<DateTime> schedule = BondAnalytics.GenerateSchedule(startDate.Date, endDate.Date, freq);
            List<DateTime> adjSchedule = BondAnalytics.GenerateAdjustedSchedule(schedule, holidays, "MF");
            DateTime todayDate = discCurveDates[0].Date;
            DateTime settleDate = BondAnalytics.NextBusDay(BondAnalytics.NextBusDay(todayDate.AddDays(1), holidays).AddDays(1), holidays);
            double[] result = new double[2];
            int i, startIndex = 0;

            if (settleDate.Date > endDate.Date)
            {
                return result;
            }

            //  Ignore historic flows.
            if (adjSchedule[0] < startDate)
                adjSchedule[0] = startDate;
            while (startIndex < adjSchedule.Count && settleDate > adjSchedule[startIndex]) startIndex++;
            double[] interpTimes = new double[adjSchedule.Count - startIndex];

            // Convert dates to times for spline interpolator. Use adjusted schedule dates for payments.
            for (i = startIndex; i < adjSchedule.Count; i++)
            {
                interpTimes[i - startIndex] = (adjSchedule[i] - todayDate).TotalDays;
            }

            //  Interpolate discount factors. Allow for change in interpolation from piecewise flat to spline at blendIndex.
            List<double> flowDiscDfs = (blendIndex > 0 ? BondAnalytics.InterpDfBlend(interpTimes, discCurveDates, discDfs, blendIndex) : BondAnalytics.InterpDfSpline(interpTimes, discCurveDates, discDfs));
            //  Interpolate inflation curve.
            double[] infFactors = BondAnalytics.InterpolateInflationFactors(adjSchedule, settleDate, infCurve);

            //  Principal flows only if incPrin and startDate in the future.
            double pv = (startDate.Date >= settleDate.Date && bIncPrin ? -infFactors[startIndex] * flowDiscDfs[0] : 0.0), fixed01 = 0.0;

            double dayCountAdjustment = dctType == BondAnalytics.DayCountType.ActActIcma ? (double) freq/12 : 1.0;
            // If the start index is non-zero, we need to include the flow from the period ending at adjSchedule[startIndex]
            for (i = (startIndex == 0 ? 1 : 0); i < interpTimes.Length; i++)
            {
                double dcf;
                if (bLongFirstCpn && startIndex + i == 2)
                {
                    // Long first coupon pays first two periods at adjSchedule[2]
                    dcf = BondAnalytics.CalcDcf(adjSchedule[0], adjSchedule[2], adjSchedule[2], dctType) * dayCountAdjustment;
                }
                else if (bLongFirstCpn && startIndex + i < 2)
                {
                    // Skip cash flows before the long first coupon
                    continue;
                }
                else
                {
                    dcf = BondAnalytics.CalcDcf(adjSchedule[startIndex + i - 1], adjSchedule[startIndex + i], adjSchedule[startIndex + i], dctType) * dayCountAdjustment;
                }
                pv += infFactors[i + startIndex] * fixedRate / 100.0 * dcf * flowDiscDfs[i];
                fixed01 += infFactors[i + startIndex] * dcf * flowDiscDfs[i];
            }
            if (bIncPrin)
                pv += infFactors[i - 1 + startIndex] * flowDiscDfs[i - 1];
            else if (Math.Abs(infFactors[i - 1 + startIndex]) > 1.0e-8)
                pv += (infFactors[i - 1 + startIndex] - infSettleFactor) * flowDiscDfs[i - 1];

            result[0] = pv;
            result[1] = fixed01;

            return result;
        }
        //  Solve for par spread that matches upfront with coupon. First find flat hazard rate, then use N-R to find spread (= coupon) which prices to zero with this hazard rate.
        public static double SolveSpread(DateTime startDate, DateTime endDate, int freq, double coupon, double upfront, BondAnalytics.DayCountType dct, DateTime[] discCurveDates, double[] discDfs, double recovery, List<DateTime> holidays, double creditStartProtDf = 1.0, double creditStartPremDf = 1.0)
        {
            double hRate = SolveHazardRateFromSpread(startDate, endDate, freq, coupon, 0.0, upfront, dct, discCurveDates, discDfs, recovery, holidays, creditStartProtDf, creditStartPremDf);
            double guess = hRate * (1.0 - recovery);

            //  Iterate using Newton-Raphson with coupon derivative = premiumPv / guess.
            for (int i = 0; i < 50; i++)
            {
                double[] error = PriceCdsFlatHazard(startDate, endDate, freq, guess, hRate, dct, discCurveDates, discDfs, recovery, holidays, creditStartProtDf, creditStartPremDf);

                if (Math.Abs(error[0]) < 1.0e-10)
                    return guess;
                if (Math.Abs(error[2]) < 1.0e-8)
                    return -1.0;

                guess *= 1.0 + error[0] / error[2];
            }

            return -1.0;
        }