/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame; import org.threeten.bp.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.forex.definition.ForexDefinition; import com.opengamma.analytics.financial.forex.definition.ForexSwapDefinition; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.instrument.InstrumentDefinition; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.legalentity.LegalEntitySource; import com.opengamma.core.link.ConventionLink; import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle; import com.opengamma.core.region.RegionSource; import com.opengamma.financial.analytics.conversion.CalendarUtils; import com.opengamma.financial.analytics.curve.BillNodeConverter; import com.opengamma.financial.analytics.curve.BondNodeConverter; import com.opengamma.financial.analytics.curve.CashNodeConverter; import com.opengamma.financial.analytics.curve.CurveNodeVisitorAdapter; import com.opengamma.financial.analytics.curve.FRANodeConverter; import com.opengamma.financial.analytics.curve.RateFutureNodeConverter; import com.opengamma.financial.analytics.curve.RollDateFRANodeConverter; import com.opengamma.financial.analytics.curve.RollDateSwapNodeConverter; import com.opengamma.financial.analytics.curve.SwapNodeConverter; import com.opengamma.financial.analytics.curve.ThreeLegBasisSwapNodeConverter; import com.opengamma.financial.analytics.ircurve.strips.BillNode; import com.opengamma.financial.analytics.ircurve.strips.BondNode; import com.opengamma.financial.analytics.ircurve.strips.CashNode; import com.opengamma.financial.analytics.ircurve.strips.CurveNodeVisitor; import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier; import com.opengamma.financial.analytics.ircurve.strips.FRANode; import com.opengamma.financial.analytics.ircurve.strips.FXForwardNode; import com.opengamma.financial.analytics.ircurve.strips.FXSwapNode; import com.opengamma.financial.analytics.ircurve.strips.PointsCurveNodeWithIdentifier; import com.opengamma.financial.analytics.ircurve.strips.RateFutureNode; import com.opengamma.financial.analytics.ircurve.strips.RollDateFRANode; import com.opengamma.financial.analytics.ircurve.strips.RollDateSwapNode; import com.opengamma.financial.analytics.ircurve.strips.SwapNode; import com.opengamma.financial.analytics.ircurve.strips.ThreeLegBasisSwapNode; import com.opengamma.financial.convention.ConventionBundleSource; import com.opengamma.financial.convention.FXForwardAndSwapConvention; import com.opengamma.financial.convention.FXSpotConvention; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.id.ExternalId; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; import com.opengamma.util.time.Tenor; /** * Helper class to create instrument definitions for curve nodes. Internally this uses * the visitor pattern as that is what was used in the previous code. However, this * leads to the odd situation where the visitor being created already has knowledge of * the node it will be visited by. This suggests there may be a better approach. */ public class CurveNodeInstrumentDefinitionFactory { private final ConventionBundleSource _conventionBundleSource; private final LegalEntitySource _legalEntitySource; private final HolidaySource _holidaySource; private final RegionSource _regionSource; /** * Create the factory. * * @param holidaySource the holiday source * @param regionSource the region source * @param conventionBundleSource the convention bundle source * @param legalEntitySource the legal entity source */ public CurveNodeInstrumentDefinitionFactory(HolidaySource holidaySource, RegionSource regionSource, ConventionBundleSource conventionBundleSource, LegalEntitySource legalEntitySource) { _holidaySource = ArgumentChecker.notNull(holidaySource, "holidaySource"); _regionSource = ArgumentChecker.notNull(regionSource, "regionSource"); _conventionBundleSource = ArgumentChecker.notNull(conventionBundleSource, "conventionBundleSource"); _legalEntitySource = ArgumentChecker.notNull(legalEntitySource, "legalEntitySource"); } /** * Creates an instrument definition for the specified node using the * supplied market data. * * @param nodeWithId the node to generate an instrument definition for * @param marketData snapshot containing the market data for the nodes * @param valuationTime valuation time for the calibration * @param fxMatrix fx matrix containing fx rates * @return an instrument definition for the node */ public InstrumentDefinition<?> createInstrumentDefinition(CurveNodeWithIdentifier nodeWithId, SnapshotDataBundle marketData, ZonedDateTime valuationTime, FXMatrix fxMatrix) { CurveNodeVisitor<InstrumentDefinition<?>> curveNodeVisitor = createCurveNodeVisitor(nodeWithId, marketData, valuationTime, fxMatrix); return nodeWithId.getCurveNode().accept(curveNodeVisitor); } private CurveNodeVisitor<InstrumentDefinition<?>> createCurveNodeVisitor(final CurveNodeWithIdentifier nodeWithId, final SnapshotDataBundle marketData, final ZonedDateTime valuationTime, final FXMatrix fxMatrix) { final ExternalId nodeDataId = nodeWithId.getIdentifier(); return new CurveNodeVisitorAdapter<InstrumentDefinition<?>>() { @Override public InstrumentDefinition<?> visitFXForwardNode(FXForwardNode node) { double combinedRate = getCombinedRate(); ExternalId conventionId = node.getFxForwardConvention(); FXForwardAndSwapConvention forwardConvention = ConventionLink.resolvable(conventionId, FXForwardAndSwapConvention.class).resolve(); ExternalId underlyingConventionId = forwardConvention.getSpotConvention(); FXSpotConvention underlyingConvention = ConventionLink.resolvable(underlyingConventionId, FXSpotConvention.class).resolve(); Currency payCurrency = node.getPayCurrency(); Currency receiveCurrency = node.getReceiveCurrency(); Tenor forwardTenor = node.getMaturityTenor(); int settlementDays = underlyingConvention.getSettlementDays(); Calendar settlementCalendar = getCalendar(forwardConvention.getSettlementRegion()); ZonedDateTime spotDate = ScheduleCalculator.getAdjustedDate(valuationTime, settlementDays, settlementCalendar); ZonedDateTime exchangeDate = ScheduleCalculator.getAdjustedDate( spotDate, forwardTenor.getPeriod(), forwardConvention.getBusinessDayConvention(), settlementCalendar, forwardConvention.isIsEOM()); return ForexDefinition.fromAmounts(payCurrency, receiveCurrency, exchangeDate, 1, -combinedRate); } private double getCombinedRate() { // An FX Forward curve node could either be a PointsCurveNodeWithIdentifier // with the forward and spot rates, or a CurveNodeWithIdentifier with a // combined rate. In the former case we need to sum the forward and // spot to get a combined rate. double base = getMarketData(nodeWithId.getIdentifier()); return nodeWithId instanceof PointsCurveNodeWithIdentifier ? base + getMarketData(((PointsCurveNodeWithIdentifier) nodeWithId).getUnderlyingIdentifier()) : base; } @Override public InstrumentDefinition<?> visitFXSwapNode(FXSwapNode node) { // An FX Swap node must be a PointsCurveNodeWithIdentifier as we need // both the forward and spot rates PointsCurveNodeWithIdentifier pointsNodeWithId = (PointsCurveNodeWithIdentifier) nodeWithId; double forward = getMarketData(pointsNodeWithId.getIdentifier()); double spot = getMarketData(pointsNodeWithId.getUnderlyingIdentifier()); ExternalId conventionId = node.getFxSwapConvention(); FXForwardAndSwapConvention forwardConvention = ConventionLink.resolvable(conventionId, FXForwardAndSwapConvention.class).resolve(); ExternalId underlyingConventionId = forwardConvention.getSpotConvention(); FXSpotConvention underlyingConvention = ConventionLink.resolvable(underlyingConventionId, FXSpotConvention.class).resolve(); Currency payCurrency = node.getPayCurrency(); Currency receiveCurrency = node.getReceiveCurrency(); Tenor forwardTenor = node.getMaturityTenor(); int settlementDays = underlyingConvention.getSettlementDays(); Calendar settlementCalendar = getCalendar(forwardConvention.getSettlementRegion()); ZonedDateTime spotDate = ScheduleCalculator.getAdjustedDate(valuationTime, settlementDays, settlementCalendar); ZonedDateTime exchangeDate = ScheduleCalculator.getAdjustedDate( spotDate, forwardTenor.getPeriod(), forwardConvention.getBusinessDayConvention(), settlementCalendar, forwardConvention.isIsEOM()); return new ForexSwapDefinition(payCurrency, receiveCurrency, spotDate, exchangeDate, 1, spot, forward); } @Override public InstrumentDefinition<?> visitCashNode(CashNode node) { CashNodeConverter nodeConverter = new CashNodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitCashNode(node); } @Override public InstrumentDefinition<?> visitFRANode(FRANode node) { FRANodeConverter nodeConverter = new FRANodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitFRANode(node); } @Override public InstrumentDefinition<?> visitRollDateFRANode(RollDateFRANode node) { RollDateFRANodeConverter nodeConverter = new RollDateFRANodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitRollDateFRANode(node); } @Override public InstrumentDefinition<?> visitRollDateSwapNode(RollDateSwapNode node) { RollDateSwapNodeConverter nodeConverter = new RollDateSwapNodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitRollDateSwapNode(node); } @Override public InstrumentDefinition<?> visitRateFutureNode(RateFutureNode node) { RateFutureNodeConverter nodeConverter = new RateFutureNodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitRateFutureNode(node); } @Override public InstrumentDefinition<?> visitThreeLegBasisSwapNode(ThreeLegBasisSwapNode node) { ThreeLegBasisSwapNodeConverter nodeConverter = new ThreeLegBasisSwapNodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitThreeLegBasisSwapNode(node); } @Override public InstrumentDefinition<?> visitSwapNode(SwapNode node) { SwapNodeConverter nodeConverter = new SwapNodeConverter(_holidaySource, _regionSource, marketData, nodeDataId, valuationTime, fxMatrix); return nodeConverter.visitSwapNode(node); } @Override public InstrumentDefinition<?> visitBillNode(BillNode node) { BillNodeConverter nodeConverter = new BillNodeConverter(_holidaySource, _regionSource, _legalEntitySource, marketData, nodeDataId, valuationTime); return nodeConverter.visitBillNode(node); } @Override public InstrumentDefinition<?> visitBondNode(BondNode node) { BondNodeConverter nodeConverter = new BondNodeConverter(_regionSource, _holidaySource, _conventionBundleSource, marketData, nodeDataId, valuationTime); return nodeConverter.visitBondNode(node); } private Calendar getCalendar(ExternalId calendarId) { return CalendarUtils.getCalendar(_regionSource, _holidaySource, calendarId); } private double getMarketData(ExternalId identifier) { Double value = marketData.getDataPoint(identifier); if (value != null) { return value; } else { throw new OpenGammaRuntimeException("Could not get market data for " + identifier); } } }; } }