TT Lab
Get started
Learn Learning paths Courses

Insurance Domain Deep Dive

The Refund Differs by One Won and Both Systems Insist They Are Right

Continue in TT Lab

In one line

A mid-term cancellation refund looks like one line, "annual premium times unexpired days divided by total days," but the basis for counting days, where you cut, and how you split into segments each produce a different answer. Pinning those three down as policy and gathering the calculation path into one is the whole of this problem.

Why this was needed

A customer canceled the policy in May. The claims system gave notice of a refund of 412,331 won, and a few days later the settlement file the accounting system made says 411,982 won. A difference of 349 won. When you call in the two teams and go through the calculations, neither side is wrong anywhere. One divided by actual calendar days, and the other followed the old practice of counting a month as 30 days. One rounded at the won unit, and the other truncated. One reflected the coverage change made in March and split the period in two, and the other calculated the whole period at once with the premium at the time of contract.

Such a difference is a few hundred won for one case. But when thousands of cases pass every month, it becomes a reconciliation item, and a reconciliation item becomes work that someone has to explain by hand every month. And when a customer asks, you cannot answer "it differs by system."

How it works

First, the basis for counting days (day count convention). Actual days (actual/actual) counts the calendar as it is. February is 28 or 29 days, and a year is 365 or 366 days. 30/360 treats every month as 30 days and a year as 360 days, so it was long used in bond interest calculation, and the simplicity of the era of hand calculation remains as it is. The two methods give different numbers of days for the same period. Python's date object gives the actual number of days directly by subtraction, and 30/360 has to be made by adjusting the day numbers by hand.

There is also a trap in deciding the end of the period. When is the expiry date of a one-year contract that started on February 29, 2024? In 2025 there is no February 29. Using monthrange() of the calendar module to get the last day of that month and pull it back is the usual handling, but if the policy terms have not decided which way to pull, it becomes the first point at which two systems diverge. Exchange dates as YYYY-MM-DD of RFC 3339 to eliminate fights over the order of the fields.

Second, the place to cut and the rounding mode. Cutting at the won unit is easily agreed, but whether to round 0.5 won up or drop it differs. It is common for the accounting practice of conservatively truncating and the claims practice of rounding so as not to disadvantage the customer to coexist in one company. The quantize() of Python's decimal module makes you choose explicitly among modes such as ROUND_HALF_UP and ROUND_DOWN. If you calculate in floating point (float), this choice itself becomes meaningless — because 0.5 is not exactly 0.5 to begin with.

Third, segment splitting. If you change coverage mid-term, the premium changes from that day too. The refund has to be calculated separately before and after the change and added to be right. But if you split, you round twice, and the sum cut per segment is off by one won from the value cut once for the whole. This is not an error but a choice. You have to decide whether to cut per segment or to cut just once at the end, and write that rule down.

계약기간 |-----------------------------------------|
변경일           ^                 해지일 ^
구간1    |-------|                          (옛 보험료)
구간2            |-------------------------|(새 보험료)
미경과분                              |----|  ← 이 겹침만 환급

What it looks like in the field

When you run into a difference, the most useless report is "there is a difference of 349 won." A useful report is "of the 349 won, 300 won is from the day count basis, 49 won from rounding, and the rest from whether the segments were reflected." The way to separate the cause by rule is simple. Change just one rule in one system's calculation to the other side's and see whether the result changes. If you toggle the three rules one at a time, it comes out which rule actually affects this contract.

Another thing you often see is the splitting of monthly-payment contracts. Dividing the annual premium by 12 usually leaves a remainder, and if you drop it, the year's billing total falls short of the annual premium, and if you round it up in every month, it overshoots. You have to decide even in which month to attach the extra 1 won for the claims system and the receipt ledger to match at year end. Some companies attach it from the earlier months and some lump it into the last month, but either way there must be a rule.

And a correction is not only changing numbers. Which calculation was taken as the reference, how much that decision moves, and how many contracts are affected must all be on one page for approval. A list with only the differences becomes useless at the second correction — because nobody knows where the starting point was.

What really matters in practice

What you will do in the next lab

You build 60 policies and 15 mid-term coverage changes yourself, calculate the same cancellation cases with actual days and with 30/360, and see how much they split. Then you cut the contracts with changes into segments, count the difference between the sum cut per segment and the value cut once, and make the monthly 12-way split match the annual premium exactly. Finally you run the claims and accounting paths side by side, point to the cause of each contract's difference by rule, and produce the correction list.