/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.generator;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.position.Counterparty;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.financial.currency.CurrencyPairs;
import com.opengamma.financial.currency.CurrencyPairsConfigPopulator;
import com.opengamma.financial.security.fx.FXForwardSecurity;
import com.opengamma.financial.security.fx.NonDeliverableFXForwardSecurity;
import com.opengamma.financial.security.option.BarrierDirection;
import com.opengamma.financial.security.option.BarrierType;
import com.opengamma.financial.security.option.EuropeanExerciseType;
import com.opengamma.financial.security.option.FXBarrierOptionSecurity;
import com.opengamma.financial.security.option.FXDigitalOptionSecurity;
import com.opengamma.financial.security.option.FXOptionSecurity;
import com.opengamma.financial.security.option.MonitoringType;
import com.opengamma.financial.security.option.NonDeliverableFXOptionSecurity;
import com.opengamma.financial.security.option.SamplingFrequency;
import com.opengamma.id.ExternalId;
import com.opengamma.master.position.ManageableTrade;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.util.i18n.Country;
import com.opengamma.util.money.Currency;
import com.opengamma.util.time.Expiry;
import com.opengamma.util.time.ExpiryAccuracy;
import com.opengamma.util.tuple.Pair;
/**
* Source of random, but reasonable, FX security instances.
*
* @param <T> the sub-type of the security
*/
public abstract class AbstractFXSecurityGenerator<T extends ManageableSecurity> extends SecurityGenerator<T> {
private static final ExternalId REGION = ExternalSchemes.countryRegionId(Country.US);
private static final double NOTIONAL = 100000;
private static final DecimalFormat RATE_FORMATTER = new DecimalFormat("###.######");
private static final DecimalFormat NOTIONAL_FORMATTER = new DecimalFormat("########.###");
private static final Boolean[] BOOLEAN_VALUES = {Boolean.TRUE, Boolean.FALSE};
private CurrencyPairs _currencyPairs = CurrencyPairsConfigPopulator.createCurrencyPairs();
/**
* Structured random information for creating the security.
*/
protected class Bundle {
private final boolean _long;
private final boolean _up;
private final Currency _firstCurrency;
private final Currency _secondCurrency;
private final Currency _paymentCurrency;
private final int _daysOffset;
private final ZonedDateTime _tradeDate;
public Bundle() {
_long = getRandom().nextBoolean();
_up = getRandom().nextBoolean();
_firstCurrency = getRandomCurrency();
Currency secondCurrency;
do {
secondCurrency = getRandomCurrency();
} while (secondCurrency == _firstCurrency);
_secondCurrency = secondCurrency;
_daysOffset = 100 + getRandom().nextInt(600);
_tradeDate = previousWorkingDay(ZonedDateTime.now().minusDays(getRandom(30)), _firstCurrency, _secondCurrency);
_paymentCurrency = getRandom().nextBoolean() ? _firstCurrency : _secondCurrency;
}
}
protected Bundle createBundle() {
return new Bundle();
}
protected FXBarrierOptionSecurity createFXBarrierOptionSecurity(final Bundle bundle) {
final Currency putCurrency = bundle._firstCurrency;
final Currency callCurrency = bundle._secondCurrency;
final double putAmount = putCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime settlementDate = nextWorkingDay(bundle._tradeDate.plusDays(bundle._daysOffset), bundle._firstCurrency, bundle._secondCurrency);
final Expiry expiry = new Expiry(settlementDate, ExpiryAccuracy.DAY_MONTH_YEAR);
final Double fxRate = getApproxFXRate(settlementDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
if (fxRate == null) {
return null;
}
final double callAmount = NOTIONAL * fxRate;
final String dateString = settlementDate.toString(DATE_FORMATTER);
final BarrierType barrierType = bundle._up ? BarrierType.UP : BarrierType.DOWN;
final BarrierDirection barrierDirection = BarrierDirection.KNOCK_IN;
final MonitoringType monitoringType = MonitoringType.CONTINUOUS;
final SamplingFrequency samplingFrequency = SamplingFrequency.DAILY_CLOSE;
final boolean invertBarrierLevel = !CurrencyPair.of(putCurrency, callCurrency).equals(getCurrencyPair(putCurrency, callCurrency));
// so if UP and ccy convention order, multiple, if UP and inverted ccy order, divide, if DOWN and ccy convention order multiply, if DOWN and inverted ccy order, divide.
final double barrierLevel = bundle._up ^ invertBarrierLevel ? fxRate * 1.5 : fxRate / 1.5;
final FXBarrierOptionSecurity fxBarrierOptionSecurity = new FXBarrierOptionSecurity(putCurrency, callCurrency, putAmount, callAmount, expiry, settlementDate, barrierType, barrierDirection,
monitoringType, samplingFrequency, barrierLevel, bundle._long);
final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
final String barrierLevelString = RATE_FORMATTER.format(barrierLevel);
final String barrierUnitString = callCurrency + "/" + putCurrency;
fxBarrierOptionSecurity.setName((bundle._long ? "Long " : "Short ") + (bundle._up ? "up" : "down") + " knock-in at " + barrierLevelString + " " + barrierUnitString + ", put " + putCurrency +
" " +
putAmountString + ", call " + callCurrency + " " + callAmountString + " on " + dateString);
return fxBarrierOptionSecurity;
}
protected FXDigitalOptionSecurity createFXDigitalOptionSecurity(final Bundle bundle) {
final Currency putCurrency = bundle._firstCurrency;
final Currency callCurrency = bundle._secondCurrency;
final Currency paymentCurrency = bundle._paymentCurrency;
final double putAmount = putCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime expiry = nextWorkingDay(bundle._tradeDate.plusDays(bundle._daysOffset), putCurrency, callCurrency);
final Double rate = getApproxFXRate(expiry.toLocalDate(), Pair.of(putCurrency, callCurrency));
if (rate == null) {
return null;
}
final double callAmount = rate * NOTIONAL;
final ZonedDateTime settlementDate = nextWorkingDay(expiry.plusDays(2), putCurrency, callCurrency);
final FXDigitalOptionSecurity security = new FXDigitalOptionSecurity(putCurrency, callCurrency, putAmount, callAmount, paymentCurrency, new Expiry(expiry), settlementDate, bundle._long);
final StringBuilder sb = new StringBuilder("Digital ");
sb.append(bundle._long ? "Long" : "Short");
sb.append(" put ").append(putCurrency).append(' ').append(NOTIONAL_FORMATTER.format(putAmount));
sb.append(", call ").append(callCurrency).append(' ').append(NOTIONAL_FORMATTER.format(callAmount));
sb.append(" on ").append(expiry.toString(DATE_FORMATTER));
security.setName(sb.toString());
return security;
}
protected FXForwardSecurity createFXForwardSecurity(final Bundle bundle) {
final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime forwardDate = nextWorkingDay(bundle._tradeDate.plusDays(bundle._daysOffset), bundle._firstCurrency, bundle._secondCurrency);
final Double fxRate = getApproxFXRate(forwardDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
if (fxRate == null) {
return null;
}
final double callAmount = NOTIONAL * fxRate;
final Currency payCurrency = bundle._long ? bundle._secondCurrency : bundle._firstCurrency;
final Currency receiveCurrency = bundle._long ? bundle._firstCurrency : bundle._secondCurrency;
final String dateString = forwardDate.toString(DATE_FORMATTER);
final FXForwardSecurity fxForwardSecurity = new FXForwardSecurity(payCurrency, callAmount, receiveCurrency, putAmount, forwardDate, REGION);
final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
fxForwardSecurity.setName("Pay " + payCurrency + " " + callAmountString + ", receive " + receiveCurrency + " " + putAmountString + " on " + dateString);
return fxForwardSecurity;
}
protected NonDeliverableFXForwardSecurity createNDFXForwardSecurity(final Bundle bundle) {
final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime forwardDate = nextWorkingDay(bundle._tradeDate.plusDays(bundle._daysOffset), bundle._firstCurrency, bundle._secondCurrency);
final Double fxRate = getApproxFXRate(forwardDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
if (fxRate == null) {
return null;
}
final double callAmount = NOTIONAL * fxRate;
final Currency payCurrency = bundle._long ? bundle._secondCurrency : bundle._firstCurrency;
final Currency receiveCurrency = bundle._long ? bundle._firstCurrency : bundle._secondCurrency;
final String dateString = forwardDate.toString(DATE_FORMATTER);
final NonDeliverableFXForwardSecurity security = new NonDeliverableFXForwardSecurity(payCurrency, callAmount, receiveCurrency, putAmount, forwardDate,
REGION, getRandom(BOOLEAN_VALUES));
final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
security.setName("Pay " + payCurrency + " " + callAmountString + ", receive " + receiveCurrency + " " + putAmountString + " on " + dateString);
return security;
}
protected FXOptionSecurity createFXOptionSecurity(final Bundle bundle) {
final Currency putCurrency = bundle._firstCurrency;
final Currency callCurrency = bundle._secondCurrency;
final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime settlementDate = bundle._tradeDate.plusDays(bundle._daysOffset);
final Double fxRate = getApproxFXRate(settlementDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
if (fxRate == null) {
return null;
}
final double callAmount = NOTIONAL * fxRate;
final Expiry expiry = new Expiry(settlementDate, ExpiryAccuracy.DAY_MONTH_YEAR);
final String dateString = settlementDate.toString(DATE_FORMATTER);
final FXOptionSecurity fxOptionSecurity = new FXOptionSecurity(putCurrency, callCurrency, putAmount, callAmount, expiry, settlementDate, bundle._long, new EuropeanExerciseType());
final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
fxOptionSecurity.setName((bundle._long ? "Long " : "Short ") + "put " + putCurrency + " " + putAmountString + ", call " + callCurrency + " " + callAmountString + " on " + dateString);
return fxOptionSecurity;
}
protected NonDeliverableFXOptionSecurity createNDFXOptionSecurity(final Bundle bundle) {
final Currency putCurrency = bundle._firstCurrency;
final Currency callCurrency = bundle._secondCurrency;
final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
final ZonedDateTime settlementDate = bundle._tradeDate.plusDays(bundle._daysOffset);
final Double fxRate = getApproxFXRate(settlementDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
if (fxRate == null) {
return null;
}
final double callAmount = NOTIONAL * fxRate;
final Expiry expiry = new Expiry(settlementDate, ExpiryAccuracy.DAY_MONTH_YEAR);
final String dateString = settlementDate.toString(DATE_FORMATTER);
final NonDeliverableFXOptionSecurity optionSecurity = new NonDeliverableFXOptionSecurity(putCurrency, callCurrency, putAmount, callAmount, expiry, settlementDate,
getRandom(BOOLEAN_VALUES), new EuropeanExerciseType(), getRandom(BOOLEAN_VALUES));
final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
optionSecurity.setName((bundle._long ? "Long " : "Short ") + "put " + putCurrency + " " + putAmountString + ", call " + callCurrency + " " + callAmountString + " on " + dateString);
return optionSecurity;
}
protected ManageableTrade createFXBarrierOptionSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final FXBarrierOptionSecurity security = createFXBarrierOptionSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getCallAmount());
trade.setPremiumCurrency(security.getCallCurrency());
return trade;
}
protected ManageableTrade createFXDigitalOptionSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final FXDigitalOptionSecurity security = createFXDigitalOptionSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getCallAmount());
trade.setPremiumCurrency(security.getCallCurrency());
return trade;
}
protected ManageableTrade createFXForwardSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final FXForwardSecurity security = createFXForwardSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getPayAmount());
trade.setPremiumCurrency(security.getPayCurrency());
return trade;
}
protected ManageableTrade createNDFXForwardSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final NonDeliverableFXForwardSecurity security = createNDFXForwardSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getPayAmount());
trade.setPremiumCurrency(security.getPayCurrency());
return trade;
}
protected ManageableTrade createFXOptionSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final FXOptionSecurity security = createFXOptionSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getCallAmount());
trade.setPremiumCurrency(security.getCallCurrency());
return trade;
}
protected ManageableTrade createNDFXOptionSecurityTrade(final Bundle bundle, final BigDecimal quantity, final SecurityPersister persister, final NameGenerator counterPartyGenerator) {
final NonDeliverableFXOptionSecurity security = createNDFXOptionSecurity(bundle);
if (security == null) {
return null;
}
ManageableTrade trade = new ManageableTrade(quantity, persister.storeSecurity(security), bundle._tradeDate.toLocalDate(), bundle._tradeDate.toOffsetDateTime().toOffsetTime(),
ExternalId.of(Counterparty.DEFAULT_SCHEME, counterPartyGenerator.createName()));
trade.setPremium(security.getCallAmount());
trade.setPremiumCurrency(security.getCallCurrency());
return trade;
}
protected CurrencyPair getCurrencyPair(Currency ccy1, Currency ccy2) {
return _currencyPairs.getCurrencyPair(ccy1, ccy2);
}
}