349ウォンの差がどこで生まれたかを規則単位で突き止める
目標
中途解約契約の未経過保険料を計算し直し、日数基準と丸め方式と区間の分割が生む差を、契約単位で指し示して、訂正リストまで出します。
なぜ重要なのか
払戻額は「年間保険料×未経過日数÷総日数」の1行のように見えますが、その1行の中に、ポリシーとして決めるべき値が3つ隠れています。日数をどう数えるか、どこで切るか、期中の変更を反映するか。 3つの値を別々に取った2つのシステムは、それぞれ正しい計算をしながらも、別の答えを出します。そのとき必要なのは「差が出ます」ではなく、「この契約の差は日数基準のせいで、あの契約は区間のせい」という説明です。 採点ツールは、書かれた数字同士を比べません。ステップ1の資料から基準値を計算し直して突き合わせるので、案内のルールにそのまま従う必要があります。
ステップ
- /root/prem/gen_prem.pyを作成して実行し、/root/prem/policies.csv(60件)と/root/prem/changes.csv(15件)を作ってください。
- 契約ごとに、実日数と30/360の2つの基準の総日数・経過日数・未経過日数を、/root/prem/daycount.jsonに書いてください。
- 実日数基準で未経過保険料を計算し、/root/prem/refund_actual.jsonに書いてください。
- 同じ契約を30/360基準で計算して/root/prem/refund_30360.jsonに書き、ステップ3との差も一緒に残してください。
- 保障変更のある契約を変更日の前後で分割し、/root/prem/segments.jsonに区間ごとの払戻額を書いてください。
- 区間ごとに端数処理した合計と、最後に一度だけ端数処理した値の差、そして月払12分割を、/root/prem/rounding.jsonに書いてください。
- 請求システムと会計システムの計算を並べて実行し、/root/prem/diff.jsonに契約ごとの差と原因を書いてください。
- 訂正リストと結論を/root/prem/report.json1つにまとめ、policies.csvのsha256も一緒に書いてください。
参考
- ステップ1の契約ルール(iは0から59): policy_idは
P0001から4桁、開始日は2024-01-01に(i * 59) % 730日を足した日、満期日は開始日の1年後の同じ日の1日前(その日がなければ、その月の末日に寄せたうえで1日前)、pay_modeはiが奇数ならmonthly、偶数ならannual、年間保険料は120000 + (i * 7777) % 880001、解約日は開始日に37 + (i * 29) % 300日を足した日です。 - ステップ1の変更ルール: iを4で割った余りが2の契約だけに変更があります。変更日は開始日に
30 + (i * 47) % 260日を足した日、新しい年間保険料は연보험료 + 60000 + (i * 3331) % 200000です(プレースホルダーは年間保険料です)。 - 日数の定義: 総日数は開始日と満期日をどちらも含めて数え、経過日数は解約日から開始日を引いた日数(解約日当日は保障しません)、未経過日数は総日数から経過日数を引いた値です。
- 30/360(US)ルール: 前の日付の日が31なら30に寄せ、後ろの日付の日が31で前が30以上なら30に寄せたうえで、
360 * 연차 + 30 * 월차 + 일차で数えます(プレースホルダーは年の差、月の差、日の差です)。総日数と未経過日数は、これに実日数と同じ方式で1を足して、両端を含めます。 - 払戻額:
연보험료 * 미경과일수 / 총일수を1ウォン単位で切ります(プレースホルダーは年間保険料、未経過日数、総日数です)。ステップ3・4・5のポリシーはROUND_HALF_UPです。 - 区間の未経過日数: 区間が解約日から満期日までと重なる日だけを数えます。重ならなければ0です。
- ステップ7の2つの経路: 請求システムは実日数・ROUND_HALF_UP・区間反映で、会計システムは30/360・ウォン未満切り捨て(ROUND_DOWN)・区間無視(元の年間保険料で全期間)です。原因は、会計の計算でルールを1つだけ請求側に変えたときに値が変わるものだけを、
daycount、rounding、segmentの順に書きます。 - よくある間違いは、未経過日数に解約日当日を入れること、30/360で両端の包含を忘れること、区間が未経過区間と重ならないのに1日と数えること、月払分割で余りを捨てることです。
- 確認:
python3 -c "import json;print(json.load(open('/root/prem/diff.json'))['by_cause'])"
契約と保障変更の資料を作る
/root/prem/gen_prem.pyを作成して実行し、/root/prem/policies.csv(60件)と/root/prem/changes.csv(15件)を作ってください。
満期日が厄介です。開始日の1年後の同じ日がない場合(2024-02-29)は、その月の末日に寄せる必要がありますが、calendar.monthrangeがその月の最後の日を教えてくれます。日付はすべてYYYY-MM-DDで書きます。
日数基準の2つを分けておく
契約ごとにterm_days、used_days、unused_daysと30/360版の3つの値を/root/prem/daycount.jsonに書き、保険期間に2月29日が含まれる契約の一覧と、2つの基準で未経過日数が異なる契約の数も一緒に残してください。
実日数はdateの引き算でそのまま出ます。30/360は、31日を寄せる2つのルールを先に適用してから、年・月・日の差を足します。両端を含めるかどうかを、2つの基準で同じに決めてはじめて比較が成り立ちます。
実日数で未経過保険料を計算する
/root/prem/refund_actual.jsonにbasis、rounding、契約ごとのunused_daysとrefund_krw、そしてtotal_krwを書いてください。このステップでは、保障変更を無視して、元の年間保険料で全期間を計算します。
Decimalで掛けて割ったあと、quantizeで1ウォンの位で切ります。実数で計算すると、0.5が正確に0.5ではないので、丸めモードを選ぶこと自体が無意味になります。
分母を30/360に変えてみる
/root/prem/refund_30360.jsonに30/360基準の払戻額とステップ3との差diff_krw、そしてdiff_rowsとtotal_diff_krwを書いてください。
分母と分子の両方を30/360に変える必要があります。片方だけを変えると、期間が1年の契約の払戻率が1を超えることが起きます。diff_krwは、30/360の値から実日数の値を引いたものです。
期中の保障変更を区間に分割する
保障変更のある契約15件だけを選び、/root/prem/segments.jsonに区間2つ(from、to、annual_krw、unused_days、refund_krw)と、区間を合算したrefund_krw、変更を無視したwhole_period_refund_krw、その差gap_krwを書いてください。
前の区間は変更日の前日までです。未経過日数は、区間が解約日から満期日までと重なる日だけを数えるので、変更日が解約日より前なら、前の区間の未経過日数は0です。分母は、2つの区間とも契約全体の総日数です。
端数を切る位置をどこに置くか
/root/prem/rounding.jsonにsegment_rounding(policies_with_gap、total_gap_krw)とmonthly_split(policies、count、all_exact)を書いてください。月払契約の12か月分割は、合計が年間保険料とぴったり同じでなければなりません。
区間ごとに切った合計と、区間ごとの正確な値をすべて足したあとに一度だけ切った値を比べます。月払の分割は、商を切り捨てで割り当てたあと、残りのウォンを前の月から1ウォンずつ上乗せすれば、合計が合います。
2つのシステムの差をルール単位で指し示す
/root/prem/diff.jsonに2つのシステムのルール、契約ごとのclaims_krw、acct_krw、diff_krw、causes、そしてdiff_rows、total_diff_krw、by_causeを書いてください。
原因は推測で付けません。会計の計算でルールを1つだけ請求側に変えて計算し直し、値が変わるルールだけが原因です。3つのルールを、それぞれ1回ずつ切り替えれば済みます。
訂正リストと結論を1枚にまとめる
/root/prem/report.jsonにpolicies_sha256、policies、changed_policies、refund_actual_total_krw、refund_30360_total_krw、basis_gap_krw、segment_gap_policies、diff_rows、total_diff_krw、by_cause、corrections、policy_basis、policy_rounding、verdictを書いてください。verdictはclaims-system-is-authoritativeです。
correctionsには、差が出た契約だけをpolicy_idの昇順で、policy_id・from_krw・to_krw・delta_krwの4つのキーだけで入れます。fromは直す値(会計)、toは基準とすることにした値(請求)です。ハッシュは、いまのpolicies.csvから計算し直します。