Skip to content

Instantly share code, notes, and snippets.

@Konyuka
Last active December 30, 2025 10:19
Show Gist options
  • Select an option

  • Save Konyuka/30c8079d81af2b2aaf7517b9b238f2fc to your computer and use it in GitHub Desktop.

Select an option

Save Konyuka/30c8079d81af2b2aaf7517b9b238f2fc to your computer and use it in GitHub Desktop.
Exposure Rating Module Integration Roadmap

Exposure Rating Module - Technical Documentation

Document Information

Field Value
Module Name Exposure Rating
Version 1.0.0
Status ✅ Implemented
Last Updated December 30, 2025
Author Actuarial Development Team
System Acentre Reinsurance & Actuarial System

1. Executive Summary

The Exposure Rating Module has been successfully integrated into the Acentre Reinsurance & Actuarial System. This module implements a deterministic exposure curve methodology for pricing Excess of Loss (XL) reinsurance layers, complementing the existing frequency-severity simulation approach and providing dual-method pricing capabilities.

1.1 Key Features Implemented

Feature Status Description
Core Exposure Curve Engine ✅ Complete Swiss Re exposure curve methodology with G(x) function
Rate on Line (ROL) Calculator ✅ Complete Poisson-weighted factor approach
Layer-by-Layer Calculation ✅ Complete Multi-layer XL pricing support
Class Curve Mapping ✅ Complete 22 default insurance class mappings
Interactive Curve Visualizer ✅ Complete Real-time Chart.js visualization
Results Storage & History ✅ Complete Full audit trail with metadata
Simulation Comparison ✅ Complete Side-by-side exposure vs simulation results
Excel Profile Integration ✅ Complete Works with Data Cleaning module outputs
Responsive UI ✅ Complete Consistent styling with Data Cleaning module

2. System Architecture

2.1 Technology Stack

Layer Technology Version
Backend Framework Laravel 10.x
Frontend Framework Vue 3 3.x
Bridge Inertia.js Latest
CSS Framework Tailwind CSS 3.x
Charts Chart.js 4.x
Math Library MathPHP 2.x
Icons Font Awesome 6.x
Date Formatting Moment.js 2.x

2.2 Module Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────┐
│                          EXPOSURE RATING MODULE                          │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                         FRONTEND (Vue 3)                          │   │
│  ├──────────────────────────────────────────────────────────────────┤   │
│  │  ExposureRating.vue (Main Page)                                   │   │
│  │  ├── Tab Navigation (Calculate | Results | History)              │   │
│  │  ├── Progress Steps Indicator                                     │   │
│  │  ├── LayerResultsTable.vue                                        │   │
│  │  ├── CurveVisualizer.vue                                          │   │
│  │  ├── ClassCurveEditor.vue                                         │   │
│  │  └── ComparisonView.vue                                           │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                    │                                     │
│                            Inertia.js Bridge                             │
│                                    │                                     │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                        BACKEND (Laravel)                          │   │
│  ├──────────────────────────────────────────────────────────────────┤   │
│  │  ExposureRatingController.php                                     │   │
│  │  ├── index() - Load page with scenarios, reconciliations         │   │
│  │  ├── calculate() - Execute exposure rating calculation           │   │
│  │  ├── getResults() - Retrieve calculation results                  │   │
│  │  ├── compare() - Compare with simulation results                  │   │
│  │  ├── getCurveData() - Get curve visualization data                │   │
│  │  ├── saveCurveConfig() - Save curve parameters                    │   │
│  │  └── saveClassCurve() - Save class curve mappings                 │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                    │                                     │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                          SERVICES                                 │   │
│  ├──────────────────────────────────────────────────────────────────┤   │
│  │  ExposureCurveService.php     - Core G(x) calculations           │   │
│  │  ExposureRatingCalculator.php - Layer loss calculations          │   │
│  │  ROLCalculator.php            - Rate on Line computation         │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                    │                                     │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │                           MODELS                                  │   │
│  ├──────────────────────────────────────────────────────────────────┤   │
│  │  ExposureCurveConfig.php      - Curve parameters (c4, c5, c6, c7)│   │
│  │  ClassCurveParameter.php      - Class-to-curve mappings          │   │
│  │  ExposureRatingResult.php     - Calculation results storage      │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

2.3 Data Flow

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Scenario      │     │  Reconciliation │     │  Class Curve    │
│   Selection     │────▶│   (Excel Data)  │────▶│   Mapping       │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                        │
                                                        ▼
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Layer Results  │◀────│   Exposure      │◀────│  G(x) Curve     │
│  & ROL/LOL      │     │   Calculator    │     │  Calculation    │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │
        ▼
┌─────────────────┐     ┌─────────────────┐
│   XL Rates      │     │   Comparison    │
│   Update        │────▶│   with Sim      │
└─────────────────┘     └─────────────────┘

3. Mathematical Foundation

3.1 Exposure Curve Formula

The exposure rating approach uses the Swiss Re Exposure Curve methodology:

Core G(x) Function

G(x) = MAX(MIN(LN((((g-1)*b)+((1-b*g)*b^x))/(1-b))/LN(b*g), 1), 0)

Where:

  • x = Damage ratio (Layer boundary / Sum Insured), bounded [0, 1]
  • b = exp(C4 + C5 * c * (1 + c))
  • g = exp((C6 + C7 * c) * c)
  • c = Class-specific curve parameter

Default Configuration Constants

Constant Default Value Description
C4 3.1 Base curvature parameter
C5 -0.15 Class sensitivity for b
C6 0.78 Base scaling parameter
C7 0.12 Class sensitivity for g
Exposure Factor 0.25 Expected loss ratio

3.2 Layer Loss Calculation

For each layer defined by (lower bound, upper bound):

Layer Loss = (G(upper_ratio) - G(lower_ratio)) × Net_Premium × Exposure_Factor

Where:

  • Upper ratio = min(upper_bound / Net_SI, 1)
  • Lower ratio = min(lower_bound / Net_SI, 1)

3.3 Rate on Line (ROL) Calculation

Uses Poisson-weighted factor approach:

Factor = Σ(k+1) × P(X=k) for k = 0 to 4+
ROL = LOL / Factor

Poisson Probabilities (where λ = LOL):

  • P(0) = exp(-λ)
  • P(1) = λ × exp(-λ)
  • P(2) = (λ² × exp(-λ)) / 2!
  • P(3) = (λ³ × exp(-λ)) / 3!
  • P(4+) = 1 - Σ P(k) for k=0 to 3

4. Database Schema

4.1 Tables Created

4.1.1 exposure_curve_configs

Stores curve configuration parameters.

CREATE TABLE exposure_curve_configs (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    c4 DECIMAL(10,6) DEFAULT 3.1,
    c5 DECIMAL(10,6) DEFAULT -0.15,
    c6 DECIMAL(10,6) DEFAULT 0.78,
    c7 DECIMAL(10,6) DEFAULT 0.12,
    exposure_factor DECIMAL(10,6) DEFAULT 0.25,
    user_id BIGINT UNSIGNED NULL,
    is_default BOOLEAN DEFAULT FALSE,
    description TEXT NULL,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

4.1.2 class_curve_parameters

Stores class-to-curve parameter mappings.

CREATE TABLE class_curve_parameters (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    class_name VARCHAR(255) NOT NULL,
    curve_parameter_c DECIMAL(10,4) NOT NULL,
    description TEXT NULL,
    user_id BIGINT UNSIGNED NULL,
    is_default BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

4.1.3 exposure_rating_results

Stores calculation results with full audit trail.

CREATE TABLE exposure_rating_results (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    scenario_id BIGINT UNSIGNED NOT NULL,
    reconciliation_id BIGINT UNSIGNED NULL,
    user_id BIGINT UNSIGNED NOT NULL,
    calculation_name VARCHAR(255) NULL,
    layer_number TINYINT NOT NULL,
    layer_limit DECIMAL(20,2),
    layer_excess DECIMAL(20,2),
    total_expected_loss DECIMAL(20,2),
    loss_on_line DECIMAL(10,6),
    rate_on_line DECIMAL(10,6),
    exposure_rate DECIMAL(10,6),
    calculation_metadata JSON NULL,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (scenario_id) REFERENCES scenarios(id) ON DELETE CASCADE,
    FOREIGN KEY (reconciliation_id) REFERENCES reconciliations(id) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    INDEX idx_scenario_layer (scenario_id, layer_number),
    INDEX idx_user_created (user_id, created_at)
);

4.1.4 xl_rates Table Modification

Added exposure rate columns:

ALTER TABLE xl_rates ADD COLUMN exposure_l1_rate DECIMAL(10,6) NULL;
ALTER TABLE xl_rates ADD COLUMN exposure_l2_rate DECIMAL(10,6) NULL;
ALTER TABLE xl_rates ADD COLUMN exposure_l3_rate DECIMAL(10,6) NULL;
ALTER TABLE xl_rates ADD COLUMN exposure_l4_rate DECIMAL(10,6) NULL;
ALTER TABLE xl_rates ADD COLUMN exposure_l5_rate DECIMAL(10,6) NULL;
ALTER TABLE xl_rates ADD COLUMN exposure_l6_rate DECIMAL(10,6) NULL;

4.2 Migration Files

File Purpose
2025_12_30_100000_create_exposure_curve_configs_table.php Creates curve configs table
2025_12_30_100001_create_class_curve_parameters_table.php Creates class curve mappings table
2025_12_30_100002_create_exposure_rating_results_table.php Creates results storage table
2025_02_06_124240_add_exposure_rates_to_xl_rates_table.php Adds exposure columns to XL rates

5. Backend Implementation

5.1 File Structure

app/
├── Http/
│   ├── Controllers/
│   │   └── ExposureRatingController.php    # Main controller (463 lines)
│   └── Requests/
│       └── ExposureRatingRequest.php       # Form validation
├── Models/
│   ├── ExposureCurveConfig.php             # Curve config model (75 lines)
│   ├── ClassCurveParameter.php             # Class curve model (131 lines)
│   └── ExposureRatingResult.php            # Results model (97 lines)
└── Services/
    └── ExposureRating/
        ├── ExposureCurveService.php        # Core G(x) calculations (242 lines)
        ├── ExposureRatingCalculator.php    # Layer calculations (389 lines)
        └── ROLCalculator.php               # ROL computation (155 lines)

5.2 ExposureRatingController.php

Location: app/Http/Controllers/ExposureRatingController.php

Methods:

Method Route HTTP Description
index() /exposure-rating GET Load main page with data
calculate() /exposure-rating/calculate POST Execute calculation
getResults() /exposure-rating/results POST Get results for scenario
compare() /exposure-rating/compare POST Compare with simulation
getCurveData() /exposure-rating/curve-data GET Get curve visualization data
saveCurveConfig() /exposure-rating/curve-config POST Save curve parameters
saveClassCurve() /exposure-rating/class-curve POST Save class curve mapping

Key Implementation:

public function index()
{
    $userId = auth()->id();

    return Inertia::render('ExposureRating', [
        'scenarios' => Scenario::where('user_id', $userId)->with('XLrates')->get(),
        'reconciliations' => Reconciliation::whereHas('excell', fn($q) => 
            $q->where('user_id', $userId)
        )->with('excell')->get(),
        'curveConfigs' => ExposureCurveConfig::forUser($userId)->get(),
        'classCurves' => ClassCurveParameter::getAllCurvesForUser($userId),
        'previousResults' => ExposureRatingResult::forUser($userId)->latest()->take(10)->get(),
        'defaultCurves' => ClassCurveParameter::DEFAULT_CLASS_CURVES,
    ]);
}

5.3 ExposureCurveService.php

Location: app/Services/ExposureRating/ExposureCurveService.php

Purpose: Core exposure curve calculations implementing the Swiss Re methodology.

Key Methods:

class ExposureCurveService
{
    private float $c4;
    private float $c5;
    private float $c6;
    private float $c7;

    public function __construct(
        float $c4 = 3.1,
        float $c5 = -0.15,
        float $c6 = 0.78,
        float $c7 = 0.12
    ) {
        $this->c4 = $c4;
        $this->c5 = $c5;
        $this->c6 = $c6;
        $this->c7 = $c7;
    }

    // Calculate b parameter: b = EXP(C4 + C5 * c * (1 + c))
    public function calculateB(float $c): float
    {
        $exponent = $this->c4 + $this->c5 * $c * (1 + $c);
        return exp($exponent);
    }
    
    // Calculate g parameter: g = EXP((C6 + C7 * c) * c)
    public function calculateG(float $c): float
    {
        $exponent = ($this->c6 + $this->c7 * $c) * $c;
        return exp($exponent);
    }
    
    // Calculate G(x) function - core exposure curve
    public function calculateGFunction(float $x, float $b, float $g): float
    {
        if ($x <= 0) return 0.0;
        if ($x >= 1) return 1.0;
        
        // Core formula implementation
        $term1 = ($g - 1) * $b;
        $term2 = (1 - $b * $g) * pow($b, $x);
        $numerator = ($term1 + $term2) / (1 - $b);
        $denominator = log($b * $g);
        
        $result = log($numerator) / $denominator;
        return max(min($result, 1), 0);
    }
    
    // Generate curve data points for visualization
    public function generateCurveData(float $c, int $points = 100): array;
    
    // Create service from configuration model
    public static function fromConfig(ExposureCurveConfig $config): self;
}

5.4 ExposureRatingCalculator.php

Location: app/Services/ExposureRating/ExposureRatingCalculator.php

Purpose: Main calculation engine that processes profile data and calculates layer losses.

Key Methods:

class ExposureRatingCalculator
{
    private ExposureCurveService $curveService;
    private ROLCalculator $rolCalculator;
    private float $exposureFactor;

    // Main calculation from profile data
    public function calculateFromProfile(
        $profileData,
        Scenario $scenario,
        array $classCurveMap = [],
        ?int $userId = null
    ): array
    {
        // Extract layers from scenario
        $layers = $this->extractLayersFromScenario($scenario);
        
        // Process each row
        foreach ($profileData as $row) {
            $subClass = $row['SUB_CLASS'];
            $netSi = $row['NET_SI'];
            $netPrem = $row['NET_PREM'];
            
            // Get curve parameter for class
            $c = $classCurveMap[$subClass] ?? ClassCurveParameter::getCurveForClass($subClass);
            
            // Calculate b and g
            $b = $this->curveService->calculateB($c);
            $g = $this->curveService->calculateG($c);
            
            // Calculate layer losses
            $layerLosses = $this->calculateLayerLosses($netSi, $netPrem, $layers, $b, $g);
        }
        
        return $this->aggregateResults($results, $layers);
    }
    
    // Extract layer configuration from scenario
    public function extractLayersFromScenario(Scenario $scenario): array
    {
        $layers = [];
        $cumulative = $scenario->deductible;
        
        for ($i = 1; $i <= 6; $i++) {
            $limitKey = "l{$i}_limit";
            if (!empty($scenario->$limitKey)) {
                $limit = $this->parseNumber($scenario->$limitKey);
                $layers[] = [
                    'name' => "Layer {$i}",
                    'number' => $i,
                    'lower' => $cumulative,
                    'upper' => $cumulative + $limit,
                    'limit' => $limit,
                ];
                $cumulative += $limit;
            }
        }
        
        return $layers;
    }
    
    // Store calculation results to database
    public function storeResults(
        Scenario $scenario,
        array $results,
        int $userId,
        ?Reconciliation $reconciliation = null,
        ?string $calculationName = null
    ): void;
}

5.5 ROLCalculator.php

Location: app/Services/ExposureRating/ROLCalculator.php

Purpose: Calculates Rate on Line from Loss on Line using Poisson-weighted factors.

Key Methods:

class ROLCalculator
{
    // Basic ROL calculation
    public function calculate(float $lol): float
    {
        if ($lol <= 0) return 0.0;

        $lambda = $lol;

        // Calculate Poisson probabilities
        $p0 = exp(-$lambda);
        $p1 = $lambda * exp(-$lambda);
        $p2 = (pow($lambda, 2) * exp(-$lambda)) / 2;
        $p3 = (pow($lambda, 3) * exp(-$lambda)) / 6;
        $p4Plus = max(0, 1 - ($p0 + $p1 + $p2 + $p3));

        // Weighted factor
        $factor = (1 * $p0) + (2 * $p1) + (3 * $p2) + (4 * $p3) + (5 * $p4Plus);

        return $factor > 0 ? $lol / $factor : 0.0;
    }
    
    // ROL with confidence interval
    public function calculateWithConfidence(float $lol, float $confidenceLevel = 0.95): array
    {
        $rol = $this->calculate($lol);
        $z = $this->getZScore($confidenceLevel);
        $standardError = sqrt($rol * (1 - min($rol, 0.99)) / max(1, $lol * 100));
        
        return [
            'rol' => $rol,
            'lower_bound' => max(0, $rol - $z * $standardError),
            'upper_bound' => $rol + $z * $standardError,
            'confidence_level' => $confidenceLevel,
        ];
    }
    
    // Get factor breakdown for debugging
    public function getFactorBreakdown(float $lol): array;
}

5.6 Models

ExposureCurveConfig.php

class ExposureCurveConfig extends Model
{
    protected $fillable = [
        'name', 'c4', 'c5', 'c6', 'c7', 'exposure_factor',
        'user_id', 'is_default', 'description',
    ];

    protected $casts = [
        'c4' => 'float',
        'c5' => 'float',
        'c6' => 'float',
        'c7' => 'float',
        'exposure_factor' => 'float',
        'is_default' => 'boolean',
    ];

    public function user() { return $this->belongsTo(User::class); }
    
    public function scopeForUser($query, $userId) {
        return $query->where('user_id', $userId)->orWhere('is_default', true);
    }
    
    public static function getDefaultConfig() {
        return static::where('is_default', true)->first() ?? new static([
            'name' => 'Default',
            'c4' => 3.1,
            'c5' => -0.15,
            'c6' => 0.78,
            'c7' => 0.12,
            'exposure_factor' => 0.25,
            'is_default' => true,
        ]);
    }
}

ClassCurveParameter.php

Includes 22 default class-to-curve mappings:

class ClassCurveParameter extends Model
{
    public const DEFAULT_CLASS_CURVES = [
        'ALL RISKS' => 3.0,
        'FIRE DOMESTIC (HOC)' => 1.5,
        'FIRE DOMESTIC' => 1.5,
        'FIRE INDUSTRIAL' => 3.0,
        'FIRE CONSEQUENTIAL LOSSES' => 3.0,
        'INDUSTRIAL ALL RISKS' => 3.0,
        'CONTRACTORS ALL RISKS' => 3.0,
        'ERECTION ALL RISKS' => 3.0,
        'CONTRACTORS PLANT AND MACHINERY' => 3.0,
        'MACHINERY BREAKDOWN' => 3.0,
        'ELECTRONIC EQUIPMENT' => 3.0,
        'MARINE CARGO' => 2.5,
        'MARINE HULL' => 2.5,
        'MOTOR COMMERCIAL' => 2.0,
        'MOTOR PRIVATE' => 1.5,
        'BURGLARY' => 2.0,
        'MONEY' => 2.0,
        'FIDELITY GUARANTEE' => 2.0,
        'PUBLIC LIABILITY' => 2.5,
        'EMPLOYERS LIABILITY' => 2.5,
        'PROFESSIONAL INDEMNITY' => 2.5,
        'WORKMEN COMPENSATION' => 2.0,
    ];

    public static function getCurveForClass(string $className, ?int $userId = null): float
    {
        // Normalize class name
        $normalizedName = strtoupper(trim($className));
        
        // Try database first, then fall back to defaults
        // Returns 3.0 as ultimate fallback
    }
    
    public static function getAllCurvesForUser(?int $userId = null): array
    {
        // Merge default curves with user-specific curves
    }
}

ExposureRatingResult.php

class ExposureRatingResult extends Model
{
    protected $fillable = [
        'scenario_id', 'reconciliation_id', 'user_id', 'calculation_name',
        'layer_number', 'layer_limit', 'layer_excess', 'total_expected_loss',
        'loss_on_line', 'rate_on_line', 'exposure_rate', 'calculation_metadata',
    ];

    protected $casts = [
        'layer_limit' => 'float',
        'layer_excess' => 'float',
        'total_expected_loss' => 'float',
        'loss_on_line' => 'float',
        'rate_on_line' => 'float',
        'exposure_rate' => 'float',
        'calculation_metadata' => 'array',
    ];

    public function scenario() { return $this->belongsTo(Scenario::class); }
    public function reconciliation() { return $this->belongsTo(Reconciliation::class); }
    public function user() { return $this->belongsTo(User::class); }
    
    public function scopeForScenario($query, $scenarioId) {
        return $query->where('scenario_id', $scenarioId)->orderBy('layer_number');
    }
    
    public function scopeForUser($query, $userId) {
        return $query->where('user_id', $userId);
    }
}

6. Frontend Implementation

6.1 File Structure

resources/js/
├── Pages/
│   └── ExposureRating.vue                  # Main page (~718 lines)
└── Components/
    └── ExposureRating/
        ├── LayerResultsTable.vue           # Results table (188 lines)
        ├── CurveVisualizer.vue             # Chart component (347 lines)
        ├── ClassCurveEditor.vue            # Class editor (232 lines)
        └── ComparisonView.vue              # Comparison component

6.2 ExposureRating.vue (Main Page)

Layout: Uses MainLayout for consistent navigation sidebar.

Features:

  • 3-tab navigation (Calculate, Results, History)
  • Visual progress steps indicator
  • Scenario dropdown selection
  • Reconciliation dropdown showing Data Cleaning process names
  • Real-time calculation results
  • History table with previous calculations

Key Template Structure:

<template>
    <Head title="Exposure Rating" />
    <Loader v-if="showLoader" />
    <MainLayout>
        <!-- Tab Navigation -->
        <nav class="isolate flex divide-x divide-gray-200 rounded-lg shadow-2xl">
            <button @click="currentTab = 'calculate'">
                <i class="fas fa-calculator mr-2"></i>Calculate
            </button>
            <button @click="currentTab = 'results'">
                <i class="fas fa-chart-bar mr-2"></i>Results
            </button>
            <button @click="currentTab = 'history'">
                <i class="fas fa-history mr-2"></i>History
            </button>
        </nav>
        
        <!-- Progress Steps -->
        <ol class="flex items-center justify-between">
            <li>Step 1: Select Scenario</li>
            <li>Step 2: Select Data</li>
            <li>Step 3: View Results</li>
        </ol>
        
        <!-- Tab Content -->
        <LayerResultsTable :results="results?.summary" />
        <CurveVisualizer />
        <ClassCurveEditor :curves="classCurves" />
        <ComparisonView :comparison="comparison" />
    </MainLayout>
</template>

Key Script Logic:

<script setup>
import { ref, computed } from 'vue';
import { useForm, Head } from '@inertiajs/vue3';
import MainLayout from '@/Layouts/MainLayout.vue';
import Loader from '@/Components/Loader.vue';
import moment from 'moment';

// Props from controller
const props = defineProps({
    scenarios: Array,
    reconciliations: Array,
    curveConfigs: Array,
    classCurves: Object,
    previousResults: Array,
    defaultCurves: Object,
});

// State
const currentTab = ref('calculate');
const results = ref(null);

// Form
const form = useForm({
    scenario_id: null,
    reconciliation_id: null,
    calculation_name: '',
    class_curves: {},
});

// Display reconciliation with Data Cleaning process name
const getReconciliationDisplayName = (recon) => {
    if (recon.excell?.import_name) {
        return recon.excell.import_name;  // Shows "Test 3", "With Morph", etc.
    }
    if (recon.excell?.original_name) {
        return recon.excell.original_name;
    }
    return `Reconciliation #${recon.id}`;
};

// Submit calculation
const calculateExposure = () => {
    form.post(route('exposure.calculate'), {
        onSuccess: (page) => {
            results.value = page.props.flash?.results;
            currentTab.value = 'results';
        },
    });
};
</script>

6.3 LayerResultsTable.vue

Purpose: Displays layer-by-layer calculation results with ROL/LOL metrics.

Features:

  • Dark theme (bg-secondary) matching Data Cleaning module
  • Layer number badges with primary color
  • Color-coded ROL values (green < 10%, yellow 10-20%, red > 20%)
  • Export button
  • Totals row with summary statistics
  • Quick stats footer

Template Structure:

<template>
    <div class="bg-secondary rounded-lg shadow-2xl overflow-hidden">
        <!-- Header -->
        <div class="px-6 py-4 border-b border-gray-700">
            <h3 class="text-lg font-semibold text-white">
                <i class="fas fa-table text-primary"></i>
                Layer Results Summary
            </h3>
        </div>

        <!-- Results Table -->
        <table class="min-w-full divide-y divide-gray-700">
            <thead class="bg-gray-800/50">
                <tr>
                    <th>Layer</th>
                    <th>Limit</th>
                    <th>Excess</th>
                    <th>Expected Loss</th>
                    <th>LOL</th>
                    <th>ROL</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(data, layerName) in results">
                    <td>
                        <span class="rounded-full bg-primary/20 text-primary">
                            {{ getLayerNumber(layerName) }}
                        </span>
                    </td>
                    <td>{{ formatNumber(data.limit) }}</td>
                    <td>{{ formatNumber(data.excess) }}</td>
                    <td>{{ formatNumber(data.total_expected_loss) }}</td>
                    <td>{{ formatPercentage(data.loss_on_line) }}</td>
                    <td :class="getRolColor(data.rate_on_line)">
                        {{ formatPercentage(data.rate_on_line) }}
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</template>

6.4 CurveVisualizer.vue

Purpose: Interactive Chart.js visualization of exposure curves.

Features:

  • Real-time curve updates with slider
  • Parameter input for curve value (c)
  • Display of calculated b and g parameters
  • Dark themed chart with grid styling
  • Educational info section explaining the curve

Key Implementation:

import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);

const curveParameter = ref(3.0);
const chartCanvas = ref(null);
let chart = null;

const updateCurve = async () => {
    const response = await axios.get(route('exposure.curve.data'), {
        params: { c: curveParameter.value }
    });
    
    curveParams.value = response.data.parameters;
    
    chart.data.datasets[0].data = response.data.points.map(p => ({
        x: p.x,
        y: p.y
    }));
    chart.update();
};

6.5 ClassCurveEditor.vue

Purpose: Edit curve parameters per insurance class.

Features:

  • White card with shadow (contrasts with dark sections)
  • Visual progress bars showing relative curve values
  • Add/Remove class functionality
  • Modal for adding new classes
  • Quick stats (class count, average, range)

6.6 ComparisonView.vue

Purpose: Side-by-side comparison of exposure vs simulation results.

Features:

  • Dark theme consistent with other components
  • Comparison table with difference calculations
  • Color-coded differences
  • Chart.js bar chart visualization

7. Routes Configuration

File: routes/web.php

use App\Http\Controllers\ExposureRatingController;

Route::middleware(['auth:sanctum', config('jetstream.auth_session'), 'verified'])
    ->group(function () {
    
    // Exposure Rating Routes
    Route::get('/exposure-rating', [ExposureRatingController::class, 'index'])
        ->name('exposurerating');
    
    Route::post('/exposure-rating/calculate', [ExposureRatingController::class, 'calculate'])
        ->name('exposure.calculate');
    
    Route::post('/exposure-rating/results', [ExposureRatingController::class, 'getResults'])
        ->name('exposure.results');
    
    Route::post('/exposure-rating/compare', [ExposureRatingController::class, 'compare'])
        ->name('exposure.compare');
    
    Route::get('/exposure-rating/curve-data', [ExposureRatingController::class, 'getCurveData'])
        ->name('exposure.curve.data');
    
    Route::post('/exposure-rating/curve-config', [ExposureRatingController::class, 'saveCurveConfig'])
        ->name('exposure.curve.save');
    
    Route::post('/exposure-rating/class-curve', [ExposureRatingController::class, 'saveClassCurve'])
        ->name('exposure.class.curve.save');
});

8. Menu Navigation

8.1 MainLayout.vue Integration

File: resources/js/Layouts/MainLayout.vue

Menu item added to sidebar navigation:

const menuItems = [
    // ... other items
    {
        name: 'Exposure Rating',
        icon: 'fas fa-chart-line',
        link: 'exposurerating',
        permission: 'exposure_rating'
    }
];

const checkSubscriptions = (subscriptions, item) => {
    // Always show for exposure rating
    if (item.link === 'exposurerating') return true;
    // ... other subscription checks
};

8.2 AppLayout.vue Integration

File: resources/js/Layouts/AppLayout.vue

Menu item also added to AppLayout for dashboard consistency.


9. UI/UX Design System

9.1 Color Scheme

Element Tailwind Class Hex Value
Primary bg-primary #dc2626 (Red)
Secondary bg-secondary #374151 (Dark Gray)
Background bg-gray-800/50 Semi-transparent dark
Text Primary text-white #FFFFFF
Text Secondary text-gray-400 #9CA3AF
Success text-green-400 #4ADE80
Warning text-yellow-400 #FACC15
Error text-red-400 #F87171

9.2 Component Styling Patterns

Dark Cards:

<div class="bg-secondary rounded-lg shadow-2xl overflow-hidden">
    <div class="px-6 py-4 border-b border-gray-700">
        <!-- Header -->
    </div>
    <div class="p-6">
        <!-- Content -->
    </div>
</div>

Badges:

<span class="inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium 
    bg-green-900/30 text-green-400 ring-1 ring-inset ring-green-500/30">
    Value
</span>

Primary Buttons:

<button class="inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 
    text-sm font-semibold text-white hover:bg-primary/90 transition-all duration-200">
    <i class="fas fa-calculator"></i> Calculate
</button>

Form Inputs (Dark Theme):

<input type="text" class="w-full px-4 py-3 bg-gray-700 border border-gray-600 
    rounded-lg text-white placeholder-gray-400 focus:ring-2 focus:ring-primary 
    focus:border-primary transition-all duration-200" />

10. Integration Points

10.1 Data Cleaning Module Integration

The Exposure Rating module integrates with the Data Cleaning module through:

  1. Reconciliation Relationship:

    • Reconciliation model has excell_id (note: double L) foreign key
    • Excell model contains import_name (the Data Cleaning process name)
    • Dropdown shows human-readable process names
  2. Profile Data Loading:

    • Loads Excel data from reconciliation file
    • Extracts SUB_CLASS, NET_SI, NET_PREM columns
    • Applies curve parameters per class

10.2 Scenario Module Integration

  • Reads layer configuration from Scenario model
  • Supports up to 6 layers (l1_limit through l6_limit)
  • Uses scenario deductible as base

10.3 XL Rates Integration

  • Updates XLrates table with exposure rates
  • Columns: exposure_l1_rate through exposure_l6_rate
  • Enables blended pricing (simulation + exposure)

11. API Reference

11.1 Calculate Exposure Rating

Endpoint: POST /exposure-rating/calculate

Request:

{
    "scenario_id": 1,
    "reconciliation_id": 2,
    "calculation_name": "Q4 2025 Calculation",
    "class_curves": {
        "ALL RISKS": 3.0,
        "FIRE DOMESTIC": 1.5
    }
}

Response:

{
    "message": "Exposure rating calculated successfully",
    "results": {
        "detail": [...],
        "summary": {
            "Layer 1": {
                "limit": 1000000,
                "excess": 500000,
                "total_expected_loss": 25000,
                "loss_on_line": 0.025,
                "rate_on_line": 0.0125
            }
        }
    }
}

11.2 Get Curve Data

Endpoint: GET /exposure-rating/curve-data?c=3.0

Response:

{
    "curve_data": {
        "points": [
            {"x": 0, "y": 0},
            {"x": 0.01, "y": 0.15},
            {"x": 1, "y": 1}
        ],
        "parameters": {
            "c": 3.0,
            "b": 5.437,
            "g": 30.123
        }
    }
}

11.3 Compare with Simulation

Endpoint: POST /exposure-rating/compare

Request:

{
    "scenario_id": 1
}

Response:

{
    "comparison": {
        "Layer 1": {
            "simulation_rate": 0.0130,
            "exposure_rate": 0.0125,
            "difference": -0.0005,
            "difference_percent": -3.85
        }
    }
}

12. Testing

12.1 Test File Locations

tests/
├── Unit/
│   └── ExposureRating/
│       ├── ExposureCurveServiceTest.php
│       ├── ROLCalculatorTest.php
│       └── ExposureRatingCalculatorTest.php
└── Feature/
    └── ExposureRatingTest.php

12.2 Test Cases

Test Description
test_calculate_b_with_default_parameters Verify b calculation
test_calculate_g_with_default_parameters Verify g calculation
test_g_function_returns_zero_for_zero_x Edge case: x = 0
test_g_function_returns_one_for_x_greater_than_one Edge case: x ≥ 1
test_g_function_is_monotonically_increasing Curve property validation
test_rol_calculation ROL from LOL
test_layer_extraction_from_scenario Layer parsing
test_exposure_rating_page_requires_authentication Auth check
test_can_calculate_exposure_rating Full calculation flow

12.3 Running Tests

# Run all exposure rating tests
php artisan test --filter=ExposureRating

# Run specific test class
php artisan test tests/Unit/ExposureRating/ExposureCurveServiceTest.php

# Run with coverage
php artisan test --filter=ExposureRating --coverage

13. Deployment

13.1 Migration Commands

# Run migrations
php artisan migrate

# Seed default configurations (if seeder exists)
php artisan db:seed --class=ExposureCurveConfigSeeder

# Clear caches
php artisan config:clear
php artisan cache:clear
php artisan view:clear

# Build frontend
npm run build

13.2 Post-Deployment Checklist

  • Migrations executed successfully
  • Database seeded with default curve configs
  • Menu item visible in sidebar
  • Exposure Rating page loads correctly
  • Scenario dropdown populated
  • Reconciliation dropdown shows process names
  • Calculation executes without errors
  • Results display correctly
  • History tab shows previous calculations
  • Curve visualizer renders chart
  • User acceptance testing completed
  • Error monitoring for 24 hours

14. Known Issues & Solutions

14.1 Menu Not Visible

Issue: Exposure Rating menu item not appearing in sidebar.

Cause: Two separate layout files (MainLayout.vue and AppLayout.vue) requiring menu addition to both.

Solution: Added menu item to both layouts with proper route name exposurerating.

14.2 Database Column Name

Issue: Database error referencing excel_id column.

Cause: Column is named excell_id (double L) in the database.

Solution: Updated all references to use excell_id and excell relationship.

14.3 Reconciliation Display Names

Issue: Reconciliation dropdown showing IDs instead of meaningful names.

Cause: Missing relationship eager loading and display logic.

Solution: Added getReconciliationDisplayName() function that returns excell.import_name.


15. Future Enhancements

15.1 Planned Features

Feature Priority Status
Custom Curve Builder UI Medium Planned
Machine Learning curve prediction Low Backlog
Batch calculation for multiple scenarios Medium Planned
PDF Report Generation High Planned
Real-time collaboration Low Backlog
Sensitivity analysis Medium Planned

15.2 Future API Extensions

Route::prefix('api/v2/exposure-rating')->group(function () {
    Route::post('/batch-calculate', 'batchCalculate');
    Route::get('/historical-comparison', 'historicalComparison');
    Route::get('/sensitivity-analysis', 'sensitivityAnalysis');
    Route::get('/export-pdf', 'exportPdf');
});

16. Appendix

16.1 Python to PHP Translation Reference

Python (Original Script) PHP (Implementation)
np.exp(x) exp($x)
np.log(x) log($x)
math.exp(x) exp($x)
b ** x pow($b, $x)
max(min(val, 1), 0) max(min($val, 1), 0)
df.apply(lambda...) collect(...)->map(fn...)
df['col'].sum() collect($arr)->sum('col')

16.2 File Inventory

File Lines Purpose
ExposureRatingController.php 463 Main controller
ExposureCurveService.php 242 Core calculations
ExposureRatingCalculator.php 389 Layer calculations
ROLCalculator.php 155 ROL computation
ExposureCurveConfig.php 75 Curve config model
ClassCurveParameter.php 131 Class curve model
ExposureRatingResult.php 97 Results model
ExposureRating.vue 718 Main Vue page
LayerResultsTable.vue 188 Results component
CurveVisualizer.vue 347 Chart component
ClassCurveEditor.vue 232 Editor component
ComparisonView.vue ~200 Comparison component

Total: ~3,400+ lines of code implemented

16.3 Glossary

Term Definition
Exposure Curve Mathematical function relating damage ratios to expected losses
G(x) The core exposure curve function
ROL Rate on Line - premium rate as percentage of layer limit
LOL Loss on Line - expected loss as percentage of layer limit
XL Excess of Loss - type of reinsurance treaty
Deductible Retention level below which cedant retains losses
Layer Limit Maximum loss covered by a reinsurance layer
Sum Insured (SI) Total value insured under a policy
Net Premium Premium after deducting reinsurance cessions

17. Support & Maintenance

Module Owner: Actuarial Development Team
Repository: ACENSURE/Acentre-System
Branch: dev
Documentation Version: 1.0.0
Last Updated: December 30, 2025


This document serves as the authoritative technical reference for the Exposure Rating module in the Acentre Reinsurance & Actuarial System.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment