【开源一个用于回测的Python交互K线工具】之【一】K线核心功能
由polll创建,最终由polll 被浏览 77 用户
动机
个人量化研究,用vn.py回测和研究策略。
发现最痛苦的事情就是写完一个策略后,根本没法方便地检查自己的交易逻辑。每次打印日志之后,翻日志再找其他K线工具来校对,这个过程简直泪流满面。
![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='375' height='147'></svg>)
在无数次面对日志抓瞎以后,决心寻找一个好用的Python K线工具,于是在知乎搜索 Python K线
哦,不错,有很多不错的Python K线案例嘛!
但是!!
刨去静态图片啊,上面的动态交互工具,都没办法让我方便地把策略回测的结果放进去。
如果能按一下键盘就可以在开平仓标记位置biu,biu跳转就更好了。
看来自己手撸一个交互K线是免不了的
![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='320' height='192'></svg>)
结合商业软件的K线,简单列一下需求:
- 屏幕K线数少的时候,反应要快
- 鼠标滚轮缩放,键盘缩放跳转
- 十字光标 显示K线详细信息
- 缩放自适应Y轴坐标
- 策略回测运行中产生的指标可以放到K线图中
- 买卖开平仓位置有箭头标记,并且通过键盘可以在标记之间跳转
成果
废话不说了,先来看成果
![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1915' height='1016'></svg>)
基础K线模块
![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1888' height='960'></svg>)
回测结果K线展示
- 界面风格抄袭了市面上看到的商业软件
- 界面缩放,十字光标移动顺畅
- 回测完以后可以直接把开平仓标记和策略的技术指标显示到界面
- 键盘PgUp和PgDn可以在开平仓点自由切换了
K线图形对象部分代码
########################################################################
# 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
low,high = (data[0]['low'],data[0]['high']) if len(data)>0 else (0,1)
for (t, open0, close0, low0, high0) in data:
if t >= len(self.pictures):
picture = QtGui.QPicture()
p = QtGui.QPainter(picture)
low,high = (min(low,low0),max(high,high0))
# 下跌蓝色(实心), 上涨红色(空心)
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)
self.ranges.append((low0,high0))
self.low,self.high = low,high
# 手动重画
#----------------------------------------------------------------------
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 self.picture:
self.picture.play(painter)
# 缓存图片
#----------------------------------------------------------------------
def createPic(self,xmin,xmax):
picture = QtGui.QPicture()
p = QtGui.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))
K线展示的核心功能使用pyqtgraph实现,使用了ItemUsesExtendedStyleOption,并且根据逻辑判断界面需要更新的部分。
相比pyqtgraph示例里面的重画整个图形,界面更新指数级加快了!!
加载1个月的1分钟K线,界面仍能较流畅显示。
载入K线数据接口,使用pandas.DataFrame格式,支持全部载入和一根根载入
#----------------------------------------------------------------------
def onBar(self, bar, nWindow = 20):
"""
新增K线数据,K线播放模式
nWindow : 最大数据窗口
"""
#----------------------------------------------------------------------
def loadData(self, datas):
"""
载入pandas.DataFrame数据
datas : 数据格式,cols : datetime, open, close, low, high, volume, openInterest
"""
展示策略的技术指标和开平仓标记,开平仓标记通过listSig传入
#----------------------------------------------------------------------
def addSig(self,sig):
"""新增信号图"""
if sig in self.sigPlots:
self.pwKL.removeItem(self.sigPlots[sig])
self.sigPlots[sig] = self.pwKL.plot()
self.sigColor[sig] = self.allColor[0]
self.allColor.append(self.allColor.popleft())
#----------------------------------------------------------------------
def showSig(self,datas):
"""刷新信号图"""
for sig in self.sigPlots:
self.sigData[sig] = datas[sig]
[self.sigPlots[sig].setData(datas[sig], pen=self.sigColor[sig][0], name=sig)\
for sig in self.sigPlots]# if sig in datas]
#----------------------------------------------------------------------
def plotMark(self):
"""显示开平仓信号"""
# 检查是否有数据
if len(self.datas)==0:
return
for arrow in self.arrows:
self.pwKL.removeItem(arrow)
# 画买卖信号
for i in range(len(self.listSig)):
# 无信号
if self.listSig[i] == 0:
continue
# 买信号
elif self.listSig[i] > 0:
arrow = pg.ArrowItem(pos=(i, self.datas[i]['low']), angle=90, brush=(255, 0, 0))
# 卖信号
elif self.listSig[i] < 0:
arrow = pg.ArrowItem(pos=(i, self.datas[i]['high']), angle=-90, brush=(0, 255, 0))
self.pwKL.addItem(arrow)
self.arrows.append(arrow)
说好的开源
下载代码并安装依赖的Python库后,双击 uiKLine.py 就可以看到K线哦 !
写在最后
感谢vn.py让我入门量化,并且一步步走到目前小有盈利。如果vn.py后续有开发回测结果K线展示界面的计划,我希望能贡献点力量!@用python的交易员
后续还会继续开源上面【回测结果K线展示】图中工具,敬请期待~
因为本人工作是开发交易客户端,推荐下我们团队开发的无限易交易客户端,可以连接国内四大期货,外盘和上海黄金模拟环境,免费使用,账户和密码都是888888。
![](data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='1920' height='1030'></svg>)
交易客户端