Java AI开源量化交易平台:从架构到实战的自动交易工程指南
简介这是一套基于JAVA的AI开源量化交易平台源码面向具备一定编程基础的程序员与量化爱好者覆盖期货、股票、外汇、数字货币等多种交易场景可实现自动与半自动交易常被用于替代文华、MC、金字塔等传统工具。平台内置历史行情回放、策略研发、模拟交易与实盘交易等完整链路兼顾策略验证与真实下单需求。资源包共579个文件整体约1.83MB以408个java源码为核心辅以62个js与24个vue构建前端界面另有xml、json、yml等配置与proto协议文件以及少量图片、字体和Dockerfile等部署相关文件结构清晰便于二次开发。目前已有378人学习下载。读者可从中获取完整的量化交易系统架构、策略引擎实现思路与前后端交互范例适合研究自动交易框架、搭建个人量化平台或进行策略回测与实盘对接的开发者参考。1. 从一张 Java 订单路由图说起这套开源量化平台到底在解决什么很多人第一次听到「基于 JAVA 的 AI 开源量化交易平台」脑子里浮现的是满屏 K 线加一个「一键暴富」按钮。真实情况恰恰相反它更像一套把行情接入、策略计算、风控校验、订单路由、成交回报串成闭环的工程骨架AI 只是挂在策略层的一个可替换模块。你手里如果已经有股票自动交易助手、通达信自动交易期货这类工具的使用经验会发现它们大多卡在同一个地方——策略能写但没法稳定地跑在期货、股票、外汇、炒币这些差异极大的交易场景上接口一换就得重写。这套平台的价值就在这用 Java 的面向对象编程把「交易所适配」和「策略逻辑」解耦让同一份量化交易策略代码换个适配器就能从股票挪到期货。它适合两类人——一类是写过 Java 基础、想把手里的策略真正跑成自动交易的开发者另一类是做 python 量化交易策略代码出身、被 Python 在并发和长期运行稳定性上折腾过、想换一套更工程化底座的人。下面按「先立住原理、再动手复现、最后讲坑」的顺序拆开讲。2. 平台分层与选型为什么用 Java 而不是 Python 扛自动交易2.1 四层架构行情、策略、风控、执行各管什么一套能落地的自动交易平台核心不是策略多聪明而是分层是否干净。我一般会把它拆成四层每层只暴露固定接口层与层之间靠事件或队列通信而不是互相直接调用。层职责关键接口常见实现行情层订阅 tick/K 线归一化不同交易所字段onTick / onBarWebSocket 客户端 本地缓存策略层接收行情产出交易信号onSignal策略基类 AI 模型推理风控层校验仓位、单笔上限、频率check(order)规则引擎 账户快照执行层下单、撤单、回报对账sendOrder / onFill交易所 REST/WS 适配器这样分的好处是期货和炒币的差异全部收敛在行情层和执行层的适配器里策略层完全无感。你写一个均线突破策略在股票上跑和在期货上跑代码一行不用改只换适配器配置。2.2 Java 在自动交易里的三个硬理由第一个是长期运行的稳定性。自动交易是 7×24 常驻进程Python 的 GIL 在高频行情回调下容易堆积延迟而 Java 的线程模型和 JVM 的 JIT 预热后长时间运行的抖动明显更小。第二个是并发。行情推送、策略计算、订单回报是三条独立的数据流Java 的并发容器和线程池能干净地把它们隔开不会出现一个慢策略拖垮整个行情接收的情况。第三个是生态。交易所的官方 SDK 大多有 Java 版本阿里巴巴开源镜像里也能快速拉到 Maven 依赖构建和部署链路成熟。提示不要因为「AI」两个字就默认策略层必须用 Python。模型训练可以在 Python 侧完成导出成 ONNX 后由 Java 侧推理工程和训练各用各的强项。2.3 最小可运行骨架一个策略基类加一个适配器接口先别急着接真实交易所用内存模拟行情把闭环跑通这是最省时间的起步方式。下面这段代码定义策略基类和行情事件任何具体策略都继承它。// 策略基类所有策略继承它只关心信号产出 public abstract class Strategy { protected String symbol; public Strategy(String symbol) { this.symbol symbol; } // 行情到达时被调用子类实现具体逻辑 public abstract Signal onTick(Tick tick); // 可选K 线收盘时调用 public void onBar(Bar bar) {} } // 行情事件归一化后的最小字段 public class Tick { public String symbol; public double lastPrice; public long timestamp; public double volume; } // 交易信号方向 数量不直接下单 public class Signal { public enum Side { BUY, SELL, HOLD } public Side side; public double price; public int qty; }逻辑说明Strategy只负责把行情转成Signal它不知道订单怎么发、发到哪个交易所。Tick字段刻意做少因为不同交易所字段差异大归一化越早越好。参数上timestamp用毫秒长整型避免时区问题qty用 int因为多数交易所下单数量是整数手。2.4 适配器接口把期货、股票、外汇、炒币的差异关进笼子执行层定义一个统一接口每个交易场景写一个实现类。这样新增一个交易所只是新增一个类不动策略。// 交易所适配器所有场景实现同一套方法 public interface ExchangeAdapter { void connect(); // 建立连接 void subscribe(String symbol); // 订阅行情 String sendOrder(OrderRequest req); // 下单返回订单号 void cancelOrder(String orderId); // 撤单 AccountSnapshot queryAccount(); // 查询账户 } // 下单请求统一字段适配器内部做映射 public class OrderRequest { public String symbol; public String side; // BUY / SELL public String type; // LIMIT / MARKET public double price; public int qty; public String clientId; // 幂等键防重复下单 }逻辑说明clientId是幂等键网络重试时交易所靠它去重这是自动交易里最容易被忽略却最致命的一个字段。queryAccount返回快照而不是实时查询是为了风控层能快速拿到仓位不用每次下单都打一次网络请求。参数上type用字符串而非枚举方便适配器直接透传给不同交易所的字段。3. 把 AI 策略接进来从模型推理到信号落地的完整链路3.1 AI 在量化里到底放在哪一层AI 不是替代策略而是替代策略里「判断」那一段。传统策略是if 均线金叉 then 买AI 策略是if 模型输出 阈值 then 买。模型输入是特征向量价格、成交量、技术指标输出是方向概率或预期收益。它挂在策略层的onTick里产出仍然是Signal下游风控和执行完全不用改。这样设计的好处是模型可以随时替换、回滚不会影响交易链路。3.2 特征工程与模型推理的 Java 侧实现训练在 Python 侧做导出 ONNXJava 侧用 ONNX Runtime 推理。下面是一个把 Tick 转成特征并推理的最小示例。// AI 策略继承 Strategy内部持有 ONNX 会话 public class AiStrategy extends Strategy { private final OrtSession session; private final DequeDouble priceWindow new ArrayDeque(); private static final int WINDOW 20; public AiStrategy(String symbol, OrtSession session) { super(symbol); this.session session; } Override public Signal onTick(Tick tick) { priceWindow.addLast(tick.lastPrice); if (priceWindow.size() WINDOW) priceWindow.pollFirst(); if (priceWindow.size() WINDOW) return hold(); // 构造特征归一化后的价格序列 float[] feat new float[WINDOW]; double mean priceWindow.stream().mapToDouble(d - d).average().orElse(0); int i 0; for (double p : priceWindow) feat[i] (float) (p / mean - 1.0); // 推理输入 shape [1, WINDOW] float[][] input {feat}; OnnxTensor tensor OnnxTensor.createTensor(env, input); float[][] out (float[][]) session.run( Collections.singletonMap(input, tensor)).get(0).getValue(); double prob out[0][0]; // 上涨概率 if (prob 0.6) return buy(tick.lastPrice); if (prob 0.4) return sell(tick.lastPrice); return hold(); } }逻辑说明特征用「价格除以窗口均值再减一」做归一化避免不同品种价格量级差异导致模型失效。WINDOW20是常见短周期参数改大更平滑但更滞后。推理输出prob是上涨概率阈值 0.6/0.4 之间留出中性区减少无效交易。参数上input名称必须和导出 ONNX 时的输入名一致否则运行时报找不到输入。3.3 信号到订单风控层必须拦下的三件事信号出来不能直接下单风控层至少拦三件事单笔数量上限、账户总仓位上限、单位时间下单频率。下面是一个规则校验的骨架。// 风控信号转订单前逐条校验 public class RiskEngine { private final int maxQtyPerOrder 10; private final double maxPositionRatio 0.3; private final int maxOrdersPerMinute 20; private final DequeLong orderTimes new ArrayDeque(); public boolean check(Signal sig, AccountSnapshot acc) { if (sig.qty maxQtyPerOrder) return false; // 单笔上限 double posValue acc.positionValue(sig.symbol); if (posValue / acc.totalEquity() maxPositionRatio) return false; // 仓位上限 long now System.currentTimeMillis(); while (!orderTimes.isEmpty() now - orderTimes.peekFirst() 60_000) orderTimes.pollFirst(); if (orderTimes.size() maxOrdersPerMinute) return false; // 频率上限 orderTimes.addLast(now); return true; } }逻辑说明三条规则分别对应「下太重」「仓位太集中」「下太频」。maxQtyPerOrder按品种调整期货一手保证金高股票可以放宽。maxPositionRatio用市值比而非手数比才能跨品种统一。频率用滑动窗口统计比固定计数器更准。参数上 60_000 是毫秒改小则限制更严。3.4 回测与实盘的差异为什么回测赚钱实盘亏回测用的是历史成交价实盘面对的是盘口和滑点。常见差异有三一是回测按收盘价成交实盘可能挂单没成交二是回测忽略手续费和资金费率期货和炒币尤其明显三是回测没有网络延迟实盘信号到下单可能差几百毫秒。做法是回测里加滑点模型和手续费实盘里用限价单加超时撤单别用市价单硬吃。4. 数据访问与存储安全量化交易绕不开的加密与一致性4.1 行情与订单数据怎么存才不丢行情数据量大、写入频繁订单数据量小但绝不能丢两者要分开存。行情用列式或时序库订单用关系库加事务。下面是一个订单落库的示例重点是幂等和状态机。-- 订单表client_id 唯一保证重试不重复插入 CREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, client_id VARCHAR(64) UNIQUE NOT NULL, symbol VARCHAR(32) NOT NULL, side VARCHAR(8) NOT NULL, price NUMERIC(18,8), qty INT NOT NULL, status VARCHAR(16) NOT NULL, -- NEW/FILLED/CANCELED/REJECTED created_at TIMESTAMP NOT NULL DEFAULT now(), updated_at TIMESTAMP NOT NULL DEFAULT now() ); -- 状态变更走独立表保留完整轨迹 CREATE TABLE order_events ( id BIGSERIAL PRIMARY KEY, client_id VARCHAR(64) NOT NULL, from_status VARCHAR(16), to_status VARCHAR(16) NOT NULL, event_time TIMESTAMP NOT NULL DEFAULT now() );逻辑说明client_id唯一约束是防重复下单的最后一道防线即使程序重试也不会插两条。status用状态机管理只允许 NEW→FILLED、NEW→CANCELED 这类合法迁移。order_events记录每次变更出问题时能完整回放。参数上NUMERIC(18,8)是为了兼容炒币的小数价格股票用不到这么多位但不会出错。4.2 敏感字段加密API Key 绝不能明文落库交易所的 API Key 和 Secret 一旦泄露账户资金直接暴露。做法是落库前用对称加密密钥放环境变量或密钥管理服务不进代码库。下面是一个加密存储的示例。// 用 AES-GCM 加密 API Secret密钥从环境变量读取 public class SecretCipher { private static final String ALGO AES/GCM/NoPadding; private final SecretKey key; public SecretCipher(byte[] keyBytes) { this.key new SecretKeySpec(keyBytes, AES); } public String encrypt(String plain) throws Exception { byte[] iv new byte[12]; new SecureRandom().nextBytes(iv); // 每次加密随机 IV Cipher c Cipher.getInstance(ALGO); c.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv)); byte[] ct c.doFinal(plain.getBytes(StandardCharsets.UTF_8)); // IV 拼在密文前解密时取出 byte[] out new byte[iv.length ct.length]; System.arraycopy(iv, 0, out, 0, iv.length); System.arraycopy(ct, 0, out, iv.length, ct.length); return Base64.getEncoder().encodeToString(out); } }逻辑说明AES-GCM 自带完整性校验密文被篡改解密会失败比 CBC 更安全。IV 每次随机且随密文一起存绝不复用否则 GCM 会严重削弱安全性。密钥从环境变量读代码里不出现明文。参数上 128 是认证标签长度位12 字节 IV 是 GCM 推荐值。4.3 数据一致性下单和记账怎么不打架下单成功但记账失败或者记账成功但下单失败都会导致账户对不上。做法是「先落库再下单回报驱动状态」用 client_id 串起来。下单前先插一条 NEW 状态订单拿到订单号后调交易所回报回来再更新状态。如果交易所调用超时靠定时对账任务拉取订单状态补齐。这样即使中间任何一步失败数据都能自愈。5. 避坑与排查自动交易上线前必须过的五道坎5.1 现象策略在回测里稳赚实盘一上线就亏原因回测用了未来数据比如用当根 K 线的收盘价判断又用同一根 K 线成交等于偷看答案。解决回测里信号必须用上一根已收盘 K 线成交用下一根开盘价加滑点和手续费重跑一遍再决定是否上线。5.2 现象程序跑几天后内存暴涨最后 OOM原因行情回调里不断往 List 里塞 tick没有清理。解决用固定长度环形缓冲或滑动窗口超过窗口就丢弃旧数据同时给 JVM 设-Xmx上限并开 GC 日志观察是否有对象持续增长。5.3 现象同一笔订单被下了两次原因网络超时后程序重试但交易所其实已经收到第一笔。解决下单请求带唯一 client_id交易所侧去重本地落库用唯一约束兜底重试前先查订单状态而不是盲目重发。5.4 现象期货和股票用同一份策略期货那边仓位算错原因期货是保证金交易仓位价值和股票市值算法不同。解决账户快照里区分「名义价值」和「占用保证金」风控规则按品种类型走不同分支别用一套公式套所有场景。5.5 现象AI 模型推理拖慢整个行情处理原因推理在行情回调线程里同步执行模型一大就阻塞。解决行情线程只入队推理放到独立线程池用有界队列加丢弃策略保证行情接收不被拖垮。模型侧做量化或裁剪降低单次推理耗时。6. 进阶技巧用影子账户验证策略再上真金白银上线前最值钱的一步是让策略先跑影子账户——接真实行情但下单只记录不发送用真实盘口价模拟成交跑够一段时间再对比。这样能暴露滑点、延迟、信号频率这些回测里看不到的问题。我一般会同时跑三套回测、影子、实盘小资金三者收益曲线偏差超过阈值就停下来查原因而不是加仓。具体做法是给执行层加一个ShadowAdapter实现同一个ExchangeAdapter接口sendOrder只写日志和内存订单簿queryAccount返回模拟账户。策略代码一行不改切换配置就能从影子切到实盘。验证指标看三个信号到下单的延迟分布、模拟成交价与盘口的偏离、单位时间信号数量是否和回测一致。偏差大的地方往往就是真金白银会亏的地方。// 影子适配器只记录不真实下单用于上线前验证 public class ShadowAdapter implements ExchangeAdapter { private final MapString, OrderRequest shadowOrders new ConcurrentHashMap(); Override public String sendOrder(OrderRequest req) { String id shadow- req.clientId; shadowOrders.put(id, req); // 只记录不调用真实交易所 return id; } Override public AccountSnapshot queryAccount() { return AccountSnapshot.simulated(); // 返回模拟账户 } // 其余方法省略 }逻辑说明影子适配器复用同一套策略和风控唯一区别是订单不出去所以能真实反映策略在实时行情下的行为。参数上clientId仍然生成方便和真实链路对比。跑影子账户的时间建议至少覆盖一个完整交易周期股票至少两周期货和炒币至少一个月样本太少说明不了问题。我自己踩过最深的坑是当年觉得回测漂亮就直接上实盘结果第一周就因为滑点把利润吃光。后来养成习惯任何策略不跑够影子账户绝不动真钱。这套 Java 平台的适配器设计恰好让影子验证变成换个实现类的事不用改策略。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →