Investment restriction
Rating distribution limits
Rating distribution limits set a minimum or maximum weight for each band of credit ratings in a portfolio. A bond policy might require at least 60% rated A- or better and cap holdings below BBB- at 10%. Acclimetry places each holding in a named rating bucket and tests every bucket's weight against its own bounds.
rating_distributionRating distribution
How policies word it
A single credit floor says what may be held. A distribution says how much of each quality is acceptable, so it suits portfolios that mix investment-grade and high-yield bonds.
- A floor on high quality: at least 60% of the portfolio rated A- or better.
- A cap on lower grades: no more than 15% rated BB+ to B-, and nothing below B-.
- Explicit buckets for gaps: unrated bonds limited to 5%, defaulted bonds to 2%.
Policies also say which rating applies when agencies disagree; some simply say the lower rating applies. With the composite grade, that choice is set once in the organisation's credit-quality policy.
- At least 60% of the bond portfolio must be rated A- or better.
- Holdings rated BB+ to B- are limited to 15%; no holding may be rated below B-.
- Unrated bonds may not exceed 5% of the portfolio.
- Where two agencies rate a bond differently, the lower rating applies.
How it is measured
Each bucket lists the rating values it holds, and each holding is matched to a bucket. A bucket's weight is its holdings' value over the portfolio's value.
- Raw field: one rating field is read as stored and matched value by value, ignoring case.
- Composite grade: each holding is first graded under the organisation's credit-quality policy. That policy aliases Moody's symbols, resolves split ratings and can notch for watches and outlooks.
- Unrated: the reserved value
__unrated__puts holdings with no rating into their own bucket.
Buckets need not cover every rating. A holding in no bucket still counts in the portfolio's value, but a bucket with no bounds at all is not evaluated (unsupported).
Each bucket returns its own result, with weight, headroom and the holdings that contribute most.
When it is breached and what follows
A bucket above its maximum or below its minimum is a breach, and warning bounds sit inside the hard ones. A downgrade can move a holding into a capped bucket: a passive breach, because the holding fitted when it was bought.
Each breach opens or joins a case with assignment, escalation and service-level clocks. A policy allowing time to sell a downgraded bond maps to a grace period in days, or to a time-bound waiver on the case.
Data the check reads
- A rating per holding in a data field, or the ratings the organisation's providers write to their fields
- The organisation's credit-quality policy, when buckets use the composite grade
- The rating values in each bucket, listed notch by notch
- Market values for every holding
- Exempt classes, such as equities, so they are not read as unrated
Rule parameters
Weight bands per rating bucket, including explicit unrated and defaulted buckets.
| Parameter | In the platform | Options and help |
|---|---|---|
| fieldCode | Rating field | Governed custom-field code carrying the rating value (the single source when the organisation names no providers). |
| buckets | Rating buckets | |
| exemptClassNames | Exempt classes | |
| useCompositeGrade | Use the composite grade | Bucket by the composite grade under the organisation's credit-quality policy instead of one field's raw value. |
Methodology choices
- Rating values are matched case-insensitively.
- The reserved value "__unrated__" captures positions with no rating.
- With "use composite grade", each holding is bucketed by its composite grade on the canonical scale under the organisation's credit-quality policy (Moody's symbols aliased, split ratings resolved, watch/outlook notched as configured).
Parameter names, methodology text and result-code meanings are shown exactly as the platform states them.
Worked examples, run by the engine
Investment-grade ratings at 700 of a 1,000 book are 70%, meeting a 60% floor; ratings match case-insensitively
Rule template:rating_distribution fieldCode: RATINGbuckets: label=Investment grade, values=AAA|AA|A|BBB, hardMin=60 · label=Unrated, values=__unrated__, hardMax=10
| Holding | Market value | RATING |
|---|---|---|
| A | 500 | AAA |
| B | 200 | bbb |
| C | 200 | BB |
| D | 100 | (blank) |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Investment grade | Compliant | 70% | – | 10% | WITHIN_LIMITS |
| Unrated | Compliant | 10% | – | 0% | WITHIN_LIMITS |
WITHIN_LIMITS: The measured value sits inside every approved bound.
Largest contributors: Investment grade → A 50%, B 20% · Unrated → D 10%
These are the product's own test cases for this template, evaluated by the Acclimetry rule engine (backend commit 8197063). The build fails if any result changes.
Unrated is a bucket, not a gap: 300 of a 1,000 book with no rating is 30%, over a 10% cap
Rule template:rating_distribution fieldCode: RATINGbuckets: label=Investment grade, values=AAA|AA|A|BBB, hardMin=60 · label=Unrated, values=__unrated__, hardMax=10
| Holding | Market value | RATING |
|---|---|---|
| A | 700 | AAA |
| D | 300 | (blank) |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Investment grade | Compliant | 70% | – | 10% | WITHIN_LIMITS |
| Unrated | Breach | 30% | – | -20% | ABOVE_HARD_MAXIMUM |
WITHIN_LIMITS: The measured value sits inside every approved bound.ABOVE_HARD_MAXIMUM: The measured value exceeds the approved hard maximum, beyond any stated tolerance.
Largest contributors: Investment grade → A 70% · Unrated → D 30%
These are the product's own test cases for this template, evaluated by the Acclimetry rule engine (backend commit 8197063). The build fails if any result changes.
A bucket with no bounds at all cannot be evaluated and says so, rather than reading as checked
Rule template:rating_distribution fieldCode: RATINGbuckets: label=High yield, values=BB|B
| Holding | Market value | RATING |
|---|---|---|
| A | 1,000 | BB |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| High yield | Not evaluated: unsupported | – | – | – | BAND_HAS_NO_BOUNDS |
BAND_HAS_NO_BOUNDS: A band in this rule version states neither a minimum nor a maximum, so there is nothing to decide against.
These are the product's own test cases for this template, evaluated by the Acclimetry rule engine (backend commit 8197063). The build fails if any result changes.
The BB+ bond is 12% of the 1,000,000 bond book, over the 10% cap for holdings below investment grade; the exempt equity fund is neither unrated nor in the denominator.
Rule template:rating_distribution fieldCode: RATINGbuckets: label=Below investment grade, values=BB+|BB|BB-|B+|B|B-, hardMax=10, warnMax=8 · label=A- or better, values=AAA|AA+|AA|AA-|A+|A|A-, hardMin=60 · label=BBB+ to BBB-, values=BBB+|BBB|BBB-, hardMax=35 · label=Unrated, values=__unrated__, hardMax=5exemptClassNames: Equities
| Holding | Class | Market value | RATING |
|---|---|---|---|
| UK Treasury 2031 | Bonds | 300,000 | AA |
| Supranational bond 2029 | Bonds | 200,000 | AAA |
| Utility bond 2033 | Bonds | 100,000 | A- |
| Industrial bond 2030 | Bonds | 250,000 | BBB |
| High-yield bond 2028 | Bonds | 120,000 | BB+ |
| Unrated private note | Bonds | 30,000 | (blank) |
| Global equity fund | Equities | 500,000 | (blank) |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Below investment grade | Breach | 12% | ≤ 10% warning ≤ 8% | -2% -4% to warning | ABOVE_HARD_MAXIMUM |
| A- or better | Compliant | 60% | ≥ 60% | 0% | WITHIN_LIMITS |
| BBB+ to BBB- | Compliant | 25% | ≤ 35% | 10% | WITHIN_LIMITS |
| Unrated | Compliant | 3% | ≤ 5% | 2% | WITHIN_LIMITS |
ABOVE_HARD_MAXIMUM: The measured value exceeds the approved hard maximum, beyond any stated tolerance.WITHIN_LIMITS: The measured value sits inside every approved bound.
Largest contributors: Below investment grade → High-yield bond 2028 12% · A- or better → UK Treasury 2031 30%, Supranational bond 2029 20%, Utility bond 2033 10% · BBB+ to BBB- → Industrial bond 2030 25%
Computed at build time by the Acclimetry rule engine (backend commit 8197063); the same code runs in the product.
Interpretation traps
List every notch
On a raw field, values match exactly, ignoring case. A bucket listing ‘BBB’ does not catch ‘BBB+’ or ‘BBB-’. The composite grade puts every provider on one scale, but buckets still list its grades.
Unrated is a bucket here
With __unrated__, a holding with no rating is weighed in its own bucket instead of stopping the result. The credit quality floor treats unrated holdings by policy instead, and by default leaves them not evaluated.
Exempt what has no rating by nature
Equities, many funds and cash carry no credit rating. Unless their classes are exempt, they land in the unrated bucket and can breach it.
Result codes this check can return
| Code | State | Meaning | What to do |
|---|---|---|---|
| WITHIN_LIMITS ✓ | Compliant | The measured value sits inside every approved bound. | – |
| ABOVE_HARD_MAXIMUM ✓ | Breach | The measured value exceeds the approved hard maximum, beyond any stated tolerance. | Remediate the exposure, or record an approved waiver against the case. |
| EMPTY_PORTFOLIO | Not applicable | The portfolio held no positions at the evaluation moment. | – |
| NO_MEASURED_POSITIONS | Not applicable | No position in the portfolio fell inside this rule's measured population. | – |
| NOT_IN_SCOPE | Not applicable | This rule version does not apply to this portfolio — the portfolio is outside the version's stated scope. Recorded so that "considered and out of scope" can be told from "never run". | – |
| AGGREGATE_MEMBER_NOT_ACCEPTED | Not evaluated: data quality | A portfolio in the measured set has no authoritative snapshot, so the set-level number cannot be stated. | Accept the named portfolios' snapshots. A partial set produces no aggregate. |
| SNAPSHOT_NOT_ACCEPTED | Not evaluated: data quality | The portfolio's newest snapshot awaits data-quality review; only accepted data may produce an authoritative result. | Accept, override or reject the snapshot on Data & Quality. |
| SNAPSHOT_REJECTED | Not evaluated: data quality | The portfolio's newest snapshot was rejected, so there is no authoritative data to measure. | Load a corrected file. |
| STALE_POSITIONS | Not evaluated: data quality | The newest snapshot is older than the organisation's staleness threshold. | Load current positions, or revisit the threshold on Data & Quality ▸ Policy. |
| STALE_PRICES | Not evaluated: data quality | A holding had no price on the position file and its approved price was older than the organisation's price-staleness threshold, so it was left unvalued. | Load a current price for the holding, price it on the position file, or revisit the threshold on Data & Quality ▸ Policy. |
| AWAITING_EXTERNAL_DATA | Pending external data | An approved input is late but still inside the organisation's grace window. This is not a pass and not an exception. | None yet. If the delivery does not arrive, this becomes a data-quality exception. |
| BAND_HAS_NO_BOUNDS ✓ | Not evaluated: unsupported | A band in this rule version states neither a minimum nor a maximum, so there is nothing to decide against. | Supersede the version with bounds on the band, or remove it. |
| METHOD_NOT_SUPPORTED | Not evaluated: unsupported | The rule version asks for a measurement method this engine does not implement. Declared rather than approximated. | Choose a supported methodology, or treat this limit as a manual obligation until the method ships. |
| EVALUATOR_ERROR | Not evaluated: system error | The evaluator raised an error. Surfaced as a visible result rather than as silence. | Report it. A system error is never a pass and never a compliant reading. |
✓ seen in the worked examples. The rest come from the platform registry’s own scan of the template source.
Acclimetry evaluates the weight of each named rating bucket against that bucket's hard and warning bounds, reading one rating field or the composite grade. It does not assign or source ratings and does not average them. An average-quality clause is tested as a custom data-field band.
Questions
Should unrated bonds count as below investment grade?
The policy decides. Here unrated holdings form their own bucket through the reserved value __unrated__, so their weight can be capped directly.
Which rating applies when agencies disagree?
With the composite grade, the organisation's policy decides: the median (the shipped default), the lowest, the highest, or the first provider in priority order. A raw field uses whatever value it holds.
How is this different from a credit quality floor?
A credit quality floor caps one thing: the weight below a minimum grade. A distribution bounds several buckets at once, including unrated and defaulted ones.