Skip to content

Commit

Permalink
Merge branch 'master' into website-changes2
Browse files Browse the repository at this point in the history
  • Loading branch information
eteridvalishvili authored Nov 15, 2024
2 parents 2b910fa + 7ab3505 commit 68c70d9
Show file tree
Hide file tree
Showing 145 changed files with 6,413 additions and 2,865 deletions.
42 changes: 6 additions & 36 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,14 @@

# _Product Model - Asset Refactoring in AssetCriteria_

_Background_

The Asset Refactoring initiative (see https://github.com/finos/common-domain-model/issues/2805) is seeking to improve the Product Model to address some long-standing issues and to ensure the continued extensibility to additional financial products and markets. A proposal has been agreed - through a cross-industry Task Force - to implement this remodelling in the CDM.

This release includes some additional functionality (following three planned major tranches of work in CDM 6 to implement the refactored model).

_What is being released?_

AssetCriteria:
- The attribute `assetIdentifier` has been refactored to model an actual asset, specified using the `Asset` choice data type, rather than just an identifier. The attribute name has also been updated to `specificAssets` to make it clear that it is a list of specific assets, all of whom are eligible to be pledged as collateral. The condition on the data type has been updated too.

ListingType:
- The cardinality of the three attributes in the data type `ListingType` (which is used in `AssetCriteria`) has been changed to none-to-many (rather than none or one); the attributes are `exchange`, `sector`, `index`. Without this, it would be only possible to select one of the values.

_Review directions_

The changes can be reviewed in PR: [#3228](https://github.com/finos/common-domain-model/pull/3228)

_Backward-incompatible changes_

This release contains changes that are not backward-compatible:
- All references to the attribute `assetIdentifier` on `AssetCriteria` need to be updated as referenced; the new attribute is `asset`.

# _Mapping Update - InterestRateForwardDebtPriceMappingProcessor updated to handle 'Percentage' quoteUnits_
# _CDM Model - CapacityUnit Enum_

_Background_

The price of bond forwards is captured as a monetary value whereas it should be a decimal/percentage. Even if the value in FpML was 'Percentage', the CDM representation value did not accurately represent this, causing misinterpretations.
In has been seen that in the ExternalUnitOfMeasure1Code from the 2Q2024 ISO External CodeSets v1, the unity Joule is supported in the Enum. However, in CDM this is not the case, as it does not appear anywhere in the CapacityUnitEnum. Therefore, the Joule unit of measure will be added to the CapacityUnitEnum for completeness and to align with 2Q2024 ISO External CodeSets v1, for versions 5 and 6 of CDM.

_What is being released?_

- An update to the **InterestRateForwardDebtPriceMappingProcessor** code to fix the described issue. This change, would correct the interpretation by dividing the current monetary value by 100, when the *quoteUnits* corresponds to the XML value '*Percentage*'.
- The **bond-fwd-generic-ex01.xml** and **bond-fwd-generic-ex02.xml** samples have been updated as the files were using the value 'Percent' but the correct value according to the enum should be 'Percentage'


_Review directions_
- Updated `CapacityUnitEnum` in cdm.base.math

In Rosetta, select the Textual Browser and inspect each of the changes identified above.
_Enumerations_

The changes can be reviewed in PR: [#3242](https://github.com/finos/common-domain-model/pull/3242)
- Updated `CapacityUnitEnum` by adding 'J' to support Joule unit

The changes can be reviewed in PR: [#3198](https://github.com/finos/common-domain-model/pull/3198)
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package cdm.event.common.processor;

import cdm.base.staticdata.party.PartyRole;
import cdm.base.staticdata.party.PartyRoleEnum;
import cdm.base.staticdata.party.metafields.ReferenceWithMetaParty;
import cdm.event.common.Trade;
import com.regnosys.rosetta.common.translation.Mapping;
import com.regnosys.rosetta.common.translation.MappingContext;
import com.regnosys.rosetta.common.translation.MappingProcessor;
import com.regnosys.rosetta.common.translation.Path;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.path.RosettaPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.subPath;
import static com.regnosys.rosetta.common.translation.MappingProcessorUtils.updateMappingSuccess;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;

/**
* Created by Tradeheader, SL
*
* @author gopazoTH
* @since 18/09/2024
*/

@SuppressWarnings("unused")
public class RelatedPartyRoleMappingProcessor extends MappingProcessor {

private static final Logger LOGGER = LoggerFactory.getLogger(RelatedPartyRoleMappingProcessor.class);

public RelatedPartyRoleMappingProcessor(RosettaPath modelPath, List<Path> synonymPaths, MappingContext context) {
super(modelPath, synonymPaths, context);
}

@Override
public void map(Path synonymPath, List<? extends RosettaModelObjectBuilder> builder, RosettaModelObjectBuilder parent) {

Collection<PartyRoleMapping> partyRoleMappings = getMappings().stream()
// find all partyTradeInformation.relatedParty mappings
.filter(m -> synonymPath.getParent().nameStartMatches(m.getXmlPath()))
// group by relatedParty path index
.collect(Collectors.groupingBy(this::getKey, collectingAndThen(toList(), this::toPartyRoleMapping)))
.values();

partyRoleMappings.forEach(m -> {
Trade.TradeBuilder tradeBuilder = (Trade.TradeBuilder) parent;

// Check if m.role is non-null before calling fromDisplayName
if (m != null && m.role != null) {
try {
// Normalize the XML value to match the enum constant (convert to uppercase and replace spaces/underscores)
PartyRoleEnum roleEnum = PartyRoleEnum.fromDisplayName(m.role);

// Create the PartyRoleBuilder and add the necessary values
PartyRole.PartyRoleBuilder partyRoleBuilder = PartyRole.builder()
.setRole(roleEnum);

// Check if m.partyReference is non-null before setting it
if (m.partyReference != null) {
partyRoleBuilder.setPartyReference(ReferenceWithMetaParty.builder()
.setExternalReference(m.partyReference));
}

// Check if m.ownershipPartyReference is non-null before setting it
if (m.ownershipPartyReference != null) {
partyRoleBuilder.setOwnershipPartyReference(ReferenceWithMetaParty.builder()
.setExternalReference(m.ownershipPartyReference));
}

// Add the PartyRole to the trade
tradeBuilder.addPartyRole(partyRoleBuilder);

// Update mappings if they are non-null
if (m.partyReferenceMapping != null && m.partyReferenceModelPath != null) {
updateMappingSuccess(m.partyReferenceMapping, m.partyReferenceModelPath);
}
if (m.roleMapping != null && m.roleModelPath != null) {
updateMappingSuccess(m.roleMapping, m.roleModelPath);
}
}
catch (IllegalArgumentException e) {
// If the value is not a valid enum constant, do nothing and skip this iteration
LOGGER.warn("Invalid PartyRoleEnum: " + m.role, e);

}
}


});
}

/**
* Group by path index, e.g. collect all mappings under relatedParty(index)
* (nonpublicExecutionReport.trade.tradeHeader.partyTradeInformation(0).relatedParty(10))
*/
private String getKey(Mapping m) {
int partyTradeInformationIndex = getPathIndex(m, "partyTradeInformation");
int relatedPartyIndex = getPathIndex(m, "relatedParty");
return partyTradeInformationIndex + "-" + relatedPartyIndex;
}

private int getPathIndex(Mapping m, String elementName) {
return subPath(elementName, m.getXmlPath())
.map(Path::getLastElement)
.map(Path.PathElement::forceGetIndex)
.orElse(-1);
}

private PartyRoleMapping toPartyRoleMapping(List<Mapping> mappings) {
try {
if (!mappings.isEmpty()) {
// Get the xmlPath from the first element in the mappings list
String xmlPath = mappings.get(0).getXmlPath().toString();

// Extract the index value from partyTradeInformation(x) in the xmlPath
Matcher matcher = Pattern.compile(".*partyTradeInformation\\((\\d+)\\).*").matcher(xmlPath);

if (matcher.find()) {
String index = matcher.group(1); // This captures the index (x) from partyTradeInformation(x)

// Dynamically build the pattern using the extracted index
Pattern dynamicPattern = Pattern.compile(".*partyTradeInformation\\(" + index + "\\)\\.partyReference.*");

// Apply the dynamic pattern to filter mappings
Mapping ownershipPartyReferenceMapping = getMapping(
getMappings().stream()
.filter(m -> m.getXmlValue() != null && dynamicPattern.matcher(m.getXmlPath().toString()).matches())
.collect(Collectors.toList()),
Path.parse("partyReference.href")
);

// Proceed with your existing mapping logic
RosettaPath ownershipPartyReferenceModelPath = getModelPath().newSubPath("ownershipPartyReference").newSubPath("externalReference");
Mapping partyReferenceMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("partyReference.href"));
RosettaPath partyReferenceModelPath = getModelPath().newSubPath("partyReference").newSubPath("externalReference");
Mapping roleMapping = getMapping(mappings.stream().filter(m -> m.getXmlValue() != null).collect(Collectors.toList()), Path.parse("role"));
RosettaPath roleModelPath = getModelPath().newSubPath("role");

// Build and return the PartyRoleMapping if the mappings are valid
if (partyReferenceMapping != null && partyReferenceMapping.getXmlValue() != null
&& roleMapping != null && roleMapping.getXmlValue() != null) { return new PartyRoleMapping(
String.valueOf(ownershipPartyReferenceMapping.getXmlValue()),
ownershipPartyReferenceMapping,
ownershipPartyReferenceModelPath,
String.valueOf(partyReferenceMapping.getXmlValue()),
partyReferenceMapping,
partyReferenceModelPath,
String.valueOf(roleMapping.getXmlValue()),
roleMapping,
roleModelPath
);
}
}
}
} catch (Exception e) {
LOGGER.error("Failed to build party role mapping from mappings {}", mappings, e);
}
return null;
}


private Mapping getMapping(List<Mapping> mappings, Path endsWith) {
return mappings.stream().filter(m -> m.getXmlPath().endsWith(endsWith)).findFirst().orElse(null);
}

private static class PartyRoleMapping {

private final String ownershipPartyReference;
private final Mapping ownershipPartyReferenceMapping;
private final RosettaPath ownershipPartyReferenceModelPath;
private final String partyReference;
private final Mapping partyReferenceMapping;
private final RosettaPath partyReferenceModelPath;
private final String role;
private final Mapping roleMapping;
private final RosettaPath roleModelPath;

public PartyRoleMapping(String ownershipPartyReference, Mapping ownershipPartyReferenceMapping, RosettaPath ownershipPartyReferenceModelPath,String partyReference, Mapping partyReferenceMapping, RosettaPath partyReferenceModelPath, String role, Mapping roleMapping, RosettaPath roleModelPath) {
this.ownershipPartyReference = ownershipPartyReference;
this.ownershipPartyReferenceMapping = ownershipPartyReferenceMapping;
this.ownershipPartyReferenceModelPath = ownershipPartyReferenceModelPath;
this.partyReference = partyReference;
this.partyReferenceMapping = partyReferenceMapping;
this.partyReferenceModelPath = partyReferenceModelPath;
this.role = role;
this.roleMapping = roleMapping;
this.roleModelPath = roleModelPath;
}
}
}
Loading

0 comments on commit 68c70d9

Please sign in to comment.