public override void FromXML(XmlNode node) { CheckNode(node, "YieldCurve"); // Read in the mandatory nodes. _curveID = GetChildValue(node, "CurveId", true); _curveDescription = GetChildValue(node, "CurveDescription", true); _currency = GetChildValue(node, "Currency", true); _discountCurveID = GetChildValue(node, "DiscountCurve", true); // Read in the segments. XmlNode segmentsNode = GetChildNode(node, "Segments"); if (segmentsNode != null) { XmlNode child = GetChildNode(segmentsNode, ""); while (child != null) { YieldCurveSegment segment = new YieldCurveSegment(); string childName = GetNodeName(child); if (childName == "Direct") { segment = new DirectYieldCurveSegment(); } else if (childName == "Simple") { segment = new SimpleYieldCurveSegment(); } else if (childName == "AverageOIS") { segment = new AverageOISYieldCurveSegment(); } else if (childName == "TenorBasis") { segment = new TenorBasisYieldCurveSegment(); } else if (childName == "CrossCurrency") { segment = new CrossCcyYieldCurveSegment(); } else if (childName == "ZeroSpread") { segment = new ZeroSpreadedYieldCurveSegment(); } else { Utils.QL_FAIL("Yield curve segment node name not recognized."); } if (segment != null) { try { segment.FromXML(child); } catch (Exception ex) { //ALOG("Exception parsing yield curve segment XML Node, name = " + childName + " and curveID = " + _curveID + " : " + ex.ToString(); } } else { //LOG("Unable to build yield curve segment for name = " << childName << " and curveID = " << curveID_); } _curveSegments.Add(segment); child = GetNextSibling(child, ""); } } else { Utils.QL_FAIL("No Segments node in XML doc for yield curve ID = " + _curveID); } // Read in the optional nodes. // Empty strings if not there (or if there and empty). _interpolationVariable = GetChildValue(node, "InterpolationVariable", false); _interpolationMethod = GetChildValue(node, "InterpolationMethod", false); _zeroDayCounter = GetChildValue(node, "YieldCurveDayCounter", false); // Add hardcoded defaults for now. if (_interpolationVariable == "") { _interpolationVariable = "Discount"; } if (_interpolationMethod == "") { _interpolationMethod = _interpolationVariable == "Zero" ? "Linear" : "LogLinear"; } if (_zeroDayCounter == "") { _zeroDayCounter = "A365"; } XmlNode nodeToTest = GetChildNode(node, "Extrapolation"); if (nodeToTest != null) { _extrapolation = GetChildValueAsBool(node, "Extrapolation", false); } else { _extrapolation = true; } nodeToTest = GetChildNode(node, "Tolerance"); if (nodeToTest != null) { _tolerance = GetChildValueAsDouble(node, "Tolerance", false); } else { _tolerance = 1.0e-12; } PopulateRequiredYieldCurveIDs(); }
private void BuildDiscountCurve() { Utils.QL_REQUIRE(_curveSegments.Count <= 1, () => "More than one zero curve segment not supported yet."); Utils.QL_REQUIRE(_curveSegments[0].CurveSegmentType() == YieldCurveSegment.Type.Zero, () => "The curve segment is not of type Zero."); // Fill a vector of zero quotes. List <ZeroQuote> zeroQuotes = new List <ZeroQuote>(); DirectYieldCurveSegment zeroCurveSegment = _curveSegments[0] as DirectYieldCurveSegment; List <string> zeroQuoteIDs = zeroCurveSegment.Quotes(); for (int i = 0; i < zeroQuoteIDs.Count; ++i) { MarketDatum marketQuote = _loader.Get(zeroQuoteIDs[i], _asofDate); if (marketQuote != null) { Utils.QL_REQUIRE(marketQuote.GetInstrumentType() == MarketDatum.InstrumentType.ZERO, () => "Market quote not of type zero."); ZeroQuote zeroQuote = marketQuote as ZeroQuote; zeroQuotes.Add(zeroQuote); } else { Utils.QL_FAIL("Could not find quote for ID " + zeroQuoteIDs[i] + " with as of date " + _asofDate + "."); } } // Create the (date, zero) pairs. Dictionary <Date, double> data = new Dictionary <Date, double>(); Convention convention = _conventions.Get(_curveSegments[0].ConventionsID()); Utils.QL_REQUIRE(convention != null, () => "No conventions found with ID: " + _curveSegments[0].ConventionsID()); Utils.QL_REQUIRE(convention.ConventionType() == Convention.Type.Zero, () => "Conventions ID does not give zero rate conventions."); ZeroRateConvention zeroConvention = convention as ZeroRateConvention; DayCounter quoteDayCounter = zeroConvention.DayCounter(); for (int i = 0; i < zeroQuotes.Count; ++i) { Utils.QL_REQUIRE(quoteDayCounter == zeroQuotes[i].DayCounter(), () => "The day counter should be the same between the conventions and the quote."); if (!zeroQuotes[i].TenorBased()) { data[zeroQuotes[i].Date()] = zeroQuotes[i].Quote().link.value(); } else { Utils.QL_REQUIRE(zeroConvention.TenorBased(), () => "Using tenor based zero rates without tenor based zero rate conventions."); Date zeroDate = _asofDate; if (zeroConvention.SpotLag() > 0) { } zeroDate = zeroConvention.SpotCalendar().advance(zeroDate, new Period(zeroConvention.SpotLag(), TimeUnit.Days)); zeroDate = zeroConvention.TenorCalendar().advance(zeroDate, zeroQuotes[i].Tenor(), zeroConvention.RollConvention(), zeroConvention.Eom()); data[zeroDate] = zeroQuotes[i].Quote().link.value(); } } Utils.QL_REQUIRE(data.Count > 0, () => "No market data found for curve spec " + _curveSpec.Name() + " with as of date " + _asofDate); // \todo review - more flexible (flat vs. linear extrap)? if (data.Keys.First() > _asofDate) { double rate = data.Values.First(); data[_asofDate] = rate; //LOG("Insert zero curve point at time zero for " + curveSpec_.name() + ": "+ "date " + _asofDate + ", "+"zero " + data[_asofDate]); } Utils.QL_REQUIRE(data.Count > 1, () => "The single zero rate quote provided should be associated with a date greater than as of date."); // First build temporary curves List <Date> dates = new List <Date>(); List <double> zeroes = new List <double>(); List <double> discounts = new List <double>(); dates.Add(data.Keys.First()); zeroes.Add(data.Values.First()); discounts.Add(1.0); Compounding zeroCompounding = zeroConvention.Compounding(); Frequency zeroCompoundingFreq = zeroConvention.CompoundingFrequency(); Dictionary <Date, double> it; foreach (KeyValuePair <Date, double> kvp in data) { Date d = kvp.Key; double r = kvp.Value; dates.Add(d); InterestRate tempRate = new InterestRate(r, quoteDayCounter, zeroCompounding, zeroCompoundingFreq); double t = quoteDayCounter.yearFraction(_asofDate, d); /* Convert zero rate to continuously compounded if necessary */ if (zeroCompounding == Compounding.Continuous) { zeroes.Add(r); } else { zeroes.Add(tempRate.equivalentRate(Compounding.Continuous, Frequency.Annual, t).value()); } discounts.Add(tempRate.discountFactor(t)); //LOG("Add zero curve point for " + curveSpec_.name() + ": " + dates.Last() + " " + zeroes.Last() + " / " + discounts.Last()); } Utils.QL_REQUIRE(dates.Count == zeroes.Count, () => "Date and zero vectors differ in size."); Utils.QL_REQUIRE(dates.Count == discounts.Count, () => "Date and discount vectors differ in size."); // Now build curve with requested conventions if (_interpolationVariable == YieldCurve.InterpolationVariable.Zero) { YieldTermStructure tempCurve = Zerocurve(dates, zeroes, quoteDayCounter); zeroes.Clear(); for (int i = 0; i < dates.Count; ++i) { double zero = tempCurve.zeroRate(dates[i], _zeroDayCounter, Compounding.Continuous).value(); zeroes.Add(zero); } _p = Zerocurve(dates, zeroes, _zeroDayCounter); } else if (_interpolationVariable == YieldCurve.InterpolationVariable.Discount) { YieldTermStructure tempCurve = Discountcurve(dates, discounts, quoteDayCounter); discounts.Clear(); for (int i = 0; i < dates.Count; ++i) { double discount = tempCurve.discount(dates[i]); discounts.Add(discount); } _p = Discountcurve(dates, discounts, _zeroDayCounter); } else { Utils.QL_FAIL("Unknown yield curve interpolation variable."); } }