/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.loader.impl.fpml; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.util.Optional; import com.opengamma.strata.basics.currency.AdjustablePayment; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.date.AdjustableDate; import com.opengamma.strata.collect.io.XmlElement; import com.opengamma.strata.loader.fpml.FpmlDocument; import com.opengamma.strata.loader.fpml.FpmlParseException; import com.opengamma.strata.loader.fpml.FpmlParserPlugin; import com.opengamma.strata.product.Trade; import com.opengamma.strata.product.TradeInfoBuilder; import com.opengamma.strata.product.common.BuySell; import com.opengamma.strata.product.common.LongShort; import com.opengamma.strata.product.common.PayReceive; import com.opengamma.strata.product.swap.Swap; import com.opengamma.strata.product.swaption.CashSwaptionSettlement; import com.opengamma.strata.product.swaption.CashSwaptionSettlementMethod; import com.opengamma.strata.product.swaption.PhysicalSwaptionSettlement; import com.opengamma.strata.product.swaption.Swaption; import com.opengamma.strata.product.swaption.SwaptionSettlement; import com.opengamma.strata.product.swaption.SwaptionTrade; /** * FpML parser for Swaptions. * <p> * This parser handles the subset of FpML necessary to populate the swaption trade model. */ final class SwaptionFpmlParserPlugin implements FpmlParserPlugin { /** * The singleton instance of the parser. */ public static final SwaptionFpmlParserPlugin INSTANCE = new SwaptionFpmlParserPlugin(); /** * Restricted constructor. */ private SwaptionFpmlParserPlugin() { } //------------------------------------------------------------------------- @Override public Trade parseTrade(FpmlDocument document, XmlElement tradeEl) { // supported elements: // 'swaption' // 'swaption/buyerPartyReference' // 'swaption/sellerPartyReference' // 'swaption/premium/payerPartyReference' // 'swaption/premium/receiverPartyReference' // 'swaption/premium/paymentAmount' // 'swaption/premium/paymentDate' // 'swaption/europeanExercise' // 'swaption/europeanExercise/expirationDate' // 'swaption/europeanExercise/expirationDate/adjustableDate' // 'swaption/europeanExercise/expirationDate/adjustableDate/unadjustedDate' // 'swaption/europeanExercise/expirationDate/adjustableDate/dateAdjustments' // 'swaption/europeanExercise/expirationTime // 'swaption/swap' // ignored elements: // 'Product.model?' // 'swaption/calculationAgent' // 'swaption/assetClass' // 'swaption/primaryAssestClass' // 'swaption/productId' // 'swaption/productType' // 'swaption/secondaryAssetClass' // 'swaption/sellerAccountReference' // 'swaption/sellerPartyReference' // 'swaption/swaptionAdjustedDates' // 'swaption/swaptionStraddle' TradeInfoBuilder tradeInfoBuilder = document.parseTradeInfo(tradeEl); XmlElement swaptionEl = tradeEl.getChild("swaption"); XmlElement europeanExerciseEl = swaptionEl.getChild("europeanExercise"); XmlElement expirationTimeEl = europeanExerciseEl.getChild("expirationTime"); // Parse the premium, expiry date, expiry time and expiry zone, longShort and swaption settlement. AdjustablePayment premium = parsePremium(swaptionEl, document, tradeInfoBuilder); AdjustableDate expiryDate = parseExpiryDate(europeanExerciseEl, document); LocalTime expiryTime = parseExpiryTime(expirationTimeEl, document); ZoneId expiryZone = parseExpiryZone(expirationTimeEl, document); LongShort longShort = parseLongShort(swaptionEl, document, tradeInfoBuilder); SwaptionSettlement swaptionSettlement = parseSettlement(swaptionEl, document); //Re use the Swap FpML parser to parse the underlying swap on this swaption. SwapFpmlParserPlugin swapParser = SwapFpmlParserPlugin.INSTANCE; Swap swap = swapParser.parseSwap(document, swaptionEl, tradeInfoBuilder); Swaption swaption = Swaption.builder() .expiryDate(expiryDate) .expiryZone(expiryZone) .expiryTime(expiryTime) .longShort(longShort) .swaptionSettlement(swaptionSettlement) .underlying(swap) .build(); return SwaptionTrade.builder() .info(tradeInfoBuilder.build()) .product(swaption) .premium(premium) .build(); } private AdjustableDate parseExpiryDate(XmlElement europeanExerciseEl, FpmlDocument document) { XmlElement expirationDate = europeanExerciseEl.getChild("expirationDate"); return expirationDate.findChild("adjustableDate") .map(el -> document.parseAdjustableDate(el)).get(); } private LocalTime parseExpiryTime(XmlElement expirationTimeEl, FpmlDocument document) { return document.parseTime(expirationTimeEl.getChild("hourMinuteTime")); } private ZoneId parseExpiryZone(XmlElement expirationTimeEl, FpmlDocument document) { String businessCenter = expirationTimeEl.getChild("businessCenter").getContent(); Optional<ZoneId> optionalZoneId = document.getZoneId(businessCenter); if (!optionalZoneId.isPresent()) { throw new FpmlParseException("Unknown businessCenter" + " attribute value: " + businessCenter); } return optionalZoneId.get(); } private AdjustablePayment parsePremium(XmlElement swaptionEl, FpmlDocument document, TradeInfoBuilder tradeInfoBuilder) { XmlElement premiumEl = swaptionEl.getChild("premium"); PayReceive payReceive = document.parsePayerReceiver(premiumEl, tradeInfoBuilder); XmlElement paymentAmountEl = premiumEl.getChild("paymentAmount"); CurrencyAmount ccyAmount = document.parseCurrencyAmount(paymentAmountEl); ccyAmount = payReceive.isPay() ? ccyAmount.negated() : ccyAmount; AdjustableDate paymentDate = premiumEl.findChild("paymentDate") .map(el -> document.parseAdjustableDate(el)).get(); return AdjustablePayment.of(ccyAmount, paymentDate); } private LongShort parseLongShort(XmlElement swaptionEl, FpmlDocument document, TradeInfoBuilder tradeInfoBuilder) { BuySell buySell = document.parseBuyerSeller(swaptionEl, tradeInfoBuilder); return buySell.isBuy() ? LongShort.LONG : LongShort.SHORT; } private SwaptionSettlement parseSettlement(XmlElement swaptionEl, FpmlDocument document) { Optional<String> optionalCashSettlement = swaptionEl.findAttribute("cashSettlement"); if (optionalCashSettlement.isPresent()) { XmlElement cashSettlementEl = swaptionEl.getChild("cashSettlement"); CashSwaptionSettlementMethod method = parseCashSettlementMethod(cashSettlementEl); LocalDate settlementDate = document.parseAdjustedRelativeDateOffset(cashSettlementEl).getUnadjusted(); return CashSwaptionSettlement.of(settlementDate, method); } else { // treat physical as the default to match FpML examples return PhysicalSwaptionSettlement.DEFAULT; } } private CashSwaptionSettlementMethod parseCashSettlementMethod(XmlElement cashSettlementEl) { if (cashSettlementEl.findChild("cashPriceAlternateMethod").isPresent()) { return CashSwaptionSettlementMethod.CASH_PRICE; } else if (cashSettlementEl.findChild("parYieldCurveUnadjustedMethod").isPresent() || cashSettlementEl.findChild("parYieldCurveAadjustedMethod").isPresent()) { return CashSwaptionSettlementMethod.PAR_YIELD; } else if (cashSettlementEl.findChild("zeroCouponYieldAdjustedMethod").isPresent()) { return CashSwaptionSettlementMethod.ZERO_COUPON_YIELD; } else { throw new FpmlParseException("Invalid swaption cash settlement method: " + cashSettlementEl); } } //------------------------------------------------------------------------- @Override public String getName() { return "swaption"; } }