demoStrategy
This commit is contained in:
parent
9dd2d7f120
commit
8b5d27f46c
@ -1,14 +1,46 @@
|
|||||||
use BackTest;
|
use BackTest;
|
||||||
|
|
||||||
create table TB_Trade
|
CREATE TABLE `TB_Trade` (
|
||||||
|
`Id` varchar(255) DEFAULT NULL,
|
||||||
|
`symbol` varchar(20) DEFAULT NULL,
|
||||||
|
`orderRef` int(11) DEFAULT '0',
|
||||||
|
`tradeID` int(11) DEFAULT '0',
|
||||||
|
`direction` tinyint(4) DEFAULT NULL,
|
||||||
|
`offset` tinyint(4) DEFAULT NULL,
|
||||||
|
`price` float DEFAULT NULL,
|
||||||
|
`volume` int(11) DEFAULT NULL,
|
||||||
|
`tradeTime` datetime DEFAULT NULL,
|
||||||
|
`amount` float DEFAULT '0',
|
||||||
|
`fee` float DEFAULT '0',
|
||||||
|
`profit` float DEFAULT '0',
|
||||||
|
`profitRate` float DEFAULT '0'
|
||||||
|
)
|
||||||
|
|
||||||
|
create table TB_Bar
|
||||||
(
|
(
|
||||||
Id varchar(255),
|
Id varchar(255),
|
||||||
symbol varchar(20),
|
symbol varchar(20),
|
||||||
orderRef varchar(20),
|
open float,
|
||||||
tradeID varchar(20),
|
high float,
|
||||||
direction varchar(10),
|
low float,
|
||||||
offset varchar(10),
|
close float,
|
||||||
price float,
|
date date,
|
||||||
volume int
|
time time,
|
||||||
|
datetime datetime,
|
||||||
|
volume long,
|
||||||
|
openInterest long
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table TB_Ema
|
||||||
|
(
|
||||||
|
Id varchar(255),
|
||||||
|
symbol varchar(20),
|
||||||
|
fastEMA float,
|
||||||
|
slowEMA float,
|
||||||
|
date date,
|
||||||
|
time time,
|
||||||
|
datetime datetime
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
@ -69,8 +69,10 @@ class EventEngine:
|
|||||||
# 事件处理线程
|
# 事件处理线程
|
||||||
self.__thread = Thread(target = self.__run)
|
self.__thread = Thread(target = self.__run)
|
||||||
|
|
||||||
# 计时器,用于触发计时器事件
|
# 计时器
|
||||||
self.__timer = QTimer()
|
self.__timer = QTimer()
|
||||||
|
|
||||||
|
# 绑定用于触发计时器事件
|
||||||
self.__timer.timeout.connect(self.__onTimer)
|
self.__timer.timeout.connect(self.__onTimer)
|
||||||
|
|
||||||
# 这里的__handlers是一个字典,用来保存对应的事件调用关系
|
# 这里的__handlers是一个字典,用来保存对应的事件调用关系
|
||||||
@ -84,10 +86,15 @@ class EventEngine:
|
|||||||
|
|
||||||
print u'eventEngine.py EventEngine.__run begin.'
|
print u'eventEngine.py EventEngine.__run begin.'
|
||||||
|
|
||||||
|
# 循环
|
||||||
while self.__active == True:
|
while self.__active == True:
|
||||||
try:
|
try:
|
||||||
event = self.__queue.get(block = True, timeout = 1) # 获取事件的阻塞时间设为1秒
|
# 阻塞模式,获取事件的阻塞时间设为1秒
|
||||||
|
event = self.__queue.get(block = True, timeout = 1)
|
||||||
|
|
||||||
|
# 执行事件处理
|
||||||
self.__process(event)
|
self.__process(event)
|
||||||
|
|
||||||
except Empty:
|
except Empty:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class BacktestingEngine(object):
|
|||||||
#self.listDataHistory = cur.fetchall()
|
#self.listDataHistory = cur.fetchall()
|
||||||
|
|
||||||
fetch_counts = 0
|
fetch_counts = 0
|
||||||
fetch_size = 10000
|
fetch_size = 1000
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
results = cur.fetchmany(fetch_size)
|
results = cur.fetchmany(fetch_size)
|
||||||
@ -311,12 +311,17 @@ class BacktestingEngine(object):
|
|||||||
event.dict_['data'] = data
|
event.dict_['data'] = data
|
||||||
self.strategyEngine.updateMarketData(event)
|
self.strategyEngine.updateMarketData(event)
|
||||||
|
|
||||||
#保存到数据库中
|
# 保存交易到数据库中
|
||||||
self.saveTradeDataToMysql()
|
self.saveTradeDataToMysql()
|
||||||
|
|
||||||
|
|
||||||
t2 = datetime.now()
|
t2 = datetime.now()
|
||||||
|
|
||||||
self.writeLog(u'回测结束,{0},耗时:{1}秒'.format(str(t2),(t2-t1).seconds))
|
self.writeLog(u'回测结束,{0},耗时:{1}秒'.format(str(t2),(t2-t1).seconds))
|
||||||
|
|
||||||
|
# 保存策略过程数据到数据库
|
||||||
|
self.strategyEngine.saveData(self.Id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
|
@ -76,7 +76,7 @@ class SimpleEmaStrategy(StrategyTemplate):
|
|||||||
# 属于updateMarketData推送的第一个Tick数据,忽略交易逻辑
|
# 属于updateMarketData推送的第一个Tick数据,忽略交易逻辑
|
||||||
self.firstMarketTick = True
|
self.firstMarketTick = True
|
||||||
|
|
||||||
self.lineK = [] # K线数据
|
self.lineBar = [] # K线数据
|
||||||
self.lineEMA = [] # 快速、慢速EMA数据
|
self.lineEMA = [] # 快速、慢速EMA数据
|
||||||
|
|
||||||
|
|
||||||
@ -250,16 +250,18 @@ class SimpleEmaStrategy(StrategyTemplate):
|
|||||||
#self.listTime.append(t)
|
#self.listTime.append(t)
|
||||||
|
|
||||||
# 保存K线数据
|
# 保存K线数据
|
||||||
k = Bar()
|
bar = Bar()
|
||||||
k.open = o
|
bar.symbol = self.symbol
|
||||||
k.high = h
|
bar.open = o
|
||||||
k.low = l
|
bar.high = h
|
||||||
k.close = c
|
bar.low = l
|
||||||
k.volume = volume
|
bar.close = c
|
||||||
k.date = t.date#
|
bar.volume = volume
|
||||||
k.datetime = t
|
bar.date = t.strftime('%Y-%m-%d')
|
||||||
|
bar.time = t.strftime('%H:%M:%S')
|
||||||
|
bar.datetime = t
|
||||||
|
|
||||||
self.lineK.append(k)
|
self.lineBar.append(bar)
|
||||||
|
|
||||||
# 计算EMA
|
# 计算EMA
|
||||||
if self.fastEMA:
|
if self.fastEMA:
|
||||||
@ -270,10 +272,11 @@ class SimpleEmaStrategy(StrategyTemplate):
|
|||||||
self.slowEMA = c
|
self.slowEMA = c
|
||||||
|
|
||||||
emaData = EmaData()
|
emaData = EmaData()
|
||||||
|
emaData.symbol = self.symbol
|
||||||
emaData.fastEMA = self.fastEMA
|
emaData.fastEMA = self.fastEMA
|
||||||
emaData.slowEMA = self.slowEMA
|
emaData.slowEMA = self.slowEMA
|
||||||
emaData.date = t.date
|
emaData.date = t.strftime('%Y-%m-%d')
|
||||||
emaData.time = t.time
|
emaData.time = t.strftime('%H:%M:%S')
|
||||||
emaData.datetime = t
|
emaData.datetime = t
|
||||||
self.lineEMA.append(emaData)
|
self.lineEMA.append(emaData)
|
||||||
|
|
||||||
@ -328,6 +331,17 @@ class SimpleEmaStrategy(StrategyTemplate):
|
|||||||
tt = time(int(hh), int(mm), int(ss), microsecond=ms)
|
tt = time(int(hh), int(mm), int(ss), microsecond=ms)
|
||||||
return tt
|
return tt
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveData(self, id):
|
||||||
|
"""保存过程数据"""
|
||||||
|
# 保存K线
|
||||||
|
print u'{0}保存K线'.format(self.name)
|
||||||
|
self.engine.saveBarToMysql(id, self.lineBar)
|
||||||
|
|
||||||
|
# 保存快速EMA和慢速EMA
|
||||||
|
self.engine.saveEmaToMysql(id, self.lineEMA)
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def print_log(event):
|
def print_log(event):
|
||||||
"""打印日志"""
|
"""打印日志"""
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime,timedelta
|
||||||
print u'StragegyEngine.py import datetime.datetime success'
|
print u'StragegyEngine.py import datetime.datetime success'
|
||||||
|
|
||||||
from pymongo import MongoClient as Connection
|
from pymongo import MongoClient as Connection
|
||||||
@ -87,7 +87,7 @@ class Bar(object):
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Constructor"""
|
"""Constructor"""
|
||||||
#self.vtSymbol = EMPTY_STRING # vt系统代码
|
|
||||||
self.symbol = EMPTY_STRING # 代码
|
self.symbol = EMPTY_STRING # 代码
|
||||||
#self.exchange = EMPTY_STRING # 交易所
|
#self.exchange = EMPTY_STRING # 交易所
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ class EmaData(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Constructor"""
|
"""Constructor"""
|
||||||
|
|
||||||
|
self.symbol = EMPTY_STRING # 代码
|
||||||
self.fastEMA = EMPTY_FLOAT # 快速EMA的数值
|
self.fastEMA = EMPTY_FLOAT # 快速EMA的数值
|
||||||
self.slowEMA = EMPTY_FLOAT # 慢速EMA的数值
|
self.slowEMA = EMPTY_FLOAT # 慢速EMA的数值
|
||||||
|
|
||||||
@ -344,9 +344,31 @@ class StrategyEngine(object):
|
|||||||
|
|
||||||
count = cur.execute(sqlstring)
|
count = cur.execute(sqlstring)
|
||||||
|
|
||||||
cx = cur.fetchall()
|
# cx = cur.fetchall()
|
||||||
|
fetch_counts = 0
|
||||||
|
|
||||||
print u'历史TICK数据载入完成,{1}~{2},共{0}条'.format(count,startDate,endDate)
|
fetch_size = 1000
|
||||||
|
|
||||||
|
while True:
|
||||||
|
results = cur.fetchmany(fetch_size)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if fetch_counts == 0:
|
||||||
|
cx = results
|
||||||
|
else:
|
||||||
|
cx = cx + results
|
||||||
|
|
||||||
|
fetch_counts = fetch_counts+fetch_size
|
||||||
|
|
||||||
|
print u'历史TICK数据载入{0}条'.format(fetch_counts)
|
||||||
|
|
||||||
|
self.writeLog(u'历史TICK数据载入完成,{1}~{2},共{0}条'.format(count,startDate,endDate))
|
||||||
|
|
||||||
|
print u'策略引擎:历史TICK数据载入完成,{1}~{2},共{0}条'.format(count,startDate,endDate)
|
||||||
|
|
||||||
return cx
|
return cx
|
||||||
else:
|
else:
|
||||||
@ -386,6 +408,118 @@ class StrategyEngine(object):
|
|||||||
td = timedelta(days=3)
|
td = timedelta(days=3)
|
||||||
|
|
||||||
return startDate-td;
|
return startDate-td;
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveBarToMysql(self,id, barList):
|
||||||
|
"""
|
||||||
|
保存K线数据到数据库
|
||||||
|
id, 回测ID
|
||||||
|
barList, 对象为Bar的列表
|
||||||
|
"""
|
||||||
|
if self.__mysqlConnected:
|
||||||
|
sql='insert into BackTest.TB_Bar (Id, symbol ,open ,high ,low ,close ,date ,time ,datetime, volume, openInterest) values '
|
||||||
|
values = ''
|
||||||
|
|
||||||
|
print u'共{0}条Bar记录.'.format(len(barList))
|
||||||
|
|
||||||
|
steps = 0
|
||||||
|
|
||||||
|
for bar in barList:
|
||||||
|
|
||||||
|
if len(values) > 0:
|
||||||
|
values = values + ','
|
||||||
|
|
||||||
|
values = values + '(\'{0}\',\'{1}\',{2},{3},{4},{5},\'{6}\',\'{7}\',\'{8}\',{9},{10})'.format(
|
||||||
|
id,
|
||||||
|
bar.symbol,
|
||||||
|
bar.open,
|
||||||
|
bar.high,
|
||||||
|
bar.low,
|
||||||
|
bar.close,
|
||||||
|
bar.date,
|
||||||
|
bar.time,
|
||||||
|
bar.datetime.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
bar.volume,
|
||||||
|
bar.openInterest)
|
||||||
|
|
||||||
|
if steps > 3600:
|
||||||
|
|
||||||
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
|
||||||
|
steps = 0
|
||||||
|
|
||||||
|
values = EMPTY_STRING
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(sql+values)
|
||||||
|
self.__mysqlConnection.commit()
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
|
||||||
|
else:
|
||||||
|
steps = steps + 1
|
||||||
|
|
||||||
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(sql+values)
|
||||||
|
self.__mysqlConnection.commit()
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveEmaToMysql(self, id, emaList):
|
||||||
|
"""
|
||||||
|
保存EMA到数据库
|
||||||
|
id,回测的编号
|
||||||
|
"""
|
||||||
|
if self.__mysqlConnected:
|
||||||
|
sql='insert into BackTest.TB_Ema (Id, symbol ,fastEMA,slowEMA ,date ,time ,datetime) values '
|
||||||
|
values = ''
|
||||||
|
|
||||||
|
print u'共{0}条EMA记录.'.format(len(emaList))
|
||||||
|
|
||||||
|
steps = 0
|
||||||
|
|
||||||
|
for ema in emaList:
|
||||||
|
|
||||||
|
if len(values) > 0:
|
||||||
|
values = values + ','
|
||||||
|
|
||||||
|
values = values + '(\'{0}\',\'{1}\',{2},{3},\'{4}\',\'{5}\',\'{6}\')'.format(
|
||||||
|
id,
|
||||||
|
ema.symbol,
|
||||||
|
ema.fastEMA,
|
||||||
|
ema.slowEMA,
|
||||||
|
ema.date,
|
||||||
|
ema.time,
|
||||||
|
ema.datetime.strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
|
||||||
|
if steps > 3600:
|
||||||
|
|
||||||
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
|
||||||
|
steps = 0
|
||||||
|
|
||||||
|
values = EMPTY_STRING
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(sql+values)
|
||||||
|
self.__mysqlConnection.commit()
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
|
||||||
|
else:
|
||||||
|
steps = steps + 1
|
||||||
|
|
||||||
|
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(sql+values)
|
||||||
|
self.__mysqlConnection.commit()
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def updateMarketData(self, event):
|
def updateMarketData(self, event):
|
||||||
@ -732,6 +866,12 @@ class StrategyEngine(object):
|
|||||||
for strategy in self.dictStrategy.values():
|
for strategy in self.dictStrategy.values():
|
||||||
strategy.stop()
|
strategy.stop()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveData(self,id):
|
||||||
|
"""保存所有策略的过程数据"""
|
||||||
|
print(u'保存所有策略的过程数据')
|
||||||
|
for strategy in self.dictStrategy.values():
|
||||||
|
strategy.saveData(id)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
class StrategyTemplate(object):
|
class StrategyTemplate(object):
|
||||||
@ -781,6 +921,11 @@ class StrategyTemplate(object):
|
|||||||
self.trading = True
|
self.trading = True
|
||||||
self.engine.writeLog(self.name + u'开始运行')
|
self.engine.writeLog(self.name + u'开始运行')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveData(self,Id):
|
||||||
|
"""保存数据"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
|
BIN
vn.trader/ContractData.vt
Normal file
BIN
vn.trader/ContractData.vt
Normal file
Binary file not shown.
11
vn.trader/LTS_connect.json
Normal file
11
vn.trader/LTS_connect.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"brokerID": "2011",
|
||||||
|
"tdAddress": "tcp://211.144.195.163:54505",
|
||||||
|
"qryAddress": "tcp://211.144.195.163:54506",
|
||||||
|
"mdAddress": "tcp://211.144.195.163:54513",
|
||||||
|
"productInfo": "LTS-Test",
|
||||||
|
"authCode": "N3EHKP4CYHZGM9VJ",
|
||||||
|
"tdPassword": "150601",
|
||||||
|
"mdPassword": "123",
|
||||||
|
"userID": "020090002037"
|
||||||
|
}
|
15
vn.trader/ctaConstant.py
Normal file
15
vn.trader/ctaConstant.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
# CTA引擎中涉及到的交易方向类型
|
||||||
|
CTAORDER_BUY = u'买开'
|
||||||
|
CTAORDER_SELL = u'卖平'
|
||||||
|
CTAORDER_SHORT = u'卖开'
|
||||||
|
CTAORDER_COVER = u'买平'
|
||||||
|
|
||||||
|
# 本地停止单状态
|
||||||
|
STOPORDER_WAITING = u'等待中'
|
||||||
|
STOPORDER_CANCELLED = u'已撤销'
|
||||||
|
STOPORDER_TRIGGERED = u'已触发'
|
||||||
|
|
||||||
|
# 本地停止单前缀
|
||||||
|
STOPORDERPREFIX = 'CtaStopOrder.'
|
445
vn.trader/ctaEngine.py
Normal file
445
vn.trader/ctaEngine.py
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from eventEngine import *
|
||||||
|
from vtConstant import *
|
||||||
|
from vtGateway import VtOrderReq, VtCancelOrderReq
|
||||||
|
|
||||||
|
from ctaConstant import *
|
||||||
|
from ctaStrategies import strategyClassDict
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class StopOrder(object):
|
||||||
|
"""本地停止单"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor"""
|
||||||
|
self.vtSymbol = EMPTY_STRING
|
||||||
|
self.orderType = EMPTY_UNICODE
|
||||||
|
self.price = EMPTY_FLOAT
|
||||||
|
self.volume = EMPTY_INT
|
||||||
|
|
||||||
|
self.strategy = None # 下停止单的策略对象
|
||||||
|
self.stopOrderID = EMPTY_STRING # 停止单的本地编号
|
||||||
|
self.status = EMPTY_STRING # 停止单状态
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CtaBarData(object):
|
||||||
|
"""K线数据"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor"""
|
||||||
|
self.vtSymbol = EMPTY_STRING # vt系统代码
|
||||||
|
self.symbol = EMPTY_STRING # 代码
|
||||||
|
self.exchange = EMPTY_STRING # 交易所
|
||||||
|
|
||||||
|
self.open = EMPTY_FLOAT # OHLC
|
||||||
|
self.high = EMPTY_FLOAT
|
||||||
|
self.low = EMPTY_FLOAT
|
||||||
|
self.close = EMPTY_FLOAT
|
||||||
|
|
||||||
|
self.date = EMPTY_STRING # bar开始的时间,日期
|
||||||
|
self.time = EMPTY_STRING # 时间
|
||||||
|
self.datetime = None # python的datetime时间对象
|
||||||
|
|
||||||
|
self.volume = EMPTY_INT # 成交量
|
||||||
|
self.openInterest = EMPTY_INT # 持仓量
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CtaTickData(object):
|
||||||
|
"""Tick数据"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor"""
|
||||||
|
self.vtSymbol = EMPTY_STRING # vt系统代码
|
||||||
|
self.symbol = EMPTY_STRING # 合约代码
|
||||||
|
self.exchange = EMPTY_STRING # 交易所代码
|
||||||
|
|
||||||
|
# 成交数据
|
||||||
|
self.lastPrice = EMPTY_FLOAT # 最新成交价
|
||||||
|
self.volume = EMPTY_INT # 最新成交量
|
||||||
|
self.openInterest = EMPTY_INT # 持仓量
|
||||||
|
|
||||||
|
self.upperLimit = EMPTY_FLOAT # 涨停价
|
||||||
|
self.lowerLimit = EMPTY_FLOAT # 跌停价
|
||||||
|
|
||||||
|
# tick的时间
|
||||||
|
self.date = EMPTY_STRING # 日期
|
||||||
|
self.time = EMPTY_STRING # 时间
|
||||||
|
self.datetime = None # python的datetime时间对象
|
||||||
|
|
||||||
|
# 五档行情
|
||||||
|
self.bidPrice1 = EMPTY_FLOAT
|
||||||
|
self.bidPrice2 = EMPTY_FLOAT
|
||||||
|
self.bidPrice3 = EMPTY_FLOAT
|
||||||
|
self.bidPrice4 = EMPTY_FLOAT
|
||||||
|
self.bidPrice5 = EMPTY_FLOAT
|
||||||
|
|
||||||
|
self.askPrice1 = EMPTY_FLOAT
|
||||||
|
self.askPrice2 = EMPTY_FLOAT
|
||||||
|
self.askPrice3 = EMPTY_FLOAT
|
||||||
|
self.askPrice4 = EMPTY_FLOAT
|
||||||
|
self.askPrice5 = EMPTY_FLOAT
|
||||||
|
|
||||||
|
self.bidVolume1 = EMPTY_INT
|
||||||
|
self.bidVolume2 = EMPTY_INT
|
||||||
|
self.bidVolume3 = EMPTY_INT
|
||||||
|
self.bidVolume4 = EMPTY_INT
|
||||||
|
self.bidVolume5 = EMPTY_INT
|
||||||
|
|
||||||
|
self.askVolume1 = EMPTY_INT
|
||||||
|
self.askVolume2 = EMPTY_INT
|
||||||
|
self.askVolume3 = EMPTY_INT
|
||||||
|
self.askVolume4 = EMPTY_INT
|
||||||
|
self.askVolume5 = EMPTY_INT
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CtaEngine(object):
|
||||||
|
"""CTA策略引擎"""
|
||||||
|
settingFileName = 'CTA_setting.json'
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, mainEngine, eventEngine, dataEngine):
|
||||||
|
"""Constructor"""
|
||||||
|
self.mainEngine = mainEngine
|
||||||
|
self.eventEngine = eventEngine
|
||||||
|
self.dataEngine = dataEngine
|
||||||
|
|
||||||
|
# 保存策略对象的字典
|
||||||
|
# key为策略名称,value为策略对象,注意策略名称不允许重复
|
||||||
|
self.strategyDict = {}
|
||||||
|
|
||||||
|
# 保存vtSymbol和策略对象映射的字典(用于推送tick数据)
|
||||||
|
# 由于可能多个strategy交易同一个vtSymbol,因此key为vtSymbol
|
||||||
|
# value为包含所有相关strategy对象的list
|
||||||
|
self.tickStrategyDict = {}
|
||||||
|
|
||||||
|
# 保存vtOrderID和strategy对象映射的字典(用于推送order和trade数据)
|
||||||
|
# key为vtOrderID,value为strategy对象
|
||||||
|
self.orderStrategyDict = {}
|
||||||
|
|
||||||
|
# 本地停止单编号计数
|
||||||
|
self.stopOrderCount = 0
|
||||||
|
# stopOrderID = STOPORDERPREFIX + str(stopOrderCount)
|
||||||
|
|
||||||
|
# 本地停止单字典
|
||||||
|
# key为stopOrderID,value为stopOrder对象
|
||||||
|
self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除
|
||||||
|
self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sendOrder(self, vtSymbol, orderType, price, volume, strategy):
|
||||||
|
"""发单"""
|
||||||
|
contract = self.dataEngine.getContract(vtSymbol)
|
||||||
|
|
||||||
|
req = VtOrderReq()
|
||||||
|
req.symbol = contract.symbol
|
||||||
|
req.exchange = contract.exchange
|
||||||
|
req.price = price
|
||||||
|
req.volume = volume
|
||||||
|
|
||||||
|
# 设计为CTA引擎发出的委托只允许使用限价单
|
||||||
|
req.priceType = PRICETYPE_LIMITPRICE
|
||||||
|
|
||||||
|
# CTA委托类型映射
|
||||||
|
if orderType == CTAORDER_BUY:
|
||||||
|
req.direction = DIRECTION_LONG
|
||||||
|
req.offset = OFFSET_OPEN
|
||||||
|
elif orderType == CTAORDER_SELL:
|
||||||
|
req.direction = DIRECTION_SHORT
|
||||||
|
req.offset = OFFSET_CLOSE
|
||||||
|
elif orderType == CTAORDER_SHORT:
|
||||||
|
req.direction = DIRECTION_SHORT
|
||||||
|
req.offset = OFFSET_OPEN
|
||||||
|
elif orderType == CTAORDER_COVER:
|
||||||
|
req.direction = DIRECTION_LONG
|
||||||
|
req.offset = OFFSET_CLOSE
|
||||||
|
|
||||||
|
vtOrderID = self.mainEngine.sendOrder(req) # 发单
|
||||||
|
self.orderDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射关系
|
||||||
|
return vtOrderID
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cancelOrder(self, vtOrderID):
|
||||||
|
"""撤单"""
|
||||||
|
# 查询报单对象
|
||||||
|
order = self.dataEngine.getOrder(vtOrderID)
|
||||||
|
|
||||||
|
# 如果查询成功
|
||||||
|
if order:
|
||||||
|
# 检查是否报单还有效,只有有效时才发出撤单指令
|
||||||
|
orderFinished = (order.status==STATUS_ALLTRADED or order.status==STATUS_CANCELLED)
|
||||||
|
if not orderFinished:
|
||||||
|
req = VtCancelOrderReq()
|
||||||
|
req.symbol = order.symbol
|
||||||
|
req.exchange = order.exchange
|
||||||
|
req.frontID = order.frontID
|
||||||
|
req.sessionID = order.sessionID
|
||||||
|
req.orderID = order.orderID
|
||||||
|
self.mainEngine.cancelOrder(req, order.gatewayName)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
|
||||||
|
"""发停止单(本地实现)"""
|
||||||
|
self.stopOrderCount += 1
|
||||||
|
stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
|
||||||
|
|
||||||
|
so = StopOrder()
|
||||||
|
so.vtSymbol = vtSymbol
|
||||||
|
so.orderType = orderType
|
||||||
|
so.price = price
|
||||||
|
so.volume = volume
|
||||||
|
so.strategy = strategy
|
||||||
|
so.stopOrderID = stopOrderID
|
||||||
|
so.status = STOPORDER_WAITING
|
||||||
|
|
||||||
|
# 保存stopOrder对象到字典中
|
||||||
|
self.stopOrderDict[stopOrderID] = so
|
||||||
|
self.workingStopOrderDict[stopOrderID] = so
|
||||||
|
|
||||||
|
return stopOrderID
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cancelStopOrder(self, stopOrderID):
|
||||||
|
"""撤销停止单"""
|
||||||
|
# 检查停止单是否存在
|
||||||
|
if stopOrderID in self.workingStopOrderDict:
|
||||||
|
so = self.workingStopOrderDict[stopOrderID]
|
||||||
|
so.status = STOPORDER_CANCELLED
|
||||||
|
del self.workingStopOrderDict[stopOrderID]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def processStopOrder(self, tick):
|
||||||
|
"""收到行情后处理本地停止单(检查是否要立即发出)"""
|
||||||
|
vtSymbol = tick.vtSymbol
|
||||||
|
|
||||||
|
# 首先检查是否有策略交易该合约
|
||||||
|
if vtSymbol in self.tickStrategyDict:
|
||||||
|
# 遍历等待中的停止单,检查是否会被触发
|
||||||
|
for so in self.workingStopOrderDict.values():
|
||||||
|
if so.vtSymbol == vtSymbol:
|
||||||
|
longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price # 多头停止单被触发
|
||||||
|
shortTriggered = so.direction==DIRECTION_SHORT and tick.lasatPrice<=so.price # 空头停止单被触发
|
||||||
|
|
||||||
|
if longTriggered or shortTriggered:
|
||||||
|
# 买入和卖出分别以涨停跌停价发单(模拟市价单)
|
||||||
|
if so.direction==DIRECTION_LONG:
|
||||||
|
price = tick.upperLimit
|
||||||
|
else:
|
||||||
|
price = tick.lowerLimit
|
||||||
|
|
||||||
|
so.status = STOPORDER_TRIGGERED
|
||||||
|
self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy)
|
||||||
|
del self.workingStopOrderDict[so.stopOrderID]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def procecssTick(self, tick):
|
||||||
|
"""处理行情推送"""
|
||||||
|
# 收到tick行情后,先处理本地停止单(检查是否要立即发出)
|
||||||
|
self.processStopOrder(tick)
|
||||||
|
|
||||||
|
# 推送tick到对应的策略对象进行处理
|
||||||
|
if tick.vtSymbol in self.tickStrategyDict:
|
||||||
|
# 将vtTickData数据转化为ctaTickData
|
||||||
|
ctaTick = CtaTickData()
|
||||||
|
d = ctaTick.__dict__
|
||||||
|
for key in d.keys():
|
||||||
|
d[key] = tick.__getattribute__(key)
|
||||||
|
# 添加datetime字段
|
||||||
|
ctaTick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f')
|
||||||
|
|
||||||
|
# 逐个推送到策略对象中
|
||||||
|
l = self.tickStrategyDict[tick.vtSymbol]
|
||||||
|
for strategy in l:
|
||||||
|
strategy.onTick(tick)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def processOrder(self, order):
|
||||||
|
"""处理委托推送"""
|
||||||
|
if order.vtOrderID in self.orderStrategyDict:
|
||||||
|
strategy = self.orderStrategyDict[order.vtOrderID]
|
||||||
|
strategy.onOrder(order)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def processTrade(self, trade):
|
||||||
|
"""处理成交推送"""
|
||||||
|
if trade.vtOrderID in self.orderStrategyDict:
|
||||||
|
strategy = self.orderStrategyDict[order.vtOrderID]
|
||||||
|
strategy.onTrade(trade)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def registerEvent(self):
|
||||||
|
"""注册事件监听"""
|
||||||
|
self.eventEngine.register(EVENT_TICK, self.procecssTick)
|
||||||
|
self.eventEngine.register(EVENT_ORDER, self.processOrder)
|
||||||
|
self.eventEngine.register(EVENT_TRADE, self.processTrade)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def insertData(self, dbName, collectionName, data):
|
||||||
|
"""插入数据到数据库(这里的data可以是CtaTickData或者CtaBarData)"""
|
||||||
|
self.mainEngine.dbInsert(dbName, collectionName, data.__dict__)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadBar(self, dbName, collectionName, startDate):
|
||||||
|
"""从数据库中读取Bar数据,startDate是datetime对象"""
|
||||||
|
d = {'datetime':{'$gte':startDate}}
|
||||||
|
cursor = self.mainEngine.dbQuery(dbName, collectionName, d)
|
||||||
|
|
||||||
|
l = []
|
||||||
|
for d in cursor:
|
||||||
|
bar = CtaBarData()
|
||||||
|
bar.__dict__ = d
|
||||||
|
l.append(bar)
|
||||||
|
|
||||||
|
return l
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadTick(self, dbName, collectionName, startDate):
|
||||||
|
"""从数据库中读取Tick数据,startDate是datetime对象"""
|
||||||
|
d = {'datetime':{'$gte':startDate}}
|
||||||
|
cursor = self.mainEngine.dbQuery(dbName, collectionName, d)
|
||||||
|
|
||||||
|
l = []
|
||||||
|
for d in cursor:
|
||||||
|
tick = CtaTickData()
|
||||||
|
tick.__dict__ = d
|
||||||
|
l.append(tick)
|
||||||
|
|
||||||
|
return l
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getToday(self):
|
||||||
|
"""获取代表今日的datetime对象"""
|
||||||
|
today = datetime.today()
|
||||||
|
today = today.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
return today
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def writeCtaLog(self, content):
|
||||||
|
"""快速发出CTA模块日志事件"""
|
||||||
|
log = VtLogData()
|
||||||
|
log.logContent = content
|
||||||
|
event = Event(type_=EVENT_CTA_LOG)
|
||||||
|
event.dict_['data'] = log
|
||||||
|
self.eventEngine.put(event)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initStrategy(self, name, strategyClass, paramDict=None):
|
||||||
|
"""初始化策略"""
|
||||||
|
# 防止策略重名
|
||||||
|
if name not in self.strategyDict:
|
||||||
|
self.strategyDict[name] = strategyClass(self, paramDict) # 创建策略对象
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'存在策略对象重名:' + name)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------
|
||||||
|
def startStrategy(self, name):
|
||||||
|
"""启动策略"""
|
||||||
|
if name in self.strategyDict:
|
||||||
|
strategy = self.strategyDict[name]
|
||||||
|
|
||||||
|
if not strategy.trading:
|
||||||
|
strategy.trading = True
|
||||||
|
strategy.start()
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'策略对象不存在:' + name)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def stopStrategy(self, name):
|
||||||
|
"""停止策略"""
|
||||||
|
if name in self.strategyDict:
|
||||||
|
strategy = self.strategyDict[name]
|
||||||
|
|
||||||
|
if strategy.trading:
|
||||||
|
strategy.trading = False
|
||||||
|
strategy.stop()
|
||||||
|
|
||||||
|
# 对该策略发出的所有限价单进行撤单
|
||||||
|
for vtOrderID, s in self.orderStrategyDict.items():
|
||||||
|
if s is strategy:
|
||||||
|
self.cancelOrder(vtOrderID)
|
||||||
|
|
||||||
|
# 对该策略发出的所有本地停止单撤单
|
||||||
|
for stopOrderID, so in self.workingStopOrderDict.items():
|
||||||
|
if so.strategy is strategy:
|
||||||
|
self.cancelStopOrder(stopOrderID)
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'策略对象不存在:' + name)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def saveStrategySetting(self):
|
||||||
|
"""保存引擎中的策略配置"""
|
||||||
|
with open(self.settingFileName, 'w') as f:
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
for name, strategy in self.strategyDict.items():
|
||||||
|
setting = {}
|
||||||
|
setting['strategyClassName'] = strategy.strategyClassName
|
||||||
|
for param in strategy.paramList:
|
||||||
|
setting[param] = strategy.__getattribute__(param)
|
||||||
|
d[name] = setting
|
||||||
|
|
||||||
|
jsonD = json.dumps(d, indent=4)
|
||||||
|
f.write(jsonD)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadStrategySetting(self):
|
||||||
|
"""读取引擎中的策略配置"""
|
||||||
|
with open(self.settingFileName) as f:
|
||||||
|
d = json.load(f)
|
||||||
|
|
||||||
|
for name, setting in d.items():
|
||||||
|
strategyClassName = setting['strategyClassName']
|
||||||
|
|
||||||
|
if strategyClassName in strategyClassDict:
|
||||||
|
strategyClass = strategyClassDict[strategyClassName]
|
||||||
|
self.initStrategy(name, strategyClass, setting)
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'无法找到策略类:' + strategyClassName)
|
||||||
|
break
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getStrategyVarialbe(self, name):
|
||||||
|
"""获取策略当前的变量字典"""
|
||||||
|
if name in self.strategyDict:
|
||||||
|
strategy = self.strategyDict[name]
|
||||||
|
d = strategy.__dict__
|
||||||
|
varDict = OrderedDict()
|
||||||
|
|
||||||
|
for key in strategy.varList:
|
||||||
|
if key in d:
|
||||||
|
varDict[key] = d[key]
|
||||||
|
|
||||||
|
return varDict
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'策略对象不存在:' + name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getStrategyParameter(self, name):
|
||||||
|
"""获取策略的参数字典"""
|
||||||
|
if name in self.strategyDict:
|
||||||
|
strategy = self.strategyDict[name]
|
||||||
|
d = strategy.__dict__
|
||||||
|
varDict = OrderedDict()
|
||||||
|
|
||||||
|
for key in strategy.paramList:
|
||||||
|
if key in d:
|
||||||
|
varDict[key] = d[key]
|
||||||
|
|
||||||
|
return varDict
|
||||||
|
else:
|
||||||
|
self.writeCtaLog(u'策略对象不存在:' + name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
7
vn.trader/ctaStrategies.py
Normal file
7
vn.trader/ctaStrategies.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''
|
||||||
|
在本文件中引入所有希望在系统中使用的策略类
|
||||||
|
'''
|
||||||
|
|
||||||
|
strategyClassDict = {}
|
164
vn.trader/ctaStrategyTemplate.py
Normal file
164
vn.trader/ctaStrategyTemplate.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
from ctaConstant import *
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CtaStrategyTemplate(object):
|
||||||
|
"""CTA策略模板"""
|
||||||
|
# 策略类的名称
|
||||||
|
strategyClassName = 'Template'
|
||||||
|
|
||||||
|
# 参数列表,保存了参数的名称
|
||||||
|
paramList = ['vtSymbol']
|
||||||
|
|
||||||
|
# 变量列表,保存了变量的名称
|
||||||
|
varList = []
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, ctaEngine, setting=None):
|
||||||
|
"""Constructor"""
|
||||||
|
self.ctaEngine = ctaEngine
|
||||||
|
|
||||||
|
self.vtSymbol = EMPTY_STRING # 交易的合约vt系统代码
|
||||||
|
|
||||||
|
self.tickDbName = EMPTY_STRING # tick数据库名称
|
||||||
|
self.barDbName = EMPTY_STRING # bar数据库名称
|
||||||
|
|
||||||
|
self.trading = False # 控制是否启动交易
|
||||||
|
|
||||||
|
self.init() # 初始化策略
|
||||||
|
|
||||||
|
if setting:
|
||||||
|
self.setParam(setting)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def init(self):
|
||||||
|
"""初始化策略(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def start(self):
|
||||||
|
"""启动策略(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def stop(self):
|
||||||
|
"""停止策略(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onTick(self, tick):
|
||||||
|
"""收到行情TICK推送(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onOrder(self, order):
|
||||||
|
"""收到委托变化推送(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onTrade(self, trade):
|
||||||
|
"""收到成交推送(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onBar(self, bar):
|
||||||
|
"""收到Bar推送(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def buy(self, price, volume, stop=False):
|
||||||
|
"""买开"""
|
||||||
|
# 如果stop为True,则意味着发本地停止单
|
||||||
|
if self.trading:
|
||||||
|
if stop:
|
||||||
|
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_BUY, price, volume, self)
|
||||||
|
else:
|
||||||
|
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_BUY, price, volume, self)
|
||||||
|
return orderID
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sell(self, price, volume, stop=False):
|
||||||
|
"""卖平"""
|
||||||
|
# 如果stop为True,则意味着发本地停止单
|
||||||
|
if self.trading:
|
||||||
|
if stop:
|
||||||
|
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_SELL, price, volume, self)
|
||||||
|
else:
|
||||||
|
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_SELL, price, volume, self)
|
||||||
|
return orderID
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def short(self, price, volume, stop=False):
|
||||||
|
"""卖开"""
|
||||||
|
# 如果stop为True,则意味着发本地停止单
|
||||||
|
if self.trading:
|
||||||
|
if stop:
|
||||||
|
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_SHORT, price, volume, self)
|
||||||
|
else:
|
||||||
|
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_SHORT, price, volume, self)
|
||||||
|
return orderID
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cover(self, price, volume, stop=False):
|
||||||
|
"""买平"""
|
||||||
|
if self.trading:
|
||||||
|
# 如果stop为True,则意味着发本地停止单
|
||||||
|
if stop:
|
||||||
|
orderID = self.ctaEngine.sendStopOrder(self.vtSymbol, CTAORDER_COVER, price, volume, self)
|
||||||
|
else:
|
||||||
|
orderID = self.ctaEngine.sendOrder(self.vtSymbol, CTAORDER_COVER, price, volume, self)
|
||||||
|
return orderID
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cancelOrder(self, orderID):
|
||||||
|
"""撤单"""
|
||||||
|
if STOPORDERPREFIX in orderID:
|
||||||
|
self.ctaEngine.cancelStopOrder(orderID)
|
||||||
|
else:
|
||||||
|
self.ctaEngine.cancelOrder(orderID)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def insertTick(self, tick):
|
||||||
|
"""向数据库中插入tick数据"""
|
||||||
|
self.ctaEngine.insertData(self.tickDbName, self.vtSymbol, tick)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def insertBar(self, bar):
|
||||||
|
"""向数据库中插入bar数据"""
|
||||||
|
self.ctaEngine.insertData(self.barDbName, self.vtSymbol, bar)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadTick(self, startDate):
|
||||||
|
"""读取tick数据"""
|
||||||
|
return self.ctaEngine.loadTick(self.tickDbName, self.vtSymbol, startDate)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadBar(self, startDate):
|
||||||
|
"""读取bar数据"""
|
||||||
|
return self.ctaEngine.loadBar(self.barDbName, self.vtSymbol, startDate)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def setParam(self, setting):
|
||||||
|
"""设置参数"""
|
||||||
|
d = self.__dict__
|
||||||
|
for key in self.paramList:
|
||||||
|
if key in setting:
|
||||||
|
d[key] = paramDict[key]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getToday(self):
|
||||||
|
"""查询当前日期"""
|
||||||
|
return self.ctaEngine.getToday()
|
||||||
|
|
||||||
|
|
||||||
|
|
2181
vn.trader/ltsDataType.py
Normal file
2181
vn.trader/ltsDataType.py
Normal file
File diff suppressed because it is too large
Load Diff
1244
vn.trader/ltsGateway.py
Normal file
1244
vn.trader/ltsGateway.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vn.trader/securitymduserapi.dll
Normal file
BIN
vn.trader/securitymduserapi.dll
Normal file
Binary file not shown.
BIN
vn.trader/securityqueryapi.dll
Normal file
BIN
vn.trader/securityqueryapi.dll
Normal file
Binary file not shown.
BIN
vn.trader/securitytraderapi.dll
Normal file
BIN
vn.trader/securitytraderapi.dll
Normal file
Binary file not shown.
4
vn.trader/uiCtaWidget.py
Normal file
4
vn.trader/uiCtaWidget.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''CTA模块相关的GUI控制组件'''
|
||||||
|
|
276
vn.trader/uiMainWindow.py
Normal file
276
vn.trader/uiMainWindow.py
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
from uiBasicWidget import *
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class MainWindow(QtGui.QMainWindow):
|
||||||
|
"""主窗口"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, mainEngine, eventEngine, dataEngine):
|
||||||
|
"""Constructor"""
|
||||||
|
super(MainWindow, self).__init__()
|
||||||
|
|
||||||
|
self.mainEngine = mainEngine
|
||||||
|
self.eventEngine = eventEngine
|
||||||
|
self.dataEngine = dataEngine
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initUi(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
self.setWindowTitle('VnTrader')
|
||||||
|
self.initCentral()
|
||||||
|
self.initMenu()
|
||||||
|
self.initStatusBar()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initCentral(self):
|
||||||
|
"""初始化中心区域"""
|
||||||
|
marketM = MarketMonitor(self.eventEngine)
|
||||||
|
logM = LogMonitor(self.eventEngine)
|
||||||
|
errorM = ErrorMonitor(self.eventEngine)
|
||||||
|
tradeM = TradeMonitor(self.eventEngine)
|
||||||
|
orderM = OrderMonitor(self.eventEngine, self.mainEngine)
|
||||||
|
positionM = PositionMonitor(self.eventEngine)
|
||||||
|
accountM = AccountMonitor(self.eventEngine)
|
||||||
|
|
||||||
|
tradingW = TradingWidget(self.mainEngine, self.mainEngine.eventEngine, self.mainEngine.dataEngine)
|
||||||
|
|
||||||
|
leftTab = QtGui.QTabWidget()
|
||||||
|
leftTab.addTab(logM, u'日志')
|
||||||
|
leftTab.addTab(errorM, u'错误')
|
||||||
|
leftTab.addTab(accountM, u'账户')
|
||||||
|
|
||||||
|
rightTab = QtGui.QTabWidget()
|
||||||
|
rightTab.addTab(tradeM, u'成交')
|
||||||
|
rightTab.addTab(orderM, u'委托')
|
||||||
|
rightTab.addTab(positionM, u'持仓')
|
||||||
|
|
||||||
|
hbox = QtGui.QHBoxLayout()
|
||||||
|
hbox.addWidget(tradingW)
|
||||||
|
hbox.addWidget(marketM)
|
||||||
|
|
||||||
|
grid = QtGui.QGridLayout()
|
||||||
|
grid.addLayout(hbox, 0, 0, 1, 2)
|
||||||
|
grid.addWidget(leftTab, 1, 0)
|
||||||
|
grid.addWidget(rightTab, 1, 1)
|
||||||
|
|
||||||
|
central = QtGui.QWidget()
|
||||||
|
central.setLayout(grid)
|
||||||
|
self.setCentralWidget(central)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initMenu(self):
|
||||||
|
"""初始化菜单"""
|
||||||
|
# 创建操作
|
||||||
|
connectCtpAction = QtGui.QAction(u'连接CTP', self)
|
||||||
|
connectCtpAction.triggered.connect(self.connectCtp)
|
||||||
|
|
||||||
|
connectLtsAction = QtGui.QAction(u'连接LTS', self)
|
||||||
|
connectLtsAction.triggered.connect(self.connectLts)
|
||||||
|
|
||||||
|
connectWindAction = QtGui.QAction(u'连接Wind', self)
|
||||||
|
connectWindAction.triggered.connect(self.connectWind)
|
||||||
|
|
||||||
|
testAction = QtGui.QAction(u'测试', self)
|
||||||
|
testAction.triggered.connect(self.testSubscribe)
|
||||||
|
|
||||||
|
exitAction = QtGui.QAction(u'退出', self)
|
||||||
|
exitAction.triggered.connect(self.close)
|
||||||
|
|
||||||
|
aboutAction = QtGui.QAction(u'关于', self)
|
||||||
|
aboutAction.triggered.connect(self.openAbout)
|
||||||
|
|
||||||
|
contractAction = QtGui.QAction(u'查询合约', self)
|
||||||
|
contractAction.triggered.connect(self.openContract)
|
||||||
|
|
||||||
|
# 创建菜单
|
||||||
|
menubar = self.menuBar()
|
||||||
|
|
||||||
|
sysMenu = menubar.addMenu(u'系统')
|
||||||
|
sysMenu.addAction(connectCtpAction)
|
||||||
|
sysMenu.addAction(connectLtsAction)
|
||||||
|
sysMenu.addAction(connectWindAction)
|
||||||
|
sysMenu.addAction(testAction)
|
||||||
|
sysMenu.addAction(exitAction)
|
||||||
|
|
||||||
|
functionMenu = menubar.addMenu(u'功能')
|
||||||
|
functionMenu.addAction(contractAction)
|
||||||
|
|
||||||
|
helpMenu = menubar.addMenu(u'帮助')
|
||||||
|
helpMenu.addAction(aboutAction)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initStatusBar(self):
|
||||||
|
"""初始化状态栏"""
|
||||||
|
self.statusLabel = QtGui.QLabel()
|
||||||
|
self.statusLabel.setAlignment(QtCore.Qt.AlignLeft)
|
||||||
|
|
||||||
|
self.statusBar().addPermanentWidget(self.statusLabel)
|
||||||
|
self.statusLabel.setText(self.getCpuMemory())
|
||||||
|
|
||||||
|
self.sbCount = 0
|
||||||
|
self.sbTrigger = 10 # 10秒刷新一次
|
||||||
|
self.eventEngine.register(EVENT_TIMER, self.updateStatusBar)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updateStatusBar(self, event):
|
||||||
|
"""在状态栏更新CPU和内存信息"""
|
||||||
|
self.sbCount += 1
|
||||||
|
|
||||||
|
if self.sbCount == self.sbTrigger:
|
||||||
|
self.sbCount = 0
|
||||||
|
self.statusLabel.setText(self.getCpuMemory())
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getCpuMemory(self):
|
||||||
|
"""获取CPU和内存状态信息"""
|
||||||
|
cpuPercent = psutil.cpu_percent()
|
||||||
|
memoryPercent = psutil.virtual_memory().percent
|
||||||
|
return u'CPU使用率:%d%% 内存使用率:%d%%' % (cpuPercent, memoryPercent)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def connectCtp(self):
|
||||||
|
"""连接CTP接口"""
|
||||||
|
self.mainEngine.connect('CTP')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def connectLts(self):
|
||||||
|
"""连接LTS接口"""
|
||||||
|
self.mainEngine.connect('LTS')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def connectWind(self):
|
||||||
|
"""连接Wind接口"""
|
||||||
|
self.mainEngine.connect('Wind')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def testSubscribe(self):
|
||||||
|
"""测试订阅"""
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = '600000'
|
||||||
|
req.exchange = EXCHANGE_SSE
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = '000062'
|
||||||
|
req.exchange = EXCHANGE_SZSE
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = 'IF1511'
|
||||||
|
req.exchange = EXCHANGE_CFFEX
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = 'CU1601'
|
||||||
|
req.exchange = EXCHANGE_SHFE
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = 'C1601'
|
||||||
|
req.exchange = EXCHANGE_DCE
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
req = VtSubscribeReq()
|
||||||
|
req.symbol = 'SR1601'
|
||||||
|
req.exchange = EXCHANGE_CZCE
|
||||||
|
self.mainEngine.subscribe(req, 'Wind')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def openAbout(self):
|
||||||
|
"""打开关于"""
|
||||||
|
try:
|
||||||
|
self.aboutW.show()
|
||||||
|
except AttributeError:
|
||||||
|
self.aboutW = AboutWidget(self)
|
||||||
|
self.aboutW.show()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def openContract(self):
|
||||||
|
"""打开合约查询"""
|
||||||
|
try:
|
||||||
|
self.contractM.show()
|
||||||
|
except AttributeError:
|
||||||
|
self.contractM = ContractMonitor(self.mainEngine.dataEngine)
|
||||||
|
self.contractM.show()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def closeEvent(self, event):
|
||||||
|
"""关闭事件"""
|
||||||
|
reply = QtGui.QMessageBox.question(self, u'退出',
|
||||||
|
u'确认退出?', QtGui.QMessageBox.Yes |
|
||||||
|
QtGui.QMessageBox.No, QtGui.QMessageBox.No)
|
||||||
|
|
||||||
|
if reply == QtGui.QMessageBox.Yes:
|
||||||
|
self.mainEngine.exit()
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class AboutWidget(QtGui.QDialog):
|
||||||
|
"""显示关于信息"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Constructor"""
|
||||||
|
super(AboutWidget, self).__init__(parent)
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initUi(self):
|
||||||
|
""""""
|
||||||
|
self.setWindowTitle(u'关于')
|
||||||
|
|
||||||
|
text = u"""
|
||||||
|
VnTrader
|
||||||
|
|
||||||
|
更新日期:2015/9/29
|
||||||
|
|
||||||
|
作者:用Python的交易员
|
||||||
|
|
||||||
|
License:MIT
|
||||||
|
|
||||||
|
主页:vnpy.org
|
||||||
|
|
||||||
|
Github:github.com/vnpy/vnpy
|
||||||
|
|
||||||
|
QQ交流群:262656087
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
开发环境
|
||||||
|
|
||||||
|
操作系统:Windows 7 专业版 64位
|
||||||
|
|
||||||
|
Python发行版:Python 2.7.6 (Anaconda 1.9.2 Win-32)
|
||||||
|
|
||||||
|
CTP:vn.ctp 2015/6/1版
|
||||||
|
|
||||||
|
图形库:PyQt4 4.11.3 Py2.7-x32
|
||||||
|
|
||||||
|
事件驱动引擎:vn.event
|
||||||
|
|
||||||
|
开发环境:WingIDE 5.0.6
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
label = QtGui.QLabel()
|
||||||
|
label.setText(text)
|
||||||
|
label.setMinimumWidth(500)
|
||||||
|
|
||||||
|
vbox = QtGui.QVBoxLayout()
|
||||||
|
vbox.addWidget(label)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
BIN
vn.trader/vnltsmd.pyd
Normal file
BIN
vn.trader/vnltsmd.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnltsqry.pyd
Normal file
BIN
vn.trader/vnltsqry.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnltstd.pyd
Normal file
BIN
vn.trader/vnltstd.pyd
Normal file
Binary file not shown.
BIN
vn.trader/vnpy.ico
Normal file
BIN
vn.trader/vnpy.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
57
vn.trader/vtConstant.py
Normal file
57
vn.trader/vtConstant.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
# 默认空值
|
||||||
|
EMPTY_STRING = ''
|
||||||
|
EMPTY_UNICODE = u''
|
||||||
|
EMPTY_INT = 0
|
||||||
|
EMPTY_FLOAT = 0.0
|
||||||
|
|
||||||
|
# 方向常量
|
||||||
|
DIRECTION_NONE = u'无方向'
|
||||||
|
DIRECTION_LONG = u'多'
|
||||||
|
DIRECTION_SHORT = u'空'
|
||||||
|
DIRECTION_UNKNOWN = u'未知'
|
||||||
|
DIRECTION_NET = u'净'
|
||||||
|
|
||||||
|
# 开平常量
|
||||||
|
OFFSET_NONE = u'无开平'
|
||||||
|
OFFSET_OPEN = u'开仓'
|
||||||
|
OFFSET_CLOSE = u'平仓'
|
||||||
|
OFFSET_CLOSETODAY = u'平今'
|
||||||
|
OFFSET_CLOSESYESTERDAY = u'平昨'
|
||||||
|
OFFSET_UNKNOWN = u'未知'
|
||||||
|
|
||||||
|
# 状态常量
|
||||||
|
STATUS_NOTTRADED = u'未成交'
|
||||||
|
STATUS_PARTTRADED = u'部分成交'
|
||||||
|
STATUS_ALLTRADED = u'全部成交'
|
||||||
|
STATUS_CANCELLED = u'已撤销'
|
||||||
|
STATUS_UNKNOWN = u'未知'
|
||||||
|
|
||||||
|
# 合约类型常量
|
||||||
|
PRODUCT_EQUITY = u'股票'
|
||||||
|
PRODUCT_FUTURES = u'期货'
|
||||||
|
PRODUCT_OPTION = u'期权'
|
||||||
|
PRODUCT_INDEX = u'指数'
|
||||||
|
PRODUCT_COMBINATION = u'组合'
|
||||||
|
PRODUCT_UNKNOWN = u'未知'
|
||||||
|
|
||||||
|
# 价格类型常量
|
||||||
|
PRICETYPE_LIMITPRICE = u'限价'
|
||||||
|
PRICETYPE_MARKETPRICE = u'市价'
|
||||||
|
PRICETYPE_FAK = u'FAK'
|
||||||
|
PRICETYPE_FOK = u'FOK'
|
||||||
|
|
||||||
|
# 期权类型
|
||||||
|
OPTION_CALL = u'看涨期权'
|
||||||
|
OPTION_PUT = u'看跌期权'
|
||||||
|
|
||||||
|
# 交易所类型
|
||||||
|
EXCHANGE_SSE = u'SSE' # 上交所
|
||||||
|
EXCHANGE_SZSE = u'SZSE' # 深交所
|
||||||
|
EXCHANGE_CFFEX = u'CFFEX' # 中金所
|
||||||
|
EXCHANGE_SHFE = u'SHFE' # 上期所
|
||||||
|
EXCHANGE_CZCE = u'CZCE' # 郑商所
|
||||||
|
EXCHANGE_DCE = u'DCE' # 大商所
|
||||||
|
EXCHANGE_UNKNOWN = 'UNKNOWN'# 未知交易所
|
||||||
|
EXCHANGE_NONE = '' # 空交易所
|
26
vn.trader/vtFunction.py
Normal file
26
vn.trader/vtFunction.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
包含一些开放中常用的函数
|
||||||
|
"""
|
||||||
|
|
||||||
|
import decimal
|
||||||
|
|
||||||
|
MAX_NUMBER = 1000000000
|
||||||
|
MAX_DECIMAL = 4
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def safeUnicode(value):
|
||||||
|
"""检查接口数据潜在的错误,保证转化为的字符串正确"""
|
||||||
|
# 检查是数字接近0时会出现的浮点数上限
|
||||||
|
if type(value) is int or type(value) is float:
|
||||||
|
if value > MAX_NUMBER:
|
||||||
|
value = 0
|
||||||
|
|
||||||
|
# 检查防止小数点位过多
|
||||||
|
if type(value) is float:
|
||||||
|
d = decimal.Decimal(str(value))
|
||||||
|
if abs(d.as_tuple().exponent) > MAX_DECIMAL:
|
||||||
|
value = round(value, ndigits=MAX_DECIMAL)
|
||||||
|
|
||||||
|
return unicode(value)
|
26
vn.trader/vtMain.py
Normal file
26
vn.trader/vtMain.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
from vtEngine import MainEngine
|
||||||
|
from uiMainWindow import *
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
"""主程序入口"""
|
||||||
|
# 设置底部任务栏图标,win7以下请注释掉
|
||||||
|
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('vn.py demo')
|
||||||
|
|
||||||
|
app = QtGui.QApplication(sys.argv)
|
||||||
|
app.setWindowIcon(QtGui.QIcon('vnpy.ico'))
|
||||||
|
app.setFont(BASIC_FONT)
|
||||||
|
|
||||||
|
mainEngine = MainEngine()
|
||||||
|
mainWindow = MainWindow(mainEngine, mainEngine.eventEngine, mainEngine.dataEngine)
|
||||||
|
mainWindow.showMaximized()
|
||||||
|
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
183
vn.trader/windGateway.py
Normal file
183
vn.trader/windGateway.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''
|
||||||
|
Wind Python API的gateway接入
|
||||||
|
'''
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
try:
|
||||||
|
from WindPy import w
|
||||||
|
except ImportError:
|
||||||
|
print u'请先安装WindPy接口'
|
||||||
|
|
||||||
|
from vtGateway import *
|
||||||
|
|
||||||
|
# 交易所类型映射
|
||||||
|
exchangeMap = {}
|
||||||
|
exchangeMap[EXCHANGE_SSE] = 'SH'
|
||||||
|
exchangeMap[EXCHANGE_SZSE] = 'SZ'
|
||||||
|
exchangeMap[EXCHANGE_CFFEX] = 'CFE'
|
||||||
|
exchangeMap[EXCHANGE_SHFE] = 'SHF'
|
||||||
|
exchangeMap[EXCHANGE_DCE] = 'DCE'
|
||||||
|
exchangeMap[EXCHANGE_CZCE] = 'CZC'
|
||||||
|
exchangeMap[EXCHANGE_UNKNOWN] = ''
|
||||||
|
exchangeMapReverse = {v:k for k,v in exchangeMap.items()}
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class WindGateway(VtGateway):
|
||||||
|
"""Wind接口"""
|
||||||
|
# 订阅wsq时传入的字段列表
|
||||||
|
wsqParamMap = {}
|
||||||
|
wsqParamMap['rt_last'] = 'lastPrice'
|
||||||
|
wsqParamMap['rt_last_vol'] = 'volume'
|
||||||
|
wsqParamMap['rt_oi'] = 'openInterest'
|
||||||
|
|
||||||
|
wsqParamMap['rt_open'] = 'openPrice'
|
||||||
|
wsqParamMap['rt_high'] = 'highPrice'
|
||||||
|
wsqParamMap['rt_low'] = 'lowPrice'
|
||||||
|
wsqParamMap['rt_pre_close'] = 'preClosePrice'
|
||||||
|
|
||||||
|
wsqParamMap['rt_high_limit'] = 'upperLimit'
|
||||||
|
wsqParamMap['rt_low_limit'] = 'lowerLimit'
|
||||||
|
|
||||||
|
wsqParamMap['rt_bid1'] = 'bidPrice1'
|
||||||
|
wsqParamMap['rt_bid2'] = 'bidPrice2'
|
||||||
|
wsqParamMap['rt_bid3'] = 'bidPrice3'
|
||||||
|
wsqParamMap['rt_bid4'] = 'bidPrice4'
|
||||||
|
wsqParamMap['rt_bid5'] = 'bidPrice5'
|
||||||
|
|
||||||
|
wsqParamMap['rt_ask1'] = 'askPrice1'
|
||||||
|
wsqParamMap['rt_ask2'] = 'askPrice2'
|
||||||
|
wsqParamMap['rt_ask3'] = 'askPrice3'
|
||||||
|
wsqParamMap['rt_ask4'] = 'askPrice4'
|
||||||
|
wsqParamMap['rt_ask5'] = 'askPrice5'
|
||||||
|
|
||||||
|
wsqParamMap['rt_bsize1'] = 'bidVolume1'
|
||||||
|
wsqParamMap['rt_bsize2'] = 'bidVolume2'
|
||||||
|
wsqParamMap['rt_bsize3'] = 'bidVolume3'
|
||||||
|
wsqParamMap['rt_bsize4'] = 'bidVolume4'
|
||||||
|
wsqParamMap['rt_bsize5'] = 'bidVolume5'
|
||||||
|
|
||||||
|
wsqParamMap['rt_asize1'] = 'askVolume1'
|
||||||
|
wsqParamMap['rt_asize2'] = 'askVolume2'
|
||||||
|
wsqParamMap['rt_asize3'] = 'askVolume3'
|
||||||
|
wsqParamMap['rt_asize4'] = 'askVolume4'
|
||||||
|
wsqParamMap['rt_asize5'] = 'askVolume5'
|
||||||
|
|
||||||
|
wsqParam = ','.join(wsqParamMap.keys())
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, eventEngine, gatewayName='Wind'):
|
||||||
|
"""Constructor"""
|
||||||
|
super(WindGateway, self).__init__(eventEngine, gatewayName)
|
||||||
|
|
||||||
|
self.w = w # Wind API对象
|
||||||
|
self.connected = False # 连接状态
|
||||||
|
|
||||||
|
# Wind的wsq更新采用的是增量更新模式,每次推送只会更新发生变化的字段
|
||||||
|
# 而vt中的tick是完整更新,因此需要本地维护一个所有字段的快照
|
||||||
|
self.tickDict = {}
|
||||||
|
|
||||||
|
self.registerEvent()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def connect(self):
|
||||||
|
"""连接"""
|
||||||
|
# 由于w.start方法会阻塞较长时间
|
||||||
|
# 因此设计为异步模式,交给事件处理线程去处理
|
||||||
|
# 另外w.start和WingIDE的debug模块有冲突,会导致异常退出
|
||||||
|
event = Event(type_=EVENT_WIND_CONNECTREQ)
|
||||||
|
self.eventEngine.put(event)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def subscribe(self, subscribeReq):
|
||||||
|
"""订阅行情"""
|
||||||
|
windSymbol = '.'.join([subscribeReq.symbol, exchangeMap[subscribeReq.exchange]])
|
||||||
|
data = self.w.wsq(windSymbol, self.wsqParam, func=self.wsqCallBack)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sendOrder(self, orderReq):
|
||||||
|
"""发单"""
|
||||||
|
log = VtLogData()
|
||||||
|
log.gatewayName = self.gatewayName
|
||||||
|
log.logContent = u'Wind接口未实现发单功能'
|
||||||
|
self.onLog(log)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cancelOrder(self, cancelOrderReq):
|
||||||
|
"""撤单"""
|
||||||
|
log = VtLogData()
|
||||||
|
log.gatewayName = self.gatewayName
|
||||||
|
log.logContent = u'Wind接口未实现撤单功能'
|
||||||
|
self.onLog(log)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getAccount(self):
|
||||||
|
"""查询账户资金"""
|
||||||
|
log = VtLogData()
|
||||||
|
log.gatewayName = self.gatewayName
|
||||||
|
log.logContent = u'Wind接口未实现查询账户功能'
|
||||||
|
self.onLog(log)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getPosition(self):
|
||||||
|
"""查询持仓"""
|
||||||
|
log = VtLogData()
|
||||||
|
log.gatewayName = self.gatewayName
|
||||||
|
log.logContent = u'Wind接口未实现查询持仓功能'
|
||||||
|
self.onLog(log)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def close(self):
|
||||||
|
self.w.stop()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def registerEvent(self):
|
||||||
|
"""注册事件监听"""
|
||||||
|
self.eventEngine.register(EVENT_WIND_CONNECTREQ, self.wConnect)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def wsqCallBack(self, data):
|
||||||
|
"""收到wsq推送"""
|
||||||
|
windSymbol = data.Codes[0]
|
||||||
|
if windSymbol in self.tickDict:
|
||||||
|
tick = self.tickDict[windSymbol]
|
||||||
|
else:
|
||||||
|
tick = VtTickData()
|
||||||
|
tick.gatewayName = self.gatewayName
|
||||||
|
symbolSplit = windSymbol.split('.')
|
||||||
|
tick.symbol = symbolSplit[0]
|
||||||
|
tick.exchange = exchangeMapReverse[symbolSplit[1]]
|
||||||
|
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
|
||||||
|
self.tickDict[windSymbol] = tick
|
||||||
|
|
||||||
|
dt = data.Times[0]
|
||||||
|
tick.time = dt.strftime('%H:%M:%S')
|
||||||
|
tick.date = dt.strftime('%Y%m%d')
|
||||||
|
|
||||||
|
# 采用遍历的形式读取数值
|
||||||
|
fields = data.Fields
|
||||||
|
values = data.Data
|
||||||
|
d = tick.__dict__
|
||||||
|
for n, field in enumerate(fields):
|
||||||
|
field = field.lower()
|
||||||
|
key = self.wsqParamMap[field]
|
||||||
|
value = values[n][0]
|
||||||
|
d[key] = value
|
||||||
|
|
||||||
|
self.onTick(tick)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def wConnect(self, event):
|
||||||
|
"""利用事件处理线程去异步连接Wind接口"""
|
||||||
|
result = self.w.start()
|
||||||
|
|
||||||
|
log = VtLogData()
|
||||||
|
log.gatewayName = self.gatewayName
|
||||||
|
|
||||||
|
if not result.ErrorCode:
|
||||||
|
log.logContent = u'Wind接口连接成功'
|
||||||
|
else:
|
||||||
|
log.logContent = u'Wind接口连接失败,错误代码%d' %result.ErrorCode
|
||||||
|
self.onLog(log)
|
Loading…
Reference in New Issue
Block a user