数据库
1. 股票账户表
tbl_stockaccount
字段 | 类型 | 默认值 | 说明 |
---|---|---|---|
uuid | varchar(36) | 主键 | |
aliasname | varchar(255) | 别名 | |
accountcode | varchar(36) | 交易用户ID | |
password | varchar(36) | 交易用户密码 | |
brokerage | varchar(36) | virtual | 券商 fake:假的 virtual:虚拟的 |
type | varchar(36) | normal | 类型 normal:普通 try:试练操盘手 trade:操盘手 |
exchangerate | double(11,2) | 0 | 交易手续费 |
followid | varchar(36) | 跟随交易用户ID | |
status | int(3) | 0 | 当前状态 0:正常 2:异常 |
2. 交易命令历史表 每日一张表
tbl_commandhistoryYYYYMMDD
字段 | 类型 | 默认值 | 说明 |
---|---|---|---|
uuid | varchar(36) | 主键 | |
stockaccountid | varchar(36) | 账户ID | |
stockcode | varchar(6) | 股票代码 | |
type | varchar(20) | 类型 buy:买入 sell:卖出 | |
scale | double(11,9) | 0 | 比例 买入交易金额占余额比例 卖出股票数量占持仓比例 |
balance | double(11,3) | 0 | 交易前余额 |
holdcount | int(11) | 0 | 交易前持仓 |
count | int(11) | 0 | 交易数量 |
status | int(3) | 0 | 当前状态 0:正常 2:异常 |
pid | varchar(36) | 父命令ID | |
dir | varchar(36) | 所在文件夹 组ID | |
error | varchar(255) | 错误信息 | |
starttime | varchar(20) | 操作开始时间 | |
endtime | varchar(20) | 操作结束时间 | |
operatetime | varchar(20) | 开始执行时间 | |
operateduration | int(11) | 0 | 执行时长 |
totalduration | int(11) | 0 | 执行总时长 |
接口
Servlet
1. 开启股票账户
/servlet/openstockaccount
此接口需要先到虚拟交易所开户,才可以使用
Request:
{
"aliasname":"lichen", //别名 英文中文手机号
"password":"12345678", //密码 6~20位 任意
"accountcode":"qwertyuioasdfghjkzxcvbnm", //交易所用户ID
"brokerage":"virtual", //券商
"type":"normal", //类型 normal:普通 try:试练 trade:操盘手
"exchangerate":"2.5" //交易费率
}
Response:
{
"statusCode":"000000",
"result":{
"uuid":"dsaf6d87sfa89sdf7asdfhasdilfakds",
"aliasname":"lichen",
"password":"1a2bc3d4e5f6g7h8i9j0k",
"accountcode":"qwertyuioasdfghjkzxcvbnm",
"brokerage":"virtual",
"exchangerate":"2.5",
"type":"normal",
"status":"0",
"followid":""
}
}
2. 关闭股票账户
/servlet/closestockaccount
Request:
{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //账户ID
}
Response:
{
"statusCode":"000000",
"result":"ok"
}
3. 修改密码
/servlet/changepassword
此接口 需要先到虚拟交易所修改后,这里才能生效
Request:
{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //账户ID
"password":"12345678", //密码 6~20位 任意
}
Response:
{
"statusCode":"000000",
"result":{
"uuid":"dsaf6d87sfa89sdf7asdfhasdilfakds",
"aliasname":"lichen",
"password":"1a2bc3d4e5f6g7h8i9j0k",
"accountcode":"qwertyuioasdfghjkzxcvbnm",
"brokerage":"virtual",
"exchangerate":"2.5",
"type":"normal",
"status":"0",
"followid":""
}
}
4. 修改类型
/servlet/changetype
Request:
{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //账户ID
"type":"try", //类型 normal:普通 try:试练 trade:操盘手
}
Response:
{
"statusCode":"000000",
"result":{
"uuid":"dsaf6d87sfa89sdf7asdfhasdilfakds",
"aliasname":"lichen",
"password":"1a2bc3d4e5f6g7h8i9j0k",
"accountcode":"qwertyuioasdfghjkzxcvbnm",
"brokerage":"virtual",
"exchangerate":"2.5",
"type":"try",
"status":"0",
"followid":""
}
}
5. 跟随操盘手
/servlet/followtrader
此账户需要是 普通账户,并且跟随的操盘账户需要是试练或者操盘手
Request:
{
"stockaccountid":"234jk32khi2u4wejfq432nrkew23jknk", //账户ID
"password":"12345678", //密码 6~20位 任意
"followid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //跟随操盘账户ID
}
Response:
{
"statusCode":"000000",
"result":{
"uuid":"234jk32khi2u4wejfq432nrkew23jknk",
"aliasname":"xiaoming",
"password":"1a2bc3d4e5f6g7h8i9j0k",
"accountcode":"kmfjo3m2f98dsn4iund8s4nijruvh2",
"brokerage":"virtual",
"exchangerate":"2.5",
"type":"normal",
"status":"0",
"followid":"dsaf6d87sfa89sdf7asdfhasdilfakds"
}
}
6. 解除跟随操盘手
/servlet/unfollowtrader
Request:
{
"stockaccountid":"234jk32khi2u4wejfq432nrkew23jknk", //账户ID
"password":"12345678", //密码 6~20位 任意
}
Response:
{
"statusCode":"000000",
"result":{
"uuid":"234jk32khi2u4wejfq432nrkew23jknk",
"aliasname":"xiaoming",
"password":"1a2bc3d4e5f6g7h8i9j0k",
"accountcode":"kmfjo3m2f98dsn4iund8s4nijruvh2",
"brokerage":"virtual",
"exchangerate":"2.5",
"type":"normal",
"status":"0",
"followid":""
}
}
7. 买入股票
/servlet/buystock
Request:
{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //账户ID
"password":"12345678", //密码 6~20位
"stockcode":"601988", //股票代码
"count":"1000" //买入数量
"execute":"true", //是否真实执行
}
Response:
{
"statusCode":"000000",
"result":{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds"
"stockcode":"601988",
"type":"buy",
"price":"7.12",
"count":"1000",
"holdstockbefore":"0",
"holdstockafter":"1000",
"balancebefore":"46000",
"balanceafter":"38862.2",
"exchangeamount":"17.8",
"stockamount":"7120",
"totalamount":"7137.8"
}
}
8. 买出股票
/servlet/sellstock
Request:
{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds", //账户ID
"password":"12345678", //密码 6~20位
"stockcode":"601988", //股票代码
"count":"1000" //买入数量
"execute":"true", //是否真实执行
}
Response:
{
"statusCode":"000000",
"result":{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds"
"stockcode":"601988",
"type":"sell",
"price":"7.12",
"count":"1000",
"holdstockbefore":"0",
"holdstockafter":"1000",
"balancebefore":"38862.2",
"balanceafter":"45964.4",
"exchangeamount":"17.8",
"stockamount":"7120",
"totalamount":"7102.2",
"createtime":"2017-06-13 15:18:32"
}
}
9. 统计数据
/servlet/commandcount
Request:
{
"duration":"100", //间隔时间
"samplerate":"2", //采样率
}
Response:
{
"statusCode":"000000",
"result":{
"stockaccountid":"dsaf6d87sfa89sdf7asdfhasdilfakds"
"stockcode":"601988",
"type":"sell",
"price":"7.12",
"count":"1000",
"holdstockbefore":"0",
"holdstockafter":"1000",
"balancebefore":"38862.2",
"balanceafter":"45964.4",
"exchangeamount":"17.8",
"stockamount":"7120",
"totalamount":"7102.2",
"createtime":"2017-06-13 15:18:32"
}
}
Cache存储结构
|名称|说明|结构|
|:--|:--:|
|cachekey_stockaccount_uuid|账户集合|Map<uuid,StockAccountMap>|
|cachekey_stockaccount_accountcode|账户名集合|Map<accountcodeid, StockAccountMap >|
| cachekey_stockaccount_followid|跟随账户集合|Map<followid, List<StockAccountMap>>|
|cachekey_stockdata_600189|股票信息|JSON|
数据算法
配置文件
配置项 | 默认值 | 说明 |
---|---|---|
TASK_THREADPOOL_SIZE | 100 | 任务执行者数量 |
REQUEST_TRADE_THREADPOOL_SIZE | 10 | http请求线程数量 |
REQUEST_TRADE_MAXCOUNT | 100 | 单词请求最大命令数量 |
IS_FAKE_TRADE | false | 是否使用假交易所 |
MAX_NEW_COMMAND_COUNT | 最大新指令数量 | |
TRADE_VIRTUAL_BASEURL | 虚拟交易所地址 | |
COMMAND_FILE_ROOT | 指令文件夹 |
文件夹
hero
为父命令文件夹
每一个父命令均创建一个命令组文件夹,组文件夹名是每个小时做一个基础名,后面跟着自增数列,每个文件夹中只有一个文件,就是操盘手的指令文件。
new
为命令文件夹
从hero
文件夹里面将父指令拷贝到new文件夹中,并通过查询跟随账户,为每一个跟随账户创建指令文件。最后将hero
中的父命令文件删除。
这是一个命令放大器,如果被放大的命令太多,会导致inodes过大,所以在配置文件中指定了一个最大新指令数量。
dispatched
为分发文件夹
分发任务从new
文件夹里获得任务,并移动到dispatched
文件夹中,这些待执行命令按照账户分别分配给了每个账户的执行者,有单独线程驱动执行中进行一步一步执行。
执行者总共有6步:
1. 获取一个命令,并按照账户写入命令文件(每天一个),将文件从`dispatched`移动到`process`中
2. 发送查询请求,等待响应
3. 获得查询结果,将查询结果保存到命令对象中
4. 发送交易请求,等待响应
5. 获得交易结果,将交易结果保存到命令对象中
6. 事后处理,补写账户命令文件,将文件写入到`finished`中,并删除`process`中的文件
database
为数据库文件夹
将完成的命令,写入到入库文件中new.db,此文件所在的文件夹为每分钟一个。当数据库准备执行时,将此文件改名为process.db
history
为数据库历史文件夹
入库完成后,将process.db文件内容写入到历史文件中,此文件每小时一个,并删除process.db文件
new
为命令文件夹
dispatched
为分发文件夹
process
为执行文件夹
finished
为完成文件夹
这4个文件夹结构完全一致,分别代表任务所处阶段。
文件夹示例
[-]/CacheFile/SyncCommand
[-]hero
[-]GID20160814165223_00000000009.cmd #组ID
[+]a1s23d3d4f5g6h76j87jk90.cmd #操盘手命令
[-]new
[-]GID20160814165223_00000000007.cmd #组ID
[+]a1s23d3d4f5g6h76j87jk90.cmd #操盘手命令
[+]s23d4f5g6h7j8k9k8j7h6g5.cmd #跟随命令
[+]z12x3c4v5b6n78m8mn7b6.cmd #跟随命令
[+]4c3xz28u7y6t5r4e3w2q3e.cmd #跟随命令
[-]GID20160814165223_00000000008.cmd #组ID
[+]a1s23d3d4f5g6h76j87jk90.cmd #操盘手命令
[+]s23d4f5g6h7j8k9k8j7h6g5.cmd #跟随命令
[+]z12x3c4v5b6n78m8mn7b6.cmd #跟随命令
[+]4c3xz28u7y6t5r4e3w2q3e.cmd #跟随命令
[-]dispatched
[-]GID20160814165223_00000000005.cmd #组ID
[+]z12x3c4v5b6n78m8mn7b6.cmd #跟随命令
[+]4c3xz28u7y6t5r4e3w2q3e.cmd #跟随命令
[-]GID20160814165223_00000000006.cmd #组ID
[+]a1s23d3d4f5g6h76j87jk90.cmd #操盘手命令
[+]s23d4f5g6h7j8k9k8j7h6g5.cmd #跟随命令
[+]z12x3c4v5b6n78m8mn7b6.cmd #跟随命令
[-]process
[-]GID20160814165223_00000000005.cmd #组ID
[+]a1s23d3d4f5g6h76j87jk90.cmd #操盘手命令
[+]s23d4f5g6h7j8k9k8j7h6g5.cmd #跟随命令
[-]GID20160814165223_00000000006.cmd #组ID
[+]4c3xz28u7y6t5r4e3w2q3e.cmd #跟随命令
[-]database
[-]201608141622.cmd #每分钟一个文件夹
[+]process.db #正在入库的文件
[-]201608141623.cmd #每分钟一个文件夹
[+]new.db #待入库的文件
[-]201608141624.cmd #每分钟一个文件夹
[+]new.db #待入库的文件
[-]history
[+]2016081415.cmd #每小时一个文件
[+]2016081416.cmd #每小时一个文件
技巧
线程使用技巧
当处于高频HTTP发送的时候,有两种解决方案:
1.使用单线程异步请求
做一个任务队列,和请求类列表(回调使用),然后单线程来获得队列中任务,制作成请求,调用异步请求,并将请求ID作为Attribute传出,在异步获得结果后,取出请求ID以及结果,在请求类列表中找到指定的请求,将结果作为参数去调用回调方法。
此方案优点: 写法简单明了 缺点:单线程调用
注意: 异步请求
2.使用多线程同步请求
做指定线程数量的线程池,和任务执行实例,做一个请求任务队列,然后单线程来获得一个执行类,判断是否是空闲,并让线程池启动执行类,执行类启动后,从任务列表中获取任务,制作成请求,调用同步请求,获得结果后,将结果作为参数去调用请求的回调方法。
此方案优点: 多线程调用 可以指定若干执行实例,由线程池控制执行线程数 缺点:结构复杂,不便于维护
注意:在线程池执行任务前,需要先判断线程是否运行,这里防止多任务进入单线程,将前任务覆盖。