Investment restriction
Custom formula limits
A custom formula limit tests a number that no standard template produces, built from named measures of the portfolio. Examples are ‘cash must cover six months of spending’ or ‘property plus infrastructure no more than 25%’. Acclimetry evaluates an approved arithmetic expression over declared inputs, such as a group's value, NAV or a governed constant.
custom_formulaCustom formula (governed)
How policies word it
Some clauses combine several quantities or bring in a figure from outside the portfolio. A charity or family-office policy may express liquidity in months of spending.
- Spending cover: cash must cover at least six months of planned spending.
- Combined share: property and infrastructure together no more than 25% of the portfolio.
- Internal overlay: a capital charge, such as gross exposure times an approved factor, within a budget.
Where a standard template fits, it is the better choice; a formula is for limits that need arithmetic across measures.
- Cash and near-cash must cover at least six months of budgeted expenditure.
- Property and infrastructure together may not exceed 25% of the portfolio.
- The capital charge on gross exposure, at 8%, must stay within the approved budget.
- Equities plus equity-like alternatives must be between 40% and 70% of the portfolio.
How it is measured
The rule version declares named inputs, each one governed primitive measure, and an expression over those names. The worked examples use a group's value, NAV, gross exposure and governed constants.
The expression allows + - * /, brackets, and the functions min(), max() and abs(). It is parsed when the draft is saved, so an unknown input or a stray character is rejected before approval.
Arithmetic runs on fixed-precision decimals. A division by zero is not evaluated, never infinity or zero.
The result is the formula's value with headroom to the bounds. A label says what the number means, such as ‘months of spending held in cash’.
When it is breached and what follows
A value outside the hard bounds is a breach, and warning bounds give notice first. A formula that cannot be evaluated returns a system-error result, never a pass.
A governed constant, such as the monthly spending figure, is part of the approved version. When the figure changes, a new version is drafted and approved by someone other than its author.
Breaches join a case with assignment, escalation and service-level clocks, like any other rule.
Data the check reads
- The inputs the formula declares, such as the value of a named class and NAV
- Any governed constants, such as monthly spending or a capital factor, recorded in the rule version
- The expression, and a label saying what the number means
- Market values for the holdings the inputs read
Rule parameters
An approved arithmetic expression over governed primitive measures - capital, regulatory, financing or internal overlays.
| Parameter | In the platform | Options and help |
|---|---|---|
| inputs | Inputs | Each input is one governed primitive measure. Reference it by name in the expression. |
| expression | Expression | Arithmetic over the declared input names: + - * / ( ) and min(), max(), abs(). |
| measureLabel | What the number means |
Methodology choices
- The expression is parsed and validated when the draft is saved - an unknown input or a stray character is a rejected draft, not a runtime surprise.
- Arithmetic runs on fixed-precision decimals; a division by zero is Not Evaluated, never infinity or zero.
Parameter names, methodology text and result-code meanings are shown exactly as the platform states them.
Worked examples, run by the engine
Stocks of 300 plus property of 200 over a 1,000 NAV, times 100, is 50, inside a cap of 60
Rule template:custom_formula inputs: name=stocks, primitive=value_of, args={"attribute":"class","group":"Stocks"} · name=property, primitive=value_of, args={"attribute":"class","group":"Property"} · name=nav, primitive=nav, args={}expression: (stocks + property) / nav * 100measureLabel: Real-asset share, percent
Bounds: hard max 60
| Holding | Class | Market value |
|---|---|---|
| EQ | Stocks | 300 |
| PROP | Property | 200 |
| BD | Bonds | 500 |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Real-asset share, percent | Compliant | 50 | ≤ 60 | 10 | WITHIN_LIMITS |
WITHIN_LIMITS: The measured value sits inside every approved bound.
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 governed constant scales the measure: gross exposure of 1,000 times a 0.08 capital factor is 80, over a cap of 75
Rule template:custom_formula inputs: name=gross, primitive=gross_exposure, args={} · name=factor, primitive=constant, args={"value":"0.08"}expression: gross * factormeasureLabel: Capital charge
Bounds: hard max 75
| Holding | Market value |
|---|---|
| A | 600 |
| B | 400 |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Capital charge | Breach | 80 | ≤ 75 | -5 | ABOVE_HARD_MAXIMUM |
ABOVE_HARD_MAXIMUM: The measured value exceeds the approved hard maximum, beyond any stated tolerance.
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.
Dividing by a measure that evaluates to zero is not infinity and not zero — it is Not Evaluated
Rule template:custom_formula inputs: name=stocks, primitive=value_of, args={"attribute":"class","group":"Stocks"} · name=property, primitive=value_of, args={"attribute":"class","group":"Property"}expression: stocks / propertymeasureLabel: Stocks per unit of property
Bounds: hard max 10
| Holding | Class | Market value |
|---|---|---|
| EQ | Stocks | 1,000 |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Stocks per unit of property | Not evaluated: data quality | – | ≤ 10 | – | FORMULA_DIVISION_BY_ZERO |
FORMULA_DIVISION_BY_ZERO: Returned by the engine; the registry’s reason-code dictionary has no entry for it.
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.
Cash of 180,000 against approved monthly spending of 40,000 covers 4.5 months, below the six-month minimum.
Rule template:custom_formula inputs: name=cash, primitive=value_of, args={"attribute":"class","group":"Cash"} · name=monthly_spend, primitive=constant, args={"value":"40000"}expression: cash / monthly_spendmeasureLabel: Months of spending held in cash
Bounds: hard min 6 · warn min 7
| Holding | Class | Market value |
|---|---|---|
| Sterling deposits | Cash | 180,000 |
| Global equity fund | Equities | 620,000 |
| Sterling bond fund | Bonds | 200,000 |
| Group | Status | Value | Bounds | Headroom to the hard bound | Code |
|---|---|---|---|---|---|
| Months of spending held in cash | Breach | 4.5 | ≥ 6 warning ≥ 7 | -1.5 -2.5 to warning | BELOW_HARD_MINIMUM |
BELOW_HARD_MINIMUM: The measured value falls below the approved hard minimum, beyond any stated tolerance.
Computed at build time by the Acclimetry rule engine (backend commit 8197063); the same code runs in the product.
Interpretation traps
Constants age
A spending figure or factor is fixed in the approved version. Review it on a schedule, for example with a manual obligation, and supersede the version when it changes.
Units are yours
The result is a plain number. Multiply by 100 for a percentage, and set the bounds in the same unit as the formula.
Watch the denominator
A ratio over a group the portfolio may not hold, such as stocks per unit of property, is not evaluated when it is empty. Choose denominators that always exist.
Result codes this check can return
| Code | State | Meaning | What to do |
|---|---|---|---|
| WITHIN_LIMITS ✓ | Compliant | The measured value sits inside every approved bound. | – |
| ABOVE_WARNING_MAXIMUM | Warning | The measured value has crossed the early-warning maximum but not the hard limit. | No breach yet; the early-warning band exists so the position can be managed before one. |
| BELOW_WARNING_MINIMUM | Warning | The measured value has crossed the early-warning minimum but not the hard limit. | No breach yet; the early-warning band exists so the position can be managed before one. |
| 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. |
| BELOW_HARD_MINIMUM | Breach | The measured value falls below the approved hard minimum, beyond any stated tolerance. | Remediate the exposure, or record an approved waiver against the case. |
| CONSERVATIVE_MISSING_DATA | Breach | Data was missing and this rule version's approved unknown-data policy is conservative_breach, so the gap is treated as a breach rather than as Not Evaluated. | Supply the missing values; the reading is deliberately pessimistic until you do. |
| EMPTY_PORTFOLIO | Not applicable | The portfolio held no positions at the evaluation moment. | – |
| 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. |
| FORMULA_DIVISION_BY_ZERO ✓ | Not evaluated: data quality | Returned by the engine; the registry’s reason-code dictionary has no entry for it. | – |
| MISSING_POSITION_DATA | Not evaluated: data quality | One or more positions had no readable value, and the partial numbers do not prove the outcome either way. | Supply the missing valuations. The platform will not fabricate a zero to produce a reading. |
| 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. |
| FORMULA_INVALID | Not evaluated: system error | A governed custom formula could not be evaluated. | Correct the formula on a new draft version; a bad formula is normally rejected at draft time. |
✓ seen in the worked examples. The rest come from the platform registry’s own scan of the template source.
Acclimetry evaluates one approved arithmetic expression over declared portfolio measures and governed constants, against the rule version's bounds. It accepts only the stated operators and functions, not free code. It does not model cash flows, spending plans or forecasts.
Questions
How do I test ‘six months of spending in cash’?
Declare the cash value and a governed constant for monthly spending, divide one by the other, and set a hard minimum of 6. The constant changes only through a new approved version.
What functions can a formula use?
Addition, subtraction, multiplication, division and brackets, plus min(), max() and abs(), over the declared input names.
What happens if the formula is wrong?
An unknown input or stray character is rejected when the draft is saved. A formula that fails during a run returns a system-error result, which is never read as compliant.