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 Policy2Response object.

Each ratable peril characteristics must be rated with a RaterPriceResponse 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": "master/rater.js",
      "enabled": true
    }
  }
}

Data Object

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

RaterPluginData
required
operationstring new_business | endorsement | renewal | reinstatement
policyExposurePerils[RaterPluginPolicyExposurePeril]
tenantTimeZonestring

optional
endorsementLocatorstring
newPaymentScheduleNamestring
quoteLocatorstring
renewalLocatorstring
RaterPluginPolicyExposurePeril
required
exposureCharacteristicsLocatorstring
perilCharacteristicsLocatorstring
policyCharacteristicsLocatorstring

Return Object

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

RaterPluginResponse
required
pricedPerilCharacteristicsmap<string,RaterPriceResponse>

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

RaterPriceResponse
required
yearlyPremiumnumber

optional
yearlyTechnicalPremiumnumber
RaterCommissionsResponse
required
recipientstring
yearlyAmountnumber

Validation

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

  • Each key in the pricedPerilCharacteristics map must correspond to a perilCharacteristicsLocator in the RaterPluginPolicyExposurePeril.

  • Likewise, each perilCharacteristicsLocator in the RaterPluginPolicyExposurePeril must have a corresponding entry in the pricedPerilCharacteristics map.

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

Sample Implementation

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, look at its peril to get a rating
  // factor and then multiply by vehicle value to get the premium.
  for (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;