diff --git a/modules/loader/src/main/java/com/opengamma/strata/loader/csv/IborCapFloorTradeCsvPlugin.java b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/IborCapFloorTradeCsvPlugin.java index 4f6094dc71..e69bbc6fbb 100644 --- a/modules/loader/src/main/java/com/opengamma/strata/loader/csv/IborCapFloorTradeCsvPlugin.java +++ b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/IborCapFloorTradeCsvPlugin.java @@ -68,7 +68,7 @@ import com.opengamma.strata.product.swap.IborRateCalculation; /** - * Handles the CSV file format for CapFloor trades. + * Handles the CSV file format for IBOR cap/floor trades. */ public class IborCapFloorTradeCsvPlugin implements TradeCsvParserPlugin, TradeCsvWriterPlugin { @@ -122,7 +122,7 @@ public Optional parseTrade( TradeCsvInfoResolver resolver) { if (requiredJavaType.isAssignableFrom(IborCapFloorTrade.class)) { - return Optional.of(resolver.parseCapFloorTrade(baseRow, info)); + return Optional.of(resolver.parseIborCapFloorTrade(baseRow, info)); } return Optional.empty(); } diff --git a/modules/loader/src/main/java/com/opengamma/strata/loader/csv/OvernightInArrearsCapFloorTradeCsvPlugin.java b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/OvernightInArrearsCapFloorTradeCsvPlugin.java new file mode 100644 index 0000000000..513d0f1174 --- /dev/null +++ b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/OvernightInArrearsCapFloorTradeCsvPlugin.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2024 - present by OpenGamma Inc. and the OpenGamma group of companies + * + * Please see distribution for license. + */ +package com.opengamma.strata.loader.csv; + +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.CAP_FLOOR_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.CURRENCY_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.DATE_ADJ_CAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.DATE_ADJ_CNV_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.DIRECTION_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.END_DATE_CAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.END_DATE_CNV_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.END_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.FIRST_REGULAR_START_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.INDEX_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.LAST_REGULAR_END_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.NOTIONAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.OVERRIDE_START_DATE_CAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.OVERRIDE_START_DATE_CNV_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.OVERRIDE_START_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PAYMENT_FREQUENCY_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_AMOUNT_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_CURRENCY_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_DATE_CAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_DATE_CNV_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.PREMIUM_DIRECTION_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.ROLL_CONVENTION_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.START_DATE_CAL_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.START_DATE_CNV_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.START_DATE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.STRIKE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.STUB_CONVENTION_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderColumns.TRADE_TYPE_FIELD; +import static com.opengamma.strata.loader.csv.CsvLoaderUtils.formattedPercentage; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import com.opengamma.strata.basics.currency.AdjustablePayment; +import com.opengamma.strata.basics.currency.Currency; +import com.opengamma.strata.basics.currency.CurrencyAmount; +import com.opengamma.strata.basics.date.AdjustableDate; +import com.opengamma.strata.basics.date.BusinessDayAdjustment; +import com.opengamma.strata.basics.date.BusinessDayConventions; +import com.opengamma.strata.basics.index.Index; +import com.opengamma.strata.basics.index.OvernightIndex; +import com.opengamma.strata.basics.schedule.Frequency; +import com.opengamma.strata.basics.schedule.PeriodicSchedule; +import com.opengamma.strata.basics.schedule.StubConvention; +import com.opengamma.strata.basics.value.ValueSchedule; +import com.opengamma.strata.collect.io.CsvOutput; +import com.opengamma.strata.collect.io.CsvRow; +import com.opengamma.strata.collect.result.ParseFailureException; +import com.opengamma.strata.loader.LoaderUtils; +import com.opengamma.strata.product.Trade; +import com.opengamma.strata.product.TradeInfo; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloor; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloorLeg; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloorTrade; +import com.opengamma.strata.product.common.CapFloor; +import com.opengamma.strata.product.common.PayReceive; +import com.opengamma.strata.product.swap.OvernightRateCalculation; + +/** + * Handles the CSV file format for Overnight in arrears cap/floor trades. + */ +public class OvernightInArrearsCapFloorTradeCsvPlugin + implements TradeCsvParserPlugin, TradeCsvWriterPlugin { + + /** + * The singleton instance of the plugin. + */ + public static final OvernightInArrearsCapFloorTradeCsvPlugin INSTANCE = + new OvernightInArrearsCapFloorTradeCsvPlugin(); + + private static final ImmutableSet HEADERS = ImmutableSet.of( + CAP_FLOOR_FIELD, + START_DATE_FIELD, + END_DATE_FIELD, + PAYMENT_FREQUENCY_FIELD, + DIRECTION_FIELD, + CURRENCY_FIELD, + STRIKE_FIELD, + NOTIONAL_FIELD, + INDEX_FIELD, + PREMIUM_AMOUNT_FIELD, + PREMIUM_CURRENCY_FIELD, + PREMIUM_DIRECTION_FIELD, + PREMIUM_DATE_FIELD, + PREMIUM_DATE_CNV_FIELD, + PREMIUM_DATE_CAL_FIELD, + DATE_ADJ_CNV_FIELD, + DATE_ADJ_CAL_FIELD, + START_DATE_CNV_FIELD, + START_DATE_CAL_FIELD, + END_DATE_CNV_FIELD, + END_DATE_CAL_FIELD, + STUB_CONVENTION_FIELD, + ROLL_CONVENTION_FIELD, + FIRST_REGULAR_START_DATE_FIELD, + LAST_REGULAR_END_DATE_FIELD, + OVERRIDE_START_DATE_FIELD, + OVERRIDE_START_DATE_CNV_FIELD, + OVERRIDE_START_DATE_CAL_FIELD); + + //------------------------------------------------------------------------- + @Override + public Set tradeTypeNames() { + return ImmutableSet.of("OVERNIGHTCAPFLOOR", "OVERNIGHT CAPFLOOR"); + } + + @Override + public Optional parseTrade( + Class requiredJavaType, + CsvRow baseRow, + List additionalRows, + TradeInfo info, + TradeCsvInfoResolver resolver) { + + if (requiredJavaType.isAssignableFrom(OvernightInArrearsCapFloorTrade.class)) { + return Optional.of(resolver.parseOvernightCapFloorTrade(baseRow, info)); + } + return Optional.empty(); + } + + @Override + public String getName() { + return OvernightInArrearsCapFloorTrade.class.getSimpleName(); + } + + @Override + public Set> supportedTradeTypes() { + return ImmutableSet.of(OvernightInArrearsCapFloorTrade.class); + } + + //------------------------------------------------------------------------- + /** + * Parses from the CSV row. + * + * @param row the CSV row + * @param info the trade info + * @param resolver the resolver used to parse additional information + * @return the parsed trade + */ + static OvernightInArrearsCapFloorTrade parseCapFloor(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) { + OvernightInArrearsCapFloorTrade overnightCapFloorTrade = parseRow(row, info); + return resolver.completeTrade(row, overnightCapFloorTrade); + } + + //------------------------------------------------------------------------- + // parses the row to a trade + private static OvernightInArrearsCapFloorTrade parseRow(CsvRow row, TradeInfo info) { + OvernightInArrearsCapFloor overnightInArrearsCapFloor = parseOvernightInArrearsCapFloor(row); + Optional paymentOpt = parsePremium(row); + return paymentOpt.map(adjustablePayment -> + OvernightInArrearsCapFloorTrade.builder() + .info(info) + .product(overnightInArrearsCapFloor) + .premium(adjustablePayment) + .build()) + .orElseGet(() -> OvernightInArrearsCapFloorTrade.builder() + .info(info) + .product(overnightInArrearsCapFloor) + .build()); + } + + // parse an OvernightInArrearsCapFloor + private static OvernightInArrearsCapFloor parseOvernightInArrearsCapFloor(CsvRow row) { + PayReceive payReceive = row.getValue(DIRECTION_FIELD, LoaderUtils::parsePayReceive); + Currency currency = row.getValue(CURRENCY_FIELD, LoaderUtils::parseCurrency); + ValueSchedule strike = ValueSchedule.of(row.getValue(STRIKE_FIELD, LoaderUtils::parseDoublePercent)); + double notional = row.getValue(NOTIONAL_FIELD, LoaderUtils::parseDouble); + OvernightIndex overnightIndex = parseOvernightIndex(row); + PeriodicSchedule paymentSchedule = parseSchedule(row, currency); + OvernightInArrearsCapFloorLeg.Builder capFloorLegBuilder = OvernightInArrearsCapFloorLeg.builder() + .payReceive(payReceive) + .paymentSchedule(paymentSchedule) + .currency(currency) + .notional(ValueSchedule.of(notional)) + .calculation(OvernightRateCalculation.of(overnightIndex)); + CapFloor capFloorType = LoaderUtils.parseCapFloor(row.getValue(CAP_FLOOR_FIELD)); + if (capFloorType.isCap()) { + capFloorLegBuilder.capSchedule(strike); + } else { + capFloorLegBuilder.floorSchedule(strike); + } + OvernightInArrearsCapFloorLeg capFloorLeg = capFloorLegBuilder.build(); + return OvernightInArrearsCapFloor.of(capFloorLeg); + } + + // parse the overnight index + private static OvernightIndex parseOvernightIndex(CsvRow row) { + Index index = row.getValue(INDEX_FIELD, LoaderUtils::findIndex); + if (index instanceof OvernightIndex) { + return (OvernightIndex) index; + } else { + throw new ParseFailureException("Index '{value}' is not an overnight index", index.getName()); + } + } + + // parse the periodic schedule for CapFloor + private static PeriodicSchedule parseSchedule(CsvRow row, Currency currency) { + PeriodicSchedule.Builder builder = PeriodicSchedule.builder(); + // basics + builder.startDate(row.getValue(START_DATE_FIELD, LoaderUtils::parseDate)); + builder.endDate(row.getValue(END_DATE_FIELD, LoaderUtils::parseDate)); + builder.frequency(Frequency.parse(row.getValue(PAYMENT_FREQUENCY_FIELD))); + // stub period is not allowed for CapFloor trade + builder.stubConvention(StubConvention.NONE); + // adjustments + BusinessDayAdjustment dateAdj = + CsvLoaderUtils.parseBusinessDayAdjustment(row, DATE_ADJ_CNV_FIELD, DATE_ADJ_CAL_FIELD) + .orElse(BusinessDayAdjustment.NONE); + builder.businessDayAdjustment(dateAdj); + CsvLoaderUtils.parseBusinessDayAdjustment(row, START_DATE_CNV_FIELD, START_DATE_CAL_FIELD) + .ifPresent(builder::startDateBusinessDayAdjustment); + CsvLoaderUtils.parseBusinessDayAdjustment(row, END_DATE_CNV_FIELD, END_DATE_CAL_FIELD) + .ifPresent(builder::endDateBusinessDayAdjustment); + // optionals + row.findValue(ROLL_CONVENTION_FIELD) + .map(LoaderUtils::parseRollConvention) + .ifPresent(builder::rollConvention); + row.findValue(FIRST_REGULAR_START_DATE_FIELD) + .map(LoaderUtils::parseDate) + .ifPresent(builder::firstRegularStartDate); + row.findValue(LAST_REGULAR_END_DATE_FIELD) + .map(LoaderUtils::parseDate) + .ifPresent(builder::lastRegularEndDate); + Optional overrideDateOpt = row.findValue(OVERRIDE_START_DATE_FIELD) + .map(ignored -> CsvLoaderUtils.parseAdjustableDate( + row, + OVERRIDE_START_DATE_FIELD, + OVERRIDE_START_DATE_CNV_FIELD, + OVERRIDE_START_DATE_CAL_FIELD, + BusinessDayConventions.MODIFIED_FOLLOWING, + currency)); + overrideDateOpt.ifPresent(builder::overrideStartDate); + return builder.build(); + } + + // parse the upfront premium amount + private static Optional parsePremium(CsvRow row) { + Optional premiumAmountOpt = row.findValue(PREMIUM_AMOUNT_FIELD) + .map(LoaderUtils::parseDouble); + if (premiumAmountOpt.isPresent()) { + Currency premiumCurrency = row.getValue(PREMIUM_CURRENCY_FIELD, LoaderUtils::parseCurrency); + PayReceive premiumDirection = row.getValue(PREMIUM_DIRECTION_FIELD, LoaderUtils::parsePayReceive); + LocalDate premiumDate = row.getValue(PREMIUM_DATE_FIELD, LoaderUtils::parseDate); + CurrencyAmount premiumAmount = CurrencyAmount.of(premiumCurrency, premiumAmountOpt.get()); + AdjustablePayment payment = premiumDirection.isPay() ? + AdjustablePayment.ofPay(premiumAmount, premiumDate) : + AdjustablePayment.ofReceive(premiumAmount, premiumDate); + return Optional.of(payment); + } + return Optional.empty(); + } + + //------------------------------------------------------------------------- + @Override + public Set headers(List trades) { + return HEADERS; + } + + @Override + public void writeCsv(CsvOutput.CsvRowOutputWithHeaders csv, OvernightInArrearsCapFloorTrade trade) { + OvernightInArrearsCapFloorLeg capFloorLeg = trade.getProduct().getCapFloorLeg(); + csv.writeCell(TRADE_TYPE_FIELD, "OvernightCapFloor"); + String capFloorType; + double strike; + if (capFloorLeg.getCapSchedule().isPresent()) { + capFloorType = "Cap"; + strike = capFloorLeg.getCapSchedule().get().getInitialValue(); + } else if (capFloorLeg.getFloorSchedule().isPresent()) { + capFloorType = "Floor"; + strike = capFloorLeg.getFloorSchedule().get().getInitialValue(); + } else { + throw new UnsupportedOperationException("Unknown CapFloor type, trade is missing cap or floor schedule"); + } + csv.writeCell(CAP_FLOOR_FIELD, capFloorType); + writePeriodicSchedule(csv, capFloorLeg.getPaymentSchedule()); + csv.writeCell(DIRECTION_FIELD, capFloorLeg.getPayReceive()); + csv.writeCell(CURRENCY_FIELD, capFloorLeg.getCurrency()); + csv.writeCell(STRIKE_FIELD, formattedPercentage(strike)); + csv.writeCell(NOTIONAL_FIELD, capFloorLeg.getNotional().getInitialValue()); + csv.writeCell(INDEX_FIELD, capFloorLeg.getCalculation().getIndex()); + trade.getPremium().ifPresent(premium -> CsvWriterUtils.writePremiumFields(csv, premium)); + csv.writeNewLine(); + } + + private void writePeriodicSchedule(CsvOutput.CsvRowOutputWithHeaders csv, PeriodicSchedule paymentSchedule) { + csv.writeCell(START_DATE_FIELD, paymentSchedule.getStartDate()); + csv.writeCell(END_DATE_FIELD, paymentSchedule.getEndDate()); + csv.writeCell(PAYMENT_FREQUENCY_FIELD, paymentSchedule.getFrequency()); + csv.writeCell(DATE_ADJ_CNV_FIELD, paymentSchedule.getBusinessDayAdjustment().getConvention()); + csv.writeCell(DATE_ADJ_CAL_FIELD, paymentSchedule.getBusinessDayAdjustment().getCalendar()); + paymentSchedule.getStartDateBusinessDayAdjustment().ifPresent(startDateAdjustment -> { + csv.writeCell(START_DATE_CNV_FIELD, startDateAdjustment.getConvention()); + csv.writeCell(START_DATE_CAL_FIELD, startDateAdjustment.getCalendar()); + }); + paymentSchedule.getEndDateBusinessDayAdjustment().ifPresent(endDateAdjustment -> { + csv.writeCell(END_DATE_CNV_FIELD, endDateAdjustment.getConvention()); + csv.writeCell(END_DATE_CAL_FIELD, endDateAdjustment.getCalendar()); + }); + paymentSchedule.getStubConvention().ifPresent( + stubConvention -> csv.writeCell(STUB_CONVENTION_FIELD, stubConvention)); + paymentSchedule.getRollConvention().ifPresent( + rollConvention -> csv.writeCell(ROLL_CONVENTION_FIELD, rollConvention)); + paymentSchedule.getFirstRegularStartDate().ifPresent( + firstRegDate -> csv.writeCell(FIRST_REGULAR_START_DATE_FIELD, firstRegDate)); + paymentSchedule.getLastRegularEndDate().ifPresent( + lastRegDate -> csv.writeCell(LAST_REGULAR_END_DATE_FIELD, lastRegDate)); + paymentSchedule.getOverrideStartDate().ifPresent(overrideStartDate -> { + csv.writeCell(OVERRIDE_START_DATE_FIELD, overrideStartDate.getUnadjusted()); + csv.writeCell(OVERRIDE_START_DATE_CNV_FIELD, overrideStartDate.getAdjustment().getConvention()); + csv.writeCell(OVERRIDE_START_DATE_CAL_FIELD, overrideStartDate.getAdjustment().getCalendar()); + }); + } + + //------------------------------------------------------------------------- + // Restricted constructor. + private OvernightInArrearsCapFloorTradeCsvPlugin() { + } + +} diff --git a/modules/loader/src/main/java/com/opengamma/strata/loader/csv/TradeCsvInfoResolver.java b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/TradeCsvInfoResolver.java index 00501f8e64..d72840b211 100644 --- a/modules/loader/src/main/java/com/opengamma/strata/loader/csv/TradeCsvInfoResolver.java +++ b/modules/loader/src/main/java/com/opengamma/strata/loader/csv/TradeCsvInfoResolver.java @@ -21,6 +21,7 @@ import com.opengamma.strata.product.TradeInfo; import com.opengamma.strata.product.TradeInfoBuilder; import com.opengamma.strata.product.capfloor.IborCapFloorTrade; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloorTrade; import com.opengamma.strata.product.common.CcpId; import com.opengamma.strata.product.credit.CdsIndexTrade; import com.opengamma.strata.product.credit.CdsTrade; @@ -338,6 +339,23 @@ public default IborCapFloorTrade completeTrade(CsvRow row, IborCapFloorTrade tra return completeTradeCommon(row, trade); } + /** + * Completes the CapFloor trade, potentially parsing additional columns. + *

+ * This is called after the trade has been parsed and after + * {@link #parseTradeInfo(CsvRow, TradeInfoBuilder)}. + *

+ * By default this calls {@link #completeTradeCommon(CsvRow, Trade)}. + * + * @param row the CSV row to parse + * @param trade the parsed trade + * @return the updated trade + */ + public default OvernightInArrearsCapFloorTrade completeTrade(CsvRow row, OvernightInArrearsCapFloorTrade trade) { + //do nothing + return completeTradeCommon(row, trade); + } + //------------------------------------------------------------------------- /** * Parses a FRA trade from CSV. @@ -486,17 +504,29 @@ public default CdsIndexTrade parseCdsIndexTrade(CsvRow row, TradeInfo info) { } /** - * Parses a CapFloor trade from CSV. + * Parses an IBOR CapFloor trade from CSV. * * @param row the CSV row to parse * @param info the trade info - * @return the CapFloor trade + * @return the IBOR CapFloor trade * @throws RuntimeException if the row contains invalid data */ - public default IborCapFloorTrade parseCapFloorTrade(CsvRow row, TradeInfo info) { + public default IborCapFloorTrade parseIborCapFloorTrade(CsvRow row, TradeInfo info) { return IborCapFloorTradeCsvPlugin.parseCapFloor(row, info, this); } + /** + * Parses an overnight CapFloor trade from CSV. + * + * @param row the CSV row to parse + * @param info the trade info + * @return the overnight CapFloor trade + * @throws RuntimeException if the row contains invalid data + */ + public default OvernightInArrearsCapFloorTrade parseOvernightCapFloorTrade(CsvRow row, TradeInfo info) { + return OvernightInArrearsCapFloorTradeCsvPlugin.parseCapFloor(row, info, this); + } + //------------------------------------------------------------------------- /** * Parses any kind of trade from CSV before standard matching. diff --git a/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvParserPlugin.ini b/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvParserPlugin.ini index 57c69cc204..3c9a0fea98 100644 --- a/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvParserPlugin.ini +++ b/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvParserPlugin.ini @@ -17,6 +17,7 @@ com.opengamma.strata.loader.csv.FxSingleTradeCsvPlugin = constants com.opengamma.strata.loader.csv.FxSwapTradeCsvPlugin = constants com.opengamma.strata.loader.csv.FxVanillaOptionTradeCsvPlugin = constants com.opengamma.strata.loader.csv.IborCapFloorTradeCsvPlugin = constants +com.opengamma.strata.loader.csv.OvernightInArrearsCapFloorTradeCsvPlugin = constants com.opengamma.strata.loader.csv.SecurityTradeCsvPlugin = constants com.opengamma.strata.loader.csv.SwapTradeCsvPlugin = constants com.opengamma.strata.loader.csv.SwaptionTradeCsvPlugin = constants diff --git a/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvWriterPlugin.ini b/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvWriterPlugin.ini index e14bc33b53..8a840721c0 100644 --- a/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvWriterPlugin.ini +++ b/modules/loader/src/main/resources/META-INF/com/opengamma/strata/config/base/TradeCsvWriterPlugin.ini @@ -21,6 +21,7 @@ com.opengamma.strata.loader.csv.CdsTradeCsvPlugin = constants com.opengamma.strata.loader.csv.CdsIndexTradeCsvPlugin = constants com.opengamma.strata.loader.csv.SecurityTradeCsvPlugin = constants com.opengamma.strata.loader.csv.IborCapFloorTradeCsvPlugin = constants +com.opengamma.strata.loader.csv.OvernightInArrearsCapFloorTradeCsvPlugin = constants com.opengamma.strata.loader.csv.GenericSecurityTradeCsvPlugin = constants # The set of alternate names diff --git a/modules/loader/src/test/java/com/opengamma/strata/loader/csv/TradeCsvLoaderTest.java b/modules/loader/src/test/java/com/opengamma/strata/loader/csv/TradeCsvLoaderTest.java index 0ae3077bde..fed06aad02 100644 --- a/modules/loader/src/test/java/com/opengamma/strata/loader/csv/TradeCsvLoaderTest.java +++ b/modules/loader/src/test/java/com/opengamma/strata/loader/csv/TradeCsvLoaderTest.java @@ -96,6 +96,9 @@ import com.opengamma.strata.product.capfloor.IborCapFloor; import com.opengamma.strata.product.capfloor.IborCapFloorLeg; import com.opengamma.strata.product.capfloor.IborCapFloorTrade; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloor; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloorLeg; +import com.opengamma.strata.product.capfloor.OvernightInArrearsCapFloorTrade; import com.opengamma.strata.product.common.CcpIds; import com.opengamma.strata.product.common.LongShort; import com.opengamma.strata.product.credit.Cds; @@ -2097,7 +2100,7 @@ public void test_load_filtered() { ImmutableList.of(FILE.getCharSource()), ImmutableList.of(FraTrade.class, TermDepositTrade.class)); assertThat(trades.getValue()).hasSize(6); - assertThat(trades.getFailures()).hasSize(24); + assertThat(trades.getFailures()).hasSize(26); assertThat(trades.getFailures().get(0).getMessage()).isEqualTo( "Trade type not allowed " + SwapTrade.class.getName() + ", only these types are supported: FraTrade, TermDepositTrade"); } @@ -2322,6 +2325,64 @@ public void test_load_CapFloor() { checkRoundtrip(IborCapFloorTrade.class, filtered, expected0, expected1); } + @Test + public void test_load_OvernightCapFloor() { + TradeCsvLoader test = TradeCsvLoader.standard(); + ValueWithFailures> trades = test.load(FILE); + + List filtered = trades.getValue().stream() + .flatMap(filtering(OvernightInArrearsCapFloorTrade.class)) + .collect(toImmutableList()); + assertThat(filtered).hasSize(2); + + OvernightInArrearsCapFloorTrade expected0 = OvernightInArrearsCapFloorTrade.builder() + .info(TradeInfo.builder() + .id(StandardId.of("OG", "123456")) + .tradeDate(date(2017, 6, 1)) + .build()) + .product(OvernightInArrearsCapFloor.of(OvernightInArrearsCapFloorLeg.builder() + .payReceive(RECEIVE) + .paymentSchedule(PeriodicSchedule.of( + date(2020, 3, 10), + date(2025, 3, 10), + Frequency.P3M, + BusinessDayAdjustment.NONE, + StubConvention.NONE, + false)) + .currency(USD) + .notional(ValueSchedule.of(10_000_000)) + .calculation(OvernightRateCalculation.of(OvernightIndices.USD_SOFR)) + .capSchedule(ValueSchedule.of(0.021)) + .build())) + .build(); + assertBeanEquals(expected0, filtered.get(0)); + + OvernightInArrearsCapFloorTrade expected1 = OvernightInArrearsCapFloorTrade.builder() + .info(TradeInfo.builder() + .id(StandardId.of("OG", "123457")) + .tradeDate(date(2017, 6, 1)) + .build()) + .product(OvernightInArrearsCapFloor.of(OvernightInArrearsCapFloorLeg.builder() + .payReceive(PAY) + .paymentSchedule(PeriodicSchedule.of( + date(2020, 3, 10), + date(2030, 3, 10), + Frequency.P6M, + BusinessDayAdjustment.NONE, + StubConvention.NONE, + false)) + .currency(EUR) + .notional(ValueSchedule.of(15_000_000)) + .calculation(OvernightRateCalculation.of(OvernightIndices.EUR_ESTR)) + .floorSchedule(ValueSchedule.of(0.005)) + .build())) + .premium(AdjustablePayment.ofReceive(CurrencyAmount.of(EUR, 5000), date(2020, 3, 10))) + .build(); + assertBeanEquals(expected1, filtered.get(1)); + + checkRoundtrip(OvernightInArrearsCapFloorTrade.class, filtered, expected0, expected1); + } + //------------------------------------------------------------------------- @Test public void test_load_invalidNoHeader() { diff --git a/modules/loader/src/test/resources/com/opengamma/strata/loader/csv/trades.csv b/modules/loader/src/test/resources/com/opengamma/strata/loader/csv/trades.csv index 8bb1368405..5348549445 100644 --- a/modules/loader/src/test/resources/com/opengamma/strata/loader/csv/trades.csv +++ b/modules/loader/src/test/resources/com/opengamma/strata/loader/csv/trades.csv @@ -45,3 +45,6 @@ CDS Index,OG,123455,01/06/2017,,,,,Buy,,,,,5,,,GBP,1000000,01/06/2017,01/06/2022 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, CapFloor,OG,123452,01/06/2017,,,,,,,,USD-LIBOR-3M,,,,,USD,10000000,10/03/2020,10/03/2025,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Cap,Receive,2.1,3M,,,, CapFloor,OG,123453,01/06/2017,,,,,,,,EUR-EURIBOR-6M,,,,,EUR,15000000,10/03/2020,10/03/2030,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Receive,EUR,5000,10/03/2020,,,,,,,,,,,,,,Floor,Pay,0.5,6M,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +OvernightCapFloor,OG,123456,01/06/2017,,,,,,,,USD-SOFR,,,,,USD,10000000,10/03/2020,10/03/2025,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Cap,Receive,2.1,3M,,,, +OvernightCapFloor,OG,123457,01/06/2017,,,,,,,,EUR-ESTR,,,,,EUR,15000000,10/03/2020,10/03/2030,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,Receive,EUR,5000,10/03/2020,,,,,,,,,,,,,,Floor,Pay,0.5,6M,,,,