Node.js股票模拟交易后台

我曾经花了一周时间开发了一个股票模拟交易后台程序,使用Node.js。代码量很少,能完成基本功能。下面给大家介绍一下其实现步骤。

基本功能

  • 开户
  • 搜索股票
  • 挂单(多单、空单)
  • 撤单(主动、被动)
  • 成交(非撮合)
  • 除权、除息
  • 查询
    • 订单状态
    • 持仓
    • 今日委托
    • 今日成交
    • 历史委托
    • 历史成交
    • 挂单列表
    • 账户详情(总收益,收益率,总资产)

其中模拟交易和真实交易最大的不同是,真实交易采用撮合制,逻辑较为复杂。模拟交易采用更简单的即时成交机制,只要符合条件,订单立即成交。

这个后台程序一共就两个js文件,一个用于处理成交,即判断成交条件,写数据库。另一个处理其他逻辑。当然这里面没有提到获取股票实时价格的问题,这是另一个系统完成,我们通过消息队列实时获取我们所关心的股票的价格,这是另一个话题了。

这个后台程序以一个node.js进程的方式运行,一个10秒一次的定时器执行成交判断。(真实交易所的撮合器也是10秒钟一次)

此外有一个WebAPI Server接受来自客户端的请求。所以总体架构,可以看成是一个微服务组成的系统。

架构图

数据库设计

账户表

`Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模拟账户',
  `MemberCode` varchar(20) DEFAULT '' COMMENT '用户编号',
  `AccountNo` varchar(255) DEFAULT NULL COMMENT '账号',
  `TranAmount` int(11) DEFAULT NULL COMMENT '模拟账户入资金额',
  `CommissionLimit` decimal(20,4) DEFAULT '2.9900' COMMENT '最低佣金',
  `CommissionRate` decimal(20,4) DEFAULT '0.0125' COMMENT '佣金比例',
  `Cash` decimal(20,4) DEFAULT '0.0000' COMMENT '现金',
  `UsableCash` decimal(20,4) DEFAULT '0.0000' COMMENT '可用资金',
  `Status` tinyint(4) DEFAULT '1' COMMENT '账号状态:1正常',
  `AccountType` tinyint(4) DEFAULT '1' COMMENT '账号类型:1现金账号,2保证金账号',
  `CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`Id`)

其中一个用户可以对应多个账户,所以有一个AccountNo作为区分。
TranAmount为初始资金,用于重置账户。佣金字段用于模拟交易的手续费和税费。可用资金字段是,当用户挂单的时候有一部分资金处于冻结状态,可用资金就是去除冻结资金的金额。

订单表

  `Id` int(11) NOT NULL AUTO_INCREMENT COMMENT '模拟交易订单表',
  `MemberCode` varchar(20) DEFAULT '' COMMENT '用户编号',
  `AccountNo` varchar(20) DEFAULT '' COMMENT '模拟账号',
  `SecuritiesType` varchar(10) DEFAULT '' COMMENT '股票类型:us,hk,sh,sz',
  `SecuritiesNo` varchar(20) DEFAULT '' COMMENT '股票编号',
  `CPrice` decimal(20,4) DEFAULT '0.0000' COMMENT '委托价',
  `Price` decimal(20,4) DEFAULT '0.0000' COMMENT '价格',
  `OrderQty` decimal(20,4) DEFAULT '0.0000' COMMENT '股票数据量',
  `Side` char(1) DEFAULT '' COMMENT '交易类型:B买、S卖',
  `OrdType` tinyint(4) DEFAULT '1' COMMENT '订单类型:1市场订单、2限价订单、3止损订单、4做空市场订单、5做空限价订单、6做空止损订单',
  `execType` tinyint(4) DEFAULT '1' COMMENT '执行类型:0新的,1成交、2取消、3拒绝',
  `Commission` decimal(20,4) DEFAULT '2.9900' COMMENT '佣金',
  `Reason` tinyint(4) DEFAULT '0' COMMENT '订单拒绝理由:0正常、1资金不足、2仓位不足、3超时失效',
  `Amount` decimal(20,4) DEFAULT '0.0000' COMMENT '金额',
  `EndTime` datetime DEFAULT NULL COMMENT '订单截止时间',
  `CreateTime` datetime DEFAULT NULL COMMENT '订单时间',
  `TurnoverTime` datetime DEFAULT NULL COMMENT '成交时间',
  PRIMARY KEY (`Id`)

这是最重要的两张表,其他几张表就不罗列详细的内容,只做简单说明

  • 资产表(记录浮动盈亏,持仓金额,各种时间范围的收益率)
  • 额外津贴记录表(记录除权,除息)
  • 资金记录表(记录特殊资金变动)
  • 仓位表
  • 仓位记录表(记录仓位变化)
  • 做空仓位记录表
  • 排行榜

挂单

挂单的核心就是向数据库插入一条记录,不过即便是简洁的js代码,也差不多写了80行代码。
首先就是一系列的判断,是否可以创建订单。

  • 参数是否在取值范围内。
  • 市价单类型,判断是否开市,未开盘时间段不能创建订单。
  • 账户异常状态不能创建订单。
  • 如果是卖多单,或者买空单,则要把仓位数据取出来判断,是否仓位够扣。
  • 如果是买多单,或者卖空单,则要计算扣除佣金(手续费)后可用资金够不够。
  • 如果是限价单或者是止损单,则判断价格设置是否在有效范围内。
    然后执行一个数据库事务,插入一条订单记录,同时修改可交易仓位或者可用资金。

撤单

撤单比挂单简单许多。主要步骤就是先判断订单是否存在,然后修改订单状态,同时修改可交易仓位或者可用资金。

模拟交易主进程

系统每隔10秒执行一次逻辑。

所有订单缓存策略

如果每隔10秒钟从数据库读取所有订单的话,效率会很低,而且过多占用数据库IO资源。所以订单数据都缓存在成交判断的进程内存中。将来也可以升级为使用redis等内存数据库来存储。
当有订单创建的时候,通过消息队列通知进程。当进程重启的时候,从数据库读取数据进行初始化。

超时订单处理

有些订单一直没有满足成交条件,但已经超过交易时间,所以要进行处理。(订单状态设置为拒绝)

成交判断

未开盘则跳过。
根据订单类型判断是否达到成交条件

'订单类型:1市场订单、2限价订单、3止损订单、4做空市场订单、5做空限价订单、6做空止损订单'
Price:订单设置的价格
price:当前股价
B:买入
S:卖出

let trigge = false
        switch (OrdType) {
            case 1:
                trigge = true;
                break;
            case 2:
            case 3:
                trigge = Side == "BS" [OrdType - 2] ? (Price >= price) : (Price <= price)
                break;
            case 4:
                trigge = true;
                break;
            case 5:
            case 6:
                trigge = Side == "BS" [6 - OrdType] ? (Price >= price) : (Price <= price)
                break;
        }

执行成交

最初是用程序执行的,后来为了执行效率和数据一致性,采用存储过程。
首先,我们需要查询出账户的现金和可用资金,以及仓位信息。
如果是卖多或者买空(减少持仓,增加现金),我们计算出此时需要增加的金额,当然这个时候可能出现仓位不够的情况,就拒绝订单。
如果是买多或者卖空(增加持仓,减少现金),我们就需要计算此时需要扣除的金额,如果出现可用金额不足,就拒绝订单。
最后,我们修改账户的实际金额和可用金额,写入持仓记录和现金变化记录,修改订单状态为已成交状态。

信息查询

普通数据库查询,这里不多赘述了。

除权、除息

由于模拟交易系统无法第一时间自动得到除权和除息的消息,所以当需要进行除权和除息的操作的时候,可能用户已经发生成交的订单。这时候需要根据持仓记录变更表进行一些计算,恢复正确的持仓,如果是除息就是根据现金记录变更表,进行资金重新计算。最后我们把这次操作的日志记录下来。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 金融知识大全(最全整理版) 本文实战财经 转自 优维金融空间,转载请注明来源! 第一部分:银行系金融知识大全! 1...
    cnnjzc阅读 6,227评论 0 14
  • 来源:实战财经 转自 优维金融空间 导读:包含银行系金融知识和基金证券类知识大全。 目录 第一部分:银行系金融知识...
    1e662a7de34a阅读 9,890评论 0 9
  • LOOPRING(路印) 去中心化代币交易撮合协议 1.5 版 daniel@loopring.org alex@...
    xiaobinZh阅读 2,570评论 0 51
  • 一觉睡到自然醒,下午三点起来。然后去西区取快递,忍不住买了六个泡芙吃,一个煎饼果子,一个菠萝,八小包的鱿鱼丝,煮了...
    馨晴Sophia阅读 97评论 0 0
  • 遇见你 在这样的年纪 有点懵懂 有点稚气 有爱有恨 有悲有喜 正直我们的最青春 我就在最好的年纪里 遇见你 时光流...
    廿姝阅读 221评论 0 0