insurance.sum-insured-average
Average clause settlement for underinsurance: the loss times sum insured over value at risk, capped at the loss.
1.0.0 (not the latest) · published 2026-10-03 by charlie · Anterra
Pinned by 22 tests, run in TypeScript, Python and Rust.
Not professional advice. This capability calculates insurance figures from published rules. It is a software component for developers, not financial advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have an actuary review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
What it does
Claim settlement under an average clause (the underinsurance, or "pro rata average", condition in UK property policies). If the sum insured is less than the value at risk, the policyholder is treated as their own insurer for the difference and is paid only that share of any loss:
payout = loss x sumInsured / valueAtRisk
For example
average_clause_settlement(£40,000.00, £150,000.00, £200,000.00, 100%, half-up)→ payout £30,000.00, average applied true, insured proportion basis points 75%, shortfall £10,000.00 insured for 150,000 of 200,000: a 40,000 loss pays 30,000average_clause_settlement(£40,000.00, £200,000.00, £200,000.00, 100%, half-up)→ payout £40,000.00, average applied false, insured proportion basis points 100%, shortfall £0.00 fully insured pays the lossaverage_clause_settlement(£40,000.00, £250,000.00, £200,000.00, 100%, half-up)→ payout £40,000.00, average applied false, insured proportion basis points 100%, shortfall £0.00 over-insured pays the loss, and the proportion stops at 100%
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
pub fn average_clause_settlement(loss: &Money, sum_insured: &Money, value_at_risk: &Money, condition_basis_points: i64, mode: &str) -> AverageSettlement
| loss | Money | the loss, 0 up to the value at risk |
| sum_insured | Money | the sum insured on the policy, 0 or more |
| value_at_risk | Money | the full value of the property at the time of the loss, more than 0 |
| condition_basis_points | int | average applies when the sum insured is below this share of the value: 10000 always, 7500 the special condition |
| mode | RoundingMode | how the averaged payout rounds to a minor unit |
| returns | AverageSettlement |
The type it declares, generated into your project
/// The payout after average and what the policyholder bears.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AverageSettlement {
pub payout: Money,
pub average_applied: bool,
/// sum insured over value at risk, rounded down, at most 10000
pub insured_proportion_basis_points: i64,
/// loss minus payout
pub shortfall: Money,
}
Your code names it in one line, in the file that uses it
fune!(insurance.sum-insured-average@^1); // then call average_clause_settlement(…)
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::math_round_div::round_div; ← from math.round-div ^1.0.0 · built alongside by fune
use super::money_amount::{assert_same_currency, money, money_from_value, money_to_value, Money}; ← from money.amount ^1.0.0 · built alongside by fune
/// Settle a claim under an average clause.
///
/// A property insured for less than it is worth is paid only the share of
/// the loss that the sum insured bears to the value at risk. With a special
/// condition of average the clause only applies once the sum insured falls
/// below that share of the value. The payout never exceeds the loss or the
/// sum insured.
///
/// # Panics
/// Panics on mixed currencies, negative amounts, a value at risk that is not
/// positive, a loss above it, or a condition outside 1..=10000.
pub fn average_clause_settlement(
loss: &Money,
sum_insured: &Money,
value_at_risk: &Money,
condition_basis_points: i64,
mode: &str,
) -> AverageSettlement {
assert_same_currency(loss, sum_insured);
assert_same_currency(loss, value_at_risk);
if value_at_risk.minor <= 0 {
panic!("valueAtRisk must be more than zero, received {}", value_at_risk.minor);
}
if loss.minor < 0 {
panic!("loss must not be negative, received {}", loss.minor);
}
if sum_insured.minor < 0 {
panic!("sumInsured must not be negative, received {}", sum_insured.minor);
}
if loss.minor > value_at_risk.minor {
panic!("loss must not exceed the value at risk");
}
if !(1..=10000).contains(&condition_basis_points) {
panic!("conditionBasisPoints must be from 1 to 10000, received {}", condition_basis_points);
}
// i128: a sum insured and a loss in pence can overflow i64 when multiplied.
let average_applied =
(sum_insured.minor as i128) * 10000 < (value_at_risk.minor as i128) * (condition_basis_points as i128);
let mut payout = if average_applied {
let wide = (loss.minor as i128) * (sum_insured.minor as i128);
let whole = wide / (value_at_risk.minor as i128);
let remainder = wide % (value_at_risk.minor as i128);
// Divide in i128, then round the remainder the same way round_div would.
whole as i64 + round_div(remainder as i64, value_at_risk.minor, mode)
} else {
loss.minor
};
payout = payout.min(loss.minor).min(sum_insured.minor);
let c = &loss.currency;
AverageSettlement {
payout: money(payout, c),
average_applied,
insured_proportion_basis_points: round_div(sum_insured.minor * 10000, value_at_risk.minor, "down").min(10000),
shortfall: money(loss.minor - payout, c),
}
}
pub fn average_settlement_to_value(s: &AverageSettlement) -> Value {
Value::obj(vec![
("payout", money_to_value(&s.payout)),
("averageApplied", Value::Bool(s.average_applied)),
("insuredProportionBasisPoints", Value::Int(s.insured_proportion_basis_points)),
("shortfall", money_to_value(&s.shortfall)),
])
}
pub fn fune_vector(args: &[Value]) -> Value {
if let Value::Float(f) = args[3] {
panic!("conditionBasisPoints must be from 1 to 10000, received {}", f);
}
average_settlement_to_value(&average_clause_settlement(
&money_from_value(&args[0]),
&money_from_value(&args[1]),
&money_from_value(&args[2]),
args[3].as_i64(),
args[4].as_str(),
))
}Install
fune build
With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:
fune add insurance.sum-insured-average
The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./insurance.sum-insured-average-1.0.0-rust.fune, or fetch it from a terminal with fune pull insurance.sum-insured-average@1.0.0:rust.
The whole function, every language, is one file too: insurance.sum-insured-average-1.0.0.fune, 23,682 bytes, sha256 9e7517e8e036bf925963d336197fbfec6cd74d78146bf5bf35abb080d3aa5d56. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before insurance.sum-insured-average
after — your function gets the result and the arguments, and returns the final result.
// fune: after insurance.sum-insured-average
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace math.round-div in insurance.sum-insured-average
// fune: replace money.amount in insurance.sum-insured-average
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show insurance.sum-insured-average --steps.
// fune: step insurance.sum-insured-average after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| insured for 150,000 of 200,000: a 40,000 loss pays 30,000 | £40,000.00, £150,000.00, £200,000.00, 100%, half-up | → | payout £30,000.00, average applied true, insured proportion basis points 75%, shortfall £10,000.00 |
| fully insured pays the loss | £40,000.00, £200,000.00, £200,000.00, 100%, half-up | → | payout £40,000.00, average applied false, insured proportion basis points 100%, shortfall £0.00 |
| over-insured pays the loss, and the proportion stops at 100% | £40,000.00, £250,000.00, £200,000.00, 100%, half-up | → | payout £40,000.00, average applied false, insured proportion basis points 100%, shortfall £0.00 |
| a total loss when underinsured pays the sum insured | £200,000.00, £150,000.00, £200,000.00, 100%, half-up | → | payout £150,000.00, average applied true, insured proportion basis points 75%, shortfall £50,000.00 |
| special condition of average at 75%: insured for 80% of value, no average | £40,000.00, £160,000.00, £200,000.00, 75%, half-up | → | payout £40,000.00, average applied false, insured proportion basis points 80%, shortfall £0.00 |
| special condition at 75%: insured for 70%, average applies in full proportion | £40,000.00, £140,000.00, £200,000.00, 75%, half-up | → | payout £28,000.00, average applied true, insured proportion basis points 70%, shortfall £12,000.00 |
| insured for exactly 75% under the 75% condition: not below it, so no average | £40,000.00, £150,000.00, £200,000.00, 75%, half-up | → | payout £40,000.00, average applied false, insured proportion basis points 75%, shortfall £0.00 |
| no average, but a loss above the sum insured is capped at it | £190,000.00, £160,000.00, £200,000.00, 75%, half-up | → | payout £160,000.00, average applied false, insured proportion basis points 80%, shortfall £30,000.00 |
| a third insured: 1,000.00 x 1/3 is 333.333..., half-up 333.33 | £1,000.00, £100,000.00, £300,000.00, 100%, half-up | → | payout £333.33, average applied true, insured proportion basis points 33.33%, shortfall £666.67 |
| the same rounded up | £1,000.00, £100,000.00, £300,000.00, 100%, up | → | payout £333.34, average applied true, insured proportion basis points 33.33%, shortfall £666.66 |
Show the other 12 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| two thirds: 666.666... half-up is 666.67 | £1,000.00, £200,000.00, £300,000.00, 100%, half-up | → | payout £666.67, average applied true, insured proportion basis points 66.66%, shortfall £333.33 |
| no loss pays nothing | £0.00, £150,000.00, £200,000.00, 100%, half-up | → | payout £0.00, average applied true, insured proportion basis points 75%, shortfall £0.00 |
| nothing insured pays nothing | £40,000.00, £0.00, £200,000.00, 100%, half-up | → | payout £0.00, average applied true, insured proportion basis points 0%, shortfall £40,000.00 |
| large values beyond 2^53 when multiplied: 9,000,000.00 of a 12,000,000.00 building insured for 10,000,000.00 | £9,000,000.00, £10,000,000.00, £12,000,000.00, 100%, half-up | → | payout £7,500,000.00, average applied true, insured proportion basis points 83.33%, shortfall £1,500,000.00 |
| a loss above the value at risk is refused | £3.00, £1.00, £2.00, 100%, half-up | → | error: loss must not exceed the value at risk |
| a zero value at risk is refused | £0.00, £1.00, £0.00, 100%, half-up | → | error: valueAtRisk must be more than zero |
| a negative sum insured is refused | £1.00, -£0.01, £2.00, 100%, half-up | → | error: sumInsured must not be negative |
| a negative loss is refused | -£1.00, £1.00, £2.00, 100%, half-up | → | error: loss must not be negative |
| a condition of 0 is refused | £1.00, £1.00, £2.00, 0%, half-up | → | error: conditionBasisPoints must be from 1 to 10000 |
| a condition above 100% is refused | £1.00, £1.00, £2.00, 100.01%, half-up | → | error: conditionBasisPoints must be from 1 to 10000 |
| a fractional condition is refused | £1.00, £1.00, £2.00, 75.005%, half-up | → | error: conditionBasisPoints must be from 1 to 10000 |
| a sum insured in another currency is refused | £1.00, €1.00, £2.00, 100%, half-up | → | error: currency mismatch |
More from the author
So a building worth 200,000 insured for 150,000 (75%) is paid 30,000 on a 40,000 loss, not 40,000. The payout is never more than the loss (over- insurance does not pay a profit) or the sum insured (a total loss when underinsured pays the sum insured).
## The condition
`conditionBasisPoints` says when average applies at all:
- **10000**: pro rata average, whenever the sum insured is below the value. - **7500** (or 8500 and so on): a *special condition of average*, where average applies only if the sum insured is below that share of the value at risk; above it, losses are paid in full up to the sum insured. When it applies, it applies in the full proportion (sum insured / value), which is the usual UK wording. Some wordings instead scale by sum insured over the threshold value; that is a different clause and not this one.
The test is strict: insured for exactly 75% under a 75% condition is not below it, so no average.
## Details
- The payout rounds to the minor unit in the caller's mode. The product of a loss and a sum insured in pence can pass 2^53 (a 12,000,000.00 building), so it is divided exactly as a big integer (i128 in Rust) before rounding. - `insuredProportionBasisPoints` is sum insured over value at risk, rounded down and capped at 10000, for display ("insured for 83.33% of value"). - `valueAtRisk` is the full reinstatement (or market) value at the time of the loss, on whatever basis the policy says; the loss may not exceed it. - Apply the excess with `insurance.excess-apply`; whether the excess comes off before or after average is a matter of the policy wording.
Files
| Path | Bytes |
|---|---|
| README.md | 1,986 |
| impl/python.py | 2,135 |
| impl/rust.rs | 3,332 |
| impl/typescript.ts | 2,377 |
| vectors.json | 9,352 |