如何建立回测系统(三)

如何建立回测系统(三)

优化版本


public class MarketPlace { private Map<String, WriteLockMap<Long, OrderQueen>> sellOrderChannel; private Map<String, WriteLockMap<Long, OrderQueen>> buyOrderChannel; private Map<String, Long> lastComplete; public MarketPlace() { this.sellOrderChannel = new ConcurrentHashMap<>(); this.buyOrderChannel = new ConcurrentHashMap<>(); this.lastComplete = new ConcurrentHashMap<>(); } public void order(Order order) { OrderQueen orderQueen; WriteLockMap<Long, OrderQueen> longOrderQueenMap; String fixCacheString = order.getTradeCode().intern(); if (order.sell()) { longOrderQueenMap = buyOrderChannel.get(order.getTradeCode()); if (longOrderQueenMap == null) { synchronized (fixCacheString) { longOrderQueenMap = new WriteLockMap<>(new ConcurrentHashMap<>()); buyOrderChannel.put(order.getTradeCode(), longOrderQueenMap); } } } else { longOrderQueenMap = sellOrderChannel.get(order.getTradeCode()); if (longOrderQueenMap == null) { synchronized (fixCacheString) { longOrderQueenMap = new WriteLockMap<>(new ConcurrentHashMap<>()); sellOrderChannel.put(order.getTradeCode(), longOrderQueenMap); } } } orderQueen = longOrderQueenMap.get(order.getPrice()); if (orderQueen == null) { longOrderQueenMap.lock(); orderQueen = new OrderQueen(order.getPrice()); longOrderQueenMap.put(order.getPrice(), orderQueen); longOrderQueenMap.unlock(); } Long price = orderQueen.try2ClinchOrder(order); if (price != null) { this.lastComplete.put(order.getTradeCode(), price); } if (!order.isComplete()) { if (order.sell()) { WriteLockMap<Long, OrderQueen> tm = this.sellOrderChannel .get(order.getTradeCode()); if (tm == null) { synchronized (fixCacheString) { WriteLockMap<Long, OrderQueen> writeLockMap = new WriteLockMap<>( new ConcurrentHashMap<>()); OrderQueen newOne = new OrderQueen(order.getPrice()); newOne.addOrder(order); writeLockMap.put(order.getPrice(), newOne); this.sellOrderChannel.put(order.getTradeCode(), writeLockMap); } } else { OrderQueen orderQueen1 = tm.get(order.getPrice()); if (orderQueen1 == null) { tm.lock(); orderQueen1 = new OrderQueen(order.getPrice()); orderQueen1.addOrder(order); tm.put(order.getPrice(), orderQueen1); tm.unlock(); } } } else { WriteLockMap<Long, OrderQueen> tm = this.buyOrderChannel .get(order.getTradeCode()); if (tm == null) { synchronized (fixCacheString) { WriteLockMap<Long, OrderQueen> writeLockMap = new WriteLockMap<>( new ConcurrentHashMap<>()); OrderQueen newOne = new OrderQueen(order.getPrice()); newOne.addOrder(order); writeLockMap.put(order.getPrice(), newOne); this.buyOrderChannel.put(order.getTradeCode(), writeLockMap); } } else { OrderQueen orderQueen1 = tm.get(order.getPrice()); if (orderQueen1 == null) { tm.lock(); orderQueen1 = new OrderQueen(order.getPrice()); orderQueen1.addOrder(order); tm.put(order.getPrice(), orderQueen1); tm.unlock(); } else { orderQueen1.addOrder(order); } } } } } public Long getLastPrice(String security) { return this.lastComplete.get(security); } }

public class OrderQueen { //最近完成的订单位置 private int lastCompleteIndex; private int lastInsertIndex; private int lastInsertCacheIndex; //归属价格级别 private long priceLevel; //订单队列 private Order[] orders; private Order[][] finishCache; public OrderQueen(long priceLevel) { this.priceLevel = priceLevel; orders = new Order[5000]; finishCache = new Order[500][]; lastCompleteIndex = -1; lastInsertIndex = 0; } private boolean needExpendOrderList() { return orders.length <= lastInsertIndex; } private void expand() { Order[] orders = new Order[5000]; this.finishCache[lastInsertCacheIndex] = this.orders; lastInsertCacheIndex++; this.orders = orders; lastInsertIndex = 0; lastCompleteIndex = -1; } public long getPriceLevel() { return priceLevel; } public synchronized void addOrder(Order order) { if (needExpendOrderList()) { expand(); } this.orders[lastInsertIndex] = order; lastInsertIndex++; } public synchronized Long try2ClinchOrder(Order order2Clinch) { Long clinchPrice = null; for (int index = lastCompleteIndex + 1; index < lastInsertIndex; index++) { Order order = this.orders[index]; long count; try { count = order2Clinch.getRemainCount() - order.getRemainCount(); } catch (NullPointerException e) { throw new NullPointerException(); } if (count < 0) { long completeCount = order.getRemainCount() - Math.abs(count); order2Clinch.addCompleteCount(completeCount); order2Clinch.complete(); order.addCompleteCount(completeCount); clinchPrice = order.getPrice(); break; } else if (count == 0) { order2Clinch.addCompleteCount(order.getRemainCount()); order2Clinch.complete(); order.addCompleteCount(order.getRemainCount()); order.complete(); clinchPrice = order.getPrice(); lastCompleteIndex++; break; } else { order2Clinch.addCompleteCount(order.getRemainCount()); order.addCompleteCount(order.getRemainCount()); order.complete(); clinchPrice = order.getPrice(); lastCompleteIndex++; } } return clinchPrice; } }

性能分析

  1. 减少每次对全部订单进行筛选排序,分出买卖单
  2. 针对国内涨跌幅限制,对价格进行分级,每个价格中订单按照时间排序

测试发现,50W的数据从原来的80000ms降低到100ms,效果显著,多线程测试亦无问题

如何建立回测系统(二)

如何建立回测系统(二)

一个简单的回放市场实现

public class MarketPlace {

    private Map<String, List<Order>> orderChannel;
    private Map<String, Long> lastComplete;

    public MarketPlace() {
        orderChannel = new ConcurrentHashMap<>();
        lastComplete = new ConcurrentHashMap<>();
    }

    public void order(Order order) {
        List<Order> list = orderChannel.get(order.getTradeCode());
        if (Objects.isNull(list)) {
            list = new ArrayList<>();
            orderChannel.put(order.getTradeCode(), list);
        }
        synchronized (list) {
            List<Order> targetPriceOrder;
            List<Order> sellOrder = list.stream()
                .filter(order1 -> !order1.isComplete())
                .filter(Order::sell)
                .sorted(Comparator.comparing(Order::getPrice)
                    .thenComparing(Order::getTimestamp))
                .collect(Collectors.toList());
            List<Order> buyOrder = list.stream()
                .filter(order1 -> !order1.isComplete())
                .filter(order1 -> !order1.sell())
                .sorted(Comparator.comparing(Order::getPrice, Comparator.reverseOrder())
                    .thenComparing(Order::getTimestamp))
                .collect(Collectors.toList());
            //进来的是买单
            if (!order.sell()) {
                if (!buyOrder.isEmpty() && buyOrder.get(0).getPrice() > order.getPrice()) {
                    list.add(order);
                    return;
                }
                //处理好后,是按照价格小,时间先排列
                targetPriceOrder = sellOrder.stream()
                    //需要找到挂单中比当前订单要价低或者相等的
                    .filter(order1 -> order.getPrice() <= order1.getPrice())
                    .collect(Collectors.toList());
            } else {
                if (!sellOrder.isEmpty() && sellOrder.get(0).getPrice() < order.getPrice()) {
                    list.add(order);
                    return;
                }
                //进来的是卖单,找买单 , 卖单进来,先开始撮合最高价的买单
                targetPriceOrder = buyOrder.stream()
                    //需要找到买单中比当前订单要价高的
                    .filter(order1 -> order.getPrice() <= order1.getPrice())
                    .collect(Collectors.toList());
                //新入订单需要撮合的数量
            }
            //新入订单需要撮合的数量
            long count = order.getRemainCount();
            //连续竞价,买单进来,开始先撮合最低价的卖单
            for (Order order1 : targetPriceOrder) {
                count = count - order1.getRemainCount();
                if (count < 0) {
                    //说明当前订单已经满足,order不需要进入队列排队,order1更新成交数,中断循环
                    order1.addCompleteCount(order1.getCount() - Math.abs(count));
                    order.addCompleteCount(order.getCount());
                    order.complete();
                    this.lastComplete.put(order.getTradeCode(), order.getPrice());
                    break;
                } else if (count == 0) {
                    //说明订单恰好满足,中断循环,将order1添加移除队列
                    order.addCompleteCount(order.getCount());
                    order1.addCompleteCount(order1.getCount());
                    order1.complete();
                    order.complete();
                    this.lastComplete.put(order.getTradeCode(), order.getPrice());
                } else {
                    //说明订单未满足,将order1添加移除队列,并继续循环
                    order1.complete();
                    order.addCompleteCount(order1.getRemainCount());
                }
                count = order.getRemainCount();
            }
            if (!order.isComplete()) {
                list.add(order);
            }
        }
    }

    public Long getLastPrice(String security) {
        return this.lastComplete.get(security);
    }

性能分析

最高对单个标的物单线程,且订单越多,处理速度越慢

T = f(m*n)

可能的优化点

  1. 减少每次对全部订单进行筛选排序,分出买卖单
  2. 针对国内涨跌幅限制,对价格进行分级,每个价格中订单按照实践排序
  3. 尽可能用基础类型

如何建立回测系统

如何建立回测系统

什么是回测

就是对市场的变化进行回放,加入我们的策略得出,如果我们参与其中会有怎么样的结果

如何回放

首先要了解平时我们说的股票当日价格是如何形成的

股票一旦出售后本身是没有价格的,在每个交易日开始前,交易所会进行集中竞价,最后取买卖集合中的笔数最多的价格作为当日的开盘价

紧接着开始连续竞价

连续竞价中,股票价格为股票的成交价,成交价如下定义:

1. 最高买进申报与最低卖出申报相同,则该价格即为成交价格;

2. 买入申报价格高于即时揭示的最低卖出申报价格时,以即时揭示的最低卖出申报价格为成交价

3. 卖出申报价格低于即时揭示的最高买入申报价格时,以即时揭示的最高买入申报价格为成交价

总结

若需要对股市状况进行回测,首先应该保存当日所有的订单情况,然后对价格进行撮合,若撮合成功,则此时可以算出当前价格

平时我们接受的数据成为股市交易的快照

其中记录着一段时间内的成交量,成交均价,成交总价格等的信息