加密货币量化交易系统的设计与实现(0.1最初版本,为了应付毕设的版本)
Posted 九重桂妖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了加密货币量化交易系统的设计与实现(0.1最初版本,为了应付毕设的版本)相关的知识,希望对你有一定的参考价值。
加密货币量化交易系统的设计与实现(0.1最初版本,为了应付毕设的版本)
注意:
写这个程序的目的是进行加密货币投资理财,但是我刚好要毕业了,需要些毕业设计,所以和导师商量了一下把原本的《基于表情识别的人工智能睡眠质量监测助手》换成了我自己的《加密货币量化交易系统的设计与实现》,这个设计里的后端服务模块(基于springboot)和 硬件动态验证令牌模块(ESP32) 都是多余的设计,为的是给论文凑字数,和满足导师对硬件的要求。
在后续版本中 后端服务将会完全用 Python 框架 FastAPI 代替掉
。
● 开发背景:毕业设计 + 个人需求
● 开发时间:2022-01-01 ~ 2022-06-01
● 工作内容 : 我在这个项目中负责的是整个项目的设计和开发。
加密货币量化交易系统,其核心工作原理是使用程序获取并处理行情数据,之后对行情数据进行实时的监测并根据量化策略产生信号。
1.1 设计思路
没过审发不了
1.2 设计方案
首先策略交易模块从加密货币交易所获取行情、订单、账户信息等数据,通过预先编写的量化策略对行情进行判断,并生成信号发送到服务器上。后端服务模块会通过接口和策略交易模块进行数据交换,在服务器上的后端服务主要是提供部分数据的存储和转发,可以获取策略模块提供的数据,并把行情数据存储在sql数据库中。浏览器可以通过访问前端程序获取前端页面,并通过页面获取当前的行情信息、策略生成的信号信息、订单、账户信息等数据,还可以对策略进行实时的回测并得到策略的回测结果,可以在线对策略的运行参数进行调整以干预策略的运行,这些数据由Vue前端程序向服务器的服务端口发送数据请求来进行交互。移动端APP会向服务器端口发送请求来获取最新的策略信号,并进行通知。登录前端和APP需要硬件动态验证令牌模块提供的一串动态验证码,硬件动态验证令牌采用ESP32为基础,动态验证令牌码的生成会先连接局域网然后从服务器获取时间戳,使用事先约定的秘钥和时间戳进行运算生成动态验证码供以验证登录系统访问账户信息和策略信息。系统框图如图1-1所示。
在这里插入图片描述
图1-1 系统框图
2.2 软件设计
系统设计中的软件程序设计与程序编写的详细思路。
2.2.1 系统软件总体设计
软件设计是基于前后端分离进行开发[4],软件设计被划分为数个模块分别是:策略交易模块、后端服务模块、Vue前端应用程序、移动端APP、硬件动态验证令牌模块。详细的模块交互方式已经在设计方案中进行过描述。
(1) 策略交易模块:可以调用交易所网络接口进行获取行情数据、获取账户信息,内置可调控的量化策略。
(2) 后端服务模块:从策略交易模块获取行情、账户信息、策略信号数据,后将行情数据存储到数据库。响应Vue前端应用程序和APP的数据请求。
(3) Vue前端应用程序:策略列表展示、发送策略切换请求、展示策略详细信息和回测结果,切换策略的运行币种,展示账户持仓、当前订单、历史成交记录,在K线上标识出策略信号,令牌验证码登录。
(4) 移动端APP:令牌验证码登录,策略信号通知。
(5) 硬件动态验证令牌模块:联网获取时间,基于时间生成动态验证口令,在前端输入口令后经过后端相同算法的验证即可完成登录。
######################
4.3 系统的主要技术框架
(1)B/S 结构
B/S 结构,即 Browser/Server(浏览器/服务器)结构,它是随着 Internet 技术的兴起,对 C/S 结构的一种改进并完善的一种结构。在 B/S 结构下,用户界面通过网页浏览 器实现与服务器的交互,有一小部分事务逻辑会在前端实现,但是主要事务逻辑还是集 中在服务器端实现。B/S 结构利用不断成熟的浏览器技术,实现更加强大的功能,极大 的节约了系统开发的成本,它是一种全新的完备的软件系统构造技术。
(2)MyBatis 框架
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可 以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通的 Java 对象)为数据库中的记录。
(3)Spring Boot 框架
Spring Boot 是伴随着 Spring 4.0 共同诞生的,它能简化 spring 的配置及开发, 并协助开发人员可以整体管理应用程序的配置而不需要做大量的配置工作,它提供了很 多开发组件,并且内嵌了 web 应用容器,如 tomcat 和 Jetty 等。其目的便是使我们的 开发变得简化并且能大幅度提高开发人员的开发效率,为了简化 Spring 功能的配置可 以引入或启动需要的 Spring 功能。这样做的好处就是避免开发人员过多的对框架的关 注,而把更多的精力与时间放在系统的业务逻辑代码中。
4.4 软件设计
(1)策略交易模块使用Python编写。主要思路:首先从交易所API获取行情、订单、账户信息等数据。初始获取行情数据时通过交易所API以天为单位循环获取历史行情数据并写入到csv文件中储存起来。策略交易模块在启动的时候会遍历读取csv文件将其转化为pandas的 DataFrame对象,随后将对象的列名修改成所需名字,将Date列指定为index这种基于时间的索引会为数据处理提供便利。策略交易模块会主动向后端服务模块发送http请求用于数据传输。内置的基础策略会根据行情数据计算出策略信号,当内置策略处于开启状态时,会主动向后端服务模块发送http请求用于传输策略信号。
(2)后端服务模块使用Java语言+Spring框架编写。主要思路:使用spring框架为基础开发,暴露出http接口来接收策略交易模块发送的请求,并在一部分接口的返回值中填入数据用以控制策略交易模块的运行。另外一部分接口用于接收前端和APP传来的请求,前端和APP的请求一部分会通过http协议的返回值返回给策略交易模块。
(3)前端主要采用html+CSS+JS语言+Vue框架编写。主要思路:编写UI界面使账户信息、行情信息、订单信息、策略信息可视化,并通过http请求发送一些对策略交易模块的控制信息给后端服务模块,后端服务模块会将请求返回给策略交易模块,已达到控制模块运行的目的。
(4)移动端APP主要采用Kotlin编写。主要思路:与Vue前端互通展示UI界面,通过向后端服务模块发送请求来查询策略信号情况,在策略产生时会显示策略信号生成的通知。
(5)硬件动态验证令牌模块主要采用Python编写。主要思路:通过连接指定的局域网WiFi来获取实时时间,内部存储着一份与服务器完全一致的密钥文件和动态口令生成程序,时间和秘钥通过口令生成程序生成一个指定的口令,用户把口令输入到登录页面,口令会被发生的后端服务模块,后端服务模块运行着一样的口令算法,在一定时间范围内产生多条口令,通过逐个对比口令判断口令是否一致,若口令一致则通过验证。
2.2.2 策略交易模块程序设计
策略交易模块使用纯Python进行开发,是整个系统的最核心组成部分。
(1) 行情数据的获取存储与实时更新
要获取加密货币交易所的行情数据,首先需要向交易所申请API key之后为了方便程序在运行时对API key的读取将其存储在一个json格式的文件中,之后设计程序在运行主方法时将这个存有秘钥的文件读取到程序中并使用json解析文件的内容之后得到秘钥以方便后续API的调用。因为不同币种交易对数量较多故需要配置一个专门的币种配置列表来告诉程序需要下载和同步哪些交易对的行情信息,为了方便人为对列表进行修改这个文件也被设计成json格式,币种配置列表文件同样会在程序初始运行时被读取。程序使其在初始运行期间会读取币种配置列表文件,并扫描其中的币种列表,将币种列表循环与本地的行情文件目录中的csv行情文件列表对比,如果没有匹配到相应的文件名称则调用交易所API进行行情数据获取,获取到的行情数据会以csv的格式储存到本地的行情文件目录中[5]。等指定的交易对的行情数据全部下载完成后,程序会根据币种列表循环依次将本地存放的csv格式的行情数据文件读取到一个全局变量klineMap中,在这个变量中以字典的形式储存所有的交易对名称信息和对应的行情数据方便后续以交易对名称直接获取行情数据。
(3) 策略交易模块中策略类
编写一个MA双均线策略来处理加密货币的行情数据,类中包含静态字段策略名称、策略简述、策略回测绘图产生的文件的储存路径、币种策略历史暂存变量、两对策略用和绘图专用的快线周期和慢线周期。编写一个生成实时回测信号的方法,对参数的数据表进行切割默认回测三十天的数据,根据币种数据表中的收盘价计算出条移动平均线,然后使用每个周期的收盘价计算出对数收益率然后根据MA线的对比来算出策略的对数收益率,之后删除掉空值,根据数据的变化点来算出多头和空头的信号点之后将行情序列数据存到对应币种的币种策略历史暂存变量中,之后将情序列数据作为函数的返回值返回给调用者。之后编写策略总回测函数使用和生成实时回测信号的方法一样的计算方法[7],计算出策略的收益图和策略收益的回撤图之后通过文件夹路径变量储存到文件夹中。策略的收益率与对应币种行情的对比图如图2-5所示。
图2-5 策略的收益率与对应币种行情的对比图
编写策略细节回测的函数,该函数使用和生成实时回测信号的方法一样的计算方法计算完成后将生成的数据切片成数天的数据之后将每个切片进行逐个绘图并储存到本地指定的文件夹中。策略的细节决策点图如图2-6所示。
图2-6 策略的细节决策点图
2.2.4 Vue前端应用程序设计
前端使用Vue框架进行编写[11],前端作为量化交易系统的可视化窗口来便捷的操控量化系统的运行。
(1) 前端账户页面
本页面需要显示交易所账户相关信息包括资产信息和部分历史订单信息。首先在js中的data中建立存放账户信息的字典变量其中包括总资产、保证金、未实现盈亏等等信息,然后建立另一个变量用于储存最近七天的交易历史订单,之后使用Vue的模板语法将data中的账户信息变量和储存最近七天的交易历史订单变量与html绑定,这样就可以将交易所的账户数据显示在网页上使用模板语法的好处是当data中的账户信息数据和账户的历史订单数据发生改变时网页上显示的数据也会随之改变而不需要进行手动刷新。
在store中的state中建立用来储存账户信息的account_information_v2变量,并建立一个无限循环不断使用callAPI_accountInfo来刷新这个储存账户信息的变量。
在生命周期函数mounted中编写一个无限循环每隔一秒调用一次callAPI_allOrders7Days发送请求到服务器来请求最近七天的历史订单,之后访问store.state.account_information_v2并将其赋值到本地data中的账户信息变量中供以刷新变量的值,在methods中定义一个将时间戳转换为可读时间字符串的方法方便阅读订单的时间。账户页面如图2-9所示。
图2-9 账户页面
(2) 前端策略页面
本页面需要显示不同策略的策略名称和简介信息,同样使用模板语法将储存在data中的策略详细信息进行展示。后面的部分根据当前默认的策略变量来匹配策略名称来展示不同策略的回测页面,可以使用策略切换按钮来切换不同的策略来进行对策略的回测,在进行回测时可以选择不同的币种交易对和根据策略名来配置策略相应的参数,并为策略回测的提交按钮配置一个监听函数当点击提交进行回测时会触发获取配置的参数并向服务器发送callAPI_backTest请求来请求回测结果,请求到的策略回测的结果会以图片的形式刷新显示在网页上。策略页面如图2-10所示。
图2-10 策略页面
(3) 前端行情页面
本页面会显示当前所选择的行情数据图像并把策略信号展示到图像上,在下方位置还可以对策略进行切换和调整策略的运行参数,还会显示当前的持仓。持仓的展示依然是使用account_information_v2变量进行模板语法绑定显示,新建一个异步的无限循环,不断的使用callAPI_getKline方法向服务器发送行情数据请求并将请求到的行情数据刷新显示到网页上,循环中也有callAPI_querySignal来不断的查询当前的策略信号刷新到行情图表中。行情页面如图2-11所示。
图2-11 行情页面
2.2.5 移动端APP程序设计
移动端APP设计采用andorid原生开发[12],开发语言采用kotlin。主要包含主页面和验证token子页面和策略信号子页面。
图3-11 策略信号页面
演示视频
加密货币量化交易系统的设计与实现(0.1最初版本,为了应付毕设的版本)
干货丨如何使用DolphinDB回放加密货币盘口与逐笔交易数据
对加密货币盘口与逐笔交易数据的回放展示,可帮助量化研究人员检验量化策略,也有助于交易员复盘,加深对市场的洞察。DolphinDB可实现盘口和逐笔交易数据的高速回放,以及对回放结果逐点查询。
DolphinDB database支持将多个分布式表同步回放并发布到流数据表,例如对盘口和交易这两个表进行同步回放。前端JavaScript使用DolphinDB Web API来轮询回放输出的流数据表,实现盘口和交易数据的可视化回放。DolphinDB自带Web服务器,整个流程可在DolphinDB内完成,无外部依赖。
加密货币盘口与逐笔交易数据回放可通过以下4个步骤来实现。用户亦可使用docker快速体验回放功能,具体请参考文末介绍。
- 部署DolphinDB节点
到官网下载DolphinDB最新版本,并部署集群。部署教程请参考单服务器集群部署教程。
- 下载盘口和逐笔交易数据
本文使用的是火币研究院提供的加密货币交易数据,可以通过火币数据API获取。获取数据的示例代码可以参考python示例代码或java示例代码。
- 导入数据到DolphinDB
本文将获取的orderBook的tick级数据保存为csv文件,通过loadTextEx函数快速地将文件导入到数据库。用户也可以通过Python API或Java API将数据导入到DolphinDB中。以下代码在DolphinDB GUI中执行。
(1)数据预处理
如果保存的csv文件中第一行是无关信息,可以采用下面脚本进行数据预处理,处理好的文件保存到某个目录,本案例将两个文件分别保存到/hdd/data/orderBook-processed和/hdd/data/tick-processes目录。如果csv文件第一行没有无关信息,可忽略这一步骤。
//删除数据文件第一行无关信息
def dataPreProcess(DIR){
if(!exists(DIR+ "-processed/"))
mkdir(DIR+ "-processed/")
fileList = exec filename from files(DIR) where isDir = false, filename like "%.csv"
for(filename in fileList){
f = file(DIR + "/" + filename)
y = f.readLines(1000000).removeHead!(1)
saveText(y, DIR+ "-processed/" + filename)
}
}
dataPreProcess("/hdd/data/orderBook")
dataPreProcess("/hdd/data/tick")
(2)创建DolphinDB数据库
根据数据量以及查询字段,数据库可按照交易标的代码和业务时间进行组合分区。本案例中,数据库的名称为dfs://huobiDB。如果需要修改,必须同时修改replay.html中数据库的名称。
def createDB(){
if(existsDatabase("dfs://huobiDB"))
dropDatabase("dfs://huobiDB")
//按照数据集的时间跨度,请自行调整VALUE分区日期范围
db1 = database(, VALUE, 2018.09.01..2018.09.30)
db2 = database(, HASH, [SYMBOL,20])
db = database("dfs://huobiDB", COMPO, [db1,db2])
}
def createTick(){
tick = table(100:0, `aggregate_ID`server_time`price`amount`buy_or_sell`first_trade_ID`last_trade_ID`product , [INT,TIMESTAMP,DOUBLE,DOUBLE,CHAR,INT,INT,SYMBOL])
db = database("dfs://huobiDB")
return db.createPartitionedTable(tick, `tick, `server_time`product)
}
def createOrderBook(){
orderData = table(100:0, `lastUpdateId`server_time`buy_1_price`buy_2_price`buy_3_price`buy_4_price`buy_5_price`buy_6_price`buy_7_price`buy_8_price`buy_9_price`buy_10_price`buy_11_price`buy_12_price`buy_13_price`buy_14_price`buy_15_price`buy_16_price`buy_17_price`buy_18_price`buy_19_price`buy_20_price`sell_1_price`sell_2_price`sell_3_price`sell_4_price`sell_5_price`sell_6_price`sell_7_price`sell_8_price`sell_9_price`sell_10_price`sell_11_price`sell_12_price`sell_13_price`sell_14_price`sell_15_price`sell_16_price`sell_17_price`sell_18_price`sell_19_price`sell_20_price`buy_1_amount`buy_2_amount`buy_3_amount`buy_4_amount`buy_5_amount`buy_6_amount`buy_7_amount`buy_8_amount`buy_9_amount`buy_10_amount`buy_11_amount`buy_12_amount`buy_13_amount`buy_14_amount`buy_15_amount`buy_16_amount`buy_17_amount`buy_18_amount`buy_19_amount`buy_20_amount`sell_1_amount`sell_2_amount`sell_3_amount`sell_4_amount`sell_5_amount`sell_6_amount`sell_7_amount`sell_8_amount`sell_9_amount`sell_10_amount`sell_11_amount`sell_12_amount`sell_13_amount`sell_14_amount`sell_15_amount`sell_16_amount`sell_17_amount`sell_18_amount`sell_19_amount`sell_20_amount`product,[INT,TIMESTAMP,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,DOUBLE,SYMBOL])
db = database("dfs://huobiDB")
return db.createPartitionedTable(orderData, `orderBook, `server_time`product)
}
(3)将文本数据导入数据库
def loadTick(path, filename, mutable tb){
tmp = filename.split("_")
product = tmp[1]
file = path + "/" + filename
t = loadText(file)
t[`product]=product
tb.append!(t)
}
def loopLoadTick(mutable tb, path){
fileList = exec filename from files(path,"%.csv")
for(filename in fileList){
print filename
loadTick(path, filename, tb)
}
}
def loadOrderBook(path, filename, mutable tb){
tmp = filename.split("_")
product = tmp[1]
file = path + "/" + filename
t = loadText(file)
t[`product] = product
tb.append!(t)
}
def loopLoadOrderBook(mutable tb, path){
fileList = exec filename from files(path, "%.csv")
for(filename in fileList){
print filename
loadOrderBook(path, filename, tb)
}
}
login("admin","123456")
tb = createOrderBook()
loopLoadOrderBook(tb, "/hdd/data/orderBook-processed")
tb = createTick()
loopLoadTick(tb, "/hdd/data/tick-processed")
(4)定义数据回放函数
def replayData(productCode, startTime, length, rate){
login(\'admin\', \'123456\');
tick = loadTable(\'dfs://huobiDB\', \'tick\');
orderbook = loadTable(\'dfs://huobiDB\', \'orderBook\');
schTick = select name,typeString as type from tick.schema().colDefs;
schOrderBook = select name,typeString as type from orderbook.schema().colDefs;
share(streamTable(100:0, schOrderBook.name, schOrderBook.type), `outOrder);
share(streamTable(100:0, schTick.name, schTick.type), `outTick);
enableTablePersistence(objByName(`outOrder), true,true, 100000);
enableTablePersistence(objByName(`outTick), true,true, 100000);
clearTablePersistence(objByName(`outOrder));
clearTablePersistence(objByName(`outTick));
share(streamTable(100:0, schOrderBook.name, schOrderBook.type), `outOrder);
share(streamTable(100:0, schTick.name, schTick.type), `outTick);
enableTablePersistence(objByName(`outOrder), true,true, 100000);
enableTablePersistence(objByName(`outTick), true,true, 100000);
endTime = temporalAdd(startTime, length, "m")
sqlTick = sql(sqlCol("*"), tick, [<product=productCode>, <server_time between timestamp(pair(startTime, endTime))>]);
sqlOrder = sql(sqlCol("*"), orderbook, [<product=productCode>, <server_time between timestamp(pair(startTime, endTime))>]);
cutCount = length * 60 / 20
trs = cutPoints(timestamp(startTime..endTime), cutCount);
rds = replayDS(sqlTick, `server_time , , trs);
rds2 = replayDS(sqlOrder, `server_time , , trs);
return submitJob(\'replay_huobi\',\'replay_huobi\', replay, [rds,rds2], [`outTick,`outOrder],`server_time ,, rate);
}
addFunctionView(replayData);
- 数据回放
下载数据回放界面的html压缩包,下载地址:https://github.com/dolphindb/applications/raw/master/cryptocurr_replay/replay.zip。将replay.zip解压到DolphinDB程序包的web目录。
在浏览器地址栏中输入http://[host]:[port]/replay.html打开数据回放界面。这里的host和port是指数据节点的IP地址和端口号,如http://192.168.1.135:8902/replay.html。
数据回放前,我们可以设置以下参数:
- Product:加密货币代码
- Replay Rate:回放速度,即每秒钟回放的记录数。如果市场每秒钟产生100笔交易,Replay Rate设置为1000就以10倍的速度回放。
- Start Time:数据的起始时间
- Length:数据的时间跨度,单位是分钟。如果Start Time设置为2018.09.17 00:00:00,Length设置为60,表示回放的数据是在2018.09.17 00:00:00-2018.09.17 00:59:59之间产生的。
回放结束后,点击左上角正方形图标按钮(“结束”按钮)。单击价格趋势图中的点,表格中会显示该时间点之前的10笔数据。具体操作请查看图片https://raw.githubusercontent.com/dolphindb/Tutorials_CN/master/images/replay/v.gif。
使用docker快速体验回放功能
我们提供了一个包含DolphinDB Server以及演示数据的docker容器,并打包成tar文件提供下载。用户仅需要安装docker环境,下载打包文件,运行下面的命令就可以快速完成演示环境部署。
tar文件下载地址:https://www.dolphindb.cn/downloads/cryptocurr_replay.tar.gz
gunzip cryptocurr_replay.tar.gz
##docker若没有赋予非管理访问权限,可以使用 sudo docker
docker import cryptocurr_replay.tar ddb/replay:v1
##生成并启动容器
docker run -dt -p 8888:8848 --name replay1 ddb/replay:v1 /bin/bash /dolphindb/start.sh
启动容器后,docker内 DolphinDB database 的访问端口被映射到宿主机8888端口,打开浏览器访问http://[宿主机ip]:8888/replay.html
,进入到回放演示界面。
为了控制docker容器大小,方便下载,演示数据仅包含2018.09.17一天的加密货币编号为ETHUSDT,ETHBTC,BTCUSDT
的交易数据。
注意:因为内置的license文件会过期, 需要从官网下载最新的社区版license替换。下载社区版解压后进入server目录下,拷贝dolphindb.lic文件覆盖docker中的/dolphindb/目录下同名文件。
sudo docker cp ./dolphindb.lic replay1:/dolphindb/dolphindb.lic
重启docker:
sudo docker restart replay1
注意事项
- 本案例当前仅限单用户使用,不支持多用户同时回放。
- 为了简化操作,数据库名称和数据库用户信息均固化在网页中,若有需要请自行修改replay.html文件
使用中有任何问题,欢迎加入智臾科技:DolphinDB技术交流群,内含二维码!
以上是关于加密货币量化交易系统的设计与实现(0.1最初版本,为了应付毕设的版本)的主要内容,如果未能解决你的问题,请参考以下文章