diff --git a/vn.datayes/README.md b/vn.datayes/README.md new file mode 100644 index 00000000..61f912f4 --- /dev/null +++ b/vn.datayes/README.md @@ -0,0 +1 @@ +To be completed. \ No newline at end of file diff --git a/vn.trader/ctpGateway.py b/vn.trader/ctpGateway.py new file mode 100644 index 00000000..a4fd81e7 --- /dev/null +++ b/vn.trader/ctpGateway.py @@ -0,0 +1,991 @@ +# encoding: UTF-8 + +from vnctpmd import MdApi +from vnctptd import TdApi + +from gateway import * + +import os + + +######################################################################## +class CtpGateway(VtGateway): + """CTP接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine): + """Constructor""" + super(CtpGateway, self).__init__(eventEngine) + + self.mdApi = None # 行情API + self.tdApi = None # 交易API + + self.mdConnected = False # 行情API连接状态 + self.tdConnected = False # 交易API连接状态 + + + + + +######################################################################## +class CtpMdApi(MdApi): + """CTP行情API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway, userID, password, brokerID, address): + """Constructor""" + super(CtpMdApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.reqID = EMPTY_INT # 操作请求编号 + + self.connectionStatus = False # 连接状态 + self.loginStatus = False # 登录状态 + + self.userID = userID # 账号 + self.password = password # 密码 + self.brokerID = brokerID # 经纪商代码 + self.address = address # 服务器地址 + + self.subscribedSymbols = set() # 已订阅合约代码 + + #---------------------------------------------------------------------- + def onFrontConnected(self): + """服务器连接""" + self.connectionStatus = True + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'行情服务器连接成功' + self.gateway.onLog(log) + + self.login() + + #---------------------------------------------------------------------- + def onFrontDisconnected(self, n): + """服务器断开""" + self.connectionStatus = False + self.loginStatus = False + self.gateway.mdConnected = False + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'行情服务器连接断开' + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def onHeartBeatWarning(self, n): + """心跳报警""" + # 因为API的心跳报警比较常被触发,且与API工作关系不大,因此选择忽略 + pass + + #---------------------------------------------------------------------- + def onRspError(self, error, n, last): + """错误回报""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspUserLogin(self, data, error, n, last): + """登陆回报""" + # 如果登录成功,推送日志信息 + if error['ErrorID'] == 0: + self.loginStatus = True + self.gateway.mdConnected = True + + log = VtLogData() + log.logContent = u'行情服务器登录完成' + self.gateway.onLog(log) + + # 重新订阅之前订阅的合约 + for subscribeReq in self.subscribedSymbols: + self.subscribe(subscribeReq) + + # 否则,推送错误信息 + else: + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspUserLogout(self, data, error, n, last): + """登出回报""" + # 如果登出成功,推送日志信息 + if error['ErrorID'] == 0: + self.loginStatus = False + self.gateway.tdConnected = False + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'行情服务器登出完成' + self.gateway.onLog(log) + + # 否则,推送错误信息 + else: + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspSubMarketData(self, data, error, n, last): + """订阅合约回报""" + # 通常不在乎订阅错误,选择忽略 + pass + + #---------------------------------------------------------------------- + def onRspUnSubMarketData(self, data, error, n, last): + """退订合约回报""" + # 同上 + pass + + #---------------------------------------------------------------------- + def onRtnDepthMarketData(self, data): + """行情推送""" + tick = VtTickData() + + tick.symbol = data['InstrumentID'] + tick.vtSymbol = '.'.join([self.gatewayName, tick.symbol]) + + tick.lastPrice = data['LastPrice'] + tick.volume = data['Volume'] + tick.openInterest = data['OpenInterest'] + tick.tickTime = '.'.join([data['UpdateTime'], str(data['UpdateMillisec']/100])) + + # CTP只有一档行情 + tick.bidPrice1 = data['BidPrice1'] + tick.bidVolume1 = data['BidVolume1'] + tick.askPrice1 = data['AskPrice1'] + tick.askVolume1 = data['AskVolume1'] + + self.gateway.onTick(tick) + + #---------------------------------------------------------------------- + def onRspSubForQuoteRsp(self, data, error, n, last): + """订阅期权询价""" + pass + + #---------------------------------------------------------------------- + def onRspUnSubForQuoteRsp(self, data, error, n, last): + """退订期权询价""" + pass + + #---------------------------------------------------------------------- + def onRtnForQuoteRsp(self, data): + """期权询价推送""" + pass + + #---------------------------------------------------------------------- + def connect(self): + """初始化连接""" + # 如果尚未建立服务器连接,则进行连接 + if not self.connectionStatus: + # 创建C++环境中的API对象,这里传入的参数是需要用来保存.con文件的文件夹路径 + path = os.getcwd() + '\\temp\\' + self.gatewayName + '\\' + if not os.path.exists(path): + os.makedirs(path) + self.createFtdcMdApi(path) + + # 注册服务器地址 + self.registerFront(self.address) + + # 初始化连接,成功会调用onFrontConnected + self.init() + + # 若已经连接但尚未登录,则进行登录 + else: + if not self.loginStatus: + self.login() + + #---------------------------------------------------------------------- + def subscribe(self, subscribeReq): + """订阅合约""" + self.subscribeMarketData(subscribeReq.symbol) + self.subscribedSymbols.add(subscribeReq) + + #---------------------------------------------------------------------- + def login(self): + """登录""" + # 如果填入了用户名密码等,则登录 + if self.userID and self.password and self.brokerID: + req = {} + req['UserID'] = self.userID + req['Password'] = self.password + req['BrokerID'] = self.brokerID + self.reqID += 1 + self.reqUserLogin(req, self.reqID) + + +######################################################################## +class CtpTdApi(TdApi): + """CTP交易API实现""" + + #---------------------------------------------------------------------- + def __init__(self, gateway, userID, password, brokerID, address): + """API对象的初始化函数""" + super(CtpTdApi, self).__init__() + + self.gateway = gateway # gateway对象 + self.gatewayName = gateway.gatewayName # gateway对象名称 + + self.reqID = EMPTY_INT # 操作请求编号 + self.orderRef = EMPTY_INT # 订单编号 + + self.connectionStatus = False # 连接状态 + self.loginStatus = False # 登录状态 + + self.userID = userID # 账号 + self.password = password # 密码 + self.brokerID = brokerID # 经纪商代码 + self.address = address # 服务器地址 + + #---------------------------------------------------------------------- + def onFrontConnected(self): + """服务器连接""" + self.connectionStatus = True + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'交易服务器连接成功' + self.gateway.onLog(log) + + self.login() + + #---------------------------------------------------------------------- + def onFrontDisconnected(self, n): + """服务器断开""" + self.connectionStatus = False + self.loginStatus = False + self.gateway.tdConnected = False + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'交易服务器连接断开' + self.gateway.onLog(log) + + #---------------------------------------------------------------------- + def onHeartBeatWarning(self, n): + """""" + pass + + #---------------------------------------------------------------------- + def onRspAuthenticate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspUserLogin(self, data, error, n, last): + """登陆回报""" + # 如果登录成功,推送日志信息 + if error['ErrorID'] == 0: + self.loginStatus = True + self.gateway.mdConnected = True + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'交易服务器登录完成' + self.gateway.onLog(log) + + # 确认结算信息 + req = {} + req['BrokerID'] = self.brokerID + req['InvestorID'] = self.userID + self.reqID += 1 + self.reqSettlementInfoConfirm(req, self.reqID) + + # 否则,推送错误信息 + else: + err = VtErrorData() + err.gatewayName = self.gateway + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspUserLogout(self, data, error, n, last): + """登出回报""" + # 如果登出成功,推送日志信息 + if error['ErrorID'] == 0: + self.loginStatus = False + self.gateway.tdConnected = False + + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'交易服务器登出完成' + self.gateway.onLog(log) + + # 否则,推送错误信息 + else: + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspUserPasswordUpdate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspTradingAccountPasswordUpdate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspOrderInsert(self, data, error, n, last): + """发单错误(柜台)""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspParkedOrderInsert(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspParkedOrderAction(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspOrderAction(self, data, error, n, last): + """撤单错误(柜台)""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRspQueryMaxOrderVolume(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspSettlementInfoConfirm(self, data, error, n, last): + """确认结算信息回报""" + log = VtLogData() + log.gatewayName = self.gatewayName + log.logContent = u'结算信息确认完成' + self.gateway.onLog(log) + + # 查询合约代码 + self.reqID += 1 + self.reqQryInstrument({}, self.reqID) + + #---------------------------------------------------------------------- + def onRspRemoveParkedOrder(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspRemoveParkedOrderAction(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspExecOrderInsert(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspExecOrderAction(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspForQuoteInsert(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQuoteInsert(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQuoteAction(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryOrder(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryTrade(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInvestorPosition(self, data, error, n, last): + """持仓查询回报""" + if error['ErrorID'] == 0: + event = Event(type_=EVENT_POSITION) + event.dict_['data'] = data + self.__eventEngine.put(event) + else: + event = Event(type_=EVENT_LOG) + log = u'持仓查询回报,错误代码:' + unicode(error['ErrorID']) + u',' + u'错误信息:' + error['ErrorMsg'].decode('gbk') + event.dict_['log'] = log + self.__eventEngine.put(event) + + #---------------------------------------------------------------------- + def onRspQryTradingAccount(self, data, error, n, last): + """资金账户查询回报""" + if error['ErrorID'] == 0: + event = Event(type_=EVENT_ACCOUNT) + event.dict_['data'] = data + self.__eventEngine.put(event) + else: + event = Event(type_=EVENT_LOG) + log = u'账户查询回报,错误代码:' + unicode(error['ErrorID']) + u',' + u'错误信息:' + error['ErrorMsg'].decode('gbk') + event.dict_['log'] = log + self.__eventEngine.put(event) + + #---------------------------------------------------------------------- + def onRspQryInvestor(self, data, error, n, last): + """投资者查询回报""" + pass + + #---------------------------------------------------------------------- + def onRspQryTradingCode(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInstrumentMarginRate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInstrumentCommissionRate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryExchange(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryProduct(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInstrument(self, data, error, n, last): + """ + 合约查询回报 + 由于该回报的推送速度极快,因此不适合全部存入队列中处理, + 选择先储存在一个本地字典中,全部收集完毕后再推送到队列中 + (由于耗时过长目前使用其他进程读取) + """ + if error['ErrorID'] == 0: + event = Event(type_=EVENT_INSTRUMENT) + event.dict_['data'] = data + event.dict_['last'] = last + self.__eventEngine.put(event) + else: + event = Event(type_=EVENT_LOG) + log = u'合约投资者回报,错误代码:' + unicode(error['ErrorID']) + u',' + u'错误信息:' + error['ErrorMsg'].decode('gbk') + event.dict_['log'] = log + self.__eventEngine.put(event) + + #---------------------------------------------------------------------- + def onRspQryDepthMarketData(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQrySettlementInfo(self, data, error, n, last): + """查询结算信息回报""" + pass + + #---------------------------------------------------------------------- + def onRspQryTransferBank(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInvestorPositionDetail(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryNotice(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQrySettlementInfoConfirm(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInvestorPositionCombineDetail(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryCFMMCTradingAccountKey(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryEWarrantOffset(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryInvestorProductGroupMargin(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryExchangeMarginRate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryExchangeMarginRateAdjust(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryExchangeRate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQrySecAgentACIDMap(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryOptionInstrTradeCost(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryOptionInstrCommRate(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryExecOrder(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryForQuote(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryQuote(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryTransferSerial(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryAccountregister(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspError(self, error, n, last): + """错误回报""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRtnOrder(self, data): + """报单回报""" + # 更新最大报单编号 + newref = data['OrderRef'] + self.orderRef = max(self.orderRef, int(newref)) + + # 创建报单数据对象 + order = VtOrderData() + order.gatewayName = self.gatewayName + + # 保存代码和报单号 + order.symbol = data['InstrumentID'] + order.vtSymbol = '.'.join([self.gatewayName, order.symbol]) + + order.orderID = data['OrderRef'] + order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) + + # 方向 + if data['Direction'] == '0': + order.direction = DIRECTION_LONG + elif data['Direction'] == '1': + order.direction = DIRECTION_SHORT + else: + order.direction = DIRECTION_UNKNOWN + + # 多空 + if data[''] + + #---------------------------------------------------------------------- + def onRtnTrade(self, data): + """成交回报""" + # 常规成交事件 + event1 = Event(type_=EVENT_TRADE) + event1.dict_['data'] = data + self.__eventEngine.put(event1) + + # 特定合约成交事件 + event2 = Event(type_=(EVENT_TRADE_CONTRACT+data['InstrumentID'])) + event2.dict_['data'] = data + self.__eventEngine.put(event2) + + #---------------------------------------------------------------------- + def onErrRtnOrderInsert(self, data, error): + """发单错误回报(交易所)""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onErrRtnOrderAction(self, data, error): + """撤单错误回报(交易所)""" + err = VtErrorData() + err.gatewayName = self.gatewayName + err.errorID = error['ErrorID'] + err.errorMsg = error['ErrorMsg'].decode('gbk') + self.gateway.onError(err) + + #---------------------------------------------------------------------- + def onRtnInstrumentStatus(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnTradingNotice(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnErrorConditionalOrder(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnExecOrder(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnExecOrderInsert(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnExecOrderAction(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnForQuoteInsert(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnQuote(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnQuoteInsert(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnQuoteAction(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnForQuoteRsp(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryContractBank(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryParkedOrder(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryParkedOrderAction(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryTradingNotice(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryBrokerTradingParams(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQryBrokerTradingAlgos(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnFromBankToFutureByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnFromFutureToBankByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromBankToFutureByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromFutureToBankByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnFromBankToFutureByFuture(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnFromFutureToBankByFuture(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromBankToFutureByFutureManual(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromFutureToBankByFutureManual(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnQueryBankBalanceByFuture(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnBankToFutureByFuture(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnFutureToBankByFuture(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnRepealBankToFutureByFutureManual(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnRepealFutureToBankByFutureManual(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onErrRtnQueryBankBalanceByFuture(self, data, error): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromBankToFutureByFuture(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnRepealFromFutureToBankByFuture(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRspFromBankToFutureByFuture(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspFromFutureToBankByFuture(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRspQueryBankAccountMoneyByFuture(self, data, error, n, last): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnOpenAccountByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnCancelAccountByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def onRtnChangeAccountByBank(self, data): + """""" + pass + + #---------------------------------------------------------------------- + def login(self, address, userid, password, brokerid): + """连接服务器""" + self.__userid = userid + self.__password = password + self.__brokerid = brokerid + + # 数据重传模式设为从本日开始 + self.subscribePrivateTopic(0) + self.subscribePublicTopic(0) + + # 注册服务器地址 + self.registerFront(address) + + # 初始化连接,成功会调用onFrontConnected + self.init() + + #---------------------------------------------------------------------- + def getInstrument(self): + """查询合约""" + self.__reqid = self.__reqid + 1 + self.reqQryInstrument({}, self.__reqid) + + #---------------------------------------------------------------------- + def getAccount(self): + """查询账户""" + self.__reqid = self.__reqid + 1 + self.reqQryTradingAccount({}, self.__reqid) + + #---------------------------------------------------------------------- + def getInvestor(self): + """查询投资者""" + self.__reqid = self.__reqid + 1 + self.reqQryInvestor({}, self.__reqid) + + #---------------------------------------------------------------------- + def getPosition(self): + """查询持仓""" + self.__reqid = self.__reqid + 1 + req = {} + req['BrokerID'] = self.__brokerid + req['InvestorID'] = self.__userid + self.reqQryInvestorPosition(req, self.__reqid) + + #---------------------------------------------------------------------- + def sendOrder(self, instrumentid, exchangeid, price, pricetype, volume, direction, offset): + """发单""" + self.__reqid = self.__reqid + 1 + req = {} + + req['InstrumentID'] = instrumentid + req['OrderPriceType'] = pricetype + req['LimitPrice'] = price + req['VolumeTotalOriginal'] = volume + req['Direction'] = direction + req['CombOffsetFlag'] = offset + + self.__orderref = self.__orderref + 1 + req['OrderRef'] = str(self.__orderref) + + req['InvestorID'] = self.__userid + req['UserID'] = self.__userid + req['BrokerID'] = self.__brokerid + req['CombHedgeFlag'] = defineDict['THOST_FTDC_HF_Speculation'] # 投机单 + req['ContingentCondition'] = defineDict['THOST_FTDC_CC_Immediately'] # 立即发单 + req['ForceCloseReason'] = defineDict['THOST_FTDC_FCC_NotForceClose'] # 非强平 + req['IsAutoSuspend'] = 0 # 非自动挂起 + req['TimeCondition'] = defineDict['THOST_FTDC_TC_GFD'] # 今日有效 + req['VolumeCondition'] = defineDict['THOST_FTDC_VC_AV'] # 任意成交量 + req['MinVolume'] = 1 # 最小成交量为1 + + self.reqOrderInsert(req, self.__reqid) + + # 返回订单号,便于某些算法进行动态管理 + return self.__orderref + + #---------------------------------------------------------------------- + def cancelOrder(self, instrumentid, exchangeid, orderref, frontid, sessionid): + """撤单""" + self.__reqid = self.__reqid + 1 + req = {} + + req['InstrumentID'] = instrumentid + req['ExchangeID'] = exchangeid + req['OrderRef'] = orderref + req['FrontID'] = frontid + req['SessionID'] = sessionid + + req['ActionFlag'] = defineDict['THOST_FTDC_AF_Delete'] + req['BrokerID'] = self.__brokerid + req['InvestorID'] = self.__userid + + self.reqOrderAction(req, self.__reqid) + + #---------------------------------------------------------------------- + def getSettlement(self): + """查询结算信息""" + self.__reqid = self.__reqid + 1 + req = {} + + req['BrokerID'] = self.__brokerid + req['InvestorID'] = self.__userid + + self.reqQrySettlementInfo(req, self.__reqid) + + #---------------------------------------------------------------------- + def confirmSettlement(self): + """确认结算信息""" + self.__reqid = self.__reqid + 1 + req = {} + + req['BrokerID'] = self.__brokerid + req['InvestorID'] = self.__userid + + self.reqSettlementInfoConfirm(req, self.__reqid) \ No newline at end of file diff --git a/vn.trader/eventEngine.py b/vn.trader/eventEngine.py new file mode 100644 index 00000000..2ceada00 --- /dev/null +++ b/vn.trader/eventEngine.py @@ -0,0 +1,196 @@ +# encoding: UTF-8 + +# 系统模块 +from Queue import Queue, Empty +from threading import Thread + +# 第三方模块 +from PyQt4.QtCore import QTimer + +# 自己开发的模块 +from eventType import * + + +######################################################################## +class EventEngine: + """ + 事件驱动引擎 + + 事件驱动引擎中所有的变量都设置为了私有,这是为了防止不小心 + 从外部修改了这些变量的值或状态,导致bug。 + + 变量说明 + __queue:私有变量,事件队列 + __active:私有变量,事件引擎开关 + __thread:私有变量,事件处理线程 + __timer:私有变量,计时器 + __handlers:私有变量,事件处理函数字典 + + + 方法说明 + __run: 私有方法,事件处理线程连续运行用 + __process: 私有方法,处理事件,调用注册在引擎中的监听函数 + __onTimer:私有方法,计时器固定事件间隔触发后,向事件队列中存入计时器事件 + start: 公共方法,启动引擎 + stop:公共方法,停止引擎 + register:公共方法,向引擎中注册监听函数 + unregister:公共方法,向引擎中注销监听函数 + put:公共方法,向事件队列中存入新的事件 + + 事件监听函数必须定义为输入参数仅为一个event对象,即: + + 函数 + def func(event) + ... + + 对象方法 + def method(self, event) + ... + + """ + + #---------------------------------------------------------------------- + def __init__(self): + """初始化事件引擎""" + # 事件队列 + self.__queue = Queue() + + # 事件引擎开关 + self.__active = False + + # 事件处理线程 + self.__thread = Thread(target = self.__run) + + # 计时器,用于触发计时器事件 + self.__timer = QTimer() + self.__timer.timeout.connect(self.__onTimer) + + # 这里的__handlers是一个字典,用来保存对应的事件调用关系 + # 其中每个键对应的值是一个列表,列表中保存了对该事件进行监听的函数功能 + self.__handlers = {} + + #---------------------------------------------------------------------- + def __run(self): + """引擎运行""" + while self.__active == True: + try: + event = self.__queue.get(block = True, timeout = 1) # 获取事件的阻塞时间设为1秒 + self.__process(event) + except Empty: + pass + + #---------------------------------------------------------------------- + def __process(self, event): + """处理事件""" + # 检查是否存在对该事件进行监听的处理函数 + if event.type_ in self.__handlers: + # 若存在,则按顺序将事件传递给处理函数执行 + [handler(event) for handler in self.__handlers[event.type_]] + + # 以上语句为Python列表解析方式的写法,对应的常规循环写法为: + #for handler in self.__handlers[event.type_]: + #handler(event) + + #---------------------------------------------------------------------- + def __onTimer(self): + """向事件队列中存入计时器事件""" + # 创建计时器事件 + event = Event(type_=EVENT_TIMER) + + # 向队列中存入计时器事件 + self.put(event) + + #---------------------------------------------------------------------- + def start(self): + """引擎启动""" + # 将引擎设为启动 + self.__active = True + + # 启动事件处理线程 + self.__thread.start() + + # 启动计时器,计时器事件间隔默认设定为1秒 + self.__timer.start(1000) + + #---------------------------------------------------------------------- + def stop(self): + """停止引擎""" + # 将引擎设为停止 + self.__active = False + + # 停止计时器 + self.__timer.stop() + + # 等待事件处理线程退出 + self.__thread.join() + + #---------------------------------------------------------------------- + def register(self, type_, handler): + """注册事件处理函数监听""" + # 尝试获取该事件类型对应的处理函数列表,若无则创建 + try: + handlerList = self.__handlers[type_] + except KeyError: + handlerList = [] + self.__handlers[type_] = handlerList + + # 若要注册的处理器不在该事件的处理器列表中,则注册该事件 + if handler not in handlerList: + handlerList.append(handler) + + #---------------------------------------------------------------------- + def unregister(self, type_, handler): + """注销事件处理函数监听""" + # 尝试获取该事件类型对应的处理函数列表,若无则忽略该次注销请求 + try: + handlerList = self.handlers[type_] + + # 如果该函数存在于列表中,则移除 + if handler in handlerList: + handlerList.remove(handler) + + # 如果函数列表为空,则从引擎中移除该事件类型 + if not handlerList: + del self.handlers[type_] + except KeyError: + pass + + #---------------------------------------------------------------------- + def put(self, event): + """向事件队列中存入事件""" + self.__queue.put(event) + + +######################################################################## +class Event: + """事件对象""" + + #---------------------------------------------------------------------- + def __init__(self, type_=None): + """Constructor""" + self.type_ = type_ # 事件类型 + self.dict_ = {} # 字典用于保存具体的事件数据 + + +#---------------------------------------------------------------------- +def test(): + """测试函数""" + import sys + from datetime import datetime + from PyQt4.QtCore import QCoreApplication + + def simpletest(event): + print u'处理每秒触发的计时器事件:%s' % str(datetime.now()) + + app = QCoreApplication(sys.argv) + + ee = EventEngine() + ee.register(EVENT_TIMER, simpletest) + ee.start() + + app.exec_() + + +# 直接运行脚本可以进行测试 +if __name__ == '__main__': + test() \ No newline at end of file diff --git a/vn.trader/eventType.py b/vn.trader/eventType.py new file mode 100644 index 00000000..12f212b1 --- /dev/null +++ b/vn.trader/eventType.py @@ -0,0 +1,53 @@ +# encoding: UTF-8 + +''' +本文件仅用于存放对于事件类型常量的定义。 + +由于python中不存在真正的常量概念,因此选择使用全大写的变量名来代替常量。 +这里设计的命名规则以EVENT_前缀开头。 + +常量的内容通常选择一个能够代表真实意义的字符串(便于理解)。 + +建议将所有的常量定义放在该文件中,便于检查是否存在重复的现象。 +''' + +# 系统相关 +EVENT_TIMER = 'eTimer' # 计时器事件,每隔1秒发送一次 +EVENT_LOG = 'eLog' # 日志事件,全局通用 + +# Gateway相关 +EVENT_TICK = 'eTick.' # TICK行情事件,可后接具体的vtSymbol +EVENT_TRADE = 'eTrade.' # 成交回报事件 +EVENT_ORDER = 'eOrder.' # 报单回报事件 +EVENT_POSITION = 'ePosition.' # 持仓回报事件 +EVENT_ACCOUNT = 'eAccount.' # 账户回报事件 +EVENT_ERROR = 'eError.' # 错误回报事件 + + +#---------------------------------------------------------------------- +def test(): + """检查是否存在内容重复的常量定义""" + check_dict = {} + + global_dict = globals() + + for key, value in global_dict.items(): + if '__' not in key: # 不检查python内置对象 + if value in check_dict: + check_dict[value].append(key) + else: + check_dict[value] = [key] + + for key, value in check_dict.items(): + if len(value)>1: + print u'存在重复的常量定义:' + str(key) + for name in value: + print name + print '' + + print u'测试完毕' + + +# 直接运行脚本可以进行测试 +if __name__ == '__main__': + test() \ No newline at end of file diff --git a/vn.trader/gateway.py b/vn.trader/gateway.py new file mode 100644 index 00000000..d2d1019f --- /dev/null +++ b/vn.trader/gateway.py @@ -0,0 +1,348 @@ +# encoding: UTF-8 + +from eventEngine import * + +# 默认空值 +EMPTY_STRING = '' +EMPTY_UNICODE = u'' +EMPTY_INT = 0 +EMPTY_FLOAT = 0.0 + +# 方向常量 +DIRECTION_NONE = 'none' +DIRECTION_LONG = 'long' +DIRECTION_SHORT = 'short' +DIRECTION_UNKNOWN = 'unknown' + +# 开平常量 +OFFSET_NONE = 'none' +OFFSET_OPEN = 'open' +OFFSET_CLOSE = 'close' +OFFSET_UNKNOWN = 'unknown' + + +######################################################################## +class VtGateway(object): + """交易接口""" + + #---------------------------------------------------------------------- + def __init__(self, eventEngine): + """Constructor""" + self.eventEngine = eventEngine + + #---------------------------------------------------------------------- + def onTick(self, tick): + """市场行情推送""" + # 通用事件 + event1 = Event(type_=EVENT_TICK) + event1.dict_['data'] = tick + self.eventEngine.put(event1) + + # 特定合约代码的事件 + event2 = Event(type_=EVENT_TICK+tick.vtSymbol) + event2.dict_['data'] = tick + self.eventEngine.put(event2) + + #---------------------------------------------------------------------- + def onTrade(self, trade): + """成交信息推送""" + # 因为成交通常都是事后才会知道成交编号,因此只需要推送通用事件 + event1 = Event(type_=EVENT_TRADE) + event1.dict_['data'] = trade + self.eventEngine.put(event1) + + #---------------------------------------------------------------------- + def onOrder(self, order): + """订单变化推送""" + # 通用事件 + event1 = Event(type_=EVENT_ORDER) + event1.dict_['data'] = order + self.eventEngine.put(event1) + + # 特定订单编号的事件 + event2 = Event(type_=EVENT_ORDER+order.vtOrderID) + event2.dict_['data'] = order + self.eventEngine.put(event2) + + #---------------------------------------------------------------------- + def onPosition(self, position): + """持仓信息推送""" + # 通用事件 + event1 = Event(type_=EVENT_POSITION) + event1.dict_['data'] = position + self.eventEngine.put(event1) + + # 特定合约代码的事件 + event2 = Event(type_=EVENT_POSITION+position.vtPositionName) + event2.dict_['data'] = position + self.eventEngine.put(event2) + + #---------------------------------------------------------------------- + def onAccount(self, account): + """账户信息推送""" + # 通用事件 + event1 = Event(type_=EVENT_ACCOUNT) + event1.dict_['data'] = account + self.eventEngine.put(event1) + + # 特定合约代码的事件 + event2 = Event(type_=EVENT_ACCOUNT+account.vtAccountID) + event2.dict_['data'] = account + self.eventEngine.put(event2) + + #---------------------------------------------------------------------- + def onError(self, error): + """错误信息推送""" + # 通用事件 + event1 = Event(type_=EVENT_ERROR) + event1.dict_['data'] = error + self.eventEngine.put(event1) + + #---------------------------------------------------------------------- + def onLog(self, log): + """日志推送""" + # 通用事件 + event1 = Event(type_=EVENT_LOG) + event1.dict_['data'] = log + self.eventEngine.put(event1) + + #---------------------------------------------------------------------- + def connect(self): + """连接""" + pass + + #---------------------------------------------------------------------- + def subscribe(self): + """订阅行情""" + pass + + #---------------------------------------------------------------------- + def sendOrder(self): + """发单""" + pass + + #---------------------------------------------------------------------- + def cancelOrder(self): + """撤单""" + pass + + #---------------------------------------------------------------------- + def close(self): + """关闭""" + pass + + +######################################################################## +class VtBaseData(object): + """回调函数推送数据的基础类,其他数据类继承于此""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.gatewayName = EMPTY_STRING # Gateway名称 + self.rawData = None # 原始数据 + + +######################################################################## +class VtTickData(VtBaseData): + """Tick行情数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtTickData, self).__init__() + + # 代码相关 + self.symbol = EMPTY_STRING # 合约代码 + self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码 + + # 成交数据 + self.lastPrice = EMPTY_FLOAT # 最新成交价 + self.volume = EMPTY_INT # 最新成交量 + self.openInterest = EMPTY_INT # 持仓量 + self.tickTime = EMPTY_STRING # 更新时间 + + # 五档行情 + 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 VtTradeData(VtBaseData): + """成交数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtTradeData, self).__init__() + + # 代码编号相关 + self.symbol = EMPTY_STRING # 合约代码 + self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码 + + self.tradeID = EMPTY_STRING # 成交编号 + self.vtTradeID = EMPTY_STRING # 成交在vt系统中的唯一编号,通常是 Gateway名.成交编号 + + self.orderID = EMPTY_STRING # 订单编号 + self.vtOrderID = EMPTY_STRING # 订单在vt系统中的唯一编号,通常是 Gateway名.订单编号 + + # 成交相关 + self.direction = EMPTY_STRING # 成交方向 + self.offset = EMPTY_STRING # 成交开平仓 + self.price = EMPTY_FLOAT # 成交价格 + self.volume = EMPTY_INT # 成交数量 + self.tradeTime = EMPTY_STRING # 成交时间 + + +######################################################################## +class VtOrderData(VtBaseData): + """订单数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtOrderData, self).__init__() + + # 代码编号相关 + self.symbol = EMPTY_STRING # 合约代码 + self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码 + + self.orderID = EMPTY_STRING # 订单编号 + self.vtOrderID = EMPTY_STRING # 订单在vt系统中的唯一编号,通常是 Gateway名.订单编号 + + # 报单相关 + self.direction = EMPTY_STRING # 报单方向 + self.offset = EMPTY_STRING # 报单开平仓 + self.price = EMPTY_FLOAT # 报单价格 + self.totalVolume = EMPTY_INT # 报单总数量 + self.tradedVolume = EMPTY_INT # 报单成交数量 + self.status = EMPTY_STRING # 报单状态 + + self.orderTime = EMPTY_STRING # 发单时间 + self.cancelTime = EMPTY_STRING # 撤单时间 + + # CTP/LTS相关 + self.frontID = EMPTY_INT # 前置机编号 + self.sessionID = EMPTY_INT # 连接编号 + + +######################################################################## +class VtPositionData(VtBaseData): + """持仓数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtPositionData, self).__init__() + + # 代码编号相关 + self.symbol = EMPTY_STRING # 合约代码 + self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 Gateway名.合约代码 + + # 持仓相关 + self.direction = EMPTY_STRING # 持仓方向 + self.position = EMPTY_INT # 持仓量 + self.frozen = EMPTY_INT # 冻结数量 + self.price = EMPTY_FLOAT # 持仓均价 + self.vtPositionName = EMPTY_STRING # 持仓在vt系统中的唯一代码,通常是vtSymbol.方向 + + +######################################################################## +class VtAccountData(VtBaseData): + """账户数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtAccountData, self).__init__() + + # 账号代码相关 + self.accountID = EMPTY_STRING # 账户代码 + self.vtAccountID = EMPTY_STRING # 账户在vt中的唯一代码,通常是 Gateway名.账户代码 + + # 代码相关 + self.preBalance = EMPTY_FLOAT # 昨日账户结算净值 + self.balance = EMPTY_FLOAT # 账户净值 + self.available = EMPTY_FLOAT # 可用资金 + self.commission = EMPTY_FLOAT # 今日手续费 + self.margin = EMPTY_FLOAT # 保证金占用 + self.closeProfit = EMPTY_FLOAT # 平仓盈亏 + self.positionProfit = EMPTY_FLOAT # 持仓盈亏 + + +######################################################################## +class VtErrorData(VtBaseData): + """错误数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtErrorData, self).__init__() + + self.errorID = EMPTY_STRING # 错误代码 + self.errorMsg = EMPTY_UNICODE # 错误信息 + + +######################################################################## +class VtLogData(VtBaseData): + """日志数据类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtLogData, self).__init__() + + self.logContent = EMPTY_UNICODE # 日志信息 + + +######################################################################## +class VtContractData(VtBaseData): + """合约详细信息类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + super(VtBaseData, self).__init__() + + self.priceTick = EMPTY_FLOAT + + +######################################################################## +class VtSubscribeReq: + """订阅行情时传入的对象类""" + + #---------------------------------------------------------------------- + def __init__(self): + """Constructor""" + self.symbol = EMPTY_STRING + self.exchange = EMPTY_STRING + + + + + + + + + diff --git a/vn.trader/test.json b/vn.trader/test.json new file mode 100644 index 00000000..b803214a --- /dev/null +++ b/vn.trader/test.json @@ -0,0 +1,5 @@ +{ + "1": 1, + "b": 2, + "c": "_____" +} \ No newline at end of file diff --git a/vn.trader/thostmduserapi.dll b/vn.trader/thostmduserapi.dll new file mode 100644 index 00000000..c7402a35 Binary files /dev/null and b/vn.trader/thostmduserapi.dll differ diff --git a/vn.trader/thosttraderapi.dll b/vn.trader/thosttraderapi.dll new file mode 100644 index 00000000..d0c6dea9 Binary files /dev/null and b/vn.trader/thosttraderapi.dll differ diff --git a/vn.trader/vnctpmd.pyd b/vn.trader/vnctpmd.pyd new file mode 100644 index 00000000..4d4ec0ef Binary files /dev/null and b/vn.trader/vnctpmd.pyd differ diff --git a/vn.trader/vnctptd.pyd b/vn.trader/vnctptd.pyd new file mode 100644 index 00000000..4701f30e Binary files /dev/null and b/vn.trader/vnctptd.pyd differ