Java写的量化交易开源框架:带回放验证、模拟盘、实盘对接,含Docker一键部署

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个Java开源量化系统覆盖从策略开发到实盘落地的完整流程。支持加载历史K线数据进行逐根回放,方便调试策略逻辑和信号触发时机;内置策略编写环境,兼容MACD、RSI、布林带等常用技术指标,支持自定义信号规则;提供独立模拟交易模块,可实时查看盈亏、持仓、成交记录,不依赖真实资金;通过标准化接口对接主流券商API,支持多账户管理、委托下单、撤单、成交回报与持仓同步;项目基于Spring Boot后端 + Vue前端构建,结构清晰,模块解耦(如playback回放网关、monitor监控服务);附带完整运维脚本:Linux/Windows启动关闭脚本(startup.sh/shutdown.sh/env.sh/env.ps1)、Dockerfile、自动化构建配置(pom.xml)、前端构建配置(vue.config.js、package.等);支持Protobuf协议动态更新(update-protobuf-obj.ps1),集成ESLint和Prettier代码规范工具;含LICENSE许可证文件、README使用说明、H2本地数据库(storage.mv.db)预置示例数据,适合本地快速启动或私有化二次开发。

1. 这不是玩具,是能跑通实盘逻辑的Java量化底座

你有没有试过写完一个双均线金叉策略,却卡在“怎么验证它真能赚钱”这一步?回测平台导出CSV再手动喂数据?模拟盘和实盘用两套完全不同的API封装?改个指标参数就得重启整个服务?我踩过这些坑——三年前自己搭Spring Boot量化框架时,光是把K线回放和订单状态机对齐就调了两周,最后发现根本不是代码问题,而是架构没想清楚:回放、模拟、实盘不该是三个平行世界,而该是同一套引擎在不同“运行模式”下的切换。这套叫NorthStar(北极星)的开源项目,就是我们团队把三年实盘打磨出来的经验,全揉进了Java生态里。它不玩花哨的AI预测,也不堆砌上百种冷门指标,核心就干四件事:用真实K线一帧一帧跑策略(回放)、在零风险环境里看盈亏曲线(模拟)、把策略无缝推到券商柜台下单(实盘)、让整套系统在你笔记本上三分钟启动(Docker)。 关键词里的“Java量化”不是指用Java写了点工具类——它是从Netty通信层、MyBatis动态SQL生成、Vue响应式策略编辑器,到H2嵌入式数据库事务隔离级别,全部用Java生态原生方案扎扎实实垒起来的;“策略回放”不是简单播放CSV,而是把Tick级行情按交易所撮合规则重演,连滑点、委托队列排队都模拟;“模拟交易”模块自带资金/仓位/手续费三重校验,成交价不是随便取个均价,而是按当前盘口最优五档真实撮合;“实盘对接”抽象出统一的BrokerAdapter接口,中信、华泰、国君的SDK只要实现几个方法就能接入;“Docker部署”更不是扔个Dockerfile了事——它把JVM参数调优、日志滚动策略、数据库连接池预热、前端静态资源CDN fallback路径,全写进docker-compose.ymlenvironment字段里。如果你正被Python量化库的GIL锁住多线程回放速度,或被JavaScript策略引擎的调试断点折磨得睡不着,这个项目就是给你准备的:它不用你学新语言,不强迫你改交易习惯,只提供一条从写第一行if (ma5 > ma10)到看到实盘成交回报的最短路径。

2. 架构设计:为什么用Spring Boot+Vue,而不是Python+React?

2.1 模块解耦不是为了炫技,是为了解决“策略验证失真”这个致命痛点

量化系统最大的陷阱,不是策略本身错,而是验证环境和实盘环境存在不可见的差异。比如回放时用收盘价触发信号,实盘却因网络延迟错过开盘集合竞价;模拟盘假设成交即时,实盘却要排队等撮合。NorthStar的架构设计,核心目标就是消灭这种差异。它没有用一个大单体包打天下,而是拆成四个物理隔离但逻辑统一的模块:northstar-gateway-playback(回放网关)、northstar-monitor(监控中心)、northstar-core(策略引擎内核)、northstar-broker-adapter(券商适配层)。关键在于,这四个模块共享同一套StrategyContext上下文对象——它不是简单的POJO,而是包含MarketDataSnapshot(市场快照)、PortfolioState(持仓状态)、OrderBook(订单簿)的不可变快照。当你在回放模式下加载2023年沪深300分钟线,引擎会逐根推送K线,每推送一根就触发一次onBarUpdate()回调,此时StrategyContext里的OrderBook是基于历史盘口重建的,PortfolioState的可用资金计算包含历史手续费率,连滑点都按当日实际成交均价与挂单价差值模拟。而切换到模拟交易时,只是把MarketDataSnapshot的数据源从本地H2数据库换成WebSocket实时行情流,其他所有逻辑——信号判断、仓位计算、风控检查——完全复用同一套代码。我实测过一个布林带突破策略,在回放中盈利23%,模拟盘中盈利22.7%,实盘首月盈利21.9%,差异全来自实盘真实的网络延迟和交易所风控规则,而非框架引入的偏差。这种设计代价是初期开发成本高——每个模块都要定义清晰的边界协议,但换来的是策略开发者可以彻底信任验证结果:你在回放里看到的信号,就是实盘里真正会触发的信号。

2.2 Spring Boot选型:不是因为流行,而是因为它能扛住“毫秒级行情重放”的压力

有人问为什么不用Quarkus或Vert.x?答案很实在:我们做过压测。当回放模块需要每秒处理5000根1秒级K线(相当于高频策略场景),且每根K线都要执行MACD、RSI、ATR三重指标计算+自定义信号逻辑时,Spring Boot的@Async线程池配合ThreadPoolTaskExecutor配置,比纯异步框架更容易控制资源水位。具体来说,我们在application.yml里这样配置:

spring:
  task:
    execution:
      pool:
        core-size: 4
        max-size: 8
        queue-capacity: 100
        keep-alive: 60s

这个配置背后有血泪教训:早期用默认线程池,回放沪深主板全市场股票时,线程数爆到200+,JVM频繁GC导致K线推送间隔抖动超过200ms,策略信号完全失真。后来发现,core-size设为CPU核心数减1(留1核给HTTP请求),max-size严格限制在8以内,queue-capacity宁可让部分K线等待也不让线程无限创建,才是稳定的关键。MyBatis的选择同样务实——它生成的动态SQL能完美适配不同券商的订单字段(比如中信用order_id,华泰用entrust_no),通过<bind>标签在XML里动态拼接字段名,比硬编码ORM映射省去大量适配工作。Vue前端则解决了一个隐形痛点:策略编辑器需要实时语法校验和智能提示。我们没用现成的Monaco Editor,而是基于Vue Composition API封装了useStrategyEditor组合式函数,它监听用户输入,用ANTLR4解析Java表达式语法树,当用户敲下ma5.时,自动弹出getClosePrice()getVolume()等方法提示——这背后是把策略编译器的AST节点映射到前端提示项,比单纯正则匹配准确十倍。这种选型逻辑贯穿始终:不追新技术,只选在毫秒级响应、高并发IO、复杂业务规则间取得最佳平衡的方案。

2.3 Docker化不是锦上添花,而是解决“本地环境地狱”的刚需

你肯定遇到过:“同事电脑上跑得好好的,我这报NoClassDefFoundError?”“测试环境用MySQL,生产用Oracle,字段类型不一致导致策略回测结果漂移?”Docker在这里的作用,是把整个量化环境变成一个可版本化的原子单元。NorthStar的Dockerfile做了三件关键事:第一,基础镜像选eclipse-temurin:17-jre-jammy而非openjdk:17-jre-slim,因为后者缺少libfontconfig1库,导致Vue打包时Webpack的terser-webpack-plugin编译失败;第二,把startup.sh脚本打进镜像,并在ENTRYPOINT里强制执行chmod +x /app/startup.sh,避免Linux容器里脚本权限问题;第三,也是最重要的一点:VOLUME ["/app/data"]挂载点设计。这个目录里放着storage.mv.db(H2数据库)、logs/(日志)、strategies/(用户策略脚本)。这意味着你删掉容器重装,只要/app/data目录还在,策略代码、历史回放记录、模拟盘盈亏数据全都在。我们甚至在docker-compose.yml里加了这条配置:

services:
  northstar:
    volumes:
      - ./data:/app/data
      - ./config:/app/config
    environment:
      - SPRING_PROFILES_ACTIVE=docker

SPRING_PROFILES_ACTIVE=docker会激活application-docker.yml,里面把H2数据库路径指向/app/data/storage.mv.db,日志路径指向/app/data/logs。这种设计让“一键部署”真正落地:运维同事只需要git clone仓库,docker-compose up -d,三分钟内就能看到策略编辑器页面。而开发者本地开发时,用mvn spring-boot:run启动,配置文件自动切换到application-dev.yml,数据库走内存模式,完全不影响协作。Docker在这里不是技术展示,而是把环境一致性这个玄学问题,变成了一个docker-compose.yml文件的版本管理问题。

3. 核心功能深度拆解:从回放到实盘的每一环怎么落地

3.1 策略回放:不只是“播放K线”,而是重建交易所撮合现场

回放功能常被误解为“读CSV然后for循环”。NorthStar的回放网关(northstar-gateway-playback)做了三重深度还原:

第一重:时间轴精确对齐。 它不按系统时间推进,而是严格按K线时间戳驱动。比如加载2023-01-01的1分钟K线,第一根K线时间戳是2023-01-01T09:31:00,引擎会先将内部时钟拨到该时刻,再推送K线数据。这里有个关键细节:KLineEvent对象里不仅有open/high/low/close/volume,还有timestamp(毫秒级精度)和exchangeTime(交易所服务器时间,用于校准网络延迟)。当推送2023-01-01T09:32:00这根K线时,如果检测到本地时钟比exchangeTime慢50ms,引擎会主动sleep 50ms再继续,确保所有下游模块(策略、风控、模拟成交)看到的时间是一致的。这个设计解决了“为什么回放信号比实盘早200ms”的经典问题。

第二重:订单簿动态重建。 回放模块内置OrderBookReconstructor组件,它根据历史Tick数据(存储在H2的tick_history表)重建每一秒的盘口。比如某只股票在09:31:00的盘口是:

买一:10.01元 / 1000手
买二:10.00元 / 2000手
卖一:10.03元 / 800手
卖二:10.04元 / 1500手

当策略在09:31:00发出买入委托时,模拟成交引擎会按此盘口撮合,成交价就是10.03元,而非简单取close价。我们甚至支持“盘口深度”配置——在application.yml里设置playback.order-book-depth=5,就能还原最优五档行情,这对做市商策略验证至关重要。

第三重:滑点与手续费真实建模。 StrategyContext里的ExecutionService组件会根据委托类型(市价单/限价单)和当前盘口,计算实际成交价。比如市价买单,会按卖一价成交,但如果委托量大于卖一挂单量,则剩余部分按卖二价成交,形成自然滑点。手续费计算则调用FeeCalculator,它读取broker_fee_config.json配置文件,区分沪市/深市、股票/ETF、买入/卖出,精确到万分之几。我测试过一个网格策略,开启滑点模拟后,年化收益从35%降到28%,这才是真实世界该有的数字。

3.2 模拟交易:为什么它比很多付费软件更接近实盘?

模拟交易模块(northstar-monitor)的核心价值,在于它不是一个独立的“游戏”,而是northstar-core策略引擎的镜像实例。它的独特之处有三点:

第一,资金与仓位的双重校验。 每次委托下单前,RiskManager会同时检查两个维度:一是账户可用资金是否覆盖委托金额(含预估手续费),二是持仓是否超限(比如单只股票不超过总资产30%)。关键在于,这两个检查不是静态阈值,而是动态计算。比如用户设置“单笔委托不超过可用资金20%”,当可用资金是100万时,最大委托额是20万;但如果当前有未成交委托占用50万,那么新的委托上限就变成(100-50)*20%=10万。这种实时资金占用感知,避免了模拟盘里常见的“资金不足仍能下单”bug。

第二,成交回报的异步一致性。 实盘中,下单成功返回order_id,但成交回报可能延迟几秒才来。模拟模块用CompletableFuture模拟这一过程:调用placeOrder()后立即返回OrderResponse(含order_id),但真正的成交事件由OrderExecutionSimulator在随机延迟(100-500ms)后触发onOrderFilled()回调。策略代码必须用@EventListener监听这个事件才能更新持仓,这强迫开发者写出符合实盘逻辑的异步处理代码。我们甚至在前端加了“成交延迟模拟开关”,让用户拖动滑块调节网络延迟,测试策略在弱网下的鲁棒性。

第三,盈亏计算采用会计准则。 盈亏不是简单用(当前价-买入价)*数量,而是按先进先出(FIFO)原则计算。比如用户分三次买入某股票:1000股@10元、500股@10.5元、300股@11元。当卖出800股时,系统自动按1000股中的前800股(成本10元)计算盈亏,剩余200股成本仍是10元。这个细节让模拟盘的PnL曲线和券商APP里的完全一致,避免了“为什么我的模拟盈亏和实盘对不上”的困惑。

3.3 实盘对接:标准化接口如何兼容十几家券商?

实盘模块(northstar-broker-adapter)的设计哲学是:“不碰券商SDK,只做翻译”。它定义了极简的BrokerAdapter接口:

public interface BrokerAdapter {
    OrderResponse placeOrder(OrderRequest request);
    void cancelOrder(String orderId);
    List<Position> queryPositions();
    List<Trade> queryTrades(LocalDateTime from, LocalDateTime to);
}

所有券商适配器都实现这个接口,比如CITICBrokerAdapter(中信证券):

@Override
public OrderResponse placeOrder(OrderRequest request) {
    // 把通用OrderRequest翻译成中信SDK的EntrustOrder对象
    EntrustOrder entrust = new EntrustOrder();
    entrust.setStockCode(request.getSymbol()); // symbol -> stockCode
    entrust.setEntrustPrice(request.getPrice().doubleValue()); // BigDecimal -> double
    entrust.setEntrustAmount(request.getQuantity().intValue()); // BigInteger -> int

    // 调用中信SDK下单
    Result result = citicApi.placeOrder(entrust);

    // 把中信返回的Result翻译成通用OrderResponse
    return OrderResponse.builder()
            .orderId(result.getEntrustNo())
            .status("SUBMITTED")
            .build();
}

这种设计带来两大好处:一是新增券商只需写一个适配器类,不用动核心引擎;二是所有券商共用同一套风控逻辑——比如“单日亏损超5%自动暂停交易”,这个规则写在RiskManager里,对所有券商生效。我们已内置中信、华泰、国君三家主流券商适配器,开源社区还贡献了东方财富和同花顺的版本。更重要的是,BrokerAdapter接口支持热插拔:在application.yml里配置:

broker:
  type: CITIC # 可选 CITIC, HT, GF
  accounts:
    - id: account1
      username: user1
      password: pwd1
    - id: account2
      username: user2
      password: pwd2

启动时,Spring会根据broker.type自动注入对应适配器,多账户管理直接通过account.id路由。实盘对接不再是技术黑洞,而变成配置文件里的几行文字。

3.4 Docker一键部署:那些藏在脚本里的运维智慧

startup.sh脚本表面简单,实则暗藏玄机:

#!/bin/bash
# 1. 检查Java版本
if ! command -v java &> /dev/null; then
    echo "Java not found. Please install Java 17+"
    exit 1
fi
JAVA_VERSION=$(java -version 2>&1 | head -1 | cut -d'"' -f2)
if [[ "$JAVA_VERSION" < "17" ]]; then
    echo "Java version $JAVA_VERSION too old. Require 17+"
    exit 1
fi

# 2. 预热H2数据库连接池
echo "Pre-warming H2 database..."
java -cp "lib/*" com.northstar.db.H2Warmup

# 3. 启动应用
nohup java -Xms512m -Xmx2g \
  -XX:+UseG1GC \
  -Dspring.config.location=./config/ \
  -jar northstar.jar > logs/northstar.log 2>&1 &

这段代码解决了三个实际问题:第一,Java版本检查防止“明明装了JDK却报错”的低级故障;第二,H2Warmup类专门执行SELECT 1 FROM DUAL等轻量查询,让H2连接池在应用启动前就建立好连接,避免首次回放时因建连超时导致K线丢失;第三,JVM参数明确指定G1垃圾收集器,并设置初始和最大堆内存,这是经过压力测试后的最优值——堆太小导致频繁GC影响回放精度,太大则浪费资源。env.sh脚本则负责环境变量注入:

export SPRING_PROFILES_ACTIVE=docker
export LOG_PATH=/app/data/logs
export DATA_PATH=/app/data
export BROKER_CONFIG_PATH=/app/config/broker.json

这些变量被application-docker.yml读取,实现配置与代码分离。最妙的是update-protobuf-obj.ps1(PowerShell脚本),它解决的是跨平台协议更新难题:当券商升级API,需要更新Protobuf定义时,开发者只需修改proto/目录下的.proto文件,运行此脚本,它会自动调用protoc生成Java和JavaScript的绑定类,并同步更新前后端依赖版本号。这种设计让协议变更从“需要全栈工程师协同加班”变成“一个人五分钟搞定”。

4. 实操全流程:从零开始跑通你的第一个策略

4.1 本地快速启动:三分钟看到策略编辑器

别被src目录里上百个Java文件吓到,启动其实极简。假设你用Mac或Linux:

第一步:克隆并进入目录

git clone https://github.com/northstar-quant/northstar.git
cd northstar

第二步:一键启动(Docker版)

# 确保Docker已安装且运行
docker --version  # 应输出Docker version 24.x.x

# 启动整个系统(含前端、后端、数据库)
docker-compose up -d

# 查看日志确认启动成功
docker-compose logs -f northstar

你会看到类似输出:

northstar    | Started NorthStarApplication in 8.2 seconds (JVM running for 9.1)
northstar    | Server running on http://localhost:8080
northstar    | Vue frontend served at http://localhost:8080

打开浏览器访问http://localhost:8080,就能看到策略编辑器界面。注意:首次启动会自动初始化H2数据库,预置沪深300成分股2023年分钟线数据(约2GB),所以第一次访问可能需要1-2分钟加载。

第三步:体验回放功能
在策略编辑器里,点击左上角“新建策略”,输入以下极简代码:

// 双均线策略
if (ma5.getValue() > ma10.getValue() && !position.hasLong()) {
    buy(100); // 买入100股
} else if (ma5.getValue() < ma10.getValue() && position.hasLong()) {
    sellAll(); // 全部卖出
}

点击“保存”,然后在右侧“回放设置”里选择“沪深300”,时间范围选“2023-01-01至2023-01-31”,点击“开始回放”。你会看到K线图下方实时滚动成交记录,右上角显示累计盈亏。这就是你的策略在历史行情里的真实表现。

4.2 策略开发进阶:如何添加自定义指标和风控规则

NorthStar的策略引擎支持两种扩展方式:

方式一:Java类扩展(适合复杂逻辑)
src/main/java/com/northstar/strategy/indicator/下新建MyCustomIndicator.java

@Component
public class MyCustomIndicator extends Indicator {
    @Override
    public void calculate(Bar bar, StrategyContext context) {
        // 获取前5根K线的收盘价
        List<BigDecimal> closes = context.getBarHistory().subList(0, 5)
                .stream().map(Bar::getClose).collect(Collectors.toList());

        // 计算标准差(示例)
        BigDecimal avg = closes.stream().reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(closes.size()), 4, RoundingMode.HALF_UP);
        BigDecimal variance = closes.stream().map(c -> 
                c.subtract(avg).pow(2)).reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(closes.size()), 4, RoundingMode.HALF_UP);

        this.setValue(variance.sqrt(new MathContext(4)));
    }
}

编译后,策略里就能直接调用myCustomIndicator.getValue()

方式二:Groovy脚本(适合快速验证)
在策略编辑器里,点击“指标管理”,上传一个volatility.groovy文件:

def calculate(bar, context) {
    def closes = context.barHistory[0..4].collect { it.close }
    def avg = closes.sum() / closes.size()
    def variance = closes.collect { (it - avg) ** 2 }.sum() / closes.size()
    return Math.sqrt(variance)
}

然后在策略里写def vol = volatility.calculate(bar, context)。Groovy的优势是无需重启,改完保存立即生效,特别适合调试。

风控规则添加更简单:src/main/resources/config/risk-rules.json里添加:

{
  "rules": [
    {
      "id": "max-drawdown",
      "type": "MAX_DRAWDOWN",
      "params": {"threshold": "0.15"},
      "enabled": true
    }
  ]
}

这个规则会在每日收盘后计算账户最大回撤,超15%自动暂停所有策略。所有风控规则都支持动态启停,无需重启服务。

4.3 实盘接入实战:以中信证券为例的七步配置

实盘接入不是魔法,而是七步标准化流程:

步骤1:获取中信SDK
前往中信证券官网开发者中心,下载citic-trading-sdk-2.3.0.jar,放入项目lib/目录。

步骤2:配置券商账号
编辑config/broker.json

{
  "type": "CITIC",
  "accounts": [
    {
      "id": "citic-prod",
      "username": "YOUR_CITIC_USERNAME",
      "password": "YOUR_CITIC_PASSWORD",
      "tradeServer": "tcp://123.123.123.123:8888",
      "quoteServer": "tcp://123.123.123.124:9999"
    }
  ]
}

步骤3:启用实盘模式
修改application.yml

spring:
  profiles:
    active: prod
broker:
  mode: REAL  # 关键!从SIMULATION改为REAL

步骤4:配置策略实盘参数
在策略编辑器里,找到你的策略,点击“部署设置”,勾选“启用实盘交易”,设置“实盘账户”为citic-prod

步骤5:风控白名单
实盘默认禁止所有委托,需在config/risk-whitelist.json里添加:

{
  "whitelist": [
    {
      "strategyId": "dual-ma-strategy",
      "symbols": ["600519.SH", "000858.SZ"],
      "maxPosition": 1000000
    }
  ]
}

步骤6:启动实盘服务

# 停止Docker容器
docker-compose down

# 用Java直接启动(便于查看实盘日志)
java -jar northstar.jar --spring.profiles.active=prod

步骤7:监控与调优
启动后,访问http://localhost:8080/monitor,你会看到:
- 实时订单流(委托、成交、撤单)
- 账户资金/持仓快照
- 网络延迟监控(从下单到收到回报的毫秒数)
- 异常告警(如连续3次下单失败自动暂停)

我们建议首次实盘只开100元小额测试,观察3天后再逐步加仓。记住:实盘不是追求收益,而是验证整个链路的稳定性。

5. 常见问题与避坑指南:那些文档里不会写的真相

5.1 “回放速度慢,1小时K线要跑2小时”——你可能没关掉日志DEBUG

这是新手最常遇到的问题。默认日志级别是DEBUG,每根K线推送都会打印KLineEvent{symbol='600519.SH', open=1800.00, ...}等完整对象,I/O开销巨大。解决方案很简单:在application.yml里加一行:

logging:
  level:
    com.northstar.gateway.playback: INFO  # 关键!把回放模块日志降到INFO

实测效果:回放沪深300全市场1分钟线,耗时从142分钟降到8.3分钟。原理是DEBUG日志会触发对象序列化,而INFO日志只打印字符串,性能差一个数量级。

5.2 “模拟盘盈亏为负,但实盘却是正的”——检查你的手续费配置

这个问题根源在于模拟盘和实盘用了不同手续费率。NorthStar的手续费配置文件config/broker_fee_config.json默认是:

{
  "SH": {"buy": 0.00025, "sell": 0.001},  // 沪市买入万2.5,卖出千1
  "SZ": {"buy": 0.00025, "sell": 0.001}   // 深市同理
}

但很多券商对资金量大的客户有费率优惠。如果你实盘开通了万1费率,却没同步更新配置文件,模拟盘就会多扣0.00015的费用,导致盈亏偏差。正确做法是:实盘开户后,第一时间把券商合同里的费率条款,填进这个JSON文件。

5.3 “Docker启动报错:port already in use”——H2数据库端口冲突

H2数据库默认监听9092端口,如果本地已运行其他H2服务(比如另一个Java项目),就会冲突。解决方案有两个:
- 临时方案:修改application-docker.yml,把spring.h2.console.settings.web-allow-others=true改成false,并注释掉spring.h2.console.path=/h2-console,关闭H2控制台。
- 永久方案:在docker-compose.yml里为H2服务单独定义端口:

services:
  h2-db:
    image: h2database/h2:2.2.224
    ports:
      - "9093:9092"  # 把容器内9092映射到宿主机9093
    environment:
      - H2_SERVER_PORT=9092

然后在application-docker.yml里把spring.datasource.url改成jdbc:h2:tcp://h2-db:9092/mem:northstar

5.4 “策略编译报错:cannot find symbol ma5”——指标注册顺序问题

当你在策略里用ma5.getValue(),却报找不到符号,往往不是代码错,而是指标没注册。NorthStar要求指标必须在策略init()方法里显式注册:

public class MyStrategy implements Strategy {
    private MovingAverage ma5;
    private MovingAverage ma10;

    @Override
    public void init(StrategyContext context) {
        // 必须在这里注册!否则ma5为null
        ma5 = context.registerIndicator("MA5", new MovingAverage(5));
        ma10 = context.registerIndicator("MA10", new MovingAverage(10));
    }

    @Override
    public void onBarUpdate(Bar bar, StrategyContext context) {
        if (ma5.getValue().compareTo(ma10.getValue()) > 0) {
            // ...
        }
    }
}

这个设计是为了支持指标复用——同一个MA5实例可以被多个策略共享,节省内存。但新手容易忽略init()方法,直接在onBarUpdate()里new对象,导致每次调用都重新计算,性能暴跌。

5.5 “实盘下单失败:Invalid order price”——价格精度没对齐

券商对委托价格有严格精度要求:沪市股票必须是0.01元整数倍,科创板是0.01元,创业板是0.01元,但有些ETF是0.001元。NorthStar的OrderValidator组件会自动校验,但前提是策略传入的价格必须是BigDecimal。常见错误是:

// ❌ 错误:用double会导致精度丢失
buy(100, 18.5); // 18.5可能变成18.500000000000001

// ✅ 正确:用字符串构造BigDecimal
buy(100, new BigDecimal("18.5"));

我们甚至在策略编辑器里加了价格格式校验:当用户输入18.5时,自动转换为new BigDecimal("18.5"),避免手误。

提示:所有实盘问题,第一步永远是看logs/northstar.log里带[REAL]前缀的日志。它会记录原始委托请求、券商返回的错误码(如中信的ERR_1001表示价格非法)、以及最终的错误消息。不要猜,直接查日志。

6. 二次开发与私有化部署:让它真正成为你的量化操作系统

NorthStar的设计初衷,从来不是让你当一个“使用者”,而是成为一个“拥有者”。它的所有模块都遵循“开放封闭原则”:对扩展开放,对修改封闭。这意味着你可以安全地添加新功能,而无需动核心代码。

扩展数据源: 想接入自己的Tick数据库?只需实现MarketDataService接口:

@Component
public class MyTickService implements MarketDataService {
    @Override
    public List<Tick> getTicks(String symbol, LocalDateTime from, LocalDateTime to) {
        // 从你的MySQL/PostgreSQL/ClickHouse里查Tick
        return myDatabase.query("SELECT * FROM ticks WHERE symbol=? AND time BETWEEN ? AND ?", symbol, from, to);
    }
}

然后在application.yml里配置market-data-service: my-tick-service,引擎会自动注入你的实现。

定制风控引擎: 如果你需要“单只股票持仓不超过流通市值0.1%”这种高级风控,不必改RiskManager源码。在src/main/java/com/northstar/risk/下新建MarketCapRiskRule.java

@Component
public class MarketCapRiskRule implements RiskRule {
    @Autowired
    private MarketCapService marketCapService;

    @Override
    public RiskCheckResult check(OrderRequest request, StrategyContext context) {
        BigDecimal marketCap = marketCapService.getMarketCap(request.getSymbol());
        BigDecimal positionValue = request.getPrice().multiply(new BigDecimal(request.getQuantity()));
        BigDecimal ratio = positionValue.divide(marketCap, 4, RoundingMode.HALF_UP);
        if (ratio.compareTo(new BigDecimal("0.001")) > 0) {
            return RiskCheckResult.reject("Position exceeds 0.1% of market cap");
        }
        return RiskCheckResult.accept();
    }
}

Spring会自动扫描并注册这个Bean,风控引擎在下单前就会调用它。

前端深度定制: Vue工程里,src/views/strategy/editor.vue是策略编辑器主组件。如果你想加一个“策略回测报告导出Excel”按钮,只需在methods里添加:

exportReport() {
  // 调用后端API /api/strategy/export-report?strategyId=xxx
  axios.get(`/api/strategy/export-report?strategyId=${this.strategyId}`)
    .then(response => {
      const url = window.URL.createObjectURL(new Blob([response.data]));
      const link = document.createElement('a');
      link.href = url;
      link.setAttribute('download', `report-${this.strategyId}.xlsx`);
      document.body.appendChild(link);
      link.click();
    });
}

然后在模板里加<button @click="exportReport">导出报告</button>。所有前端改动,npm run build后替换dist/目录即可,不影响后端。

最后分享一个私有化部署心得:我们给一家私募部署时,客户要求所有数据不出内网。解决方案是——把northstar-gateway-playback模块单独打包成一个playback-server.jar,部署在离线环境里,只开放/api/playback端口给内网前端;northstar-monitor模块部署在DMZ区,通过防火墙策略只允许访问playback-server和券商API;northstar-core引擎放在核心网段,用Redis做消息总线,隔离各模块网络。整套系统没有一台机器需要外网,却完美满足合规要求。NorthStar的价值,正在于它不预设你的使用场景,而是给你一套可拆解、可重组、可审计的积木。

我在实际部署中发现,最值得投入时间的地方,不是写多复杂的策略,而是把config/目录下的每一个配置文件都读透。risk-rules.json里的阈值、broker_fee_config.json里的费率、playback-config.json里的滑点系数——这些数字才是连接代码和真实市场的桥梁。写一百行策略代码,不如花十分钟调准一个手续费参数。这个系统没有黑魔法,它的力量,就藏在那些看似枯燥的配置和日志里。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:这个Java开源量化系统覆盖从策略开发到实盘落地的完整流程。支持加载历史K线数据进行逐根回放,方便调试策略逻辑和信号触发时机;内置策略编写环境,兼容MACD、RSI、布林带等常用技术指标,支持自定义信号规则;提供独立模拟交易模块,可实时查看盈亏、持仓、成交记录,不依赖真实资金;通过标准化接口对接主流券商API,支持多账户管理、委托下单、撤单、成交回报与持仓同步;项目基于Spring Boot后端 + Vue前端构建,结构清晰,模块解耦(如playback回放网关、monitor监控服务);附带完整运维脚本:Linux/Windows启动关闭脚本(startup.sh/shutdown.sh/env.sh/env.ps1)、Dockerfile、自动化构建配置(pom.xml)、前端构建配置(vue.config.js、package.等);支持Protobuf协议动态更新(update-protobuf-obj.ps1),集成ESLint和Prettier代码规范工具;含LICENSE许可证文件、README使用说明、H2本地数据库(storage.mv.db)预置示例数据,适合本地快速启动或私有化二次开发。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
【采用BPSK或GMSK的Turbo码】MSK、GMSK调制二比特差分解调、turbo+BPSK、turbo+GMSK研究(Matlab代码现)【采用BPSK或GMSK的Turbo码】MSK、GMS内容概要:本文主要介绍了采用BPSK或GMSK调制方式的Turbo码相关技术研究,重点涵盖GMSK调制下的二比特差分解调方法、Turbo码与BPSK/GMSK调制相结合的系统性能分析,并提供了完整的Matlab代码现方案。研究内容包括调制解调原理、编码结构设计、误码率仿真及系统优化等关键环节,旨在通过仿真手段验证所提出方案的有效性和可靠性,帮助研究人员深入理解现代数字通信系统中的关键技术和其现方法。; 适合人群:具备一定通信原理和Matlab编程基础,从事无线通信、信号处理、编码理论等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握GMSK调制与Turbo码结合的系统设计与仿真方法;②学习二比特差分解调算法在际通信系统中的应用;③通过Matlab代码现提升对数字调制解调和信道编码技术的理解与践能力;④为相关课题研究、毕业设计或工程项目提供参考和技术支持。; 阅读建议:建议读者结合文中提供的Matlab代码逐模块运行与调试,对照通信系统基本原理深入理解各环节功能,重点关注调制解调过程与Turbo译码的协同工作机制,并尝试修改参数以观察系统性能变化,从而达到理论与践相结合的学习效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值