Skip to content

Commit cc07291

Browse files
authored
Merge branch 'master' into chrisisla-patch-3
2 parents 01fcc8b + 7e9da8d commit cc07291

20 files changed

+317
-212
lines changed

RELEASE.md

Lines changed: 8 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,13 @@
1-
# _Product Model - Qualification of Total Return Swaps (TRS) with a Debt Underlier_
2-
3-
_Background_
4-
5-
Following ESMA Guidelines, Total Return Swaps with a debt instrument as their underlier (bond, loan, etc) must report field 2.11 - `Asset Class` as 'CRDT', while TRS on an equity index or a basket of equities should report `Asset Class` as 'EQUI'. Currently in the CDM, a Total Return Swap with a debt underlier is not classified correctly, and thus is being reported incorrectly as well. This release aims at fixing the `Qualify_AssetClass_Credit` function such that Total Return Swaps on a bond or a loan report AssetClass as 'CRDT'.
6-
7-
_What is being released?_
8-
9-
- The function `Qualify_AssetClass_Credit` is increasing its coverage to include Total Return Swaps with an underlier of a `loan` or a `securityType` of `debt`.
10-
11-
_Functions_
12-
13-
- Updated `Qualify_AssetClass_Credit` function to support Total Return Swap products, defined as having an `interestRatePayout` and a `performancePayout`. The function checks the `performancePayout` that `underlier -> loan` is present or that `underlier -> security -> securityType = Debt`.
14-
15-
_Review directions_
16-
17-
In Rosetta, select the Textual View and inspect the change identified above
18-
19-
The changes can be reviewed in PR: [#2855](https://github.com/finos/common-domain-model/pull/2855)
20-
21-
# _Product Model - Qualification of Foreign Exchange NDS_
22-
23-
_Background_
24-
25-
Currently, Foreign Exchange Non-Deliverable Swaps are not supported in the Common Domain Model. This release adds qualification support for this kind of product.
26-
27-
_What is being released?_
28-
29-
- Added the function `Qualify_ForeignExchange_NDS` that qualifies as true if a product has two forward payouts with an FX underlier and the `cashSettlementTerms` populated.
30-
_Review directions_
31-
32-
In the Rosetta platform, select the Textual View and inspect each of the changes identified above.
33-
34-
PR: [#2866](https://github.com/finos/common-domain-model/pull/2866)
35-
36-
# _Addition of new enumeration to AvailableInventory_
37-
38-
_Background_
39-
40-
The ```AvailableInventory``` type supports two major uses cases:
41-
42-
1. Where a lender wants to distribute the details of the securities that they have available to lend, and
43-
2. Where a borrower wants to locate specific securities that they want to borrow.
44-
45-
For the second use case, the ```SecurityLocate``` type was created, which extends ```AvailableInventory``` but has no additional attributes within it.
46-
47-
When using these two types, there is currently no way to differentiate between when the user is intending to implement use case 1 (i.e. use the ```AvailableInventory``` type) or use case 2 (i.e. use the ```SecurityLocate``` type).
48-
49-
As an example, the following valid JSON could represent a lender saying they have 10000 shares available of security GB00000000012, or a borrower requesting 10000 shares of the security.
50-
51-
```javascript
52-
{
53-
"availableInventoryRecord": [
54-
{
55-
"identifier": {
56-
"identifier": "00001"
57-
},
58-
"security": {
59-
"securityType": "Equity",
60-
"productIdentifier": {
61-
"identifier": "GB00000000012",
62-
"source": "ISIN"
63-
}
64-
},
65-
"availableQuantity": {
66-
"value": 10000
67-
}
68-
}
69-
]
70-
}
71-
```
1+
# _Python Generator v2_
722

733
_What is being released?_
4+
This release uses the new version of the Python generator (v2) which includes the following changes:
745

75-
A new ```AvailableInventoryTypeEnum``` enumeration has been added with two options:
76-
- _AvailableToLend_ - for where a party wants to expose the securities that they have available (i.e. use case 1)
77-
- _RequestToBorrow_ - for where a party wants to request specific securities from another party (i.e. use case 2)
78-
79-
The new enumeration has been added to the ```AvailableInventory``` type and named ```availableInventoryType```. It has been set with _(1..1)_ cardinality, making it mandatory.
80-
81-
The ```availableQuantity``` attribute within ```AvailableInventoryRecord``` has also been renamed to just ```quantity``` to make it more generic and thus applicable to more use cases.
82-
6+
- Migration to Pydantic 2.x
7+
- More comprehensive support for Rosetta's operators
8+
- Resolves the defect exposed by [PR 2766](https://github.com/finos/common-domain-model/pull/2766)
9+
- Includes an update to the Python Rosetta runtime library used to encapsulate the Pydantic support (now version 2.0.0)
10+
-
8311
_Review directions_
8412

85-
The changes can be reviewed in PR: [#2810](https://github.com/finos/common-domain-model/pull/2810)
86-
87-
# _Compatibility_
88-
This change introduces a new mandatory enumeration to an existing type. By definition this means that this is a breaking change, as any pre-existing implementation of the ```AvailableInventory``` or ```SecurityLocate``` type will no longer work.
89-
13+
The changes can be reviewed in PR: [#2869](https://github.com/finos/common-domain-model/pull/2869)

codefresh.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ steps:
117117
working_directory: ./rosetta-source
118118
shell: sh
119119
commands:
120-
- python3 -m pip install "pydantic==1.*"
120+
- export PYTHONDONTWRITEBYTECODE=1
121+
- python3 -m pip install pydantic
121122
- python3 -m pip install jsonpickle
122-
- python3 -m pip install ./target/classes/cdm/python/runtime/rosetta_runtime-1.0.0-py3-none-any.whl
123+
- python3 -m pip install ./target/classes/cdm/python/runtime/rosetta_runtime-2.0.0-py3-none-any.whl
123124
- |-
124125
python3 -m pip wheel --no-deps --only-binary :all: --wheel-dir ./target/classes/cdm/python ./target/classes/cdm/python
125126
- python3 -m pip install ./target/classes/cdm/python/python_cdm-*-py3-none-any.whl
126127
- python3 -m pip install pytest
127-
- pytest ./src/test/python/
128-
- rm -rf ./src/test/python/serialization/__pycache__ ./src/test/python/semantics/__pycache__
128+
- pytest -p no:cacheprovider ./src/test/python/
129129

130130
DeployDaml:
131131
stage: 'build'

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181

8282
<repoServerHost>oss.sonatype.org</repoServerHost>
8383

84-
<rosetta.bundle.version>10.15.7</rosetta.bundle.version>
84+
<rosetta.bundle.version>10.15.8</rosetta.bundle.version>
8585
<rosetta.code-gen.version>${rosetta.bundle.version}</rosetta.code-gen.version>
8686
<rosetta.dsl.version>9.7.0</rosetta.dsl.version>
8787

rosetta-source/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@
742742
<version>${rosetta.code-gen.version}</version>
743743
<type>jar</type>
744744
<outputDirectory>${project.build.directory}/classes/cdm/python</outputDirectory>
745-
<includes>runtime/rosetta_runtime-1.0.0-py3-none-any.whl</includes>
745+
<includes>runtime/rosetta_runtime-2.0.0-py3-none-any.whl</includes>
746746
</artifactItem>
747747
</artifactItems>
748748
<overWriteReleases>false</overWriteReleases>

rosetta-source/src/test/python/__init__.py

Whitespace-only changes.

rosetta-source/src/test/python/run_tests.sh

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,37 @@
1-
import pytest
1+
'''testing cardinality enforcement'''
22
import datetime
3+
import pytest
34
from cdm.base.datetime.DateList import DateList
45
from rosetta.runtime.utils import ConditionViolationError
56

67

78
def test_1_many_fail():
9+
'''DateList cannot be empty'''
810
dl = DateList(date=[])
911
with pytest.raises(ConditionViolationError):
1012
dl.validate_conditions()
1113

1214

13-
def test_1_many_fail_nopar():
15+
def test_1_many_fail_empty_constructor():
16+
'''DateList cannot be empty'''
1417
dl = DateList()
1518
with pytest.raises(ConditionViolationError):
1619
dl.validate_conditions()
1720

1821

1922
def test_1_many_pass():
23+
'''Valid DateList'''
2024
dl = DateList(date=[datetime.date(2020, 1, 1)])
2125
dl.validate_conditions()
2226

2327

2428
if __name__ == "__main__":
25-
print("first one")
26-
test_1_many_pass()
27-
print("second one")
28-
test_1_many_fail()
29-
print("third one")
30-
test_1_many_fail_nopar()
31-
32-
33-
# EOF
29+
print("test_1_many_pass")
30+
test_1_many_pass()
31+
print("test_1_many_fail")
32+
test_1_many_fail()
33+
print("test_1_many_fail_empty_constructor")
34+
test_1_many_fail_empty_constructor()
3435

3536

37+
# EOF
Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,50 @@
1+
'''Full attribute validation - pydantic and constraints'''
12
import pytest
23
from pydantic import ValidationError
34
from rosetta.runtime.utils import ConditionViolationError
45
from cdm.base.math.NonNegativeQuantity import NonNegativeQuantity
56
from cdm.base.math.UnitType import UnitType
7+
from cdm.base.datetime.Frequency import Frequency
8+
from cdm.base.datetime.PeriodExtendedEnum import PeriodExtendedEnum
69

7-
'''
8-
def test_recursive_conds():
9-
unit = UnitType(currency='EUR')
10-
mq = NonNegativeQuantity(value=10, unit=unit)
11-
mq.validate_model()
1210

13-
'''
14-
15-
def test_recursive_conds_base_fail():
11+
def test_recursive_conditions_base_fail():
12+
'''condition_0_AmountOnlyExists violation'''
1613
unit = UnitType(currency='EUR')
1714
mq = NonNegativeQuantity(unit=unit)
1815
with pytest.raises(ConditionViolationError):
1916
mq.validate_model()
2017

21-
def test_recursive_conds_direct_fail():
18+
19+
def test_recursive_conditions_direct_fail():
20+
'''Negative quantity condition violation'''
2221
unit = UnitType(currency='EUR')
2322
mq = NonNegativeQuantity(value=-10, unit=unit)
2423
with pytest.raises(ConditionViolationError):
2524
mq.validate_model()
2625

2726

28-
def test_attrib_validity():
27+
def test_bad_attrib_validation():
28+
'''Invalid attribute assigned'''
2929
unit = UnitType(currency='EUR')
3030
mq = NonNegativeQuantity(value=10, unit=unit)
3131
mq.frequency = 'Blah'
3232
with pytest.raises(ValidationError):
3333
mq.validate_model()
3434

35+
36+
def test_correct_attrib_validation():
37+
'''Valid attribute assigned'''
38+
unit = UnitType(currency='EUR')
39+
mq = NonNegativeQuantity(value=10, unit=unit)
40+
mq.frequency = Frequency(periodMultiplier=1, period=PeriodExtendedEnum.M)
41+
mq.validate_model()
42+
43+
3544
if __name__ == "__main__":
36-
test_recursive_conds_base_fail()
37-
test_recursive_conds_direct_fail()
38-
test_attrib_validity()
45+
test_recursive_conditions_base_fail()
46+
test_recursive_conditions_direct_fail()
47+
test_bad_attrib_validation()
48+
test_correct_attrib_validation()
3949

4050
# EOF
Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,10 @@
11
import pytest
22
from rosetta.runtime.utils import ConditionViolationError
33
from rosetta.runtime.utils import if_cond
4-
##from cdm.base.math.Measure import Measure
54
from cdm.base.math.QuantitySchedule import QuantitySchedule
65
from cdm.base.math.UnitType import UnitType
7-
##from drr.regulation.cftc.rewrite.CFTCPart43TransactionReport import CFTCPart43TransactionReport
6+
from rosetta.runtime.utils import _resolve_rosetta_attr
87

9-
10-
'''
11-
def test_if_cond_pass():
12-
unit = UnitType(currency='EUR')
13-
multiplier = Measure(value=1)
14-
qs = QuantitySchedule(value=1, unit=unit, multiplier=multiplier)
15-
qs.validate_conditions()
16-
17-
18-
def test_if_cond_fail():
19-
unit = UnitType(currency='EUR')
20-
multiplier = Measure(value=-1)
21-
qs = QuantitySchedule(unit=unit, multiplier=multiplier)
22-
with pytest.raises(ConditionViolationError):
23-
qs.validate_conditions()
24-
25-
'''
268
def test_if_cond_literals():
279
class T:
2810
def __init__(self):
@@ -48,18 +30,6 @@ def __init__(self):
4830
self)
4931
assert res
5032

51-
'''
52-
def test_if_cond_any():
53-
class T:
54-
def __init__(self):
55-
self.actionType = "TERM"
56-
self.eventType = 'CORP'
57-
self = T()
58-
fnc = CFTCPart43TransactionReport.condition_0_EventTypeCondition
59-
res = fnc(self)
60-
assert res
61-
62-
'''
6333
def test_if_direct():
6434
class T:
6535
def __init__(self):
@@ -75,7 +45,10 @@ def __init__(self):
7545
'True', T())
7646

7747
if __name__ == "__main__":
78-
test_if_cond_literals()
79-
test_if_direct()
48+
print('test_if_cond_literals',end='')
49+
test_if_cond_literals()
50+
print('...passed\ntest_if_direct', end='')
51+
test_if_direct()
52+
print('...passed')
8053

8154
# EOF
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'''Tests of the local registration of conditions'''
2+
import inspect
3+
import pytest
4+
from rosetta.runtime.utils import rosetta_local_condition
5+
from rosetta.runtime.utils import execute_local_conditions
6+
from rosetta.runtime.utils import ConditionViolationError
7+
8+
9+
def test_pre_post_conditions():
10+
'''Tests the registration of functions in two different registries'''
11+
_pre_registry = {}
12+
_post_registry = {}
13+
self = inspect.currentframe()
14+
15+
# A local PRE condition
16+
@rosetta_local_condition(_pre_registry)
17+
def some_local_condition():
18+
print(f'Pre {self}')
19+
return True
20+
21+
# A local POST condition
22+
@rosetta_local_condition(_post_registry)
23+
def some_local_post_condition():
24+
print(f'Post {self}')
25+
return True
26+
27+
# Check all PRE conditions
28+
execute_local_conditions(_pre_registry, 'Pre-condition')
29+
30+
print('Some Code....')
31+
32+
# Check all POST conditions
33+
execute_local_conditions(_post_registry, 'Post-condition')
34+
35+
36+
def test_raise_local_cond():
37+
'''checks if exception is raised and it is of the correct type'''
38+
_registry = {}
39+
@rosetta_local_condition(_registry)
40+
def some_failing_local_post_condition():
41+
return False
42+
43+
with pytest.raises(ConditionViolationError):
44+
execute_local_conditions(_registry, 'condition')
45+
46+
47+
if __name__ == '__main__':
48+
test_pre_post_conditions()
49+
test_raise_local_cond()
50+
51+
# EOF

0 commit comments

Comments
 (0)