搭建自己的 K 线回测平台:从数据源、K 线组件到策略信号
上一篇文章里,我们理清了整体的架构思路,和我们需要完成的模块设计,这一篇我们将着重分析二者系统之间的数据交互方案。
LLM 系统很适合做投研、新闻分析、市场情绪判断和风险解释,但如果我要真正测试一个交易策略,第一步还是要回到最基础的问题:
- 我的数据从哪里来?
- 我的 K 线图怎么展示?
- 我的技术指标怎么算?
- 我的买入信号如何生成?
- 我的卖出规则是什么?
- 如果放到历史数据里执行,结果会怎么样?
所以这一篇文章主要进行说明如何搭建自己的第一版 K 线回测平台。
这个系统的目标不是一上来就实现自动交易,而是先跑通一条最基础的链路:
ETF 标的管理 → 日 K 数据获取 → K 线展示 → 技术指标计算 → 技术信号生成 → 模拟买入 / 卖出 → 回测结果统计。
一、先确定回测平台第一阶段要做什么
在第一阶段,我们不需要把系统做得太复杂。
因为对个人开发者,尤其是假如自己是金融新手来说,一开始最容易犯的错误就是:还没把一个简单策略测清楚,就开始堆很多复杂功能。
比如一上来就做:
- 自动交易
- 券商 API
- 高频行情
- 分钟级 K 线
- 多策略组合
- 多 Agent 决策
- 持仓管理
- 实盘资金同步
这些东西听起来很完整,但对第一阶段来说反而容易分散注意力。
所以我们的第一版回测平台只需要完成下面几个核心功能:
- 添加 ETF 标的
- 获取 / 导入日 K 数据
- 展示 K 线图
- 计算技术指标
- 生成技术评分和信号
- 用信号做模拟买入 / 卖出
- 查看持仓、交易明细、资金曲线、收益率统计
有了这条链路之后,就可以测试一些最基础的策略,比如:
- MA 趋势策略
- RSI 过滤策略
- 成交量确认策略
- 均线突破策略
- 简单止损止盈策略
先把简单策略测清楚,再考虑复杂策略。
二、K 线图组件:KLineCharts
既然是 K 线回测系统,首先我们需要有一个 K 线图组件。
这里我选择的是 KLineCharts。
GitHub 地址:
https://github.com/klinecharts/KLineChart
这是一个我很早之前就开始关注和使用的 K 线图组件,GitHub 上目前有着3.7K🌟。
我选择它的原因主要有几个:
- 它专门面向 K 线场景,而不是普通图表库硬凑出来的 K 线图。
- 支持蜡烛图、成交量、指标线等常见金融图表能力。
- 前端集成相对清晰,适合和 Vue 项目结合。
- 对个人回测系统来说,功能已经足够。
上面都是扯淡的,主要是免费。
在我们的系统里,KLineCharts 主要负责展示:
- 日 K 蜡烛图
- 成交量
- MA5 / MA10 / MA20 / MA60
- 买入信号点
- 卖出信号点
- 回测交易标记
也就是说,它不是单纯为了“好看”,而是为了让我能直观看到策略信号到底出现在什么位置。
比如系统给出一个 BUY_CANDIDATE,我希望在图上能看到:
- 当时价格是不是在 MA20 上方?
- MA5 和 MA10 是否形成短期向上趋势?
- RSI 是否已经过热?
- 成交量有没有配合?
- 后续几天价格有没有继续上涨?
- 是否很快触发止损?
这些都需要通过 K 线图来辅助判断。
三、数据源选择:AData + Tushare
有了图表组件之后,第二个问题就是数据源。
不管是股票还是 ETF,实时行情数据都不是一个特别容易免费获取的东西。尤其如果要稳定、长期、实时、分钟级数据,通常都会涉及付费或限制。
但我们当前阶段并不需要高频实时行情。
我的第一阶段目标是做日 K 回测,所以日 K 数据就够了。
因此我选择了两个数据源作为基础:
1. AData
GitHub 地址:
https://github.com/1nchaos/adata
AData 是一个面向 A 股数据获取的开源项目,可以用于获取一些常见的股票、指数、ETF 等数据。
对个人测试来说,它的优势是:
- 使用成本低
- 接入相对简单
- 适合本地测试
- 对 A 股数据比较友好
2. Tushare
官网地址:
Tushare 在国内量化数据圈里比较常见,很多人应该都知道。
它的优势是:
- 数据类型比较丰富
- 文档相对完善
- 生态成熟
- 适合做历史数据获取和策略研究
当然,Tushare 的部分能力会受到积分或权限影响,但如果只是个人学习、测试和搭建第一版系统,很多免费能力已经够用了。
所以在第一阶段,我们的数据源策略是:
先保证能稳定拿到日 K 数据,不追求一开始就拿到最完整、最实时、最高频的数据。
四、为什么第一阶段只做日 K
很多人一做交易系统,就想直接做分钟级 K 线,甚至实时行情。
但我现在不打算这么做。
原因很简单:日 K 更适合第一阶段策略测试。
日 K 的好处是:
- 数据量相对小
- 计算简单
- 回测速度快
- 信号更稳定
- 不容易陷入高频噪音
- 更适合新手理解趋势
而分钟级数据会带来很多额外复杂度:
- 数据量巨大
- 清洗成本高
- 缺失和异常更多
- 回测速度变慢
- 更容易受到噪音影响
- 更接近短线交易,对新手并不友好
所以我们现在只做:
ETF 日 K 回测。
这个范围已经足够完成第一版系统验证。
五、回测平台的核心模块
我们的回测平台暂时命名为:
personal-kline-assistant |
技术栈大致是:
后端:Spring Boot + MySQL |
第一阶段核心模块如下。
1. 标的管理
用于维护我们需要测试的 ETF 或股票。
比如:
510300 沪深300ETF |
不过第一阶段不建议标的太多。
建议倾向于先测试 1 到 2 个 ETF,把流程跑通之后再扩展。
2. 日 K 数据管理
日 K 数据包括:
- 日期
- 开盘价
- 最高价
- 最低价
- 收盘价
- 成交量
- 成交额
系统需要支持两种方式:
- 从数据源拉取
- 从 CSV 导入
为什么还要保留 CSV 导入?
因为在开发和测试阶段,CSV 是最稳定、最容易调试的方式。
如果数据源接口挂了,或者字段变了,我仍然可以用 CSV 继续跑系统逻辑。
3. 技术指标计算
第一版指标不需要太多,我主要保留几个基础指标:
- MA5
- MA10
- MA20
- MA60
- RSI14
- ATR14
- 成交量比率
这些指标足够支撑第一阶段的趋势策略测试。
比如:
close > MA20 |
这些条件组合起来,就可以生成一个基础的 BUY_CANDIDATE 信号。
4. 技术评分
为了让系统有统一标准,我们需要设计一个技术评分。
比如:
- 收盘价在 MA20 上方,加分
- MA5 > MA10,加分
- MA10 > MA20,加分
- RSI 在合理区间,加分
- 成交量确认,加分
- 价格没有远离 MA20,加分
最终得到一个 0 到 100 的技术评分。
这个分数不是为了预测未来,而是为了让系统稳定判断:
当前这个标的是否满足我的技术观察条件。
5. 技术信号
根据技术评分和指标条件,系统生成几个信号:
| 信号 | 含义 |
|---|---|
| BUY_CANDIDATE | 可以进入买入候选 |
| WATCH | 可以观察,但条件还不够强 |
| NEUTRAL | 没有明显机会 |
| AVOID | 不适合买入 |
| SELL_WARNING | 如果已有持仓,需要警惕 |
这里我特别强调:
BUY_CANDIDATE 不是买入命令,只是买入候选。
它只是说明技术条件比较好,可以进入下一步观察。
真正是否模拟交易,还需要结合止损、止盈、仓位、市场环境和后续 LLM 风险过滤。
6. 模拟回测
有了信号之后,就可以做最简单的模拟回测。
比如规则可以是:
买入: |
回测系统需要输出:
- 每笔交易记录
- 买入日期
- 卖出日期
- 买入价格
- 卖出价格
- 单笔收益率
- 总收益率
- 胜率
- 最大回撤
- 权益曲线
- 当前持仓
这些指标能帮助我判断:
这个信号到底有没有测试价值。
六、一个完整回测链路长什么样
从系统角度看,完整链路大概是:
添加 ETF 标的 |
这就是第一阶段回测系统的核心。
它不复杂,但很关键。
因为只有这条链路跑通之后,我才知道:
- 数据是否可用
- 指标是否正常
- 信号是否合理
- 回测是否可复现
- 策略是否值得继续观察
七、为什么还要接入 LLM 系统
做到这里,其实已经有了一个完整的 K 线回测系统。
但它还有一个问题:
K 线只能告诉我价格发生了什么,不能告诉我为什么发生。
比如某个 ETF 出现了 BUY_CANDIDATE,技术上看起来不错。
但我还想知道:
- 当前大盘情绪怎么样?
- 有没有政策风险?
- 有没有重大新闻?
- 外围市场是否大跌?
- 资金面是否支持?
- 这次上涨是不是只是短期反弹?
- 这个信号最可能失败在哪里?
这些问题就更适合交给 LLM 情绪分析系统。
所以我们后续会把 daily_stock_analysis 接进来。
不过它不是用来直接替我买卖,而是做风险过滤。
八、为什么把 LLM 系统作为外部分析服务
daily_stock_analysis 本身已经有:
- FastAPI 服务
- 分析接口
- 任务状态接口
- 历史报告接口
- Agent 接口
- 行情 / 回测 / 配置接口
所以它天然适合作为一个外部分析服务。
而我的回测系统是 Spring Boot,LLM 系统是 FastAPI,那么最自然的通信方式就是:
Spring Boot 通过 HTTP 调用 FastAPI。
这样做有几个好处:
- 两个系统保持独立。
- 不需要互相侵入源码。
- LLM 系统更新时影响较小。
- Java 系统只关心输入和输出。
- 调试方便,可以直接用 Postman / curl 测试。
- 以后换模型或换 LLM 项目,不影响 K 线系统核心逻辑。
这点对我很重要。
因为 daily_stock_analysis 是一个 GitHub 开源项目,我希望后续还能持续拉取原作者更新。
如果我把大量自己的逻辑写进它的核心代码,以后更新会很麻烦。
所以第一阶段我选择:
尽量不修改 LLM 项目核心代码,而是通过 HTTP API 进行通信。
九、K 线系统和 LLM 系统的数据流
整合后的数据流是:
- K 线系统生成
technical_signal。 - 判断
signal_type是否为BUY_CANDIDATE。 - 如果不是
BUY_CANDIDATE,不调用 LLM。 - 如果是
BUY_CANDIDATE,K 线系统调用 LLM 系统 API。 - LLM 系统返回:
sentimentScoreriskScoreriskLevelsummarypositiveFactorsnegativeFactorsriskFactorscontrarianViewrawReport
- K 线系统保存到
ai_analysis_snapshot。 - K 线系统根据规则生成
final_trade_decision。 - 前端展示最终结果。
这条链路的重点是:
K 线系统负责产生候选,LLM 系统负责分析风险,最终决策仍然由 K 线系统生成。
十、为什么不直接使用 LLM 系统原始接口
虽然 daily_stock_analysis 有很多接口,但我不想让 K 线系统直接依赖它复杂的原始返回结构。
原因是:
- 原始字段可能很多
- 结构可能偏向 LLM 系统内部使用
- 后续开源项目更新时字段可能变化
- K 线系统并不需要所有信息
所以我们需要在 LLM 系统外面封装一个稳定的适配接口。
例如:
POST /api/quant/etf-risk-analysis |
请求体可以设计成:
{ |
响应体可以设计成:
{ |
这样我的回测系统只需要适配这个稳定 JSON 即可。
十一、在回测系统中封装 LlmAnalysisClient
接下来我们需要一个客户端来和llm系统进行交互,我们需要回测系统里封装一个客户端:
llm |
这里的职责划分是:
LlmAnalysisClient 只负责 HTTP 调用 LLM 系统。
AiAnalysisService 负责判断是否需要调用、读取缓存、保存结果、生成最终决策。
也就是说,Controller 不直接调用 LLM。
业务逻辑统一放在 Service 层,HTTP 通信统一放在 Client 层。
这样以后如果 LLM 系统接口变了,只需要改 LlmAnalysisClient 和 DTO,不会影响整个回测系统。
十二、缓存规则:同一天同一标的不重复调用
LLM 调用有成本,也可能会比较慢。
所以我们还需要加一个缓存规则:
同一个
symbolCode + analysisDate只分析一次。
也就是说,如果今天已经有:
510300 + 2026-05-04 |
的 AI 分析结果,就不需要重复调用 LLM。
直接从 ai_analysis_snapshot 表中读取即可。
流程如下:
- 检查
ai_analysis_snapshot是否已有今日结果。 - 有则直接返回。
- 没有才调用 LLM API。
- 调用成功后保存。
- 调用失败则保存失败状态,并返回保守决策。
这可以避免频繁调用 LLM,也能让前端重复刷新时不会浪费资源。
十三、失败降级:LLM 挂了也不能影响 K 线系统
还有一个非常重要的问题:如果 LLM 系统挂了怎么办?
比如:
- LLM 服务没启动
- FastAPI 超时
- 模型调用失败
- 网络异常
- 返回字段异常
这时候不能让整个 K 线系统崩掉。
所以要设计了一个保守降级规则:
如果 LLM 调用失败: |
也就是说:
不因为 LLM 调用失败就默认允许交易。
这点很重要。
因为 LLM 在我的系统里是风险过滤器。
如果风险过滤器不可用,那么最保守的处理就是继续观察,而不是放行。
十四、最终决策由 K 线系统生成
整合之后,最终决策会保存在 final_trade_decision 中。
我不让 LLM 系统直接生成最终交易动作。
原因是:
- K 线系统掌握技术信号。
- K 线系统掌握策略规则。
- K 线系统掌握回测和模拟交易。
- LLM 系统只提供外部风险输入。
所以两个系统的边界是:
LLM 系统: |
这个边界越清晰,后续系统越容易维护。
十五、最终集成架构
最终得到的集成架构大概是:
personal-kline-assistant Spring Boot + Vue + MySQL |
一句话总结:
用 HTTP API 保持系统边界,用 MySQL 保存分析结果,用 K 线系统做最终决策。
这就是我当前认为最适合第一阶段的整合方式。
十六、当前阶段的成果
到这里,我们其实已经完成了一个简单但完整的回测系统设计。
它具备:
- ETF 标的管理
- 日 K 数据获取 / 导入
- K 线图展示
- 技术指标计算
- 技术评分
- 技术信号生成
- 模拟回测
- 交易明细
- 权益曲线
- 收益率统计
- 与 LLM 系统的数据通信设计
- AI 风险分析结果缓存
- 最终决策生成
这已经是一条比较完整的个人量化辅助链路。
当然,它还不是一个自动交易系统,也不是一个能保证盈利的系统。
它更像是一个交易测试实验台。
我可以用它不断测试:
- 某个技术信号有没有价值
- 某个 ETF 是否适合趋势策略
- 止损止盈设置是否合理
- LLM 风险过滤是否能减少错误信号
- 回测结果和实时模拟是否一致
这比一开始直接问 AI “能不能买”要踏实得多。
十七、总结
这一篇主要记录了如何进行搭建和设计我们的第一个回测平台,并且是如何通过http交互来进行两个系统之间的数据调度。
整体思路是:
- 用 KLineCharts 做 K 线图展示。
- 用 AData / Tushare / CSV 解决日 K 数据来源。
- 用 Spring Boot + MySQL 管理数据和回测逻辑。
- 用 MA / RSI / ATR / 成交量生成基础技术信号。
- 用模拟买入 / 卖出验证策略效果。
- 用 HTTP API 接入 LLM 系统做风险过滤。
- 用 MySQL 保存 AI 分析结果和最终决策。
这一阶段最重要的不是“马上赚钱”,而是先把完整链路搭起来。
因为只有链路跑通之后,后面才能继续做:
- 虚拟资金测试
- ETF 策略验证
- 交易计划模块
- 仓位计算
- 交易日志
- K 线策略和 K 线 + LLM 策略的对比测试
最后展示一下当前平台的模样,和数据的获取方式是否达到了我们效果,我们来测试获取一个沪深300的ETF华泰柏瑞的数据来进行指标和信号的生成:
所有的技术指标和信号都获取正常,包括k线的数据。
下一篇,就是激动人心的时刻,我们会开始使用虚拟资金,对一个简单的 ETF 策略做模拟测试。
来测试:
如果系统给出 BUY_CANDIDATE,我按规则模拟买入,再按止损止盈规则卖出,结果到底会怎么样?

交流与讨论
评论由 Valine / LeanCloud 提供,点击后连接第三方服务。