ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring Boot+Vue量化交易系统实战:从行情采集到信号推送

Spring Boot+Vue量化交易系统实战:从行情采集到信号推送 简介基于Spring Boot与Vue.js技术栈的量化交易系统完整源码面向正在学习金融科技、微服务架构或前后端分离开发的Java工程师与前端开发者。后端以33个Java类文件实现交易业务逻辑、RESTful接口服务、数据访问与策略回测等核心功能前端通过8个Vue组件与8个JS文件完成实时行情展示、K线图表绘制、订单管理和策略参数配置等交互界面辅以XML、properties及JSON配置支撑数据库连接、项目构建与运行参数设置。资源共67个文件压缩包约506KB项目结构规范清晰包含标准Maven目录、前端工程、静态资源、数据库配置及README说明文档便于快速导入与二次开发。已有1059人下载学习对于希望从零理解量化交易全链路、参考Spring Boot与Vue前后端整合方式的开发者完整源码覆盖从市场数据获取、交易信号计算到订单执行的核心步骤可直接作为课程设计、毕业设计或金融科技项目的起步模板。1. 直接用springbootvue搭量化交易系统先想清楚这三件事拿到一个springbootvue的量化交易系统源码.zip第一反应通常是解压、导入IDE、跑起来看效果但这个动作往往在高版本JDK和Node依赖上卡住一小时。量化交易系统跟普通CRUD后台不一样的地方在于它的价值在数据链路和策略触发行情从哪来、信号怎么算出来、算出来之后怎么推给前端并且不丢单。springboot负责把行情采集、策略计算、订单管理串成一条可靠的后端流水线vue负责把行情、持仓、信号变成随时能看的操作台。这套组合本身不产生策略收益但它决定了策略能不能稳定跑、能不能回测、能不能在盘中被监督。适合的人群是做量化策略但缺工程化能力的人以及想从单机脚本升级成前后端分离系统的后端开发者。这篇文章按我实际搭这套系统的顺序写先定模块边界再写后端关键代码再写前端信号展示最后落到拿到源码zip后怎么跑通和改造成自己的。2. 量化交易系统的模块边界与springboot侧的数据骨架量化交易系统跟爬虫加Excel的区别在于状态管理和时序一致性。行情是不断追加的时间序列策略是定期计算信号订单是有状态流转的实体风控是对异常状态的拦截。把这四类东西放到springboot里先别急着写代码先把边界划清楚。2.1 为什么前后端分离的springbootvue组合适合量化交易常见做法是单机脚本跑策略另一个脚本画图两个脚本用文件或数据库对消息。方案不是不行但到了要加回测、加参数配置、加多账户管理的阶段没有前端操作界面每改一次参数都要改代码重启策略本身和工程代码耦合在一起。springbootvue的最大价值是把策略计算和策略操作分离后端专注行情接入、计算、风控、记录前端专注看K线、改参数、看信号、看持仓。分离之后的接口边界其实很清晰前端需要的数据无非是行情序列、策略信号、账户持仓、历史交易记录后端把这些做成REST接口加一个WebSocket推送通道就够用了。2.2 六个核心模块的职责划分与数据库表设计我一般会把系统拆成六个模块行情数据源、行情存储、策略引擎、信号推送、订单管理、风控。每个模块在springboot里对应一个独立Service在数据库里对应一组表。先看模块职责行情数据源负责对接交易所或数据服务商的API定时拉取K线行情存储负责将K线落到数据库策略引擎从库里取行情计算指标生成买卖信号信号推送把信号通过WebSocket发给前端订单管理记录模拟或实盘下单风控负责过滤异常信号比如价格偏离过大、连续重复下单、持仓超限。这六个模块里行情数据源和策略引擎是最容易写耦合的很多人图省事在策略里直接调行情API表面看少了一层实际上一换数据源就要改策略代码后续维护成本很高。对应到数据库可以设计成这四张核心表表名核心字段用途kline_datasymbol, period, open, high, low, close, volume, ts存储K线symbol加period做联合索引strategy_signalsymbol, strategy, action, price, ts, status策略信号status区分已推送/已成交/已取消account_ordersymbol, action, quantity, price, status, create_ts订单记录strategy_paramstrategy, param_name, param_value策略参数前端改参数写这里表设计里最值得注意的一点是kline_data不要按天分表量化策略回测时经常要跨几个月取数据单表加联合索引在千万级别内性能完全够用。不要为了看起来高级引入复杂的分片后期维护成本远大于收益。strategy_signal表一定要冗余symbol和action不要通过join去查K线表因为策略信号是高频写入低频查询独立表能让排查问题时分外清爽。2.3 行情数据源选型与springboot接入配置行情数据源决定策略的下限。免费的数据源接口可能延迟高或者有访问频控实盘时信号晚了几秒问题就大了。常见做法是回测用免费高频数据实盘用券商或专业数据商的API。接入方式上springboot里用接口加策略模式把不同数据源的获取逻辑封装成同一个接口换数据源只改配置不改成程序代码。public interface MarketDataProvider { String providerName(); ListKline fetchKline(String symbol, String period, int limit); }这个接口只做一件事按品种、周期、条数返回K线。实现类内部可以是HTTP调用也可以是SDK封装只要返回的Kline对象字段完整即可。调用方不关心数据来自哪个源只看symbol、period和ts三个字段去重。这样回测时用历史数据源实盘时切到实时行情源策略引擎的代码一行都不用动。提示做量化交易系统之前先确认数据源的使用条款是否允许商用和存储。部分公开行情接口只允许个人学习使用落地到生产环境之前要替换成有授权的数据服务。接入配置放在application.yml里把数据源的地址、Token、行情品种和轮询周期抽出来。这样部署时可以针对不同环境覆盖配置不需要改代码。3. springboot后端从行情入库到策略触发的可复现代码这一章的目标是让你在本地把行情采集、策略计算、信号推送这条闭环跑通。以比特币或A股某只股票的分钟级K线为例最小闭环包含三个类一个定时任务负责拉K线一个策略服务负责算信号一个WebSocket推送负责通知前端。3.1 行情采集定时任务与K线入库行情采集最直接的方式是springboot自带的Scheduled注解但要注意单机部署时用分布式锁避免多个实例重复采集。这里先写单实例版本多实例的坑放在最后一章讲。Component public class KlineCollectTask { private static final Logger log LoggerFactory.getLogger(KlineCollectTask.class); Autowired private MarketDataService marketDataService; Autowired private KlineMapper klineMapper; Scheduled(cron 0 */1 * * * ?) public void collectMinuteKline() { ListString symbols marketDataService.loadWatchSymbols(); for (String symbol : symbols) { try { ListKline klines marketDataService.fetchKline(symbol, 1m); klineMapper.batchUpsert(klines); log.info(collect kline success, symbol{}, count{}, symbol, klines.size()); } catch (Exception e) { log.error(collect kline failed, symbol{}, symbol, e); // 记录失败任务方便事后补偿 } } } }这段代码的逻辑是每分钟触发一次读取监控列表里的交易品种调用行情服务拉取最近一分钟的K线然后批量写入数据库。batchUpsert用的是MySQL的INSERT ... ON DUPLICATE KEY UPDATE以symbol和ts为唯一键重复拉取不会产生重复数据。参数上需要注意三点。第一cron表达式在分布式环境里要配合任务调度框架使用单机可以忽略。第二fetchKline的返回数量不要贪多一般拉最近5-10根K线就够策略计算取最后一根。第三失败日志里不要只记录symbol建议把失败原因和拉取时间一起记事后补数据时能知道从哪个时间点开始补。如果某个品种连续多次采集失败不要无限重试把任务状态置为异常靠监控告警通知人处理。3.2 策略引擎与信号生成策略引擎是系统里跟业务耦合最深的部分。常见做法是把策略实现设计成统一接口每个策略一个独立类参数从strategy_param表读取这样改参数不需要重启系统。public interface Strategy { String strategyName(); Signal evaluate(KlineContext context); }具体策略比如双均线核心逻辑在evaluate里Component public class MaCrossStrategy implements Strategy { Override public String strategyName() { return ma_cross; } Override public Signal evaluate(KlineContext context) { ListKline klines context.getKlines(); if (klines.size() MAX_BARS) { return null; // 数据不足时不出信号 } double fastMa calculateMa(klines, 5); double slowMa calculateMa(klines, 20); double prevFast calculateMa(klines.subList(0, klines.size() - 1), 5); double prevSlow calculateMa(klines.subList(0, klines.size() - 1), 20); if (prevFast prevSlow fastMa slowMa) { return Signal.buy(context.getSymbol(), context.getClose()); } if (prevFast prevSlow fastMa slowMa) { return Signal.sell(context.getSymbol(), context.getClose()); } return null; } }这里的核心是prevFast和prevSlow的计算用于确认金叉死叉发生在当前这根K线而不是历史已经交叉过。如果不回看上一根K线的均线值策略会在一段持续的均线排列中反复发信号。判断方式用prevFast prevSlow fastMa slowMa保证信号只在交叉的那一根产生。策略引擎遍历所有策略把产生的信号写入strategy_signal表再通过WebSocket推给前端。这里有一个容易忽略的问题信号状态机。同一根K线上多个策略同时触发是正常的前端要不要弹窗、要不要下单都需要根据status字段判断避免重复消费。类名职责说明KlineCollectTask定时采集Scheduled每周期触发MarketDataService数据源抽象屏蔽行情API差异MaCrossStrategy双均线策略实现Strategy接口SignalWebSocket信号推送对所有连接广播3.3 WebSocket推送信号到前端Springboot对WebSocket的支持在spring-boot-starter-websocket里实现方式不复杂。核心是维护一个session集合把信号推给连接的客户端Component public class SignalWebSocket { private final CopyOnWriteArraySetWebSocketSession sessions new CopyOnWriteArraySet(); public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); } public void afterConnectionClosed(WebSocketSession session) { sessions.remove(session); } public void pushSignal(Signal signal) { String payload JSON.toJSONString(signal); for (WebSocketSession session : sessions) { synchronized (session) { session.sendMessage(new TextMessage(payload)); } } } }使用CopyOnWriteArraySet而不是普通Set是因为策略引擎高频推送时会有并发遍历和修改同时发生。synchronized(session)保证同一个连接的消息顺序避免两个信号同时发时客户端收到乱序消息。如果只给单个用户推送可以在session里绑定用户标识这里简化为全量广播。推送失败时要记录日志因为前端重连后需要靠REST接口补拉信号后端这边不能把推送失败当成无事发生。4. vue前端把策略信号变成可操作界面的关键几处前端部分的核心不是组件库选型而是解决好两件事行情数据从哪来怎么渲染策略信号怎么推送给用户且不丢失。Vue生态里常见组合是Vue3加Element Plus加ECharts使用Vite作为构建工具。源码zip里如果是Vue2项目可以保留但如果从头开始建议直接Vue3。4.1 vue项目初始化的环境配置与依赖安装vue项目最容易卡住的环节往往不是代码而是依赖安装和版本问题。Node.js版本过旧或过新npm install都会报错。常见做法是使用Node.js 18 LTS版本配合npm或pnpm安装依赖。node -v npm init -y npm install vue3 vue-router4 pinia npm install -D vite vitejs/plugin-vue npm install echarts element-plus axios这里把ECharts单独列出是因为K线图需要用到它的candlestick图表类型。Element Plus负责表格和表单ECharts负责K线。安装完成后在vite.config.js里配置代理把/api请求转发到后端服务解决开发环境跨域export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } });代理配置的要点是changeOrigin必须为true否则后端拿到的Host还是前端地址有些网关会根据Host做路由这里不设置会在联调时出现诡异的404。生产环境则不需要代理由Nginx统一转发/api和/ws路径到后端服务。4.2 基于vue-router的行情页面与路由参数传递量化交易系统的前端页面通常分成行情、策略、订单、风控四个视图。行情页面选择某个交易品种后策略页面需要知道当前选的是哪个symbol。常见做法是使用vue-router的路由参数。const router createRouter({ history: createWebHashHistory(), routes: [ { path: /market, component: MarketView }, { path: /strategy/:symbol, component: StrategyView, props: true }, { path: /orders, component: OrderView }, { path: /risk, component: RiskView } ] });在行情页面跳转时携带symbol参数const goStrategy (symbol) { router.push({ path: /strategy/${symbol} }); };策略页面通过route.params取出symbol再向后端请求对应的K线和策略参数。这套做法的好处是刷新页面后参数还在URL里不会因为刷新丢失上下文。很多Vue2转Vue3的开发者容易在这里踩坑Vue3的route.params必须是响应式监听不能直接解构否则参数变化时页面不会更新。路由路径对应组件页面职责/marketMarketView品种列表与行情总览/strategy/:symbolStrategyView当前品种策略与信号/ordersOrderView订单与持仓记录/riskRiskView风控规则与告警注意Vue3中如果在一个复用组件内监听路由参数变化要用watch(() route.params.symbol, ...)而不是直接在setup里读取一次否则从BTCUSDT切到ETHUSDT时页面内容不刷新。4.3 用ECharts渲染行情K线与信号标记K线图的性能瓶颈主要在数据量大时重绘卡顿。常见做法是ECharts的setOption传入notMerge: true避免旧数据和新数据合并时的开销同时在组件卸载时销毁实例。import * as echarts from echarts; const chart echarts.init(document.getElementById(klineChart)); chart.setOption({ xAxis: { type: category, data: klines.map(k k.ts) }, yAxis: { scale: true }, series: [{ type: candlestick, data: klines.map(k [k.open, k.close, k.low, k.high]), itemStyle: { color: #ef232a, color0: #14b143, borderColor: #ef232a, borderColor0: #14b143 } }] }, true);ECharts的candlestick数据格式是[open, close, low, high]顺序不能搞反否则K线阴阳线渲染错乱。信号标记可以用markPoint在对应K线上画箭头买点为绿色箭头卖点为红色箭头。前端拿到信号之后还要做一个时间对齐后端推送的信号时间戳对应到K线的哪一根如果对应关系错了箭头会画偏看起来很像是信号乱跳。4.4 前端WebSocket收信号的容错处理前端WebSocket比后端的复杂之处在于连接断开重连。浏览器原生WebSocket不提供自动重连需要在onclose事件里做指数退避。function connectWebSocket(url) { const ws new WebSocket(url); ws.onopen () { console.log(signal ws connected); reconnectAttempt 0; }; ws.onmessage (event) { const signal JSON.parse(event.data); handleSignal(signal); }; ws.onclose () { reconnectAttempt; const delay Math.min(1000 * 2 ** reconnectAttempt, 30000); setTimeout(() connectWebSocket(url), delay); }; }重连时要注意消息丢失的问题。WebSocket断开期间产生的信号前端无法补收。常见做法是前端在重连成功后主动调用REST接口拉取最近未读信号。这个补偿逻辑一定要做否则断网几分钟后策略页面上的信号会出现缺口。另外组件卸载时一定要调用ws.close()否则页面路由切换后连接还挂在后台多个页面同时收信号界面上会重复弹窗提醒。5. 从源码zip到本地跑通的五个操作和三个坑现在假设你手里拿到了一个springbootvue的量化交易系统源码.zip先不要急着双击导入IDE。按下面顺序操作可以少走弯路。5.1 解压结构与版本核对解压后先看目录结构确认后端是Maven还是Gradle前端是Vue2还是Vue3。查看pom.xml里的spring-boot-starter-parent版本号如果版本过高例如Spring Boot 3.x需要确认JDK是否匹配。Spring Boot 3.x要求JDK 17以上Spring Boot 2.x可以用JDK 8或11。5.2 数据库初始化与配置修改找到application.yml或application.properties修改数据库连接地址、用户名密码。很多量化交易系统源码自带SQL脚本优先执行脚本初始化库表。这一步的建议是不要直接改生产配置先把数据库改为本地localhost跑通后再改回去。账号权限建议单独建一个专用账号只授予这个库的增删改查权限不要随手用root连上去调试。5.3 后端启动与接口验证后端启动完成后用curl验证接口是否响应curl http://localhost:8080/api/kline?symbolBTCUSDT正常返回JSON数组说明数据库连接和行情接口没问题。如果返回404检查controller路径上是否有/api前缀如果返回500查看日志中的SQL异常。如果返回空数组优先检查定时任务是否已经触发K线表里是否有数据而不是先怀疑接口逻辑。5.4 前端依赖安装与端口代理前端进入源码目录后npm install注意如果源码里package-lock.json存在不要轻易删除否则依赖版本会漂移。启动后通过Vite代理访问后端接口。遇到跨域问题先检查代理配置而不是改后端的CORS因为生产环境最终还是要靠Nginx转发后端放开CORS反而容易埋下安全隐患。5.5 三个实际踩过的坑第一个坑是springboot版本太高导致的循环依赖报错。Spring Boot 2.6开始默认禁止循环依赖老源码的Service互相注入时直接启动失败。解决办法是排查循环依赖关系并把它们拆开而不是在配置里关闭限制关闭限制只是掩盖问题。第二个坑是Vue项目npm install过程中出现ERESOLVE错误这是Node.js版本与依赖冲突导致。先用nvm切换Node.js 16或18再删除node_modules和package-lock.json重新安装。不要一上来就升级依赖版本那会让整个项目换一套依赖树风险更大。第三个坑是K线数据时间跨度为UTC前端显示8小时偏差。常见做法是后端存储UTC时间前端在格式化时转换本地时区不要在库里时区混用。判断方法是打开数据库看一眼ts字段如果跟本地时间正好差8小时那就是UTC时间。除了跑通从源码学习量化交易系统的关键在策略接口和回测逻辑。建议把自带的策略替换成自己的策略前先跑一次回测并保存基准结果后续优化才有对比对象。参数调优时用策略参数表在前端改比改代码重编译效率高得多。本文还有配套的精品资源点击获取
返回列表