##1. 引言##
目前项目使用了开源的vnpy框架来做量化交易(python版), 但是公司采购的wind历史数据TDBAPI没有python版(只有C++版,JAVA版,C#版,matlab版)。有两种想法:1) 把C++版的封装成python版; 2)把JAVA版封装成WEB API版。 基于项目进度和对技术的熟悉程度,目前采用第二种,即把wind原生的TDBAPI(JAVA版)封装成WEB API,无需考虑客户端的语言问题(当然在该项目中是用python调用)
##2. 开发环境## JDK: jdk-8u131-windows-x64; java version "1.8.0_131", 64bit IDE: IntelliJ idea 2017.1.3
Spring Boot
##3. 开发过程##
###3.1 创建项目###
在IntelliJ idea中创建初始项目,具体过程参照Gaussic的博客,其中由于本项目不需要数据库,因此只选择了Web,没有选择JPA和H2。另外需要新建lib文件夹,原因看下文。项目结构如下所示:
###3.2 导入jar包和dll文件###
下图是Wind api 提供的jar包,64bit 的dll文件夹(服务器64bit,也可以按需选32bit 的):
封装成web api 需要内容: tdbapi.jar, x64文件夹里面的2个dll,lib文件(dll是必须的,lib不太清楚)
下面就是如何在spring boot 里面导入本地的tadbapi.jar和需要再运行时获取的dll文件。在开发环境IntelliJ里面导入是很简单的。 1) 导入jar包: ctrl + alt + shift + s 打开 “project structure” 在 “Modules” 直接点 “+” 导入jar包。(可参照http://jingyan.baidu.com/article/fec7a1e5f79e2b1191b4e74f.html)
2) 运行时调用dll: 这就更简单了,直接在项目根目录里面添加就可以了。
以上做法在IntelliJ里面可以正常使用,但是用maven将spring boot 项目打包成jar就有问题了。一是本地jar包找不到,二是dll也找不到,三是打成jar包Test失败。具体如下:
1)maven 导入本地jar包
直接网上找了一个:http://blog.csdn.net/mayp1/article/details/53996402 新建了一个lib文件夹,把需要的jar包导入。 主要注意的是按照博客修改后的pom.xml里面写自己应用的入口。
2)运行时调用dll
在java.library.path里面能够找到dll就可以正常运行,以下是2种解决思路: a. 在程序中导入dll: System.load, System.loadLibrary b. 将dll放在java.library.path中的路径下。 花了大把时间尝试方法a,但是可能因为同阶段有其他bug的影响,快到deadline了还是没有成功。所以直接采用方法b。直接把dll和lib文件放在了jdk的bin文件夹下(不推荐)
3)打成jar包Test问题
可能是Spring boot 的test模块无法找到dll文件,在用maven打jar包的时候一直卡在Test。所以直接把Test模块给剔除了。。。(不推荐)
##3.3 功能开发##
开发环境(在IntelliJ 里) 目前需要实现的功能(目前实现Wind TDBAPI里面的部分功能)
###3.3.1 登录模块###
Wind登录模块写在Controller的构造函数里面,避免每次请求数据重新登录
###3.3.2 获取K线数据### 前三点介绍wind api中的数据结构,与其在web api中的对应。(a. Wind 中的数据结构; b. web api中的数据结构)
1) 请求K线数据结构
a. Wind 请求K线数据结构
b. Web api 请求K线数据结构
注意不需要构造函数,Controller里面会自动匹配。
2) 枚举类型
a. Wind 枚举类型
b. Web Api 枚举类型转换
为了方便客户端(时间参数: 传 0,1,2 不如传 'second', 'minute' 直观),进行下面的自动转换(switch case 很low就是了,还好类型少。。。。)
3) 返回数据类型
a. Wind 原生返回数据类型
b. web api 返回数据类
4) 请求K线数据
//获取K线数据
@RequestMapping(value = "/getKLine", method = RequestMethod.POST)
public List<TdbKLine> getKLine(@RequestBody KLineParam kLineParam) {
int m_nMaxOutputCount = 1000;
if(kLineParam.getKey() != "xxxxxxx") return null;
ReqKLine req = new ReqKLine();
req.setCode(kLineParam.getCode()); //Required
req.setMarketKey(kLineParam.getMarketKey());//Required
req.setCQFlag(kLineParam.getCqFlag()); //默认为0
req.setCQDate(kLineParam.getCqDate()); //默认为0
req.setQJFlag(kLineParam.getQjFlag()); //默认为0
req.setCycType(kLineParam.getCycType()); //Required
req.setCycDef(kLineParam.getCycDef()); //Optional,default 1
req.setAutoComplete(kLineParam.getAutoComplete()); //自动补齐(0:不补齐,1:补齐)
req.setBeginDate(kLineParam.getBeginDate()); //Optional,default today
req.setEndDate(kLineParam.getEndDate()); //Optional,default today
req.setBeginTime(kLineParam.getBeginTime()); //开始时间
req.setEndTime(kLineParam.getEndTime()); //结束时间
KLine[] kline = client.getKLine(req);
if (kline==null) {
System.out.println("NetWork Error,getKline failed!");//网络断开,可根据此消息,调用Client.close()后重连
}
List<TdbKLine> tdbKLines = new ArrayList<>();
int nIndex = 0;
for(KLine k : kline) {
if (nIndex++ > m_nMaxOutputCount) break;
TdbKLine tdbKLine = new TdbKLine(k.getWindCode(), k.getCode(), k.getDate(), k.getTime(),
k.getOpen(), k.getHigh(), k.getLow(), k.getClose(), k.getVolume());
tdbKLines.add(tdbKLine);
}
return tdbKLines;
}
###3.3.3 获取Tick数据### 与获取K线数据类似
##3.4 测试用例
test.py
#请求tick数据的参数
tick_param = {'code': 'cu00.SHF', 'marketKey':'SHF-1-0','date':20170608, 'beginTime':0, 'endTime':0}
tick_url = 'http://xxx.xxx.xxx.xx:xxxx/getTick'
#发送请求
r = requests.post(url=tick_url, json=kline_param)
#返回json数据
print(r.json())
code: github