/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.pnl;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.LocalDate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.sesame.Environment;
import com.opengamma.sesame.FXMatrixFn;
import com.opengamma.sesame.marketdata.HistoricalMarketDataFn;
import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.util.result.Result;
import com.opengamma.util.time.LocalDateRange;
/**
* Converts historical PnL numbers to use today's fx rate. Performs the
* calculation TodayPnL(T) = PnL(T) * FX(T) / FX(today). PnLPeriodBound
* parameter defines whether FX(T-1) or FX(T), i.e. start or end, respectively,
* is used.
*/
public class DefaultHistoricalPnLFXConverterFn implements HistoricalPnLFXConverterFn {
private static final Logger s_logger = LoggerFactory.getLogger(DefaultHistoricalPnLFXConverterFn.class);
private final FXMatrixFn _fxMatrixFn;
private final HistoricalMarketDataFn _historicalMarketDataFn;
//specify whether to use start or end rate
private final PnLPeriodBound _periodBound;
private final boolean _rollRequired;
/**
* Constructs a new instance.
* @param fxMatrixFn the fx matrix for sourcing current spot rate
* @param historicalMarketDataFn the historical market data function to source the time series from
* @param periodBound the PnL period bound to use
*/
public DefaultHistoricalPnLFXConverterFn(FXMatrixFn fxMatrixFn, HistoricalMarketDataFn historicalMarketDataFn, PnLPeriodBound periodBound) {
_fxMatrixFn = fxMatrixFn;
_historicalMarketDataFn = historicalMarketDataFn;
_periodBound = periodBound;
_rollRequired = rollRequired(periodBound);
}
@Override
public Result<LocalDateDoubleTimeSeries> convertToSpotRate(Environment env, CurrencyPair currencyPair, LocalDateDoubleTimeSeries hts) {
LocalDateRange fxDateRange = getDateRange(hts);
s_logger.debug("Sourcing {} fx rates for period {}.", currencyPair, fxDateRange);
Result<LocalDateDoubleTimeSeries> ccyPairHtsResult = _historicalMarketDataFn.getFxRates(env, currencyPair, fxDateRange);
Result<FXMatrix> fxMatrixResult = _fxMatrixFn.getFXMatrix(env, ImmutableSet.of(currencyPair.getBase(), currencyPair.getCounter()));
if (!Result.allSuccessful(fxMatrixResult, ccyPairHtsResult)) {
return Result.failure(fxMatrixResult, ccyPairHtsResult);
}
FXMatrix fxMatrix = fxMatrixResult.getValue();
LocalDateDoubleTimeSeries ccyPairHts = rollIfRequired(currencyPair, ccyPairHtsResult.getValue().reciprocal());
double envFxRate = fxMatrix.getFxRate(currencyPair.getBase(), currencyPair.getCounter());
LocalDateDoubleTimeSeries resultHts = hts.multiply(ccyPairHts).divide(envFxRate);
return Result.success(resultHts);
}
private LocalDateDoubleTimeSeries rollIfRequired(CurrencyPair currencyPair, LocalDateDoubleTimeSeries ccyPairHts) {
if (_rollRequired) {
s_logger.debug("Rolling {} series since period bound is {}", currencyPair, _periodBound);
return rollSeries(ccyPairHts);
} else {
return ccyPairHts;
}
}
/**
* Rolls series so that time[i + 1] = rate[i]. This results in the start
* time and end rate being trimmed. Won't be an issue since the series should
* have been padded at the beginning.
*/
private LocalDateDoubleTimeSeries rollSeries(LocalDateDoubleTimeSeries ccyPairHts) {
List<LocalDate> dates = Lists.newLinkedList();
List<Double> rates = Lists.newLinkedList();
for (int i = 0; i < ccyPairHts.size() - 1; i++) {
LocalDate time = ccyPairHts.getTimeAtIndex(i + 1);
Double rate = ccyPairHts.getValueAtIndex(i);
dates.add(time);
rates.add(rate);
}
return ImmutableLocalDateDoubleTimeSeries.of(dates, rates);
}
/**
* Returns the appropriate date range to source fx rates for.
*/
private LocalDateRange getDateRange(LocalDateDoubleTimeSeries hts) {
LocalDate start = hts.getEarliestTime();
LocalDate end = hts.getLatestTime();
//pad start of period if we're sourcing start rates
if (_rollRequired) {
start = start.minusDays(7);
}
return LocalDateRange.of(start, end, true);
}
/**
* Whether fx rates need to be rolled in order to
* correspond to the correct pnl date.
*/
private boolean rollRequired(PnLPeriodBound bound) {
return PnLPeriodBound.START.equals(bound);
}
}