Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add measure/calculation API for capital index bond
See #1331
- Loading branch information
1 parent
6f02366
commit f61f13a
Showing
8 changed files
with
1,101 additions
and
53 deletions.
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
...rc/main/java/com/opengamma/strata/measure/bond/CapitalIndexedBondMeasureCalculations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/** | ||
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies | ||
* | ||
* Please see distribution for license. | ||
*/ | ||
package com.opengamma.strata.measure.bond; | ||
|
||
import com.opengamma.strata.basics.currency.CurrencyAmount; | ||
import com.opengamma.strata.basics.currency.MultiCurrencyAmount; | ||
import com.opengamma.strata.collect.ArgChecker; | ||
import com.opengamma.strata.data.scenario.CurrencyScenarioArray; | ||
import com.opengamma.strata.data.scenario.MultiCurrencyScenarioArray; | ||
import com.opengamma.strata.data.scenario.ScenarioArray; | ||
import com.opengamma.strata.market.param.CurrencyParameterSensitivities; | ||
import com.opengamma.strata.market.sensitivity.PointSensitivities; | ||
import com.opengamma.strata.measure.rate.RatesScenarioMarketData; | ||
import com.opengamma.strata.pricer.bond.DiscountingCapitalIndexedBondTradePricer; | ||
import com.opengamma.strata.pricer.bond.LegalEntityDiscountingProvider; | ||
import com.opengamma.strata.pricer.rate.RatesProvider; | ||
import com.opengamma.strata.product.bond.ResolvedCapitalIndexedBondTrade; | ||
|
||
/** | ||
* Multi-scenario measure calculations for capital indexed bond trades. | ||
* <p> | ||
* Each method corresponds to a measure, typically calculated by one or more calls to the pricer. | ||
*/ | ||
final class CapitalIndexedBondMeasureCalculations { | ||
|
||
/** | ||
* Default implementation. | ||
*/ | ||
public static final CapitalIndexedBondMeasureCalculations DEFAULT = new CapitalIndexedBondMeasureCalculations( | ||
DiscountingCapitalIndexedBondTradePricer.DEFAULT); | ||
/** | ||
* One basis point, expressed as a {@code double}. | ||
*/ | ||
private static final double ONE_BASIS_POINT = 1e-4; | ||
|
||
/** | ||
* Pricer for {@link ResolvedCapitalIndexedBondTrade}. | ||
*/ | ||
private final DiscountingCapitalIndexedBondTradePricer tradePricer; | ||
|
||
/** | ||
* Creates an instance. | ||
* | ||
* @param tradePricer the pricer for {@link ResolvedCapitalIndexedBondTrade} | ||
*/ | ||
CapitalIndexedBondMeasureCalculations( | ||
DiscountingCapitalIndexedBondTradePricer tradePricer) { | ||
this.tradePricer = ArgChecker.notNull(tradePricer, "tradePricer"); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
// calculates present value for all scenarios | ||
CurrencyScenarioArray presentValue( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
return CurrencyScenarioArray.of( | ||
legalEntityMarketData.getScenarioCount(), | ||
i -> presentValue( | ||
trade, | ||
ratesMarketData.scenario(i).ratesProvider(), | ||
legalEntityMarketData.scenario(i).discountingProvider())); | ||
} | ||
|
||
// present value for one scenario | ||
CurrencyAmount presentValue( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesProvider ratesProvider, | ||
LegalEntityDiscountingProvider discountingProvider) { | ||
|
||
return tradePricer.presentValue(trade, ratesProvider, discountingProvider); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
// calculates calibrated sum PV01 for all scenarios | ||
MultiCurrencyScenarioArray pv01CalibratedSum( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
return MultiCurrencyScenarioArray.of( | ||
legalEntityMarketData.getScenarioCount(), | ||
i -> pv01CalibratedSum( | ||
trade, | ||
ratesMarketData.scenario(i).ratesProvider(), | ||
legalEntityMarketData.scenario(i).discountingProvider())); | ||
} | ||
|
||
// calibrated sum PV01 for one scenario | ||
MultiCurrencyAmount pv01CalibratedSum( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesProvider ratesProvider, | ||
LegalEntityDiscountingProvider discountingProvider) { | ||
|
||
PointSensitivities pointSensitivity = tradePricer.presentValueSensitivity(trade, ratesProvider, discountingProvider); | ||
return discountingProvider.parameterSensitivity(pointSensitivity).total().multipliedBy(ONE_BASIS_POINT); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
// calculates calibrated bucketed PV01 for all scenarios | ||
ScenarioArray<CurrencyParameterSensitivities> pv01CalibratedBucketed( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
return ScenarioArray.of( | ||
legalEntityMarketData.getScenarioCount(), | ||
i -> pv01CalibratedBucketed( | ||
trade, | ||
ratesMarketData.scenario(i).ratesProvider(), | ||
legalEntityMarketData.scenario(i).discountingProvider())); | ||
} | ||
|
||
// calibrated bucketed PV01 for one scenario | ||
CurrencyParameterSensitivities pv01CalibratedBucketed( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesProvider ratesProvider, | ||
LegalEntityDiscountingProvider discountingProvider) { | ||
|
||
PointSensitivities pointSensitivity = tradePricer.presentValueSensitivity(trade, ratesProvider, discountingProvider); | ||
return discountingProvider.parameterSensitivity(pointSensitivity).multipliedBy(ONE_BASIS_POINT); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
// calculates currency exposure for all scenarios | ||
MultiCurrencyScenarioArray currencyExposure( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
return MultiCurrencyScenarioArray.of( | ||
legalEntityMarketData.getScenarioCount(), | ||
i -> currencyExposure( | ||
trade, | ||
ratesMarketData.scenario(i).ratesProvider(), | ||
legalEntityMarketData.scenario(i).discountingProvider())); | ||
} | ||
|
||
// currency exposure for one scenario | ||
MultiCurrencyAmount currencyExposure( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesProvider ratesProvider, | ||
LegalEntityDiscountingProvider discountingProvider) { | ||
|
||
return tradePricer.currencyExposure(trade, ratesProvider, discountingProvider); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
// calculates current cash for all scenarios | ||
CurrencyScenarioArray currentCash( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
return CurrencyScenarioArray.of( | ||
legalEntityMarketData.getScenarioCount(), | ||
i -> currentCash( | ||
trade, | ||
ratesMarketData.scenario(i).ratesProvider())); | ||
} | ||
|
||
// current cash for one scenario | ||
CurrencyAmount currentCash( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesProvider ratesProvider) { | ||
|
||
return tradePricer.currentCash(trade, ratesProvider); | ||
} | ||
|
||
} |
169 changes: 169 additions & 0 deletions
169
...in/java/com/opengamma/strata/measure/bond/CapitalIndexedBondTradeCalculationFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/** | ||
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies | ||
* | ||
* Please see distribution for license. | ||
*/ | ||
package com.opengamma.strata.measure.bond; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.ImmutableSet; | ||
import com.opengamma.strata.basics.ReferenceData; | ||
import com.opengamma.strata.basics.StandardId; | ||
import com.opengamma.strata.basics.currency.Currency; | ||
import com.opengamma.strata.calc.Measure; | ||
import com.opengamma.strata.calc.runner.CalculationFunction; | ||
import com.opengamma.strata.calc.runner.CalculationParameters; | ||
import com.opengamma.strata.calc.runner.FunctionRequirements; | ||
import com.opengamma.strata.collect.result.FailureReason; | ||
import com.opengamma.strata.collect.result.Result; | ||
import com.opengamma.strata.data.scenario.ScenarioMarketData; | ||
import com.opengamma.strata.measure.Measures; | ||
import com.opengamma.strata.measure.rate.RatesMarketDataLookup; | ||
import com.opengamma.strata.measure.rate.RatesScenarioMarketData; | ||
import com.opengamma.strata.product.SecurityId; | ||
import com.opengamma.strata.product.bond.CapitalIndexedBond; | ||
import com.opengamma.strata.product.bond.CapitalIndexedBondTrade; | ||
import com.opengamma.strata.product.bond.ResolvedCapitalIndexedBondTrade; | ||
|
||
/** | ||
* Perform calculations on a single {@code CapitalIndexedBondTrade} for each of a set of scenarios. | ||
* <p> | ||
* This uses the standard discounting calculation method. | ||
* An instance of {@link LegalEntityDiscountingMarketDataLookup} must be specified. | ||
* The supported built-in measures are: | ||
* <ul> | ||
* <li>{@linkplain Measures#PRESENT_VALUE Present value} | ||
* <li>{@linkplain Measures#PV01_CALIBRATED_SUM PV01 calibrated sum} | ||
* <li>{@linkplain Measures#PV01_CALIBRATED_BUCKETED PV01 calibrated bucketed} | ||
* <li>{@linkplain Measures#CURRENCY_EXPOSURE Currency exposure} | ||
* <li>{@linkplain Measures#CURRENT_CASH Current cash} | ||
* <li>{@linkplain Measures#RESOLVED_TARGET Resolved trade} | ||
* </ul> | ||
* | ||
* <h4>Price</h4> | ||
* Strata uses <i>decimal prices</i> for bonds in the trade model, pricers and market data. | ||
* For example, a price of 99.32% is represented in Strata by 0.9932. | ||
*/ | ||
public class CapitalIndexedBondTradeCalculationFunction | ||
implements CalculationFunction<CapitalIndexedBondTrade> { | ||
|
||
/** | ||
* The calculations by measure. | ||
*/ | ||
private static final ImmutableMap<Measure, SingleMeasureCalculation> CALCULATORS = | ||
ImmutableMap.<Measure, SingleMeasureCalculation>builder() | ||
.put(Measures.PRESENT_VALUE, CapitalIndexedBondMeasureCalculations.DEFAULT::presentValue) | ||
.put(Measures.PV01_CALIBRATED_SUM, CapitalIndexedBondMeasureCalculations.DEFAULT::pv01CalibratedSum) | ||
.put(Measures.PV01_CALIBRATED_BUCKETED, CapitalIndexedBondMeasureCalculations.DEFAULT::pv01CalibratedBucketed) | ||
.put(Measures.CURRENCY_EXPOSURE, CapitalIndexedBondMeasureCalculations.DEFAULT::currencyExposure) | ||
.put(Measures.CURRENT_CASH, CapitalIndexedBondMeasureCalculations.DEFAULT::currentCash) | ||
.put(Measures.RESOLVED_TARGET, (rt, smd1, smd2) -> rt) | ||
.build(); | ||
|
||
private static final ImmutableSet<Measure> MEASURES = CALCULATORS.keySet(); | ||
|
||
/** | ||
* Creates an instance. | ||
*/ | ||
public CapitalIndexedBondTradeCalculationFunction() { | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
@Override | ||
public Class<CapitalIndexedBondTrade> targetType() { | ||
return CapitalIndexedBondTrade.class; | ||
} | ||
|
||
@Override | ||
public Set<Measure> supportedMeasures() { | ||
return MEASURES; | ||
} | ||
|
||
@Override | ||
public Optional<String> identifier(CapitalIndexedBondTrade target) { | ||
return target.getInfo().getId().map(id -> id.toString()); | ||
} | ||
|
||
@Override | ||
public Currency naturalCurrency(CapitalIndexedBondTrade trade, ReferenceData refData) { | ||
return trade.getProduct().getCurrency(); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
@Override | ||
public FunctionRequirements requirements( | ||
CapitalIndexedBondTrade trade, | ||
Set<Measure> measures, | ||
CalculationParameters parameters, | ||
ReferenceData refData) { | ||
|
||
// extract data from product | ||
CapitalIndexedBond product = trade.getProduct(); | ||
Currency currency = product.getCurrency(); | ||
SecurityId securityId = product.getSecurityId(); | ||
StandardId legalEntityId = product.getLegalEntityId(); | ||
|
||
// use lookup to build requirements | ||
RatesMarketDataLookup ratesLookup = parameters.getParameter(RatesMarketDataLookup.class); | ||
FunctionRequirements ratesReqs = ratesLookup.requirements( | ||
ImmutableSet.of(), ImmutableSet.of(product.getRateCalculation().getIndex())); | ||
LegalEntityDiscountingMarketDataLookup ledLookup = parameters.getParameter(LegalEntityDiscountingMarketDataLookup.class); | ||
FunctionRequirements ledReqs = ledLookup.requirements(securityId, legalEntityId, currency); | ||
return ratesReqs.combinedWith(ledReqs); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
@Override | ||
public Map<Measure, Result<?>> calculate( | ||
CapitalIndexedBondTrade trade, | ||
Set<Measure> measures, | ||
CalculationParameters parameters, | ||
ScenarioMarketData scenarioMarketData, | ||
ReferenceData refData) { | ||
|
||
// resolve the trade once for all measures and all scenarios | ||
ResolvedCapitalIndexedBondTrade resolved = trade.resolve(refData); | ||
|
||
// use lookup to query market data | ||
RatesMarketDataLookup ratesLookup = parameters.getParameter(RatesMarketDataLookup.class); | ||
RatesScenarioMarketData ratesMarketData = ratesLookup.marketDataView(scenarioMarketData); | ||
LegalEntityDiscountingMarketDataLookup ledLookup = parameters.getParameter(LegalEntityDiscountingMarketDataLookup.class); | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData = ledLookup.marketDataView(scenarioMarketData); | ||
|
||
// loop around measures, calculating all scenarios for one measure | ||
Map<Measure, Result<?>> results = new HashMap<>(); | ||
for (Measure measure : measures) { | ||
results.put(measure, calculate(measure, resolved, ratesMarketData, legalEntityMarketData)); | ||
} | ||
return results; | ||
} | ||
|
||
// calculate one measure | ||
private Result<?> calculate( | ||
Measure measure, | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData) { | ||
|
||
SingleMeasureCalculation calculator = CALCULATORS.get(measure); | ||
if (calculator == null) { | ||
return Result.failure(FailureReason.UNSUPPORTED, "Unsupported measure for CapitalIndexedBondTrade: {}", measure); | ||
} | ||
return Result.of(() -> calculator.calculate(trade, ratesMarketData, legalEntityMarketData)); | ||
} | ||
|
||
//------------------------------------------------------------------------- | ||
@FunctionalInterface | ||
interface SingleMeasureCalculation { | ||
public abstract Object calculate( | ||
ResolvedCapitalIndexedBondTrade trade, | ||
RatesScenarioMarketData ratesMarketData, | ||
LegalEntityDiscountingScenarioMarketData legalEntityMarketData); | ||
} | ||
|
||
} |
Oops, something went wrong.