How to use the Generic Instrument packages¶
The Generic Instrument provides a flexible framework to model and lifecycle custom payoffs in Daml Finance. It encapsulates the Contingent Claims library, which gives us the tools to model the economic terms of the payoff.
To follow the code snippets used in this page in Daml Studio, you can clone the Daml Finance repository and run the scripts in the Instrument/Generic/Test/Intermediated/BondCoupon.daml and Instrument/Generic/Test/EuropeanOption.daml files.
The Generic Instrument and the Contingent Claims library are introduced in the Payoff Modeling tutorial, which we encourage you to check out.
How to create a Generic Instrument¶
Define the Claim of a Bond¶
Consider a fixed rate bond which pays a 4% coupon per annum with a coupon period of 6 months. Assume that there are two coupons remaining until maturity: one to be paid today and one to be paid in 180 days. This can be modeled in the following way:
let
today = toDateUTC now
expiry = addDays today 180
bondLabel = "ABC.DE 4% p.a. " <> show expiry <> " Corp"
claims = mapClaimToUTCTime $ andList
[ when (at today) $ scale (Const 0.02) $ one cashInstrument
, when (at expiry) $ scale (Const 0.02) $ one cashInstrument
, when (at expiry) $ scale (Const 1.0) $ one cashInstrument
]
Now that we have specified the economic terms of the payoff we can create a generic instrument:
instrument <- originateGeneric csd issuer bondLabel "Bond" now claims pp now
On every coupon payment date, the issuer will need to lifecycle the instrument. This will result in a lifecycle effect for the coupon, which can be then be claimed and settled. This process is described in detail in Getting Started: Lifecycling.
Define the Claim of a European Option¶
Alternatively, if you want to model a European Option instead, you can define the claim as follows
let
exerciseClaim = scale (Observe spot - Const strike) $ one ccy
option = european maturity exerciseClaim
This uses the european builder function, which is included in Contingent Claims.
Compared to the bond, where the passage of time results in a coupon payment being due, the option instrument requires a manual Election: the holder of the instrument holding needs to choose whether or not to exercise the option. How this is done is described in the next section.
How to lifecycle a Generic Instrument¶
Election based lifecycling of Contingent Claims based instruments¶
We describe how to lifecycle an option instrument (which can be Exercised or Expired), but the same concepts apply to other Election based instruments (for example, a callable bond that can be Called or NotCalled). A similar workflow is also applicable to some of the instruments available in the Bond, Swap, and Option packages.
First, an Election factory is created:
-- Create election offers to allow holders to create elections
electionFactoryCid <- submit broker do
toInterfaceContractId <$> createCmd Election.Factory with
provider = broker
observers = M.fromList pp
Then, election offers are created for the different election choices that are available. Specifically, for option instruments, an election offer to exercise is created:
exerciseElectionFactoryCid <- submit broker do
createCmd ElectionOffer with
provider = broker
id = Id "EXERCISE"
description = "OPTION-AAPL - Exercise"
claim = "EXERCISE"
observers = S.singleton publicParty
instrument = genericInstrument
factoryCid = electionFactoryCid
Similarly, an election offer to expire the option is also created:
expireElectionFactoryCid <- submit broker do
createCmd ElectionOffer with
provider = broker
id = Id "EXPIRE"
description = "OPTION-AAPL - Expire"
claim = "EXPIRE"
observers = S.singleton publicParty
instrument = genericInstrument
factoryCid = electionFactoryCid
Assuming the investor wants to exercise the option, an election candidate contract is created. In order to do this, the investor presents a holding for which an election should be made, and also specifies the amount that this election applies to. This amount cannot exceed the quantity of the holding:
-- One cannot exercise for more units than they own
submitMultiMustFail [investor1] [publicParty] do
exerciseCmd exerciseElectionFactoryCid CreateElectionCandidate with
elector = investor1
electionTime = dateToDateClockTime maturity
holdingCid = investor1GenericHoldingCid
amount = 5000.0
Instead, the elected amount must be the same as the holding quantity, or lower:
-- Create election
exerciseOptionProposalCid <- submitMulti [investor1] [publicParty] do
exerciseCmd exerciseElectionFactoryCid CreateElectionCandidate with
elector = investor1
electionTime = dateToDateClockTime maturity
holdingCid = investor1GenericHoldingCid
amount = 500.0
A time event is also required to indicate when the election is made:
currentTimeCid <- createDateClock (S.singleton broker) maturity S.empty
It is now possible to create the Election:
exerciseOptionCid <- submit broker do
exerciseCmd exerciseOptionProposalCid ValidateElectionCandidate with
currentTimeCid
Note: these templates (election offer and election candidate) are not considered a core part of the Daml Finance library. There can be different processes to create the Election, so this is rather application specific. Still, in order to showcase one way how this could be done, this workflow is included here for convenience.
The Election has a flag electorIsOwner, which indicates whether the election is on behalf of the owner of the holding. This is typically the case for options, where the option holder has the right, but not the obligation, to exercise the option. On the other hand, for callable bonds it is not the holding owner (the bond holder) who gets to decide whether the bond is redeemed early. Instead, it is the counterparty. In this case, electorIsOwner would be false.
A lifecycle rule is required to specify how to process the Election:
-- Apply election to generate new instrument version + effects
lifecycleRuleCid <- toInterfaceContractId <$> submit bank do
createCmd Lifecycle.Rule with
providers = S.singleton bank
observers= M.empty
lifecycler = broker
id = Id "LifecycleRule"
description = "Rule to lifecycle a generic instrument"
This is similar to time-based lifecycling.
Finally, it is possible to apply the Election according to the lifecycle rule provided:
(Some exercisedOption, [effectCid]) <- submit broker do
exerciseCmd exerciseOptionCid Election.Apply with
observableCids = [observableCid]
exercisableCid = lifecycleRuleCid
This creates lifecycle effects, which can be claimed and settled in the usual way (as described in Getting Started: Lifecycling). However, the holding contract used to claim the effect must be compatible with the election that has been made: if Alice made an election and electorIsOwner = True, then only a holding where owner = alice will be accepted.