/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.examples.finance; import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_FOLLOWING; import static com.opengamma.strata.measure.StandardComponents.marketDataFactory; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.time.LocalDate; import java.util.List; import java.util.Locale; import com.google.common.collect.ImmutableList; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.StandardId; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.basics.date.BusinessDayAdjustment; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.basics.date.DaysAdjustment; import com.opengamma.strata.basics.date.HolidayCalendarIds; import com.opengamma.strata.basics.index.IborIndices; import com.opengamma.strata.basics.schedule.Frequency; import com.opengamma.strata.basics.schedule.PeriodicSchedule; import com.opengamma.strata.calc.CalculationRules; import com.opengamma.strata.calc.CalculationRunner; import com.opengamma.strata.calc.Column; import com.opengamma.strata.calc.Results; import com.opengamma.strata.calc.marketdata.MarketDataConfig; import com.opengamma.strata.calc.marketdata.MarketDataFilter; import com.opengamma.strata.calc.marketdata.MarketDataRequirements; import com.opengamma.strata.calc.marketdata.PerturbationMapping; import com.opengamma.strata.calc.marketdata.ScenarioDefinition; import com.opengamma.strata.calc.runner.CalculationFunctions; import com.opengamma.strata.data.MarketData; import com.opengamma.strata.data.scenario.ScenarioArray; import com.opengamma.strata.data.scenario.ScenarioMarketData; import com.opengamma.strata.examples.marketdata.ExampleMarketData; import com.opengamma.strata.examples.marketdata.ExampleMarketDataBuilder; import com.opengamma.strata.market.curve.Curve; import com.opengamma.strata.market.curve.CurveId; import com.opengamma.strata.market.curve.CurveParallelShifts; import com.opengamma.strata.measure.Measures; import com.opengamma.strata.measure.StandardComponents; import com.opengamma.strata.product.Trade; import com.opengamma.strata.product.TradeAttributeType; import com.opengamma.strata.product.TradeInfo; import com.opengamma.strata.product.common.PayReceive; import com.opengamma.strata.product.swap.FixedRateCalculation; import com.opengamma.strata.product.swap.IborRateCalculation; import com.opengamma.strata.product.swap.NotionalSchedule; import com.opengamma.strata.product.swap.PaymentSchedule; import com.opengamma.strata.product.swap.RateCalculationSwapLeg; import com.opengamma.strata.product.swap.Swap; import com.opengamma.strata.product.swap.SwapLeg; import com.opengamma.strata.product.swap.SwapTrade; /** * Example to illustrate using the scenario framework to apply shifts to calibrated curves. * <p> * Two scenarios are run: * <ul> * <li>A base scenario with no perturbations applied to the market data</li> * <li>A scenario with a 1 basis point shift applied to all curves</li> * </ul> * Present value and PV01 are calculated for a single swap. The present value from the second scenario * is compared to the sum of the present value and PV01 from the base scenario. * <p> * This makes use of the example engine and the example market data environment. */ public class CurveScenarioExample { private static final double ONE_BP = 1e-4; /** * Runs the example, pricing the instruments, producing the output as an ASCII table. * * @param args ignored */ public static void main(String[] args) { // setup calculation runner component, which needs life-cycle management // a typical application might use dependency injection to obtain the instance try (CalculationRunner runner = CalculationRunner.ofMultiThreaded()) { calculate(runner); } } // obtains the data and calculates the grid of results private static void calculate(CalculationRunner runner) { // the trade that will have measures calculated List<Trade> trades = ImmutableList.of(createVanillaFixedVsLibor3mSwap()); // the columns, specifying the measures to be calculated List<Column> columns = ImmutableList.of( Column.of(Measures.PRESENT_VALUE), Column.of(Measures.PV01_CALIBRATED_SUM)); // use the built-in example market data ExampleMarketDataBuilder marketDataBuilder = ExampleMarketData.builder(); // the complete set of rules for calculating measures LocalDate valuationDate = LocalDate.of(2014, 1, 22); CalculationFunctions functions = StandardComponents.calculationFunctions(); CalculationRules rules = CalculationRules.of( functions, Currency.USD, marketDataBuilder.ratesLookup(valuationDate)); // mappings that select which market data to apply perturbations to // this applies the perturbations above to all curves PerturbationMapping<Curve> mapping = PerturbationMapping.of( Curve.class, MarketDataFilter.ofIdType(CurveId.class), // no shift for the base scenario, 1bp absolute shift to calibrated curves (zeros) CurveParallelShifts.absolute(0, ONE_BP)); // create a scenario definition containing the single mapping above // this creates two scenarios - one for each perturbation in the mapping ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(mapping); // build a market data snapshot for the valuation date MarketData marketData = marketDataBuilder.buildSnapshot(valuationDate); // the reference data, such as holidays and securities ReferenceData refData = ReferenceData.standard(); // calculate the results MarketDataRequirements reqs = MarketDataRequirements.of(rules, trades, columns, refData); ScenarioMarketData scenarioMarketData = marketDataFactory().createMultiScenario(reqs, MarketDataConfig.empty(), marketData, refData, scenarioDefinition); Results results = runner.calculateMultiScenario(rules, trades, columns, scenarioMarketData, refData); // TODO Replace the results processing below with a report once the reporting framework supports scenarios // The results are lists of currency amounts containing one value for each scenario ScenarioArray<?> pvList = (ScenarioArray<?>) results.get(0, 0).getValue(); ScenarioArray<?> pv01List = (ScenarioArray<?>) results.get(0, 1).getValue(); double pvBase = ((CurrencyAmount) pvList.get(0)).getAmount(); double pvShifted = ((CurrencyAmount) pvList.get(1)).getAmount(); double pv01Base = ((CurrencyAmount) pv01List.get(0)).getAmount(); NumberFormat numberFormat = new DecimalFormat("###,##0.00", new DecimalFormatSymbols(Locale.ENGLISH)); System.out.println(" PV (base) = " + numberFormat.format(pvBase)); System.out.println(" PV (1 bp curve shift) = " + numberFormat.format(pvShifted)); System.out.println("PV01 (algorithmic differentiation) = " + numberFormat.format(pv01Base)); System.out.println(" PV01 (finite difference) = " + numberFormat.format(pvShifted - pvBase)); } //----------------------------------------------------------------------- // create a vanilla fixed vs libor 3m swap private static Trade createVanillaFixedVsLibor3mSwap() { NotionalSchedule notional = NotionalSchedule.of(Currency.USD, 100_000_000); SwapLeg payLeg = RateCalculationSwapLeg.builder() .payReceive(PayReceive.PAY) .accrualSchedule(PeriodicSchedule.builder() .startDate(LocalDate.of(2014, 9, 12)) .endDate(LocalDate.of(2021, 9, 12)) .frequency(Frequency.P6M) .businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.USNY)) .build()) .paymentSchedule(PaymentSchedule.builder() .paymentFrequency(Frequency.P6M) .paymentDateOffset(DaysAdjustment.NONE) .build()) .notionalSchedule(notional) .calculation(FixedRateCalculation.of(0.015, DayCounts.THIRTY_U_360)) .build(); SwapLeg receiveLeg = RateCalculationSwapLeg.builder() .payReceive(PayReceive.RECEIVE) .accrualSchedule(PeriodicSchedule.builder() .startDate(LocalDate.of(2014, 9, 12)) .endDate(LocalDate.of(2021, 9, 12)) .frequency(Frequency.P3M) .businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.USNY)) .build()) .paymentSchedule(PaymentSchedule.builder() .paymentFrequency(Frequency.P3M) .paymentDateOffset(DaysAdjustment.NONE) .build()) .notionalSchedule(notional) .calculation(IborRateCalculation.of(IborIndices.USD_LIBOR_3M)) .build(); return SwapTrade.builder() .product(Swap.of(payLeg, receiveLeg)) .info(TradeInfo.builder() .addAttribute(TradeAttributeType.DESCRIPTION, "Fixed vs Libor 3m") .counterparty(StandardId.of("example", "A")) .settlementDate(LocalDate.of(2014, 9, 12)) .build()) .build(); } }