Travel

A per-traveller travel model that rates each traveller by age, then applies destination and trip length.

A default model in Ptally. Personal Travel: Per-Traveler Modules is one of the default models you can select when you start a new project in Ptally. It comes with its rate tables, rules and 6 tests, ready to adapt to your own product.
The line of business

Travel

Travel insurance covers medical costs, cancellation and other losses while a customer is away. A single policy often covers several travellers of different ages, and cost depends heavily on destination and trip length.

Methodology

How the model rates

Each traveller is priced individually by a child model that looks up a base premium by age band. The parent model adds the travellers together, then applies a destination factor and a trip length factor, plus a policy fee.

This is the modules pattern: one quote carries a list of travellers, and the same child model rates each one. Travellers over 85 and trips over 90 days are declined, and long USA trips are referred.

Three further factors adjust the policy premium: the cover level chosen, whether winter sports are included and whether pre-existing medical conditions are declared.

6inputs
9steps
4lookup tables
6tests

Inputs

6 fields
FieldLabelTypeDefaultAllowed values
destination_regionDestination regionstringeuropeeurope, worldwide_ex_usa, worldwide_inc_usa
trip_length_daysTrip length (days)integer73, 7, 14, 21, 30, 60
travelersTravelersarraylistA list of items, each rated by the child model
cover_levelCover levelstringstandardessential, standard, premium
winter_sportsWinter sports coverbooleanfalsefalse, true
medical_conditionsDeclared medical conditionsbooleanfalsefalse, true
The model

Step by step

Steps run in order. Each one can use the inputs and the outputs of every step before it.

01Lookup table
Destination region loading

Look up a multiplier by destination region. Worldwide + USA is the most expensive.

{
  "id": "region_rates",
  "key": "region_rates",
  "step": "collection",
  "name": "Destination region loading",
  "retain": true,
  "collection": [
    ["destination_region", "region_factor"],
    ["europe", 1],
    ["worldwide_ex_usa", 1.6],
    ["worldwide_inc_usa", 2.2]
  ],
  "format": {
    "region_factor": {
      "key": "region_factor",
      "label": "Destination region factor",
      "exp": "collection.filter(destination_region=={{destination_region}}).map(region_factor).first()",
      "type": "decimal",
      "def": 1
    }
  }
}
02Calculation
Trip length factor

Longer trips attract a load. 7 days = 1.0, 30 days ≈ 2.3, capped at 3.0.

{
  "id": "trip_length_calc",
  "key": "trip_length_calc",
  "step": "calculation",
  "name": "Trip length factor",
  "def": 1,
  "formula": "min(1 + ({{trip_length_days}} - 7) * 0.06, 3.0)"
}
03Modules
Per-traveler rating

Iterate the travelers[] array. Child model returns each traveler's base premium; parent sums into policy total and finds the oldest.

Child model: Per-Traveler Rating (child)

Child model. Prices a single traveler by age band.

Age-band base rate. Look up a per-traveler base premium by age band. Older travelers cost more.

{
  "id": "per_traveler_rating",
  "key": "per_traveler_rating",
  "step": "modules",
  "name": "Per-traveler rating",
  "format": {
    "travelers_base_total": {
      "key": "travelers_base_total",
      "label": "Sum of per-traveler base premiums",
      "exp": "collection.sum(result)",
      "type": "decimal",
      "def": 0
    },
    "travelers_max_age": {
      "key": "travelers_max_age",
      "label": "Oldest traveler age",
      "exp": "collection.map(age).max()",
      "type": "integer",
      "def": 0
    }
  },
  "child_model_steps": [
    {
      "id": "age_band_rates",
      "key": "age_band_rates",
      "step": "collection",
      "name": "Age-band base rate",
      "retain": true,
      "collection": [
        ["age_max", "band_base"],
        [17, 22],
        [40, 28],
        [55, 38],
        [70, 60],
        [80, 110],
        [200, 200]
      ],
      "format": {
        "band_base": {
          "key": "band_base",
          "label": "Age-band base premium",
          "exp": "collection.filter(age_max>={{age}}).map(band_base).first()",
          "type": "decimal",
          "def": 28
        }
      }
    }
  ]
}
04Lookup table
Cover level factor

Higher cover levels pay more for medical expenses, cancellation and baggage, so they cost more.

{
  "id": "cover_level_rates",
  "key": "cover_level_rates",
  "step": "collection",
  "name": "Cover level factor",
  "retain": true,
  "collection": [
    ["cover_level_value", "cover_level_factor_rel"],
    ["essential", 0.8],
    ["standard", 1],
    ["premium", 1.35]
  ],
  "format": {
    "cover_level_factor": {
      "key": "cover_level_factor",
      "label": "Cover level factor",
      "exp": "collection.filter(cover_level_value=={{cover_level}}).map(cover_level_factor_rel).first()",
      "type": "decimal",
      "def": 1
    }
  }
}
05Lookup table
Winter sports factor

Skiing and snowboarding carry a much higher chance of injury and rescue costs.

{
  "id": "winter_sports_rates",
  "key": "winter_sports_rates",
  "step": "collection",
  "name": "Winter sports factor",
  "retain": true,
  "collection": [
    ["winter_sports_value", "winter_sports_factor_rel"],
    [false, 1],
    [true, 1.4]
  ],
  "format": {
    "winter_sports_factor": {
      "key": "winter_sports_factor",
      "label": "Winter sports factor",
      "exp": "collection.filter(winter_sports_value=={{winter_sports}}).map(winter_sports_factor_rel).first()",
      "type": "decimal",
      "def": 1
    }
  }
}
06Lookup table
Declared medical conditions factor

Pre-existing medical conditions that are declared and covered increase the expected medical cost.

{
  "id": "medical_rates",
  "key": "medical_rates",
  "step": "collection",
  "name": "Declared medical conditions factor",
  "retain": true,
  "collection": [
    ["medical_conditions_value", "medical_factor_rel"],
    [false, 1],
    [true, 1.5]
  ],
  "format": {
    "medical_factor": {
      "key": "medical_factor",
      "label": "Declared medical conditions factor",
      "exp": "collection.filter(medical_conditions_value=={{medical_conditions}}).map(medical_factor_rel).first()",
      "type": "decimal",
      "def": 1
    }
  }
}
07Calculation
Policy premium

Sum of per-traveler bases × region factor × trip-length factor, plus £5 policy fee. Multiplied by the cover level factor, winter sports factor, declared medical conditions factor.

{
  "id": "policy_premium",
  "key": "policy_premium",
  "step": "calculation",
  "name": "Policy premium",
  "def": 0,
  "formula": "round(({{travelers_base_total}} * {{region_factor}} * {{trip_length_calc}} * {{cover_level_factor}} * {{winter_sports_factor}} * {{medical_factor}}) + 5, 2)"
}
08Decline rules
Travel exclusions

Decline travelers over 85 or trips longer than 90 days: outside standard product terms.

{
  "id": "travel_exclusions",
  "key": "travel_exclusions",
  "step": "exclusion",
  "name": "Travel exclusions",
  "def": false,
  "items": [
    {
      "id": "traveler_too_old",
      "name": "Traveler over 85",
      "exp": "{{travelers_max_age}} > 85",
      "def": false
    },
    {
      "id": "trip_too_long",
      "name": "Trip over 90 days",
      "exp": "{{trip_length_days}} > 90",
      "def": false
    }
  ]
}
09Refer rules
Travel refers

Refer USA trips longer than 21 days for underwriter review.

{
  "id": "travel_refers",
  "key": "travel_refers",
  "step": "refer",
  "name": "Travel refers",
  "def": false,
  "items": [
    {
      "id": "long_usa_trip",
      "name": "Long USA trip",
      "exp": "{{destination_region}} == 'worldwide_inc_usa' && {{trip_length_days}} > 21",
      "def": false
    }
  ]
}
Worked example

Worked example: premium cover with winter sports

This quote was priced by the Ptally rating engine. The trace below follows it through every step to the final result.

Quote input
{
  "destination_region": "europe",
  "trip_length_days": 7,
  "travelers": [
    {
      "age": 30
    }
  ],
  "cover_level": "premium",
  "winter_sports": true,
  "medical_conditions": false
}
Result57.92VALID
Calculation trace
  1. Destination region loadingLookup table
    region_factor = 1
  2. Trip length factorCalculation
    min(1 + (7 - 7) * 0.06, 3.0) = 1
  3. Per-traveler ratingModules
    Item 1 {"age":30} → 28travelers_base_total = 28 · travelers_max_age = 30
  4. Cover level factorLookup table
    cover_level_factor = 1.35
  5. Winter sports factorLookup table
    winter_sports_factor = 1.4
  6. Declared medical conditions factorLookup table
    medical_factor = 1
  7. Policy premiumCalculation
    round((28 * 1 * 1 * 1.35 * 1.4 * 1) + 5, 2) = 57.92
  8. Travel exclusionsDecline rules
    Traveler over 85: not firedTrip over 90 days: not fired
  9. Travel refersRefer rules
    Long USA trip: not fired
Start from this model

Select travel 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.

Talk to our team