A multi-location small business package rating property and liability per location.
A business owner's policy packages property and general liability cover for small and medium businesses into one policy. Many businesses have more than one site, so the model needs to price each location and combine them.
A child model prices property and liability for each location. The parent model sums the locations, then applies a business class factor and an experience credit for established businesses, plus a policy fee.
Policies with no locations or more than 10 are declined as outside standard BOP scope. Restaurants, new businesses and policies with four or more locations are referred.
Three further factors adjust the policy premium: recent claims, the security at the premises and the highest flood risk across the locations.
| Field | Label | Type | Default | Allowed values |
|---|---|---|---|---|
business_class | Business class | string | office | office, retail, light_manufacturing, restaurant |
years_in_business | Years in business | integer | 5 | 1, 3, 5, 10, 20 |
locations | Locations | array | list | A list of items, each rated by the child model |
claims_3y | Claims in the last 3 years | integer | 0 | 0, 1, 2, 3 |
security | Security | string | standard | none, standard, monitored_alarm |
flood_exposure | Flood exposure | string | low | low, medium, high |
Steps run in order. Each one can use the inputs and the outputs of every step before it.
Load or discount by business class. Restaurants attract the highest liability load.
{
"id": "class_rates",
"key": "class_rates",
"step": "collection",
"name": "Business class factor",
"retain": true,
"collection": [
["business_class", "class_factor"],
["office", 0.9],
["retail", 1.1],
["light_manufacturing", 1.3],
["restaurant", 1.55]
],
"format": {
"class_factor": {
"key": "class_factor",
"label": "Business class factor",
"exp": "collection.filter(business_class=={{business_class}}).map(class_factor).first()",
"type": "decimal",
"def": 1
}
}
}Businesses trading 5+ years get up to 15% credit, capped at 20 years.
{
"id": "experience_calc",
"key": "experience_calc",
"step": "calculation",
"name": "Experience credit",
"def": 1,
"formula": "max(1 - (min({{years_in_business}}, 20) - 5) * 0.01, 0.85)"
}Iterate locations[]. Child model returns each location's property + GL premium; parent sums into a policy total.
Prices a single BOP location: property (buildings + contents) + general liability.
State rate lookup. Property and GL base rates per $1000 by state.
Property premium. (Building + contents value) in thousands × state property rate.
General liability premium. Annual sales in thousands × state GL rate.
{
"id": "per_location_rating",
"key": "per_location_rating",
"step": "modules",
"name": "Per-location rating",
"format": {
"locations_total": {
"key": "locations_total",
"label": "Sum of per-location premiums",
"exp": "collection.sum(result)",
"type": "decimal",
"def": 0
},
"locations_count": {
"key": "locations_count",
"label": "Number of locations",
"exp": "collection.count()",
"type": "integer",
"def": 0
}
},
"child_model_steps": [
{
"id": "state_rates",
"key": "state_rates",
"step": "collection",
"name": "State rate lookup",
"retain": true,
"collection": [
["state", "state_property_rate", "state_gl_rate"],
["CA", 3.2, 0.8],
["NY", 3.6, 0.95],
["TX", 2.4, 0.6],
["FL", 4.5, 0.85],
["IL", 2.8, 0.7]
],
"format": {
"state_property_rate": {
"key": "state_property_rate",
"label": "State property rate per $1000",
"exp": "collection.filter(state=={{state}}).map(state_property_rate).first()",
"type": "decimal",
"def": 3
},
"state_gl_rate": {
"key": "state_gl_rate",
"label": "State GL rate per $1000",
"exp": "collection.filter(state=={{state}}).map(state_gl_rate).first()",
"type": "decimal",
"def": 0.75
}
}
},
{
"id": "property_premium",
"key": "property_premium",
"step": "calculation",
"name": "Property premium",
"def": 0,
"formula": "round((({{building_value}} + {{contents_value}}) / 1000) * {{state_property_rate}}, 2)"
},
{
"id": "gl_premium",
"key": "gl_premium",
"step": "calculation",
"name": "General liability premium",
"def": 0,
"formula": "round(({{sales}} / 1000) * {{state_gl_rate}}, 2)"
}
]
}Load for claims in recent years. A clean record is neutral; each band of claims adds a larger load.
{
"id": "bop_claims_rates",
"key": "bop_claims_rates",
"step": "collection",
"name": "Claims history load",
"retain": true,
"collection": [
["claims_3y_min", "claims_3y_max", "bop_claims_factor_rel"],
[0, 0, 1],
[1, 1, 1.15],
[2, 2, 1.3],
[3, 99, 1.5]
],
"format": {
"bop_claims_factor": {
"key": "bop_claims_factor",
"label": "Claims history load",
"exp": "collection.filter(claims_3y_min<={{claims_3y}}).filter(claims_3y_max>={{claims_3y}}).map(bop_claims_factor_rel).first()",
"type": "decimal",
"def": 1
}
}
}Alarms and monitoring reduce theft and fire losses across the business's premises.
{
"id": "bop_security_rates",
"key": "bop_security_rates",
"step": "collection",
"name": "Security factor",
"retain": true,
"collection": [
["security_value", "bop_security_factor_rel"],
["none", 1.1],
["standard", 1],
["monitored_alarm", 0.92]
],
"format": {
"bop_security_factor": {
"key": "bop_security_factor",
"label": "Security factor",
"exp": "collection.filter(security_value=={{security}}).map(bop_security_factor_rel).first()",
"type": "decimal",
"def": 1
}
}
}Locations in higher flood risk areas carry a load for property damage and business interruption.
{
"id": "bop_flood_rates",
"key": "bop_flood_rates",
"step": "collection",
"name": "Flood exposure factor",
"retain": true,
"collection": [
["flood_exposure_value", "bop_flood_factor_rel"],
["low", 1],
["medium", 1.1],
["high", 1.3]
],
"format": {
"bop_flood_factor": {
"key": "bop_flood_factor",
"label": "Flood exposure factor",
"exp": "collection.filter(flood_exposure_value=={{flood_exposure}}).map(bop_flood_factor_rel).first()",
"type": "decimal",
"def": 1
}
}
}Sum of per-location premiums × business class factor × experience credit, plus £100 policy fee. Multiplied by the claims history load, security factor, flood exposure factor.
{
"id": "policy_premium",
"key": "policy_premium",
"step": "calculation",
"name": "Policy premium",
"def": 0,
"formula": "round(({{locations_total}} * {{class_factor}} * {{experience_calc}} * {{bop_claims_factor}} * {{bop_security_factor}} * {{bop_flood_factor}}) + 100, 2)"
}Decline policies with no locations supplied or with more than 10 locations (outside standard BOP scope).
{
"id": "bop_exclusions",
"key": "bop_exclusions",
"step": "exclusion",
"name": "BOP exclusions",
"def": false,
"items": [
{
"id": "no_locations",
"name": "No locations supplied",
"exp": "{{locations_count}} == 0",
"def": false
},
{
"id": "too_many_locations",
"name": "More than 10 locations",
"exp": "{{locations_count}} > 10",
"def": false
}
]
}Refer restaurants (high liability), new businesses, and multi-site policies with 4+ locations for underwriter review.
{
"id": "bop_refers",
"key": "bop_refers",
"step": "refer",
"name": "BOP refers",
"def": false,
"items": [
{
"id": "new_business",
"name": "New business (<2 years trading)",
"exp": "{{years_in_business}} < 2",
"def": false
},
{
"id": "restaurant_class",
"name": "Restaurant class",
"exp": "{{business_class}} == 'restaurant'",
"def": false
},
{
"id": "many_locations",
"name": "4+ locations",
"exp": "{{locations_count}} >= 4",
"def": false
}
]
}This quote was priced by the Ptally rating engine. The trace below follows it through every step to the final result.
{
"business_class": "office",
"years_in_business": 8,
"locations": [
{
"state": "CA",
"building_value": 500000,
"contents_value": 80000,
"sales": 750000
}
],
"claims_3y": 1,
"security": "monitored_alarm",
"flood_exposure": "medium"
}class_factor = 0.9max(1 - (min(8, 20) - 5) * 0.01, 0.85) = 0.97Item 1 {"state":"CA","building_value":500000,"contents_value":80000,"sales":750000} → 2,456locations_total = 2,456 · locations_count = 1bop_claims_factor = 1.15bop_security_factor = 0.92bop_flood_factor = 1.1round((2,456 * 0.9 * 0.97 * 1.15 * 0.92 * 1.1) + 100, 2) = 2,595.29No locations supplied: not firedMore than 10 locations: not firedNew business (<2 years trading): not firedRestaurant class: not fired4+ locations: not firedSelect business owner's policy (bop) as the default model for a new project, then change the rates, rules and tests to match your product. Our consultants can do this for you, with no implementation fee.