vnpy/vn.training/quotation_ext.py
2015-10-20 22:50:56 +08:00

2597 lines
94 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
import os
import sys
import pickle
import math
import datetime
import itertools
import matplotlib
matplotlib.use("WXAgg", warn=True) # 这个要紧跟在 import matplotlib 之后,而且必须安装了 wxpython 2.8 才行。
import matplotlib.pyplot as pyplot
import matplotlib.font_manager as font_manager
import numpy
from matplotlib.ticker import NullLocator, FixedLocator, MultipleLocator, FuncFormatter, NullFormatter
from matplotlib.patches import Ellipse
__font_properties__= font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc')
__color_lightsalmon__= '#ffa07a'
__color_pink__= '#ffc0cb'
__color_navy__= '#000080'
__color_gold__= '#FDDB05'
__color_gray30__= '0.3'
__color_gray70__= '0.7'
__color_lightblue__= 'lightblue'
__shrink__= 1.0 / 4
__expbase__= 1.1
class SubPlot_BasicInfo:
'''
公司的基本信息
Note: this is not "real" subplot, no Axes object contained.
'''
def __init__(self, pdata, parent, name):
self._name= name
self._pdata= pdata
self._cominfo= self._pdata[u'公司信息']
self._parent= parent
self._Axes= None
self._xsize, \
self._ysize= self._compute_size()
def _compute_size(self):
return (300.0, 1.8)
def get_size(self):
return (self._xsize, self._ysize)
def build_axes(self, figobj, rect):
axes= figobj.add_axes(rect)
axes.set_frame_on(False)
self._Axes= axes
self.set_xticks()
self.set_yticks()
def set_xticks(self):
axes= self._Axes
xaxis= axes.get_xaxis()
# 设定 X 轴坐标的范围
#==================================================================================================================================================
axes.set_xlim(0, self._xsize)
xaxis.set_major_locator(NullLocator())
for mal in axes.get_xticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_xticklabels(minor=True):
mil.set_visible(False)
def set_yticks(self):
axes= self._Axes
yaxis= axes.get_yaxis()
# 设定 X 轴坐标的范围
#==================================================================================================================================================
axes.set_ylim(0, self._ysize)
yaxis.set_major_locator(NullLocator())
for mal in axes.get_yticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_yticklabels(minor=True):
mil.set_visible(False)
def plot(self):
self.plot_codesymbol(xbase=0.0, ybase=self._ysize)
self.plot_codesymbol_2(xbase=self._xsize, ybase=self._ysize)
self.plot_companyname(xbase=0.0, ybase=self._ysize-0.8)
self.plot_companylocation(xbase=48.0, ybase=self._ysize)
self.plot_mainbusiness(xbase=48.0, ybase=self._ysize)
self.plot_description(xbase=90.0, ybase=self._ysize)
self.plot_sortinginfo(xbase=165.0, ybase=self._ysize)
def plot_codesymbol(self, xbase, ybase):
'''
交易代码、公司简称
'''
txtstr= self._cominfo[u'代码'] + u' ' + self._cominfo[u'简称']
label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left')
label.set_fontsize(16.0)
def plot_codesymbol_2(self, xbase, ybase):
txtstr= self._cominfo[u'简称二']
label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='right')
label.set_fontsize(16.0)
def plot_companyname(self, xbase, ybase):
'''
曾用名、全名、英文名
'''
txtstr= self._cominfo[u'基本情况'][u'曾用名']
txtlist= txtstr.split('->')
if len(txtlist) > 15:
txtstr= ' -> '.join(txtlist[:5]) + ' ->\n' + ' -> '.join(txtlist[5:10]) + ' ->\n' + ' -> '.join(txtlist[10:15]) + ' ->\n' + ' -> '.join(txtlist[15:]) + '\n'
elif len(txtlist) > 10:
txtstr= ' -> '.join(txtlist[:5]) + ' ->\n' + ' -> '.join(txtlist[5:10]) + ' ->\n' + ' -> '.join(txtlist[10:]) + '\n'
elif len(txtlist) > 5:
txtstr= ' -> '.join(txtlist[:5]) + ' ->\n' + ' -> '.join(txtlist[5:]) + '\n'
else:
txtstr= ' -> '.join(txtlist) + '\n'
txtstr += self._cominfo[u'基本情况'][u'公司名称'] + '\n'
txtstr += self._cominfo[u'基本情况'][u'英文名称']
label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left')
label.set_fontsize(4.5)
def plot_companylocation(self, xbase, ybase):
'''
地域、所属行业、上市日期
'''
txtstr= self._cominfo[u'公司概况'][u'区域'] + ' ' + self._cominfo[u'公司概况'][u'所属行业'] + ' ' + self._cominfo[u'发行相关'][u'上市日期']
label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left')
label.set_fontsize(6.5)
def plot_mainbusiness(self, xbase, ybase):
'''
主营业务
'''
# 查找表: (<文字长度>, <每行字数>, <字体大小>, <Y轴偏移量>)
lookups= (
(20, 10, 12.0, 0.5),
(45, 15, 8.2, 0.5),
(80, 20, 6.2, 0.5),
(125, 25, 5.0, 0.5),
(180, 30, 4.1, 0.5),
(245, 35, 3.5, 0.4),
(999999, 37, 3.4, 0.4)
)
txtstr= self._cominfo[u'基本情况'][u'主营业务']
length= len(txtstr)
for sizelimit, linelimit, fontsize, yshift in lookups:
if length <= sizelimit:
txtstr= '\n'.join([txtstr[linelimit*idx : linelimit*(idx+1)] for idx in range(length//linelimit + 1)])
fsize= fontsize
ycoord= ybase - yshift
break
label= self._Axes.text(xbase, ycoord, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left', color='blue')
label.set_fontsize(fsize)
def plot_description(self, xbase, ybase):
'''
公司简介
'''
# 查找表: (<文字长度>, <每行字数>, <字体大小>)
lookups= (
(150, 30, 7.0),
(240, 40, 5.6),
(329, 47, 4.8),
(432, 54, 4.2),
(576, 64, 3.5),
(670, 67, 3.4),
(792, 72, 3.1),
(960, 80, 2.8),
(1222, 94, 2.4),
(1428, 102, 2.26),
(1620, 108, 2.12),
(1938, 114, 2.00),
(999999, 130, 1.75)
)
txtstr= self._cominfo[u'公司概况'][u'公司简介'] # 26 ~ 2600 字符
length= len(txtstr)
for sizelimit, linelimit, fontsize in lookups:
if length <= sizelimit:
txtstr= '\n'.join([txtstr[linelimit*idx : linelimit*(idx+1)] for idx in range(length//linelimit + 1)])
fsize= fontsize
break
label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left')
label.set_fontsize(fsize)
def plot_sortinginfo(self, xbase, ybase):
'''
行业板块信息
'''
infolist= self._cominfo[u'行业板块']
for idx in range(len(infolist)//10 + 1):
txtstr= '\n'.join(infolist[10*idx : 10*(idx+1)])
if not txtstr:
break
xcoord= xbase + 25.0*idx
label= self._Axes.text(xcoord, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left', color='blue')
label.set_fontsize(3.4)
class SubPlot_Financial:
'''
换手率子图
'''
pass
class SubPlot_PriceBase:
'''
'''
def __init__(self, pdata, parent, xparams, name):
'''
'''
self._name= name # 派生类自己设置
self._pdata= pdata
self._parent= parent
self._expbase= __expbase__
self._xparams= xparams
self._shrink= __shrink__ if name == 'pricefs' else 1.0
# 绘图数据
quotes= pdata[u'行情']
if name == 'pricefs':
self._dates= quotes[u'日期']
self._open= quotes[u'开盘']
self._close= quotes[u'收盘']
self._high= quotes[u'最高']
self._low= quotes[u'最低']
if u'简化' in quotes: self._simple= quotes[u'简化']
# if u'换手率' in quotes: self._torate= quotes[u'换手率']
# if u'成交量' in quotes: self._volume= quotes[u'成交量']
# if u'成交额' in quotes: self._turnover= quotes[u'成交额']
if u'3日均' in quotes: self._average3= quotes[u'3日均']
if u'5日均' in quotes: self._average5= quotes[u'5日均']
if u'10日均' in quotes: self._average10= quotes[u'10日均']
if u'30日均' in quotes: self._average30= quotes[u'30日均']
if u'60日均' in quotes: self._average60= quotes[u'60日均']
if u'开盘二' in quotes:
self._open_2= quotes[u'开盘二']
self._close_2= quotes[u'收盘二']
self._high_2= quotes[u'最高二']
self._low_2= quotes[u'最低二']
if u'简化二' in quotes: self._simple_2= quotes[u'简化二']
# if u'换手率二' in quotes: self._torate_2= quotes[u'换手率二']
# if u'成交量二' in quotes: self._volume_2= quotes[u'成交量二']
# if u'成交额二' in quotes: self._turnover_2= quotes[u'成交额二']
if u'3日均二' in quotes: self._average3_2= quotes[u'3日均二']
if u'5日均二' in quotes: self._average5_2= quotes[u'5日均二']
if u'10日均二' in quotes: self._average10_2= quotes[u'10日均二']
if u'30日均二' in quotes: self._average30_2= quotes[u'30日均二']
if u'60日均二' in quotes: self._average60_2= quotes[u'60日均二']
else:
sidx, eidx= pdata[u'任务描述'][u'起始偏移'], pdata[u'任务描述'][u'结束偏移']
self._dates= quotes[u'日期'][sidx:eidx]
self._open= quotes[u'开盘'][sidx:eidx]
self._close= quotes[u'收盘'][sidx:eidx]
self._high= quotes[u'最高'][sidx:eidx]
self._low= quotes[u'最低'][sidx:eidx]
if u'简化' in quotes: self._simple= quotes[u'简化'][sidx:eidx]
# if u'换手率' in quotes: self._torate= quotes[u'换手率'][sidx:eidx]
# if u'成交量' in quotes: self._volume= quotes[u'成交量'][sidx:eidx]
# if u'成交额' in quotes: self._turnover= quotes[u'成交额'][sidx:eidx]
if u'3日均' in quotes: self._average3= quotes[u'3日均'][sidx:eidx]
if u'5日均' in quotes: self._average5= quotes[u'5日均'][sidx:eidx]
if u'10日均' in quotes: self._average10= quotes[u'10日均'][sidx:eidx]
if u'30日均' in quotes: self._average30= quotes[u'30日均'][sidx:eidx]
if u'60日均' in quotes: self._average60= quotes[u'60日均'][sidx:eidx]
if u'开盘二' in quotes:
self._open_2= quotes[u'开盘二'][sidx:eidx]
self._close_2= quotes[u'收盘二'][sidx:eidx]
self._high_2= quotes[u'最高二'][sidx:eidx]
self._low_2= quotes[u'最低二'][sidx:eidx]
if u'简化二' in quotes: self._simple_2= quotes[u'简化二'][sidx:eidx]
# if u'换手率二' in quotes: self._torate_2= quotes[u'换手率二'][sidx:eidx]
# if u'成交量二' in quotes: self._volume_2= quotes[u'成交量二'][sidx:eidx]
# if u'成交额二' in quotes: self._turnover_2= quotes[u'成交额二'][sidx:eidx]
if u'3日均二' in quotes: self._average3_2= quotes[u'3日均二'][sidx:eidx]
if u'5日均二' in quotes: self._average5_2= quotes[u'5日均二'][sidx:eidx]
if u'10日均二' in quotes: self._average10_2= quotes[u'10日均二'][sidx:eidx]
if u'30日均二' in quotes: self._average30_2= quotes[u'30日均二'][sidx:eidx]
if u'60日均二' in quotes: self._average60_2= quotes[u'60日均二'][sidx:eidx]
self._length= len(self._dates) # XXX: 由派生类设定
# 衍生数据
#==============================================================================================================
self._xindex= numpy.arange(self._length) # X 轴上的 index一个辅助数据
self._zipoc= zip(self._open, self._close)
self._up= numpy.array( [ True if po < pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内上涨的一个序列
self._down= numpy.array( [ True if po > pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内下跌的一个序列
self._side= numpy.array( [ True if po == pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内走平的一个序列
if u'开盘二' in quotes:
self._zipoc_2= zip(self._open_2, self._close_2)
self._up_2= numpy.array( [ True if po < pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内上涨的一个序列
self._down_2= numpy.array( [ True if po > pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内下跌的一个序列
self._side_2= numpy.array( [ True if po == pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内走平的一个序列
self._Axes= None
self._AxisX= None
self._AxisY= None
self._xsize= 0.0
self._ysize= 0.0
self._yhighlim= 0 # Y 轴最大坐标
self._ylowlim= 0 # Y 轴最小坐标
if u'开盘二' in self._pdata[u'行情']:
self._Axes_2= None # 如果有第二个行情数据,就建立另一个 Axes 对象
self._AxisX_2= None
self._AxisY_2= None
self._yhighlim_2= 0 # Y 轴最大坐标
self._ylowlim_2= 0 # Y 轴最小坐标
self._compute_size()
self._ytickset= self._compute_ytickset() # 需放在前一句后面
def _compute_size(self):
'''
根据绘图数据 pdata 计算出本子图的尺寸,修改数据成员
'''
quotes= self._pdata[u'行情']
popen= self._open[0] # int 类型
phigh= max( [ph for ph in self._high if ph is not None] ) # 最高价
plow= min( [pl for pl in self._low if pl is not None] ) # 最低价
# Y 轴范围
if self._name == 'pricefs':
yhighlim= phigh * 1.2 # K线子图 Y 轴最大坐标
ylowlim= plow / 1.2 # K线子图 Y 轴最小坐标
else:
yhighlim= phigh * 1.1 # K线子图 Y 轴最大坐标
ylowlim= plow / 1.1 # K线子图 Y 轴最小坐标
self._yhighlim= yhighlim
self._ylowlim= ylowlim
if u'开盘二' in quotes:
popen_2= self._open_2[0] # 同上
phigh_2= max( [ph for ph in self._high_2 if ph is not None] ) # 第二个行情的最高价
phigh= max(phigh, int(phigh_2 * popen / float(popen_2))) # 以第一个行情为基准修正出的总最高价
plow_2= min( [pl for pl in self._low_2 if pl is not None] ) # 最低价
plow= min(plow, int(plow_2 * popen / float(popen_2))) # 以第一个行情为基准修正出的总最低价
if self._name == 'pricefs':
yhighlim= phigh * 1.2 # K线子图 Y 轴最大坐标
ylowlim= plow / 1.2 # K线子图 Y 轴最小坐标
else:
yhighlim= phigh * 1.1 # K线子图 Y 轴最大坐标
ylowlim= plow / 1.1 # K线子图 Y 轴最小坐标
ylowlim_2= ylowlim * popen_2 / float(popen)
yhighlim_2= yhighlim * popen_2 / float(popen)
self._yhighlim= yhighlim
self._ylowlim= ylowlim
self._yhighlim_2= yhighlim_2
self._ylowlim_2= ylowlim_2
# XXX: 价格在 Y 轴上的 “份数”。注意,虽然最高与最低价是以第一个行情为基准修正出来的,但其中包含的倍数因子对结果无影响,即:
# log(base, num1) - log(base, num2) ==
# log(base, num1/num2) ==
# log(base, k*num1/k*num2) ==
# log(base, k*num1) - log(base, k*num2)
# ,这是对数运算的性质。
xmargin= self._xparams['xmargin']
self._xsize= (self._length + xmargin*2) * self._shrink # int, 所有数据的长度,就是天数
self._ysize= (math.log(yhighlim, self._expbase) - math.log(ylowlim, self._expbase)) * self._shrink # float
def get_size(self):
return (self._xsize, self._ysize)
def get_ylimits(self):
return (self._yhighlim, self._ylowlim)
def build_axes(self, figobj, rect):
'''
初始化 self._Axes 对象
'''
# 添加 Axes 对象
#==================================================================================================================================================
if self._name == 'price' and 'torate' in self._parent._subplots:
sharex= self._parent._subplots['torate'].get_axes()
axes= figobj.add_axes(rect, axis_bgcolor='black', sharex=sharex)
elif self._name == 'pricefs' and 'toratefs' in self._parent._subplots:
sharex= self._parent._subplots['toratefs'].get_axes()
axes= figobj.add_axes(rect, axis_bgcolor='black', sharex=sharex)
else:
axes= figobj.add_axes(rect, axis_bgcolor='black')
axes.set_axisbelow(True) # 网格线放在底层
# axes.set_zorder(1) # XXX: 不顶用
# axes.patch.set_visible(False) # hide the 'canvas'
axes.set_yscale('log', basey=self._expbase) # 使用对数坐标
# 改变坐标线的颜色
#==================================================================================================================================================
for child in axes.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color(__color_gold__)
# 得到 X 轴 和 Y 轴 的两个 Axis 对象
#==================================================================================================================================================
xaxis= axes.get_xaxis()
yaxis= axes.get_yaxis()
# 设置两个坐标轴上的网格线
#==================================================================================================================================================
xaxis.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
xaxis.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
if self._name == 'pricefs': # 如果是小图,就不设辅助网格线
yaxis.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.1)
else:
yaxis.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
yaxis.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
yaxis.set_label_position('left')
self._Axes= axes
self._AxisX= xaxis
self._AxisY= yaxis
if u'开盘二' in self._pdata[u'行情']:
# 添加 Axes 对象。注意,设置 axes_2 而不是 axes 的网格线,从而不会跑到 axes 边框上边的做法不顶用。
#==================================================================================================================================================
axes_2= axes.twinx() # twinx is problematic, no use no more.
# XXX: 下面这三行把第一个 axes 放在上面,这样不会被第二个 axes 的图形遮盖。用 zorder 不顶用。
axes.figure.axes[-2:]= [axes_2, axes] # XXX:
axes.set_frame_on(False) # 如果不做此设定axes_2 的内容会看不见
axes_2.set_frame_on(True)
axes_2.set_axis_bgcolor('black')
axes_2.set_axisbelow(True) # 网格线放在底层
axes_2.set_yscale('log', basey=self._expbase) # 使用对数坐标
# 改变坐标线的颜色
#==================================================================================================================================================
for child in axes_2.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color(__color_gold__)
# 得到 X 轴 和 Y 轴 的两个 Axis 对象
#==================================================================================================================================================
xaxis_2= axes_2.get_xaxis()
yaxis_2= axes_2.get_yaxis()
# 设置两个坐标轴上的网格线
#==================================================================================================================================================
# xaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
# xaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
# if self._name == 'pricefs': # 如果是小图,就不设辅助网格线
# yaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.1)
# else:
# yaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
# yaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
yaxis_2.set_label_position('right')
self._Axes_2= axes_2
self._AxisX_2= xaxis_2
self._AxisY_2= yaxis_2
def set_xticks(self):
xMajorLocator= self._xparams['xMajorLocator']
xMinorLocator= self._xparams['xMinorLocator']
axes= self._Axes
xaxis= self._AxisX
# 设定 X 轴坐标的范围
#==================================================================================================================================================
xmargin= self._xparams['xmargin']
axes.set_xlim(-xmargin, self._length + xmargin)
# 先设置 label 位置,再将 X 轴上的坐标设为不可见。因为与 成交量子图 共用 X 轴
#==================================================================================================================================================
# 设定 X 轴的 Locator 和 Formatter
xaxis.set_major_locator(xMajorLocator)
# xaxis.set_major_formatter(xMajorFormatter)
xaxis.set_minor_locator(xMinorLocator)
# xaxis.set_minor_formatter(xMinorFormatter)
# 将 X 轴上的坐标设为不可见。
for mal in axes.get_xticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_xticklabels(minor=True):
mil.set_visible(False)
def set_xticks_2(self):
quotes= self._pdata[u'行情']
axes= self._Axes_2
xaxis= self._AxisX_2
xMajorLocator= self._xparams['xMajorLocator']
xMinorLocator= self._xparams['xMinorLocator']
# 设定 X 轴坐标的范围
#==================================================================================================================================================
xmargin= self._xparams['xmargin']
axes.set_xlim(-xmargin, self._length + xmargin)
# 先设置 label 位置,再将 X 轴上的坐标设为不可见。因为与 成交量子图 共用 X 轴
#==================================================================================================================================================
# 设定 X 轴的 Locator 和 Formatter
xaxis.set_major_locator(xMajorLocator)
# xaxis.set_major_formatter(xMajorFormatter)
xaxis.set_minor_locator(xMinorLocator)
# xaxis.set_minor_formatter(xMinorFormatter)
# 将 X 轴上的坐标设为不可见。
for mal in axes.get_xticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_xticklabels(minor=True):
mil.set_visible(False)
def _compute_ytickset(self):
'''
计算 Y 轴坐标点的位置,包括第二个行情
'''
quotes= self._pdata[u'行情']
expbase= self._expbase
ytickset= {}
yhighlim= self._yhighlim
ylowlim= self._ylowlim
if u'开盘二' in quotes:
yhighlim_2= self._yhighlim_2
ylowlim_2= self._ylowlim_2
if self._name == 'price' and 'pricefs' in self._parent._subplots:
tsetfs= self._parent._subplots['pricefs'].get_ytickset()
majors= tsetfs['major']
while majors[-1] < yhighlim: majors.append(majors[-1] * expbase)
while majors[0] > ylowlim: majors.insert(0, majors[0] / expbase)
minors= tsetfs['minor']
while minors[-1] < yhighlim: minors.append(minors[-1] * expbase)
while minors[0] > ylowlim: minors.insert(0, minors[0] / expbase)
ytickset['major']= [loc for loc in majors if loc > ylowlim and loc < yhighlim]
ytickset['minor']= [loc for loc in minors if loc > ylowlim and loc < yhighlim]
else:
# 主要坐标点
#----------------------------------------------------------------------------
majors= [ylowlim]
while majors[-1] < yhighlim: majors.append(majors[-1] * 1.1)
# 辅助坐标点
#----------------------------------------------------------------------------
minors= [ylowlim * 1.1**0.5]
while minors[-1] < yhighlim: minors.append(minors[-1] * 1.1)
ytickset['major']= [loc for loc in majors if loc > ylowlim and loc < yhighlim] # 注意第一项ylowlim被排除掉了
ytickset['minor']= [loc for loc in minors if loc > ylowlim and loc < yhighlim]
if u'开盘二' in quotes:
popen= self._open[0] # int 类型
popen_2= self._open_2[0] # 同上
ytickset['major_2']= [loc * popen_2 / popen for loc in ytickset['major']]
ytickset['minor_2']= [loc * popen_2 / popen for loc in ytickset['minor']]
return ytickset
def get_ytickset(self):
return self._ytickset
def set_yticks(self):
'''
设置第一只行情的 Y 轴坐标,包括坐标值在图中间的显示
'''
axes= self._Axes
ylowlim= self._ylowlim
yhighlim= self._yhighlim
yaxis= self._AxisY
majorticks= self._ytickset['major']
minorticks= self._ytickset['minor']
# 设定 Y 轴坐标的范围
#==================================================================================================================================================
axes.set_ylim(ylowlim, yhighlim)
# 设定 Y 轴上的坐标
#==================================================================================================================================================
# 主要坐标点
#----------------------------------------------------------------------------
yMajorLocator= FixedLocator(numpy.array(majorticks))
# 确定 Y 轴的 MajorFormatter
def y_major_formatter(num, pos=None):
return str(round(num/1000.0, 2))
yMajorFormatter= FuncFormatter(y_major_formatter)
# 设定 X 轴的 Locator 和 Formatter
yaxis.set_major_locator(yMajorLocator)
yaxis.set_major_formatter(yMajorFormatter)
# 设定 Y 轴主要坐标点与辅助坐标点的样式
fsize= 4 if self._name == 'pricefs' else 6
for mal in axes.get_yticklabels(minor=False):
mal.set_fontsize(fsize)
# 辅助坐标点
#----------------------------------------------------------------------------
yMinorLocator= FixedLocator(numpy.array(minorticks))
# 确定 Y 轴的 MinorFormatter
def y_minor_formatter(num, pos=None):
return str(round(num/1000.0, 2))
yMinorFormatter= FuncFormatter(y_minor_formatter)
# 设定 X 轴的 Locator 和 Formatter
yaxis.set_minor_locator(yMinorLocator)
yaxis.set_minor_formatter(yMinorFormatter)
# 设定 Y 轴辅助坐标点的样式
if self._name == 'pricefs':
for mil in axes.get_yticklabels(minor=True):
mil.set_visible(False)
else:
for mil in axes.get_yticklabels(minor=True):
mil.set_fontsize(5)
mil.set_color('blue')
def set_yticks_2(self):
'''
子图右侧的 Y 轴坐标
'''
axes= self._Axes_2
yaxis= self._AxisY_2
yhighlim_2= self._yhighlim_2
ylowlim_2= self._ylowlim_2
majorticks_2= self._ytickset['major_2']
minorticks_2= self._ytickset['minor_2']
# 设定 Y 轴坐标的范围
#==================================================================================================================================================
axes.set_ylim(ylowlim_2, yhighlim_2)
# 设定 Y 轴上的坐标
#==================================================================================================================================================
# 主要坐标点
#----------------------------------------------------------------------------
yMajorLocator= FixedLocator(numpy.array(majorticks_2))
# 确定 Y 轴的 MajorFormatter
def y_major_formatter(num, pos=None):
return str(round(num/1000.0, 2))
yMajorFormatter= FuncFormatter(y_major_formatter)
# 设定 X 轴的 Locator 和 Formatter
yaxis.set_major_locator(yMajorLocator)
yaxis.set_major_formatter(yMajorFormatter)
# 设定 Y 轴主要坐标点与辅助坐标点的样式
fsize= 4 if self._name == 'pricefs' else 6
for mal in axes.get_yticklabels(minor=False):
mal.set_fontsize(fsize)
# 辅助坐标点
#----------------------------------------------------------------------------
yMinorLocator= FixedLocator(numpy.array(minorticks_2)) # XXX minor ticks 已经在上面一并设置,这里不需要了。
# 确定 Y 轴的 MinorFormatter
def y_minor_formatter(num, pos=None):
return str(round(num/1000.0, 2))
yMinorFormatter= FuncFormatter(y_minor_formatter)
# 设定 X 轴的 Locator 和 Formatter
yaxis.set_minor_locator(yMinorLocator)
yaxis.set_minor_formatter(yMinorFormatter)
# 设定 Y 轴主要坐标点与辅助坐标点的样式
if self._name == 'pricefs':
for mil in axes.get_yticklabels(minor=True):
mil.set_visible(False)
else:
for mil in axes.get_yticklabels(minor=True):
mil.set_fontsize(5)
mil.set_color('blue')
def plot(self):
'''
由派生类自己定义
'''
pass
def plot_candlestick(self):
'''
绘制 K 线
'''
axes= self._Axes
xindex= self._xindex
up= self._up
down= self._down
side= self._side
# 绘制 K 线部分
#==================================================================================================================================================
# 对开收盘价进行视觉修正
for idx, poc in enumerate(self._zipoc):
if poc[0] == poc[1] and None not in poc:
variant= int(round((poc[1]+1000)/2000.0, 0))
self._open[idx]= poc[0] - variant # 稍微偏离一点,使得在图线上不致于完全看不到
self._close[idx]= poc[1] + variant
rarray_open= numpy.array(self._open)
rarray_close= numpy.array(self._close)
rarray_high= numpy.array(self._high)
rarray_low= numpy.array(self._low)
# XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。
# XXX: 可以使用 alpha 参数调节透明度
if True in up:
axes.vlines(xindex[up], rarray_low[up], rarray_high[up], edgecolor='red', linewidth=0.6, label='_nolegend_', alpha=0.5)
axes.vlines(xindex[up], rarray_open[up], rarray_close[up], edgecolor='red', linewidth=3.0, label='_nolegend_', alpha=0.5)
if True in down:
axes.vlines(xindex[down], rarray_low[down], rarray_high[down], edgecolor='green', linewidth=0.6, label='_nolegend_', alpha=0.5)
axes.vlines(xindex[down], rarray_open[down], rarray_close[down], edgecolor='green', linewidth=3.0, label='_nolegend_', alpha=0.5)
if True in side:
axes.vlines(xindex[side], rarray_low[side], rarray_high[side], edgecolor='0.7', linewidth=0.6, label='_nolegend_', alpha=0.5)
axes.vlines(xindex[side], rarray_open[side], rarray_close[side], edgecolor='0.7', linewidth=3.0, label='_nolegend_', alpha=0.5)
def plot_simplified(self):
'''
绘制简化行情
'''
xindex= self._xindex
axes= self._Axes
rarray_simple= numpy.array(self._simple)
axes.plot(xindex, rarray_simple, 'o-', color='white', linewidth=0.3, label='simple', \
markersize=0.3, markeredgecolor='white', markeredgewidth=0.1, alpha=0.3) # 简化行情
def plot_average(self):
'''
绘制均线
'''
# 绘制均线部分
#==================================================================================================================================================
quotes= self._pdata[u'行情']
xindex= self._xindex
axes= self._Axes
if self._name == 'pricefs':
widthw= 0.1
widthn= 0.07
mksize= 0.07
mkwidth= 0.1
alpha= 1.0
else:
widthw= 0.2
widthn= 0.1
mksize= 0.3
mkwidth= 0.1
alpha= 1.0
if u'3日均' in quotes:
rarray_3dayave= numpy.array(self._average3)
axes.plot(xindex, rarray_3dayave, 'o-', color='white', linewidth=widthw, label='avg_3', \
markersize=mksize, markeredgecolor='white', markeredgewidth=mkwidth, alpha=alpha) # 3日均线
if u'5日均' in quotes:
rarray_5dayave= numpy.array(self._average5)
axes.plot(xindex, rarray_5dayave, 'o-', color='white', linewidth=widthn, label='avg_5', \
markersize=mksize, markeredgecolor='white', markeredgewidth=mkwidth, alpha=alpha) # 5日均线
if u'10日均' in quotes:
rarray_10dayave= numpy.array(self._average10)
axes.plot(xindex, rarray_10dayave, 'o-', color='yellow', linewidth=widthn, label='avg_10', \
markersize=mksize, markeredgecolor='yellow', markeredgewidth=mkwidth, alpha=alpha) # 10日均线
if u'30日均' in quotes:
rarray_30dayave= numpy.array(self._average30)
axes.plot(xindex, rarray_30dayave, 'o-', color='cyan', linewidth=widthn, label='avg_30', \
markersize=mksize, markeredgecolor='cyan', markeredgewidth=mkwidth, alpha=alpha) # 30日均线
if u'60日均' in quotes:
rarray_60dayave= numpy.array(self._average60)
axes.plot(xindex, rarray_60dayave, 'o-', color='magenta', linewidth=widthn, label='avg_60', \
markersize=mksize, markeredgecolor='magenta', markeredgewidth=mkwidth, alpha=alpha) # 60日均线
def plot_adjustnotes(self):
'''
绘制复权提示
'''
quotes= self._pdata[u'行情']
firstday= self._dates[0]
lastday= self._dates[-1]
ylowlim= self._ylowlim
yhighlim= self._yhighlim
axes= self._Axes
# 使用 annotate() 进行标注。不用了,留作纪念。
#===========================================================================================================================
# adjdict= dict(quotes[u'相对复权']) # key 是 date stringvalue 是相对复权因子float 类型)
# el= Ellipse((2, -1), 0.5, 0.5)
# for idx, dstr in enumerate(self._dates):
# if dstr in adjdict:
# axes.plot([idx, idx], [ylowlim, yhighlim], '-', color='purple', linewidth=0.1)
# axes.annotate(u'复权\n' + str(adjdict[dstr]),
# fontproperties=__font_properties__,
# xy=(idx, yhighlim/1.1), xycoords='data',
# xytext=(10, 5), textcoords='offset points', size=5, verticalalignment="center",
# bbox=dict(boxstyle="round", facecolor='white', edgecolor='purple'),
# arrowprops=dict(arrowstyle="wedge,tail_width=1.",
# facecolor='white', edgecolor='purple',
# patchA=None,
# patchB=el,
# relpos=(0.2, 0.8),
# connectionstyle="arc3,rad=-0.1"),
# alpha=0.5
# )
adjrecs= [rec for rec in quotes[u'相对复权'] if rec[0] >= firstday and rec[0] <= lastday]
if self._name == 'pricefs':
fsize= 3.0
ycoord= yhighlim/1.3
alpha= 1.0
else:
fsize= 5.0
ycoord= yhighlim/1.12
alpha= 1.0
for dstr, afac in adjrecs:
idx= self._dates.index(dstr)
axes.plot([idx, idx], [ylowlim, yhighlim], '-', color='purple', linewidth=0.1)
label= axes.text( \
idx, ycoord, \
u'复权 ' + str(afac) + u'\n' + dstr, \
fontproperties=__font_properties__, \
horizontalalignment='left', \
verticalalignment='top', \
color='purple', \
alpha=alpha
)
label.set_fontsize(fsize)
def plot_capchangenotes(self):
'''
绘制流通股本变更提示
注意两个问题:
1. 流通股本变更提示中的日期可能不是交易日期
2. 流通股本变更提示涵盖个股的所有历史,有些内容可能在绘图目标区间以外
'''
pdata= self._pdata
axes= self._Axes
ylowlim= self._ylowlim
yhighlim= self._yhighlim
firstday= self._dates[0]
lastday= self._dates[-1]
# 把目标区间之外的记录滤掉
changerecs= [rec for rec in pdata[u'公司信息'][u'流通股变更'] if rec[u'变更日期'] >= firstday and rec[u'变更日期'] <= lastday]
# 使用 annotate() 进行标注。不用了,留作纪念。
#===========================================================================================================================
# el= Ellipse((2, -1), 0.5, 0.5)
# for datestr, chrate in changerecs:
# dstr= [ds for ds in self._dates if ds >= datestr][0]
# idx= self._dates.index(dstr)
# axes.plot([idx, idx], [ylowlim, yhighlim], '-', color='yellow', linewidth=0.1)
# axes.annotate(u'流通股\n' + str(chrate),
# fontproperties=__font_properties__,
# xy=(idx, yhighlim/1.1), xycoords='data',
# xytext=(10, 5), textcoords='offset points', size=5, verticalalignment="center",
# bbox=dict(boxstyle="round", facecolor='white', edgecolor='yellow'),
# arrowprops=dict(arrowstyle="wedge,tail_width=1.",
# facecolor='white', edgecolor='yellow',
# patchA=None,
# patchB=el,
# relpos=(0.2, 0.8),
# connectionstyle="arc3,rad=-0.1"),
# alpha=0.5
# )
if self._name == 'pricefs':
fsize= 3.0
ycoord= yhighlim/1.1
alpha= 1.0
else:
fsize= 5.0
ycoord= yhighlim/1.05
alpha= 0.8
for record in changerecs:
datestr= record[u'变更日期']
chrate= record[u'变更比']
dstr= [ds for ds in self._dates if ds >= datestr][0]
idx= self._dates.index(dstr)
axes.plot([idx, idx], [ylowlim, yhighlim], '-', color='yellow', linewidth=0.1)
label= axes.text( \
idx, ycoord, \
u'流通股 ' + str(chrate) + u'\n' + datestr, \
fontproperties=__font_properties__, \
horizontalalignment='left', \
verticalalignment='top', \
color='yellow', \
alpha=alpha
)
label.set_fontsize(fsize)
def plot_candlestick_2(self):
'''
绘制第二条 K 线
'''
xindex= self._xindex
axes= self._Axes_2
up= self._up_2
down= self._down_2
side= self._side_2
# 对开收盘价进行视觉修正
for idx, poc in enumerate( self._zipoc_2 ):
if poc[0] == poc[1] and None not in poc:
self._open_2[idx]= poc[0] - 5 # 稍微偏离一点,使得在图线上不致于完全看不到
self._close_2[idx]= poc[1] + 5
rarray_open= numpy.array(self._open_2)
rarray_close= numpy.array(self._close_2)
rarray_high= numpy.array(self._high_2)
rarray_low= numpy.array(self._low_2)
# XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。
# XXX: 可以使用 alpha 参数调节透明度
if True in up:
axes.vlines(xindex[up], rarray_low[up], rarray_high[up], edgecolor='0.7', linewidth=0.6, label='_nolegend_', alpha=0.5)
axes.vlines(xindex[up], rarray_open[up], rarray_close[up], edgecolor='0.7', linewidth=3.0, label='_nolegend_', alpha=0.5)
if True in down:
axes.vlines(xindex[down], rarray_low[down], rarray_high[down], edgecolor='0.3', linewidth=0.6, label='_nolegend_', alpha=0.5)
axes.vlines(xindex[down], rarray_open[down], rarray_close[down], edgecolor='0.3', linewidth=3.0, label='_nolegend_', alpha=0.5)
if True in side:
axes.vlines(xindex[side], rarray_low[side], rarray_high[side], edgecolor='1.0', linewidth=0.6, label='_nolegend_', alpha=1.0)
axes.vlines(xindex[side], rarray_open[side], rarray_close[side], edgecolor='1.0', linewidth=3.0, label='_nolegend_', alpha=1.0)
def plot_capitalinfo(self):
'''
绘制行情首日和尾日的股本信息
'''
def find_biggestblank(didx):
'''
找出 X 轴某个位置图中最大的空隙
'''
pstart= self._open[0]
ptarget= self._open[didx]
compseq= [yhighlim, ptarget, ylowlim]
if u'开盘二' in quotes:
pstart_2= self._open_2[0]
ptarget_2= self._open_2[didx]
padjust= int(ptarget_2 * pstart / float(pstart_2))
compseq.append(padjust)
compseq.sort(reverse=True) # 图中的三个或四个关键点,高到底排序
diff, hi, low= max([(math.log(compseq[idx]/float(compseq[idx+1]), expbase), compseq[idx], compseq[idx+1]) for idx in range(len(compseq)-1)])
# XXX: for debugging
# print(compseq)
# print([diff, hi, low])
return (hi*low)**0.5 # 相乘再开平方,在 log 坐标系里看起来就是在中间位置。
def repr_int(intnum):
'''
123456789 --> '1,2345,6789'
'''
if type(intnum) not in (int, long): return str(intnum)
if intnum == 0: return '0'
if intnum < 0:
intnum= -intnum
isminus= True
else:
isminus= False
intstr= str(intnum)
intstr= '0'*((4-len(intstr)%4)%4) + intstr
intlist= [intstr[i:i+4] for i in range(0, len(intstr), 4)]
intlist[0]= intlist[0].lstrip('0')
return ('-' + ','.join(intlist)) if isminus else ','.join(intlist)
pdata= self._pdata
quotes= pdata[u'行情']
ylowlim= self._ylowlim
yhighlim= self._yhighlim
length= self._length
expbase= self._expbase
capinfo= pdata[u'公司信息'][u'股本变更记录']
axes= self._Axes
firstday, lastday= self._dates[0], self._dates[-1]
fsize= 5.0 if self._name == 'price' else 3.0
# 首日总股本与流通股信息
#====================================================================================================================================
chunk= [rec for rec in capinfo if rec[u'变更日期'] <= firstday]
if chunk:
allshares= repr_int(chunk[-1][u'总股本'])
circulating= repr_int(chunk[-1][u'流通股'])
else:
allshares= 'None'
circulating= 'None'
infostr= u'总股本: ' + allshares + '\n' + u'流通股: ' + circulating
label= axes.text(0, find_biggestblank(didx=0), infostr, fontproperties=__font_properties__, color='0.7')
label.set_fontsize(fsize)
# label.set_zorder(0) # XXX: 放在底层
# 尾日总股本与流通股信息
#====================================================================================================================================
chunk= [rec for rec in capinfo if rec[u'变更日期'] <= lastday]
if chunk:
allshares= repr_int(chunk[-1][u'总股本'])
circulating= repr_int(chunk[-1][u'流通股'])
else:
allshares= 'None'
circulating= 'None'
infostr= u'总股本: ' + allshares + '\n' + u'流通股: ' + circulating
label= axes.text(length-1, find_biggestblank(didx=length-1), infostr, fontproperties=__font_properties__, horizontalalignment='right', color='0.7')
label.set_fontsize(fsize)
# label.set_zorder(0) # XXX: 放在底层
def plot_usernotes(self):
'''
'''
pdata= self._pdata
quotes= pdata[u'行情']
sidx= self._pdata[u'任务描述'][u'起始偏移']
eidx= self._pdata[u'任务描述'][u'结束偏移']
axes= self._Axes
usernotes= pdata[u'用户标记']
alldates= quotes[u'日期'][sidx:eidx]
lowprices= quotes[u'最低'][sidx:eidx]
expbase= self._expbase
# 绘制短线买点标记
for note in usernotes:
if note[u'类型'] == u'筛选结果':
dstr= note[u'日期']
# 日期不在绘图区间范围内,忽略
if dstr not in alldates:
continue
# 决定箭头的颜色
result= note[u'结果']
color= 'magenta' if result == u'上涨' else 'cyan' if result == u'下跌' else 'white'
# 箭头的起始位置
idx= alldates.index(dstr)
xpos= idx
ypos= lowprices[idx] / expbase
# 箭头的长度
dx= 0.0
dy= ypos * (expbase-1) * 0.9
# 头端的长度
head_length= dy * 0.2
# 绘制箭头
arrow_params={'length_includes_head':True, 'shape':'full', 'head_starts_at_zero':False}
axes.arrow(xpos, ypos, dx, dy, facecolor=color, edgecolor=color, linewidth=0.7, head_width=0.6, head_length=head_length, **arrow_params)
def plot_vlines(self):
xindex= self._xindex
up= self._up
down= self._down
side= self._side
axes= self._Axes
lwidth= 2.4 * self._shrink
# 绘制 K 线部分
#==================================================================================================================================================
rarray_high= numpy.array(self._high)
rarray_low= numpy.array(self._low)
# XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。
# XXX: 可以使用 alpha 参数调节透明度
if True in up:
axes.vlines(xindex[up], rarray_low[up], rarray_high[up], edgecolor='red', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in down:
axes.vlines(xindex[down], rarray_low[down], rarray_high[down], edgecolor='green', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in side:
axes.vlines(xindex[side], rarray_low[side], rarray_high[side], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=0.5)
def plot_vlines_2(self):
xindex= self._xindex
axes= self._Axes_2
up= self._up_2
down= self._down_2
side= self._side_2
lwidth= 2.4 * self._shrink
rarray_high= numpy.array(self._high_2)
rarray_low= numpy.array(self._low_2)
# XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。
# XXX: 可以使用 alpha 参数调节透明度
if True in up:
axes.vlines(xindex[up], rarray_low[up], rarray_high[up], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in down:
axes.vlines(xindex[down], rarray_low[down], rarray_high[down], edgecolor='0.3', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in side:
axes.vlines(xindex[side], rarray_low[side], rarray_high[side], edgecolor='1.0', linewidth=lwidth, label='_nolegend_', alpha=1.0)
def plot_datenotes(self):
'''
内部的日期注释,由派生类定义
'''
pass
def plot_pricenotes(self):
'''
内部的价格注释,由派生类定义
'''
pass
class SubPlot_PriceFullSpan(SubPlot_PriceBase):
'''
'''
def plot(self):
'''
绘图
'''
pdata= self._pdata
# if u'简化' in pdata:
# self.plot_simplified()
# else:
# self.plot_candlestick()
self.plot_vlines()
self.plot_average()
if u'相对复权' in pdata[u'行情']:
self.plot_adjustnotes()
if u'流通股变更' in pdata[u'公司信息']:
self.plot_capchangenotes()
if u'股本变更记录' in pdata[u'公司信息']:
self.plot_capitalinfo()
self.set_xticks()
self.set_yticks()
if u'开盘二' in pdata[u'行情']:
self.plot_vlines_2()
self.set_xticks_2()
self.set_yticks_2()
self.plot_datenotes()
if 'price' in self._parent._subplots:
self.plot_windowspan()
def plot_datenotes(self):
'''
日期在图中间的显示
'''
ylowlim= self._ylowlim
axes= self._Axes
sdindex= self._xparams['sdindex']
mdindex= self._xparams['mdindex']
# 每季度第一个交易日
for ix in sdindex:
newlab= axes.text(ix - (1/self._shrink), ylowlim*1.03, self._dates[ix])
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(4)
newlab.set_rotation('45')
# newlab.set_rotation('vertical')
# newlab.set_horizontalalignment('left')
# newlab.set_verticalalignment('bottom')
# newlab.set_verticalalignment('center')
newlab.set_zorder(0) # XXX: 放在底层
# 每月第一个交易日
for ix in mdindex:
newlab= axes.text(ix - (0.8/self._shrink), ylowlim * 1.03, self._dates[ix])
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(3)
newlab.set_rotation('45')
# newlab.set_rotation('vertical')
# newlab.set_horizontalalignment('left')
# newlab.set_verticalalignment('top') # 不行
# newlab.set_verticalalignment('center')
# newlab.set_verticalalignment('bottom')
newlab.set_zorder(0) # XXX: 放在底层
def plot_windowspan(self):
axes= self._Axes
jobstat= self._pdata[u'任务描述']
sindex, eindex= jobstat[u'起始偏移'], jobstat[u'结束偏移']
hibdry, lobdry= self._parent._subplots['price'].get_ylimits()
xcoord= sindex - 1
width= eindex - sindex + 1
ycoord= lobdry
height= hibdry - lobdry
window= matplotlib.patches.Rectangle((xcoord, ycoord), width, height, fill=False, edgecolor=__color_lightblue__, linewidth=0.3, alpha=0.7)
window.set_zorder(-1) # 放在底层
axes.add_patch(window)
class SubPlot_Price(SubPlot_PriceBase):
'''
'''
def plot(self):
'''
绘图
'''
pdata= self._pdata
# if u'简化' in pdata:
# self.plot_simplified()
# else:
# self.plot_candlestick()
self.plot_candlestick()
self.plot_average()
if u'相对复权' in pdata[u'行情']:
self.plot_adjustnotes()
if u'流通股变更' in pdata[u'公司信息']:
self.plot_capchangenotes()
if u'股本变更记录' in pdata[u'公司信息']:
self.plot_capitalinfo()
if u'用户标记' in pdata:
self.plot_usernotes()
self.set_xticks()
self.set_yticks()
if u'开盘二' in pdata[u'行情']:
self.plot_candlestick_2()
self.set_xticks_2()
self.set_yticks_2()
self.plot_pricenotes()
self.plot_datenotes()
def plot_datenotes(self):
'''
日期在图中间的显示
'''
ylowlim= self._ylowlim
yhighlim= self._yhighlim
axes= self._Axes
mdindex= self._xparams['mdindex']
wdindex= self._xparams['wdindex']
# 每月第一个交易日
for iy in [ylowlim*1.1, yhighlim/1.21]:
for ix in mdindex:
newlab= axes.text(ix-1, iy, self._dates[ix])
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(4)
newlab.set_rotation('vertical')
# newlab.set_horizontalalignment('left')
# newlab.set_verticalalignment('bottom')
# newlab.set_verticalalignment('center')
newlab.set_zorder(0) # XXX: 放在底层
# 每周第一个交易日,根据这个可以推算出全部确切的日期。
# for iy in minorticks[0:-1:6]:
for iy in [ylowlim*1.01, yhighlim/1.09]:
for ix in wdindex:
newlab= axes.text(ix-0.8, iy, self._dates[ix])
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(3)
newlab.set_rotation('vertical')
# newlab.set_horizontalalignment('left')
# newlab.set_verticalalignment('top') # 不行
# newlab.set_verticalalignment('center')
# newlab.set_verticalalignment('bottom')
newlab.set_zorder(0) # XXX: 放在底层
def plot_pricenotes(self):
# 价格数值在图中间的显示
#==================================================================================================================================================
quotes= self._pdata[u'行情']
axes= self._Axes
majorticks= self._ytickset['major']
minorticks= self._ytickset['minor']
mdindex= self._xparams['mdindex']
def price_note(num):
return str(round(num/1000.0, 2))
if u'开盘二' in quotes:
majorticks_2= self._ytickset['major_2']
minorticks_2= self._ytickset['minor_2']
for iy, iy2 in zip(sorted(majorticks[:-1] + minorticks[1:-1]), sorted(majorticks_2[:-1] + minorticks_2[1:-1])):
for ix in mdindex[1:-1:3]:
newlab= axes.text(ix+6, iy*1.001, price_note(iy) + ' / ' + price_note(iy2))
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(3)
newlab.set_zorder(0) # XXX: 放在底层
else:
for iy in sorted(majorticks[:-1] + minorticks[1:-1]):
for ix in mdindex[1:-1:3]:
newlab= axes.text(ix+9, iy*1.001, price_note(iy))
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(3)
newlab.set_zorder(0) # XXX: 放在底层
class SubPlot_TORateBase:
'''
换手率子图
'''
def __init__(self, pdata, parent, xparams, name):
self._name= name
self._pdata= pdata
self._parent= parent
self._xparams= xparams
self._shrink= __shrink__ if name == 'toratefs' else 1.0
self._tostep= 0 # 每一格代表的换手率数值
self._yrange= 0
self._xsize= 0 # int
self._ysize= 0 # int
self._Axes= None
self._AxisX= None
self._AxisY= None
if u'换手率二' in pdata[u'行情']:
self._Axes_2= None
self._AxisX_2= None
self._AxisY_2= None
self._tostep_2= 0
# 绘图数据
quotes= pdata[u'行情']
if name == 'toratefs':
self._dates= quotes[u'日期']
self._open= quotes[u'开盘']
self._close= quotes[u'收盘']
self._high= quotes[u'最高']
self._low= quotes[u'最低']
if u'简化' in quotes: self._simple= quotes[u'简化']
if u'换手率' in quotes: self._torate= quotes[u'换手率']
if u'成交量' in quotes: self._volume= quotes[u'成交量']
if u'成交额' in quotes: self._turnover= quotes[u'成交额']
if u'开盘二' in quotes:
self._open_2= quotes[u'开盘二']
self._close_2= quotes[u'收盘二']
self._high_2= quotes[u'最高二']
self._low_2= quotes[u'最低二']
if u'简化二' in quotes: self._simple_2= quotes[u'简化二']
if u'换手率二' in quotes: self._torate_2= quotes[u'换手率二']
if u'成交量二' in quotes: self._volume_2= quotes[u'成交量二']
if u'成交额二' in quotes: self._turnover_2= quotes[u'成交额二']
else:
sidx, eidx= pdata[u'任务描述'][u'起始偏移'], pdata[u'任务描述'][u'结束偏移']
self._dates= quotes[u'日期'][sidx:eidx]
self._open= quotes[u'开盘'][sidx:eidx]
self._close= quotes[u'收盘'][sidx:eidx]
self._high= quotes[u'最高'][sidx:eidx]
self._low= quotes[u'最低'][sidx:eidx]
if u'简化' in quotes: self._simple= quotes[u'简化'][sidx:eidx]
if u'换手率' in quotes: self._torate= quotes[u'换手率'][sidx:eidx]
if u'成交量' in quotes: self._volume= quotes[u'成交量'][sidx:eidx]
if u'成交额' in quotes: self._turnover= quotes[u'成交额'][sidx:eidx]
if u'开盘二' in quotes:
self._open_2= quotes[u'开盘二'][sidx:eidx]
self._close_2= quotes[u'收盘二'][sidx:eidx]
self._high_2= quotes[u'最高二'][sidx:eidx]
self._low_2= quotes[u'最低二'][sidx:eidx]
if u'简化二' in quotes: self._simple_2= quotes[u'简化二'][sidx:eidx]
if u'换手率二' in quotes: self._torate_2= quotes[u'换手率二'][sidx:eidx]
if u'成交量二' in quotes: self._volume_2= quotes[u'成交量二'][sidx:eidx]
if u'成交额二' in quotes: self._turnover_2= quotes[u'成交额二'][sidx:eidx]
# 衍生数据
#==============================================================================================================
self._length= len(self._dates)
self._xindex= numpy.arange(self._length) # X 轴上的 index一个辅助数据
self._zipoc= zip(self._open, self._close)
self._up= numpy.array( [ True if po < pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内上涨的一个序列
self._down= numpy.array( [ True if po > pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内下跌的一个序列
self._side= numpy.array( [ True if po == pc and po is not None else False for po, pc in self._zipoc] ) # 标示出该天股价日内走平的一个序列
if u'开盘二' in quotes:
self._zipoc_2= zip(self._open_2, self._close_2)
self._up_2= numpy.array( [ True if po < pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内上涨的一个序列
self._down_2= numpy.array( [ True if po > pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内下跌的一个序列
self._side_2= numpy.array( [ True if po == pc and po is not None else False for po, pc in self._zipoc_2] ) # 标示出该天股价日内走平的一个序列
self._compute_size()
def _compute_size(self):
'''
根据 pdata 计算自身尺寸
'''
def _compute_step(maxto):
'''
maxto 是 换手率 最大值。返回每格单位(最小 500, 代表 0.5%)以及格数
'''
for i in range(9):
if maxto > (4 * 500 * (2**i)): # 换手率最大是 100000, 代表 100%
continue
else:
tostep= 500 * (2**i)
tosize= int(round((maxto + tostep/2.0 - 1) / float(tostep), 0))
break
return (tostep, tosize)
quotes= self._pdata[u'行情']
xmargin= self._xparams['xmargin']
self._xsize= (self._length + xmargin*2) * self._shrink
maxto= max(self._torate)
self._tostep, self._yrange= _compute_step(maxto=maxto)
if u'换手率二' in quotes:
maxto_2= max(self._torate_2)
self._tostep_2, yrange_2= _compute_step(maxto=maxto_2)
self._yrange= max(self._yrange, yrange_2) # 成交量部分在 Y 轴所占的 “份数”
self._ysize= self._yrange * self._shrink
def get_size(self):
return (self._xsize, self._ysize)
def build_axes(self, figobj, rect):
# 第一只:添加 Axes 对象
#==================================================================================================================================================
axes= figobj.add_axes(rect, axis_bgcolor='black')
axes.set_axis_bgcolor('black')
axes.set_axisbelow(True) # 网格线放在底层
# 第一只:改变坐标线的颜色
#==================================================================================================================================================
for child in axes.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color(__color_gold__)
# child.set_zorder(3) # XXX: 放在上层,好像没什么用。
# 得到 X 轴 和 Y 轴 的两个 Axis 对象
#==================================================================================================================================================
xaxis= axes.get_xaxis()
yaxis= axes.get_yaxis()
# 设置两个坐标轴上的 grid
#==================================================================================================================================================
xaxis.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
xaxis.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
yaxis.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
yaxis.grid(True, 'minor', color='0.3', linestyle='solid', linewidth=0.1)
self._Axes= axes
self._AxisX= xaxis
self._AxisY= yaxis
if u'换手率二' in self._pdata[u'行情']:
# 添加 Axes 对象
#==================================================================================================================================================
axes_2= axes.twinx()
# XXX: 下面这三行把第一个 axes 放在上面,这样不会被第二个 axes 的图形遮盖。用 zorder 不顶用。
axes.figure.axes[-2:]= [axes_2, axes] # XXX: 把第一个 axes 放在上面,用 zorder 不顶用。
axes.set_frame_on(False) # 如果不做此设定axes_2 的内容会看不见
axes_2.set_frame_on(True)
axes_2.set_axis_bgcolor('black')
axes_2.set_axisbelow(True) # 网格线放在底层
# 改变坐标线的颜色
#==================================================================================================================================================
for child in axes_2.get_children():
if isinstance(child, matplotlib.spines.Spine):
child.set_color(__color_gold__)
# 得到 X 轴 和 Y 轴 的两个 Axis 对象
#==================================================================================================================================================
xaxis_2= axes_2.get_xaxis()
yaxis_2= axes_2.get_yaxis()
# 设置网格线
#==================================================================================================================================================
# xaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
# xaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
# yaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
# yaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
self._Axes_2= axes_2
self._AxisX_2= xaxis_2
self._AxisY_2= yaxis_2
def get_axes(self):
return self._Axes
def plot(self):
'''
绘制换手率图形
'''
self.plot_torate()
self.set_xticks()
self.set_yticks()
if u'换手率二' in self._pdata[u'行情']:
self.plot_torate_2()
self.set_xticks_2()
self.set_yticks_2()
def plot_torate(self):
'''
绘制换手率
'''
xindex= self._xindex
stopset= self._xparams['mdindex'] if self._name == 'torate' else self._xparams['sdindex']
axes= self._Axes
up= self._up
down= self._down
side= self._side
rarray_to= numpy.array(self._torate)
tozeros= numpy.zeros(self._length) # 辅助数据
lwidth= 3.0 if self._name == 'torate' else 2.4 * self._shrink
# XXX: 如果 up/down/side 各项全部为 False那么 vlines() 会报错。
if True in up:
axes.vlines(xindex[up], tozeros[up], rarray_to[up], edgecolor='red', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in down:
axes.vlines(xindex[down], tozeros[down], rarray_to[down], edgecolor='green', linewidth=lwidth, label='_nolegend_', alpha=0.5)
if True in side:
axes.vlines(xindex[side], tozeros[side], rarray_to[side], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=0.5)
# 绘制平均换手率(直线)
toeffect= [num for num in self._torate if num is not None]
toaverage= sum(toeffect) / float(len(toeffect))
axes.plot([-1, self._length], [toaverage, toaverage], '-', color='yellow', linewidth=0.2, alpha=0.7)
# 换手率数值在图中间的显示
#==================================================================================================================================================
for ix in stopset[2:-1:3]:
newlab= axes.text(ix+8, toaverage, str(round(toaverage/1000.0, 2)) + '%')
newlab.set_font_properties(__font_properties__)
newlab.set_color('yellow')
newlab.set_fontsize(3)
# newlab.set_zorder(0) # XXX: 放在底层
# newlab.set_verticalalignment('center')
def plot_torate_2(self):
'''
绘制第二条换手率柱状图
'''
quotes= self._pdata[u'行情']
xindex= self._xindex
axes= self._Axes_2
up= self._up_2
down= self._down_2
side= self._side_2
rarray_to= numpy.array(self._torate_2)
tozeros= numpy.zeros(self._length) # 辅助数据
lwidth, alpha= (0.7, 0.5) if self._name == 'torate' else (0.3, 0.7)
# XXX: 如果 up/down/side 各项全部为 False那么 vlines() 会报错。
if True in up:
axes.vlines(xindex[up], tozeros[up], rarray_to[up], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=alpha)
if True in down:
axes.vlines(xindex[down], tozeros[down], rarray_to[down], edgecolor='0.3', linewidth=lwidth, label='_nolegend_', alpha=alpha)
if True in side:
axes.vlines(xindex[side], tozeros[side], rarray_to[side], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=1.0)
def set_xticks(self):
'''
X 轴坐标
'''
length= self._length
xmargin= self._xparams['xmargin']
axes= self._Axes
xaxis= self._AxisX
# xaxis.set_tick_params(which='both', direction='out') # XXX: 坐标点设到外面去,也可以用 Axes.tick_params(),好像 matplotlib 1.0.1 才有
# 设定 X 轴坐标的范围
#==================================================================================================================================================
axes.set_xlim(-xmargin, length + xmargin)
xMajorLocator= self._xparams['xMajorLocator']
xMinorLocator= self._xparams['xMinorLocator']
xMajorFormatter= self._xparams['xMajorFormatter']
xMinorFormatter= self._xparams['xMinorFormatter']
# 设定 X 轴的 Locator 和 Formatter
xaxis.set_major_locator(xMajorLocator)
xaxis.set_minor_locator(xMinorLocator)
if self._name == 'torate':
xaxis.set_major_formatter(xMajorFormatter)
xaxis.set_minor_formatter(xMinorFormatter)
# 设定 X 轴主要坐标点与辅助坐标点的样式
for mal in axes.get_xticklabels(minor=False):
mal.set_fontsize(4)
mal.set_horizontalalignment('right')
mal.set_rotation('45')
for mil in axes.get_xticklabels(minor=True):
mil.set_fontsize(4)
mil.set_color('blue')
mil.set_horizontalalignment('right')
mil.set_rotation('45')
else:
# 设为不可见
for mal in axes.get_xticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_xticklabels(minor=True):
mil.set_visible(False)
def set_xticks_2(self):
length= self._length
xmargin= self._xparams['xmargin']
axes= self._Axes_2
xaxis= self._AxisX_2
# xaxis.set_tick_params(which='both', direction='out') # XXX: 坐标点设到外面去,也可以用 Axes.tick_params(),好像 matplotlib 1.0.1 才有
# 设定 X 轴坐标的范围
#==================================================================================================================================================
axes.set_xlim(-xmargin, length + xmargin)
xMajorLocator= self._xparams['xMajorLocator']
xMinorLocator= self._xparams['xMinorLocator']
# 设定 X 轴的 Locator 和 Formatter
xaxis.set_major_locator(xMajorLocator)
xaxis.set_minor_locator(xMinorLocator)
# 设为不可见
for mal in axes.get_xticklabels(minor=False):
mal.set_visible(False)
for mil in axes.get_xticklabels(minor=True):
mil.set_visible(False)
def set_yticks(self):
'''
设置 Y 轴坐标
'''
axes= self._Axes
yaxis= self._AxisY
tostep= self._tostep
yrange= self._yrange
stopset= self._xparams['mdindex'] if self._name == 'torate' else self._xparams['sdindex']
# 设定换手率 Y 轴坐标的范围
#==================================================================================================================================================
axes.set_ylim(0, tostep*yrange)
# 主要坐标点
#==================================================================================================================================================
majorticks= [tostep*i for i in range(yrange)]
yMajorLocator= FixedLocator(numpy.array(majorticks))
# 确定 Y 轴的 MajorFormatter
def y_major_formatter(num, pos=None):
return str(round(num/1000.0, 2)) + '%'
yMajorFormatter= FuncFormatter(y_major_formatter)
# 确定 Y 轴的 MinorFormatter
yMinorFormatter= NullFormatter()
# 第一只:设定 X 轴的 Locator 和 Formatter
yaxis.set_major_locator(yMajorLocator)
yaxis.set_major_formatter(yMajorFormatter)
# 设定 Y 轴主要坐标点的样式
for mal in axes.get_yticklabels(minor=False):
mal.set_font_properties(__font_properties__)
mal.set_fontsize(5) # 这个必须放在前一句后面,否则作用会被覆盖
# 辅助坐标点
#==================================================================================================================================================
if self._name == 'torate':
minorticks= list( itertools.chain.from_iterable( mi for mi in [[ma + (tostep/4.0)*i for i in range(1, 4)] for ma in majorticks] ) )
yMinorLocator= FixedLocator(numpy.array(minorticks))
yaxis.set_minor_locator(yMinorLocator)
def y_minor_formatter(num, pos=None):
return str(round(num/1000.0, 3)) + '%'
yMinorFormatter= FuncFormatter(y_minor_formatter)
yaxis.set_minor_formatter(yMinorFormatter)
# 设定 Y 轴主要坐标点的样式
for mil in axes.get_yticklabels(minor=True):
mil.set_font_properties(__font_properties__)
mil.set_fontsize(4) # 这个必须放在前一句后面,否则作用会被覆盖
else:
# minorticks= list( itertools.chain.from_iterable( mi for mi in [[ma + (tostep/4.0)*i for i in range(1, 4)] for ma in majorticks] ) )
minorticks= list( [ma + (tostep/2.0) for ma in majorticks] )
yMinorLocator= FixedLocator(numpy.array(minorticks))
yaxis.set_minor_locator(yMinorLocator)
# 设定 Y 轴主要坐标点的样式
for mil in axes.get_yticklabels(minor=True):
mil.set_visible(False)
# 换手率数值在图中间的显示
#==================================================================================================================================================
for iy in range(int(tostep/2.0), tostep*yrange, int(tostep/2.0)):
for ix in stopset[1:-1:3]:
newlab= axes.text(ix+8, iy, y_major_formatter(iy))
newlab.set_font_properties(__font_properties__)
newlab.set_color('0.3')
newlab.set_fontsize(3)
newlab.set_zorder(0) # XXX: 放在底层
# newlab.set_verticalalignment('center')
def set_yticks_2(self):
'''
设置 Y 轴坐标
'''
axes= self._Axes_2
yaxis= self._AxisY_2
tostep= self._tostep_2
yrange= self._yrange # 与 1 是一样的
# 设定换手率 Y 轴坐标的范围
#==================================================================================================================================================
axes.set_ylim(0, tostep*yrange)
# 主要坐标点
#==================================================================================================================================================
majorticks= [tostep*i for i in range(yrange)]
yMajorLocator= FixedLocator(numpy.array(majorticks))
# 确定 Y 轴的 MajorFormatter
def y_major_formatter(num, pos=None):
return str(round(num/1000.0, 2)) + '%'
yMajorFormatter= FuncFormatter(y_major_formatter)
# 确定 Y 轴的 MinorFormatter
yMinorFormatter= NullFormatter()
# 第一只:设定 X 轴的 Locator 和 Formatter
yaxis.set_major_locator(yMajorLocator)
yaxis.set_major_formatter(yMajorFormatter)
# 设定 Y 轴主要坐标点的样式
for mal in axes.get_yticklabels(minor=False):
mal.set_font_properties(__font_properties__)
mal.set_fontsize(5) # 这个必须放在前一句后面,否则作用会被覆盖
# 辅助坐标点
#==================================================================================================================================================
if self._name == 'torate':
minorticks= list( itertools.chain.from_iterable( mi for mi in [[ma + (tostep/4.0)*i for i in range(1, 4)] for ma in majorticks] ) )
yMinorLocator= FixedLocator(numpy.array(minorticks))
def y_minor_formatter(num, pos=None):
return str(round(num/1000.0, 3)) + '%'
yMinorFormatter= FuncFormatter(y_minor_formatter)
yaxis.set_minor_locator(yMinorLocator)
yaxis.set_minor_formatter(yMinorFormatter)
# 设定 Y 轴主要坐标点的样式
for mil in axes.get_yticklabels(minor=True):
mil.set_font_properties(__font_properties__)
mil.set_fontsize(4) # 这个必须放在前一句后面,否则作用会被覆盖
else:
minorticks= list( [ma + (tostep/2.0) for ma in majorticks] )
yMinorLocator= FixedLocator(numpy.array(minorticks))
yaxis.set_minor_locator(yMinorLocator)
# 设定 Y 轴主要坐标点的样式
for mil in axes.get_yticklabels(minor=True):
mil.set_visible(False)
class SubPlot_TORate(SubPlot_TORateBase):
pass
class SubPlot_TORateFullSpan(SubPlot_TORateBase):
pass
class MyFigure:
'''
'''
def __init__(self, pdata):
self._pdata= pdata # 绘图数据
self._figfacecolor= __color_pink__
self._figedgecolor= __color_navy__
self._figdpi= 300
self._figlinewidth= 1.0
self._xfactor= 10.0 / 230.0 # x size * x factor = x length
self._yfactor= 0.3 # y size * y factor = y length
jobstat= pdata[u'任务描述']
self._xsize_left= 12.0 # left blank
self._xsize_right= 12.0 # right blank
self._ysize_top= 0.3 # top blank
self._ysize_bottom= 1.2 # bottom blank
self._ysize_gap1= 0.2
self._ysize_gap2= 0.3 if (jobstat[u'历史价格子图'] or jobstat[u'历史换手率子图'] or jobstat[u'财务指标子图']) else 0.0
# 建立 X 轴参数
#===============================================================================================================
if jobstat[u'价格子图'] or jobstat[u'换手率子图']:
xparams= {'xmargin': 1}
xparams.update(self._compute_xparams()) # 与 X 轴坐标点相关的数据结构
if jobstat[u'历史价格子图'] or jobstat[u'历史换手率子图'] or jobstat[u'财务指标子图']:
xparams_fs= {'xmargin': 3}
xparams_fs.update(self._compute_xparams_fullspan())
# 建立子图对象
#===============================================================================================================
self._subplots= {}
if jobstat[u'公司信息子图']:
name= 'basic'
self._subplots[name]= SubPlot_BasicInfo(pdata=pdata, parent=self, name=name)
if jobstat[u'历史价格子图']: # XXX: 这个要放在 价格子图 前面,因为后者可能会用到它的 Y 轴坐标点位置
name= 'pricefs'
self._subplots[name]= SubPlot_PriceFullSpan(pdata=pdata, parent=self, xparams=xparams_fs, name=name)
if jobstat[u'价格子图']:
name= 'price'
self._subplots[name]= SubPlot_Price(pdata=pdata, parent=self, xparams=xparams, name=name)
if jobstat[u'财务指标子图']:
name= 'financial'
self._subplots[name]= SubPlot_Financial(pdata=pdata, parent=self, xparams=xparams_fs, name=name)
if jobstat[u'换手率子图']:
name= 'torate'
self._subplots[name]= SubPlot_TORate(pdata=pdata, parent=self, xparams=xparams, name=name)
if jobstat[u'历史换手率子图']:
name= 'toratefs'
self._subplots[name]= SubPlot_TORateFullSpan(pdata=pdata, parent=self, xparams=xparams_fs, name=name)
# 根据子图对象的尺寸计算自身的尺寸
#===============================================================================================================
self._xsize, \
self._ysize= self._compute_size()
self._xlength= self._xsize * self._xfactor
self._ylength= self._ysize * self._yfactor
# 根据计算出的尺寸建立 Figure 对象
#===============================================================================================================
self._Fig= pyplot.figure(figsize=(self._xlength, self._ylength), dpi=self._figdpi, facecolor=self._figfacecolor, \
edgecolor=self._figedgecolor, linewidth=self._figlinewidth) # Figure 对象
# 用新建立的 Figure 对象交给子图对象,完成子图对象的初始化
#===============================================================================================================
rects= self._compute_rect()
if 'basic' in self._subplots:
self._subplots['basic'].build_axes(figobj=self._Fig, rect=rects['basic'])
# XXX: 这个要放在 price 前面,因为后者要用到它的 Axes 对象
if 'torate' in self._subplots:
self._subplots['torate'].build_axes(figobj=self._Fig, rect=rects['torate'])
if 'price' in self._subplots:
self._subplots['price'].build_axes(figobj=self._Fig, rect=rects['price'])
# XXX: 这个要放在 pricefs 前面
if 'toratefs' in self._subplots:
self._subplots['toratefs'].build_axes(figobj=self._Fig, rect=rects['toratefs'])
if 'pricefs' in self._subplots:
self._subplots['pricefs'].build_axes(figobj=self._Fig, rect=rects['pricefs'])
def _compute_size(self):
'''
根据子图的尺寸计算自身尺寸
'''
pdata= self._pdata
jobstat= pdata[u'任务描述']
x_left, x_right= self._xsize_left, self._xsize_right
y_top, y_bottom= self._ysize_top, self._ysize_bottom
y_gap1= self._ysize_gap1
y_gap2= self._ysize_gap2
x_basic, y_basic= self._subplots['basic'].get_size() if 'basic' in self._subplots else (0.0, 0.0)
x_price, y_price= self._subplots['price'].get_size() if 'price' in self._subplots else (0.0, 0.0)
x_pricefs, y_pricefs= self._subplots['pricefs'].get_size() if 'pricefs' in self._subplots else (0.0, 0.0)
x_torate, y_torate= self._subplots['torate'].get_size() if 'torate' in self._subplots else (0.0, 0.0)
x_toratefs, y_toratefs= self._subplots['toratefs'].get_size() if 'toratefs' in self._subplots else (0.0, 0.0)
x_financial, y_financial= self._subplots['financial'].get_size() if 'financial' in self._subplots else (0.0, 0.0)
x_all= x_left + max(x_price, x_basic, x_pricefs) + x_right
y_all= y_top + y_basic + y_gap1 + y_pricefs + y_toratefs + y_financial + y_gap2 + y_price + y_torate + y_bottom
return (x_all, y_all)
def get_sizeset(self):
sizeset= {
'x': self._xsize,
'y': self._ysize,
'top': self._ysize_top,
'bottom': self._ysize_bottom,
'left': self._xsize_left,
'right': self._xsize_right
}
return sizeset
def _compute_rect(self):
'''
'''
pdata= self._pdata
jobstat= pdata[u'任务描述']
x_left= self._xsize_left
x_right= self._xsize_right
y_top= self._ysize_top
y_bottom= self._ysize_bottom
x_all= self._xsize
y_all= self._ysize
y_gap1= self._ysize_gap1 # basic 与 financial 之间的空隙
y_gap2= self._ysize_gap2 # toratefs 与 price 之间的空隙
x_basic, y_basic= self._subplots['basic'].get_size() if 'basic' in self._subplots else (0.0, 0.0)
x_price, y_price= self._subplots['price'].get_size() if 'price' in self._subplots else (0.0, 0.0)
x_pricefs, y_pricefs= self._subplots['pricefs'].get_size() if 'pricefs' in self._subplots else (0.0, 0.0)
x_torate, y_torate= self._subplots['torate'].get_size() if 'torate' in self._subplots else (0.0, 0.0)
x_toratefs, y_toratefs= self._subplots['toratefs'].get_size() if 'toratefs' in self._subplots else (0.0, 0.0)
x_financial, y_financial= self._subplots['financial'].get_size() if 'financial' in self._subplots else (0.0, 0.0)
rects= {}
if 'basic' in self._subplots:
rect= ((x_left + (x_all-x_left-x_right-x_basic)/2) / x_all, (y_all - y_top - y_basic)/y_all, x_basic/x_all, y_basic/y_all) # K线图部分
rects['basic']= rect
if 'price' in self._subplots:
rect= ((x_left + (x_all-x_left-x_right-x_price)/2) / x_all, (y_bottom + y_torate)/y_all, x_price/x_all, y_price/y_all) # K线图部分
rects['price']= rect
if 'torate' in self._subplots:
rect= ((x_left + (x_all-x_left-x_right-x_torate)/2)/x_all, y_bottom/y_all, x_torate/x_all, y_torate/y_all) # 成交量部分
rects['torate']= rect
if 'pricefs' in self._subplots:
rect= ((x_left + (x_all-x_left-x_right-x_pricefs)/2)/x_all, (y_all - y_top - y_basic - y_gap1 - y_pricefs)/y_all, x_pricefs/x_all, y_pricefs/y_all)
rects['pricefs']= rect
if 'toratefs' in self._subplots:
rect= ((x_left + (x_all-x_left-x_right-x_toratefs)/2)/x_all, (y_bottom + y_torate + y_price + y_gap2)/y_all, x_toratefs/x_all, y_toratefs/y_all)
rects['toratefs']= rect
return rects
def _compute_xparams(self):
'''
主要坐标点是每月第一个交易日,辅助坐标点是每周第一个交易日
'''
quotes= self._pdata[u'行情']
sidx= self._pdata[u'任务描述'][u'起始偏移']
eidx= self._pdata[u'任务描述'][u'结束偏移']
# 设定 X 轴上的坐标
#==================================================================================================================================================
datelist= [ datetime.date(int(ys), int(ms), int(ds)) for ys, ms, ds in [ dstr.split('-') for dstr in quotes[u'日期'][sidx:eidx] ] ]
# 确定 X 轴的 MajorLocator
mdindex= [] # 每个月第一个交易日在所有日期列表中的 index
allyears= set([d.year for d in datelist]) # 所有的交易年份
for yr in sorted(allyears):
allmonths= set([d.month for d in datelist if d.year == yr]) # 当年所有的交易月份
for mon in sorted(allmonths):
monthday= min([dt for dt in datelist if dt.year==yr and dt.month==mon]) # 当月的第一个交易日
mdindex.append(datelist.index(monthday))
xMajorLocator= FixedLocator(numpy.array(mdindex))
# 确定 X 轴的 MinorLocator
wdindex= {} # value: 每周第一个交易日在所有日期列表中的 index; key: 当周的序号 week number当周是第几周
for d in datelist:
isoyear, weekno= d.isocalendar()[0:2]
dmark= isoyear*100 + weekno
if dmark not in wdindex:
wdindex[dmark]= datelist.index(d)
wdindex= sorted(wdindex.values())
xMinorLocator= FixedLocator(numpy.array(wdindex))
# 确定 X 轴的 MajorFormatter 和 MinorFormatter
def x_major_formatter(idx, pos=None):
return datelist[idx].strftime('%Y-%m-%d')
def x_minor_formatter(idx, pos=None):
return datelist[idx].strftime('%m-%d')
xMajorFormatter= FuncFormatter(x_major_formatter)
xMinorFormatter= FuncFormatter(x_minor_formatter)
return {'xMajorLocator': xMajorLocator,
'xMinorLocator': xMinorLocator,
'xMajorFormatter': xMajorFormatter,
'xMinorFormatter': xMinorFormatter,
'mdindex': mdindex,
'wdindex': wdindex
}
def _compute_xparams_fullspan(self):
'''
主要坐标点是每季第一个交易日,辅助坐标点是每月第一个交易日。是给宏观子图用的。
'''
quotes= self._pdata[u'行情']
datelist= [ datetime.date(int(ys), int(ms), int(ds)) for ys, ms, ds in [ dstr.split('-') for dstr in quotes[u'日期'] ] ]
# 确定 X 轴的 MinorLocator
mdindex= [] # 每个月第一个交易日在所有日期列表中的 index
sdindex= [] # 每季度第一个交易日在所有日期列表中的 index
ydindex= [] # 每年第一个交易日在所有日期列表中的 index
allyears= set([d.year for d in datelist]) # 所有的交易年份
for yr in sorted(allyears):
allmonths= set([d.month for d in datelist if d.year == yr]) # 当年所有的交易月份
for mon in sorted(allmonths):
monthday= min([dt for dt in datelist if dt.year==yr and dt.month==mon]) # 当月的第一个交易日
idx= datelist.index(monthday)
if mon in (1, 4, 7, 10):
sdindex.append(idx)
if mon == 1:
ydindex.append(idx)
else:
mdindex.append(idx)
xMajorLocator= FixedLocator(numpy.array(sdindex))
xMinorLocator= FixedLocator(numpy.array(mdindex))
# 确定 X 轴的 MajorFormatter 和 MinorFormatter
def x_major_formatter(idx, pos=None):
return datelist[idx].strftime('%Y-%m-%d')
def x_minor_formatter(idx, pos=None):
return datelist[idx].strftime('%m-%d')
xMajorFormatter= FuncFormatter(x_major_formatter)
xMinorFormatter= FuncFormatter(x_minor_formatter)
return {'xMajorLocator': xMajorLocator,
'xMinorLocator': xMinorLocator,
'xMajorFormatter': xMajorFormatter,
'xMinorFormatter': xMinorFormatter,
'sdindex': sdindex,
'mdindex': mdindex,
'ydindex': ydindex
}
def plot(self):
'''
'''
# self.plot_title()
# 调用子图对象的绘图函数
if 'basic' in self._subplots:
self._subplots['basic'].plot()
if 'price' in self._subplots:
self._subplots['price'].plot()
if 'torate' in self._subplots:
self._subplots['torate'].plot()
if 'pricefs' in self._subplots:
self._subplots['pricefs'].plot()
if 'toratefs' in self._subplots:
self._subplots['toratefs'].plot()
def plot_title(self):
'''
绘制整个 Figure 的标题
'''
info= self._pdata[u'公司信息']
figobj= self._Fig
# 整个 figure 的标题
subtitle= (info[u'代码'] + ' ' if u'代码' in info else '') + info[u'简称']
subtitle_2= (info[u'代码二'] + ' ' if u'代码二' in info else '') + info[u'简称二']
figobj.suptitle(subtitle + ' / ' + subtitle_2, fontsize=12, fontproperties=__font_properties__)
def savefig(self, figpath):
'''
保存图片
'''
self._Fig.savefig(figpath, dpi=self._figdpi, facecolor=self._figfacecolor, edgecolor=self._figedgecolor, linewidth=self._figlinewidth)
if __name__ == '__main__':
# pfile 指明存放绘图数据的 pickle filefigpath 指定图片需存放的路径
pfile= sys.argv[1]
figpath= sys.argv[2]
# 绘图数据 pdata
fileobj= open(name=pfile, mode='rb')
pdata= pickle.load(fileobj)
fileobj.close()
os.remove(pfile)
myfig= MyFigure(pdata=pdata)
myfig.plot()
myfig.savefig(figpath=figpath)