Investment restriction
Eligibility tests (investment grade, listing, domicile)
An eligibility test is a condition every holding must meet, such as ‘investment-grade bonds only’, ‘listed securities only’ or ‘no bond maturing after 2035’. Unlike a weight limit, one ineligible holding is enough for a breach. Acclimetry checks each holding against the rule's predicates on attributes or data fields and reports the weight of those that fail.
eligibility_testEligibility test
How policies word it
Eligibility clauses sit under headings such as ‘Permissible investments’ or ‘Eligible instruments’. They state what a holding must be, not how much of it may be held:
- Credit: bonds must be rated BBB- or better at purchase.
- Listing: only securities admitted to a regulated market, and regulated funds.
- Size: equities must have a market capitalisation of at least £500 million.
- Domicile or maturity: issuers domiciled in OECD countries; no bond maturing after 2035.
Many clauses apply ‘at purchase’. Monitoring tests current holdings, so a downgrade after purchase shows as a breach that the policy may treat as passive.
- Bonds must be rated investment grade (BBB- or better) at the time of purchase.
- Only listed securities and regulated collective investment schemes may be held.
- No bond held may mature after 31 December 2035.
- Equity holdings must have a market capitalisation of at least £500 million.
- Issuers must be domiciled in an OECD member country.
How it is measured
The rule version lists one or more predicates, and every measured holding must satisfy all of them. Each predicate names an attribute, an operator and a value or a set of values.
- Attributes: a built-in attribute of the holding, or a governed data field written as
field:CODE. - Numeric operators: at least, at most, above or below. Both sides are read as fixed-precision decimals.
- Set operators: the value is, or is not, in a list, matched without regard to case.
The result is compliant only when every holding passes. Otherwise it is a breach, with the failing holdings named and their combined weight reported.
A holding that a predicate cannot read, because the value is missing or unparseable, is a coverage gap. The result is then not evaluated, and the uncovered weight is shown.
When it is breached and what follows
An ineligible purchase is an active breach. A downgrade, a delisting or a change of domicile turns an eligible holding ineligible: a passive breach.
The reason code's action is to divest the holding or record an approved waiver naming it. A policy that allows time to sell, such as 90 days after a downgrade, maps to a grace period in days.
Proposed purchases can be checked against the same predicates in Trade Impact, which covers the ‘at purchase’ wording.
Data the check reads
- The attribute or data field each predicate reads, for every measured holding
- The operator and the value or list of values for each predicate
- Exempt classes the test does not apply to, such as equities for a maturity test
- Market values, to report the ineligible weight
Rule parameters
Every holding must satisfy the declared eligibility predicates — rating, market cap, listing, domicile, maturity or any governed instrument feature.
| Parameter | In the platform | Options and help |
|---|---|---|
| testName | Test name | How the test is named on results, e.g. "Investment grade only". |
| predicates | Predicates | Every holding must satisfy all of them. Attribute accepts a built-in name or field:CODE. |
| exemptClassNames | Exempt classes |
Methodology choices
- Numeric operators (gte/lte/gt/lt) parse both sides as fixed-precision decimals; an unparseable value is a coverage gap, never a pass.
- Set operators match case-insensitively.
Parameter names, methodology text and result-code meanings are shown exactly as the platform states them.
Worked examples, run by the engine
Every holding rated inside the approved set is eligible; set operators match case-insensitively
Rule template:eligibility_test testName: Investment grade onlypredicates: attribute=field:RATING, operator=in, values=AAA|AA|A|BBB
| Holding | Market value | RATING |
|---|---|---|
| A | 600 | AAA |
| B | 400 | bbb |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Investment grade only | Compliant | 0% | – | – | ALL_HOLDINGS_ELIGIBLE |
ALL_HOLDINGS_ELIGIBLE: Every measured holding satisfied the eligibility predicates.
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 300 holding under a 1,000 market-cap floor is ineligible, breaching at 30% of the 1,000 book
Rule template:eligibility_test testName: Market-cap floorpredicates: attribute=field:MCAP, operator=gte, value=1000
| Holding | Market value | MCAP |
|---|---|---|
| A | 700 | 5000 |
| B | 300 | 500 |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Market-cap floor | Breach | 30% | – | – | INELIGIBLE_HOLDING_PRESENT |
INELIGIBLE_HOLDING_PRESENT: At least one holding failed an eligibility predicate the mandate requires.
Largest contributors: Market-cap floor → B 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 holding a predicate cannot see is a coverage gap — eligibility is withheld, not assumed
Rule template:eligibility_test testName: Investment grade onlypredicates: attribute=field:RATING, operator=in, values=AAA|AA|A|BBB
| Holding | Market value | RATING |
|---|---|---|
| A | 900 | AAA |
| B | 100 | (blank) |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Investment grade only | Not evaluated: data quality | – | – | – | ELIGIBILITY_COVERAGE_INCOMPLETE |
ELIGIBILITY_COVERAGE_INCOMPLETE: Some holdings lack the data an eligibility predicate needs, so "every holding is eligible" cannot be asserted.
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 2041 bond, 15% of the portfolio, fails the maturity predicate, so the test breaches; the equity fund is exempt and needs no maturity.
Rule template:eligibility_test testName: No bond maturing after 2035predicates: attribute=field:MATURITY_YEAR, operator=lte, value=2035, label=Maturity year 2035 or earlierexemptClassNames: Equities
| Holding | Class | Market value | MATURITY_YEAR |
|---|---|---|---|
| UK Treasury 2029 | Bonds | 300,000 | 2029 |
| Supranational bond 2033 | Bonds | 250,000 | 2033 |
| Corporate bond 2041 | Bonds | 150,000 | 2041 |
| Global equity fund | Equities | 300,000 | (blank) |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| No bond maturing after 2035 | Breach | 15% | – | – | INELIGIBLE_HOLDING_PRESENT |
INELIGIBLE_HOLDING_PRESENT: At least one holding failed an eligibility predicate the mandate requires.
Largest contributors: No bond maturing after 2035 → Corporate bond 2041 15%
Computed at build time by the Acclimetry rule engine (backend commit 8197063); the same code runs in the product.
Interpretation traps
Every predicate, every holding
Predicates combine with AND. An ‘either/or’ clause, such as ‘listed or a regulated fund’, needs separate rules by class, combined if necessary with a composite rule.
Exempt what the test cannot read
A maturity predicate cannot read an equity. Exempt the classes it does not apply to, or the result stops at a coverage gap.
List every value
Set values match regardless of case but otherwise exactly. ‘Investment grade’ written as a list must include every notch from AAA to BBB-.
At purchase or at all times
The rule tests current holdings at every run. If the policy binds only at purchase, record that, and decide how a later failure is handled.
Result codes this check can return
| Code | State | Meaning | What to do |
|---|---|---|---|
| ALL_HOLDINGS_ELIGIBLE ✓ | Compliant | Every measured holding satisfied the eligibility predicates. | – |
| INELIGIBLE_HOLDING_PRESENT ✓ | Breach | At least one holding failed an eligibility predicate the mandate requires. | Divest the holding, or record an approved waiver naming it. |
| 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. |
| ELIGIBILITY_COVERAGE_INCOMPLETE ✓ | Not evaluated: data quality | Some holdings lack the data an eligibility predicate needs, so "every holding is eligible" cannot be asserted. | Supply the missing attribute for the named holdings. |
| 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. |
| 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.
Regulatory analogues
- Pension schemes: investment predominantly on regulated markets (2005 Regulations, reg 4)
- NURS: 20% in non-eligible securities (COLL 5.6.5R)
- SEC Rule 2a-7: a 397-day maturity limit per instrument (US money market funds)
Acclimetry evaluates whether every measured holding satisfies all the rule's predicates, and reports the weight of those that fail. It reads the data loaded for each holding and does not source ratings, listings or domicile. It tests current holdings at each run.
Questions
How do I test ‘investment grade only’?
Either as a predicate that the rating is in the investment-grade list, or as a credit quality floor with a 0% limit below BBB-. The floor also applies the organisation's split-rating and unrated policy.
What happens if a holding has no data for the test?
It is a coverage gap. The result is not evaluated (data quality) and the uncovered weight is reported; eligibility is never assumed.
How does this differ from a permitted or prohibited list?
A permitted or prohibited list names instruments, issuers, classes or countries. An eligibility test applies conditions to every holding, so new holdings are covered without updating a list.
Sources
- Occupational Pension Schemes (Investment) Regulations 2005, regulation 4 · OGL v3.0 · Read on12 October 2026
- FCA Handbook, COLL 5.6 (NURS investment powers), read 12 October 2026; pending FCA API snapshot · © FCA, OGL v3.0 · Read on12 October 2026
- 17 CFR 270.2a-7 (SEC Rule 2a-7, money market funds), via Cornell LII · public domain · Read on12 October 2026