Rating overview

In BriteLines, rating is performed by taking the details about a risk type (fields, items, rate tables, and calculations) and details about a quote (risks, field answers, chosen items) and combining them to determine each item’s premium, limits, and deductible.

Writing calculations

Calculations are single-line statements that return a numeric result.

Example:

baseRate * rateFactorTable * seniorDiscount

This calculation says to take the baseRate and multiply it by the rateFactorTable and the seniorDiscount.

Reference names

Each field, item, rate table, and calculation has a reference name. Calculations use this reference name to refer to the fields, items, rate tables, and calculations. This allows information about these entities to change (like updating a label or default value) without affecting rating logic.

Valid reference names

A reference name must be a valid Python identifier. This means a reference name can only start with a letter or underscore and can only contain letters, numbers, and underscores.

The following are valid reference names:

  • gender
  • medical_expense_baseRate_table
  • bodilyInjuryLimit
  • driver1
  • _id

The following are invalid reference names:

  • date-of-birth (contains hyphens)
  • $value (contains a dollar sign)
  • 1stdriver (starts with a number)

Reserved reference names

Some reference names are already reserved by BriteLines. These reference names are unavailable to be used by any field, rate table, calculation, or item.

The following reference names are reserved by BriteLines along with other Python built-ins:

  • bc
  • Q

Arithmetic operations

The following arithmetic operations can be performed on references that return numbers (fields of type number, rate tables, and calculations).

Addition

The + character is used to add numbers together.

baseRateTable + numberField + otherCalculation

Subtraction

The - character is used to subtract one number from another number.

baseRateTable - numberField

Multiplication

The * character is used to multiply numbers together.

baseRateTable * rateTableFactor

Division

The / character is used to divide one number by another number.

baseRateTable / 2

Comparison operations

Comparison operations compare values and return either true or false according to the condition.

Greater than

True if the left operand is greater than the right.

numOfDrivers > numOfVehicles

Less than

True if the left operand is less than the right.

numOfDrivers < numOfVehicles

Equal to

True if both operands are equal.

numOfDrivers == numOfVehicles

Not equal to

True if operands aren’t equal.

numOfDrivers != numOfVehicles

Greater than or equal to

True if the left operand is greater than or equal to the right.

numOfDrivers >= numOfVehicles

Less than or equal to

True if the left operand is less than or equal to the right.

numOfDrivers <= numOfVehicles

Scopes

Scopes determine which references are available for calculation. Shared calculations and item calculations have different scopes.

Shared calculations have a scope that can reference all fields, rate tables, and other shared calculations. They can’t reference specific item calculations.

Item calculations have a scope that can reference all fields, rate tables, shared calculations, and the other calculations for that item.

Because of these rules, shared calculation reference names must be unique across all fields, items, rate tables, and calculations. Item calculations reference names, however, only need to be unique across fields, items, rate tables, shared calculations, and the current item’s calculations.

Example:

The items bodilyInjury and comprehensive could both have calculations called baseRate, because the names wouldn’t clash. You couldn’t, however, have a shared calculation called baseRate, because when rating the bodilyInjury item, the rating engine wouldn’t know if it should use the value from the item’s baseRate calculation or the risk type’s baseRate shared calculation.

Circular reference errors

Calculations must not reference each other.

Example:

You couldn’t have the two calculations below. They would result in a circular reference error because baseRate refers to rateCalc and rateCalc refers to baseRate. The rating engine wouldn’t have any way of determining the value for either one.

baseRate

rateCalc * seniorDiscount

and…

rateCalc

baseRate * otherFactor

Calculation types

Calculations have the following types:

  • Variable
  • Premium
  • Limit
  • Deductible

Variable calculations simply perform the calculation and store it in the reference name. The result can then be used by other calculations. All shared calculations are variable calculations.

Premium, limit, and deductible calculations result in the item’s premium, limit, and deductible respectively.

Available variables

bc.policyInceptionDate

The date on which the policy was first created.

bc.age(bc.policyInceptionDate)

If the policy inception date is 2014-01-01 and the rating date is 2017-01-01, then the above calculation will resolve to 3.

bc.transactionEffectiveDate

The effective date of the current transaction.

bc.age(bc.transactionEffectiveDate)

bc.policyTermEffectiveDate

The effective date of the current term.

bc.age(bc.policyTermEffectiveDate)

bc.isTransactionNewBusiness

Returns True if the current transaction type is New Business; otherwise, it will return False.

2 if bc.isTransactionNewBusiness else 4

If the transaction type is New Business, then the above calculation will return 2; otherwise, it will return 4.

bc.isTransactionRenewal

Returns True if the current transaction type is Renewal; otherwise, it will return False.

2 if bc.isTransactionRenewal else 4

If the transaction type is Renewal, then the above calculation will return 2; otherwise, it will return 4.

bc.isTransactionEndorsement

Returns True if the current transaction type is Endorsement; otherwise, it will return False.

2 if bc.isTransactionEndorsement else 4

If the transaction type is Endorsement, then the above calculation will return 2; otherwise, it will return 4.

bc.isTransactionCancellation

Returns True if the current transaction type is Cancellation; otherwise, it will return False.

2 if bc.isTransactionCancellation else 4

If the transaction type is Cancellation, then the above calculation will return 2; otherwise, it will return 4.

bc.isTransactionRewrite

Returns True if the current transaction type is Rewrite; otherwise, it will return False.

2 if bc.isTransactionRewrite else 4

If the transaction type is Rewrite, then the above calculation will return 2; otherwise, it will return 4.

Available functions

The rating engine provides several functions that you can use in your calculations.

bc.optional

This function marks data fields, rate tables, and items as optional. If a rate table is marked as optional, then BriteLines will determine which fields the rate table uses as sources and mark them as optional.

To use bc.optional(), wrap the data field, rate table, or item reference names in the function.

bc.optional with Rate Tables

Example:

bc.min(primaryDriverRateTable, bc.optional(secondaryDriverRateTable))

By wrapping secondaryDriverRateTable as bc.optional, all of the data fields that secondaryDriverRateTable points to will be optional during quoting.

If secondaryDriverRateTable has no default value set or you want to override the default value, then you can pass a default value to the bc.optional function.

Note: The default value must be a number.

bc.min(primaryDriverRateTable, bc.optional(secondaryDriverRateTable, default=1))
bc.optional with Items

Example:

mandatoryItem.premium.term.value + bc.optional(optionalItem.premium.term.value, default=0)

In this example, we add a mandatory item’s premium to an optional item’s premium.

  • If the mandatory item’s premium is 50 and the optional item exists on the policy and has a premium of 50, then this calculation will return 100.
  • If the mandatory item’s premium is 50 and the optional item hasn’t been enabled for the policy, then this calculation will return 50.
  • If the mandatory item’s premium is 50 and the optional item exists on the policy but can’t be resolved yet, then this calculation will return 50.
Optional fields in quoting

During quoting, a field will only be considered optional if:

  1. All calculations that refer directly to the field are wrapped in bc.optional().
  2. All rate tables that refer to the field are wrapped in bc.optional().

This means a field can be optional or required depending on the items selected for a risk.

Example:

  • If a quote has Item 1 enabled, then all Item 1 calculations mark the additionalDriver field as optional. It is optional on the quote.
  • If Item 2 is enabled on the quote and it has calculations that refer to additionalDriver, which aren’t wrapped in bc.optional(), then Additional Driver becomes a required field (because it’s not filled out, then Item 2 couldn’t rate).

bc.age

Returns the age of something in years from a given date or number.

bc.age(dateOfBirth)

This allows us to always collect the date of origin and find its age dynamically. When we collect birthday, year built, and year purchased, bc.age() will always use the quote’s rating date to find the age based on these dates.

Example:

If the following are true:

  • You collect dateOfBirth as a date input
  • The user chooses “01/31/1992”
  • The rating date year is 2017

Then bc.age(dateOfBirth) will return 25.

This function isn’t simply the difference in years; it also accounts for aging in the current year. So if the rating date is “12/13/2017” and the user types “12/15/2000”, then the value is not 17, but 16 because the 15th hasn’t yet passed.

Users can also pass in numbers, in which case bc.age() assumes the number is a year. For instance, a calculation of bc.age(2010) will return 8 if the current year is 2018. This is useful for calculating the age of vehicles:

bc.age(vehicleModelYear)

bc.age() will always return the difference between the rating date and the value passed into the function. This means bc.age() may return a negative number for future dates and years. To ensure that no values lower than 0 are returned, change your calculation to:

bc.max(bc.age(vehicleModelYear), 0)`

bc.condition

Returns different results based on whether the condition is true or false.

bc.condition(hasAntiLockBrakes, 0.95, 1.0)

If the user has selected Yes for the hasAntiLockBrakes boolean field, this calculation would return 0.95. If the user hasn’t selected Yes, then it would return 1.0.

bc.if_item

Returns different results based on whether the risk has a specific item selected.

bc.if_item('comprehensive', 0.95, 1.0)

This calculation would return 0.95 if the comprehensive coverage is present on the quote. If the coverage isn’t present, then the calculation would return 1.0.

bc.round

Rounds values based on round_to and round_method.

bc.round(some_number, round_to=bc.NEAREST_HUNDRED, round_method=bc.ROUND_UP)

bc.round also works with an alternative syntax:

bc.round(some_number, 2)

This is a simplified version that would round some_number to the second decimal place.

round_to options
  • TWO_DECIMALS
  • ONE_DECIMAL
  • NEAREST_ONE
  • NEAREST_TEN
  • NEAREST_HUNDRED
  • NEAREST_THOUSAND

Note: round_to options default to TWO_DECIMALS (the nearest penny).

round_method options
  • bc.ROUND_UP
  • bc.ROUND_DOWN
  • bc.ROUND_CEILING
  • bc.ROUND_FLOOR
  • bc.ROUND_HALF_UP

Note: round_method options default to bc.ROUND_HALF_UP (natural rounding).

bc.max

Returns the maximum value from all values passed to it. If primaryDriverRateTable resolves to 800.0 and secondaryDriverRateTable resolves to 400.0, then the following function would return 800.0.

bc.max(primaryDriverRateTable, secondaryDriverRateTable)

bc.min

Returns the minimum value from all values passed to it. If primaryDriverRateTable resolves to 800.0 and secondaryDriverRateTable resolves to 400.0, then the following function would return 400.0.

bc.min(primaryDriverRateTable, secondaryDriverRateTable)

limits

Uses <itemName>.limits.<limitName> to access an item’s limits.

bodilyInjury.limits.bodilyInjuryLimit

Pro-Rata premium calculation

When a premium-bearing change is made to a policy mid-term, affected items must prorate the premium against the term. When a policy is canceled mid-term, items must be prorated based upon the cancellation effective date.

The pro-rata premium can be determined against different transaction types with different term lengths and granularity.

Pro-rata premium is calculated and stored per item. The total policy pro-rata premium and policy term premium are also calculated and stored. The total pro-rata premium and the policy term premium, as well as each individual item pro-rata premium and policy term premium, are displayed in Rate Preview.

The pro-rata premium calculation currently assumes a one-year policy term with “day” granularity, meaning that the premium is calculated to the day.

Total pro-rata premium is calculated as the sum of all individual item pro-rata premiums.

Pro-rata premium per item is calculated as follows:

((units * (calculated rate - prior rate))/granularity) + prior pro-Rata premium

Example

  • Term: 1 year
  • Effective date: 1/1/2017
  • Granularity: day (365 units for 1/1/2017 – 1/1/2018)

Policy Inception – 1/1/2017

Coverage A

  • Full term amount: $365
  • Variables:
    • Units = days(1/1/2018 – 1/1/2017) = 365
    • Calculated rate = 365
    • Prior rate = 0
    • Prior pro-rata premium = 0
  • Pro-rata premium = ((365 * (365 – 0)) / 365) + 0 = $365

Endorsement – 5/3/2017

Coverage A

  • Full term amount: $730
  • Variables:
    • Units = days(1/1/2018 – 5/3/2017) = 243
    • Calculated rate = 730
    • Prior rate = 365
    • Prior pro-rata premium = 365
  • Pro-rata premium = ((243 * (730 – 365)) / 365) + 365 = $608

Endorsement – 9/2/2017

Coverage A

  • Full term amount: $1095
  • Variables:
    • Units = days(1/1/2018 – 9/2/2017) = 121
    • Calculated rate = 1095
    • Prior rate = 730
    • Prior pro-rata premium = 608
  • Pro-rata premium = ((243 * (730 – 365)) / 365) + 365 = $729