Skip to content

Commit

Permalink
Add FxSingleBarrierOptionTradeCsvPlugin and related tests. (#2324)
Browse files Browse the repository at this point in the history
  • Loading branch information
santorog authored Jul 7, 2021
1 parent 34addca commit 141ffb2
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ public static CurrencyAmount parseCurrencyAmountWithDirection(
* @param directionField the direction field
* @return if a valid currency amount can be read
*/
public static boolean hasValidCurrencyAmount(
public static Optional<CurrencyAmount> tryParseCurrencyAmountWithDirection(
CsvRow row,
String currencyField,
String amountField,
Expand All @@ -510,7 +510,10 @@ public static boolean hasValidCurrencyAmount(
Optional<Double> amount = row.findValue(amountField, LoaderUtils::parseDouble);
Optional<PayReceive> direction = row.findValue(directionField, LoaderUtils::parsePayReceive);

return (currency.isPresent() && amount.isPresent() && direction.isPresent());
if (currency.isPresent() && amount.isPresent() && direction.isPresent()) {
return Optional.of(CurrencyAmount.of(currency.get(), direction.get().normalize(amount.get())));
}
return Optional.empty();
}

//-------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (C) 2021 - 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.BARRIER_LEVEL_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.BARRIER_TYPE_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.KNOCK_TYPE_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.REBATE_AMOUNT_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.REBATE_CURRENCY_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.REBATE_DIRECTION_FIELD;
import static com.opengamma.strata.loader.csv.CsvLoaderColumns.TRADE_TYPE_FIELD;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.opengamma.strata.basics.currency.AdjustablePayment;
import com.opengamma.strata.collect.io.CsvOutput;
import com.opengamma.strata.collect.io.CsvRow;
import com.opengamma.strata.loader.LoaderUtils;
import com.opengamma.strata.product.Trade;
import com.opengamma.strata.product.TradeInfo;
import com.opengamma.strata.product.fxopt.FxSingleBarrierOption;
import com.opengamma.strata.product.fxopt.FxSingleBarrierOptionTrade;
import com.opengamma.strata.product.fxopt.FxVanillaOptionTrade;
import com.opengamma.strata.product.option.BarrierType;
import com.opengamma.strata.product.option.KnockType;
import com.opengamma.strata.product.option.SimpleConstantContinuousBarrier;

/**
* Handles the CSV files format for FX Single Barrier Option trades.
*/
public class FxSingleBarrierOptionTradeCsvPlugin implements TradeCsvParserPlugin, TradeCsvWriterPlugin<FxSingleBarrierOptionTrade> {

/**
* The singleton instance of the plugin.
*/
public static final FxSingleBarrierOptionTradeCsvPlugin INSTANCE = new FxSingleBarrierOptionTradeCsvPlugin();

/**
* The CSV headers.
*/
public static final Set<String> HEADERS = ImmutableSet.<String>builder()
.addAll(FxVanillaOptionTradeCsvPlugin.INSTANCE.headers(ImmutableList.of()))
.add(BARRIER_TYPE_FIELD)
.add(KNOCK_TYPE_FIELD)
.add(BARRIER_LEVEL_FIELD)
.add(REBATE_AMOUNT_FIELD)
.add(REBATE_CURRENCY_FIELD)
.add(REBATE_DIRECTION_FIELD)
.build();

/**
* Restricted constructor
*/
private FxSingleBarrierOptionTradeCsvPlugin() {
}

@Override
public Set<String> tradeTypeNames() {
return ImmutableSet.of("FXSINGLEBARRIEROPTION", "FX SINGLE BARRIER OPTION");
}

@Override
public Optional<Trade> parseTrade(
Class<?> requiredJavaType,
CsvRow baseRow,
List<CsvRow> additionalRows,
TradeInfo info,
TradeCsvInfoResolver resolver) {

if (requiredJavaType.isAssignableFrom(FxSingleBarrierOptionTrade.class)) {
return Optional.of(parse(baseRow, info, resolver));
}
return Optional.empty();
}

private FxSingleBarrierOptionTrade parse(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
FxVanillaOptionTrade vanillaOptionTrade = resolver.parseFxVanillaOptionTrade(row, info);

BarrierType barrierType = row.getValue(BARRIER_TYPE_FIELD, LoaderUtils::parseBarrierType);
KnockType knockType = row.getValue(KNOCK_TYPE_FIELD, LoaderUtils::parseKnockType);
double barrierLevel = row.getValue(BARRIER_LEVEL_FIELD, LoaderUtils::parseDouble);
AdjustablePayment premium = CsvLoaderUtils.parsePremiumFromDefaultFields(row);

FxSingleBarrierOption.Builder productBuilder = FxSingleBarrierOption.builder()
.underlyingOption(vanillaOptionTrade.getProduct())
.barrier(SimpleConstantContinuousBarrier.of(barrierType, knockType, barrierLevel));

CsvLoaderUtils.tryParseCurrencyAmountWithDirection(
row,
REBATE_CURRENCY_FIELD,
REBATE_AMOUNT_FIELD,
REBATE_DIRECTION_FIELD
).ifPresent(productBuilder::rebate);

return FxSingleBarrierOptionTrade.builder()
.product(productBuilder.build())
.premium(premium)
.info(info)
.build();
}

@Override
public String getName() {
return FxSingleBarrierOptionTrade.class.getSimpleName();
}

@Override
public Set<Class<?>> supportedTradeTypes() {
return ImmutableSet.of(FxSingleBarrierOptionTrade.class);
}

@Override
public Set<String> headers(List<FxSingleBarrierOptionTrade> trades) {
return HEADERS;
}

@Override
public void writeCsv(CsvOutput.CsvRowOutputWithHeaders csv, FxSingleBarrierOptionTrade trade) {
csv.writeCell(TRADE_TYPE_FIELD, "FxSingleBarrierOption");
writeSingleBarrierOption(csv, trade.getProduct());
CsvWriterUtils.writePremiumFields(csv, trade.getPremium());
csv.writeNewLine();
}

void writeSingleBarrierOption(CsvOutput.CsvRowOutputWithHeaders csv, FxSingleBarrierOption product) {
CsvWriterUtils.writeFxVanillaOption(csv, product.getUnderlyingOption());
CsvWriterUtils.writeBarrierFields(csv, product.getBarrier(), LocalDate.now(ZoneId.systemDefault()));
product.getRebate().ifPresent(ccyAmount -> CsvWriterUtils.writeCurrencyAmount(
csv,
ccyAmount,
REBATE_AMOUNT_FIELD,
REBATE_CURRENCY_FIELD,
REBATE_DIRECTION_FIELD));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ com.opengamma.strata.loader.csv.CdsTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.CdsIndexTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FraTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxNdfTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSingleBarrierOptionTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSingleTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSwapTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxVanillaOptionTradeCsvPlugin = constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ com.opengamma.strata.loader.csv.FraTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.TermDepositTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FullSwapTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxNdfTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSingleBarrierOptionTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSingleTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.FxSwapTradeCsvPlugin = constants
com.opengamma.strata.loader.csv.SwaptionTradeCsvPlugin = constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static com.opengamma.strata.collect.TestHelper.date;
import static com.opengamma.strata.product.common.BuySell.BUY;
import static com.opengamma.strata.product.common.BuySell.SELL;
import static com.opengamma.strata.product.common.LongShort.SHORT;
import static com.opengamma.strata.product.common.PayReceive.PAY;
import static com.opengamma.strata.product.common.PayReceive.RECEIVE;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -116,8 +117,13 @@
import com.opengamma.strata.product.fx.FxSingleTrade;
import com.opengamma.strata.product.fx.FxSwap;
import com.opengamma.strata.product.fx.FxSwapTrade;
import com.opengamma.strata.product.fxopt.FxSingleBarrierOption;
import com.opengamma.strata.product.fxopt.FxSingleBarrierOptionTrade;
import com.opengamma.strata.product.fxopt.FxVanillaOption;
import com.opengamma.strata.product.fxopt.FxVanillaOptionTrade;
import com.opengamma.strata.product.option.BarrierType;
import com.opengamma.strata.product.option.KnockType;
import com.opengamma.strata.product.option.SimpleConstantContinuousBarrier;
import com.opengamma.strata.product.payment.BulletPayment;
import com.opengamma.strata.product.payment.BulletPaymentTrade;
import com.opengamma.strata.product.swap.CompoundingMethod;
Expand Down Expand Up @@ -182,7 +188,7 @@ public void test_load_failures() {
}

@Test
public void test_load_fx_forwards() throws Exception {
public void test_load_fx_forwards() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades.csv");
ImmutableList<CharSource> charSources = ImmutableList.of(locator.getCharSource());
Expand Down Expand Up @@ -220,7 +226,7 @@ public void test_load_fx_forwards() throws Exception {
}

@Test
public void test_load_fx_forwards_with_legs_in_same_direction() throws Exception {
public void test_load_fx_forwards_with_legs_in_same_direction() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades_legs_same_direction.csv");
ValueWithFailures<List<Trade>> loadedData = standard.load(locator);
Expand All @@ -235,7 +241,7 @@ public void test_load_fx_forwards_with_legs_in_same_direction() throws Exception
}

@Test
public void test_load_fx_forwards_fullFormat() throws Exception {
public void test_load_fx_forwards_fullFormat() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades2.csv");
ImmutableList<CharSource> charSources = ImmutableList.of(locator.getCharSource());
Expand Down Expand Up @@ -311,7 +317,7 @@ public void test_load_fx_forwards_fullFormat() throws Exception {

//-------------------------------------------------------------------------
@Test
public void test_load_fx_swaps() throws Exception {
public void test_load_fx_swaps() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades.csv");
ImmutableList<CharSource> charSources = ImmutableList.of(locator.getCharSource());
Expand Down Expand Up @@ -349,7 +355,7 @@ public void test_load_fx_swaps() throws Exception {
}

@Test
public void test_load_fx_swaps_fullFormat() throws Exception {
public void test_load_fx_swaps_fullFormat() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades2.csv");
ImmutableList<CharSource> charSources = ImmutableList.of(locator.getCharSource());
Expand Down Expand Up @@ -379,7 +385,7 @@ public void test_load_fx_swaps_fullFormat() throws Exception {

//-------------------------------------------------------------------------
@Test
public void test_load_fx_vanilla_option() throws Exception {
public void test_load_fx_vanilla_option() {
TradeCsvLoader standard = TradeCsvLoader.standard();
ResourceLocator locator = ResourceLocator.of("classpath:com/opengamma/strata/loader/csv/fxtrades.csv");
ImmutableList<CharSource> charSources = ImmutableList.of(locator.getCharSource());
Expand Down Expand Up @@ -1672,6 +1678,49 @@ private SwaptionTrade expectedSwaption2() {
return SwaptionTrade.of(swapTrade.getInfo(), swaption, premium);
}

private FxSingleTrade expectedFxSingle() {
double notional = 1.0e6;
double fxRate = 1.1d;
return FxSingleTrade.of(
TradeInfo.empty(),
FxSingle.of(
CurrencyAmount.of(EUR, notional),
CurrencyAmount.of(USD, -notional * fxRate),
LocalDate.of(2014, 5, 13)));
}

private FxVanillaOptionTrade expectedFxVanillaOption() {
return FxVanillaOptionTrade.builder()
.product(FxVanillaOption.builder()
.longShort(SHORT)
.expiryDate(LocalDate.of(2014, 5, 9))
.expiryTime(LocalTime.of(13, 10))
.expiryZone(ZoneId.of("Z"))
.underlying(expectedFxSingle().getProduct())
.build())
.premium(AdjustablePayment.of(Payment.of(CurrencyAmount.of(USD, 230.3), LocalDate.of(2014, 1, 12))))
.build();
}

private FxSingleBarrierOptionTrade expectedFxSingleBarrierOptionWithRebate() {
return FxSingleBarrierOptionTrade.builder()
.product(FxSingleBarrierOption.of(
expectedFxVanillaOption().getProduct(),
SimpleConstantContinuousBarrier.of(BarrierType.UP, KnockType.KNOCK_IN, 17.2),
CurrencyAmount.of(USD, 17.666)))
.premium(AdjustablePayment.of(Payment.of(CurrencyAmount.of(USD, 230.3), LocalDate.of(2014, 1, 12))))
.build();
}

private FxSingleBarrierOptionTrade expectedFxSingleBarrierOptionWithoutRebate() {
return FxSingleBarrierOptionTrade.builder()
.product(FxSingleBarrierOption.of(
expectedFxVanillaOption().getProduct(),
SimpleConstantContinuousBarrier.of(BarrierType.DOWN, KnockType.KNOCK_OUT, 17.2)))
.premium(AdjustablePayment.of(Payment.of(CurrencyAmount.of(USD, 230.3), LocalDate.of(2014, 1, 12))))
.build();
}

//-------------------------------------------------------------------------
@Test
public void test_load_bulletPayment() {
Expand Down Expand Up @@ -2072,6 +2121,22 @@ public void test_load_genericSecurity() {
}

//-------------------------------------------------------------------------

@Test
public void test_FxSingleBarrierOption() {
ResourceLocator file = ResourceLocator.of(
"classpath:com/opengamma/strata/loader/csv/fx_single_barrier_option_trade.csv");

ValueWithFailures<List<FxSingleBarrierOptionTrade>> trades = TradeCsvLoader.standard().parse(
ImmutableList.of(file.getCharSource()), FxSingleBarrierOptionTrade.class);

checkRoundtrip(
FxSingleBarrierOptionTrade.class,
trades.getValue(),
expectedFxSingleBarrierOptionWithRebate(),
expectedFxSingleBarrierOptionWithoutRebate());
}

@Test
public void test_load_CapFloor() {
TradeCsvLoader test = TradeCsvLoader.standard();
Expand Down Expand Up @@ -2331,6 +2396,7 @@ public void coverage() {
coverPrivateConstructor(SwapTradeCsvPlugin.class);
coverPrivateConstructor(TermDepositTradeCsvPlugin.class);
coverPrivateConstructor(FullSwapTradeCsvPlugin.class);
coverPrivateConstructor(FxSingleBarrierOptionTradeCsvPlugin.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Strata Trade Type,Payment Date Convention,Payment Date Calendar,Long Short,Expiry Date,Expiry Time,Expiry Zone,Premium Date,Premium Date Convention,Premium Date Calendar,Premium Direction,Premium Currency,Premium Amount,Leg 1 Direction,Leg 1 Payment Date,Leg 1 Currency,Leg 1 Notional,Leg 2 Direction,Leg 2 Payment Date,Leg 2 Currency,Leg 2 Notional,Barrier Type,Knock Type,Barrier Level,Rebate Amount,Rebate Currency,Rebate Direction
FxSingleBarrierOption,,,Short,2014-05-09,13:10,Z,2014-01-12,NoAdjust,NoHolidays,Receive,USD,230.3,Receive,2014-05-13,EUR,1000000,Pay,2014-05-13,USD,-1100000,Up,KnockIn,17.2,17.666,USD,Receive
FxSingleBarrierOption,,,Short,2014-05-09,13:10,Z,2014-01-12,NoAdjust,NoHolidays,Receive,USD,230.3,Receive,2014-05-13,EUR,1000000,Pay,2014-05-13,USD,-1100000,Down,KnockOut,17.2,,,

0 comments on commit 141ffb2

Please sign in to comment.