// Converts an FpML 'InflationRateCalculation' to a {@code RateCalculation}. private RateCalculation parseInflation(XmlElement legEl, XmlElement calcEl, XmlElement inflationEl, PeriodicSchedule accrualSchedule, FpmlDocument document) { // supported elements: // 'calculationPeriodAmount/calculation/inflationRateCalculation' // 'calculationPeriodAmount/calculation/inflationRateCalculation/floatingRateIndex' // 'calculationPeriodAmount/calculation/inflationRateCalculation/indexTenor?' // 'calculationPeriodAmount/calculation/inflationRateCalculation/floatingRateMultiplierSchedule?' // 'calculationPeriodAmount/calculation/inflationRateCalculation/inflationLag' // 'calculationPeriodAmount/calculation/inflationRateCalculation/interpolationMethod' // 'calculationPeriodAmount/calculation/inflationRateCalculation/initialIndexLevel?' // 'calculationPeriodAmount/calculation/dayCountFraction' // ignored elements: // 'calculationPeriodAmount/calculation/inflationRateCalculation/indexSource' // 'calculationPeriodAmount/calculation/inflationRateCalculation/mainPublication' // 'calculationPeriodAmount/calculation/inflationRateCalculation/fallbackBondApplicable' // 'calculationPeriodAmount/calculation/floatingRateCalculation/initialRate?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/finalRateRounding?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/averagingMethod?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/negativeInterestRateTreatment?' // 'resetDates' // rejected elements: // 'calculationPeriodAmount/calculation/floatingRateCalculation/spreadSchedule*' // 'calculationPeriodAmount/calculation/floatingRateCalculation/rateTreatment?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/capRateSchedule?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/floorRateSchedule?' // 'stubCalculationPeriodAmount' document.validateNotPresent(inflationEl, "spreadSchedule"); document.validateNotPresent(inflationEl, "rateTreatment"); document.validateNotPresent(inflationEl, "capRateSchedule"); document.validateNotPresent(inflationEl, "floorRateSchedule"); document.validateNotPresent(legEl, "stubCalculationPeriodAmount"); // TODO: parse fixed stub rate InflationRateCalculation.Builder builder = InflationRateCalculation.builder(); // index builder.index(document.parsePriceIndex(inflationEl)); // lag builder.lag(document.parsePeriod(inflationEl.getChild("inflationLag"))); // interpolation string interpStr = inflationEl.getChild("interpolationMethod").Content; builder.indexCalculationMethod(interpStr.ToLower(Locale.ENGLISH).Contains("linear") ? PriceIndexCalculationMethod.INTERPOLATED : PriceIndexCalculationMethod.MONTHLY); // initial index inflationEl.findChild("initialIndexLevel").ifPresent(el => { builder.firstIndexValue(document.parseDecimal(el)); }); // gearing inflationEl.findChild("floatingRateMultiplierSchedule").ifPresent(el => { builder.gearing(parseSchedule(el, document)); }); return(builder.build()); }
// Converts an FpML 'FloatingRateCalculation' to a {@code RateCalculation}. private RateCalculation parseFloat(XmlElement legEl, XmlElement calcEl, XmlElement floatingEl, PeriodicSchedule accrualSchedule, FpmlDocument document) { // supported elements: // 'calculationPeriodAmount/calculation/floatingRateCalculation' // 'calculationPeriodAmount/calculation/floatingRateCalculation/floatingRateIndex' // 'calculationPeriodAmount/calculation/floatingRateCalculation/indexTenor?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/floatingRateMultiplierSchedule?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/spreadSchedule*' // 'calculationPeriodAmount/calculation/floatingRateCalculation/initialRate?' (Ibor only) // 'calculationPeriodAmount/calculation/floatingRateCalculation/averagingMethod?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/negativeInterestRateTreatment?' // 'calculationPeriodAmount/calculation/dayCountFraction' // 'resetDates/resetRelativeTo' // 'resetDates/fixingDates' // 'resetDates/rateCutOffDaysOffset' (OIS only) // 'resetDates/resetFrequency' // 'resetDates/resetDatesAdjustments' // 'stubCalculationPeriodAmount/initalStub' (Ibor only, Overnight must match index) // 'stubCalculationPeriodAmount/finalStub' (Ibor only, Overnight must match index) // ignored elements: // 'calculationPeriodAmount/calculation/floatingRateCalculation/finalRateRounding?' // 'calculationPeriodAmount/calculation/discounting?' // 'resetDates/calculationPeriodDatesReference' // rejected elements: // 'calculationPeriodAmount/calculation/floatingRateCalculation/spreadSchedule/type?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/rateTreatment?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/capRateSchedule?' // 'calculationPeriodAmount/calculation/floatingRateCalculation/floorRateSchedule?' // 'resetDates/initialFixingDate' document.validateNotPresent(floatingEl, "rateTreatment"); document.validateNotPresent(floatingEl, "capRateSchedule"); document.validateNotPresent(floatingEl, "floorRateSchedule"); Index index = document.parseIndex(floatingEl); if (index is IborIndex) { IborRateCalculation.Builder iborRateBuilder = IborRateCalculation.builder(); // day count iborRateBuilder.dayCount(document.parseDayCountFraction(calcEl.getChild("dayCountFraction"))); // index iborRateBuilder.index((IborIndex)document.parseIndex(floatingEl)); // gearing floatingEl.findChild("floatingRateMultiplierSchedule").ifPresent(el => { iborRateBuilder.gearing(parseSchedule(el, document)); }); // spread if (floatingEl.getChildren("spreadSchedule").size() > 1) { throw new FpmlParseException("Only one 'spreadSchedule' is supported"); } floatingEl.findChild("spreadSchedule").ifPresent(el => { document.validateNotPresent(el, "type"); iborRateBuilder.spread(parseSchedule(el, document)); }); // initial fixed rate floatingEl.findChild("initialRate").ifPresent(el => { iborRateBuilder.firstRegularRate(document.parseDecimal(el)); }); // negative rates floatingEl.findChild("negativeInterestRateTreatment").ifPresent(el => { iborRateBuilder.negativeRateMethod(parseNegativeInterestRateTreatment(el)); }); // resets legEl.findChild("resetDates").ifPresent(resetDatesEl => { document.validateNotPresent(resetDatesEl, "initialFixingDate"); document.validateNotPresent(resetDatesEl, "rateCutOffDaysOffset"); resetDatesEl.findChild("resetRelativeTo").ifPresent(el => { iborRateBuilder.fixingRelativeTo(parseResetRelativeTo(el)); }); iborRateBuilder.fixingDateOffset(document.parseRelativeDateOffsetDays(resetDatesEl.getChild("fixingDates"))); Frequency resetFreq = document.parseFrequency(resetDatesEl.getChild("resetFrequency")); if (!accrualSchedule.Frequency.Equals(resetFreq)) { ResetSchedule.Builder resetScheduleBuilder = ResetSchedule.builder(); resetScheduleBuilder.resetFrequency(resetFreq); floatingEl.findChild("averagingMethod").ifPresent(el => { resetScheduleBuilder.resetMethod(parseAveragingMethod(el)); }); resetScheduleBuilder.businessDayAdjustment(document.parseBusinessDayAdjustments(resetDatesEl.getChild("resetDatesAdjustments"))); iborRateBuilder.resetPeriods(resetScheduleBuilder.build()); } }); // stubs legEl.findChild("stubCalculationPeriodAmount").ifPresent(stubsEl => { stubsEl.findChild("initialStub").ifPresent(el => { iborRateBuilder.initialStub(parseStubCalculation(el, document)); }); stubsEl.findChild("finalStub").ifPresent(el => { iborRateBuilder.finalStub(parseStubCalculation(el, document)); }); }); return(iborRateBuilder.build()); } else if (index is OvernightIndex) { OvernightRateCalculation.Builder overnightRateBuilder = OvernightRateCalculation.builder(); document.validateNotPresent(floatingEl, "initialRate"); // TODO: should support this in the model // stubs legEl.findChild("stubCalculationPeriodAmount").ifPresent(stubsEl => { stubsEl.findChild("initialStub").ifPresent(el => { checkStubForOvernightIndex(el, document, (OvernightIndex)index); }); stubsEl.findChild("finalStub").ifPresent(el => { checkStubForOvernightIndex(el, document, (OvernightIndex)index); }); }); // day count overnightRateBuilder.dayCount(document.parseDayCountFraction(calcEl.getChild("dayCountFraction"))); // index overnightRateBuilder.index((OvernightIndex)document.parseIndex(floatingEl)); // accrual method FloatingRateName idx = FloatingRateName.of(floatingEl.getChild("floatingRateIndex").Content); if (idx.Type == FloatingRateType.OVERNIGHT_COMPOUNDED) { overnightRateBuilder.accrualMethod(OvernightAccrualMethod.COMPOUNDED); } // gearing floatingEl.findChild("floatingRateMultiplierSchedule").ifPresent(el => { overnightRateBuilder.gearing(parseSchedule(el, document)); }); // spread if (floatingEl.getChildren("spreadSchedule").size() > 1) { throw new FpmlParseException("Only one 'spreadSchedule' is supported"); } floatingEl.findChild("spreadSchedule").ifPresent(el => { document.validateNotPresent(el, "type"); overnightRateBuilder.spread(parseSchedule(el, document)); }); // negative rates floatingEl.findChild("negativeInterestRateTreatment").ifPresent(el => { overnightRateBuilder.negativeRateMethod(parseNegativeInterestRateTreatment(el)); }); // rate cut off legEl.findChild("resetDates").ifPresent(resetDatesEl => { document.validateNotPresent(resetDatesEl, "initialFixingDate"); resetDatesEl.findChild("rateCutOffDaysOffset").ifPresent(el => { Period cutOff = document.parsePeriod(el); if (cutOff.toTotalMonths() != 0) { throw new FpmlParseException("Invalid 'rateCutOffDaysOffset' value, expected days-based period: " + cutOff); } overnightRateBuilder.rateCutOffDays(-cutOff.Days); }); }); return(overnightRateBuilder.build()); } else { throw new FpmlParseException("Invalid 'floatingRateIndex' type, not Ibor or Overnight"); } }
// parses the payment schedule private PaymentSchedule parseSwapPaymentSchedule(XmlElement legEl, XmlElement calcEl, FpmlDocument document) { // supported elements: // 'paymentDates/paymentFrequency' // 'paymentDates/payRelativeTo' // 'paymentDates/paymentDaysOffset?' // 'paymentDates/paymentDatesAdjustments' // 'calculationPeriodAmount/calculation/compoundingMethod' // 'paymentDates/firstPaymentDate?' // 'paymentDates/lastRegularPaymentDate?' // ignored elements: // 'paymentDates/calculationPeriodDatesReference' // 'paymentDates/resetDatesReference' // 'paymentDates/valuationDatesReference' PaymentSchedule.Builder paymentScheduleBuilder = PaymentSchedule.builder(); // payment dates XmlElement paymentDatesEl = legEl.getChild("paymentDates"); // frequency paymentScheduleBuilder.paymentFrequency(document.parseFrequency(paymentDatesEl.getChild("paymentFrequency"))); //default for IRS is pay relative to period end; Strata model will apply the defaulting but the values is needed //here for first and last payment date checks PaymentRelativeTo payRelativeTo = paymentDatesEl.findChild("payRelativeTo").map(el => parsePayRelativeTo(el)).orElse(PaymentRelativeTo.PERIOD_END); paymentScheduleBuilder.paymentRelativeTo(payRelativeTo); // dates if (payRelativeTo == PaymentRelativeTo.PERIOD_END) { // ignore data if not PeriodEnd and hope schedule is worked out correctly by other means // this provides compatibility for old code that ignored these FpML fields paymentDatesEl.findChild("firstPaymentDate").map(el => document.parseDate(el)).ifPresent(date => paymentScheduleBuilder.firstRegularStartDate(date)); paymentDatesEl.findChild("lastRegularPaymentDate").map(el => document.parseDate(el)).ifPresent(date => paymentScheduleBuilder.lastRegularEndDate(date)); } // offset Optional <XmlElement> paymentOffsetEl = paymentDatesEl.findChild("paymentDaysOffset"); BusinessDayAdjustment payAdjustment = document.parseBusinessDayAdjustments(paymentDatesEl.getChild("paymentDatesAdjustments")); if (paymentOffsetEl.Present) { Period period = document.parsePeriod(paymentOffsetEl.get()); if (period.toTotalMonths() != 0) { throw new FpmlParseException("Invalid 'paymentDatesAdjustments' value, expected days-based period: " + period); } Optional <XmlElement> dayTypeEl = paymentOffsetEl.get().findChild("dayType"); bool fixingCalendarDays = period.Zero || (dayTypeEl.Present && dayTypeEl.get().Content.Equals("Calendar")); if (fixingCalendarDays) { paymentScheduleBuilder.paymentDateOffset(DaysAdjustment.ofCalendarDays(period.Days, payAdjustment)); } else { paymentScheduleBuilder.paymentDateOffset(DaysAdjustment.ofBusinessDays(period.Days, payAdjustment.Calendar)); } } else { paymentScheduleBuilder.paymentDateOffset(DaysAdjustment.ofCalendarDays(0, payAdjustment)); } // compounding calcEl.findChild("compoundingMethod").ifPresent(compoundingEl => { paymentScheduleBuilder.compoundingMethod(CompoundingMethod.of(compoundingEl.Content)); }); return(paymentScheduleBuilder.build()); }