交易委托账本

交易委托账本(OrderBook)是整个撮合引擎里最核心也是最复杂的数据结构,每个交易对都需要维护一份交易委托账本,账本里保存着指定交易对所有待撮合的委托单。每份账本都有两个队列,一个卖单队列和一个买单队列,两个队列都需要按照价格优先、时间优先的原则进行排序。

所谓价格优先、时间优先,即是说:卖单队列的委托单是按价格由低到高排序,买单队列则相反,按价格由高到低排序;相同价格的委托单,则是按下单时间的先后来排序。

如上图,每个小方格表示一个委托单,标 H 的是排在头部的委托单,N则是与 H 同价格但下单时间上排在 H 后面的委托单,S则是下一档位价格的第一个委托单。可以从图中明显看出,横向上,委托单是按时间排序的,竖向上,又是按价格排序的。

撮合的时候,都是先取出 H 委托单与新委托单进行匹配。如果新委托单是买单,则获取卖单队列的 H 单出来匹配;如果新委托单是卖单,则获取买单队列的 H 单。如果 H 单全部匹配成交了,那标识为 N 的委托单就变成了新的 H 单。如果第一排的全部委托单都匹配完了,那就 S 单会变成新的 H 单。

交易委托账本可支持一些操作方法,包括初始化、增加买卖委托单、移除买卖委托单、获取头部委托单等。交易委托账本的类图大概如下:

其中,getHead 和 popHead 方法的区别是:get 只读头部委托单但不会移除它,而 pop 会将头部委托单从队列中移除。

订单队列

买单队列和卖单队列可以设计为使用统一的订单队列类型,两者只有价格排序方向不同,那订单队列就可以用一个属性来表示排序方向。队列里的所有订单可以采用二维数组或二维链表来保存,考虑到主要操作是插入和删除,用链表比用数组效率更高。如果想让操作效率更高,那就需要使用更复杂的数据结构了,比如再结合跳表。目前版本为了简单,采用简单的二维链表即可。

使用二维链表的话,那链表中的每个元素保存的就是横向上按时间排序的订单链表,这些订单链表又组成了竖向上按价格排序的链表。

另外,还可以保存一个 Map,将价格作为 Key,将同价格的订单链表作为 Value,这样就能加快同价格订单的查询。

订单队列可支持的操作方法也很简单,包括初始化、新增订单、移除订单、获取头部订单等。其类图大概如下:

sortBy 指定价格排序的方向,**parentList **保存整个二维链表,第一维以价格排序,第二维以时间排序,elementMap 则是 Key 为价格、Value 为第二维订单链表的键值对。

委托单

委托单则是撮合引擎里最基本的数据结构了,其数据主要是从上游服务传输过来的,其类图大概如下:

action 声明对委托单要进行哪种操作,我们只需支持两种操作:下单(create)和撤单(cancel)。symbol 指定该委托单所属的交易对,orderId 是该委托单的唯一标识,side 指明是买入(buy)还是卖出(sell)。**type 表示交易类型,即限价交易(limit)或市价交易(market)**等,我们的 MVP 版本只支持限价交易。**amount **是购买数量,price 是购买价格,timestamp 则是订单时间。

**toJson() **和 fromJson() 方法是为了支持订单数据传输时的序列化和反序列化。

成交记录

撮合成交的委托单就会生成对应的成交记录,成交记录需要发布到 MQ 给下游服务消费。成交记录的数据结构如下图:

maker 指挂单,是本来挂在交易委托账本里的订单,而 taker 则是吃单,是指吃掉 maker 的订单。**makerId **和 takerId 就是挂单和吃单的订单 ID。takerSide 就是吃单的买卖方向,我们在行情软件里看到的成交记录会有不同颜色,就是由这个 takerSide 决定的。**amount **就是成交数量,price 指成交价格,**timestamp **是成交时间。

Redis缓存

我们需要用 Redis 缓存委托单数据和撮合中的交易对数据,主要有两个作用,一是可以对请求做去重处理,二是程序重启后可以恢复数据。

由于网络中断或延迟,或其他异常情况,上游服务有可能会重复发送相同请求给到撮合引擎,因此,程序是需要做去重处理的,有了数据缓存就可以解决去重问题了。另外,由于我们采用的是内存撮合,撮合时的数据都是直接保存在程序内存里的,一旦程序退出了,那所有数据也都消失了,重启后就需要从其他地方重新加载数据,采用Redis缓存就可以很快速地缓存数据和加载数据。

开启一个交易对引擎时需要将交易对缓存,关闭时则从缓存中删除,保证缓存的都是运行中的交易对,当重启时,就可以重新启动这些交易对的撮合引擎了。需要缓存的交易对数据包括两个:symbol 和 price,即标识和价格。关于价格,每次产生新的成交记录时,价格也需要同步更新,因此价格的更新会非常频繁。而标识基本无需更新,因此两者最好分开缓存。

所有交易对的 symbol 可以统一缓存到一个 set 里。我们可以将 key 值设置为 matching:symbols,用 Redis 的 sadd 和 srem 命令将不同的 symbol 缓存到该 key 值里或从 key 中删除。而 price 则可以保存为 string 类型,为不同交易对的价格设置不同的 key,key 值可以设置为 matching:price:{symbol},{symbol} 为具体交易对的 symbol 值。

每个委托单也需要缓存和更新,为了能够从缓存中最快地读取和更新委托单数据,最好为每个委托单都设置一个单独的 key,key 值可以设置为 matching:order:{symbol}:{orderId}:{action},而 value 值建议设置为 hash 类型,因为 hash 类型特别适合存储结构化的对象。

交易对和委托单数据都缓存了,就能够解决去重问题和程序重启后重新启动各交易对的撮合引擎了,但其实还有一个问题,撮合引擎里的交易委托账本如何恢复?该问题先留给大伙去思考,后续章节我再来讲解我的方案。

小结

撮合引擎里涉及到的数据结构其实并不多,最复杂的也只有交易委托账本,其设计还会直接关系到撮合的速度。Redis 缓存的设计也有些学问在里面,设计得不好也一样会影响整体的撮合性能。本小节完成了数据结构的设计,下一小节我们就开始深入到代码实现。

最后,请抽时间研究下遗留的思考题:撮合引擎里的交易委托账本如何恢复?

php+撮合引擎,撮合引擎开发:数据结构设计相关推荐

  1. 数据结构设计_撮合引擎开发:数据结构设计

    价值超5万的撮合引擎:开篇 价值超5万的撮合引擎:MVP版本 交易委托账本 交易委托账本(OrderBook)是整个撮合引擎里最核心也是最复杂的数据结构,每个交易对都需要维护一份交易委托账本,账本里保 ...

  2. 华为快应用引擎架构及开发实践

    目 录 1 快应用技术架构 1.1快应用介绍及其特点 1.2华为快应用引擎架构简介 1.2.1 应用开发(前端框架 + 组件& API 能力) 1.2.2 系统整合(应用管理,卡片-嵌入式SD ...

  3. SuperSQL:跨数据源、跨DC、跨执行引擎的高性能大数据SQL中间件

    导语:SuperSQL是腾讯数据平台部自研的跨数据源.跨数据中心.跨执行引擎的统一大数据SQL分析平台/中间件,支持对接适配多类外部开源SQL执行引擎,如Spark.Hive等. 背景 SuperSQ ...

  4. java版开源工作流引擎ccflow从表数据数据源导入设置

    为什么80%的码农都做不了架构师?>>>    关键字 驰骋工作流引擎 流程快速开发平台 workflow ccflow jflow  .net开源工作流 从表数据导入设置 概要说明 ...

  5. 火山引擎云原生大数据在金融行业的实践

    本文整理自火山引擎云原生计算研发工程师-张云尧 在 DataFun 智能金融峰会上的演讲.大数据架构向云原生演进是行业的重要趋势,火山引擎协助关键金融客户在大数据云原生方向进行了深度实践,形成了整体解 ...

  6. 大数据时代:大数据引擎或改变大数据竞争格局

    对于传统企业而言,无需任何繁杂的技术手段,只需要接入百度大数据引擎,即可利用大数据去帮助现有业务进行升级和创新了.峰哥认为百度此举是在加大此次大数据台风的风力.换一种角度看,这也是百度为了快速丰富各行 ...

  7. 聚播微信群控云控引擎二次开发SDK服务端对接接口

    聚播微信群控云控引擎二次开发SDK服务端对接接口 case HeartBeatReq: {// 客户端发送的心跳包heartBeatReqHandler.handleMsg(ctx, msgVo);b ...

  8. 火山引擎 DataLeap:「数据血缘」踩过哪些坑?来看看字节跳动内部进化史

    动手点关注 干货不迷路 DataLeap 是火山引擎数智平台 VeDI 旗下的大数据研发治理套件产品,帮助用户快速完成数据集成.开发.运维.治理.资产.安全等全套数据中台建设,降低工作成本和数据维护成 ...

  9. Quick BI产品核心功能大图(四):Quick引擎加速--十亿数据亚秒级分析

    简介: 随着数字化进程的深入,数据应用的价值被越来越多的企业所重视.基于数据进行决策分析是应用价值体现的重要场景,不同行业和体量的公司广泛依赖BI产品制作报表.仪表板和数据门户,以此进行决策分析. 在 ...

最新文章

  1. java jar包 和 war包 区别
  2. C++操作Redis的简单例子
  3. Python绘图 二维、三维
  4. 服务器上的VGA切换原理,VGA切换器使用方法和常见问题说明
  5. 概述---《TCP/IP协议》卷一
  6. OpenGL Multi-Indirect Draw小行星的实例
  7. shell中的小括号与大括号
  8. [原创] 指针操作程序答案 — 谭浩强C语言习题答案
  9. tomcat.exe java home,tomcat.exe启动和startup.bat启动的不同
  10. Pocket英语语法---三、英语动词的特点是什么
  11. Java OpenCV之Mat类的概述、常用构造方法、常用函数
  12. Mybatis自定义分布式二级缓存实现与遇到的一些问题解决方案!
  13. Code-NFine:NFine权限控制
  14. Ubuntu 18.04 LTS版本 GoldenDict安装与配置
  15. android 功能防抖,Android RxJava 实战系列:功能防抖
  16. 股票量化对冲策略的黄金时期要来了?
  17. deep learning编程作业总结1---喵咪识别
  18. python多个文件夹合并成一个文件夹
  19. RISC_V(0) 指令集架构
  20. APP——功耗测试(耗电测试)——基础知识

热门文章

  1. CP-ABE和KP-ABE
  2. 读《测试构架师修炼之道》-Chapter3 测试构架师应该做的事
  3. 动态规划之扔鸡蛋(或手机)问题
  4. [047量化交易]python获取股票 量比 换手率 市盈率-动态 市净率 总市值 流通市值
  5. 女程序员和男程序员有区别吗?
  6. 微信蓝牙设备连接不上原因总结(5)
  7. 怎样实现ZBrush中Magnify膨胀笔刷的应用
  8. python战反爬虫:爬取猫眼电影数据 (二)(Requests, BeautifulSoup, MySQLdb,re等库)
  9. Linux(SLES)挂载NTFS移动硬盘实践
  10. linux 中etc全拼,英语中“等等”缩写成为etc.吗?要加一点吗?全拼是...