/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.covariance;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.threeten.bp.Period;
import com.opengamma.analytics.financial.covariance.CovarianceMatrixCalculator;
import com.opengamma.analytics.financial.covariance.HistoricalCovarianceCalculator;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.core.position.Portfolio;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ValueProperties;
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.engine.view.ViewCalculationConfiguration;
import com.opengamma.engine.view.ViewDefinition;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.financial.analytics.DoubleLabelledMatrix2D;
import com.opengamma.financial.analytics.timeseries.DateConstraint;
import com.opengamma.financial.temptarget.TempTarget;
import com.opengamma.financial.temptarget.TempTargetRepository;
import com.opengamma.financial.view.HistoricalViewEvaluationMarketDataMode;
import com.opengamma.financial.view.HistoricalViewEvaluationTarget;
import com.opengamma.financial.view.ViewEvaluationFunction;
import com.opengamma.id.UniqueId;
import com.opengamma.timeseries.DoubleTimeSeries;
/**
* Iterates a view client over a window of historical data to get time series of values from which a covariance matrix can be constructed. The target will identify the item(s) for which data should be
* gathered to build the matrix from.
*/
public abstract class SampledCovarianceMatrixFunction extends AbstractFunction.NonCompiledInvoker {
/**
* When used in "permissive" mode, will use this period as a default sampling duration.
*/
private static final Period DEFAULT_SAMPLING_PERIOD = Period.ofMonths(1);
/**
* The type supported by this class.
*/
protected static final ComputationTargetType TYPE = ComputationTargetType.PORTFOLIO.or(ComputationTargetType.PORTFOLIO_NODE).or(ComputationTargetType.POSITION);
/**
* Returns the type of data used to construct the matrix, and distinguish between different sub-class implementations. For example, this might be market data, risk factors or something else.
*
* @return the type, not null
*/
protected abstract String getDataType();
protected Set<ValueRequirement> createRequirements(final ComputationTargetSpecification tempTargetSpec) {
return Collections.singleton(new ValueRequirement(ValueRequirementNames.HISTORICAL_TIME_SERIES, tempTargetSpec, ValueProperties.withAny(ViewEvaluationFunction.PROPERTY_CALC_CONFIG).get()));
}
protected void addValueRequirements(final FunctionCompilationContext context, final Portfolio portfolio, final ViewCalculationConfiguration calcConfig) {
addValueRequirements(context, portfolio.getRootNode(), calcConfig);
}
protected void addValueRequirements(final FunctionCompilationContext context, final PortfolioNode node, final ViewCalculationConfiguration calcConfig) {
for (PortfolioNode child : node.getChildNodes()) {
addValueRequirements(context, child, calcConfig);
}
for (Position child : node.getPositions()) {
addValueRequirements(context, child, calcConfig);
}
}
protected abstract void addValueRequirements(FunctionCompilationContext context, Position position, ViewCalculationConfiguration calcConfig);
protected void addValueRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ViewCalculationConfiguration calcConfig) {
if (target.getValue() instanceof Portfolio) {
addValueRequirements(context, (Portfolio) target.getValue(), calcConfig);
} else if (target.getValue() instanceof PortfolioNode) {
addValueRequirements(context, (PortfolioNode) target.getValue(), calcConfig);
} else if (target.getValue() instanceof Position) {
addValueRequirements(context, (Position) target.getValue(), calcConfig);
}
}
protected ViewCalculationConfiguration createViewCalculationConfiguration(final ViewDefinition viewDefinition, final String calcConfigName) {
return new ViewCalculationConfiguration(viewDefinition, calcConfigName);
}
protected <T extends Comparable<? super T>> DoubleLabelledMatrix2D createCovarianceMatrix(DoubleTimeSeries<T>[] timeSeries, Object[] labels) {
final CovarianceMatrixCalculator calculator = new CovarianceMatrixCalculator(new HistoricalCovarianceCalculator());
int len = timeSeries.length;
// Any nulls or empty time series (missing data) will upset the calculator, so we'll remove them and produce a best efforts matrix with what is left
for (int i = 0; i < len; i++) {
if ((timeSeries[i] == null) || timeSeries[i].isEmpty()) {
len--;
timeSeries[i] = timeSeries[len];
labels[i] = labels[len];
i--;
}
}
if (len == 0) {
throw new IllegalArgumentException("No time series");
}
if (len != timeSeries.length) {
timeSeries = Arrays.copyOf(timeSeries, len);
labels = Arrays.copyOf(labels, len);
}
// The time-series must all have corresponding dates - delete any points which are not common to all time-series
final Comparable<? super T>[][] times = new Comparable[len][];
final double[][] values = new double[len][];
for (int i = 0; i < len; i++) {
times[i] = timeSeries[i].timesArray();
values[i] = timeSeries[i].valuesArrayFast();
}
boolean ended = false;
int timeIndex = 0;
do {
Comparable<? super T> earliest = times[0][timeIndex];
boolean mismatch = false;
for (int i = 1; i < len; i++) {
int c = earliest.compareTo((T) times[i][timeIndex]);
if (c != 0) {
mismatch = true;
if (c > 0) {
earliest = times[i][timeIndex];
}
}
}
if (mismatch) {
for (int i = 0; i < len; i++) {
if (earliest.equals(times[i][timeIndex])) {
System.arraycopy(times[i], timeIndex + 1, times[i], timeIndex, times[i].length - (timeIndex + 1));
System.arraycopy(values[i], timeIndex + 1, values[i], timeIndex, values[i].length - (timeIndex + 1));
times[i][times[i].length - 1] = null;
if (times[i][timeIndex] == null) {
ended = true;
}
}
}
} else {
timeIndex++;
for (int i = 0; i < len; i++) {
if ((timeIndex >= times[i].length) || (times[i][timeIndex] == null)) {
ended = true;
}
}
}
} while (!ended);
if (timeIndex < 1) {
throw new IllegalArgumentException("Time series union is empty");
}
DoubleTimeSeries<T>[] newTimeSeries = null;
for (int i = 0; i < len; i++) {
if (times[i].length > timeIndex) {
if (newTimeSeries == null) {
newTimeSeries = new DoubleTimeSeries[len];
System.arraycopy(timeSeries, 0, newTimeSeries, 0, len);
}
final Double[] value = new Double[timeIndex];
for (int j = 0; j < timeIndex; j++) {
value[j] = values[i][j];
}
newTimeSeries[i] = timeSeries[i].newInstance(Arrays.copyOf((T[]) times[i], timeIndex), value);
}
}
if (newTimeSeries != null) {
timeSeries = newTimeSeries;
}
// Keys will just be sequential numbers
final Double[] keys = new Double[len];
for (int i = 0; i < timeSeries.length; i++) {
keys[i] = (double) i;
}
// Calculate the co-variance matrix
final DoubleMatrix2D unlabelled = calculator.evaluate(timeSeries);
// Label it
return new DoubleLabelledMatrix2D(keys, labels, keys, labels, unlabelled.getData());
}
// CompiledFunctionDefinition
@Override
public ComputationTargetType getTargetType() {
return TYPE;
}
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
return context.getViewCalculationConfiguration() != null;
}
@Override
protected ValueProperties.Builder createValueProperties() {
final ValueProperties.Builder properties = super.createValueProperties();
properties.with("Type", getDataType());
return properties;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
return Collections.singleton(new ValueSpecification(ValueRequirementNames.COVARIANCE_MATRIX, target.toSpecification(), createValueProperties().withAny(ValuePropertyNames.SAMPLING_PERIOD).get()));
}
private String anyConstraintOrNull(final ValueProperties constraints, final String name) {
final Set<String> values = constraints.getValues(name);
if ((values == null) || values.isEmpty()) {
return null;
} else {
return values.iterator().next();
}
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final ValueProperties constraints = desiredValue.getConstraints();
final String lookbackPeriodString = anyConstraintOrNull(constraints, ValuePropertyNames.SAMPLING_PERIOD);
final DateConstraint startDate;
if (lookbackPeriodString == null) {
if (!OpenGammaCompilationContext.isPermissive(context)) {
return null;
}
startDate = DateConstraint.VALUATION_TIME.minus(DEFAULT_SAMPLING_PERIOD);
} else {
startDate = DateConstraint.VALUATION_TIME.minus(Period.parse(lookbackPeriodString));
}
final ViewDefinition viewDefinition = context.getViewCalculationConfiguration().getViewDefinition();
final HistoricalViewEvaluationTarget tempTarget = new HistoricalViewEvaluationTarget(viewDefinition.getMarketDataUser(), startDate.toString(), true, DateConstraint.VALUATION_TIME.toString(),
false, null, HistoricalViewEvaluationMarketDataMode.HISTORICAL);
final ViewCalculationConfiguration calcConfig = createViewCalculationConfiguration(tempTarget.getViewDefinition(), context.getViewCalculationConfiguration().getName());
addValueRequirements(context, target, calcConfig);
tempTarget.getViewDefinition().addViewCalculationConfiguration(calcConfig);
final TempTargetRepository targets = OpenGammaCompilationContext.getTempTargets(context);
final UniqueId tempTargetId = targets.locateOrStore(tempTarget);
final ComputationTargetSpecification targetSpec = new ComputationTargetSpecification(TempTarget.TYPE, tempTargetId);
return createRequirements(targetSpec);
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
final TempTarget tempTargetObject = OpenGammaCompilationContext.getTempTargets(context).get(inputs.keySet().iterator().next().getTargetSpecification().getUniqueId());
if (!(tempTargetObject instanceof HistoricalViewEvaluationTarget)) {
return null;
}
final HistoricalViewEvaluationTarget historicalTarget = (HistoricalViewEvaluationTarget) tempTargetObject;
final DateConstraint startDate = DateConstraint.parse(historicalTarget.getStartDate());
final DateConstraint endDate = DateConstraint.parse(historicalTarget.getEndDate());
final Period samplingPeriod = startDate.periodUntil(endDate);
return Collections
.singleton(new ValueSpecification(ValueRequirementNames.COVARIANCE_MATRIX, target.toSpecification(), createValueProperties()
.with(ValuePropertyNames.SAMPLING_PERIOD, samplingPeriod.toString()).get()));
}
}