Chapter 8 - Expected Claims Technique#
import numpy as np
import pandas as pd
import chainladder as cl
from IPython.display import display
pd.set_option("display.max_columns", None)
pd.set_option("display.width", 1000)
# Helper functions, skip to the next section for actual exhibits
def as_series(tri):
s = tri.to_frame(origin_as_datetime=False).iloc[:, 0]
s.index = [int(getattr(i, "year", i)) for i in s.index]
return s
def avg_ex_high_low(values):
values = np.asarray(values, dtype=float).flatten()
return (values.sum() - values.max() - values.min()) / (len(values) - 2)
def unpaid_exhibit(reported, paid, expected):
out = pd.DataFrame(index=list(reported.origin.year))
out["Reported (2)"] = as_series(reported.latest_diagonal).values
out["Paid (3)"] = as_series(paid.latest_diagonal).values
out["Expected Claims (4)"] = as_series(expected).values
out["Case Outstanding (5)"] = out["Reported (2)"] - out["Paid (3)"]
out["IBNR (6)"] = out["Expected Claims (4)"] - out["Reported (2)"]
out["Total Unpaid (7)"] = out["Expected Claims (4)"] - out["Paid (3)"]
return out
P140 (Exhibit I Sheet 1)#
auto_bi = cl.load_sample("friedland_auto_bi_insurer")
reported_pattern = {
12: 4, 24: 2.9, 36: 1.8, 48: 1.4, 60: 1.2, 72: 1.1, 84: 1.03, 96: 1.02, 108: 1.005,
}
paid_pattern = {
12: 90, 24: 15, 36: 5, 48: 2.5, 60: 1.75, 72: 1.35, 84: 1.25, 96: 1.15, 108: 1.05,
}
reported_bi = cl.DevelopmentConstant(patterns=reported_pattern, style="cdf").fit_transform(
auto_bi["Reported Claims"]
)
paid_bi = cl.DevelopmentConstant(patterns=paid_pattern, style="cdf").fit_transform(
auto_bi["Paid Claims"]
)
reported_ultimate = cl.Chainladder().fit(reported_bi).ultimate_
paid_ultimate = cl.Chainladder().fit(paid_bi).ultimate_
initial_selected = (reported_ultimate + paid_ultimate) / 2
trend_factors = np.round(
cl.Trend(trends=[0.145], dates=[("2008-12-31", "2000-01-01")])
.fit(auto_bi["Earned Premium"])
.trend_.latest_diagonal,
3,
)
tort_factors = np.array([0.670, 0.670, 0.670, 0.670, 0.750, 1.0, 1.0, 1.0, 1.0])
trended_adj = np.round(
trend_factors * initial_selected * tort_factors.reshape(1, 1, -1, 1), 0
)
claim_ratio = np.round(trended_adj / auto_bi["Earned Premium"].latest_diagonal, 2)
years = list(auto_bi["Reported Claims"].origin.year)
exhibit_i_s1 = pd.DataFrame(index=years)
exhibit_i_s1["Reported (2)"] = as_series(auto_bi["Reported Claims"].latest_diagonal).values
exhibit_i_s1["Paid (3)"] = as_series(auto_bi["Paid Claims"].latest_diagonal).values
exhibit_i_s1["CDF Reported (4)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(reported_bi))["CDF"]
).values
exhibit_i_s1["CDF Paid (5)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(paid_bi))["CDF"]
).values
exhibit_i_s1["Ult Reported (6)"] = as_series(reported_ultimate).values
exhibit_i_s1["Ult Paid (7)"] = as_series(paid_ultimate).values
exhibit_i_s1["Initial Selected (8)"] = as_series(initial_selected).values
exhibit_i_s1["Earned Premium (9)"] = as_series(auto_bi["Earned Premium"].latest_diagonal).values
exhibit_i_s1["Trend to 7/1/08 (10)"] = as_series(trend_factors).values
exhibit_i_s1["Tort Reform (11)"] = tort_factors
exhibit_i_s1["Trended Adj Ult (12)"] = as_series(trended_adj).values
exhibit_i_s1["Trended Adj Claim Ratio (13)"] = as_series(claim_ratio).values
display(exhibit_i_s1)
ratios = as_series(claim_ratio)
avg_00_05 = float(np.round(ratios.loc[2000:2005].mean(), 3))
avg_00_05_xhl = float(np.round(avg_ex_high_low(ratios.loc[2000:2005]), 3))
avg_01_06 = float(np.round(ratios.loc[2001:2006].mean(), 3))
avg_01_06_xhl = float(np.round(avg_ex_high_low(ratios.loc[2001:2006]), 3))
selected_claim_ratio = 0.80
el_reported = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(
auto_bi["Reported Claims"], sample_weight=auto_bi["Earned Premium"].latest_diagonal
)
el_paid = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(
auto_bi["Paid Claims"], sample_weight=auto_bi["Earned Premium"].latest_diagonal
)
expected_2008 = float(el_reported.ultimate_.loc[:, :, "2008", :].sum())
unpaid_2008 = float(el_paid.ibnr_.loc[:, :, "2008", :].sum())
ibnr_2008 = float(el_reported.ibnr_.loc[:, :, "2008", :].sum())
exhibit_i_s1_summary = pd.Series(
{
"Avg 2000-2005 (14)": avg_00_05,
"Avg 2000-2005 ex Hi/Lo (14)": avg_00_05_xhl,
"Avg 2001-2006 (14)": avg_01_06,
"Avg 2001-2006 ex Hi/Lo (14)": avg_01_06_xhl,
"Selected Claim Ratio (15)": selected_claim_ratio,
"Expected Claims 2008 (16)": expected_2008,
"Total Unpaid 2008 (17)": unpaid_2008,
"IBNR 2008 (17)": ibnr_2008,
},
name="Items (14)-(17)",
)
display(
exhibit_i_s1_summary.map(
lambda x: f"{x:,.0f}" if abs(x) >= 1 else f"{x:.3f}"
).to_frame()
)
| Reported (2) | Paid (3) | CDF Reported (4) | CDF Paid (5) | Ult Reported (6) | Ult Paid (7) | Initial Selected (8) | Earned Premium (9) | Trend to 7/1/08 (10) | Tort Reform (11) | Trended Adj Ult (12) | Trended Adj Claim Ratio (13) | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2000 | 10000000.0 | 9500000.0 | 1.005 | 1.05 | 10050000.0 | 9975000.0 | 10012500.0 | 24000000.0 | 2.954 | 0.67 | 19816540.0 | 0.83 |
| 2001 | 8000000.0 | 7200000.0 | 1.020 | 1.15 | 8160000.0 | 8280000.0 | 8220000.0 | 18000000.0 | 2.580 | 0.67 | 14209092.0 | 0.79 |
| 2002 | 9400000.0 | 7600000.0 | 1.030 | 1.25 | 9682000.0 | 9500000.0 | 9591000.0 | 19000000.0 | 2.253 | 0.67 | 14477710.0 | 0.76 |
| 2003 | 15600000.0 | 7800000.0 | 1.100 | 1.35 | 17160000.0 | 10530000.0 | 13845000.0 | 23000000.0 | 1.968 | 0.67 | 18255463.0 | 0.79 |
| 2004 | 16500000.0 | 11200000.0 | 1.200 | 1.75 | 19800000.0 | 19600000.0 | 19700000.0 | 32000000.0 | 1.719 | 0.75 | 25398225.0 | 0.79 |
| 2005 | 18500000.0 | 10200000.0 | 1.400 | 2.50 | 25900000.0 | 25500000.0 | 25700000.0 | 47000000.0 | 1.501 | 1.00 | 38575700.0 | 0.82 |
| 2006 | 16500000.0 | 6000000.0 | 1.800 | 5.00 | 29700000.0 | 30000000.0 | 29850000.0 | 50000000.0 | 1.311 | 1.00 | 39133350.0 | 0.78 |
| 2007 | 14000000.0 | 3000000.0 | 2.900 | 15.00 | 40600000.0 | 45000000.0 | 42800000.0 | 57000000.0 | 1.145 | 1.00 | 49006000.0 | 0.86 |
| 2008 | 8700000.0 | 750000.0 | 4.000 | 90.00 | 34800000.0 | 67500000.0 | 51150000.0 | 62000000.0 | 1.000 | 1.00 | 51150000.0 | 0.82 |
| Items (14)-(17) | |
|---|---|
| Avg 2000-2005 (14) | 0.797 |
| Avg 2000-2005 ex Hi/Lo (14) | 0.798 |
| Avg 2001-2006 (14) | 0.788 |
| Avg 2001-2006 ex Hi/Lo (14) | 0.787 |
| Selected Claim Ratio (15) | 0.800 |
| Expected Claims 2008 (16) | 49,600,000 |
| Total Unpaid 2008 (17) | 48,850,000 |
| IBNR 2008 (17) | 40,900,000 |
# Exhibit I Sheet 1 — reconcile to Friedland PDF p140
assert np.allclose(
exhibit_i_s1["Reported (2)"],
[10000000, 8000000, 9400000, 15600000, 16500000, 18500000, 16500000, 14000000, 8700000],
)
assert np.allclose(
exhibit_i_s1["Paid (3)"],
[9500000, 7200000, 7600000, 7800000, 11200000, 10200000, 6000000, 3000000, 750000],
)
assert np.allclose(
exhibit_i_s1["Ult Reported (6)"],
[10050000, 8160000, 9682000, 17160000, 19800000, 25900000, 29700000, 40600000, 34800000],
)
assert np.allclose(
exhibit_i_s1["Ult Paid (7)"],
[9975000, 8280000, 9500000, 10530000, 19600000, 25500000, 30000000, 45000000, 67500000],
)
assert np.allclose(
exhibit_i_s1["Initial Selected (8)"],
[10012500, 8220000, 9591000, 13845000, 19700000, 25700000, 29850000, 42800000, 51150000],
)
assert np.allclose(
exhibit_i_s1["Earned Premium (9)"],
[24000000, 18000000, 19000000, 23000000, 32000000, 47000000, 50000000, 57000000, 62000000],
)
assert np.allclose(
exhibit_i_s1["Trend to 7/1/08 (10)"],
[2.954, 2.58, 2.253, 1.968, 1.719, 1.501, 1.311, 1.145, 1],
)
assert np.allclose(
exhibit_i_s1["Trended Adj Ult (12)"],
[19816540, 14209092, 14477710, 18255463, 25398225, 38575700, 39133350, 49006000, 51150000],
)
assert np.allclose(
exhibit_i_s1["Trended Adj Claim Ratio (13)"],
[0.83, 0.79, 0.76, 0.79, 0.79, 0.82, 0.78, 0.86, 0.82],
)
assert np.isclose(avg_00_05, 0.797)
assert np.isclose(avg_00_05_xhl, 0.798)
assert np.isclose(avg_01_06, 0.788)
assert np.isclose(avg_01_06_xhl, 0.788, atol=0.001)
assert np.isclose(expected_2008, 49600000)
assert np.isclose(unpaid_2008, 48850000)
assert np.isclose(ibnr_2008, 40900000)
P141 (Exhibit I Sheet 2)#
gl = cl.load_sample("friedland_gl_self_insurer")
reported_pattern = {
12: 3.104, 24: 1.940, 36: 1.616, 48: 1.394, 60: 1.244, 72: 1.131,
84: 1.077, 96: 1.051, 108: 1.030, 120: 1.020, 132: 1.015,
}
paid_pattern = {
12: 20.373, 24: 5.093, 36: 3.183, 48: 2.274, 60: 1.749, 72: 1.489,
84: 1.306, 96: 1.187, 108: 1.109, 120: 1.067, 132: 1.046,
}
reported = cl.DevelopmentConstant(patterns=reported_pattern, style="cdf").fit_transform(
gl["Reported Claims"]
)
paid = cl.DevelopmentConstant(patterns=paid_pattern, style="cdf").fit_transform(
gl["Paid Claims"]
)
reported_ultimate = cl.Chainladder().fit(reported).ultimate_
paid_ultimate = cl.Chainladder().fit(paid).ultimate_
selected_ultimate = (reported_ultimate + paid_ultimate) / 2
population = gl["Population"].latest_diagonal
trend_factors = np.round(
cl.Trend(trends=[0.075], dates=[("2008-12-31", "1998-01-01")])
.fit(gl["Population"])
.trend_.latest_diagonal,
3,
)
trended_ult = np.round(selected_ultimate * trend_factors, 0)
pure_premium = np.round(trended_ult / population, 2)
years = list(gl["Reported Claims"].origin.year)
exhibit_i_s2 = pd.DataFrame(index=years)
exhibit_i_s2["Reported (2)"] = as_series(gl["Reported Claims"].latest_diagonal).values
exhibit_i_s2["Paid (3)"] = as_series(gl["Paid Claims"].latest_diagonal).values
exhibit_i_s2["CDF Reported (4)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(reported))["CDF"]
).values
exhibit_i_s2["CDF Paid (5)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(paid))["CDF"]
).values
exhibit_i_s2["Ult Reported (6)"] = as_series(reported_ultimate).values
exhibit_i_s2["Ult Paid (7)"] = as_series(paid_ultimate).values
exhibit_i_s2["Initial Selected (8)"] = as_series(selected_ultimate).values
exhibit_i_s2["Population (9)"] = as_series(population).values
exhibit_i_s2["Trend to 7/1/08 (10)"] = as_series(trend_factors).values
exhibit_i_s2["Trended Ult (11)"] = as_series(trended_ult).values
exhibit_i_s2["Trended Pure Premium (12)"] = as_series(pure_premium).values
display(exhibit_i_s2)
pp = as_series(pure_premium)
avg_00_05 = float(np.round(pp.loc[2000:2005].mean(), 2))
avg_00_05_xhl = float(np.round(avg_ex_high_low(pp.loc[2000:2005]), 2))
avg_01_06 = float(np.round(pp.loc[2001:2006].mean(), 2))
avg_01_06_xhl = float(np.round(avg_ex_high_low(pp.loc[2001:2006]), 2))
selected_pure_premium = 3.50
el_reported = cl.ExpectedLoss(apriori=selected_pure_premium).fit(
reported, sample_weight=population
)
el_paid = cl.ExpectedLoss(apriori=selected_pure_premium).fit(
paid, sample_weight=population
)
expected_2008 = float(el_reported.ultimate_.loc[:, :, "2008", :].sum())
unpaid_2008 = float(el_paid.ibnr_.loc[:, :, "2008", :].sum())
ibnr_2008 = float(el_reported.ibnr_.loc[:, :, "2008", :].sum())
exhibit_i_s2_summary = pd.Series(
{
"Avg 2000-2005 (13)": avg_00_05,
"Avg 2000-2005 ex Hi/Lo (13)": avg_00_05_xhl,
"Avg 2001-2006 (13)": avg_01_06,
"Avg 2001-2006 ex Hi/Lo (13)": avg_01_06_xhl,
"Selected Pure Premium (14)": selected_pure_premium,
"Expected Claims 2008 (15)": expected_2008,
"Total Unpaid 2008 (16)": unpaid_2008,
"IBNR 2008 (16)": ibnr_2008,
},
name="Items (13)-(16)",
)
display(
exhibit_i_s2_summary.map(
lambda x: f"{x:,.0f}" if abs(x) >= 100 else f"{x:.2f}"
).to_frame()
)
| Reported (2) | Paid (3) | CDF Reported (4) | CDF Paid (5) | Ult Reported (6) | Ult Paid (7) | Initial Selected (8) | Population (9) | Trend to 7/1/08 (10) | Trended Ult (11) | Trended Pure Premium (12) | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1998 | 900000.0 | 890000.0 | 1.015 | 1.046 | 913500.0 | 930940.0 | 922220.0 | 709000.0 | 2.061 | 1900695.0 | 2.68 |
| 1999 | 1200000.0 | 1170000.0 | 1.020 | 1.067 | 1224000.0 | 1248390.0 | 1236195.0 | 724000.0 | 1.917 | 2369786.0 | 3.27 |
| 2000 | 1300000.0 | 1265000.0 | 1.030 | 1.109 | 1339000.0 | 1402885.0 | 1370942.5 | 736000.0 | 1.783 | 2444390.0 | 3.32 |
| 2001 | 1800000.0 | 1600000.0 | 1.051 | 1.187 | 1891800.0 | 1899200.0 | 1895500.0 | 740000.0 | 1.659 | 3144634.0 | 4.25 |
| 2002 | 1450000.0 | 1200000.0 | 1.077 | 1.306 | 1561650.0 | 1567200.0 | 1564425.0 | 750000.0 | 1.543 | 2413908.0 | 3.22 |
| 2003 | 1400000.0 | 1050000.0 | 1.131 | 1.489 | 1583400.0 | 1563450.0 | 1573425.0 | 760000.0 | 1.436 | 2259438.0 | 2.97 |
| 2004 | 2400000.0 | 900000.0 | 1.244 | 1.749 | 2985600.0 | 1574100.0 | 2279850.0 | 770000.0 | 1.335 | 3043600.0 | 3.95 |
| 2005 | 1800000.0 | 860000.0 | 1.394 | 2.274 | 2509200.0 | 1955640.0 | 2232420.0 | 775000.0 | 1.242 | 2772666.0 | 3.58 |
| 2006 | 1500000.0 | 525000.0 | 1.616 | 3.183 | 2424000.0 | 1671075.0 | 2047537.5 | 780000.0 | 1.156 | 2366953.0 | 3.03 |
| 2007 | 1200000.0 | 750000.0 | 1.940 | 5.093 | 2328000.0 | 3819750.0 | 3073875.0 | 785000.0 | 1.075 | 3304416.0 | 4.21 |
| 2008 | 600000.0 | 170000.0 | 3.104 | 20.373 | 1862400.0 | 3463410.0 | 2662905.0 | 790000.0 | 1.000 | 2662905.0 | 3.37 |
| Items (13)-(16) | |
|---|---|
| Avg 2000-2005 (13) | 3.55 |
| Avg 2000-2005 ex Hi/Lo (13) | 3.52 |
| Avg 2001-2006 (13) | 3.50 |
| Avg 2001-2006 ex Hi/Lo (13) | 3.44 |
| Selected Pure Premium (14) | 3.50 |
| Expected Claims 2008 (15) | 2,765,000 |
| Total Unpaid 2008 (16) | 2,595,000 |
| IBNR 2008 (16) | 2,165,000 |
# Exhibit I Sheet 2 — reconcile to Friedland PDF p141
assert np.allclose(
exhibit_i_s2["Reported (2)"],
[900000, 1200000, 1300000, 1800000, 1450000, 1400000, 2400000, 1800000, 1500000, 1200000, 600000],
)
assert np.allclose(
exhibit_i_s2["Paid (3)"],
[890000, 1170000, 1265000, 1600000, 1200000, 1050000, 900000, 860000, 525000, 750000, 170000],
)
assert np.allclose(
exhibit_i_s2["Ult Reported (6)"],
[913500, 1224000, 1339000, 1891800, 1561650, 1583400, 2985600, 2509200, 2424000, 2328000, 1862400],
atol=1,
)
assert np.allclose(
exhibit_i_s2["Ult Paid (7)"],
[930940, 1248390, 1402885, 1899200, 1567200, 1563450, 1574100, 1955640, 1671075, 3819750, 3463410],
atol=1,
)
assert np.allclose(
exhibit_i_s2["Population (9)"],
[709000, 724000, 736000, 740000, 750000, 760000, 770000, 775000, 780000, 785000, 790000],
)
assert np.allclose(
exhibit_i_s2["Trend to 7/1/08 (10)"],
[2.061, 1.917, 1.783, 1.659, 1.543, 1.436, 1.335, 1.242, 1.156, 1.075, 1],
)
assert np.allclose(
exhibit_i_s2["Trended Ult (11)"],
[1900695, 2369786, 2444390, 3144635, 2413908, 2259438, 3043600, 2772666, 2366953, 3304416, 2662905],
atol=1,
)
assert np.allclose(
exhibit_i_s2["Trended Pure Premium (12)"],
[2.68, 3.27, 3.32, 4.25, 3.22, 2.97, 3.95, 3.58, 3.03, 4.21, 3.37],
atol=0.01,
)
assert np.isclose(avg_00_05, 3.55)
assert np.isclose(avg_00_05_xhl, 3.52)
assert np.isclose(avg_01_06, 3.50)
assert np.isclose(avg_01_06_xhl, 3.45, atol=0.011) # PDF 3.45; 3.445 rounds to 3.44
assert np.isclose(expected_2008, 2765000)
assert np.isclose(unpaid_2008, 2595000)
assert np.isclose(ibnr_2008, 2165000)
P142 (Exhibit II Sheet 1)#
ia = cl.load_sample("friedland_us_industry_auto")
reported_pattern = {
12: 1.292, 24: 1.110, 36: 1.051, 48: 1.023, 60: 1.011,
72: 1.006, 84: 1.003, 96: 1.001, 108: 1.000, 120: 1.000,
}
paid_pattern = {
12: 2.390, 24: 1.404, 36: 1.184, 48: 1.085, 60: 1.040,
72: 1.020, 84: 1.011, 96: 1.006, 108: 1.004, 120: 1.002,
}
reported = cl.DevelopmentConstant(patterns=reported_pattern, style="cdf").fit_transform(
ia["Reported Claims"]
)
paid = cl.DevelopmentConstant(patterns=paid_pattern, style="cdf").fit_transform(
ia["Paid Claims"]
)
reported_ultimate = cl.Chainladder().fit(reported).ultimate_
paid_ultimate = cl.Chainladder().fit(paid).ultimate_
selected_ultimate = np.round((reported_ultimate + paid_ultimate) / 2, 0)
earned_premium = ia["Earned Premium"].latest_diagonal
estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)
selected_claim_ratio = np.array(
[0.75, 0.75, 0.75, 0.75, 0.75, 0.65, 0.65, 0.65, 0.65, 0.65]
)
sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)
el_reported = cl.ExpectedLoss(apriori=1).fit(
ia["Reported Claims"], sample_weight=sample_weight
)
el_paid = cl.ExpectedLoss(apriori=1).fit(
ia["Paid Claims"], sample_weight=sample_weight
)
expected_claims = np.round(el_reported.ultimate_, 0)
years = list(ia["Reported Claims"].origin.year)
exhibit_ii_s1 = pd.DataFrame(index=years)
exhibit_ii_s1["Reported (2)"] = as_series(ia["Reported Claims"].latest_diagonal).values
exhibit_ii_s1["Paid (3)"] = as_series(ia["Paid Claims"].latest_diagonal).values
exhibit_ii_s1["CDF Reported (4)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(reported))["CDF"]
).values
exhibit_ii_s1["CDF Paid (5)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(paid))["CDF"]
).values
exhibit_ii_s1["Ult Reported (6)"] = as_series(np.round(reported_ultimate, 0)).values
exhibit_ii_s1["Ult Paid (7)"] = as_series(np.round(paid_ultimate, 0)).values
exhibit_ii_s1["Initial Selected (8)"] = as_series(selected_ultimate).values
exhibit_ii_s1["Earned Premium (9)"] = as_series(earned_premium).values
exhibit_ii_s1["Estimated Claim Ratio (10)"] = as_series(estimated_claim_ratios).values
exhibit_ii_s1["Selected Claim Ratio (11)"] = selected_claim_ratio
exhibit_ii_s1["Expected Claims (12)"] = as_series(expected_claims).values
display(exhibit_ii_s1)
| Reported (2) | Paid (3) | CDF Reported (4) | CDF Paid (5) | Ult Reported (6) | Ult Paid (7) | Initial Selected (8) | Earned Premium (9) | Estimated Claim Ratio (10) | Selected Claim Ratio (11) | Expected Claims (12) | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1998 | 47742304.0 | 47644187.0 | 1.000 | 1.002 | 47742304.0 | 47739475.0 | 47740890.0 | 68574209.0 | 0.696 | 0.75 | 51430657.0 |
| 1999 | 51185767.0 | 51000534.0 | 1.000 | 1.004 | 51185767.0 | 51204536.0 | 51195152.0 | 68544981.0 | 0.747 | 0.75 | 51408736.0 |
| 2000 | 54837929.0 | 54533225.0 | 1.001 | 1.006 | 54892767.0 | 54860424.0 | 54876596.0 | 68907977.0 | 0.796 | 0.75 | 51680983.0 |
| 2001 | 56299562.0 | 55878421.0 | 1.003 | 1.011 | 56468461.0 | 56493084.0 | 56480772.0 | 72544955.0 | 0.779 | 0.75 | 54408716.0 |
| 2002 | 58592712.0 | 57807215.0 | 1.006 | 1.020 | 58944268.0 | 58963359.0 | 58953814.0 | 79228887.0 | 0.744 | 0.75 | 59421665.0 |
| 2003 | 57565344.0 | 55930654.0 | 1.011 | 1.040 | 58198563.0 | 58167880.0 | 58183221.0 | 86643542.0 | 0.672 | 0.65 | 56318302.0 |
| 2004 | 56976657.0 | 53774672.0 | 1.023 | 1.085 | 58287120.0 | 58345519.0 | 58316320.0 | 91763523.0 | 0.636 | 0.65 | 59646290.0 |
| 2005 | 56786410.0 | 50644994.0 | 1.051 | 1.184 | 59682517.0 | 59963673.0 | 59823095.0 | 94115312.0 | 0.636 | 0.65 | 61174953.0 |
| 2006 | 54641339.0 | 43606497.0 | 1.110 | 1.404 | 60651886.0 | 61223522.0 | 60937704.0 | 95272279.0 | 0.640 | 0.65 | 61926981.0 |
| 2007 | 48853563.0 | 27229969.0 | 1.292 | 2.390 | 63118803.0 | 65079626.0 | 64099215.0 | 95176240.0 | 0.673 | 0.65 | 61864556.0 |
# Exhibit II Sheet 1 — reconcile to Friedland PDF p142
assert np.allclose(
exhibit_ii_s1["Reported (2)"],
[47742304, 51185767, 54837929, 56299562, 58592712, 57565344, 56976657, 56786410, 54641339, 48853563],
)
assert np.allclose(
exhibit_ii_s1["Paid (3)"],
[47644187, 51000534, 54533225, 55878421, 57807215, 55930654, 53774672, 50644994, 43606497, 27229969],
)
assert np.allclose(
exhibit_ii_s1["Ult Reported (6)"],
[47742304, 51185767, 54892767, 56468461, 58944268, 58198563, 58287120, 59682517, 60651886, 63118803],
atol=1,
)
assert np.allclose(
exhibit_ii_s1["Ult Paid (7)"],
[47739475, 51204536, 54860424, 56493084, 58963359, 58167880, 58345519, 59963673, 61223522, 65079626],
atol=1,
)
assert np.allclose(
exhibit_ii_s1["Initial Selected (8)"],
[47740890, 51195152, 54876596, 56480772, 58953814, 58183221, 58316320, 59823095, 60937704, 64099215],
)
assert np.allclose(
exhibit_ii_s1["Earned Premium (9)"],
[68574209, 68544981, 68907977, 72544955, 79228887, 86643542, 91763523, 94115312, 95272279, 95176240],
)
assert np.allclose(
exhibit_ii_s1["Estimated Claim Ratio (10)"],
[0.696, 0.747, 0.796, 0.779, 0.744, 0.672, 0.636, 0.636, 0.640, 0.673],
)
assert np.allclose(
exhibit_ii_s1["Expected Claims (12)"],
[51430657, 51408736, 51680983, 54408716, 59421665, 56318302, 59646290, 61174953, 61926981, 61864556],
)
P143 (Exhibit II Sheet 2)#
exhibit_ii_s2 = unpaid_exhibit(ia["Reported Claims"], ia["Paid Claims"], expected_claims)
display(exhibit_ii_s2)
display(exhibit_ii_s2.sum().rename("Total").to_frame().T)
| Reported (2) | Paid (3) | Expected Claims (4) | Case Outstanding (5) | IBNR (6) | Total Unpaid (7) | |
|---|---|---|---|---|---|---|
| 1998 | 47742304.0 | 47644187.0 | 51430657.0 | 98117.0 | 3688353.0 | 3786470.0 |
| 1999 | 51185767.0 | 51000534.0 | 51408736.0 | 185233.0 | 222969.0 | 408202.0 |
| 2000 | 54837929.0 | 54533225.0 | 51680983.0 | 304704.0 | -3156946.0 | -2852242.0 |
| 2001 | 56299562.0 | 55878421.0 | 54408716.0 | 421141.0 | -1890846.0 | -1469705.0 |
| 2002 | 58592712.0 | 57807215.0 | 59421665.0 | 785497.0 | 828953.0 | 1614450.0 |
| 2003 | 57565344.0 | 55930654.0 | 56318302.0 | 1634690.0 | -1247042.0 | 387648.0 |
| 2004 | 56976657.0 | 53774672.0 | 59646290.0 | 3201985.0 | 2669633.0 | 5871618.0 |
| 2005 | 56786410.0 | 50644994.0 | 61174953.0 | 6141416.0 | 4388543.0 | 10529959.0 |
| 2006 | 54641339.0 | 43606497.0 | 61926981.0 | 11034842.0 | 7285642.0 | 18320484.0 |
| 2007 | 48853563.0 | 27229969.0 | 61864556.0 | 21623594.0 | 13010993.0 | 34634587.0 |
| Reported (2) | Paid (3) | Expected Claims (4) | Case Outstanding (5) | IBNR (6) | Total Unpaid (7) | |
|---|---|---|---|---|---|---|
| Total | 543481587.0 | 498050368.0 | 569281839.0 | 45431219.0 | 25800252.0 | 71231471.0 |
# Exhibit II Sheet 2 — reconcile to Friedland PDF p143
assert np.allclose(
exhibit_ii_s2["Case Outstanding (5)"],
[98117, 185233, 304704, 421141, 785497, 1634690, 3201985, 6141416, 11034842, 21623594],
)
assert np.allclose(
exhibit_ii_s2["IBNR (6)"],
[3688353, 222969, -3156946, -1890846, 828953, -1247042, 2669633, 4388543, 7285642, 13010993],
)
assert np.allclose(
exhibit_ii_s2["Total Unpaid (7)"],
[3786470, 408202, -2852242, -1469705, 1614450, 387648, 5871618, 10529959, 18320484, 34634587],
)
assert np.isclose(exhibit_ii_s2["Reported (2)"].sum(), 543481587)
assert np.isclose(exhibit_ii_s2["Paid (3)"].sum(), 498050368)
assert np.isclose(exhibit_ii_s2["Expected Claims (4)"].sum(), 569281839)
assert np.isclose(exhibit_ii_s2["Case Outstanding (5)"].sum(), 45431219)
assert np.isclose(exhibit_ii_s2["IBNR (6)"].sum(), 25800252)
assert np.isclose(exhibit_ii_s2["Total Unpaid (7)"].sum(), 71231471)
P144 (Exhibit III Sheet 1)#
xyz = cl.load_sample("friedland_xyz_auto_bi")
reported_pattern = {
12: 2.551, 24: 1.512, 36: 1.196, 48: 1.085, 60: 1.064, 72: 1.013,
84: 1.003, 96: 0.992, 108: 0.992, 120: 0.999, 132: 1.000,
}
paid_pattern = {
12: 21.999, 24: 6.569, 36: 3.160, 48: 2.007, 60: 1.525, 72: 1.268,
84: 1.116, 96: 1.054, 108: 1.031, 120: 1.014, 132: 1.010,
}
reported = cl.DevelopmentConstant(patterns=reported_pattern, style="cdf").fit_transform(
xyz["Reported Claims"]
)
paid = cl.DevelopmentConstant(patterns=paid_pattern, style="cdf").fit_transform(
xyz["Paid Claims"]
)
reported_ultimate = np.round(cl.Chainladder().fit(reported).ultimate_, 0)
paid_ultimate = np.round(cl.Chainladder().fit(paid).ultimate_, 0)
selected_ultimate = (reported_ultimate + paid_ultimate) / 2
earned_premium = xyz["Earned Premium"].latest_diagonal
estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)
avg_98_03 = float(np.round((selected_ultimate / earned_premium).iloc[:, :, 0:6, :].mean(), 3))
selected_claim_ratio = np.array(
[0.783, 0.783, 0.783, 0.783, 0.783, 0.783, 0.871, 0.783, 0.658, 0.638, 0.825]
)
sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)
el_reported = cl.ExpectedLoss(apriori=1).fit(
xyz["Reported Claims"], sample_weight=sample_weight
)
el_paid = cl.ExpectedLoss(apriori=1).fit(
xyz["Paid Claims"], sample_weight=sample_weight
)
expected_claims = np.round(el_reported.ultimate_, 0)
xyz_reported_ultimate = reported_ultimate
xyz_paid_ultimate = paid_ultimate
xyz_expected_claims = expected_claims
years = list(xyz["Reported Claims"].origin.year)
exhibit_iii_s1 = pd.DataFrame(index=years)
exhibit_iii_s1["Reported (2)"] = as_series(xyz["Reported Claims"].latest_diagonal).values
exhibit_iii_s1["Paid (3)"] = as_series(xyz["Paid Claims"].latest_diagonal).values
exhibit_iii_s1["CDF Reported (4)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(reported))["CDF"]
).values
exhibit_iii_s1["CDF Paid (5)"] = as_series(
cl.model_diagnostics(cl.Chainladder().fit(paid))["CDF"]
).values
exhibit_iii_s1["Ult Reported (6)"] = as_series(reported_ultimate).values
exhibit_iii_s1["Ult Paid (7)"] = as_series(paid_ultimate).values
exhibit_iii_s1["Initial Selected (8)"] = as_series(selected_ultimate).values
exhibit_iii_s1["Earned Premium (9)"] = as_series(earned_premium).values
exhibit_iii_s1["Estimated Claim Ratio (10)"] = as_series(estimated_claim_ratios).values
exhibit_iii_s1["Selected Claim Ratio (11)"] = selected_claim_ratio
exhibit_iii_s1["Expected Claims (12)"] = as_series(expected_claims).values
display(exhibit_iii_s1)
print(f"Average estimated claim ratio 1998-2003: {avg_98_03}")
| Reported (2) | Paid (3) | CDF Reported (4) | CDF Paid (5) | Ult Reported (6) | Ult Paid (7) | Initial Selected (8) | Earned Premium (9) | Estimated Claim Ratio (10) | Selected Claim Ratio (11) | Expected Claims (12) | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1998 | 15822.0 | 15822.0 | 1.000 | 1.010 | 15822.0 | 15980.0 | 15901.0 | 20000.0 | 0.795 | 0.783 | 15660.0 |
| 1999 | 25107.0 | 24817.0 | 0.999 | 1.014 | 25082.0 | 25164.0 | 25123.0 | 31500.0 | 0.798 | 0.783 | 24664.0 |
| 2000 | 37246.0 | 36782.0 | 0.992 | 1.031 | 36948.0 | 37922.0 | 37435.0 | 45000.0 | 0.832 | 0.783 | 35235.0 |
| 2001 | 38798.0 | 38519.0 | 0.992 | 1.054 | 38488.0 | 40599.0 | 39543.5 | 50000.0 | 0.791 | 0.783 | 39150.0 |
| 2002 | 48169.0 | 44437.0 | 1.003 | 1.116 | 48314.0 | 49592.0 | 48953.0 | 61183.0 | 0.800 | 0.783 | 47906.0 |
| 2003 | 44373.0 | 39320.0 | 1.013 | 1.268 | 44950.0 | 49858.0 | 47404.0 | 69175.0 | 0.685 | 0.783 | 54164.0 |
| 2004 | 70288.0 | 52811.0 | 1.064 | 1.525 | 74786.0 | 80537.0 | 77661.5 | 99322.0 | 0.782 | 0.871 | 86509.0 |
| 2005 | 70655.0 | 40026.0 | 1.085 | 2.007 | 76661.0 | 80332.0 | 78496.5 | 138151.0 | 0.568 | 0.783 | 108172.0 |
| 2006 | 48804.0 | 22819.0 | 1.196 | 3.160 | 58370.0 | 72108.0 | 65239.0 | 107578.0 | 0.606 | 0.658 | 70786.0 |
| 2007 | 31732.0 | 11865.0 | 1.512 | 6.569 | 47979.0 | 77941.0 | 62960.0 | 62438.0 | 1.008 | 0.638 | 39835.0 |
| 2008 | 18632.0 | 3409.0 | 2.551 | 21.999 | 47530.0 | 74995.0 | 61262.5 | 47797.0 | 1.282 | 0.825 | 39433.0 |
Average estimated claim ratio 1998-2003: 0.783
# Exhibit III Sheet 1 — reconcile to Friedland PDF p144
assert np.allclose(
exhibit_iii_s1["Reported (2)"],
[15822, 25107, 37246, 38798, 48169, 44373, 70288, 70655, 48804, 31732, 18632],
)
assert np.allclose(
exhibit_iii_s1["Paid (3)"],
[15822, 24817, 36782, 38519, 44437, 39320, 52811, 40026, 22819, 11865, 3409],
)
assert np.allclose(
exhibit_iii_s1["Ult Reported (6)"],
[15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],
atol=1,
)
assert np.allclose(
exhibit_iii_s1["Ult Paid (7)"],
[15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],
atol=1,
)
assert np.allclose(
exhibit_iii_s1["Initial Selected (8)"],
[15901, 25123, 37435, 39543, 48953, 47404, 77662, 78497, 65239, 62960, 61262],
atol=1,
)
assert np.allclose(
exhibit_iii_s1["Earned Premium (9)"],
[20000, 31500, 45000, 50000, 61183, 69175, 99322, 138151, 107578, 62438, 47797],
)
assert np.allclose(
exhibit_iii_s1["Estimated Claim Ratio (10)"],
[0.795, 0.798, 0.832, 0.791, 0.800, 0.685, 0.782, 0.568, 0.606, 1.008, 1.282],
)
assert np.isclose(avg_98_03, 0.783)
assert np.allclose(
exhibit_iii_s1["Expected Claims (12)"],
[15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],
atol=1,
)
P145 (Exhibit III Sheet 2)#
def relative_level_triangle(base, years=range(2004, 2009)):
pieces = []
for year in years:
rel = base / base.loc[:, :, str(year), :]
rel.columns = [str(year)]
pieces.append(rel)
return cl.concat(pieces, axis=1)
selected_view = np.round(selected_ultimate.iloc[:, :, 4:, :], 0)
earned_premium_view = earned_premium.iloc[:, :, 4:, :]
base_trend_2008 = (
cl.Trend(trends=[0.03425], dates=[("2008-12-31", "2002-01-01")])
.fit(selected_view)
.trend_
)
severity_trend_adjustment = relative_level_triangle(base_trend_2008)
base_tort_2008 = (
cl.Trend(
trends=[-0.25, 0.670 / 0.75 - 1],
dates=[("2007-12-31", "2006-12-31"), ("2006-12-31", "2005-12-31")],
)
.fit(selected_view)
.trend_
)
tort_reform_adjustment = relative_level_triangle(base_tort_2008)
rate_changes = [0, 0.05, 0.075, 0.15, 0.1, -0.2, -0.2]
olf = cl.parallelogram_olf(
rate_changes,
pd.to_datetime([f"{y}-01-01" for y in range(2002, 2009)]),
vertical_line=True,
)["OLF"].values
base_rate = selected_view * 0 + olf.reshape(selected_view.shape)
rate_level_adjustment = relative_level_triangle(base_rate)
adjusted_claim_ratios = (
selected_view * severity_trend_adjustment * tort_reform_adjustment
) / (earned_premium_view * rate_level_adjustment)
ay = list(range(2002, 2009))
ty = list(range(2004, 2009))
exhibit_iii_s2_ult = pd.DataFrame(
{"Initial Selected Ult (2)": as_series(selected_view).values}, index=ay
)
display(exhibit_iii_s2_ult)
exhibit_iii_s2_trend = pd.DataFrame(
np.round(severity_trend_adjustment.values.squeeze().T, 3),
index=ay,
columns=[f"Trend to {y} ({n})" for y, n in zip(ty, [3, 4, 5, 6, 7])],
)
display(exhibit_iii_s2_trend)
exhibit_iii_s2_tort = pd.DataFrame(
np.round(tort_reform_adjustment.values.squeeze().T, 3),
index=ay,
columns=[f"Tort to {y} ({n})" for y, n in zip(ty, [8, 9, 10, 11, 12])],
)
display(exhibit_iii_s2_tort)
exhibit_iii_s2_prem = pd.DataFrame(
{"Earned Premium (13)": as_series(earned_premium_view).values}, index=ay
)
display(exhibit_iii_s2_prem)
exhibit_iii_s2_rate = pd.DataFrame(
np.round(rate_level_adjustment.values.squeeze().T, 3),
index=ay,
columns=[f"Rate to {y} ({n})" for y, n in zip(ty, [14, 15, 16, 17, 18])],
)
display(exhibit_iii_s2_rate)
exhibit_iii_s2_lr = pd.DataFrame(
np.round(adjusted_claim_ratios.values.squeeze().T, 3),
index=ay,
columns=[f"OL Claim Ratio {y} ({n})" for y, n in zip(ty, [19, 20, 21, 22, 23])],
)
display(exhibit_iii_s2_lr)
vals = adjusted_claim_ratios.values.squeeze()
all_years = adjusted_claim_ratios.mean(axis="origin").values.squeeze()
ex_high_low = (vals.sum(axis=1) - vals.max(axis=1) - vals.min(axis=1)) / 5
latest_5 = (
adjusted_claim_ratios.loc[:, :, "2004":, :].mean(axis="origin").values.squeeze()
)
latest_3 = (
adjusted_claim_ratios.loc[:, :, "2006":, :].mean(axis="origin").values.squeeze()
)
average_claim_ratios = np.vstack([all_years, ex_high_low, latest_5, latest_3])
selected_expected_claim_ratio = ex_high_low
exhibit_iii_s2_avgs = pd.DataFrame(
np.round(average_claim_ratios, 3),
index=["All Years (24)", "All Years ex Hi/Lo (24)", "Latest 5 (24)", "Latest 3 (24)"],
columns=ty,
)
exhibit_iii_s2_avgs.loc["Selected (25)"] = np.round(selected_expected_claim_ratio, 3)
display(exhibit_iii_s2_avgs)
| Initial Selected Ult (2) | |
|---|---|
| 2002 | 48953.0 |
| 2003 | 47404.0 |
| 2004 | 77662.0 |
| 2005 | 78496.0 |
| 2006 | 65239.0 |
| 2007 | 62960.0 |
| 2008 | 61262.0 |
| Trend to 2004 (3) | Trend to 2005 (4) | Trend to 2006 (5) | Trend to 2007 (6) | Trend to 2008 (7) | |
|---|---|---|---|---|---|
| 2002 | 1.070 | 1.106 | 1.144 | 1.183 | 1.224 |
| 2003 | 1.034 | 1.070 | 1.106 | 1.144 | 1.183 |
| 2004 | 1.000 | 1.034 | 1.070 | 1.106 | 1.144 |
| 2005 | 0.967 | 1.000 | 1.034 | 1.070 | 1.106 |
| 2006 | 0.935 | 0.967 | 1.000 | 1.034 | 1.070 |
| 2007 | 0.904 | 0.935 | 0.967 | 1.000 | 1.034 |
| 2008 | 0.874 | 0.904 | 0.935 | 0.967 | 1.000 |
| Tort to 2004 (8) | Tort to 2005 (9) | Tort to 2006 (10) | Tort to 2007 (11) | Tort to 2008 (12) | |
|---|---|---|---|---|---|
| 2002 | 1.000 | 1.000 | 0.893 | 0.67 | 0.67 |
| 2003 | 1.000 | 1.000 | 0.893 | 0.67 | 0.67 |
| 2004 | 1.000 | 1.000 | 0.893 | 0.67 | 0.67 |
| 2005 | 1.000 | 1.000 | 0.893 | 0.67 | 0.67 |
| 2006 | 1.119 | 1.119 | 1.000 | 0.75 | 0.75 |
| 2007 | 1.493 | 1.493 | 1.333 | 1.00 | 1.00 |
| 2008 | 1.493 | 1.493 | 1.333 | 1.00 | 1.00 |
| Earned Premium (13) | |
|---|---|
| 2002 | 61183.0 |
| 2003 | 69175.0 |
| 2004 | 99322.0 |
| 2005 | 138151.0 |
| 2006 | 107578.0 |
| 2007 | 62438.0 |
| 2008 | 47797.0 |
| Rate to 2004 (14) | Rate to 2005 (15) | Rate to 2006 (16) | Rate to 2007 (17) | Rate to 2008 (18) | |
|---|---|---|---|---|---|
| 2002 | 1.129 | 1.298 | 1.428 | 1.142 | 0.914 |
| 2003 | 1.075 | 1.236 | 1.360 | 1.088 | 0.870 |
| 2004 | 1.000 | 1.150 | 1.265 | 1.012 | 0.810 |
| 2005 | 0.870 | 1.000 | 1.100 | 0.880 | 0.704 |
| 2006 | 0.791 | 0.909 | 1.000 | 0.800 | 0.640 |
| 2007 | 0.988 | 1.136 | 1.250 | 1.000 | 0.800 |
| 2008 | 1.235 | 1.420 | 1.562 | 1.250 | 1.000 |
| OL Claim Ratio 2004 (19) | OL Claim Ratio 2005 (20) | OL Claim Ratio 2006 (21) | OL Claim Ratio 2007 (22) | OL Claim Ratio 2008 (23) | |
|---|---|---|---|---|---|
| 2002 | 0.758 | 0.682 | 0.573 | 0.555 | 0.718 |
| 2003 | 0.659 | 0.593 | 0.498 | 0.483 | 0.624 |
| 2004 | 0.782 | 0.703 | 0.591 | 0.573 | 0.740 |
| 2005 | 0.632 | 0.568 | 0.477 | 0.463 | 0.598 |
| 2006 | 0.803 | 0.722 | 0.606 | 0.588 | 0.760 |
| 2007 | 1.377 | 1.238 | 1.040 | 1.008 | 1.304 |
| 2008 | 1.354 | 1.217 | 1.022 | 0.991 | 1.282 |
| 2004 | 2005 | 2006 | 2007 | 2008 | |
|---|---|---|---|---|---|
| All Years (24) | 0.909 | 0.818 | 0.687 | 0.666 | 0.861 |
| All Years ex Hi/Lo (24) | 0.871 | 0.783 | 0.658 | 0.638 | 0.825 |
| Latest 5 (24) | 0.989 | 0.890 | 0.747 | 0.725 | 0.937 |
| Latest 3 (24) | 1.178 | 1.059 | 0.890 | 0.863 | 1.115 |
| Selected (25) | 0.871 | 0.783 | 0.658 | 0.638 | 0.825 |
# Exhibit III Sheet 2 — reconcile to Friedland PDF p145
assert np.allclose(
as_series(selected_view).values,
[48953, 47404, 77662, 78497, 65239, 62960, 61262],
atol=1,
)
assert np.allclose(
exhibit_iii_s2_trend.values,
[
[1.070, 1.106, 1.144, 1.183, 1.224],
[1.034, 1.070, 1.106, 1.144, 1.183],
[1.000, 1.034, 1.070, 1.106, 1.144],
[0.967, 1.000, 1.034, 1.070, 1.106],
[0.935, 0.967, 1.000, 1.034, 1.070],
[0.904, 0.935, 0.967, 1.000, 1.034],
[0.874, 0.904, 0.935, 0.967, 1.000],
],
)
assert np.allclose(
exhibit_iii_s2_tort.values,
[
[1.000, 1.000, 0.893, 0.670, 0.670],
[1.000, 1.000, 0.893, 0.670, 0.670],
[1.000, 1.000, 0.893, 0.670, 0.670],
[1.000, 1.000, 0.893, 0.670, 0.670],
[1.119, 1.119, 1.000, 0.750, 0.750],
[1.493, 1.493, 1.333, 1.000, 1.000],
[1.493, 1.493, 1.333, 1.000, 1.000],
],
)
assert np.allclose(
exhibit_iii_s2_prem["Earned Premium (13)"],
[61183, 69175, 99322, 138151, 107578, 62438, 47797],
)
assert np.allclose(
exhibit_iii_s2_rate.values,
[
[1.129, 1.298, 1.428, 1.142, 0.914],
[1.075, 1.236, 1.360, 1.088, 0.870],
[1.000, 1.150, 1.265, 1.012, 0.810],
[0.870, 1.000, 1.100, 0.880, 0.704],
[0.791, 0.909, 1.000, 0.800, 0.640],
[0.988, 1.136, 1.250, 1.000, 0.800],
[1.235, 1.420, 1.562, 1.250, 1.000],
],
)
assert np.allclose(
exhibit_iii_s2_lr.values,
[
[0.758, 0.682, 0.573, 0.555, 0.718],
[0.659, 0.593, 0.498, 0.483, 0.624],
[0.782, 0.703, 0.591, 0.573, 0.740],
[0.632, 0.568, 0.477, 0.463, 0.598],
[0.803, 0.722, 0.606, 0.588, 0.760],
[1.377, 1.238, 1.040, 1.008, 1.304],
[1.354, 1.217, 1.022, 0.991, 1.282],
],
)
assert np.allclose(
np.round(average_claim_ratios, 3),
[
[0.909, 0.818, 0.687, 0.666, 0.861],
[0.871, 0.783, 0.658, 0.638, 0.825],
[0.989, 0.890, 0.747, 0.725, 0.937],
[1.178, 1.059, 0.890, 0.863, 1.115],
],
)
assert np.allclose(
np.round(selected_expected_claim_ratio, 3), [0.871, 0.783, 0.658, 0.638, 0.825]
)
P146 (Exhibit III Sheet 3)#
exhibit_iii_s3 = unpaid_exhibit(
xyz["Reported Claims"], xyz["Paid Claims"], xyz_expected_claims
)
display(exhibit_iii_s3)
display(exhibit_iii_s3.sum().rename("Total").to_frame().T)
| Reported (2) | Paid (3) | Expected Claims (4) | Case Outstanding (5) | IBNR (6) | Total Unpaid (7) | |
|---|---|---|---|---|---|---|
| 1998 | 15822.0 | 15822.0 | 15660.0 | 0.0 | -162.0 | -162.0 |
| 1999 | 25107.0 | 24817.0 | 24664.0 | 290.0 | -443.0 | -153.0 |
| 2000 | 37246.0 | 36782.0 | 35235.0 | 464.0 | -2011.0 | -1547.0 |
| 2001 | 38798.0 | 38519.0 | 39150.0 | 279.0 | 352.0 | 631.0 |
| 2002 | 48169.0 | 44437.0 | 47906.0 | 3732.0 | -263.0 | 3469.0 |
| 2003 | 44373.0 | 39320.0 | 54164.0 | 5053.0 | 9791.0 | 14844.0 |
| 2004 | 70288.0 | 52811.0 | 86509.0 | 17477.0 | 16221.0 | 33698.0 |
| 2005 | 70655.0 | 40026.0 | 108172.0 | 30629.0 | 37517.0 | 68146.0 |
| 2006 | 48804.0 | 22819.0 | 70786.0 | 25985.0 | 21982.0 | 47967.0 |
| 2007 | 31732.0 | 11865.0 | 39835.0 | 19867.0 | 8103.0 | 27970.0 |
| 2008 | 18632.0 | 3409.0 | 39433.0 | 15223.0 | 20801.0 | 36024.0 |
| Reported (2) | Paid (3) | Expected Claims (4) | Case Outstanding (5) | IBNR (6) | Total Unpaid (7) | |
|---|---|---|---|---|---|---|
| Total | 449626.0 | 330627.0 | 561514.0 | 118999.0 | 111888.0 | 230887.0 |
# Exhibit III Sheet 3 — reconcile to Friedland PDF p146
assert np.allclose(
exhibit_iii_s3["Expected Claims (4)"],
[15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],
atol=1,
)
assert np.allclose(
exhibit_iii_s3["Case Outstanding (5)"],
[0, 290, 465, 278, 3731, 5052, 17477, 30629, 25985, 19867, 15223],
atol=1,
)
assert np.allclose(
exhibit_iii_s3["IBNR (6)"],
[-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],
atol=1,
)
assert np.allclose(
exhibit_iii_s3["Total Unpaid (7)"],
[-162, -152, -1547, 631, 3469, 14844, 33698, 68146, 47967, 27970, 36024],
atol=1,
)
assert np.isclose(exhibit_iii_s3["Reported (2)"].sum(), 449626)
assert np.isclose(exhibit_iii_s3["Paid (3)"].sum(), 330629)
assert np.isclose(exhibit_iii_s3["Expected Claims (4)"].sum(), 561516, atol=1)
assert np.isclose(exhibit_iii_s3["Case Outstanding (5)"].sum(), 118997, atol=1)
assert np.isclose(exhibit_iii_s3["IBNR (6)"].sum(), 111890, atol=1)
assert np.isclose(exhibit_iii_s3["Total Unpaid (7)"].sum(), 230887, atol=1)
P147 (Exhibit III Sheet 4)#
exhibit_iii_s4 = pd.DataFrame(index=years)
exhibit_iii_s4["Reported (2)"] = exhibit_iii_s1["Reported (2)"]
exhibit_iii_s4["Paid (3)"] = exhibit_iii_s1["Paid (3)"]
exhibit_iii_s4["Dev Ult Reported (4)"] = exhibit_iii_s1["Ult Reported (6)"]
exhibit_iii_s4["Dev Ult Paid (5)"] = exhibit_iii_s1["Ult Paid (7)"]
exhibit_iii_s4["Expected Claims (6)"] = exhibit_iii_s1["Expected Claims (12)"]
display(exhibit_iii_s4)
display(exhibit_iii_s4.sum().rename("Total").to_frame().T)
| Reported (2) | Paid (3) | Dev Ult Reported (4) | Dev Ult Paid (5) | Expected Claims (6) | |
|---|---|---|---|---|---|
| 1998 | 15822.0 | 15822.0 | 15822.0 | 15980.0 | 15660.0 |
| 1999 | 25107.0 | 24817.0 | 25082.0 | 25164.0 | 24664.0 |
| 2000 | 37246.0 | 36782.0 | 36948.0 | 37922.0 | 35235.0 |
| 2001 | 38798.0 | 38519.0 | 38488.0 | 40599.0 | 39150.0 |
| 2002 | 48169.0 | 44437.0 | 48314.0 | 49592.0 | 47906.0 |
| 2003 | 44373.0 | 39320.0 | 44950.0 | 49858.0 | 54164.0 |
| 2004 | 70288.0 | 52811.0 | 74786.0 | 80537.0 | 86509.0 |
| 2005 | 70655.0 | 40026.0 | 76661.0 | 80332.0 | 108172.0 |
| 2006 | 48804.0 | 22819.0 | 58370.0 | 72108.0 | 70786.0 |
| 2007 | 31732.0 | 11865.0 | 47979.0 | 77941.0 | 39835.0 |
| 2008 | 18632.0 | 3409.0 | 47530.0 | 74995.0 | 39433.0 |
| Reported (2) | Paid (3) | Dev Ult Reported (4) | Dev Ult Paid (5) | Expected Claims (6) | |
|---|---|---|---|---|---|
| Total | 449626.0 | 330627.0 | 514930.0 | 605028.0 | 561514.0 |
# Exhibit III Sheet 4 — reconcile to Friedland PDF p147
assert np.allclose(
exhibit_iii_s4["Dev Ult Reported (4)"],
[15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],
atol=1,
)
assert np.allclose(
exhibit_iii_s4["Dev Ult Paid (5)"],
[15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],
atol=1,
)
assert np.allclose(
exhibit_iii_s4["Expected Claims (6)"],
[15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],
atol=1,
)
assert np.isclose(exhibit_iii_s4["Dev Ult Reported (4)"].sum(), 514929, atol=1)
assert np.isclose(exhibit_iii_s4["Dev Ult Paid (5)"].sum(), 605030, atol=1)
assert np.isclose(exhibit_iii_s4["Expected Claims (6)"].sum(), 561516, atol=1)
P148 (Exhibit III Sheet 5)#
exhibit_iii_s5 = pd.DataFrame(index=years)
exhibit_iii_s5["Case Outstanding (2)"] = exhibit_iii_s3["Case Outstanding (5)"]
exhibit_iii_s5["Dev IBNR Reported (3)"] = (
exhibit_iii_s4["Dev Ult Reported (4)"] - exhibit_iii_s4["Reported (2)"]
)
exhibit_iii_s5["Dev IBNR Paid (4)"] = (
exhibit_iii_s4["Dev Ult Paid (5)"] - exhibit_iii_s4["Reported (2)"]
)
exhibit_iii_s5["Expected IBNR (5)"] = exhibit_iii_s3["IBNR (6)"]
display(exhibit_iii_s5)
display(exhibit_iii_s5.sum().rename("Total").to_frame().T)
| Case Outstanding (2) | Dev IBNR Reported (3) | Dev IBNR Paid (4) | Expected IBNR (5) | |
|---|---|---|---|---|
| 1998 | 0.0 | 0.0 | 158.0 | -162.0 |
| 1999 | 290.0 | -25.0 | 57.0 | -443.0 |
| 2000 | 464.0 | -298.0 | 676.0 | -2011.0 |
| 2001 | 279.0 | -310.0 | 1801.0 | 352.0 |
| 2002 | 3732.0 | 145.0 | 1423.0 | -263.0 |
| 2003 | 5053.0 | 577.0 | 5485.0 | 9791.0 |
| 2004 | 17477.0 | 4498.0 | 10249.0 | 16221.0 |
| 2005 | 30629.0 | 6006.0 | 9677.0 | 37517.0 |
| 2006 | 25985.0 | 9566.0 | 23304.0 | 21982.0 |
| 2007 | 19867.0 | 16247.0 | 46209.0 | 8103.0 |
| 2008 | 15223.0 | 28898.0 | 56363.0 | 20801.0 |
| Case Outstanding (2) | Dev IBNR Reported (3) | Dev IBNR Paid (4) | Expected IBNR (5) | |
|---|---|---|---|---|
| Total | 118999.0 | 65304.0 | 155402.0 | 111888.0 |
# Exhibit III Sheet 5 — reconcile to Friedland PDF p148
assert np.allclose(
exhibit_iii_s5["Dev IBNR Reported (3)"],
[0, -25, -298, -311, 144, 577, 4499, 6006, 9566, 16247, 28898],
atol=1,
)
assert np.allclose(
exhibit_iii_s5["Dev IBNR Paid (4)"],
[158, 58, 676, 1802, 1423, 5485, 10249, 9678, 23304, 46209, 56363],
atol=1,
)
assert np.allclose(
exhibit_iii_s5["Expected IBNR (5)"],
[-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],
atol=1,
)
assert np.isclose(exhibit_iii_s5["Case Outstanding (2)"].sum(), 118997, atol=1)
assert np.isclose(exhibit_iii_s5["Dev IBNR Reported (3)"].sum(), 65303, atol=1)
assert np.isclose(exhibit_iii_s5["Dev IBNR Paid (4)"].sum(), 155405, atol=3)
assert np.isclose(exhibit_iii_s5["Expected IBNR (5)"].sum(), 111890, atol=1)
P149 (Exhibit IV Sheet 1)#
def changing_conditions_exhibit(triangle, claim_ratio, actual_ibnr_values):
earned = np.round(triangle["Earned Premium"].latest_diagonal, 0)
el = cl.ExpectedLoss(apriori=claim_ratio).fit(
triangle["Reported Claims"], sample_weight=earned
)
expected = np.round(el.ultimate_, 0)
reported = triangle["Reported Claims"].latest_diagonal
estimated_ibnr = np.round(el.ibnr_, 0).fillzero()
actual_ibnr = estimated_ibnr.copy()
actual_ibnr.values = np.array(actual_ibnr_values, dtype=float).reshape(
estimated_ibnr.shape
)
difference = np.round(actual_ibnr - estimated_ibnr, 0).fillzero()
out = pd.DataFrame(index=list(triangle["Reported Claims"].origin.year))
out["Earned Premium (2)"] = as_series(earned).values
out["Claim Ratio (3)"] = claim_ratio
out["Expected Claims (4)"] = as_series(expected).values
out["Reported (5)"] = as_series(reported).values
out["Estimated IBNR (6)"] = as_series(estimated_ibnr).values
out["Actual IBNR (7)"] = as_series(actual_ibnr).values
out["Difference (8)"] = as_series(difference).values
return out
uspp = cl.load_sample("friedland_uspp")
exhibit_iv_steady = changing_conditions_exhibit(
uspp.loc["Steady State"],
0.70,
[0, 0, 0, 0, 8508, 8934, 18761, 49249, 103422, 249764],
)
exhibit_iv_incr_claim = changing_conditions_exhibit(
uspp.loc["Increasing Claim"],
0.70,
[0, 0, 0, 0, 8508, 10210, 22782, 63320, 140358, 356805],
)
print("Steady-State")
display(exhibit_iv_steady)
display(exhibit_iv_steady[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
print("Increasing Claim Ratios")
display(exhibit_iv_incr_claim)
display(exhibit_iv_incr_claim[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
Steady-State
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 1000000.0 | 0.7 | 700000.0 | 700000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 1050000.0 | 0.7 | 735000.0 | 735000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 1102500.0 | 0.7 | 771750.0 | 771750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 1157625.0 | 0.7 | 810338.0 | 810338.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 1215506.0 | 0.7 | 850854.0 | 842346.0 | 8508.0 | 8508.0 | 0.0 |
| 2004 | 1276282.0 | 0.7 | 893397.0 | 884463.0 | 8934.0 | 8934.0 | 0.0 |
| 2005 | 1340096.0 | 0.7 | 938067.0 | 919306.0 | 18761.0 | 18761.0 | 0.0 |
| 2006 | 1407100.0 | 0.7 | 984970.0 | 935722.0 | 49248.0 | 49249.0 | 1.0 |
| 2007 | 1477455.0 | 0.7 | 1034218.0 | 930797.0 | 103421.0 | 103422.0 | 1.0 |
| 2008 | 1551328.0 | 0.7 | 1085930.0 | 836166.0 | 249764.0 | 249764.0 | 0.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 12577892.0 | 8804524.0 | 8365888.0 | 438636.0 | 438638.0 | 2.0 |
Increasing Claim Ratios
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 1000000.0 | 0.7 | 700000.0 | 700000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 1050000.0 | 0.7 | 735000.0 | 735000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 1102500.0 | 0.7 | 771750.0 | 771750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 1157625.0 | 0.7 | 810338.0 | 810338.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 1215506.0 | 0.7 | 850854.0 | 842346.0 | 8508.0 | 8508.0 | 0.0 |
| 2004 | 1276282.0 | 0.7 | 893397.0 | 1010815.0 | -117418.0 | 10210.0 | 127628.0 |
| 2005 | 1340096.0 | 0.7 | 938067.0 | 1116300.0 | -178233.0 | 22782.0 | 201015.0 |
| 2006 | 1407100.0 | 0.7 | 984970.0 | 1203071.0 | -218101.0 | 63320.0 | 281421.0 |
| 2007 | 1477455.0 | 0.7 | 1034218.0 | 1263224.0 | -229006.0 | 140358.0 | 369364.0 |
| 2008 | 1551328.0 | 0.7 | 1085930.0 | 1194523.0 | -108593.0 | 356805.0 | 465398.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 12577892.0 | 8804524.0 | 9647367.0 | -842843.0 | 601983.0 | 1444826.0 |
# Exhibit IV Sheet 1 — reconcile to Friedland PDF p149
assert np.allclose(
exhibit_iv_steady["Earned Premium (2)"],
[1000000, 1050000, 1102500, 1157625, 1215506, 1276282, 1340096, 1407100, 1477455, 1551328],
)
assert np.allclose(
exhibit_iv_steady["Expected Claims (4)"],
[700000, 735000, 771750, 810338, 850854, 893397, 938067, 984970, 1034219, 1085930],
atol=1,
)
assert np.isclose(exhibit_iv_steady["Earned Premium (2)"].sum(), 12577893, atol=1)
assert np.isclose(exhibit_iv_steady["Expected Claims (4)"].sum(), 8804525, atol=1)
assert np.isclose(exhibit_iv_steady["Estimated IBNR (6)"].sum(), 438638, atol=2)
assert np.isclose(exhibit_iv_steady["Difference (8)"].sum(), 0, atol=2)
assert np.allclose(
exhibit_iv_incr_claim["Estimated IBNR (6)"],
[0, 0, 0, 0, 8508, -117418, -178233, -218101, -229006, -108593],
atol=1,
)
assert np.isclose(exhibit_iv_incr_claim["Estimated IBNR (6)"].sum(), -842841, atol=2)
assert np.isclose(exhibit_iv_incr_claim["Actual IBNR (7)"].sum(), 601984, atol=1)
assert np.isclose(exhibit_iv_incr_claim["Difference (8)"].sum(), 1444824, atol=2)
P150 (Exhibit IV Sheet 2)#
exhibit_iv_case = changing_conditions_exhibit(
uspp.loc["Increasing Case"],
0.70,
[0, 0, 0, 0, 8509, 8934, 4690, 22162, 54296, 154745],
)
exhibit_iv_both = changing_conditions_exhibit(
uspp.loc["Increasing Claim Case"],
0.70,
[0, 0, 0, 0, 8509, 10210, 5695, 28494, 73688, 221064],
)
print("Increasing Case Outstanding Strength")
display(exhibit_iv_case)
display(exhibit_iv_case[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
print("Increasing Claim Ratios and Case Outstanding Strength")
display(exhibit_iv_both)
display(exhibit_iv_both[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
Increasing Case Outstanding Strength
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 1000000.0 | 0.7 | 700000.0 | 700000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 1050000.0 | 0.7 | 735000.0 | 735000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 1102500.0 | 0.7 | 771750.0 | 771750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 1157625.0 | 0.7 | 810338.0 | 810338.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 1215506.0 | 0.7 | 850854.0 | 842346.0 | 8508.0 | 8509.0 | 1.0 |
| 2004 | 1276282.0 | 0.7 | 893397.0 | 884463.0 | 8934.0 | 8934.0 | 0.0 |
| 2005 | 1340096.0 | 0.7 | 938067.0 | 933377.0 | 4690.0 | 4690.0 | 0.0 |
| 2006 | 1407100.0 | 0.7 | 984970.0 | 962808.0 | 22162.0 | 22162.0 | 0.0 |
| 2007 | 1477455.0 | 0.7 | 1034218.0 | 979922.0 | 54296.0 | 54296.0 | 0.0 |
| 2008 | 1551328.0 | 0.7 | 1085930.0 | 931185.0 | 154745.0 | 154745.0 | 0.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 12577892.0 | 8804524.0 | 8551189.0 | 253335.0 | 253336.0 | 1.0 |
Increasing Claim Ratios and Case Outstanding Strength
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 1000000.0 | 0.7 | 700000.0 | 700000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 1050000.0 | 0.7 | 735000.0 | 735000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 1102500.0 | 0.7 | 771750.0 | 771750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 1157625.0 | 0.7 | 810338.0 | 810338.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 1215506.0 | 0.7 | 850854.0 | 842346.0 | 8508.0 | 8509.0 | 1.0 |
| 2004 | 1276282.0 | 0.7 | 893397.0 | 1010815.0 | -117418.0 | 10210.0 | 127628.0 |
| 2005 | 1340096.0 | 0.7 | 938067.0 | 1133386.0 | -195319.0 | 5695.0 | 201014.0 |
| 2006 | 1407100.0 | 0.7 | 984970.0 | 1237897.0 | -252927.0 | 28494.0 | 281421.0 |
| 2007 | 1477455.0 | 0.7 | 1034218.0 | 1329895.0 | -295677.0 | 73688.0 | 369365.0 |
| 2008 | 1551328.0 | 0.7 | 1085930.0 | 1330264.0 | -244334.0 | 221064.0 | 465398.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 12577892.0 | 8804524.0 | 9901691.0 | -1097167.0 | 347660.0 | 1444827.0 |
# Exhibit IV Sheet 2 — reconcile to Friedland PDF p150
assert np.isclose(exhibit_iv_case["Estimated IBNR (6)"].sum(), 253336, atol=1)
assert np.isclose(exhibit_iv_case["Difference (8)"].sum(), 0, atol=1)
assert np.isclose(exhibit_iv_both["Estimated IBNR (6)"].sum(), -1097165, atol=2)
assert np.isclose(exhibit_iv_both["Actual IBNR (7)"].sum(), 347660, atol=1)
assert np.isclose(exhibit_iv_both["Difference (8)"].sum(), 1444824, atol=3)
P151 (Exhibit V)#
us_auto = cl.load_sample("friedland_us_auto")
exhibit_v_steady = changing_conditions_exhibit(
us_auto.loc["Steady State"],
0.75,
[0, 0, 0, 0, 8509, 29354, 61644, 173073, 363454, 758599],
)
exhibit_v_mix = changing_conditions_exhibit(
us_auto.loc["Changing Product Mix"],
0.75,
[0, 0, 0, 0, 8509, 29354, 71855, 239057, 596924, 1445385],
)
print("Steady-State (No Change in Product Mix)")
display(exhibit_v_steady)
display(exhibit_v_steady[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
print("Changing Product Mix")
display(exhibit_v_mix)
display(exhibit_v_mix[["Earned Premium (2)", "Expected Claims (4)", "Reported (5)", "Estimated IBNR (6)", "Actual IBNR (7)", "Difference (8)"]].sum().rename("Total").to_frame().T)
Steady-State (No Change in Product Mix)
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 2000000.0 | 0.75 | 1500000.0 | 1500000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 2100000.0 | 0.75 | 1575000.0 | 1575000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 2205000.0 | 0.75 | 1653750.0 | 1653750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 2315250.0 | 0.75 | 1736438.0 | 1736438.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 2431013.0 | 0.75 | 1823260.0 | 1814751.0 | 8509.0 | 8509.0 | 0.0 |
| 2004 | 2552563.0 | 0.75 | 1914422.0 | 1885068.0 | 29354.0 | 29354.0 | 0.0 |
| 2005 | 2680191.0 | 0.75 | 2010143.0 | 1948499.0 | 61644.0 | 61644.0 | 0.0 |
| 2006 | 2814201.0 | 0.75 | 2110651.0 | 1937577.0 | 173074.0 | 173073.0 | -1.0 |
| 2007 | 2954911.0 | 0.75 | 2216183.0 | 1852729.0 | 363454.0 | 363454.0 | 0.0 |
| 2008 | 3102656.0 | 0.75 | 2326992.0 | 1568393.0 | 758599.0 | 758599.0 | 0.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 25155785.0 | 18866839.0 | 17472205.0 | 1394634.0 | 1394633.0 | -1.0 |
Changing Product Mix
| Earned Premium (2) | Claim Ratio (3) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|---|
| 1999 | 2000000.0 | 0.75 | 1500000.0 | 1500000.0 | 0.0 | 0.0 | 0.0 |
| 2000 | 2100000.0 | 0.75 | 1575000.0 | 1575000.0 | 0.0 | 0.0 | 0.0 |
| 2001 | 2205000.0 | 0.75 | 1653750.0 | 1653750.0 | 0.0 | 0.0 | 0.0 |
| 2002 | 2315250.0 | 0.75 | 1736438.0 | 1736438.0 | 0.0 | 0.0 | 0.0 |
| 2003 | 2431013.0 | 0.75 | 1823260.0 | 1814751.0 | 8509.0 | 8509.0 | 0.0 |
| 2004 | 2552563.0 | 0.75 | 1914422.0 | 1885068.0 | 29354.0 | 29354.0 | 0.0 |
| 2005 | 2999262.0 | 0.75 | 2249446.0 | 2193545.0 | 55902.0 | 71855.0 | 15953.0 |
| 2006 | 3564016.0 | 0.75 | 2673012.0 | 2471446.0 | 201566.0 | 239057.0 | 37491.0 |
| 2007 | 4281446.0 | 0.75 | 3211084.0 | 2680487.0 | 530598.0 | 596924.0 | 66326.0 |
| 2008 | 5196516.0 | 0.75 | 3897387.0 | 2556695.0 | 1340692.0 | 1445385.0 | 104693.0 |
| Earned Premium (2) | Expected Claims (4) | Reported (5) | Estimated IBNR (6) | Actual IBNR (7) | Difference (8) | |
|---|---|---|---|---|---|---|
| Total | 29645066.0 | 22233799.0 | 20067180.0 | 2166621.0 | 2391084.0 | 224463.0 |
# Exhibit V — reconcile to Friedland PDF p151
assert np.allclose(
exhibit_v_steady["Earned Premium (2)"],
[2000000, 2100000, 2205000, 2315250, 2431013, 2552563, 2680191, 2814201, 2954911, 3102656],
atol=1,
)
assert np.isclose(exhibit_v_steady["Earned Premium (2)"].sum(), 25155785, atol=1)
assert np.isclose(exhibit_v_steady["Expected Claims (4)"].sum(), 18866839, atol=1)
assert np.isclose(exhibit_v_steady["Estimated IBNR (6)"].sum(), 1394634, atol=1)
assert np.isclose(exhibit_v_steady["Difference (8)"].sum(), 0, atol=1)
assert np.isclose(exhibit_v_mix["Earned Premium (2)"].sum(), 29645066, atol=1)
assert np.isclose(exhibit_v_mix["Expected Claims (4)"].sum(), 22233799, atol=1)
assert np.isclose(exhibit_v_mix["Estimated IBNR (6)"].sum(), 2166620, atol=1)
assert np.isclose(exhibit_v_mix["Actual IBNR (7)"].sum(), 2391084, atol=1)
assert np.isclose(exhibit_v_mix["Difference (8)"].sum(), 224465, atol=2)