"""Policy data and attributes
This Space contains policy data and Cells associated with
policy attributes and policy values.
.. rubric:: References
Attributes:
PolicyData: `PandasData`_ object holding model point data as a pandas
DataFrame read from the model point file.
ProductSpec: `ExcelRange`_ object which is a dict like object for associating
product spec parameters to model point keys.
LifeTable: Alias for :mod:`~fastlife.model.LifeTable`
PremTerm: Alias for :func:`PolicyTerm`
.. _PandasData:
https://docs.modelx.io/en/latest/reference/dataclient.html#pandasdata
.. _ExcelRange:
https://docs.modelx.io/en/latest/reference/dataclient.html#excelrange
"""
from modelx.serialize.jsonvalues import *
_formula = None
_bases = []
_allow_none = True
_spaces = []
# ---------------------------------------------------------------------------
# Cells
[docs]def AnnPremRate():
"""Annualized Premium Rate per Sum Assured"""
return GrossPremRate() * PremFreq().mask(PremFreq() == 0, other=1/10)
[docs]def CashValueRate(t):
"""Cash Value Rate per Sum Assured"""
return np.maximum(ReserveNLP_Rate('PREM', t) - SurrCharge(t), 0)
[docs]def GrossPremRate():
"""Gross Premium Rate per Sum Assured per payment"""
def get_value(pol):
prod = pol['Product']
alpha = pol['LoadAcqSA']
beta = pol['LoadMaintPrem']
delta = pol['LoadMaintPrem2']
gamma = pol['LoadMaintSA']
gamma2 = pol['LoadMaintSA2']
freq = pol['PremFreq']
x, n, m = pol['IssueAge'], pol['PolicyTerm'], pol['PolicyTerm']
comf = LifeTable[pol['Sex'], pol['IntRate_PREM'], pol['TableID_PREM']]
if prod == 'TERM' or prod == 'WL':
return (comf.Axn(x, n) + alpha + gamma * comf.AnnDuenx(x, n, freq)
+ gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / (1-beta-delta) / freq / comf.AnnDuenx(x, m, freq)
elif prod == 'ENDW':
return (comf.Exn(x, n) + comf.Axn(x, n) + alpha + gamma * comf.AnnDuenx(x, n, freq)
+ gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / (1-beta-delta) / freq / comf.AnnDuenx(x, m, freq)
else:
raise ValueError('invalid product')
result = PolicyDataExt1().apply(get_value, axis=1)
result.name = 'GrossPremRate'
return result
[docs]def GrossPremTable():
"""Gross premium table"""
return None
[docs]def InitSurrCharge():
"""Initial Surrender Charge Rate"""
def get_value(pol):
prod, polt, gen = pol['Product'], pol['PolicyType'], pol['Gen']
term = pol['PolicyTerm']
param1 = SpecLookup.match("SurrChargeParam1", prod, polt, gen).value
param2 = SpecLookup.match("SurrChargeParam2", prod, polt, gen).value
if param1 is None or param2 is None:
raise ValueError('SurrChargeParam not found')
return param1 + param2 * min(term / 10, 1)
result = PolicyData().apply(get_value, axis=1)
result.name = 'InitSurrCharge'
return result
[docs]def IntRate(RateBasis):
"""Interest Rate"""
if RateBasis == 'PREM':
basis = 'IntRatePrem'
elif RateBasis == 'VAL':
basis = 'IntRateVal'
else:
raise ValueError('invalid RateBasis')
def get_value(pol):
result = SpecLookup.match(basis,
pol["Product"],
pol["PolicyType"],
pol["Gen"]).value
if result is not None:
return result
else:
raise ValueError('lookup failed')
result = PolicyData().apply(get_value, axis=1)
result.name = 'IntRate_' + RateBasis
return result
[docs]def LoadAcqSA():
"""Acquisition Loading per Sum Assured"""
param1 = Product().apply(lambda prod: SpecLookup("LoadAcqSAParam1", prod))
param2 = Product().apply(lambda prod: SpecLookup("LoadAcqSAParam2", prod))
result = param1 + param2 * np.minimum(PolicyTerm() / 10, 1)
result.name = 'LoadAcqSA'
return result
[docs]def LoadMaintPrem():
"""Maintenance Loading per Gross Premium"""
def get_value(pol):
if SpecLookup("LoadMaintPremParam1", pol["Product"]) is not None:
return SpecLookup("LoadMaintPremParam1", pol["Product"])
elif SpecLookup("LoadMaintPremParam2", pol["Product"]) is not None:
param = SpecLookup("LoadMaintPremParam2", pol["Product"])
return (param + min(10, pol["PolicyTerm"])) / 100
else:
raise ValueError('LoadMaintPrem parameters not found')
result = PolicyData().apply(get_value, axis=1)
result.name = 'LoadMaintPrem'
return result
[docs]def LoadMaintSA():
"""Maintenance Loading per Sum Assured during Premium Payment"""
def get_value(pol):
result = SpecLookup.match("LoadMaintSA",
pol["Product"],
pol["PolicyType"],
pol["Gen"]).value
if result is not None:
return result
else:
raise ValueError('lookup failed')
result = PolicyData().apply(get_value, axis=1)
result.name = 'LoadMaintSA'
return result
[docs]def LoadMaintSA2():
"""Maintenance Loading per Sum Assured after Premium Payment"""
def get_value(pol):
result = SpecLookup.match("LoadMaintSA2",
pol["Product"],
pol["PolicyType"],
pol["Gen"]).value
if result is not None:
return result
else:
raise ValueError('lookup failed')
result = PolicyData().apply(get_value, axis=1)
result.name = 'LoadMaintSA2'
return result
[docs]def NetPremRate(basis):
"""Net Premium Rate"""
def get_value(pol):
prod = pol['Product']
gamma2 = pol['LoadMaintSA2']
x, n, m = pol['IssueAge'], pol['PolicyTerm'], pol['PolicyTerm']
comf = LifeTable[pol['Sex'], pol['IntRate_' + basis], pol['TableID_' + basis]]
if prod == 'TERM' or prod == 'WL':
return (comf.Axn(x, n) + gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / comf.AnnDuenx(x, n)
elif prod == 'ENDW':
return (comf.Axn(x, n) + gamma2 * comf.AnnDuenx(x, n-m, 1, m)) / comf.AnnDuenx(x, n)
else:
raise ValueError('invalid product')
result = PolicyDataExt1().apply(get_value, axis=1)
result.name = 'NetPremRate_' + basis
return result
[docs]def ReserveNLP_Rate(basis, t):
"""Net level premium reserve rate"""
def get_value(pol):
prod = pol['Product']
gamma2 = pol['LoadMaintSA2']
netp = pol['NetPremRate_' + basis]
x, n, m = pol['IssueAge'], pol['PolicyTerm'], pol['PolicyTerm']
lt = LifeTable[pol['Sex'], pol['IntRate_' + basis], pol['TableID_' + basis]]
if t <= m:
return lt.Axn(x+t, n-t) + (gamma2 * lt.AnnDuenx(x+t, n-m, 1, m-t)
- netp * lt.AnnDuenx(x+t, m-t))
elif t <=n:
return lt.Axn(x+t, n-t) + gamma2 * lt.AnnDuenx(x+t, n-m, 1, m-t)
else:
return 0
result = PolicyDataExt2().apply(get_value, axis=1)
result.name = 'ReserveNLP_Rate'
return result
[docs]def ReserveRate():
"""Valuation Reserve Rate per Sum Assured"""
return None
[docs]def SurrCharge(t):
"""Surrender Charge Rate per Sum Assured"""
m = PremTerm()
return InitSurrCharge() * np.maximum((np.minimum(m, 10) - t) / np.minimum(m, 10), 0)
[docs]def TableID(RateBasis):
"""Mortality Table ID"""
if RateBasis == 'PREM':
basis = "MortTablePrem"
elif RateBasis == 'VAL':
basis = "MortTableVal"
else:
raise ValueError('invalid RateBasis')
def get_value(pol):
result = SpecLookup.match(basis,
pol["Product"],
pol["PolicyType"],
pol["Gen"]).value
if result is not None:
return result
else:
raise ValueError('lookup failed')
result = PolicyData().apply(get_value, axis=1)
result.name = 'TableID_' + RateBasis
return result
[docs]def UernPremRate():
"""Unearned Premium Rate"""
return None
Product = lambda: PolicyData()['Product']
PolicyType = lambda: PolicyData()['PolicyType']
Gen = lambda: PolicyData()['Gen']
Channel = lambda: PolicyData()['Channel']
Sex = lambda: PolicyData()['Sex']
Duration = lambda: PolicyData()['Duration']
IssueAge = lambda: PolicyData()['IssueAge']
PremFreq = lambda: PolicyData()['PremFreq']
PolicyTerm = lambda: PolicyData()['PolicyTerm']
PolicyCount = lambda: PolicyData()['PolicyCount']
SumAssured = lambda: PolicyData()['SumAssured']
[docs]def LoadMaintPrem2():
"""Maintenance Loading per Gross Premium for Premium Waiver"""
result = pd.Series(0.002, index=PolicyData().index)
result[PremTerm() < 10] = 0.001
result[PremTerm() < 5] = 0.0005
result.name = 'LoadMaintPrem2'
return result
[docs]def PolicyDataExt1():
"""Extended Poicy Data"""
data = pd.concat([PolicyData(),
LoadAcqSA(),
LoadMaintPrem(),
LoadMaintPrem2(),
LoadMaintSA(),
LoadMaintSA2(),
IntRate('PREM'),
TableID('PREM')], axis=1)
return data
[docs]def PolicyDataExt2():
"""Extended Poicy Data"""
data = pd.concat([PolicyDataExt1(),
NetPremRate('PREM')], axis=1)
return data
[docs]def SpecLookup(spec, prod=None, polt=None, gen=None):
"""Look up product specs"""
return ProductSpec.get((spec, prod, polt, gen), None)
# ---------------------------------------------------------------------------
# References
LifeTable = ("Interface", ("...", "LifeTable"), "auto")
PremTerm = ("Interface", (".", "PolicyTerm"), "auto")
ProductSpec = ("Pickle", 3020542092744)
PolicyData = ("Pickle", 3020548337160)