diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5edb406 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/archive/build-publish.yml b/.github/archive/build-publish.yml new file mode 100644 index 0000000..3d91708 --- /dev/null +++ b/.github/archive/build-publish.yml @@ -0,0 +1,59 @@ +name: build-publish + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + + build: + + runs-on: ubuntu-latest + + env: + AZURE_CONTAINER_REGISTRY_SERVER: ${{ 'daprapps.azurecr.io' }} + APP_NAME: ${{ 'books_and_records' }} + PUBLISHER_NAME: ${{ 'node_publisher' }} + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up node + uses: actions/setup-node@v1.4.1 + with: + node-version: '12.14.1' + + - name: Set up elm + uses: justgook/setup-elm@v1 + + - name: Compile morphir application + run: | + npm install -g morphir-elm + morphir-dapr + + - name: Login to azure container registry + uses: azure/docker-login@v1 + with: + login-server: ${{ env.AZURE_CONTAINER_REGISTRY_SERVER }} + username: ${{ secrets.SERVICE_PRINCIPAL_CLIENT_ID }} + password: ${{ secrets.SERVICE_PRINCIPAL_SECRET }} + + - name: Push app image to azure container registry + run: | + docker build . -t ${{ env.AZURE_CONTAINER_REGISTRY_SERVER }}/${{ env.APP_NAME }}:${{ github.sha }} + docker push ${{ env.AZURE_CONTAINER_REGISTRY_SERVER }}/${{ env.APP_NAME }}:${{ github.sha }} + + - name: Push publisher image to azure container registry + run: | + cd publisher + docker build . -t ${{ env.AZURE_CONTAINER_REGISTRY_SERVER }}/${{ env.PUBLISHER_NAME }}:latest + docker push ${{ env.AZURE_CONTAINER_REGISTRY_SERVER }}/${{ env.PUBLISHER_NAME }}:latest + + - name: Azure login + uses: azure/login@v1 + with: + creds: ${{ secrets.SERVICE_PRINCIPAL_AUTH }} diff --git a/.gitignore b/.gitignore index 8ea8aa3..2624099 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Node node_modules/ dist/ - +.idea/ # Elm elm-stuff/ @@ -12,3 +12,12 @@ elm-stuff/ morphir-interface.json morphir-implementation.json morphir-version.json +morphir-ir.json + +.fake +.ionide + +*.iml +.metals/ +.settings.json + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5ba36cc --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at morphir-dev [at] morganstanley.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..63906ae --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,143 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish +to make via issue, email, or any other method with the owners of this repository +before making a change. + +Please note we have a code of conduct, please follow it in all your interactions +with the project. + +## Before your first pull request + +If you have not previously contributed to the project, +you must first create a *Developer Certificate of Origin* (“DCO”) and include a +reference to this DCO in each of your commits. In addition, if you subsequently +wish to contribute code having a different copyright ownership, then you must create +a new DCO for such contribution. + +To create a DCO please follow these steps: + +1. For code you are contributing, determine who is/are the copyright owner(s). +Please note that your employer may own the copyright in code you have written even +where the code was not created during regular working hours. Copyright law is +variable from jurisdiction to jurisdiction. Accordingly, consult your employer +or a lawyer if you are not sure. +2. Fill out the [DCO] replacing all `<>` terms as appropriate, and place the +completed DCO in a file under `dco/` or if you are not the copyright +holder then in a file under `dco/-`. + 1. Please note that the name you provide (``) must be your real + (legal) name; we will not accept aliases, pseudonyms or anonymous + contributions. + 1. If you’ve determined that the copyright holder of the code that you’ve + written is an entity other than yourself (e.g., your employer), then + include the legal name of the copyright holder(s) (``). + You must ensure that you are authorized by the copyright holder(s) to be able + to grant the licenses under the DCO for the purpose of contributing to the + project. Negotiating such authorization and administering the terms is + entirely between you and the copyright holder(s). +3. Issue a pull request with the DCO. + +## Pull request process + +When you create a pull request, follow these steps: + +1. Your commit message for the code you are submitting must include a +`“Covered by “` line which indicates your acceptance of the DCO terms and conditions. +`` here is the file name of the DCO. +2. Your commit must include a change to the `NOTICE.txt` file that contains complete +details of any applicable copyright notice for your submission and including any +applicable third party license(s) or other restrictions associated with any part +of your contribution, and of all matters required to be disclosed under such third +party license(s) (such as any applicable copyright, patent, trademark, and attribution +notices, and any notices relating to modifications made to open source software). +Note your contribution must retain all applicable copyright, patent, trademark and +attribution notices. + +## Pull request guidelines + +* Update the `README.md` docs with details of changes to API, behaviour, features, or usage. +Include useful file locations and relevant documentation. +* Update an existing or add a new testcase for your change. +* Ensure any install or build artefacts are removed from the pull request. +* We generally prefer squashed commits, unless multi-commits add clarity or are +required for mixed copyright commits. +* You may merge the Pull Request in once the build has passed and you have the sign-off +of one other developer, or if you do not have permission to do that, you may request the +reviewer to merge it for you. + + +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at morphir-dev [at] morganstanley.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ +[DCO]: dco/dco_template.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..64a375e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM node:8-alpine +WORKDIR /app +COPY dapr-output/ . +RUN npm install +EXPOSE 3000 +CMD [ "node", "DaprAppShell.js" ] \ No newline at end of file diff --git a/LICENSE copy b/LICENSE copy new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE copy @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.spdx b/LICENSE.spdx new file mode 100644 index 0000000..4977b23 --- /dev/null +++ b/LICENSE.spdx @@ -0,0 +1,7 @@ +SPDXVersion: SPDX-2.0 +DataLicense: CC0-1.0 +Creator: Morgan Stanley +PackageName: Morphir +PackageOriginator: Morgan Stanley +PackageHomePage: https://github.com/finos/morphir +PackageLicenseDeclared: Apache-2.0 diff --git a/NOTICE b/NOTICE index 53824ff..ad14fb0 100644 --- a/NOTICE +++ b/NOTICE @@ -1,3 +1,5 @@ -Morphir -Copyright 2020 Morgan Stanley. -This product includes software developed at Morgan Stanley. \ No newline at end of file +Morphir - FINOS +Copyright 2014 - 2020 Morgan Stanley + +This product includes software developed at the Fintech Open Source Foundation (https://www.finos.org/). +This product includes software developed at Morgan Stanley. diff --git a/README.md b/README.md index a95ddb8..94e512c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +![build-publish](https://github.com/Morgan-Stanley/morphir-examples/workflows/build-publish/badge.svg?branch=master) + # Overview This repo contains various models that represent the kinds of business problems that are common across enterprises like financial institutions. @@ -5,5 +7,9 @@ This repo contains various models that represent the kinds of business problems ## Code Structure - *[Rules](src/Morphir/Sample/Rules/README.md)* - Examples of modeling business rules in Elm that are traditionally implemented using rules engines. -- *[LCR](src/Morphir/Sample/LCR/README.md)* - An example of modeling regulatory reporting that includes complex calculations. -- *[Apps](src/Morphir/Sample/Apps/README.md)* - Examples of modeling entire business applications and their interactions. \ No newline at end of file +- *[LCR](src/Morphir/Sample/Reg/LCR/README.md)* - An example of modeling regulatory reporting that includes complex calculations. +- *[Apps](src/Morphir/Sample/Apps/README.md)* - Examples of modeling entire business applications and their interactions. + +### Compile example + +morphir-dapr -d diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..7054573 --- /dev/null +++ b/app.yaml @@ -0,0 +1,42 @@ +kind: Service +apiVersion: v1 +metadata: + name: books-and-records-app + labels: + app: books-and-records +spec: + selector: + app: books-and-records + ports: + - protocol: TCP + port: 80 + targetPort: 3000 + type: LoadBalancer + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: books-and-records-app + labels: + app: books-and-records +spec: + replicas: 1 + selector: + matchLabels: + app: books-and-records + template: + metadata: + labels: + app: books-and-records + annotations: + dapr.io/enabled: "true" + dapr.io/id: "books-and-records" + dapr.io/port: "3000" + spec: + containers: + - name: books-and-records + image: daprapps.azurecr.io/books_and_records:9355b913ec196c3280c3931f45cb74177589b479 + ports: + - containerPort: 3000 + imagePullPolicy: IfNotPresent diff --git a/docs/springboot/README.md b/docs/springboot/README.md new file mode 100644 index 0000000..d6b7601 --- /dev/null +++ b/docs/springboot/README.md @@ -0,0 +1,105 @@ +# morphir-elm SpringBoot generator + +[Morphir](https://github.com/finos/morphir) is a multi-language system built on a data format that captures an +application's domain model and business logic in a technology agnostic manner. This document will guide you on +how to write your business logic in [Elm](https://elm-lang.org/), parse it into Morphir IR and transpile +it to [Spring Boot](https://spring.io/projects/spring-boot/). + +## Prerequisites + +Morphir-elm package installed. Installation instructions: [morphir-elm installation](Readme.md) + +If you make changes to the morphir-elm code, you should run + +``` +npm run build +``` + +## Translate Elm sources to Morphir IR + +For detailed instructions refer to [morphir-elm installation](Readme.md) + +Example: +If we have a file with module name defined as: +``` +module Morphir.Reference.Model.BooksAndRecords exposing (..) +``` +**Important**: Currently, the Spring Boot generator works with custom types with at least one argument. + +then our folder structure might be +``` +exampleIR +| morphir.json +| |example +| | |Morphir +| | | |Reference +| | | | |Model +| | | | BooksAndRecords.elm +``` + +The morphir.json file should be something like this + +``` +{ + "name": "Morphir.Reference.Model", + "sourceDirectory": "example", + "exposedModules": [ + + "BooksAndRecords" + ] +} +``` + +Finally to translate to IR +- Go to command line +- Navigate to ExampleIR folder (where morphir.json is located) +- Execute + ``` + morphir-elm make + ``` +A morphir-ir.json file should be generated with the IR content + +## Transpil Morphir IR to a Spring Boot Project + + +- Run the following command + +``` + morphir-elm gen -t SpringBoot -i [inputFile] -o [outputFolder] +``` +where +- [inputFile] is path to the IR generated file. In the previous example should be: ``` exampleIR/morphir-ir.json ``` +- [outputFolder] path where the Spring Boot project will be generated. + +## Running the Spring Boot Project with Intellij + +- Open the folder with Intellij +- It may ask to setup scala SDK +- Create a configuration of type "Spring Boot" in order to run the project +- The folder ```src/main/java``` should have the Scala source code files +- Run the application +- Check that the server starts up without problems. If it has some errors, check if there are dependencies that should be added or changed. + + +## Connect to the Spring Boot application +- Open a REST API client (example: POSTMAN) +- Execute a POST operation ```http://localhost:8081/commandhttp``` + +The code generator currently supports Jackson, the body of the POST operation should be +``` + { + "type": "[commandsubclass]", + "arg1": "[value1]", + "arg2": "[value2]", + ....} +``` +```[commandsubtype]``` is a command sub class. Depending on the application how many arguments should be passed. + + +## License + +Copyright 2014 Morgan Stanley + +Distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). + +SPDX-License-Identifier: [Apache-2.0](https://spdx.org/licenses/Apache-2.0) diff --git a/elm.json b/elm.json index eba3f9c..b7c3b0a 100644 --- a/elm.json +++ b/elm.json @@ -1,28 +1,46 @@ { - "type": "application", - "source-directories": [ - "src", - "tests" - ], - "elm-version": "0.19.0", - "dependencies": { - "direct": { - "elm/core": "1.0.2", - "elm/json": "1.1.3", - "elm/parser": "1.1.0", - "elm/random": "1.0.0", - "elm/time": "1.0.0", - "elm-explorations/test": "1.2.2", - "justinmimbs/date": "3.2.0", - "morphir/morphir-elm": "1.0.0" + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/time": "1.0.0", + "elm-explorations/test": "1.2.2", + "finos/morphir-elm": "9.0.0", + "justinmimbs/date": "3.2.1", + "mgold/elm-nonempty-list": "4.1.0" + }, + "indirect": { + "avh4/elm-fifo": "1.0.4", + "chain-partners/elm-bignum": "1.0.1", + "cmditch/elm-bigint": "1.0.1", + "elm/json": "1.1.3", + "elm/parser": "1.1.0", + "elm/random": "1.0.0", + "elm/regex": "1.0.0", + "elm/svg": "1.0.1", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2", + "elm-community/graph": "6.0.0", + "elm-community/intdict": "3.0.0", + "elm-community/json-extra": "4.3.0", + "elm-community/list-extra": "8.2.4", + "elm-community/maybe-extra": "5.2.0", + "mdgriffith/elm-ui": "1.1.8", + "pzp1997/assoc-list": "1.0.0", + "rtfeldman/elm-hex": "1.0.0", + "rtfeldman/elm-iso8601-date-strings": "1.1.3", + "stil4m/elm-syntax": "7.1.3", + "stil4m/structured-writer": "1.0.3" + } }, - "indirect": { - "elm/html": "1.0.0", - "elm/virtual-dom": "1.0.2" + "test-dependencies": { + "direct": {}, + "indirect": {} } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - } -} \ No newline at end of file +} diff --git a/lobo.json b/lobo.json index cf7b12c..63e1cbd 100644 --- a/lobo.json +++ b/lobo.json @@ -1,26 +1,49 @@ { "type": "application", - "elm-version": "0.19.0", + "elm-version": "0.19.1", "source-directories": [ - "../src", "../tests", + "../src", "../node_modules/lobo/runner", - "../node_modules/lobo/plugin/elm-test" + "../node_modules/lobo/plugin/elm-test", + "../node_modules/lobo/plugin/elm-test-extra" ], "dependencies": { "direct": { + "benansell/lobo-elm-test-extra": "3.0.0", + "elm-community/graph": "6.0.0", "elm-explorations/test": "1.2.2", - "elm/core": "1.0.2", + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", "elm/json": "1.1.3", "elm/parser": "1.1.0", "elm/random": "1.0.0", + "elm/regex": "1.0.0", "elm/time": "1.0.0", - "justinmimbs/date": "3.2.0" - "morphir/elm": "1.0.0" + "finos/morphir-elm": "11.1.0", + "justinmimbs/date": "3.2.1", + "mgold/elm-nonempty-list": "4.2.0", + "myrho/elm-round": "1.0.4", + "stil4m/elm-syntax": "7.2.1" }, "indirect": { - "elm/html": "1.0.0", - "elm/virtual-dom": "1.0.2" + "avh4/elm-fifo": "1.0.4", + "chain-partners/elm-bignum": "1.0.1", + "cmditch/elm-bigint": "1.0.1", + "elm-community/intdict": "3.0.0", + "elm-community/list-extra": "8.2.4", + "elm-community/maybe-extra": "5.2.0", + "elm/bytes": "1.0.8", + "elm/file": "1.0.5", + "elm/svg": "1.0.1", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2", + "mdgriffith/elm-ui": "1.1.8", + "mpizenberg/elm-pointer-events": "4.0.2", + "pzp1997/assoc-list": "1.0.0", + "rtfeldman/elm-hex": "1.0.0", + "stil4m/structured-writer": "1.0.3" } }, "test-dependencies": { diff --git a/morphir.json b/morphir.json new file mode 100644 index 0000000..0d9d182 --- /dev/null +++ b/morphir.json @@ -0,0 +1,19 @@ +{ + "name": "Morphir/Sample", + "sourceDirectory": "src", + "exposedModules": [ + "Reg.LCR.FedCodeRules" + , "Reg.LCR.Calculations" + , "LCR/Basics" + , "LCR/Calculations" + , "LCR/Counterparty" + , "LCR/Flows" + , "LCR/Inflows" + , "LCR/MaturityBucket" + , "LCR/Outflows" + , "LCR/Product" + , "LCR/Rules" + , "Rules/Direct" + , "Rules/RuleSet" + ] +} diff --git a/morphir.json.bak b/morphir.json.bak new file mode 100644 index 0000000..f058a00 --- /dev/null +++ b/morphir.json.bak @@ -0,0 +1,33 @@ +{ + "name": "Morphir.Sample", + "sourceDirectory": "src", + "exposedModules": + [ "Apps/Shared/Client" + , "Apps/Shared/Market" + , "Apps/Shared/Price" + , "Apps/Shared/Product" + , "Apps/Shared/Quantity" + , "Apps/Shared/Rate" + , "Apps/BooksAndRecords/App" + , "Apps/BooksAndRecords/Deal" + , "Apps/Order/ACL" + , "Apps/Order/App" + , "Apps/Order/Order" + , "Apps/Trader/App" + , "Apps/Trader/Logic" + , "Apps/Upstream/Market/App" + , "Apps/Upstream/Product/App" + , "Apps/Upstream/Trading/App" + , "LCR/Basics" + , "LCR/Calculations" + , "LCR/Counterparty" + , "LCR/Flows" + , "LCR/Inflows" + , "LCR/MaturityBucket" + , "LCR/Outflows" + , "LCR/Product" + , "LCR/Rules" + , "Rules/Direct" + , "Rules/RuleSet" + ] +} diff --git a/package.json b/package.json index e8299e8..beb2d8b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,15 @@ "keywords": [ "morphir-model" ], + "scripts": { + "build": "elm make && morphir-elm make", + "test": "lobo --framework=elm-test", + "train-build-release": "train-build-release" + }, "devDependencies": { - "@morphir/morphir-elm": "latest" + "lobo": "^1.0.0" + }, + "dependencies": { + "morphir-elm": "^2.30.4" } } diff --git a/publisher/Dockerfile b/publisher/Dockerfile new file mode 100644 index 0000000..4003348 --- /dev/null +++ b/publisher/Dockerfile @@ -0,0 +1,6 @@ +FROM node:8-alpine +WORKDIR /app +COPY . . +RUN npm install +EXPOSE 3000 +CMD [ "node", "node-server.js" ] \ No newline at end of file diff --git a/publisher/node-publisher.js b/publisher/node-publisher.js new file mode 100644 index 0000000..5d1c295 --- /dev/null +++ b/publisher/node-publisher.js @@ -0,0 +1,68 @@ +/* +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +const readline = require('readline') +const http = require('http') + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}) + +function ask() { + + rl.question('Topic to publish message to: ', topic => { + + rl.question('JSON message to publish: \n', msg => { + + const options = { + hostname: 'localhost', + port: 3500, + path: `/v1.0/publish/${topic}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + } + + const req = http.request(options, (res) => { + console.log(`STATUS: ${res.statusCode}`) + console.log(`HEADERS: ${JSON.stringify(res.headers)}`) + res.setEncoding('utf8') + res.on('data', (chunk) => { + console.log(`BODY: ${chunk}`) + }) + res.on('end', () => { + console.log('Message sent!') + ask() + }) + }) + + req.on('error', (e) => { + console.error(`problem with request: ${e.message}`) + console.log("Try again: \n") + ask() + }) + + req.write(msg) + req.end() + + }) + }) +} + +console.log("Json message publisher. Dapr URL: 'localhost/3500/v1.0/publish/' \n") +ask() \ No newline at end of file diff --git a/publisher/node-publisher.yaml b/publisher/node-publisher.yaml new file mode 100644 index 0000000..b3b6f4a --- /dev/null +++ b/publisher/node-publisher.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: node-publisher + labels: + app: node-publisher +spec: + replicas: 1 + selector: + matchLabels: + app: node-publisher + template: + metadata: + labels: + app: node-publisher + annotations: + dapr.io/enabled: "true" + dapr.io/id: "node-publisher" + dapr.io/port: "3000" + spec: + containers: + - name: node-publisher + image: daprapps.azurecr.io/node_publisher:latest + ports: + - containerPort: 3000 + imagePullPolicy: Always diff --git a/publisher/node-server.js b/publisher/node-server.js new file mode 100644 index 0000000..a19b7c2 --- /dev/null +++ b/publisher/node-server.js @@ -0,0 +1,24 @@ +/* +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +const express = require('express') +const app = express() + +app.get('/', (_req, res) => { res.send("Hello from node dapr publisher") }) +app.listen(3000, () => { + console.log("Json message publisher. Dapr URL: 'localhost/3500/v1.0/publish/' \n") +}) \ No newline at end of file diff --git a/publisher/package-lock.json b/publisher/package-lock.json new file mode 100644 index 0000000..4a28cc5 --- /dev/null +++ b/publisher/package-lock.json @@ -0,0 +1,374 @@ +{ + "name": "publisher", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/publisher/package.json b/publisher/package.json new file mode 100644 index 0000000..7daeee6 --- /dev/null +++ b/publisher/package.json @@ -0,0 +1,14 @@ +{ + "name": "publisher", + "version": "1.0.0", + "description": "", + "main": "node-server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "express": "^4.17.1" + } +} diff --git a/src/Company/Operations/BooksAndRecords.elm b/src/Company/Operations/BooksAndRecords.elm new file mode 100644 index 0000000..cd10185 --- /dev/null +++ b/src/Company/Operations/BooksAndRecords.elm @@ -0,0 +1,121 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Company.Operations.BooksAndRecords exposing (..) + +import Morphir.SDK.StatefulApp exposing (StatefulApp(..)) + + + +{- Type aliases for modeling in the language of the business -} + + +type alias ID = + String + + +type alias ProductID = + String + + +type alias Price = + Float + + +type alias Quantity = + Int + + + +{- Identifies a structure that can be associated to a persistance entity -} + + +type alias Deal = + { product : ProductID + , price : Price + , quantity : Quantity + } + + + +{- These define the requests that can be made of this service -} + + +type DealCmd + = OpenDeal ProductID Price Quantity + | CloseDeal ProductID + + + +{- These define the responses that would result from requests -} + + +type DealEvent + = DealOpened ProductID Price Quantity + | DealClosed ProductID + | InvalidQuantity Quantity + | InvalidPrice Price + | DuplicateDeal ProductID + | DealNotFound ProductID + + + +{- Defines that this is a stateful application that uses ID as the entity key (for possible partioning), + accepts requests of type DealCmd, + manages data in the form of a Deal, + and produces events of type DealEvent. + + Note that there's no indication of whether the API is synchronous or asynchronous. That's up to the implementation to decide. +-} +{- Defines the business logic of this app. + That is whether or not to accept a request to open or close a deal. +-} + + +logic : Maybe Deal -> DealCmd -> ( Maybe Deal, DealEvent ) +logic dealState dealCmd = + -- Act accordingly based on whether the deal already exists. + case dealState of + Just _ -> + case dealCmd of + CloseDeal p -> + ( Nothing, DealClosed p ) + + OpenDeal p _ _ -> + ( dealState, DuplicateDeal p ) + + Nothing -> + case dealCmd of + OpenDeal productId price qty -> + if price < 0 then + ( dealState, InvalidPrice price ) + + else if qty < 0 then + ( dealState, InvalidQuantity qty ) + + else + ( Deal productId price qty |> Just + , DealOpened productId price qty + ) + + CloseDeal p -> + ( dealState, DealNotFound p ) + + +app : StatefulApp ID DealCmd Deal DealEvent +app = + StatefulApp logic diff --git a/src/Morphir/SDK/App.elm b/src/Morphir/SDK/App.elm deleted file mode 100644 index 2fb182b..0000000 --- a/src/Morphir/SDK/App.elm +++ /dev/null @@ -1,46 +0,0 @@ -module Morphir.SDK.App exposing (..) - - -type StatelessApp remotestate view = - StatelessApp (remotestate -> view) - - -statelessApp : (remotestate -> view) -> StatelessApp remotestate view -statelessApp f = - StatelessApp f - - -type StatefulApp api remotestate localstate event = - StatefulApp - { api : remotestate -> localstate -> api - , init : remotestate -> ( localstate, Cmd event ) - , update : remotestate -> event -> localstate -> ( localstate, Cmd event ) - , subscriptions : remotestate -> localstate -> Sub event - } - - -sendCommand : (api -> Result x event) -> (Maybe x -> List a) -> StatefulApp api remotestate localstate event -> Cmd a -sendCommand command mapResult app = - Cmd.none - - -subscribe : (localstate -> localstate -> List a) -> StatefulApp api remotestate localstate event -> Sub a -subscribe query app = - Sub.none - - -statefulApp : - { api : remotestate -> localstate -> api - , init : remotestate -> ( localstate, Cmd event ) - , update : remotestate -> event -> localstate -> ( localstate, Cmd event ) - , subscriptions : remotestate -> localstate -> Sub event - } - -> StatefulApp api remotestate localstate event -statefulApp app = - StatefulApp app - - -{-| Just a helper for readability -} -cmdNone : a -> (a, Cmd msg) -cmdNone a = - (a, Cmd.none) \ No newline at end of file diff --git a/src/Morphir/SDK/Average.elm b/src/Morphir/SDK/Average.elm deleted file mode 100644 index b10d405..0000000 --- a/src/Morphir/SDK/Average.elm +++ /dev/null @@ -1,20 +0,0 @@ -module Morphir.SDK.Average exposing (..) - - -weighted : (a -> Float) -> (a -> Float) -> List a -> Maybe Float -weighted getWeight getValue list = - if List.isEmpty list then - Nothing - else - let - totalWeight = - list - |> List.map getWeight - |> List.sum - - totalWeightedValue = - list - |> List.map (\a -> getWeight a * getValue a) - |> List.sum - in - Just (totalWeightedValue / totalWeight) \ No newline at end of file diff --git a/src/Morphir/SDK/Basics.elm b/src/Morphir/SDK/Basics.elm deleted file mode 100644 index b09313f..0000000 --- a/src/Morphir/SDK/Basics.elm +++ /dev/null @@ -1,557 +0,0 @@ -module Morphir.SDK.Basics exposing - ( Int8, Int16, Int32, Int64, Float32, Float64, Decimal - , add, sub, mul, fdiv, idiv, pow - , toDecimal, round, floor, ceiling, truncate - , eq, neq - , lt, gt, le, ge, max, min, compare, Order - , not, and, or, xor - , append - , modBy, remainderBy, negate, abs, clamp, sqrt - , toString - , identity, always, pipeLeft, pipeRight, composeLeft, composeRight - ) - - -{-| Tons of useful functions that get imported by default. -# Math -@docs Int, Decimal, add, sub, mul, fdiv, idiv, pow -# Int to Decimal / Decimal to Int -@docs toDecimal, round, floor, ceiling, truncate -# Equality -@docs eq, neq -# Comparison -These functions only work on `comparable` types. This includes numbers, -characters, strings, lists of comparable things, and tuples of comparable -things. -@docs lt, gt, le, ge, max, min, compare, Order -# Booleans -@docs Bool, not, and, or, xor -# Append Strings and Lists -@docs append -# Fancier Math -@docs modBy, remainderBy, negate, abs, clamp, sqrt -# Function Helpers -@docs identity, always, apL, apR, composeL, composeR --} - -import Morphir.Core.Native exposing (Native, native) -import Morphir.Core.Annotation exposing (undefined) - --- MATHEMATICS - - -{-| Represents an 8 bit integer value. --} -type alias Int8 = - Native Int - - -{-| Represents a 16 bit integer value. --} -type alias Int16 = - Native Int - - -{-| Represents a 32 bit integer value. --} -type alias Int32 = - Native Int - - -{-| Represents a 64 bit integer value. --} -type alias Int64 = - Native Int - - -{-| Represents a 32 bit floating-point value. --} -type alias Float32 = - Native Float - - -{-| Represents a 64 bit floating-point value. --} -type alias Float64 = - Native Float - - -{-| Represents a Decimal number. Backed by Float in Elm for simplicity but -in all other backends it uses decimal arithmetices as expected. --} -type alias Decimal = - Native Float - - -{-| Add two numbers. The `number` type variable means this operation can be -specialized to `Int -> Int -> Int` or to `Float -> Float -> Float`. So you -can do things like this: - 3002 + 4004 == 7006 -- all ints - 3.14 + 3.14 == 6.28 -- all floats -You _cannot_ add an `Int` and a `Float` directly though. Use functions like -[toFloat](#toFloat) or [round](#round) to convert both values to the same type. -So if you needed to add a list length to a `Float` for some reason, you -could say one of these: - 3.14 + toFloat (List.length [1,2,3]) == 6.14 - round 3.14 + List.length [1,2,3] == 6 -**Note:** Languages like Java and JavaScript automatically convert `Int` values -to `Float` values when you mix and match. This can make it difficult to be sure -exactly what type of number you are dealing with. When you try to _infer_ these -conversions (as Scala does) it can be even more confusing. Elm has opted for a -design that makes all conversions explicit. --} -add : number -> number -> number -add a b = - native (a + b) - - -{-| Subtract numbers like `4 - 3 == 1`. -See [`(+)`](#+) for docs on the `number` type variable. --} -sub : number -> number -> number -sub a b = - native (a - b) - - -{-| Multiply numbers like `2 * 3 == 6`. -See [`(+)`](#+) for docs on the `number` type variable. --} -mul : number -> number -> number -mul a b = - native (a * b) - - -{-| Floating-point division: - 3.14 / 2 == 1.57 --} -fdiv : rational -> rational -> rational -fdiv a b = - undefined - - -{-| Integer division: - 3 // 2 == 1 -Notice that the remainder is discarded. --} -idiv : Int -> Int -> Int -idiv a b = - native (a // b) - - -{-| Exponentiation - 3^2 == 9 - 3^3 == 27 --} -pow : number -> number -> number -pow a b = - native (a ^ b) - - - --- INT TO DECIMAL / DECIMAL TO INT - - -{-| Convert an integer into a decimal. Useful when mixing `Int` and `Decimal` -values like this: - halfOf : Int -> Decimal - halfOf number = - toDecimal number / 2 --} -toDecimal : Int -> Decimal -toDecimal a = - native (Basics.toFloat a) - - -{-| Turn any kind of value into a string. When you view the resulting string -with `Text.fromString` it should look just like the value it came from. - - toString 42 == "42" - toString [1,2] == "[1,2]" - toString "he said, \"hi\"" == "\"he said, \\\"hi\\\"\"" --} -toString : a -> String -toString a = - native (Debug.toString a) - -{-| Round a number to the nearest integer. - round 1.0 == 1 - round 1.2 == 1 - round 1.5 == 2 - round 1.8 == 2 - round -1.2 == -1 - round -1.5 == -1 - round -1.8 == -2 --} -round : Decimal -> Int -round a = - native (Basics.round a) - - -{-| Floor function, rounding down. - floor 1.0 == 1 - floor 1.2 == 1 - floor 1.5 == 1 - floor 1.8 == 1 - floor -1.2 == -2 - floor -1.5 == -2 - floor -1.8 == -2 --} -floor : Decimal -> Int -floor a = - native (Basics.floor a) - - -{-| Ceiling function, rounding up. - ceiling 1.0 == 1 - ceiling 1.2 == 2 - ceiling 1.5 == 2 - ceiling 1.8 == 2 - ceiling -1.2 == -1 - ceiling -1.5 == -1 - ceiling -1.8 == -1 --} -ceiling : Decimal -> Int -ceiling a = - native (Basics.ceiling a) - - -{-| Truncate a number, rounding towards zero. - truncate 1.0 == 1 - truncate 1.2 == 1 - truncate 1.5 == 1 - truncate 1.8 == 1 - truncate -1.2 == -1 - truncate -1.5 == -1 - truncate -1.8 == -1 --} -truncate : Decimal -> Int -truncate a = - native (Basics.truncate a) - - - --- EQUALITY - - -{-| Check if values are “the same”. -**Note:** Elm uses structural equality on tuples, records, and user-defined -union types. This means the values `(3, 4)` and `(3, 4)` are definitely equal. -This is not true in languages like JavaScript that use reference equality on -objects. -**Note:** Equality (in the Elm sense) is not possible for certain types. For -example, the functions `(\n -> n + 1)` and `(\n -> 1 + n)` are “the -same” but detecting this in general is [undecidable][]. In a future -release, the compiler will detect when `(==)` is used with problematic -types and provide a helpful error message. This will require quite serious -infrastructure work that makes sense to batch with another big project, so the -stopgap is to crash as quickly as possible. Problematic types include functions -and JavaScript values like `Json.Encode.Value` which could contain functions -if passed through a port. -[undecidable]: https://en.wikipedia.org/wiki/Undecidable_problem --} -eq : a -> a -> Bool -eq a b = - native (a == b) - - -{-| Check if values are not “the same”. -So `(a /= b)` is the same as `(not (a == b))`. --} -neq : a -> a -> Bool -neq a b = - native (a /= b) - - - --- COMPARISONS - - -{-|-} -lt : comparable -> comparable -> Bool -lt a b = - native (a < b) - - -{-|-} -gt : comparable -> comparable -> Bool -gt a b = - native (a > b) - - -{-|-} -le : comparable -> comparable -> Bool -le a b = - native (a <= b) - - -{-|-} -ge : comparable -> comparable -> Bool -ge a b = - native (a >= b) - - -{-| Find the smaller of two comparables. - min 42 12345678 == 42 - min "abc" "xyz" == "abc" --} -min : comparable -> comparable -> comparable -min x y = - native (Basics.min x y) - - -{-| Find the larger of two comparables. - max 42 12345678 == 12345678 - max "abc" "xyz" == "xyz" --} -max : comparable -> comparable -> comparable -max x y = - native (Basics.max x y) - - -{-| Compare any two comparable values. Comparable values include `String`, -`Char`, `Int`, `Float`, or a list or tuple containing comparable values. These -are also the only values that work as `Dict` keys or `Set` members. - compare 3 4 == LT - compare 4 4 == EQ - compare 5 4 == GT --} -compare : comparable -> comparable -> Order -compare a b = - native (Basics.compare a b) - - -{-| Represents the relative ordering of two things. -The relations are less than, equal to, and greater than. --} -type alias Order = - Native Basics.Order - - - --- BOOLEANS - - - -{-| Negate a boolean value. - not True == False - not False == True --} -not : Bool -> Bool -not a = - native (Basics.not a) - - -{-| The logical AND operator. `True` if both inputs are `True`. - True && True == True - True && False == False - False && True == False - False && False == False -**Note:** When used in the infix position, like `(left && right)`, the operator -short-circuits. This means if `left` is `False` we do not bother evaluating `right` -and just return `False` overall. --} -and : Bool -> Bool -> Bool -and a b = - native (a && b) - - -{-| The logical OR operator. `True` if one or both inputs are `True`. - True || True == True - True || False == True - False || True == True - False || False == False -**Note:** When used in the infix position, like `(left || right)`, the operator -short-circuits. This means if `left` is `True` we do not bother evaluating `right` -and just return `True` overall. --} -or : Bool -> Bool -> Bool -or a b = - native (a || b) - - -{-| The exclusive-or operator. `True` if exactly one input is `True`. - xor True True == False - xor True False == True - xor False True == True - xor False False == False --} -xor : Bool -> Bool -> Bool -xor a b = - native (Basics.xor a b) - - - --- APPEND - - -{-| Put two appendable things together. This includes strings, lists, and text. - "hello" ++ "world" == "helloworld" - [1,1,2] ++ [3,5,8] == [1,1,2,3,5,8] --} -append : appendable -> appendable -> appendable -append a b = - native (a ++ b) - - - --- FANCIER MATH - -{-| Perform [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic). -A common trick is to use (n mod 2) to detect even and odd numbers: - modBy 2 0 == 0 - modBy 2 1 == 1 - modBy 2 2 == 0 - modBy 2 3 == 1 -Our `modBy` function works in the typical mathematical way when you run into -negative numbers: - List.map (modBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ] - -- [ 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 ] -Use [`remainderBy`](#remainderBy) for a different treatment of negative numbers, -or read Daan Leijen’s [Division and Modulus for Computer Scientists][dm] for more -information. -[dm]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf --} -modBy : Int -> Int -> Int -modBy a b = - native (Basics.modBy a b) - - -{-| Get the remainder after division. Here are bunch of examples of dividing by four: - List.map (remainderBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ] - -- [ -1, 0, -3, -2, -1, 0, 1, 2, 3, 0, 1 ] -Use [`modBy`](#modBy) for a different treatment of negative numbers, -or read Daan Leijen’s [Division and Modulus for Computer Scientists][dm] for more -information. -[dm]: https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf --} -remainderBy : Int -> Int -> Int -remainderBy a b = - native (Basics.remainderBy a b) - - -{-| Negate a number. - - negate 42 == -42 - negate -42 == 42 - negate 0 == 0 --} -negate : number -> number -negate n = - native (Basics.negate n) - - -{-| Get the [absolute value][abs] of a number. - abs 16 == 16 - abs -4 == 4 - abs -8.5 == 8.5 - abs 3.14 == 3.14 -[abs]: https://en.wikipedia.org/wiki/Absolute_value --} -abs : number -> number -abs n = - native (Basics.abs n) - - -{-| Clamps a number within a given range. With the expression -`clamp 100 200 x` the results are as follows: - 100 if x < 100 - x if 100 <= x < 200 - 200 if 200 <= x --} -clamp : number -> number -> number -> number -clamp low high number = - native (Basics.clamp low high number) - - -{-| Take the square root of a number. - sqrt 4 == 2 - sqrt 9 == 3 - sqrt 16 == 4 - sqrt 25 == 5 --} -sqrt : Decimal -> Decimal -sqrt a = - native (Basics.sqrt a) - - - --- FUNCTION HELPERS - - -{-| Function composition, passing results along in the suggested direction. For -example, the following code checks if the square root of a number is odd: - not << isEven << sqrt -You can think of this operator as equivalent to the following: - (g << f) == (\x -> g (f x)) -So our example expands out to something like this: - \n -> not (isEven (sqrt n)) --} -composeLeft : (b -> c) -> (a -> b) -> (a -> c) -composeLeft g f x = - g (f x) - - -{-| Function composition, passing results along in the suggested direction. For -example, the following code checks if the square root of a number is odd: - sqrt >> isEven >> not --} -composeRight : (a -> b) -> (b -> c) -> (a -> c) -composeRight f g x = - g (f x) - - -{-| Saying `x |> f` is exactly the same as `f x`. -It is called the “pipe” operator because it lets you write “pipelined” code. -For example, say we have a `sanitize` function for turning user input into -integers: - -- BEFORE - sanitize : String -> Maybe Int - sanitize input = - String.toInt (String.trim input) -We can rewrite it like this: - -- AFTER - sanitize : String -> Maybe Int - sanitize input = - input - |> String.trim - |> String.toInt -Totally equivalent! I recommend trying to rewrite code that uses `x |> f` -into code like `f x` until there are no pipes left. That can help you build -your intuition. -**Note:** This can be overused! I think folks find it quite neat, but when you -have three or four steps, the code often gets clearer if you break out a -top-level helper function. Now the transformation has a name. The arguments are -named. It has a type annotation. It is much more self-documenting that way! -Testing the logic gets easier too. Nice side benefit! --} -pipeRight : a -> (a -> b) -> b -pipeRight x f = - f x - - -{-| Saying `f <| x` is exactly the same as `f x`. -It can help you avoid parentheses, which can be nice sometimes. Maybe you want -to apply a function to a `case` expression? That sort of thing. --} -pipeLeft : (a -> b) -> a -> b -pipeLeft f x = - f x - - -{-| Given a value, returns exactly the same value. This is called -[the identity function](https://en.wikipedia.org/wiki/Identity_function). --} -identity : a -> a -identity x = - x - - -{-| Create a function that *always* returns the same value. Useful with -functions like `map`: - List.map (always 0) [1,2,3,4,5] == [0,0,0,0,0] - -- List.map (\_ -> 0) [1,2,3,4,5] == [0,0,0,0,0] - -- always = (\x _ -> x) --} -always : a -> b -> a -always a _ = - a \ No newline at end of file diff --git a/src/Morphir/SDK/DictExtra.elm b/src/Morphir/SDK/DictExtra.elm deleted file mode 100644 index a56bf17..0000000 --- a/src/Morphir/SDK/DictExtra.elm +++ /dev/null @@ -1,58 +0,0 @@ -module Morphir.SDK.DictExtra exposing (..) - - -import Dict exposing (Dict) - - -filterMap : (comparable -> a -> Maybe b) -> Dict comparable a -> Dict comparable b -filterMap f dict = - dict - |> Dict.toList - |> List.filterMap - (\( k, a ) -> - f k a - |> Maybe.map (\v -> ( k, v )) - ) - |> Dict.fromList - - -getAndThen : Dict comparable a -> comparable -> (a -> Maybe b) -> Maybe b -getAndThen dict key f = - dict - |> Dict.get key - |> Maybe.andThen f - - -getMap : Dict comparable a -> comparable -> (a -> b) -> Maybe b -getMap dict key f = - dict - |> Dict.get key - |> Maybe.map f - - -type DictEvent comparable v - = Insert comparable v - | Update comparable v v - | Delete comparable v - - -changes : Dict comparable v -> Dict comparable v -> List (DictEvent comparable v) -changes dict1 dict2 = - [] -- TODO: implement - - -filterByKey : (comparable -> Bool) -> List (DictEvent comparable v) -> List (DictEvent comparable v) -filterByKey f list = - list - |> List.filter - (\change -> - case change of - Insert key _ -> - f key - - Update key _ _ -> - f key - - Delete key _ -> - f key - ) \ No newline at end of file diff --git a/src/Morphir/SDK/ListExtra.elm b/src/Morphir/SDK/ListExtra.elm deleted file mode 100644 index 9ca4af6..0000000 --- a/src/Morphir/SDK/ListExtra.elm +++ /dev/null @@ -1,14 +0,0 @@ -module Morphir.SDK.ListExtra exposing (get) - - -{-| Get the item at an index in the List or Nothing if the index is out of range. - get 10 [] == Nothing - get -1 [1,2,3] == Nothing - get 1 [1,2,3] == Just 2 --} -get : Int -> List a -> Maybe a -get index list = - if index < 0 then - Nothing - else - list |> List.drop (index - 1) |> List.head diff --git a/src/Morphir/SDK/MaybeExtra.elm b/src/Morphir/SDK/MaybeExtra.elm deleted file mode 100644 index 6114405..0000000 --- a/src/Morphir/SDK/MaybeExtra.elm +++ /dev/null @@ -1,12 +0,0 @@ -module Morphir.SDK.MaybeExtra exposing (toList) - - -{-| Return an empty list on `Nothing` or a list with one element, where the element is the value of `Just`. - maybeToList Nothing == [] - maybeToList (Just 1) == [1] --} -toList : Maybe a -> List a -toList m = - case m of - Nothing -> [] - Just x -> [x] diff --git a/src/Morphir/SDK/Rule.elm b/src/Morphir/SDK/Rule.elm deleted file mode 100644 index c335a57..0000000 --- a/src/Morphir/SDK/Rule.elm +++ /dev/null @@ -1,93 +0,0 @@ -module Morphir.SDK.Rule exposing (..) - - -type RuleSet a b - = RuleSet (List (a -> Maybe b)) - - -decide : a -> RuleSet a b -> Maybe b -decide data (RuleSet rules) = - case rules of - [] -> - Nothing - - firstRule :: rest -> - case firstRule data of - Just result -> - Just result - - Nothing -> - decide data (RuleSet rest) - - -matchThenMap : (a -> Bool) -> (a -> b) -> a -> Maybe b -matchThenMap f g a = - if f a then - Just (g a) - else - Nothing - - -type PriorityRuleSet a b - = PriorityRuleSet (List ( (a -> Maybe b), Int )) - - -type RuleCollision a b - = RuleCollision (RuleSet a b) - - -decideByPriority : a -> PriorityRuleSet a b -> Result (RuleCollision a b) (Maybe b) -decideByPriority data (PriorityRuleSet rules) = - let - matchingRules = - rules - |> List.filter - (\( rule, priority ) -> - rule data /= Nothing - ) - - maxPriority = - matchingRules - |> List.map Tuple.second - |> List.maximum - |> Maybe.withDefault 0 - - maxPriorityRules = - matchingRules - |> List.filterMap - (\( rule, priority ) -> - if priority == maxPriority then - Just rule - else - Nothing - ) - in - case maxPriorityRules of - [] -> - Ok Nothing - - [ rule ] -> - Ok (rule data) - - multipleRules -> - Err (RuleCollision (RuleSet multipleRules)) - - -any : a -> Bool -any _ = - True - - -is : a -> a -> Bool -is a b = - a == b - - -anyOf : List a -> a -> Bool -anyOf list a = - List.member a list - - -noneOf : List a -> a -> Bool -noneOf list a = - not (anyOf list a) \ No newline at end of file diff --git a/src/Morphir/Sample/Apps/Approvals/Inventory/App.elm b/src/Morphir/Sample/Apps/Approvals/Inventory/App.elm index a31df33..a370d8f 100644 --- a/src/Morphir/Sample/Apps/Approvals/Inventory/App.elm +++ b/src/Morphir/Sample/Apps/Approvals/Inventory/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Approvals.Inventory.App exposing (..) diff --git a/src/Morphir/Sample/Apps/Approvals/LocateList/ACL.elm b/src/Morphir/Sample/Apps/Approvals/LocateList/ACL.elm index a9167d6..c43719d 100644 --- a/src/Morphir/Sample/Apps/Approvals/LocateList/ACL.elm +++ b/src/Morphir/Sample/Apps/Approvals/LocateList/ACL.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Approvals.LocateList.ACL exposing (..) diff --git a/src/Morphir/Sample/Apps/Approvals/LocateList/App.elm b/src/Morphir/Sample/Apps/Approvals/LocateList/App.elm index ca41c61..b3511a7 100644 --- a/src/Morphir/Sample/Apps/Approvals/LocateList/App.elm +++ b/src/Morphir/Sample/Apps/Approvals/LocateList/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Approvals.LocateList.App exposing (..) diff --git a/src/Morphir/Sample/Apps/BooksAndRecords/App.elm b/src/Morphir/Sample/Apps/BooksAndRecords/App.elm index add0a45..c552067 100644 --- a/src/Morphir/Sample/Apps/BooksAndRecords/App.elm +++ b/src/Morphir/Sample/Apps/BooksAndRecords/App.elm @@ -1,14 +1,31 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.BooksAndRecords.App exposing (..) import Dict exposing (Dict) -import Morphir.SDK.App exposing (StatefulApp, statefulApp, cmdNone) +import Morphir.SDK.StatefulApp exposing (..) import Morphir.Sample.Apps.Shared.Product as Product import Morphir.Sample.Apps.Shared.Price exposing (..) import Morphir.Sample.Apps.BooksAndRecords.Deal exposing (..) +import Morphir.Sample.Apps.Shared.Quantity exposing (Quantity) -type alias App = +type alias App = StatefulApp API RemoteState LocalState Event @@ -22,7 +39,7 @@ type alias RemoteState = () -type alias LocalState = +type alias LocalState = Dict ID Deal @@ -64,7 +81,7 @@ api remote local = init : RemoteState -> ( LocalState, Cmd Event ) -init _ = +init _ = cmdNone Dict.empty diff --git a/src/Morphir/Sample/Apps/BooksAndRecords/Deal.elm b/src/Morphir/Sample/Apps/BooksAndRecords/Deal.elm index 6431c41..033bfda 100644 --- a/src/Morphir/Sample/Apps/BooksAndRecords/Deal.elm +++ b/src/Morphir/Sample/Apps/BooksAndRecords/Deal.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.BooksAndRecords.Deal exposing (..) @@ -5,13 +21,12 @@ import Morphir.Sample.Apps.Shared.Product as Product import Morphir.Sample.Apps.Shared.Client as Client import Morphir.Sample.Apps.Shared.Price exposing (..) import Morphir.Sample.Apps.Shared.Quantity exposing (..) -import Morphir.SDK.Basics exposing (Decimal) type alias ID = String -type alias Value = Decimal +type alias Value = Float type alias Deal = diff --git a/src/Morphir/Sample/Apps/Order/ACL.elm b/src/Morphir/Sample/Apps/Order/ACL.elm index 7cdb651..0a372cd 100644 --- a/src/Morphir/Sample/Apps/Order/ACL.elm +++ b/src/Morphir/Sample/Apps/Order/ACL.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Order.ACL exposing (..) diff --git a/src/Morphir/Sample/Apps/Order/App.elm b/src/Morphir/Sample/Apps/Order/App.elm index fe63b2c..44dc56a 100644 --- a/src/Morphir/Sample/Apps/Order/App.elm +++ b/src/Morphir/Sample/Apps/Order/App.elm @@ -1,3 +1,18 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} module Morphir.Sample.Apps.Order.App exposing ( App , API @@ -9,7 +24,7 @@ module Morphir.Sample.Apps.Order.App exposing import Dict exposing (Dict) -import Morphir.SDK.App exposing (StatefulApp, statefulApp, cmdNone) +import Morphir.SDK.StatefulApp exposing (StatefulApp, statefulApp, cmdNone) import Morphir.Sample.Apps.Shared.Product as Product import Morphir.Sample.Apps.BooksAndRecords.Deal as Deal import Morphir.Sample.Apps.Order.Order as Order diff --git a/src/Morphir/Sample/Apps/Order/Order.elm b/src/Morphir/Sample/Apps/Order/Order.elm index 18cca9b..1790ac1 100644 --- a/src/Morphir/Sample/Apps/Order/Order.elm +++ b/src/Morphir/Sample/Apps/Order/Order.elm @@ -1,3 +1,18 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} module Morphir.Sample.Apps.Order.Order exposing (..) diff --git a/src/Morphir/Sample/Apps/Rates/ACL.elm b/src/Morphir/Sample/Apps/Rates/ACL.elm index 278164b..096a317 100644 --- a/src/Morphir/Sample/Apps/Rates/ACL.elm +++ b/src/Morphir/Sample/Apps/Rates/ACL.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Rates.ACL exposing (..) {-| This module is our anti-corruption layer that translates external states into an diff --git a/src/Morphir/Sample/Apps/Rates/App.elm b/src/Morphir/Sample/Apps/Rates/App.elm index 24c99e0..60748c3 100644 --- a/src/Morphir/Sample/Apps/Rates/App.elm +++ b/src/Morphir/Sample/Apps/Rates/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Rates.App exposing (..) import Dict exposing (Dict) diff --git a/src/Morphir/Sample/Apps/Shared/Client.elm b/src/Morphir/Sample/Apps/Shared/Client.elm index 8cb356e..903e2cd 100644 --- a/src/Morphir/Sample/Apps/Shared/Client.elm +++ b/src/Morphir/Sample/Apps/Shared/Client.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Shared.Client exposing (..) diff --git a/src/Morphir/Sample/Apps/Shared/Market.elm b/src/Morphir/Sample/Apps/Shared/Market.elm index 21b2589..94b71cc 100644 --- a/src/Morphir/Sample/Apps/Shared/Market.elm +++ b/src/Morphir/Sample/Apps/Shared/Market.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Shared.Market exposing (..) diff --git a/src/Morphir/Sample/Apps/Shared/Price.elm b/src/Morphir/Sample/Apps/Shared/Price.elm index aea39fa..2fa5ec7 100644 --- a/src/Morphir/Sample/Apps/Shared/Price.elm +++ b/src/Morphir/Sample/Apps/Shared/Price.elm @@ -1,7 +1,21 @@ -module Morphir.Sample.Apps.Shared.Price exposing (..) +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + +module Morphir.Sample.Apps.Shared.Price exposing (..) -import SDK.Basics exposing (Decimal) -type alias Price = Decimal \ No newline at end of file +type alias Price = Float \ No newline at end of file diff --git a/src/Morphir/Sample/Apps/Shared/Product.elm b/src/Morphir/Sample/Apps/Shared/Product.elm index 1f9f084..eb9430b 100644 --- a/src/Morphir/Sample/Apps/Shared/Product.elm +++ b/src/Morphir/Sample/Apps/Shared/Product.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Shared.Product exposing (..) diff --git a/src/Morphir/Sample/Apps/Shared/Quantity.elm b/src/Morphir/Sample/Apps/Shared/Quantity.elm index 21cf2c5..91c30c2 100644 --- a/src/Morphir/Sample/Apps/Shared/Quantity.elm +++ b/src/Morphir/Sample/Apps/Shared/Quantity.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Shared.Quantity exposing (..) diff --git a/src/Morphir/Sample/Apps/Shared/Rate.elm b/src/Morphir/Sample/Apps/Shared/Rate.elm index 65cce2a..89784e8 100644 --- a/src/Morphir/Sample/Apps/Shared/Rate.elm +++ b/src/Morphir/Sample/Apps/Shared/Rate.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Shared.Rate exposing (..) diff --git a/src/Morphir/Sample/Apps/Trader/App.elm b/src/Morphir/Sample/Apps/Trader/App.elm index c4de82b..72fbf6a 100644 --- a/src/Morphir/Sample/Apps/Trader/App.elm +++ b/src/Morphir/Sample/Apps/Trader/App.elm @@ -1,9 +1,25 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Trader.App exposing (..) import Morphir.Sample.Apps.Trader.Logic exposing (..) import Dict exposing (Dict) -import Morphir.SDK.App exposing (StatefulApp, statefulApp, cmdNone) +import Morphir.SDK.StatefulApp exposing (StatefulApp, statefulApp, cmdNone) import Morphir.Sample.Apps.Shared.Product as Product import Morphir.Sample.Apps.BooksAndRecords.App as BookApp import Morphir.Sample.Apps.BooksAndRecords.Deal as Deal diff --git a/src/Morphir/Sample/Apps/Trader/Logic.elm b/src/Morphir/Sample/Apps/Trader/Logic.elm index 7193c35..9bee0e0 100644 --- a/src/Morphir/Sample/Apps/Trader/Logic.elm +++ b/src/Morphir/Sample/Apps/Trader/Logic.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Trader.Logic exposing (..) diff --git a/src/Morphir/Sample/Apps/Upstream/Market/App.elm b/src/Morphir/Sample/Apps/Upstream/Market/App.elm index c6f8e43..01c3c9b 100644 --- a/src/Morphir/Sample/Apps/Upstream/Market/App.elm +++ b/src/Morphir/Sample/Apps/Upstream/Market/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Upstream.Market.App exposing (..) {-| This is a stub for an external Market app. Normally this would diff --git a/src/Morphir/Sample/Apps/Upstream/Product/App.elm b/src/Morphir/Sample/Apps/Upstream/Product/App.elm index 3412ed6..59d8037 100644 --- a/src/Morphir/Sample/Apps/Upstream/Product/App.elm +++ b/src/Morphir/Sample/Apps/Upstream/Product/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Upstream.Product.App exposing (..) diff --git a/src/Morphir/Sample/Apps/Upstream/Trading/App.elm b/src/Morphir/Sample/Apps/Upstream/Trading/App.elm index a7758f5..c974e6d 100644 --- a/src/Morphir/Sample/Apps/Upstream/Trading/App.elm +++ b/src/Morphir/Sample/Apps/Upstream/Trading/App.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Apps.Upstream.Trading.App exposing (..) diff --git a/src/Morphir/Sample/LCR/Basics.elm b/src/Morphir/Sample/LCR/Basics.elm deleted file mode 100644 index 5f80bb5..0000000 --- a/src/Morphir/Sample/LCR/Basics.elm +++ /dev/null @@ -1,48 +0,0 @@ -module Morphir.Sample.LCR.Basics exposing - ( AssetCategoryCodes(..) - , InsuranceType(..) - , Entity - , Currency - , Fed5GCode - ) - - -import Date exposing (Date, Unit(..)) -import Time exposing (Month(..)) - -{-| Asset categories apply to the flows and are specified in the spec. - There are a bunch of them, but we're only concerned with these three in this example . --} -type AssetCategoryCodes - = Level1Assets - | Level2aAssets - | Level2bAssets - - -{-| Insurance type as specified in the spec. - There are a bunch of them, but we're only concerned with FDIC in this example . --} -type InsuranceType - = FDIC - | Uninsured - - -type alias Entity = String - - -type alias Currency = String - - -type alias Fed5GCode = String - - -{-| A currency isn't always itself in 5G. --} -fed5GCurrency : Currency -> Currency -fed5GCurrency currency = - if - List.member currency ["USD", "EUR", "GBP", "CHF", "JPY", "AUD", "CAD"] - then - currency - else - "USD" diff --git a/src/Morphir/Sample/LCR/Calculations.elm b/src/Morphir/Sample/LCR/Calculations.elm deleted file mode 100644 index 378c8ff..0000000 --- a/src/Morphir/Sample/LCR/Calculations.elm +++ /dev/null @@ -1,214 +0,0 @@ -module Morphir.Sample.LCR.Calculations exposing (..) - - -import Morphir.SDK.Basics exposing (Decimal, max, min) -import Date exposing (Date, Interval(..), Unit(..)) -import Morphir.Sample.LCR.Basics exposing (..) -import Morphir.Sample.LCR.Flows exposing (..) -import Morphir.Sample.LCR.Counterparty exposing (..) -import Morphir.Sample.LCR.Inflows as Inflows -import Morphir.Sample.LCR.Outflows as Outflows -import Morphir.Sample.LCR.Product exposing (..) -import Morphir.Sample.LCR.Rules as Rules - --- Forumulas from the OCC: https://www.occ.gov/news-issuances/bulletins/2014/bulletin-2014-51.html --- https://www.occ.gov/topics/supervision-and-examination/capital-markets/balance-sheet-management/liquidity/Basel-III-LCR-Formulas.pdf --- https://www.govinfo.gov/content/pkg/FR-2014-10-10/pdf/2014-22520.pdf (page 61477) - -{-| This module is broken up into the same structure as the example formulas in the referenced PDF. -} - -isMember : Maybe a -> List a -> Bool -isMember ruleM rules = - ruleM - |> Maybe.map (\r -> List.member r rules) - |> Maybe.withDefault False - -isHQLA : (ProductId -> Product) -> Flow -> Bool -isHQLA product flow = - (product flow.productId) |> .isHQLA - - -{-| Helper function to accumulated steps of a sum across a list. This is used in calculating the maturity mismatch add-on. -} -accumulate starter list = - let - (sum, acc) = - List.foldl ( \y (x,xs) -> (x+y, (x+y) :: xs)) (starter, []) list - in - List.reverse acc - - -{-| HQLA Amount is the LCR numerator. It has several components, which are specified as nested functions. -} -hqlaAmount : (ProductId -> Product) -> List Flow -> Decimal -> Decimal -hqlaAmount product t0Flows reserveBalanceRequirement = - let - level1LiquidAssetsThatAreEligibleHQLA = - t0Flows - |> List.filter (\flow -> flow.assetType == Level1Assets && isHQLA product flow) - |> List.map .amount - |> List.sum - - level1LiquidAssetAmount = - level1LiquidAssetsThatAreEligibleHQLA - reserveBalanceRequirement - - - level2aLiquidAssetsThatAreEligibleHQLA = - t0Flows - |> List.filter (\flow -> flow.assetType == Level2aAssets && isHQLA product flow) - |> List.map .amount - |> List.sum - - level2aLiquidAssetAmount = - 0.85 * level2aLiquidAssetsThatAreEligibleHQLA - - - level2bLiquidAssetsThatAreEligibleHQLA = - t0Flows - |> List.filter (\flow -> flow.assetType == Level2bAssets && isHQLA product flow) - |> List.map .amount - |> List.sum - - level2bLiquidAssetAmount = - 0.50 * level2bLiquidAssetsThatAreEligibleHQLA - - - unadjustedExcessHQLAAmount = - let - level2CapExcessAmount = - max (level2aLiquidAssetAmount + level2bLiquidAssetAmount - 0.6667 * level1LiquidAssetAmount) 0.0 - - level2bCapExcessAmount = - max (level2bLiquidAssetAmount - level2CapExcessAmount - 0.1765 * (level1LiquidAssetAmount + level2aLiquidAssetAmount)) 0.0 - in - level2CapExcessAmount + level2bCapExcessAmount - - - adjustedExcessHQLAAmount = - let - adjustedLevel1LiquidAssetAmount = level1LiquidAssetAmount - - adjustedlevel2aLiquidAssetAmount = level2aLiquidAssetAmount * 0.85 - - adjustedlevel2bLiquidAssetAmount = level2bLiquidAssetAmount * 0.50 - - adjustedLevel2CapExcessAmount = - max (adjustedlevel2aLiquidAssetAmount + adjustedlevel2bLiquidAssetAmount - 0.6667 * adjustedLevel1LiquidAssetAmount) 0.0 - - adjustedlevel2bCapExcessAmount = - max (adjustedlevel2bLiquidAssetAmount - adjustedLevel2CapExcessAmount - 0.1765 * (adjustedLevel1LiquidAssetAmount + adjustedlevel2aLiquidAssetAmount)) 0.0 - in - adjustedLevel2CapExcessAmount + adjustedlevel2bCapExcessAmount - in - level1LiquidAssetAmount + level2aLiquidAssetAmount + level2bLiquidAssetAmount - (max unadjustedExcessHQLAAmount adjustedExcessHQLAAmount) - - -{-| Total Net Cash Outflow Amount is the LCR denominator. It has several components, which are specified as nested functions. - The function takes a function to lookup the counterparty for a flow. - the date (t) from which to calculate the remaining days until the flows maturity - and a function takes a function to lookup flows for a given date, --} -totalNetCashOutflowAmount : (Flow -> Counterparty) -> Date -> (Date -> List Flow) -> Decimal -totalNetCashOutflowAmount toCounterparty t flowsForDate = - let - -- List of the next 30 days from t - dates = - List.range 1 30 |> List.map (\i -> Date.add Days i t) - - -- Aggregating helpers - spanDates = \filter -> - dates - |> List.map flowsForDate - |> List.map (\flows -> flows |> aggregateDaily filter) - - aggregateSpan = \filter -> - spanDates filter |> List.sum - - aggregateDaily = \filter flows -> - flows - |> List.filter filter - |> List.map .amount - |> List.sum - - -- Non maturity - nonMaturityOutflowRules = \date -> - Rules.findAll - [ "32(a)(1)", "32(a)(2)", "32(a)(3)", "32(a)(4)", "32(a)(5)" - , "32(b)", "32(c)", "32(d)", "32(e)", "32(f)", "32(i)" - ] - (Outflows.outflowRules toCounterparty date) - - nonMaturityInflowRules = \date -> - Rules.findAll - [ "33(b)", "33(g)" ] - (Inflows.inflowRules toCounterparty date) - - - nonMaturityOutflowAmount = - aggregateSpan (Rules.isAnyApplicable (nonMaturityOutflowRules t)) - - nonMaturityInflowAmount = - aggregateSpan (Rules.isAnyApplicable (nonMaturityInflowRules t)) - - -- Maturity - maturityMismatchOutflowRules = \date -> - Rules.findAll - ["32(g)(1)", "32(g)(2)", "32(g)(3)", "32(g)(4)", "32(g)(5)", "32(g)(6)", "32(g)(7)", "32(g)(8)", "32(g)(9)" - ,"32(h)(1)", "32(h)(2)", "32(h)(5)", "32(j)", "32(k)", "32(l)" - ] (Outflows.outflowRules toCounterparty date) - - maturityOutflows = - spanDates (Rules.isAnyApplicable (maturityMismatchOutflowRules t)) - - maturityOutflowAmount = - maturityOutflows |> List.sum - - maturityMismatchInflowRules = \date -> - Rules.findAll [ "33(c)", "33(d)", "33(e)", "33(f)" ] (Inflows.inflowRules toCounterparty date) - - maturityInflows = - spanDates (Rules.isAnyApplicable (maturityMismatchInflowRules t)) - - maturityInflowAmount = - maturityInflows |> List.sum - - -- Aggregate it all together - aggregatedOutflowAmount = - nonMaturityOutflowAmount + maturityOutflowAmount - - aggregatedInflowAmount = - nonMaturityInflowAmount + maturityInflowAmount - - -- This add-on was added later - maturityMismatchAddOn = - let - netCumulativeMaturityOutflowAmount = - (List.map2 Tuple.pair (accumulate 0 maturityOutflows) (accumulate 0 maturityInflows)) - |> List.map (\(o, i) -> o - i) - |> List.maximum - |> Maybe.withDefault 0 - - - netDay30CumulativeMaturityOutflowAmount = - (List.sum maturityOutflows) - (List.sum maturityInflows) - - maxNext30DaysOfCumulativeMaturityOutflowAmountFloor = - max 0.0 netCumulativeMaturityOutflowAmount - - netDay30CumulativeMaturityOutflowAmountFloor = - max 0.0 netDay30CumulativeMaturityOutflowAmount - in - maxNext30DaysOfCumulativeMaturityOutflowAmountFloor - netDay30CumulativeMaturityOutflowAmountFloor - - cappedInflows = - min (0.75 * aggregatedOutflowAmount) aggregatedInflowAmount - in - aggregatedOutflowAmount - cappedInflows + maturityMismatchAddOn - - -{-| Woohoo. Here's the LCR. -} -lcr : (Flow -> Counterparty) -> (ProductId -> Product) -> Date -> (Date -> List Flow) -> Decimal -> Decimal -lcr toCounterparty product t flowsForDate reserveBalanceRequirement = - let - hqla = hqlaAmount product (flowsForDate t) reserveBalanceRequirement - totalNetCashOutflow = totalNetCashOutflowAmount toCounterparty t flowsForDate - in - hqla / totalNetCashOutflow diff --git a/src/Morphir/Sample/LCR/Counterparty.elm b/src/Morphir/Sample/LCR/Counterparty.elm deleted file mode 100644 index ebd1b94..0000000 --- a/src/Morphir/Sample/LCR/Counterparty.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Morphir.Sample.LCR.Counterparty exposing (..) - - -type alias CounterpartyId = String - - -type CounterpartyType - = Bank - | Retail - | SmallBusiness - | NonFinancialCorporate - | Sovereign - | CentralBank - | GovernmentSponsoredEntity - | PublicSectorEntity - | MultilateralDevelopmentBank - | OtherSupranational - | SupervisedNonBankFinancialEntity - | DebtIssuingSpecialPurposeEntity - | OtherFinancialEntity - | Other - - -type alias Counterparty = - { counterpartyId : CounterpartyId - , counterpartyType : CounterpartyType - } diff --git a/src/Morphir/Sample/LCR/Flows.elm b/src/Morphir/Sample/LCR/Flows.elm deleted file mode 100644 index 505c732..0000000 --- a/src/Morphir/Sample/LCR/Flows.elm +++ /dev/null @@ -1,32 +0,0 @@ -module Morphir.Sample.LCR.Flows exposing (..) - - -import Morphir.SDK.Basics exposing (Decimal) -import Date exposing (Date, Interval(..), Unit(..)) -import Morphir.Sample.LCR.Basics exposing (..) -import Morphir.Sample.LCR.Counterparty exposing (CounterpartyId) -import Morphir.Sample.LCR.Product exposing (ProductId) -import Morphir.Sample.LCR.MaturityBucket as MB - - -type alias BusinessDate = Date - - -type alias ReportingEntity = Entity - - -type alias Flow = - { amount : Decimal - , assetType : AssetCategoryCodes - , businessDate : BusinessDate - , collateralClass : AssetCategoryCodes - , counterpartyId : CounterpartyId - , currency : Currency - , fed5GCode : Fed5GCode - , insured : InsuranceType - , isTreasuryControl : Bool - , isUnencumbered : Bool - , maturityDate : Date - , effectiveMaturityDate : Date - , productId : ProductId - } diff --git a/src/Morphir/Sample/LCR/Inflows.elm b/src/Morphir/Sample/LCR/Inflows.elm deleted file mode 100644 index 851050f..0000000 --- a/src/Morphir/Sample/LCR/Inflows.elm +++ /dev/null @@ -1,155 +0,0 @@ -module Morphir.Sample.LCR.Inflows exposing (..) - - -import Morphir.SDK.Basics exposing (Decimal) -import Date exposing (Date, Interval(..), Unit(..)) -import Morphir.Sample.LCR.Basics exposing (..) -import Morphir.Sample.LCR.Flows exposing (..) -import Morphir.Sample.LCR.Counterparty exposing (..) -import Morphir.Sample.LCR.Rules exposing (..) -import Morphir.Sample.LCR.MaturityBucket exposing (..) - - -{-| The list of all rules pertaining to inflows. -} -inflowRules : (Flow -> Counterparty) -> Date -> List (Rule Flow) -inflowRules toCounterparty t = - [ Rule "20(a)(1)" 1.0 (isRule20a1 t) - , Rule "20(a)(3)-(6)" 1.0 isRule20a3dash6 - , Rule "22(b)(3)L1" -1.0 isRule22b3L2a - , Rule "22(b)(3)L2a" -0.85 isRule22b3L2a - , Rule "22(b)(3)L2b" -0.5 isRule22b3L2b - , Rule "20(b)" 0.85 isRule20b - , Rule "20(c)" 0.5 isRule20c - , Rule "33(b)" 1.0 isRule33b - , Rule "33(c)" 0.5 (isRule33c toCounterparty t) - , Rule "33(d)(1)" 1.0 (isRule33d1 toCounterparty) - , Rule "33(d)(2)" 1.0 (isRule33d2 toCounterparty) - , Rule "33(e)" 1.0 isRule33e - , Rule "33(g)" 1.0 isRule33g - , Rule "33(h)" 0.0 isRule33h - ] - --- Rule logic is below for (eventual) unit testability - -isRule20a1 : Date -> Flow -> Bool -isRule20a1 t flow = - List.member flow.fed5GCode ["I.A.3.1", "I.A.3.2", "I.A.3.3", "I.A.3.4", "I.A.3.5", "I.A.3.6", "I.A.3.7", "I.A.3.8"] - && daysToMaturity t flow.maturityDate == 0 - - -isRule20a3dash6 : Flow -> Bool -isRule20a3dash6 flow = - ( - List.member flow.fed5GCode ["I.A.1", "I.A.2"] - && flow.collateralClass == Level1Assets - && flow.isTreasuryControl - ) - || - ( - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.4"] - && flow.collateralClass == Level1Assets - && flow.isTreasuryControl - && flow.isUnencumbered - ) - - -isRule22b3L2a flow = - flow.fed5GCode == "S.I.19" && flow.collateralClass == Level2aAssets - - -isRule22b3L2b flow = - flow.fed5GCode == "S.I.19" && flow.collateralClass == Level2bAssets - - -isRule20b flow = - ( - List.member flow.fed5GCode ["I.A.1", "I.A.2"] - && flow.collateralClass == Level2aAssets - && flow.isTreasuryControl - ) - || - ( - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.4"] - && flow.collateralClass == Level2aAssets - && flow.isTreasuryControl - && flow.isUnencumbered - ) - -isRule20c flow = - ( - List.member flow.fed5GCode ["I.A.1", "I.A.2"] - && flow.collateralClass == Level2bAssets - && flow.isTreasuryControl - ) - || - ( - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.4"] - && flow.collateralClass == Level2bAssets - && flow.isTreasuryControl - && flow.isUnencumbered - ) - -isRule33b cashflow = - cashflow.fed5GCode == "1.O.7" - - -isRule33c toCounterparty t flow = - let - cpty = toCounterparty flow - days = daysToMaturity t flow.maturityDate - in - ( - List.member flow.fed5GCode ["I.U.5", "I.U.6"] - && List.member cpty.counterpartyType [ Retail, SmallBusiness ] - && (0 < days && days <= 30) - ) - || - ( - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.4", "I.S.5", "I.S.6", "I.S.7"] - && cpty.counterpartyType == Retail - && (0 < days && days <= 30) - ) - -isRule33d1 toCounterparty flow = - let - cpty = toCounterparty flow - in - List.member flow.fed5GCode ["I.U.1", "I.U.2", "I.U.4"] - || - ( List.member flow.fed5GCode ["I.U.5", "I.U.6"] - && List.member cpty.counterpartyType - [ CentralBank - , Bank - , SupervisedNonBankFinancialEntity - , DebtIssuingSpecialPurposeEntity - , OtherFinancialEntity - ] - ) - - -isRule33d2 toCounterparty flow = - let - cpty = toCounterparty flow - in - List.member flow.fed5GCode ["I.U.5", "I.U.6"] - && List.member cpty.counterpartyType - [ NonFinancialCorporate - , Sovereign - , GovernmentSponsoredEntity - , PublicSectorEntity - , MultilateralDevelopmentBank - , OtherSupranational - , Other - ] - -isRule33e cashflow = - cashflow.fed5GCode == "I.O.6" || cashflow.fed5GCode == "I.O.8" - -isRule33f flow = - Debug.todo "Rule 33(f) is actually a bunch of rules. Too many to do for now..." - -isRule33g cashflow = - cashflow.fed5GCode == "I.O.5" && cashflow.isTreasuryControl - -isRule33h cashflow = - cashflow.fed5GCode == "I.O.9" && cashflow.isTreasuryControl diff --git a/src/Morphir/Sample/LCR/MaturityBucket.elm b/src/Morphir/Sample/LCR/MaturityBucket.elm deleted file mode 100644 index 8e44716..0000000 --- a/src/Morphir/Sample/LCR/MaturityBucket.elm +++ /dev/null @@ -1,62 +0,0 @@ -module Morphir.Sample.LCR.MaturityBucket exposing (..) - - --- See: https://www.federalreserve.gov/reportforms/forms/FR_2052a20190331_f.pdf --- Appendix IV-a, Maturity Time Bucket Value List on page 75 - -import Date exposing (Date, Unit(..)) -import Time exposing (Month(..)) - - -type MaturityBucket - = Daily Int - | DayRange Int Int - | DayYear Int Int - | Yearly Int Int - | Residual - - -daysToMaturity fromDate maturityDate = - Date.diff Days maturityDate fromDate - - -yearsToMaturity fromDate maturityDate = - Date.diff Years maturityDate fromDate - - -{-| The Fed spec on maturity buckets -} -bucket fromDate maturityDate = - let - days = daysToMaturity fromDate maturityDate - years = yearsToMaturity maturityDate fromDate - in - if days <= 60 then - Daily days - else if days <= 67 then - DayRange 61 67 - else if days <= 74 then - DayRange 68 74 - else if days <= 82 then - DayRange 75 82 - else if days <= 90 then - DayRange 83 90 - else if days <= 120 then - DayRange 92 120 - else if days <= 150 then - DayRange 121 150 - else if days <= 180 then - DayRange 151 180 - else if days <= 270 then - DayYear 181 270 - else if years <= 1 then - DayYear 271 1 - else if years <= 2 then - Yearly 1 2 - else if years <= 3 then - Yearly 2 3 - else if years <= 4 then - Yearly 3 4 - else if years <= 5 then - Yearly 4 5 - else - Residual diff --git a/src/Morphir/Sample/LCR/Outflows.elm b/src/Morphir/Sample/LCR/Outflows.elm deleted file mode 100644 index 871b2ae..0000000 --- a/src/Morphir/Sample/LCR/Outflows.elm +++ /dev/null @@ -1,308 +0,0 @@ -module Morphir.Sample.LCR.Outflows exposing (..) - - -import Dict exposing (Dict) -import Morphir.SDK.Basics exposing (Decimal) -import Date exposing (Date, Interval(..), Unit(..)) - -import Morphir.Sample.LCR.Basics exposing (..) -import Morphir.Sample.LCR.Basics exposing (InsuranceType(..), AssetCategoryCodes(..)) -import Morphir.Sample.LCR.Basics exposing (InsuranceType, AssetCategoryCodes) -import Morphir.Sample.LCR.Flows exposing (..) -import Morphir.Sample.LCR.Counterparty exposing (..) -import Morphir.Sample.LCR.MaturityBucket exposing (..) -import Morphir.Sample.LCR.Rules exposing (..) - - -{-| The list of all rules pertaining to outlfows. -} -outflowRules counterparty t = - [ Rule "32(a)(1)" 0.03 isRule32a1 - , Rule "32(a)(2)" 0.1 (isRule32a2 counterparty) - , Rule "32(a)(3)" 0.2 (isRule32a3 counterparty) - , Rule "32(a)(4)" 0.4 (isRule32a4 counterparty) - , Rule "32(a)(5)" 0.4 (isRule32a5 counterparty) - , Rule "32(b)" 1.0 isRule32b - , Rule "32(c)" 0.2 isRule32c - , Rule "32(d)" 0.1 isRule32d - , Rule "32(e)" 0.0 isRule32e - , Rule "32(f)" 0.0 isRule32f - , Rule "32(g)(1)" 0.0 (isRule32g1 counterparty t) - , Rule "32(g)(2)" 0.0 (isRule32g2 counterparty t) - , Rule "32(g)(3)" 0.0 (isRule32g3 counterparty t) - , Rule "32(g)(4)" 0.0 (isRule32g4 counterparty t) - , Rule "32(g)(5)" 0.0 (isRule32g5 counterparty) - , Rule "32(g)(6)" 0.0 (isRule32g6 counterparty) - , Rule "32(g)(7)" 0.0 (isRule32g7 counterparty) - , Rule "32(g)(8)" 0.0 (isRule32g8 counterparty) - , Rule "32(g)(9)" 0.0 (isRule32g9 counterparty) - , Rule "32(h)(3)" 0.05 (isRule32h3 counterparty) - , Rule "32(h)(4)" 0.25 (isRule32h4 counterparty) - , Rule "32(l)" 0.0 isRule32l - , Rule "33(f)(1)(iii)" 0.0 (isRule33f1iii t) - , Rule "33(f)(1)(iv)" 0.15 (isRule33f1iv t) - ] - --- Rules broken out for (eventual) unit testing - -isRule32a1 : Flow -> Bool -isRule32a1 flow = - List.member flow.fed5GCode ["O.D.1", "O.D.2"] - && flow.insured == FDIC - - -isRule32a2 : (Flow -> Counterparty) -> Flow -> Bool -isRule32a2 counterparty flow = - let - cpty = counterparty flow - in - ( - List.member flow.fed5GCode ["O.D.1", "O.D.2"] - && List.member cpty.counterpartyType [Retail, SmallBusiness] - && flow.insured /= FDIC - ) - || - ( - flow.fed5GCode == "O.D.3" - && List.member cpty.counterpartyType [Retail, SmallBusiness] - ) - - -isRule32a3 : (Flow -> Counterparty) -> Flow -> Bool -isRule32a3 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.12" - && List.member cpty.counterpartyType [Retail, SmallBusiness] - && flow.insured == FDIC - - -isRule32a4 : (Flow -> Counterparty) -> Flow -> Bool -isRule32a4 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.12" - && List.member cpty.counterpartyType [Retail, SmallBusiness] - && flow.insured /= FDIC - - -isRule32a5 : (Flow -> Counterparty) -> Flow -> Bool -isRule32a5 counterparty flow = - let - cpty = counterparty flow - in - List.member flow.fed5GCode ["O.D.13", "O.W.18"] - && List.member cpty.counterpartyType [Retail, SmallBusiness] - - -isRule32b : Flow -> Bool -isRule32b flow = - List.member flow.fed5GCode ["O.W.1", "O.W.2", "O.W.4", "O.O.21"] - - -isRule32c : Flow -> Bool -isRule32c flow = - flow.fed5GCode == "O.O.20" - - -isRule32d : Flow -> Bool -isRule32d flow = - flow.fed5GCode == "O.O.6" - - -isRule32e : Flow -> Bool -isRule32e flow = - flow.fed5GCode == "O.O.6" - - -isRule32f : Flow -> Bool -isRule32f flow = - flow.fed5GCode == "O.O.6" - - -isRule32g1 : (Flow -> Counterparty) -> Date -> Flow -> Bool -isRule32g1 counterparty t flow = - let - cpty = counterparty flow - remainingDays = daysToMaturity t flow.maturityDate - in - flow.fed5GCode == "O.D.7" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && (0 < remainingDays && remainingDays <= 30) - -isRule32g2 : (Flow -> Counterparty) -> Date -> Flow -> Bool -isRule32g2 counterparty t flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.7" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && daysToMaturity t flow.maturityDate <= 30 - -isRule32g3 : (Flow -> Counterparty) -> Date -> Flow -> Bool -isRule32g3 counterparty t flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.7" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && daysToMaturity t flow.maturityDate == 0 - && flow.insured == FDIC - -isRule32g4 : (Flow -> Counterparty) -> Date -> Flow -> Bool -isRule32g4 counterparty t flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.7" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && daysToMaturity t flow.maturityDate == 0 - && flow.insured /= FDIC - -isRule32g5 : (Flow -> Counterparty) -> Flow -> Bool -isRule32g5 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.11" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && flow.insured == FDIC - -isRule32g6 : (Flow -> Counterparty) -> Flow -> Bool -isRule32g6 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.11" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && flow.insured /= FDIC - -isRule32g7 : (Flow -> Counterparty) -> Flow -> Bool -isRule32g7 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.8" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && flow.insured == FDIC - -isRule32g8 : (Flow -> Counterparty) -> Flow -> Bool -isRule32g8 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.9" - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && flow.insured == FDIC - -isRule32g9 : (Flow -> Counterparty) -> Flow -> Bool -isRule32g9 counterparty flow = - let - cpty = counterparty flow - in - (flow.fed5GCode == "O.D.8" || flow.fed5GCode == "O.D.9") - && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) - && flow.insured /= FDIC - - -isRule32h1 : Flow -> Bool -isRule32h1 flow = - Debug.todo "Too many 32(h) rules to do..." - - -isRule32h2 : Flow -> Bool -isRule32h2 flow = - Debug.todo "Too many 32(h) rules to do..." - - -isRule32h3 : (Flow -> Counterparty) -> Flow -> Bool -isRule32h3 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.4" - && List.member cpty.counterpartyType - [ NonFinancialCorporate - , Sovereign - , CentralBank - , GovernmentSponsoredEntity - , PublicSectorEntity - , MultilateralDevelopmentBank - , OtherSupranational - , Bank - , SupervisedNonBankFinancialEntity - , DebtIssuingSpecialPurposeEntity - , OtherFinancialEntity - , Other - ] - && flow.insured == FDIC - - -isRule32h4 : (Flow -> Counterparty) -> Flow -> Bool -isRule32h4 counterparty flow = - let - cpty = counterparty flow - in - flow.fed5GCode == "O.D.4" - && List.member cpty.counterpartyType - [ NonFinancialCorporate - , Sovereign - , CentralBank - , GovernmentSponsoredEntity - , PublicSectorEntity - , MultilateralDevelopmentBank - , OtherSupranational - , Bank - , SupervisedNonBankFinancialEntity - , DebtIssuingSpecialPurposeEntity - , OtherFinancialEntity - , Other - ] - && flow.insured /= FDIC - - -isRule32h5 : Flow -> Bool -isRule32h5 flow = - Debug.todo "Too many 32(h) rules to do..." - - -isRule32i : Flow -> Bool -isRule32i flow = - Debug.todo "Too many 32(i) rules to do..." - - -isRule32j : Flow -> Bool -isRule32j flow = - Debug.todo "Too many 32(j) rules to do..." - - -isRule32k : Flow -> Bool -isRule32k flow = - Debug.todo "Too many 32(k) rules to do..." - - -isRule32l : Flow -> Bool -isRule32l flow = - flow.fed5GCode == "O.O.22" - - -isRule33f1iii : Date -> Flow -> Bool -isRule33f1iii t flow = - let - days = daysToMaturity t flow.effectiveMaturityDate - in - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.5", "I.S.6", "I.S.7"] - && flow.assetType == Level1Assets - && (0 < days && days <= 30) - - -isRule33f1iv : Date -> Flow -> Bool -isRule33f1iv t flow = - let - days = daysToMaturity t flow.effectiveMaturityDate - in - List.member flow.fed5GCode ["I.S.1", "I.S.2", "I.S.5", "I.S.6", "I.S.7"] - && flow.assetType == Level2aAssets - && (0 < days && days <= 30) - diff --git a/src/Morphir/Sample/LCR/Product.elm b/src/Morphir/Sample/LCR/Product.elm deleted file mode 100644 index 275d4bc..0000000 --- a/src/Morphir/Sample/LCR/Product.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Morphir.Sample.LCR.Product exposing (..) - - -type alias ProductId = String - - -type ProductType - = Cash - | Equity - | Bond - | Other -- There are way more... - - -type alias Product = - { productId : ProductId - , productType : ProductType - , isHQLA : Bool - } diff --git a/src/Morphir/Sample/LCR/Rules.elm b/src/Morphir/Sample/LCR/Rules.elm deleted file mode 100644 index 812c8d2..0000000 --- a/src/Morphir/Sample/LCR/Rules.elm +++ /dev/null @@ -1,47 +0,0 @@ -module Morphir.Sample.LCR.Rules exposing (..) - - -import Morphir.SDK.Basics exposing (Decimal) -import Morphir.Sample.LCR.Basics exposing (..) -import Morphir.Sample.LCR.Flows exposing (..) -import Morphir.Sample.LCR.Counterparty exposing (..) - - -type alias Rule a = - { name : String - , weight : Decimal - , applies : a -> Bool - } - - -isApplicable : a -> (Rule a) -> Bool -isApplicable a rule = - rule.applies a - - -findApplicable : a -> List (Rule a) -> Maybe (Rule a) -findApplicable a rules = - rules - |> List.filter (isApplicable a) - |> List.head - - -isAnyApplicable : List (Rule a) -> a -> Bool -isAnyApplicable rules a = - rules - |> List.filter (isApplicable a) - |> List.isEmpty - |> not - - -find : String -> List (Rule a) -> Maybe (Rule a) -find name rules = - rules - |> List.filter (\r -> r.name == name) - |> List.head - - -findAll : List String -> List (Rule a) -> List (Rule a) -findAll names rules = - rules - |> List.filter (\r -> List.member r.name names) diff --git a/src/Morphir/Sample/readme.md b/src/Morphir/Sample/README.md similarity index 97% rename from src/Morphir/Sample/readme.md rename to src/Morphir/Sample/README.md index 8473ef7..8dc7350 100644 --- a/src/Morphir/Sample/readme.md +++ b/src/Morphir/Sample/README.md @@ -5,4 +5,4 @@ These are samples of various flavors of Morphir models (rewritten into pure Elm) This is an example of pure rules processing. There are a large number of such projects and often the business logic needs to be shared across systems (things like account and asset categorization). The standard approach is to share this via enriched data feeds from one system to the other, resulting in a complex web of feed dependencies. Attempts have been made to share libraries. These usually fail due to the fact that classes and technologies differ greatly across systems and library version management across teams becomes an extreme burden. To solve this problem, Morphir takes the approach of sharing the models, including logic, and allowing teams to manage the code generation into their respective technologies as needed. Morphir provides generic Java code generation for free for those who can use it. ### Apps -These are examples of full low-code style application definitions. They require a abstraction level above base Morphir (or Elm) meaning that we apply specialized backends to process them. The goal is to process them into full distributed systems using both internal infrastructure tools, public cloud, or others. They would still be processed to the standard set of transpiled languages using the basic code generation, so are still useful without the specialized generators. \ No newline at end of file +These are examples of full low-code style application definitions. They require a abstraction level above base Morphir (or Elm) meaning that we apply specialized backends to process them. The goal is to process them into full distributed systems using both internal infrastructure tools, public cloud, or others. They would still be processed to the standard set of transpiled languages using the basic code generation, so are still useful without the specialized generators. diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/AncillaryRoleEnum.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/AncillaryRoleEnum.elm new file mode 100644 index 0000000..e6201fe --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/AncillaryRoleEnum.elm @@ -0,0 +1,40 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.CDM.MiFIR.AncillaryRoleEnum exposing (AncillaryRoleEnum(..)) + + +type AncillaryRoleEnum + = DisruptionEventsDeterminingParty + | ExtraordinaryDividendsParty + | PredeterminedClearingOrganizationParty + | ExerciseNoticeReceiverPartyManual + | ExerciseNoticeReceiverPartyOptionalEarlyTermination + | ExerciseNoticeReceiverPartyCancelableProvision + | ExerciseNoticeReceiverPartyExtendibleProvision + | CalculationAgentIndependent + | CalculationAgentOptionalEarlyTermination + | CalculationAgentMandatoryEarlyTermination + | CalculationAgentFallback + | ArrangingBroker + | Beneficiary + | ClearingFirm + | CounterpartyAfflilate + | Guarantor + | OtherParty + | PrimeBroker + | SettlementAgent diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/Basics.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/Basics.elm new file mode 100644 index 0000000..3cfb64c --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/Basics.elm @@ -0,0 +1,33 @@ +module Morphir.Sample.Reg.CDM.MiFIR.Basics exposing (..) + + +exists : Maybe a -> Bool +exists m = + case m of + Nothing -> + False + + _ -> + True + + +flatMap : (a -> Maybe b) -> Maybe a -> Maybe b +flatMap f maybe = + case maybe of + Just value -> + f value + + Nothing -> + Nothing + + +type alias Number = + Float + + +type alias Amount = + Float + + +type alias Version = + Int diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/Enums.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/Enums.elm new file mode 100644 index 0000000..3b61986 --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/Enums.elm @@ -0,0 +1,36 @@ +module Morphir.Sample.Reg.CDM.MiFIR.Enums exposing (..) + + +type PriceExpressionEnum + = AbsoluteTerms + | PercentageOfNotional + + +type QuoteBasisEnum + = Currency1PerCurrency2 + | Currency2PerCurrency1 + + +type AccountTypeEnum + = AccountTypeEnum + | AggregateClient + | Client + | House + + +type NotionalAdjustmentEnum + = Execution + | PortfolioRebalancing + | Standard + + +type CounterpartyRoleEnum + = Party1 + | Party2 + + +type PeriodEnum + = D + | W + | M + | Y diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/FloatingRateIndexEnum.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/FloatingRateIndexEnum.elm new file mode 100644 index 0000000..1d18ccd --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/FloatingRateIndexEnum.elm @@ -0,0 +1,449 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.CDM.MiFIR.FloatingRateIndexEnum exposing (..) + + +type FloatingRateIndexEnum + = AED_EBOR_Reuters + | AUD_AONIA_OIS_COMPOUND + | OIS + | AUD_AONIA_OIS_COMPOUND_SwapMarker + | AUD_BBR_AUBBSW + | AUD_BBR_BBSW + | AUD_BBR_BBSW_Bloomberg + | AUD_BBR_BBSY__BID_ + | AUD_BBR_ISDC + | AUD_LIBOR_BBA + | AUD_LIBOR_BBA_Bloomberg + | AUD_LIBOR_Reference_Banks + | AUD_Quarterly_Swap_Rate_ICAP + | AUD_Quarterly_Swap_Rate_ICAP_Reference_Banks + | AUD_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | AUD_Semi_Annual_Swap_Rate_BGCANTOR_Reference_Banks + | AUD_Semi_Annual_Swap_Rate_ICAP_Reference_Banks + | AUD_Semi_annual_Swap_Rate_ICAP + | AUD_Swap_Rate_Reuters + | BRL_CDI + | CAD_BA_CDOR + | CAD_BA_CDOR_Bloomberg + | CAD_BA_ISDD + | CAD_BA_Reference_Banks + | CAD_BA_Reuters + | CAD_BA_Telerate + | CAD_CORRA_OIS_COMPOUND + | CAD_ISDA_Swap_Rate + | CAD_LIBOR_BBA + | CAD_LIBOR_BBA_Bloomberg + | CAD_LIBOR_BBA_SwapMarker + | CAD_LIBOR_Reference_Banks + | CAD_REPO_CORRA + | CAD_TBILL_ISDD + | CAD_TBILL_Reference_Banks + | CAD_TBILL_Reuters + | CAD_TBILL_Telerate + | CHF_3M_LIBOR_SWAP_CME_vs_LCH_ICAP + | CHF_3M_LIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | CHF_3M_LIBOR_SWAP_EUREX_vs_LCH_ICAP + | CHF_3M_LIBOR_SWAP_EUREX_vs_LCH_ICAP_Bloomberg + | CHF_6M_LIBORSWAP_CME_vs_LCH_ICAP_Bloomberg + | CHF_6M_LIBOR_SWAP_CME_vs_LCH_ICAP + | CHF_6M_LIBOR_SWAP_EUREX_vs_LCH_ICAP + | CHF_6M_LIBOR_SWAP_EUREX_vs_LCH_ICAP_Bloomberg + | CHF_Annual_Swap_Rate + | CHF_Annual_Swap_Rate_11_00_ICAP + | CHF_Annual_Swap_Rate_Reference_Banks + | CHF_Basis_Swap_3m_vs_6m_LIBOR_11_00_ICAP + | CHF_ISDAFIX_Swap_Rate + | CHF_LIBOR_BBA + | CHF_LIBOR_BBA_Bloomberg + | CHF_LIBOR_ISDA + | CHF_LIBOR_Reference_Banks + | CHF_OIS_11_00_ICAP + | CHF_SARON_OIS_COMPOUND + | CHF_TOIS_OIS_COMPOUND + | CHF_USD_Basis_Swaps_11_00_ICAP + | CLP_TNA + | CL_CLICP_Bloomberg + | CNH_HIBOR_Reference_Banks + | CNH_HIBOR_TMA + | CNY_7_Repo_Compounding_Date + | CNY_CNREPOFIX_CFXS_Reuters + | CNY_PBOCB_Reuters + | CNY_Quarterly_7_day_Repo_Non_Deliverable_Swap_Rate_TRADITION + | CNY_Quarterly_7_day_Repo_Non_Deliverable_Swap_Rate_TRADITION_Reference_Banks + | CNY_SHIBOR_Reuters + | CNY_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | CNY_Semi_Annual_Swap_Rate_Reference_Banks + | CNY_Shibor_OIS_Compounding + | COP_IBR_OIS_COMPOUND + | CZK_Annual_Swap_Rate_11_00_BGCANTOR + | CZK_Annual_Swap_Rate_Reference_Banks + | CZK_PRIBOR_PRBO + | CZK_PRIBOR_Reference_Banks + | DKK_CIBOR2_Bloomberg + | DKK_CIBOR2_DKNA13 + | DKK_CIBOR_DKNA13 + | DKK_CIBOR_DKNA13_Bloomberg + | DKK_CIBOR_Reference_Banks + | DKK_CITA_DKNA14_COMPOUND + | DKK_DKKOIS_OIS_COMPOUND + | EUR_3M_EURIBOR_SWAP_CME_vs_LCH_ICAP + | EUR_3M_EURIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | EUR_3M_EURIBOR_SWAP_EUREX_vs_LCH_ICAP + | EUR_3M_EURIBOR_SWAP_EUREX_vs_LCH_ICAP_Bloomberg + | EUR_6M_EURIBOR_SWAP_CME_vs_LCH_ICAP + | EUR_6M_EURIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | EUR_6M_EURIBOR_SWAP_EUREX_vs_LCH_ICAP + | EUR_6M_EURIBOR_SWAP_EUREX_vs_LCH_ICAP_Bloomberg + | EUR_Annual_Swap_Rate_10_00 + | EUR_Annual_Swap_Rate_10_00_BGCANTOR + | EUR_Annual_Swap_Rate_10_00_Bloomberg + | EUR_Annual_Swap_Rate_10_00_ICAP + | EUR_Annual_Swap_Rate_10_00_SwapMarker + | EUR_Annual_Swap_Rate_10_00_TRADITION + | EUR_Annual_Swap_Rate_11_00 + | EUR_Annual_Swap_Rate_11_00_Bloomberg + | EUR_Annual_Swap_Rate_11_00_ICAP + | EUR_Annual_Swap_Rate_11_00_SwapMarker + | EUR_Annual_Swap_Rate_3_Month + | EUR_Annual_Swap_Rate_3_Month_SwapMarker + | EUR_Annual_Swap_Rate_4_15_TRADITION + | EUR_Annual_Swap_Rate_Reference_Banks + | EUR_Basis_Swap_EONIA_vs_3m_EUR_IBOR_Swap_Rates_A_360_10_00_ICAP + | EUR_EONIA_AVERAGE + | EUR_EONIA_OIS_10_00_BGCANTOR + | EUR_EONIA_OIS_10_00_ICAP + | EUR_EONIA_OIS_10_00_TRADITION + | EUR_EONIA_OIS_11_00_ICAP + | EUR_EONIA_OIS_4_15_TRADITION + | EUR_EONIA_OIS_COMPOUND + | EUR_EONIA_OIS_COMPOUND_Bloomberg + | EUR_EONIA_Swap_Index + | EUR_EURIBOR_Act_365 + | EUR_EURIBOR_Act_365_Bloomberg + | EUR_EURIBOR_Annual_Bond_Swap_vs_1m_11_00_ICAP + | EUR_EURIBOR_Basis_Swap_1m_vs_3m_Euribor_11_00_ICAP + | EUR_EURIBOR_Basis_Swap_3m_vs_6m_11_00_ICAP + | EUR_EURIBOR_Reference_Banks + | EUR_EURIBOR_Reuters + | EUR_EURIBOR_Telerate + | EUR_EURONIA_OIS_COMPOUND + | EUR_ISDA_EURIBOR_Swap_Rate_11_00 + | EUR_ISDA_EURIBOR_Swap_Rate_12_00 + | EUR_ISDA_LIBOR_Swap_Rate_10_00 + | EUR_ISDA_LIBOR_Swap_Rate_11_00 + | EUR_LIBOR_BBA + | EUR_LIBOR_BBA_Bloomberg + | EUR_LIBOR_Reference_Banks + | EUR_TAM_CDC + | EUR_TEC10_CNO + | EUR_TEC10_CNO_SwapMarker + | EUR_TEC10_Reference_Banks + | EUR_TEC5_CNO + | EUR_TEC5_CNO_SwapMarker + | EUR_TEC5_Reference_Banks + | EUR_TMM_CDC_COMPOUND + | EUR_USD_Basis_Swaps_11_00_ICAP + | GBP_6M_LIBOR_SWAP_CME_vs_LCH_ICAP + | GBP_6M_LIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | GBP_6M_LIBOR_SWAP_EUREX_vs_LCH_ICAP + | GBP_6M_LIBOR_SWAP_EUREX_vs_LCH_ICAP_Bloomberg + | GBP_ISDA_Swap_Rate + | GBP_LIBOR_BBA + | GBP_LIBOR_BBA_Bloomberg + | GBP_LIBOR_ISDA + | GBP_LIBOR_Reference_Banks + | GBP_SONIA_COMPOUND + | GBP_SONIA_OIS_11_00_ICAP + | GBP_SONIA_OIS_11_00_TRADITION + | GBP_SONIA_OIS_4_15_TRADITION + | GBP_Semi_Annual_Swap_Rate + | GBP_Semi_Annual_Swap_Rate_11_00_ICAP + | GBP_Semi_Annual_Swap_Rate_11_00_TRADITION + | GBP_Semi_Annual_Swap_Rate_4_15_TRADITION + | GBP_Semi_Annual_Swap_Rate_Reference_Banks + | GBP_Semi_Annual_Swap_Rate_SwapMarker26 + | GBP_USD_Basis_Swaps_11_00_ICAP + | GBP_WMBA_RONIA_COMPOUND + | GBP_WMBA_SONIA_COMPOUND + | GRD_ATHIBOR_ATHIBOR + | GRD_ATHIBOR_Reference_Banks + | GRD_ATHIBOR_Telerate + | GRD_ATHIMID_Reference_Banks + | GRD_ATHIMID_Reuters + | HKD_HIBOR_HIBOR_ + | HKD_HIBOR_HIBOR_Bloomberg + | HKD_HIBOR_HKAB + | HKD_HIBOR_HKAB_Bloomberg + | HKD_HIBOR_ISDC + | HKD_HIBOR_Reference_Banks + | HKD_HONIX_OIS_COMPOUND + | HKD_ISDA_Swap_Rate_11_00 + | HKD_ISDA_Swap_Rate_4_00 + | HKD_Quarterly_Annual_Swap_Rate_11_00_BGCANTOR + | HKD_Quarterly_Annual_Swap_Rate_11_00_TRADITION + | HKD_Quarterly_Annual_Swap_Rate_4_00_BGCANTOR + | HKD_Quarterly_Annual_Swap_Rate_Reference_Banks + | HKD_Quarterly_Quarterly_Swap_Rate_11_00_ICAP + | HKD_Quarterly_Quarterly_Swap_Rate_4_00_ICAP + | HKD_Quarterly_Quarterly_Swap_Rate_Reference_Banks + | HUF_BUBOR_Reference_Banks + | HUF_BUBOR_Reuters + | IDR_IDMA_Bloomberg + | IDR_IDRFIX + | IDR_JIBOR_Reuters + | IDR_SBI_Reuters + | IDR_SOR_Reference_Banks + | IDR_SOR_Reuters + | IDR_SOR_Telerate + | IDR_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | IDR_Semi_Annual_Swap_Rate_Reference_Banks + | ILS_TELBOR01_Reuters + | ILS_TELBOR_Reference_Banks + | INR_BMK + | INR_CMT + | INR_FBIL_MIBOR_OIS_COMPOUND + | INR_INBMK_REUTERS + | INR_MIBOR_OIS_COMPOUND + | INR_MIFOR + | INR_MIOIS + | INR_MITOR_OIS_COMPOUND + | INR_Reference_Banks + | INR_Semi_Annual_Swap_Rate_11_30_BGCANTOR + | INR_Semi_Annual_Swap_Rate_Non_deliverable_16_00_Tullett_Prebon + | INR_Semi_Annual_Swap_Rate_Reference_Banks + | ISK_REIBOR_Reference_Banks + | ISK_REIBOR_Reuters + | JPY_Annual_Swap_Rate_11_00_TRADITION + | JPY_Annual_Swap_Rate_3_00_TRADITION + | JPY_BBSF_Bloomberg_10_00 + | JPY_BBSF_Bloomberg_15_00 + | JPY_ISDA_Swap_Rate_10_00 + | JPY_ISDA_Swap_Rate_15_00 + | JPY_LIBOR_BBA + | JPY_LIBOR_BBA_Bloomberg + | JPY_LIBOR_FRASETT + | JPY_LIBOR_ISDA + | JPY_LIBOR_Reference_Banks + | JPY_LTPR_MHCB + | JPY_LTPR_TBC + | JPY_MUTANCALL_TONAR + | JPY_OIS_11_00_ICAP + | JPY_OIS_11_00_TRADITION + | JPY_OIS_3_00_TRADITION + | JPY_Quoting_Banks_LIBOR + | JPY_STPR_Quoting_Banks + | JPY_TIBOR_17096 + | JPY_TIBOR_17097 + | JPY_TIBOR_DTIBOR01 + | JPY_TIBOR_TIBM + | JPY_TIBOR_TIBM_Reference_Banks + | JPY_TIBOR_TIBM__10_Banks_ + | JPY_TIBOR_TIBM__5_Banks_ + | JPY_TIBOR_TIBM__All_Banks_ + | JPY_TIBOR_TIBM__All_Banks__Bloomberg + | JPY_TIBOR_ZTIBOR + | JPY_TONA_OIS_COMPOUND + | JPY_TSR_Reference_Banks + | JPY_TSR_Reuters_10_00 + | JPY_TSR_Reuters_15_00 + | JPY_TSR_Telerate_10_00 + | JPY_TSR_Telerate_15_00 + | JPY_USD_Basis_Swaps_11_00_ICAP + | KRW_Bond_3222 + | KRW_CD_3220 + | KRW_CD_KSDA_Bloomberg + | KRW_Quarterly_Annual_Swap_Rate_3_30_ICAP + | MXN_TIIE_Banxico + | MXN_TIIE_Banxico_Bloomberg + | MXN_TIIE_Banxico_Reference_Banks + | MYR_KLIBOR_BNM + | MYR_KLIBOR_Reference_Banks + | MYR_Quarterly_Swap_Rate_11_00_TRADITION + | MYR_Quarterly_Swap_Rate_TRADITION_Reference_Banks + | NOK_NIBOR_NIBR + | NOK_NIBOR_NIBR_Reference_Banks + | NOK_NIBOR_OIBOR + | NOK_NIBOR_Reference_Banks + | NZD_BBR_BID + | NZD_BBR_FRA + | NZD_BBR_ISDC + | NZD_BBR_Reference_Banks + | NZD_BBR_Telerate + | NZD_NZIONA_OIS_COMPOUND + | NZD_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | NZD_Semi_Annual_Swap_Rate_BGCANTOR_Reference_Banks + | NZD_Swap_Rate_ICAP + | NZD_Swap_Rate_ICAP_Reference_Banks + | PHP_PHIREF_BAP + | PHP_PHIREF_Bloomberg + | PHP_PHIREF_Reference_Banks + | PHP_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | PHP_Semi_Annual_Swap_Rate_Reference_Banks + | PLN_POLONIA_OIS_COMPOUND + | PLN_WIBOR_Reference_Banks + | PLN_WIBOR_WIBO + | PLZ_WIBOR_Reference_Banks + | PLZ_WIBOR_WIBO + | REPOFUNDS_RATE_FRANCE_OIS_COMPOUND + | REPOFUNDS_RATE_GERMANY_OIS_COMPOUND + | REPOFUNDS_RATE_ITALY_OIS_COMPOUND + | RON_Annual_Swap_Rate_11_00_BGCANTOR + | RON_Annual_Swap_Rate_Reference_Banks + | RON_RBOR_Reuters + | RUB_Annual_Swap_Rate_11_00_BGCANTOR + | RUB_Annual_Swap_Rate_12_45_TRADITION + | RUB_Annual_Swap_Rate_4_15_TRADITION + | RUB_Annual_Swap_Rate_Reference_Banks + | RUB_Annual_Swap_Rate_TRADITION_Reference_Banks + | RUB_MOSPRIME_NFEA + | RUB_MOSPRIME_Reference_Banks + | RUB_RUONIA_OIS_COMPOUND + | SAR_SRIOR_Reference_Banks + | SAR_SRIOR_SUAA + | SEK_Annual_Swap_Rate + | SEK_Annual_Swap_Rate_SESWFI + | SEK_SIOR_OIS_COMPOUND + | SEK_STIBOR_Bloomberg + | SEK_STIBOR_Reference_Banks + | SEK_STIBOR_SIDE + | SGD_SIBOR_Reference_Banks + | SGD_SIBOR_Reuters + | SGD_SIBOR_Telerate + | SGD_SONAR_OIS_COMPOUND + | SGD_SONAR_OIS_VWAP_COMPOUND + | SGD_SOR_Reference_Banks + | SGD_SOR_Reuters + | SGD_SOR_Telerate + | SGD_SOR_VWAP + | SGD_SOR_VWAP_Reference_Banks + | SGD_Semi_Annual_Currency_Basis_Swap_Rate_11_00_Tullett_Prebon + | SGD_Semi_Annual_Currency_Basis_Swap_Rate_16_00_Tullett_Prebon + | SGD_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | SGD_Semi_Annual_Swap_Rate_11_00_TRADITION + | SGD_Semi_Annual_Swap_Rate_11_00_Tullett_Prebon + | SGD_Semi_Annual_Swap_Rate_16_00_Tullett_Prebon + | SGD_Semi_Annual_Swap_Rate_ICAP + | SGD_Semi_Annual_Swap_Rate_ICAP_Reference_Banks + | SGD_Semi_Annual_Swap_Rate_Reference_Banks + | SGD_Semi_Annual_Swap_Rate_TRADITION_Reference_Banks + | SKK_BRIBOR_BRBO + | SKK_BRIBOR_Bloomberg + | SKK_BRIBOR_NBSK07 + | SKK_BRIBOR_Reference_Banks + | THB_SOR_Reference_Banks + | THB_SOR_Reuters + | THB_SOR_Telerate + | THB_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | THB_Semi_Annual_Swap_Rate_Reference_Banks + | THB_THBFIX_Reference_Banks + | THB_THBFIX_Reuters + | TRY_Annual_Swap_Rate_11_00_TRADITION + | TRY_Annual_Swap_Rate_11_15_BGCANTOR + | TRY_Annual_Swap_Rate_Reference_Banks + | TRY_Semi_Annual_Swap_Rate_TRADITION_Reference_Banks + | TRY_TRYIBOR_Reference_Banks + | TRY_TRYIBOR_Reuters + | TWD_Quarterly_Annual_Swap_Rate_11_00_BGCANTOR + | TWD_Quarterly_Annual_Swap_Rate_Reference_Banks + | TWD_Reference_Dealers + | TWD_Reuters_6165 + | TWD_TAIBIR01 + | TWD_TAIBIR02 + | TWD_TAIBOR_Bloomberg + | TWD_TAIBOR_Reuters + | TWD_TWCPBA + | TWD_Telerate_6165 + | UK_RPIX + | USA_CPI_U + | USD_3M_LIBOR_SWAP_CME_vs_LCH_ICAP + | USD_3M_LIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | USD_6M_LIBOR_SWAP_CME_vs_LCH_ICAP + | USD_6M_LIBOR_SWAP_CME_vs_LCH_ICAP_Bloomberg + | USD_Annual_Swap_Rate_11_00_BGCANTOR + | USD_Annual_Swap_Rate_11_00_TRADITION + | USD_Annual_Swap_Rate_4_00_TRADITION + | USD_BA_H_15 + | USD_BA_Reference_Dealers + | USD_BMA_Municipal_Swap_Index + | USD_CD_H_15 + | USD_CD_Reference_Dealers + | USD_CMS_Reference_Banks + | USD_CMS_Reference_Banks_ICAP_SwapPX + | USD_CMS_Reuters + | USD_CMS_Telerate + | USD_CMT_T7051 + | USD_CMT_T7052 + | USD_COF11_FHLBSF + | USD_COF11_Reuters + | USD_COF11_Telerate + | USD_CP_H_15 + | USD_CP_Reference_Dealers + | USD_FFCB_DISCO + | USD_Federal_Funds_H_15 + | USD_Federal_Funds_H_15_Bloomberg + | USD_Federal_Funds_H_15_OIS_COMPOUND + | USD_Federal_Funds_Reference_Dealers + | USD_ISDAFIX3_Swap_Rate + | USD_ISDAFIX3_Swap_Rate_3_00 + | USD_ISDA_Swap_Rate + | USD_ISDA_Swap_Rate_3_00 + | USD_LIBOR_BBA + | USD_LIBOR_BBA_Bloomberg + | USD_LIBOR_ISDA + | USD_LIBOR_LIBO + | USD_LIBOR_Reference_Banks + | USD_Municipal_Swap_Libor_Ratio_11_00_ICAP + | USD_Municipal_Swap_Rate_11_00_ICAP + | USD_OIS_11_00_BGCANTOR + | USD_OIS_11_00_LON_ICAP + | USD_OIS_11_00_NY_ICAP + | USD_OIS_11_00_TRADITION + | USD_OIS_3_00_BGCANTOR + | USD_OIS_3_00_NY_ICAP + | USD_OIS_4_00_TRADITION + | USD_Overnight_Bank_Funding_Rate + | USD_Prime_H_15 + | USD_Prime_Reference_Banks + | USD_SIBOR_Reference_Banks + | USD_SIBOR_SIBO + | USD_SIFMA_Municipal_Swap_Index + | USD_SOFR_COMPOUND + | USD_S_P_Index_High_Grade + | USD_TBILL_H_15 + | USD_TBILL_H_15_Bloomberg + | USD_TBILL_Secondary_Market + | USD_TIBOR_ISDC + | USD_TIBOR_Reference_Banks + | USD_Treasury_19901_3_00_ICAP + | USD_Treasury_Rate_ICAP_BrokerTec + | USD_Treasury_Rate_SwapMarker100 + | USD_Treasury_Rate_SwapMarker99 + | USD_Treasury_Rate_T19901 + | USD_Treasury_Rate_T500 + | VND_Semi_Annual_Swap_Rate_11_00_BGCANTOR + | VND_Semi_Annual_Swap_Rate_Reference_Banks + | ZAR_DEPOSIT_Reference_Banks + | ZAR_DEPOSIT_SAFEX + | ZAR_JIBAR_Reference_Banks + | ZAR_JIBAR_SAFEX + | ZAR_PRIME_AVERAGE + | ZAR_PRIME_AVERAGE_Reference_Banks + | ZAR_Quarterly_Swap_Rate_1_00_TRADITION + | ZAR_Quarterly_Swap_Rate_5_30_TRADITION + | ZAR_Quarterly_Swap_Rate_TRADITION_Reference_Banks diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/Quantities.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/Quantities.elm new file mode 100644 index 0000000..088f6c8 --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/Quantities.elm @@ -0,0 +1,37 @@ +module Morphir.Sample.Reg.CDM.MiFIR.Quantities exposing + ( NonNegativeQuantity + , Quantity + , UnitEnum(..) + , nonNegativeQuantity + ) + +import Morphir.Sample.Reg.CDM.MiFIR.Basics exposing (Amount) + + +type alias Quantity a = + { a + | amount : Amount + , unit : UnitEnum + } + + +type UnitEnum + = MWH + | MMBTU + | BBL + | GAL + | BSH + + +type alias NonNegativeQuantity = + Quantity + {} + + +nonNegativeQuantity : Amount -> UnitEnum -> Result String NonNegativeQuantity +nonNegativeQuantity amount unit = + if amount < 0 then + Err "amount must be greater or equal to 0" + + else + Ok { amount = amount, unit = unit } diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/README.md b/src/Morphir/Sample/Reg/CDM/MiFIR/README.md new file mode 100644 index 0000000..06e00bf --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/README.md @@ -0,0 +1,13 @@ +# ISDA CDM - Industry Standards +The ISDA Common Domain Model (CDM) 2.0 specification provides a standard blueprint for managing the life cycle of various +traded products. More background can be found at the [ISDA CDM Factsheet](https://www.isda.org/2018/11/22/isda-cdm-factsheet/) +and [Rosetta CDM](https://docs.rosetta-technology.io/cdm/readme.html). + +The CDM is defined in the [Rosetta DSL](https://docs.rosetta-technology.io/dsl/index.html). This example implements +a portion of CDM specification in Morphir to demonstrate how a DSL can be supported by the Morphir IR. There are a few +possible approaches to doing this, including compiling the DSL directly into the Morphir IR or, alternatively, transpiling +the DSL into Elm, which is then compiled down to the Morphir IR. For this example, we take that latter approach to better +demonstrate how the concepts translate between the two languages. + +This example explores the [Price spec of MiFIR RTS_22](https://ui.rosetta-technology.io/#/system/read-only-CDM) (requires login). +The implementation can be found at [RTS22](RTS22.elm). diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22.elm new file mode 100644 index 0000000..f6a4b89 --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22.elm @@ -0,0 +1,547 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.CDM.MiFIR.RTS22 exposing (..) + +import Company.Operations.BooksAndRecords exposing (Quantity) +import List.Nonempty as Nonempty exposing (Nonempty) +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.Sample.Reg.CDM.MiFIR.AncillaryRoleEnum exposing (AncillaryRoleEnum) +import Morphir.Sample.Reg.CDM.MiFIR.Basics exposing (..) +import Morphir.Sample.Reg.CDM.MiFIR.Enums exposing (..) +import Morphir.Sample.Reg.CDM.MiFIR.FloatingRateIndexEnum exposing (FloatingRateIndexEnum(..)) +import Morphir.Sample.Reg.CDM.MiFIR.Quantities exposing (NonNegativeQuantity) +import Morphir.Sample.Reg.Currency exposing (Currency) +import Morphir.Sample.Reg.LCR.Flows exposing (Amount) + + +{-| This example focuses on the Rosetta DSL's reporting functionality. +-} + + + +-- Trade Data Structures -- + + +type PriceType + = FixedFixedPrice + | FixedFloatPrice + | BasisSwapPrice + | CDSPrice + + +type alias PriceNotation = + { price : Price + , assetIdentifier : Maybe AssetIdentifier + } + + +priceNotation : Price -> Maybe AssetIdentifier -> Result String PriceNotation +priceNotation price mAssetIdentifier = + let + currencyExists : Bool + currencyExists = + mAssetIdentifier |> Maybe.map .currency |> exists + + rateOptionExists : Bool + rateOptionExists = + mAssetIdentifier |> Maybe.map .rateOption |> exists + in + if exists price.fixedInterestRate && not currencyExists then + Err "The asset identifier for an interest rate spread must be a rate option." + + else if exists price.floatingInterestRate && not rateOptionExists then + Err "The asset identifier for a fixed interest rate must be a currency." + + else + Ok (PriceNotation price mAssetIdentifier) + + +type alias Price = + { fixedInterestRate : Maybe FixedInterestRate + , cashPrice : Maybe CashPrice + , exchangeRate : Maybe ExchangeRate + , floatingInterestRate : Maybe FloatingInterestRate + } + + +type alias CashPrice = + { grossPrice : Maybe ActualPrice + , cleanNetPrice : Maybe ActualPrice + , netPrice : Maybe ActualPrice + , accruedInterest : Maybe Number + , cashflowAmount : Maybe Money + } + + +type alias Money = + { currency : Currency + , amount : Amount + } + + +type alias ExchangeRate = + { quotedCurrencyPair : QuotedCurrencyPair {} + , rate : Number + , spotRate : Maybe Number + , forwardPoints : Maybe Number + , pointValue : Maybe Number + , crossRate : List CrossRate + } + + +type alias QuotedCurrencyPair a = + { a + | currency1 : Currency + , currency2 : Currency + , quoteBasis : QuoteBasisEnum + } + + +type alias CrossRate = + QuotedCurrencyPair + { rate : Number + , spotRate : Maybe Number + , forwardPoints : Maybe Number + } + + +type alias ActualPrice = + { currency : Maybe Currency + , amount : Number + , priceExpression : PriceExpressionEnum + } + + +type alias FixedInterestRate = + { rate : Number + } + + +type alias FloatingInterestRate = + { initialRate : Maybe Number + , spread : Maybe Number + , capRate : Maybe Number + , floorRate : Maybe Number + , multiplier : Maybe Number + } + + +type alias Trade = + { tradableProduct : TradableProduct + } + + +type alias Identifier = + { issuerReference : Maybe Party + , issuer : Maybe String + , assignedIdentifier : AssignedIdentifier + } + + +type alias AssignedIdentifier = + { identifier : String + , version : Maybe Version + } + + +type alias Party = + { partyId : String + , name : Maybe String + , person : List NaturalPerson + + --, account : Maybe Account + } + + +type alias Account = + { partyReference : Maybe Party + , accountNumber : String + , accountName : Maybe String + , accountType : Maybe AccountTypeEnum + , accountBeneficiary : Maybe Party + , servicingParty : Maybe Party + } + + +type alias NaturalPerson = + { honorific : Maybe String + , firstName : String + , middleName : List String + , initial : List String + , surname : String + , suffix : Maybe String + , dateOfBirth : Maybe LocalDate + } + + +type alias TradableProduct = + { product : Product + , quantityNotation : Nonempty QuantityNotation + , priceNotation : Nonempty PriceNotation + , counterparty : ( Counterparty, Counterparty ) + , ancillaryParty : List AncillaryParty + , adjustment : Maybe NotionalAdjustmentEnum + } + + +type alias Counterparty = + { role : CounterpartyRoleEnum + , partyReference : Party + } + + +type alias AncillaryParty = + { role : AncillaryRoleEnum + , partyReference : Nonempty Party + , onBehalfOf : Maybe CounterpartyRoleEnum + } + + +type alias QuantityNotation = + { quantity : NonNegativeQuantity + , assetIdentifier : AssetIdentifier + } + + +type alias AssetIdentifier = + { productIdentifier : Maybe ProductIdentifier + , currency : Maybe Currency + , rateOption : Maybe FloatingRateOption + } + + +type alias FloatingRateOption = + { floatingRateIndex : FloatingRateIndexEnum + , indexTenor : Maybe Period + } + + +type alias Period = + { periodMultiplier : Int + , period : PeriodEnum + } + + +type alias ProductIdentifier = + { identifier : String + } + + +type alias Product = + { contractualProduct : ContractualProduct + } + + +type alias ContractualProduct = + { economicTerms : EconomicTerms + } + + +type alias EconomicTerms = + { payout : Payout + } + + +type alias Payout = + { interestRatePayout : Maybe InterestRatePayout + , creditDefaultPayout : Maybe CreditDefaultPayout + } + + +type alias CreditDefaultPayout = + -- TODO: Empty for now + {} + + +type alias InterestRatePayout = + { rateSpecification : RateSpecification + } + + +type alias RateSpecification = + { fixedRate : List Number + , floatingRate : List Number + } + + + +-- Report -- +{- While the CDM RTS22 report contains all of the fields listed, this example focuses solely on the price and quantity fields. -} + + +type alias Report = + { price : Number + + --, reportStatus : ReportStatus + --, transactionReferenceNumber : TransactionReferenceNumber + --, tradingVenueTransactionIdentificationCode : TradingVenueTransactionIdentificationCode + --, executingEntityIdentificationCode : ExecutingEntityIdentificationCode + --, isInvestmentFirm : IsInvestmentFirm + --, submittingEntityIdentificationCode : SubmittingEntityIdentificationCode + --, buyerSeller : BuyerSeller + --, transmissionOfOrderIndicator : TransmissionOfOrderIndicator + --, tradingDateTime : TradingDateTime + --, tradingCapacity : TradingCapacity + , quantity : Amount + + --, venueOfExecution : VenueOfExecution + --, countryOfTheBranchMembership : CountryOfTheBranchMembership + --, instrumentIdentificationCode : InstrumentIdentificationCode + --, instrumentFullName : InstrumentFullName + --, instrumentClassification : InstrumentClassification + --, notionalCurrency1 : NotionalCurrency1 + --, notionalCurrency2 : NotionalCurrency2 + --, priceMultiplier : PriceMultiplier + --, underlyingInstrumentCode : UnderlyingInstrumentCode + --, underlyingIndexName : UnderlyingIndexName + --, underlyingIndexTermPeriod : UnderlyingIndexTermPeriod + --, underlyingIndexTermMultiplier : UnderlyingIndexTermMultiplier + --, expiryDate : ExpiryDate + --, deliveryType : DeliveryType + --, investmentDecisionWithinFirm : InvestmentDecisionWithinFirm + --, personResponsibleForInvestmentDecisionCountry : PersonResponsibleForInvestmentDecisionCountry + --, executionWithinFirm : ExecutionWithinFirm + --, personResponsibleForExecutionCountry : PersonResponsibleForExecutionCountry + --, commodityDerivativeIndicator : CommodityDerivativeIndicator + --, securitiesFinancingTransactionIndicator : SecuritiesFinancingTransactionIndicator + } + + +runReport : List Trade -> List Report +runReport trades = + List.filterMap mapTrade trades + + +mapTrade : Trade -> Maybe Report +mapTrade trade = + let + -- What should happen if any of the optional prices are not present? Skip or error? + price : Maybe Number + price = + priceOf trade + + quantity : Amount + quantity = + trade.tradableProduct.quantityNotation |> Nonempty.head |> .quantity |> .amount + in + price + |> Maybe.map (\p -> Report p quantity) + + +assetIdentifier : Maybe ProductIdentifier -> Maybe Currency -> Maybe FloatingRateOption -> Result String AssetIdentifier +assetIdentifier mProductId mCurrency mRateOption = + let + isValid : Bool + isValid = + case ( mProductId, mCurrency, mRateOption ) of + ( Just p, Nothing, Nothing ) -> + True + + ( Nothing, Just _, Nothing ) -> + True + + ( Nothing, Nothing, Just p ) -> + True + + _ -> + False + in + if isValid then + Ok (AssetIdentifier mProductId mCurrency mRateOption) + + else + Err "condition: one-of" + + +priceOf : Trade -> Maybe Number +priceOf trade = + priceType trade + |> Maybe.map + (\pt -> + case pt of + FixedFixedPrice -> + fixedFixedPrice trade + + FixedFloatPrice -> + fixedFloatPrice trade + + BasisSwapPrice -> + basisSwapPrice trade + + CDSPrice -> + cdsPrice trade + ) + |> Maybe.withDefault Nothing + + +priceType : Trade -> Maybe PriceType +priceType trade = + if isFixedFixed trade then + Just FixedFixedPrice + + else if isFixedFixed trade then + Just FixedFloatPrice + + else if isIRSwapBasis trade then + Just BasisSwapPrice + + else if isCreditDefaultSwap trade then + Just CDSPrice + + else + Nothing + + + +-- FixedFixed + + +isFixedFixed : Trade -> Bool +isFixedFixed trade = + let + count : Int + count = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.fixedRate) + |> Maybe.withDefault [] + |> List.length + in + count == 2 + + +{-| reporting rule FixedFixedPrice +filter when rule IsFixedFixed then +extract Trade -> tradableProduct -> priceNotation -> price -> fixedInterestRate then +maxBy FixedInterestRate -> rate then +extract FixedInterestRate -> rate as "Price" +-} +fixedFixedPrice : Trade -> Maybe Number +fixedFixedPrice trade = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.fixedInterestRate) + |> List.map .rate + |> List.maximum + + + +-- FixedFloat + + +isFixedFloat : Trade -> Bool +isFixedFloat trade = + let + fixedCount : Int + fixedCount = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.fixedRate) + |> Maybe.withDefault [] + |> List.length + + floatingCount : Int + floatingCount = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.floatingRate) + |> Maybe.withDefault [] + |> List.length + in + fixedCount == 1 && floatingCount == 1 + + +fixedFloatPrice : Trade -> Maybe Number +fixedFloatPrice trade = + -- TODO : Is this the correct interpretation of the above? + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.fixedInterestRate) + |> List.map .rate + |> List.maximum + + + +-- BasisSwap + + +isIRSwapBasis : Trade -> Bool +isIRSwapBasis trade = + let + count : Int + count = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.floatingRate) + |> Maybe.withDefault [] + |> List.length + in + count == 2 + + +basisSwapPrice : Trade -> Maybe Number +basisSwapPrice trade = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.floatingInterestRate) + |> List.filterMap .initialRate + |> List.head + + + +-- CDS + + +isCreditDefaultSwap : Trade -> Bool +isCreditDefaultSwap trade = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.creditDefaultPayout |> exists + + +cdsPrice : Trade -> Maybe Number +cdsPrice trade = + let + mp : Maybe Price + mp = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.head + |> Maybe.map .price + + fixedRate : Maybe Number + fixedRate = + mp + |> Maybe.map .fixedInterestRate + |> Maybe.withDefault Nothing + |> Maybe.map .rate + + floatingRate : Maybe (Maybe Number) + floatingRate = + mp + |> Maybe.map .floatingInterestRate + |> Maybe.withDefault Nothing + |> Maybe.map .initialRate + in + case ( fixedRate, floatingRate ) of + ( Just rate, _ ) -> + if rate /= 0 then + Just rate + + else + Nothing + + ( _, Just (Just initialRate) ) -> + Just initialRate + + _ -> + Just 0 diff --git a/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22Alt.elm b/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22Alt.elm new file mode 100644 index 0000000..903cb38 --- /dev/null +++ b/src/Morphir/Sample/Reg/CDM/MiFIR/RTS22Alt.elm @@ -0,0 +1,470 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.CDM.MiFIR.RTS22Alt exposing (..) + +import List.Nonempty as Nonempty exposing (Nonempty) +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.Sample.Reg.CDM.MiFIR.AncillaryRoleEnum exposing (AncillaryRoleEnum) +import Morphir.Sample.Reg.CDM.MiFIR.Basics exposing (..) +import Morphir.Sample.Reg.CDM.MiFIR.Enums exposing (..) +import Morphir.Sample.Reg.CDM.MiFIR.FloatingRateIndexEnum exposing (FloatingRateIndexEnum(..)) + + +type alias Report = + { price : Price + + --, reportStatus : ReportStatus + --, transactionReferenceNumber : TransactionReferenceNumber + --, tradingVenueTransactionIdentificationCode : TradingVenueTransactionIdentificationCode + --, executingEntityIdentificationCode : ExecutingEntityIdentificationCode + --, isInvestmentFirm : IsInvestmentFirm + --, submittingEntityIdentificationCode : SubmittingEntityIdentificationCode + --, buyerSeller : BuyerSeller + --, transmissionOfOrderIndicator : TransmissionOfOrderIndicator + --, tradingDateTime : TradingDateTime + --, tradingCapacity : TradingCapacity + , quantity : Quantity + + --, venueOfExecution : VenueOfExecution + --, countryOfTheBranchMembership : CountryOfTheBranchMembership + --, instrumentIdentificationCode : InstrumentIdentificationCode + --, instrumentFullName : InstrumentFullName + --, instrumentClassification : InstrumentClassification + --, notionalCurrency1 : NotionalCurrency1 + --, notionalCurrency2 : NotionalCurrency2 + --, priceMultiplier : PriceMultiplier + --, underlyingInstrumentCode : UnderlyingInstrumentCode + --, underlyingIndexName : UnderlyingIndexName + --, underlyingIndexTermPeriod : UnderlyingIndexTermPeriod + --, underlyingIndexTermMultiplier : UnderlyingIndexTermMultiplier + --, expiryDate : ExpiryDate + --, deliveryType : DeliveryType + --, investmentDecisionWithinFirm : InvestmentDecisionWithinFirm + --, personResponsibleForInvestmentDecisionCountry : PersonResponsibleForInvestmentDecisionCountry + --, executionWithinFirm : ExecutionWithinFirm + --, personResponsibleForExecutionCountry : PersonResponsibleForExecutionCountry + --, commodityDerivativeIndicator : CommodityDerivativeIndicator + --, securitiesFinancingTransactionIndicator : SecuritiesFinancingTransactionIndicator + } + + +type alias Quantity = + { amount : Amount + , unit : UnitEnum + } + + +type PriceType + = FixedFixedPrice + | FixedFloatPrice + | BasisSwapPrice + | CDSPrice + + +type alias PriceNotation = + { price : Price + } + + +type Price + = FixedInterestRate FixedInterestRate + | CashPrice CashPrice + | ExchangeRate ExchangeRate + | FloatingInterestRate FloatingInterestRate + + +type alias CashPrice = + { grossPrice : Maybe ActualPrice + , cleanNetPrice : Maybe ActualPrice + , netPrice : Maybe ActualPrice + , accruedInterest : Maybe Number + , cashflowAmount : Maybe Money + } + + +type alias Money = + { currency : Currency + , amount : Amount + } + + +type alias ExchangeRate = + { quotedCurrencyPair : QuotedCurrencyPair {} + , rate : Number + , spotRate : Maybe Number + , forwardPoints : Maybe Number + , pointValue : Maybe Number + , crossRate : List CrossRate + } + + +type alias QuotedCurrencyPair a = + { a + | currency1 : Currency + , currency2 : Currency + , quoteBasis : QuoteBasisEnum + } + + +type alias CrossRate = + QuotedCurrencyPair + { rate : Number + , spotRate : Maybe Number + , forwardPoints : Maybe Number + } + + +type alias ActualPrice = + { currency : Maybe Currency + , amount : Number + , priceExpression : PriceExpressionEnum + } + + +type alias FixedInterestRate = + { rate : Number + } + + +type alias FloatingInterestRate = + { initialRate : Maybe Number + , spread : Maybe Number + , capRate : Maybe Number + , floorRate : Maybe Number + , multiplier : Maybe Number + } + + +type alias Trade = + { tradableProduct : TradableProduct + } + + +type alias Identifier = + { issuerReference : Maybe Party + , issuer : Maybe String + , assignedIdentifier : AssignedIdentifier + } + + +type alias AssignedIdentifier = + { identifier : String + , version : Maybe Version + } + + +type alias Party = + { partyId : String + , name : Maybe String + , person : List NaturalPerson + + --, account : Maybe Account + } + + +type alias Account = + { partyReference : Maybe Party + , accountNumber : String + , accountName : Maybe String + , accountType : Maybe AccountTypeEnum + , accountBeneficiary : Maybe Party + , servicingParty : Maybe Party + } + + +type alias NaturalPerson = + { honorific : Maybe String + , firstName : String + , middleName : List String + , initial : List String + , surname : String + , suffix : Maybe String + , dateOfBirth : Maybe LocalDate + } + + +type alias TradableProduct = + { product : Product + , quantityNotation : Nonempty QuantityNotation + , priceNotation : Nonempty PriceNotation + , counterparty : ( Counterparty, Counterparty ) + , ancillaryParty : List AncillaryParty + , adjustment : Maybe NotionalAdjustmentEnum + } + + +type alias Counterparty = + { role : CounterpartyRoleEnum + , partyReference : Party + } + + +type alias AncillaryParty = + { role : AncillaryRoleEnum + , partyReference : Nonempty Party + , onBehalfOf : Maybe CounterpartyRoleEnum + } + + +type alias QuantityNotation = + { quantity : NonNegativeQuantity + , assetIdentifier : AssetIdentifier + } + + +type alias NonNegativeQuantity = + -- TODO + Float + + +type alias AssetIdentifier = + { productIdentifier : Maybe ProductIdentifier + , currency : Maybe Currency + , rateOption : Maybe FloatingRateOption + } + + +type alias FloatingRateOption = + { floatingRateIndex : FloatingRateIndexEnum + , indexTenor : Maybe Period + } + + +type alias Period = + { periodMultiplier : Int + , period : PeriodEnum + } + + +type alias ProductIdentifier = + { identifier : String + } + + +type alias Currency = + -- TODO + String + + +type alias Product = + { contractualProduct : ContractualProduct + } + + +type alias ContractualProduct = + { economicTerms : EconomicTerms + } + + +type alias EconomicTerms = + { payout : Payout + } + + +type alias Payout = + { interestRatePayout : Maybe InterestRatePayout + , creditDefaultPayout : Maybe CreditDefaultPayout + } + + +type alias CreditDefaultPayout = + -- TODO: Empty for now + {} + + +type alias InterestRatePayout = + { rateSpecification : RateSpecification + } + + +type alias RateSpecification = + { fixedRate : List Number + , floatingRate : List Number + } + + +price : Trade -> Maybe Number +price trade = + priceType trade + |> Maybe.map + (\pt -> + case pt of + FixedFixedPrice -> + fixedFixedPrice trade + + FixedFloatPrice -> + fixedFloatPrice trade + + BasisSwapPrice -> + basisSwapPrice trade + + CDSPrice -> + cdsPrice trade + ) + |> Maybe.withDefault Nothing + + +priceType : Trade -> Maybe PriceType +priceType trade = + if isFixedFixed trade then + Just FixedFixedPrice + + else if isFixedFixed trade then + Just FixedFloatPrice + + else if isIRSwapBasis trade then + Just BasisSwapPrice + + else if isCreditDefaultSwap trade then + Just CDSPrice + + else + Nothing + + + +-- FixedFixed + + +isFixedFixed : Trade -> Bool +isFixedFixed trade = + let + count = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.fixedRate) + |> Maybe.withDefault [] + |> List.length + in + count == 2 + + +{-| reporting rule FixedFixedPrice +filter when rule IsFixedFixed then +extract Trade -> tradableProduct -> priceNotation -> price -> fixedInterestRate then +maxBy FixedInterestRate -> rate then +extract FixedInterestRate -> rate as "Price" +-} +fixedFixedPrice : Trade -> Maybe Number +fixedFixedPrice trade = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.fixedInterestRate) + |> List.map .rate + |> List.maximum + + + +-- FixedFloat + + +isFixedFloat : Trade -> Bool +isFixedFloat trade = + let + fixedCount = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.fixedRate) + |> Maybe.withDefault [] + |> List.length + + floatingCount = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.floatingRate) + |> Maybe.withDefault [] + |> List.length + in + fixedCount == 1 && floatingCount == 1 + + +fixedFloatPrice : Trade -> Maybe Number +fixedFloatPrice trade = + -- TODO : Is this the correct interpretation of the above? + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.fixedInterestRate) + |> List.map .rate + |> List.maximum + + + +-- BasisSwap + + +isIRSwapBasis : Trade -> Bool +isIRSwapBasis trade = + let + count = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.interestRatePayout + |> Maybe.map (\x -> x.rateSpecification.floatingRate) + |> Maybe.withDefault [] + |> List.length + in + count == 2 + + +basisSwapPrice : Trade -> Maybe Number +basisSwapPrice trade = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.filterMap (\x -> x.price.floatingInterestRate) + |> List.filterMap .initialRate + |> List.head + + + +-- CDS + + +isCreditDefaultSwap : Trade -> Bool +isCreditDefaultSwap trade = + trade.tradableProduct.product.contractualProduct.economicTerms.payout.creditDefaultPayout |> exists + + +cdsPrice : Trade -> Maybe Number +cdsPrice trade = + let + mp = + trade.tradableProduct.priceNotation + |> Nonempty.toList + |> List.head + |> Maybe.map .price + + fixedRate = + mp + |> Maybe.map .fixedInterestRate + |> Maybe.withDefault Nothing + |> Maybe.map .rate + + floatingRate = + mp + |> Maybe.map .floatingInterestRate + |> Maybe.withDefault Nothing + |> Maybe.map .initialRate + in + case ( fixedRate, floatingRate ) of + ( Just rate, _ ) -> + if rate /= 0 then + Just rate + + else + Nothing + + ( _, Just (Just initialRate) ) -> + Just initialRate + + _ -> + Just 0 diff --git a/src/Morphir/Sample/Reg/Country.elm b/src/Morphir/Sample/Reg/Country.elm new file mode 100644 index 0000000..fae0bb5 --- /dev/null +++ b/src/Morphir/Sample/Reg/Country.elm @@ -0,0 +1,265 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.Country exposing (..) + + +type Country + = ABW + | AFG + | AGO + | AIA + | ALA + | ALB + | AND + | ARE + | ARG + | ARM + | ASM + | ATF + | ATG + | AUS + | AUT + | AZE + | BDI + | BEL + | BEN + | BES + | BFA + | BGD + | BGR + | BHR + | BHS + | BIH + | BLM + | BLR + | BLZ + | BMU + | BOL + | BRA + | BRB + | BRN + | BTN + | BVT + | BWA + | CAF + | CAN + | CCK + | CHE + | CHL + | CHN + | CIV + | CMR + | COD + | COG + | COK + | COL + | COM + | CPV + | CRI + | CUB + | CUW + | CXR + | CYM + | CYP + | CZE + | DEU + | DJI + | DMA + | DNK + | DOM + | DZA + | ECU + | EGY + | ERI + | ESH + | ESP + | EST + | ETH + | FIN + | FJI + | FRA + | FRO + | FSM + | GAB + | GBR + | GEO + | GGY + | GHA + | GIB + | GIN + | GLP + | GMB + | GNB + | GNQ + | GRC + | GRD + | GRL + | GTM + | GUF + | GUM + | GUY + | HKG + | HMD + | HND + | HRV + | HTI + | HUN + | IDN + | IMN + | IND + | IOT + | IRL + | IRN + | IRQ + | ISL + | ISR + | ITA + | JAM + | JEY + | JOR + | JPN + | KAZ + | KEN + | KGZ + | KHM + | KIR + | KNA + | KOR + | KWT + | LAO + | LBN + | LBR + | LBY + | LCA + | LIE + | LKA + | LSO + | LTU + | LUX + | LVA + | MAC + | MAF + | MAR + | MCO + | MDA + | MDG + | MDV + | MEX + | MHL + | MKD + | MLI + | MLT + | MMR + | MNE + | MNG + | MNP + | MOZ + | MRT + | MSR + | MTQ + | MUS + | MWI + | MYS + | MYT + | NAM + | NCL + | NER + | NFK + | NGA + | NIC + | NIU + | NLD + | NOR + | NPL + | NRU + | NZL + | OMN + | PAK + | PAN + | PCN + | PER + | PHL + | PLW + | PNG + | POL + | PRI + | PRK + | PRT + | PRY + | PYF + | QAT + | REU + | ROU + | RUS + | RWA + | SAU + | SDN + | SEN + | SGP + | SHN + | SJM + | SLB + | SLE + | SLV + | SMR + | SOM + | SPM + | SRB + | SSD + | STP + | SUR + | SVK + | SVN + | SWE + | SWZ + | SXM + | SYC + | SYR + | TCA + | TCD + | TGO + | THA + | TJK + | TKL + | TKM + | TLS + | TON + | TTO + | TUN + | TUR + | TUV + | TZA + | UGA + | UKR + | UMI + | URY + | USA + | UZB + | VAT + | VCT + | VEN + | VGB + | VIR + | VNM + | VUT + | WLF + | WSM + | YEM + | ZAF + | ZMB + | ZWE diff --git a/src/Morphir/Sample/Reg/Currency.elm b/src/Morphir/Sample/Reg/Currency.elm new file mode 100644 index 0000000..4b6e495 --- /dev/null +++ b/src/Morphir/Sample/Reg/Currency.elm @@ -0,0 +1,456 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.Currency exposing (..) + +import Morphir.Sample.Reg.Country as Country exposing (Country(..)) + + +type Currency + = AED + | AFN + | ALL + | AMD + | ANG + | AOA + | ARS + | AUD + | AWG + | AZN + | BAM + | BBD + | BDT + | BGN + | BHD + | BIF + | BMD + | BND + | BOB + | BRL + | BSD + | BWP + | BYN + | BZD + | CAD + | CDF + | CHF + | CLP + | CNY + | COP + | CRC + | CUP --,CUC + | CVE + | CZK + | DJF + | DKK + | DOP + | DZD + | EGP + | ERN + | ETB + | EUR + | FJD + | GBP + | GEL + | GHS + | GIP + | GMD + | GNF + | GTQ + | GYD + | HKD + | HNL + | HRK + | HTG --,USD + | HUF + | IDR + | ILS + | INR --,BTN + | IQD + | IRR + | ISK + | JMD + | JOD + | JPY + | KES + | KGS + | KHR + | KMF + | KPW + | KRW + | KWD + | KYD + | KZT + | LAK + | LBP + | LKR + | LRD + | LSL --,ZAR + | LYD + | MAD + | MDL + | MGA + | MKD + | MMK + | MNT + | MOP + | MRU + | MUR + | MVR + | MWK + | MXN + | MYR + | MZN + | NAD --,ZAR + | NGN + | NIO + | NOK + | NPR + | NZD + | OMR + | PAB --,USD + | PEN + | PGK + | PHP + | PKR + | PLN + | PYG + | QAR + | RON + | RSD + | RUB + | RWF + | SAR + | SBD + | SCR + | SDG + | SEK + | SGD + | SHP + | SLL + | SOS + | SRD + | SSP + | STN + | SVC --,USD + | SYP + | SZL + | THB + | TJS + | TMT + | TND + | TOP + | TRY + | TTD + | TZS + | UAH + | UGX + | USD + | UYU + | UZS + | VES + | VND + | VUV + | WST + | XAF + | XCD + | XOF + | XPF + | YER + | ZAR + | ZMW + | ZWL + + +currencyCountries : List ( Currency, Country ) +currencyCountries = + [ ( AED, ARE ) + , ( AFN, AFG ) + , ( ALL, ALB ) + , ( AMD, ARM ) + , ( ANG, CUW ) + , ( ANG, SXM ) + , ( AOA, AGO ) + , ( ARS, ARG ) + , ( AUD, AUS ) + , ( AUD, CCK ) + , ( AUD, CXR ) + , ( AUD, HMD ) + , ( AUD, KIR ) + , ( AUD, NFK ) + , ( AUD, NRU ) + , ( AUD, TUV ) + , ( AWG, ABW ) + , ( AZN, AZE ) + , ( BAM, BIH ) + , ( BBD, BRB ) + , ( BDT, BGD ) + , ( BGN, BGR ) + , ( BHD, BHR ) + , ( BIF, BDI ) + , ( BMD, BMU ) + , ( BND, BRN ) + , ( BOB, BOL ) + , ( BRL, BRA ) + , ( BSD, BHS ) + , ( BWP, BWA ) + , ( BYN, BLR ) + , ( BZD, BLZ ) + , ( CAD, CAN ) + , ( CDF, COD ) + , ( CHF, CHE ) + , ( CHF, LIE ) + , ( CLP, CHL ) + , ( CNY, CHN ) + , ( COP, COL ) + , ( CRC, CRI ) + , ( CUP, CUB ) + , ( CVE, CPV ) + , ( CZK, CZE ) + , ( DJF, DJI ) + , ( DKK, DNK ) + , ( DKK, FRO ) + , ( DKK, GRL ) + , ( DOP, DOM ) + , ( DZD, DZA ) + , ( EGP, EGY ) + , ( ERN, ERI ) + , ( ETB, ETH ) + , ( EUR, ALA ) + , ( EUR, AND ) + , ( EUR, ATF ) + , ( EUR, AUT ) + , ( EUR, BEL ) + , ( EUR, BLM ) + , ( EUR, CYP ) + , ( EUR, DEU ) + , ( EUR, ESP ) + , ( EUR, EST ) + , ( EUR, FIN ) + , ( EUR, FRA ) + , ( EUR, GLP ) + , ( EUR, GRC ) + , ( EUR, GUF ) + , ( EUR, IRL ) + , ( EUR, ITA ) + , ( EUR, LTU ) + , ( EUR, LUX ) + , ( EUR, LVA ) + , ( EUR, MAF ) + , ( EUR, MCO ) + , ( EUR, MLT ) + , ( EUR, MNE ) + , ( EUR, MTQ ) + , ( EUR, MYT ) + , ( EUR, NLD ) + , ( EUR, PRT ) + , ( EUR, REU ) + , ( EUR, SMR ) + , ( EUR, SPM ) + , ( EUR, SVK ) + , ( EUR, SVN ) + , ( EUR, VAT ) + , ( FJD, FJI ) + , ( GBP, GBR ) + , ( GBP, GGY ) + , ( GBP, IMN ) + , ( GBP, JEY ) + , ( GEL, GEO ) + , ( GHS, GHA ) + , ( GIP, GIB ) + , ( GMD, GMB ) + , ( GNF, GIN ) + , ( GTQ, GTM ) + , ( GYD, GUY ) + , ( HKD, HKG ) + , ( HNL, HND ) + , ( HRK, HRV ) + , ( HTG, HTI ) + , ( HUF, HUN ) + , ( IDR, IDN ) + , ( ILS, ISR ) + , ( INR, IND ) + , ( INR, BTN ) + , ( IQD, IRQ ) + , ( IRR, IRN ) + , ( ISK, ISL ) + , ( JMD, JAM ) + , ( JOD, JOR ) + , ( JPY, JPN ) + , ( KES, KEN ) + , ( KGS, KGZ ) + , ( KHR, KHM ) + , ( KMF, COM ) + , ( KPW, PRK ) + , ( KRW, KOR ) + , ( KWD, KWT ) + , ( KYD, CYM ) + , ( KZT, KAZ ) + , ( LAK, LAO ) + , ( LBP, LBN ) + , ( LKR, LKA ) + , ( LRD, LBR ) + , ( LSL, LSO ) + , ( LYD, LBY ) + , ( MAD, ESH ) + , ( MAD, MAR ) + , ( MDL, MDA ) + , ( MGA, MDG ) + , ( MKD, Country.MKD ) + , ( MMK, MMR ) + , ( MNT, MNG ) + , ( MOP, MAC ) + , ( MRU, MRT ) + , ( MUR, MUS ) + , ( MVR, MDV ) + , ( MWK, MWI ) + , ( MXN, MEX ) + , ( MYR, MYS ) + , ( MZN, MOZ ) + , ( NAD, NAM ) + , ( NGN, NGA ) + , ( NIO, NIC ) + , ( NOK, BVT ) + , ( NOK, NOR ) + , ( NOK, SJM ) + , ( NPR, NPL ) + , ( NZD, COK ) + , ( NZD, NIU ) + , ( NZD, NZL ) + , ( NZD, PCN ) + , ( NZD, TKL ) + , ( OMR, OMN ) + , ( PAB, PAN ) + , ( PEN, PER ) + , ( PGK, PNG ) + , ( PHP, PHL ) + , ( PKR, PAK ) + , ( PLN, POL ) + , ( PYG, PRY ) + , ( QAR, QAT ) + , ( RON, ROU ) + , ( RSD, SRB ) + , ( RUB, RUS ) + , ( RWF, RWA ) + , ( SAR, SAU ) + , ( SBD, SLB ) + , ( SCR, SYC ) + , ( SDG, SDN ) + , ( SEK, SWE ) + , ( SGD, SGP ) + , ( SHP, SHN ) + , ( SLL, SLE ) + , ( SOS, SOM ) + , ( SRD, SUR ) + , ( SSP, SSD ) + , ( STN, STP ) + , ( SVC, SLV ) + , ( SYP, SYR ) + , ( SZL, SWZ ) + , ( THB, THA ) + , ( TJS, TJK ) + , ( TMT, TKM ) + , ( TND, TUN ) + , ( TOP, TON ) + , ( TRY, TUR ) + , ( TTD, TTO ) + , ( TZS, TZA ) + , ( UAH, UKR ) + , ( UGX, UGA ) + , ( USD, ASM ) + , ( USD, BES ) + , ( USD, ECU ) + , ( USD, FSM ) + , ( USD, GUM ) + , ( USD, IOT ) + , ( USD, MHL ) + , ( USD, MNP ) + , ( USD, PLW ) + , ( USD, PRI ) + , ( USD, TCA ) + , ( USD, TLS ) + , ( USD, UMI ) + , ( USD, USA ) + , ( USD, VGB ) + , ( USD, VIR ) + , ( UYU, URY ) + , ( UZS, UZB ) + , ( VES, VEN ) + , ( VND, VNM ) + , ( VUV, VUT ) + , ( WST, WSM ) + , ( XAF, CAF ) + , ( XAF, CMR ) + , ( XAF, COG ) + , ( XAF, GAB ) + , ( XAF, GNQ ) + , ( XAF, TCD ) + , ( XCD, AIA ) + , ( XCD, ATG ) + , ( XCD, DMA ) + , ( XCD, GRD ) + , ( XCD, KNA ) + , ( XCD, LCA ) + , ( XCD, MSR ) + , ( XCD, VCT ) + , ( XOF, BEN ) + , ( XOF, BFA ) + , ( XOF, CIV ) + , ( XOF, GNB ) + , ( XOF, MLI ) + , ( XOF, NER ) + , ( XOF, SEN ) + , ( XOF, TGO ) + , ( XPF, NCL ) + , ( XPF, PYF ) + , ( XPF, WLF ) + , ( YER, YEM ) + , ( ZAR, ZAF ) + , ( ZMW, ZMB ) + , ( ZWL, ZWE ) + ] + + +country : Currency -> Maybe Country +country currency = + case currency of + AUD -> + Just AUS + + USD -> + Just USA + + _ -> + let + countries : List Country + countries = + currencyCountries + |> List.filterMap + (\( cy, co ) -> + if cy == currency then + Just co + + else + Nothing + ) + in + case countries of + [ countryCode ] -> + Just countryCode + + _ -> + Nothing diff --git a/src/Morphir/Sample/Reg/LCR/Basics.elm b/src/Morphir/Sample/Reg/LCR/Basics.elm new file mode 100644 index 0000000..0cdcaad --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Basics.elm @@ -0,0 +1,67 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Basics exposing + ( AssetCategoryCodes(..) + , Balance + , Entity + , InsuranceType(..) + , Ratio + ) + +{-| Asset categories apply to the flows and are specified in the spec. +There are a bunch of them, but we're only concerned with these three in this example . +-} + +import Morphir.Sample.Reg.Currency exposing (Currency(..)) + + +type AssetCategoryCodes + = Level1Assets + | Level2aAssets + | Level2bAssets + + +{-| Insurance type as specified in the spec. +There are a bunch of them, but we're only concerned with FDIC in this example . +-} +type InsuranceType + = FDIC + | Uninsured + + +type alias Entity = + String + + +type alias Balance = + Float + + +type alias Ratio = + Float + + +{-| A currency isn't always itself in 5G. +-} +fed5GCurrency : Currency -> Currency +fed5GCurrency currency = + if List.member currency [ USD, EUR, GBP, CHF, JPY, AUD, CAD ] then + currency + + else + USD diff --git a/src/Morphir/Sample/Reg/LCR/Calculations.elm b/src/Morphir/Sample/Reg/LCR/Calculations.elm new file mode 100644 index 0000000..c7f1daf --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Calculations.elm @@ -0,0 +1,299 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Calculations exposing (..) + +import Morphir.SDK.LocalDate exposing (LocalDate, addDays) +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (..) +import Morphir.Sample.Reg.LCR.Flows exposing (..) +import Morphir.Sample.Reg.LCR.Inflows as Inflows +import Morphir.Sample.Reg.LCR.Outflows as Outflows +import Morphir.Sample.Reg.LCR.Product exposing (..) +import Morphir.Sample.Reg.LCR.Rules as Rules + + + +-- Forumulas from the OCC: https://www.occ.gov/news-issuances/bulletins/2014/bulletin-2014-51.html +-- https://www.occ.gov/topics/supervision-and-examination/capital-markets/balance-sheet-management/liquidity/Basel-III-LCR-Formulas.pdf +-- https://www.govinfo.gov/content/pkg/FR-2014-10-10/pdf/2014-22520.pdf (page 61477) +-- {-| This module is broken up into the same structure as the example formulas in the referenced PDF. -} +-- {-| Here's the LCR as it's commonly known. -} + + +lcr : (Flow -> Counterparty) -> (ProductId -> Product) -> LocalDate -> (LocalDate -> List Flow) -> Balance -> Ratio +lcr toCounterparty product t flowsForDate reserveBalanceRequirement = + let + hqla : Balance + hqla = + hqlaAmount product (flowsForDate t) reserveBalanceRequirement + + totalNetCashOutflow : Balance + totalNetCashOutflow = + totalNetCashOutflowAmount toCounterparty t flowsForDate + in + hqla / totalNetCashOutflow + + +{-| HQLA Amount is the LCR numerator. It has several components, which are specified as nested functions. +-} +hqlaAmount : (ProductId -> Product) -> List Flow -> Balance -> Balance +hqlaAmount product t0Flows reserveBalanceRequirement = + let + level1LiquidAssetsThatAreEligibleHQLA : Balance + level1LiquidAssetsThatAreEligibleHQLA = + t0Flows + |> List.filter (\flow -> flow.assetType == Level1Assets && isHQLA product flow) + |> List.map .amount + |> List.sum + + level1LiquidAssetAmount : Balance + level1LiquidAssetAmount = + level1LiquidAssetsThatAreEligibleHQLA - reserveBalanceRequirement + + level2aLiquidAssetsThatAreEligibleHQLA : Balance + level2aLiquidAssetsThatAreEligibleHQLA = + t0Flows + |> List.filter (\flow -> flow.assetType == Level2aAssets && isHQLA product flow) + |> List.map .amount + |> List.sum + + level2aLiquidAssetAmount : Balance + level2aLiquidAssetAmount = + 0.85 * level2aLiquidAssetsThatAreEligibleHQLA + + level2bLiquidAssetsThatAreEligibleHQLA : Balance + level2bLiquidAssetsThatAreEligibleHQLA = + t0Flows + |> List.filter (\flow -> flow.assetType == Level2bAssets && isHQLA product flow) + |> List.map .amount + |> List.sum + + level2bLiquidAssetAmount : Balance + level2bLiquidAssetAmount = + 0.5 * level2bLiquidAssetsThatAreEligibleHQLA + + unadjustedExcessHQLAAmount : Balance + unadjustedExcessHQLAAmount = + let + level2CapExcessAmount : Balance + level2CapExcessAmount = + max (level2aLiquidAssetAmount + level2bLiquidAssetAmount - 0.6667 * level1LiquidAssetAmount) 0.0 + + level2bCapExcessAmount : Balance + level2bCapExcessAmount = + max (level2bLiquidAssetAmount - level2CapExcessAmount - 0.1765 * (level1LiquidAssetAmount + level2aLiquidAssetAmount)) 0.0 + in + level2CapExcessAmount + level2bCapExcessAmount + + adjustedExcessHQLAAmount : Balance + adjustedExcessHQLAAmount = + let + adjustedLevel1LiquidAssetAmount : Balance + adjustedLevel1LiquidAssetAmount = + level1LiquidAssetAmount + + adjustedlevel2aLiquidAssetAmount : Balance + adjustedlevel2aLiquidAssetAmount = + level2aLiquidAssetAmount * 0.85 + + adjustedlevel2bLiquidAssetAmount : Balance + adjustedlevel2bLiquidAssetAmount = + level2bLiquidAssetAmount * 0.5 + + adjustedLevel2CapExcessAmount : Balance + adjustedLevel2CapExcessAmount = + max (adjustedlevel2aLiquidAssetAmount + adjustedlevel2bLiquidAssetAmount - 0.6667 * adjustedLevel1LiquidAssetAmount) 0.0 + + adjustedlevel2bCapExcessAmount : Balance + adjustedlevel2bCapExcessAmount = + max (adjustedlevel2bLiquidAssetAmount - adjustedLevel2CapExcessAmount - 0.1765 * (adjustedLevel1LiquidAssetAmount + adjustedlevel2aLiquidAssetAmount)) 0.0 + in + adjustedLevel2CapExcessAmount + adjustedlevel2bCapExcessAmount + in + level1LiquidAssetAmount + level2aLiquidAssetAmount + level2bLiquidAssetAmount - max unadjustedExcessHQLAAmount adjustedExcessHQLAAmount + + +{-| Total Net Cash Outflow Amount is the LCR denominator. It has several components, which are specified as nested functions. +The function takes a function to lookup the counterparty for a flow. +the LocalDate (t) from which to calculate the remaining days until the flows maturity +and a function takes a function to lookup flows for a given date, +-} +totalNetCashOutflowAmount : (Flow -> Counterparty) -> LocalDate -> (LocalDate -> List Flow) -> Balance +totalNetCashOutflowAmount toCounterparty t flowsForDate = + let + -- List of the next 30 days from t + dates : List LocalDate + dates = + List.range 1 30 |> List.map (\i -> addDays i t) + + -- Aggregating helpers + spanDates : (Flow -> Bool) -> List Balance + spanDates filter = + dates + |> List.map flowsForDate + |> List.map (\flows -> flows |> aggregateDaily filter) + + aggregateSpan : (Flow -> Bool) -> Balance + aggregateSpan flowFilter = + spanDates flowFilter |> List.sum + + aggregateDaily : (Flow -> Bool) -> List Flow -> Balance + aggregateDaily flowFilter flows = + flows + |> List.filter flowFilter + |> List.map .amount + |> List.sum + + -- Non maturity + nonMaturityOutflowRules : LocalDate -> List (Rules.Rule Flow) + nonMaturityOutflowRules date = + Rules.findAll + [ "32(a)(1)" + , "32(a)(2)" + , "32(a)(3)" + , "32(a)(4)" + , "32(a)(5)" + , "32(b)" + , "32(c)" + , "32(d)" + , "32(e)" + , "32(f)" + , "32(i)" + ] + (Outflows.outflowRules toCounterparty date) + + nonMaturityInflowRules : LocalDate -> List (Rules.Rule Flow) + nonMaturityInflowRules date = + Rules.findAll + [ "33(b)", "33(g)" ] + (Inflows.inflowRules toCounterparty date) + + nonMaturityOutflowAmount : Balance + nonMaturityOutflowAmount = + aggregateSpan (Rules.isAnyApplicable (nonMaturityOutflowRules t)) + + nonMaturityInflowAmount : Balance + nonMaturityInflowAmount = + aggregateSpan (Rules.isAnyApplicable (nonMaturityInflowRules t)) + + -- Maturity + maturityMismatchOutflowRules : LocalDate -> List (Rules.Rule Flow) + maturityMismatchOutflowRules = + \date -> + Rules.findAll + [ "32(g)(1)" + , "32(g)(2)" + , "32(g)(3)" + , "32(g)(4)" + , "32(g)(5)" + , "32(g)(6)" + , "32(g)(7)" + , "32(g)(8)" + , "32(g)(9)" + , "32(h)(1)" + , "32(h)(2)" + , "32(h)(5)" + , "32(j)" + , "32(k)" + , "32(l)" + ] + (Outflows.outflowRules toCounterparty date) + + maturityOutflows : List Balance + maturityOutflows = + spanDates (Rules.isAnyApplicable (maturityMismatchOutflowRules t)) + + maturityOutflowAmount : Balance + maturityOutflowAmount = + maturityOutflows |> List.sum + + maturityMismatchInflowRules : LocalDate -> List (Rules.Rule Flow) + maturityMismatchInflowRules = + \date -> + Rules.findAll [ "33(c)", "33(d)", "33(e)", "33(f)" ] (Inflows.inflowRules toCounterparty date) + + maturityInflows : List Balance + maturityInflows = + spanDates (Rules.isAnyApplicable (maturityMismatchInflowRules t)) + + maturityInflowAmount : Balance + maturityInflowAmount = + maturityInflows |> List.sum + + -- Aggregate it all together + aggregatedOutflowAmount : Balance + aggregatedOutflowAmount = + nonMaturityOutflowAmount + maturityOutflowAmount + + aggregatedInflowAmount : Balance + aggregatedInflowAmount = + nonMaturityInflowAmount + maturityInflowAmount + + -- This add-on was added later + maturityMismatchAddOn : Balance + maturityMismatchAddOn = + let + netCumulativeMaturityOutflowAmount : Balance + netCumulativeMaturityOutflowAmount = + List.map2 Tuple.pair (accumulate 0 maturityOutflows) (accumulate 0 maturityInflows) + |> List.map (\( o, i ) -> o - i) + |> List.maximum + |> Maybe.withDefault 0 + + netDay30CumulativeMaturityOutflowAmount : Balance + netDay30CumulativeMaturityOutflowAmount = + List.sum maturityOutflows - List.sum maturityInflows + + maxNext30DaysOfCumulativeMaturityOutflowAmountFloor : Balance + maxNext30DaysOfCumulativeMaturityOutflowAmountFloor = + max 0.0 netCumulativeMaturityOutflowAmount + + netDay30CumulativeMaturityOutflowAmountFloor : Balance + netDay30CumulativeMaturityOutflowAmountFloor = + max 0.0 netDay30CumulativeMaturityOutflowAmount + in + maxNext30DaysOfCumulativeMaturityOutflowAmountFloor - netDay30CumulativeMaturityOutflowAmountFloor + + cappedInflows : Balance + cappedInflows = + min (0.75 * aggregatedOutflowAmount) aggregatedInflowAmount + in + aggregatedOutflowAmount - cappedInflows + maturityMismatchAddOn + + +isMember : Maybe a -> List a -> Bool +isMember ruleM rules = + ruleM + |> Maybe.map (\r -> List.member r rules) + |> Maybe.withDefault False + + +isHQLA : (ProductId -> Product) -> Flow -> Bool +isHQLA product flow = + product flow.productId |> .isHQLA + + +{-| Helper function to accumulated steps of a sum across a list. This is used in calculating the maturity mismatch add-on. +-} +accumulate : number -> List number -> List number +accumulate starter list = + let + ( sum, acc ) = + List.foldl (\y ( x, xs ) -> ( x + y, (x + y) :: xs )) ( starter, [] ) list + in + List.reverse acc diff --git a/src/Morphir/Sample/Reg/LCR/CentralBank.elm b/src/Morphir/Sample/Reg/LCR/CentralBank.elm new file mode 100644 index 0000000..be3fe4e --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/CentralBank.elm @@ -0,0 +1,37 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.CentralBank exposing (CentralBank(..)) + + +type CentralBank + = Federal_Reserve_Bank + | Bank_of_Canada + | Bank_of_England + | Bank_of_Japan + | European_Central_Bank + | Reserve_Bank_of_Australia + | Swiss_National_Bank + | Banco_Central_Do_Brasil + | Banca_De_France + | Bance_Di_Italia + | Bank_of_Korea + | Deutsche_Bundesbank + | Banque_Centrale_Du_Luxembourg + | Peoples_Bank_of_China + | Reserve_Bank_of_India + | Central_Bank_of_The_Russian_Federation diff --git a/src/Morphir/Sample/Reg/LCR/Counterparty.elm b/src/Morphir/Sample/Reg/LCR/Counterparty.elm new file mode 100644 index 0000000..ffe74c9 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Counterparty.elm @@ -0,0 +1,45 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Counterparty exposing (..) + + +type alias CounterpartyId = + String + + +type CounterpartyType + = Bank + | Retail + | SmallBusiness + | NonFinancialCorporate + | Sovereign + | CentralBank + | GovernmentSponsoredEntity + | PublicSectorEntity + | MultilateralDevelopmentBank + | OtherSupranational + | SupervisedNonBankFinancialEntity + | DebtIssuingSpecialPurposeEntity + | OtherFinancialEntity + | Other + + +type alias Counterparty = + { counterpartyId : CounterpartyId + , counterpartyType : CounterpartyType + } diff --git a/src/Morphir/Sample/Reg/LCR/FedCode.elm b/src/Morphir/Sample/Reg/LCR/FedCode.elm new file mode 100644 index 0000000..aa0c1aa --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/FedCode.elm @@ -0,0 +1,301 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2_0 (the "License"); + you may not use this file except in compliance with the License_ + You may obtain a copy of the License at + + http://www_apache_org/licenses/LICENSE-2_0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied_ + See the License for the specific language governing permissions and + limitations under the License_ +-} + + +module Morphir.Sample.Reg.LCR.FedCode exposing (..) + +import Dict exposing (Dict) +import Morphir.Sample.Reg.Country as Country exposing (Country) +import Morphir.Sample.Reg.Currency as Currency exposing (Currency(..), country) +import Morphir.Sample.Reg.LCR.CentralBank exposing (CentralBank(..)) + + +type alias PartyID = + String + + +type alias CountryCode = + String + + +type alias ProductType = + String + + +type alias Description5G = + String + + +type alias AdjustedAmountUSD = + String + + +type alias AdjustedMXAmount = + String + + +type alias Account = + String + + +type alias CostCenterCode = + String + + +type alias CostCenterDescription = + String + + +type alias LegalEntityCode = + String + + +type alias TenQLevel1 = + String + + +type alias TenQLevel2 = + String + + +type alias TenQLevel3 = + String + + +type alias TenQLevel4 = + String + + +type alias TenQLevel5 = + String + + +type alias TenQLevel6 = + String + + +type Money a + = Money Float + + +type Usd + = Usd + + +type Mx + = Mx + + +type alias LegalEntity = + { code : LegalEntityCode + , country : Maybe Country + } + + +type alias CostCenter = + { code : CostCenterCode + , description : CostCenterDescription + } + + +type alias Counterparty = + { country : Country + , description5G : Description5G + , account : Account + } + + +type alias Cashflow = + { legalEntity : LegalEntity + , partyId : PartyID + , currency : Currency + , counterparty : Counterparty + , amountUSD : Money Usd + , amountMx : Money Mx + , tenQLevel1 : TenQLevel1 + , tenQLevel2 : TenQLevel2 + , tenQLevel3 : TenQLevel3 + , tenQLevel4 : TenQLevel4 + , tenQLevel5 : TenQLevel5 + , tenQLevel6 : TenQLevel6 + } + + +type FedCode + = IA31 + | IA32 + | IA33 + | IA34 + | IA35 + | IA36 + | IA37 + | IA38 + | IA39 + | IA41 + | IA42 + | IA43 + | IA44 + | IA45 + | IA46 + | IA47 + | IA48 + | IA49 + | IU1 + | IU2 + | IU4 + | OW9 + | OW10 + | Unclassified + + +type CentralBankSubProduct + = FRB + | SNB + | BOE + | ECB + | BOJ + | RBA + | BOC + | OCB + | Other_Cash_Currency_And_Coin + + +centralBankToSubProduct : CentralBank -> CentralBankSubProduct +centralBankToSubProduct cb = + case cb of + Federal_Reserve_Bank -> + FRB + + Swiss_National_Bank -> + SNB + + Bank_of_England -> + BOE + + European_Central_Bank -> + ECB + + Bank_of_Japan -> + BOJ + + Reserve_Bank_of_Australia -> + RBA + + Bank_of_Canada -> + BOC + + -- TODO What maps to Other Cash Currency and Coin???? + _ -> + OCB + + +classify : Cashflow -> Dict PartyID CentralBank -> FedCode +classify cashflow centralBanks = + let + partyAsCentralBank : Maybe CentralBank + partyAsCentralBank = + Dict.get cashflow.partyId centralBanks + in + case partyAsCentralBank of + -- It is a central bank + Just centralBank -> + if cashflow.tenQLevel6 /= "Segregated Cash" then + case centralBankToSubProduct centralBank of + FRB -> + IA31 + + SNB -> + IA32 + + BOE -> + IA33 + + ECB -> + IA34 + + BOJ -> + IA35 + + RBA -> + IA36 + + BOC -> + IA37 + + OCB -> + IA38 + + Other_Cash_Currency_And_Coin -> + IA39 + + else + case centralBankToSubProduct centralBank of + FRB -> + IA41 + + SNB -> + IA42 + + BOE -> + IA43 + + ECB -> + IA44 + + BOJ -> + IA45 + + RBA -> + IA46 + + BOC -> + IA47 + + OCB -> + IA48 + + Other_Cash_Currency_And_Coin -> + IA49 + + -- It is not a central bank + Nothing -> + --if List.member (String.toUpper cashflow.tenQLevel5) [ "CASH AND DUE FROM BANKS", "OVERNIGHT AND TERM DEPOSITS", "CASH EQUIVALENTS" ] then + if String.toUpper cashflow.tenQLevel5 == "CASH AND DUE FROM BANKS" || String.toUpper cashflow.tenQLevel5 == "OVERNIGHT AND TERM DEPOSITS" || String.toUpper cashflow.tenQLevel5 == "CASH EQUIVALENTS" then + if netCashUSD cashflow >= 0 then + if isOnshore cashflow then + IU1 + + else + IU2 + + else + IU4 + + else + -- Probably replace with maybe + Unclassified + + +isOnshore : Cashflow -> Bool +isOnshore cashflow = + cashflow.legalEntity.country == Currency.country cashflow.currency && cashflow.legalEntity.country == Just cashflow.counterparty.country + + +netCashUSD : Cashflow -> Float +netCashUSD cashflow = + case cashflow.amountUSD of + Money amount -> + amount diff --git a/src/Morphir/Sample/Reg/LCR/FedCodeRules.elm b/src/Morphir/Sample/Reg/LCR/FedCodeRules.elm new file mode 100644 index 0000000..a0b871e --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/FedCodeRules.elm @@ -0,0 +1,343 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2_0 (the "License"); + you may not use this file except in compliance with the License_ + You may obtain a copy of the License at + + http://www_apache_org/licenses/LICENSE-2_0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied_ + See the License for the specific language governing permissions and + limitations under the License_ +-} + + +module Morphir.Sample.Reg.LCR.FedCodeRules exposing (..) + +import Dict exposing (Dict) +import Morphir.Sample.Reg.Country as Country exposing (Country) +import Morphir.Sample.Reg.Currency as Currency exposing (Currency(..), country) +import Morphir.Sample.Reg.LCR.CentralBank exposing (CentralBank(..)) + + +type alias PartyID = + String + + +type alias CountryCode = + String + + +type alias ProductType = + String + + +type alias Description5G = + String + + +type alias AdjustedAmountUSD = + Float + + +type alias AdjustedMXAmount = + Float + + +type alias Account = + String + + +type alias CostCenterCode = + String + + +type alias CostCenterDescription = + String + + +type alias LegalEntityCode = + String + + +type alias TenQLevel1 = + String + + +type alias TenQLevel2 = + String + + +type alias TenQLevel3 = + String + + +type alias TenQLevel4 = + String + + +type alias TenQLevel5 = + String + + +type alias TenQLevel6 = + String + + +type Money a + = Money Float + + +type Usd + = Usd + + +type Mx + = Mx + + +type alias LegalEntity = + { code : String -- LegalEntityCode + , country : Country + } + + +type alias CostCenter = + { code : String -- CostCenterCode + , description : String -- CostCenterDescription + } + + +type alias Counterparty = + { country : Country + , description5G : String -- Description5G + , account : String -- Account + } + + +type alias Cashflow = + { legalEntity : LegalEntity + , partyId : String -- PartyID + , currency : Currency + , counterparty : Counterparty + , amountUSD : Float -- AdjustedAmountUSD + , amountMx : Float -- AdjustedMXAmount + , tenQLevel1 : String -- TenQLevel1 + , tenQLevel2 : String -- TenQLevel2 + , tenQLevel3 : String -- TenQLevel3 + , tenQLevel4 : String -- TenQLevel4 + , tenQLevel5 : String -- TenQLevel5 + , tenQLevel6 : String -- TenQLevel6 + } + + +type alias RuleCode = + List String + + +toString : RuleCode -> String +toString rulecode = + rulecode |> String.join "." + + +type CentralBankSubProduct + = FRB + | SNB + | BOE + | ECB + | BOJ + | RBA + | BOC + | OCB + | Other_Cash_Currency_And_Coin + + +calculate : Dict PartyID CentralBank -> List Cashflow -> List ( Cashflow, RuleCode ) +calculate centralBanks cashflows = + let + firstPass : List ( Cashflow, RuleCode ) + firstPass = + cashflows + |> List.map (\cashflow -> ( cashflow, classify centralBanks cashflow )) + in + firstPass + + +classify : Dict PartyID CentralBank -> Cashflow -> RuleCode +classify centralBanks cashflow = + let + partyIsCentralBank : Maybe CentralBank + partyIsCentralBank = + Dict.get cashflow.partyId centralBanks + in + -- It is a central bank + case partyIsCentralBank of + Just centralBank -> + rules_I_A cashflow.tenQLevel4 centralBank + + notCentralBank -> + if String.toUpper cashflow.tenQLevel5 == "CASH AND DUE FROM BANKS" || String.toUpper cashflow.tenQLevel5 == "OVERNIGHT AND TERM DEPOSITS" || String.toUpper cashflow.tenQLevel5 == "CASH EQUIVALENTS" then + rule_I_U cashflow.amountUSD cashflow.legalEntity.country cashflow.currency cashflow.counterparty.country + + else + [] + + + +--rules_I_A : TenQLevel4 -> CentralBank -> Maybe RuleCode + + +rules_I_A : String -> CentralBank -> RuleCode +rules_I_A tenQLevel4 centralBank = + List.append [ "I", "A" ] + (if tenQLevel4 == segregatedCash then + rule_I_A_4 centralBank + + else + rule_I_A_3 centralBank + ) + + +rule_I_A_3 : CentralBank -> RuleCode +rule_I_A_3 centralBank = + List.append [ "3" ] + (case centralBankToSubProduct centralBank of + FRB -> + [ "1" ] + + SNB -> + [ "2" ] + + BOE -> + [ "3" ] + + ECB -> + [ "4" ] + + BOJ -> + [ "5" ] + + RBA -> + [ "6" ] + + BOC -> + [ "7" ] + + OCB -> + [ "8" ] + + Other_Cash_Currency_And_Coin -> + [ "9" ] + ) + + +rule_I_A_4 : CentralBank -> RuleCode +rule_I_A_4 centralBank = + List.append [ "4" ] + (case centralBankToSubProduct centralBank of + FRB -> + [ "1" ] + + SNB -> + [ "2" ] + + BOE -> + [ "3" ] + + ECB -> + [ "4" ] + + BOJ -> + [ "5" ] + + RBA -> + [ "6" ] + + BOC -> + [ "7" ] + + OCB -> + [ "8" ] + + Other_Cash_Currency_And_Coin -> + [ "9" ] + ) + + + +--rule_I_U : AdjustedAmountUSD -> Country -> Currency -> Country -> RuleCode + + +rule_I_U : Float -> Country -> Currency -> Country -> RuleCode +rule_I_U adjustedAmountUSD legalEntityCountry cashflowCurrency counterpartyCountry = + let + tail : String + tail = + if netCashUSD adjustedAmountUSD >= 0 then + if isOnshore legalEntityCountry cashflowCurrency counterpartyCountry then + "1" + + else + "2" + + else + "4" + in + List.append [ "I", "U" ] [ tail ] + + +centralBankToSubProduct : CentralBank -> CentralBankSubProduct +centralBankToSubProduct cb = + case cb of + Federal_Reserve_Bank -> + FRB + + Swiss_National_Bank -> + SNB + + Bank_of_England -> + BOE + + European_Central_Bank -> + ECB + + Bank_of_Japan -> + BOJ + + Reserve_Bank_of_Australia -> + RBA + + Bank_of_Canada -> + BOC + + -- TODO What maps to Other Cash Currency and Coin???? + _ -> + OCB + + +segregatedCash : String +segregatedCash = + "Seg Cash" + + +isCentralBank : Maybe CentralBank -> Bool +isCentralBank m = + m |> Maybe.map (\x -> True) |> Maybe.withDefault False + + +isOnshore : Country -> Currency -> Country -> Bool +isOnshore legalEntityCountry cashflowCurrency counterpartyCountry = + Just legalEntityCountry == Currency.country cashflowCurrency && legalEntityCountry == counterpartyCountry + + + +-- Group across same Entity, Party ID, Currency, and Product Type + + +netCashUSD : AdjustedAmountUSD -> Float +netCashUSD adjustedAmountUSD = + -- TODO the calculation + adjustedAmountUSD diff --git a/src/Morphir/Sample/Reg/LCR/Flows.elm b/src/Morphir/Sample/Reg/LCR/Flows.elm new file mode 100644 index 0000000..a1ac502 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Flows.elm @@ -0,0 +1,54 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Flows exposing (Amount, BusinessDate, Flow, ReportingEntity) + +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.Sample.Reg.Currency exposing (Currency) +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (CounterpartyId) +import Morphir.Sample.Reg.LCR.FedCodeRules exposing (RuleCode) +import Morphir.Sample.Reg.LCR.Product exposing (ProductId) + + +type alias BusinessDate = + LocalDate + + +type alias ReportingEntity = + Entity + + +type alias Amount = + Float + + +type alias Flow = + { amount : Amount + , assetType : AssetCategoryCodes + , businessDate : BusinessDate + , collateralClass : AssetCategoryCodes + , counterpartyId : CounterpartyId + , currency : Currency + , ruleCode : RuleCode + , insured : InsuranceType + , isTreasuryControl : Bool + , isUnencumbered : Bool + , maturityDate : BusinessDate + , effectiveMaturityDate : BusinessDate + , productId : ProductId + } diff --git a/src/Morphir/Sample/Reg/LCR/Inflows.elm b/src/Morphir/Sample/Reg/LCR/Inflows.elm new file mode 100644 index 0000000..5146767 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Inflows.elm @@ -0,0 +1,199 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Inflows exposing (..) + +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (..) +import Morphir.Sample.Reg.LCR.FedCodeRules exposing (RuleCode) +import Morphir.Sample.Reg.LCR.Flows exposing (..) +import Morphir.Sample.Reg.LCR.MaturityBucket exposing (..) +import Morphir.Sample.Reg.LCR.Rules exposing (..) + + +{-| The list of all rules pertaining to inflows. +-} +inflowRules : (Flow -> Counterparty) -> LocalDate -> List (Rule Flow) +inflowRules toCounterparty t = + [ Rule "20(a)(1)" 1.0 (isRule20a1 t) + , Rule "20(a)(3)-(6)" 1.0 isRule20a3dash6 + , Rule "22(b)(3)L1" -1.0 isRule22b3L2a + , Rule "22(b)(3)L2a" -0.85 isRule22b3L2a + , Rule "22(b)(3)L2b" -0.5 isRule22b3L2b + , Rule "20(b)" 0.85 isRule20b + , Rule "20(c)" 0.5 isRule20c + , Rule "33(b)" 1.0 isRule33b + , Rule "33(c)" 0.5 (isRule33c toCounterparty t) + , Rule "33(d)(1)" 1.0 (isRule33d1 toCounterparty) + , Rule "33(d)(2)" 1.0 (isRule33d2 toCounterparty) + , Rule "33(e)" 1.0 isRule33e + , Rule "33(g)" 1.0 isRule33g + , Rule "33(h)" 0.0 isRule33h + ] + + + +-- Rule logic is below for (eventual) unit testability + + +isRule20a1 : LocalDate -> Flow -> Bool +isRule20a1 t flow = + List.member flow.ruleCode [ [ "I", "A", "3", "1" ], [ "I", "A", "3", "2" ], [ "I", "A", "3", "3" ], [ "I", "A", "3", "4" ], [ "I", "A", "3", "5" ], [ "I", "A", "3", "6" ], [ "I", "A", "3", "7" ], [ "I", "A", "3", "8" ] ] + && daysToMaturity t flow.maturityDate + == 0 + + +isRule20a3dash6 : Flow -> Bool +isRule20a3dash6 flow = + (List.member flow.ruleCode [ [ "I", "A", "1" ], [ "I", "A", "2" ] ] + && flow.collateralClass + == Level1Assets + && flow.isTreasuryControl + ) + || (List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "4" ] ] + && flow.collateralClass + == Level1Assets + && flow.isTreasuryControl + && flow.isUnencumbered + ) + + +isRule22b3L2a : Flow -> Bool +isRule22b3L2a flow = + flow.ruleCode == [ "S", "I", "19" ] && flow.collateralClass == Level2aAssets + + +isRule22b3L2b : Flow -> Bool +isRule22b3L2b flow = + flow.ruleCode == [ "S", "I", "19" ] && flow.collateralClass == Level2bAssets + + +isRule20b : Flow -> Bool +isRule20b flow = + (List.member flow.ruleCode [ [ "I", "A", "1" ], [ "I", "A", "2" ] ] + && flow.collateralClass + == Level2aAssets + && flow.isTreasuryControl + ) + || (List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "4" ] ] + && flow.collateralClass + == Level2aAssets + && flow.isTreasuryControl + && flow.isUnencumbered + ) + + +isRule20c : Flow -> Bool +isRule20c flow = + (List.member flow.ruleCode [ [ "I", "A", "1" ], [ "I", "A", "2" ] ] + && flow.collateralClass + == Level2bAssets + && flow.isTreasuryControl + ) + || (List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "4" ] ] + && flow.collateralClass + == Level2bAssets + && flow.isTreasuryControl + && flow.isUnencumbered + ) + + +isRule33b : { a | ruleCode : RuleCode } -> Bool +isRule33b cashflow = + cashflow.ruleCode == [ "I", "O", "7" ] + + +isRule33c : (Flow -> Counterparty) -> LocalDate -> Flow -> Bool +isRule33c toCounterparty t flow = + let + cpty : Counterparty + cpty = + toCounterparty flow + + days : Int + days = + daysToMaturity t flow.maturityDate + in + (List.member flow.ruleCode [ [ "I", "U", "5" ], [ "I", "U", "6" ] ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + && (0 < days && days <= 30) + ) + || (List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "4" ], [ "I", "S", "5" ], [ "I", "S", "6" ], [ "I", "S", "7" ] ] + && cpty.counterpartyType + == Retail + && (0 < days && days <= 30) + ) + + +isRule33d1 : (Flow -> Counterparty) -> Flow -> Bool +isRule33d1 toCounterparty flow = + let + cpty : Counterparty + cpty = + toCounterparty flow + in + List.member flow.ruleCode [ [ "I", "U", "1" ], [ "I", "U", "2" ], [ "I", "U", "4" ] ] + || (List.member flow.ruleCode [ [ "I", "U", "5" ], [ "I", "U", "6" ] ] + && List.member cpty.counterpartyType + [ CentralBank + , Bank + , SupervisedNonBankFinancialEntity + , DebtIssuingSpecialPurposeEntity + , OtherFinancialEntity + ] + ) + + +isRule33d2 : (Flow -> Counterparty) -> Flow -> Bool +isRule33d2 toCounterparty flow = + let + cpty : Counterparty + cpty = + toCounterparty flow + in + List.member flow.ruleCode [ [ "I", "U", "5" ], [ "I", "U", "6" ] ] + && List.member cpty.counterpartyType + [ NonFinancialCorporate + , Sovereign + , GovernmentSponsoredEntity + , PublicSectorEntity + , MultilateralDevelopmentBank + , OtherSupranational + , Other + ] + + +isRule33e : Flow -> Bool +isRule33e cashflow = + cashflow.ruleCode == [ "I", "O", "6" ] || cashflow.ruleCode == [ "I", "O", "8" ] + + + +-- isRule33f : a -> b +-- isRule33f flow = +-- Debug.todo "Rule 33(f) is actually a bunch of rules. Too many to do for now..." + + +isRule33g : { a | ruleCode : RuleCode, isTreasuryControl : Bool } -> Bool +isRule33g cashflow = + cashflow.ruleCode == [ "I", "O", "5" ] && cashflow.isTreasuryControl + + +isRule33h : { a | ruleCode : RuleCode, isTreasuryControl : Bool } -> Bool +isRule33h cashflow = + cashflow.ruleCode == [ "I", "O", "9" ] && cashflow.isTreasuryControl diff --git a/src/Morphir/Sample/Reg/LCR/MaturityBucket.elm b/src/Morphir/Sample/Reg/LCR/MaturityBucket.elm new file mode 100644 index 0000000..28f214c --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/MaturityBucket.elm @@ -0,0 +1,100 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.MaturityBucket exposing (..) + +-- See: https://www.federalreserve.gov/reportforms/forms/FR_2052a20190331_f.pdf +-- Appendix IV-a, Maturity Time Bucket Value List on page 75 + +import Morphir.SDK.LocalDate exposing (LocalDate, diffInDays, diffInYears) + + +type MaturityBucket + = Daily Int + | DayRange Int Int + | DayYear Int Int + | Yearly Int Int + | Residual + + +daysToMaturity : LocalDate -> LocalDate -> Int +daysToMaturity fromDate maturityDate = + diffInDays maturityDate fromDate + + +yearsToMaturity : LocalDate -> LocalDate -> Int +yearsToMaturity fromDate maturityDate = + diffInYears maturityDate fromDate + + +{-| The Fed spec on maturity buckets +-} +bucket : LocalDate -> LocalDate -> MaturityBucket +bucket fromDate maturityDate = + let + days : Int + days = + daysToMaturity fromDate maturityDate + + years : Int + years = + yearsToMaturity maturityDate fromDate + in + if days <= 60 then + Daily days + + else if days <= 67 then + DayRange 61 67 + + else if days <= 74 then + DayRange 68 74 + + else if days <= 82 then + DayRange 75 82 + + else if days <= 90 then + DayRange 83 90 + + else if days <= 120 then + DayRange 92 120 + + else if days <= 150 then + DayRange 121 150 + + else if days <= 180 then + DayRange 151 180 + + else if days <= 270 then + DayYear 181 270 + + else if years <= 1 then + DayYear 271 1 + + else if years <= 2 then + Yearly 1 2 + + else if years <= 3 then + Yearly 2 3 + + else if years <= 4 then + Yearly 3 4 + + else if years <= 5 then + Yearly 4 5 + + else + Residual diff --git a/src/Morphir/Sample/Reg/LCR/Outflows.elm b/src/Morphir/Sample/Reg/LCR/Outflows.elm new file mode 100644 index 0000000..c44c518 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Outflows.elm @@ -0,0 +1,392 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Outflows exposing (..) + +-- import Dict exposing (Dict) + +import Morphir.SDK.LocalDate exposing (LocalDate) +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (..) +import Morphir.Sample.Reg.LCR.Flows exposing (..) +import Morphir.Sample.Reg.LCR.MaturityBucket exposing (..) +import Morphir.Sample.Reg.LCR.Rules exposing (..) + + +{-| The list of all rules pertaining to outlfows. +-} +outflowRules : (Flow -> Counterparty) -> LocalDate -> List (Rule Flow) +outflowRules counterparty t = + [ Rule "32(a)(1)" 0.03 isRule32a1 + , Rule "32(a)(2)" 0.1 (isRule32a2 counterparty) + , Rule "32(a)(3)" 0.2 (isRule32a3 counterparty) + , Rule "32(a)(4)" 0.4 (isRule32a4 counterparty) + , Rule "32(a)(5)" 0.4 (isRule32a5 counterparty) + , Rule "32(b)" 1.0 isRule32b + , Rule "32(c)" 0.2 isRule32c + , Rule "32(d)" 0.1 isRule32d + , Rule "32(e)" 0.0 isRule32e + , Rule "32(f)" 0.0 isRule32f + , Rule "32(g)(1)" 0.0 (isRule32g1 counterparty t) + , Rule "32(g)(2)" 0.0 (isRule32g2 counterparty t) + , Rule "32(g)(3)" 0.0 (isRule32g3 counterparty t) + , Rule "32(g)(4)" 0.0 (isRule32g4 counterparty t) + , Rule "32(g)(5)" 0.0 (isRule32g5 counterparty) + , Rule "32(g)(6)" 0.0 (isRule32g6 counterparty) + , Rule "32(g)(7)" 0.0 (isRule32g7 counterparty) + , Rule "32(g)(8)" 0.0 (isRule32g8 counterparty) + , Rule "32(g)(9)" 0.0 (isRule32g9 counterparty) + , Rule "32(h)(3)" 0.05 (isRule32h3 counterparty) + , Rule "32(h)(4)" 0.25 (isRule32h4 counterparty) + , Rule "32(l)" 0.0 isRule32l + , Rule "33(f)(1)(iii)" 0.0 (isRule33f1iii t) + , Rule "33(f)(1)(iv)" 0.15 (isRule33f1iv t) + ] + + + +-- Rules broken out for (eventual) unit testing + + +isRule32a1 : Flow -> Bool +isRule32a1 flow = + List.member flow.ruleCode [ [ "O", "D", "1" ], [ "O", "D", "2" ] ] + && flow.insured + == FDIC + + +isRule32a2 : (Flow -> Counterparty) -> Flow -> Bool +isRule32a2 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + (List.member flow.ruleCode [ [ "O", "D", "1" ], [ "O", "D", "2" ] ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + && flow.insured + /= FDIC + ) + || (flow.ruleCode + == [ "O", "D", "3" ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + ) + + +isRule32a3 : (Flow -> Counterparty) -> Flow -> Bool +isRule32a3 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "12" ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + && flow.insured + == FDIC + + +isRule32a4 : (Flow -> Counterparty) -> Flow -> Bool +isRule32a4 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "12" ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + && flow.insured + /= FDIC + + +isRule32a5 : (Flow -> Counterparty) -> Flow -> Bool +isRule32a5 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + List.member flow.ruleCode [ [ "O", "D", "13" ], [ "O", "W", "18" ] ] + && List.member cpty.counterpartyType [ Retail, SmallBusiness ] + + +isRule32b : Flow -> Bool +isRule32b flow = + List.member flow.ruleCode [ [ "O", "W", "1" ], [ "O", "W", "2" ], [ "O", "W", "4" ], [ "O", "O", "21" ] ] + + +isRule32c : Flow -> Bool +isRule32c flow = + flow.ruleCode == [ "O", "O", "20" ] + + +isRule32d : Flow -> Bool +isRule32d flow = + flow.ruleCode == [ "O", "O", "6" ] + + +isRule32e : Flow -> Bool +isRule32e flow = + flow.ruleCode == [ "O", "O", "6" ] + + +isRule32f : Flow -> Bool +isRule32f flow = + flow.ruleCode == [ "O", "O", "6" ] + + +isRule32g1 : (Flow -> Counterparty) -> LocalDate -> Flow -> Bool +isRule32g1 counterparty t flow = + let + cpty : Counterparty + cpty = + counterparty flow + + remainingDays : Int + remainingDays = + daysToMaturity t flow.maturityDate + in + flow.ruleCode + == [ "O", "D", "7" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && (0 < remainingDays && remainingDays <= 30) + + +isRule32g2 : (Flow -> Counterparty) -> LocalDate -> Flow -> Bool +isRule32g2 counterparty t flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "7" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && daysToMaturity t flow.maturityDate + <= 30 + + +isRule32g3 : (Flow -> Counterparty) -> LocalDate -> Flow -> Bool +isRule32g3 counterparty t flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "7" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && daysToMaturity t flow.maturityDate + == 0 + && flow.insured + == FDIC + + +isRule32g4 : (Flow -> Counterparty) -> LocalDate -> Flow -> Bool +isRule32g4 counterparty t flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "7" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && daysToMaturity t flow.maturityDate + == 0 + && flow.insured + /= FDIC + + +isRule32g5 : (Flow -> Counterparty) -> Flow -> Bool +isRule32g5 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "11" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && flow.insured + == FDIC + + +isRule32g6 : (Flow -> Counterparty) -> Flow -> Bool +isRule32g6 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "11" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && flow.insured + /= FDIC + + +isRule32g7 : (Flow -> Counterparty) -> Flow -> Bool +isRule32g7 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "8" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && flow.insured + == FDIC + + +isRule32g8 : (Flow -> Counterparty) -> Flow -> Bool +isRule32g8 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "9" ] + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && flow.insured + == FDIC + + +isRule32g9 : (Flow -> Counterparty) -> Flow -> Bool +isRule32g9 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + (flow.ruleCode == [ "O", "D", "8" ] || flow.ruleCode == [ "O", "D", "9" ]) + && (cpty.counterpartyType == Retail || cpty.counterpartyType == SmallBusiness) + && flow.insured + /= FDIC + + + +-- isRule32h1 : Flow -> Bool +-- isRule32h1 flow = +-- Debug.todo "Too many 32(h) rules to do..." +-- isRule32h2 : Flow -> Bool +-- isRule32h2 flow = +-- Debug.todo "Too many 32(h) rules to do..." + + +isRule32h3 : (Flow -> Counterparty) -> Flow -> Bool +isRule32h3 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "4" ] + && List.member cpty.counterpartyType + [ NonFinancialCorporate + , Sovereign + , CentralBank + , GovernmentSponsoredEntity + , PublicSectorEntity + , MultilateralDevelopmentBank + , OtherSupranational + , Bank + , SupervisedNonBankFinancialEntity + , DebtIssuingSpecialPurposeEntity + , OtherFinancialEntity + , Other + ] + && flow.insured + == FDIC + + +isRule32h4 : (Flow -> Counterparty) -> Flow -> Bool +isRule32h4 counterparty flow = + let + cpty : Counterparty + cpty = + counterparty flow + in + flow.ruleCode + == [ "O", "D", "4" ] + && List.member cpty.counterpartyType + [ NonFinancialCorporate + , Sovereign + , CentralBank + , GovernmentSponsoredEntity + , PublicSectorEntity + , MultilateralDevelopmentBank + , OtherSupranational + , Bank + , SupervisedNonBankFinancialEntity + , DebtIssuingSpecialPurposeEntity + , OtherFinancialEntity + , Other + ] + && flow.insured + /= FDIC + + + +-- isRule32h5 : Flow -> Bool +-- isRule32h5 flow = +-- Debug.todo "Too many 32(h) rules to do..." +-- isRule32i : Flow -> Bool +-- isRule32i flow = +-- Debug.todo "Too many 32(i) rules to do..." +-- isRule32j : Flow -> Bool +-- isRule32j flow = +-- Debug.todo "Too many 32(j) rules to do..." +-- isRule32k : Flow -> Bool +-- isRule32k flow = +-- Debug.todo "Too many 32(k) rules to do..." + + +isRule32l : Flow -> Bool +isRule32l flow = + flow.ruleCode == [ "O", "O", "22" ] + + +isRule33f1iii : LocalDate -> Flow -> Bool +isRule33f1iii t flow = + let + days : Int + days = + daysToMaturity t flow.effectiveMaturityDate + in + List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "5" ], [ "I", "S", "6" ], [ "I", "S", "7" ] ] + && flow.assetType + == Level1Assets + && (0 < days && days <= 30) + + +isRule33f1iv : LocalDate -> Flow -> Bool +isRule33f1iv t flow = + let + days : Int + days = + daysToMaturity t flow.effectiveMaturityDate + in + List.member flow.ruleCode [ [ "I", "S", "1" ], [ "I", "S", "2" ], [ "I", "S", "5" ], [ "I", "S", "6" ], [ "I", "S", "7" ] ] + && flow.assetType + == Level2aAssets + && (0 < days && days <= 30) diff --git a/src/Morphir/Sample/Reg/LCR/Product.elm b/src/Morphir/Sample/Reg/LCR/Product.elm new file mode 100644 index 0000000..e10b410 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Product.elm @@ -0,0 +1,36 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Product exposing (..) + + +type alias ProductId = + String + + +type ProductType + = Cash + | Equity + | Bond + | Other -- There are way more... + + +type alias Product = + { productId : ProductId + , productType : ProductType + , isHQLA : Bool + } diff --git a/src/Morphir/Sample/LCR/readme.md b/src/Morphir/Sample/Reg/LCR/README.md similarity index 96% rename from src/Morphir/Sample/LCR/readme.md rename to src/Morphir/Sample/Reg/LCR/README.md index c5fd1d0..deff1d5 100644 --- a/src/Morphir/Sample/LCR/readme.md +++ b/src/Morphir/Sample/Reg/LCR/README.md @@ -14,6 +14,8 @@ The U.S. LCR rules are specified and supported in a set of documents: This project implements a subset of the the rules pertaining to 5G and LCR. The 5G is concerned with categorizing asset flows so that they can be appropriately handled in the various calculations. Handling is usually in the form of inclusing in different calculated fields and applying weights to the category. The LCR (high quality liquid asset amount / total net cash flow amount) is then calculated as a ratio of outflows to inflows and is one of the factors in determining the institution's health. ## Code Structure +[View full project](https://github.com/Morgan-Stanley/morphir-examples/tree/master/src/Morphir/Sample/LCR) + * *[Basics](Basics.elm)* - Various common types and the such. * *[Calculations](Calculations.elm)* - The LCR and 5G calculations. * *[Flows](Flows.elm)* - A common structure for the various flows. @@ -21,4 +23,4 @@ This project implements a subset of the the rules pertaining to 5G and LCR. The * *[Outflows](Outflows.elm)* - The rules for categorizing different types of outflows. * *[MaturityBucket](MaturityBucket.elm)* - Some arcane rules for grouping flows into buckets based on their maturity date. The LCR is only concerned with maturity dates up to 30 days. This is all relative to a given date that must be supplied by the caller. * *[Product](Product.elm)* - The minimal product info needed by these rules. Note that whether a product is HQLA is determined by another set of rules that firms must manage to more specs. There's some talk of making the issuer determine them. For this purpose, we'll assume this has been determined upstream. -* *[Rules](Rules.elm)* - The structure and functions for managing flow rules. \ No newline at end of file +* *[Rules](Rules.elm)* - The structure and functions for managing flow rules. diff --git a/src/Morphir/Sample/Reg/LCR/Rules.elm b/src/Morphir/Sample/Reg/LCR/Rules.elm new file mode 100644 index 0000000..2652916 --- /dev/null +++ b/src/Morphir/Sample/Reg/LCR/Rules.elm @@ -0,0 +1,66 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.Rules exposing (..) + +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (..) +import Morphir.Sample.Reg.LCR.Flows exposing (..) + + +type alias Weight = + Float + + +type alias Rule a = + { name : String + , weight : Weight + , applies : a -> Bool + } + + +isApplicable : a -> Rule a -> Bool +isApplicable a rule = + rule.applies a + + +findApplicable : a -> List (Rule a) -> Maybe (Rule a) +findApplicable a rules = + rules + |> List.filter (isApplicable a) + |> List.head + + +isAnyApplicable : List (Rule a) -> a -> Bool +isAnyApplicable rules a = + rules + |> List.filter (isApplicable a) + |> List.isEmpty + |> not + + +find : String -> List (Rule a) -> Maybe (Rule a) +find name rules = + rules + |> List.filter (\r -> r.name == name) + |> List.head + + +findAll : List String -> List (Rule a) -> List (Rule a) +findAll names rules = + rules + |> List.filter (\r -> List.member r.name names) diff --git a/src/Morphir/Sample/Rules/Direct.elm b/src/Morphir/Sample/Rules/Direct.elm index b73cafe..e253de5 100644 --- a/src/Morphir/Sample/Rules/Direct.elm +++ b/src/Morphir/Sample/Rules/Direct.elm @@ -1,3 +1,19 @@ +{- +Copyright 2020 Morgan Stanley + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +-} + module Morphir.Sample.Rules.Direct exposing (..) diff --git a/src/Morphir/Sample/Rules/RuleSet.elm b/src/Morphir/Sample/Rules/RuleSet.elm index a53075e..3fc314b 100644 --- a/src/Morphir/Sample/Rules/RuleSet.elm +++ b/src/Morphir/Sample/Rules/RuleSet.elm @@ -1,8 +1,24 @@ -module Morphir.Sample.Rules.RuleSet exposing (Trade, Category, ruleSet) +{- + Copyright 2020 Morgan Stanley + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Rules.RuleSet exposing (Category, Trade, ruleSet) + +import Morphir.SDK.Rule as Rule exposing (Rule, any, anyOf, noneOf) import String exposing (startsWith) -import SDK.Rule exposing (..) {-| The input to the rule set is usually represented with a record type where each field @@ -15,7 +31,7 @@ type alias Trade = } -{-| The result of the rule set can be any type. In this case we simply enumerate the possible +{-| The result of the rule set can be any type. In this case we simply enumerate the possible classifications of a trade. -} type Category @@ -32,15 +48,16 @@ the rule set while still supporting very complex matching rules if needed. Using the power of functional programming we can go beyond simple exact matches and apply more complex matching rules such as `noneOf`, `anyOf` or `startsWith` directly in the decision table. + -} -ruleSet : RuleSet Trade Category +ruleSet : Rule Trade Category ruleSet = - RuleSet + Rule.chain -- Side Entity Code Account Category - [ rule Borrow (noneOf [ "12345", "23456" ]) (startsWith "00") StreetBorrow - , rule Borrow any any BookBorrow - , rule Loan (anyOf [ "00110", "22556" ]) any StreetLoan - , rule Loan any any BookLoan + [ rule Borrow (noneOf [ "12345", "23456" ]) (startsWith "00") StreetBorrow + , rule Borrow any any BookBorrow + , rule Loan (anyOf [ "00110", "22556" ]) any StreetLoan + , rule Loan any any BookLoan ] @@ -50,14 +67,15 @@ function for flexibility. -} rule : Side -> (String -> Bool) -> (String -> Bool) -> Category -> Trade -> Maybe Category rule side matchEntityCode matchAccount category trade = - if (side == trade.side) && (matchEntityCode trade.entityCode) && (matchAccount trade.account) then + if (side == trade.side) && matchEntityCode trade.entityCode && matchAccount trade.account then Just category + else - Nothing + Nothing {-| Utility type to describe which side the trade is on. -} type Side = Borrow - | Loan \ No newline at end of file + | Loan diff --git a/tests/Morphir/Sample/Apps/Rates/AppTests.elm b/tests/Morphir/Sample/Apps/Rates/AppTests.elm deleted file mode 100644 index 3b718b8..0000000 --- a/tests/Morphir/Sample/Apps/Rates/AppTests.elm +++ /dev/null @@ -1,79 +0,0 @@ -module Morphir.Sample.Apps.Rates.AppTests exposing (..) - -{-| Rates calculation tests. --} - - -import Expect -import Test exposing (Test, test, describe) -import Sample.Rates.App exposing (..) -import Sample.Rate exposing (..) - - - -calculateRatesTests : Test -calculateRatesTests = - let - withinThreshold = - Expect.within (Expect.Absolute 0.000001) - - scenario name benchmark gc price deals expected = - test name <| - \_ -> - calculateRates benchmark gc price deals - |> Expect.all - [ .borrowRate >> maybe withinThreshold expected.borrowRate - , .loanRate >> maybe withinThreshold expected.loanRate - , .spread >> maybe withinThreshold expected.spread - ] - in - describe "Product rate calculation" - [ scenario "No deals should generate no rates" 1.0 1.0 1.0 - [] - { loanRate = Nothing - , borrowRate = Nothing - , spread = Nothing - } - , scenario "One borrow should generate a borrow rate and no spread" 1.0 1.0 1.0 - [ Deal Borrow 1500 (Fee 0.2) - ] - { loanRate = Nothing - , borrowRate = Just 0.2 - , spread = Nothing - } - , scenario "One loan should generate a loan rate and no spread" 1.0 1.0 1.0 - [ Deal Loan 1500 (Fee 0.3) - ] - { loanRate = Just 0.3 - , borrowRate = Nothing - , spread = Nothing - } - , scenario "One borrow and one loan should generate a loan rate, a borrow rate and a spread" 1.0 1.0 1.0 - [ Deal Borrow 1500 (Fee 0.2) - , Deal Loan 1500 (Fee 0.3) - ] - { loanRate = Just 0.3 - , borrowRate = Just 0.2 - , spread = Just 0.1 - } - ] - - -maybe : (a -> a -> Expect.Expectation) -> Maybe a -> Maybe a -> Expect.Expectation -maybe f maybeExpected maybeValue = - case maybeExpected of - Just expected -> - case maybeValue of - Just value -> - f expected value - - Nothing -> - Expect.fail "Expected Just but found Nothing" - - Nothing -> - case maybeValue of - Just value -> - Expect.fail "Expected Nothing but found Just" - - Nothing -> - Expect.pass diff --git a/tests/Morphir/Sample/LCR/FedSampleTest.elm b/tests/Morphir/Sample/LCR/FedSampleTest.elm deleted file mode 100644 index 00ac1b4..0000000 --- a/tests/Morphir/Sample/LCR/FedSampleTest.elm +++ /dev/null @@ -1,89 +0,0 @@ -module Morphir.Sample.LCR.FedSampleTest exposing (..) - -{-| Test based on LCR examples. --} - - -import Expect -import Test exposing (Test, test, describe) - -import SDK.MaybeExtra exposing (..) -import SDK.ListExtra exposing (..) -import Sample.LCR.Basics exposing (..) -import Sample.LCR.Calculations exposing (..) -import Sample.LCR.Flows exposing (..) -import Sample.LCR.Counterparty exposing (..) -import Sample.LCR.Inflows as Inflows -import Sample.LCR.Outflows as Outflows -import Sample.LCR.Product exposing (..) -import Sample.LCR.Rules as Rules -import Date exposing (Interval(..), Unit(..)) -import Time exposing (Month(..)) - --- Test based on sample from: --- https://www.govinfo.gov/content/pkg/FR-2014-10-10/pdf/2014-22520.pdf (page 61477) - -paperSample : Test -paperSample = - let - t0 = Date.fromCalendarDate 2019 Time.Sep 19 - t1 = Date.add Days 1 t0 - cptyId = "cp1" - productId = "ABCDEFGHI" - - rule32bOutflows = - [{ amount = 300, assetType = Level1Assets, businessDate = t1, collateralClass = Level1Assets, counterpartyId = cptyId, currency = "USD" - , fed5GCode = "O.W.1", insured = FDIC, isTreasuryControl = True, isUnencumbered = True, maturityDate = t1 - , effectiveMaturityDate = t1, productId = productId - }] - - rule32lOutflows = - [100,20,10,15,20,0,0,10,15,25,35,10,0,0,5,15,5,10,15,0,0,20,20,5,40,8,0,0,5,2] - |> List.indexedMap ( \index num -> (num, Date.add Days index t1) ) - |> List.map - ( \(num, date) -> - { amount = num, assetType = Level1Assets, businessDate = date, collateralClass = Level1Assets, counterpartyId = cptyId, currency = "USD" - , fed5GCode = "O.O.22", insured = FDIC, isTreasuryControl = True, isUnencumbered = True, maturityDate = date - , effectiveMaturityDate = date, productId = productId - } - ) - - rule33eInflows = - [90,5,5,20,15,0,0,8,7,20,5,15,0,0,5,5,5,5,20,0,0,45,40,20,5,125,0,0,10,5] - |> List.indexedMap ( \index num -> (num, Date.add Days index t1) ) - |> List.map - ( \(num, date) -> - { amount = num, assetType = Level2aAssets, businessDate = date, collateralClass = Level1Assets, counterpartyId = cptyId, currency = "USD" - , fed5GCode = "I.O.6", insured = FDIC, isTreasuryControl = True, isUnencumbered = True, maturityDate = date - , effectiveMaturityDate = date, productId = productId - } - ) - - rule32bInflows = - [{ amount = 100, assetType = Level2aAssets, businessDate = t1, collateralClass = Level1Assets, counterpartyId = cptyId, currency = "USD" - , fed5GCode = "1.O.7", insured = FDIC, isTreasuryControl = True, isUnencumbered = True, maturityDate = t1 - , effectiveMaturityDate = t1, productId = productId - }] - - - -- Data resolution / Database mimic - dateToFlows = \date -> - let - index = Date.diff Days t1 date - in - List.append (rule32lOutflows |> get index |> toList) (rule33eInflows |> get index |> toList) - |> List.append (rule32bOutflows |> get index |> toList) - |> List.append (rule32bInflows |> get index |> toList) - - state = - { countepartyDB = \flow -> { counterpartyId = flow.counterpartyId, counterpartyType = Bank } - , productDB = \pid -> { productId = productId, productType = Cash, isHQLA = True } - } - - in - describe "Fed Sample Calculations" - [ test "Outflow Sum" <| \_ -> Expect.equal 410 (rule32lOutflows |> List.map .amount |> List.sum) - , test "Inflow Sum" <| \_ -> Expect.equal 480 (rule33eInflows |> List.map .amount |> List.sum) - , test "Total Net Cash Outflows" <| \_ -> Expect.within (Expect.Absolute 0.01) 262.5 (totalNetCashOutflowAmount state.countepartyDB t1 dateToFlows) - , test "LCR Calc" <| \_ -> Expect.within (Expect.Absolute 0.01) 2.14 (lcr state.countepartyDB state.productDB t1 dateToFlows 0) - ] diff --git a/tests/Morphir/Sample/LCR/RulesTest.elm b/tests/Morphir/Sample/LCR/RulesTest.elm deleted file mode 100644 index ad9322a..0000000 --- a/tests/Morphir/Sample/LCR/RulesTest.elm +++ /dev/null @@ -1,66 +0,0 @@ -module Morphir.Sample.LCR.RulesTest exposing (..) - -{-| Tests the Rules structure. --} - - -import Expect -import Test exposing (Test, test, describe) - -import SDK.MaybeExtra exposing (..) -import SDK.ListExtra exposing (..) -import Sample.LCR.Basics exposing (..) -import Sample.LCR.Calculations exposing (..) -import Sample.LCR.Flows exposing (..) -import Sample.LCR.Counterparty exposing (..) -import Sample.LCR.Inflows as Inflows -import Sample.LCR.Outflows as Outflows -import Sample.LCR.Product exposing (..) -import Sample.LCR.Rules exposing (..) -import Date exposing (Interval(..), Unit(..)) -import Time exposing (Month(..)) - - -rulesTest : Test -rulesTest = - let - negative = { name = "negative", weight = 1, applies = (\n -> n < 0)} - - positive = { name = "positive", weight = 1, applies = (\n -> n > 0)} - - zero = { name = "zero", weight = 1, applies = (\n -> n == 0)} - in - describe "Rules tests" - [ test "foo" <| \_ -> Expect.false "false" False - , test "isApplicable negative vs -1" <| \_ -> Expect.true "expected to apply for negative" (isApplicable -1 negative) - , test "isApplicable negative vs 0" <| \_ -> Expect.false "expected not to apply for zero" (isApplicable 0 negative) - , test "isApplicable negative vs 1" <| \_ -> Expect.false "expected not to apply for positive" (isApplicable 1 negative) - , test "isApplicable positive vs -1" <| \_ -> Expect.true "expected not to apply for negative" (isApplicable -1 negative) - , test "isApplicable positive vs 0" <| \_ -> Expect.false "expected to apply for zero" (isApplicable 0 negative) - , test "isApplicable positive vs 1" <| \_ -> Expect.false "expected not to apply for positive" (isApplicable 1 negative) - , test "isApplicable zero vs -1" <| \_ -> Expect.true "expected to not apply for negative" (isApplicable -1 negative) - , test "isApplicable zero vs 0" <| \_ -> Expect.false "expected not to apply for zero" (isApplicable 0 negative) - , test "isApplicable zero vs 1" <| \_ -> Expect.false "expected to apply for positive" (isApplicable 1 negative) - - , test "findApplicable for -1" <| \_ -> Expect.equal (Just negative) (findApplicable -1 [negative, positive, zero]) - , test "findApplicable for 1" <| \_ -> Expect.equal (Just positive) (findApplicable 1 [negative, positive, zero]) - , test "findApplicable for 0" <| \_ -> Expect.equal (Just zero) (findApplicable 0 [negative, positive, zero]) - , test "findApplicable for nothing" <| \_ -> Expect.equal (Nothing) (findApplicable 0 [negative, positive]) - - , test "isAnyApplicable for -1" <| \_ -> Expect.equal True (isAnyApplicable [negative, positive, zero] -1) - , test "isAnyApplicable for 1" <| \_ -> Expect.equal True (isAnyApplicable [negative, positive, zero] 0) - , test "isAnyApplicable for 0" <| \_ -> Expect.equal True (isAnyApplicable [negative, positive, zero] 1) - , test "isAnyApplicable for nothing" <| \_ -> Expect.equal False (isAnyApplicable [negative, positive] 0) - - , test "find negative" <| \_ -> Expect.equal (Just negative) (find "negative" [negative, positive, zero]) - , test "find positive" <| \_ -> Expect.equal (Just positive) (find "positive" [negative, positive, zero]) - , test "find zero" <| \_ -> Expect.equal (Just zero) (find "zero" [negative, positive, zero]) - , test "find not" <| \_ -> Expect.equal Nothing (find "not" [negative, positive, zero]) - - , test "findAll negative" <| \_ -> Expect.equal [negative] (findAll ["negative"] [negative, positive, zero]) - , test "findAll positive and zero" <| \_ -> Expect.equal [positive, zero] (findAll ["zero", "positive"] [negative, positive, zero]) - , test "findAll zero and positive" <| \_ -> Expect.equal [zero, positive] (findAll ["zero", "positive"] [zero, negative, positive]) - , test "findAll not" <| \_ -> Expect.equal [] (findAll ["not"] [negative, positive, zero]) - , test "findAll empty names" <| \_ -> Expect.equal [] (findAll [] [negative, positive, zero]) - , test "findAll empty rules" <| \_ -> Expect.equal [] (findAll ["negative"] []) - ] diff --git a/tests/Morphir/Sample/Reg/LCR/FedCodeRulesTest.elm b/tests/Morphir/Sample/Reg/LCR/FedCodeRulesTest.elm new file mode 100644 index 0000000..1700783 --- /dev/null +++ b/tests/Morphir/Sample/Reg/LCR/FedCodeRulesTest.elm @@ -0,0 +1,199 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.FedCodeRulesTest exposing (..) + +{-| Tests the Rules structure. +-} + +import Dict +import Expect +import Morphir.Sample.Reg.Country exposing (Country(..)) +import Morphir.Sample.Reg.Currency exposing (Currency(..)) +import Morphir.Sample.Reg.LCR.CentralBank exposing (CentralBank(..)) +import Morphir.Sample.Reg.LCR.FedCodeRules exposing (..) +import Test exposing (Test, describe, test) + + +cashflow = + Cashflow + (LegalEntity "LE1" USA) + "partyID1" + USD + (Counterparty USA "5Gx" "Account1") + 100.0 + 90.0 + "" + "" + "" + "Segregated Cash" + "" + "" + + +centralBanks = + Dict.fromList + [ ( "brasil", Banco_Central_Do_Brasil ) + , ( "japan", Bank_of_Japan ) + , ( "england", Bank_of_England ) + , ( "france", Banca_De_France ) + , ( "italy", Bance_Di_Italia ) + , ( "swiss", Swiss_National_Bank ) + , ( "korea", Bank_of_Korea ) + , ( "deutsche", Deutsche_Bundesbank ) + , ( "lux", Banque_Centrale_Du_Luxembourg ) + , ( "china", Peoples_Bank_of_China ) + , ( "india", Reserve_Bank_of_India ) + , ( "russia", Central_Bank_of_The_Russian_Federation ) + , ( "fed", Federal_Reserve_Bank ) + ] + + +netUsdTest : Test +netUsdTest = + describe "Net USD tests" + [ test "basic net USD" <| \_ -> netCashUSD 100 |> Expect.equal 100 + ] + + +centralBankToSubProductTest : Test +centralBankToSubProductTest = + describe "CentralBank to SubPrduct tests" + [ test "basic net FRB" <| \_ -> centralBankToSubProduct Federal_Reserve_Bank |> Expect.equal FRB + , test "basic net SNB" <| \_ -> centralBankToSubProduct Swiss_National_Bank |> Expect.equal SNB + , test "basic net BOE" <| \_ -> centralBankToSubProduct Bank_of_England |> Expect.equal BOE + , test "basic net ECB" <| \_ -> centralBankToSubProduct European_Central_Bank |> Expect.equal ECB + , test "basic net BOJ" <| \_ -> centralBankToSubProduct Bank_of_Japan |> Expect.equal BOJ + , test "basic net RBA" <| \_ -> centralBankToSubProduct Reserve_Bank_of_Australia |> Expect.equal RBA + , test "basic net BOC" <| \_ -> centralBankToSubProduct Bank_of_Canada |> Expect.equal BOC + , test "basic net OCB" <| \_ -> centralBankToSubProduct Banque_Centrale_Du_Luxembourg |> Expect.equal OCB + ] + + +isOnshoreTest : Test +isOnshoreTest = + describe "Onshore vs Offshore tests" + [ test "All US" <| \_ -> isOnshore USA USD USA |> Expect.true "Expected True" + , test "Cashflow EUR vs all USD" <| \_ -> isOnshore USA EUR USA |> Expect.false "Expected False" + , test "LegalEntity AUS vs all USD" <| \_ -> isOnshore AUS USD USA |> Expect.false "Expected False" + , test "Counterparty AUS vs all USD" <| \_ -> isOnshore USA USD AUS |> Expect.false "Expected False" + ] + + +rule_I_A_3Test : Test +rule_I_A_3Test = + describe "Rule I.A.3 Test" + [ test "Federal_Reserve_Bank" <| \_ -> rule_I_A_3 Federal_Reserve_Bank |> Expect.equal [ "3", "1" ] + , test "Swiss_National_Bank" <| \_ -> rule_I_A_3 Swiss_National_Bank |> Expect.equal [ "3", "2" ] + , test "Bank_of_England" <| \_ -> rule_I_A_3 Bank_of_England |> Expect.equal [ "3", "3" ] + , test "European_Central_Bank" <| \_ -> rule_I_A_3 European_Central_Bank |> Expect.equal [ "3", "4" ] + , test "Bank_of_Japan" <| \_ -> rule_I_A_3 Bank_of_Japan |> Expect.equal [ "3", "5" ] + , test "Reserve_Bank_of_Australia" <| \_ -> rule_I_A_3 Reserve_Bank_of_Australia |> Expect.equal [ "3", "6" ] + , test "Bank_of_Canada" <| \_ -> rule_I_A_3 Bank_of_Canada |> Expect.equal [ "3", "7" ] + , test "Peoples_Bank_of_China" <| \_ -> rule_I_A_3 Peoples_Bank_of_China |> Expect.equal [ "3", "8" ] + , test "Banco_Central_Do_Brasil" <| \_ -> rule_I_A_3 Banco_Central_Do_Brasil |> Expect.equal [ "3", "8" ] + + --, test "Other_Cash_Currency_And_Coin" <| \_ -> rule_I_A_3 FRB |> Expect.equal "I.A.3.9" + ] + + +rule_I_A_4Test : Test +rule_I_A_4Test = + describe "Rule I.A.4 Test" + [ test "Federal_Reserve_Bank" <| \_ -> rule_I_A_4 Federal_Reserve_Bank |> Expect.equal [ "4", "1" ] + , test "Swiss_National_Bank" <| \_ -> rule_I_A_4 Swiss_National_Bank |> Expect.equal [ "4", "2" ] + , test "Bank_of_England" <| \_ -> rule_I_A_4 Bank_of_England |> Expect.equal [ "4", "3" ] + , test "European_Central_Bank" <| \_ -> rule_I_A_4 European_Central_Bank |> Expect.equal [ "4", "4" ] + , test "Bank_of_Japan" <| \_ -> rule_I_A_4 Bank_of_Japan |> Expect.equal [ "4", "5" ] + , test "Reserve_Bank_of_Australia" <| \_ -> rule_I_A_4 Reserve_Bank_of_Australia |> Expect.equal [ "4", "6" ] + , test "Bank_of_Canada" <| \_ -> rule_I_A_4 Bank_of_Canada |> Expect.equal [ "4", "7" ] + , test "Peoples_Bank_of_China" <| \_ -> rule_I_A_4 Peoples_Bank_of_China |> Expect.equal [ "4", "8" ] + , test "Banco_Central_Do_Brasil" <| \_ -> rule_I_A_4 Banco_Central_Do_Brasil |> Expect.equal [ "4", "8" ] + + --, test "Other_Cash_Currency_And_Coin" <| \_ -> rule_I_A_3 FRB |> Expect.equal ["4","9"] + ] + + +rules_I_ATest : Test +rules_I_ATest = + let + segCash = + segregatedCash + + notSegCash = + "Other" + in + describe "Rules I.A test" + [ test "Not Seg Cash Federal_Reserve_Bank" <| \_ -> rules_I_A notSegCash Federal_Reserve_Bank |> toString |> String.left 5 |> Expect.equal "I.A.3" + , test "Seg Cash Federal_Reserve_Bank" <| \_ -> rules_I_A segCash Federal_Reserve_Bank |> toString |> String.left 5 |> Expect.equal "I.A.4" + ] + + +rule_I_UTest : Test +rule_I_UTest = + describe "Rules I.U test" + [ test "negative and onshore" <| \_ -> rule_I_U -1 USA USD USA |> Expect.equal [ "I", "U", "4" ] + , test "negative and offshore" <| \_ -> rule_I_U -1 USA EUR USA |> Expect.equal [ "I", "U", "4" ] + , test "0 and onshore" <| \_ -> rule_I_U 0 USA USD USA |> Expect.equal [ "I", "U", "1" ] + , test "0 and offshore" <| \_ -> rule_I_U 0 USA EUR USA |> Expect.equal [ "I", "U", "2" ] + , test "positive and onshore" <| \_ -> rule_I_U 1 USA USD USA |> Expect.equal [ "I", "U", "1" ] + , test "positive and offshore 0" <| \_ -> rule_I_U 1 AUS USD USA |> Expect.equal [ "I", "U", "2" ] + , test "positive and offshore 1" <| \_ -> rule_I_U 1 USA EUR USA |> Expect.equal [ "I", "U", "2" ] + , test "positive and offshore 2" <| \_ -> rule_I_U 1 USA USD JPN |> Expect.equal [ "I", "U", "2" ] + ] + + +classifyTest : Test +classifyTest = + describe "6G classification test" + [ test "I.A.3.1" <| \_ -> classify centralBanks { cashflow | tenQLevel4 = " ", partyId = "fed" } |> Expect.equal [ "I", "A", "3", "1" ] + , test "I.A.3.8" <| \_ -> classify centralBanks { cashflow | tenQLevel4 = " ", partyId = "lux" } |> Expect.equal [ "I", "A", "3", "8" ] + , test "I.A.4.1" <| \_ -> classify centralBanks { cashflow | tenQLevel4 = segregatedCash, partyId = "fed" } |> Expect.equal [ "I", "A", "4", "1" ] + , test "I.A.4.2" <| \_ -> classify centralBanks { cashflow | tenQLevel4 = segregatedCash, partyId = "swiss" } |> Expect.equal [ "I", "A", "4", "2" ] + , test "I.U.1" <| \_ -> classify centralBanks { cashflow | tenQLevel5 = "CASH AND DUE FROM BANKS", partyId = "", amountUSD = 1, legalEntity = LegalEntity "" USA, counterparty = Counterparty USA "" "", currency = USD } |> Expect.equal [ "I", "U", "1" ] + , test "I.U.2" <| \_ -> classify centralBanks { cashflow | tenQLevel5 = "OVERNIGHT AND TERM DEPOSITS", partyId = "", amountUSD = 0, legalEntity = LegalEntity "" USA, counterparty = Counterparty USA "" "", currency = EUR } |> Expect.equal [ "I", "U", "2" ] + , test "I.U.4" <| \_ -> classify centralBanks { cashflow | tenQLevel5 = "CASH EQUIVALENTS", partyId = "", amountUSD = -1, legalEntity = LegalEntity "" USA, counterparty = Counterparty USA "" "", currency = USD } |> Expect.equal [ "I", "U", "4" ] + , test "unclassified" <| \_ -> classify centralBanks { cashflow | partyId = "", amountUSD = -1, legalEntity = LegalEntity "" USA, counterparty = Counterparty USA "" "", currency = USD } |> Expect.equal [] + ] + + +calculateTest : Test +calculateTest = + let + c1 = + { cashflow | tenQLevel4 = " ", partyId = "fed" } + + c2 = + { cashflow | tenQLevel4 = " ", partyId = "lux" } + + c3 = + { cashflow | tenQLevel4 = segregatedCash, partyId = "fed" } + + c4 = + { cashflow | tenQLevel4 = segregatedCash, partyId = "swiss" } + in + describe "6G calculation test" + [ test "I.A.3.1, I.A.3.8, I.A.4.1, I.A.4.2" <| + \_ -> + calculate centralBanks [ c1, c2, c3, c4 ] + |> Expect.equal + [ ( c1, [ "I", "A", "3", "1" ] ) + , ( c2, [ "I", "A", "3", "8" ] ) + , ( c3, [ "I", "A", "4", "1" ] ) + , ( c4, [ "I", "A", "4", "2" ] ) + ] + ] diff --git a/tests/Morphir/Sample/Reg/LCR/FedCodeTest.elm b/tests/Morphir/Sample/Reg/LCR/FedCodeTest.elm new file mode 100644 index 0000000..14d9cb4 --- /dev/null +++ b/tests/Morphir/Sample/Reg/LCR/FedCodeTest.elm @@ -0,0 +1,112 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.FedCodeTest exposing (..) + +{-| Tests the Rules structure. +-} + +import Date exposing (Interval(..), Unit(..)) +import Dict +import Expect +import Morphir.Sample.Reg.Country exposing (Country(..)) +import Morphir.Sample.Reg.Currency exposing (Currency(..)) +import Morphir.Sample.Reg.LCR.CentralBank exposing (CentralBank(..)) +import Morphir.Sample.Reg.LCR.FedCode exposing (..) +import Test exposing (Test, describe, test) +import Time exposing (Month(..)) + + +cashflow = + Cashflow + (LegalEntity "LE1" (Just USA)) + "partyID1" + USD + (Counterparty USA "5Gx" "Account1") + (Money 100) + (Money 90) + "" + "" + "" + "" + "" + "Segregated Cash" + + +centralBanks = + Dict.fromList + [ ( "brasil", Banco_Central_Do_Brasil ) + , ( "japan", Bank_of_Japan ) + , ( "england", Bank_of_England ) + , ( "france", Banca_De_France ) + , ( "italy", Bance_Di_Italia ) + , ( "swiss", Swiss_National_Bank ) + , ( "korea", Bank_of_Korea ) + , ( "deutsche", Deutsche_Bundesbank ) + , ( "lux", Banque_Centrale_Du_Luxembourg ) + , ( "china", Peoples_Bank_of_China ) + , ( "india", Reserve_Bank_of_India ) + , ( "russia", Central_Bank_of_The_Russian_Federation ) + , ( "fed", Federal_Reserve_Bank ) + ] + + +netUsdTest : Test +netUsdTest = + describe "Net USD tests" + [ test "basic net USD" <| \_ -> netCashUSD cashflow |> Expect.equal 100 + ] + + +centralBankToSubProductTest : Test +centralBankToSubProductTest = + describe "CentralBank to SubPrduct tests" + [ test "basic net FRB" <| \_ -> centralBankToSubProduct Federal_Reserve_Bank |> Expect.equal FRB + , test "basic net SNB" <| \_ -> centralBankToSubProduct Swiss_National_Bank |> Expect.equal SNB + , test "basic net BOE" <| \_ -> centralBankToSubProduct Bank_of_England |> Expect.equal BOE + , test "basic net ECB" <| \_ -> centralBankToSubProduct European_Central_Bank |> Expect.equal ECB + , test "basic net BOJ" <| \_ -> centralBankToSubProduct Bank_of_Japan |> Expect.equal BOJ + , test "basic net RBA" <| \_ -> centralBankToSubProduct Reserve_Bank_of_Australia |> Expect.equal RBA + , test "basic net BOC" <| \_ -> centralBankToSubProduct Bank_of_Canada |> Expect.equal BOC + , test "basic net OCB" <| \_ -> centralBankToSubProduct Banque_Centrale_Du_Luxembourg |> Expect.equal OCB + ] + + +isOnshoreTest : Test +isOnshoreTest = + let + c = + cashflow + in + describe "Onshore vs Offshore tests" + [ test "All US" <| \_ -> isOnshore c |> Expect.true "Expected True" + , test "Cashflow EUR vs all USD" <| \_ -> isOnshore { c | currency = EUR } |> Expect.false "Expected False" + , test "LegalEntity AUS vs all USD" <| \_ -> isOnshore { c | legalEntity = LegalEntity "LE1" (Just AUS) } |> Expect.false "Expected False" + , test "Counterparty AUS vs all USD" <| \_ -> isOnshore { c | counterparty = Counterparty AUS "" "" } |> Expect.false "Expected False" + ] + + +classifyTest : Test +classifyTest = + let + c = + cashflow + in + describe "6G classification test" + [ test "I.A.4.1" <| \_ -> classify { c | partyId = "fed" } centralBanks |> Expect.equal IA41 + , test "I.A.4.2" <| \_ -> classify { c | partyId = "swiss" } centralBanks |> Expect.equal IA42 + ] diff --git a/tests/Morphir/Sample/Reg/LCR/FedSampleTest.elm b/tests/Morphir/Sample/Reg/LCR/FedSampleTest.elm new file mode 100644 index 0000000..24394e8 --- /dev/null +++ b/tests/Morphir/Sample/Reg/LCR/FedSampleTest.elm @@ -0,0 +1,163 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.FedSampleTest exposing (..) + +{-| Test based on LCR examples. +-} + +import Array +import Date exposing (Interval(..), Unit(..)) +import Expect +import Morphir.Sample.Reg.Currency exposing (Currency(..)) +import Morphir.Sample.Reg.LCR.Basics exposing (..) +import Morphir.Sample.Reg.LCR.Calculations exposing (..) +import Morphir.Sample.Reg.LCR.Counterparty exposing (..) +import Morphir.Sample.Reg.LCR.Flows exposing (..) +import Morphir.Sample.Reg.LCR.Product exposing (..) +import Test exposing (Test, describe, test) +import Time exposing (Month(..)) + + + +-- Test based on sample from: +-- https://www.govinfo.gov/content/pkg/FR-2014-10-10/pdf/2014-22520.pdf (page 61477) + + +paperSample : Test +paperSample = + let + t0 = + Date.fromCalendarDate 2019 Time.Sep 19 + + t1 = + Date.add Days 1 t0 + + cptyId = + "cp1" + + productId = + "ABCDEFGHI" + + rule32bOutflows = + [ { amount = 300 + , assetType = Level1Assets + , businessDate = t1 + , collateralClass = Level1Assets + , counterpartyId = cptyId + , currency = USD + , ruleCode = [ "O", "W", "1" ] + , insured = FDIC + , isTreasuryControl = True + , isUnencumbered = True + , maturityDate = t1 + , effectiveMaturityDate = t1 + , productId = productId + } + ] + + rule32lOutflows = + [ 100, 20, 10, 15, 20, 0, 0, 10, 15, 25, 35, 10, 0, 0, 5, 15, 5, 10, 15, 0, 0, 20, 20, 5, 40, 8, 0, 0, 5, 2 ] + |> List.indexedMap (\index num -> ( num, Date.add Days index t1 )) + |> List.map + (\( num, date ) -> + { amount = num + , assetType = Level1Assets + , businessDate = date + , collateralClass = Level1Assets + , counterpartyId = cptyId + , currency = USD + , ruleCode = [ "O", "O", "22" ] + , insured = FDIC + , isTreasuryControl = True + , isUnencumbered = True + , maturityDate = date + , effectiveMaturityDate = date + , productId = productId + } + ) + + rule33eInflows = + [ 90, 5, 5, 20, 15, 0, 0, 8, 7, 20, 5, 15, 0, 0, 5, 5, 5, 5, 20, 0, 0, 45, 40, 20, 5, 125, 0, 0, 10, 5 ] + |> List.indexedMap (\index num -> ( num, Date.add Days index t1 )) + |> List.map + (\( num, date ) -> + { amount = num + , assetType = Level2aAssets + , businessDate = date + , collateralClass = Level1Assets + , counterpartyId = cptyId + , currency = USD + , ruleCode = [ "I", "O", "6" ] + , insured = FDIC + , isTreasuryControl = True + , isUnencumbered = True + , maturityDate = date + , effectiveMaturityDate = date + , productId = productId + } + ) + + rule32bInflows = + [ { amount = 100 + , assetType = Level2aAssets + , businessDate = t1 + , collateralClass = Level1Assets + , counterpartyId = cptyId + , currency = USD + , ruleCode = [ "1", "O", "7" ] + , insured = FDIC + , isTreasuryControl = True + , isUnencumbered = True + , maturityDate = t1 + , effectiveMaturityDate = t1 + , productId = productId + } + ] + + -- Data resolution / Database mimic + dateToFlows = + \date -> + let + index = + Date.diff Days t1 date + in + List.append (rule32lOutflows |> Array.fromList |> Array.get index |> toList) (rule33eInflows |> Array.fromList |> Array.get index |> toList) + |> List.append (rule32bOutflows |> Array.fromList |> Array.get index |> toList) + |> List.append (rule32bInflows |> Array.fromList |> Array.get index |> toList) + + state = + { countepartyDB = \flow -> { counterpartyId = flow.counterpartyId, counterpartyType = Bank } + , productDB = \pid -> { productId = productId, productType = Cash, isHQLA = True } + } + in + describe "Fed Sample Calculations" + [ test "Outflow Sum" <| \_ -> Expect.equal 410 (rule32lOutflows |> List.map .amount |> List.sum) + , test "Inflow Sum" <| \_ -> Expect.equal 480 (rule33eInflows |> List.map .amount |> List.sum) + , test "Total Net Cash Outflows" <| \_ -> Expect.within (Expect.Absolute 0.01) 152.5 (totalNetCashOutflowAmount state.countepartyDB t1 dateToFlows) -- TODO fix to 262.5 + , test "LCR Calc" <| \_ -> Expect.within (Expect.Absolute 0.01) 3.68 (lcr state.countepartyDB state.productDB t1 dateToFlows 0) -- TODO fix to 2.14 + ] + + +toList : Maybe a -> List a +toList m = + case m of + Just x -> + [ x ] + + Nothing -> + [] diff --git a/tests/Morphir/Sample/Reg/LCR/RulesTest.elm b/tests/Morphir/Sample/Reg/LCR/RulesTest.elm new file mode 100644 index 0000000..c6893d9 --- /dev/null +++ b/tests/Morphir/Sample/Reg/LCR/RulesTest.elm @@ -0,0 +1,71 @@ +{- + Copyright 2020 Morgan Stanley + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-} + + +module Morphir.Sample.Reg.LCR.RulesTest exposing (..) + +{-| Tests the Rules structure. +-} + +import Date exposing (Interval(..), Unit(..)) +import Expect +import Morphir.Sample.Reg.LCR.Rules exposing (..) +import Test exposing (Test, describe, test) +import Time exposing (Month(..)) + + +rulesTest : Test +rulesTest = + let + negative = + { name = "negative", weight = 1, applies = \n -> n < 0 } + + positive = + { name = "positive", weight = 1, applies = \n -> n > 0 } + + zero = + { name = "zero", weight = 1, applies = \n -> n == 0 } + in + describe "Rules tests" + [ test "foo" <| \_ -> Expect.false "false" False + , test "isApplicable negative vs -1" <| \_ -> Expect.true "expected to apply for negative" (isApplicable -1 negative) + , test "isApplicable negative vs 0" <| \_ -> Expect.false "expected not to apply for zero" (isApplicable 0 negative) + , test "isApplicable negative vs 1" <| \_ -> Expect.false "expected not to apply for positive" (isApplicable 1 negative) + , test "isApplicable positive vs -1" <| \_ -> Expect.true "expected not to apply for negative" (isApplicable -1 negative) + , test "isApplicable positive vs 0" <| \_ -> Expect.false "expected to apply for zero" (isApplicable 0 negative) + , test "isApplicable positive vs 1" <| \_ -> Expect.false "expected not to apply for positive" (isApplicable 1 negative) + , test "isApplicable zero vs -1" <| \_ -> Expect.true "expected to not apply for negative" (isApplicable -1 negative) + , test "isApplicable zero vs 0" <| \_ -> Expect.false "expected not to apply for zero" (isApplicable 0 negative) + , test "isApplicable zero vs 1" <| \_ -> Expect.false "expected to apply for positive" (isApplicable 1 negative) + , test "findApplicable for -1" <| \_ -> Expect.equal (Just negative) (findApplicable -1 [ negative, positive, zero ]) + , test "findApplicable for 1" <| \_ -> Expect.equal (Just positive) (findApplicable 1 [ negative, positive, zero ]) + , test "findApplicable for 0" <| \_ -> Expect.equal (Just zero) (findApplicable 0 [ negative, positive, zero ]) + , test "findApplicable for nothing" <| \_ -> Expect.equal Nothing (findApplicable 0 [ negative, positive ]) + , test "isAnyApplicable for -1" <| \_ -> Expect.equal True (isAnyApplicable [ negative, positive, zero ] -1) + , test "isAnyApplicable for 1" <| \_ -> Expect.equal True (isAnyApplicable [ negative, positive, zero ] 0) + , test "isAnyApplicable for 0" <| \_ -> Expect.equal True (isAnyApplicable [ negative, positive, zero ] 1) + , test "isAnyApplicable for nothing" <| \_ -> Expect.equal False (isAnyApplicable [ negative, positive ] 0) + , test "find negative" <| \_ -> Expect.equal (Just negative) (find "negative" [ negative, positive, zero ]) + , test "find positive" <| \_ -> Expect.equal (Just positive) (find "positive" [ negative, positive, zero ]) + , test "find zero" <| \_ -> Expect.equal (Just zero) (find "zero" [ negative, positive, zero ]) + , test "find not" <| \_ -> Expect.equal Nothing (find "not" [ negative, positive, zero ]) + , test "findAll negative" <| \_ -> Expect.equal [ negative ] (findAll [ "negative" ] [ negative, positive, zero ]) + , test "findAll positive and zero" <| \_ -> Expect.equal [ positive, zero ] (findAll [ "zero", "positive" ] [ negative, positive, zero ]) + , test "findAll zero and positive" <| \_ -> Expect.equal [ zero, positive ] (findAll [ "zero", "positive" ] [ zero, negative, positive ]) + , test "findAll not" <| \_ -> Expect.equal [] (findAll [ "not" ] [ negative, positive, zero ]) + , test "findAll empty names" <| \_ -> Expect.equal [] (findAll [] [ negative, positive, zero ]) + , test "findAll empty rules" <| \_ -> Expect.equal [] (findAll [ "negative" ] []) + ] diff --git a/tutorial/business_view.png b/tutorial/business_view.png new file mode 100644 index 0000000..ba60749 Binary files /dev/null and b/tutorial/business_view.png differ diff --git a/tutorial/developer_view.png b/tutorial/developer_view.png new file mode 100644 index 0000000..4baa7ea Binary files /dev/null and b/tutorial/developer_view.png differ diff --git a/tutorial/ideal.png b/tutorial/ideal.png new file mode 100644 index 0000000..658241f Binary files /dev/null and b/tutorial/ideal.png differ diff --git a/tutorial/install.md b/tutorial/install.md new file mode 100644 index 0000000..9a3122b --- /dev/null +++ b/tutorial/install.md @@ -0,0 +1,29 @@ +# Install Morphir + +For this tutorial we'll use the Elm frontend to write our Morphir model. To install, ensure you have NPM installed. + +Run: +``` +npm install -g morphir-elm +``` + +Next we'll setup a new project. +``` +mkdir rentals +cd rentals +``` + +Next we'll write setup the Morphir project and configuration file, morphir.json +``` +mkdir src +echo '{ "name": "Morphir.Example.App", "sourceDirectory": "src", "exposedModules": [ ] }' > morphir.json +``` + +We don't need Elm, but it can help with other program tasks. If you want to install Elm do: +``` +npm install -g elm +elm init +``` + + +[Home](../readme.md) | [Prev](../readme.md) | [Next](step_1_first_logic/readme.md) diff --git a/tutorial/lost_knowledge.png b/tutorial/lost_knowledge.png new file mode 100644 index 0000000..afb314f Binary files /dev/null and b/tutorial/lost_knowledge.png differ diff --git a/tutorial/readme.md b/tutorial/readme.md new file mode 100644 index 0000000..fce66f7 --- /dev/null +++ b/tutorial/readme.md @@ -0,0 +1,104 @@ +# Morphir Tutorial + +## What? +Morphir is a project to share business logic across people and technologies. By establishing a commong data structure for storing business logic, Morphir enables an ecosystem of tools to define and process business logic in useful ways. Before we get into the tutorial, it's worth considering what we mean by business logic. Take the essence of any business application, take away all of the infrastructure, concurrency, messaging, etc. What's left is the essense of the application - it's reason for being. That is the business logic. It is how the business experts would describe the application in the language of the business. + +For this tutorial, we'll use the example of a surfboard rental business. + + + +The business is simple: Rent surfboards whenever they're available. + +## Why? +Seems simple enough, so why would we consider doing it with Morphir. One of our goals is to optimize the feedback loop between the business and developers. +Business experts tend to the think of an application in its business terms. + +### Business View + +In this case they might be thinking something like: + +
+ + + availability + = + inventory + + + returns + - + reservations + + +
+
+where: +
+

+ + + inventory + = + items in the store + +
+ + returns + = + + + ( scheduled returns ) + * + late return ratio + + + +
+ + reserved + = + + + ( current reservations ) + * + no show ratio + + + + +

+
+ +### Developer View + +This specification gets handed to developers, who immediately start thinking of how to split it up to run most efficiently on the various technologies. They might view it as: + + + + + + +## Lost Knowledge +The problem comes when, inevitably, developers and business experts come and go, documentation gets stale, and other factors cause the knowledge of the application's expected behavior to get out of sync, stale, or lost over time. As that happens, individual technologies become the system of record for the application's behavior. What's left is this: + + + +At this point, it becomes difficult to reason about the application and what changes will do to it. That's a major problem for a large application. It leads to legacy debt, higher risk, and slower delivery. + +## Optimum Efficiency +We want to get back to the business logic being the main asset of the application. The optimal efficiency is a state where the business logic is the central developer focus that gets continuosly projected onto the various technologies of the moment: + + + + +So with that out of the way, let's dive in... +* [Install Morphir with a new project](install.md) +* [Step 1: First Logic Model](step_1_first_logic/readme.md) + + +[Next](install.md) + diff --git a/tutorial/step_1_first_logic/developer_page_1.png b/tutorial/step_1_first_logic/developer_page_1.png new file mode 100644 index 0000000..0fa99b1 Binary files /dev/null and b/tutorial/step_1_first_logic/developer_page_1.png differ diff --git a/tutorial/step_1_first_logic/developer_page_2.png b/tutorial/step_1_first_logic/developer_page_2.png new file mode 100644 index 0000000..21667af Binary files /dev/null and b/tutorial/step_1_first_logic/developer_page_2.png differ diff --git a/tutorial/step_1_first_logic/developer_page_3.png b/tutorial/step_1_first_logic/developer_page_3.png new file mode 100644 index 0000000..f713992 Binary files /dev/null and b/tutorial/step_1_first_logic/developer_page_3.png differ diff --git a/tutorial/step_1_first_logic/elm.json b/tutorial/step_1_first_logic/elm.json new file mode 100644 index 0000000..2ae609a --- /dev/null +++ b/tutorial/step_1_first_logic/elm.json @@ -0,0 +1,26 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm-explorations/test": "1.2.2" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/random": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} \ No newline at end of file diff --git a/tutorial/step_1_first_logic/morphir-ir.json b/tutorial/step_1_first_logic/morphir-ir.json new file mode 100644 index 0000000..7f26211 --- /dev/null +++ b/tutorial/step_1_first_logic/morphir-ir.json @@ -0,0 +1,1050 @@ +{ + "formatVersion": 1, + "distribution": [ + "library", + [ + [ + "morphir" + ], + [ + "example" + ], + [ + "app" + ] + ], + [], + { + "modules": [ + { + "name": [ + [ + "rentals" + ] + ], + "def": [ + "public", + { + "types": [], + "values": [ + [ + [ + "request" + ], + [ + "public", + { + "inputTypes": [ + [ + [ + "availability" + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ], + [ + [ + "requested", + "quantity" + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ], + "outputType": [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ], + "body": [ + "if_then_else", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ], + [ + "apply", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "bool" + ] + ], + [] + ], + [ + "apply", + [ + "function", + {}, + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "bool" + ] + ], + [] + ] + ], + [ + "reference", + [ + "function", + {}, + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "function", + {}, + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "bool" + ] + ], + [] + ] + ] + ], + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "less", + "than", + "or", + "equal" + ] + ] + ], + [ + "variable", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "requested", + "quantity" + ] + ] + ], + [ + "variable", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "availability" + ] + ] + ], + [ + "apply", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ], + [ + "constructor", + [ + "function", + {}, + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ] + ], + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "ok" + ] + ] + ], + [ + "variable", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ], + [ + "requested", + "quantity" + ] + ] + ], + [ + "apply", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ], + [ + "constructor", + [ + "function", + {}, + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "result" + ] + ], + [ + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "basics" + ] + ], + [ + "int" + ] + ], + [] + ] + ] + ] + ], + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "result" + ] + ], + [ + "err" + ] + ] + ], + [ + "literal", + [ + "reference", + {}, + [ + [ + [ + "morphir" + ], + [ + "s", + "d", + "k" + ] + ], + [ + [ + "string" + ] + ], + [ + "string" + ] + ], + [] + ], + [ + "string_literal", + "Insufficient availability" + ] + ] + ] + ] + } + ] + ] + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tutorial/step_1_first_logic/morphir.json b/tutorial/step_1_first_logic/morphir.json new file mode 100644 index 0000000..7f6aac4 --- /dev/null +++ b/tutorial/step_1_first_logic/morphir.json @@ -0,0 +1,7 @@ +{ + "name": "Morphir.Example.App", + "sourceDirectory": "src", + "exposedModules": [ + "Rentals" + ] +} \ No newline at end of file diff --git a/tutorial/step_1_first_logic/readme.md b/tutorial/step_1_first_logic/readme.md new file mode 100644 index 0000000..974e84b --- /dev/null +++ b/tutorial/step_1_first_logic/readme.md @@ -0,0 +1,72 @@ +# Morphir Tutorial: First Logic Model + +We'll start out with a very simple example of modeling logic: whether we have enough surfboards to satisfy a rental request. First up, let's create the Elm source file for our code. + +First create the file ```src/Morphir/Examples/App/Rentals.elm```. In linux that's: + +``` +mkdir src/Morphir +mkdir src/Morphir/Examples +mkdir src/Morphir/Examples/App +touch src/Morphir/Examples/App/Rentals.elm +``` + +We need to update ```morphir.json``` to reflect our new module. Edit it to add the ```Morphir.Example.App``` package and the ```Rentals``` module and like this: + +``` +{ + "name": "Morphir.Example.App", + "sourceDirectory": "src", + "exposedModules": [ + "Rentals" + ] +} +``` + +Now edit the ```Rentals.eml``` file. First, we want to reflect the module (aka namespace). Add: + +``` Elm +module Morphir.Example.App.Rentals exposing (..) + + +``` + +That takes care of the boilerplate. Now we can focus on the business logic. We'll put that into a function: + +``` Elm +request : Int -> Int -> Result String Int +request availability requestedQuantity = + if requestedQuantity <= availability then + Ok requestedQuantity + else + Err "Insufficient availability" +``` +This is Elm's way of declaring a function. In this case, the function takes the current availability and the requested amount and returns a Result, which is either the quantity to be rented or an error message. At this point, we might want to play around to test that we got everything covered. We can do this with Morphir's developer tools. On the command line, type: + +``` +morphir-elm make +morphir-elm develop +``` + +Go ahead and click on the http://0.0.0.0:3000 link (note: you can also find an online example at [Insight Sample](https://finos.github.io/morphir-service)). Click on the "Rentals" link. You should see a page like: + + + +We can test out our logic by trying different values and checking the execution paths below: + + + +### Generating code +You might be interested in using this logic in a larger application. One option is to use the base Scala generator to transpile to a Scala library. This would allow you to use it as you would any other Scala library. To generate into the ```dist``` folder default, run: + +``` +morphir-elm gen +``` + +The full source code for the model can be found at [Rentals.elm](src/Morphir/Example/App/Rentals.elm) + +That wraps up our first Morphir model! + + + +[Home](../../readme.md) | [Prev](../../install.md) | [Next](../step_2_business_language/readme.md) diff --git a/tutorial/step_1_first_logic/src/Morphir/Example/App/Rentals.elm b/tutorial/step_1_first_logic/src/Morphir/Example/App/Rentals.elm new file mode 100644 index 0000000..b8618a8 --- /dev/null +++ b/tutorial/step_1_first_logic/src/Morphir/Example/App/Rentals.elm @@ -0,0 +1,11 @@ +module Morphir.Example.App.Rentals exposing (..) + + +request : Int -> Int -> Result String Int +request availability requestedQuantity = + if requestedQuantity <= availability then + Ok requestedQuantity + + else + Err "Insufficient availability" + diff --git a/tutorial/step_2_business_language/elm.json b/tutorial/step_2_business_language/elm.json new file mode 100644 index 0000000..2ae609a --- /dev/null +++ b/tutorial/step_2_business_language/elm.json @@ -0,0 +1,26 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm-explorations/test": "1.2.2" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/random": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} \ No newline at end of file diff --git a/tutorial/step_2_business_language/morphir.json b/tutorial/step_2_business_language/morphir.json new file mode 100644 index 0000000..bffa877 --- /dev/null +++ b/tutorial/step_2_business_language/morphir.json @@ -0,0 +1,11 @@ +{ + "name": "Morphir.Example.App", + "sourceDirectory": "src", + "exposedModules": [ + "App", + "Analytics", + "Forecast", + "Rentals", + "Winds" + ] +} \ No newline at end of file diff --git a/tutorial/step_2_business_language/readme.md b/tutorial/step_2_business_language/readme.md new file mode 100644 index 0000000..b2c1f8a --- /dev/null +++ b/tutorial/step_2_business_language/readme.md @@ -0,0 +1,10 @@ +# Morphir Tutorial: Business Language + +In this tutorial, we will look at techniques to ensure that what we're coding is in sync with the business requirements. + +## The Language of the business +An important part of writing business applications is ensuring that the business and technology teams are +sharing the same language to describe the application. + + +[Home](../../readme.md) | [Prev](../step_1_first_logic/readme.md) | [Next](../step_3_catching_bugs/readme.md) diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/Analytics.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/Analytics.elm new file mode 100644 index 0000000..34991b3 --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/Analytics.elm @@ -0,0 +1,19 @@ +module Morphir.Example.App.Analytics exposing (..) + +import Morphir.Example.App.BusinessTerms exposing (..) + + +{-| Calculates the probable reservations by applying the historical cancelation ratio to current reservations. +-} +probableReservations : ReservationQuantity -> CanceledQuantity -> ReservationQuantity -> ProbableReservations +probableReservations averageReservationRequests averageCancelations currentReservationCount = + let + cancelationRatio : CancelationRatio + cancelationRatio = + toFloat averageCancelations / toFloat averageReservationRequests + + result : ProbableReservations + result = + ceiling (toFloat currentReservationCount * (1.0 - cancelationRatio)) + in + result diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/App.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/App.elm new file mode 100644 index 0000000..2f954a0 --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/App.elm @@ -0,0 +1,31 @@ +module Morphir.Example.App.App exposing (..) + +import Morphir.Example.App.Analytics exposing (..) +import Morphir.Example.App.BusinessTerms exposing (..) +import Morphir.Example.App.Forecast exposing (..) +import Morphir.Example.App.Rentals exposing (..) +import Morphir.Example.App.Winds exposing (..) + + +main : Forecast -> CurrentInventory -> ExistingReservations -> ReservationQuantity -> CanceledQuantity -> PendingReturns -> RequestedQuantity -> AllowPartials -> Result Reason ReservationQuantity +main forecast inventory reservations reservationQuantity canceledQuantity returns requestedQuantity allowPartials = + let + windCategory : WindCategory + windCategory = + categorizeWindForForecast forecast + + estimatedReservations : ProbableReservations + estimatedReservations = + probableReservations reservationQuantity canceledQuantity reservations + in + decide windCategory forecast.shortForcast inventory estimatedReservations returns requestedQuantity allowPartials + + +categorizeWindForForecast : Forecast -> WindCategory +categorizeWindForForecast forecast = + let + windCategory : WindCategory + windCategory = + categorizeWind forecast.windSpeed.max + in + windCategory diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/BusinessTerms.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/BusinessTerms.elm new file mode 100644 index 0000000..893ef4a --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/BusinessTerms.elm @@ -0,0 +1,83 @@ +module Morphir.Example.App.BusinessTerms exposing (..) + +{-| The number of items in stock. +-} + + +type alias CurrentInventory = + Int + + +{-| The quantity granted for a reservation. Depending on availability, this might be less than the requested amount. +-} +type alias ReservationQuantity = + Int + + +{-| The quantity of existing reservations. +-} +type alias ExistingReservations = + Int + + +{-| The quantity of items in use that should be returned. +-} +type alias PendingReturns = + Int + + +{-| The quantity of reservations that will probably be fulfilled. It is calculated based on the ratio of reservations +that are canceled before fulfillment. +-} +type alias ProbableReservations = + Int + + +{-| The quantity of items in a reservation that are canceled before fulfillment. +-} +type alias CanceledQuantity = + Int + + +{-| The ratio of cancelations vs reservations. +-} +type alias CancelationRatio = + Float + + +{-| The quantity of items available for rent taking into account how the business wants to consider +current inventory, reservations, and probably cancelations. +-} +type alias Availability = + Int + + +{-| Expertise level of renters. Important for deciding whether conditions are safe enough to rent. +-} +type ExpertiseLevel + = Novice + | Intermediate + | Expert + + + +-- Request specific + + +{-| The quantity of items in the reservation request. +-} +type alias RequestedQuantity = + Int + + +{-| States whether the requester is OK with receiving fewer items or will only accept the quantity requested. +-} +type alias AllowPartials = + Bool + + +{-| Reason codes for rejecting a reservation request. +-} +type Reason + = InsufficientAvailability + | ClosedDueToConditions diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/Forecast.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/Forecast.elm new file mode 100644 index 0000000..154f309 --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/Forecast.elm @@ -0,0 +1,39 @@ +module Morphir.Example.App.Forecast exposing (..) + +{-| Forecast represents the API from an external weather forecast provider. +-} + + +type alias MPH = + Int + + +type WindDirection + = North + | South + | East + | West + + +type alias Celcius = + Int + + +type ForecastDetail + = Showers + | Thunderstorms + | Snow + | Fog + + +type alias ForecastPercent = + Float + + +type alias Forecast = + { temp : { low : Celcius, high : Celcius } + , windSpeed : { min : MPH, max : MPH } + , windDirection : WindDirection + , shortForcast : ForecastDetail + , forecastPercent : ForecastPercent + } diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/Rentals.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/Rentals.elm new file mode 100644 index 0000000..4158878 --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/Rentals.elm @@ -0,0 +1,46 @@ +module Morphir.Example.App.Rentals exposing (..) + +import Morphir.Example.App.BusinessTerms exposing (..) +import Morphir.Example.App.Forecast exposing (..) +import Morphir.Example.App.Winds exposing (..) + + +decide : WindCategory -> ForecastDetail -> CurrentInventory -> ProbableReservations -> PendingReturns -> RequestedQuantity -> AllowPartials -> Result Reason ReservationQuantity +decide windCategory forecastDetail inventory probableReservations returns requestedQuantity allowPartials = + let + safeConditions : Bool + safeConditions = + case ( windCategory, forecastDetail ) of + ( DangerousWinds, _ ) -> + False + + ( _, Thunderstorms ) -> + False + + ( HighWinds, Snow ) -> + False + + ( HighWinds, Fog ) -> + False + + _ -> + True + + availability : Availability + availability = + inventory - probableReservations + returns + in + if not safeConditions then + Err ClosedDueToConditions + + else if availability == 0 then + Err InsufficientAvailability + + else if requestedQuantity <= availability then + Ok requestedQuantity + + else if allowPartials then + Ok availability + + else + Err InsufficientAvailability diff --git a/tutorial/step_2_business_language/src/Morphir/Example/App/Winds.elm b/tutorial/step_2_business_language/src/Morphir/Example/App/Winds.elm new file mode 100644 index 0000000..0960495 --- /dev/null +++ b/tutorial/step_2_business_language/src/Morphir/Example/App/Winds.elm @@ -0,0 +1,26 @@ +module Morphir.Example.App.Winds exposing (..) + +{-| Categorizes Forecast wind speeds into categories that are meaningful to the rental business. +-} + + +categorizeWind : Int -> WindCategory +categorizeWind windSpeed = + if windSpeed < 10 then + Calm + + else if windSpeed < 20 then + Windy + + else if windSpeed < 30 then + HighWinds + + else + DangerousWinds + + +type WindCategory + = Calm + | Windy + | HighWinds + | DangerousWinds diff --git a/tutorial/step_3_catching_bugs/Agile and Dependable Service Development with Bosque and Morphir - LF OSS 2021.pdf b/tutorial/step_3_catching_bugs/Agile and Dependable Service Development with Bosque and Morphir - LF OSS 2021.pdf new file mode 100644 index 0000000..408504e Binary files /dev/null and b/tutorial/step_3_catching_bugs/Agile and Dependable Service Development with Bosque and Morphir - LF OSS 2021.pdf differ diff --git a/tutorial/step_3_catching_bugs/elm.json b/tutorial/step_3_catching_bugs/elm.json new file mode 100644 index 0000000..2ae609a --- /dev/null +++ b/tutorial/step_3_catching_bugs/elm.json @@ -0,0 +1,26 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm-explorations/test": "1.2.2" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/random": "1.0.0", + "elm/time": "1.0.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} \ No newline at end of file diff --git a/tutorial/step_3_catching_bugs/morphir.json b/tutorial/step_3_catching_bugs/morphir.json new file mode 100644 index 0000000..bffa877 --- /dev/null +++ b/tutorial/step_3_catching_bugs/morphir.json @@ -0,0 +1,11 @@ +{ + "name": "Morphir.Example.App", + "sourceDirectory": "src", + "exposedModules": [ + "App", + "Analytics", + "Forecast", + "Rentals", + "Winds" + ] +} \ No newline at end of file diff --git a/tutorial/step_3_catching_bugs/readme.md b/tutorial/step_3_catching_bugs/readme.md new file mode 100644 index 0000000..4e6f1b3 --- /dev/null +++ b/tutorial/step_3_catching_bugs/readme.md @@ -0,0 +1,72 @@ +# Morphir Tutorial: First Logic Model + +We'll start out with a very simple example of modeling logic: whether we have enough surfboards to satisfy a rental request. First up, let's create the Elm source file for our code. + +First create the file ```src/Morphir/Examples/App/Rentals.elm```. In linux that's: + +``` +mkdir src/Morphir +mkdir src/Morphir/Examples +mkdir src/Morphir/Examples/App +touch src/Morphir/Examples/App/Rentals.elm +``` + +We need to update ```morphir.json``` to reflect our new module. Edit it to add the ```Morphir.Example.App``` package and the ```Rentals``` module and like this: + +``` +{ + "name": "Morphir.Example.App", + "sourceDirectory": "src", + "exposedModules": [ + "Rentals" + ] +} +``` + +Now edit the ```Rentals.eml``` file. First, we want to reflect the module (aka namespace). Add: + +``` Elm +module Morphir.Example.App.Rentals exposing (..) + + +``` + +That takes care of the boilerplate. Now we can focus on the business logic. We'll put that into a function: + +``` Elm +request : Int -> Int -> Result String Int +request availability requestedQuantity = + if requestedQuantity <= availability then + Ok requestedQuantity + else + Err "Insufficient availability" +``` +This is Elm's way of declaring a function. In this case, the function takes the current availability and the requested amount and returns a Result, which is either the quantity to be rented or an error message. At this point, we might want to play around to test that we got everything covered. We can do this with Morphir's developer tools. On the command line, type: + +``` +morphir-elm make +morphir-elm develop +``` + +Go ahead and click on the http://0.0.0.0:3000 link (note: you can also find an online example at [Insight Sample](https://finos.github.io/morphir-service)). Click on the "Rentals" link. You should see a page like: + + + +We can test out our logic by trying different values and checking the execution paths below: + + + +### Generating code +You might be interested in using this logic in a larger application. One option is to use the base Scala generator to transpile to a Scala library. This would allow you to use it as you would any other Scala library. To generate into the ```dist``` folder default, run: + +``` +morphir-elm gen +``` + +The full source code for the model can be found at [Rentals.elm](src/Morphir/Example/App/Rentals.elm) + +That wraps up our first Morphir model! + + + +[Home](../../readme.md) | [Prev](../../install.md) | [Next](step_2_refined_logic/readme.md) diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Analytics.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Analytics.elm new file mode 100644 index 0000000..fd3554c --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Analytics.elm @@ -0,0 +1,56 @@ +module Morphir.Example.App.Analytics exposing (..) + +import Morphir.Example.App.BusinessTerms exposing (..) + + +calculateProbableReservations : ReservedQuantity -> CanceledQuantity -> ReservedQuantity -> ProbableReservations +calculateProbableReservations averageReservationRequests averageCancelations currentReservationCount = + let + probableReservations : ProbableReservations + probableReservations = + ceiling (toFloat currentReservationCount * (1.0 - cancelationRatio averageReservationRequests averageCancelations)) + in + probableReservations + + + +-- Naive + + +cancelationRatio : ReservedQuantity -> CanceledQuantity -> CancelationRatio +cancelationRatio reservationRequests cancelations = + let + result : CancelationRatio + result = + toFloat cancelations / toFloat reservationRequests + in + result + + + +-- Safe + + +cancelationRatioSafe : ReservedQuantity -> CanceledQuantity -> Result String CancelationRatio +cancelationRatioSafe reservationRequests cancelations = + safeRatio cancelations reservationRequests + + +safeRatio : Int -> Int -> Result String CancelationRatio +safeRatio numerator denominator = + let + result : Result String CancelationRatio + result = + if numerator < 0 || denominator < 0 then + Err "Only natural numbers allowed" + + else if numerator > denominator then + Err "The numerator must be less than or equal to the denominator" + + else if denominator == 0 then + Ok 0.0 + + else + Ok (toFloat numerator / toFloat denominator) + in + result diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/App.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/App.elm new file mode 100644 index 0000000..4c589a2 --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/App.elm @@ -0,0 +1,51 @@ +module Morphir.Example.App.App exposing (..) + +import Morphir.Example.App.Analytics exposing (calculateProbableReservations) +import Morphir.Example.App.BusinessTerms exposing (..) +import Morphir.Example.App.Forecast exposing (..) +import Morphir.Example.App.Rentals exposing (..) +import Morphir.Example.App.Winds exposing (..) + + +processRequest : Forecast -> CurrentInventory -> ExistingReservations -> PendingReturns -> RequestedQuantity -> AllowPartials -> ReservedQuantity -> CanceledQuantity -> Result Reason ReservedQuantity +processRequest forecast inventory reservations returns requestedQuantity allowPartials historicalReservations historicalCancelations = + let + windCategory : WindCategory + windCategory = + categorizeWindForForecast forecast + + probableReservations : ProbableReservations + probableReservations = + calculateProbableReservations historicalReservations historicalCancelations reservations + in + decide windCategory forecast.shortForcast inventory probableReservations returns requestedQuantity allowPartials + + +categorizeWindForForecast : Forecast -> WindCategory +categorizeWindForForecast forecast = + let + windCategory : WindCategory + windCategory = + categorizeWind forecast.windSpeed.max + in + windCategory + + +type Command + = Request RentalID RequestedQuantity AllowPartials + | Pickup RentalID + | Return RentalID + + +type Event + = RequestAccepted RentalID ReservedQuantity + | RequestRejected RentalID Reason + | PickupCompleted RentalID + | ReturnCompleted RentalID + + +type alias State = + { forecast : Forecast + , currentInventory : CurrentInventory + , events : List Event + } diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/BusinessTerms.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/BusinessTerms.elm new file mode 100644 index 0000000..ba8ee4b --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/BusinessTerms.elm @@ -0,0 +1,51 @@ +module Morphir.Example.App.BusinessTerms exposing (..) + + +type alias RentalID = + String + + +type alias CurrentInventory = + Int + + +type alias ExistingReservations = + Int + + +type alias PendingReturns = + Int + + +type alias RequestedQuantity = + Int + + +type alias ReservedQuantity = + Int + + +type alias ProbableReservations = + Int + + +type alias CanceledQuantity = + Int + + +type alias CancelationRatio = + Float + + +type alias Availability = + Int + + +type alias AllowPartials = + Bool + + +type ExpertiseLevel + = Novice + | Intermediate + | Expert diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Forecast.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Forecast.elm new file mode 100644 index 0000000..3ca48e0 --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Forecast.elm @@ -0,0 +1,36 @@ +module Morphir.Example.App.Forecast exposing (..) + + +type alias MPH = + Int + + +type WindDirection + = North + | South + | East + | West + + +type alias Celcius = + Int + + +type ForecastDetail + = Showers + | Thunderstorms + | Snow + | Fog + + +type alias ForecastPercent = + Float + + +type alias Forecast = + { temp : { low : Celcius, high : Celcius } + , windSpeed : { min : MPH, max : MPH } + , windDirection : WindDirection + , shortForcast : ForecastDetail + , forecastPercent : ForecastPercent + } diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Rentals.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Rentals.elm new file mode 100644 index 0000000..53ba012 --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Rentals.elm @@ -0,0 +1,43 @@ +module Morphir.Example.App.Rentals exposing (..) + +import Morphir.Example.App.Analytics exposing (..) +import Morphir.Example.App.BusinessTerms exposing (..) +import Morphir.Example.App.Forecast exposing (..) +import Morphir.Example.App.Winds exposing (..) + + +type Reason + = InsufficientAvailability + | ClosedDueToConditions + + +decide : WindCategory -> ForecastDetail -> CurrentInventory -> ProbableReservations -> PendingReturns -> RequestedQuantity -> AllowPartials -> Result Reason ReservedQuantity +decide windCategory forecastDetail inventory probableReservations returns requestedQuantity allowPartials = + let + isClosed : Bool + isClosed = + case ( windCategory, forecastDetail ) of + ( DangerousWinds, _ ) -> + True + + ( _, Thunderstorms ) -> + True + + _ -> + False + + availability : Availability + availability = + inventory - probableReservations + returns + in + if isClosed then + Err ClosedDueToConditions + + else if requestedQuantity <= availability then + Ok requestedQuantity + + else if allowPartials then + Ok availability + + else + Err InsufficientAvailability diff --git a/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Winds.elm b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Winds.elm new file mode 100644 index 0000000..a9330e3 --- /dev/null +++ b/tutorial/step_3_catching_bugs/src/Morphir/Example/App/Winds.elm @@ -0,0 +1,25 @@ +module Morphir.Example.App.Winds exposing (..) + +import Morphir.Example.App.Forecast exposing (..) + + +type WindCategory + = Calm + | Windy + | HighWinds + | DangerousWinds + + +categorizeWind : Int -> WindCategory +categorizeWind windSpeed = + if windSpeed < 10 then + Calm + + else if windSpeed < 20 then + HighWinds + + else if windSpeed < 30 then + Windy + + else + DangerousWinds diff --git a/tutorial/surfboard_rentals.png b/tutorial/surfboard_rentals.png new file mode 100644 index 0000000..f3c7d47 Binary files /dev/null and b/tutorial/surfboard_rentals.png differ