# -*- coding: utf-8 -*-
"""
Python K线模块,包含十字光标和鼠标键盘交互
Support By 量投科技(http://www.quantdo.com.cn/)
"""
import traceback
import talib as ta
import numpy as np
import pandas as pd
import os
from functools import partial
from collections import deque
from qtpy.QtGui import *
from qtpy.QtCore import *
from qtpy.QtWidgets import *
from qtpy import QtGui,QtCore
from uiCrosshair import Crosshair
from uiCustomMenu import CustomMenu
import pyqtgraph as pg
from qtpy.QtGui import QPainter, QPainterPath, QPen, QColor, QPixmap, QIcon, QBrush, QCursor,QFont
import datetime as dt
import json
import sys
from sys import path
path.append('F:\\vnpy-1.9.0\\examples\\CtaBacktesting')
reload(sys)
sys.setdefaultencoding('utf-8')
#from runBacktesting_WH import calculateDailyResult_to_CSV as strategyDoubleMa_rb9999DailyResult
#from runBacktesting_WH import get_strategy_init_days as strategyDoubleMa_get_strategy_init_days
#from runBacktesting_WH import calculateDailyResult_init as strategyDoubleMa_calculateDailyResult_init
import importlib
import runBacktesting_ShortTermStrategy_RB as STRB
import runBacktesting_ShortTermStrategy_Overhigh_RB as STOVRB
import runBacktesting_RB as DMARB
import runBacktesting_Volatility_RB as VRB
# 字符串转换
#---------------------------------------------------------------------------------------
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s
########################################################################
# 键盘鼠标功能
########################################################################
class KeyWraper(QWidget):
"""键盘鼠标功能支持的元类"""
#初始化
#----------------------------------------------------------------------
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setMouseTracking(True)
#重载方法keyPressEvent(self,event),即按键按下事件方法
#----------------------------------------------------------------------
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Up:
self.onUp()
elif event.key() == QtCore.Qt.Key_Down:
self.onDown()
elif event.key() == QtCore.Qt.Key_Left:
self.onLeft()
elif event.key() == QtCore.Qt.Key_Right:
self.onRight()
elif event.key() == QtCore.Qt.Key_PageUp:
self.onPre()
elif event.key() == QtCore.Qt.Key_PageDown:
self.onNxt()
#重载方法mousePressEvent(self,event),即鼠标点击事件方法
#----------------------------------------------------------------------
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
self.onRClick(event.pos())
elif event.button() == QtCore.Qt.LeftButton:
self.onLClick(event.pos())
#重载方法mouseReleaseEvent(self,event),即鼠标点击事件方法
#----------------------------------------------------------------------
def mouseRelease(self, event):
if event.button() == QtCore.Qt.RightButton:
self.onRRelease(event.pos())
elif event.button() == QtCore.Qt.LeftButton:
self.onLRelease(event.pos())
self.releaseMouse()
#重载方法wheelEvent(self,event),即滚轮事件方法
#----------------------------------------------------------------------
def wheelEvent(self, event):
if event.angleDelta().y() > 0 :
self.onUp()
else:
self.onDown()
pass
return
#重载方法paintEvent(self,event),即拖动事件方法
#----------------------------------------------------------------------
def paintEvent(self, event):
self.onPaint()
# PgDown键
#----------------------------------------------------------------------
def onNxt(self):
pass
# PgUp键
#----------------------------------------------------------------------
def onPre(self):
pass
# 向上键和滚轮向上
#----------------------------------------------------------------------
def onUp(self):
pass
# 向下键和滚轮向下
#----------------------------------------------------------------------
def onDown(self):
pass
# 向左键
#----------------------------------------------------------------------
def onLeft(self):
pass
# 向右键
#----------------------------------------------------------------------
def onRight(self):
pass
# 鼠标左单击
#----------------------------------------------------------------------
def onLClick(self,pos):
pass
# 鼠标右单击
#----------------------------------------------------------------------
def onRClick(self,pos):
pass
# 鼠标左释放
#----------------------------------------------------------------------
def onLRelease(self,pos):
pass
# 鼠标右释放
#----------------------------------------------------------------------
def onRRelease(self,pos):
pass
# 画图
#----------------------------------------------------------------------
def onPaint(self):
pass
########################################################################
# 选择缩放功能支持
########################################################################
class CustomViewBox(pg.ViewBox):
#----------------------------------------------------------------------
def __init__(self, *args, **kwds):
pg.ViewBox.__init__(self, *args, **kwds)
# 拖动放大模式
#self.setMouseMode(self.RectMode)
## 右键自适应
#----------------------------------------------------------------------
def mouseClickEvent(self, ev):
if ev.button() == QtCore.Qt.RightButton:
self.autoRange()
########################################################################
# 时间序列,横坐标支持
########################################################################
class MyStringAxis(pg.AxisItem):
"""时间序列横坐标支持"""
# 初始化
#----------------------------------------------------------------------
def __init__(self, xdict, *args, **kwargs):
pg.AxisItem.__init__(self, *args, **kwargs)
self.minVal = 0
self.maxVal = 0
self.xdict = xdict
self.x_values = np.asarray(xdict.keys())
self.x_strings = xdict.values()
self.setPen(color=(255, 255, 255, 255), width=0.8)
self.setStyle(tickFont = QFont("Roman times",10,QFont.Bold),autoExpandTextSpace=True)
# 更新坐标映射表
#----------------------------------------------------------------------
def update_xdict(self, xdict):
self.xdict.update(xdict)
self.x_values = np.asarray(self.xdict.keys())
self.x_strings = self.xdict.values()
# 将原始横坐标转换为时间字符串,第一个坐标包含日期
#----------------------------------------------------------------------
def tickStrings(self, values, scale, spacing):
strings = []
for v in values:
vs = v * scale
if vs in self.x_values:
vstr = self.x_strings[np.abs(self.x_values-vs).argmin()]
vstr = vstr.strftime('%Y-%m-%d')
else:
vstr = ""
strings.append(vstr)
return strings
########################################################################
# K线图形对象
########################################################################
class CandlestickItem(pg.GraphicsObject):
"""K线图形对象"""
# 初始化
#----------------------------------------------------------------------
def __init__(self, data):
"""初始化"""
pg.GraphicsObject.__init__(self)
# 数据格式: [ (time, open, close, low, high),...]
self.data = data
# 只重画部分图形,大大提高界面更新速度
self.rect = None
self.picture = None
self.setFlag(self.ItemUsesExtendedStyleOption)
# 画笔和画刷
w = 0.4
self.offset = 0
self.low = 0
self.high = 1
self.picture = QtGui.QPicture()
self.pictures = []
self.bPen = pg.mkPen(color=(0, 240, 240, 255), width=w*2)
self.bBrush = pg.mkBrush((0, 240, 240, 255))
self.rPen = pg.mkPen(color=(255, 60, 60, 255), width=w*2)
self.rBrush = pg.mkBrush((255, 60, 60, 255))
self.rBrush.setStyle(Qt.NoBrush)
# 刷新K线
self.generatePicture(self.data)
# 画K线
#----------------------------------------------------------------------
def generatePicture(self,data=None,redraw=False):
"""重新生成图形对象"""
# 重画或者只更新最后一个K线
if redraw:
self.pictures = []
elif self.pictures:
self.pictures.pop()
w = 0.4
bPen = self.bPen
bBrush = self.bBrush
rPen = self.rPen
rBrush = self.rBrush
self.low,self.high = (np.min(data['low']),np.max(data['high'])) if len(data)>0 else (0,1)
npic = len(self.pictures)
for (t, open0, close0, low0, high0) in data:
if t >= npic:
picture = QtGui.QPicture()
p = QtGui.QPainter(picture)
# 下跌蓝色(实心), 上涨红色(空心)
pen,brush,pmin,pmax = (bPen,bBrush,close0,open0)\
if open0 > close0 else (rPen,rBrush,open0,close0)
p.setPen(pen)
p.setBrush(brush)
# 画K线方块和上下影线
if open0 == close0:
p.drawLine(QtCore.QPointF(t-w,open0), QtCore.QPointF(t+w, close0))
else:
p.drawRect(QtCore.QRectF(t-w, open0, w*2, close0-open0))
if pmin > low0:
p.drawLine(QtCore.QPointF(t,low0), QtCore.QPointF(t, pmin))
if high0 > pmax:
p.drawLine(QtCore.QPointF(t,pmax), QtCore.QPointF(t, high0))
p.end()
self.pictures.append(picture)
# 手动重画
#----------------------------------------------------------------------
def update(self):
if not self.scene() is None:
self.scene().update()
# 自动重画
#----------------------------------------------------------------------
def paint(self, painter, opt, w):
rect = opt.exposedRect
xmin,xmax = (max(0,int(rect.left())),min(int(len(self.pictures)),int(rect.right())))
if not self.rect == (rect.left(),rect.right()) or self.picture is None:
self.rect = (rect.left(),rect.right())
self.picture = self.createPic(xmin,xmax)
self.picture.play(painter)
elif not self.picture is None:
self.picture.play(painter)
# 缓存图片
#----------------------------------------------------------------------
def createPic(self,xmin,xmax):
picture = QPicture()
p = QPainter(picture)
[pic.play(p) for pic in self.pictures[xmin:xmax]]
p.end()
return picture
# 定义边界
#----------------------------------------------------------------------
def boundingRect(self):
return QtCore.QRectF(0,self.low,len(self.pictures),(self.high-self.low))
#----------------------------------------------------------------------
def hide(self):
self.parent.hide()
pass
########################################################################
class KLineWidget(KeyWraper):
"""用于显示价格走势图"""
# 窗口标识
clsId = 0
# 保存K线数据的列表和Numpy Array对象
listBar = []
listVol = []
listHigh = []
listLow = []
KLINE_DATE = []
KLINE_OPEN = []
KLINE_HIGH = []
KLINE_LOW = []
KLINE_SHORT_TERM_LOW = []
KLINE_SHORT_TERM_HIGH = []
KLINE_SHORT_TERM_LIST_ALL=[]
KLINE_SHORT_TERM_LIST_FIRST=[]
KLINE_SHORT_TERM_LIST_LIMIT=[]
KLINE_WAIBAORI=[]
KLINE_GJR_BUY=[]
KLINE_GJR_SELL=[]
listClose = []
listSig = []
listOpenInterest = []
arrows = []
KLINE_SHORT_TERM_LIST_ALL_arrows = []
KLINE_SHORT_TERM_LIST_FIRST_arrows = []
KLINE_SHORT_TERM_LIST_LIMIT_arrows = []
KLINE_WAI_BAO_RI_arrows = []
KLINE_GJR_BUY_arrows = []
KLINE_GJR_SELL_arrows = []
curves = []
KLINE_SHORT_TERM_LIST_ALL_curves = []
KLINE_SHORT_TERM_LIST_FIRST_curves = []
KLINE_SHORT_TERM_LIST_LIMIT_curves = []
listSig_deal_DIRECTION = []
listSig_deal_OFFSET = []
KLINE_CLOSE=[]
start_date=[] #[20090327开始日期,列表的位置]
end_date=[] #[20181127结束日期,结束的位置]
KLINE_show =True
MA_SHORT_show =False
MA_LONG_show =False
listbarshow =False
SHORT_TERM_SHOW =False
SHORT_TERM_SHOW_FIRST=False
SHORT_TERM_SHOW_LIMIT=False
SHORT_TERM_SHOW_ALL =False
WAIBAORI_SHOW =False
GJRBUY_SHOW =False
signal_show =True
# 是否完成了历史数据的读取
initCompleted = False
#----------------------------------------------------------------------
def __init__(self,parent=None):
"""Constructor"""
self.parent = parent
super(KLineWidget, self).__init__(parent)
# 当前序号
self.index = None # 下标
self.countK = 60 # 显示的K线范围
KLineWidget.clsId += 1
self.windowId = str(KLineWidget.clsId)
# 缓存数据
self.datas = []
self.listBar = []
self.listbarshow= True
self.listVol = []
self.listHigh = []
self.listLow = []
self.listSig = []
self.listOpenInterest = []
self.arrows = []
self.curves = []
self.listSig_deal_DIRECTION = []
self.listSig_deal_OFFSET = []
self.KLINE_CLOSE=[]
self.MA_SHORT_real=[]
self.MA_LONG_real=[]
self.start_time=[]
self.MA_SHORT_show=False
self.MA_LONG_show=False
self.SHORT_TERM_SHOW=False
self.SHORT_TERM_SHOW_FIRST=False
self.SHORT_TERM_SHOW_LIMIT=False
self.SHORT_TERM_SHOW_ALL=False
self.signal_show=False
self.cur_jsonname=u'json\\uiKLine_startpara.json'
self.SP_signal='close'
self.BP_signal='close'
# 所有K线上信号图
self.allColor = deque(['blue','green','yellow','white'])
self.sigData = {}
self.sigColor = {}
self.sigPlots = {}
# 所副图上信号图
self.allSubColor = deque(['blue','green','yellow','white'])
self.subSigData = {}
self.subSigColor = {}
self.subSigPlots = {}
# 初始化完成
self.initCompleted = False
# 调用函数
self.initUi()
self.menu= CustomMenu(self)
#----------------------------------------------------------------------
# 初始化相关
#----------------------------------------------------------------------
def initUi(self):
"""初始化界面"""
self.setWindowTitle(u'K线工具')
# 主图
self.pw = pg.PlotWidget()
# 界面布局
self.lay_KL = pg.GraphicsLayout(border=(100,100,100))
self.lay_KL.setContentsMargins(10, 10, 10, 10)
self.lay_KL.setSpacing(0)
self.lay_KL.setBorder(color=(255, 0, 0, 255), width=0.8)
self.lay_KL.setZValue(0)
self.KLtitle = self.lay_KL.addLabel(u'')
self.pw.setCentralItem(self.lay_KL)
# 设置横坐标
xdict = {}
self.axisTime = MyStringAxis(xdict, orientation='bottom')
# 初始化子图
self.initplotKline()
self.initplotVol()
self.initplotOI()
# 注册十字光标
self.crosshair = Crosshair(self.pw,self)
# 设置界面
self.vb = QVBoxLayout()
self.vb.addWidget(self.pw)
self.setLayout(self.vb)
# 初始化完成
self.initCompleted = True
#----------------------------------------------------------------------
def makePI(self,name):
"""生成PlotItem对象"""
vb = CustomViewBox()
plotItem = pg.PlotItem(viewBox = vb, name=name ,axisItems={'bottom': self.axisTime})
plotItem.setMenuEnabled(False)
plotItem.setClipToView(True)
plotItem.hideAxis('left')
plotItem.showAxis('right')
plotItem.setDownsampling(mode='peak')
plotItem.setRange(xRange = (0,1),yRange = (0,1))
plotItem.getAxis('right').setWidth(30)
plotItem.getAxis('right').setStyle(tickFont = QFont("Roman times",10,QFont.Bold))
plotItem.getAxis('right').setPen(color=(255, 0, 0, 255), width=0.8)
plotItem.showGrid(True,True)
plotItem.hideButtons()
return plotItem
#----------------------------------------------------------------------
def initplotVol(self):
"""初始化成交量子图"""
self.pwVol = self.makePI('_'.join([self.windowId,'PlotVOL']))
self.volume = CandlestickItem(self.listVol)
self.pwVol.addItem(self.volume)
self.pwVol.setMaximumHeight(50)
self.pwVol.setXLink('_'.join([self.windowId,'PlotOI']))
self.pwVol.hideAxis('bottom')
self.lay_KL.nextRow()
self.lay_KL.addItem(self.pwVol)
#----------------------------------------------------------------------
def initplotKline(self):
"""初始化K线子图以及指标子图"""
self.pwKL = self.makePI('_'.join([self.windowId,'PlotKL&