Rating Plugin

This page describes instructions for using the new JavaScript-based rating engine for Socotra, which is replacing the existing Liquid-based rating system.

The plugin will rate all perils for a policy transaction at one time. This allows doing calculations that are common across perils one time, and also makes it easier to manage situations where there are interdependencies between perils, such as in “minimum total premium” scenarios.

The basic approach is that a set of peril characteristics objects each need to be rated. The identities of those characteristics are passed by locator in the policyExposurePerils property of the data object, and each can be correlated with actual objects that are found in the policy property, which contains a full PolicyResponse object.

Each ratable peril characteristics must be rated with a RatingPluginPriceResponse object in the return object.

Note

This topic does not apply to rating for Premium Reports. Those continue to use Liquid rating.

Enabling the Plugin

To enable the rating plugin for a product, in that product’s policy.json file, add a getPerilRates property to the plugin enablement section. For example:

{
  "plugins": {
    "getPerilRates": {
      "path": "main/rater.js",
      "enabled": true
    }
  }
}

Rating Calculations

Rating is done on a “per-peril” basis, where each segment of each peril is priced independently. There are three options for how the pricing calculation is done:

  • Specify the premium rate and let Socotra compute the actual price by multiplying by the duration of the segment; or

  • Specify the actual premium for the segment and let Socotra infer the rate; or

  • Specify both the actual premium and the rate

You may want to specify exact premium for situations like these:

  • Charge a flat dollar amount in certain situations

  • Apply special requirements for premium amounts, such as rounding to the nearest dollar or nickel

  • Scale by a different method than months, such as by days, or “360 day years”, etc.

  • Calculate month durations differently than the default calculation

  • Have the persisted rate to be different from the effective rate, such as in rounding operations

The choice is made by using the properties yearlyPremium and exactPremium for each pricing calculation that you return from the plugin. The rules are as follows:

When returning each RatingPluginPriceResponse from the rating plugin, you may now choose to set either yearlyPremium, exactPremium, or both.

If yearlyPremium is set and exactPremium is not set, the system will divide the yearlyPremium by twelve and store it on the monthPremium property of the peril characteristics. It will also calculate the price by multiplying the yearly premium rate by the duration (in years) of the segment, and store it on the premium property, subject to rounding based on the tenant currency (two places after the decimal for most currencies).

Note

Though the plugin will use the yearlyPrmeium property to indicate the rate, it will be divided by twelve and stored as monthPremium on the PerilCharacteristicsResponse. The yearlyPremium itself will not be persisted.

If exactPremium is set and yearlyPremium is not set, then the exactPremium is the amount that will be rounded and stored on the peril characteristics on the premium property. In addition, the exactPremium amount will be divided by the number of months in the interval (using the same month counting algorithm already used for scaling) and will be persisted as monthPremium.

If yearlyPremium is set and exactPremium is also set, then each value will be stored independantly on the peril characteristics as specified, rounded as appropriate (note that yearlyPremium will be divided by 12 and stored as monthPremium; yearlyPremium will not be persisted). In this way you can control both values precisely and independently.

Note

You can mix how each peril characteristics object is handled. For example, you could set exactPremium for one and yearlyPremium for another, and both for a third.

Data Object

The following data object is passed to the plugin for use by the plugin code:

RatingPluginData
required
operation string new_business | endorsement | renewal | reinstatement
policyExposurePerils [RatingPluginCharacteristics]
tenantTimeZone string

optional
endorsementLocator string
newPaymentScheduleName string
quoteLocator string
renewalLocator string
RatingPluginCharacteristics
required
policyCharacteristicsLocator string
exposureCharacteristicsLocator string
perilCharacteristicsLocator string

Return Object

The object returned from the plugin should take the following form:

RatingPluginResponse
required
pricedPerilCharacteristics map<string,RatingPluginPriceResponse>

optional
exceptionMessage string

The pricedPerilCharacteristics property is a map of the peril characteristics locators that are being priced to the specific pricing information:

RatingPluginPriceResponse
optional
exactPremium string
yearlyPremium string
yearlyTechnicalPremium string
RatingPluginCommissionsResponse
required
recipient string

optional
yearlyAmount string

Validation

The response from the plugin must conform to the following criteria:

  • The keys in the return object’s pricedPerilCharacteristics map must correspond 1:1 with each perilCharacteristicsLocator in the input’s policyExposurePerils.

  • No priced characteristics other than those specified in the request should be returned.

  • Commissions are optional, but if they are included they must each include both a recipient and a yearlyAmount.

Script Example: Calculating by Rate

The following implementation of rater.js illustrates a simplified rating algorithm:

function getPerilRates(data)
{
  let policy = data.policy;

  // This is the return object that we'll decorate with rating calculations
  let ret = { pricedPerilCharacteristics: {} };

  // For each peril characteristics object, look at its peril to get a rating
  // factor and then multiply by vehicle value to get the premium.
  for (const pep of data.policyExposurePerils)
  {
    let peril = getPerilFromPerilCharacteristicsLocator(policy, pep);
    let exposureCharacteristics = getExposureCharacteristicsFromExposureCharacteristicsLocator(policy, pep);
    let vehicleValue = parseFloat(exposureCharacteristics.fieldValues.vehicle_value[0]);

    let ratingFactor;

    switch (peril.name)
    {
      case "bodily_injury": ratingFactor = 52.0; break;
      case "collision": ratingFactor = 45.5; break;
      case "comprehensive": ratingFactor = 32.5; break;
      default: ratingFactor = 12.0; break;
    }

    let yearlyPremium = round2(vehicleValue * ratingFactor / 1000);
    ret.pricedPerilCharacteristics[pep.perilCharacteristicsLocator] = {
      yearlyPremium: yearlyPremium,
      yearlyTechnicalPremium: round2(yearlyPremium * 0.8),
      commissions:[
          {
            yearlyAmount: round2(yearlyPremium * 0.1),
            recipient: "broker_abc" // a code that specifies who gets the commission
          }
      ]
    }
  }
  return ret;
}
function getPerilFromPerilCharacteristicsLocator(policy, pep)
{
  for (exp of policy.exposures)
    for (peril of exp.perils)
      if (peril.characteristics.some(c => c.locator == pep.perilCharacteristicsLocator))
        return peril;
  throw `Peril characteristics ${pep.perilCharacteristicsLocator} not found!`;
}
function getExposureCharacteristicsFromExposureCharacteristicsLocator(policy, pep)
{
  for (exp of policy.exposures)
    if (exp.characteristics.some(c => c.locator == pep.exposureCharacteristicsLocator))
      return exp.characteristics.find(c => c.locator == pep.exposureCharacteristicsLocator);
  throw `ExposureCharacteristics ${pep.exposureCharacteristicsLocator} not found!`;
}
function round2(amount)
{
  return Math.round(amount * 100.0) / 100.0;
}

exports.getPerilRates = getPerilRates;

Script Example: Calculating Exact Premium

Suppose you have existing rating logic that calculates a yearly premium, but you want to assess it using a 360 day per year algorithm. You could then do something like this:

Note

This Zip file contains the required library and associated support files for this example.

const { DateCalc } = require('../lib/DateCalc.js');
const { PolicyContext } = require('../lib/PolicyContext.js');

function getPerilRates(data)
{
  const dateCalc = new DateCalc(data.tenantTimeZone,
                                parseInt(data.policy.originalContractStartTimestamp));
  const context = preprocessContext(new PolicyContext(data.policy));
  const charsToRate = data.policyExposurePerils
                          .map(pep => context.getPerilCharacteristics(pep.perilCharacteristicsLocator));

  const prices = {};

  for (const ch of charsToRate)
  {
    context.destringifyCharacteristicsTimestamps(ch);

    // Assume some calculations have set the variable annualPremium
    const annualPremium = 500;

    // Calculate the number of days being rated
    const days = dateCalc.dayCountWhole(ch.coverageStartTimestamp,
                                        ch.coverageEndTimestamp);

    prices[ch.locator] = { yearlyPremium: annualPremium,
                           exactPremium: annualPremium * days / 365 };
  }
  return { pricedPerilCharacteristics: prices };
}
function preprocessContext(context)
{
  for (const perCh of context.allPerilCharacteristics())
  {
    perCh.coverageStartTimestamp = parseInt(perCh.coverageStartTimestamp);
    perCh.coverageEndTimestamp = parseInt(perCh.coverageEndTimestamp);
  }
  return context;
}

exports.getPerilRates = getPerilRates;