How to use the Swap Instrument package¶
To follow the code snippets used in this page in Daml Studio, you can clone the Daml Finance repository and run the scripts included in the Instrument/Swap/Test/ folder.
Prerequisites¶
Swap instruments share many similarities with Bond instruments. This page builds on the page for the Bond Instruments. 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 instrument 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 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"
floatingRate = FloatingRate with
referenceRateId
referenceRateType = SingleFixing CalculationPeriodStartDate
fixingDates = DateOffset with
periodMultiplier = 0
period = D
dayType = Some Business
businessDayConvention = NoAdjustment
businessCenters = ["USD"]
ownerReceivesFix = False
fixRate = 0.0201
paymentPeriod = M
paymentPeriodMultiplier = 3
dayCountConvention = Act360
businessDayConvention = ModifiedFollowing
The floating leg depends on a reference rate, which is specified in the FloatingRate data structure. It supports two types of reference rates, which are configurable using the ReferenceRateTypeEnum:
- Libor/Euribor style rates with a single fixing
- SOFR style reference rates (using a compounded index)
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
For more information on calendar, schedule, and day count functions, see the date utility functions tutorial.
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:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd interestRateSwapFactoryCid InterestRateSwapFactory.Create with
interestRate = InterestRate with
instrument
description
periodicSchedule
holidayCalendarIds
calendarDataProvider
dayCountConvention
floatingRate
fixRate
ownerReceivesFix
currency
lastEventTimestamp
observers = fromList observers
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:
cashInstrument <- originate custodian issuer "USD" TransferableFungible "US Dollars" observers now
foreignCashInstrument <- originate custodian issuer "EUR" TransferableFungible "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:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd currencySwapFactoryCid CurrencySwapFactory.Create with
currencySwap = CurrencySwap with
instrument
description
periodicSchedule
holidayCalendarIds
calendarDataProvider
dayCountConvention
ownerReceivesBase
baseRate
foreignRate
baseCurrency
foreignCurrency
fxRate
lastEventTimestamp
observers = fromList observers
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:
cashInstrument <- originate custodian issuer "USD" TransferableFungible "US Dollars" observers now
foreignCashInstrument <- originate custodian issuer "EUR" TransferableFungible "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:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd foreignExchangeSwapFactoryCid ForeignExchangeSwapFactory.Create with
foreignExchange = ForeignExchange with
instrument
description
baseCurrency
foreignCurrency
firstFxRate
finalFxRate
issueDate
firstPaymentDate
maturityDate
lastEventTimestamp
observers = fromList observers
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:
- 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”.
- 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:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd creditDefaultSwapFactoryCid CreditDefaultSwapFactory.Create with
creditDefault = CreditDefault with
instrument
description
periodicSchedule
holidayCalendarIds
calendarDataProvider
dayCountConvention
fixRate
ownerReceivesFix
defaultProbabilityReferenceId
recoveryRateReferenceId
currency
lastEventTimestamp
observers = fromList observers
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 an interest rate and another one which pays the performance of an asset (or a basket of assets). 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
floatingRate = None
fixRate = 0.0201
paymentPeriod = M
paymentPeriodMultiplier = 3
dayCountConvention = Act360
businessDayConvention = ModifiedFollowing
In our example, the issuer pays the asset leg of the swap.
Finally, we create the asset swap instrument:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd assetSwapFactoryCid AssetSwapFactory.Create with
asset = Asset with
instrument
description
periodicSchedule
holidayCalendarIds
calendarDataProvider
dayCountConvention
floatingRate
fixRate
ownerReceivesRate
underlyings
currency
lastEventTimestamp
observers = fromList observers
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).
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
There is one exception to this: a total return asset swap with dividend passthrough. In this case, you can use unadjusted fixings for the instrument and lifecycle the dividend event separately using a dedicated asset swap DistributionRule:
-- Lifecycle the asset swap to create an effect for the dividend.
(Some swapInstrumentAfterDividend, [effectCid]) <- submitMulti [issuer] [publicParty] do
exerciseCmd distributionRuleCid Lifecycle.Evolve with
observableCids = []
eventCid = aaplDividendDistributionEventCid
instrument = swapInstrumentAfterFirstPayment
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 = holidayCalendarIds
calculationPeriodDatesAdjustments = CalculationPeriodDatesAdjustments with
businessDayConvention = ModifiedFollowing
businessCenters = holidayCalendarIds
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 = holidayCalendarIds
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 = holidayCalendarIds
calculationPeriodDatesAdjustments = CalculationPeriodDatesAdjustments with
businessDayConvention = ModifiedFollowing
businessCenters = holidayCalendarIds
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 = holidayCalendarIds
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 = holidayCalendarIds
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:
- None: No special stub rate is provided. Instead, use the same rate as was specified in the corresponding Calculation.
- Specific stubRate: Use this specific fix rate.
- 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:
let
instrument = InstrumentKey with
issuer
depository
id = Id label
version = "0"
holdingStandard
cid <- submitMulti [issuer] [publicParty] do
exerciseCmd fpmlSwapFactoryCid FpmlSwapFactory.Create with
fpml = Fpml with
instrument
description
swapStreams
issuerPartyRef
currencies
calendarDataProvider
lastEventTimestamp
observers = fromList observers
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.