Skip to main content

A package for market compression of network data.

Project description

compnet — Compression for Market Network data

CircleCI version PyPI Latest Release Downloads License Buy Me A Coffee

About

compnet is a package for market compression of network data.

It is based on xxx.

How to get started

Given a dataframe el containing a network's edge list, start by constructing the graph representation $G$ via the class compnet.Graph:

import pandas as pd
import compnet as cn

el = pd.DataFrame([['A','B', 10],
                   ['B','C', 15],
                   ['B','A', 5],
                   ],
                  columns=['SOURCE', 'TARGET' ,'AMOUNT'])
g = cn.Graph(el)

If the dataframe does not contain columns named 'SOURCE', 'TARGET', and 'AMOUNT', the corresponding column names should be passed as well to compnet.Graph via the parameters source, target, and amount.

For example:

el = pd.DataFrame([['A','B', 10],
                   ['B','C', 15],
                   ['B','A', 5],
                   ],
                  columns=['bank', 'counterpart' ,'notional'])
g = cn.Graph(el, source='bank', target='counterpart', amount='notional')

Once the graph object g is created, it is possible to quickly inspect its properties as

g.describe()

which returns the gross, compressed, and excess market sizes of the graph

┌─────────────────┬──────────┐
│                 │   AMOUNT │
├─────────────────┼──────────┤
│ Gross size      │       30 │
│ Compressed size │       15 │
│ Excess size     │       15 │
└─────────────────┴──────────┘

This data is also accessible as a pandas.Series via the attribute g.properties.

Denoting by $A$ the weighted adjacency matrix of the network with elements $A_{ij}$, the gross, compressed, and excess market sizes are respectively defined as

$$ GMS = \sum_{i}\sum_{j} A_{ij} $$

$$ CMS = \frac{1}{2}\sum_i\left|\sum_j \left(A_{ij} - A_{ji}\right) \right| $$

$$ EMS = GMS - CMS $$

Notice in particular that $\sum_j \left(A_{ij} - A_{ji}\right)$ represents the net position of node $i$.

The net position of each node are also accessible as g.net_flow, which returns

A    -5.0
B   -10.0
C    15.0

Similarly, the gross amount for each node can be accessed as g.gross_flow, which returns

         OUT  IN  GROSS_TOTAL
ENTITY                       
A       10.0   5         15.0
B       20.0  10         30.0
C        0.0  15         15.0

At this point, it is possible to run a compression algorithm on g via the method Graph.compress. For any two graphs one can further compute the compression efficiency

$$CE = 1 - \frac{EMS_2}{EMS_1} $$

with $EMS_j$ the excess market size of graph $j$. Moreover, the compression ratio of order p for two adjacency matrices $A$ and $A^c$ is defined as

$$CR_p(A, A^c) = \frac{||L(A^c, N)||_p}{||L(A, N)||_p} $$

with $N$ the number of nodes and $||L(A, N)||_p$ the $p$-norm of the average absolute weight:

$$||L(A, N)||_p = \left( \frac{1}{N(N-1)} \sum_{i\ne j} |A_{ij}|^p \right)^{1/p}$$

Notice that $L(A, N)=\frac{1}{N(N-1)} \sum_{i\ne j} |A_{ij}|$ is a measure of the overall connectivity of the network: it quantifies, on average, how strongly nodes are connected. If considering an unweighted network (i.e. $A_{ij}\in \{0,1\}$), then $L(A, N)$ corresponds to the density of the network, that is the fraction of possible links that are actually present. In the case of weighted networks instead, $L(A, N)$ represents the average strength or intensity of the connections, taking into account the magnitude of each weight.

The compression factor of order p for two adjacency matrices $A$ and $A^c$ is then defined as

$$CF_p(A, A^c) = 1 - CR_p.$$

Four options for compression are currently available: bilateral, c, nc-ed, nc-max.

Bilateral compression

Bilateral compression compresses only edges between pairs of nodes. In our example above there exists two edges (trades) in opposite directions between node A and node B, which can be bilaterally compressed.

Running

g_bc = g.compress(type='bilateral')
g_bc

returns the following bilaterally compressed graph object

compnet.Graph object:
┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ A        │ B        │        5 │
│ B        │ C        │       15 │
└──────────┴──────────┴──────────┘

with compression efficiency and factor

Compression Efficiency CE = 0.667
Compression Factor CF(p=2) = 0.718

Conservative compression

Under conservative compression only existing edges (trades) are reduced or removed. No new edge is added.

The resulting conservatively compressed graph is always a sub-graph of the original graph. Moreover, the resulting conservatively compressed graph is always a directed acyclic graph (DAG), since all loops within the graph are removed.

The conservatively compressed graph can be obtained as

g_cc = g.compress(type='c')
g_cc

which in our example above returns

compnet.Graph object:
┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ A        │ B        │        5 │
│ B        │ C        │       15 │
└──────────┴──────────┴──────────┘

with compression efficiency and factor

Compression Efficiency CE = 0.667
Compression Factor CF(p=2) = 0.718

Non-conservative Equally-Distributed compression

Under non-conservative compression previously non-existent edges may be introduced.

Non-conservative compression allows to achieve an after-compression GMS equal to the CMS, thus removing all excess intermediation amounts in the network.

However, there is no unique solution to this problem.

The equally-distributed approach provides the simplest possible solution, by distributing flows from nodes with negative net flows to nodes with positive net flows on a pro-rata basis, that is distributing flows equally.

The non-conservatively equally-distributed compressed graph can be obtained as

g_cc = g.compress(type='nc-ed')
g_nced

which in our example above returns

compnet.Graph object:
┌────────┬───────────────┬────────────┐
│ SOURCE │ TARGET        │   AMOUNT   │
├────────┼───────────────┼────────────┤
│ A      │ C             │          5 │
│ B      │ C             │         10 │
└────────┴───────────────┴────────────┘

with compression efficiency and factor

Compression Efficiency CE = 1.0
Compression Factor CF(p=2) = 0.402

Maximal non-conservative compression

An alternative solution to the non-conservative compression problem is achieved by minimising the number of links and maximising their concentration.

This solution is in a sense diametrically opposed to the previous equally-distributed solution. While both solutions achieve a post-compression GMS equal to the network's CMS, the present maximal non-conservative approach achieves in general a lower compression factor at any order $p\ge 1$.

The non-conservative maximally compressed graph can be obtained as

g_ncmax = g.compress(type='nc-max')
g_ncmax

which in our example above returns

compnet.Graph object:
┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ B        │ C        │       10 │
│ A        │ C        │        5 │
└──────────┴──────────┴──────────┘

with compression efficiency and factor

Compression Efficiency CE = 1.0
Compression Factor CF(p=2) = 0.402

Although in this case both the equally-distributed and maximal compressions yield the same result, this needs not be the case in general.

Considering for instance the network

el = pd.DataFrame([['A','B', 4],
                   ['B','C', 3],
                   ['C','D', 5],
                   ],
                  columns=['SOURCE', 'TARGET' ,'AMOUNT'])
g = cn.Graph(el)

one finds the following equally-distributed compressed network

compnet.Graph object:
┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ A        │ B        │ 0.666667 │
│ A        │ D        │ 3.33333  │
│ C        │ B        │ 0.333333 │
│ C        │ D        │ 1.66667  │
└──────────┴──────────┴──────────┘

with compression efficiency and factor

Compression Efficiency CE = 1.0
Compression Factor CF(p=2) = 0.463

Maximally non-conservative compression yields instead

compnet.Graph object:
┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ A        │ D        │        4 │
│ C        │ D        │        1 │
│ C        │ B        │        1 │
└──────────┴──────────┴──────────┘

with compression efficiency and factor

Compression Efficiency CE = 1.0
Compression Factor CF(p=2) = 0.4

Grouping along additional dimensions

When considering networks with additional dimensions or layers, such as time, collateral type, market sub-segments, etc. compnet.Graph allows to describe and perform compression on each such layer independently via the parameter grouper, taking either a single field (as str) or multiple ones (as list) if grouping along multiple dimensions is necessary.

For instance, one might consider the market described by the tensor $A^\tau$ with elements $A^\tau_{ij}$, where $\tau$ indexes time with daily frequency. This can be represented for example by the following edge list:

el = pd.DataFrame([['A','B', 15, '2025-02-10'],
                   ['B','C', 15, '2025-02-10'],
                   ['B','A',  5, '2025-02-10'],
                   ['A','B', 20, '2025-02-11'],
                   ['B','C', 15, '2025-02-11'],
                   ['B','A',  6, '2025-02-11'],
                   ['A','B', 25, '2025-02-12'],
                   ['B','C', 15, '2025-02-12'],
                   ['B','A',  7, '2025-02-12'],
                   ],
                  columns=['lender', 'borrower' ,'amount', 'date'])

Creating the graph object as usual via

g = cn.Graph(el, source='lender', target='borrower', amount='amount', grouper='date')

and requesting its description as

g.describe()

one is presented with the following output describing the time evolution of the network's gross, compressed, and excess sizes:

┌────────────┬──────────────┬───────────────────┬───────────────┐
│ date       │   Gross size │   Compressed size │   Excess size │
├────────────┼──────────────┼───────────────────┼───────────────┤
│ 2025-02-10 │           35 │                15 │            20 │
│ 2025-02-11 │           41 │                15 │            26 │
│ 2025-02-12 │           47 │                18 │            29 │
└────────────┴──────────────┴───────────────────┴───────────────┘

As before, this data is also accessible as a pandas.DataFrame via the attribute g.properties.

Central clearing

The method centrally_clear allows to clear all positions through a common counterparty specified via the parameter ccp_name, introducing a new entity should ccp_name not be part of the list of entities already.

For instance

el = pd.DataFrame([['A','B', 4],
                   ['B','C', 3],
                   ['C','D', 5],
                   ],
                  columns=['SOURCE', 'TARGET' ,'AMOUNT'])
cn.Graph(el).centrally_clear()

returns

┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ CCP      │ B        │        4 │
│ CCP      │ C        │        3 │
│ CCP      │ D        │        5 │
│ A        │ CCP      │        4 │
│ B        │ CCP      │        3 │
│ C        │ CCP      │        5 │
└──────────┴──────────┴──────────┘

By definition, the central clearing operation doubles the Graph's GMS, while CMS is invariant.

It is also possible to return directly the bilaterally compressed graph as

cn.Graph(el).centrally_clear(net=True)

which yields

┌──────────┬──────────┬──────────┐
│ SOURCE   │ TARGET   │   AMOUNT │
├──────────┼──────────┼──────────┤
│ A        │ CCP      │        4 │
│ C        │ CCP      │        2 │
│ CCP      │ B        │        1 │
│ CCP      │ D        │        5 │
└──────────┴──────────┴──────────┘

Any grouper specified on Graph is automatically accounted for.

Author

Luca Mingarelli, 2022

Python

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

compnet-0.8.8.tar.gz (39.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

compnet-0.8.8-py3-none-any.whl (36.4 kB view details)

Uploaded Python 3

File details

Details for the file compnet-0.8.8.tar.gz.

File metadata

  • Download URL: compnet-0.8.8.tar.gz
  • Upload date:
  • Size: 39.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for compnet-0.8.8.tar.gz
Algorithm Hash digest
SHA256 ae66daada10bb138af5950b7f549a317da8ce5ada9edccb9004afe2cd291c6a7
MD5 344af6bd4fd3efe56dcbf4aa027800a0
BLAKE2b-256 202b4b3ec6f364a546a2cb8bd969966a3763830bf509d7d8aa9aa486ba1d40fc

See more details on using hashes here.

File details

Details for the file compnet-0.8.8-py3-none-any.whl.

File metadata

  • Download URL: compnet-0.8.8-py3-none-any.whl
  • Upload date:
  • Size: 36.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for compnet-0.8.8-py3-none-any.whl
Algorithm Hash digest
SHA256 97730095002bed1a3f4f79ee919753c25a39ca152e57f58506e7cb7b63469fd2
MD5 b8fa80794d20c60d8f4286c6ddda9c7b
BLAKE2b-256 2addd1f2276b176089e9463f6d5c332a5a6874456f4b74ea49c84310783acf06

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page