数字货币跨市场套利(搬砖)引擎
This commit is contained in:
parent
e5be9ba35f
commit
a36c3d338e
20
vnpy/trader/app/cmaStrategy/CMA_setting.json
Normal file
20
vnpy/trader/app/cmaStrategy/CMA_setting.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "S45_BINANCE_FCOIN",
|
||||||
|
"className": "Strategy45",
|
||||||
|
"vtSymbol": "btc_usdt",
|
||||||
|
"master_exchange":"BINANCE",
|
||||||
|
"slave_exchange":"FCOIN",
|
||||||
|
"master_gateway":"BINANCE_2",
|
||||||
|
"slave_gateway":"FCOIN_3",
|
||||||
|
"baseUpLine":10,
|
||||||
|
"baseMidLine":0,
|
||||||
|
"baseDnLine":-10,
|
||||||
|
"minDiff":0.01,
|
||||||
|
"inputSS":0.01,
|
||||||
|
"min_trade_size":0.002,
|
||||||
|
"mode":"tick",
|
||||||
|
"auto_init": true,
|
||||||
|
"auto_start": true
|
||||||
|
}
|
||||||
|
]
|
10
vnpy/trader/app/cmaStrategy/__init__.py
Normal file
10
vnpy/trader/app/cmaStrategy/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
from vnpy.trader.app.cmaStrategy.cmaEngine import CmaEngine
|
||||||
|
from vnpy.trader.app.cmaStrategy.uiCmaWidget import CmaEngineManager
|
||||||
|
|
||||||
|
appName = 'CrossMarketArbitrage'
|
||||||
|
appDisplayName = u'跨市场套利'
|
||||||
|
appEngine = CmaEngine
|
||||||
|
appWidget = CmaEngineManager
|
||||||
|
appIco = 'cma.ico'
|
1996
vnpy/trader/app/cmaStrategy/cmaEngine.py
Normal file
1996
vnpy/trader/app/cmaStrategy/cmaEngine.py
Normal file
File diff suppressed because it is too large
Load Diff
309
vnpy/trader/app/cmaStrategy/cmaTemplate.py
Normal file
309
vnpy/trader/app/cmaStrategy/cmaTemplate.py
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''
|
||||||
|
本文件包含了CTA引擎中的策略开发用模板,开发策略时需要继承CtaTemplate类。
|
||||||
|
'''
|
||||||
|
|
||||||
|
from datetime import datetime,timedelta
|
||||||
|
import os,csv
|
||||||
|
from vnpy.trader.app.ctaStrategy.ctaBase import *
|
||||||
|
from vnpy.trader.vtConstant import *
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CmaTemplate(object):
|
||||||
|
"""跨市场套利策略模板"""
|
||||||
|
|
||||||
|
# 策略类的名称和作者
|
||||||
|
className = 'CmaTemplate'
|
||||||
|
author = u'李来佳'
|
||||||
|
|
||||||
|
# 策略的基本参数
|
||||||
|
name = 'StrategyName' # 策略实例名称
|
||||||
|
vtSymbol = EMPTY_STRING # 交易的合约vt系统代码
|
||||||
|
symbol = EMPTY_STRING # 交易的合约代码
|
||||||
|
|
||||||
|
base_symbol = EMPTY_STRING # 交易主货币
|
||||||
|
quote_symbol = EMPTY_STRING # 基准货币
|
||||||
|
master_symbol = EMPTY_STRING # 主交易所币对
|
||||||
|
slave_symbol = EMPTY_STRING # 从交易所币对
|
||||||
|
|
||||||
|
master_exchange = EMPTY_STRING # 主交易所
|
||||||
|
slave_exchange = EMPTY_STRING # 次交易所
|
||||||
|
|
||||||
|
master_gateway = EMPTY_STRING # 主交易所网关
|
||||||
|
slave_gateway = EMPTY_STRING # 次交易所网关
|
||||||
|
|
||||||
|
# 策略的基本变量,由引擎管理
|
||||||
|
inited = False # 是否进行了初始化
|
||||||
|
trading = False # 是否启动交易,由引擎管理
|
||||||
|
backtesting = False # 是否回测
|
||||||
|
|
||||||
|
# 参数列表,保存了参数的名称
|
||||||
|
paramList = ['name',
|
||||||
|
'className',
|
||||||
|
'author',
|
||||||
|
'vtSymbol',
|
||||||
|
'master_exchange',
|
||||||
|
'slave_exchange',
|
||||||
|
'master_gateway',
|
||||||
|
'slave_gateway'
|
||||||
|
]
|
||||||
|
|
||||||
|
# 变量列表,保存了变量的名称
|
||||||
|
varList = ['inited',
|
||||||
|
'trading',
|
||||||
|
'master_entrust',
|
||||||
|
'slave_entrust']
|
||||||
|
|
||||||
|
tradingOpen = True # 允许开仓
|
||||||
|
forceTradingClose = False # 强制平仓标志
|
||||||
|
delayMission = [] # 延迟的任务
|
||||||
|
position = None # 持仓
|
||||||
|
|
||||||
|
is_7x24 = True # 是否7x24运行
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def __init__(self, cmaEngine, setting):
|
||||||
|
"""Constructor"""
|
||||||
|
self.cmaEngine = cmaEngine
|
||||||
|
|
||||||
|
# 委托单状态
|
||||||
|
self.master_entrust = 0 # 0 表示没有委托,1 表示存在多仓的委托,-1 表示存在空仓的委托
|
||||||
|
self.slave_entrust = 0 # 0 表示没有委托,1 表示存在多仓的委托,-1 表示存在空仓的委托
|
||||||
|
|
||||||
|
# 保存委托单编号和相关委托单的字典
|
||||||
|
# key为委托单编号
|
||||||
|
# value为该合约相关的委托单
|
||||||
|
self.uncompletedOrders = {}
|
||||||
|
|
||||||
|
# 设置策略的参数
|
||||||
|
if setting:
|
||||||
|
self.writeCtaLog(u'基类设置参数')
|
||||||
|
d = self.__dict__
|
||||||
|
for key in self.paramList:
|
||||||
|
if key in setting:
|
||||||
|
d[key] = setting[key]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def onInit(self):
|
||||||
|
"""初始化策略(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def onStart(self):
|
||||||
|
"""启动策略(必须由用户继承实现)"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def onStop(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 cancelOrder(self, vtOrderID):
|
||||||
|
"""撤单"""
|
||||||
|
|
||||||
|
# 如果发单号为空字符串,则不进行后续操作
|
||||||
|
if not vtOrderID or vtOrderID == '':
|
||||||
|
return
|
||||||
|
|
||||||
|
if STOPORDERPREFIX in vtOrderID:
|
||||||
|
self.cmaEngine.cancelStopOrder(vtOrderID)
|
||||||
|
else:
|
||||||
|
self.cmaEngine.cancelOrder(vtOrderID)
|
||||||
|
|
||||||
|
def saveData(self):
|
||||||
|
"""保持bar数据"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onTimer(self):
|
||||||
|
"""定时执行任务
|
||||||
|
由mainEngine驱动"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def setParam(self, setting):
|
||||||
|
"""设置参数"""
|
||||||
|
self.writeCtaLog(u'使用参数:{}'.format(setting))
|
||||||
|
d = self.__dict__
|
||||||
|
for key in self.paramList:
|
||||||
|
if key in setting:
|
||||||
|
d[key] = setting[key]
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def writeCtaLog(self, content):
|
||||||
|
"""记录CTA日志"""
|
||||||
|
try:
|
||||||
|
self.cmaEngine.writeCtaLog(content, strategy_name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
content = self.name + ':' + content
|
||||||
|
self.cmaEngine.writeCtaLog(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def writeCtaError(self, content):
|
||||||
|
"""记录CTA出错日志"""
|
||||||
|
try:
|
||||||
|
self.cmaEngine.writeCtaError(content, strategy_name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
content = self.name + ':' + content
|
||||||
|
self.cmaEngine.writeCtaError(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def writeCtaWarning(self, content):
|
||||||
|
"""记录CTA告警日志"""
|
||||||
|
try:
|
||||||
|
self.cmaEngine.writeCtaWarning(content, strategy_name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
content = self.name + ':' + content
|
||||||
|
self.cmaEngine.writeCtaWarning(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def writeCtaNotification(self, content):
|
||||||
|
"""记录CTA通知日志"""
|
||||||
|
content = self.name + ':' + content
|
||||||
|
|
||||||
|
if not self.backtesting:
|
||||||
|
self.cmaEngine.writeCtaNotification(content)
|
||||||
|
else:
|
||||||
|
self.cmaEngine.writeCtaLog(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def writeCtaCritical(self, content):
|
||||||
|
"""记录CTA系统异常日志"""
|
||||||
|
|
||||||
|
if not self.backtesting:
|
||||||
|
try:
|
||||||
|
self.cmaEngine.writeCtaCritical(content,strategy_name=self.name)
|
||||||
|
except Exception as ex:
|
||||||
|
content = self.name + ':' + content
|
||||||
|
self.cmaEngine.writeCtaCritical(content)
|
||||||
|
else:
|
||||||
|
content = self.name + ':' + content
|
||||||
|
self.cmaEngine.writeCtaError(content)
|
||||||
|
|
||||||
|
def sendSignal(self,direction,price, level):
|
||||||
|
"""发送信号通知"""
|
||||||
|
try:
|
||||||
|
if not self.backtesting:
|
||||||
|
self.cmaEngine.sendCtaSignal(source=self.name, symbol=self.vtSymbol, direction=direction, price=price, level=level)
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
self.writeCtaError(u'sendSignal Exception:{0}'.format(str(ex)))
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def putEvent(self):
|
||||||
|
"""发出策略状态变化事件"""
|
||||||
|
self.cmaEngine.putStrategyEvent(self.name)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def getEngineType(self):
|
||||||
|
"""查询当前运行的环境"""
|
||||||
|
return self.cmaEngine.engineType
|
||||||
|
|
||||||
|
def append_data(self, file_name, dict_data, field_names=None):
|
||||||
|
"""
|
||||||
|
添加数据到csv文件中
|
||||||
|
:param file_name: csv的文件全路径
|
||||||
|
:param dict_data: OrderedDict
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not isinstance(dict_data, dict):
|
||||||
|
self.writeCtaError(u'append_data,输入数据不是dict')
|
||||||
|
return
|
||||||
|
|
||||||
|
dict_fieldnames = list(dict_data.keys()) if field_names is None else field_names
|
||||||
|
|
||||||
|
if not isinstance(dict_fieldnames, list):
|
||||||
|
self.writeCtaError(u'append_data,输入字段不是list')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
if not os.path.exists(file_name):
|
||||||
|
self.writeCtaLog(u'create csv file:{}'.format(file_name))
|
||||||
|
with open(file_name, 'a', encoding='utf8', newline='') as csvWriteFile:
|
||||||
|
writer = csv.DictWriter(f=csvWriteFile, fieldnames=dict_fieldnames, dialect='excel')
|
||||||
|
self.writeCtaLog(u'write csv header:{}'.format(dict_fieldnames))
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerow(dict_data)
|
||||||
|
else:
|
||||||
|
with open(file_name, 'a', encoding='utf8', newline='') as csvWriteFile:
|
||||||
|
writer = csv.DictWriter(f=csvWriteFile, fieldnames=dict_fieldnames, dialect='excel')
|
||||||
|
writer.writerow(dict_data)
|
||||||
|
except Exception as ex:
|
||||||
|
self.writeCtaError(u'append_data exception:{}'.format(str(ex)))
|
||||||
|
|
||||||
|
|
||||||
|
def checkExistDelayMission(self, func):
|
||||||
|
if len(self.delayMission) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for mission in self.delayMission:
|
||||||
|
if 'func' in mission and mission['func'] == func:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def cancelForceClose(self):
|
||||||
|
"""
|
||||||
|
取消强制平仓
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forceCloseAllPos(self):
|
||||||
|
"""
|
||||||
|
策略实现上层调度,强制平所有仓位,不再开仓
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def forceOpenPos(self, longPos, shortPos):
|
||||||
|
"""
|
||||||
|
策略实现上层调度,强制开仓
|
||||||
|
:param longPos: 对应开仓的多单[{"price": 2560, volume": 77, "symbol": "RB99", "margin": -953, "direction": "long" }]
|
||||||
|
:param shortPos: 对应开仓的空单[{"price": 2560, volume": 77, "symbol": "RB99", "margin": -953, "direction": "short" }]
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def cancelAllOrders(self):
|
||||||
|
"""
|
||||||
|
撤销所有委托
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getPositions(self):
|
||||||
|
"""
|
||||||
|
获取策略当前持仓
|
||||||
|
:return: [{'vtSymbol':symbol,'direction':direction,'volume':volume]
|
||||||
|
"""
|
||||||
|
if not self.position:
|
||||||
|
return []
|
||||||
|
l = []
|
||||||
|
if self.position.longPos > 0:
|
||||||
|
l.append({'vtSymbol': self.vtSymbol, 'direction': DIRECTION_LONG, 'volume': self.position.longPos})
|
||||||
|
|
||||||
|
if abs(self.position.shortPos) > 0:
|
||||||
|
l.append({'vtSymbol': self.vtSymbol, 'direction': DIRECTION_SHORT, 'volume': abs(self.position.shortPos)})
|
||||||
|
|
||||||
|
self.writeCtaLog(u'当前持仓:{}'.format(l))
|
||||||
|
return l
|
13
vnpy/trader/app/cmaStrategy/language/__init__.py
Normal file
13
vnpy/trader/app/cmaStrategy/language/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# 默认设置
|
||||||
|
from vnpy.trader.app.cmaStrategy.language.chinese import text
|
||||||
|
|
||||||
|
# 是否要使用英文
|
||||||
|
from vnpy.trader.vtGlobal import globalSetting
|
||||||
|
if globalSetting['language'] == 'english':
|
||||||
|
from vnpy.trader.app.cmaStrategy.language.english import text
|
19
vnpy/trader/app/cmaStrategy/language/chinese/text.py
Normal file
19
vnpy/trader/app/cmaStrategy/language/chinese/text.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
INIT = u'初始化'
|
||||||
|
START = u'启动'
|
||||||
|
STOP = u'停止'
|
||||||
|
FORCEINIT = u'强制初始化'
|
||||||
|
|
||||||
|
CMA_ENGINE_STARTED = u'跨市场套利引擎启动成功'
|
||||||
|
|
||||||
|
CMA_STRATEGY = u'CMA策略'
|
||||||
|
LOAD_STRATEGY = u'加载策略'
|
||||||
|
INIT_ALL = u'全部初始化'
|
||||||
|
START_ALL = u'全部启动'
|
||||||
|
STOP_ALL = u'全部停止'
|
||||||
|
SAVE_POSITION_DATA = u'保存持仓'
|
||||||
|
|
||||||
|
STRATEGY_LOADED = u'策略加载成功'
|
||||||
|
|
||||||
|
SAVE_POSITION_QUESTION = u'是否要保存策略持仓数据到数据库?'
|
20
vnpy/trader/app/cmaStrategy/language/english/text.py
Normal file
20
vnpy/trader/app/cmaStrategy/language/english/text.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
INIT = u'Init'
|
||||||
|
START = u'Start'
|
||||||
|
STOP = u'Stop'
|
||||||
|
FORCEINIT = u'ForceInit'
|
||||||
|
|
||||||
|
|
||||||
|
CMA_ENGINE_STARTED = u'Cross Market Abitrage engine started.'
|
||||||
|
|
||||||
|
CMA_STRATEGY = u'CMA Strategy'
|
||||||
|
LOAD_STRATEGY = u'Load Strategy'
|
||||||
|
INIT_ALL = u'Init All'
|
||||||
|
START_ALL = u'Start All'
|
||||||
|
STOP_ALL = u'Stop All'
|
||||||
|
SAVE_POSITION_DATA = u'Save Position Data'
|
||||||
|
|
||||||
|
STRATEGY_LOADED = u'Strategy loaded.'
|
||||||
|
|
||||||
|
SAVE_POSITION_QUESTION = u'Do you want to save strategy position data into database?'
|
51
vnpy/trader/app/cmaStrategy/strategy/__init__.py
Normal file
51
vnpy/trader/app/cmaStrategy/strategy/__init__.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''
|
||||||
|
动态载入所有的策略类,先从vnpy/trader/app/ctaStrategy/strategy下加载,其次,从工作目录下strategy加载。
|
||||||
|
如果重复,工作目录的strategy优先。
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import importlib
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
# 用来保存策略类的字典
|
||||||
|
ARBITRAGE_STRATEGY_CLASS = {}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def loadStrategyModule(moduleName):
|
||||||
|
"""使用importlib动态载入模块"""
|
||||||
|
try:
|
||||||
|
print('loading {0}'.format(moduleName))
|
||||||
|
module = importlib.import_module(moduleName)
|
||||||
|
|
||||||
|
# 遍历模块下的对象,只有名称中包含'Strategy'的才是策略类
|
||||||
|
for k in dir(module):
|
||||||
|
if 'Strategy' in k:
|
||||||
|
print('adding {} into STRATEGY_CLASS'.format(k))
|
||||||
|
v = module.__getattribute__(k)
|
||||||
|
if k in ARBITRAGE_STRATEGY_CLASS:
|
||||||
|
print('Replace strategy {} with {}'.format(k,moduleName))
|
||||||
|
ARBITRAGE_STRATEGY_CLASS[k] = v
|
||||||
|
except Exception as ex:
|
||||||
|
print('-' * 20)
|
||||||
|
print('Failed to import strategy file %s:' % moduleName)
|
||||||
|
print('Exception:{},{}'.format(str(ex),traceback.format_exc()))
|
||||||
|
|
||||||
|
# 获取目录路径
|
||||||
|
path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
print('init strategies from {}'.format(path))
|
||||||
|
|
||||||
|
# 遍历strategy目录下的文件
|
||||||
|
for root, subdirs, files in os.walk(path):
|
||||||
|
for name in files:
|
||||||
|
# 只有文件名中包含strategy且非.pyc的文件,才是策略文件
|
||||||
|
if 'strategy' in name and '.pyc' not in name:
|
||||||
|
# 模块名称需要上前缀
|
||||||
|
moduleName = 'vnpy.trader.app.cmaStrategy.strategy.' + name.replace('.py', '')
|
||||||
|
loadStrategyModule(moduleName)
|
||||||
|
|
||||||
|
|
||||||
|
print('finished load arbitrage strategy modules')
|
305
vnpy/trader/app/cmaStrategy/uiCmaWidget.py
Normal file
305
vnpy/trader/app/cmaStrategy/uiCmaWidget.py
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
'''
|
||||||
|
CMA模块相关的GUI控制组件
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
from time import sleep
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from vnpy.trader.app.cmaStrategy.language import text
|
||||||
|
from vnpy.trader.uiBasicWidget import QtWidgets, QtGui, QtCore, BasicCell
|
||||||
|
from vnpy.trader.vtEvent import *
|
||||||
|
from vnpy.trader.app.cmaStrategy.strategy import *
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CmaValueMonitor(QtWidgets.QTableWidget):
|
||||||
|
"""参数监控"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Constructor"""
|
||||||
|
super(CmaValueMonitor, self).__init__(parent)
|
||||||
|
|
||||||
|
self.keyCellDict = {}
|
||||||
|
self.data = None
|
||||||
|
self.inited = False
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initUi(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
self.setRowCount(1)
|
||||||
|
self.verticalHeader().setVisible(False)
|
||||||
|
self.setEditTriggers(self.NoEditTriggers)
|
||||||
|
|
||||||
|
self.setMaximumHeight(self.sizeHint().height())
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updateData(self, data):
|
||||||
|
"""更新数据"""
|
||||||
|
if not self.inited:
|
||||||
|
# 设置标题
|
||||||
|
|
||||||
|
self.setColumnCount(len(data))
|
||||||
|
self.setHorizontalHeaderLabels(data.keys())
|
||||||
|
|
||||||
|
# 新增数据
|
||||||
|
col = 0
|
||||||
|
for k, v in data.items():
|
||||||
|
cell = QtWidgets.QTableWidgetItem(v)
|
||||||
|
self.keyCellDict[k] = cell
|
||||||
|
self.setItem(0, col, cell)
|
||||||
|
col += 1
|
||||||
|
|
||||||
|
self.inited = True
|
||||||
|
else:
|
||||||
|
# 更新数据
|
||||||
|
for k, v in data.items():
|
||||||
|
cell = self.keyCellDict[k]
|
||||||
|
cell.setText(str(v))
|
||||||
|
|
||||||
|
#cell.setBackgroundColor()
|
||||||
|
|
||||||
|
# 调整表格宽度为自适应
|
||||||
|
self.resizeColumnsToContents()
|
||||||
|
self.resizeRowsToContents()
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CmaStrategyManager(QtWidgets.QGroupBox):
|
||||||
|
"""策略管理组件"""
|
||||||
|
signal = QtCore.Signal(type(Event()))
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, cmaEngine, eventEngine, name, parent=None):
|
||||||
|
"""Constructor"""
|
||||||
|
super(CmaStrategyManager, self).__init__(parent)
|
||||||
|
|
||||||
|
self.cmaEngine = cmaEngine
|
||||||
|
self.eventEngine = eventEngine
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
self.updateMonitor()
|
||||||
|
self.registerEvent()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initUi(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
self.setTitle(self.name)
|
||||||
|
|
||||||
|
self.paramMonitor = CmaValueMonitor(self) # 参数监控
|
||||||
|
self.varMonitor = CmaValueMonitor(self) # 变量监控
|
||||||
|
|
||||||
|
height = 80
|
||||||
|
self.paramMonitor.setFixedHeight(height)
|
||||||
|
self.varMonitor.setFixedHeight(height)
|
||||||
|
|
||||||
|
buttonInit = QtWidgets.QPushButton(text.INIT)
|
||||||
|
buttonStart = QtWidgets.QPushButton(text.START)
|
||||||
|
buttonStop = QtWidgets.QPushButton(text.STOP)
|
||||||
|
buttonInitForce = QtWidgets.QPushButton(text.FORCEINIT)
|
||||||
|
buttonInit.clicked.connect(self.init)
|
||||||
|
buttonStart.clicked.connect(self.start)
|
||||||
|
buttonStop.clicked.connect(self.stop)
|
||||||
|
buttonInitForce.clicked.connect(self.initForce)
|
||||||
|
|
||||||
|
hbox1 = QtWidgets.QHBoxLayout()
|
||||||
|
hbox1.addWidget(buttonInit)
|
||||||
|
hbox1.addWidget(buttonStart)
|
||||||
|
hbox1.addWidget(buttonStop)
|
||||||
|
hbox1.addWidget(buttonInitForce)
|
||||||
|
|
||||||
|
hbox1.addStretch()
|
||||||
|
|
||||||
|
hbox2 = QtWidgets.QHBoxLayout()
|
||||||
|
hbox2.addWidget(self.paramMonitor)
|
||||||
|
|
||||||
|
hbox3 = QtWidgets.QHBoxLayout()
|
||||||
|
hbox3.addWidget(self.varMonitor)
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addLayout(hbox1)
|
||||||
|
vbox.addLayout(hbox2)
|
||||||
|
vbox.addLayout(hbox3)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updateMonitor(self, event=None):
|
||||||
|
"""显示策略最新状态"""
|
||||||
|
|
||||||
|
# 获取策略的参数目录
|
||||||
|
paramDict = self.cmaEngine.getStrategyParam(self.name)
|
||||||
|
if paramDict:
|
||||||
|
self.paramMonitor.updateData(paramDict)
|
||||||
|
|
||||||
|
# 获取策略的变量目录
|
||||||
|
varDict = self.cmaEngine.getStrategyVar(self.name)
|
||||||
|
if varDict:
|
||||||
|
self.varMonitor.updateData(varDict)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def registerEvent(self):
|
||||||
|
"""注册事件监听"""
|
||||||
|
|
||||||
|
# 绑定事件的更新函数为updateMonitor
|
||||||
|
self.signal.connect(self.updateMonitor)
|
||||||
|
|
||||||
|
# 注册事件
|
||||||
|
self.eventEngine.register(EVENT_CTA_STRATEGY+self.name, self.signal.emit)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def init(self):
|
||||||
|
"""初始化策略"""
|
||||||
|
self.cmaEngine.initStrategy(self.name)
|
||||||
|
|
||||||
|
def initForce(self):
|
||||||
|
"""强制初始化策略"""
|
||||||
|
self.cmaEngine.initStrategy(self.name, force = True)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def start(self):
|
||||||
|
"""启动策略"""
|
||||||
|
self.cmaEngine.startStrategy(self.name)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def stop(self):
|
||||||
|
"""停止策略"""
|
||||||
|
self.cmaEngine.stopStrategy(self.name)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class CmaEngineManager(QtWidgets.QWidget):
|
||||||
|
"""CTA引擎管理组件"""
|
||||||
|
signal = QtCore.Signal(type(Event()))
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, cmaEngine, eventEngine, parent=None):
|
||||||
|
"""Constructor"""
|
||||||
|
super(CmaEngineManager, self).__init__(parent)
|
||||||
|
|
||||||
|
self.cmaEngine = cmaEngine
|
||||||
|
self.eventEngine = eventEngine
|
||||||
|
|
||||||
|
self.strategyLoaded = False
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
self.registerEvent()
|
||||||
|
|
||||||
|
# 记录日志
|
||||||
|
self.cmaEngine.writeCtaLog(text.CMA_ENGINE_STARTED)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initUi(self):
|
||||||
|
"""初始化界面"""
|
||||||
|
self.setWindowTitle(u'Cross Market Arbitrage')
|
||||||
|
|
||||||
|
# 按钮
|
||||||
|
loadButton = QtWidgets.QPushButton(text.LOAD_STRATEGY)
|
||||||
|
initAllButton = QtWidgets.QPushButton(text.INIT_ALL)
|
||||||
|
startAllButton = QtWidgets.QPushButton(text.START_ALL)
|
||||||
|
stopAllButton = QtWidgets.QPushButton(text.STOP_ALL)
|
||||||
|
savePositionButton = QtWidgets.QPushButton(text.SAVE_POSITION_DATA)
|
||||||
|
|
||||||
|
loadButton.clicked.connect(self.load)
|
||||||
|
initAllButton.clicked.connect(self.initAll)
|
||||||
|
startAllButton.clicked.connect(self.startAll)
|
||||||
|
stopAllButton.clicked.connect(self.stopAll)
|
||||||
|
|
||||||
|
# 滚动区域,放置所有的CtaStrategyManager
|
||||||
|
self.scrollArea = QtWidgets.QScrollArea()
|
||||||
|
self.scrollArea.setWidgetResizable(True)
|
||||||
|
|
||||||
|
# CTA组件的日志监控
|
||||||
|
self.cmaLogMonitor = QtWidgets.QTextEdit()
|
||||||
|
self.cmaLogMonitor.setReadOnly(True)
|
||||||
|
self.cmaLogMonitor.setMaximumHeight(200)
|
||||||
|
|
||||||
|
# 设置布局
|
||||||
|
hbox2 = QtWidgets.QHBoxLayout()
|
||||||
|
hbox2.addWidget(loadButton)
|
||||||
|
hbox2.addWidget(initAllButton)
|
||||||
|
hbox2.addWidget(startAllButton)
|
||||||
|
hbox2.addWidget(stopAllButton)
|
||||||
|
hbox2.addWidget(savePositionButton)
|
||||||
|
hbox2.addStretch()
|
||||||
|
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
vbox.addLayout(hbox2)
|
||||||
|
vbox.addWidget(self.scrollArea)
|
||||||
|
vbox.addWidget(self.cmaLogMonitor)
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def initStrategyManager(self):
|
||||||
|
"""初始化策略管理组件界面"""
|
||||||
|
w = QtWidgets.QWidget()
|
||||||
|
vbox = QtWidgets.QVBoxLayout()
|
||||||
|
|
||||||
|
for name in self.cmaEngine.strategyDict.keys():
|
||||||
|
# 为每一个策略实例,创建对应的管理组件实例
|
||||||
|
strategyManager = CmaStrategyManager(self.cmaEngine, self.eventEngine, name)
|
||||||
|
vbox.addWidget(strategyManager)
|
||||||
|
sleep(0.2)
|
||||||
|
|
||||||
|
vbox.addStretch()
|
||||||
|
|
||||||
|
w.setLayout(vbox)
|
||||||
|
self.scrollArea.setWidget(w)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def initAll(self):
|
||||||
|
"""全部初始化"""
|
||||||
|
for name in self.cmaEngine.strategyDict.keys():
|
||||||
|
self.cmaEngine.initStrategy(name)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def startAll(self):
|
||||||
|
"""全部启动"""
|
||||||
|
for name in self.cmaEngine.strategyDict.keys():
|
||||||
|
self.cmaEngine.startStrategy(name)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def stopAll(self):
|
||||||
|
"""全部停止"""
|
||||||
|
for name in self.cmaEngine.strategyDict.keys():
|
||||||
|
self.cmaEngine.stopStrategy(name)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def load(self):
|
||||||
|
"""加载策略"""
|
||||||
|
if not self.strategyLoaded:
|
||||||
|
try:
|
||||||
|
self.cmaEngine.loadSetting()
|
||||||
|
self.initStrategyManager()
|
||||||
|
self.strategyLoaded = True
|
||||||
|
self.cmaEngine.writeCtaLog(text.STRATEGY_LOADED)
|
||||||
|
except Exception as ex:
|
||||||
|
self.cmaEngine.writeCtaError(str(ex))
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def updateCtaLog(self, event):
|
||||||
|
"""更新CTA相关日志"""
|
||||||
|
log = event.dict_['data']
|
||||||
|
content = '\t'.join([log.logTime, log.logContent])
|
||||||
|
self.cmaLogMonitor.append(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def registerEvent(self):
|
||||||
|
"""注册事件监听"""
|
||||||
|
self.signal.connect(self.updateCtaLog)
|
||||||
|
self.eventEngine.register(EVENT_CTA_LOG, self.signal.emit)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user