一个保险领域通用计算引擎的设计

尼克徐 发布于 2014年05月09日 | 更新于 2016年04月15日
cnsoft 等1人欣赏。

我近期正在做的一个东西,是维护和扩展一个保险方面的计算引擎。

根据被保险人的年龄,性别,所选产品等的情况,来计算保费,以及保单利益等,最后产生报表(报表在几页到十几页之间)。

这个引擎采用c语言,部署到ios平台和windows平台。

几十种产品,上百个数据表,中间的计算也很复杂。

以前的计算引擎代码,有十几年历史,除了一些数据表外,各种全是硬编码。

近三十万行代码,一旦出错或改动,只能debug。多么痛苦的事情。

而且最近还要改成为另一个国家用的。于是我要做一个重构(除了重用数据以外,其实是推倒重来...)。

目前已经完成主要部分,还有一些正在编程中。

在这里分享一下基本思路。(待续)

共39条回复
LLeiFeng 回复于 2014年05月09日

期待更新...

尼克徐 回复于 2014年05月09日

重构引擎的目的

1, 尽量把计算保费和各种数据的步骤和输入输出等,都放在配置文件里。减小开发新保险产品时的代码量(最终减小到零代码量)。

2, 提高开发效率和维护效率100%-200%

3,最终目标:非专业人士在短期培训后,就可以用ipad所见即所得配置计算引擎,配置好后,把配置文件email给开发人员。一个新的保险产品的计算引擎部分就完成了。

以下是设计框图,解释待续。

alt text

tinyfool 回复于 2014年05月09日

赞,虽然这个行业我不懂,但是我觉得你做的事情很有意思,也有挑战,加油

淮左青衣 回复于 2014年05月09日

相当于自己实现一个公式分析器?最早之前在做电信产品的时候,我们的PM设计过类似的一个东西。设计师用IBM的rose设计业务流程图,然后用code去分析Rose的文件(某个旧版本的rose生成的文件好像是普通文本,可以用code直接分析,高版本的就不行了),生成我们自己的业务规则,然后用自己的引擎去跑这一条条的规则。

最后实现的效果就是业务设计人员50%+的改动不需要程序员的参与就可以完成了,而且无需等待部署:)

Silence 回复于 2014年05月09日

感觉各种测试可能是关键。

尼克徐 回复于 2014年05月09日

一,Engine Calculation (引擎计算部分)

这一部分是这个引擎的核心部分。

因为所有的保费和报表计算部分,都可以用excel来模拟(在我们这里叫做mock up),所以我就做了一个excel的解释执行器。

先把excel转成xml,然后用c语言解释执行。

一段excel转的xml:

<tr>
        <td type="double" index="A16"><![CDATA[1.0]]></td>
        <td type="string" index="B16"><![CDATA[Accidental Death & Dismemberment]]></td>
        <td type="expression" index="C16"><![CDATA[IF($C$7="Y","PP","PW")&"I"&K16]]></td>
        <td type="expression" index="D16"><![CDATA[VLOOKUP($C$5&"I"&$D$7&C16&$C$6,sc_premium!A3:E546,4,0)]]></td>
        <td type="expression" index="E16"><![CDATA[VLOOKUP($C$5&"I"&$D$7&C16&$C$6,sc_premium!A3:F546,5,0)]]></td>
        <td type="expression" index="F16"><![CDATA[VLOOKUP($C$5&"I"&$D$7&C16&$C$6,sc_premium!A3:F546,6,0)]]></td>
        <td type="expression" index="G16"><![CDATA[TRUNC(E16*F16/$F$14,4)]]></td>
        <td type="expression" index="H16"><![CDATA[ROUND($G16*H$13,2)]]></td>
        <td type="expression" index="I16"><![CDATA[ROUND($G16*I$13,2)]]></td>
        <td type="expression" index="J16"><![CDATA[ROUND($G16*J$13,2)]]></td>
        <td type="string" index="K16"><![CDATA[A]]></td>
        <td type="string" index="L16"><![CDATA[]]></td>
        <td type="string" index="M16"><![CDATA[]]></td>
        <td type="string" index="N16"><![CDATA[]]></td>
        <td type="string" index="O16"><![CDATA[]]></td>
        <td type="string" index="P16"><![CDATA[]]></td>
        <td type="string" index="Q16"><![CDATA[]]></td>
</tr>
尼克徐 回复于 2014年05月09日

为了方便开发者查看excel在ios上的执行情况,我在ipad上做了一个excel的ui编辑界面。

alt text

最麻烦的地方就是查错(我不希望用debugger),尤其是excel的公式执行时出错。所以我在公式的错误检查上下了不少功夫:

alt text

以及公式的单步执行:

alt text

尼克徐 回复于 2014年05月09日 | 更新于 2016年04月15日

这个计算模块已经重构了两次,比较成熟了,经历了10多个保险产品的考验,性能和结果准确性满足要求。

做这个模块时所遇见的问题及其解决方案。

1, 多平台部署时的内存问题。

这个引擎部署在ios上使用时效果很好,但转成dll后部署到windows平台,就会crash.其主要问题在于对string的内存释放时出错。目前虽已经稳定,总觉得string的alloc和释放还有些不规矩。正在考虑用stringtable来整体解决string的alloc和释放。

2, 性能问题

最开始时,这个计算引擎只能把excel表格从头到尾计算一遍来获得结果。

当excel表格很大时(700-1000行以上),整个计算一遍速度就太慢了。可以很明显看到延迟。

现在这个引擎已经能做到局部计算,不用计算全表,只计算所需要的量。根据所求的量的公式,来追溯公式里所需的其他变量(跨表),而其他变量又追溯另外的变量,引起连锁反应而获得此数值。经过这方面的加强,性能有了很大提高。

有些数据的计算,需要大量的相关表格,此时,可以把相关表格的计算结果做成一个表格来使用,就不需要引用那么多表格了。

目前的计算引擎是解释执行的,最近学了一些编译原理,有想法把excel转成汇编语言执行,性能想必又会有提高...

moulton 回复于 2014年05月09日

听这就头晕。。。。

尼克徐 回复于 2014年05月09日

3楼 @tinyfool 谢谢!

4楼 @淮左青衣 公式分析器肯定是其中一部分啦。你说的那个产品很有趣啊。

5楼 @Silence 测试,测试,再测试!

9楼 @moulton 如果你看到以前的引擎的一堆shit代码时,头可以更晕一些...

Palm 回复于 2014年05月09日

假如 车险产品: 需要根据 车龄 行驶里程 贷款 过户 优惠系数 浮动比率 以及险种依赖 等 因素 这个是不是就基本扛不住了?? 似乎你这个只能适用普通险种 比如旅游 类非车险产品 是吗??

尼克徐 回复于 2014年05月09日

11楼 @Palm

我这个是通用引擎,目前计算寿险和意外险为主。

这么说吧,用c或c++计算能瞬间出结果的,这个引擎也能瞬间出结果。有差异但可以小到忽略不计。

该险种的excel表格能瞬间出结果的,这个引擎也能瞬间出结果。

如果这个险种用c语言编计算引擎,都需20-30秒才能计算出结果,用这个引擎不可能更快。

adad184 回复于 2014年05月09日

不得不说.. 7楼那个图 看了就没有用的欲望了..

尼克徐 回复于 2014年05月09日

13楼 @adad184 不用说你,我也没有用它的欲望:-) 因时间紧张这个界面做的没那么友善。诸如调整行列宽,字体等功能待添加。

表格里公式和数据是精算师对某保险产品做的一个mock up,原封不动的搬到ipad上显示。保险公司的人整天跟这些打交道,习惯了。

相比直接去编c程序,肯定这个要受欢迎的多。

enno 回复于 2014年05月10日

7楼 @尼克徐 我最近也做excel在ipad上的浏览和编辑,觉得工作量太大,后来就买了一个产品,用html+json来实现,完美匹配excel。

尼克徐 回复于 2014年05月10日

15楼 @enno 谢谢这个信息。我目前做的主要是把excel里的公式提取出来计算,浏览和编辑什么的只是附带。

enno 回复于 2014年05月10日

16楼 @尼克徐 嗯,公式和样式是大问题,所以购买产品之前花了大时间去测试,基本上没有问题。就是加载的excel数据比较大,会有load时间。

Palm 回复于 2014年05月11日

12楼 @尼克徐 恩恩 道理我同意 但是你这个是适用于客户需求自动变更吗? 如你所说 解析excel 然后计算 这个 excel 很繁琐 你会不会很繁琐以至于 专业才能看懂和配置?

尼克徐 回复于 2014年05月11日

18楼 @Palm 我理解你的意思。

我们这里的excel是由精算师做的,整体看上去满复杂的。

但是,测试人员虽然不懂程序,也是可以看懂这个excel,并比照excel测试app的正确性。

测试人员许多都是刚从大学毕业的,来这里实习。一周培训后就上岗了。

我相信,通过短期培训,客户会掌握根据需求修改excel并适配到app中去。

@Palm 看来你也是这个行业内的啊,呵呵。能否介绍一下你那方面?谢谢。

cnsoft 回复于 2014年05月11日

@尼克徐 计算的事让服务器端来会不会好一些. 现在不都是Cloud based的么.

尼克徐 回复于 2014年05月11日

20楼 @cnsoft 这是公司的决策了。

优点是,ipad离线客户端,只有在submit数据时才连后台,用户体验很好。

另外,其实在server端也同样运行这一套,是为了支持在银行系统的销售终端(出于安全等方面考虑,银行只能用web界面不希望用ipad)。那就是用java做web编程并调用服务器上的dll了。

尼克徐 回复于 2014年05月11日

二,Input Data Mapping 和 Output Data Mapping

Input Data Mapping 是把保存在结构体里的用户数据,传递到计算引擎中去。

Output Data Mapping 则是把计算结果传递出来,保存到相应结构体变量,格式化等等规整后,便于传递到服务器端以及报表引擎去。

这一部分别看只是数据的传递,实现起来却是很麻烦的。

实际上我今天还来公司加班,正在对这部分做一次重构。

以前的实现,只是简单的硬编码,写set value/get value的方式,把数据向计算引擎里传,或者传出来。

随着所做的product越来越多(时间紧任务重,半成品的engine也要用上啊),发现了弊病:

1, 每个product大体相同,但set value/get value处却总有些微妙区别,导致要根据产品不同加条件判断,增加输入输出语句。这就又逐渐增加了“垃圾代码”。

2,还有的在输入数据时,还需要engine进行验证,有的要输入整个结构体数组,有的输出时要进行结构体数组的初始化....

林林总总的小而麻烦的地方,导致引擎代码在输入输出部分,补丁片片。

可以想象,一两年后,这里就是个大坑了。

目前正在做的重构是,使用类似hibernate的概念,写一个属性匹配的xml,并动态识别struct的属性,来进行自动的输入输出匹配,与此同时进行属性的验证。

尼克徐 回复于 2014年05月11日

忽然发现自己,混入社区十大英雄了,激动一下!

咳咳,感谢各位给我的鼓励与支持,今后一定再接再厉...

tinyfool 回复于 2014年05月11日

23楼 @尼克徐 加油

尼克徐 回复于 2014年05月11日

24楼 @tinyfool 谢谢!

cnsoft 回复于 2014年05月12日

21楼 @尼克徐 很帅. 这种应用 真是体现了程序的价值. 程序就是生产力啊. 好多年以前我门做过基于excel的所谓万能报表. 真没这么高级, 十大英雄 有我赞的功劳 哈哈

尼克徐 回复于 2014年05月12日

26楼 @cnsoft 谢谢鼓励!

我就是这么想的,让程序多干点,让人少干点。把程序的生产力“压榨”出来,解放专业人员。

amosji 回复于 2014年05月12日

谢谢分享 读了之后大开了眼界 期待更新
不过你的公司允许你把产品的设计思路放出来吗?还是说这是个开源引擎?
如果是开源的话,可以问下source code放在哪里吗?

LiYing 回复于 2014年05月12日

1, 公式或脚本或程序,解析可以使用 Antlr http://www.antlr.org/

2, 内部数据模型向别的表现形式的转换 比如Data Model =>XML,或Data Model => Excel的 可使用模板库 FreeMarker http://freemarker.org/

3, 不赞同以Excel作为数据模型 数据模型应该结构化,便于程序处理,也便于在各模块或各子系统之间传递 Excel应该理解为根据数据模型产生的用户表现层,也就是 View 层 这部分没能完全理解楼主的意图,如果有理解错误,勿怪

尼克徐 回复于 2014年05月12日

29楼 @LiYing

1,关于antlr, 现在这个引擎用的是非常老旧的vc6.0作为环境,无法搭载此框架。

2, 这个模版库Freemarker有趣,我会好好看看。谢谢!以后可能会用上。

excel转xml我写了个很短的程序,效果还不错。

3,Excel是作为计算引擎存在的,具体的输入输出数据,在程序里是结构化的,并被传入excel计算引擎去计算和传出来。

该excel对于用户来说不可见,用户看到的是另一些友好的界面。

尼克徐 回复于 2014年05月12日

28楼 @amosji 思路还是可以放出来的,代码肯定不能。

LiYing 回复于 2014年05月13日

30楼 @尼克徐 1, 如果是 VC6 的话,要查一下antlr能否输出 VC6 代码,我估计是可以的输出标准 C 或C++的,这样在 VC6 中就可以用了

2, Freemarker 是 Java 库,你的 VC6 应该用不了

需要找 C 或 C++ 下的模板库

但思路是一致的,这部分工作尽量交给模板库,不要自己造轮子

尼克徐 回复于 2014年05月13日

32楼 @LiYing 非常感谢!会借鉴你的思路。

我有时候会用到第三方库,有的因内存或环境限制而不合适,有的很好用。

从头造轮子,可能有以下原因:

1,没有意识到有合适的轮子存在(这个最不应该,是我需要警觉的地方)

2,自己这个轮子需要有太多调整。

3,市面上的轮子太“重”和“胖”了,不适合自己用。

4,运行环境和开发语言等的限制

我在这里面用的parser,不是antlr,但也是借鉴了一个开源代码w3parser,

只因该parser的执行速度很快,错误检查和单步执行做的特别好。是java的,我转成了c,非常好用。

excel转xml,用到java里的很多lib,也很好用。

尼克徐 回复于 2016年04月05日

转眼两年过去了,这个引擎虽不太完善,但已经成功覆盖了所有意外险种的计算,占公司险种的30%,大大降低了险种计算引擎的开发难度。

近日,会重新启动该项目。

此次会写一个桌面应用给保险公司的精算师和开发人员用。计划用C++写(兼容以前的C代码)。

其目的是达到完全不需要写代码,就能配置好意外险险种的计算引擎的程度。

而这个应用,其设置方式并不只适用于意外险,会尝试用于其他险种的计算引擎生成。

Palm 回复于 2016年04月07日

加油, 我很想看看 你是怎么处理车险产品的计算引擎 , 因为车险这块保费计算因子比非车险产品的计算因子多好几个数量级,商业险各责任的计算方式也不仅相同 , 我觉得很费劲!

尼克徐 回复于 2016年04月07日

35楼 @Palm 恩,我对车险之类的也很感兴趣。

等我开发的差不多了,能共享我一些车险的mock up供我测试一下不?

数据可以不对,其计算模式差不多就行。

我这个引擎,原则上是为所有的保险产品设计的...

Palm 回复于 2016年04月12日

36楼 @尼克徐 ok 我到时候找人试试看

尼克徐 回复于 2016年04月13日

37楼 @Palm 谢谢!

Palm 回复于 2016年04月15日

38楼 @尼克徐 不客气

本帖有39个回复,因为您没有注册或者登录本站,所以,只能看到本帖的10条回复。如果想看到全部回复,请注册或者登录本站。

登录 或者 注册
[顶 楼]
|
|
[底 楼]
|
|
[首 页]