diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9a5f43d..e1c0137 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,10 @@ ChangeLog PLEASE NOTE THAT THE API IS STILL VERY UNSTABLE AS MORE USE CASES / FEATURES ARE ADDED REGULARLY +v0.3.2 (XX-XX-2022) +------------------- +* release on PyPI + v0.3.1 (23-03-2020) ------------------- * Improved the documentation of the vasicek module @@ -16,17 +20,14 @@ v0.2.1 (04-04-2019) ------------------- * Including credit metrics style variance calculation - v0.2.0 (29-03-2019) ------------------- * Refactoring to include threshold model functionality (formerly with transitionMatrix library) - v0.1.1 (11-07-2017) ------------------- * Training: Notebook examples - v0.1.0 (16-04-2017) ------------------- * First public release of the package \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 07d1d9f..0000000 --- a/TODO.md +++ /dev/null @@ -1,21 +0,0 @@ -## Todo List - -### Provide additional functions for default rate distributions - -* Complete the universe of analytic solutions for Gaussian models - * Include some interesting special cases (e.g., large pool + single exposure) - * Include some tractable inhomogeneous problems -* Calculate more statistical moments (e.g., skew, kurtosis) -* Expand to non-Gaussian distributions - -### Expand to loss distributions that include recovery risk - - -### Make a more robust implementation - -### Introduce testing framework - -The functions should ultimately be coded to a high standard of robustness: -* input validation -* exception handling -* controlled accuracy \ No newline at end of file diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 0000000..18fd17c --- /dev/null +++ b/_static/custom.css @@ -0,0 +1,14 @@ +/* override table width restrictions */ +@media screen and (min-width: 767px) { + + .wy-table-responsive table td { + /* !important prevents the common CSS stylesheets from + overriding this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + + .wy-table-responsive { + overflow: visible !important; + } + +} \ No newline at end of file diff --git a/description.rst b/description.rst deleted file mode 100644 index ae11b45..0000000 --- a/description.rst +++ /dev/null @@ -1,160 +0,0 @@ -portfolioAnalytics -========================= - -portfolioAnalytics is a Python powered library for the calculation of semi-analytic approximations to portfolio credit -models - -* Author: `Open Risk `_ -* License: Apache 2.0 -* Code Documentation: `Read The Docs `_ -* Mathematical Documentation: `Open Risk Manual `_ -* Training: `Open Risk Academy `_ -* Development Website: `Github `_ - - -Functionality -------------- - -You can use portfolioAnalytics to create semi-analytic loss distributions for a variety of stylized credit portfolios -The library provides semi-analytical functions useful for testing the accuracy of credit portfolio simulation models -The basic formulas are reasonably simple and well known: They underpin the calculation of RWA (risk weighted assets), -and in turn required capital, thus ensuring stability for the entire banking systems worldwide - -You can also use the library to estimate transition thresholds for stochastic processes - - -**NB: portfolioAnalytics is still in active development. If you encounter issues please raise them in our -github repository** - - -Vasicek Portfolio Models Library ----------------------------------------------- - -Dependencies: scipy, sympy - -Portfolio Model Examples -------------------------- - -Check the jupyter notebooks and python scripts - -Current Functions ------------------ - -* vasicek_base -* vasicek_base_el -* vasicek_base_ul -* vasicek_lim -* vasicek_lim_el -* vasicek_lim_ul -* vasicek_lim_q - -The Vasicek Base family produces finite pool loss probabilities and measures (EL, UL) - -The Vasicek Lim family produces asymptotic pool loss probabities and measures (EL, UL, Quantile) - -Limitations -------------- -The portfolioAnalytics library provides a range of powerful modelling functionalities that are -are of relevance in real credit portfolio management activities. Yet achieving the tractability -and usability of a semi-analytic calculation suite is not without some tradeoffs. Several -simplifications are made (extensively documented in the Mathematical Documentation). Those -simplifications imply that when using the portfolioAnalytics models to assess the -risk in actual portfolios it is important to assess - - -Installation -======================= - -You can install and use the portfolioAnalytics package in any system that supports the `Scipy ecosystem of tools `_ - -Dependencies ------------------ - -- portfolioAnalytics requires Python 3 -- the thresholds module depends on the Open Risk transitionMatrix and correlationMatrix libraries -- It depends on numerical and data processing Python libraries (Numpy, Scipy, Pandas) -- The Visualization API depends on Matplotlib -- The precise dependencies are listed in the requirements.txt file. -- portfolioAnalytics may work with earlier versions of these packages but this has not been tested - - -From PyPi -------------- - -.. code:: bash - - pip3 install pandas - pip3 install matplotlib - pip3 install portfolioAnalytics - -From sources -------------- - -Download the sources to your preferred directory: - -.. code:: bash - - git clone https://github.com/open-risk/portfolioAnalytics - - -Using virtualenv ----------------- - -It is advisable to install the package in a virtualenv so as not to interfere with your system's python distribution - -.. code:: bash - - virtualenv -p python3 tm_test - source tm_test/bin/activate - -If you do not have pandas already installed make sure you install it first (will also install numpy) - -.. code:: bash - - pip3 install pandas - pip3 install matplotlib - pip3 install -r requirements.txt - -Finally issue the install command and you are ready to go! - -.. code:: bash - - python3 setup.py install - -File structure ------------------ -The distribution has the following structure: - -| portfolioAnalytics The library source code -| estimators Estimator methods (TODO) -| utils Helper classes and methods -| thresholds Algorithms for calibrating AR(n) process thresholds to input transition rates -| vasicek Collection of portfolio analytic solutions -| creditmetrics Analytic calculation of variance for credit metrics style models -| examples Usage examples -| datasets Contains a variety of datasets useful for getting started with portfolioAnalytics -| tests Testing suite - -Testing ----------------------- - -It is a good idea to run the test-suite. Before you get started: - -- Adjust the source directory path in portfolioAnalytics/__init__ and then issue the following in at the root of the distribution -- Unzip the data files in the datasets directory - -.. code:: bash - - python3 test.py - -Getting Started -======================= - -Check the Usage pages in this documentation - -Look at the examples directory for a variety of typical workflows. - -For more in depth study, the Open Risk Academy has courses elaborating on the use of the library - -- Analysis of Credit Migration using Python portfolioAnalytics: https://www.openriskacademy.com/course/view.php?id=38 - diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css new file mode 100644 index 0000000..18fd17c --- /dev/null +++ b/docs/source/_static/custom.css @@ -0,0 +1,14 @@ +/* override table width restrictions */ +@media screen and (min-width: 767px) { + + .wy-table-responsive table td { + /* !important prevents the common CSS stylesheets from + overriding this as on RTD they are loaded after this stylesheet */ + white-space: normal !important; + } + + .wy-table-responsive { + overflow: visible !important; + } + +} \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 6fb4830..1f5fc90 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -19,6 +19,7 @@ import sys import os import matplotlib + matplotlib.use('agg') sys.path.insert(0, os.path.abspath('../../')) @@ -39,7 +40,6 @@ # The full version, including alpha/beta/rc tags release = __version__ - # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -54,6 +54,7 @@ 'sphinx.ext.autosummary', 'sphinx.ext.coverage', 'sphinx.ext.imgmath', + 'sphinx.ext.viewcode', 'sphinx.ext.autosectionlabel', 'sphinx.ext.githubpages', 'sphinx.ext.todo', @@ -87,7 +88,6 @@ # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' - # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -102,13 +102,13 @@ # documentation. # # html_theme_options = {} + html_theme_options = { - 'canonical_url': '', 'analytics_id': '', - 'logo_only': False, + 'logo_only': True, 'display_version': True, 'prev_next_buttons_location': 'bottom', - 'style_external_links': False, + 'style_external_links': True, # Toc options 'collapse_navigation': True, 'sticky_navigation': True, @@ -117,7 +117,6 @@ 'titles_only': False } - # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". @@ -133,13 +132,13 @@ # # html_sidebars = {} +html_css_files = ['custom.css'] # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = 'portfolioAnalyticsdoc' - # -- Options for LaTeX output ------------------------------------------------ latex_elements = { @@ -168,7 +167,6 @@ 'Open Risk', 'manual'), ] - # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples @@ -178,7 +176,6 @@ [author], 1) ] - # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples @@ -190,11 +187,10 @@ 'Miscellaneous'), ] - # -- Extension configuration ------------------------------------------------- extensions.append('sphinx.ext.todo') -todo_include_todos=True +todo_include_todos = True # extensions.append('sphinx_automodapi.automodapi') # numpydoc_show_class_members = False diff --git a/docs/source/description.rst b/docs/source/description.rst index 508a446..8460adf 100644 --- a/docs/source/description.rst +++ b/docs/source/description.rst @@ -1 +1,153 @@ -.. include:: ../../description.rst \ No newline at end of file +portfolioAnalytics +========================= + +portfolioAnalytics is a Python powered library for the calculation of semi-analytic approximations to portfolio credit models + +* Author: `Open Risk `_ +* License: Apache 2.0 +* Code Documentation: `Read The Docs `_ +* Mathematical Documentation: `Open Risk Manual `_ +* Training: `Open Risk Academy `_ +* Development Website: `Github `_ + + +Functionality +------------- + +You can use portfolioAnalytics to create semi-analytic loss distributions for a variety of stylized credit portfolios. The library provides semi-analytical functions useful for testing the accuracy of credit portfolio simulation models. The basic formulas are reasonably simple and well known: They underpin the calculation of RWA (risk weighted assets), and in turn required capital, thus ensuring stability for the entire banking systems worldwide. You can also use the library to estimate transition thresholds for stochastic processes + + +**NB: portfolioAnalytics is still in active development. If you encounter issues please raise them in our github repository** + + +Vasicek Portfolio Models Library +---------------------------------------------- + +Dependencies: scipy, sympy + +Portfolio Model Examples +------------------------- + +Check the jupyter notebooks and python scripts + +Current Functions +----------------- + +* vasicek_base +* vasicek_base_el +* vasicek_base_ul +* vasicek_lim +* vasicek_lim_el +* vasicek_lim_ul +* vasicek_lim_q + +The Vasicek Base family produces finite pool loss probabilities and measures (EL, UL) + +The Vasicek Lim family produces asymptotic pool loss probabities and measures (EL, UL, Quantile) + +Limitations +------------- +The portfolioAnalytics library provides a range of powerful modelling functionalities that are +are of relevance in real credit portfolio management activities. Yet achieving the tractability +and usability of a semi-analytic calculation suite is not without some tradeoffs. Several +simplifications are made (extensively documented in the Mathematical Documentation). Those +simplifications imply that when using the portfolioAnalytics models to assess the +risk in actual portfolios it is important to assess + + +Installation +======================= + +You can install and use the portfolioAnalytics package in any system that supports the `Scipy ecosystem of tools `_ + +Dependencies +----------------- + +- portfolioAnalytics requires Python 3 +- the thresholds module depends on the Open Risk transitionMatrix and correlationMatrix libraries +- It depends on numerical and data processing Python libraries (Numpy, Scipy, Pandas) +- The Visualization API depends on Matplotlib +- The precise dependencies are listed in the requirements.txt file. +- portfolioAnalytics may work with earlier versions of these packages but this has not been tested + + +From PyPi +------------- + +.. code:: bash + + pip3 install pandas + pip3 install matplotlib + pip3 install portfolioAnalytics + +From sources +------------- + +Download the sources to your preferred directory: + +.. code:: bash + + git clone https://github.com/open-risk/portfolioAnalytics + + +Using virtualenv +---------------- + +It is advisable to install the package in a virtualenv so as not to interfere with your system's python distribution + +.. code:: bash + + virtualenv -p python3 tm_test + source tm_test/bin/activate + +If you do not have pandas already installed make sure you install it first (will also install numpy) + +.. code:: bash + + pip3 install pandas + pip3 install matplotlib + pip3 install -r requirements.txt + +Finally issue the install command and you are ready to go! + +.. code:: bash + + python3 setup.py install + +File structure +----------------- +The distribution has the following structure: + +| portfolioAnalytics The library source code +| estimators Estimator methods (TODO) +| utils Helper classes and methods +| thresholds Algorithms for calibrating AR(n) process thresholds to input transition rates +| vasicek Collection of portfolio analytic solutions +| creditmetrics Analytic calculation of variance for credit metrics style models +| examples Usage examples +| datasets Contains a variety of datasets useful for getting started with portfolioAnalytics +| tests Testing suite + +Testing Framework +---------------------- + +It is a good idea to run the test-suite. Before you get started: + +- Adjust the source directory path in portfolioAnalytics/__init__ and then issue the following in at the root of the distribution +- Unzip the data files in the datasets directory + +.. code:: bash + + python3 test.py + +Getting Started +======================= + +Check the Examples pages in this documentation + +Look at the examples directory for a variety of typical workflows. + +For more in depth study, the Open Risk Academy has courses elaborating on the use of the library + +- Analysis of `Credit Migration using Python portfolioAnalytics: `_ + diff --git a/docs/source/examples.rst b/docs/source/examples.rst index ca390ee..3663a0e 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -1,7 +1,7 @@ Examples ======== -The examples directory includes python scripts and jupyter notebooks to help you get started +The portfolioAnalytics packages offers quite a lot of functionality. Here we break down some of the main workflows for those getting started. The examples directory includes python scripts and jupyter notebooks to help you get started - Generating loss distributions analytically - Estimating thresholds given a multi-period transition matrix set @@ -10,8 +10,7 @@ The examples directory includes python scripts and jupyter notebooks to help you Python Scripts ------------------------------------------- -Located in examples/python (For testing purposes all examples can be run using the run_examples.py script -located in the root directory) +Located in examples/python (For testing purposes all examples can be run using the run_examples.py script located in the root directory) .. automodule:: examples.python :noindex: @@ -30,6 +29,11 @@ located in the root directory) Jupyter Notebooks ------------------------------------------- -* Adjust_NotRated_State.ipynb -* Matrix_Operations.ipynb +* Examples.ipynb * Portfolio_Examples.ipynb + + +Open Risk Academy Scripts +------------------------------------------- + +TODO \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index f56fcb2..1ccdeeb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,20 +1,22 @@ -.. portfolioAnalytics documentation master file, created by - sphinx-quickstart on Thu May 3 17:51:16 2018. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +.. portfolioAnalytics documentation master file portfolioAnalytics Documentation ================================ +.. image:: ../../examples/Thresholds.png + +**NB: portfolioAnalytics is still in alpha release / active development. If you encounter issues please raise them in our github repository** + .. toctree:: - :maxdepth: 4 + :maxdepth: 2 :caption: Contents: description - usage + examples modules - changelog + testing roadmap + changelog Indices and tables ================== diff --git a/docs/source/modules.rst b/docs/source/modules.rst index 5195a9f..5d04dc8 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -1,7 +1,11 @@ API ============================== +The portfolioAnalytics package structure and API. + +.. warning:: The library is still being expanded / refactored. Significant structure and API changes are likely. + .. toctree:: - :maxdepth: 4 + :maxdepth: 2 portfolioAnalytics \ No newline at end of file diff --git a/docs/source/portfolioAnalytics.creditmetrics.rst b/docs/source/portfolioAnalytics.creditmetrics.rst index 2b71e45..30240ae 100644 --- a/docs/source/portfolioAnalytics.creditmetrics.rst +++ b/docs/source/portfolioAnalytics.creditmetrics.rst @@ -2,40 +2,42 @@ portfolioAnalytics.creditmetrics subpackage ================================================== -CreditMetrics Portfolio Models Library ----------------------------------------------- - -A python library that provides semi-analytical functions useful in the context credit portfolio models +CreditMetrics Style Functions +---------------------------------- +Inverse normal function +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. automodule:: portfolioAnalytics.creditmetrics +.. automodule:: portfolioAnalytics.creditmetrics.Ninv :members: :undoc-members: :show-inheritance: -Functions ----------- - -Inverse normal function -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. autofunction:: Ninv - Variance calculation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: variance +.. automodule:: portfolioAnalytics.creditmetrics.variance + :members: + :undoc-members: + :show-inheritance: + Expected Loss Calculation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: creditmetrics_el +.. automodule:: portfolioAnalytics.creditmetrics.creditmetrics_el + :members: + :undoc-members: + :show-inheritance: -Loss Volatility +Loss Volatility (Unexpected Loss) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: creditmetrics_ul +.. automodule:: portfolioAnalytics.creditmetrics.creditmetrics_ul + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/portfolioAnalytics.rst b/docs/source/portfolioAnalytics.rst index 57d3176..0197734 100644 --- a/docs/source/portfolioAnalytics.rst +++ b/docs/source/portfolioAnalytics.rst @@ -1,12 +1,15 @@ portfolioAnalytics Package ============================ -.. automodule:: portfolioAnalytics +The core module + +.. automodule:: portfolioAnalytics.model :noindex: -Subpackages ------------ + +portfolioAnalytics Subpackages +============================== .. toctree:: diff --git a/docs/source/portfolioAnalytics.thresholds.rst b/docs/source/portfolioAnalytics.thresholds.rst index 53c2302..b066e42 100644 --- a/docs/source/portfolioAnalytics.thresholds.rst +++ b/docs/source/portfolioAnalytics.thresholds.rst @@ -1,6 +1,8 @@ portfolioAnalytics.thresholds subpackage ========================================= +.. warning:: The Thresholds subpackage has a dependency on transitionMatrix that need to be installed in the same python environment + portfolioAnalytics.thresholds.model module ---------------------------------------------- @@ -13,13 +15,25 @@ portfolioAnalytics.thresholds.model module ThresholdSet ~~~~~~~~~~~~~~~~~~~ -.. autoclass:: ThresholdSet - :members: - - .. automethod:: __init__ +.. automodule:: portfolioAnalytics.thresholds.ThresholdSet + :members: + :undoc-members: + :show-inheritance: Integrate G ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: integrate_g \ No newline at end of file +.. automodule:: portfolioAnalytics.thresholds.integrate_g + :members: + :undoc-members: + :show-inheritance: + + +Integrate F +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: portfolioAnalytics.thresholds.integrate_f + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/portfolioAnalytics.vasicek.rst b/docs/source/portfolioAnalytics.vasicek.rst index 7fd398b..1d06733 100644 --- a/docs/source/portfolioAnalytics.vasicek.rst +++ b/docs/source/portfolioAnalytics.vasicek.rst @@ -4,45 +4,75 @@ portfolioAnalytics.vasicek subpackage Functions ---------- -.. automodule:: portfolioAnalytics.vasicek - :members: - :undoc-members: - :show-inheritance: +The Vasicek subpackage implements currently the following: + +* vasicek_base implements a finite homogeneous pool +* vasicek_base_el implements the expected loss for the vasicek_base case +* vasicek_base_ul implements the standard deviation for the vasicek_base case +* vasicek_lim implements the limiting case for large N +* vasicek_lim_el implements the expected loss for the vasicek_lim case +* vasicek_lim_ul implements the standard deviation for the vasicek_lim case +* vasicek_lim_q implements the quantile for the vasicek_lim case + Vasicek Base Distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_base +.. automodule:: portfolioAnalytics.vasicek.vasicek_base + :members: + :undoc-members: + :show-inheritance: + Vasicek Base Distribution Expected Loss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_base_el +.. automodule:: portfolioAnalytics.vasicek.vasicek_base_el + :members: + :undoc-members: + :show-inheritance: + Vasicek Base Distribution Unexpected Loss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_base_ul +.. automodule:: portfolioAnalytics.vasicek.vasicek_base_ul + :members: + :undoc-members: + :show-inheritance: Vasicek Limit Distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_lim +.. automodule:: portfolioAnalytics.vasicek.vasicek_lim + :members: + :undoc-members: + :show-inheritance: Vasicek Limit Distribution Expected Loss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_lim_el +.. automodule:: portfolioAnalytics.vasicek.vasicek_lim_el + :members: + :undoc-members: + :show-inheritance: + Vasicek Limit Distribution Unexpected Loss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_lim_ul +.. automodule:: portfolioAnalytics.vasicek.vasicek_lim_ul + :members: + :undoc-members: + :show-inheritance: Vasicek Limit Distribution Quantile Loss ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. autofunction:: vasicek_lim_q +.. automodule:: portfolioAnalytics.vasicek.vasicek_lim_q + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/roadmap.rst b/docs/source/roadmap.rst index 5a83cdb..592142d 100644 --- a/docs/source/roadmap.rst +++ b/docs/source/roadmap.rst @@ -2,7 +2,8 @@ Roadmap ========================= portfolioAnalytics aims to become the most intuitive and versatile tool to analyse discrete transition data. -This roadmap lays out upcoming steps in this journey. +This roadmap lays out the general upcoming steps in this journey. The Todo List has some more concrete tasks that may have been already raised as issue son github. + 0.3.X -------------------------- @@ -12,4 +13,35 @@ The 0.3.X family of releases will focus on rounding out a number of functionalit - Further documenting the existing functionality - Further tests, of both code and algorithms + +0.4.X +------------------------- + +Provide additional functions for default rate distributions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Complete the universe of analytic solutions for Gaussian models + * Include some interesting special cases (e.g., large pool + single exposure) + * Include some tractable inhomogeneous problems +* Calculate more statistical moments (e.g., skew, kurtosis) +* Expand to non-Gaussian distributions + +Expand to loss distributions that include recovery risk +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Make a more robust implementation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The functions should ultimately be coded to a high standard of robustness: +* input validation +* exception handling +* controlled accuracy + +Introduce testing framework +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +Todo List +========================= + Feature requests, bug reports and any other issues are welcome to log at the `Github Repository `_ \ No newline at end of file diff --git a/docs/source/testing.rst b/docs/source/testing.rst new file mode 100644 index 0000000..5d1343f --- /dev/null +++ b/docs/source/testing.rst @@ -0,0 +1,39 @@ +Testing +================== +Testing portfolioAnalytics has two major components: + +* normal code testing aiming to certify the correctness of code execution +* algorithm testing aiming to validate the correctness of algorithmic implementation + +.. note:: In general algorithmic testing is not as precise as code testing and may be more subject to uncertainties such as numerical accuracy. + + +Running all the examples +------------------------ +Running all the examples is a quick way to check that everything is installed properly, all paths are defined etc. At the root of the distribution: + +.. code:: bash + + python3 run_examples.py + + +.. warning:: The script might generate a number of files / images at random places within the distribution + + +Test Suite +------------- +The testing framework is based on unittest. + +Then run all tests + +.. code:: bash + + python3 test.py + +For an individual test: + +.. code:: bash + + pytest tests/test_TESTNAME.py + + diff --git a/docs/source/usage.rst b/docs/source/usage.rst deleted file mode 100644 index 720383f..0000000 --- a/docs/source/usage.rst +++ /dev/null @@ -1,10 +0,0 @@ -Usage -============================== - -The portfolioAnalytics packages offers a lot of functionality. Here we break down some of the main workflows for those -getting started - -.. toctree:: - :maxdepth: 4 - - examples diff --git a/examples/python/calculate_thresholds.py b/examples/python/calculate_thresholds.py index d5c1ab4..0adfb0f 100644 --- a/examples/python/calculate_thresholds.py +++ b/examples/python/calculate_thresholds.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # PortfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of PortfolioAnalytics. This is notwithstanding any licenses of @@ -16,7 +16,7 @@ """Example of calculating thresholds.""" import transitionMatrix as tm -from transitionMatrix.predefined import Generic +from transitionMatrix.creditratings.predefined import Generic from portfolioAnalytics.thresholds.model import ThresholdSet from portfolioAnalytics.thresholds.settings import AR_Model diff --git a/examples/python/calculate_variance.py b/examples/python/calculate_variance.py index 6e574ad..b44edee 100644 --- a/examples/python/calculate_variance.py +++ b/examples/python/calculate_variance.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk (https://www.openriskmanagement.com), all rights reserved +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com), all rights reserved # # PortfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of PortfolioAnalytics. This is notwithstanding any licenses of diff --git a/examples/python/conditional_migration_matrix.py b/examples/python/conditional_migration_matrix.py index dda7eb2..e0aee9f 100644 --- a/examples/python/conditional_migration_matrix.py +++ b/examples/python/conditional_migration_matrix.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -21,12 +21,11 @@ """ import numpy as np -import transitionMatrix as tm -import portfolioAnalytics as pal +from portfolioAnalytics import source_path from portfolioAnalytics.thresholds.model import ThresholdSet, ConditionalTransitionMatrix from portfolioAnalytics.thresholds.settings import AR_Model -from portfolioAnalytics import source_path + dataset_path = source_path + "datasets/" # A Generic matrix with 7 non-absorbing and one absorbing state diff --git a/examples/python/loss_distributions.py b/examples/python/loss_distributions.py index 6ee43eb..86f6ea5 100644 --- a/examples/python/loss_distributions.py +++ b/examples/python/loss_distributions.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk (https://www.openriskmanagement.com), all rights reserved +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com), all rights reserved # # PortfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of PortfolioAnalytics. This is notwithstanding any licenses of @@ -20,6 +20,7 @@ from portfolioAnalytics.utils.portfolio import Portfolio # Load the portfolio data +print(dataset_path) json_data = open(dataset_path + '/portfolio_data1.json').read() data = json.loads(json_data) diff --git a/examples/python/portfolio_model.py b/examples/python/portfolio_model.py index cc4a7a2..b5abb3c 100644 --- a/examples/python/portfolio_model.py +++ b/examples/python/portfolio_model.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of diff --git a/examples/python/validate_thresholds.py b/examples/python/validate_thresholds.py index 1cdb998..61a9d3e 100644 --- a/examples/python/validate_thresholds.py +++ b/examples/python/validate_thresholds.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # PortfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of PortfolioAnalytics. This is notwithstanding any licenses of @@ -21,7 +21,7 @@ """ import transitionMatrix as tm -from transitionMatrix.predefined import Generic +from transitionMatrix.creditratings.predefined import Generic from portfolioAnalytics.thresholds.model import ThresholdSet from portfolioAnalytics.thresholds.settings import AR_Model from portfolioAnalytics import source_path diff --git a/examples/python/visualize_thresholds.py b/examples/python/visualize_thresholds.py index ddde5a3..71cc988 100644 --- a/examples/python/visualize_thresholds.py +++ b/examples/python/visualize_thresholds.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -21,7 +21,7 @@ from matplotlib import collections as mc import transitionMatrix as tm -from transitionMatrix.predefined import Generic +from transitionMatrix.creditratings.predefined import Generic from portfolioAnalytics.thresholds.model import ThresholdSet from portfolioAnalytics.thresholds.settings import AR_Model diff --git a/index.rst b/index.rst deleted file mode 100644 index 685bfe7..0000000 --- a/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -.. portfolioAnalytics documentation master file, created by - sphinx-quickstart on Mon Jun 17 20:47:15 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to portfolioAnalytics's documentation! -============================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/portfolioAnalytics/__init__.py b/portfolioAnalytics/__init__.py index 40660e0..673b4d6 100644 --- a/portfolioAnalytics/__init__.py +++ b/portfolioAnalytics/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of diff --git a/portfolioAnalytics/creditmetrics.py b/portfolioAnalytics/creditmetrics.py index a6dc90d..04dc417 100644 --- a/portfolioAnalytics/creditmetrics.py +++ b/portfolioAnalytics/creditmetrics.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -15,15 +15,21 @@ import math -from portfolioAnalytics.utils import bivariatenormal as bv from scipy import stats -"""Implement Credit Metrics style variance calculations.""" +from portfolioAnalytics.utils import bivariatenormal as bv + +"""Implement Credit Metrics style variance calculations. + + +""" # wrapper for inverse cumulative normal density def Ninv(x): - """Inverse normal function.""" + """Inverse normal function. + + """ return stats.norm.ppf(x, loc=0.0, scale=1.0) @@ -31,9 +37,12 @@ def Ninv(x): def variance(portfolio, correlation, loadings): - """Variance calculation.""" + """Variance calculation. + + + """ n = portfolio.psize - variance = 0.0 + variance_sum = 0.0 name_var = 0.0 # Portfolio Variance du to correlation @@ -47,18 +56,21 @@ def variance(portfolio, correlation, loadings): rho = loadings[portfolio.factor[i]] * loadings[portfolio.factor[j]] * Omega variance_pair = portfolio.exposure[i] * portfolio.exposure[j] * ( bv.BivariateNormalDistribution(a1, a2, rho) - p1 * p2) - variance += variance_pair + variance_sum += variance_pair # Idiosyncratic Portfolio Variance due to name concentration for i in range(n - 1): p1 = portfolio.rating[i] name_var += portfolio.exposure[i] * portfolio.exposure[i] * (p1 - p1 * p1) - return 2 * variance + name_var + return 2 * variance_sum + name_var def creditmetrics_el(portfolio, correlation, loadings): - """Expected Loss Calculation.""" + """Credit Metrics Expected Loss Calculation. + + + """ n = portfolio.psize el = 0.0 print(portfolio) @@ -74,6 +86,8 @@ def creditmetrics_el(portfolio, correlation, loadings): def creditmetrics_ul(portfolio, correlation, loadings): - """Loss Volatility.""" + """Credit Metrics Loss Volatility (Standard Deviation of Loss) + + """ result = variance(portfolio, correlation, loadings) return math.sqrt(result) diff --git a/portfolioAnalytics/estimators/__init__.py b/portfolioAnalytics/estimators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/portfolioAnalytics/model.py b/portfolioAnalytics/model.py index 0959012..e150d47 100644 --- a/portfolioAnalytics/model.py +++ b/portfolioAnalytics/model.py @@ -2,7 +2,7 @@ """This module is part of the portfolioAnalytics package.""" -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -15,15 +15,14 @@ # limitations under the License. import json + import portfolioAnalytics.vasicek as va -from portfolioAnalytics.utils.portfolio import Portfolio class LossDistribution(object): """The Loss Distribution object exposes the core functionality of the portfolioAnalytics library.a - Depending on the selected method, it produces estimates of the moments of loss distribution, or point - estimates for a given stress scenario. + Depending on the selected method, it produces estimates of the moments of loss distribution, or point estimates for a given stress scenario. .. Todo:: Something to do @@ -67,13 +66,18 @@ def calculate(self, method=None, periods=None, portfolio=None, asset_correlation self.stddev.append(va.vasicek_base_ul(N, p, asset_correlation)) def to_json(self, json_file=None, accuracy=5): - """Serialize to JSON.""" + """Serialize to JSON. + + + """ something = [] serialized = json.dumps(something, indent=2, separators=(',', ': ')) return serialized def from_json(self, json_file): - """Read from JSON.""" + """Read from JSON. + + """ pass def print_moments(self, format_type='Standard', accuracy=2): @@ -104,5 +108,7 @@ def print_moments(self, format_type='Standard', accuracy=2): def plot(self, rating): - """Plot the loss distributions.""" + """Plot the loss distributions. + + """ pass diff --git a/portfolioAnalytics/settings.py b/portfolioAnalytics/settings.py index 788b5af..78e8e6f 100644 --- a/portfolioAnalytics/settings.py +++ b/portfolioAnalytics/settings.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -12,7 +12,9 @@ # either express or implied. See the License for the specific language governing permissions and # limitations under the License. -"""Integration Settings for Threshold Based Methods.""" +"""Integration Settings for Threshold Based Methods. + +""" GRID_POINTS = 3000 PRECISION = 1.e-8 diff --git a/portfolioAnalytics/thresholds/__init__.py b/portfolioAnalytics/thresholds/__init__.py index 6e7d3d3..ed3b984 100644 --- a/portfolioAnalytics/thresholds/__init__.py +++ b/portfolioAnalytics/thresholds/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -12,7 +12,9 @@ # either express or implied. See the License for the specific language governing permissions and # limitations under the License. -"""module portfolioAnalytics.thresholds.""" +"""module portfolioAnalytics.thresholds. + +""" from .model import * from .settings import * diff --git a/portfolioAnalytics/thresholds/model.py b/portfolioAnalytics/thresholds/model.py index 8f41d3c..7ab90e1 100644 --- a/portfolioAnalytics/thresholds/model.py +++ b/portfolioAnalytics/thresholds/model.py @@ -2,7 +2,7 @@ """This module is part of the portfolioAnalytics package.""" -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -61,6 +61,7 @@ class ThresholdSet(object): """The Threshold set object stores a multiperiod migration/default threshold structure as a numpy array. .. Todo:: Separate integration method from transition data + """ def __init__(self, ratings=None, periods=None, TMSet=None, json_file=None): @@ -120,12 +121,11 @@ def __init__(self, ratings=None, periods=None, TMSet=None, json_file=None): raise ValueError def fit(self, AR_Model, ri, dt=1.0): - """Fit Thresholds given autoregressive model and transition matrix given the initial state ri + """ Fit Thresholds given autoregressive model and transition matrix given the initial state ri. + + .. note:: - .. note:: The threshold corresponding to the starting rating is set by convention to NaN. - The threshold corresponding to a defaulted state is set by convention to - Infinity - These values are stored in memory as numpy NaN and Infinity value respectively - They are serialized as strings "nan" and "-inf" respectively + The threshold corresponding to the starting rating is set by convention to NaN. The threshold corresponding to a defaulted state is set by convention to - Infinity. These values are stored in memory as numpy NaN and Infinity value respectively. They are serialized as strings "nan" and "-inf" respectively. """ @@ -509,10 +509,10 @@ def print(self, format_type='Standard', accuracy=2): entry = np.around(self.A[:, :, k], accuracy) for s_in in range(entry.shape[0]): for s_out in range(entry.shape[1]): - if format_type is 'Standard': + if format_type == 'Standard': format_string = "{0:." + str(accuracy) + "f}" print(format_string.format(entry[s_in, s_out]) + ' ', end='') - elif format_type is 'Percent': + elif format_type == 'Percent': print("{0:.2f}%".format(100 * entry[s_in, s_out]) + ' ', end='') print('') print('') @@ -573,10 +573,10 @@ def print_matrix(self, format_type='Standard', accuracy=2, state=None): entry = np.around(self.T[:, :, k], accuracy) for s_in in range(entry.shape[0]): for s_out in range(entry.shape[1]): - if format_type is 'Standard': + if format_type == 'Standard': format_string = "{0:." + str(accuracy) + "f}" print(format_string.format(entry[s_in, s_out]) + ' ', end='') - elif format_type is 'Percent': + elif format_type == 'Percent': print("{0:.2f}%".format(100 * entry[s_in, s_out]) + ' ', end='') print('') print('') @@ -585,10 +585,10 @@ def print_matrix(self, format_type='Standard', accuracy=2, state=None): entry = np.around(self.T[:, :, k], accuracy) # print(entry) for s_out in range(entry.shape[1]): - if format_type is 'Standard': + if format_type == 'Standard': format_string = "{0:." + str(accuracy) + "f}" print(format_string.format(entry[state, s_out]) + ' ', end='') - elif format_type is 'Percent': + elif format_type == 'Percent': print("{0:.2f}%".format(100 * entry[state, s_out]) + ' ', end='') print('') diff --git a/portfolioAnalytics/thresholds/settings.py b/portfolioAnalytics/thresholds/settings.py index 5c33f7b..f882039 100644 --- a/portfolioAnalytics/thresholds/settings.py +++ b/portfolioAnalytics/thresholds/settings.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -12,7 +12,9 @@ # either express or implied. See the License for the specific language governing permissions and # limitations under the License. -"""Integration Settings.""" +"""Integration Settings. + +""" GRID_POINTS = 2000 PRECISION = 1.e-8 diff --git a/portfolioAnalytics/utils/__init__.py b/portfolioAnalytics/utils/__init__.py index 677ad8f..5a7d6c7 100644 --- a/portfolioAnalytics/utils/__init__.py +++ b/portfolioAnalytics/utils/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of diff --git a/portfolioAnalytics/utils/bivariatenormal.py b/portfolioAnalytics/utils/bivariatenormal.py index b1346a7..191d25e 100644 --- a/portfolioAnalytics/utils/bivariatenormal.py +++ b/portfolioAnalytics/utils/bivariatenormal.py @@ -3,7 +3,7 @@ """Implementation of the bivariate normal function.""" -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -30,38 +30,41 @@ def N(x): def BivariateNormalDistribution(a, b, rho): - """Bivariate Normal Distribution.""" - # based on Z. Drezner, "Computation of the bivariate normal integral", Mathematics of Computation 32, pp. 277-279, 1978. - # uses 8-point Gaussian quadrature + """Bivariate Normal Distribution. + based on Z. Drezner, "Computation of the bivariate normal integral", + Mathematics of Computation 32, pp. 277-279, 1978. + uses 8-point Gaussian quadrature + + """ Epsilon = 1e-12 - if (rho > 1 - Epsilon): - if (a < b): + if rho > 1 - Epsilon: + if a < b: value = N(a) else: value = N(b) return value - if (rho < -(1 - Epsilon)): - if (a < -b): + if rho < -(1 - Epsilon): + if a < -b: value = 0 else: value = N(a) - N(-b) return value - if (a <= 0 and b <= 0 and rho <= 0): + if a <= 0 and b <= 0 and rho <= 0: value = Phi_Sum(a, b, rho) - elif (a <= 0 and b >= 0 and rho >= 0): + elif a <= 0 <= b and rho >= 0: value = N(a) - Phi_Sum(a, -b, -rho) - elif (a >= 0 and b <= 0 and rho >= 0): + elif a >= 0 and b <= 0 and rho >= 0: value = N(b) - Phi_Sum(-a, b, -rho) - elif (a >= 0 and b >= 0 and rho <= 0): + elif a >= 0 and b >= 0 and rho <= 0: value = N(a) + N(b) - 1 + Phi_Sum(-a, -b, rho) else: value = BivariateNormalDistribution(a, 0, rhoc(a, b, rho)) value = value + BivariateNormalDistribution(b, 0, rhoc(b, a, rho)) - if ((a > 0 and b < 0) or (a < 0 and b > 0)): + if (a > 0 and b < 0) or (a < 0 and b > 0): value = value - 0.5 return value @@ -115,6 +118,6 @@ def Phi_Sum(a, b, rho): def rhoc(a, b, rho): """Rho_c Helper Function.""" x = 1.0 - if (a < 0): + if a < 0: x = -1.0 return x * (rho * a - b) / math.sqrt(a * a - 2 * rho * a * b + b * b) diff --git a/portfolioAnalytics/utils/converters.py b/portfolioAnalytics/utils/converters.py index 7998299..83eea41 100644 --- a/portfolioAnalytics/utils/converters.py +++ b/portfolioAnalytics/utils/converters.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of diff --git a/portfolioAnalytics/utils/portfolio.py b/portfolioAnalytics/utils/portfolio.py index 2fce8d8..c353d07 100644 --- a/portfolioAnalytics/utils/portfolio.py +++ b/portfolioAnalytics/utils/portfolio.py @@ -53,9 +53,12 @@ def loadjson(self, data): """Load portfolio data from JSON object. The data format for the input json object is a list of dictionaries as follows - [{"ID":"1","PD":"0.015","EAD":"40","FACTOR":0}, - ... - {"ID":"2","PD":"0.286","EAD":"20","FACTOR":0}] + + .. code-block:: python + + [{"ID":"1","PD":"0.015","EAD":"40","FACTOR":0}, + ... + {"ID":"2","PD":"0.286","EAD":"20","FACTOR":0}] """ self.psize = len(data) diff --git a/portfolioAnalytics/vasicek.py b/portfolioAnalytics/vasicek.py index 13f79c0..b4eb72f 100644 --- a/portfolioAnalytics/vasicek.py +++ b/portfolioAnalytics/vasicek.py @@ -1,5 +1,5 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -15,13 +15,6 @@ See `Vasicek Distribution `_ -* vasicek_base_ implements a finite homogeneous pool -* vasicek_base_el_ implements the expected loss for the vasicek_base case -* vasicek_base_ul_ implements the standard deviation for the vasicek_base case -* vasicek_lim_ implements the limiting case for large N -* vasicek_lim_el_ implements the expected loss for the vasicek_lim case -* vasicek_lim_ul_ implements the standard deviation for the vasicek_lim case -* vasicek_lim_q_ implements the quantile for the vasicek_lim case """ @@ -72,7 +65,7 @@ def vasicek_base_el(N, p, rho): def vasicek_base_ul(N, p, rho): - """Unexpected Loss for the Vasicek Base distribution. + """Unexpected Loss (Standard Deviation) for the Vasicek Base distribution. :param N: The number of entities in the portfolio :param p: The probability of default diff --git a/requirements.txt b/requirements.txt index aae1a81..24cd86b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ -scipy>=1.0 -sympy>=1.3 -numpy>=1.16.2 -pandas>=0.24.2 -scikit-learn>=0.21.2 -matplotlib>=3.0.3 -requests>=2.21.0 -statsmodels>=0.9.0 -Sphinx>=1.8.4 -sphinx-rtd-theme>=0.4.3 +matplotlib +numpy +pandas +requests +scipy +setuptools +Sphinx +sphinx-rtd-theme +transitionMatrix +correlationMatrix \ No newline at end of file diff --git a/run_examples.py b/run_examples.py index 417c1d4..d4c2b15 100644 --- a/run_examples.py +++ b/run_examples.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -12,14 +12,18 @@ # either express or implied. See the License for the specific language governing permissions and # limitations under the License. -"""Run all examples to test that everything is working with the current version.""" +""" Run all examples to test that everything is working with the current version of the library -from portfolioAnalytics import source_path +""" -examples_path = source_path + "examples/python/" -filelist = ['calculate_variance'] -# filelist = ['loss_distributions', 'calculate_variance', 'calculate_thresholds', 'portfolio_model', 'validate_thresholds', 'visualize_thresholds'] +import os +examples_path = os.path.join("examples", "python") + +# filelist = ['loss_distributions', 'portfolio_model'] + +filelist = ['loss_distributions', 'calculate_variance', 'calculate_thresholds', 'portfolio_model', + 'validate_thresholds', 'visualize_thresholds'] if __name__ == '__main__': @@ -27,7 +31,8 @@ try: print('\nExecuting example file: ', example.upper()) print('-----------------------' + '-' * len(example)) - exec(open(examples_path + example + ".py").read()) + path = os.path.join(examples_path, example + ".py") + exec(open(path).read()) except Exception: print('**********************' + '*' * len(example)) print('ERROR in example file', example) diff --git a/setup.py b/setup.py index aad77e5..88dc62e 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2014-2019 Open Risk (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of portfolioAnalytics. This is notwithstanding any licenses of @@ -17,20 +17,20 @@ from setuptools import setup -__version__ = '0.2' +__version__ = '0.3' ver = __version__ -long_descr = open('description.rst', 'r', encoding='utf8').read() +long_descr = open('docs/source/description.rst', 'r', encoding='utf8').read() setup(name='portfolioAnalytics', version=ver, description='A Python powered library for calculating semi-analytic portfolio loss metrics', long_description=long_descr, + long_description_content_type='text/x-rst', author='Open Risk', author_email='info@openrisk.eu', - packages=['portfolioAnalytics', 'portfolioAnalytics.estimators', 'portfolioAnalytics.utils', 'tests', - 'portfolioAnalytics.thresholds', 'portfolioAnalytics.portfolio_models', 'datasets', 'examples.python'], + packages=['portfolioAnalytics', 'datasets', 'examples.python'], include_package_data=True, url='https://github.com/open-risk/portfolioAnalytics', install_requires=[ diff --git a/test.py b/test.py index 1096df3..edfe081 100644 --- a/test.py +++ b/test.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved (https://www.openriskmanagement.com) +# (c) 2017-2022 Open Risk, all rights reserved (https://www.openriskmanagement.com) # # portfolioAnalytics is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -12,7 +12,7 @@ # either express or implied. See the License for the specific language governing permissions and # limitations under the License. -"""Run the portfolioAnalytics test suite.""" +"""Run the portfolioAnalytics unittest suite.""" import sys import unittest diff --git a/tests/test_datasets.py b/tests/test_datasets.py index fb0b171..7104fa1 100644 --- a/tests/test_datasets.py +++ b/tests/test_datasets.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # TransitionMatrix is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -15,27 +15,16 @@ import unittest -import transitionMatrix as tm -from transitionMatrix.predefined import Minimal -from transitionMatrix import source_path, dataset_path - ACCURATE_DIGITS = 7 class TestDatasets(unittest.TestCase): ''' - Load in-memory matrices + Load in-memory datasets ''' - def test_minimal_matrix(self): - a = tm.TransitionMatrix(values=Minimal) - a.validate() - self.assertEqual(a.dimension, 3) - - def test_matrix_set_load_csv(self): - a = tm.TransitionMatrixSet(csv_file=dataset_path + "sp_1981-2016.csv", temporal_type='Cumulative') - a.validate() - self.assertEqual(a.periods, [1, 2, 3, 5, 7, 10, 15, 20]) + def test_minimal(self): + pass if __name__ == "__main__": diff --git a/tests/test_estimators.py b/tests/test_estimators.py index 6aa93e3..a53d9cd 100644 --- a/tests/test_estimators.py +++ b/tests/test_estimators.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # TransitionMatrix is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -14,52 +14,8 @@ import unittest -import pandas as pd - -import transitionMatrix as tm -from transitionMatrix import source_path -from transitionMatrix.estimators import cohort_estimator as es -from transitionMatrix.estimators import aalen_johansen_estimator as aj - ACCURATE_DIGITS = 2 class TestSimpleEstimator(unittest.TestCase): - pass - - -class TestCohortEstimator(unittest.TestCase): - - def test_cohort_estimator_counts(self): - dataset_path = source_path + "datasets/" - data = pd.read_csv(dataset_path + 'synthetic_data5.csv') - event_count = data[data['Timestep'] < 4]['ID'].count() - definition = [('0', "Stage 1"), ('1', "Stage 2"), ('2', "Stage 3")] - myState = tm.StateSpace(definition) - sorted_data = data.sort_values(['ID', 'Timestep'], ascending=[True, True]) - myEstimator = es.CohortEstimator(states=myState, ci={'method': 'goodman', 'alpha': 0.05}) - result = myEstimator.fit(sorted_data) - self.assertEqual(event_count, myEstimator.counts) - - -class TestAalenJohansenEstimator(unittest.TestCase): - """ - Test the estimation of a simple 2x2 transition matrix with absorbing state - - .. note: The result is subject to sampling error! Ensure the required accuracy corresponds to the input data size - - """ - - def test_aalenjohansen_simple_transitions(self): - dataset_path = source_path + "datasets/" - data = pd.read_csv(dataset_path + 'synthetic_data8.csv') - sorted_data = data.sort_values(['Time', 'ID'], ascending=[True, True]) - definition = [('0', "G"), ('1', "B")] - myState = tm.StateSpace(definition) - myEstimator = aj.AalenJohansenEstimator(states=myState) - labels = {'Timestamp': 'Time', 'From_State': 'From', 'To_State': 'To', 'ID': 'ID'} - result, times = myEstimator.fit(sorted_data, labels=labels) - self.assertAlmostEqual(result[0, 0, -1], 0.5, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(result[0, 1, -1], 0.5, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertEqual(result[1, 0, -1], 0.0) - self.assertEqual(result[1, 1, -1], 1.0) + pass \ No newline at end of file diff --git a/tests/test_model.py b/tests/test_model.py index 7b131c5..f1ae4e8 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # TransitionMatrix is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -14,130 +14,15 @@ import unittest -import transitionMatrix as tm -from transitionMatrix import source_path -import pandas as pd -from scipy.linalg import expm ACCURATE_DIGITS = 7 -class TestTransitionMatrix(unittest.TestCase): - ''' - Default instance (2x2 identity matrix) - ''' - def test_instantiate_matrix(self): - a = tm.TransitionMatrix() - self.assertAlmostEqual(a[0, 0], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[0, 1], 0.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 0], 0.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 1], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) +class LossDistribution(unittest.TestCase): - b = tm.TransitionMatrix([[1.0, 3.0], [1.0, 4.0]]) - self.assertAlmostEqual(b[0, 0], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(b[0, 1], 3.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(b[1, 0], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(b[1, 1], 4.0, places=ACCURATE_DIGITS, msg=None, delta=None) - - def test_csv_io(self): - a = tm.TransitionMatrix() - a.to_csv("test.csv") - b = tm.TransitionMatrix(csv_file="test.csv") - self.assertAlmostEqual(a[0, 0], b[0, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[0, 1], b[0, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 0], b[1, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 1], b[1, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - - def test_json_io(self): - a = tm.TransitionMatrix() - a.to_json("test.json") - b = tm.TransitionMatrix(json_file="test.json") - self.assertAlmostEqual(a[0, 0], b[0, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[0, 1], b[0, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 0], b[1, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 1], b[1, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - - def test_validation(self): - a = tm.TransitionMatrix() - self.assertEqual(a.validate(), True) - b = tm.TransitionMatrix(values=[1.0, 3.0]) - self.assertEqual(b.validate()[0][0], 'Matrix Dimensions Differ: ') - c = tm.TransitionMatrix(values=[[0.75, 0.25], [0.0, 0.9]]) - self.assertEqual(c.validate()[0][0], 'Rowsum not equal to one: ') - d = tm.TransitionMatrix(values=[[0.75, 0.25], [-0.1, 1.1]]) - self.assertEqual(d.validate()[0][0], 'Negative Probabilities: ') - - def test_generator(self): - a = tm.TransitionMatrix([[1.0, 3.0], [1.0, 4.0]]) - self.assertAlmostEqual(a[0, 0], expm(a.generator())[0, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[0, 1], expm(a.generator())[0, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 0], expm(a.generator())[1, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a[1, 1], expm(a.generator())[1, 1], places=ACCURATE_DIGITS, msg=None, delta=None) - - -class TestTransitionMatrixSet(unittest.TestCase): - - def test_instantiate_matrix_set(self): - periods = 5 - a = tm.TransitionMatrixSet(dimension=2, periods=periods) - self.assertEqual(a.temporal_type, 'Incremental') - self.assertAlmostEqual(a.entries[0][0, 0], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) - self.assertAlmostEqual(a.entries[periods-1][0, 0], 1.0, places=ACCURATE_DIGITS, msg=None, delta=None) + def test_calculate(self): pass - def test_set_validation(self): - a = tm.TransitionMatrixSet(dimension=2, periods=5) - self.assertEqual(a.validate(), True) - - def test_set_cumulate_incremental(self): - a = tm.TransitionMatrix(values=[[0.6, 0.2, 0.2], [0.2, 0.6, 0.2], [0.2, 0.2, 0.6]]) - a_set = tm.TransitionMatrixSet(values=a, periods=3, method='Copy', temporal_type='Incremental') - b_set = a_set - b_set.cumulate() - b_set.incremental() - self.assertAlmostEqual(a_set.entries[2][0, 0], b_set.entries[2][0, 0], places=ACCURATE_DIGITS, msg=None, delta=None) - pass - - def test_set_csv_io(self): - pass - - def test_set_json_io(self): - pass - - -class TestStateSpace(unittest.TestCase): - - def test_instantiate_state(self): - definition = [('0', "AAA"), ('1', "AA"), ('2', "A"), ('3', "BBB"), - ('4', "BB"), ('5', "B"), ('6', "CCC"), ('7', "D")] - s = tm.StateSpace(definition) - self.assertEqual(s.definition[0][1], 'AAA') - - def test_get_states(self): - definition = [('0', "AAA"), ('1', "AA"), ('2', "A"), ('3', "BBB"), - ('4', "BB"), ('5', "B"), ('6', "CCC"), ('7', "D")] - s = tm.StateSpace(definition) - self.assertEqual(s.get_states()[0], '0') - - def test_get_state_labels(self): - definition = [('0', "AAA"), ('1', "AA"), ('2', "A"), ('3', "BBB"), - ('4', "BB"), ('5', "B"), ('6', "CCC"), ('7', "D")] - s = tm.StateSpace(definition) - self.assertEqual(s.get_state_labels()[0], 'AAA') - - def test_generic(self): - s = tm.StateSpace() - n = 10 - s.generic(n=n) - self.assertEqual(s.get_state_labels()[n-1], str(n-1)) - - def test_validate_dataset(self): - dataset_path = source_path + "datasets/" - data = pd.read_csv(dataset_path + 'test.csv', dtype={'State': str}) - definition = [('0', "Stage 1"), ('1', "Stage 2"), ('2', "Stage 3")] - s = tm.StateSpace(definition) - self.assertEqual(s.validate_dataset(dataset=data)[0], "Dataset contains the expected states.") - if __name__ == "__main__": diff --git a/tests/test_utils.py b/tests/test_utils.py index 19b4d78..8e4714b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ # encoding: utf-8 -# (c) 2017-2019 Open Risk, all rights reserved +# (c) 2017-2022 Open Risk, all rights reserved # # TransitionMatrix is licensed under the Apache 2.0 license a copy of which is included # in the source distribution of TransitionMatrix. This is notwithstanding any licenses of @@ -13,27 +13,15 @@ # limitations under the License. import unittest -import transitionMatrix as tm -from transitionMatrix import source_path -import pandas as pd ACCURATE_DIGITS = 7 -class TestPreprocessing(unittest.TestCase): +class TestBivariateNormal(unittest.TestCase): - def test_bin_timestamps(self): + def test_bivariate_normal_distribution(self): + pass - dataset_path = source_path + "datasets/" - data = pd.read_csv(dataset_path + 'synthetic_data1.csv') - event_count = data['ID'].count() - cohort_data, cohort_intervals = tm.utils.bin_timestamps(data, cohorts=5) - cohort_data['Count'] = cohort_data['Count'].astype(int) - self.assertEqual(event_count, cohort_data['Count'].sum()) - - -class TestDataSetGenerators(unittest.TestCase): - pass if __name__ == "__main__":