How To Use the Swap Extension Package

To follow the script used in this tutorial, you can clone the Daml Finance repository. In particular, the Swap test folder src/test/daml/Daml/Finance/Instrument/Swap/Test/ is the starting point of this tutorial.

Prerequisites

The Swap extension has many similarities with the Bond extension. This tutorial builds on the Bond Tutorial. Please check it out before reading the Swap specifics below.

How To Create a Swap Instrument

There are different types of swaps, which differ both in the way regular payments are defined and whether notional is exchanged. In order to create a swap instrument, you first have to decide what type of swap you need. The swap extension package currently supports the following types of swaps:

Interest Rate

Interest rate swap is the type of swap that shares most similarities with a bond. It has two legs: one which pays a fix rate and another one which pays a floating rate. These rates are paid at the end of every payment period.

As an example, we will create a swap instrument paying Libor 3M vs a 2.01% p.a. with a 3M payment period. This example is taken from src/test/daml/Daml/Finance/Instrument/Swap/Test/InterestRate.daml, where all the details are available.

We start by defining the terms:

  let
    issueDate = date 2019 Jan 16
    firstPaymentDate = date 2019 Feb 15
    maturityDate = date 2019 May 15
    referenceRateId = "USD/LIBOR/3M"
    ownerReceivesFix = False
    fixRate = 0.0201
    paymentPeriod = M
    paymentPeriodMultiplier = 3
    dayCountConvention = Act360
    businessDayConvention = ModifiedFollowing

The floating leg depends on a reference rate, which is defined by the referenceRateId variable. The value of the reference rate is observed at the beginning of each payment period.

The ownerReceivesFix variable is used to specify whether a holding owner of this instrument receives the fix or the floating leg. This is not needed for bonds, because the regular payments are always in one direction (from the issuer to the holder). However, in the case of a swap with two counterparties A and B, we need the ownerReceivesFix variable to specify who receives fix and who receives floating. In this example, the holding owner receives the floating leg.

Just as for bonds, we can use these variables to create a PeriodicSchedule:

    let
      (y, m, d) = toGregorian firstCouponDate
      periodicSchedule = PeriodicSchedule with
        businessDayAdjustment =
          BusinessDayAdjustment with
            calendarIds = holidayCalendarIds
            convention = businessDayConvention
        effectiveDateBusinessDayAdjustment = None
        terminationDateBusinessDayAdjustment = None
        frequency =
          Periodic Frequency with
            rollConvention = DOM d
            period = Period with
              period = couponPeriod
              periodMultiplier = couponPeriodMultiplier
        effectiveDate = issueDate
        firstRegularPeriodStartDate = Some firstCouponDate
        lastRegularPeriodEndDate = Some maturityDate
        stubPeriodType = None
        terminationDate = maturityDate

Note that this instrument only has one periodic schedule, which is used for both the fixed and the floating leg. It is also used for both the calculation period (to determine which floating rate to be used) and the payment period (to determine when payments are done). The FpML swap template below offers more flexibility here. It has individual schedules, both for the fixed/floating leg and for the calculation/payment periods. That would allow you to specify whether payments should be made e.g. after each calculation period or only after every second calculation period.

Now that we have defined the terms we can create the swap instrument:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd InterestRateSwap.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; periodicSchedule; holidayCalendarIds
        calendarDataProvider; dayCountConvention; ownerReceivesFix; fixRate; referenceRateId; currency

Once this is done, you can create a holding on it using Account.credit. The owner of the holding receives the floating leg (and pays the fix leg).

Currency

Currency swaps are quite similar to interest rate swaps, except that the two legs are in different currencies. Consequently, we need to create two cash instruments:

  cashInstrumentCid <- originate custodian issuer "USD" "US Dollars" observers now
  foreignCashInstrumentCid <- originate custodian issuer "EUR" "Euro" observers now

In the swap template they are referred to as base currency and foreign currency.

Here is an example of a fix vs fix currency swap: 3% p.a. in USD vs 2% p.a. in EUR with payments every 3M:

  let
    issueDate = date 2019 Jan 16
    firstPaymentDate = date 2019 Feb 15
    maturityDate = date 2019 May 15
    ownerReceivesBase = False
    baseRate = 0.03
    foreignRate = 0.02
    fxRate = 1.1
    paymentPeriod = M
    paymentPeriodMultiplier = 3
    dayCountConvention = Act360
    businessDayConvention = ModifiedFollowing

In this example, the holding owner receives the foreign currency leg.

In order to calculate the interest rate payments, a notional is required in each currency. The quantity of the holding refers to the notional of the base currency. The notional of the foreign currency is defined as the quantity of the holding multiplied by the specified fxRate.

Note that this template is limited to fixed rates. It also does not support exchange of notionals. If you need floating rates or exchange of notionals, please use the FpML swap template below. It supports both of those features.

Here is how we create the currency swap instrument, using the two currencies defined above:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd CurrencySwap.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; periodicSchedule; holidayCalendarIds
        calendarDataProvider; dayCountConvention; ownerReceivesBase; baseRate; foreignRate
        baseCurrency; foreignCurrency; fxRate

Once the instrument is created, you can create a holding on it. In our example, it the owner of the holding receives the foreign currency leg (and pays the base currency leg).

Foreign Exchange

Despite the similarities in name, foreign exchange swaps (or FX swaps) are quite different from currency swaps. An FX swap does not pay or receive interest. Instead, the two legs define an initial FX transaction and a final FX transaction. Each transaction requires an FX rate and a transaction date, which are predetermined between the counterparties.

The FX transactions involve two currencies. In the swap template these are referred to as base currency and foreign currency. The convention is that the holding owner receives the foreign currency in the initial transaction (and pays it in the final transaction).

Here is an example of an USD vs EUR FX swap. First, we define the two cash instruments:

  cashInstrumentCid <- originate custodian issuer "USD" "US Dollars" observers now
  foreignCashInstrumentCid <- originate custodian issuer "EUR" "Euro" observers now

Then, we define the transaction dates and FX rates:

  let
    issueDate = date 2019 Jan 16
    firstPaymentDate = date 2019 Feb 15
    maturityDate = date 2019 May 15
    firstFxRate = 1.1
    finalFxRate = 1.2

The firstPaymentDate variable defines the date of the initial FX transaction. Generally, this is on the issue date or shortly afterwards.

Finally, we create the FX swap instrument:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd ForeignExchange.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; firstFxRate; finalFxRate; issueDate
        firstPaymentDate; maturityDate, baseCurrency; foreignCurrency

Once the instrument is created, you can create a holding on it. The owner of the holding receives the foreign currency in the initial transaction. In the final transaction the sides are reversed.

Credit Default

A credit default swap (CDS) pays a protection amount in case of a credit default event, in exchange for a fix rate at the end of every payment period. The protection amount is defined as 1-recoveryRate. The recoveryRate is defined as the amount recovered when a borrower defaults, expressed as a percentage of notional.

If a credit event occurs, the swap expires after the protection amount has been paid, i.e., no more rate payments are required afterwards.

Here is an example of a CDS that pays 1-recoveryRate in the case of a default on TSLA bonds:

issueDate = date 2019 Jan 16
firstPaymentDate = date 2019 Feb 15
maturityDate = date 2019 May 15
defaultProbabilityReferenceId = "TSLA-DEFAULT-PROB"
recoveryRateReferenceId = "TSLA-RECOVERY-RATE"
ownerReceivesFix = False
fixRate = 0.0201
paymentPeriod = M
paymentPeriodMultiplier = 3
dayCountConvention = Act360
businessDayConvention = ModifiedFollowing

In our example, the issuer pays the protection leg of the swap.

As you can see in this example, two observables are required for a CDS:

  1. defaultProbabilityReferenceId: The reference ID of the default probability observable. For example, in case of protection against a “TSLA bond payment default” this should be a valid reference to the “TSLA default probability”.
  2. recoveryRateReferenceId: The reference ID of the recovery rate observable. For example, in case of a “TSLA bond payment default with a 60% recovery rate” this should be a valid reference to the “TSLA bond recovery rate”.

Finally, we create the CDS instrument:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd CreditDefaultSwap.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; periodicSchedule; holidayCalendarIds
        calendarDataProvider; dayCountConvention; ownerReceivesFix; fixRate
        defaultProbabilityReferenceId; recoveryRateReferenceId; currency

Once the instrument is created, you can create a holding on it. In our example, the owner of the holding receives the protection leg (and pays the fix leg).

Asset

An asset swap is a general type of swap with two legs: one which pays a fix rate and another one which pays the performance of an asset. It can be used to model:

  • equity swaps
  • some types of commodity swaps (of the form performance vs rate)
  • other swaps with the same payoff on other asset types.

Here is an example of an asset swap that pays AAPL total return vs 2.01% fix p.a., payment every 3M:

  let
    issueDate = date 2019 Jan 16
    firstPaymentDate = date 2019 Feb 15
    maturityDate = date 2019 May 15
    referenceAssetId = "AAPL-CLOSE-ADJ"
    ownerReceivesFix = False
    fixRate = 0.0201
    paymentPeriod = M
    paymentPeriodMultiplier = 3
    dayCountConvention = Act360
    businessDayConvention = ModifiedFollowing

In our example, the issuer pays the asset leg of the swap.

One observable is required: referenceAssetId. The template calculates the performance for each payment period using this observable. Performance is calculated from the start date to the end date of each payment period. The reference asset Observable needs to contain the appropriate type of fixings:

  • unadjusted fixings in case of a price return asset swap
  • adjusted fixings in case of a total return asset swap

Finally, we create the asset swap instrument:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd AssetSwap.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; periodicSchedule; holidayCalendarIds
        calendarDataProvider; dayCountConvention; ownerReceivesFix; fixRate; referenceAssetId; currency

Once this is done, you can create a holding on it. The owner of the holding receives the asset leg (and pays the fix leg).

FpML

Unlike the other swap types above, the FpML swap template is not a new type of payoff. Instead, it allows you to input other types of swaps using the FpML schema. Currently, interest rate swaps and currency swaps are supported. The template can quite easily be extended to FX swaps.

Specifically, it allows you to specify one swapStream object for each leg of the swap.

We start by defining the general terms:

  let
    issueDate = date 2022 Sep 14
    firstRegularPeriodDate = date 2022 Sep 20
    lastRegularPeriodDate = date 2023 Jun 20
    maturityDate = date 2023 Sep 14
    firstRegularPeriodDateFixLeg = date 2022 Sep 20
    lastRegularPeriodDateFixLeg = firstRegularPeriodDateFixLeg
    referenceRateId = "USD/LIBOR/3M"
    referenceRateOneMonthId = "USD/LIBOR/1M"
    fixRate = 0.02
    paymentPeriod = Regular M
    paymentPeriodMultiplier = 3
    dayCountConvention = Act360
    businessDayConvention = ModifiedFollowing
    issuerPartyRef = "Counterparty"
    clientPartyRef = "ExecutingParty"

The issuerPartyRef and the clientPartyRef variables are used to specify who pays each leg (see payerPartyReference below).

The fixed leg of the swap can now be defined using Daml data types that correspond to the swapStream schema:

    swapStreamFixedLeg = SwapStream with
      payerPartyReference = clientPartyRef
      receiverPartyReference = issuerPartyRef
      calculationPeriodDates = CalculationPeriodDates with
        id = "fixedLegCalcPeriodDates"
        effectiveDate = AdjustableDate with
          unadjustedDate = issueDate
          dateAdjustments = BusinessDayAdjustments with
            businessDayConvention = NoAdjustment
            businessCenters = []
        terminationDate = AdjustableDate with
          unadjustedDate = maturityDate
          dateAdjustments = BusinessDayAdjustments with
            businessDayConvention = ModifiedFollowing
            businessCenters = holidayCalendarId
        calculationPeriodDatesAdjustments = CalculationPeriodDatesAdjustments with
          businessDayConvention = ModifiedFollowing
          businessCenters = holidayCalendarId
        firstPeriodStartDate = None
        firstRegularPeriodStartDate = Some firstRegularPeriodDateFixLeg
        lastRegularPeriodEndDate = Some lastRegularPeriodDateFixLeg
        calculationPeriodFrequency = CalculationPeriodFrequency with
          periodMultiplier = 1
          period = Regular Y
          rollConvention = DOM 20
      paymentDates = PaymentDates with
        calculationPeriodDatesReference = "fixedLegCalcPeriodDates"
        paymentFrequency = PaymentFrequency with
          periodMultiplier = 1
          period = Regular Y
        firstPaymentDate = Some firstRegularPeriodDateFixLeg
        lastRegularPaymentDate = Some lastRegularPeriodDateFixLeg
        payRelativeTo = CalculationPeriodEndDate
        paymentDatesAdjustments = BusinessDayAdjustments with
            businessDayConvention = ModifiedFollowing
            businessCenters = holidayCalendarId
        paymentDaysOffset = None
      resetDates = None
      calculationPeriodAmount = CalculationPeriodAmount with
        calculation = Calculation with
          notionalScheduleValue = NotionalSchedule_Regular NotionalSchedule with
            id = "fixedLegNotionalSchedule"
            notionalStepSchedule = NotionalStepSchedule with
              initialValue = 1000000.0
              step = []
              currency = "USD"
          rateTypeValue = RateType_Fixed FixedRateSchedule with
            initialValue = fixRate
            step = []
          dayCountFraction = dayCountConvention
          compoundingMethodEnum = None
      stubCalculationPeriodAmount = None
      principalExchanges = None

As you can see, the Daml SwapStream data type matches the swapStream FpML schema. Please note that the actual parsing from FpML to Daml is not done by this template. It has to be implemented on the client side.

Similarly, the floating leg of the swap is defined like this:

    swapStreamFloatingLeg = SwapStream with
      payerPartyReference = issuerPartyRef
      receiverPartyReference = clientPartyRef
      calculationPeriodDates = CalculationPeriodDates with
        id = "floatingLegCalcPeriodDates"
        effectiveDate = AdjustableDate with
          unadjustedDate = issueDate
          dateAdjustments = BusinessDayAdjustments with
            businessDayConvention = NoAdjustment
            businessCenters = []
        terminationDate = AdjustableDate with
          unadjustedDate = maturityDate
          dateAdjustments = BusinessDayAdjustments with
            businessDayConvention = ModifiedFollowing
            businessCenters = holidayCalendarId
        calculationPeriodDatesAdjustments = CalculationPeriodDatesAdjustments with
          businessDayConvention = ModifiedFollowing
          businessCenters = holidayCalendarId
        firstPeriodStartDate = None
        firstRegularPeriodStartDate = Some firstRegularPeriodDate
        lastRegularPeriodEndDate = Some lastRegularPeriodDate
        calculationPeriodFrequency = CalculationPeriodFrequency with
          periodMultiplier = paymentPeriodMultiplier
          period = paymentPeriod
          rollConvention = DOM 20
      paymentDates = PaymentDates with
        calculationPeriodDatesReference = "floatingLegCalcPeriodDates"
        paymentFrequency = PaymentFrequency with
          periodMultiplier = paymentPeriodMultiplier
          period = paymentPeriod
        firstPaymentDate = Some firstRegularPeriodDate
        lastRegularPaymentDate = Some lastRegularPeriodDate
        payRelativeTo = CalculationPeriodEndDate
        paymentDatesAdjustments = BusinessDayAdjustments with
            businessDayConvention = ModifiedFollowing
            businessCenters = holidayCalendarId
        paymentDaysOffset = None
      resetDates = Some ResetDates with
        calculationPeriodDatesReference = "floatingLegCalcPeriodDates"
        resetRelativeTo = CalculationPeriodStartDate
        fixingDates = FixingDates with
          periodMultiplier = -2
          period = D
          dayType = Some Business
          businessDayConvention = NoAdjustment
          businessCenters = fixingHolidayCalendarId
        resetFrequency = ResetFrequency with
          periodMultiplier = paymentPeriodMultiplier
          period = paymentPeriod
        resetDatesAdjustments = ResetDatesAdjustments with
          businessDayConvention = ModifiedFollowing
          businessCenters = holidayCalendarId
      calculationPeriodAmount = CalculationPeriodAmount with
        calculation = Calculation with
          notionalScheduleValue = NotionalSchedule_Regular NotionalSchedule with
            id = "floatingLegNotionalSchedule"
            notionalStepSchedule = NotionalStepSchedule with
              initialValue = 1000000.0
              step = []
              currency = "USD"
          rateTypeValue = RateType_Floating FloatingRateCalculation with
            floatingRateIndex = referenceRateId
            indexTenor = Some Period with
              periodMultiplier = paymentPeriodMultiplier
              period = M
            spreadSchedule = [SpreadSchedule with initialValue = 0.005]
            finalRateRounding = None
          dayCountFraction = dayCountConvention
          compoundingMethodEnum = Some Straight
      stubCalculationPeriodAmount = Some StubCalculationPeriodAmount with
        calculationPeriodDatesReference = "floatingLegCalcPeriodDates"
        initialStub = Some $ StubValue_StubRate 0.015
        finalStub = Some $ StubValue_FloatingRate
          [ StubFloatingRate with
              floatingRateIndex=referenceRateOneMonthId
              indexTenor = Some (Period with period = M; periodMultiplier = 1)
          , StubFloatingRate with
              floatingRateIndex = referenceRateId
              indexTenor = Some (Period with period = M; periodMultiplier = 3)
          ]
      principalExchanges = None

There are three main ways to define which interest rate should be used for a stub period. They are all included in the fix or floating leg above, either in the inital or in the final stub period. In short, it depends on the content of StubCalculationPeriodAmount:

  1. None: No special stub rate is provided. Instead, use the same rate as was specified in the corresponding Calculation.
  2. Specific stubRate: Use this specific fix rate.
  3. Specific floatingRate: Use this specific floating rate (if one rate is provided). If two rates are provided: use linear interpolation between the two rates.

Finally, we create the FpML swap instrument:

    cid <- toInterfaceContractId <$> submitMulti [depository, issuer] [] do
      createCmd FpmlSwap.Instrument with
        depository; issuer; id = Id label; version = "0"; description
        observers = M.fromList observers; lastEventTimestamp; swapStreams; issuerPartyRef
        calendarDataProvider; currencies

Once this is done, you can create a holding on it. In this particular example trade, the notional is specified in the FpML instrument. This means that you would only book a unit holding (quantity=1.0) on the instrument.

Frequently Asked Questions

Why do the swaps have an issuer?

In the case of bonds, the instrument has a well-defined issuer. This is not necessarily the case for swaps, where two counterparties A and B swap the payments associated with each leg. However, in practice one of the counterparties is often a swap dealer, who shares some of the characteristics of a bond issuer. For the purpose of lifecycling in Daml Finance, we require one of the counterparties to take the role as issuer. This counterparty will serve as calculation agent and provide the observables required to calculate the swap payments.

The documentation of the Daml Finance asset model contains an OTC swap example.