From a21b5437b6f26bfe064212fe71bd5e0cd1c8253c Mon Sep 17 00:00:00 2001 From: Georgina Tarres Date: Tue, 21 Oct 2025 09:49:52 +0000 Subject: [PATCH 1/4] CDM Event Model - Termination for Schedules --- .../src/main/rosetta/base-math-func.rosetta | 22 ++++++++++++++++++ ...pml-confirmation-workflowstep-func.rosetta | 7 +++--- .../product-common-settlement-func.rosetta | 23 +++++++++++++++---- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/rosetta-source/src/main/rosetta/base-math-func.rosetta b/rosetta-source/src/main/rosetta/base-math-func.rosetta index 00dda044a8..188e29986b 100755 --- a/rosetta-source/src/main/rosetta/base-math-func.rosetta +++ b/rosetta-source/src/main/rosetta/base-math-func.rosetta @@ -133,6 +133,28 @@ func UpdateAmount: <"Updates an amount based on the given QuantityChangeDirectio Decrease then oldAmount - changeAmount, Replace then changeAmount +func UpdateDatedValues: <"Updates the amounts of the dated values based on the given QuantityChangeDirectionEnum. For all periods after the effective date of the change, if the direction is Increase, the old amount and change amount are summed, if the direction is Decrease, then the change amount is subtracted from the old amount, and if the direction is Replace then the change amount replaces the old amount."> + inputs: + datedValues DatedValue (0..*) + changeAmount number (0..1) + direction QuantityChangeDirectionEnum (1..1) + effectiveDate date (0..1) + output: + newdatedValues DatedValue (0..*) + + add newdatedValues: + datedValues + extract + DatedValue { + date: item -> date, + value: + if item -> date >= effectiveDate then ( + if direction = QuantityChangeDirectionEnum -> Increase then item -> value + changeAmount + else if direction = QuantityChangeDirectionEnum -> Decrease then item -> value - changeAmount + else if direction = QuantityChangeDirectionEnum -> Replace then changeAmount) + else item -> value + } + func FilterQuantity: <"Filter list of quantities based on unit type."> inputs: quantities Quantity (0..*) <"List of quantities to filter."> diff --git a/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta b/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta index 735c843cf2..e8db8b5a12 100644 --- a/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta +++ b/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta @@ -221,7 +221,7 @@ func MapTerminationToPrimitiveInstruction: quantityChange: termination -> tradeNotionalChangeModel -> tradeNotionalChangeModelSequence0 extract QuantityChangeInstruction { - change: MapPriceQuantity, + change: MapPriceQuantity(termination), direction: if exists then Replace, ... }, @@ -286,16 +286,17 @@ func MapTerminationToPrimitiveInstruction: func MapPriceQuantity: inputs: - f fpml.TradeNotionalChangeModelSequence0 (0..1) + f fpml.TradeNotionalChange (0..1) output: priceQuantityList PriceQuantity (0..*) add priceQuantityList: PriceQuantity { quantity: CreateQuantityWithLocation( - MapTradeNotionalChangeModelSequenceToNonNegativeQuantitySchedule(f), + MapTradeNotionalChangeModelSequenceToNonNegativeQuantitySchedule(f -> tradeNotionalChangeModel -> tradeNotionalChangeModelSequence0), CreateQuantityKey(empty, empty) ), + effectiveDate: MapAdjustedDateToAdjustableOrRelativeDate(f -> agreementAndEffectiveDatesModel -> effectiveDate -> date, empty), ... } diff --git a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta index 2a93e3a0b0..cf619fb909 100644 --- a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta +++ b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta @@ -1,6 +1,7 @@ namespace cdm.product.common.settlement : <"Common product settlement concepts: cash vs physical, non-deliverable, money and cashflow, delivery vs payment."> version "${project.version}" +import cdm.base.datetime.* import cdm.base.math.* import cdm.observable.asset.* @@ -27,7 +28,8 @@ func UpdateAmountForEachMatchingQuantity: <"Updates any price or quantity from t extract UpdateQuantityAmountForEachMatchingQuantity( item, - change -> quantity, // FilterChangePriceQuantity(priceQuantity, change) -> quantity, + change, // FilterChangePriceQuantity(priceQuantity, change), + // PriceQuantity used as input to keep the quantity and effectiveDate linked direction ), observable: priceQuantity -> observable, @@ -76,20 +78,31 @@ func UpdateQuantityAmountForEachMatchingQuantity: <"Updates any quantity from th inputs: quantity NonNegativeQuantitySchedule (0..1) <"List of NonNegativeQuantitySchedule to update."> [metadata location] - change NonNegativeQuantitySchedule (0..*) <"List of new NonNegativeQuantitySchedule to use where the units match."> + change PriceQuantity (0..*) <"List of new PriceQuantity to use where the units match."> // PriceQuantity defined as input to keep the quantity and effectiveDate linked direction QuantityChangeDirectionEnum (1..1) <"Enum specifying how the updated amounts should be applied, e.g., add, subtract or replace."> output: updatedQuantity NonNegativeQuantitySchedule (0..1) [metadata location] - alias changedAmount: + alias changeMatching: change - filter UnitEquals(item -> unit, quantity -> unit) + filter UnitEquals(item -> quantity then first -> unit, quantity -> unit) then first - then extract UpdateAmount(quantity -> value, value, direction) + + alias changedAmount: + UpdateAmount(quantity -> value, changeMatching -> quantity only-element -> value, direction) + + alias changeEffectiveDate: + AdjustableDateResolution(changeMatching -> effectiveDate -> adjustableDate) + //ResolveAdjustableDate(changeMatching -> effectiveDate) + // TODO uncomment usage once function is available + + alias changedDatedValue: + UpdateDatedValues(quantity -> datedValue, changeMatching -> quantity only-element -> value, direction, changeEffectiveDate) set updatedQuantity: quantity set updatedQuantity -> value: changedAmount default quantity -> value + set updatedQuantity -> datedValue : changedDatedValue default quantity -> datedValue func PriceUnitEquals: <"Compares two PriceSchedule to check if all attributes match, except for the amount."> inputs: From f9ad978c6da05cde77e415024457c1c1f818108d Mon Sep 17 00:00:00 2001 From: Georgina Tarres <118831133+gtarres@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:20:27 +0100 Subject: [PATCH 2/4] Refine description for change input in UpdateQuantityAmount function --- .../src/main/rosetta/product-common-settlement-func.rosetta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta index cf619fb909..8f10fda2b5 100644 --- a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta +++ b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta @@ -78,7 +78,7 @@ func UpdateQuantityAmountForEachMatchingQuantity: <"Updates any quantity from th inputs: quantity NonNegativeQuantitySchedule (0..1) <"List of NonNegativeQuantitySchedule to update."> [metadata location] - change PriceQuantity (0..*) <"List of new PriceQuantity to use where the units match."> // PriceQuantity defined as input to keep the quantity and effectiveDate linked + change PriceQuantity (0..*) <"List of new PriceQuantity elements to be applied where units match. PriceQuantity is defined as input to ensure the quantity change remains associated with its corresponding effectiveDate."> direction QuantityChangeDirectionEnum (1..1) <"Enum specifying how the updated amounts should be applied, e.g., add, subtract or replace."> output: updatedQuantity NonNegativeQuantitySchedule (0..1) From 855f0e69aaae8322fa7ff0cd6b749249758dbbec Mon Sep 17 00:00:00 2001 From: Georgina Tarres Date: Wed, 5 Nov 2025 14:50:49 +0000 Subject: [PATCH 3/4] Minor style improvements --- ...est-fpml-confirmation-workflowstep-func.rosetta | 14 +++++++------- .../rosetta/product-common-settlement-func.rosetta | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta b/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta index 2b386a1b79..33b4062691 100644 --- a/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta +++ b/rosetta-source/src/main/rosetta/ingest-fpml-confirmation-workflowstep-func.rosetta @@ -261,13 +261,13 @@ func MapTerminationToPrimitiveInstruction: counterpartyList ) }, - quantityChange: termination -> tradeNotionalChangeModel -> tradeNotionalChangeModelSequence0 - extract - QuantityChangeInstruction { - change: MapPriceQuantity(termination), - direction: if exists then Replace, - ... - }, + quantityChange: + if termination exists + then QuantityChangeInstruction { + change: MapPriceQuantity(termination), + direction: Replace, + ... + }, transfer: TransferInstruction { transferState: diff --git a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta index 8f10fda2b5..6c24818042 100644 --- a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta +++ b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta @@ -86,7 +86,7 @@ func UpdateQuantityAmountForEachMatchingQuantity: <"Updates any quantity from th alias changeMatching: change - filter UnitEquals(item -> quantity then first -> unit, quantity -> unit) + filter UnitEquals(item -> quantity first -> unit, quantity -> unit) then first alias changedAmount: From d771669a44df5c46959f597c7b60ec751a0b8ea4 Mon Sep 17 00:00:00 2001 From: Georgina Tarres Date: Mon, 10 Nov 2025 10:59:34 +0000 Subject: [PATCH 4/4] fixed bug for multiple quantities --- .../product-common-settlement-func.rosetta | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta index 6c24818042..cf8ec6e5b6 100644 --- a/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta +++ b/rosetta-source/src/main/rosetta/product-common-settlement-func.rosetta @@ -25,11 +25,11 @@ func UpdateAmountForEachMatchingQuantity: <"Updates any price or quantity from t direction ), quantity: priceQuantity -> quantity - extract - UpdateQuantityAmountForEachMatchingQuantity( + extract + UpdateQuantityAmountForEachMatchingQuantity( item, change, // FilterChangePriceQuantity(priceQuantity, change), - // PriceQuantity used as input to keep the quantity and effectiveDate linked + // PriceQuantity used as input to keep the quantity and effectiveDate linked direction ), observable: priceQuantity -> observable, @@ -85,24 +85,32 @@ func UpdateQuantityAmountForEachMatchingQuantity: <"Updates any quantity from th [metadata location] alias changeMatching: - change - filter UnitEquals(item -> quantity first -> unit, quantity -> unit) + SplitQuantityChange(change) + filter UnitEquals(item -> quantity only-element -> unit, quantity -> unit) then first alias changedAmount: - UpdateAmount(quantity -> value, changeMatching -> quantity only-element -> value, direction) + UpdateAmount( + quantity -> value, + changeMatching -> quantity only-element -> value, + direction + ) alias changeEffectiveDate: AdjustableDateResolution(changeMatching -> effectiveDate -> adjustableDate) - //ResolveAdjustableDate(changeMatching -> effectiveDate) - // TODO uncomment usage once function is available - + // ResolveAdjustableDate(changeMatching -> effectiveDate) + // TODO uncomment usage once function is available alias changedDatedValue: - UpdateDatedValues(quantity -> datedValue, changeMatching -> quantity only-element -> value, direction, changeEffectiveDate) + UpdateDatedValues( + quantity -> datedValue, + changeMatching -> quantity only-element -> value, + direction, + changeEffectiveDate + ) set updatedQuantity: quantity set updatedQuantity -> value: changedAmount default quantity -> value - set updatedQuantity -> datedValue : changedDatedValue default quantity -> datedValue + set updatedQuantity -> datedValue: changedDatedValue default quantity -> datedValue func PriceUnitEquals: <"Compares two PriceSchedule to check if all attributes match, except for the amount."> inputs: @@ -125,3 +133,22 @@ func PriceUnitEquals: <"Compares two PriceSchedule to check if all attributes ma set result: unitMatches and perUnitOfMatches and priceTypeMatches and priceExpressionMatches and arithmeticOperatorMatches + +func SplitQuantityChange: <"Splits a PriceQuantity list into separate elements, each containing a single quantity and its corresponding effective date, if multiple quantities exist."> + inputs: + changeList PriceQuantity (1..*) <"Original PriceQuantity list where each of them may contain multiple quantities."> + output: + splitChange PriceQuantity (1..*) <"New list of PriceQuantity where each of them contain a single quantity."> + + set splitChange: <"Iterates through the original PriceQuantity list and the quantity list."> + changeList + extract change [ + change -> quantity + extract quantity [ + PriceQuantity { + quantity: quantity, + effectiveDate: change -> effectiveDate, + ... + } + ] + ] then flatten \ No newline at end of file