├── requirements.txt ├── datasets ├── scorecard_data.json ├── data_TEST1.json ├── scorecard_structure.json ├── data_EBA_PF.json ├── scorecard_TEST1.json └── scorecard_EBA_PF.json ├── static └── federated_models.png ├── docs ├── ScorecardCharacteristics.xlsx ├── ProjectFinanceScorecard.md └── SpecificationReference.md ├── test.py ├── basic_example.py ├── eba_example.py ├── eba_api_example.py ├── hierarchicalScorecard.py ├── .gitignore └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | networkx 2 | requests 3 | -------------------------------------------------------------------------------- /datasets/scorecard_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1" : 1, 3 | "1.2" : 2, 4 | "2" : 1 5 | } -------------------------------------------------------------------------------- /datasets/data_TEST1.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1" : 1, 3 | "1.2" : 2, 4 | "2.1" : 3, 5 | "2.2" : 4 6 | } -------------------------------------------------------------------------------- /static/federated_models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-risk/openRiskScore/HEAD/static/federated_models.png -------------------------------------------------------------------------------- /docs/ScorecardCharacteristics.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-risk/openRiskScore/HEAD/docs/ScorecardCharacteristics.xlsx -------------------------------------------------------------------------------- /datasets/scorecard_structure.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "0", 3 | "weight" : 1, 4 | "children": [ 5 | { 6 | "id": "1", 7 | "weight": 0.5, 8 | "children": [ 9 | { 10 | "id": "1.1", 11 | "weight": 0.5 12 | }, 13 | { 14 | "id": "1.2", 15 | "weight": 0.5 16 | } 17 | ] 18 | }, 19 | { 20 | "id": "2", 21 | "weight": 0.5 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /datasets/data_EBA_PF.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1" : 1, 3 | "1.2" : 1, 4 | "1.3" : 1, 5 | "1.4.1" : 1, 6 | "1.4.2" : 1, 7 | "1.4.3" : 1, 8 | "2.1" : 1, 9 | "2.2" : 1, 10 | "2.3" : 1, 11 | "2.4" : 1, 12 | "2.5" : 1, 13 | "2.6" : 1, 14 | "3.1" : 1, 15 | "3.2.1" : 1, 16 | "3.2.2" : 1, 17 | "3.2.3" : 1, 18 | "3.2.4" : 1, 19 | "3.2.5" : 1, 20 | "3.3.1" : 1, 21 | "3.3.2" : 1, 22 | "3.4.1" : 1, 23 | "3.4.2" : 1, 24 | "3.4.3" : 1, 25 | "3.5.1" : 1, 26 | "3.5.2" : 1, 27 | "4.1" : 1, 28 | "4.2" : 1, 29 | "4.3" : 1, 30 | "5.1" : 1, 31 | "5.2" : 1, 32 | "5.3" : 1, 33 | "5.4" : 1, 34 | "5.5" : 1 35 | } -------------------------------------------------------------------------------- /datasets/scorecard_TEST1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "0", 3 | "weight" : 1, 4 | "children": [ 5 | { 6 | "id": "1", 7 | "weight": 0.5, 8 | "children": [ 9 | { 10 | "id": "1.1", 11 | "weight": 0.5 12 | }, 13 | { 14 | "id": "1.2", 15 | "weight": 0.5 16 | } 17 | ] 18 | }, 19 | { 20 | "id": "2", 21 | "weight": 0.5, 22 | "children": [ 23 | { 24 | "id": "2.1", 25 | "weight": 0.5 26 | }, 27 | { 28 | "id": "2.2", 29 | "weight": 0.5 30 | } 31 | ] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | # (c) 2021 - 2024 Open Risk (https://www.openriskmanagement.com) 2 | # 3 | # openRiskScore is licensed under the Apache 2.0 license a copy of which is included 4 | # in the source distribution of openRiskScore. This is notwithstanding any licenses of 5 | # third-party software included in this distribution. You may not use this file except in 6 | # compliance with the License. 7 | # 8 | # Unless required by applicable law or agreed to in writing, software distributed under 9 | # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | # either express or implied. See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import json 14 | 15 | from hierarchicalScorecard import HierarchicalScorecard 16 | 17 | """ 18 | Test Example (TODO move to tests) 19 | """ 20 | 21 | # Load the scorecard structure from file 22 | scorecard = json.load(open("./datasets/scorecard_TEST1.json")) 23 | 24 | # Create a new scorecard 25 | PFScore = HierarchicalScorecard(scorecard) 26 | 27 | # Load the attribute values 28 | scorecard_data = json.load(open("./datasets/data_TEST1.json")) 29 | 30 | # calculate the score 31 | score = PFScore.score(scorecard_data) 32 | 33 | print(score) 34 | -------------------------------------------------------------------------------- /basic_example.py: -------------------------------------------------------------------------------- 1 | # (c) 2021 - 2024 Open Risk (https://www.openriskmanagement.com) 2 | # 3 | # openRiskScore is licensed under the Apache 2.0 license a copy of which is included 4 | # in the source distribution of openRiskScore. This is notwithstanding any licenses of 5 | # third-party software included in this distribution. You may not use this file except in 6 | # compliance with the License. 7 | # 8 | # Unless required by applicable law or agreed to in writing, software distributed under 9 | # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | # either express or implied. See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import json 14 | 15 | from hierarchicalScorecard import HierarchicalScorecard 16 | 17 | """ 18 | Basic Example. This illustrates the process using minimal structure / data for easy inspection 19 | """ 20 | 21 | # Load the scorecard structure from a json file that defines it 22 | scorecard = json.load(open("./datasets/scorecard_structure.json")) 23 | 24 | # Create a new scorecard object 25 | PFScore = HierarchicalScorecard(scorecard) 26 | 27 | # Load the attribute values from a json file that provides them 28 | scorecard_data = json.load(open("./datasets/scorecard_data.json")) 29 | 30 | # calculate the score 31 | score = PFScore.score(scorecard_data) 32 | 33 | print(score) 34 | -------------------------------------------------------------------------------- /eba_example.py: -------------------------------------------------------------------------------- 1 | # (c) 2021 - 2024 Open Risk (https://www.openriskmanagement.com) 2 | # 3 | # openRiskScore is licensed under the Apache 2.0 license a copy of which is included 4 | # in the source distribution of openRiskScore. This is notwithstanding any licenses of 5 | # third-party software included in this distribution. You may not use this file except in 6 | # compliance with the License. 7 | # 8 | # Unless required by applicable law or agreed to in writing, software distributed under 9 | # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | # either express or implied. See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import json 14 | 15 | from hierarchicalScorecard import HierarchicalScorecard 16 | 17 | """ 18 | A realistic Scorecard Example using a fully specified scorecard: 19 | The European Banking Authority's Project Finance Specialized Lending Specification 20 | 21 | """ 22 | 23 | # Load the scorecard structure from file 24 | scorecard = json.load(open("./datasets/scorecard_EBA_PF.json")) 25 | 26 | # Create a new project finance scorecard 27 | PFScore = HierarchicalScorecard(scorecard) 28 | 29 | # Load the attribute values 30 | scorecard_data = json.load(open("./datasets/data_EBA_PF.json")) 31 | 32 | # calculate the score 33 | score = PFScore.score(scorecard_data) 34 | 35 | print(score) 36 | -------------------------------------------------------------------------------- /docs/ProjectFinanceScorecard.md: -------------------------------------------------------------------------------- 1 | # Standardized Specialized Lending Scorecard for Project Finance 2 | 3 | ## Risk Factor Hierarchy 4 | The Project Finance Scorecard is a hierarchical scorecard. 5 | 6 | ### Top Level Risk Factor Groups 7 | Characteristics are grouped in a hierarchical tree structure with five top level **Risk Factor Groups**: 8 | 9 | * Financial Strength 10 | * Political and Legal Environment 11 | * Transaction Characteristics 12 | * Strength of Sponsor 13 | * Security Package 14 | 15 | ### Lower Level Decomposition of Risk Factor Groups 16 | Risk Factor Groups resolve in a number of **Risk Factors** 17 | 18 | For example the Financial Strength Risk Factor Group assesses the following: 19 | 20 | * Market Conditions 21 | * Financial Ratios 22 | * Stress Analysis 23 | * Financial Structure 24 | 25 | 26 | ## Score Calculation 27 | 28 | ### Partial Scores 29 | Each Risk Factor or Risk Sub-Factor Characteristic (as appropriate) is mapped into one of four possible Attributes as per the below schedule 30 | 31 | * Category 1: Strong 32 | * Category 2: Good 33 | * Category 3: Satisfactory 34 | * Category 4: Weak 35 | 36 | The numerical Partial Score of each attribute is the Category cardinality (i.e., 1, 2, 3, 4) 37 | 38 | ### Characteristic Weights 39 | Characteristics at all levels of the hierarchy are assigned weights of relative importance. These weight assignments constitute a model component that must be specified and justified (documented) separately. 40 | 41 | Relative weights must be between 5% and 60%. 42 | 43 | 44 | ### Overall Score 45 | The overall score is the weighted sum of partial scores. The overall score is rounded to the nearest cardinal number -------------------------------------------------------------------------------- /eba_api_example.py: -------------------------------------------------------------------------------- 1 | # (c) 2021 - 2024 Open Risk (https://www.openriskmanagement.com) 2 | # 3 | # openRiskScore is licensed under the Apache 2.0 license a copy of which is included 4 | # in the source distribution of openRiskScore. This is notwithstanding any licenses of 5 | # third-party software included in this distribution. You may not use this file except in 6 | # compliance with the License. 7 | # 8 | # Unless required by applicable law or agreed to in writing, software distributed under 9 | # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | # either express or implied. See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import json 14 | 15 | import requests 16 | 17 | from hierarchicalScorecard import HierarchicalScorecard 18 | 19 | """ 20 | A realistic Scorecard Example using a fully specified scorecard: 21 | The European Banking Authority's Project Finance Specialized Lending Specification 22 | 23 | This example fetches the attribute data from a remote server 24 | 25 | """ 26 | 27 | # Load the scorecard structure from file 28 | scorecard = json.load(open("./datasets/scorecard_EBA_PF.json")) 29 | 30 | # Create a new project finance scorecard 31 | PFScore = HierarchicalScorecard(scorecard) 32 | 33 | # Load the attribute values from a remote end point 34 | # select the endpoint 35 | server_url = 'http://localhost:8000/api/portfolio_data/scorecards/' 36 | scorecard_id = '1/' 37 | # fetch the data 38 | response = requests.get(server_url + scorecard_id) 39 | # TODO validate conformance 40 | scorecard_data = response.json()['scorecard_data'] 41 | 42 | # calculate the score 43 | score = PFScore.score(scorecard_data) 44 | 45 | print(score) 46 | -------------------------------------------------------------------------------- /hierarchicalScorecard.py: -------------------------------------------------------------------------------- 1 | # (c) 2021 - 2024 Open Risk (https://www.openriskmanagement.com) 2 | # 3 | # openRiskScore is licensed under the Apache 2.0 license a copy of which is included 4 | # in the source distribution of openRiskScore. This is notwithstanding any licenses of 5 | # third-party software included in this distribution. You may not use this file except in 6 | # compliance with the License. 7 | # 8 | # Unless required by applicable law or agreed to in writing, software distributed under 9 | # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 10 | # either express or implied. See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | 14 | import networkx as nx 15 | from networkx.readwrite import json_graph 16 | 17 | 18 | class HierarchicalScorecard: 19 | 20 | def __init__(self, structure): 21 | self.structure = structure 22 | self.G = json_graph.tree_graph(structure) 23 | 24 | def score(self, data): 25 | """ 26 | 27 | The weight of each node is used to construct the weighted average partial score that is being aggregated to 28 | the node parent (summing over all sibling nodes) 29 | 30 | """ 31 | 32 | leaf_nodes = [x for x in self.G.nodes() if self.G.out_degree(x) == 0] 33 | path_scores = [] 34 | for x in leaf_nodes: 35 | 36 | # construct path from root to leaf node of scorecard 37 | path = nx.shortest_path(self.G, source='0', target=x) 38 | scores = [] 39 | # get the partial score of the leaf node 40 | i = len(path) - 1 41 | weight = self.G.nodes[x]['weight'] # this weight should be unity 42 | attribute = data[x] 43 | partial_score = weight * attribute 44 | i -= 1 45 | scores.append(partial_score) 46 | # iterate over path 47 | while i >= 0: 48 | node = path[i] 49 | weight = self.G.nodes[node]['weight'] 50 | partial_score = weight * scores[0] 51 | i -= 1 52 | scores.insert(0, partial_score) 53 | path_scores.append(scores) 54 | 55 | total_score = 0 56 | for scores in path_scores: 57 | total_score += scores[0] 58 | return round(total_score) 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | .python-version 87 | 88 | # pipenv 89 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 90 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 91 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 92 | # install all needed dependencies. 93 | #Pipfile.lock 94 | 95 | # celery beat schedule file 96 | celerybeat-schedule 97 | 98 | # SageMath parsed files 99 | *.sage.py 100 | 101 | # Environments 102 | .env 103 | .venv 104 | env/ 105 | venv/ 106 | ENV/ 107 | env.bak/ 108 | venv.bak/ 109 | 110 | # Spyder project settings 111 | .spyderproject 112 | .spyproject 113 | 114 | # Rope project settings 115 | .ropeproject 116 | 117 | # mkdocs documentation 118 | /site 119 | 120 | # mypy 121 | .mypy_cache/ 122 | .dmypy.json 123 | dmypy.json 124 | 125 | # Pyre type checker 126 | .pyre/ 127 | 128 | -------------------------------------------------------------------------------- /docs/SpecificationReference.md: -------------------------------------------------------------------------------- 1 | ## Prescribed Methodology 2 | This document excerpts the relevant segments of the EBA reference that defines the standardized scorecard for project finance exposures 3 | 4 | ### Article 2: Applicable assessment criteria for different classes of specialised lending exposures 5 | 6 | (a) where the purpose of the specialised lending exposure is to finance the development or acquisition of large, complex and expensive installations, in particular, power plants, chemical processing plants, mines, transportation infrastructure, environment, and telecommunications infrastructure, so that the income generated by the assets is the money generated by the contracts for the facility’s output obtained from one or several third parties (‘project finance exposures’), institutions shall apply the assessment criteria referred to in Annex I (see included spreadsheet ScorecardCharacteristics.xlsx) 7 | 8 | ### Article 3: Documentation 9 | 10 | #### 1. 11 | Institutions shall document for each type of specialised lending exposure the assignment of weights to each factor and the justification for these assignments in accordance with Article 1(3)(d). 12 | 13 | 14 | ### Article 1: Process for taking into account the factors affecting risk weights 15 | 16 | #### 2 (b) 17 | Assess the specialised lending exposure with reference to each factor, against the assessment criteria provided for each of the sub-factors, some of which are, in turn, further specified in sub-factor components, contained in the relevant Annex to this Regulation 18 | 19 | #### 3 (a) 20 | Where one or several of the sub-factors are further specified in sub-factor components, determine the cardinal numbers of the categories to which the specialised lending exposure is assigned for each sub-factor component on the basis of the assessment referred to in paragraph 2(b), and combine the assignments of these sub-factor components on the basis of their relative importance to determine the cardinal numbers of the categories of these sub-factors 21 | 22 | #### 3 (b) 23 | Where none of the sub-factors are further specified in sub-factor components, determine the cardinal numbers of the categories to which the specialised lending exposure is assigned for each sub-factor on the basis of the assessment referred to in paragraph 2(b) 24 | 25 | #### 3 (c) 26 | Combine the assignments of the sub-factors on the basis of their relative importance to determine the cardinal number of the categories of the respective factors 27 | 28 | #### 3 (d) 29 | Specify the weight in percentage that they assign to each factor, on the basis of the relative importance of each factor under the condition that such a weight is not lower than 5% and not higher than 60% 30 | 31 | #### 3 (e) 32 | Determine the weighted average of the cardinal numbers of the categories under which they have classified the specialised lending exposure for all factors; 33 | 34 | #### 3 (f) 35 | Where the weighted average is a decimal number, round that number to the nearest cardinal number 36 | 37 | 38 | ## References 39 | 40 | * EBA/RTS/2016/02 (13 June 2016). FINAL draft Regulatory Technical Standards on Assigning Risk Weights to Specialised Lending Exposures under Article 153(9) of Regulation (EU) No 575/2013 (Capital Requirements Regulation – CRR) 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openRiskScore 2 | 3 | **openRiskScore** A Python framework for risk scoring in both classic and federated/decentralized contexts 4 | 5 | ![image](static/federated_models.png) 6 | 7 | ## Summary Information 8 | 9 | * Author: [Open Risk](http://www.openriskmanagement.com) 10 | * License: Apache 2.0 11 | * Code Documentation: [Read The Docs](Upcoming) 12 | * Mathematical Documentation: [Open Risk Manual](https://www.openriskmanual.org/wiki/Category:SME_Credit_Risk) 13 | * Development website: [Github](https://github.com/open-risk/openRiskScore) 14 | * Discussions: [Open Risk Commons](https://www.openriskcommons.org/c/open-source/openriskscore/16) 15 | 16 | **NB: openRiskScore is still in active development. The alpha release will be available here** 17 | 18 | ## Introduction 19 | 20 | openRiskScore aims to support the development of both expert based and statistical [risk scoring](https://www.openriskmanual.org/wiki/Risk_Score) and risk rating models. 21 | 22 | The library aims to wrap popular machine learning frameworks as algorithmic backends and focuses on supporting high quality risk model development and maintenance. 23 | 24 | Two important use cases for openRiskScore are [credit risk scoring](https://www.openriskmanual.org/wiki/Category:Credit_Scoring_Models), and sustainability (ESG) ratings and scores. It is envisaged that scoring activities can be either pursued by a standalone entity (operating on its own data) or in federation (independent entities sharing some data sets using federated learning principles, algorithms and tools). 25 | 26 | ### Standalone Mode 27 | In *standalone mode* openRiskScore emulates a classic use case where, e.g., a financial institution or other credit provider aims to develop a risk scoring system on the basis of data it has in its possession. Use cases for the standalone mode are both as intended (standalone) scoring system and as a validation framework for federated applications. 28 | 29 | ### Federated Mode 30 | The federated mode essentially facilitates the development of a *generic* (pooled) scorecard that applies to a wide population (which is assumed homogeneous) 31 | 32 | ## Documentation 33 | 34 | * [Project Finance Scorecard](docs/ProjectFinanceScorecard.md). Description of the Standardized Specialized Lending Scorecard for Project Finance 35 | * [Scorecard Characteristics](docs/ScorecardCharacteristics.xlsx). Documentation of the definitions of the scorecard 36 | * [Specification Reference](docs/SpecificationReference.md). This document excerpts the relevant segments of the EBA official reference that defines the standardized credit scorecard for project finance exposures 37 | 38 | ## Further Documentation and Reading 39 | 40 | ### Credit Scoring 41 | * [How to Build a Credit Scorecard](https://www.openriskmanual.org/wiki/How_to_Build_a_Credit_Scorecard) 42 | * [How to Build an SME Credit Scorecard](https://www.openriskmanual.org/wiki/How_to_Build_an_SME_Credit_Scorecard) 43 | * [Credit Scoring with Python](https://www.openriskmanual.org/wiki/Credit_Scoring_with_Python) 44 | 45 | ### ESG Scoring 46 | * [List of ESG Factos](https://www.openriskmanual.org/wiki/List_of_ESG_Factors) 47 | 48 | ### Semantic Documentation of Risk Models 49 | * [Risk Model Ontology](https://www.openriskmanual.org/wiki/Risk_Model_Ontology) 50 | * [Embedding PMML in DOAM](https://www.openriskmanagement.com/towards-semantic-description-of-machine-learning-models/) 51 | 52 | ## Learning Modules at the Open Risk Academy 53 | * [Exploratory Risk Data Analysis using Pandas, Seaborn and Statsmodels](https://www.openriskacademy.com/course/view.php?id=48) 54 | * [Analysis of Credit Migration using Python TransitionMatrix](https://www.openriskacademy.com/course/view.php?id=38) 55 | 56 | ## White Papers on Federated Risk Analysis 57 | 58 | * [White Paper: Federated Credit Systems, Part I: Unbundling The Credit Provision Business Model](https://www.openriskmanagement.com/white_paper_federated_credit_part_i_systems_unbundling_the_credit_provision_business_model/) 59 | * [White Paper: Federated Credit Systems, Part II: Techniques for Federated Data Analysis](https://www.openriskmanagement.com/white_paper_federated_credit_systems_part_ii_techniques_for_federated_data_analysis/) -------------------------------------------------------------------------------- /datasets/scorecard_EBA_PF.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "0", 3 | "weight": 1, 4 | "children": [ 5 | { 6 | "id": "1", 7 | "weight": 0.2, 8 | "children": [ 9 | { 10 | "id": "1.1", 11 | "weight": 0.25 12 | }, 13 | { 14 | "id": "1.2", 15 | "weight": 0.25 16 | }, 17 | { 18 | "id": "1.3", 19 | "weight": 0.25 20 | }, 21 | { 22 | "id": "1.4", 23 | "weight": 0.25, 24 | "children": [ 25 | { 26 | "id": "1.4.1", 27 | "weight": 0.3333 28 | }, 29 | { 30 | "id": "1.4.2", 31 | "weight": 0.3333 32 | }, 33 | { 34 | "id": "1.4.3", 35 | "weight": 0.3333 36 | } 37 | ] 38 | } 39 | ] 40 | }, 41 | { 42 | "id": "2", 43 | "weight": 0.2, 44 | "children": [ 45 | { 46 | "id": "2.1", 47 | "weight": 0.1666 48 | }, 49 | { 50 | "id": "2.2", 51 | "weight": 0.1666 52 | }, 53 | { 54 | "id": "2.3", 55 | "weight": 0.1666 56 | }, 57 | { 58 | "id": "2.4", 59 | "weight": 0.1666 60 | }, 61 | { 62 | "id": "2.5", 63 | "weight": 0.1666 64 | }, 65 | { 66 | "id": "2.6", 67 | "weight": 0.1666 68 | } 69 | ] 70 | }, 71 | { 72 | "id": "3", 73 | "weight": 0.2, 74 | "children": [ 75 | { 76 | "id": "3.1", 77 | "weight": 0.2 78 | }, 79 | { 80 | "id": "3.2", 81 | "weight": 0.2, 82 | "children": [ 83 | { 84 | "id": "3.2.1", 85 | "weight": 0.2 86 | }, 87 | { 88 | "id": "3.2.2", 89 | "weight": 0.2 90 | }, 91 | { 92 | "id": "3.2.3", 93 | "weight": 0.2 94 | }, 95 | { 96 | "id": "3.2.4", 97 | "weight": 0.2 98 | }, 99 | { 100 | "id": "3.2.5", 101 | "weight": 0.2 102 | } 103 | ] 104 | }, 105 | { 106 | "id": "3.3", 107 | "weight": 0.2, 108 | "children": [ 109 | { 110 | "id": "3.3.1", 111 | "weight": 0.5 112 | }, 113 | { 114 | "id": "3.3.2", 115 | "weight": 0.5 116 | } 117 | ] 118 | }, 119 | { 120 | "id": "3.4", 121 | "weight": 0.2, 122 | "children": [ 123 | { 124 | "id": "3.4.1", 125 | "weight": 0.3333 126 | }, 127 | { 128 | "id": "3.4.2", 129 | "weight": 0.3333 130 | }, 131 | { 132 | "id": "3.4.3", 133 | "weight": 0.3333 134 | } 135 | ] 136 | }, 137 | { 138 | "id": "3.5", 139 | "weight": 0.2, 140 | "children": [ 141 | { 142 | "id": "3.5.1", 143 | "weight": 0.5 144 | }, 145 | { 146 | "id": "3.5.2", 147 | "weight": 0.5 148 | } 149 | ] 150 | } 151 | ] 152 | }, 153 | { 154 | "id": "4", 155 | "weight": 0.2, 156 | "children": [ 157 | { 158 | "id": "4.1", 159 | "weight": 0.3333 160 | }, 161 | { 162 | "id": "4.2", 163 | "weight": 0.3333 164 | }, 165 | { 166 | "id": "4.3", 167 | "weight": 0.3333 168 | } 169 | ] 170 | }, 171 | { 172 | "id": "5", 173 | "weight": 0.2, 174 | "children": [ 175 | { 176 | "id": "5.1", 177 | "weight": 0.2 178 | }, 179 | { 180 | "id": "5.2", 181 | "weight": 0.2 182 | }, 183 | { 184 | "id": "5.3", 185 | "weight": 0.2 186 | }, 187 | { 188 | "id": "5.4", 189 | "weight": 0.2 190 | }, 191 | { 192 | "id": "5.5", 193 | "weight": 0.2 194 | } 195 | ] 196 | } 197 | ] 198 | } --------------------------------------------------------------------------------