The Projection Space#

The by-contract projection of the VA_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 = 4            # or switch the default

t counts projection months, 0-based: t = 0 is the month containing the 계약일 and the first 기본보험료, and t = proj_len() - 1 the last month before attained age omega_age. proj_len() is the number of projected months, so the frame is range(proj_len()) and the policy year containing month t is t // 12 + 1.

Two dates cut the projection in two. pay_months() ends the premium-paying period, and the monthly deduction steps up there rather than down, because the 계약관리비용 for the period after 납입완료 was collected inside the premium and is now drawn back out of the fund. t_ann() is the 연금개시나이 계약해당일: the 특별계정 exists for t < t_ann() and is empty afterwards, the whole 계약자적립액 having moved to the 일반계정 [S6].

The age basis

Every age in this model is 보험나이 (boheom nai, insurance age): 만나이 with fractions of six months or more rounded up, incrementing on the policy anniversary and not on the birthday, under 표준약관 제21조 [REG-R25]. It is the contractual age, the index of every Korean rate card, and the basis the 경험생명표 is graduated on, so the model point ages and both mortality bases are on one basis and no shift is applied. Reading a 만나이 model point against this table would understate the rate by about half a year of ageing on every row. The one place Korean practice uses 만나이 instead is the 가입나이 envelope, 만15세–70세 [S1] [S2], which is an issue rule rather than a projection quantity.

Input data

Inputs are external files: plain CSVs in the model folder’s parent directory, products/variable_annuity/, read at run time rather than stored inside the model. 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

fund_file

data.fund_table()

fund_table.csv

charge_file

data.charge_table()

charge_table.csv

risk_prem_file

data.risk_prem_table()

risk_prem_table.csv

return_scenario_file

data.return_scenario()

return_scenario.csv

crediting_file

data.crediting_table()

crediting_table.csv

Symbol map

The technical notes use compact actuarial symbols; the cells use lifelib names. For a reader holding the notes beside the model that mapping is the most useful thing in the file. Notes symbol to cells:

Notes symbol

Cells

Meaning

(row label)

model_point()

The selected model point row

t

(the index of result_cf)

Projection month, 0-based

x

age_at_entry()

가입나이, 보험나이

x + floor(t/12)

age(t)

Attained 보험나이 in month t

(none)

sex()

M or F

n_p

pay_term()

납입기간, in years

12 n_p

pay_months()

납입기간, in months

y

annuity_age()

연금개시나이, 보험나이

T

t_ann()

Month of the 연금개시 계약해당일

m

defer_years()

연금개시 전 보험기간, in years

t = 0..N-1

proj_len()

Number of projected months, N

omega

omega_age

Terminal age of the table, 120

(switch)

gmab_flag()

1 보증형, 0 미보증형

(none)

fund_set()

Allocation set, on the ladder

(none)

scenario_id()

Return path identifier

P

basic_prem_pp()

기본보험료, monthly

12 P

prem_ann_pp()

Annualized 기본보험료

12 P n_p

prem_total_pp()

보험료총액 over 납입기간

P(t)

premium_mth_pp(t)

기본보험료 payable in month t

A(t)

addl_prem_pp(t)

추가납입보험료 in month t

(sum)

prem_pp(t)

Total premium in month t

alpha

acq_charge_pp(t)

계약체결비용

beta_1

maint_charge_in_pp(t)

계약관리비용, 납입기간 이내

beta_2

maint_charge_after_pp(t)

계약관리비용, 납입기간 이후

gamma

other_charge_pp(t)

기타비용

(sum)

prem_charge_pp(t)

Deducted at premium payment

1 - l

prem_alloc_ratio(t)

특별계정 투입 ratio, 91.33%

P_sa(t)

prem_to_av_pp(t)

특별계정 투입보험료

r(x)

risk_prem_rate(x)

위험보험료 rate by 보험나이

R(t)

risk_prem_pp(t)

위험보험료

c_d

gmdb_charge_pp(t)

최저사망보험금 보증비용

c_a

gmab_charge_asset_pp(t)

GMAB 보증비용, asset part

c_p

gmab_charge_prem_pp(t)

GMAB 보증비용, premium part

(sum)

gmab_charge_pp(t)

최저연금적립금 보증비용

B(t)

gmab_prem_base_pp(t)

보험료총액, the c_p base

D(t)

mth_deduct_pp(t)

월공제액

f_j

fund_mgmt_fee(j)

특별계정 운용보수, fund j

w_j

fund_alloc(j)

Allocation to fund j

i_j

gross_return(j)

Gross asset return, fund j

F_j(t)

fund_pp(t, j)

Fund j balance, end of month

(within-month)

fund_pp_at(t, j, timing)

BEF_PREM / BEF_DEDUCT / …

AV(t)

av_pp(t)

계약자적립액, end of month

(within-month)

av_pp_at(t, timing)

The same, inside the month

I(t)

inv_income_pp(t)

Gross separate-account return

M(t)

mgmt_fee_pp(t)

특별계정 운용보수 taken

(none)

bond_weight(t)

채권형 share of the account

b_min

bond_floor()

Mandatory 채권형 minimum

(none)

derisk_amount_pp(t)

Pre-annuitisation de-risking

W(t)

wd_pp(t)

중도인출금

(cumulative)

wd_cum_pp(t)

중도인출 to date

K_d(t)

prem_paid_pp(t)

이미 납입한 보험료

DB(t)

db_pp(t)

사망보험금, 연금개시 전

(top-up)

gmdb_claim_pp(t)

GMDB guarantee claim

C

surr_chg_pp(t)

해약공제액

C_max

surr_chg_cap_pp()

표준해약공제액, 별표 14

CV(t)

cv_pp(t)

해약환급금

AV(T)

av_ann_pp()

계약자적립액 at 연금개시

K(T)

gmab_base_pp()

최저연금적립금 strike

(payoff)

gmab_claim_pp()

GMAB intrinsic value at T

(fund)

annuity_fund_pp()

연금재원

i_c

annuity_int_rate()

공시이율 at annuitisation

(ladder)

credit_rate(k)

Max[공시이율, 최저보증이율]

k p_y

ann_surv(k)

연금사망률 survivorship

a-due

annuity_factor()

종신연금 10년 보증기간부

Y

annuity_ann_pp()

연금 연액, gross

(charge)

annuity_charge_pp()

연금수령기간 중 계약관리비용

Y_net

annuity_net_pp()

연금 연액 actually paid

q(x)

mort_rate_at_age(x)

보험사망률 at 보험나이 x

q_a(x)

ann_mort_rate_at_age(x)

연금사망률 at 보험나이 x

q(t)

mort_rate(t)

Annual death rate in month t

(monthly)

mort_rate_mth(t)

Monthly death rate

w(t)

lapse_rate(t)

Annual 해지율

(monthly)

lapse_rate_mth(t)

Monthly 해지율

l(t)

pols_if(t)

In force, start of month t

(within-month)

pols_if_at(t, timing)

BEF_DECR / BEF_LAPSE / …

d(t)

pols_death(t)

Expected deaths

s(t)

pols_lapse(t)

Expected 해지

(horizon)

pols_maturity(t)

Survivors at omega_age

l(T)

pols_annuitised()

Count reaching 연금개시

(obligation)

pols_annuity_oblig(t)

Payments due in month t

(income)

premiums(t)

영업보험료 received

(outgo)

claims(t, kind)

Benefit outgo by kind

(released)

claims_from_av(t, kind)

The 특별계정 part of it

(outgo)

withdrawals(t)

중도인출금 paid

(outgo)

expenses(t)

Insurer’s own expenses

(outgo)

commissions(t)

모집수수료

(transfer)

prem_to_av(t)

일반계정 to 특별계정

(transfer)

av_charges(t)

특별계정 to 일반계정, monthly

(transfer)

surr_charges(t)

해약공제액 retained

(transfer)

av_transfer(t)

연금재원 moved at 연금개시

(memo)

gmdb_claims(t)

GMDB strain on the 일반계정

(memo)

gmab_claims(t)

GMAB strain at T

CF(t)

net_cf(t)

Net cash flow, income positive

CF_g(t)

net_cf_gen(t)

The 일반계정 ledger

CF_s(t)

net_cf_sep(t)

The 특별계정 ledger

The two deduction points, and why they are not one

Confusing them is the commonest way to get a Korean variable model wrong, and the conditions are explicit [S7 제2조]: 「월공제액이라 함은 해당월의 위험보험료, 계약관리비용(납입기간 종료 후 유지관련비용), 최저사망적립금 보증비용 및 … 보증비용의 합계액을 말합니다. … 다만, 계약체결비용, 계약관리비용(납입기간 중 유지관련비용), 계약관리비용(기타비용)은 보험료를 납입할 때 공제하며 …」.

So prem_charge_pp() is taken out of the premium in the 일반계정 and never enters the fund, while mth_deduct_pp() is taken out of the 계약자적립액 by cancelling units on the 월계약해당일, and mgmt_fee_pp() is different again — it is deducted inside the 기준가격 [S7 제43조제2호], which is why it is written as a factor on the growth rather than as a unit cancellation. The identity [R2] is:

특별계정 투입보험료 = 납입보험료 − (계약체결비용 + 납입 중 계약유지비용 + 기타비용)
                    = 순보험료 + 납입 후 계약유지비용

and the second line is the one that matters: the 계약관리비용 for the period after 납입완료 is collected during the premium period and carried inside the account value, then drawn back out month by month once premiums stop. On the anchor cell that is ₩273,990 of every ₩300,000 reaching the fund — 91.33%, inside the 91.3%–91.5% three carriers publish on this cell [S1] [S2] [S6] — and check_prem_alloc() asserts it.

The premium-based guarantee charge deserves its own paragraph

gmab_charge_prem_pp() is 0.30% a year of the 보험료총액 — 「이미 납입한 보험료(특약보험료 제외) 및 추후 납입할 기본보험료 합계」, the whole premium the policyholder has undertaken to pay, past and future — levied 「납입기간(최대 7년) 동안」 [S1]. On the anchor cell that is ₩9,000 a month against a first-year account value of ₩3,216,621: over 3% a year of the fund at outset, falling below 0.5% by year seven. A model that treats guarantee charges as basis points on the account value misstates the early-duration cash flow of this contract by an order of magnitude, which is why the asset and premium components are separate cells with separate bases.

The guarantees, and what one path says about them

gmdb_claim_pp() is max(0, 이미 납입한 보험료 계약자적립액) and is a real expected cash flow on this path, because the mortality decrement is a probability applied to a deterministic account value. gmab_claim_pp() is max(0, K(T) AV(T)) at the single date T, paid only on survival and persistency to it: the GMAB is void on surrender, on lapse, on death before annuitisation and on 조기연금개시 [S1] [S6] [S7 제50조제3항] [R1]. A model that treated it as a floor on the account at every duration would overstate its cost by the whole of the pre-annuitisation exit probability, which on a seven-year persistency below 30% [R1] is most of it.

On one path both figures are intrinsic values, and by Jensen’s inequality each is a lower bound on the expected cost. The base run’s account is above the strike at annuitisation, so it reports a GMAB cost of exactly zero while collecting the full guarantee charge; model point 4 runs the mandated −1.00% return, on which it is not. The gap between gmab_charges() collected and gmab_claims() incurred is a single-path residual and not a profit, and the statutory 보증준비금 — a CTE(70) over a thousand scenarios, or a standard factor, whichever is greater [REG-R10] [REG-R26] [R1] — is not computable from this run and is not published by it.

Which account, and how the model proves it

Every transfer in net_cf_gen() and net_cf_sep() is one of the movements 감독규정 제5-7조 permits between the two accounts [REG-R15]: premium receipt and benefit payment, transfer to the general account of the amounts needed for risk cover and for acquisition, maintenance and administration, management fees, and the 연금재원 moved at 연금개시. Each appears in both ledgers with opposite signs, so their sum is the whole-contract external cash flow net_cf() and nothing else. check_net_cf() asserts exactly that, and it is the identity this product’s cash flow statement turns on.

Model points

model_point_table.csv carries ten contracts. Point 1 is the anchor: 남자 보험나이 40, 기본보험료 ₩300,000 월납, 10년납, 연금개시나이 60, 보증형, 채권형 50% / 주식형 50% — the illustration point three independent carriers publish [S1] [S2] [S6], and the only cell at which the composite’s parameters can be checked against published surrender-value tables. Point 2 is the same cell female. Point 3 is the 미보증형, with the GMAB and its two charge components both removed [S4] [S5]. Points 4 and 5 are the anchor on the other two mandated illustration returns, −1.00% and 3.75% [R2], point 4 being the only shipped cell on which the GMAB finishes in the money. Point 6 is a 5년납 contract with a ten-year 연금개시 전 보험기간, which is the <12년 rung of the mandatory 채권형 ladder at 80% [S1], and whose surrender charge is cut by the 표준해약공제액 cap [REG-R20]. Point 7 sits exactly on the =12년 rung at 70%. Point 8 exercises 추가납입 at 100% of the basic premium, which grows the GMDB base and the GMAB strike without a loading [S1] and — because 보험료총액 is defined on premiums paid as well as payable — grows the premium-based guarantee charge with it. Point 9 exercises 중도인출, 10% of the 해약환급금 once a year from the eleventh 계약해당일 (t = 132, the start of the twelfth policy year), which re-bases both guarantees proportionally [S2] [S7 제51조제8항]: without that adjustment a policyholder could withdraw the fund and keep the strike [R1]. Point 10 is the issue envelope — 가입나이 70, 연금개시나이 80, 기본보험료 at the ₩1,000,000 per 구좌 maximum [S1] [S2] [S9] — on the min_guar crediting basis, so the 최저보증이율 ladder rather than the 공시이율 sets the annuity factor.

Cells Descriptions#

model_point()[source]#

The selected model point, a row of data.model_point_table().

policy_id()[source]#

The contract’s identifier, carried through to the printed output.

sex()[source]#

The insured’s sex, M or F: the rating factor of both mortality bases.

age_at_entry()[source]#

가입나이 (gaibnai, issue age) in 보험나이, at the 계약일.

The retrieved envelope is 만15세–70세 [S1] [S2]; the shipped points run 35 to 70.

basic_prem_pp()[source]#

기본보험료 (gibon boheomnyo), the level monthly basic premium per contract.

Every front-end charge on this product is a rate on this amount rather than on the premium actually paid, which is why 추가납입보험료 attracts no loading [S1]. The observed minimum runs ₩50,000 to ₩500,000 by carrier and the maximum is ₩1,000,000 per 구좌 [S1] [S2] [S9].

pay_term()[source]#

납입기간 (nabip gigan), the premium-paying term in whole years.

Retrieved terms are 5 / 7 / 10 / 12 / 15 / 20년납 [S1] [S2].

annuity_age()[source]#

연금개시나이 (yeongeum gaesi nai) in 보험나이, the annuitisation age.

The retrieved envelope is 45–80 [S1] [S5] [S6] [S9]. The 연금개시 전 보험기간 runs from the 계약일 to the day before the 연금개시나이 계약해당일 [S4] [S5] [S7].

gmab_flag()[source]#

1 on the 보증형 (GMAB elected), 0 on the 미보증형.

The 최저연금적립금보증 has been elective since April 2016 [R2], so the same chassis has to support both forms; the charge and the guarantee are switched together, because a 미보증형 pays neither [S4] [S5].

fund_set()[source]#

The 특별계정 allocation set, a key of data.fund_table().

The three sets sit on the three rungs of the mandatory 채권형 ladder — bond80_eq20 for a 연금개시 전 보험기간 under twelve years, bond70_eq30 at exactly twelve and bond50_eq50 above it [S1] [R1] — so a model point’s allocation sits on its constraint rather than slack of it.

scenario_id()[source]#

The deterministic return path, a key of data.return_scenario().

base is the 2026 평균공시이율 of 2.50% net of the blended 운용보수 [REG-R48]; low and high are the other two returns a Korean variable illustration must show, −1.00% and 3.75% [R2].

crediting_basis()[source]#

The payout-phase crediting basis, a key of data.crediting_table().

addl_prem_ratio()[source]#

추가납입보험료 as a multiple of the 기본보험료, paid monthly; 0 switches it off.

Capped cumulatively at 200% of the basic premium paid and payable, universally across the retrieved contracts [S1] [S2] [S4] [S5] [S6] [S7] [S10], and attracting no loading [S1].

wd_ratio()[source]#

중도인출 as a fraction of the 해약환급금, once a policy year; 0 switches it off.

wd_start_year()[source]#

The number of completed policy years after which the first 중도인출 is taken.

An elapsed count, not a 1-based policy-year label: the first withdrawal falls on the wd_start_year-th 계약해당일, t = 12 * wd_start_year(). With 11 on model point 9 that is t = 132, the start of the twelfth policy year. 0 with the module off.

The contract permits twelve withdrawals a policy year from one month after the 계약일 [S1]; the model takes at most one a year, on the 계약해당일 [std].

pols_if_init()[source]#

The number of contracts in force at t = 0: 1, the model point being one 구좌.

Korean variable annuities are written in 구좌 (gujwa), repeatable units of the basic premium, with the withdrawal residual floor and the maximum basic premium both expressed per 구좌 [S1] [S2] [S10]. One 구좌 keeps the model point one contract.

pay_months()[source]#

납입기간 in months, 12 * pay_term(), and the month the deduction steps up.

t_ann()[source]#

T, the projection month of the 연금개시나이 계약해당일.

(annuity_age() - age_at_entry()) * 12. The 특별계정 exists for t < t_ann(); at t_ann() the whole 계약자적립액, floored at the GMAB where one was bought, is transferred to the 일반계정 and run at the 공시이율 [S6].

defer_years()[source]#

연금개시 전 보험기간 in whole years — the band of the mandatory 채권형 ladder.

proj_len()[source]#

The number of projected months, N — the frame’s exclusive end.

The frame is range(proj_len()), so t runs 0 proj_len() - 1 and len(result_cf()) == proj_len(). The contract is a 종신연금형 and has no maturity, so the horizon is the terminal age of the shipped mortality table: the projection runs while age(t) < omega_age, which is (omega_age - age_at_entry()) * 12 months. The survivors at that horizon leave through pols_maturity() with no payment, so the in-force roll-forward closes and the truncation is visible rather than absorbed.

age(t)[source]#

Attained 보험나이 at the start of projection month t.

age_at_entry() + t // 12: 보험나이 increments on the policy anniversary, not on the birthday [REG-R25 제21조].

policy_year(t)[source]#

The policy year containing month t, 1-based: t // 12 + 1.

yrs_completed(t)[source]#

Completed whole policy years at the end of month t: (t + 1) // 12.

This is the t of the 해약공제 scale, whose published rows are labelled 경과 완성 연수 [S2].

fund_ids()[source]#

The 특별계정 fund identifiers of this contract’s allocation set, in order.

fund_alloc(j)[source]#

The share of the 특별계정 투입보험료 allocated to fund j, fixed at issue.

Selection at issue is up to three funds including a 채권형, in 5% steps [S1] [S5]. There is no rebalancing in the base run, so the realised mix drifts away from this allocation and the drift is itself a modelled quantity — which is what the automatic de-risking rules exist to control.

fund_mgmt_fee(j)[source]#

특별계정 운용보수 of fund j, per annum: 채권형 0.40%, 주식형 0.60% [S2].

Deducted daily, at rate/365, out of net assets before the 기준가격 is struck [S7 제43조제2호] — so it lives inside the unit price and not in the 월공제액. The model takes it monthly at rate/12 [std], the monthly grid having no daily step.

fund_is_bond(j)[source]#

True when fund j is the 채권형, the fund the mandatory floor is measured on.

bond_floor()[source]#

The mandatory minimum 채권형 weight for this contract’s 연금개시 전 보험기간.

The same ladder appears at two carriers eight years apart — 12년 미만 ≥80%, 12년 ≥70%, 12년 초과 ≥50% — and it binds both the premium allocation and the account mix, surviving every later 펀드변경 [S1] [R1].

gross_return(j)[source]#

The annual gross separate-account asset return of fund j, constant.

Gross of the 운용보수, which fund_mgmt_fee() then takes inside the 기준가격, so the management fee is a modelled cash flow rather than an assumption. Every return assumption on this product is [std]: the only realised Korean figures retrieved are the top of a cross-sectional distribution in a trade article [R10] and one live fund panel at 904.24원 against the statutory 1,000.00 opening price after fourteen years [S11], and no volatility, correlation or time series was retrieved at all.

fund_growth(j)[source]#

One month’s growth factor on fund j: gross return, then the 운용보수.

(1 + i_j) ** (1/12) * (1 - f_j / 12). The two are written as separate factors because they happen at different places — the return accrues on the assets, the fee is taken out of net assets before the 기준가격 is struck [S7 제43조제2호].

charge_rate(line)[source]#

One row of the 수수료 안내표: data.charge_table()’s value for line.

The lines are the ones a savings-type variable contract must publish — 계약체결비용, 계약관리비용, 위험보험료, 보증비용, 특별계정운용비용, 해약공제비용 and the 연금수령기간 중 비용 — plus the commission scale and the insurer’s own unit expenses [R2].

loading_rate()[source]#

부가보험료 as a fraction of the 기본보험료: 5.17% + 3.50% + 0.00% = 8.67%.

The complement, 91.33%, is the 특별계정 투입 ratio, and it is also the 부가보험료 that 별표 14 excludes in computing the 연납순보험료 for the 표준해약공제액 cap [REG-R20].

prem_ann_pp()[source]#

The annualized 기본보험료, 12 * basic_prem_pp().

prem_total_pp()[source]#

보험료총액 as contracted: the whole 기본보험료 payable over the 납입기간.

12 * pay_term() * basic_prem_pp() — ₩36,000,000 (3,600만원) on the anchor cell.

premium_mth_pp(t)[source]#

기본보험료 payable in month t, level and in advance.

addl_prem_pp(t)[source]#

추가납입보험료 paid in month t, subject to the cumulative 200% cap.

Permitted to 200% of the basic premium paid and payable, cumulative, with no loading [S1] — the single largest lever a policyholder has over this product’s cost, and the reason the front-loaded 계약체결비용 is levied on the 기본보험료 alone. Off unless the model point sets addl_prem_ratio().

prem_pp(t)[source]#

Total premium per contract in month t: 기본보험료 plus 추가납입보험료.

acq_charge_pp(t)[source]#

계약체결비용 (gyeyak chegyeol biyong), 5.17% of the 기본보험료 [S2].

Deducted from the premium in the 일반계정 before it reaches the fund, for ten years from the 계약일 and nil thereafter [S2]. A charge on a premium cannot outlive the premium, so the model levies it for the shorter of ten years and the 납입기간 [std]; the two coincide on [S2]’s own 10년납 contract. ₩15,510 a month on the anchor cell.

maint_charge_in_pp(t)[source]#

계약관리비용 inside the 납입기간, 3.50% of the 기본보험료 [S2].

Deducted from the premium, in the 일반계정. ₩10,500 a month on the anchor cell.

other_charge_pp(t)[source]#

기타비용, nil in the base run [std].

[R2]’s cash-flow identity names it and [S7 제2조] confirms it is deducted at premium payment, but no retrieved 상품요약서 quantifies it. Held at zero so the premium allocation reproduces the observed 91.33% exactly; the line is kept because the identity needs it.

prem_charge_pp(t)[source]#

The whole deduction taken at premium payment, in the 일반계정 [S7 제2조].

계약체결비용 + 납입 중 계약관리비용 + 기타비용. It never enters the 특별계정, which is the first of the product’s two deduction points and the one a model most easily conflates with the second.

prem_alloc_ratio(t)[source]#

특별계정 투입보험료 as a fraction of the 기본보험료 in month t.

91.33% while both front-end charges run, rising to 100% once the 계약체결비용 stops [R2]. Three carriers’ first-year illustrations put the realised ratio at 91.3%, 91.3% and 91.4% on the anchor cell [S1] [S2] [S6], and the textbook publishes 90.0% for a 10년납 as the flat-by-age industry figure [R2].

prem_to_av_pp(t)[source]#

특별계정 투입보험료: what actually reaches the fund and buys 좌 in month t.

납입보험료 (계약체결비용 + 납입 계약유지비용 + 기타비용) = 순보험료 + 납입 계약유지비용 [R2]. The second reading is the one that matters: the 계약관리비용 for the period after 납입완료 is collected here and carried inside the account value, then drawn back out by maint_charge_after_pp() once premiums stop. 추가납입 보험료 attracts no loading and enters in full [S1].

risk_prem_rate(x)[source]#

위험보험료 rate at attained 보험나이 x, a fraction of the 기본보험료.

A banded lookup on data.risk_prem_table(): the applicable row is the highest age_from at or below x. The published band is 0.004%–0.011% — ₩12 to ₩32 a month on the anchor cell [S2] [S4] — and it is that small because the risk premium buys only the 고도재해장해급여금 and, on the retrieved contracts, no basic death cover at all, which is why the textbook can call the natural premium immaterial and the premium allocation flat by age [R2]. The scale across the band is [std].

risk_prem_pp(t)[source]#

위험보험료 (wiheom boheomnyo) in month t, part of the 월공제액.

₩24 a month on the anchor cell at issue. It buys the 고도재해장해급여금 of ₩10,000,000 per 구좌 [S1] [S2], which this model charges for and never pays: no 장해 incidence rate on this contract’s basis was retrieved, the 참조순보험요율 display being a 장기손해보험 one that does not reach the life side [REG-R34] [REG-R61]. The omission is named in the model docstring rather than hidden.

maint_charge_after_pp(t)[source]#

계약관리비용 after 납입완료, 1.33% of the 기본보험료 [S2].

Taken from the 계약자적립액 on the 월계약해당일 [S7 제2조] — ₩3,990 a month on the anchor cell, where the document prints ₩4,000. This is the step the Korean expense stack is most often modelled wrong at: at 납입완료 the monthly deduction rises, with no premium arriving to offset it, and the cumulative separate-account contribution of [S2]’s own illustration falls from ₩32,877,360 at ten years to ₩32,393,520 at twenty.

gmdb_charge_pp(t)[source]#

최저사망보험금 보증비용, 연 0.07% of the 계약자적립액, monthly [S1].

Deducted from the 특별계정 in the 월공제액 and held in the 일반계정 as 최저사망보험금 보증준비금, out of which the shortfall on a death below premiums paid is met [R2]. Compulsory: 감독규정 제7-60조제7호 requires 변액보험 to set a 최저사망보험금 [REG-R16], and all 36 products in the 2017 industry census carried one [R1].

gmab_prem_base_pp(t)[source]#

보험료총액, the base of the premium component of the GMAB charge.

「이미 납입한 보험료(특약보험료 제외) 및 추후 납입할 기본보험료 합계」 [S1] — the whole premium the policyholder has undertaken to pay, past and future. With no 추가납입 it is the constant prem_total_pp(), ₩36,000,000 on the anchor cell; with 추가납입 it grows, because a paid additional premium is 이미 납입한 보험료. Note the asymmetry that creates: the charge base and the guarantee strike both grow, but the strike keeps growing after the charge has stopped at seven years.

gmab_charge_asset_pp(t)[source]#

최저연금적립금 보증비용, asset component: 연 0.25% of the 계약자적립액 [S1].

gmab_charge_prem_pp(t)[source]#

최저연금적립금 보증비용, premium component: 연 0.30% of 보험료총액 [S1].

Levied 「납입기간(최대 7년) 동안」 — for the shorter of the 납입기간 and seven years — and not on the fund. ₩9,000 a month on the anchor cell, which is over 3% a year of the first-year account value and below 0.5% by year seven.

gmab_charge_pp(t)[source]#

최저연금적립금 보증비용 in month t: the asset and premium components together.

mth_deduct_pp(t)[source]#

월공제액 (wolgongjeaek) — the second deduction point, taken from the fund.

위험보험료 + 납입 후 계약관리비용 + 최저사망보험금 보증비용 + 최저연금적립금 보증비용, cancelled out of the 계약자적립액 on the 월계약해당일 [S7 제2조]. Capped at the available account value [std]: the contract meets an unpayable deduction by ending the premium holiday or lapsing [S1], neither of which this model represents.

wd_pp(t)[source]#

중도인출금 (jungdo inchul) taken in month t, per contract.

Off unless the model point sets wd_ratio(). Taken once a policy year on the 계약해당일 [std] against a contract permitting twelve a year [S1], and bounded by every published limit: at most 50% of the 해약환급금, a residual 계약자적립액 of at least ₩5,000,000 per 구좌 — the anchor carrier’s figure [S1], matched by [S5]; [S2] publishes ₩3,000,000 — and cumulative withdrawals inside the first ten years no greater than the premiums actually paid — the last of these a tax rule showing through into the policy conditions, since it is what keeps the 소득세법 시행령 제25조 ten-year exemption open [S1] [S2] [S5] [REG-R58]. No fee is charged [S1] [S2] [S9].

wd_cum_pp(t)[source]#

Cumulative 중도인출금 taken to the end of month t, per contract.

Nil before the first month: the accumulation opens at zero in t = 0.

prem_paid_gross_pp(t)[source]#

Premiums actually paid to the end of month t, before any withdrawal reduction.

The base of the ten-year cumulative withdrawal limit [S1] [S2] [S5], which is stated on 「실제 납입한 보험료 총액」 and so is not the reduced guarantee base prem_paid_pp(). Nil before the first month: the accumulation opens at zero in t = 0, the 계약일 premium being prem_pp(0).

fund_pp_at(t, j, timing)[source]#

The balance of fund j inside month t, per contract, at timing.

BEF_PREM opens the month; BEF_DEDUCT has taken the 특별계정 투입보험료 in at the fixed allocation; AFT_DEDUCT has cancelled the 월공제액 and any 중도인출 pro rata across the funds; AFT_DERISK has applied the mandatory pre-annuitisation reallocation. fund_pp() then grows this last one over the month.

The account opens at nil in month 0: there is no 특별계정 balance before the first premium, so BEF_PREM is 0 at t = 0 and fund_pp(t - 1, j) thereafter.

derisk_amount_pp(t)[source]#

The amount moved into the 채권형 by the mandatory pre-annuitisation de-risking.

「「연금지급개시일 − 3년」시점부터 매년 연계약해당일에 … 채권형 … 계약자적립액의 합계가 펀드 전체 계약자적립액의 80% 미만인 경우 … 자동 조정됩니다」 [S1]. Applied at the three annual 계약해당일 inside the window, after the 월공제액 and before the month’s growth [std]. Unlike 펀드자동재배분 and 펀드자동전환옵션, which the base run leaves off because a single deterministic path cannot distinguish them from a different fixed allocation, this one is not optional and is on.

fund_pp(t, j)[source]#

The balance of fund j at the end of month t, per contract.

AFT_DERISK grown by fund_growth(), which carries the gross asset return and the 운용보수 together, the fee being deducted inside the 기준가격 [S7 제43조제2호]. Zero from t_ann(), the 특별계정 having been emptied into the 일반계정 [S6].

av_pp_at(t, timing)[source]#

계약자적립액 inside month t, per contract: the funds summed at timing.

av_pp(t)[source]#

계약자적립액 (gyeyakja jeongnibaek) at the end of month t, per contract.

「납입보험료에서 월공제액 및 인출금액 등을 공제한 금액을 특별계정의 운용실적을 반영하여 계산한 금액」, which 「특별계정의 평가 등에 따라 매일 변동할 수 있습니다」 [S7 제2조] [S4] [S5]. On the monthly grid:

AV(t) = [ AV(t-1) + P_sa(t) - D(t) - W(t) ] x (1+i)^(1/12) x (1 - f/12)

fund by fund. The exact form sits in the 산출방법서, a filed 기초서류 that is not public [REG-R18 제7-64조], so this recursion is a [std] construction consistent with, and not derived from, the retrieved documents — and that limit applies equally to the surrender value and the annuity amount. It is the hard boundary on how far a public-source reconstruction of a Korean variable annuity can go.

bond_weight(t)[source]#

The 채권형 share of the 계약자적립액 at the end of month t.

Compared against bond_floor() by check_bond_floor(). With no rebalancing it drifts, and on the shipped scenarios it drifts upwards, the 채권형 carrying the lower 운용보수 on the same gross asset return.

inv_income_pp(t)[source]#

Gross separate-account investment return credited over month t, per contract.

Before the 운용보수. Not a liability cash flow and not a column of result_cf(): it is the asset-side quantity the account roll-forward needs, and this library projects gross liability cash flows only.

mgmt_fee_pp(t)[source]#

특별계정 운용보수 taken over month t, per contract — 0.50% a year blended.

Taken out of net assets before the 기준가격 is struck [S7 제43조제2호] and transferred to the 일반계정 [REG-R15 제5-7조], so it is insurer income even though the policyholder never sees a deduction: the unit price is simply lower. Note that the disclosed 투자수익률 is already net of it — [S6]’s illustration shows a gross-to-net gap of 0.01pp on a product with no guarantee charge, far too small to contain a management fee, while [S1]’s gap of 0.32pp is exactly its two account-based guarantee charges.

fund_expense_pp(t)[source]#

증권거래비용 and 기초펀드 보수·비용, nil in the base run [std].

Borne directly by separate-account assets under 자본시장법 제188조 [S7 제44조] and paid to third parties rather than to the insurer, which is why it is a cash flow of its own rather than a charge. Both lines are ex-post estimates of actual spend and not contractual rates — [S2] states its figures are estimated from FY2023 — so setting them to zero keeps the modelled charges contractual. Observed 0.00%–0.79% and 0.01%–0.45% [S2] [S4]: the omission understates the drag by up to about half a percentage point a year.

prem_paid_pp(t)[source]#

이미 납입한 보험료 at the end of month t — the strike of both guarantees.

기본보험료 plus 추가납입보험료 actually paid, excluding 특약보험료 [S1] [S4] [S7 제2조], reduced proportionally by any 중도인출 [S2] [S7 제51조제8항]:

이미 납입한 보험료 (after) = (before)
    x (중도인출 전 계약자적립액 − 중도인출금액) ÷ (중도인출 전 계약자적립액)

Without that adjustment a policyholder could withdraw the fund and keep the strike, and [R1] is explicit that it is a guarantee-risk mitigant rather than a convenience: 「중도인출금은 최저보증한도에서 차감된다」.

The strike opens at nil in month 0, the 계약일 premium being prem_pp(0).

db_pp(t)[source]#

사망보험금 payable on a death in month t, per contract, 연금개시 전.

Max(계약자적립액, 이미 납입한 보험료) — the account value with premiums paid as a floor and no 기본사망보험금 at all on the representative design [S1] [S4] [S10] [R2]. The GMDB ceases at 연금개시, 「일반적으로 연금개시 후 보장은 소멸됨」 [R2], so this is zero from t_ann().

gmdb_claim_pp(t)[source]#

The GMDB guarantee claim per death: max(0, 이미 납입한 보험료 계약자적립액).

The insurer’s own cost, met out of the 일반계정 보증준비금 rather than out of the fund [R2]. On this path it is a real expected cash flow, the mortality decrement being a probability applied to a deterministic account value; across paths it is the intrinsic value of a strip of puts and a lower bound on their expected cost.

surr_chg_cap_pp()[source]#

표준해약공제액 — the statutory ceiling on the surrender charge, 별표 14 [REG-R20].

5% x 연납순보험료 x 해약공제계수, the coefficient being the 납입기간 capped at 12 and the 연납순보험료 excluding the level-spread 부가보험료 [REG-R19 제7-66조제1항제3호] [REG-R20]. ₩1,643,940 on the anchor cell, against which the representative ₩830,000 is 50.5%. Note 6 to 별표 14 further requires the acquisition cost loaded onto the premium to be discounted at the 평균공시이율 and subtracted from the cap; no retrieved document works that netting and the exact residual cap is [unverified] [REG-R21], so the model applies the gross cap and it binds on three shipped points.

surr_chg_pp(t)[source]#

해약공제액 (haeyak gongjeaek) applying to a surrender at the end of month t.

C x (n k) ÷ n in completed whole years k, nil from n, with n the 해약공제기간 — the 납입기간 capped at seven years, which is statutory [REG-R19 제7-66조제1항제2호]. The run-off is linear in the amount, not in the ratio: all three retrieved scales fit that function exactly [S2] [S4] [S5], and the published ratio falls far faster only because its denominator is growing. C is [S2]’s ₩830,000 on the anchor cell, capped at surr_chg_cap_pp().

The charge is the unamortised 계약체결비용 [R2], which is why the composite takes its acquisition cost and its surrender charge from one carrier: pairing [S2]’s 5.17% with [S5]’s ₩1,077,000 would recover more on surrender than was ever loaded.

cv_pp(t)[source]#

해약환급금 (haeyak hwanreupgeum) on a surrender at the end of month t.

max(0, 계약자적립액 해약공제액), the zero floor being statutory — 「계약자적립액 에서 해약공제액을 공제한 금액이 음(陰)의 값인 경우에는 이를 영(零)으로 처리한다」 [REG-R19 제7-66조제1항제1호]. There is no guarantee on it at any duration [S1] [S6] [S7 제50조제3항] [S8] [S10], and on the representative scale it is zero for roughly the first four months. 변액보험 is also barred from the 무해지/저해지환급형 forms by 제7-66조제4항제1호 [REG-R19], so the cliff-shaped curve that dominates this library’s protection products cannot appear here.

av_ann_pp()[source]#

계약자적립액 at the 연금개시나이 계약해당일, per contract: av_pp(t_ann() - 1).

gmab_base_pp()[source]#

K(T), the 최저연금적립금 strike: 이미 납입한 보험료 at annuitisation.

Premium refund at 100%, the textbook identity 「연금개시시 계약자적립금(최저보증 포함) = Max(기납입보험료, 연금개시시 계약자적립금)」 [R2] and the level of the only retrieved charged-GMAB contract [S1]. Three other rules are in the market — step-up, ratchet and roll-up — and [R1]’s fifteen-year worked comparison shows that the guarantee design, not the guarantee level, drives the option cost; they are specified in product-spec.md and not run.

gmab_claim_pp()[source]#

max(0, K(T) AV(T)) — the GMAB payoff per contract reaching annuitisation.

A European option struck on one date. It is not payable on surrender, not payable on death before annuitisation and forfeited on 조기연금개시 [S1] [S6] [S7 제50조제3항] [S8] [S10] [R1] — 「만기 전에 사망 또는 해약이 발생하는 경우 이 보증은 성립하지 않으며」 — so it is weighted by pols_annuitised(), which carries every decrement that occurred before T.

On one path this is the option’s intrinsic value at maturity and not its value: zero on the base and high scenarios, positive on the low one. Read it with the model docstring’s warning beside it.

annuity_fund_pp()[source]#

연금재원 per contract: the 계약자적립액 at T, floored at the GMAB.

Transferred from the 특별계정 to the 일반계정 — 「연금개시시점부터 계약자적립액 모두에 대하여 특별계정에서 일반계정으로 자동전환하여 공시이율로 운용합니다」 [S6] — with the guarantee top-up added from the 일반계정 보증준비금.

decl_rate(k)[source]#

공시이율 declared in the k-th completed year of the 연금수령기간.

Re-declared monthly off a published 공시기준이율 [REG-R18] [REG-R24]; the base run holds it at the 2026 평균공시이율 of 2.50% [REG-R48] [std].

min_guar_rate(k)[source]#

최저보증이율 in the k-th completed year: 1.00% / 0.75% / 0.50% [S1].

credit_rate(k)[source]#

Max[공시이율, 최저보증이율] in the k-th completed year of the payout phase.

annuity_int_rate()[source]#

The interest rate the annuity is struck at: credit_rate() at duration 0.

「연금사망률 및 공시이율을 적용하여 산출방법서에 따라」 [S1] [S2] [S5], the rates in force at annuitisation. The contract then moves the annuity with the 공시이율 as it is re-declared [S5]; this model holds it level [std] and says so.

ann_mort_rate_at_age(x)[source]#

연금사망률 at 보험나이 x — the annuitant basis, from data.mort_table().

A [std] Makeham curve fitted so its complete expectation of life at 65 rounds to the 제10회 경험생명표 65세 기대여명, 23.7 years male and 27.1 female [REG-R33]; the fitted values are 23.663 and 27.060. The 실제 table is not published [REG-R34]. The contract lets the insurer re-strike this basis at annuitisation but only in the policyholder’s favour [S1] [S2] [S5]; that one-way ratchet is not modelled.

mort_rate_at_age(x)[source]#

보험사망률 at 보험나이 x — the insurance basis, from data.mort_table().

The ann_mort_rate_at_age() curve at mu / 0.80 [std]: Korea prices annuities on a separate and lighter 연금생명표, and neither table is public [REG-R34]. This is the basis of the death decrement and therefore of the GMDB cost.

mort_rate(t)[source]#

The annual death rate applying in month t, on the basis then in force.

보험사망률 through the 연금개시 전 보험기간, 연금사망률 afterwards: the two periods are priced on different tables in Korea and the model does not pretend otherwise.

mort_rate_mth(t)[source]#

The monthly death rate in month t: 1 - (1 - mort_rate(t)) ** (1/12).

A uniform-force split of the annual rate [std].

lapse_rate(t)[source]#

The annual 해지율 in the policy year containing month t.

Zero from t_ann(): the representative payout form is a 종신연금형 and no retrieved document allows a surrender of it [std]. The scale is calibrated so the seven-year persistency is 28.9%, against the only published Korean figure for this product — 「변액보험의 7년 평균 유지율은 30% 미만으로 알려져 있다」, second-hand inside [R1] and reported from a 2016 금융감독원 release that was not retrieved.

Lapse here is static and exogenous, and it is neither in reality. [R1] states the market and reserving convention plainly — 「동적해지율이란 최저보증 발생률(In-the- moneyness)에 따라 해지율을 달리 적용하는 방법으로 … 최저보증 발생률이 높을수록 해지율을 감소시키고」 — but no retrieved document publishes a functional form or a single parameter, so any dynamic-lapse formula here would be a [std] construction and the base run does not attempt one.

lapse_rate_mth(t)[source]#

The monthly 해지율 in month t: 1 - (1 - lapse_rate(t)) ** (1/12).

pols_if(t)[source]#

Contracts in force at the start of month t.

pols_if(0) = pols_if_init(), and this is the weight carried by every cash flow on the same result_cf() row, so the exposure column and the cash flows beside it reconcile. It is a genuine policy count on both sides of annuitisation — contracts before t_ann(), living annuitants after it. The end-of-month count is pols_if_at(t, "AFT_DECR").

pols_death(t)[source]#

Expected deaths during month t, taken first among the decrements [std].

pols_lapse(t)[source]#

Expected 해지 during month t, taken on the survivors of the month’s deaths.

pols_maturity(t)[source]#

Survivors carried out at the horizon, so the roll-forward closes.

A 종신연금형 has no maturity and pays nothing here: the projection stops at attained age omega_age and this cells makes the truncation visible rather than absorbing it into the last row’s decrements. Non-zero only in the last row, t = proj_len() - 1.

pols_if_at(t, timing)[source]#

The in-force count inside month t: BEF_DECR / BEF_LAPSE / AFT_DECR.

pols_annuitised()[source]#

The count reaching the 연금개시나이 계약해당일, pols_if(t_ann()).

This is the indicator the GMAB payoff is weighted by, and it carries every decrement — mortality and lapse — that occurred before T.

is_annuity_month(t)[source]#

True in a month an annuity instalment falls due: T, T+12, T+24, …

The annuity is paid annually in advance on the 연금개시 계약해당일 [std], which is the granularity the 연금 연액 and the 0.5% 연금수령기간 중 계약관리비용 are published on [S4].

pols_annuity_oblig(t)[source]#

The count an instalment is owed to in month t.

Inside the 10-year 보증기간 that is every contract that annuitised, whether the annuitant is alive or not: 「사망하더라도 남은 보증기간의 연금은 지급됩니다」, the remaining instalments falling due on their dates or commutable at the 공시이율 [S2] [S5]. After it, the survivors only — 「보증기간 후 사망시 계약은 소멸」 [S1] [S5]. The step down at the end of the 보증기간 is real and is visible in the output.

ann_surv(k)[source]#

k p_y — survival from 연금개시나이 to k years later, on the 연금사망률.

annuity_factor()[source]#

The 종신연금형 10년 보증기간부 annuity-due factor at annuity_int_rate().

sum(v**k for k in 0..9) + sum(v**k * k_p_y for k in 10..omega): the first ten instalments are certain and the rest life-contingent. The menu is uniform across carriers — 보증기간 of 10 / 15 / 20 years, to age 100, or 기대여명보증, in 정액형 or 체증형 [S1] [S2] [S5] — and the composite takes the ten-year level form because it is the modal election, the only one exercising both longevity and a guarantee period, and the form the 소득세법 종신형 연금보험 route is written around, that route requiring the guarantee period to sit within the published 기대여명 연수 [REG-R58]. The sum is truncated at omega_age for consistency with the projection horizon.

annuity_ann_pp()[source]#

연금 연액 (yeongeum yeonaek), gross: annuity_fund_pp() / annuity_factor().

annuity_charge_pp()[source]#

연금수령기간 중 계약관리비용: 연금 연액의 0.5%, netted off each payment [S4].

Two forms are published — this proportional one [S4] and 구좌당 매월 min(영업보험료의 3.5%, ₩4,000) [S2]. The composite takes the proportional form [std] because it is scale-free and needs no reference to a premium that has stopped being paid.

annuity_net_pp()[source]#

The annuity instalment actually paid, 연금 연액 net of the payout-phase charge.

premiums(t)[source]#

영업보험료 received in month t: 기본보험료 plus 추가납입보험료, in force weighted.

Received into the 일반계정, out of which the front-end charges are retained and the remainder transferred to the 특별계정 [S7 제2조] [R2].

claim_pp(t, kind)[source]#

Benefit per contract for one kind: DEATH, LAPSE, ANNUITY, MATURITY.

pols_decr(t, kind)[source]#

The count a benefit of kind is paid on in month t.

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

Benefit outgo in month t, for one kind or, with kind=None, all four.

DEATH is the 사망보험금 including its GMDB top-up; LAPSE the 해약환급금; ANNUITY the instalment net of the payout-phase charge; MATURITY is structurally nil, a 종신연금형 paying nothing at the horizon. The column claims is deliberately absent from result_cf(), which publishes the split lines so that they sum to net_cf().

claims_from_av(t, kind)[source]#

The part of a benefit of kind released out of the 특별계정 in month t.

On death that is the 계약자적립액 alone, the guarantee top-up coming from the 일반계정 보증준비금 [R2]; on a 해지 it is the whole account value, of which the policyholder receives the 해약환급금 and the insurer retains the 해약공제액.

withdrawals(t)[source]#

중도인출금 paid to the policyholder in month t, out of the 특별계정.

fund_expenses(t)[source]#

증권거래비용 and 기초펀드 보수 borne by separate-account assets — nil [std].

expenses(t)[source]#

The insurer’s own expenses in month t — not the charges it collects.

₩300,000 per contract at issue and ₩3,000 per contract per month thereafter, both [std] and both held level with no inflation [std]. No Korean carrier publishes a unit cost: the 사업비 disclosure is of charges, not of costs [R2] [S12], so the acquisition and maintenance charges above are sourced and these are not.

comm_rate(y)[source]#

모집수수료율 in policy year y, a fraction of 보험료총액; nil after year five.

1.34% / 0.41% / 0.28% / 0.25% / 0.11%, the 2017 census mean for a 월납 변액연금 [R1 <표 Ⅴ-3>]. The five sum to 2.39% of the premiums the policyholder will pay, against the mean total of 2.11% the same table reports: a mean of contract totals is not the sum of per-year means, and the model runs the per-year scale. The observed range is wide — 0.63%–2.38% in year one and 1.10%–3.13% in total — and channel is a first-order parameter: the one variable annuity [R1] found buyable directly online carried no acquisition commission at all, and bancassurance and online 계약체결비용 were capped at 50% of the tied-agent level from 2016. The composite is a 전속설계사 contract [std] and says so.

commissions(t)[source]#

모집수수료 paid in month t, one twelfth of the policy year’s rate [std].

prem_to_av(t)[source]#

특별계정 투입보험료 transferred 일반계정 → 특별계정 in month t, in force weighted.

One of the transfers 감독규정 제5-7조 permits between the two accounts [REG-R15]. It is internal: it appears in both account ledgers with opposite signs and cancels out of net_cf().

av_charges(t)[source]#

월공제액 and 특별계정 운용보수 transferred 특별계정 → 일반계정 in month t.

The 월공제액 by cancelling units on the 월계약해당일, the 운용보수 daily inside the 기준가격 [S7 제2조] [S7 제43조제2호]; both are 「위험보장에 필요한 금액」 and 「사업비」 transfers under 감독규정 제5-7조 [REG-R15], and the two guarantee components of the 월공제액 are held in the 일반계정 as 보증준비금 [R2]. Internal.

surr_charges(t)[source]#

해약공제액 retained by the insurer on the month’s 해지, 특별계정 → 일반계정.

min(해약공제액, 계약자적립액), the statutory zero floor on the 해약환급금 meaning the insurer never recovers more than the account holds [REG-R19]. Internal.

av_transfer(t)[source]#

연금재원 moved 특별계정 → 일반계정 at t_ann(), in force weighted.

「연금개시시점부터 계약자적립액 모두에 대하여 특별계정에서 일반계정으로 자동전환하여 공시이율로 운용합니다」 [S6]. The account-value part only: the GMAB top-up beside it is a movement within the 일반계정, from the 보증준비금 to the 연금재원, and so appears in neither ledger. Internal.

prem_charges(t)[source]#

계약체결비용, 납입 중 계약관리비용 and 기타비용 retained in the 일반계정 — a memo.

Never a transfer: this money reaches the 일반계정 with the premium and simply does not leave it [S7 제2조].

annuity_charges(t)[source]#

연금수령기간 중 계약관리비용 retained in month t — a memo.

Netted off the instalment [S4], so it never leaves the 일반계정 and appears in no ledger; claims() already carries the payment net of it.

gmdb_charges(t)[source]#

최저사망보험금 보증비용 collected in month t — a memo on the guarantee.

gmab_charges(t)[source]#

최저연금적립금 보증비용 collected in month t — a memo on the guarantee.

Compare it with gmab_claims(), and read the difference as what it is: a single-path residual, not a profit. On the base scenario the GMAB finishes out of the money and the whole charge is collected against an intrinsic cost of zero.

gmdb_claims(t)[source]#

The GMDB strain on the 일반계정 in month t: top-up times deaths.

gmab_claims(t)[source]#

The GMAB strain at t_ann(): intrinsic payoff times the count annuitising.

A movement inside the 일반계정 — 보증준비금 to 연금재원 — so it does not appear in net_cf(). Its cash-flow consequence is a larger annuity for the rest of the projection, which is where a reader should look for it.

charge_income(t)[source]#

Every charge line collected in month t, summed — a memo, never a ledger line.

계약체결비용 + 계약관리비용 + 위험보험료 + 두 보증비용 + 특별계정 운용보수 + 해약공제액 + 연금수령기간 중 계약관리비용. It is a memo because most of it is an internal transfer rather than an external cash flow: adding it to premiums() would count the same money twice.

net_cf(t)[source]#

Net cash flow in month t, income positive: income less outgo.

The whole-contract external cash flow — what crosses the boundary of the insurer, on either side of the 특별계정 / 일반계정 line:

premiums − 사망보험금 − 해약환급금 − 연금 − 중도인출금
         − 특별계정 third-party costs − expenses − commissions

Every internal transfer is absent by construction, which is what makes the columns of result_cf() sum to this line. Investment return is absent too: this library projects gross liability cash flows and leaves the asset side, discounting, the 책임준비금, the IFRS 17 CSM and the K-ICS 요구자본 to a separate layer that consumes them. net_cf_gen() and net_cf_sep() decompose this line by account and check_net_cf() asserts that they add back to it.

net_cf_sep(t)[source]#

The 특별계정 ledger in month t, income positive.

In: the 특별계정 투입보험료. Out: the 월공제액 and 운용보수 transferred to the 일반계정, the 해약공제액 retained there, the 계약자적립액 released on a death or a 해지, the 중도인출금, the 연금재원 moved at 연금개시, and the third-party costs borne directly by separate-account assets under 자본시장법 제188조 [S7 제44조]. Investment return is excluded, as in net_cf().

net_cf_gen(t)[source]#

The 일반계정 ledger in month t, income positive.

In: the whole 영업보험료, the transfers received from the 특별계정, and the 연금재원 at 연금개시. Out: the 특별계정 투입보험료 transferred away, the GMDB top-up met from the 보증준비금, the annuity instalments, and the insurer’s own expenses and commission. The GMAB top-up does not appear: it is a movement within this account.

check_net_cf_resid(t)[source]#

Residual of the account-boundary identity in month t; zero to floating point.

net_cf(t) net_cf_gen(t) net_cf_sep(t). Every transfer 감독규정 제5-7조 permits between the two accounts [REG-R15] appears in the two ledgers with opposite signs, so their sum must be the whole-contract external cash flow and nothing else. This is the identity a 변액연금보험 has to cross the 특별계정 / 일반계정 boundary to state, and a model that cannot state it has not represented the boundary.

check_net_cf()[source]#

True when the cash flow statement reconciles at every projected month.

No argument and a bool return; check_net_cf_resid() is the signed per-month residual. The tolerance is relative to the size of the month’s gross flows, cash flows on this product running to eight figures in KRW.

check_pols_roll_fwd_resid(t)[source]#

In-force roll-forward residual in month t; zero to floating point.

pols_if(t) pols_if(t+1) = deaths + 해지 + horizon survivors, written on the start-of-month counts pols_if() carries. In the horizon month pols_if(t+1) is zero and pols_maturity() absorbs the survivors.

check_pols_roll_fwd()[source]#

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

No argument and a bool return; check_pols_roll_fwd_resid() is the signed per-month residual. In force is a probability of order 1, so the tolerance is roll_fwd_tol.

check_av_roll_fwd_resid(t)[source]#

계약자적립액 roll-forward residual in month t, per contract.

AV(t) AV(t−1) = 특별계정 투입보험료 월공제액 중도인출금 + gross investment return 특별계정 운용보수 the 연금재원 transferred at 연금개시. The mandatory de-risking does not appear because it conserves the total; it moves money between funds, not out of the account.

Defined on every month of the frame including t = 0, whose opening balance is nil: the first row carries the single premium, the 계약체결비용 and the whole first month’s charge stack, and the identity closes on it.

check_av_roll_fwd()[source]#

True when the account recursion closes at every projected month.

No argument and a bool return; check_av_roll_fwd_resid() is the signed per-month residual, measured per contract on a balance of order 1e7 in KRW, so the tolerance is relative.

check_charge_split_resid(t)[source]#

Residual of the separate-account investment identity in month t, per contract.

gross asset return = 특별계정 운용보수 + the change in the 계약자적립액 from investment. It is the guard against the charge-base confusion this product is most often modelled wrong at: the 운용보수 is inside the 기준가격, the 월공제액 cancels units, the 계약체결비용 never enters the fund at all, and the premium component of the GMAB charge is on a base that is not the fund — four bases in one stack.

check_charge_split()[source]#

True when the separate-account investment identity closes at every month.

check_gmdb_floor_resid(t)[source]#

Residual of the GMDB decomposition in month t, per contract.

사망보험금 = 계약자적립액 + max(0, 이미 납입한 보험료 계약자적립액), i.e. the benefit splits exactly into the part released from the 특별계정 and the part met from the 일반계정 보증준비금 [R2]. That split is what makes the two account ledgers add back to net_cf().

check_gmdb_floor()[source]#

True when the death benefit splits cleanly into fund and guarantee at every month.

check_bond_floor_resid(t)[source]#

Shortfall of the 채권형 weight below the mandatory floor in month t; zero when met.

Negative if the ladder is breached. The floor 「채권형 최저편입비율」 binds both the premium allocation and the account mix and survives every later 펀드변경 [S1] [R1], and the insurer has a direct financial interest in it beyond the policyholder’s: the 별표 24 보증준비금 standard factor is indexed to 주식비중한도, 「기초서류상 최대 주식투자 비중을 적용함」, so a lower equity cap is a lower reserve floor [R1] [REG-R26].

check_bond_floor()[source]#

True when the 채권형 weight meets the mandatory ladder at every projected month.

check_surr_chg_cap_resid(t)[source]#

Excess of the 해약공제액 over the 표준해약공제액 in month t; zero when compliant.

감독규정 제7-66조제1항제3호 requires the 해약공제액 to be the 표준해약공제액 of 별표 14 [REG-R19] [REG-R20], and 제1항제2호 caps the 해약공제기간 at seven years where the 납입기간 is seven years or more. The cap binds on three shipped model points, all 5년납, whose level charge scaled from the anchor cell would otherwise exceed it.

check_surr_chg_cap()[source]#

True when the surrender charge is inside the statutory cap at every month.

check_prem_alloc_resid(t)[source]#

Residual of the premium-allocation identity in month t, per contract.

While both front-end charges run, the 기본보험료 reaching the 특별계정 is exactly 1 부가보험료율 of it — 91.33% on the composite, inside the 91.3%–91.5% three carriers publish on the anchor cell [S1] [S2] [S6] and inside [R1]’s industry band of 「납입보험료의 5~15%를 … 차감한 후 85~95%만 투자」. 추가납입보험료 is subtracted first, because it carries no loading [S1] and so is not part of that ratio.

check_prem_alloc()[source]#

True when the premium allocation matches the published fee stack at every month.

result_cf()[source]#

The cash flow statement: one row per projection month, indexed by t.

pols_if opens each row and is the count in force at the start of the month, so it is the weight carried by every cash flow beside it. The remaining columns are the whole-contract external cash flows and they sum to net_cf: income less outgo, income positive. There is no claims column — the split lines are published instead, so the columns add up without a reader having to know which to skip.

result_pols()[source]#

In-force movements, indexed by t.

Rows read across: pols_if pols_death pols_lapse pols_maturity is the next row’s pols_if, which is what check_pols_roll_fwd() asserts.

result_av()[source]#

The 계약자적립액 recursion, per contract, indexed by t.

The columns are the account roll-forward in the order the month applies them, then the quantities the guarantees are struck on. Reading a row left to right is reading check_av_roll_fwd_resid().

result_charges()[source]#

The 상품요약서 fee stack as separate lines, per contract, indexed by t.

Five of these are the lines the task of modelling this product turns on, and they are separate columns because they are deducted from different bases at different times and land in different accounts: 계약체결비용 and 납입 중 계약관리비용 out of the premium in the 일반계정; 위험보험료, 납입 후 계약관리비용 and both guarantee components out of the 계약자적립액 on the 월계약해당일; 특별계정 운용보수 inside the 기준가격. Collapsing them into one charge is the error the columns exist to prevent.