借贷系统在计算利息时,总少不了对利率的处理。利率字段通常由资产端同事手工录入,或者通过数据接口传入(最终也是由接口调用方的同事手工录入)。而人工操作不可避免会出现错误,即使加入了初审、复审等交叉审核环节,审核也会因为系统和业务逐渐稳定而流于形式。

       本文尝试从不同角度解答“系统如何自动甄别标的利率是否合理”这一问题,在深入讨论之前,先做以下约定:

  • 在判断某产品利率是否合理的时候,应该选取同类型产品的利率池(比如历史利率)作为参考。
  • 活动期间的爆款产品的利率不能作为参考。


c compile

图片来源


法律层面的考虑


       针对民间借贷,《最高人民法院关于审理民间借贷案件适用法律若干问题的规定》已经明确制定了两线三区,即由24%和36%划分成的三个区域:24%以下的法律保护区,24%~36%的自然债务区和36%以上的无效区,需要注意的是,利息的计算涵盖了基础利息和各种服务费。

       因此在主流程中,如果资产方没有提供明确的利率范围,产品方也应将法律层面的利率规定考虑到系统中。比如在审核时,可以加入以下逻辑:

总利息 = 基础利率 + 各种服务费
//换算成年总利息
年总利息 = 年化(总利息)

年利率 = 年总利息 / 本金

if (年利率大于36) {
    返回错误并告警
}

返回成功

代码清单1

       在实际工程中,可以将36%做成配置项,根据实际需要灵活调整。


数学层面的考虑


       在系统运营一段时间之后,可以从数据库中提取出各种产品的历史利率,作为后续新产品利率的合理性的参考依据。

       假设有一个数据集,保存了最近1个月以来类型A的产品的利率样本,每天销售20个产品,利率从8.3%~9.8%之间,数据集总共有600个样本。此时要如何判断一个待上架A类产品的利率R是否合理呢?

//测试数据,生成600个随机数,范围在830~980之间
//在实际项目中,利率数据会相对集中,离散程度比随机数小很多
for (int i = 0; i < 600; i++) {
    System.out.println((int)(830 + Math.random() * 150));
}

代码清单2


       在统计学中,标准差反应了一组样本的离散程度,在本案例中,正好可以用于判断某个利率落入某个区间内的概率。我们定义算数平均值μ和标准差σ,则在正态分布中,落入μ±σ区间内的概率为68%(对应深蓝色区域),落入μ±2σ区间内的概率为95%(对应浅蓝色区域),落入μ±3σ区间内的概率为99%(对应灰色区域)。正态分布如下图所示:

c compile

图片来源


       在业务中,如果行情较好,接入的资产相对激进,对应的产品利率浮动较大,我们可以认为只要利率落入μ±3σ区间即可。反之,如果较为保守,我们只认为落入μ±σ区间的利率才是正常的。同样,落入哪个区间同样可以做成灵活的配置。


ABA问题

       ABA问题是原子CAS操作中一个潜在的问题。我们定义cas(n, a, b)为:

  • 对于一个变量n,当n等于a时,修改成b。
  • 当n不等于a时,返回失败。

       存在一种情况,某个线程b在当前线程a执行cas(n, a, b)之前,将n修改成x,再将n修改回a,而当前线程a无法感知变量n被修改过,这就是ABA问题。解决ABA问题通常需要额外的时间戳或者版本号。

       回到我们的场景里,首先需要明确的是,并非在所有时候都可以修改产品利率,在审核之前可以修改任意次产品利率,在审核之后则不允许修改。考虑这种情况:在审核操作执行时,判断利率是否合理之前,利率字段被人为地修改成其他的值,之后又被修改原先的值。这种情况有3种解决方案:

  • 不认为是bug。
  • 审核操作和修改标的利率的操作共用一个悲观锁,防止并发问题。
  • 在产品表里增加版本号,利用版本号实现乐观锁,防止并发问题。

管理层面的考虑

       除了上述问题之外,很多中小公司的开发者通常还会遇到这样或者那样的问题,比如“XX,这个sql很急,你先帮我执行我再补申请”,“这个bug很严重,先上线再说”。生产环境的代码和数据的发布流程混乱,难免会有各种各样的问题:

  • 因为bug导致产品审核之后还能被修改利率,并且恰巧有人修改了利率。如果系统设计时对关键操作保留了操作记录,可以启动一个事后处理线程,检测到这种误操作时,执行告警。
  • 用sql直接update数据库里的字段。这种情况下即绕过了业务上的检查,也不会对操作留痕,系统无法处理这类情况。

       针对上述的情况,笔者认为管理层有不可推卸的责任。操作流程不规范,权限管理不明朗,产品质量不达标,再优秀的系统设计也无法应对这些情况。