The Projection Space#

The by-policy projection of the Medical_KR_S model.

The Space is parameterized by point_id, so Projection[1] is an ItemSpace projecting model point 1:

>>> Projection[1].result_cf()          # the worked example's anchor cell
>>> Projection.point_id = 8            # or switch the default

t counts policy months, 0-based: t = 0 is the first policy month and t = proj_len() - 1 the last, so the frame is range(proj_len()) and result_cf() has proj_len() rows — proj_len() is the number of projected months, the exclusive end of the frame, and not the last index. Month t is the interval from t to t + 1 months after the 계약일. The policy year y = policy_year(t) = t // 12 + 1 is a contractual 1-based label and keeps its own clock: it is derived from t, never indexed by it.

Age basis

The projection is on 만나이 (man nai, age last birthday), incremented at each policy anniversary. The contract is not: it prices and renews on 보험나이 (boheom nai, insurance age), the 만나이 at the 계약일 with a fraction under six months discarded and six months or more rounded up, and the 표준약관 works the arithmetic out in its own example. The model carries 만나이 because every calibration statistic available for this product — the NHIS coverage ratios by age band, the 국가데이터처 생명표 mortality decrement and the 금융감독원 premium series — is compiled on 만나이, and because a deterministic single-cell projection cannot represent the distribution of issue dates within the year that separates the two conventions. The two differ for half of all issue dates, so the difference is a half-year of age on average, and it is recorded here rather than silently absorbed.

Input data

Inputs are external files: plain CSVs living in the model folder’s parent directory, products/indemnity_medical/, read at run time rather than stored inside the model. The model folder therefore holds nothing but formulas — no _data/, no IOSpec, no embedded values — so a diff of the model shows logic changes only, and an input can be edited or swapped without rewriting the model. This follows annuallife.TradLife_A; contrast basiclife.BasicTerm_S, which keeps its inputs inside the model through modelx’s IOSpec machinery.

Each table has a filename Reference and a reader Cells, both on Data, reached here through the data Reference:

Reference

Cells

File

model_point_file

data.model_point_table()

model_point_table.csv

mort_table_file

data.mort_table()

mort_table.csv

lapse_table_file

data.lapse_table()

lapse_table.csv

utilisation_table_file

data.utilisation_table()

utilisation_table.csv

severity_table_file

data.severity_table()

severity_table.csv

claim_shape_file

data.claim_shape_table()

claim_shape_table.csv

oop_ceiling_file

data.oop_ceiling_table()

oop_ceiling_table.csv

Naming

Cells names follow lifelib’s basiclife.BasicTerm_S wherever that model has an analogue — pols_* for policy counts, plural nouns for cash flows, *_rate for annual rates with *_rate_mth for their monthly companions, *_pp for per-policy amounts, claims(t, kind) with an uppercase kind string, pols_if_at(t, timing) for the within-month in-force reads. Quantities that live on the policy year rather than the policy month take y and never t, because the two are different clocks and this product runs on both. The technical notes use compact symbols instead. The mapping is:

Notes symbol

Cells

Meaning

(model point row)

model_point()

The selected model point

x

issue_age()

가입나이, 만나이 at 계약일

age(t)

age(t)

Attained 만나이 in month t

y(t)

policy_year(t)

floor(t/12) + 1

(none)

sex()

M or F

proj_len

proj_len()

Number of projected months

P0

premium_mth_pp()

First-year office premium

s

np_share()

비급여 share of the premium

(switch)

np_rider()

비급여 특약 held

(switch)

three_np()

3대비급여형 held

L

annual_limit()

연간 보험가입금액 per 보장종목

Lv

visit_cap()

통원 1회당 한도

(decile)

oop_decile()

본인부담상한제 소득분위

clinic_share()

Clinic-tier share of 급여 통원

(switch)

nhi_covered()

Inside 국민건강보험 or not

k_trend

trend_mult()

Multiplier on the cost trend

k_util

util_mult()

Multiplier on the frequencies

(switch)

reld_on()

요율 상대도 in operation

(switch)

noclaim_on()

무사고 할인 in operation

(rate)

suspend_rate()

개인실손 중지 decrement

(table)

mort_rate(t)

Annual 만나이 mortality

q(t)

mort_rate_mth(t)

Monthly mortality in month t

(table)

lapse_rate(t)

Annual lapse rate, policy year

w(t)

lapse_rate_mth(t)

Monthly lapse in month t

(rate)

suspend_rate_mth(t)

Monthly 중지 decrement

d_ren(t)

renewal_decline(t)

Decline of the annual renewal

l(t)

pols_if(t)

In force at the start of t

l(t)(1-q), l(t+1)

pols_if_at(t, timing)

Within-month in-force reads

(none)

pols_death(t)

Deaths in month t

(none)

pols_lapse(t)

Lapses in month t

(none)

pols_suspend(t)

개인실손 중지 in month t

(none)

pols_renewal_decline(t)

Renewals declined in month t

(none)

pols_maturity(t)

Cover ending at the horizon

b(y)

util_band(y)

Five-year utilisation band

n_adm(y)

adm_rate(y)

Admissions a year per policy

n_ge(y)

visit_rate_ge(y)

급여 통원 visits a year

n_np(y)

visit_rate_np(y)

비급여 통원 visits a year

a_ph, a_in, a_mr

act_rate_physio/inject/mri(y)

3대비급여 acts a year

D(y)

los_days(y)

Mean days per admission

f_ge(y), f_np(y)

trend_ge(y), trend_np(y)

Cost trend from year 1

r_ge, r_np

retain_rate_ge/np()

자기부담률, 20% / 30% / 60%

(table)

sev_points(stream)

Cost-probability pairs

C_ge(y)

oop_incurred_ge(y)

급여 본인부담금 incurred

S(decile)

oop_ceiling()

본인부담상한액

tau(y)

oop_trunc(y)

본인부담상한제 truncation

(loss)

loss_incurred_pp(y)

보장대상 의료비 incurred

paid_in(y)

claims_ge_in_pp(y)

급여 입원, before the limit

paid_out(y)

claims_ge_out_pp(y)

급여 통원, before the limit

(limit)

ge_limit_factor(y)

급여 annual-limit factor

(sum)

claims_ge_pp(y)

급여 claim after the limit

paid_np_in(y)

claims_np_in_pp(y)

비급여 입원 incl 상급병실료

paid_np_out(y)

claims_np_out_pp(y)

비급여 통원 incl the carve-out

(limit)

np_limit_factor(y)

비급여 annual-limit factor

(sum)

claims_np_main_pp(y)

비급여 main after the limit

a_ph_eff, a_in_eff

acts_physio_eff/inject_eff(y)

Acts after the gates

paid_3(y)

claims_np_three_pp(y)

3대비급여 after its sub-limits

(sum)

claims_np_pp(y)

All rider claims

C(y)

claims_np_rated_pp(y)

Rated 비급여 claim, exemptions out

(table)

shape_mean(), shape_rel(k)

The claim-shape distribution

band(a)

band_of(a)

요율 상대도 band of an amount

w_b(y)

band_share(y, b)

Share of contracts in band b

r_b

band_relativity(b)

100 / 200 / 300 / 400%

Sum w_b r_b (b>=2)

reld_surcharge(y)

The surcharge pool

r_1 solved

reld_solved(y)

Revenue-neutral band-1 factor

r_1

reld_one(y)

After the discount cap

Sum w_b r_b

reld_avg(y)

Average relativity applied

(switch)

reld_active(y)

Whether the loop is running

nc(y)

noclaim_share(y)

Share earning the 10% discount

a

age_load

4.0% age loading at renewal

b_ge, b_np

basis_incr_ge/np()

Basis change, in the corridor

base_ge(y)

prem_ge_base(y)

급여 기준보험료 a month

base_np(y)

prem_np_base(y)

비급여 기준보험료 a month

gross(y)

prem_gross_mth(y)

Office premium a month

P l(t)

premiums(t)

Premium income

claims_*(t)

claims(t, kind)

Benefit outgo by kind

claims(t)

claims(t)

All benefit outgo

e(t)

expenses(t)

Maintenance expense

ec(t)

claim_expenses(t)

Claim handling, on its own line

comm(t)

commissions(t)

Commission outgo

net_cf(t)

net_cf(t)

Net cash flow, income positive

The reimbursement machinery, in the order it must be applied

Everything in this product is a rule for reducing an incurred cost to a payable amount, and the order matters. Getting it wrong is the classic implementation failure here.

1 — the 본인부담상한제 first, as an exclusion from covered loss. The NHIS refunds a member’s annual 본인일부부담금 above an income-graded ceiling, and the 표준약관 excludes anything so refundable from cover twice over. oop_trunc() is the deterministic representation: the year’s incurred 급여 본인부담금 is scaled to the ceiling when it would otherwise exceed it. Two refinements a careful reader should know are deliberate simplifications: the ceiling runs on the calendar year while every contractual limit runs on the policy year, and a proportional truncation of an expectation is not the same thing as truncating each realisation.

2 — the co-payment and the deductible, per event. Inpatient reimbursement is a flat percentage of the covered cost — 80% on the 급여 side, 70% on the 비급여 side, 40% on either where 국민건강보험 entitlement does not apply. Outpatient reimbursement is the cost less max(flat floor, 자기부담률 x cost), then capped per visit. The flat floor is ₩10,000 at the clinic tier and ₩20,000 at the hospital tier on the 급여 side and a flat ₩30,000 at every provider on the 비급여 side, which is why the 급여 side needs a provider mix and the 비급여 side does not. The shape matters more than the formula: at the clinic tier the deductible is ₩10,000 until the covered cost reaches ₩50,000 and 20% above it, so a ₩10,000 visit pays nothing, a ₩50,000 visit pays ₩40,000, and the ₩200,000 per-visit cap binds at a covered cost of ₩250,000. Applying the deductible to the mean cost instead of to the distribution removes the kink and overstates the claim.

3 — the ₩2,000,000 annual inpatient co-payment cap, on what remains. Where the 20% retained on inpatient 급여 treatment exceeds ₩2,000,000 in a policy year, the excess is reimbursed. It applies to inpatient treatment only; there is no annual cap on the outpatient deductible.

4 — the 3대비급여 sub-limits, which displace the main limit for their three classes. 도수·체외충격파·증식치료 share one 50-act counter and a ₩3,500,000 money limit; 주사료 has its own 50 acts and ₩2,500,000; MRI has ₩3,000,000 and no count limit. Both gates are hard and neither is pro-rated: the limit that binds first stops cover for the rest of the policy year and only the 계약해당일 restores it. Cover beyond the first ten physical-therapy acts is conditional on a documented clinical re-assessment every ten acts, which a projection can only represent as a continuation probability at the boundary. Non-covered injections of 항암제, 항생제 and 희귀의약품 leave the ₩2,500,000 sub-limit and are reimbursed inside the main 비급여 limit, so the sub-limit bites on the discretionary end of injection use and not on oncology.

5 — the annual aggregate, per 보장종목 and per policy year. 상해 and 질병 carry separate ₩50,000,000 limits on each of the two parts, so the whole-contract annual exposure is ₩100,000,000 in the ordinary reading. The split between the two 보장종목 is [std]; nothing published gives it.

A word on what a deterministic projection can and cannot say about the limits. E[min(X, L)] != min(E[X], L), so a projection that applies a limit to an expectation understates the limit’s bite by ignoring dispersion. On every shipped model point the ₩2,000,000 inpatient cap, the three 3대비급여 money limits, the two 50-act counters, the 100-visit cap and the ₩50,000,000 annual limits do not bind, because the expected annual claim of a single cell is two orders of magnitude below them — the supervisor’s own tail figure is that 0.005% of insureds took more than ₩50,000,000 in 2019. The machinery is implemented anyway, because it binds under any seriatim or stochastic run and because check_annual_limits() is what proves it is wired correctly. The one limit that does bind on a shipped model point is the 본인부담상한제 truncation, on model point 8, whose high-utilisation cell sits on the lowest 본인부담상한액 decile.

비급여 할인·할증 — the loop, and why it is a distribution and not a rate

This is the mechanism that makes the contract unlike anything else in this repository. The renewal premium of the 비급여 rider is a function of the individual policyholder’s own prior-year non-covered claim amount, through five bands: 1단계 (no claim) at a solved discount, 2단계 (up to ₩1,000,000) at 100%, and 3단계, 4단계 and 5단계 at 200%, 300% and 400% as the prior-year claim crosses ₩1,000,000, ₩1,500,000 and ₩3,000,000. A hard floor sits under the surcharge: below ₩1,000,000 of prior-year claims there is no surcharge at all, which is why 2단계 is exactly 100%. Claims arising from a 국민건강보험법 산정특례 condition, and all claims of an insured graded 장기요양 1등급 or 2등급, are struck out of the count — the severely ill are exempt from the experience rating, which is the only direct statutory cross-reference between this model and LTC_KR_S.

The band is memoryless. It depends on the claim experience of the previous year alone: a single bad year cannot compound into a permanently higher premium and a single clean year returns the policyholder to the discount band. There is therefore no no-claims ladder and no Markov chain to carry — the band distribution at renewal y is simply the distribution of the annual rated claim in year y - 1, which is what band_share() computes from claim_shape_table() rescaled to that year’s mean. The loop is nonetheless live, and visibly so: the thresholds are fixed money amounts while the claim level trends, so contracts migrate into the surcharge bands year by year without anything in the model changing.

The discount is solved, not set. The wording defines it as the solution to a revenue-neutrality constraint — the surcharge funds the discount, so that the rider collects the same net premium before and after the relativity is applied. Writing w_b for the share of rider net premium in band b, neutrality is Sum_b w_b r_b = 1, hence r_1 = (1 - Sum_{b>=2} w_b r_b) / w_1. On the commencement band distribution that gives r_1 = 0.698 / 0.729 = 0.9575, a 4.25% discount, against a published 잠정 figure of 5%. Solving rather than hard-coding is the deliberate choice, and check_relativity_neutral() is the identity that proves it. A [std] cap of 5% sits under the discount, from the two published values; once the claim level has trended far enough that the surcharge pool would fund more than 5%, the cap binds, the scheme stops being neutral and the average rider premium rises above the base. That is the loop reaching the aggregate, and it is a feature of the design rather than of this implementation.

Two things run alongside and are different animals. The 무사고 할인 takes 10% off the whole office premium — 급여 and 비급여 together — after two consecutive years with no 비급여 claim, where the relativity has a one-year lookback and touches only the rider; the two stack. And the relativity was deferred three years after launch, so it starts at the fourth policy year, which is why the first three renewals of the anchor cell are a plain attained-age re-rate.

1년 갱신 — the renewal recursion and its corridor

The policy term is one year and renews automatically. The premium re-rates on everything: the attained age, the basis, and — on the rider only — the experience relativity. The recursion is

base(y) = base(y-1) x (1 + a) x (1 + b(y)), a = 0.04, abs(b(y)) <= 0.25

and the order of operations is the thing a careless reading gets wrong. The 표준약관’s own illustration labels its basis increment 「전년도 기준보험료 x 25%」, but 3,640 is 25% of 14,560 = 14,000 x 1.04, not of 14,000: the corridor applies to the age-adjusted prior premium, and reproducing the illustration’s printed row 14,000 -> 18,200 -> 23,660 -> 30,758 -> 39,985 -> 51,980 requires it. Getting it wrong costs 4% of the corridor every year and compounds. The corridor binds per 위험구분단위, not on the portfolio average, which is why the two units are re-rated separately here and check_renewal_corridor() tests each of them.

b(y) is not an input. Each priced unit is re-rated at its own claim trend, clipped to the corridor: the 급여 unit at the growth of the statutory co-payment and the 비급여 unit at the growth of non-covered spend, which the public statistician measures at 1.0% and 8.1% respectively. That is a [std] re-rating rule and it is what keeps the two halves of the model internally consistent — the loss ratio of each unit is stable unless the corridor clips it, which is exactly what happens on model point 10.

Modules that are off in the base run

  • 개인실손 중지·재개, the suspension facility for a policyholder doubly covered by a 단체실손, which 감독규정 제7-63조제2항제7호 makes mandatory. Carried as a decrement with suspend_rate = 0 on every model point but 9. Resumption is not modelled: the contract that resumes is a different projection.

  • The 40% branch, where 국민건강보험 entitlement does not apply. nhi_covered = 0 on model point 10 raises the 자기부담률 to 60% on both parts and switches the 본인부담상한제 off, because a life outside the scheme is not refunded by it.

  • The experience relativity and the 무사고 할인, both switchable and both off on model points 5 and 9. With the relativity off the contract is a plain attained-age renewable, which is what 1세대 through 3세대 were.

What is not modelled, and is a limitation rather than an omission

The behavioural response to the experience rating. The supervisor’s own worked example has a policyholder cutting his claims by 93% in response to a surcharge — from ₩10,000,000 to ₩700,000 — and shows him saving ₩300,000 of premium and ₩2,700,000 of out-of-pocket cost in one year. The contract is designed to change the insured’s behaviour, and this model projects the loop on a fixed frequency basis: it models the premium’s response to claims and not the claims’ response to premium.

The frequency half of the claim distribution. claim_shape_table() trends its amounts and holds its zero-claim mass fixed, so the proportion of contracts with no rated claim is constant at 72.9% while the size of a claim grows with age and cost. In reality the frequency of claiming rises with age too, and the 1단계 share would fall.

Anything a measurement basis would add. No 책임준비금, no CSM, no risk adjustment, no K-ICS requirement, no 해약환급금준비금 and no policyholder tax. On a one-year indemnity contract the 잔여보장요소 is at most one year’s unearned premium and the 발생사고요소 is the material item, and the 해약환급금준비금 has nothing to bite on because there is no surrender value at all.

Sign convention

net_cf() is income positive — premiums less claims, less maintenance expense, less claim handling expense, less commission — which is the library-wide sign and the notes’ own, so unlike the whole life and payout annuity models there is no outgo-positive liability_cf companion to publish: one stream, one sign, one name.

Three absences that are product facts

There is no death benefit: on death from a non-covered cause the contract pays the 계약자적립액 and the 미경과보험료, and on a one-year pure protection contract the 계약자적립액 is nil, so mortality is a pure liability-releasing decrement and no claims_death exists. There is no surrender value: 「이 상품은 1년만기 순수보장성 상품으로 해약환급금이 발생하지 않습니다」, so there is no cv_pp, no claims_lapse, no 보험계약대출 and no 보험료 자동대출납입 to break a missed premium. And there is no waiting period on the general cover, which is unusual among Korean health products and is a direct consequence of the indemnity form: there is no lump sum to anti-select against.

Cells Descriptions#

model_point()[source]#

The selected model point as a Series.

sex()[source]#

The sex, M or F. A rating factor at every carrier.

The 손해보험협회 comparison tool is filtered by 성별 and 보험나이 and by nothing else, which is the disclosure’s own confirmation that the rate scale is an age x sex table [S7]. The scale itself is not public, so the sex effect reaches this model through the [std] utilisation factors rather than through a premium table.

issue_age()[source]#

x: the 가입나이 on a 만나이 basis, 0 to 65 [std].

Nothing was retrieved for 4세대 or 5세대: the 표준약관 sets no issue age, it is a 사업방법서 matter and the 사업방법서 is not published [REG-R2]. The only observed range is 0-49 on one direct-channel 2세대 product [S4]. The composite takes 0-65, a whole-of-working-life envelope that stays clear of the 노후실손 boundary at 75 [REG-R17 제7-63조제2항제6호다목]. This is the widest single [std] in the specification.

premium_mth_pp()[source]#

P0: the first-year monthly office premium per policy, KRW.

An input, not a computed quantity, and the one number in this model with a genuinely authoritative published anchor: the joint FSC/FSS launch release prints ₩11,982 as the 4세대 premium for a 40세 남자 on a 10-carrier 손해보험 average as at 2021-06, against 1세대 ₩40,749, 2세대 ₩24,738 and 3세대 ₩13,326 for the same insured [R1]. Age 40 male is also the 기준연령 요건 of 감독규정 제1-2조제2호, so the anchor cell is the cell Korean supervisory disclosure is quoted on [REG-R9].

No age x sex rate scale exists in public for any generation of this product, so the other model points’ premiums are [std], scaled off the anchor by the 4.0% age slope of age_load and floored at the child rate — a pure extrapolation of 4% a year down to age 0 gives a premium the market does not write.

Only the first year is an input. Every later year is the renewal recursion of prem_ge_base() and prem_np_base().

np_share()[source]#

s: the 비급여 특약 share of the first-year office premium [std].

Two published values, and they differ. The FSC/FSS FAQ states that when both parts are held the rider is 「전체 보험료의 60% 수준」 and works an example at 급여 ₩5,000 plus 비급여 ₩8,000 for a 45-year-old male, a 61.5% share [R2]. The 표준약관’s own renewal illustration implies 48.75%: solving its band-2 and band-5 rows at renewal +1, g + n = 18,200 and g + 4n = 44,818, gives n = 8,873 [S1]. The composite takes 60%, because [R2] is a statement about the market as sold while the 표준약관 figure is a stylised illustration at an unnamed age.

The share matters more than its size suggests, because the relativity multiplies only the rider: at 60% a band-5 policyholder pays 0.40 + 0.60 x 4.00 = 2.80x the base total premium, but only 2.46x at 48.75%.

np_rider()[source]#

Whether the 실손의료보험 특별약관 (비급여 실손의료비) is held [S1].

On in the base run. Switched off it removes 60% of the premium, the 100-visit cap, the three sub-limits and the whole experience-rating loop — the contract becomes a plain 급여-only attained-age renewable. Model point 5 is that election.

three_np()[source]#

Whether 3대비급여형 is held [S1 특별약관 제1조]; on in the base run.

It is a 보장종목 of the 특약 and not a separate contract, and its money and count limits displace the main ₩50,000,000 비급여 limit for the three classes. Where it is not held those treatments are not covered at all rather than falling back into the main limit. Model point 6 is that election.

annual_limit()[source]#

L: the 연간 보험가입금액 per 보장종목, KRW.

The 표준약관 sets a ceiling and leaves the level to the carrier: 「5천만원 이내에서 회사가 정한 금액 중 계약자가 선택한 금액」 [S1 제5조]. No 4세대 menu was retrieved; a 5세대 carrier menu is ₩50,000,000 / ₩30,000,000 / ₩10,000,000 [S3]. The composite takes the maximum [std], because every published premium comparison is quoted on the full limit and because a lower limit only truncates the severity distribution further. 상해 and 질병 carry separate limits, so the whole-contract annual exposure with both parts held is ₩100,000,000.

visit_cap()[source]#

Lv: the 통원 1회당 한도, KRW; ₩200,000 in the composite [S1 제5조제5항].

A ceiling in the wording, like the annual limit; ₩100,000 to ₩200,000 observed on a 5세대 carrier menu [S3]. It binds at a covered outpatient cost of ₩250,000 on the 급여 side, the same crossing point at both provider tiers.

oop_decile()[source]#

The insured’s NHI-contribution decile, 1 to 10, for the 본인부담상한제 [R10].

A model point attribute rather than an assumption because the ceiling is set by the insured’s own income, and the spread is nine-fold: on the 2026 scale a 1분위 insured is refunded everything above ₩900,000 a year and a 10분위 insured everything above ₩8,430,000. The 급여 claim distribution is truncated, and truncated differently by income decile — which is exactly why the 비급여 half, which has no such truncation, is 57.1% of claims against a 15.8% share of national spend.

clinic_share()[source]#

The share of 급여 통원 visits at the ₩10,000 clinic tier [std].

The 표준약관’s deductible table has two rows: ₩10,000 at 의료법 제3조제2항 institutions other than 종합병원, at 보건소·보건의료원·보건지소, at 보건진료소 and at their pharmacies; ₩20,000 at 전문요양기관, 상급종합병원, 종합병원 and their pharmacies [S1 제3조 <표1>]. 0.63 is the 2025 claim split by provider class — 의원 32.0%, 병원 21.8% and 요양병원 2.8% against 종합병원 17.6% and 상급종합 15.0% [R7] — normalised over the named classes. Provider mix is a first-order variable in this product in a way it is in nothing else in the library.

nhi_covered()[source]#

Whether the insured is inside 국민건강보험 or 의료급여 [S1 제3조제3항제1호].

Where the insured falls outside 국민건강보험법 제5조·제53조·제54조 or the 의료급여법 equivalents — most commonly a suspension of entitlement — reimbursement falls to 40% of the amount actually borne on both parts, still within the annual limit. This is a state, not an event: it persists while entitlement is suspended, which is why it is a model point attribute. It also switches the 본인부담상한제 off, since a life outside the scheme is not refunded by it. Model point 10 is that branch.

trend_mult()[source]#

A multiplier on the medical cost trend of both units [std]; 1.0 in the base.

It is the switch that exercises the ±25% renewal corridor, which no shipped trend reaches on its own: at 4.5 the 비급여 unit’s re-rate would be 36.45% and the corridor clips it to 25%, so the rider’s premium falls behind its own claim trend year after year. Model point 10 carries it, and its loss ratio rises from 0.533 to 0.591 over the ten projected years — which is less than it sounds, because the corridor and the 4% age loading compose to admit a re-rate of 1.25 x 1.04 = 1.30 a year. The corridor does not bind economically below a 30% claim trend, and that is a model finding rather than an artefact of this parameterisation.

util_mult()[source]#

A multiplier on every claim frequency [std]; 1.0 in the base.

The claim distribution of this product is extraordinarily concentrated — 65% of insureds claim nothing in a year and the top decile takes about 74% of all claims [R4] [R5] [R6] — so a single cell carrying the population mean frequency is not a policyholder anybody would recognise. Model point 8 carries 10.0, a cell inside that top decile sitting on the lowest 본인부담상한액 decile, and it is the only shipped point on which the public truncation binds — at 0.8018 in policy year 1 and 0.6024 by policy year 10.

reld_on()[source]#

Whether the 요율 상대도 (비급여 할인·할증) is in operation [S1 특별약관 제6조].

On in the base run, but only from reld_start_year: the clause was in the wording from launch and its application was deferred three years 「충분한 통계 확보 등을 위하여」, commencing 2024-07-01 [R3]. A 4세대 policy written in 2021 therefore had three renewals at flat relativity before the loop switched on. Off on model points 5 and 9, where the contract is a plain attained-age renewable.

noclaim_on()[source]#

Whether the 무사고 할인 is in operation [R1] [S3]; on in the base run.

10% off the whole office premium — 급여 and 비급여 together — after two consecutive years with no 비급여 claim, excluding 4대 중증질환 claims from the test. It has a two-year lookback where the relativity has one, it applies to the whole premium where the relativity applies only to the rider, and it stacks with the band-1 discount.

suspend_rate()[source]#

The annual 개인실손 중지 decrement [std]; 0 in the base run.

A policyholder covered by a 단체실손 may suspend the individual policy for the duration and resume it within one month of the group cover ending, and the facility is mandatory under 감독규정 제7-63조제2항제7호 [R16] [S3] [REG-R17]. It is carried here as a decrement and not as a state: the contract that resumes is a different projection, entering the product in force at resumption. Model point 9 carries 3% a year.

proj_len()[source]#

The number of projected policy months: the exclusive end of the frame, so the projection runs over t = 0 .. proj_len() - 1 and result_cf() has proj_len() rows.

Two five-year 보장내용 변경주기 — ten policy years — or the run to max_cover_age if that comes first, which on the shipped model points it does not. The horizon is stated rather than contractual, and the distinction is the whole point. 감독규정 제7-63조제2항제6호나목 requires the 보험기간 및 보장내용 변경주기 to be five years or less [REG-R17], and at the fifth 계약해당일 a 4세대 contract’s benefit terms are replaced by whatever the supervisor is then prescribing, at a premium the insurer sets for that product. No projection of this contract past the first 재가입 is a projection of this contract’s terms; the model assumes re-entry on unchanged terms, twice over, and says so.

The contract boundary this raises is genuinely contestable — a one-year term, an unrestricted right to re-rate, a supervisor-set cap on that re-rating, a five-year re-entry into a wording the insurer does not control and an obligation not to refuse re-entry on health grounds — and this model asserts no answer to it.

policy_year(t)[source]#

y(t): the policy year containing policy month t, 1-based.

The 연간 of this contract is 「계약일로부터 매1년 단위로 도래하는 계약해당일 전일까지의 기간」 [S1 제5조제2항] — a policy year measured from the contract date and not a calendar year. All four ₩50,000,000 limits, the ₩200,000 per-visit cap, the 100-visit count, the three 3대비급여 sub-limits and the ₩2,000,000 inpatient co-payment cap run on this clock and reset at each 계약해당일. The 본인부담상한제 does not, and neither do the insurer’s own statistics; both run on the calendar year, and this model does not attempt to reconcile the two clocks.

age(t)[source]#

The attained 만나이 in policy month t, incremented at the policy anniversary.

Not the 보험나이 the contract prices on — see the Space docstring. The anniversary increment is itself a [std] convention: a deterministic single-cell projection has no birthday to increment on.

pols_if_init()[source]#

The in-force count at the start of the projection: one policy.

Every cash flow in result_cf() is per policy issued, so the frame is a unit projection and scales linearly.

mort_rate(t)[source]#

The annual mortality rate at the attained 만나이 in policy month t.

The [std] Makeham construction of mort_table.csv, read at age(t) and held flat at the terminal age. On this contract death releases the liability: the main contract pays nothing on death beyond the 미경과보험료, so the direction of prudence is the reverse of every protection product in this library and an over-statement of mortality is anti-conservative.

mort_rate_mth(t)[source]#

The monthly mortality rate in policy month t, from the annual rate.

1 - (1 - q)^(1/12): a uniform force of mortality across the policy year [std], the library-wide monthly conversion.

lapse_rate(t)[source]#

The annual lapse rate in policy month t, from lapse_table.csv.

Keyed by policy year; policy years beyond the last row take that row. This is non-payment lapse only. A missed premium produces a 납입최고 of at least 14 days and the contract terminates the day after it ends [REG-R25 제26조], and there is nothing to break the fall: with no surrender value there is no 보험료 자동대출납입 to advance the premium, and 표준약관 제33조 excludes 「순수보장성보험 등」 from policy lending anyway.

lapse_rate_mth(t)[source]#

The monthly lapse rate in policy month t, from the annual rate.

1 - (1 - w)^(1/12), the library-wide conversion. The annual rate is lapse_rate(); the two must not be confused, which is why the library spells them apart.

suspend_rate_mth(t)[source]#

The monthly 개인실손 중지 decrement in policy month t.

1 - (1 - r)^(1/12) on the model point’s annual rate; identically zero on every model point but 9. Suspension is not a lapse — the policyholder retains the right to resume — but for this projection it ends the cash flows, and the contract that resumes is a different projection.

renewal_decline(t)[source]#

The proportion declining the annual renewal at the end of policy month t.

Non-zero only in the twelfth month of a policy year, when the contract comes up for renewal. The asymmetry is contractual: 「the policyholder may decline renewal; the insurer may not」, within the 보장내용 변경주기 and the age range, provided the prior premium was paid [S5] [S3]. So this is a policyholder option on a contract the insurer cannot exit, and its rate is [std] — no published 실손 renewal-decline series exists, only the 3.3% blended in-force decay [R7].

pols_if(t)[source]#

l(t): the in-force probability at the start of policy month t.

pols_if_init() at t = 0, then l(t+1) = l(t)(1 - q)(1 - w)(1 - susp)(1 - decline). This is the weight on every cash flow of the same result_cf() row. Zero outside 0 .. proj_len() - 1.

pols_if_at(t, timing)[source]#

The in-force probability at a point inside policy month t.

"BEF_DECR"

l(t), the start of the month, before any decrement; the same number as pols_if() and the weight on that month’s cash flows.

"BEF_LAPSE"

after deaths, before lapses.

"BEF_SUSPEND"

after lapses, before the 개인실손 중지 decrement.

"BEF_RENEWAL"

after suspension, before the renewal decline — which acts only in the twelfth month of each policy year.

"AFT_DECR"

l(t+1), the end-of-month state, and zero in the final projected month because the projection horizon ends there.

The processing order — mortality, then lapse, then suspension, then the renewal decline — is [std]; nothing published fixes it, and on rates of this size the ordering is worth less than a basis point a year.

pols_death(t)[source]#

Expected deaths at the end of policy month t.

A pure liability release. On death from a cause the policy does not cover the insurer pays the 계약자적립액 and the 미경과보험료 of 감독규정 제7-66조제5항 and the contract terminates [REG-R17 제7-63조제1항제1호] [REG-R25 제22조]; on a one-year pure protection contract the 계약자적립액 is nil to the precision this model works at, so the payment reduces to the return of unearned premium and there is no claims_death anywhere in this model. This is the only place in ``krlib`` where that provision has no financial content — in Cancer_KR_S and LTC_KR_S the same clause forces an account balance into a non-savings product.

pols_lapse(t)[source]#

Expected lapses at the end of policy month t, from the survivors of mortality.

Pays nothing: 「이 상품은 1년만기 순수보장성 상품으로 해약환급금이 발생하지 않습니다」 [S3], so there is no claims_lapse limb and no cv_pp cells. A policyholder who cancels mid-term recovers the 미경과보험료 under 상법 제649조, which is a return of premium and not a surrender value.

pols_suspend(t)[source]#

Expected 개인실손 중지 suspensions at the end of policy month t.

Zero on every model point but 9. See suspend_rate().

pols_renewal_decline(t)[source]#

Expected renewals declined at the end of policy month t.

Non-zero only in the twelfth month of each policy year. It is a separate decrement from pols_lapse() because it is a separate act: a lapse is a missed premium and a decline is the exercise of a contractual option at a contractual date, and on a one-year renewable contract the second is the one the product is exposed to.

pols_maturity(t)[source]#

Policies whose cover ends at the scheduled end of the projection.

The library-wide meaning: the count whose cover ends because the contract reaches its scheduled end, whether or not anything is paid for it. Nothing is paid — there is no maturity benefit on a 순수보장성 contract, so there is no claims(t, "MATURITY") limb — but the count is needed for the in-force roll-forward to close in the final month. What ends here is the stated horizon of proj_len(), which is the second 재가입 or the maximum cover age, whichever comes first; the contract itself continues into the then-current generation.

util_band(y)[source]#

The five-year utilisation band containing the attained age in policy year y.

The lower edge of the band, capped at the last band of the table. Utilisation is published by five-year band and not by single year, which is the granularity the NHIS coverage-ratio series comes at.

util_value(y, column)[source]#

A column of the utilisation table at the model point’s sex and the band for y.

The frequency columns carry the model point’s util_mult(); los_days does not, because a longer stay is not a more frequent one and the room-differential cap is applied against the stay length itself.

adm_rate(y)[source]#

n_adm(y): admissions a year per policy giving rise to a paid claim [std].

An admission drives both halves of the claim — the 급여 본인부담금 and the 비급여 cost of the same stay — which is why it is one frequency and not two. The level is solved from the anchor calibration; the age curve follows the NHIS coverage ratio by age band [R9].

los_days(y)[source]#

D(y): the mean length of stay per admission, days [std].

Carried because the 상급병실료 차액 cap is ₩100,000 per day averaged over the whole admission, the average being total non-covered room charge divided by total days [S1]. A single expensive night inside a long stay is therefore smoothed against the stay length rather than capped night by night — a materially more generous treatment than a nightly cap, and one a per-night implementation understates.

visit_rate_ge(y)[source]#

n_ge(y): 급여 통원 visits a year per policy giving rise to a paid claim [std].

4세대 merged 외래 and 처방조제 into one visit with one deductible; the 3세대 wording carried a separate ₩8,000 처방조제 deductible on top [S5] [S1]. This count is of merged visits, which is why a 3세대 frequency basis cannot be carried across without adjustment.

visit_rate_np(y)[source]#

n_np(y): 비급여 통원 visits a year per policy [std], before the count cap.

Excludes the three 3대비급여 classes, which are counted as acts and carry their own deductibles and limits.

act_rate_physio(y)[source]#

a_ph(y): acts a year of 도수치료, 체외충격파치료 and 증식치료 [std].

The three share one 50-act counter and one ₩3,500,000 money limit, so they are one frequency here. Two or more of them at one visit are each counted and each separately deducted [S1 특별약관 제3조(3)제4항제1호], which is the counting rule that makes the ₩30,000 per-act floor expensive for the insured. 근골격계 질환 claims, of which this is the bulk, were 15.8% of all 실손 claims in 2025 [R7].

act_rate_inject(y)[source]#

a_in(y): acts a year of non-covered injection [std].

Two or more injections at one visit or admission are one act with one deduction [S1 제3조(3)제4항제2호] — the opposite of the physical-therapy rule, and worth money to the insurer. 주사료 means 「주사치료시 사용된 행위, 약제 및 치료재료대」: the procedure, the drug and the consumables together. Non-covered injections were ₩2.81조, 18.5% of all claims, in 2024 [R8].

act_rate_mri(y)[source]#

a_mr(y): acts a year of non-covered MRI or MRA [std].

MRI at two or more sites, or the same site twice, are separate acts each carrying its own deduction [S1 제3조(3)제4항제3호] — worth money to the insured rather than to the insurer. There is no count limit on this class, only the ₩3,000,000 money limit.

trend_ge(y)[source]#

f_ge(y): the cumulative 급여 cost trend from policy year 1 to policy year y.

(1 + med_trend_ge x trend_mult)^(y-1). The 국민건강보험공단 진료비 실태조사 measures 2024 growth of the statutory co-payment at 1.0% against 4.3% for the scheme’s own outlay [R9] [REG-R41]; that is the anchor [std].

trend_np(y)[source]#

f_np(y): the cumulative 비급여 cost trend from policy year 1 to policy year y.

(1 + med_trend_np x trend_mult)^(y-1), anchored on the same survey’s 8.1% growth in non-covered spend in 2024 [R9] [REG-R41]. 비급여 is compounding at roughly twice the rate of the whole, and it is the half of the claim that has no public price: the 건강보험심사평가원 price survey found 도수치료 quoted between ₩5,000 and ₩600,000 across Seoul hospitals [R2]. That divergence is why the two priced units are re-rated separately.

retain_rate_ge()[source]#

r_ge: the 급여 자기부담률, 20% [S1] or 60% outside 국민건강보험.

The wording always expresses it as its complement — 「본인부담금의 80%에 해당하는 금액」 — and never as a co-payment percentage. Where 국민건강보험 entitlement does not apply, reimbursement falls to 40% of the amount actually borne [S1 제3조제3항제1호], which is a retention of 60% [std]: the wording states the reimbursement and this model states the retention, and the flat outpatient deductible floor still applies on top.

retain_rate_np()[source]#

r_np: the 비급여 자기부담률, 30% [S1 특별약관 제3조] or 60% outside 국민건강보험.

The 30% against the 급여 side’s 20% is the generation’s design statement: 「필수 치료인 급여에 대해서는 보장을 확대하되, 환자의 선택사항인 비급여에 대해서는 의료이용에 따라 보험료가 할인·할증되도록 하였습니다」 [R1].

sev_points(stream)[source]#

The discrete cost distribution of a severity stream, as (cost, probability) pairs.

Eight streams: ge_in, ge_out, np_in, np_room, np_out, physio, inject and mri. The distribution and not its mean is what the model needs, because the deductible is max(flat floor, percentage x cost) and the per-visit cap truncates the top: the payment is a kinked function of cost, and applying the rule to a mean removes both kinks.

sev_mean(stream)[source]#

The expected cost of one event in a severity stream, KRW, before any trend.

oop_incurred_ge(y)[source]#

C_ge(y): the 급여 본인부담금 incurred in policy year y, before any truncation.

The insured’s own share under 국민건강보험법 요양급여 or 의료급여법 의료급여, both 일부본인부담금 and 전액본인부담금 [S1 기본형 제3조] [REG-R53], across admissions and outpatient visits. This is the quantity the 본인부담상한제 caps.

oop_ceiling()[source]#

S: the 본인부담상한액 for the model point’s income decile, KRW [R10].

국민건강보험법 제44조제2항 creates the 본인부담상한제: the NHIS refunds the excess of a member’s annual 본인일부부담금 over an income-graded ceiling [REG-R53], operated as 사전급여 and 사후환급 over the calendar year. On the 2026 scale the ceiling runs from ₩900,000 at 1분위 to ₩8,430,000 at 10분위. Infinite where the insured is outside the scheme, since nothing is refunded to a life the scheme does not cover.

oop_trunc(y)[source]#

tau(y): the 본인부담상한제 truncation factor on the 급여 covered loss in year y.

The single most important interaction in the product, and the one a model built from the policy wording alone will miss. The 표준약관 excludes the refundable amount twice over — 제5조제3항 limits the reimbursement to what the insured actually bore net of any amount refundable ex ante or ex post, and 제4조제3항제1호 excludes outright 「국민건강보험공단으로부터 사전 또는 사후 환급이 가능한 금액(본인부담금 상한제)」 [S1]. So the 급여 half of the claim is bounded above, per insured per year, at roughly 0.80 x 본인부담상한액 — ₩720,000 for a 1분위 insured in 2026 and ₩6,744,000 for a 10분위 one, a nine-fold spread driven by income and nothing else.

min(1, ceiling / incurred) is a [std] deterministic representation: a proportional truncation of an expectation is not the same thing as truncating each realisation, and it understates the truncation’s bite for the same reason a limit applied to a mean understates a limit’s. It is applied first, as an exclusion from covered loss, and the ₩2,000,000 inpatient cap second on what remains — the order matters, because both reduce the insured’s retention on heavy 급여 use and a model that applies them in the wrong order double-counts the relief.

claims_ge_in_pp(y)[source]#

paid_in(y): 급여 입원 reimbursement per policy in year y, before the annual limit.

0.80 x 본인부담금 [S1 기본형 제3조], plus the top-up: where the 20% retained on inpatient treatment exceeds ₩2,000,000 in a policy year, the excess is reimbursed within the annual limit [S1 제5조제4항] [REG-R17 제7-63조제2항제2호], so that beyond ₩2,000,000 of retained inpatient co-payment the 자기부담률 on further inpatient 급여 cost is effectively nil for the rest of the year. The cap is not a 4세대 novelty — the identical ₩2,000,000 appears in a 2세대 carrier document [S4]. It applies to inpatient treatment only; there is no annual cap on the outpatient deductible.

paid_out_per_visit(stream, floor, retain, trend)[source]#

The expected reimbursement of one outpatient visit of a severity stream.

E[ min( max(0, c - max(floor, retain x c)), visit_cap ) ] over the stream’s own cost distribution. The deductible is a flat floor that becomes a percentage: at the 급여 clinic tier it is ₩10,000 until the covered cost reaches ₩50,000 and 20% thereafter, at the hospital tier ₩20,000 until ₩100,000, and on the 비급여 side a flat ₩30,000 until ₩100,000 and 30% thereafter. Above the crossing point the payment is a straight percentage of cost until the per-visit cap binds — at ₩250,000 of covered cost on the 급여 side, the same point at both tiers.

claims_ge_out_pp(y)[source]#

paid_out(y): 급여 통원 reimbursement per policy in year y, before the annual limit.

Per visit, the covered cost less max(d_tier, 20% x cost), then capped at the per-visit limit [S1 기본형 제3조 <표1>], blended over the two provider tiers by clinic_share(). Where two or more visits fall on one day for the same treatment purpose they count as one, and 「공제금액은 2회 이상의 중복방문 의료기관 중 가장 높은 공제금액을 적용합니다」 — the highest applicable deductible [S1 제3조제8항]; the visit counts here are already of merged visits [std].

ge_limit_factor(y)[source]#

The proportion of the raw 급여 claim that survives the annual limit in year y.

상해급여형 and 질병급여형 carry separate ₩50,000,000 limits [S1 제5조], so the raw claim is split by share_injury and each part capped at annual_limit(). The split is [std]: nothing published gives it, and it matters only where the limit binds, which on a deterministic expectation is nowhere.

claims_ge_pp(y)[source]#

The whole 급여 claim per policy in policy year y, after the annual limit.

claims_np_in_pp(y)[source]#

paid_np_in(y): 비급여 입원 reimbursement per policy in year y, before the limit.

Two limbs [S1 특별약관 제3조]. 0.70 x 비급여 의료비 excluding the room charge; and the 상급병실료 차액 at 50% of the non-covered room charge, capped at ₩100,000 per day averaged over the whole admission — the average being total non-covered room charge divided by total days, which is why los_days() is carried and why a per-night implementation of the cap understates the benefit.

visits_np_eff(y)[source]#

The 비급여 통원 visits actually covered in policy year y.

Capped at 100 visits a policy year [S1 특별약관] — the count cap the 급여 side does not have, and the only count limit outside the 3대비급여 classes.

claims_np_out_pp(y)[source]#

paid_np_out(y): 비급여 통원 reimbursement per policy in year y, before the limit.

Per visit, the covered cost less max(₩30,000, 30% x cost), then capped [S1 특별약관 <표1>]. The floor is flat at every provider, not tiered as on the 급여 side, which is why the wording does not repeat the 급여 side’s highest-applicable-deductible rule for multiple same-day visits.

The injection carve-out is added here. 비급여 injections of 항암제, 항생제 (항진균제 포함) and 희귀의약품, each defined by a 식품의약품안전처 classification instrument, leave the ₩2,500,000 injection sub-limit and are reimbursed inside the main ₩50,000,000 비급여 limit [S1 특별약관 제3조(3)제2항]. So the sub-limit bites on the discretionary end of injection use — the 영양제 and 비타민제 the same wording restricts by licensed indication — and not on oncology. Where the carve-out sits is a first-order calibration question and not a detail: non-covered injections were 18.5% of all claims in 2024 [R8].

np_limit_factor(y)[source]#

The proportion of the raw main 비급여 claim that survives the annual limit.

상해비급여형 and 질병비급여형 carry separate ₩50,000,000 limits [S1 특별약관 제5조], split by share_injury exactly as on the 급여 side. The 3대비급여 classes are outside this limit and carry their own.

claims_np_main_pp(y)[source]#

The main 비급여 claim per policy in policy year y, after the annual limit.

Zero when the 특별약관 is not held.

paid_per_act(stream, trend)[source]#

The expected reimbursement of one act of a 3대비급여 class.

E[ max(0, c - max(₩30,000, 30% x c)) ] [S1 특별약관 제3조(3) <표1>]. There is no per-visit cap on these classes: what caps them is the annual money limit and, on two of the three, the act counter.

acts_physio_eff(y)[source]#

a_ph_eff(y): physical-therapy acts actually covered in policy year y.

Two gates. The 50-act counter is shared by 도수치료, 체외충격파치료 and 증식치료 together [S1 특별약관 제3조(3) <표1>]. And cover beyond the first ten acts is conditional: 「각 치료횟수를 합산하여 최초 10회 보장하고, 이후 객관적이고 일반적으로 인정되는 검사결과 등을 토대로 증상의 개선, 병변호전 등이 확인된 경우에 한하여 10회 단위로 연간 50회까지 보상합니다」, on a named clinical test set — 관절가동 (ROM), 통증평가척도, 자세평가 및 근력검사(MMT) and 초음파 검사 — with the insurer bearing the whole cost of the assessment [S1] [R2]. This is the only place in ``krlib`` where a benefit is gated on a clinical review rather than on a definition, and a projection can only represent it as a continuation probability at each ten-act boundary: physio_cont_prob, [std], with no observed range because none is published.

acts_inject_eff(y)[source]#

a_in_eff(y): injection acts actually covered in policy year y, net of the carve-out.

Capped at the same 50 acts a policy year as the physical-therapy trio, but on its own counter [S1 특별약관 제3조(3) <표1>]. The share carved out to the main 비급여 limit — 항암제, 항생제 and 희귀의약품 — is removed here and added in claims_np_out_pp(), so no act is counted twice.

claims_physio_pp(y)[source]#

The 도수·체외충격파·증식치료 claim per policy in year y, after its sub-limit.

Capped at ₩3,500,000 a policy year [S1 특별약관 제3조(3) <표1>]. Both gates are hard and neither is pro-rated, and the wording works both cases: where ₩3,500,000 is exhausted after 30 treatments on 2022-10-31 cover is excluded for the following 151 days and resumes at the 계약해당일 2023-04-01; where 50 treatments are used but only ₩3,000,000 paid, cover is excluded for the following 182 days. The limit that binds first stops cover for the rest of the policy year and only the anniversary restores it — a censored counting process with an annual reset, not a rate.

claims_inject_pp(y)[source]#

The 주사료 claim per policy in year y, after its sub-limit.

Capped at ₩2,500,000 a policy year [S1 특별약관 제3조(3) <표1>], on the acts left after the 항암제·항생제·희귀의약품 carve-out.

claims_mri_pp(y)[source]#

The 자기공명영상진단 claim per policy in year y, after its sub-limit.

Capped at ₩3,000,000 a policy year and at no act count [S1 특별약관 제3조(3) <표1>] — the only one of the three classes without a counter.

claims_np_three_pp(y)[source]#

paid_3(y): the whole 3대비급여 claim per policy in policy year y.

These sit inside the 특별약관 but carry their own money and count limits instead of the ₩50,000,000 aggregate, which is why they are summed apart from claims_np_main_pp() and never pass through np_limit_factor().

claims_np_pp(y)[source]#

All rider claims per policy in policy year y: the main limb and the 3대비급여.

claims_ann_pp(y, kind=None)[source]#

The claim per policy in policy year y, by kind, after every limit.

"GE_IN", "GE_OUT", "NP_IN", "NP_OUT", "NP_THREE". The 급여 and main 비급여 limbs carry their own annual-limit factor so that the printed columns still add to the limited total; the 3대비급여 limb is already at its own sub-limits. With no kind the sum over all five.

loss_incurred_pp(y)[source]#

The 보장대상 의료비 incurred per policy in policy year y, before any reduction.

The supervisor’s own identity, and it is worth writing down because it is not the identity a fixed-sum health product obeys: covered_loss = 급여 본인부담금 + 비급여 의료비 [R7]. Both terms are set outside the contract — the first by the public fee schedule and the co-payment schedule made under 국민건강보험법 제44조제1항, the second by the provider, since 요양급여 covers everything except what the 보건복지부장관 designates 비급여대상 under 제41조제4항 and 비급여 is therefore a residual defined by exclusion from a list that moves [REG-R53].

The 급여 limb is net of the 본인부담상한제, which is an exclusion from covered loss and not a reduction of the benefit. This is the quantity the indemnity principle bounds the claim by: 「동일한 위험을 보장하는 2개 이상의 계약에 중복 가입 하더라도 실제 발생한 손해(비용)를 초과하여 보험금을 지급하지 않습니다」 [S1 제37조·제38조].

claims_np_rated_pp(y)[source]#

C(y): the 비급여 claim of policy year y that counts for the 요율 상대도.

Two exclusions from the count [S1 특별약관 제6조제3항] [REG-R54]: 비급여 claims arising from a 국민건강보험법 산정특례 condition — 암질환, 뇌혈관질환, 심장질환, 희귀난치성질환 등 — and all claims of an insured graded 장기요양 1등급 or 2등급 under 노인장기요양보험법. The severely ill are exempt from the experience rating, and that is a direct statutory cross-reference between this model and LTC_KR_S, the only such link in the library. reld_exempt_share is the [std] share struck out, anchored on the 15.0% of 2025 claims the supervisor attributes to 암 and 뇌·심혈관질환 [R7].

The window itself is 「보험료 갱신 전 12개월 이내 기간」 [S1 특별약관 제6조제3항], with an operational three-month offset because renewal notices go out about a month ahead — 「계약해당일이 속한 달의 3개월 전 말일부터 직전 1년간」 [R12] — which 5세대 writes into the standard text. On an annual grid the offset is invisible; on a model that tried to resolve it, the point would be that a claims-to-renewal lag of zero over-states the responsiveness of the loop.

shape_buckets()[source]#

The bucket ids of the annual rated 비급여 claim distribution.

shape_share(bucket)[source]#

The share of contracts in a bucket of the claim-shape distribution.

Bucket 0 is the no-claim mass, 72.9% of contracts assessed at commencement [R12], and it is what becomes 1단계.

shape_mean()[source]#

The mean annual rated 비급여 claim of the shape table as tabulated, KRW.

The table is tabulated at the anchor cell’s first-year level; the model reads it as a shape and rescales it, so this is the divisor that turns a tabulated amount into a multiple of the mean.

shape_rel(bucket)[source]#

The bucket’s annual rated 비급여 claim as a multiple of the distribution’s mean.

Dimensionless by construction, so the same shape serves every model point and every projection year: the level comes from claims_np_rated_pp() and only the dispersion comes from the table.

band_of(amount)[source]#

The 요율 상대도 band of an annual rated 비급여 claim [S1 특별약관 제6조제3항].

1단계 at ₩0 with no claim; 2단계 above zero and below ₩1,000,000; 3단계 to ₩1,500,000; 4단계 to ₩3,000,000; 5단계 at or above it. A hard floor sits under the surcharge — 「할증은 … 보험금 지급실적이 연간 100만원 이상인 계약에 한하여 적용」 [S1 특별약관 제6조제4항] — which is why 2단계 has a relativity of exactly 100%.

band_relativity(b)[source]#

r_b: the 요율 상대도 of band b, for b in 2..5 [S1 특별약관 제6조제3항].

100% / 200% / 300% / 400%. The 표준약관 states the factor as a 요율 상대도 and the press releases as a 할인·할증율 of − / +100% / +200% / +300% [R1] [R3]; they are the same numbers, and band 3 pays twice the base rate, band 4 three times and band 5 four times. Band 1’s factor is not a constant — it is solved, at reld_one().

band_share(y, b)[source]#

w_b(y): the share of contracts in band b at the renewal opening policy year y.

The band is memoryless: 「보험금 지급(사고) 이력이 1년마다 초기화됩니다」 [R2], so the band at renewal y depends on the claim experience of year y - 1 alone. There is no no-claims ladder and no chain to carry — the distribution is simply the claim-shape distribution rescaled to that year’s mean rated claim, read against the fixed money thresholds. In the first policy year there is no prior year, so every contract sits at 2단계, which is the 100% base.

Because the thresholds are fixed money amounts and the claim level trends, the mix migrates into the surcharge bands year by year with nothing in the model changing. That migration is the loop.

reld_surcharge(y)[source]#

The surcharge pool at renewal y: the sum of w_b r_b over bands 2 to 5.

This is the revenue the wording requires be distributed to the 1단계 contracts.

reld_solved(y)[source]#

r_1 solved from revenue neutrality at renewal y, before the discount cap.

The wording does not fix the discount; it fixes the constraint that determines it: 「매년 상대도 적용 전·후의 총 보험료 수준이 일치하도록 3~5단계의 할증대상자의 할증재원을 1단계(할인) 대상자들에게 분배할 경우 산출됨」 [S1]. The scheme is revenue-neutral within the rider, so Sum_b w_b r_b = 1 and r_1 = (1 - Sum_{b>=2} w_b r_b) / w_1.

On the commencement band distribution 72.9 / 25.3 / 0.8 / 0.7 / 0.3 [R12] that gives 0.302 of surcharge pool and r_1 = 0.698 / 0.729 = 0.9575, a 4.25% discount. The published values bracket it and one is outside: 「5% 내외」 at launch [R1], −5% 잠정 at commencement [R3], a 95% relativity in the wording’s own illustration [S1], and a carrier writing it as 「α%」 rather than a number [S3]. Solving rather than hard-coding is deliberate: it makes the scheme self-financing inside the model, which is what the wording requires, and it means a change to the band distribution propagates correctly instead of silently breaking neutrality. On the FSC’s alternative distribution 62.1 / 36.6 / 1.3 [R3] the same identity gives 0.9791, a discount of only 2.1% — the sensitivity is material, which is why the two distributions are recorded rather than averaged.

reld_one(y)[source]#

r_1: the band-1 relativity actually applied at renewal y, after the discount cap.

reld_disc_cap is a [std] floor under the discount at 5%, from the two published values [R1] [R3]. It does not bind at commencement, where the solved figure is 4.25%; it binds once the claim level has trended far enough that contracts have migrated into the surcharge bands and the pool would fund more than 5%. From that point the scheme stops being revenue-neutral and the average rider premium rises above the base — which is the loop reaching the aggregate, and a feature of the design rather than of this implementation.

reld_active(y)[source]#

Whether the 요율 상대도 is applied at the renewal opening policy year y.

Off unless the rider is held and the model point elects it, and off for the first reld_start_year − 1 policy years: the clause was in the 4세대 wording from launch but its application was deferred three years 「충분한 통계 확보 등을 위하여」 and commenced 2024-07-01 [R3] [R1], so a policy written in 2021 had three renewals at flat relativity before the loop switched on. The same two-year deferral is being repeated on 5세대, whose differential starts 2028-05-06 [S3].

reld_avg(y)[source]#

The average 요율 상대도 applied to the rider’s premium in policy year y.

w_1 r_1 + Sum_{b>=2} w_b r_b. Exactly 1 while the discount cap is slack, because that is what revenue neutrality means; above 1 once the cap binds. The relativity applies to the 순보험료 — 「순보험료(특별약관의 순보험료 총액을 대상으로 합니다)」 [S1] — and this model applies it to the rider’s office premium and re-grosses at the same expense ratio, which is arithmetically identical [std] unless the rider’s expense loading contains a fixed per-policy amount, which no retrieved document states either way.

noclaim_share(y)[source]#

nc(y): the share of contracts earning the 무사고 할인 in policy year y.

「직전 2년간 비급여 보험금(4대 중증질환 치료를 위한 보험금은 제외) 미수령시 차기 1년간 보험료(급여(주계약) + 비급여(특약))의 10%를 할인」 [R1]. A two-year lookback where the relativity has one, applied to the whole office premium where the relativity touches only the rider, and it stacks with the band-1 discount: the launch release prints a three-year timeline in which years 1 and 2 give only the rider discount and year 3 adds the 10%.

The two years are treated as independent [std] — nothing published gives the persistence of claiming from one year to the next — so the share is the product of the two years’ no-claim shares.

basis_incr_ge(y)[source]#

b_ge(y): the 급여 unit’s basis change at the renewal opening policy year y.

Not an input. Each priced unit is re-rated at its own claim trend, clipped to the ±25% corridor: 「갱신계약의 보험료는 매년 최대 25% 범위(나이의 증가로 인한 보험료 증감분은 제외) 내에서 인상 또는 인하될 수 있습니다」 [S1 제30조제2항], now in the regulation itself at 감독규정 제7-63조제2항제3호 [REG-R17]. The corridor binds per 위험구분단위, not on the portfolio average, which is why the two units are clipped separately. The rule is [std]; it is what keeps each unit’s loss ratio stable unless the corridor clips it.

basis_incr_np(y)[source]#

b_np(y): the 비급여 unit’s basis change at the renewal opening policy year y.

The same rule on the 비급여 trend. At trend_mult() of 4.5 the unclipped re-rate would be 36.45% and the corridor holds it to 25%, so the rider’s premium falls behind its own claim trend year after year — which is model point 10, and is the shape of the problem the supervisor’s corridor creates for the insurer.

prem_ge_base(y)[source]#

base_ge(y): the 급여 주계약 기준보험료 a month in policy year y, KRW.

base(y) = base(y-1) x (1 + a) x (1 + b_ge(y)). The order of operations is the thing to get right: the 표준약관’s illustration labels its basis increment 「전년도 기준보험료 x 25%」, but 3,640 is 25% of 14,000 x 1.04 and not of 14,000, so the corridor applies to the age-adjusted prior premium [S1 제30조]. Reproducing the printed row 14,000 → 18,200 → 23,660 → 30,758 → 39,985 → 51,980 requires it, and getting it wrong costs 4% of the corridor every year and compounds.

prem_np_base(y)[source]#

base_np(y): the 비급여 특약 기준보험료 a month in policy year y, KRW.

The same recursion on the rider’s own trend and its own corridor. Zero where the 특별약관 is not held. This is the base premium, before the 요율 상대도: the corridor applies to the pre-relativity premium — 「요율 상대도 적용 전 보험료」 [S1 특별약관 제6조제2항] [REG-R17 제7-63조제2항제3의2호] — so a band-5 policyholder can face 1.25 x 4.00 = 5.00x the previous year’s base rider rate in a single step. That is the sharpest number in the product.

prem_gross_mth(y)[source]#

gross(y): the office premium a month in policy year y, KRW.

[ base_ge(y) + base_np(y) x reld_avg(y) ] x (1 - 0.10 x nc(y)). Three things compose here and each is on a different clock: the annual attained-age re-rate inside the corridor, the one-year-lookback experience relativity on the rider alone, and the two-year-lookback 무사고 할인 on the whole premium.

The 표준약관 extends its own illustration across the five bands and every row is reproduced by this formula at s = 0.4875: at renewal +1, 18,200 x (0.5125 + 0.4875 x 2) = 27,073; at +2, 23,660 x (0.5125 + 0.4875 x 4) = 58,263 [S1]. That reproduction is the check that the composition is the right one, and it is where the 48.75% rider share implied by the wording comes from.

premiums(t)[source]#

Premium income at the start of policy month t, an inflow.

gross(y(t)) x l(t). 월납 (monthly) and level within the policy year, which is the only mode retrieved and the mode the whole published FSS premium series is quoted on [R7] [R8] [S3] [S4]; no 연납 or 일시납 variant was found and none is modelled. Premiums cease on termination — the premium-paying period is coterminous with the one-year 보험기간 and renews with it — and there is no 납입면제: no premium-waiver clause appears in either retrieved 실손 wording, which is [unverified] as an absence rather than proved.

claims(t, kind=None)[source]#

Benefit outgo at the end of policy month t, by kind.

"GE_IN" 급여 입원, "GE_OUT" 급여 통원, "NP_IN" 비급여 입원 including the 상급병실료 차액, "NP_OUT" 비급여 통원 including the injection carve-out, and "NP_THREE" the three sub-limited 3대비급여 classes. With no kind, the sum.

The annual claim of the policy year is spread evenly across its twelve months [std]. Nothing published gives a within-year seasonality for this product, and the contract’s own machinery — every limit, every counter, the co-payment cap and the relativity window — runs on the policy year, so the month is a presentation grid for an annual quantity rather than a unit of account.

expenses(t)[source]#

Maintenance expense at the end of policy month t.

expense_maint_rate x premiums(t). The only expense datum retrieved for this product is the aggregate: 손해조사비 plus 사업비 of about ₩2.9조 on ₩18.0조 of 2025 premium — 16.1% — which reconciles with the FSS’s stated break-even loss ratio of about 85% [R7]. No 상품요약서 with a 사업비 disclosure was obtained for any generation, so the split into 6% acquisition and renewal commission, 7% maintenance and 3% claim handling is [std], and check_expense_split() ties the three back to the published total.

There is no month-0 acquisition strain here, and that is a product fact rather than an omission: on a one-year renewable contract renewed on a rolling basis the acquisition/renewal distinction has no content after year one, and what a sister library would call acquisition expense is carried in commissions() as a level rate on every premium.

claim_expenses(t)[source]#

Claim handling expense at the end of policy month t, on its own line.

expense_claim_rate x claims(t). It stands beside expenses() rather than inside it because 손해조사비 is the component the FSS itself separates out [R7], and because the experience-rating machinery makes claim frequency a driver of expense as well as of benefit. Nothing published splits the aggregate: the 3% is a [std] decomposition of a figure the FSS quotes as a percentage of premium, and the two bases coincide at a loss ratio of 100% — about where this line has actually run, 101.0% in 2025 [R7]. Making it proportional to claims is the [std] choice because that is what drives it.

commissions(t)[source]#

Commission outgo at the end of policy month t.

comm_rate x premiums(t), level. 감독규정 제4-32조제5항 caps first-year commission on a 보장성보험 at the first year’s premium, which is nowhere near binding at this level [REG-R22]. This limb carries the acquisition element of the 16% aggregate; see expenses() for why there is no separate first-year strain.

net_cf(t)[source]#

net_cf(t): the net cash flow of policy month t, income positive.

Premiums less every benefit limb, less maintenance expense, less claim handling expense, less commission. The library-wide sign and the notes’ own, so there is no outgo-positive liability_cf companion to publish.

The shape to expect is not a sister library’s. There is no new-business strain: a one-year renewable contract with no reserve accumulation and a level expense rate starts positive and stays positive while the premium keeps pace with the claim. What moves the margin is the interaction of three things on different clocks — the claim trend, which the two priced units re-rate against inside a corridor that can clip it; the age curve of utilisation, which the stylised 4% age loading does not track; and the experience relativity, which is neutral until the discount cap binds and then adds to the premium. Year-1 claims on the anchor cell are 82.8% of year-1 premium, which reproduces the published 4세대 2022 combined loss ratio exactly.

check_pols_roll_fwd_resid(t)[source]#

The in-force roll-forward residual in policy month t; zero everywhere.

pols_if(t) - pols_if(t+1) - deaths - lapses - suspensions - renewal declines - maturities. Five decrements, and the middle two are what make this product’s roll-forward different from a term assurance’s: the 개인실손 중지 facility is a supervisory requirement rather than a product feature, and the annual renewal is an option the policyholder holds and the insurer does not. Without them the roll-forward would appear to lose lives with no cause.

check_pols_roll_fwd()[source]#

True when the in-force roll-forward closes in every projected month.

The library-wide form of a roll-forward check: no argument, one bool over all t, so one test can call it across every model. check_pols_roll_fwd_resid() gives the signed residual of the month that failed.

check_net_cf_resid(t)[source]#

The cash flow statement residual in policy month t; zero everywhere.

net_cf() against premiums - claims - expenses - claim_expenses - commissions, with claims() summing over its own kinds exactly as result_cf() prints them. A benefit limb present in the total but missing from the printed statement, or counted twice, shows up here; so does a claim expense folded back into expenses() instead of standing on its own line.

check_net_cf()[source]#

True when the printed cash flow statement adds to net_cf in every month.

check_claim_shape_resid()[source]#

The claim-shape distribution’s two normalisation residuals; zero.

The shares must sum to 1 and the relative amounts must average to 1 under those shares, or the band mix is a distribution over something other than the contracts in force and reld_solved() divides by the wrong denominator. A negative share would pass both sums and is caught separately.

check_claim_shape()[source]#

True when the claim-shape distribution is a proper distribution with mean 1.

check_band_shares_resid(y)[source]#

The 요율 상대도 band-share residual at renewal y; zero everywhere.

The five band shares partition the in-force contracts, so they sum to 1 and none is negative. A bucket that fell through band_of() — an amount that matched no band — would show here as a shortfall.

check_band_shares()[source]#

True when the band shares partition the contracts in every policy year.

check_relativity_neutral_resid(y)[source]#

The revenue-neutrality residual of the 요율 상대도 at renewal y; zero everywhere.

The wording requires 「매년 상대도 적용 전·후의 총 보험료 수준이 일치하도록」 [S1] — the rider collects the same net premium before and after the relativity — so while the discount cap is slack Sum_b w_b r_b must be exactly 1. Once the cap binds the scheme is more than neutral and the sum rises above 1; what must never happen is a scheme that funds a discount it has not collected, so the residual there is the shortfall below 1 rather than the excess above it. Where the relativity is not in operation the average must be exactly 1, which is the third case.

check_relativity_neutral()[source]#

True when the experience relativity is self-financing in every policy year.

check_renewal_corridor_resid(y)[source]#

The ±25% renewal corridor residual at renewal y; zero everywhere.

Measured per 위험구분단위 — separately on the 급여 unit and the 비급여 unit, because that is how 감독규정 제7-63조제2항제3호 states it [REG-R17] — and measured against the age-adjusted prior premium, which is the order of operations the 표준약관’s own illustration obeys [S1 제30조]. A residual here means either that the clip is missing or that the age loading has been applied on the wrong side of it.

check_renewal_corridor()[source]#

True when neither priced unit moves more than 25% in a year, age effect excluded.

check_annual_limits_resid(y)[source]#

The contractual-limit residual in policy year y; zero everywhere.

Every money and count limit the contract carries, tested at once: the two ₩50,000,000 annual aggregates across their 상해/질병 split, the three 3대비급여 money sub-limits, the 100-visit 비급여 통원 cap, the two 50-act counters and the per-visit cap on both outpatient limbs. On every shipped model point the residual is zero because none of these binds on a deterministic expectation — see the Space docstring on E[min(X, L)] != min(E[X], L) — so what this check proves is that the machinery is wired, not that it is exercised. Do not delete a limit because it reads slack.

check_annual_limits()[source]#

True when no contractual money or count limit is exceeded in any policy year.

check_indemnity_resid(y)[source]#

The indemnity residual in policy year y; zero everywhere.

The defining constraint of this product and of no other in the repository: the contract reimburses an incurred cost, so the claim can never exceed the 보장대상 의료비 that produced it — 「실제 발생한 손해(비용)를 초과하여 보험금을 지급하지 않습니다」 [S1 제37조·제38조] [S3]. A co-payment applied as a multiplier instead of a retention, a deductible subtracted twice, or a per-visit cap applied to the wrong side of the deduction would all show here.

check_indemnity()[source]#

True when the claim never exceeds the incurred covered loss in any policy year.

check_oop_ceiling_resid(y)[source]#

The 본인부담상한제 residual in policy year y; zero everywhere.

The 급여 covered loss after truncation may not exceed the insured’s own 본인부담상한액, because anything above it is refunded by the NHIS and is excluded from cover outright [S1 제4조제3항제1호] [R10] [REG-R53]. On model point 8 the truncation binds and this is the check that says the exclusion was applied to the covered loss rather than to the benefit.

check_oop_ceiling()[source]#

True when the truncated 급여 covered loss stays inside the public annual ceiling.

check_expense_split_resid()[source]#

The expense-split residual; zero.

The 6% commission, 7% maintenance and 3% claim handling are a [std] split of one published aggregate — 손해조사비 plus 사업비 of about 16.1% of premium in 2025 [R7] — and the split must reconcile to it, or the model’s margin is not the market’s.

check_expense_split()[source]#

True when the three expense components reconcile to the published aggregate.

result_cf()[source]#

Result table of cash flows, indexed by policy month t.

pols_if is the start-of-month in-force probability, which is the weight applied to every cash flow on the same row. net_cf carries the library’s income-positive sign. expenses is maintenance only and the claim handling expense stands beside it in its own claim_expenses column, which is what the two names mean library-wide. The five claims_* columns are the split the contract itself makes — the two 급여 limbs of the 주계약, the two main 비급여 limbs of the 특약 and the 3대비급여 classes with their own sub-limits — and they sum to the whole benefit outgo; there is deliberately no claims subtotal column beside them.

result_pols()[source]#

Result table of in-force movements and decrement rates, indexed by policy month t.

result_prem()[source]#

Result table of the renewal and experience-rating ledger, indexed by policy year.

One row a policy year, because that is the clock the whole mechanism runs on. The five band shares, the surcharge pool, the solved and the applied band-1 relativity, the average relativity, the 무사고 share and the two base premiums are all here, so that the loop can be read off a single frame rather than reconstructed from the cash flow statement.