Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mandate

Constraints as code. An investment mandate is a set of ratio constraints, and once the mandate is data rather than a hardcoded screen, everything downstream becomes generic machinery: point-in-time evaluation over a panel, compliance transition matrices, grace windows, several mandates treated as raters on the same holdings, and the one that motivated the library, decomposing every compliance flip into whether the numerator moved or the denominator did.

Every shop that runs a rulebook rebuilds this badly in-house. This is the primitive, with no opinion about which rulebook you run.

The question this exists to answer

A leverage screen divides debt by market capitalisation. Debt is a slow quarterly accounting series; market cap moves every day. So when a holding breaches the screen and has to be sold, what actually happened? Did the company lever up, or did its share price fall?

flips answers that per event, by counterfactual. Freeze the numerator at its previous value and ask whether the flip still happens. Freeze the denominator and ask the same. Each flip comes back labelled:

denominator-driven   freezing the numerator still reproduces the flip
numerator-driven     freezing the denominator still reproduces the flip
either sufficient    both counterfactuals reproduce it
interaction only     neither alone reproduces it

A mandate whose breaches are overwhelmingly denominator-driven is not measuring the thing it claims to measure. It is forcing trades on price movement wearing the costume of a fundamental rule. That generalises well past the screen it came from, to ESG exclusions, index reconstitution, ratings triggers and any threshold rule applied to a ratio with a market-priced denominator.

Install

pip install -e .

Requires Python 3.10 or newer, numpy and pandas. Nothing else.

Use

import pandas as pd
from mandate import RatioConstraint, Mandate, evaluate, flips

# A panel is long: one row per (entity, period), plus the columns your
# constraints reference.
panel = pd.read_csv("panel.csv", parse_dates=["q"])   # ticker, q, debt, mcap

leverage = Mandate("house-rules", (
    RatioConstraint("debt_mcap", numerator="debt", denominator="mcap",
                    threshold=0.30, op="<"),
))

verdicts = evaluate(panel, leverage)          # adds ratio, pass, compliant
events = flips(verdicts, leverage.constraints[0])
print(events["cause"].value_counts(normalize=True))

window_periods makes the denominator a trailing mean, which is how the 24-month and 36-month index screens are actually written:

RatioConstraint("debt_mcap24", "debt", "mcap", 1/3, window_periods=8)

with_grace(verdicts, 2) applies a cure or disposal window, so a breach only binds after N consecutive failing periods. transitions(verdicts) gives the row-normalised compliance transition matrix. disagreement(panel, [...]) evaluates several mandates over the same items and marks each one unanimous or contested, which is the input a reliability measure such as Krippendorff's alpha wants.

Portfolio-level rules live in mandate.portfolio, where the first resident is UCITS 5/10/40, the concentration rule every European retail fund runs under. It is there as proof that the DSL is rulebook-shaped rather than shaped by the screens it was extracted from.

What the example shows

python example.py builds a synthetic quarterly panel of 300 entities where market cap is deliberately more volatile than debt, and runs the whole library over it:

panel: 18000 rows, 300 entities
compliant share: 66.5 percent

compliance flips: 1284
denominator-driven    71.9
either sufficient     12.5
interaction only      12.1
numerator-driven       3.6

transition matrix, P(state next | state now):
compliant  False  True
prev
False      0.897  0.103
True       0.057  0.943

breaches binding, no grace:          6032
breaches binding, two-quarter grace: 4850

four mandates rating the same items:
unanimous    64.7
contested    35.3

Three point six percent of flips are attributable to the business changing. That is synthetic data chosen to make the mechanism visible, not an empirical claim about any real market, but the mechanism is not synthetic: it is what happens whenever a slow numerator is divided by a fast denominator and compared to a fixed threshold.

Presets

mandate.presets ships the four Shariah leverage screens, being AAOIFI, DJIM, S&P and MSCI, since those are the rulebooks the library was extracted from and they make a convenient set of four mutually disagreeing raters. They are examples, not the subject. revenue_purity is there as a template for writing your own.

Validation

The library was extracted from a private screening engine and checked against it rather than against its own tests. On that engine's panel it reproduces the AAOIFI compliance flips to the row, with an identical cause for every event, and the engine's analysis path was then refactored to run on this library and produces byte-identical output on all four standards.

That panel is not public, so tests/test_engine_parity.py skips by default. Point it at your own reference export with MANDATE_PARITY_PANEL and MANDATE_PARITY_FLIPS if you have one.

python -m pytest tests -q

Licence

MIT. Sandeep Singh Rai, ORCID 0009-0001-3360-9205.

About

Constraints as code: investment mandates as data, evaluated point-in-time, with every breach decomposed into business change or market move.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages