/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.bond;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Clock;
import org.threeten.bp.Instant;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.interestrate.NelsonSiegelSvennsonBondCurveModel;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.provider.calculator.generic.LastTimeCalculator;
import com.opengamma.analytics.math.curve.FunctionalDoublesCurve;
import com.opengamma.analytics.math.function.ParameterizedFunction;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.statistics.leastsquare.LeastSquareResults;
import com.opengamma.analytics.math.statistics.leastsquare.NonLinearLeastSquare;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.region.RegionSource;
import com.opengamma.core.security.Security;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.CompiledFunctionDefinition;
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.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.OpenGammaExecutionContext;
import com.opengamma.financial.analytics.conversion.BondSecurityConverter;
import com.opengamma.financial.convention.ConventionBundleSource;
import com.opengamma.financial.security.FinancialSecuritySource;
import com.opengamma.financial.security.bond.BondSecurity;
import com.opengamma.financial.security.bond.GovernmentBondSecurity;
import com.opengamma.util.money.Currency;
/**
*
*/
public class NelsonSiegelSvenssonBondCurveFunction extends AbstractFunction {
/** Name of the property type*/
public static final String PROPERTY_CURVE_CALCULATION_TYPE = "Nelson_Siegel_Svennson_Bond_Curve";
/** Name of the property*/
public static final String PROPERTY_PREFIX = "Nelson-Siegel-Svennson";
private static final Logger s_logger = LoggerFactory.getLogger(NelsonSiegelSvenssonBondCurveFunction.class);
private static final NonLinearLeastSquare MINIMISER = new NonLinearLeastSquare();
private static final LastTimeCalculator LAST_DATE = LastTimeCalculator.getInstance();
private static final NelsonSiegelSvennsonBondCurveModel MODEL = new NelsonSiegelSvennsonBondCurveModel();
//private static final ParameterLimitsTransform[] TRANSFORMS = new ParameterLimitsTransform[] {new SingleRangeLimitTransform(0, LimitType.GREATER_THAN), new NullTransform(), new NullTransform(),
// new NullTransform(), new NullTransform(), new NullTransform()};
private static final BitSet FIXED_PARAMETERS = new BitSet(6);
//TODO remove this hard-coding
private static final String ISSUER_NAME = "US TREASURY N/B";
private static final Currency CURRENCY = Currency.USD;
static {
FIXED_PARAMETERS.set(0);
}
private ValueSpecification _result;
private Set<ValueSpecification> _results;
@Override
public void init(final FunctionCompilationContext context) {
_result = new ValueSpecification(ValueRequirementNames.NSS_BOND_CURVE, ComputationTargetSpecification.of(CURRENCY), createValueProperties().with(
PROPERTY_CURVE_CALCULATION_TYPE, PROPERTY_PREFIX + "_" + CURRENCY.getCode()).get());
_results = Sets.newHashSet(_result);
}
@Override
public String getShortName() {
return "NelsonSiegelSvennsonBondCurveFunction";
}
@Override
public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) {
return new AbstractInvokingCompiledFunction() {
@SuppressWarnings("synthetic-access")
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final HolidaySource holidaySource = OpenGammaExecutionContext.getHolidaySource(executionContext);
final ConventionBundleSource conventionSource = OpenGammaExecutionContext.getConventionBundleSource(executionContext);
final RegionSource regionSource = OpenGammaExecutionContext.getRegionSource(executionContext);
final Clock snapshotClock = executionContext.getValuationClock();
final ZonedDateTime now = ZonedDateTime.now(snapshotClock);
final BondSecurityConverter converter = new BondSecurityConverter(holidaySource, conventionSource, regionSource);
final FinancialSecuritySource securitySource = executionContext.getSecuritySource(FinancialSecuritySource.class);
final Collection<Security> allBonds = new ArrayList<Security>(securitySource.getBondsWithIssuerName(ISSUER_NAME));
final Iterator<Security> iter = allBonds.iterator();
while (iter.hasNext()) {
final Security sec = iter.next();
if (sec instanceof BondSecurity) {
final BondSecurity bond = (BondSecurity) sec;
if (bond.getLastTradeDate().getExpiry().isBefore(now)) {
iter.remove();
}
s_logger.info(bond.getLastTradeDate().toString());
} else {
throw new OpenGammaRuntimeException("non-bond security " + sec + " returned by getAllBondsOfIssuerType()");
}
}
final int n = allBonds.size();
final double[] t = new double[n];
final double[] ytm = new double[n];
int i = 0;
for (final Security security : allBonds) {
final GovernmentBondSecurity bondSec = (GovernmentBondSecurity) security;
final Object ytmObject = inputs.getValue(new ValueRequirement(ValueRequirementNames.YTM, ComputationTargetType.SECURITY, security.getUniqueId()));
if (ytmObject == null) {
s_logger.warn("Could not get YTM for " + security.getUniqueId());
continue;
}
if (!(ytmObject instanceof Double)) {
throw new IllegalArgumentException("YTM should be a double");
}
final InstrumentDefinition<?> definition = converter.visitGovernmentBondSecurity(bondSec);
final String bondStringName = PROPERTY_PREFIX + "_" + CURRENCY.getCode();
final InstrumentDerivative bond = definition.toDerivative(now, bondStringName);
t[i] = bond.accept(LAST_DATE);
ytm[i++] = ((Double) ytmObject / 100);
}
final DoubleMatrix1D initialValues = new DoubleMatrix1D(new double[] {1, 2, 3, 4, 2, 3 });
final ParameterizedFunction<Double, DoubleMatrix1D, Double> parameterizedFunction = MODEL.getParameterizedFunction();
final LeastSquareResults result = MINIMISER.solve(new DoubleMatrix1D(t), new DoubleMatrix1D(ytm), parameterizedFunction, initialValues);
final DoubleMatrix1D parameters = result.getFitParameters();
final FunctionalDoublesCurve curve = FunctionalDoublesCurve.from(parameterizedFunction.asFunctionOfArguments(parameters));
final YieldCurve yieldCurve = YieldCurve.from(curve);
return Sets.newHashSet(new ComputedValue(_result, yieldCurve));
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.CURRENCY;
}
@SuppressWarnings("synthetic-access")
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
return CURRENCY.equals(target.getValue());
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
if (canApplyTo(context, target)) {
final FinancialSecuritySource securitySource = context.getSecuritySource(FinancialSecuritySource.class);
final Collection<Security> allBonds = new ArrayList<Security>(securitySource.getBondsWithIssuerName("US TREASURY N/B"));
final Iterator<Security> iter = allBonds.iterator();
while (iter.hasNext()) {
final Security sec = iter.next();
if (sec instanceof BondSecurity) {
final BondSecurity bond = (BondSecurity) sec;
if (bond.getLastTradeDate().getExpiry().toInstant().isBefore(atInstant)) {
iter.remove();
}
s_logger.info(bond.getLastTradeDate().toString());
} else {
throw new OpenGammaRuntimeException("non-bond security " + sec + " returned by getAllBondsOfIssuerType()");
}
}
final Set<ValueRequirement> requirements = new HashSet<ValueRequirement>();
for (final Security sec : allBonds) {
if (sec instanceof BondSecurity) {
final BondSecurity bond = (BondSecurity) sec;
if (!bond.getCurrency().equals(CURRENCY)) {
throw new OpenGammaRuntimeException("Currency for bond " + bond.getUniqueId() + " (" + bond.getCurrency() + ") did not match that required (" + CURRENCY + ")");
}
requirements.add(new ValueRequirement(ValueRequirementNames.YTM, ComputationTargetType.SECURITY, bond.getUniqueId()));
} else {
throw new OpenGammaRuntimeException("non-bond security " + sec + " returned with bonds of issuer type");
}
}
return requirements;
}
return Sets.newHashSet();
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
return _results;
}
};
}
}