-
Notifications
You must be signed in to change notification settings - Fork 31
Rosetta Syntax Upgrade
This document describes the desired state for the Rosetta DSL. The primary focus of this document is on the Syntax, with details on Scoping and Validation features captured inside code examples.
Further Scoping, Validation and Code Generation details to come.
As part of on-going improvement and to facilitate the opening of CDM contributions to the community, we will implement a number of changes to the Rosetta DSL that will make reading and writing easier. Further, simplifications to the meta-model - the Ecore - should make external Code Generator contributions easier.
Current Rosetta DSL concepts:
- Data (classes, enums, basic types, record types)
- Synonym (mappings, conditionals, custom)
- Validation (choice rules, data rules)
- Calculation (calculation, functions)
- Function Specification
- Regulatory Rule
In essence, we see two object types in Rosetta: Data and Functions, and we wish to simplify the Rosetta DSL to reflect this simplification.
The reduced number of objects in Ecore as well as fewer Grammar Rules will support greater reuse of Scoping, Validation and Code Generation features.
Below is a summary of changes to achieve this goal:
- Define consistent syntax (with respect to colon, semi-colons, braces, comments, definitions) between Data and Functions
- Replace
class
,enum
andrecord type
with Data - Replace
calculation
,function
,alias
andspec
with Functions - Reuse consistent Function syntax for
data rule
,choice rule
,one of
andalias
- Support different views for DSL Readers vs. DSL Writers
- Regulatory Rules to be tackled in another phase of work
The anatomy of Rosetta concepts are generally described using Extended Backus–Naur form and describes the Grammar Rules that governs the syntax.
We start by specifying three root elements: Data, Function and Annotation. For each root element, we state its form, then as list of tasks to migrate from the current syntax to the proposed along with examples of current vs. proposed.
Defining Data objects takes the following form:
'data' Name ('extends' [Data])? ':' Definition?
Annotation*
Attribute+
Condition*
where:
-
Definition:
STRING
-
Annotation:
'[' [Annotation] [Attribute] ']' // square braces represent cross reference to existing Annotation (by Name) and one of its Attributes
-
Attribute:
Name Type Cardinality (':' Definition)? Annotation*
-
Condition:
'condition' Name? ':' Definition? Annotation* RosettaExpression+
- Replace
class
,enum
andrecord type
with Data - Remove semi-colon at end of each attribute
- Remove angle-brackets in Definitions
- Remove quotes in synonym paths and enum synonyms, use a single Grammar tree to represent Synonyms
Current:
class Party key <"A class to specify a party, without a qualification as to whether this party is a legal entity or a natural person, although the model provides the ability to associate a person (or set of persons) to a party, which use case would imply that such party would be a legal entity (even if not formally specified as such). ">
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0 , CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value Party meta id maps 2]
{
partyId string (1..*) scheme <"The identifier associated with a party, e.g. the 20 digits LEI code.">;
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value partyId meta partyIdScheme]
[synonym DTCC_11_0, DTCC_9_0 value partyId maps 2 meta partyIdScheme]
[synonym ISDA_Create_1_0 value id]
name string (0..1) scheme <"The party name.">;
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value partyName meta entityNameScheme]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value entityName meta entityNameScheme]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value entityName path "referenceEntity" meta entityNameScheme]
[synonym CME_SubmissionIRS_1_0 value SID path "Hdr"]
[synonym ISDA_Create_1_0 value name]
[synonym ISDA_Create_1_0 value partyA_name, partyB_name]
person NaturalPerson (0..*) <"The person(s) who might be associated with the party as part of the execution, contract or legal document.">;
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value person]
account Account (0..1) <"The account that might be associated with the party. At most one account can be specified, as it is expected that this information is used in the context of a contract or legal document where only one account per party can be associated with such object.">;
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0, ISDA_Create_1_0 value account]
}
Proposed:
data Party: "A class to specify a party, without a qualification as to whether this party is a legal entity or a natural person, although the model provides the ability to associate a person (or set of persons) to a party, which use case would imply that such party would be a legal entity (even if not formally specified as such)."
[keyed]
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0 , CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value Party meta id maps 2]
partyId string (1..*): "The identifier associated with a party, e.g. the 20 digits LEI code."
[metadata scheme]
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value partyId meta partyIdScheme]
[synonym DTCC_11_0, DTCC_9_0 value partyId maps 2 meta partyIdScheme]
[synonym ISDA_Create_1_0 value id]
name string (0..1): "The party name"
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value partyName meta entityNameScheme]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value entityName meta entityNameScheme]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17 value entityName path referenceEntity meta entityNameScheme]
[synonym CME_SubmissionIRS_1_0 value SID path Hdr]
[synonym ISDA_Create_1_0 value name]
[synonym ISDA_Create_1_0 value partyA_name, partyB_name]
person NaturalPerson (0..*): "The person(s) who might be associated with the party as part of the execution, contract or legal document"
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0 value person]
account Account (0..1): "The account that might be associated with the party. At most one account can be specified, as it is expected that this information is used in the context of a contract or legal document where only one account per party can be associated with such object."
[synonym FpML_5_10, DTCC_11_0, DTCC_9_0, CME_ClearedConfirm_1_17, CME_SubmissionIRS_1_0, ISDA_Create_1_0 value account]
- Move
data rule
intocondition
expression blocks inside Data-Body - Support optional
else
block inif
expression
Current:
class Contract key <"A class to specify a financial contract object...">
[synonym FpML_5_10, CME_SubmissionIRS_1_0, CME_ClearedConfirm_1_17 meta id path "trade"]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, CME_ClearedConfirm_1_17, Rosetta_Workbench meta id]
{
contractIdentifier Identifier (1..*) <"The identifier(s) that uniquely identify a contract...">;
[synonym FpML_5_10, CME_SubmissionIRS_1_0 value partyTradeIdentifier path "trade.tradeHeader"]
tradeDate TradeDate (1..1) <"The date on which the contract has been executed.">;
partyRole PartyRole (0..*) <"The role(s) that party(ies) may have...">;
...
}
data rule Contract_hedgingParty <"FpML specifies that there cannot be more than 2 hedging parties.">
when Contract -> partyRole -> role = PartyRoleEnum.HedgingParty
then Contract -> partyRole -> role count <= 2
Proposed:
annotation keyed: "Applies to classes. Indicates when a class can be referred to by other classes via an identifier"
data Contract: "A class to specify a financial contract object..."
[keyed]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, CME_ClearedConfirm_1_17 meta id path trade]
[synonym FpML_5_10, CME_SubmissionIRS_1_0, CME_ClearedConfirm_1_17, Rosetta_Workbench meta id]
contractIdentifier Identifier (1..*): "The identifier(s) that uniquely identify a contract..."
[synonym FpML_5_10, CME_SubmissionIRS_1_0 value partyTradeIdentifier path trade.tradeHeader]
tradeDate TradeDate (1..1): "The date on which the contract has been executed."
...
condition Contract_hedgingParty: "FpML specifies that there cannot be more than 2 hedging parties."
if partyRole -> role = PartyRoleEnum.HedgingParty
then partyRole -> role count <= 2
- Move
choice rule
intocondition
expression blocks inside Data-Body - Support syntactic sugar to pass all Attributes/Inputs into a Function call when none specified
Current:
class SecurityValuationModel one of <"The security valuation model choice, which can either be based on nominal amount as for a bond, or on the number of contract units as for equity.">
{
bondValuationModel BondValuationModel (0..1) <"The valuation model when the security is a bond.">;
unitContractValuationModel UnitContractValuationModel (0..1) <"The valuation model when the security is a unit contract like equity.">;
}
class InitialMarginCalculation <"Defines the initial margin calculation applicable to a single piece of collateral.">
{
marginRatio number (0..1) <"An element defining ...">;
[synonym FpML_5_10 value marginRatio]
marginRatioThreshold number (0..2) <"An element defining a margin ratio threshold...">;
[synonym FpML_5_10 value marginRatioThreshold]
haircut number (0..1) <"An element defining a haircut ...">;
[synonym FpML_5_10 value haircut]
...
}
choice rule InitialMarginCalculation_choice
for InitialMarginCalculation required choice between
marginRatio and haircut
Proposed:
data SecurityValuationModel: "The security valuation model choice, which can either be based on nominal amount as for a bond, or on the number of contract units as for equity."
bondValuationModel BondValuationModel (0..1): "The valuation model when the security is a bond."
unitContractValuationModel UnitContractValuationModel (0..1): "The valuation model when the security is a unit contract like equity."
condition: OneOf // when a function is invoked without any arguments, all the arguments in scope i.e. all data attributes
data InitialMarginCalculation: "Defines the initial margin calculation applicable to a single piece of collateral."
marginRatio number (0..1): "An element defining ..."
[synonym FpML_5_10 value marginRatio]
marginRatioThreshold number (0..2): "An element defining a margin ratio threshold..."
[synonym FpML_5_10 value marginRatioThreshold]
haircut number (0..1): "An element defining a haircut ..."
[synonym FpML_5_10 value haircut]
...
condition InitialMarginCalculation_choice: "Replaces choice rule InitialMarginCalculation_choice"
[validation]
RequiredChoice marginRatio, haircut
// See sections below for more on 'func' syntax
//
func OneOf: "For a set of inputs, checks exactly 1 exists, when used without arguments, takes all object attributes as input."
inputs:
inputs any (1..*) // data type any does not currently exist
output:
result boolean (1..1)
func OptionalChoice: "For the set of inputs, at most 1 exists"
inputs:
inputs any (1..*)
output:
result boolean (1..1)
func RequiredChoice: "For the set of inputs, exactly 1 exists"
inputs:
inputs any (1..*)
output:
result boolean (1..1)
Enum and Record Type examples to follow...
calculation
, function
, spec
and alias
are 4 language features that are all Functions
-
function
is a function signature only i.e. function name, inputs and outputs -
calculation
is a function where the inputs are inferred for readability -
alias
is a function that takes an single object as input and outputs a child of that object -
spec
was recently introduced as a way to consolidate the above 3
Functions take the form:
'func' Name ('implements' [Function])? ':' Definition?
Annotation*
Input*
Output*
Label*
Condition*
Assignment*
Post-Condition*
where:
-
Input
'inputs' ':' Attribute+
-
Output
'output' ':' Attribute
-
Label: used as a bookmark for long Rosetta Paths (ie
Contract -> product -> contractualProduct -> payout -> ... -> amount
) and complex expressions'label' Name ':' Definition? RosettaExpression
-
Assignment: used to build the output object
'assign-output' AttributePath ':' Definition? RosettaExpression // Rosetta Expressions do not support assignment operations, hence the
-
AttributePath refers to a reference to an attributes on a Rosetta element i.e.
Contract -> product -> ... -> quantity
-
Post-Condition
'post-condition' Name? ':' Definition? Annotation* RosettaExpression+
- Replace
calculation
,function
andspec
with Functions. The form of aspec
is already very similar to the proposed, so no examples here. - Introduce a concept of higher-order functions.
- Support resolving and invoking functions based on Enum values.
Current:
calculation FixedAmount <"2006 ISDA Definition Article 5 Section 5.1. Calculation of a Fixed Amount...">
{
fixedAmount : calculationAmount * fixedRate * dayCountFraction
where
calculationAmount : InterestRatePayout -> quantity -> notionalSchedule -> notionalStepSchedule -> initialValue
fixedRate : InterestRatePayout -> rateSpecification -> fixedRate -> initialValue
dayCountFraction : InterestRatePayout -> dayCountFraction
}
calculation DayCountFractionEnum.ACT_360 <"...">
{
: daysInPeriod / 360
where
daysInPeriod <"Number of calendar in the calculation period">:
CalculationPeriod( InterestRatePayout -> calculationPeriodDates ) -> daysInPeriod
}
function ResolveRateIndex( index FloatingRateIndexEnum ) <"The function to specify that...">
{
rate number;
}
function CalculationPeriod( calculationPeriodDates CalculationPeriodDates ) <"...">
{
startDate date;
endDate date;
daysInPeriod int;
daysInLeapYearPeriod int;
isFirstPeriod boolean;
isLastPeriod boolean;
}
Proposed:
func FixedAmount: "2006 ISDA Definition Article 5 Section 5.1. Calculation of a Fixed Amount..."
inputs:
interestRatePayout InterestRatePayout (1..1)
output:
fixedAmount number (1..1)
label calculationAmount: "The calculation amount as extracted from the interest rate payout"
interestRatePayout -> quantity -> notionalSchedule -> notionalStepSchedule -> initialValue
label fixedRate: "a shortcut from the Interest Rate Payout to the Fixed Rate"
interestRatePayout -> rateSpecification -> fixedRate -> initialValue
label dayCountFraction: "..."
interestRatePayout -> dayCountFraction func
assign-output fixedAmount: "assigns the output 'fixedAmount' with the result of the mathematical operation"
calculationAmount * fixedRate * dayCountFraction( interestRatePayout )
Notes on func FixedAmount
- the
func
keyword inlabel dayCountFraction
resolves the function that is attached to the Enum's value - The data-type of
label dayCountFraction
isfunction
whose inputs and output are specified byfunc DayCountFraction
- Scoping: a
label
can be created oninputs
oroutput
- Scoping: a
condition
can reference only global elements, inputs andlabel
s based oninputs
- Scoping: a
post-condition
can reference global elements, inputs, the output and anylabel
- Scoping: The name of the
assign-output
block should reference only theoutput
- Scoping: The expression within the
assign-output
can reference global elements, inputs, the output and anylabel
- Validation:
output
is optional in the grammar but is mandatory forfunc
s that don't implement otherfunc
s - Validation: when the
func
keyword is used to resolve a Function, check thatDayCountFractionEnum
isattached to
afunc
and saidfunc
has at least 1 implementation
func DayCountFraction: "A description of inputs and outputs for all day count fraction calculations"
[attached to DayCountFractionEnum]
inputs:
interestRatePayout InterestRatePayout (1..1)
output:
result number (1..1)
func ACT_365 implements DayCountFraction : "An concrete specification of a Day Count Fraction calculation, where both inputs and output have been 'inhereted' from its parent: the func DayCountFraction"
label daysInPeriod:
CalculationPeriod( interestRatePayout -> calculationPeriodDates ) -> daysInPeriod
assign-output:
result = daysInPeriod / 360
Notes on func DayCountFraction
- The
attached to
Annotation states that this Function is associated with the EnumDayCountFractionEnum
- Implicitly, all implementations of DayCountFraction will be
attached to
values onDayCountFractionEnum
- Validation: each Enum type has only a single
func
attached.
Notes on func ACT_365
-
func
s canimplement
at most 1 otherfunc
-
inputs
andoutput
are inherited from the parent with no overriding of inputs or outputs allowed - Validation: input and output definitions are not allowed
- Validation: the
func
's name needs to be a value from the Enum specified in the annotation of the parent - Validation: for each enum value, only a single
func
is attached
func ResolveRateIndex: "The function to ..."
inputs:
index FloatingRateIndexEnum (1..1)
output:
rate number (1..1)
data CalculationPeriodResult: "..."
startDate date (1..1)
endDate date (1..1)
daysInPeriod int (1..1)
daysInLeapYearPeriod int (1..1)
isFirstPeriod boolean (1..1)
isLastPeriod boolean (1..1)
function CalculationPeriod: "..."
inputs:
calculationPeriodDates CalculationPeriodDates (1..1)
output:
result CalculationPeriodResult (1..1)
Note on CalculationPeriodResult
- Where previously we allowed creation of anonymous
classes
in the definition offunction
, now all Data types need to be explicitly defined
- Replace
alias
with Functions
Current:
alias forwardFX
ForwardPayout -> underlier -> singleUnderlier -> underlyingProduct -> foreignExchange
Proposed:
func ForwardFX:
[alias]
inputs:
forwardPayout ForwardPayout (1..1)
output:
foreignExchange ForeignExchange (1..1)
assign-output foreignExchange:
forwardPayout -> underlier -> singleUnderlier -> underlyingProduct -> foreignExchange
Any Function invocation must be done by a Client, where a Client is someone or some system that makes use of the generated code. Functions can call other Functions
Annotations will be a new concept that represents non-core information. For example, the mandatory parts of Data are its name and attributes, therefore all other descriptors should be placed inside annotations. For Attributes, the name, type and cardinality are mandatory and so synonyms are represented in annotations.
Annotations can be defined in the language or in the grammar. Those defined in the language take the following form:
'annotation' Name Definition?
Attribute*
For annotations defined in grammar, they can take on any form definable via the xText Grammar syntax.
Annotation usage take the following forms:
-
Annotation - an annotation with attributes
annotation metadata: "Example definition of an annotation type" scheme string (0..1) reference string (0..1) data Party: "..." partyId string (1..*): "..." [metadata scheme]
-
Marker Annotation - an annotation with no attributes
annotation keyed: "Indicates whether an object should support storage of a `global key`" data Party: "..." [keyed] partyId string (1..*): "..." name string (0..1): "..."
-
Structured Annotation - where the strucutre of the annotation is defined in grammar
data Party: "..." partyId string (1..*): "..." [synonym FpML_5_10 value partyId meta partyIdScheme] // re-uses existing synonym grammar rules
We recognise that a Rosetta reader and a Rosetta editor may want different views of the DSL and so we wish to support in Rosetta Core the ability to switch between "views". When writing Rosetta, the writer needs to be specific about type and cardinality, i.e. when passing object references into functions or nesting function calls.
Everything that is between square brackets is an annotation that provides additional information and can be folded (hidden) in the editor. Taking an example of the data object Party
from CDM, the example "reader" view should look like the below
Party: "A class to specify a party..."
- partyId : "The identifier associated with a party, e.g. the 20 digits LEI code."
- name : "The party name"
- person : "The person(s) who might be associated with the party as part of the execution, contract or legal document"
- account : "The account that might be associated with the party. At most one account can be specified, as it is expected that this information is used in the context of a contract or legal document where only one account per party can be associated with such object."
An example of the function FixedAmount
from CDM in the "reader" view:
FixedAmount: "2006 ISDA Definition Article 5 Section 5.1. Calculation of a Fixed Amount..."
= calculationAmount * fixedRate * dayCountFraction
where:
calculationAmount: "The quantity or notional used to compute the Fixed Amount."
fixedRate: "The observed rate."
dayCountFraction: "The Day Count Convention used to count the number of days within a period."
DDR Reporting is an extension case to core CDM modelling (Products and Events) and will be addressed once the above work has come reached a conclusion.
There are 4 aspects to the report:
- When to report (daily)
- If the event is eligible for reporting
- Define the fields to be reported from the cdm model classes
- Define the mappings to the output reporting format (e.g. ISO 20022)
This is the way we currently can define regulatory context. We define a regulatoryRegime, mandate and segment and combine them to reference a provision.
regulatoryRegime ESMA_MiFID_I
regulatoryRegime ESMA_MiFID_II
regulatoryRegime ESMA_MiFIR
regulatoryRegime CFTC_DFA
mandate regulation
mandate specification
mandate guideline
segment article
segment whereas
segment annex
segment section
segment field
reportingStandard ISO_20022
// Example usage (you can annotate any class, attribute or rule)
// [regulatoryReference ESMA_MiFIR regulation "RTS 22" article "2" provision "Do what I say, not what I do."]
Proposed syntax example:
MifidIITransactionReport
[Report]
reported daily for T-1
when ESMA_MiFIR regulation "RTS 22" applies
using reportingStandard ISO_20022
report fields
// The fields need to reference two rules. One to get data from and one to project data to.
// To start with - we can just use the entire path, then refactor if/when it gets more complex.
- reportStatus from Event -> xxx -> yyy to ISO_20022 -> xxx
- price from Event -> zzz -> ccc to ISO_20022 -> xxx
- txid from Event -> vvv -> ddd to ISO_20022 -> xxx
RTS_22_Is_Transaction: "When eligible for rts 22"
[Eligibility]
[regulatoryReference ESMA_MiFIR regulation "RTS 22" article "2" provision "For the purposes of Article 26 of Regulation (EU) No 600/2014, the conclusion of an acquisition or disposal of a financial instrument referred to in Article 26(2) of Regulation (EU) No 600/2014 shall constitute a transaction."]
for Event
primitive -> inception -> must exist
or
primitive -> termination -> must exist