/** * Copyright (C) 2014 - 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 java.time.LocalDate; import org.joda.beans.ser.JodaBeanSer; import com.opengamma.strata.basics.ReferenceData; import com.opengamma.strata.basics.StandardId; import com.opengamma.strata.basics.currency.Currency; 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.basics.schedule.RollConventions; import com.opengamma.strata.basics.schedule.StubConvention; import com.opengamma.strata.basics.value.ValueAdjustment; import com.opengamma.strata.basics.value.ValueSchedule; import com.opengamma.strata.basics.value.ValueStep; import com.opengamma.strata.product.TradeInfo; import com.opengamma.strata.product.common.PayReceive; import com.opengamma.strata.product.swap.CompoundingMethod; import com.opengamma.strata.product.swap.FixedRateCalculation; import com.opengamma.strata.product.swap.FixingRelativeTo; import com.opengamma.strata.product.swap.IborRateCalculation; import com.opengamma.strata.product.swap.NotionalSchedule; import com.opengamma.strata.product.swap.PaymentRelativeTo; import com.opengamma.strata.product.swap.PaymentSchedule; import com.opengamma.strata.product.swap.RateCalculationSwapLeg; import com.opengamma.strata.product.swap.ResolvedSwapLeg; import com.opengamma.strata.product.swap.Swap; import com.opengamma.strata.product.swap.SwapTrade; /** * Demonstrate use of the API for interest rate swaps. * <p> * This class exists for demonstration purposes to aid with understanding interest rate swaps. * It is not intended to be used in a production environment. */ public class SwapTradeExample { /** * Launch demo, no arguments needed. * * @param args no arguments needed */ public static void main(String[] args) { SwapTradeExample demo = new SwapTradeExample(); demo.fixedSwapLeg(); demo.floatingSwapLeg(); demo.vanillaFixedVsLibor3mSwap(); } //----------------------------------------------------------------------- public void fixedSwapLeg() { // a PeriodicSchedule generates a schedule of accrual periods // - interest is accrued every 3 months from 2014-02-12 to 2014-07-31 // - accrual period dates are adjusted "modified following" using the "GBLO" holiday calendar // - there will be a long initial stub // - the regular accrual period dates will be at the end-of-month PeriodicSchedule accrualSchedule = PeriodicSchedule.builder() .startDate(LocalDate.of(2014, 2, 12)) .endDate(LocalDate.of(2016, 7, 31)) .businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.GBLO)) .frequency(Frequency.P3M) .stubConvention(StubConvention.LONG_INITIAL) .rollConvention(RollConventions.EOM) .build(); // a PaymentSchedule generates a schedule of payment periods, based on the accrual schedule // - payments are every 6 months // - payments are 2 business days after the end of the period // - straight compounding is used (the payments are less frequent than the accrual, so compounding occurs) PaymentSchedule paymentSchedule = PaymentSchedule.builder() .paymentFrequency(Frequency.P6M) .paymentRelativeTo(PaymentRelativeTo.PERIOD_END) .paymentDateOffset(DaysAdjustment.ofBusinessDays(2, HolidayCalendarIds.GBLO)) .compoundingMethod(CompoundingMethod.STRAIGHT) .build(); // a NotionalSchedule generates a schedule of notional amounts, based on the payment schedule // - in this simple case the notional is 1 million GBP and does not change NotionalSchedule notionalSchedule = NotionalSchedule.of(Currency.GBP, 1_000_000); // a RateCalculationSwapLeg can represent a fixed or floating swap leg // - a FixedRateCalculation is used to represent a fixed rate // - the "Act/Act ISDA" day count is used // - the rate starts at 0.8% and reduces to 0.7% RateCalculationSwapLeg swapLeg = RateCalculationSwapLeg.builder() .payReceive(PayReceive.PAY) .accrualSchedule(accrualSchedule) .paymentSchedule(paymentSchedule) .notionalSchedule(notionalSchedule) .calculation(FixedRateCalculation.builder() .dayCount(DayCounts.ACT_ACT_ISDA) .rate(ValueSchedule.of( 0.008, ValueStep.of(LocalDate.of(2015, 1, 31), ValueAdjustment.ofReplace(0.007)))) .build()) .build(); // a ResolvedSwapLeg has all the dates of the cash flows // it remains valid so long as the holiday calendar does not change ResolvedSwapLeg resolvedLeg = swapLeg.resolve(ReferenceData.standard()); System.out.println("===== Fixed ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(swapLeg)); System.out.println(); System.out.println("===== Fixed resolved ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(resolvedLeg)); System.out.println(); } //----------------------------------------------------------------------- public void floatingSwapLeg() { // a PeriodicSchedule generates a schedule of accrual periods // - interest is accrued every 6 months from 2014-02-12 to 2014-07-31 // - accrual period dates are adjusted "modified following" using the "GBLO" holiday calendar // - there will be a long initial stub // - the regular accrual period dates will be at the end-of-month PeriodicSchedule accrualSchedule = PeriodicSchedule.builder() .startDate(LocalDate.of(2014, 2, 12)) .endDate(LocalDate.of(2016, 7, 31)) .businessDayAdjustment(BusinessDayAdjustment.of(MODIFIED_FOLLOWING, HolidayCalendarIds.GBLO)) .frequency(Frequency.P6M) .stubConvention(StubConvention.LONG_INITIAL) .rollConvention(RollConventions.EOM) .build(); // a PaymentSchedule generates a schedule of payment periods, based on the accrual schedule // - payments are every 6 months // - payments are 2 business days after the end of the period // - no compounding is needed as the payment schedule matches the accrual schedule PaymentSchedule paymentSchedule = PaymentSchedule.builder() .paymentFrequency(Frequency.P6M) .paymentRelativeTo(PaymentRelativeTo.PERIOD_END) .paymentDateOffset(DaysAdjustment.ofBusinessDays(2, HolidayCalendarIds.GBLO)) .build(); // a NotionalSchedule generates a schedule of notional amounts, based on the payment schedule // - in this simple case the notional is 1 million GBP and does not change NotionalSchedule notionalSchedule = NotionalSchedule.of(Currency.GBP, 1_000_000); // a RateCalculationSwapLeg can represent a fixed or floating swap leg // - an IborRateCalculation is used to represent a floating Ibor rate // - the "Act/Act ISDA" day count is used // - the index is GBP LIBOR 6M // - fixing is 2 days before the start of the period using the "GBLO" holiday calendar RateCalculationSwapLeg swapLeg = RateCalculationSwapLeg.builder() .payReceive(PayReceive.RECEIVE) .accrualSchedule(accrualSchedule) .paymentSchedule(paymentSchedule) .notionalSchedule(notionalSchedule) .calculation(IborRateCalculation.builder() .dayCount(DayCounts.ACT_ACT_ISDA) .index(IborIndices.GBP_LIBOR_6M) .fixingRelativeTo(FixingRelativeTo.PERIOD_START) .fixingDateOffset(DaysAdjustment.ofBusinessDays(-2, HolidayCalendarIds.GBLO)) .build()) .build(); // a ResolvedSwapLeg has all the dates of the cash flows // it remains valid so long as the holiday calendar does not change ResolvedSwapLeg resolvedLeg = swapLeg.resolve(ReferenceData.standard()); System.out.println("===== Floating ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(swapLeg)); System.out.println(); System.out.println("===== Floating resolved ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(resolvedLeg)); System.out.println(); } //----------------------------------------------------------------------- public void vanillaFixedVsLibor3mSwap() { // we are paying a fixed rate every 3 months at 1.5% with a 100 million notional RateCalculationSwapLeg payLeg = RateCalculationSwapLeg.builder() .payReceive(PayReceive.PAY) .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)) .startDateBusinessDayAdjustment(BusinessDayAdjustment.NONE) .build()) .paymentSchedule(PaymentSchedule.builder() .paymentFrequency(Frequency.P3M) .paymentDateOffset(DaysAdjustment.NONE) .build()) .notionalSchedule(NotionalSchedule.builder() .currency(Currency.USD) .amount(ValueSchedule.of(100_000_000)) .build()) .calculation(FixedRateCalculation.of(0.015, DayCounts.THIRTY_U_360)) .build(); // we are receiving USD LIBOR 3M every 3 months with a 100 million notional RateCalculationSwapLeg 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)) .startDateBusinessDayAdjustment(BusinessDayAdjustment.NONE) .build()) .paymentSchedule(PaymentSchedule.builder() .paymentFrequency(Frequency.P3M) .paymentDateOffset(DaysAdjustment.NONE) .build()) .notionalSchedule(NotionalSchedule.builder() .currency(Currency.USD) .amount(ValueSchedule.of(100_000_000)) .build()) .calculation(IborRateCalculation.of(IborIndices.USD_LIBOR_3M)) .build(); // a SwapTrade combines the two legs SwapTrade trade = SwapTrade.builder() .info(TradeInfo.builder() .id(StandardId.of("OG-Trade", "1")) .tradeDate(LocalDate.of(2014, 9, 10)) .build()) .product(Swap.of(payLeg, receiveLeg)) .build(); System.out.println("===== Vanilla fixed vs Libor3m ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(trade)); System.out.println(); System.out.println("===== Vanilla fixed vs Libor3m pay leg ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(payLeg.resolve(ReferenceData.standard()))); System.out.println(); System.out.println("===== Vanilla fixed vs Libor3m receive leg ====="); System.out.println(JodaBeanSer.PRETTY.xmlWriter().write(receiveLeg.resolve(ReferenceData.standard()))); System.out.println(); } }