Skip to content

Commit

Permalink
Enhanced CsvInfoResolver (#1570)
Browse files Browse the repository at this point in the history
Split CsvInfoResolver to TradeCsvInfoResolver/PositionCsvInfoResolver
Allow greater ability to influence the trade that was created
Add parsing helper method to strictly filter by type
  • Loading branch information
jodastephen authored and brianweller89 committed Oct 3, 2017
1 parent da5e06b commit 164d799
Show file tree
Hide file tree
Showing 11 changed files with 391 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,17 @@
package com.opengamma.strata.loader.csv;

import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.collect.io.CsvRow;
import com.opengamma.strata.product.PositionInfoBuilder;
import com.opengamma.strata.product.TradeInfoBuilder;
import com.opengamma.strata.product.common.ExchangeId;
import com.opengamma.strata.product.etd.EtdContractCode;
import com.opengamma.strata.product.etd.EtdContractSpec;
import com.opengamma.strata.product.etd.EtdContractSpecId;
import com.opengamma.strata.product.etd.EtdIdUtils;
import com.opengamma.strata.product.etd.EtdType;

/**
* Resolves security information from CSV files, enriching the parser.
* <p>
* Data loaded from a CSV may contain additional information that needs to be captured.
* This plugin point allows the additional CSV columns to be parsed and captured.
*
* @deprecated Use {@link TradeCsvInfoResolver} or {@link PositionCsvInfoResolver}
*/
public interface CsvInfoResolver {
@Deprecated
public interface CsvInfoResolver extends TradeCsvInfoResolver, PositionCsvInfoResolver {

/**
* Obtains an instance that uses the standard set of reference data.
Expand All @@ -43,54 +37,4 @@ public static CsvInfoResolver of(ReferenceData refData) {
return StandardCsvInfoResolver.of(refData);
}

//-------------------------------------------------------------------------
/**
* Gets the reference data being used.
*
* @return the reference data
*/
public abstract ReferenceData getReferenceData();

/**
* Parses attributes into {@code TradeInfo}.
* <p>
* If they are available, the trade ID, date, time and zone will have been set
* before this method is called. They may be altered if necessary, although
* this is not recommended.
*
* @param row the CSV row to parse
* @param builder the builder to update
*/
public default void parseTradeInfo(CsvRow row, TradeInfoBuilder builder) {
// do nothing
}

/**
* Parses attributes into {@code PositionInfo}.
* <p>
* If it is available, the position ID will have been set before this method is called.
* It may be altered if necessary, although this is not recommended.
*
* @param row the CSV row to parse
* @param builder the builder to update
*/
public default void parsePositionInfo(CsvRow row, PositionInfoBuilder builder) {
// do nothing
}

/**
* Parses the contract specification from the row.
*
* @param row the CSV row to parse
* @param type the ETD type
* @return the loaded positions, position-level errors are captured in the result
*/
public default EtdContractSpec parseEtdContractSpec(CsvRow row, EtdType type) {
ExchangeId exchangeId = ExchangeId.of(row.getValue(StandardCsvInfoResolver.EXCHANGE_FIELD));
EtdContractCode contractCode = EtdContractCode.of(row.getValue(StandardCsvInfoResolver.CONTRACT_CODE_FIELD));
EtdContractSpecId specId = EtdIdUtils.contractSpecId(type, exchangeId, contractCode);
return getReferenceData().findValue(specId).orElseThrow(
() -> new IllegalArgumentException("ETD contract specification not found in reference data: " + specId));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ final class FraTradeCsvLoader {
* @param resolver the resolver used to parse additional information
* @return the parsed trade
*/
static FraTrade parse(CsvRow row, TradeInfo info, CsvInfoResolver resolver) {
static FraTrade parse(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
FraTrade trade = parseRow(row, info, resolver);
return resolver.completeTrade(row, trade);
}

// parse the row to a trade
private static FraTrade parseRow(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
BuySell buySell = LoaderUtils.parseBuySell(row.getValue(BUY_SELL_FIELD));
double notional = LoaderUtils.parseDouble(row.getValue(NOTIONAL_FIELD));
double fixedRate = LoaderUtils.parseDoublePercent(row.getValue(FIXED_RATE_FIELD));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (C) 2017 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.loader.csv;

import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.collect.io.CsvRow;
import com.opengamma.strata.product.PositionInfoBuilder;
import com.opengamma.strata.product.SecurityPosition;
import com.opengamma.strata.product.common.ExchangeId;
import com.opengamma.strata.product.etd.EtdContractCode;
import com.opengamma.strata.product.etd.EtdContractSpec;
import com.opengamma.strata.product.etd.EtdContractSpecId;
import com.opengamma.strata.product.etd.EtdFuturePosition;
import com.opengamma.strata.product.etd.EtdIdUtils;
import com.opengamma.strata.product.etd.EtdOptionPosition;
import com.opengamma.strata.product.etd.EtdType;

/**
* Resolves additional information when parsing position CSV files.
* <p>
* Data loaded from a CSV may contain additional information that needs to be captured.
* This plugin point allows the additional CSV columns to be parsed and captured.
* It also allows the ETD contract specification to be loaded.
*/
public interface PositionCsvInfoResolver {

/**
* Obtains an instance that uses the standard set of reference data.
*
* @return the loader
*/
public static PositionCsvInfoResolver standard() {
return StandardCsvInfoResolver.of(ReferenceData.standard());
}

/**
* Obtains an instance that uses the specified set of reference data.
*
* @param refData the reference data
* @return the loader
*/
public static PositionCsvInfoResolver of(ReferenceData refData) {
return StandardCsvInfoResolver.of(refData);
}

//-------------------------------------------------------------------------
/**
* Gets the reference data being used.
*
* @return the reference data
*/
public abstract ReferenceData getReferenceData();

/**
* Parses attributes into {@code PositionInfo}.
* <p>
* If it is available, the position ID will have been set before this method is called.
* It may be altered if necessary, although this is not recommended.
*
* @param row the CSV row to parse
* @param builder the builder to update
*/
public default void parsePositionInfo(CsvRow row, PositionInfoBuilder builder) {
// do nothing
}

/**
* Completes the position, potentially parsing additional columns.
* <p>
* This is called after the position has been parsed and after
* {@link #parsePositionInfo(CsvRow, PositionInfoBuilder)}.
*
* @param row the CSV row to parse
* @param position the parsed position
* @param spec the contract specification
* @return the updated position
*/
public default EtdFuturePosition completePosition(CsvRow row, EtdFuturePosition position, EtdContractSpec spec) {
// do nothing
return position;
}

/**
* Completes the position, potentially parsing additional columns.
* <p>
* This is called after the position has been parsed and after
* {@link #parsePositionInfo(CsvRow, PositionInfoBuilder)}.
*
* @param row the CSV row to parse
* @param position the parsed position
* @param spec the contract specification
* @return the updated position
*/
public default EtdOptionPosition completePosition(CsvRow row, EtdOptionPosition position, EtdContractSpec spec) {
// do nothing
return position;
}

/**
* Completes the position, potentially parsing additional columns.
* <p>
* This is called after the position has been parsed and after
* {@link #parsePositionInfo(CsvRow, PositionInfoBuilder)}.
*
* @param row the CSV row to parse
* @param position the parsed position
* @return the updated position
*/
public default SecurityPosition completePosition(CsvRow row, SecurityPosition position) {
// do nothing
return position;
}

/**
* Parses the contract specification from the row.
*
* @param row the CSV row to parse
* @param type the ETD type
* @return the loaded positions, position-level errors are captured in the result
*/
public default EtdContractSpec parseEtdContractSpec(CsvRow row, EtdType type) {
ExchangeId exchangeId = ExchangeId.of(row.getValue(StandardCsvInfoResolver.EXCHANGE_FIELD));
EtdContractCode contractCode = EtdContractCode.of(row.getValue(StandardCsvInfoResolver.CONTRACT_CODE_FIELD));
EtdContractSpecId specId = EtdIdUtils.contractSpecId(type, exchangeId, contractCode);
return getReferenceData().findValue(specId).orElseThrow(
() -> new IllegalArgumentException("ETD contract specification not found in reference data: " + specId));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public final class PositionCsvLoader {
/**
* The resolver, providing additional information.
*/
private final CsvInfoResolver resolver;
private final PositionCsvInfoResolver resolver;

//-------------------------------------------------------------------------
/**
Expand All @@ -152,7 +152,7 @@ public final class PositionCsvLoader {
* @return the loader
*/
public static PositionCsvLoader standard() {
return new PositionCsvLoader(CsvInfoResolver.standard());
return new PositionCsvLoader(PositionCsvInfoResolver.standard());
}

/**
Expand All @@ -162,7 +162,7 @@ public static PositionCsvLoader standard() {
* @return the loader
*/
public static PositionCsvLoader of(ReferenceData refData) {
return new PositionCsvLoader(CsvInfoResolver.of(refData));
return new PositionCsvLoader(PositionCsvInfoResolver.of(refData));
}

/**
Expand All @@ -171,12 +171,12 @@ public static PositionCsvLoader of(ReferenceData refData) {
* @param resolver the resolver used to parse additional information
* @return the loader
*/
public static PositionCsvLoader of(CsvInfoResolver resolver) {
public static PositionCsvLoader of(PositionCsvInfoResolver resolver) {
return new PositionCsvLoader(resolver);
}

// restricted constructor
private PositionCsvLoader(CsvInfoResolver resolver) {
private PositionCsvLoader(PositionCsvInfoResolver resolver) {
this.resolver = ArgChecker.notNull(resolver, "resolver");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@ final class SecurityCsvLoader {
* @param resolver the resolver used to parse additional information
* @return the parsed trade
*/
static SecurityTrade parseTrade(CsvRow row, TradeInfo info, CsvInfoResolver resolver) {
static SecurityTrade parseTrade(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
SecurityTrade trade = parseRow(row, info, resolver);
return resolver.completeTrade(row, trade);
}

// parse the row to a trade
private static SecurityTrade parseRow(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
String securityIdScheme = row.findValue(SECURITY_ID_SCHEME_FIELD).orElse(DEFAULT_SECURITY_SCHEME);
String securityIdValue = row.getValue(SECURITY_ID_FIELD);
SecurityId securityId = SecurityId.of(securityIdScheme, securityIdValue);
Expand All @@ -94,7 +100,7 @@ static SecurityTrade parseTrade(CsvRow row, TradeInfo info, CsvInfoResolver reso
* @param resolver the resolver used to parse additional information
* @return the parsed position
*/
static Position parsePosition(CsvRow row, PositionInfo info, CsvInfoResolver resolver) {
static Position parsePosition(CsvRow row, PositionInfo info, PositionCsvInfoResolver resolver) {
if (row.findValue(EXPIRY_FIELD).isPresent()) {
// etd
if (row.findValue(PUT_CALL_FIELD).isPresent() || row.findValue(EXERCISE_PRICE_FIELD).isPresent()) {
Expand All @@ -116,12 +122,13 @@ static Position parsePosition(CsvRow row, PositionInfo info, CsvInfoResolver res
* @param resolver the resolver used to parse additional information
* @return the parsed position
*/
static SecurityPosition parseSimple(CsvRow row, PositionInfo info, CsvInfoResolver resolver) {
static SecurityPosition parseSimple(CsvRow row, PositionInfo info, PositionCsvInfoResolver resolver) {
String securityIdScheme = row.findValue(SECURITY_ID_SCHEME_FIELD).orElse(DEFAULT_SECURITY_SCHEME);
String securityIdValue = row.getValue(SECURITY_ID_FIELD);
SecurityId securityId = SecurityId.of(securityIdScheme, securityIdValue);
DoublesPair quantity = parseQuantity(row);
return SecurityPosition.ofLongShort(info, securityId, quantity.getFirst(), quantity.getSecond());
SecurityPosition position = SecurityPosition.ofLongShort(info, securityId, quantity.getFirst(), quantity.getSecond());
return resolver.completePosition(row, position);
}

//-------------------------------------------------------------------------
Expand All @@ -133,12 +140,13 @@ static SecurityPosition parseSimple(CsvRow row, PositionInfo info, CsvInfoResolv
* @param resolver the resolver of additional security information
* @return the parsed position
*/
static EtdFuturePosition parseFuture(CsvRow row, PositionInfo info, CsvInfoResolver resolver) {
static EtdFuturePosition parseFuture(CsvRow row, PositionInfo info, PositionCsvInfoResolver resolver) {
EtdContractSpec contract = resolver.parseEtdContractSpec(row, EtdType.FUTURE);
Pair<YearMonth, EtdVariant> variant = parseVariant(row, EtdType.FUTURE);
EtdFutureSecurity security = contract.createFuture(variant.getFirst(), variant.getSecond());
DoublesPair quantity = parseQuantity(row);
return EtdFuturePosition.ofLongShort(info, security, quantity.getFirst(), quantity.getSecond());
EtdFuturePosition position = EtdFuturePosition.ofLongShort(info, security, quantity.getFirst(), quantity.getSecond());
return resolver.completePosition(row, position, contract);
}

/**
Expand All @@ -149,15 +157,16 @@ static EtdFuturePosition parseFuture(CsvRow row, PositionInfo info, CsvInfoResol
* @param resolver the resolver of additional security information
* @return the parsed position
*/
static EtdOptionPosition parseOption(CsvRow row, PositionInfo info, CsvInfoResolver resolver) {
static EtdOptionPosition parseOption(CsvRow row, PositionInfo info, PositionCsvInfoResolver resolver) {
EtdContractSpec contract = resolver.parseEtdContractSpec(row, EtdType.OPTION);
Pair<YearMonth, EtdVariant> variant = parseVariant(row, EtdType.OPTION);
int version = row.findValue(VERSION_FIELD).map(Integer::parseInt).orElse(DEFAULT_OPTION_VERSION_NUMBER);
PutCall putCall = LoaderUtils.parsePutCall(row.getValue(PUT_CALL_FIELD));
double strikePrice = Double.parseDouble(row.getValue(EXERCISE_PRICE_FIELD));
EtdOptionSecurity security = contract.createOption(variant.getFirst(), variant.getSecond(), version, putCall, strikePrice);
DoublesPair quantity = parseQuantity(row);
return EtdOptionPosition.ofLongShort(info, security, quantity.getFirst(), quantity.getSecond());
EtdOptionPosition position = EtdOptionPosition.ofLongShort(info, security, quantity.getFirst(), quantity.getSecond());
return resolver.completePosition(row, position, contract);
}

//-------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
/**
* Standard CSV information resolver.
*/
final class StandardCsvInfoResolver implements CsvInfoResolver {
@SuppressWarnings("deprecation")
final class StandardCsvInfoResolver implements CsvInfoResolver, TradeCsvInfoResolver, PositionCsvInfoResolver {

static final String EXCHANGE_FIELD = "Exchange";
static final String CONTRACT_CODE_FIELD = "Contract Code";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ final class SwapTradeCsvLoader {
* @param resolver the resolver used to parse additional information
* @return the parsed trade
*/
static SwapTrade parse(CsvRow row, TradeInfo info, CsvInfoResolver resolver) {
static SwapTrade parse(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
SwapTrade trade = parseRow(row, info, resolver);
return resolver.completeTrade(row, trade);
}

// parse the row to a trade
private static SwapTrade parseRow(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
Optional<String> conventionOpt = row.findValue(CONVENTION_FIELD);
if (conventionOpt.isPresent()) {
return parseWithConvention(row, info, resolver, conventionOpt.get());
Expand All @@ -77,7 +83,7 @@ static SwapTrade parse(CsvRow row, TradeInfo info, CsvInfoResolver resolver) {
}

// parse a trade based on a convention
static SwapTrade parseWithConvention(CsvRow row, TradeInfo info, CsvInfoResolver resolver, String conventionStr) {
static SwapTrade parseWithConvention(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver, String conventionStr) {
BuySell buySell = LoaderUtils.parseBuySell(row.getValue(BUY_SELL_FIELD));
double notional = LoaderUtils.parseDouble(row.getValue(NOTIONAL_FIELD));
double fixedRate = LoaderUtils.parseDoublePercent(row.getValue(FIXED_RATE_FIELD));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ final class TermDepositTradeCsvLoader {
* @param resolver the resolver used to parse additional information
* @return the parsed trade
*/
static TermDepositTrade parse(CsvRow row, TradeInfo info, CsvInfoResolver resolver) {
static TermDepositTrade parse(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
TermDepositTrade trade = parseRow(row, info, resolver);
return resolver.completeTrade(row, trade);
}

// parse the row to a trade
private static TermDepositTrade parseRow(CsvRow row, TradeInfo info, TradeCsvInfoResolver resolver) {
BuySell buySell = LoaderUtils.parseBuySell(row.getValue(BUY_SELL_FIELD));
double notional = LoaderUtils.parseDouble(row.getValue(NOTIONAL_FIELD));
double fixedRate = LoaderUtils.parseDoublePercent(row.getValue(FIXED_RATE_FIELD));
Expand Down
Loading

0 comments on commit 164d799

Please sign in to comment.