---
name: cdm-data-modeling
description: Model financial trades in the FINOS Common Domain Model (CDM) and check that the result is faithful to the normative model. Use when composing CDM objects for a trade, choosing between the Rune DSL source and the published JSON Schema, placing a fact in the right CDM layer, naming CDM types and enumerations, or reviewing a CDM instance or Rosetta definition for correctness.
license: Community-Spec-1.0
---

# Modelling a trade in the FINOS CDM

The Common Domain Model (CDM) is FINOS's machine-readable, machine-executable model of how financial
products are traded and managed across the transaction lifecycle. It is written in the **Rune DSL** and
distributed as Rune source, as generated executable code, and as JSON Schema. This skill covers the part
that decides whether a model is *correct*: putting each fact in the right layer, composing objects the way
the model's own rules expect, and using names that really exist.

**Rule zero.** Every CDM name you write — type, attribute, enumeration, enumeration value, function,
condition, namespace — must exist in the normative Rune DSL source. If you cannot point at the line, do
not use the name. Never invent a plausible-sounding CDM identifier.

## 1. Where the model lives

| Source | Authoritative for |
| --- | --- |
| `rosetta-source/src/main/rosetta/*.rosetta` | The model itself: types, attributes, cardinalities, enumerations, `condition` rules, functions and qualification logic. This is the source of truth. |
| `docs/*.md` in the repository | Prose explanations — `docs/product-model.md`, `docs/event-model.md`, `docs/process-model.md`, `docs/legal-agreements.md`, `docs/reference-data-model.md`, `docs/design-principles.md`, `docs/namespace.md` and `docs/serialization.md`. |
| `https://cdm.finos.org/schemas/<version>/` | The generated JSON Schema for every CDM type, for validating JSON instances. |
| `https://cdm.finos.org/okf.tar.gz` | Offline bundle of the documentation, the Rune DSL source and the latest JSON Schema set — use it when there is no network, or when you want one consistent snapshot. |

This skill is published at
`https://cdm.finos.org/.agents/cdm-plugin/skills/cdm-data-modeling/SKILL.md`.

Practical first move: grep the Rune source for the type you are about to use and read its declaration,
rather than trusting prose or memory.

```bash
grep -rn '\bNonTransferableProduct\b' rosetta-source/src/main/rosetta/
```

## 2. The layered model

The CDM is partitioned into namespaces, organised so that an inner layer can be used without the layers
outside it. The layers that matter when modelling a trade, inner (most reusable, least business meaning)
to outer:

| Layer | Namespace groups | What it defines |
| --- | --- | --- |
| Base and reference data | `cdm.base`, `cdm.base.datetime`, `cdm.base.datetime.daycount`, `cdm.base.math`, `cdm.base.staticdata.asset.*`, `cdm.base.staticdata.identifier`, `cdm.base.staticdata.party` | Measures and units (`MeasureBase`, `Quantity`, `UnitType`), dates and schedules (`Period`, `Frequency`, `AdjustableOrRelativeDate`, `BusinessDayAdjustments`, `RollConventionEnum`), day counts (`DayCountFractionEnum`), identifiers (`Identifier`, `AssignedIdentifier`), and the parties (`Party`, `PartyIdentifier`, `LegalEntity`, `EntityIdentifier`, `Counterparty`, `CounterpartyRoleEnum`). |
| Observable | `cdm.observable.asset`, `cdm.observable.asset.fro`, `cdm.observable.asset.calculatedrate`, `cdm.observable.common`, `cdm.observable.event` | What can be observed and priced: `Observable`, `Index`, `InterestRateIndex`, `FloatingRateIndex`, `FloatingRateIndexEnum`, `PriceQuantity`, `PriceSchedule`, `Price`, `PriceTypeEnum`, and the observed data itself (`Observation`, a root type, plus `ObservationIdentifier`). |
| Product | `cdm.product.template`, `cdm.product.asset`, `cdm.product.common.schedule`, `cdm.product.common.settlement`, `cdm.product.collateral`, `cdm.product.qualification` | What is traded: `NonTransferableProduct`, `TransferableProduct`, `EconomicTerms`, the `Payout` choice (`InterestRatePayout`, `SettlementPayout`, `OptionPayout`, `PerformancePayout`, `CreditDefaultPayout`, `CommodityPayout`, `AssetPayout`, `FixedPricePayout`), the shared `PayoutBase`, leg schedules (`CalculationPeriodDates`, `PaymentDates`, `ResetDates`), and the product qualification functions. |
| Event | `cdm.event.common`, `cdm.event.instructioncomposition`, `cdm.event.position`, `cdm.event.qualification`, `cdm.event.workflow` | What happens to a trade: `TradeState`, `Trade`, `TradableProduct`, `TradeLot`, `BusinessEvent`, the primitive instructions, the `Create_*` state-transition functions, `State`, `Reset`, `Transfer`, `TransferState`, and the workflow (`EventInstruction`, `WorkflowStep`). |
| Legal documentation | `cdm.legaldocumentation.common`, `cdm.legaldocumentation.confirmation`, `cdm.legaldocumentation.csa`, `cdm.legaldocumentation.master` (with its `cdm.legaldocumentation.master.isda`, `cdm.legaldocumentation.master.icma` and `cdm.legaldocumentation.master.isla` siblings), `cdm.legaldocumentation.transaction` | The agreements attached to a trade: `LegalAgreement`, `LegalAgreementBase`, `AgreementTerms`, `LegalAgreementIdentification`, `MasterAgreement`, `MasterAgreementSchedule`, `GlobalMasterSecuritiesLendingAgreement`, `CreditSupportAgreementElections`. |
| Ingest | `cdm.ingest.fpml.confirmation.*` | Mapping functions from FpML confirmation into the layers above. Read these when you must consume FpML rather than invent a mapping. |

Reference data is deliberately narrow: the CDM integrates only the reference data it needs for the
products, events, legal agreements and functions in scope — essentially the party and the legal entity.
Do not look for a securities master or a currency table in the model.

## 3. How a trade is composed from the layers

A trade is not one object; it is an event-layer object that *uses* every layer below it.

```text
TradeState                              [rootType] [metadata key] — one trade at one point in its life
├── trade            Trade              [metadata key]
│   ├── (Trade extends TradableProduct, from cdm.product.template)
│   │   ├── product          NonTransferableProduct (1..1)
│   │   │   ├── identifier   ProductIdentifier (0..*)
│   │   │   ├── taxonomy     ProductTaxonomy (0..*)
│   │   │   └── economicTerms EconomicTerms (1..1)
│   │   │       ├── payout       Payout (1..*)        ← the legs of the trade
│   │   │       ├── effectiveDate / terminationDate / dateAdjustments
│   │   │       └── terminationProvision, calculationAgent, collateral
│   │   ├── tradeLot         TradeLot (1..*)
│   │   │   └── priceQuantity PriceQuantity (1..*)    ← notional and price, per leg
│   │   └── counterparty     Counterparty (2..2)      ← role Party1 / Party2 → a Party
│   ├── tradeIdentifier      TradeIdentifier (1..*)
│   ├── tradeDate            date (1..1)
│   └── party, partyRole, executionDetails, contractDetails
├── state             State
│   ├── closedState   ClosedState         ← ClosedStateEnum when the trade is closed
│   └── positionState PositionStatusEnum
├── resetHistory      Reset (0..*)        ← fixings appended by reset events
└── transferHistory   TransferState (0..*) ← settlement instructions and their status
```

Three consequences of that shape drive most modelling decisions:

1. **The product is party-agnostic.** A payout names its two sides as `CounterpartyRoleEnum -> Party1` /
   `Party2` through `PayerReceiver`. It is `Trade`'s `counterparty` attribute that binds those roles to
   real `Party` objects, so the same product can back many trades between different pairs of parties.
2. **Quantity and price are measures, not amounts.** `PayoutBase -> priceQuantity` is a
   `ResolvablePriceQuantity`; the actual notional and price live in the `PriceQuantity` objects under
   `TradeLot`, and are linked by the
   `[metadata address "pointsTo"=PriceQuantity->quantity]` and
   `[metadata address "pointsTo"=PriceQuantity->price]` annotations on the resolvable attributes. Fill
   the `TradeLot`, not the payout, with the trade's numbers.
3. **The event layer never mutates a `Trade`.** Each event produces a new `TradeState`, and
   `Instruction -> before` is a `[metadata reference]` to the state it acted on. Lineage is data, not a
   convention.

## 4. Choosing an input: Rune DSL source vs published JSON Schema

| You need to know | Read | Why |
| --- | --- | --- |
| Does this type/attribute exist, and is it optional? | `rosetta-source/src/main/rosetta/*.rosetta` | Declarations carry cardinality (`(0..1)`, `(1..*)`) and the `[metadata ...]` annotations the schema does not show. |
| What business rules constrain the data? | Rune source `condition` blocks | Conditions are model rules; they are **not** represented in the generated schema at all. |
| What does the model compute or infer? | Rune source `func` declarations | Functions marked `[calculation]`, `[codeImplementation]`, or `[qualification Product]` / `[qualification BusinessEvent]` are the executable part of the model. |
| Will this JSON instance validate? | `https://cdm.finos.org/schemas/<version>/<schema>.schema.json` | The published schema is the machine-checkable shape of each type. |
| How should the JSON look? | `docs/serialization.md` | Fixes the Rune JSON envelope. |

Facts about the published schema set you can rely on:

- The 7.0.0 `cdm-json-schema` artifact ships 1136 `*.schema.json` files (80 of them generated
  `metafields-*` wrappers for the `[metadata scheme]` and `[metadata reference]` cases), served under
  `https://cdm.finos.org/schemas/<version>/`.
- The file name is the fully qualified type with dots turned into dashes:
  `cdm.event.common.TradeState` → `cdm-event-common-TradeState.schema.json`.
- Each file declares `$schema` as JSON Schema **draft-04**, an `$anchor` holding the CDM namespace and a
  `title` holding the simple type name. Object types carry `properties` and `required`; enumerations
  carry `enum` and `oneOf`. Cross-type links are relative `$ref`s to sibling schema files (for example
  `cdm-base-datetime-PeriodExtendedEnum.schema.json`).
- Schemas validate **shape only**. A CDM object that passes the schema can still break the model's own
  conditions, and functions are not represented at all.

JSON instances use the Rune serialization envelope described in `docs/serialization.md`: the document
identifies the model, version and fully qualified type, and metadata is carried in `@`-prefixed attributes.

```json
{
  "@model": "cdm",
  "@version": "7.0.0",
  "@type": "cdm.event.common.TradeState"
}
```

## 5. Worked walk-through: a fixed-versus-floating interest rate swap

The sequence below is the order that keeps the model valid: reference data first, then the product, then
the trade, then the events.

**Step 1 — Reference data.** Create the two `Party` objects with their `partyId PartyIdentifier`
(an `identifier` plus an `identifierType PartyIdentifierTypeEnum`), then two `Counterparty` objects whose
`role` is `CounterpartyRoleEnum -> Party1` and `Party2` and whose `partyReference` is a `[metadata reference]`
to those parties. Everything downstream refers to parties by role, so this is done once and first.

**Step 2 — The product shell.** Build a `NonTransferableProduct` with `economicTerms EconomicTerms`,
optionally populated `identifier ProductIdentifier` and `taxonomy ProductTaxonomy`. `EconomicTerms`
carries the dates that apply to every leg (`effectiveDate`, `terminationDate`, `dateAdjustments`) and the
`payout` list.

**Step 3 — The two legs.** The model's own IR-swap qualification expects a swap to have
`payout as InterestRatePayout count = 2`; each leg extends `PayoutBase`, so each has
`payerReceiver PayerReceiver`, a `priceQuantity ResolvablePriceQuantity`, `settlementTerms` and
`principalPayment`.

- Fixed leg: `rateSpecification -> FixedRateSpecification -> rateSchedule RateSchedule -> price
  PriceSchedule`.
- Floating leg: `rateSpecification -> FloatingRateSpecification`, carrying `rateOption InterestRateIndex`
  (a `FloatingRateIndex` with `floatingRateIndex FloatingRateIndexEnum` and `indexTenor`) and
  `spreadSchedule SpreadSchedule`.

Both legs then take the common interest-rate terms: `dayCountFraction DayCountFractionEnum`,
`calculationPeriodDates CalculationPeriodDates` (with `calculationPeriodFrequency CalculationPeriodFrequency`
holding `periodMultiplier`, `period PeriodExtendedEnum` and `rollConvention RollConventionEnum`),
`paymentDates PaymentDates` (with `paymentFrequency Frequency` and `payRelativeTo PayRelativeToEnum`) and,
on the leg that resets, `resetDates ResetDates` (with `resetFrequency ResetFrequency`, `fixingDates` and
`resetRelativeTo`).

**Step 4 — Price and quantity.** Put the notional and the rate where the model expects them: one
`TradeLot` with one `PriceQuantity` per leg. A `PriceQuantity` holds `quantity NonNegativeQuantitySchedule`
and `price PriceSchedule` (plus `observable` when the price is an observed rate), and each schedule is a
measure — a `value` with a `unit UnitType` whose `currency` names the currency. Do not stuff notional or
rate into the payout's own attributes; the `[metadata address]` annotations exist precisely so the
payout points at these objects.

**Step 5 — The execution event.** Everything so far is product data. The trade is created by an
`ExecutionInstruction`, which takes the `product NonTransferableProduct`, `priceQuantity PriceQuantity (1..*)`,
`counterparty Counterparty (2..2)`, `parties`, `executionDetails ExecutionDetails`, `tradeDate` and
`tradeIdentifier TradeIdentifier (1..*)`; `Create_Execution` turns it into a `TradeState`. To compose an
event, place that instruction inside a `BusinessEvent`, whose `instruction` is an `Instruction` holding a
`primitiveInstruction PrimitiveInstruction` and optionally a `before TradeState`; `Create_TradeState`
applies the composite instruction and returns the after state.

**Step 6 — Contract formation.** Confirmation is a separate primitive, not part of execution:
`ContractFormationInstruction` carries the `legalAgreement LegalAgreement (0..*)` — typically a
`MasterAgreement` or a `MasterAgreementSchedule` — and the formed contract shows up in
`Trade -> contractDetails ContractDetails`, which also holds `governingLaw GoverningLawEnum`.

**Step 7 — Qualification.** Do not assert the product type by hand. `ProductTaxonomy` values come from the
product qualification functions: the leaf functions that carry the `[qualification Product]` annotation —
for example `Qualify_InterestRate_IRSwap_FixedFloat` — take `economicTerms EconomicTerms (1..1)` and return
`is_product boolean (1..1)`, and they are built from un-annotated classification helpers such as
`Qualify_AssetClass_InterestRate`, `Qualify_BaseProduct_IRSwap` and `Qualify_SubProduct_FixedFloat`.
Likewise, the event's `eventQualifier` is derived by the `[qualification BusinessEvent]` functions such as
`Qualify_Execution`, `Qualify_ContractFormation` and `Qualify_Termination`, each taking a `BusinessEvent`
and returning `is_event boolean (1..1)`.

**Step 8 — Lifecycle.** Subsequent events reuse the same mechanism with a different primitive instruction:

| Business outcome | Primitive instruction | Effect on the after state |
| --- | --- | --- |
| Increase or partial termination | `QuantityChangeInstruction` (`direction QuantityChangeDirectionEnum`) | Adds a `TradeLot`, or amends an existing one |
| Amendment of the terms | `TermsChangeInstruction` | Replaces `product` and/or `ancillaryParty` |
| Fixing | `ResetInstruction` | Appends a `Reset` to `resetHistory` |
| Payment or delivery | `TransferInstruction` | Appends a `TransferState` to `transferHistory` |
| Exercise of an option | `ExerciseInstruction` | Applies an inner `PrimitiveInstruction` |
| Clearing, allocation or novation | `SplitInstruction` with `PartyChangeInstruction` breakdowns (`Create_Split`) | Produces several after states |
| Full termination | `QuantityChangeInstruction` taking every lot's quantity to zero | `positionState` becomes `PositionStatusEnum -> Closed`, and `Create_TradeState` sets `closedState` to `ClosedStateEnum -> Terminated` |

## 6. Checks before calling a model done

1. **Names.** Every type, attribute and enumeration value greps out of
   `rosetta-source/src/main/rosetta/`. Enumeration values are members of the named enumeration, not
   free strings.
2. **Cardinality.** Mandatory attributes are present and lists respect their lower bound —
   `TradableProduct` needs exactly two `counterparty` entries, `NonTransferableProduct` needs
   `economicTerms`, a payout needs a `PayerReceiver`.
3. **Conditions.** Read the `condition` blocks on the types you populated; they encode the rules that
   make the object a valid CDM object, and the JSON Schema will not catch them.
4. **Qualification.** The object's qualifiable constituents (the `Product`, the `BusinessEvent`) evaluate
   true against at least one qualification function. If not, the model is structurally plausible and
   still wrong.
5. **Lineage.** Every event that changes a trade points its `before` at the preceding `TradeState` (or is
   an execution, which has no before state), and cross-object links use `[metadata reference]` rather
   than duplicated data.
6. **Serialization.** The JSON document declares `@model`, `@version` and the fully qualified `@type`,
   and metadata uses `@key` / `@ref` / `@data` / `@scheme`.

When a name or a rule surprises you, the answer is in the Rune source or in the offline bundle at
`https://cdm.finos.org/okf.tar.gz` — not in a guess.
