digital-asset / daml-finance

Apache License 2.0
17 stars 16 forks source link

Inconsistent return value from HasClaims -> getClaims #99

Open GeorgSchneider opened 1 year ago

GeorgSchneider commented 1 year ago

I can see a few use cases where claims-based utilities work on the current state of the claims tree. For example, to forecast future cashflows, to classify the current instrument state ("is it still a barrier option, or has it knocked in and can be considered a vanilla option now"), or for pricing and risk calculations. Additionally, we currently have inconsistent behaviour in that the derivative instrument always returns the current tree (and never could return the initial one), and the bond instruments do the opposite. For these reasons I think getClaims on HasClaims (or the to-be-added choice that calls it) needs to always return the current state of the tree, instead of the initial tree as is currently done for bonds.

In order to do that we have several options:

  1. Store past events, elections, and observables on the instrument so we can replay them to get to the current state: this can have a large space requirement and likely undoes the storage benefits from not storing the tree explicitly.
  2. Take events, elections, and observables as input to the getClaims function (resp. the wrapping choice): this seems most practical, the only caveat is that for generic derivative instruments those are actually not required (but we can just pass empty arrays in this case)
  3. Implement custom "aging" logic on the bond instrument itself (which we had there previously), which would use the lastEventTimestamp to remove past coupons from the list, and only generate the tree from what's remaining: this has the downside of duplicating "aging" logic that we have in contingent-claims and we'd somehow need to ensure (ie. test) that the implementations match and always arrive at the same tree (difficult to test for all cases). Furthermore, in the general case where we can have stochastic conditions or elections, we'd always need to store when those are "hit" on the instrument, so we can correctly reproduce the current tree (which resembles option 1 above). This seems like we'll end up with potentially very complex, custom, redundant "aging" logic on these instruments.

I think we might need some more complex instrument implementations (with stochastic conditions and/or elections) in order to best assess and decide here.

GeorgSchneider commented 1 year ago

matteolimberto-da:

Thanks for summarising the options we have.

The getClaimsAsOf method that was introduced was effectively going in the direction of option 2 and would need observations to be passed as an input. My thinking was indeed that, as you mention, this did not seem well defined for the generic instrument.

It was replaced by an "aging" utility function taking an initial tree + acquisition time + an array of "events" (time or election) + observations and returning the aged claim. This is effectively the option 1 you describe. It is then up to the implementer to decide what to store at the instrument level (for the bond use-case, we just need lastEventTimestamp, for a path-dependent option we would need barrier-hit times). Observations are currently stored externally, but could indeed be stored together with the instrument. I feel that, for most instruments, this is much cheaper than storing the full claims tree and gives us ability to replay scenarios starting at instrument inception.

I indeed left it ambiguous what getClaims is supposed to return and accepted the inconsistency between the generic derivative (current instrument) and the bond instrument (initial instrument). I think I justified it to myself by thinking that in the generic case we have no way to recover the previous state, hence the claims tree could effectively be seen as a new initial instrument.

brianweir-da commented 1 year ago

I've been analysing this issue. From what I can tell :

  1. we have no way of determining exactly which observations/events/elections where actually applied to previous calls to lifecycle an instrument;
  2. we don't have control over the observations/events/elections contracts as an investor/issuer/etc., meaning the provider of then can archive them at any point;
  3. will the caller to getClaims always have visibility on all of the observations/events/elections contracts applied to all lifecycles of an instrument?

The cleanest option I can think of is that we have a separate contract which records all the observations/events/elections applied to each successful lifecycle of an instrument, alongside some meta-data like the time applied and the instrument id/version of the previous and new instrument. If we store the full contract details on this contract, it would allow an easy path for a new party to get the visibility on all the data they would need to produce the current claim. Also, this would allow for aging as we'd track the time of the lifecycle.

Another option would be to update the HasClaims interface to make implementations track each lifecycle. As mentioned above, if we want to store the full details then this would lead to a lot of information being stored on the instrument contract. Currently the instrument gets updated on each lifecycle call to Evolve so we could easily add this information to the contract at this point.