Flutter K线系统拆解(二):指标引擎与增量计算
目标
这篇只讲一件事:
如何把 K 线指标计算做成高性能、可维护、可测试的生产级引擎。
一、最佳实践架构:三层拆分
指标模块建议拆成三层:
IndicatorConfig(配置层):维护 MA/BOLL/MACD 等参数与开关IndicatorEngine(计算层):只负责输入数据、输出结果,不关心 UIChartAdapter(消费层):把结果喂给渲染,不做任何业务计算
核心原则:
- 渲染层只读结果,不参与计算
- 计算层纯函数化,避免隐式全局状态
- 配置变更和行情更新走不同刷新路径
二、指标计算顺序要固定
生产上要明确“依赖顺序”,不能随意调整。
推荐顺序:
- 输入
List<KLineEntity> - 计算
MA - 计算
BOLL(依赖 MA) - 计算
Volume MA - 计算
KDJ - 计算
MACD - 计算
RSI - 计算
WR - 计算
SuperTrend - 结果回填到每个
KLineEntity - 渲染层直接读取实体字段
建议把顺序写进统一入口方法,避免外部随意单独调用。
三、更新策略:全量与增量必须分流
这是性能分水岭。
1) 全量计算(初始化/切参数)
- 场景:首次加载历史数据、用户修改指标参数
- 策略:完整跑一遍指标链路
2) 增量计算(实时推送)
- 场景:WebSocket 新增一根,或更新最后一根未收线 K
- 策略:只重算受影响尾部区间,不重跑全历史
实践建议:
- 新增一根:走
addLastData(...) - 更新末根:走
updateLastData(...) - 配置变化:强制全量
不要混用,否则很容易出现“偶发指标跳变”。
四、算法实现的性能要点
1) 滚动窗口优先
均线、方差这类窗口指标,优先使用滑动累积变量。
例如 BOLL 的方差计算可维护 sumClose2,避免每步重新遍历窗口。
2) 避免重复数学开销
像 log/exp、格式化、字符串拼接都放在计算后或展示层,
不要在每帧、每点位重复做。
3) 结果就地回填
计算结果直接写回数据实体,渲染阶段只读取。
这样可避免渲染期再做派生计算。
五、配置管理最佳实践
推荐做法:
- 配置快照注入计算器,不直接依赖可变全局 Map
- 参数改动有明确版本号,触发全量重算
- 配置存储只负责持久化,不直接触发 UI 与计算耦合逻辑
六、并发与线程策略
当数据规模大到几千到上万根时,建议:
- UI 线程只做增量计算
- 参数变更触发的全量计算下放 isolate
- 计算完成后一次性回传结果快照,避免碎片化 setState
七、测试与验收标准
指标引擎必须有自动化对照测试,至少覆盖:
- 全量计算结果一致性(固定输入 -> 固定输出)
- 增量与全量一致性(同一尾部数据,结果相同)
- 边界数据(空数据、单点、超长窗口、异常值)
- 参数变更后的重算正确性
工程上建议补两类基准:
- 10k 数据全量耗时
- 高频实时更新(例如 10Hz)下的帧稳定性
八、常见错误清单(建议直接排查)
- 每次推送都全量重算
- 计算层读写全局可变状态
- 渲染阶段临时算指标
- 参数变化不触发全量重算
- 增量路径未覆盖“更新最后一根”场景
总结
指标模块的最佳实践可以压成三句话:
- 计算顺序固定,渲染只消费结果
- 全量与增量分流,实时更新永不全量
- 配置注入与自动化测试先行,保证可维护和可验证
把这三条做到位,K 线在复杂业务下也能稳定、可控、可扩展。