vnpy/vn.strategy/strategydemo/strategyEngine.py
2015-11-09 22:13:13 +08:00

1164 lines
41 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# encoding: UTF-8
from datetime import datetime,timedelta
print u'StragegyEngine.py import datetime.datetime success'
from pymongo import MongoClient as Connection
print u'demoStrategy.py import pymongo.Connection success'
from pymongo.errors import *
print u'demoStrategy.py import pymongo.errors.* success'
from eventEngine import *
print u'demoStrategy.py import eventEngine.* success'
from vtConstant import *
import MySQLdb
import os
import sys
import cPickle
import json
# 常量定义
OFFSET_OPEN = '0' # 开仓
OFFSET_CLOSE = '1' # 平仓
DIRECTION_BUY = '0' # 买入 多
DIRECTION_SELL = '1' # 卖出 空
PRICETYPE_LIMIT = '2' # 限价
# buy 买入开仓 开多 : DIRECTION_BUY = '0' OFFSET_OPEN = '0'
# sell 卖出平仓 平多 : DIRECTION_SELL = '1' OFFSET_CLOSE = '1'
# short 卖出开仓 开空 : DIRECTION_SELL = '1' OFFSET_OPEN = '0'
# cover 买入平仓 平空 : DIRECTION_BUY = '0' OFFSET_CLOSE = '1'
########################################################################
class Tick:
"""Tick数据对象"""
#----------------------------------------------------------------------
def __init__(self, symbol):
"""Constructor"""
self.symbol = symbol # 合约代码
self.openPrice = 0 # OHLC
self.highPrice = 0
self.lowPrice = 0
self.lastPrice = 0
self.volume = 0 # 成交量
self.openInterest = 0 # 持仓量
self.upperLimit = 0 # 涨停价
self.lowerLimit = 0 # 跌停价
self.time = '' # 更新时间和毫秒
self.ms= 0
self.bidPrice1 = 0 # 深度行情
self.bidPrice2 = 0
self.bidPrice3 = 0
self.bidPrice4 = 0
self.bidPrice5 = 0
self.askPrice1 = 0
self.askPrice2 = 0
self.askPrice3 = 0
self.askPrice4 = 0
self.askPrice5 = 0
self.bidVolume1 = 0
self.bidVolume2 = 0
self.bidVolume3 = 0
self.bidVolume4 = 0
self.bidVolume5 = 0
self.askVolume1 = 0
self.askVolume2 = 0
self.askVolume3 = 0
self.askVolume4 = 0
self.askVolume5 = 0
########################################################################
class Bar(object):
"""K线数据"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
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 EmaData(object):
"""数据"""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
self.symbol = EMPTY_STRING # 代码
self.fastEMA = EMPTY_FLOAT # 快速EMA的数值
self.slowEMA = EMPTY_FLOAT # 慢速EMA的数值
self.date = EMPTY_STRING # EMA开始的时间日期
self.time = EMPTY_STRING # 时间
self.datetime = None # python的datetime时间对象
########################################################################
class Trade(object):
"""成交数据对象"""
#----------------------------------------------------------------------
def __init__(self, symbol):
"""Constructor"""
self.symbol = symbol # 合约代码
self.orderRef = '' # 报单号
self.tradeID = '' # 成交编号
self.direction = None # 方向
self.offset = None # 开平
self.price = 0 # 成交价
self.volume = 0 # 成交量
self.tradeTime = '' # 成交时间
########################################################################
class Order(object):
"""报单数据对象"""
#----------------------------------------------------------------------
def __init__(self, symbol):
"""Constructor"""
self.symbol = symbol # 合约代码
self.orderRef = '' # 报单编号
self.direction = None # 方向
self.offset = None # 开平
self.price = 0 # 委托价
self.volumeOriginal = 0 # 报单量
self.volumeTraded = 0 # 已成交数量
self.insertTime = '' # 报单时间
self.cancelTime = '' # 撤单时间
self.frontID = 0 # 前置机编号
self.sessionID = 0 # 会话编号
self.status = '' # 报单状态代码
self.preTradeID = '' # 上一成交单编号(用于平仓)
########################################################################
class StopOrder(object):
"""
停止单对象
用于实现价格突破某一水平后自动追入
即通常的条件单和止损单
"""
#----------------------------------------------------------------------
def __init__(self, symbol, direction, offset, price, volume, strategy):
"""Constructor"""
self.symbol = symbol
self.direction = direction
self.offset = offset
self.price = price
self.volume = volume
self.strategy = strategy
########################################################################
class StrategyEngine(object):
"""策略引擎"""
#----------------------------------------------------------------------
def __init__(self, eventEngine, mainEngine, backtesting=False):
"""Constructor"""
self.__eventEngine = eventEngine # 引用事件引擎
self.mainEngine = mainEngine # 主引擎在回测中为backtestingEngine,在交易中为MainEngine
self.backtesting = backtesting # 是否在进行回测
# 获取代表今日的datetime
t = datetime.today()
self.today = t.replace(hour=0, minute=0, second=0, microsecond=0)
# 保存所有报单数据的字典
self.__dictOrder = {}
# 保存策略对象的字典
# key为策略名称
# value为策略对象
self.dictStrategy = {}
# 保存合约代码和策略对象映射关系的字典
# key为合约代码
# value为交易该合约的策略列表
self.__dictSymbolStrategy = {}
# 保存报单编号和策略对象映射关系的字典
# key为报单编号
# value为策略对象
self.__dictOrderRefStrategy = {}
# 保存合约代码和相关停止单(止损单)的字典
# key为合约代码
# value为该合约相关的停止单列表
self.__dictStopOrder = {}
# MongoDB/Mysql数据库相关
#self.__mongoConnected = False
self.__mysqlConnected = False
#self.__mongoConnection = None
self.__mysqlConnection = None
#self.__mongoTickDB = None
# 调用函数
#self.__connectMongo()
self.__connectMysql()
self.__registerEvent()
#----------------------------------------------------------------------
def createStrategy(self, strategyName, strategySymbol, strategyClass, strategySetting):
"""创建策略(实例化)"""
strategy = strategyClass(strategyName, strategySymbol, self)
self.writeLog(u"创建策略:{0}".format(strategyName))
self.dictStrategy[strategyName] = strategy
strategy.loadSetting(strategySetting)
# 订阅合约行情注意这里因为是CTP所以ExchangeID可以忽略
self.mainEngine.subscribe(strategySymbol, None)
# 注册策略监听
self.registerStrategy(strategySymbol, strategy)
#----------------------------------------------------------------------
#def __connectMongo(self):
# """连接MongoDB数据库"""
# try:
# self.__mongoConnection = Connection()
# self.__mongoConnected = True
# self.__mongoTickDB = self.__mongoConnection['TickDB']
# self.writeLog(u'策略引擎连接MongoDB成功')
# except ConnectionFailure:
# self.writeLog(u'策略引擎连接MongoDB失败')
#-------------------------------------------#---------------------------
# def __recordTickToMongo(self, data):
# """将Tick数据插入到MongoDB中"""
# if self.__mongoConnected:
# symbol = data['InstrumentID']
# data['date'] = self.today
# self.__mongoTickDB[symbol].insert(data)
#
# #----------------------------------------------------------------------
# def loadTickFromMongo(self, symbol, startDate, endDate=None):
# """从MongoDB中读取Tick数据"""
# if self.__mongoConnected:
# collection = self.__mongoTickDB[symbol]
#
# # 如果输入了读取TICK的最后日期
# if endDate:
# cx = collection.find({'date':{'$gte':startDate, '$lte':endDate}})
# else:
# cx = collection.find({'date':{'$gte':startDate}})
# return cx
# else:
# return None
#----------------------------------------------------------------------
def __connectMysql(self):
"""连接MysqlDB"""
# 载入json文件
fileName = 'mysql_connect.json'
try:
f = file(fileName)
except IOError:
self.writeLog(u'回测引擎读取Mysql_connect.json失败')
return
# 解析json文件
setting = json.load(f)
try:
mysql_host = str(setting['host'])
mysql_port = int(setting['port'])
mysql_user = str(setting['user'])
mysql_passwd = str(setting['passwd'])
mysql_db = str(setting['db'])
except IOError:
self.writeLog(u'回测引擎读取Mysql_connect.json,连接配置缺少字段,请检查')
return
try:
self.__mysqlConnection = MySQLdb.connect(host=mysql_host, user=mysql_user,
passwd=mysql_passwd, db=mysql_db, port=mysql_port)
self.__mysqlConnected = True
self.writeLog(u'策略引擎连接MysqlDB成功')
except ConnectionFailure:
self.writeLog(u'策略引擎连接MysqlDB失败')
#----------------------------------------------------------------------
def __recordTickToMysql(self, data):
"""将Tick数据插入到MysqlDB中"""
#if self.__mongoConnected:
# symbol = data['InstrumentID']
# data['date'] = self.today
# self.__mongoTickDB[symbol].insert(data)
pass
#----------------------------------------------------------------------
def __executeMysql(self, sql):
"""执行mysql语句"""
if not self.__mysqlConnected:
self.__connectMysql()
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
try:
cur.execute(sql)
self.__mysqlConnection.commit()
except Exception, e:
print e
print sql
self.__connectMysql()
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
cur.execute(sql)
self.__mysqlConnection.commit()
#----------------------------------------------------------------------
def loadTickFromMysql(self, symbol, startDate, endDate=None):
"""从MysqlDB中读取Tick数据"""
if self.__mysqlConnected:
#获取指针
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
if endDate:
#指定开始与结束日期
sqlstring = ' select \'{0}\' as InstrumentID, str_to_date(concat(ndate,\' \', ntime),' \
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB_{0}MI ' \
'where ndate between cast(\'{1}\' as date) and cast(\'{2}\' as date)'.format(symbol, startDate, endDate)
elif startDate:
#指定开始日期
sqlstring = ' select \'{0}\' as InstrumentID,str_to_date(concat(ndate,\' \', ntime),' \
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB__{0}MI ' \
'where ndate > cast(\'{1}\' as date)'.format( symbol, startDate)
else:
#没有指定,所有日期数据
sqlstring =' select \'{0}\' as InstrumentID,str_to_date(concat(ndate,\' \', ntime),' \
'\'%Y-%m-%d %H:%i:%s\') as UpdateTime,price as LastPrice,vol as Volume,' \
'position_vol as OpenInterest,bid1_price as BidPrice1,bid1_vol as BidVolume1, ' \
'sell1_price as AskPrice1, sell1_vol as AskVolume1 from TB__{0}MI '.format(symbol)
print sqlstring
count = cur.execute(sqlstring)
# cx = cur.fetchall()
fetch_counts = 0
cx = None
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
else:
return None
#----------------------------------------------------------------------
def loadBarFromMysql(self, symbol, startDate, endDate, barType='M5'):
"""从MysqlDB中读取Bar数据
startDate,(包含)开始日期
endDate (包含)结束日期
barType ='M1' K线类型,1分钟线
barType ='M5' K线类型5分钟线
barType = 'D1' K线类型日线
"""
if self.__mysqlConnected:
#获取指针
cur = self.__mysqlConnection.cursor(MySQLdb.cursors.DictCursor)
if endDate:
# 指定开始与结束日期
sqlstring = 'select open,high,low,close,volume,date,time,datetime from TB_{0}{1} ' \
'where date between cast(\'{2}\' as date) and cast(\'{3}\' as date) ' \
'order by datetime'.format(symbol, barType, startDate, endDate)
elif startDate:
# 指定开始日期
sqlstring = 'select open,high,low,close,volume,date,time,datetime from TB_{0}{1} ' \
'where date > cast(\'{2}\' as date) order by datetime'.format(symbol,
barType, startDate)
else:
# 没有指定,所有日期数据
sqlstring = 'select open,high,low,close,volume,date,time,datetime from TB_{0}{1} ' \
' order by datetime'.format(symbol, barType)
print sqlstring
count = cur.execute(sqlstring)
# cx = cur.fetchall()
fetch_counts = 0
cx = None
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'历史{0}Bar数据载入{1}'.format(barType, fetch_counts)
self.writeLog(u'历史{0}Bar数据载入完成{1}~{2},共{3}'.format(barType,startDate, endDate, count))
print u'策略引擎:历史{0}Bar数据载入完成{1}~{2},共{3}'.format(barType,startDate, endDate, count)
return cx
else:
return None
#----------------------------------------------------------------------
def getMysqlDeltaDate(self,symbol, startDate, decreaseDays):
"""从mysql获取交易日天数差"""
try:
if self.__mysqlConnected:
#获取指针
cur = self.__mysqlConnection.cursor()
sqlstring='select distinct ndate from TB_{0}MI where ndate < ' \
'cast(\'{1}\' as date) order by ndate desc limit {2},1'.format(symbol, startDate, decreaseDays-1)
self.writeLog(sqlstring)
count = cur.execute(sqlstring)
if count > 0:
result = cur.fetchone()
return result[0]
else:
self.writeLog(u'MysqlDB没有查询结果请检查日期')
else:
self.writeLog(u'MysqlDB未连接请检查')
except MySQLdb.Error, e:
self.writeLog(u'MysqlDB载入数据失败请检查.Error %s'.format(e))
td = timedelta(days=3)
return startDate-td;
#----------------------------------------------------------------------
def saveBarToMysql(self,id, barList):
"""
保存K线数据到数据库
id 回测ID
barList 对象为Bar的列表
"""
# 保存本地pickle文件
resultPath=os.getcwd()+'/result'
if not os.path.isdir(resultPath):
os.mkdir(resultPath)
resultFile = u'{0}/{1}_Bar.pickle'.format(resultPath,id)
cache= open(resultFile, mode='w')
cPickle.dump(barList,cache)
cache.close()
# 保存数据库
self.__connectMysql()
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))
if len(barList) == 0:
return
counts = 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 counts >= 3600:
self.__executeMysql(sql+values)
print u'写入{0}条Bar记录'.format(counts)
counts = 0
values = ''
else:
counts = counts + 1
if counts > 0:
self.__executeMysql(sql+values)
print u'写入{0}条Bar记录'.format(counts)
#----------------------------------------------------------------------
def saveEmaToMysql(self, id, emaList):
"""
保存EMA到数据库
id,回测的编号
"""
# 保存本地pickle文件
resultPath=os.getcwd()+'/result'
if not os.path.isdir(resultPath):
os.mkdir(resultPath)
resultFile = u'{0}/{1}_Ema.pickle'.format(resultPath, id)
cache= open(resultFile, mode='w')
cPickle.dump(emaList,cache)
cache.close()
self.__connectMysql()
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))
if len(emaList) == 0:
return
counts = 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 counts >= 3600:
self.__executeMysql(sql+values)
print u'写入{0}条EMA记录'.format(counts)
counts = 0
values = ''
else:
counts = counts + 1
if counts > 0:
self.__executeMysql(sql+values)
print u'写入{0}条EMA记录'.format(counts)
#----------------------------------------------------------------------
def updateMarketData(self, event):
"""行情更新"""
data = event.dict_['data']
symbol = data['InstrumentID']
# 检查是否存在交易该合约的策略
if symbol in self.__dictSymbolStrategy:
# 创建TICK数据对象并更新数据
tick = Tick(symbol)
# tick.openPrice = data['OpenPrice']
# tick.highPrice = data['HighestPrice']
# tick.lowPrice = data['LowestPrice']
tick.lastPrice = float(data['LastPrice'])
tick.volume = data['Volume']
tick.openInterest = data['OpenInterest']
# tick.upperLimit = data['UpperLimitPrice']
# tick.lowerLimit = data['LowerLimitPrice']
tick.time = data['UpdateTime']
tick.bidPrice1 = float(data['BidPrice1'])
# tick.bidPrice2 = data['BidPrice2']
# tick.bidPrice3 = data['BidPrice3']
# tick.bidPrice4 = data['BidPrice4']
# tick.bidPrice5 = data['BidPrice5']
tick.askPrice1 = float(data['AskPrice1'])
# tick.askPrice2 = data['AskPrice2']
# tick.askPrice3 = data['AskPrice3']
# tick.askPrice4 = data['AskPrice4']
# tick.askPrice5 = data['AskPrice5']
tick.bidVolume1 = data['BidVolume1']
# tick.bidVolume2 = data['BidVolume2']
# tick.bidVolume3 = data['BidVolume3']
# tick.bidVolume4 = data['BidVolume4']
# tick.bidVolume5 = data['BidVolume5']
tick.askVolume1 = data['AskVolume1']
# tick.askVolume2 = data['AskVolume2']
# tick.askVolume3 = data['AskVolume3']
# tick.askVolume4 = data['AskVolume4']
# tick.askVolume5 = data['AskVolume5']
# 首先检查停止单是否需要发出
self.__processStopOrder(tick)
# 将该TICK数据推送给每个策略
for strategy in self.__dictSymbolStrategy[symbol]:
strategy.onTick(tick)
# 将数据插入MongoDB/Mysql数据库实盘建议另开程序记录TICK数据
if not self.backtesting:
# self.__recordTickToMongo(data)
self.__recordTickToMysql(data)
#----------------------------------------------------------------------
def __processStopOrder(self, tick):
"""处理停止单"""
symbol = tick.symbol
lastPrice = tick.lastPrice
upperLimit = tick.upperLimit
lowerLimit = tick.lowerLimit
# 如果当前有该合约上的止损单
if symbol in self.__dictStopOrder:
# 获取止损单列表
listSO = self.__dictStopOrder[symbol] # SO:stop order
# 准备一个空的已发止损单列表
listSent = []
for so in listSO:
# 如果是买入停止单,且最新成交价大于停止触发价
if so.direction == DIRECTION_BUY and lastPrice >= so.price:
# 以当日涨停价发出限价单买入
print u'sendOrder({0},{1},{2},{3},{4}'.format(symbol,'Direction_Buy',so.offset,upperLimit,so.volume)
ref = self.sendOrder(symbol, DIRECTION_BUY, so.offset,
upperLimit, so.volume, tick.time, so.strategy)
# 触发策略的止损单发出更新
so.strategy.onStopOrder(ref)
# 将该止损单对象保存到已发送列表中
listSent.append(so)
# 如果是卖出停止单,且最新成交价小于停止触发价
elif so.direction == DIRECTION_SELL and lastPrice <= so.price:
print u'sendOrder({0},{1},{2},{3},{4}'.format(symbol,'Direction_Sell',so.offset,upperLimit,so.volume)
ref = self.sendOrder(symbol, DIRECTION_SELL, so.offset,
lowerLimit, so.volume,tick.time, so.strategy)
so.strategy.onStopOrder(ref)
listSent.append(so)
# 从停止单列表中移除已经发单的停止单对象
if listSent:
for so in listSent:
listSO.remove(so)
# 检查停止单列表是否为空,若为空,则从停止单字典中移除该合约代码
if not listSO:
del self.__dictStopOrder[symbol]
#----------------------------------------------------------------------
def updateOrder(self, event):
"""事件响应:报单更新"""
data = event.dict_['data']
orderRef = data['OrderRef']
# 检查是否存在监听该报单的策略
if orderRef in self.__dictOrderRefStrategy:
# 创建Order数据对象
order = Order(data['InstrumentID'])
order.orderRef = data['OrderRef']
order.direction = data['Direction']
order.offset = data['CombOffsetFlag']
order.price = data['LimitPrice']
order.volumeOriginal = data['VolumeTotalOriginal']
order.volumeTraded = data['VolumeTraded']
order.insertTime = data['InsertTime']
order.cancelTime = data['CancelTime']
order.frontID = data['FrontID']
order.sessionID = data['SessionID']
order.status = data['OrderStatus']
# 推送给策略
strategy = self.__dictOrderRefStrategy[orderRef]
strategy.onOrder(order)
# 记录该Order的数据
self.__dictOrder[orderRef] = data
#----------------------------------------------------------------------
def updateTrade(self, event):
"""事件响应:成交更新"""
data = event.dict_['data']
orderRef = data['OrderRef']
if orderRef in self.__dictOrderRefStrategy:
# 创建Trade数据对象
trade = Trade(data['InstrumentID']) # 合约代码
trade.orderRef = orderRef # 报单号
trade.tradeID = data['TradeID'] # 成交编号
trade.direction = data['Direction'] # 方向
trade.offset = data['OffsetFlag'] # 开平
trade.price = data['Price'] # 成交价
trade.volume = data['Volume'] # 成交量
trade.tradeTime = data['TradeTime'] # 成交时间
# 推送给策略
strategy = self.__dictOrderRefStrategy[orderRef]
strategy.onTrade(trade)
#----------------------------------------------------------------------
def sendOrder(self, symbol, direction, offset, price, volume, ordertime, strategy):
"""
发单(仅允许限价单)
symbol合约代码
direction方向DIRECTION_BUY/DIRECTION_SELL
offset开平OFFSET_OPEN/OFFSET_CLOSE
price下单价格
volume下单手数
ordertime:下单时间(回归测试使用)
strategy策略对象
"""
contract = self.mainEngine.selectInstrument(symbol)
if contract:
# 调用主引擎的发单函数
ref = self.mainEngine.sendOrder(symbol,
contract['ExchangeID'],
price,
PRICETYPE_LIMIT,
volume,
direction,
offset,
ordertime)
# 添加报单编号及其映射的策略
self.__dictOrderRefStrategy[ref] = strategy
return ref
#----------------------------------------------------------------------
def cancelOrder(self, orderRef):
"""
撤单
"""
print u'strategyEngine.py cancelOrder() begin.'
order = self.__dictOrder[orderRef]
symbol = order['InstrumentID']
contract = self.mainEngine.selectInstrument(symbol)
if contract:
#调用主引擎的撤单函数
self.mainEngine.cancelOrder(symbol,
contract['ExchangeID'],
orderRef,
order['FrontID'],
order['SessionID'])
print u'strategyEngine.py cancelOrder() end.'
#----------------------------------------------------------------------
def __registerEvent(self):
"""注册事件监听"""
# 注册 订阅行情数据更新事件
self.__eventEngine.register(EVENT_MARKETDATA, self.updateMarketData)
# 注册 订阅订单更新事件
self.__eventEngine.register(EVENT_ORDER, self.updateOrder)
# 注册 订阅交易响应事件
self.__eventEngine.register(EVENT_TRADE ,self.updateTrade)
#----------------------------------------------------------------------
def writeLog(self, log):
"""写日志"""
event = Event(type_=EVENT_LOG)
event.dict_['log'] = log
self.__eventEngine.put(event)
#----------------------------------------------------------------------
def registerStrategy(self, symbol, strategy):
"""注册策略对合约TICK数据的监听"""
print u'strategyEngine.py registerStrategy() begin.'
# 尝试获取监听该合约代码的策略的列表,若无则创建
try:
listStrategy = self.__dictSymbolStrategy[symbol]
except KeyError:
listStrategy = []
self.__dictSymbolStrategy[symbol] = listStrategy
# 防止重复注册
if strategy not in listStrategy:
listStrategy.append(strategy)
print u'strategyEngine.py registerStrategy() end.'
#----------------------------------------------------------------------
def placeStopOrder(self, symbol, direction, offset, price, volume, strategy):
"""
下停止单(止损单,运行于本地引擎中)
注意这里的price是停止单的触发价
"""
# 创建止损单对象
print u'strategyEngine.py placeStopOrder() symbol:{0}, direction:{1}, offset:{2}, price:{3}, volume:{4}.'\
.format(symbol, direction, offset, price, volume)
so = StopOrder(symbol, direction, offset, price, volume, strategy)
# 获取该合约相关的止损单列表
try:
listSO = self.__dictStopOrder[symbol]
except KeyError:
listSO = []
self.__dictStopOrder[symbol] = listSO
# 将该止损单插入列表中
listSO.append(so)
return so
#----------------------------------------------------------------------
def cancelStopOrder(self, so):
"""撤销停止单"""
symbol = so.symbol
try:
listSO = self.__dictStopOrder[symbol]
if so in listSO:
listSO.remove(so)
if not listSO:
del self.__dictStopOrder[symbol]
except KeyError:
pass
#----------------------------------------------------------------------
def startAll(self):
"""启动所有策略"""
print( u'启动所有策略')
for strategy in self.dictStrategy.values():
strategy.start()
#----------------------------------------------------------------------
def stopAll(self):
"""停止所有策略"""
print(u'停止所有策略')
for strategy in self.dictStrategy.values():
strategy.stop()
#----------------------------------------------------------------------
def saveData(self,id):
"""保存所有策略的过程数据"""
print(u'保存所有策略的过程数据')
for strategy in self.dictStrategy.values():
strategy.saveData(id)
########################################################################
class StrategyTemplate(object):
"""策略模板"""
#----------------------------------------------------------------------
def __init__(self, name, symbol, engine):
"""Constructor"""
self.name = name # 策略名称(注意唯一性)
self.symbol = symbol # 策略交易的合约
self.engine = engine # 策略引擎对象
self.trading = False # 策略是否启动交易
#----------------------------------------------------------------------
def onTick(self, tick):
"""行情更新"""
raise NotImplementedError
#----------------------------------------------------------------------
def onTrade(self, trade):
"""交易更新"""
raise NotImplementedError
#----------------------------------------------------------------------
def onOrder(self, order):
"""报单更新"""
raise NotImplementedError
#----------------------------------------------------------------------
def onStopOrder(self, orderRef):
"""停止单更新"""
raise NotImplementedError
#----------------------------------------------------------------------
def onBar(self, o, h, l, c, volume, time):
"""K线数据更新"""
raise NotImplementedError
#----------------------------------------------------------------------
def start(self):
"""
启动交易
这里是最简单的改变self.trading
有需要可以重新实现更复杂的操作
"""
self.trading = True
self.engine.writeLog(self.name + u'开始运行')
#----------------------------------------------------------------------
def saveData(self,Id):
"""保存数据"""
raise NotImplementedError
#----------------------------------------------------------------------
def stop(self):
"""
停止交易
同上
"""
self.trading = False
self.engine.writeLog(self.name + u'停止运行')
#----------------------------------------------------------------------
def loadSetting(self, setting):
"""
载入设置
setting通常是一个包含了参数设置的字典
"""
raise NotImplementedError
#----------------------------------------------------------------------
def buy(self, price, volume, orderTime, stopOrder=False ):
"""买入开仓"""
print u'strategyEngine.py StrategyTemplate({3}) buy(symbol:{0}, price:{1},volume:{2},time:{4})'.\
format(self.symbol, price, volume, self.name,orderTime)
if self.trading:
if stopOrder:
# 止损单
so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY,
OFFSET_OPEN, price, volume, self)
return so
else:
# 委托单
ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY,
OFFSET_OPEN, price, volume, orderTime, self)
return ref
else:
return None
#print (u'strategyEngine.py buy() end.')
#----------------------------------------------------------------------
def cover(self, price, volume,orderTime, stopOrder=False):
"""买入平仓"""
print u'strategyEngine.py StrategyTemplate({3}) cover(symbol:{0}, price:{1},volume:{2},time:{4})'.\
format(self.symbol, price, volume, self.name, orderTime)
if self.trading:
if stopOrder:
# 止损单
so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY,
OFFSET_CLOSE, price, volume, self)
return so
else:
# 委托单
ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY,
OFFSET_CLOSE, price, volume, orderTime, self)
return ref
else:
return None
print (u'strategyEngine.py cover() end.')
#----------------------------------------------------------------------
def sell(self, price, volume, orderTime, stopOrder=False):
"""卖出平仓"""
print u'strategyEngine.py StrategyTemplate({3}) sell(symbol:{0}, price:{1},volume:{2},time:{4})'.\
format(self.symbol, price, volume, self.name, orderTime)
if self.trading:
if stopOrder:
# 止损单
so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL,
OFFSET_CLOSE, price, volume, self)
return so
else:
# 委托单
ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL,
OFFSET_CLOSE, price, volume, orderTime, self)
return ref
else:
return None
#print u'strategyEngine.py sell() end.'
#----------------------------------------------------------------------
def short(self, price, volume, orderTime, stopOrder=False):
"""卖出开仓"""
print u'strategyEngine.py StrategyTemplate({3}) short(symbol:{0}, price:{1},volume:{2},time:{4})'.\
format(self.symbol, price, volume, self.name, orderTime)
if self.trading:
if stopOrder:
# 止损单
so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL,
OFFSET_OPEN, price, volume, self)
return so
else:
# 委托单
ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL,
OFFSET_OPEN, price, volume, orderTime, self)
return ref
else:
return None
#----------------------------------------------------------------------
def cancelOrder(self, orderRef):
"""撤单"""
self.engine.cancelOrder(orderRef)
#----------------------------------------------------------------------
def cancelStopOrder(self, so):
"""撤销停止单"""
self.engine.cancelStopOrder(so)