数字货币跨市场套利(搬砖)引擎

This commit is contained in:
msincenselee 2018-09-01 20:51:45 +08:00
parent e5be9ba35f
commit a36c3d338e
11 changed files with 2743 additions and 0 deletions

View 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
}
]

View 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'

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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'是否要保存策略持仓数据到数据库?'

View 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?'

View 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')

View 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)