A股打板实战中如何提升性能-Java版本

0、前言

好板对速度要求极高,普通人比不过大户的专用通道,只能在有限的资源条件下尽量提升速度,0.1秒可能都排在几万手后面去了。

如果要监控大量的股票标的,采用 go 协程性能会更好,可以看我另一篇性能对比测试:
JDK21虚拟线程、OS线程、GO协程性能对比测试

A股微型低频套利交易-Java版本

一、云服务器硬件层面

跑程序可以用云服务器或者个人电脑,不过个人电脑的后台程序太多了,占用CPU资源,除非电脑性能足够好,否则还是推荐使用云服务器。

如我在A股微型低频套利交易-Java版本结尾提到的一样:服务器逻辑处理单元数量 > 监控标的数量那就是最好的。

对CPU线程数量的要求取决于个人的策略,比如我最近帮朋友开发的这个策略,每天监控的标的最多不超过七八个,所以选择了8C密集计算型 独占式的服务器,服务器地址要么选上海,要么选深圳

千万别图便宜买到了共享版本的云服务器(比独占式的便宜一倍),那个性能太差了。咨询客服买性能最好的即可
在这里插入图片描述

[root@iZwz9gmcys6qhe2mtmig34Z ~]# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8
On-line CPU(s) list:   0-7
Thread(s) per core:    2
Core(s) per socket:    4
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 85
Model name:            Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz
Stepping:              7
CPU MHz:               2500.002
BogoMIPS:              5000.00
Hypervisor vendor:     KVM
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              1024K
L3 cache:              36608K
NUMA node0 CPU(s):     0-7

4个核心,每个核心支持2个线程,所以总共有 8个逻辑处理单元(也可以理解为8个CPU)。监控每天的七八个标的足够了。

二、软件层面

线程池要隔离,一个线程池-MOMENTUM_POOL用来盯每个标的,一个线程池-OTHER_POOL用来处理其他的低频的工作。

盯盘线程池
public static ThreadPoolExecutor MOMENTUM_POOL = new ThreadPoolExecutor(
    10                  // corePoolSize: 核心池大小,即即使线程空闲,也会保持的线程数。
    10,                  // maximumPoolSize: 线程池允许的最大线程数。
    100L,                 // keepAliveTime: 允许线程池中空闲的线程最大空闲时间。
    TimeUnit.SECONDS,     // timeUnit: keepAliveTime 的时间单位,这里是秒。
    new SynchronousQueue<>(),  // workQueue: 任务队列,SynchronousQueue 是一个特殊的队列,它不存储任务,任务会直接被分配到线程上。
    new NamedThreadFactory("MX-", false)  // threadFactory: 用于创建线程的工厂,NamedThreadFactory 会创建名称以 "MX-" 为前缀的线程(并且不是守护线程)。
);

由于某些策略可能9:30:00第一档成交数据跳出来就会触发买入,所以开盘前就必须创建好核心线程,省去9:30:00创建线程的时间。

MOMENTUM_POOL.prestartAllCoreThreads();

9:29:55秒拆分策略,按竞价结果把标的拆分到对应的策略缓存里
更新账户可用金额,不能等到下单时才去获取可用金额,节省时间,并且在OTHER_POOL里面新开一个线程每分钟更新一下可用金额。

 、、、
private static final AtomicBoolean buy = new AtomicBoolean(false);//当日是否已经下过单

private AtomicInteger availableAmount = new AtomicInteger(0);//可用余额
 、、、
 
@Scheduled(cron = "55 29 9 * * ?")
public void callAuction() {
    if (!todayTrade.get()) {
        return;
    }
    AccountVo accountVo = tradeApiService.queryAccount();
    availableAmount.set(accountVo.getAvailableAmount().intValue());
    log.info("【竞价结束】当前账户可用资金: {},{}", availableAmount.intValue(), JSONUtil.toJsonStr(accountVo));
    、、、
    、、、拆分策略
    、、、
    log.info("【竞价结束】策略1股票池:{}",
            JSONUtil.toJsonStr(POLICY_1.values().stream().map(PolicyQue::getName).collect(Collectors.toList())));
    log.info("【竞价结束】策略2股票池:{}",
            JSONUtil.toJsonStr(POLICY_2.values().stream().map(PolicyQue::getName).collect(Collectors.toList())));
    log.info("【竞价结束】策略3股票池:{}",
            JSONUtil.toJsonStr(POLICY_3.values().stream().map(PolicyQue::getName).collect(Collectors.toList())));
    log.info("【竞价结束】策略4股票池:{}",
            JSONUtil.toJsonStr(POLICY_4.values().stream().map(PolicyQue::getName).collect(Collectors.toList())));
    log.info("【竞价结束】策略5股票池:{}",
            JSONUtil.toJsonStr(POLICY_5.values().stream().map(PolicyQue::getName).collect(Collectors.toList())));
    log.info("【竞价结束】反向指标股票池:{}",
            JSONUtil.toJsonStr(POLICY_CANCEL.stream().map(PolicyQue::getName).collect(Collectors.toList())));
}

开盘时把每个标的都丢到盯盘线程池里

@Scheduled(cron = "0 30 9 * * ?")
public void start() {
    if (todayTrade.get()) {
        log.info("【开盘->开启扫板策略监控】");
        for (PolicyQue policyQue : POLICY_3.values()) {
            MOMENTUM_POOL.submit(() -> policy3(policyQue));
        }
        for (PolicyQue policyQue : POLICY_2.values()) {
            MOMENTUM_POOL.submit(() -> policy2(policyQue));
        }
        for (PolicyQue policyQue : POLICY_1.values()) {
            MOMENTUM_POOL.submit(() -> policy1(policyQue));
        }
        OTHER_POOL.submit(this::updateAvailableAmount);
        if (CollectionUtils.isNotEmpty(POLICY_CANCEL)) {
            MOMENTUM_POOL.submit(this::policyCancel);
        }
        MOMENTUM_POOL.submit(this::policyIdle);
    }
}

拿其中一个最简单的策略举例

public void policy4(PolicyQue policyQue) {
    try {
        while (true) {
            if (DateUtil.hour(new Date(), true) >= 10) {
                log.info("【POLICY_4】10点还未触发,取消 {} 监控策略4", policyQue.getName());
                break;
            }
            Pankou pankou = stockProcessor.getPankou(policyQue.getCode());//获取盘口数据
            if (pankou.getCurrentPrice().doubleValue() == policyQue.getLimitUp().doubleValue() && pankou.getSale1() == 0) {
                order("【POLICY_4】完全上板", policyQue, pankou.getCurrentPrice(),
                        AutomaticTradingEnum.POLICY_QUE_4, null, new Date(), pankou);//下单方法
                break;
            }
            、、、
            、、、
            、、、
            //Thread.sleep(1);//追求速度,这行都直接注了
        }
        Thread.sleep(1);
    } catch (InterruptedException interruptedException) {
        log.info("【中断监控】取消策略4监控策略 {}", policyQue.getName());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

由于这个策略每天只会下单一次,在下单接口必须加锁+AtomicBoolean控制

private synchronized boolean buyOrder(Double currentPrice, String code, AutomaticTradingEnum automaticTradingEnum) {
    if (buy.get()) {
        throw new RuntimeException("【下单】今日已经下过单了,不再买入,中断策略");
    }
    、、、
    、、、
    、、、
}

这是2025-01-17的实盘数据
触发策略的历史成交数据时间戳 “timestamp”:1737077409000 = “2025-01-17 09:30:09”
2025-01-17 09:30:09.911 触发策略,下单
2025-01-17 09:30:10.013 交易所返回下单结果
基本上能1秒内触发+下单成功
在这里插入图片描述
最终成交时间大概是13:11分
在这里插入图片描述

当下单成功即可中断线程池内的其他所有盯盘线程

MOMENTUM_POOL.shutdownNow();

收盘后清除当日所有缓存,重置配置等等

@Scheduled(cron = "0 3 15 * * ?")
 public void reset() {
     todayTrade.set(false);
     POLICY_5.clear();
     POLICY_4.clear();
     POLICY_3.clear();
     POLICY_2.clear();
     POLICY_1.clear();
     STOCK_MAP.clear();
     POLICY_IDLE.clear();
     POLICY_CANCEL.clear();
     policyQueMapper.delete(new LambdaQueryWrapper<>());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值