Logo
pyAgrum 1.8.1 on Jupyter

Tutorials

  • ▶ Tutorial
    • ▷ Tutorial
    • ▷ Tutorial2
  • ▶ Examples
    • ▷ Asthma
    • ▷ Kaggle Titanic
    • ▷ Naive Credit Default Modeling
    • ▷ Causality And Learning
    • ▷ Sensitivity Analysis Using Credal Networks
    • ▷ Quasi Continuous
    • ▷ Parameters Learning With Pandas
    • ▷ Bayesian Beta Coin
  • ▶ Models
    • ▷ Influence Diagram
    • ▷ Dynamic Bn
    • ▷ Markov Random Field
    • ▷ Credal Networks
    • ▷ O3PRM
  • ▶ Learning
    • ▷ Structural Learning
    • ▷ Learning Classifier
    • ▷ Learning And Essential Graphs
    • ▷ Dirichlet Prior And Weigthed Database
    • ▷ Parametric Em
    • ▷ Chi2 And Scores From Bn Learner
  • ▶ Inference
    • ▷ Graphical Inference
    • ▷ Relevance Reasoning
    • ▷ Lazy Propagation Advanced Features
    • ▷ Approximate Inference
    • ▷ Sampling Inference
  • ▶ Classifier
    • ▷ Learning
    • ▷ Discretizer
    • ▷ Compare Classifiers With Sklearn
    • ▷ Cross Validation
    • ▷ Binary And Nary Classifier From Bn
  • ▶ Causality
    • ▷ Tobacco
    • ▷ Simpson Paradox
    • ▷ Multinomial Simpson Paradox
    • ▷ Do Calculus Examples
    • ▷ Counterfactual
  • ▶ Applications
    • ▷ Ipywidgets
  • ▶ Tools
    • ▷ Potentials
    • ▷ Aggregators
    • ▷ Explain
    • ▷ Kl For BNs
    • ▷ Comparing Bn
    • ▷ Colouring And Exporting BNs
    • ▷ Config For PyAgrum
pyAgrum

Naive modeling of credit defaults using a Markov Random Field¶

Creative Commons License aGrUM interactive online version

This notebook is the adaptation to pyAgrum of the model proposed by Gautier Marti which is itself inspired by the paper Graphical Models for Correlated Defaults (adaptation by Marvin Lasserre and Pierre-Henri Wuillemin).

In [1]:
import numpy as np
import matplotlib.pyplot as plt

import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.lib.mrf2graph as m2g

Constructing the model¶

Three sectors co-dependent in their stress state are modelled: (Finance, Energy, Retail). For each of these sectors, we consider a universe of three issuers.

Within Finance Within Energy Within Retail
Deutsche Bank EDF New Look
Metro Bank Petrobras Matalan
Barclays EnQuest Marks & Spencer

The probability of default of these issuers is partly idiosyncratic and partly depending on the stress within their respective sectors. Some distressed names such as New Look can have a high marginal probability of default, so high that the state of their sector (normal or stressed) does not matter much. Some other high-yield risky names such as Petrobras may strongly depend on how the whole energy sector is doing. On the other hand, a company such as EDF should be quite robust, and would require a very acute and persistent sector stress to move its default probability significantly higher.

We can encode this basic knowledge in terms of potentials:

image-2.png

Conventions:

Sector variables can be either in the state no stress or in the state under stress while issuer variables can be either in the state no default or in the state default.

The choice of these potentials is not an easy task; It can be driven by historical fundamental or statistical relationships or forward-looking views.

Given a network structure, and a set of potentials, one can mechanically do inference using the pyAgrum library.

We will show how to obtain a distribution of the number of defaults (and the expected number of defaults), a distribution of the losses (and the expected loss). From the joint probability table, we could also extract the default correlation

tl;dr We use here the library pyAgrum to illustrate, on a toy example, how one can use a Markov Random Field (MRF) for modelling the distribution of defaults and losses in a credit portfolio.

Building accurate PGM/MRF models require expert knowledge for considering a relevant graph structure, and attributing useful potentials. For this toy model, we use the following structure:

image.png

The first step consists in creating the model. For that, we use the pyAgrum class pyAgrum.MarkovRandomField.

In [2]:
# building nodes (with types)
mn = gum.MarkovRandomField('Credit default modeling')

# Adding sector variables
sectors = ['Finance', 'Energy', 'Retail']
finance, energy, retail = [mn.add(gum.LabelizedVariable(name, '', ['no stress', 'under stress'])) 
                           for name in sectors]

# Adding issuer variables
edf, petro, eq = [mn.add(gum.LabelizedVariable(name, '', ['no default', 'default'])) 
              for name in ['EDF','Petrobras','EnQuest']]

mb, db, ba = [mn.add(gum.LabelizedVariable(name, '', ['no default', 'default'])) 
              for name in ['Metro Bank', 'Deutsche Bank', 'Barclays']]

nl, matalan, ms = [mn.add(gum.LabelizedVariable(name, '', ['no default', 'default'])) 
              for name in ['New Look', 'Matalan', 'Marks & Spencer']]
In [3]:
# Adding and filling factors between sectors
mn.addFactor([finance, energy])[:] = [[90, 70],
                                      [60, 10]]
mn.addFactor([finance, retail])[:] = [[80, 10],
                                      [30, 80]]
mn.addFactor([energy, retail])[:]  = [[60,  5],
                                      [70, 95]]

# Adding factors between sector and issuer
mn.addFactor([db, finance])[:] = [[90, 30],
                                  [10, 60]]
mn.addFactor([mb, finance])[:] = [[80, 40],
                                  [ 5, 60]]
mn.addFactor([ba, finance])[:] = [[90, 20],
                                  [20, 50]]

mn.addFactor([edf, energy])[:]   = [[90, 5],
                                    [80, 40]]
mn.addFactor([petro, energy])[:] = [[60, 50],
                                    [ 5, 60]]
mn.addFactor([eq, energy])[:]    = [[80, 20],
                                    [10, 50]]


mn.addFactor([nl, retail])[:]      = [[ 5, 60],
                                      [ 2, 90]]
mn.addFactor([matalan, retail])[:] = [[40, 30],
                                      [20, 70]]
mn.addFactor([ms, retail])[:]       = [[80, 10],
                                      [30, 50]]
In [4]:
nodetypes={n:0.1 if n in sectors else 
             0.2 for n in mn.names()}

gnb.sideBySide(gnb.getMRF(mn,view='graph',nodeColor=nodetypes),
               gnb.getMRF(mn,view='factorgraph',nodeColor=nodetypes),
              captions=['The model as a Markov random field','The model as a factor graph'])

gnb.sideBySide(mn.factor({'Energy', 'Finance'}),
               mn.factor({'Finance', 'Retail'}),
               mn.factor({'Energy', 'Retail'}),
               mn.factor({'Deutsche Bank', 'Finance'}),
               mn.factor({'Metro Bank', 'Finance'}),
               mn.factor({'Barclays', 'Finance'}),
               mn.factor({'EDF', 'Energy'}),
               mn.factor({'Petrobras', 'Energy'}),
               mn.factor({'EnQuest', 'Energy'}),
               mn.factor({'New Look', 'Retail'}),
               mn.factor({'Matalan', 'Retail'}),
               mn.factor({'Marks & Spencer', 'Retail'}),
              ncols=3)
pyAgrum▶Examples▷Naive Credit Default ModelingG pyAgrum▶Examples▷Naive Credit Default ModelingNew Look New Look pyAgrum▶Examples▷Naive Credit Default ModelingMatalan Matalan pyAgrum▶Examples▷Naive Credit Default ModelingEnergy Energy pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEDF EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingRetail Retail pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--Retail pyAgrum▶Examples▷Naive Credit Default ModelingFinance Finance pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Energy pyAgrum▶Examples▷Naive Credit Default ModelingBarclays Barclays pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Barclays pyAgrum▶Examples▷Naive Credit Default ModelingDeutsche Bank Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Retail pyAgrum▶Examples▷Naive Credit Default ModelingMetro Bank Metro Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Metro Bank pyAgrum▶Examples▷Naive Credit Default ModelingMarks & Spencer Marks & Spencer pyAgrum▶Examples▷Naive Credit Default ModelingRetail--New Look pyAgrum▶Examples▷Naive Credit Default ModelingRetail--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingRetail--Marks & Spencer
The model as a Markov random field
pyAgrum▶Examples▷Naive Credit Default ModelingG pyAgrum▶Examples▷Naive Credit Default ModelingNew Look New Look pyAgrum▶Examples▷Naive Credit Default ModelingMatalan Matalan pyAgrum▶Examples▷Naive Credit Default ModelingEnergy Energy pyAgrum▶Examples▷Naive Credit Default ModelingFinance Finance pyAgrum▶Examples▷Naive Credit Default ModelingBarclays Barclays pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEDF EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingDeutsche Bank Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingMarks & Spencer Marks & Spencer pyAgrum▶Examples▷Naive Credit Default ModelingRetail Retail pyAgrum▶Examples▷Naive Credit Default ModelingMetro Bank Metro Bank pyAgrum▶Examples▷Naive Credit Default Modelingf0#1 pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf1#2 pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#7 pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Deutsche Bank pyAgrum▶Examples▷Naive Credit Default Modelingf1#4 pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Petrobras pyAgrum▶Examples▷Naive Credit Default Modelingf2#9 pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--New Look pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf2#11 pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Marks & Spencer pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#2 pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#6 pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Metro Bank pyAgrum▶Examples▷Naive Credit Default Modelingf0#8 pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Barclays pyAgrum▶Examples▷Naive Credit Default Modelingf1#3 pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--EDF pyAgrum▶Examples▷Naive Credit Default Modelingf1#5 pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--EnQuest pyAgrum▶Examples▷Naive Credit Default Modelingf2#10 pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Matalan pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Retail
The model as a factor graph
Finance
Energy
no stress
under stress
no stress
90.000070.0000
under stress
60.000010.0000
Finance
Retail
no stress
under stress
no stress
80.000010.0000
under stress
30.000080.0000
Energy
Retail
no stress
under stress
no stress
60.00005.0000
under stress
70.000095.0000
Deutsche Bank
Finance
no default
default
no stress
90.000030.0000
under stress
10.000060.0000
Metro Bank
Finance
no default
default
no stress
80.000040.0000
under stress
5.000060.0000
Barclays
Finance
no default
default
no stress
90.000020.0000
under stress
20.000050.0000
EDF
Energy
no default
default
no stress
90.00005.0000
under stress
80.000040.0000
Petrobras
Energy
no default
default
no stress
60.000050.0000
under stress
5.000060.0000
EnQuest
Energy
no default
default
no stress
80.000020.0000
under stress
10.000050.0000
New Look
Retail
no default
default
no stress
5.000060.0000
under stress
2.000090.0000
Matalan
Retail
no default
default
no stress
40.000030.0000
under stress
20.000070.0000
Marks & Spencer
Retail
no default
default
no stress
80.000010.0000
under stress
30.000050.0000

Making inferences¶

Example 1 & 2¶

The first two examples consists in finding the chance of EDF to default with no evidence and knowing that the energy sector is not under stress. For that, we can use the getPosterior method:

In [5]:
gnb.sideBySide(gum.getPosterior(mn, target='EDF', evs={}),
               gum.getPosterior(mn, target='EDF', evs={'Energy':'no stress'}),
               captions=['$P(EDF)$','$P(EDF|Energy=$under stress$)$'])
EDF
no default
default
0.90720.0928

$P(EDF)$
EDF
no default
default
0.94740.0526

$P(EDF|Energy=$under stress$)$

Hence, if we cannot observe the state of the energy sector, then EDF has a higher chance of defaulting (9%) as it is possible that the energy sector is under stress.

Example 3 & 4¶

Given that Marks & Spencer (a much safer name) has defaulted, New Look has now an increased chance of defaulting: 97%.

Even if the retail sector is doing ok, New Look is a distressed name with a high probability of default (92%).

In [6]:
gnb.sideBySide(gum.getPosterior(mn, target='New Look', evs={'Marks & Spencer': 'default'}),
               gum.getPosterior(mn, target='New Look', evs={'Retail':'no stress'}),
               captions=['P(New Look|Marks & Spencer=default)',
                         'P(New Look | Retail = no stress)'])
New Look
no default
default
0.02860.9714

P(New Look|Marks & Spencer=default)
New Look
no default
default
0.07690.9231

P(New Look | Retail = no stress)

pyAgrum offers the option to vizualise the marginal probability of each variable using showInference.

In [7]:
# gum.config['factorgraph','edge_length_inference']=1.
gnb.showInference(mn, size='7!',nodeColor=nodetypes)
pyAgrum▶Examples▷Naive Credit Default ModelingG Inference in   0.78ms pyAgrum▶Examples▷Naive Credit Default ModelingFinance <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.289082</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingEnergy <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.344821</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingRetail <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.401304</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingEDF <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.459775</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.514956</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.579274</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingMetro Bank <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.634824</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingDeutsche Bank <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.687906</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingBarclays <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.740673</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingNew Look <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.793196</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingMatalan <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.845757</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default ModelingMarks & Spencer <rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:31.902265</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style> pyAgrum▶Examples▷Naive Credit Default Modelingf0#1 pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#2 pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#7 pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Deutsche Bank pyAgrum▶Examples▷Naive Credit Default Modelingf1#4 pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Petrobras pyAgrum▶Examples▷Naive Credit Default Modelingf2#9 pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--New Look pyAgrum▶Examples▷Naive Credit Default Modelingf2#11 pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Marks & Spencer pyAgrum▶Examples▷Naive Credit Default Modelingf0#2 pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#6 pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Metro Bank pyAgrum▶Examples▷Naive Credit Default Modelingf0#8 pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Barclays pyAgrum▶Examples▷Naive Credit Default Modelingf1#3 pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--EDF pyAgrum▶Examples▷Naive Credit Default Modelingf1#5 pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--EnQuest pyAgrum▶Examples▷Naive Credit Default Modelingf2#10 pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Matalan

Impact of defaults on a credit portfolio and the distribution of losses¶

Amongst the 9 available issuers, let’s consider the following portfolio containing bonds from these 6 issuers:

In [8]:
portfolio_names = ['EDF', 'Petrobras', 'EnQuest', 'Matalan', 'Barclays']
portfolio_exposures = [2e6, 400e3, 300e3, 600e3, 3e6]
LGD = 0.9
losses = {portfolio_names[i]:(portfolio_exposures[i]*LGD) for i in range(len(portfolio_names))}

print('** Total notional: USD {} MM'.format(sum(portfolio_exposures)/1e6))
** Total notional: USD 6.3 MM

We have a USD 6.3MM total exposure to these names (notional).

We assume that in case of default, we recover only 10% (LGD = 90%).

Let’s start using the model:

In [9]:
ie = gum.ShaferShenoyMRFInference(mn)
ie.makeInference()
p=ie.jointPosterior(set(sectors))

print(f'** There is {100*p[{"Retail":0,"Energy":0,"Finance":0}]:.2f}% chance that no sector in under stress.')
print()
p
** There is 42.38% chance that no sector in under stress.

Out[9]:
Energy
Retail
Finance
no stress
under stress
no stress
no stress
0.42380.0105
under stress
0.00830.0000
under stress
no stress
0.29990.1215
under stress
0.12510.0109

If we observe that the finance sector is doing ok, and that Marks & Spencer is still doing business as usual, then this is the current joint probabilities associated to the potential defaults in the portfolio:

In [10]:
ie = gum.ShaferShenoyMRFInference(mn)
ie.addJointTarget(set(portfolio_names))
ie.setEvidence({'Finance': 'no stress', 'Marks & Spencer': 'no default'})
ie.makeInference()
portfolio_posterior = ie.jointPosterior(set(portfolio_names))

Note that the line ie.addJointTarget(set(portfolio_names)) is important here because adding hard evidences removes the corresponding nodes in the graph and then no factor containing the variables in portfolio_names can be found. The method addJointTarget ensure that such a factor exists.

In [11]:
portfolio_posterior
Out[11]:
Barclays
EDF
Petrobras
EnQuest
Matalan
no default
default
no default
no default
no default
no default
0.14950.0332
default
0.15520.0345
default
no default
0.03830.0085
default
0.04120.0092
default
no default
no default
0.12680.0282
default
0.13500.0300
default
no default
0.04310.0096
default
0.06270.0139
default
no default
no default
no default
0.00840.0019
default
0.00880.0020
default
no default
0.00260.0006
default
0.00340.0008
default
no default
no default
0.00810.0018
default
0.01020.0023
default
no default
0.00770.0017
default
0.01700.0038

We now want to compute the distribution of the number of defaults. For that, we create a new Markov random field containing an additional node Number of defaults connected to each issuers:

In [12]:
mn2 = gum.MarkovRandomField(mn)

mn2.add(gum.RangeVariable('Number of defaults', '', 0, len(portfolio_names)))
factor = mn2.addFactor(['Number of defaults',*portfolio_names]).fillWithFunction('+'.join(portfolio_names))
In [13]:
gum.config['factorgraph','edge_length']=1.1
nodetypes={n:0.1 if n in sectors else 
             0.3 if "Number of defaults"==n else
             0.2 for n in mn2.names()}
gnb.flow.add(gnb.getMRF(mn2, view="graph",size='7!',nodeColor=nodetypes))
gnb.flow.add(gnb.getMRF(mn2, view="factorgraph",size='7!',nodeColor=nodetypes))
gnb.flow.display()
pyAgrum▶Examples▷Naive Credit Default ModelingG pyAgrum▶Examples▷Naive Credit Default ModelingNew Look New Look pyAgrum▶Examples▷Naive Credit Default ModelingMatalan Matalan pyAgrum▶Examples▷Naive Credit Default ModelingNumber of defaults Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingMatalan--Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingEnergy Energy pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEDF EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingRetail Retail pyAgrum▶Examples▷Naive Credit Default ModelingEnergy--Retail pyAgrum▶Examples▷Naive Credit Default ModelingFinance Finance pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Energy pyAgrum▶Examples▷Naive Credit Default ModelingBarclays Barclays pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Barclays pyAgrum▶Examples▷Naive Credit Default ModelingDeutsche Bank Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Retail pyAgrum▶Examples▷Naive Credit Default ModelingMetro Bank Metro Bank pyAgrum▶Examples▷Naive Credit Default ModelingFinance--Metro Bank pyAgrum▶Examples▷Naive Credit Default ModelingBarclays--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingBarclays--Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras--Barclays pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras--EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras--Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingEDF--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingEDF--Barclays pyAgrum▶Examples▷Naive Credit Default ModelingEDF--Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEDF--EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingEDF--Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest--Barclays pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest--Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingMarks & Spencer Marks & Spencer pyAgrum▶Examples▷Naive Credit Default ModelingRetail--New Look pyAgrum▶Examples▷Naive Credit Default ModelingRetail--Matalan pyAgrum▶Examples▷Naive Credit Default ModelingRetail--Marks & Spencer
pyAgrum▶Examples▷Naive Credit Default ModelingG pyAgrum▶Examples▷Naive Credit Default ModelingNew Look New Look pyAgrum▶Examples▷Naive Credit Default ModelingMatalan Matalan pyAgrum▶Examples▷Naive Credit Default ModelingEnergy Energy pyAgrum▶Examples▷Naive Credit Default ModelingFinance Finance pyAgrum▶Examples▷Naive Credit Default ModelingBarclays Barclays pyAgrum▶Examples▷Naive Credit Default ModelingPetrobras Petrobras pyAgrum▶Examples▷Naive Credit Default ModelingEDF EDF pyAgrum▶Examples▷Naive Credit Default ModelingEnQuest EnQuest pyAgrum▶Examples▷Naive Credit Default ModelingDeutsche Bank Deutsche Bank pyAgrum▶Examples▷Naive Credit Default ModelingMarks & Spencer Marks & Spencer pyAgrum▶Examples▷Naive Credit Default ModelingRetail Retail pyAgrum▶Examples▷Naive Credit Default ModelingNumber of defaults Number of defaults pyAgrum▶Examples▷Naive Credit Default ModelingMetro Bank Metro Bank pyAgrum▶Examples▷Naive Credit Default Modelingf1#5 pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#5--EnQuest pyAgrum▶Examples▷Naive Credit Default Modelingf1#3 pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#3--EDF pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12 pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--Matalan pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--Barclays pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--Petrobras pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--EDF pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--EnQuest pyAgrum▶Examples▷Naive Credit Default Modelingf3#4#5#8#10#12--Number of defaults pyAgrum▶Examples▷Naive Credit Default Modelingf2#11 pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Marks & Spencer pyAgrum▶Examples▷Naive Credit Default Modelingf2#11--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf2#9 pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--New Look pyAgrum▶Examples▷Naive Credit Default Modelingf2#9--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf1#4 pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#4--Petrobras pyAgrum▶Examples▷Naive Credit Default Modelingf0#7 pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#7--Deutsche Bank pyAgrum▶Examples▷Naive Credit Default Modelingf1#2 pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf1#2--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#1 pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Energy pyAgrum▶Examples▷Naive Credit Default Modelingf0#1--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf2#10 pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Matalan pyAgrum▶Examples▷Naive Credit Default Modelingf2#10--Retail pyAgrum▶Examples▷Naive Credit Default Modelingf0#8 pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#8--Barclays pyAgrum▶Examples▷Naive Credit Default Modelingf0#6 pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#6--Metro Bank pyAgrum▶Examples▷Naive Credit Default Modelingf0#2 pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Finance pyAgrum▶Examples▷Naive Credit Default Modelingf0#2--Retail

Once this new network is created, we can obtain the distribution of Number of defaults using getPosterior :

In [14]:
ndefault_posterior = gum.getPosterior(mn2, target='Number of defaults',
                                           evs={'Finance': 'no stress', 'Marks & Spencer': 'no default'})
ndefault_posterior
Out[14]:
Number of defaults
0
1
2
3
4
5
0.14950.36200.31190.13710.03570.0038
In [15]:
fig, ax = plt.subplots()
l = ndefault_posterior.tolist()
ax.bar(range(len(l)), l)
ax.set_xlabel('Number of defaults')
ax.set_title('Distribution of the number of defaults in the portfolio')
plt.show()
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <cc:Work> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> <dc:date>2023-05-24T14:47:32.652530</dc:date> <dc:format>image/svg+xml</dc:format> <dc:creator> <cc:Agent> <dc:title>Matplotlib v3.7.1, https://matplotlib.org/</dc:title> </cc:Agent> </dc:creator> </cc:Work> </rdf:RDF> <style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>