[Add]新增FutuTrader,修改futuGateway兼容Python3新版
This commit is contained in:
parent
9f5b786d1e
commit
1c5abeb986
19
examples/FutuTrader/CTA_setting.json
Normal file
19
examples/FutuTrader/CTA_setting.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "double ema",
|
||||||
|
"className": "DoubleMaStrategy",
|
||||||
|
"vtSymbol": "rb1805"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "atr rsi",
|
||||||
|
"className": "AtrRsiStrategy",
|
||||||
|
"vtSymbol": "IC1802"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "king keltner",
|
||||||
|
"className": "KkStrategy",
|
||||||
|
"vtSymbol": "IH1802"
|
||||||
|
}
|
||||||
|
]
|
7
examples/FutuTrader/Futu_connect.json
Normal file
7
examples/FutuTrader/Futu_connect.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 11111,
|
||||||
|
"market": "US",
|
||||||
|
"password": "123123",
|
||||||
|
"env": "REAL"
|
||||||
|
}
|
10
examples/FutuTrader/RM_setting.json
Normal file
10
examples/FutuTrader/RM_setting.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"active": false,
|
||||||
|
"orderFlowLimit": 50,
|
||||||
|
"orderFlowClear": 1,
|
||||||
|
"orderSizeLimit": 100,
|
||||||
|
"tradeLimit": 1000,
|
||||||
|
"workingOrderLimit": 20,
|
||||||
|
"orderCancelLimit": 10,
|
||||||
|
"marginRatioLimit": 0.85
|
||||||
|
}
|
20
examples/FutuTrader/VT_setting.json
Normal file
20
examples/FutuTrader/VT_setting.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"fontFamily": "微软雅黑",
|
||||||
|
"fontSize": 12,
|
||||||
|
|
||||||
|
"mongoHost": "localhost",
|
||||||
|
"mongoPort": 27017,
|
||||||
|
"mongoLogging": true,
|
||||||
|
|
||||||
|
"darkStyle": true,
|
||||||
|
"language": "chinese",
|
||||||
|
|
||||||
|
"logActive": true,
|
||||||
|
"logLevel": "debug",
|
||||||
|
"logConsole": true,
|
||||||
|
"logFile": true,
|
||||||
|
|
||||||
|
"tdPenalty": ["IF", "IH", "IC"],
|
||||||
|
|
||||||
|
"maxDecimal": 4
|
||||||
|
}
|
46
examples/FutuTrader/run.py
Normal file
46
examples/FutuTrader/run.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# vn.trader模块
|
||||||
|
from vnpy.event import EventEngine
|
||||||
|
from vnpy.trader.vtEngine import MainEngine
|
||||||
|
from vnpy.trader.uiQt import createQApp
|
||||||
|
from vnpy.trader.uiMainWindow import MainWindow
|
||||||
|
|
||||||
|
# 加载底层接口
|
||||||
|
from vnpy.trader.gateway import futuGateway
|
||||||
|
|
||||||
|
# 加载上层应用
|
||||||
|
from vnpy.trader.app import riskManager#, ctaStrategy
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
"""主程序入口"""
|
||||||
|
# 创建Qt应用对象
|
||||||
|
qApp = createQApp()
|
||||||
|
|
||||||
|
# 创建事件引擎
|
||||||
|
ee = EventEngine()
|
||||||
|
|
||||||
|
# 创建主引擎
|
||||||
|
me = MainEngine(ee)
|
||||||
|
|
||||||
|
# 添加交易接口
|
||||||
|
me.addGateway(futuGateway)
|
||||||
|
|
||||||
|
# 添加上层应用
|
||||||
|
me.addApp(riskManager)
|
||||||
|
#me.addApp(ctaStrategy)
|
||||||
|
|
||||||
|
# 创建主窗口
|
||||||
|
mw = MainWindow(me, ee)
|
||||||
|
mw.showMaximized()
|
||||||
|
|
||||||
|
# 在主线程中启动Qt事件循环
|
||||||
|
sys.exit(qApp.exec_())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
BIN
examples/FutuTrader/temp/ContractData.vt.dat
Normal file
BIN
examples/FutuTrader/temp/ContractData.vt.dat
Normal file
Binary file not shown.
1
examples/FutuTrader/temp/ContractData.vt.dir
Normal file
1
examples/FutuTrader/temp/ContractData.vt.dir
Normal file
@ -0,0 +1 @@
|
|||||||
|
'data', (0, 1462865)
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"username": "请在quantos.org申请",
|
|
||||||
"token": "请在quantos.org申请",
|
|
||||||
"strategy": 625,
|
|
||||||
"tradeAddress": "tcp://gw.quantos.org:8901",
|
|
||||||
"dataAddress": "tcp://data.tushare.org:8910"
|
|
||||||
}
|
|
@ -11,8 +11,9 @@ from time import sleep
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
import futuquant as ft
|
from futuquant import (OpenQuoteContext, OpenHKTradeContext, OpenUSTradeContext,
|
||||||
from futuquant import (RET_ERROR, RET_OK, TrdEnv,
|
RET_ERROR, RET_OK,
|
||||||
|
TrdEnv, TrdSide, OrderType, OrderStatus, ModifyOrderOp,
|
||||||
StockQuoteHandlerBase, OrderBookHandlerBase,
|
StockQuoteHandlerBase, OrderBookHandlerBase,
|
||||||
TradeOrderHandlerBase, TradeDealHandlerBase)
|
TradeOrderHandlerBase, TradeDealHandlerBase)
|
||||||
|
|
||||||
@ -33,23 +34,20 @@ productMap[PRODUCT_WARRANT] = 'WARRANT'
|
|||||||
productMap[PRODUCT_BOND] = 'BOND'
|
productMap[PRODUCT_BOND] = 'BOND'
|
||||||
|
|
||||||
directionMap = {}
|
directionMap = {}
|
||||||
directionMap[DIRECTION_LONG] = '0'
|
directionMap[DIRECTION_LONG] = TrdSide.BUY
|
||||||
directionMap[DIRECTION_SHORT] = '1'
|
directionMap[DIRECTION_SHORT] = TrdSide.SELL
|
||||||
directionMapReverse = {v:k for k,v in directionMap.items()}
|
directionMapReverse = {v:k for k,v in directionMap.items()}
|
||||||
|
|
||||||
statusMapReverse = {}
|
statusMapReverse = {}
|
||||||
statusMapReverse['0'] = STATUS_UNKNOWN
|
statusMapReverse[OrderStatus.NONE] = STATUS_UNKNOWN
|
||||||
statusMapReverse['1'] = STATUS_NOTTRADED
|
statusMapReverse[OrderStatus.SUBMITTED] = STATUS_NOTTRADED
|
||||||
statusMapReverse['2'] = STATUS_PARTTRADED
|
statusMapReverse[OrderStatus.FILLED_PART] = STATUS_PARTTRADED
|
||||||
statusMapReverse['3'] = STATUS_ALLTRADED
|
statusMapReverse[OrderStatus.FILLED_ALL] = STATUS_ALLTRADED
|
||||||
statusMapReverse['4'] = STATUS_CANCELLED
|
statusMapReverse[OrderStatus.CANCELLED_ALL] = STATUS_CANCELLED
|
||||||
statusMapReverse['5'] = STATUS_REJECTED
|
statusMapReverse[OrderStatus.CANCELLED_PART] = STATUS_CANCELLED
|
||||||
statusMapReverse['6'] = STATUS_CANCELLED
|
statusMapReverse[OrderStatus.SUBMIT_FAILED] = STATUS_REJECTED
|
||||||
statusMapReverse['7'] = STATUS_CANCELLED
|
statusMapReverse[OrderStatus.FAILED] = STATUS_REJECTED
|
||||||
statusMapReverse['8'] = STATUS_UNKNOWN
|
statusMapReverse[OrderStatus.DISABLED] = STATUS_CANCELLED
|
||||||
statusMapReverse['21'] = STATUS_UNKNOWN
|
|
||||||
statusMapReverse['22'] = STATUS_UNKNOWN
|
|
||||||
statusMapReverse['23'] = STATUS_UNKNOWN
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -137,7 +135,7 @@ class FutuGateway(VtGateway):
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def connectQuote(self):
|
def connectQuote(self):
|
||||||
"""连接行情功能"""
|
"""连接行情功能"""
|
||||||
self.quoteCtx = ft.OpenQuoteContext(self.host, self.port)
|
self.quoteCtx = OpenQuoteContext(self.host, self.port)
|
||||||
|
|
||||||
# 继承实现处理器类
|
# 继承实现处理器类
|
||||||
class QuoteHandler(StockQuoteHandlerBase):
|
class QuoteHandler(StockQuoteHandlerBase):
|
||||||
@ -176,9 +174,9 @@ class FutuGateway(VtGateway):
|
|||||||
"""连接交易功能"""
|
"""连接交易功能"""
|
||||||
# 连接交易接口
|
# 连接交易接口
|
||||||
if self.market == 'US':
|
if self.market == 'US':
|
||||||
self.tradeCtx = ft.OpenUSTradeContext(self.host, self.port)
|
self.tradeCtx = OpenUSTradeContext(self.host, self.port)
|
||||||
else:
|
else:
|
||||||
self.tradeCtx = ft.OpenHKTradeContext(self.host, self.port)
|
self.tradeCtx = OpenHKTradeContext(self.host, self.port)
|
||||||
|
|
||||||
# 继承实现处理器类
|
# 继承实现处理器类
|
||||||
class OrderHandler(TradeOrderHandlerBase):
|
class OrderHandler(TradeOrderHandlerBase):
|
||||||
@ -204,7 +202,7 @@ class FutuGateway(VtGateway):
|
|||||||
return RET_OK, content
|
return RET_OK, content
|
||||||
|
|
||||||
# 只有港股实盘交易才需要解锁
|
# 只有港股实盘交易才需要解锁
|
||||||
if self.market == 'HK' and self.env == 0:
|
if self.market == 'HK' and self.env == TrdEnv.REAL:
|
||||||
self.tradeCtx.unlock_trade(self.password)
|
self.tradeCtx.unlock_trade(self.password)
|
||||||
|
|
||||||
# 设置回调处理对象
|
# 设置回调处理对象
|
||||||
@ -229,26 +227,25 @@ class FutuGateway(VtGateway):
|
|||||||
def sendOrder(self, orderReq):
|
def sendOrder(self, orderReq):
|
||||||
"""发单"""
|
"""发单"""
|
||||||
side = directionMap[orderReq.direction]
|
side = directionMap[orderReq.direction]
|
||||||
priceType = 0 # 只支持限价单
|
priceType = OrderType.NORMAL # 只支持限价单
|
||||||
|
|
||||||
# 设置价格调整模式为向内调整(即买入调整后价格比原始价格低)
|
# 设置价格调整模式为向内调整(即买入调整后价格比原始价格低)
|
||||||
if orderReq.direction == DIRECTION_LONG:
|
if orderReq.direction == DIRECTION_LONG:
|
||||||
priceMode = PriceRegularMode.LOWER
|
adjustLimit = 0.05
|
||||||
else:
|
else:
|
||||||
priceMode = PriceRegularMode.UPPER
|
adjustLimit = -0.05
|
||||||
|
|
||||||
code, data = self.tradeCtx.place_order(orderReq.price, orderReq.volume,
|
code, data = self.tradeCtx.place_order(orderReq.price, orderReq.volume,
|
||||||
orderReq.symbol, side,
|
orderReq.symbol, side, priceType,
|
||||||
priceType, self.env,
|
trd_env=self.env,
|
||||||
order_deal_push=True,
|
adjust_limit=adjustLimit)
|
||||||
price_mode=priceMode)
|
|
||||||
|
|
||||||
if code:
|
if code:
|
||||||
self.writeError(code, u'委托失败:%s' %data)
|
self.writeError(code, u'委托失败:%s' %data)
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
for ix, row in data.iterrows():
|
for ix, row in data.iterrows():
|
||||||
orderID = str(row['orderid'])
|
orderID = str(row['order_id'])
|
||||||
|
|
||||||
vtOrderID = '.'.join([self.gatewayName, orderID])
|
vtOrderID = '.'.join([self.gatewayName, orderID])
|
||||||
|
|
||||||
@ -257,8 +254,8 @@ class FutuGateway(VtGateway):
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def cancelOrder(self, cancelOrderReq):
|
def cancelOrder(self, cancelOrderReq):
|
||||||
"""撤单"""
|
"""撤单"""
|
||||||
code, data = self.tradeCtx.set_order_status(0, int(cancelOrderReq.orderID),
|
code, data = self.tradeCtx.modify_order(ModifyOrderOp.CANCEL, cancelOrderReq.orderID,
|
||||||
self.env)
|
0, 0, trd_env=self.env)
|
||||||
|
|
||||||
if code:
|
if code:
|
||||||
self.writeError(code, u'撤单失败:%s' %data)
|
self.writeError(code, u'撤单失败:%s' %data)
|
||||||
@ -304,9 +301,8 @@ class FutuGateway(VtGateway):
|
|||||||
|
|
||||||
account.accountID = '%s_%s' %(self.gatewayName, self.market)
|
account.accountID = '%s_%s' %(self.gatewayName, self.market)
|
||||||
account.vtAccountID = '.'.join([self.gatewayName, account.accountID])
|
account.vtAccountID = '.'.join([self.gatewayName, account.accountID])
|
||||||
account.balance = float(row['ZCJZ'])
|
account.balance = float(row['total_assets'])
|
||||||
account.margin = float(row['GPBZJ'])
|
account.available = float(row['avl_withdrawal_cash'])
|
||||||
account.available = float(row['XJJY'])
|
|
||||||
|
|
||||||
self.onAccount(account)
|
self.onAccount(account)
|
||||||
|
|
||||||
@ -448,7 +444,7 @@ class FutuGateway(VtGateway):
|
|||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def processOrderBook(self, data):
|
def processOrderBook(self, data):
|
||||||
"""订单簿推送"""
|
"""订单簿推送"""
|
||||||
symbol = data['stock_code']
|
symbol = data['code']
|
||||||
|
|
||||||
tick = self.tickDict.get(symbol, None)
|
tick = self.tickDict.get(symbol, None)
|
||||||
if not tick:
|
if not tick:
|
||||||
@ -487,25 +483,24 @@ class FutuGateway(VtGateway):
|
|||||||
order.symbol = row['code']
|
order.symbol = row['code']
|
||||||
order.vtSymbol = order.symbol
|
order.vtSymbol = order.symbol
|
||||||
|
|
||||||
order.orderID = str(row['orderid'])
|
order.orderID = str(row['order_id'])
|
||||||
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
|
||||||
|
|
||||||
order.price = float(row['price'])
|
order.price = float(row['price'])
|
||||||
order.totalVolume = int(row['qty'])
|
order.totalVolume = int(row['qty'])
|
||||||
order.tradedVolume = int(row['dealt_qty'])
|
order.tradedVolume = int(row['dealt_qty'])
|
||||||
|
order.orderTime = row['create_time'].split(' ')[-1]
|
||||||
t = datetime.fromtimestamp(float(row['submited_time']))
|
|
||||||
order.orderTime = t.strftime('%H:%M:%S')
|
|
||||||
|
|
||||||
order.status = statusMapReverse.get(str(row['status']), STATUS_UNKNOWN)
|
order.status = statusMapReverse.get(row['order_status'], STATUS_UNKNOWN)
|
||||||
order.direction = directionMapReverse[str(row['order_side'])]
|
order.direction = directionMapReverse[row['trd_side']]
|
||||||
|
|
||||||
self.onOrder(order)
|
self.onOrder(order)
|
||||||
|
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
def processDeal(self, data):
|
def processDeal(self, data):
|
||||||
"""处理成交推送"""
|
"""处理成交推送"""
|
||||||
for ix, row in data.iterrows():
|
for ix, row in data.iterrows():
|
||||||
tradeID = row['dealid']
|
tradeID = row['deal_id']
|
||||||
if tradeID in self.tradeSet:
|
if tradeID in self.tradeSet:
|
||||||
continue
|
continue
|
||||||
self.tradeSet.add(tradeID)
|
self.tradeSet.add(tradeID)
|
||||||
@ -519,14 +514,13 @@ class FutuGateway(VtGateway):
|
|||||||
trade.tradeID = tradeID
|
trade.tradeID = tradeID
|
||||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||||
|
|
||||||
trade.orderID = row['orderid']
|
trade.orderID = row['order_id']
|
||||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||||
|
|
||||||
trade.price = float(row['price'])
|
trade.price = float(row['price'])
|
||||||
trade.volume = float(row['qty'])
|
trade.volume = float(row['qty'])
|
||||||
trade.direction = directionMapReverse[str(row['order_side'])]
|
trade.direction = directionMapReverse[row['trd_side']]
|
||||||
|
|
||||||
t = datetime.fromtimestamp(float(row['time']))
|
trade.tradeTime = row['create_time'].split(' ')[-1]
|
||||||
trade.tradeTime = t.strftime('%H:%M:%S')
|
|
||||||
|
|
||||||
self.onTrade(trade)
|
self.onTrade(trade)
|
Loading…
Reference in New Issue
Block a user