Skip to content

Rosetta Syntax Upgrade

Jim Wang edited this page Aug 16, 2019 · 17 revisions

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.

Background

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

Goal

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:

  1. Define consistent syntax (with respect to colon, semi-colons, braces, comments, definitions) between Data and Functions
  2. Replace class, enum and record type with Data
  3. Replace calculation, function, alias and spec with Functions
  4. Reuse consistent Function syntax for data rule, choice rule, one of and alias
  5. Support different views for DSL Readers vs. DSL Writers
  6. Regulatory Rules to be tackled in another phase of work

Anatomy

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.

Data (replaces class, enums and record type)

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+
    

Key Tasks with Examples:

  1. Replace class, enum and record type with Data
  2. Remove semi-colon at end of each attribute
  3. Remove angle-brackets in Definitions
  4. 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]
  1. Move data rule into condition expression blocks inside Data-Body
  2. Support optional else block in if 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
  1. Move choice rule into condition expression blocks inside Data-Body
  2. 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...

Function (data rule, choice rule, alias, one of and spec)

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*
    Alias*
    Condition*      
    Assignment*     
    Post-Condition* 

where:

  • Input

     'inputs' ':' 
     	Attribute+
    
  • Output

      'output' ':'
          Attribute
    
  • Alias: used as a bookmark for long Rosetta Paths (ie Contract -> product -> contractualProduct -> payout -> ... -> amount) and complex expressions

      'alias' 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+
    

Key Actions with Examples:

  1. Replace calculation, function and spec with Functions. The form of a spec is already very similar to the proposed, so no examples here.
  2. Introduce a concept of higher-order functions.
  3. 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)

    alias calculationAmount: "The calculation amount as extracted from the interest rate payout"
	    interestRatePayout -> quantity -> notionalSchedule -> notionalStepSchedule -> initialValue
          
    alias fixedRate: "a shortcut from the Interest Rate Payout to the Fixed Rate"
        interestRatePayout -> rateSpecification -> fixedRate -> initialValue

    alias 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 in alias dayCountFraction resolves the function that is attached to the Enum's value
  • The data-type of alias dayCountFraction is function whose inputs and output are specified by func DayCountFraction
  • Scoping: an alias can be created on inputs or output
  • Scoping: a condition can reference only global elements, inputs and alias's based on inputs
  • Scoping: a post-condition can reference global elements, inputs, the output and any alias
  • Scoping: The name of the assign-output block should reference only the output
  • Scoping: The expression within the assign-output can reference global elements, inputs, the output and any alias
  • Validation: output is optional in the grammar but is mandatory for funcs that don't implement other funcs
  • Validation: when the func keyword is used to resolve a Function, check that DayCountFractionEnum is attached to a func and said func 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"    
    alias 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 Enum DayCountFractionEnum
  • Implicitly, all implementations of DayCountFraction will be attached to values on DayCountFractionEnum
  • Validation: each Enum type has only a single func attached.

Notes on func ACT_365

  • funcs can implement at most 1 other func
  • inputs and output 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 of function, now all Data types need to be explicitly defined
  1. 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

// syntactic sugar 
//
alias forwardFX
	ForwardPayout -> underlier -> singleUnderlier -> underlyingProduct -> foreignExchange

Usage

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

Annotation (replaces metadata, reference, scheme, key, synonym)

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.

Examples

Annotation usage take the following forms:

  1. 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]
    
  2. 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): "..."
    
  3. 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
    

Views

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."

DRR Reporting

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:

  1. When to report (daily)
  2. If the event is eligible for reporting
  3. Define the fields to be reported from the cdm model classes
  4. 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