SimulatedExchangeEngine
DolphinDB 的 SimulatedExchangeEngine 插件实现了一个完整的模拟交易所系统,支持接收行情快照数据与交易员委托订单,实时构建订单簿,支持将不同的订单委托撮合成交。该插件可输出交易明细与订单明细,为量化策略回测提供真实的交易环境模拟。
安装插件
版本要求
DolphinDB Server 2.00.16 和 3.00.4,支持 Linux x86-64。
注:该插件仅支持 DolphinDB 商业版。
安装步骤
-
在 DolphinDB 客户端中使用
listRemotePlugins命令查看插件仓库中的插件信息。注:仅展示当前操作系统和 server 版本支持的插件。若无预期插件,可在 DolphinDB 用户社区进行反馈。
login("admin", "123456") listRemotePlugins() -
使用
installPlugin命令完成插件安装。installPlugin("SimulatedExchangeEngine") -
使用
loadPlugin命令加载插件。loadPlugin("SimulatedExchangeEngine")
接口说明
createSimulatedExchangeEngine
SEEPlugin::createSimulatedExchangeEngine(name, config, quoteColMap)详情
创建一个模拟交易所。返回一个句柄,可以用于其他接口。
参数
-
name 字符串标量,交易所唯一标识,不能为空字符串。
-
config 键为 STRING 类型的字典,表示模拟交易所引擎的配置项。目前支持的配置如下表:
配置项 类型/形式 说明 marketType STRING 必填,市场类型,目前仅支持 “CFETS_XBOND”。 outputInterval INT 选填,表示输出快照的最小间隔,单位为毫秒,默认值为 3000。可选值:
-
小于等于 0:表示不输出快照;
-
大于等于 1000:当快照发生变化时,按此间隔输出快照。
forceTriggerInterval INT 选填,表示输出快照的最大间隔,单位为毫秒。默认值为 60000。可选值:
-
小于等于 0:表示不强制输出快照;
-
大于等于 1000:当快照长时间无变化时,超过 forceTriggerInterval 也会强制输出一次快照。
注:当 forceTriggerInterval 生效时(≥ 1000),其值必须大于 outputInterval。
sessionBegin SECOND 向量 选填,表示快照输出时间区间的起止时刻。传入的时间区间必须按顺序从早到晚递增,且不能重叠。 sessionEnd SECOND 向量 quotePriority INT 必填,表示行情撮合的优先级:
-
0:表示用户订单优先与用户订单撮合;
-
1:表示用户订单优先与行情虚拟订单撮合。
checkQuote INT 选填,表示是否会校验债券报价数据的合理性,即 bidYTM, bidYTC, askClean, askDirty 为严格递增,bidDirty, askTYM, askYTC 为严格递减;dirtyPrice, cleanPrice 的买一价小于卖一价,YTM, YTC 的买一价大于卖一价。可选值:
-
1:默认值,校验;
-
0:不校验上述内容,由用户自行保证。
user 表 选填,表示用户信息表。表结构如下:
-
userName,STRING,用户名称;
-
userId,INT,用户 ID;
-
password,STRING,用户密码;
-
isValid,BOOL,用户是否有效。
userGroup 表 选填,表示用户组信息表。表结构如下:
-
groupName,STRING,用户组名称;
-
groupId,INT,用户组 ID;
-
groupType,STRING,用户组类型;
-
priority,INT,用户组优先级;
-
userId,INT[],该用户组包含的用户 ID。
riskControl 表 选填,表示风控配置表。表结构如下:
-
userId,INT,用户 ID;
-
maxDailyOrderCount,INT/LONG,每日最大订单数量限制;
-
maxDailyTradeAmount,LONG/DOUBLE,每日最大交易金额限制;
-
orderSubmissionRate,INT,订单提交速率限制;
-
priceDeviationLimit,DOUBLE,价格偏差限制;
-
yieldDeviationBP,DOUBLE,收益率偏差限制(基点)。
blackWhiteList 表 选填,表示黑白名单配置表。表结构如下:
-
groupId,INT,用户组 ID;
-
black,INT[],黑名单用户 ID;
-
white,INT[],白名单用户 ID。
bondDetail 表 选填,表示债券详细信息表。表结构如下:
-
symbol,STRING,债券代码;
-
symbolSource,STRING,债券来源;
-
bondType,STRING,债券类型;
-
maturity,DOUBLE,到期期限;
-
duration,DOUBLE,久期;
-
prevCleanPrice,DOUBLE,前一日净价;
-
prevDirtyPrice,DOUBLE,前一日全价;
-
prevYTM,DOUBLE,前一日到期收益率;
-
prevYTC,DOUBLE,前一日赎回收益率;
-
internalRating,STRING,内部评级;
-
externalRating,STRING,外部评级;
-
issuanceMethod,STRING,发行方式。
注:上述表中,STRING 类型的列也可以支持 SYMBOL 类型。
-
-
quoteColMap 一个字典,类型为 (string, string),用于映射行情表中的列名。映射规则为“必须的列名 → 实际表中的列名”。必须的列名如下表所示:
名称 表中对应列的类型 说明 symbol STRING 标的 symbolSource STRING 交易所 settlType INT 清算速度,可选值包括:
-
0:T0,当日清算;
-
1:T1,交易日后一个工作日清算;
-
2:T2,交易日后两个工作日清算。
bidQty LONG[] 买数量 bidYTM DOUBLE[] 买到期收益率 bidYTC DOUBLE[] 买行权收益率(可选) bidCleanPrice DOUBLE[] 买委托净价 bidDirtyPrice DOUBLE[] 买委托全价(可选) askQty LONG[] 卖数量 askYTM DOUBLE[] 卖到期收益率 askYTC DOUBLE[] 卖行权收益率(可选) askCleanPrice DOUBLE[] 卖委托净价 askDirtyPrice DOUBLE[] 卖委托净价(可选) YTM DOUBLE 最新到期收益率 YTC DOUBLE 最新行权收益率(可选) cleanPrice DOUBLE 最新净价 dirtyPrice DOUBLE 最新全价(可选) -
insertMsg
语法
SEEPlugin::insertMsg(engine, msg)
详情
插入深度行情表。
参数
-
engine 交易所句柄。
-
msg 行情表。表结构在首次调用接口后将被锁定,不可修改。表中必须包含在创建交易所时,由 quoteColMap 指定的列。
注:
-
行情中 askYield 必须是递减排序,bidYield 必须是递增排序,否则交易所行为未定义。
-
askYield、askQty、askSettlType 长度相同;bidYield、bidQty、bidSettlType 长度相同。
submitOrder
语法
SEEPlugin::submitOrder(engine, msg)
详情
插入用户订单。撤单请使用 cancelOrder。
参数
-
engine 交易所句柄。
-
msg 一个元组,表示用户订单信息。按顺序包含如下信息:
-
symbol:STRING 类型标量,表示标的。
-
symbolSource:STRING 类型标量,表示市场。
-
orderType:整型标量,目前只支持 1,表示限价单。
-
settlType:整型标量,表示清算速度,可选值包括:0-当日清算,1-交易日后一个工作日清算,2-交易日后两个工作日清算。
-
matchPrice:字符串标量。表示价格类型。
-
direction:整型标量。可选值包括:1-买,2-卖。
-
price:浮点型标量,表示价格。
-
qty:整型标量,表示数量。
-
userOrderId:字符串标量,表示用户自定义编号。
-
traderId:整型标量,表示交易员编号。
-
timeInForce:整型标量,表示委托订单有效性。可选值包括:0-默认,1-FOK,2-FAK。
-
expirationTime:TIME 类型标量,表示委托订单的到期时间,可填空值。非空时要求时间晚于当前时间,否则无效。
-
label:字符串标量,表示标签。
-
dropSimulatedExchangeEngine
语法
SEEPlugin::dropSimulatedExchangeEngine(engine)
详情
删除该交易所。
参数
-
engine 交易所句柄。
getOutputSchema
语法
SEEPlugin::getOutputSchema(engine, outputType)
详情
获取输出表的结构。
参数
-
engine 交易所句柄。
-
outputType 字符串标量,支持 orderDetails、tradeDetails 以及 snapshot。
snapshot 表结构参见 getSnapshot 章节。
orderDetails 表结构如下:
| 列名 | 类型 | 说明 |
|---|---|---|
| symbol | STRING | 标的 |
| submitTime | TIMESTAMP | 下单时间 |
| writeTime | TIMESTAMP | 写入表时间 |
| symbolSource | STRING | 市场 |
| orderType | INT | 委托类型 |
| settlType | INT | 清算速度 |
| matchPrice | STRING | 价格类型 |
| direction | INT | 方向 |
| price | DOUBLE | 价格 |
| qty | LONG | 量 |
| userOrderId | STRING | 客户自定义编号 |
| traderId | INT | 交易员号 |
| timeInForce | INT | 委托订单有效性 |
| label | STRING | 标签 |
| orderId | LONG | 订单号 |
| status | INT |
委托状态。其中: 0:未成交,2:部分成交,4:已成交,5:全部撤单,6:部分撤单,7:拒单 |
| remainedQty | LONG | 未成交量 |
| turnover | DOUBLE | 成交总额 |
| orderInfo | STRING | 订单拒单等时的详细信息(正常订单该字段为空) |
tradeDetails 表结构如下:
| 列名 | 类型 | 说明 |
|---|---|---|
| symbol | STRING | 标的 |
| settlType | INT | 清算速度 |
| tradeTime | TIMESTAMP | 成交时间 |
| tradeId | LONG | 成交编号 |
| bidUserOrderId | STRING | 客户自定义买编号 |
| askUserOrderId | STRING | 客户自定义卖编号 |
| symbolSource | STRING | 市场 |
| direction | INT | 方向 |
| tradePrice | DOUBLE | 成交价格 |
| matchPrice | STRING | 价格类型 |
| tradeQty | LONG | 成交数量 |
| bidOrderId | LONG | 买委托订单号 |
| askOrderId | LONG | 卖委托订单号 |
| bidTraderId | INT | 买交易员 |
| askTraderId | INT | 卖交易员 |
setOutput
语法
SEEPlugin::setOutput(engine, output)
详情
设置输出表。如果设置了快照表,满足快照输出条件时,将当前快照输出到该表中。
参数
-
engine 交易所句柄。
-
output 键为 STRING 类型的字典,用于设置输出表。字典的 key 支持 orderDetails、tradeDetails 以及 snapshot。
getSnapshot
语法
SEEPlugin::getSnapshot(engine, symbolSource, symbol, settlType, matchPrice)
详情
获取快照。
参数
-
engine 交易所句柄。
-
symbolSource 字符串标量,表示市场。
-
symbol 字符串标量,用于指定标的。
-
settlType 整型标量,表示清算速度,支持 0 (T0)、1 (T1)、2 (T2)。
-
matchPrice 字符串标量,表示价格类型。
返回的表结构如下:
| 列名 | 类型 | 说明 |
|---|---|---|
| symbol | STRING | 标的代码 |
| symbolSource | STRING | 市场 |
| timestamp | TIMESTAMP | 时间 |
| settlType | INT | 清算速度 |
| matchPrice | STRING | 价格类型 |
| bidQty | LONG[] | 报买量(元) |
| bidPrice | DOUBLE[] | 报买到期收益率、行情收益率、净价或者全价 |
| bidParty | INT[] | 买报价方,traderId 数组 |
| askQty | LONG[] | 卖单数量列表 |
| askPrice | DOUBLE[] | 报买到期收益率、行情收益率、净价或者全价 |
| askParty | INT[] | 卖报价方,traderId 数组 |
| lastPrice | DOUBLE | 最新成交价 |
| tradeQty | LONG | 总成交数量 |
cancelOrder
语法
SEEPlugin::cancelOrder(engine, orderId, [traderId], [label=""])
详情
根据订单 ID 取消订单,或者根据 traderID 和 label 取消订单。
当 orderID 不为空值时,系统根据指定的订单 ID 执行撤单操作。
当 orderID 为空值时,系统根据 traderID 和 label 撤单。
参数
-
engine 交易所句柄。
-
orderID 整型标量,订单 ID。
-
traderID 可选参数,字符串标量,交易员号。
-
label 可选参数,字符串标量,订单标签。
getOrderStatus
语法
SEEPlugin::getOrderStatus(engine, orderId, [traderId])
详情
根据订单 ID orderId 或者交易员号 traderId 获取订单信息。当 orderId 非空时,按照订单 ID 获取信息,当 orderId 为空时,按照 traderId 获取信息。
返回的表的结构与 orderDetails 表相似,仅缺少 writeTime 列。
参数
-
engine 交易所句柄。
-
orderId 整型数值,表示订单 ID。
-
traderId 整型数值。
resetSimulatedExchangeEngine
语法
SEEPlugin::resetSimulatedExchangeEngine(engine)
详情
重置交易所。取消所有尚未成交的用户订单,清空内部的所有行情。
参数
-
engine 交易所句柄。
getEngineList
语法
SEEPlugin::getEngineList()
详情
获取当前存在的所有交易所引擎。返回一个 key 为 name、value 为 engine 的字典对象。
setSecurityReference
语法
SEEPlugin::setSecurityReference(engine, securityReferenceData)
详情
设置基本信息表,用于追加或修改 config 中的基础信息。
参数
-
engine 交易所句柄。
-
securityReferenceData 一个字典,表示可以动态增加相应的基本表数据。其 key 为 user, userGroup, riskControl, blackWhiteList, bondDetail。
使用示例
下例展示 SimulatedExchangeEngine 插件各接口的基本使用方法。
-
加载/安装插件
// 安装插件 login("admin", "123456") listRemotePlugins() installPlugin("SimulatedExchangeEngine") loadPlugin("SimulatedExchangeEngine") -
准备数据
// 创建用户表 user = table(1:0, `userName`userId`password`isValid, [STRING,INT,STRING,BOOL]) for(userId in 1..22){ insert into user values("user"+string(userId), userId, "123456", true) } // 创建用户组表 userGroup = table(1:0, `groupName`groupId`groupType`priority`userId, [STRING,INT,STRING,INT,INT[]]) insert into userGroup values("group1", 1, "做市", 1, [[1,2,3,4,5,6,7,8,9,10]]) insert into userGroup values("group2", 2, "经营", 31, [[13,14]]) insert into userGroup values("group3", 3, "经营", 32, [[15,16]]) insert into userGroup values("group4", 4, "经营", 33, [[17,18]]) insert into userGroup values("group5", 5, "经营", 34, [[19,20]]) insert into userGroup values("group6", 6, "做市", 3, [[11,12]]) insert into userGroup values("group7", 7, "经营", 36, [[21,22]]) // 定义价格转换函数 def convert(matchType, value){ if(matchType == 'YTM'){ t = (5 - value) \ 4.5 }else if(matchType == 'YTC'){ t = sqrt((5 - value) \ 4.5) }else if(matchType == 'CleanPrice'){ t = (value - 20) \ 20 }else{ t = sqrt((value - 20) \ 20) } YTM = 5 - 4.5 * t YTC = 5 - 4.5 * pow(t, 2) CleanPrice = 20 + 20 * t DirtyPrice = 20 + 20 * pow(t, 2) return {'YTM': YTM, 'YTC': YTC, 'CleanPrice': CleanPrice, 'DirtyPrice': DirtyPrice} } // 创建债券详细信息表 symbol = lpad(string(0..30), 5, "0") bondType = take("国债", size(symbol)) maturity = take(1.0, size(symbol)) duration = take(1.0, size(symbol)) price1 = convert("CleanPrice", 25.58735610) price2 = convert("CleanPrice", 25.387) prevCleanPrice = take([25.58735610, 25.387], size(symbol)) prevDirtyPrice = take([price1.DirtyPrice, price2.DirtyPrice], size(symbol)) prevYTM = take([price1.YTM, price2.YTM], size(symbol)) prevYTC = take([price1.YTC, price2.YTC], size(symbol)) internalRating = take("AAA", size(symbol)) externalRating = take("AAA", size(symbol)) issuanceMethod = take("YEAR", size(symbol)) bondDetail = table(symbol, take("ESP", size(symbol)) as symbolSource, bondType, maturity, duration, prevCleanPrice, prevDirtyPrice, prevYTM, prevYTC, internalRating, externalRating, issuanceMethod) -
插件各接口的基本使用方法
// 创建模拟交易所引擎 config = dict(STRING, ANY) config["marketType"] = "CFETS_XBOND" config["quotePriority"] = 0 config["user"] = user config["userGroup"] = userGroup config["bondDetail"] = bondDetail quoteColMap = dict( `symbol`symbolSource`settlType`bidQty`bidYTM`bidYTC`bidCleanPrice`bidDirtyPrice`askQty`askYTM`askYTC`askCleanPrice`askDirtyPrice`YTM`YTC`cleanPrice`dirtyPrice, `symbol`symbolSource`settlType`bidQty`bidYTM`bidYTC`bidCleanPrice`bidDirtyPrice`askQty`askYTM`askYTC`askCleanPrice`askDirtyPrice`YTM`YTC`cleanPrice`dirtyPrice ) engine = SEEPlugin::createSimulatedExchangeEngine("simulatedTest", config, quoteColMap) // 创建输出表 // 获取订单明细表结构 orderSchema = SEEPlugin::getOutputSchema(engine, "orderDetails") // 获取成交明细表结构 tradeSchema = SEEPlugin::getOutputSchema(engine, "tradeDetails") // 获取快照表结构 snapshotSchema = SEEPlugin::getOutputSchema(engine, "snapshot") // 创建输出表 tradeTable = table(1:0, `symbol`settlType`tradeTime`traderID`bidUserOrderID`askUserOrderID`symbolSource`direction`tradePrice`matchPrice`tradeQty`bidOrderID`askOrderID`bidtraderID`asktraderID, [STRING,INT,TIMESTAMP,LONG,STRING,STRING,STRING,INT,DOUBLE,STRING,LONG,LONG,LONG,INT,INT]) // 设置输出表 outputDict = dict(STRING, ANY) outputDict = dict(STRING, ANY) outputDict["tradeDetails"] = tradeTable SEEPlugin::setOutput(engine, outputDict) // 提交用户订单 // 卖单 SEEPlugin::submitOrder(engine, ("00001", "ESP", 1, 2, "YTM", ORDER_SEL, 3.9, 300000, "1", 1, DEFAULT, NULL, "")) // 买单 SEEPlugin::submitOrder(engine, ("00001", "ESP", 1, 2, "YTM", ORDER_BUY, 3.78, 350000, "1", 2, DEFAULT, time(), "")) // 插入行情数据 price1 = each(convert{"CleanPrice"}, [23.67, 23.78, 23.8, 23.9, 24.0, 24.1]) price2 = each(convert{"CleanPrice"}, [23.65, 23.4, 23.3, 23.2, 23.1, 23.0]) price3 = convert("CleanPrice", 23.66) SEEPlugin::insertMsg(engine, ("00001", "ESP", 2, [100000, 100000, 100000, 100000, 100000, 100000], price2.YTM, price2.YTC, price2.CleanPrice, price2.DirtyPrice, [100000, 100000, 100000, 100000, 100000, 100000], price1.YTM, price1.YTC, price1.CleanPrice, price1.DirtyPrice, price3.YTM, price3.YTC, price3.CleanPrice, price3.DirtyPrice)) // 查询订单状态 // 根据交易员 ID 查询 res = SEEPlugin::getOrderStatus(engine, , 1) select * from res // 根据订单 ID 查询 res = SEEPlugin::getOrderStatus(engine, 1, ) select * from res // 获取指定标的快照 snapshot = SEEPlugin::getSnapshot(engine, "ESP", "00001", 2, "YTM") select * from snapshot // 根据订单 ID 撤单 SEEPlugin::cancelOrder(engine, 1) // 修改基本信息 // 设置黑白名单 blackwhite = table( [1, 3] as groupId, arrayVector([3, 5], [1, 3, 6, 1, 6]) as black, arrayVector([1, 2], [int(), int()]) as white ) SEEPlugin::setSecurityReference(engine, {"blackWhiteList":blackwhite}) // 设置风控配置 userId = user.userId n = size(userId) maxDailyOrderCount = long(100 + rand(10000, n)) maxDailyTradeAmount = long(200000000 + rand(10000, n)) orderSubmissionRate = take(200, n) priceDeviationLimit = take(200.0, n) yieldDeviationBP = take(200.03, n) riskTb = table(userId, maxDailyOrderCount, maxDailyTradeAmount, orderSubmissionRate, priceDeviationLimit, yieldDeviationBP) SEEPlugin::setSecurityReference(engine, {"riskControl":riskTb}) // 获取交易所列表 // 获取所有交易所引擎 engineList = SEEPlugin::getEngineList() engineList.keys() // 通过名称获取引擎句柄 myEngine = engineList["simulatedTest"] // 重置交易所 SEEPlugin::resetSimulatedExchangeEngine(engine) // 删除交易所引擎 SEEPlugin::dropSimulatedExchangeEngine(engine)
