/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.pnl;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Clock;
import org.threeten.bp.LocalDate;
import com.google.common.collect.Sets;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries;
import com.opengamma.core.position.PositionOrTrade;
import com.opengamma.core.position.Trade;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValueProperties.Builder;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.financial.analytics.model.forex.FXUtils;
import com.opengamma.financial.analytics.timeseries.DateConstraint;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils;
import com.opengamma.financial.security.FinancialSecurityUtils;
import com.opengamma.financial.security.future.FutureSecurity;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.UniqueId;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolutionResult;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
import com.opengamma.util.money.MoneyCalculationUtils;
/**
*
*/
public abstract class AbstractTradeOrDailyPositionPnLFunction extends AbstractFunction.NonCompiledInvoker {
private static final Logger s_logger = LoggerFactory.getLogger(AbstractTradeOrDailyPositionPnLFunction.class);
private final String _mark2MarketField;
private final String _costOfCarryField;
private final String _resolutionKey;
/**
* @param resolutionKey the resolution key, not-null
* @param mark2MarketField the mark to market data field name, not-null
* @param costOfCarryField the cost of carry field name, not-null
*/
public AbstractTradeOrDailyPositionPnLFunction(final String resolutionKey, final String mark2MarketField, final String costOfCarryField) {
super();
ArgumentChecker.notNull(resolutionKey, "resolutionKey");
ArgumentChecker.notNull(mark2MarketField, "mark data field");
ArgumentChecker.notNull(costOfCarryField, "cost of carry data field");
_resolutionKey = resolutionKey;
_mark2MarketField = mark2MarketField;
_costOfCarryField = costOfCarryField;
}
protected abstract LocalDate getPreferredTradeDate(Clock valuationClock, PositionOrTrade positionOrTrade);
protected abstract DateConstraint getTimeSeriesStartDate(PositionOrTrade positionOrTrade);
protected abstract DateConstraint getTimeSeriesEndDate(PositionOrTrade positionOrTrade);
protected abstract LocalDate checkAvailableData(LocalDate originalTradeDate, HistoricalTimeSeries markToMarketSeries, Security security, String markDataField, String resolutionKey);
protected abstract String getResultValueRequirementName();
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final ValueRequirement desiredValue = desiredValues.iterator().next();
BigDecimal tradeValue = null;
HistoricalTimeSeries htsMarkToMarket = null;
HistoricalTimeSeries htsCostOfCarry = null;
final PositionOrTrade trade = target.getPositionOrTrade();
for (final ComputedValue input : inputs.getAllValues()) {
if (ValueRequirementNames.VALUE.equals(input.getSpecification().getValueName())) {
tradeValue = BigDecimal.valueOf((Double) input.getValue());
if (trade instanceof Trade) {
// Need to scale the value by the trade quantity
tradeValue = ((Trade) trade).getQuantity().multiply(tradeValue);
}
} else if (ValueRequirementNames.HISTORICAL_TIME_SERIES.equals(input.getSpecification().getValueName())) {
final String field = input.getSpecification().getProperty(HistoricalTimeSeriesFunctionUtils.DATA_FIELD_PROPERTY);
if (_costOfCarryField.equals(field)) {
htsCostOfCarry = (HistoricalTimeSeries) input.getValue();
} else if (_mark2MarketField.equals(field)) {
htsMarkToMarket = (HistoricalTimeSeries) input.getValue();
}
}
}
final Security security = trade.getSecurity();
LocalDate tradeDate = getPreferredTradeDate(executionContext.getValuationClock(), trade);
tradeDate = checkAvailableData(tradeDate, htsMarkToMarket, security, _mark2MarketField, _resolutionKey);
final ValueSpecification valueSpecification = new ValueSpecification(getResultValueRequirementName(), target.toSpecification(), desiredValue.getConstraints());
final double costOfCarry = getCostOfCarry(security, tradeDate, htsCostOfCarry);
Double markToMarket = htsMarkToMarket.getTimeSeries().getValue(tradeDate);
if (security instanceof FutureSecurity) {
final FutureSecurity futureSecurity = (FutureSecurity) security;
markToMarket = markToMarket * futureSecurity.getUnitAmount();
}
final BigDecimal dailyPnL = tradeValue.subtract(trade.getQuantity().multiply(BigDecimal.valueOf(markToMarket + costOfCarry)));
s_logger.debug("{} security: {} quantity: {} fairValue: {} markToMarket: {} costOfCarry: {} dailyPnL: {}",
new Object[] {trade.getUniqueId(), trade.getSecurity().getExternalIdBundle(), trade.getQuantity(), tradeValue, markToMarket, costOfCarry, dailyPnL });
final ComputedValue result = new ComputedValue(valueSpecification, MoneyCalculationUtils.rounded(dailyPnL).doubleValue());
return Sets.newHashSet(result);
}
private double getCostOfCarry(final Security security, final LocalDate tradeDate, final HistoricalTimeSeries costOfCarryTS) {
double result = 0.0d;
if (costOfCarryTS != null) {
final Double histCost = costOfCarryTS.getTimeSeries().getValue(tradeDate);
if (histCost != null) {
result = histCost;
}
}
return result;
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final PositionOrTrade positionOrTrade = target.getPositionOrTrade();
final Security security = positionOrTrade.getSecurity();
final ValueRequirement securityOrTradeValue;
if (positionOrTrade instanceof Trade) {
// If a TRADE, request the SECURITY's value and scale up during the execute
securityOrTradeValue = new ValueRequirement(ValueRequirementNames.VALUE, ComputationTargetType.SECURITY, security.getUniqueId(), getCurrencyProperty(security));
} else {
// If a POSITION, request the POSITION's value and DON'T scale during the execute
securityOrTradeValue = new ValueRequirement(ValueRequirementNames.VALUE, target.toSpecification(), getCurrencyProperty(security));
}
final HistoricalTimeSeriesResolver resolver = OpenGammaCompilationContext.getHistoricalTimeSeriesResolver(context);
final ExternalIdBundle bundle = security.getExternalIdBundle();
final DateConstraint startDate = getTimeSeriesStartDate(positionOrTrade);
final DateConstraint endDate = getTimeSeriesEndDate(positionOrTrade);
final ValueRequirement markToMarketValue = getMarkToMarketSeriesRequirement(resolver, bundle, startDate, endDate);
final ValueRequirement costOfCarryValue = getCostOfCarrySeriesRequirement(resolver, bundle, endDate);
if (markToMarketValue == null && costOfCarryValue == null) {
return null;
}
final Set<ValueRequirement> requirements = Sets.newHashSet(securityOrTradeValue);
if (markToMarketValue != null) {
requirements.add(markToMarketValue);
}
if (costOfCarryValue != null) {
requirements.add(costOfCarryValue);
}
return requirements;
}
protected ValueProperties getCurrencyProperty(final Security security) {
final Currency ccy = FinancialSecurityUtils.getCurrency(security);
if (ccy != null) {
return ValueProperties.with(ValuePropertyNames.CURRENCY, ccy.getCode()).get();
} else {
return ValueProperties.none();
}
}
private ValueRequirement getMarkToMarketSeriesRequirement(final HistoricalTimeSeriesResolver resolver, final ExternalIdBundle bundle, final DateConstraint startDate, final DateConstraint endDate) {
final HistoricalTimeSeriesResolutionResult timeSeries = resolver.resolve(bundle, null, null, null, _mark2MarketField, _resolutionKey);
if (timeSeries == null) {
return null;
}
return HistoricalTimeSeriesFunctionUtils.createHTSRequirement(timeSeries, _mark2MarketField, startDate, true, endDate, true);
}
private ValueRequirement getCostOfCarrySeriesRequirement(final HistoricalTimeSeriesResolver resolver, final ExternalIdBundle bundle, final DateConstraint endDate) {
final HistoricalTimeSeriesResolutionResult timeSeries = resolver.resolve(bundle, null, null, null, _costOfCarryField, _resolutionKey);
if (timeSeries == null) {
return null;
}
return HistoricalTimeSeriesFunctionUtils.createHTSRequirement(timeSeries, _costOfCarryField, endDate, true, endDate, true);
}
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
return !FXUtils.isFXSecurity(target.getPositionOrTrade().getSecurity());
}
protected ValueProperties.Builder createValueProperties(final ComputationTarget target) {
final ValueProperties.Builder properties = createValueProperties();
final Currency ccy = FinancialSecurityUtils.getCurrency(target.getPositionOrTrade().getSecurity());
if (ccy != null) {
properties.with(ValuePropertyNames.CURRENCY, ccy.getCode());
}
return properties;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
return Collections.singleton(new ValueSpecification(getResultValueRequirementName(), target.toSpecification(), createValueProperties(target).get()));
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
if (inputs.isEmpty()) {
return null;
}
UniqueId uidMarkToMarket = null;
UniqueId uidCostOfCarry = null;
for (final Map.Entry<ValueSpecification, ValueRequirement> input : inputs.entrySet()) {
if (ValueRequirementNames.HISTORICAL_TIME_SERIES.equals(input.getValue().getValueName())) {
if ("MarkToMarket".equals(input.getValue().getConstraint(".name"))) {
uidMarkToMarket = input.getKey().getTargetSpecification().getUniqueId();
} else {
uidCostOfCarry = input.getKey().getTargetSpecification().getUniqueId();
}
}
}
final Builder propertiesBuilder = createValueProperties(target);
if (uidMarkToMarket != null) {
propertiesBuilder.with("MarkToMarketTimeSeries", uidMarkToMarket.toString());
}
if (uidCostOfCarry != null) {
propertiesBuilder.with("CostOfCarryTimeSeries", uidCostOfCarry.toString());
}
return Collections.singleton(new ValueSpecification(getResultValueRequirementName(), target.toSpecification(), propertiesBuilder.get()));
}
}