逐条规则找出 349 元差额的来源
目标
重新计算中途退保合同的未到期保费,并按合同指出天数基准、舍入方式和分段所造成的差异,最后输出更正清单。
为什么重要
退还金额看起来只是“年保费乘以未到期天数再除以总天数”这一行公式,但这一行里藏着三个必须由策略来确定的值:天数怎么计算、在哪里取整、是否反映保险期间内的变更。 三个值设置得不同的两个系统,各自的计算都没有错,却会给出不同的答案。这时需要的不是“存在差异”,而是“这份合同的差异来自天数基准,那份合同的差异来自分段”这样的解释。 评分器不会拿你写的数字互相比较。它会根据第 1 步的数据重新计算基准值再核对,所以必须严格遵循说明中的规则。
步骤
- 创建并运行 /root/prem/gen_prem.py,生成 /root/prem/policies.csv(60 份)和 /root/prem/changes.csv(15 笔)。
- 把每份合同在实际天数和 30/360 两种基准下的总天数、已过天数、未到期天数写入 /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.json 中,并同时写入 policies.csv 的 sha256。
参考
- 第 1 步合同规则(i 取 0 到 59):policy_id 从
P0001开始,共四位数字;开始日期是 2024-01-01 加上(i * 59) % 730天;到期日是开始日期一年后的同一天的前一天(如果该日期不存在,则先提前到当月最后一天,再取前一天);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 步的两条路径:计费系统采用实际天数、ROUND_HALF_UP、反映分段;会计系统采用 30/360、舍去韩元以下(ROUND_DOWN)、忽略分段(用原来的年保费计算整个期间)。原因只写出:在会计计算中仅把一条规则改成计费系统的做法时值会发生变化的那些规则,按
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 笔)。
到期日比较棘手。开始日期一年后的同一天不存在的情况(2024-02-29),要提前到当月最后一天,calendar.monthrange 可以告诉你当月的最后一天。日期一律写成 YYYY-MM-DD。
区分两种天数基准
把每份合同的 term_days、used_days、unused_days 以及 30/360 版本的三个值写入 /root/prem/daycount.json,并同时保留保险期间内包含 2 月 29 日的合同清单,以及两种基准下未到期天数不同的合同数量。
实际天数直接用 date 相减得到。30/360 先应用调整 31 日的两条规则,再把年、月、日的差相加。是否包含两端,两种基准必须定得一样,比较才成立。
按实际天数计算未到期保费
在 /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。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。两个分段的分母都是整份合同的总天数。
在哪里取整
在 /root/prem/rounding.json 中写入 segment_rounding(policies_with_gap、total_gap_krw)和 monthly_split(policies、count、all_exact)。按月缴费合同的 12 个月拆分,合计必须与年保费完全相等。
把每段取整后的合计,与把各段精确值全部相加后只取整一次的值做比较。按月缴费拆分时,先把商向下取整分配,再把剩余的韩元从前面的月份开始每月加 1 韩元,合计就能对上。
按规则指出两个系统的差异
在 /root/prem/diff.json 中写入两个系统的规则、每份合同的 claims_krw、acct_krw、diff_krw、causes,以及 diff_rows、total_diff_krw、by_cause。
不要凭猜测归因。在会计计算中只把一条规则换成计费系统的做法再重新计算,值发生变化的规则才是原因。把三条规则各切换一次即可。
把更正清单和结论汇总成一页
在 /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 四个键。from 是要更正的值(会计),to 是决定作为基准的值(计费)。哈希要现在从 policies.csv 重新计算。