diff --git a/vn.training/Public/Public.py b/vn.training/Public/Public.py new file mode 100644 index 00000000..cf19b820 --- /dev/null +++ b/vn.training/Public/Public.py @@ -0,0 +1,480 @@ +# -*- coding: utf-8 -*- + + + +import os +import copy +import itertools +import math +import datetime +import logging +import logging.handlers + + + + + +class IndentLogger: + ''' + + ''' + + def __init__(self, logger, indent): + self._logger= logger + self._indent= indent + + + + def indent_levelup(self, level=1): + self._indent= self._indent - level + + def indent_leveldown(self, level=1): + self._indent= self._indent + level + + def set_indent_level(self, level): + self._indent= level + + #--------------------------------------------------------------- + + def set_critical(self): + self._logger.setLevel(logging.CRITICAL) + + def set_error(self): + self._logger.setLevel(logging.ERROR) + + def set_warning(self): + self._logger.setLevel(logging.WARNING) + + def set_info(self): + self._logger.setLevel(logging.INFO) + + def set_debug(self): + self._logger.setLevel(logging.DEBUG) + + def set_notset(self): + self._logger.setLevel(logging.NOTSET) + + #--------------------------------------------------------------- + + def critical(self, message): + self._logger.critical('\t' * self._indent + message) + + def error(self, message): + self._logger.error('\t' * self._indent + message) + + def warning(self, message): + self._logger.warning('\t' * self._indent + message) + + def info(self, message): + self._logger.info('\t' * self._indent + message) + + def debug(self, message): + self._logger.debug('\t' * self._indent + message) + + def noset(self, message): + self._logger.noset('\t' * self._indent + message) + + + +def TempLogger(loggername, filename=None, taskdir=None): + ''' + + ''' + + if not taskdir: + taskdir= __dir_tmpfiles__ + + if not os.path.exists(taskdir): + os.mkdir(taskdir, 0o700) + + if not filename: + timestamp= datetime.datetime.now() + filename= os.path.join(taskdir, loggername + '_' + timestamp.strftime('%Y-%m-%d_%H:%M:%S,%f')) + else: + filename= os.path.join(taskdir, filename) + + if not os.path.exists(filename): + os.mknod(filename, 0o700) + + myformatstr= "%(asctime)s %(levelname)-9s>> %(message)s" + myformatter= logging.Formatter(myformatstr) + + myhandler= logging.handlers.RotatingFileHandler(filename=filename, mode='a', encoding='utf-8') + myhandler.setFormatter(myformatter) + + mylogger= logging.getLogger(name=loggername) + mylogger.setLevel(level=logging.DEBUG) + mylogger.addHandler(myhandler) + + ilogger= IndentLogger(logger=mylogger, indent=0) + return ilogger + + + +def 计算个股换手率(个股行情, 个股股本变更记录): + ''' + + ''' + 个股股本变更列表= [rec for rec in 个股股本变更记录 if rec['流通股'] != 0 and rec['变更日期'] <= 个股行情['日期'][-1]] + + 个股股本变更字典= {} + for rec in 个股股本变更列表: + if rec['变更日期'] in 个股行情['日期']: + 个股股本变更字典[rec['变更日期']]= rec + else: + 个股股本变更字典[ [ds for ds in 个股行情['日期'] if ds > rec['变更日期']][0] ]= rec + + 当前流通股= 个股股本变更字典[min(个股股本变更字典.keys())]['流通股'] + + 换手率= [] + for ds, vol in zip(个股行情['日期'], 个股行情['成交量']): + if ds in 个股股本变更字典: + 当前流通股= 个股股本变更字典[ds]['流通股'] + 换手率.append( vol*100000/当前流通股 ) + 个股行情['换手率']= 换手率 + + + +def 计算复权行情(个股行情, 均线参数=None): + ''' + + ''' + 日期= 个股行情['日期'] + + 复权开盘= copy.copy(个股行情['开盘']) + 复权最高= copy.copy(个股行情['最高']) + 复权收盘= copy.copy(个股行情['收盘']) + 复权最低= copy.copy(个股行情['最低']) + 复权开收中= copy.copy(个股行情['开收中']) + + 复权记录= [] + + sidx= 1 + done= False + while not done: + done= True + + for idx, date in enumerate(日期[sidx:], start=sidx): + 涨幅= (复权开盘[idx] - 复权收盘[idx-1]) / 复权收盘[idx-1] + if 涨幅 <= -0.12: + 复权因子= round(复权收盘[idx-1]/复权开盘[idx], 2) + 调整因子= round(复权因子, 1) + if abs(round(复权因子-调整因子, 2)) <= 0.01: + 复权因子= 调整因子 + 复权开盘[:idx]= [nr/复权因子 for nr in 复权开盘[:idx]] + 复权最高[:idx]= [nr/复权因子 for nr in 复权最高[:idx]] + 复权收盘[:idx]= [nr/复权因子 for nr in 复权收盘[:idx]] + 复权最低[:idx]= [nr/复权因子 for nr in 复权最低[:idx]] + 复权开收中[:idx]= [nr/复权因子 for nr in 复权开收中[:idx]] + + 复权记录.append( (date, 复权因子) ) + sidx= idx + done= False + break + + 复权行情= {} + 复权行情['复权记录']= 复权记录 + + 复权行情['日期']= copy.copy(日期) + 复权行情['开盘']= 复权开盘 + 复权行情['最高']= 复权最高 + 复权行情['收盘']= 复权收盘 + 复权行情['最低']= 复权最低 + 复权行情['开收中']= 复权开收中 + if 均线参数: + 复权行情['均线集']= { n : 计算序列加权均线(复权开盘, 复权最高, 复权收盘, 复权最低, n) for n in 均线参数 } + + return 复权行情 + + + +def 计算序列加权均线(开盘序列, 最高序列, 收盘序列, 最低序列, n): + ''' + + ''' + length= len(开盘序列) + if length < n: + return [None] * length + + sumhilo= sum(最高序列[:n]) + sum(最低序列[:n]) + sumopen= sum(开盘序列[:n]) + sumclose= sum(收盘序列[:n]) + + 输出序列= [ ((sumhilo / 2 + sumopen) / 2 + sumclose) / (2*n) ] + + for idx in range(n, length): + sumhilo= sumhilo - 最高序列[idx-n] - 最低序列[idx-n] + 最高序列[idx] + 最低序列[idx] + sumopen= sumopen - 开盘序列[idx-n] + 开盘序列[idx] + sumclose= sumclose - 收盘序列[idx-n] + 收盘序列[idx] + 输出序列.append( ((sumhilo / 2 + sumopen) / 2 + sumclose) / (2*n) ) + + return [None] * (n-1) + 输出序列 + + + +def 补全个股行情(完整日期, 个股行情): + ''' + + ''' + 代码= 个股行情.pop('代码') if '代码' in 个股行情 else None + 日期= 个股行情.pop('日期') + + for idx, dstr in enumerate(完整日期): + if dstr not in 日期: + 日期.insert(idx, dstr) + for seq in 个股行情.values(): + seq.insert(idx, None) + + if 代码: + 个股行情['代码']= 代码 + 个股行情['日期']= 日期 + + + +def 计算个股行情衍生数据(ilogger, 个股行情, 均线参数=None): + ''' + + ''' + length= len(个股行情['开盘']) + + 开盘= 个股行情['开盘'] + 最高= 个股行情['最高'] + 收盘= 个股行情['收盘'] + 最低= 个股行情['最低'] + + if 均线参数 and '均线集' not in 个股行情: + 个股行情['均线集']= {n : 计算序列加权均线(开盘, 最高, 收盘, 最低, n) for n in 均线参数} + if '均线集' in 个股行情: + 个股行情['均线走势标记集']= {n : [None]*(n-1)+计算走势标记(序列=序列[n-1:]) if length>=n else [None]*length for n, 序列 in 个股行情['均线集'].items()} + + 开收中= 个股行情['开收中'] + + 开收中线走势标记= 计算走势标记(序列=开收中) + + 个股行情['开收中线走势标记']= 开收中线走势标记 + + 最小长度= 个股行情.pop('目标偏移') if '目标偏移' in 个股行情 else 0 + 截去行情头部无效片断(行情数据=个股行情, 最小长度=最小长度) + + 开收中线走势拐点= 计算走势拐点(目标序列=开收中, 走势标记=开收中线走势标记) + 个股行情['开收中线走势拐点']= 开收中线走势拐点 + + + +def 截去行情头部无效片断(行情数据, 最小长度): + ''' + + ''' + length= len(行情数据['开盘']) + + dirlists= [seq for seq in 行情数据.values() if (type(seq) is list) and (len(seq)==length)] + subkeys= ('均线集', '均线走势标记集') + sublists= [行情数据[key].values() for key in subkeys if key in 行情数据] + + cntlist= [seq.count(None) for seq in dirlists] + itor= itertools.chain.from_iterable(seq for seq in sublists) + cntlist.extend( [seq.count(None) for seq in itor] ) + 截去长度= max(最小长度, max(cntlist)) + + for seq in dirlists: + del seq[:截去长度] + itor= itertools.chain.from_iterable(seq for seq in sublists) + for seq in itor: + del seq[:截去长度] + + + +def 计算均值(序列): + ''' + + ''' + if not 序列: + return None + 长度= len(序列) + 均值= sum(序列)/长度 + 最大值= max(序列) + 最小值= min(序列) + 标准差= math.sqrt(sum([(nr-均值)**2 for nr in 序列]) / 长度) + + return (均值, 最大值, 最小值, 标准差, 长度) + + + +def 计算日内定时均线(价格序列, 调整时间序列, 格点粒度, 间隔点数, 定时点数, 需要规整=True): + ''' + + ''' + 日期对象= 调整时间序列[0].date() + + datetime_0925= datetime.datetime.combine(日期对象, datetime.time(hour=9, minute=25)) + + 格点序列= [datetime_0925 + datetime.timedelta(seconds=格点粒度*i) for i in range(int((3600*4+1000)/格点粒度))] + + if 需要规整: + 规整时间序列= [] + 格点当前位置= 0 + for 时间 in 调整时间序列: + while 格点序列[格点当前位置] < 时间: + 格点当前位置 += 1 + 前方格点= 格点序列[格点当前位置] + 后方格点= 格点序列[格点当前位置-1] + 规整时间= 前方格点 if 前方格点-时间 <= 时间-后方格点 else 后方格点 + 规整时间序列.append(规整时间) + else: + 规整时间序列= 调整时间序列 + + 目标格点序列= [时间 for 时间 in 格点序列 if 时间>=规整时间序列[0] and 时间<=规整时间序列[-1]] + + 补全价格序列= [] + 当前价格= 价格序列[0] + for 格点 in 目标格点序列: + if 格点 in 规整时间序列: + 当前价格= 价格序列[规整时间序列.index(格点)] + 补全价格序列.append(当前价格) + + 定时均线= {} + for 点数 in 定时点数: + 偏移序列= range(点数-1, len(目标格点序列), 间隔点数) + 时间序列= [目标格点序列[偏移] for 偏移 in 偏移序列] + 均线序列= [ sum(补全价格序列[偏移-点数+1 : 偏移+1]) / 点数 for 偏移 in 偏移序列 ] + 定时均线[点数]= { + '时间序列': 时间序列, + '均线序列': 均线序列, + } + + return 定时均线 + + + +def 计算走势标记(序列): + ''' + + ''' + length= len(序列) + if length < 2: + return ['-'] * length + + 标记序列= [] + 当前方向= '/' if 序列[1] > 序列[0] else \ + '\\' if 序列[1] < 序列[0] else \ + '-' + + for idx in range(1, length-1): + sign= '/' if 序列[idx] > 序列[idx-1] and 序列[idx+1] >= 序列[idx] else \ + '\\' if 序列[idx] < 序列[idx-1] and 序列[idx+1] <= 序列[idx] else \ + '^' if 序列[idx] > 序列[idx-1] and 序列[idx+1] < 序列[idx] else \ + 'v' if 序列[idx] < 序列[idx-1] and 序列[idx+1] > 序列[idx] else \ + '/' if 当前方向 in '/-' and 序列[idx+1] > 序列[idx] else \ + '\\' if 当前方向 in '\\-' and 序列[idx+1] < 序列[idx] else \ + '^' if 当前方向 == '/' and 序列[idx+1] < 序列[idx] else \ + 'v' if 当前方向 == '\\' and 序列[idx+1] > 序列[idx] else \ + '-' + + 当前方向= '/' if sign in '/v' else \ + '\\' if sign in '\\^' else \ + 当前方向 + + 标记序列.append(sign) + + return ['-'] + 标记序列 + ['/' if 序列[-1] > 序列[-2] else '\\' if 序列[-1] < 序列[-2] else '-'] + + + +def 计算走势拐点(目标序列, 走势标记, 扩展=True): + ''' + + ''' + + length= len(目标序列) + if length <= 2: + return [] + + 走势拐点= [] + + for idx, sign in [(i, s) for i, s in enumerate(走势标记) if s in ('^', 'v')]: + 拐点记录= {} + 拐点记录['偏移']= idx + 拐点记录['类型']= sign + if 扩展: + # 计算关键度 + 拐点记录['关键度']= 计算最新极点关键度(序列=目标序列[:idx+1], 类型=sign)['关键度'] + + 走势拐点.append(拐点记录) + + return 走势拐点 + + + +def 计算最新极点关键度(序列, 类型=None): + ''' + + ''' + 长度= len(序列) + + if 类型 is None: + for i in range(1, 长度): + if 序列[-i] > 序列[-(i+1)]: + 类型= '^' + break + elif 序列[-i] < 序列[-(i+1)]: + 类型= 'v' + break + + 结果= { + '类型': 类型, + '关键度': 长度, + '偏移': 长度-1, + } + + if 长度 < 2: + return 结果 + + if 类型 == '^': + chunk= [idx for idx, item in enumerate(reversed(序列)) if item > 序列[-1]] + elif 类型 == 'v': + chunk= [idx for idx, item in enumerate(reversed(序列)) if item < 序列[-1]] + else: + return 长度 + + 结果['关键度']= chunk[0] if chunk else 长度 + + return 结果 + + + +def repr_data(data, indent=0): + ''' + + ''' + tlist= (list, dict, set, tuple) + dtype= type(data) + + if dtype is list: + head= '\t'*indent + '[' + body= ',\n'.join( [repr_data(data=item, indent=indent+1) for item in data] ) + tail= '\n' + '\t'*indent + ']' + return head + '\t' + body.lstrip() + tail + + elif dtype is dict: + head= '\t'*indent + '{' + body= ',\n'.join( ['\t'*(indent+1) + str(key) + ' :' + ( ('\n' + repr_data(data=val, indent=indent+1)) if type(val) in tlist else ('\t' + str(val)) ) for key, val in sorted(data.items())] ) + tail= '\n' + '\t'*indent + '}' + return head + '\t' + body.lstrip() + tail + + elif dtype is set: + head= '\t'*indent + '{' + body= ',\n'.join( [repr_data(data=item, indent=indent+1) for item in sorted(data)] ) + tail= '\n' + '\t'*indent + '}' + return head + '\t' + body.lstrip() + tail + + elif dtype is tuple: + head= '\t'*indent + '(' + body= ',\n'.join( [repr_data(data=item, indent=indent+1) for item in data] ) + tail= '\n' + '\t'*indent + ')' + return head + '\t' + body.lstrip() + tail + + else: + return '\t'*indent + str(data) + + + diff --git a/vn.training/Public/__init__.py b/vn.training/Public/__init__.py new file mode 100644 index 00000000..f2120afc --- /dev/null +++ b/vn.training/Public/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + + + + diff --git a/vn.training/SubPlot/__init__.py b/vn.training/SubPlot/__init__.py new file mode 100644 index 00000000..faaaf799 --- /dev/null +++ b/vn.training/SubPlot/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + + diff --git a/vn.training/SubPlot/公司信息子图.py b/vn.training/SubPlot/公司信息子图.py new file mode 100644 index 00000000..551d874e --- /dev/null +++ b/vn.training/SubPlot/公司信息子图.py @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- + + + + + +import matplotlib.ticker as ticker +import matplotlib.font_manager as font_manager + + + +__font_properties__= font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc') + + + +__横轴倍率__= 10.0 / 230.0 +__纵轴倍率__= 0.3 + + + + + +class 公司信息子图: + ''' + 公司的基本信息 + ''' + + def __init__(self, parent, 绘图数据): + self._parent= parent + self._公司信息= 绘图数据['公司信息'] + + self._Axes= None + + self._横轴尺寸, \ + self._纵轴尺寸= self.计算本图尺寸() + + self._横轴宽度= self._横轴尺寸 * __横轴倍率__ + self._纵轴高度= self._纵轴尺寸 * __纵轴倍率__ + + + + def 计算本图尺寸(self): + return (300.0, 1.8) + + + + def 返回本图大小(self): + return (self._横轴尺寸*__横轴倍率__, self._纵轴尺寸*__纵轴倍率__) + + + + def 平面初始化(self, 图片对象, 子图偏移, 全图大小): + 子图横移, \ + 子图纵移= 子图偏移 + + 本图宽度= self._横轴宽度 + 本图高度= self._纵轴高度 + + 全图宽度, \ + 全图高度= 全图大小 + + 布局参数= ( 子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 本图高度/全图高度 ) + + axes= 图片对象.add_axes(布局参数) + axes.set_frame_on(False) + self._Axes= axes + + self.设置横轴参数() + self.设置纵轴参数() + + + + def 设置横轴参数(self): + axes= self._Axes + xaxis= axes.get_xaxis() + + # 设定 X 轴坐标的范围 + #================================================================================================================================================== + axes.set_xlim(0, self._横轴尺寸) + + xaxis.set_major_locator(ticker.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 设置纵轴参数(self): + axes= self._Axes + yaxis= axes.get_yaxis() + + # 设定 X 轴坐标的范围 + #================================================================================================================================================== + axes.set_ylim(0, self._纵轴尺寸) + + yaxis.set_major_locator(ticker.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 绘图(self): + self.绘制公司代码简称(xbase=0.0, ybase=self._纵轴尺寸) + self.绘制指数简称(xbase=self._横轴尺寸, ybase=self._纵轴尺寸) + self.绘制公司名称(xbase=0.0, ybase=self._纵轴尺寸-0.8) + self.绘制公司地域行业(xbase=48.0, ybase=self._纵轴尺寸) + self.绘制公司主营业务(xbase=48.0, ybase=self._纵轴尺寸) + self.绘制公司简介(xbase=90.0, ybase=self._纵轴尺寸) + self.绘制公司分类信息(xbase=165.0, ybase=self._纵轴尺寸) + + + + def 绘制公司代码简称(self, xbase, ybase): + ''' + 交易代码、公司简称 + ''' + + txtstr= self._公司信息['个股代码'] + ' ' + self._公司信息['个股简称'] + label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left') + label.set_fontsize(16.0) + + + + def 绘制指数简称(self, xbase, ybase): + txtstr= self._公司信息['指数简称'] + label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='right') + label.set_fontsize(16.0) + + + + def 绘制公司名称(self, xbase, ybase): + ''' + 曾用名、全名、英文名 + ''' + + txtstr= self._公司信息['基本情况']['曾用名'] + 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._公司信息['基本情况']['公司名称'] + '\n' + txtstr += self._公司信息['基本情况']['英文名称'] + + label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left') + label.set_fontsize(4.5) + + + + def 绘制公司地域行业(self, xbase, ybase): + ''' + 地域、所属行业、上市日期 + ''' + + txtstr= self._公司信息['公司概况']['区域'] + ' ' + self._公司信息['公司概况']['所属行业'] + ' ' + self._公司信息['发行相关']['上市日期'] + + label= self._Axes.text(xbase, ybase, txtstr, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left') + label.set_fontsize(6.5) + + + + def 绘制公司主营业务(self, xbase, ybase): + ''' + 主营业务 + ''' + # 查找表: (<文字长度>, <每行字数>, <字体大小>, ) + 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._公司信息['基本情况']['主营业务'] + 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 绘制公司简介(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._公司信息['公司概况']['公司简介'] # 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 绘制公司分类信息(self, xbase, ybase): + ''' + 行业板块信息 + ''' + infolist= self._公司信息['行业板块'] + + 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) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vn.training/SubPlot/分时价格子图.py b/vn.training/SubPlot/分时价格子图.py new file mode 100644 index 00000000..40a6bf6c --- /dev/null +++ b/vn.training/SubPlot/分时价格子图.py @@ -0,0 +1,580 @@ +# -*- coding: utf-8 -*- + + + + + +import datetime +import numpy +import math + + + +import matplotlib.ticker as ticker +import matplotlib.font_manager as font_manager + + + +import Public.Public as Public + + + + + +__font_properties__= font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc') + +__纵轴倍率__= 3.0 + + + + + +class 分时价格子图: + + def __init__(self, parent, 目标日期, 绘图数据): + ''' + + ''' + self._parent= parent + self._ilogger= Public.IndentLogger(logger=parent._ilogger._logger, indent=parent._ilogger._indent+1) + + # 原始数据 + 任务参数= 绘图数据['任务参数'] + self._公司信息= 绘图数据['公司信息'] + + # 当日数据 + 当日数据= 绘图数据['分时数据'][目标日期] + self._目标日期= 目标日期 + # self._日期对象= 当日数据['日期对象'] + # self._时间常数= 当日数据['时间常数'] + + # 横轴参数(需放在前面) + #=================================================================================================== + self._横轴参数= parent._横轴参数 + self._纵轴参数= None # XXX: 现在还不能计算,因为上下限与其他子图有关。 + self._坐标底数= 1.1 + + # 个股行情 + #=================================================================================================== + + # 分时行情是否存在 + self._个股行情有效= 当日数据['个股行情有效'] + + # 日线数据 + self._个股当日开盘= 当日数据['个股当日开盘'] + self._个股当日最高= 当日数据['个股当日最高'] + self._个股前日收盘= 当日数据['个股前日收盘'] # 前日收盘可能是 None + self._个股当日最低= 当日数据['个股当日最低'] + + if self._个股行情有效: + # 分时数据 + 个股分时行情= 当日数据['个股分时行情'] + self._个股价格序列= 个股分时行情['价格序列'] + + # 分时行情坐标序列 + self._个股调整时间= parent._个股调整时间 + self._个股坐标序列= parent._个股坐标序列 + + # 分时衍生数据(均线等) + self._分时格点粒度= 格点粒度= 任务参数['分时格点粒度'] + 间隔点数= 任务参数['均线间隔点数'] + 定时点数= 任务参数['均线定时点数'] + self._个股定时均线= Public.计算日内定时均线( \ + 价格序列=self._个股价格序列, \ + 调整时间序列=self._个股调整时间, \ + 格点粒度=格点粒度, \ + 间隔点数=间隔点数, \ + 定时点数=定时点数, \ + 需要规整=False \ + ) + for 均线 in self._个股定时均线.values(): + 均线['坐标序列']= self._parent.计算调整时间序列坐标(调整时间序列=均线['时间序列']) + 均线['走势标记']= Public.计算走势标记(序列=均线['均线序列']) + + # TODO: 指数行情 + #=================================================================================================== + + # 日线数据 + self._指数当日开盘= 当日数据['指数当日开盘'] + self._指数当日最高= 当日数据['指数当日最高'] + self._指数前日收盘= 当日数据['指数前日收盘'] # 前日收盘可能是 None + self._指数当日最低= 当日数据['指数当日最低'] + + + + # 平面对象,留待后面初始化 + #=================================================================================================== + self._布局参数= None + + self._指数平面= None + self._指数横轴= None + self._指数纵轴= None + + self._个股平面= None + self._个股横轴= None + self._个股纵轴= None + + + + def 计算纵轴坐标区间(self): + ''' + + ''' + 个股开盘= self._个股当日开盘 + 指数开盘= self._指数当日开盘 + + 个股最高= max(self._个股前日收盘, self._个股当日最高) * 1.01 if self._个股前日收盘 else self._个股当日最高 * 1.01 + 个股最低= min(self._个股前日收盘, self._个股当日最低) * 0.99 if self._个股前日收盘 else self._个股当日最低 * 0.99 + + 指数最高= self._指数当日最高 * 1.01 + 指数最低= self._指数当日最低 * 0.99 + + 个股综合最高= max(个股最高, 指数最高*个股开盘/指数开盘) + 个股综合最低= min(个股最低, 指数最低*个股开盘/指数开盘) + + 指数综合最高= max(指数最高, 个股最高*指数开盘/个股开盘) + 指数综合最低= min(指数最低, 个股最低*指数开盘/个股开盘) + + 纵标区间= {} + 纵标区间['个股最高']= 个股综合最高 + 纵标区间['个股最低']= 个股综合最低 + 纵标区间['指数最高']= 指数综合最高 + 纵标区间['指数最低']= 指数综合最低 + + return 纵标区间 + + + + def 计算纵轴参数(self, 坐标区间=None): + ''' + + ''' + def 计算个股坐标参数(基准价格, 坐标起点, 坐标终点): + ''' + + ''' + # 计算主坐标值 + 步进= 基准价格 / 100.0 + 主坐标值= [基准价格] + + 当前价格= 基准价格 + 步进 + while 当前价格 < 坐标终点: + 主坐标值.append( round(当前价格, -1) ) + 当前价格= 当前价格 + 步进 + + 当前价格= 基准价格 - 步进 + while 当前价格 > 坐标起点: + 主坐标值.append( round(当前价格, -1) ) + 当前价格= 当前价格 - 步进 + + 主坐标值= sorted(set(主坐标值)) + + # 计算副坐标值 + 步进= max(round(基准价格*0.01/4.0, -1), 10.0) # 选择一个步进值,10.0 代表最小刻度: 0.01元 + 副坐标值= [] + 当前价格= round(坐标起点+5.0, -1) + while 当前价格 < 坐标终点: + 副坐标值.append(当前价格) + 当前价格= 当前价格 + 步进 + 副坐标值= [价格 for 价格 in 副坐标值 if 价格 not in 主坐标值] + + 坐标参数= { + '个股主坐标值': 主坐标值, + '个股副坐标值': 副坐标值, + } + + return 坐标参数 + + if 坐标区间 is None: + 坐标区间= self.计算纵轴坐标区间() + + 个股坐标起点= 坐标区间['个股最低'] + 个股坐标终点= 坐标区间['个股最高'] + 纵轴尺寸= math.log(个股坐标终点, self._坐标底数) - math.log(个股坐标起点, self._坐标底数) + 纵轴高度= 纵轴尺寸 * __纵轴倍率__ + + 纵轴参数= {} + 纵轴参数['个股坐标起点']= 个股坐标起点 + 纵轴参数['个股坐标终点']= 个股坐标终点 + 纵轴参数['纵轴尺寸']= 纵轴尺寸 + 纵轴参数['纵轴高度']= 纵轴高度 + + # 指数部分,暂时这样 + 纵轴参数['指数坐标起点']= 坐标区间['指数最低'] + 纵轴参数['指数坐标终点']= 坐标区间['指数最高'] + + 基准价格= self._个股前日收盘 if self._个股前日收盘 else self._个股当日开盘 + 纵轴参数['基准价格']= 基准价格 + 纵轴参数.update( 计算个股坐标参数(基准价格=基准价格, 坐标起点=个股坐标起点, 坐标终点=个股坐标终点) ) + + self._纵轴参数= 纵轴参数 + + + + def 返回纵轴高度(self): + ''' + + ''' + return self._纵轴参数['纵轴高度'] + + + + def 返回指数平面(self): + ''' + + ''' + return self._指数平面 + + + + def 平面初始化(self, 图片对象, 子图偏移, 全图大小, sharex): + ''' + + ''' + # 计算自身的布局参数 + 子图横移, \ + 子图纵移= 子图偏移 + + 全图宽度, \ + 全图高度= 全图大小 + + 本图宽度= self._横轴参数['横轴宽度'] + 本图高度= self._纵轴参数['纵轴高度'] + + 布局参数= (子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 本图高度/全图高度) + + self._布局参数= 布局参数 + 坐标底数= self._坐标底数 + + # 指数部分 + #======================================================================================= + + # XXX: 指数与个股布局参数一样的话,label 一定要设成不一样,见 add_axes() 官方文档。 + 指数平面= 图片对象.add_axes(布局参数, axis_bgcolor='none', label='指数平面', sharex=sharex) + 指数平面.set_frame_on(False) + 指数平面.set_axisbelow(True) # 网格线放在底层 + 指数平面.set_yscale('log', basey=坐标底数) # 使用对数坐标 + + 指数横轴= 指数平面.get_xaxis() + 指数纵轴= 指数平面.get_yaxis() + + self._指数平面= 指数平面 + self._指数横轴= 指数横轴 + self._指数纵轴= 指数纵轴 + + self.设置指数横轴() + self.设置指数纵轴() + + # 个股部分 + #======================================================================================= + # 个股平面= 指数平面.twinx() # XXX: twinx 有问题,使用了以后指数的 ticks 就关不掉。可能是 bug + + # XXX: 指数与个股布局参数一样的话,label 一定要设成不一样,见 add_axes() 官方文档。 + 个股平面= 图片对象.add_axes(布局参数, axis_bgcolor='none', label='个股平面', sharex=sharex) + 个股平面.set_frame_on(False) + 个股平面.set_axisbelow(True) # 网格线放在底层 + 个股平面.set_yscale('log', basey=坐标底数) # 使用对数坐标 + + 个股横轴= 个股平面.get_xaxis() + 个股纵轴= 个股平面.get_yaxis() + + self._个股平面= 个股平面 + self._个股横轴= 个股横轴 + self._个股纵轴= 个股纵轴 + + self.设置个股横轴() + self.设置个股纵轴() + + + + def 设置指数横轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 指数横轴= self._指数横轴 + 横轴参数= self._横轴参数 + + 指数横轴.set_ticks_position('none') + + #================================================================================= + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + xMajorFormatter= 横轴参数['xMajorFormatter'] + xMinorFormatter= 横轴参数['xMinorFormatter'] + + 指数横轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 指数横轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 指数平面.set_xlim(坐标起点, 坐标终点) + 指数横轴.set_major_locator(xMajorLocator) + 指数横轴.set_minor_locator(xMinorLocator) + #================================================================================= + + for 主坐标 in 指数平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 指数平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置指数纵轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 指数纵轴= self._指数纵轴 + 纵轴参数= self._纵轴参数 + + 坐标起点= 纵轴参数['指数坐标起点'] + 坐标终点= 纵轴参数['指数坐标终点'] + + 指数平面.set_ylim(坐标起点, 坐标终点) + # 指数纵轴.set_label_position('left') # XXX: 不顶用 + 指数纵轴.set_ticks_position('none') + + for 主坐标 in 指数平面.get_yticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 指数平面.get_yticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置个股横轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 个股横轴= self._个股横轴 + 横轴参数= self._横轴参数 + + 个股横轴.set_ticks_position('none') + + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + 个股平面.set_xlim(坐标起点, 坐标终点) + + # xMajorLocator= 横轴参数['xMajorLocator'] + # xMinorLocator= 横轴参数['xMinorLocator'] + # xMajorFormatter= 横轴参数['xMajorFormatter'] + # xMinorFormatter= 横轴参数['xMinorFormatter'] + + # 个股横轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # 个股横轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + # 个股横轴.set_major_locator(xMajorLocator) + # 个股横轴.set_minor_locator(xMinorLocator) + + for 主坐标 in 个股平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 个股平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置个股纵轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 个股纵轴= self._个股纵轴 + 纵轴参数= self._纵轴参数 + + 个股纵轴.set_ticks_position('none') + + 坐标起点= 纵轴参数['个股坐标起点'] + 坐标终点= 纵轴参数['个股坐标终点'] + + 主坐标值= 纵轴参数['个股主坐标值'] + 副坐标值= 纵轴参数['个股副坐标值'] + + # 个股纵轴.set_label_position('right') # XXX: 不顶用 + + 个股纵轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 个股纵轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 个股平面.set_ylim(坐标起点, 坐标终点) + + # 主坐标点 + yMajorLocator= ticker.FixedLocator( numpy.array(主坐标值) ) + 个股纵轴.set_major_locator(yMajorLocator) + + # def y_major_formatter(num, pos=None): + # return str(round(num/1000.0, 3)) + # yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + # 个股纵轴.set_major_formatter(yMajorFormatter) + + for 主坐标 in 个股平面.get_yticklabels(minor=False): + 主坐标.set_visible(False) + + # 副坐标点 + yMinorLocator= ticker.FixedLocator( numpy.array(副坐标值) ) + 个股纵轴.set_minor_locator(yMinorLocator) + + # def y_minor_formatter(num, pos=None): + # return str(round(num/1000.0, 3)) + # yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + # 个股纵轴.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 个股平面.get_yticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 绘图(self): + ''' + + ''' + self.绘制辅助标记() + self.绘制个股价格走势() + + if self._个股行情有效: + self.绘制个股价格均线() + + + + def 绘制个股价格走势(self): + ''' + + ''' + 个股平面= self._个股平面 + + # 特征价格 + 横标起点, 横标终点= self._横轴参数['坐标起点'], self._横轴参数['坐标终点'] + + if self._个股前日收盘: + 个股平面.plot((横标起点, 横标终点), (self._个股前日收盘, self._个股前日收盘), '-', color='lightblue', linewidth=0.7, alpha=0.5) + 个股平面.plot((横标起点, 横标终点), (self._个股当日开盘, self._个股当日开盘), '-', color='yellow', linewidth=0.7, alpha=0.5) + 个股平面.plot((横标起点, 横标终点), (self._个股当日最高, self._个股当日最高), '-', color='red', linewidth=0.7, alpha=0.3) + 个股平面.plot((横标起点, 横标终点), (self._个股当日最低, self._个股当日最低), '-', color='green', linewidth=0.7, alpha=0.5) + + if self._个股行情有效: + 坐标阵列= numpy.array(self._个股坐标序列) + 价格阵列= numpy.array(self._个股价格序列) + # 价格走势 + 个股平面.plot(坐标阵列, 价格阵列, 'o-', color='white', linewidth=0.15, label='_nolegend_', \ + markersize=0.3, markeredgecolor='white', markeredgewidth=0.1, alpha=1.0) + + + + + + def 绘制个股价格均线(self): + ''' + + ''' + 个股平面= self._个股平面 + 格点粒度= self._分时格点粒度 + 定时均线= self._个股定时均线 + + for 类别, 均线 in 定时均线.items(): + 坐标序列= 均线['坐标序列'] + 均线序列= 均线['均线序列'] + 走势标记= 均线['走势标记'] + + # 绘制均线 + 坐标阵列= numpy.array(坐标序列) + 均线阵列= numpy.array(均线序列) + + lcolor= 'yellow' if 类别*格点粒度 <= 300 else \ + 'cyan' if 类别*格点粒度 <= 600 else \ + 'magenta' + + 个股平面.plot(坐标阵列, 均线阵列, 'o-', color=lcolor, linewidth=0.1, label='_nolegend_', \ + markersize=0.2, markeredgecolor=lcolor, markeredgewidth=0.1, alpha=0.7) + + # 绘制均线拐点 + 底点阵列= numpy.array([均值 if 标记=='v' else None for 均值, 标记 in zip(均线序列, 走势标记)]) + 顶点阵列= numpy.array([均值 if 标记=='^' else None for 均值, 标记 in zip(均线序列, 走势标记)]) + + 个股平面.plot(坐标阵列, 底点阵列, '^', color=lcolor, label='均线底点', \ + markersize=1.5, markeredgecolor=lcolor, markeredgewidth=0.0, alpha=1.0) + + 个股平面.plot(坐标阵列, 顶点阵列, 'v', color=lcolor, label='均线顶点', \ + markersize=1.5, markeredgecolor=lcolor, markeredgewidth=0.0, alpha=1.0) + + + + def 绘制辅助标记(self): + ''' + + ''' + 指数平面= self._指数平面 + 个股平面= self._个股平面 + 横轴参数= self._横轴参数 + 纵轴参数= self._纵轴参数 + 公司信息= self._公司信息 + + 目标日期= self._目标日期 + + 指数纵标起点= 纵轴参数['指数坐标起点'] + 指数纵标终点= 纵轴参数['指数坐标终点'] + + 个股纵标起点= 纵轴参数['个股坐标起点'] + 个股纵标终点= 纵轴参数['个股坐标终点'] + + 标注位置= 横轴参数['标注位置'] + + # 画公司名称、目标日期 + #============================================================================================================ + 标注内容= 公司信息['个股代码'] + ' ' + 公司信息['个股简称'] + ' ' + 目标日期 + + 纵标= (指数纵标起点*指数纵标终点)**0.5 + 指数平面.text( 标注位置['09:30'], 纵标, 标注内容, fontproperties=__font_properties__, \ + color='0.3', fontsize=27, alpha=0.3, verticalalignment='center') + 指数平面.text( 标注位置['15:00']-300.0, 纵标, 标注内容, fontproperties=__font_properties__, \ + color='0.3', fontsize=27, alpha=0.3, horizontalalignment='right', verticalalignment='center') + + # 画时间标记 + #============================================================================================================ + # 15 分钟主时间点 + for iy in [指数纵标起点*1.004, 指数纵标终点*0.993]: + for ix, 时间, 表示 in zip(横轴参数['主坐标值'], 横轴参数['主标时间'], 横轴参数['主标表示']): + 标注= 指数平面.text(ix, iy, 表示, color='0.3', fontsize=8, zorder=0) + if 表示 in ('11:30', '15:00'): 标注.set_horizontalalignment('right') + + # 5 分钟副时间点 + for iy in [指数纵标起点*1.001, 指数纵标终点*0.997]: + for ix, 时间, 表示 in zip(横轴参数['副坐标值'], 横轴参数['副标时间'], 横轴参数['副标表示']): + 指数平面.text(ix, iy, 表示, color='0.3', fontsize=5, zorder=0) + + # 画价格标记 + #============================================================================================================ + 标注位置组一= 横轴参数['标注位置组一'] + + 主标价格= [nr for nr in 纵轴参数['个股主坐标值'] if nr > 个股纵标起点*1.01 and nr < 个股纵标终点*0.99] + 副标价格= [nr for nr in 纵轴参数['个股副坐标值'] if nr > 个股纵标起点*1.01 and nr < 个股纵标终点*0.99] + for 横标 in 标注位置组一: + for 纵标 in 主标价格: + 个股平面.text(横标-30.0, 纵标, str(纵标/1000.0), color='0.3', fontsize=3.0, horizontalalignment='right', zorder=0) + for 纵标 in 副标价格: + 个股平面.text(横标+30.0, 纵标, str(纵标/1000.0), color='0.3', fontsize=3.0, zorder=0) + + # 画档位标记 + #============================================================================================================ + 标注位置组二= 横轴参数['标注位置组二'] + 基准价格= 纵轴参数['基准价格'] + + 正向档位= [nr for nr in 纵轴参数['个股主坐标值'] if nr >= 基准价格 and nr < 个股纵标终点*0.99] + 负向档位= list(reversed([nr for nr in 纵轴参数['个股主坐标值'] if nr < 基准价格 and nr > 个股纵标起点])) + + for 横标 in 标注位置组二: + for 档位, 纵标 in enumerate(正向档位, start=1): + 个股平面.text(横标+30.0, 纵标*1.001, str(档位), color='red', fontsize=25, alpha=0.17, zorder=0) + for 档位, 纵标 in enumerate(负向档位, start=1): + 个股平面.text(横标+30.0, 纵标*1.001, str(档位), color='green', fontsize=25, alpha=0.2, zorder=0) + + + + + + + + + + + diff --git a/vn.training/SubPlot/分时手数子图.py b/vn.training/SubPlot/分时手数子图.py new file mode 100644 index 00000000..18962bb9 --- /dev/null +++ b/vn.training/SubPlot/分时手数子图.py @@ -0,0 +1,436 @@ +# -*- coding: utf-8 -*- + + + + + +import numpy + + + +# import matplotlib.spines as spines +import matplotlib.ticker as ticker + + + +import Public.Public as Public + + + + + + + +class 分时手数子图: + + def __init__(self, parent, 目标日期, 绘图数据): + ''' + + ''' + self._parent= parent + self._ilogger= Public.IndentLogger(logger=parent._ilogger._logger, indent=parent._ilogger._indent+1) + + # 原始数据 + 任务参数= 绘图数据['任务参数'] + + # 当日数据 + 当日数据= 绘图数据['分时数据'][目标日期] + self._本图日期= 目标日期 + # self._日期对象= 当日数据['日期对象'] + # self._时间常数= 当日数据['时间常数'] + + # 个股行情 + #=================================================================================================== + + # 分时行情是否存在 + self._个股行情有效= 当日数据['个股行情有效'] + + # 日线数据 + self._个股平均成交= 当日数据['个股平均成交'] # 格式: 行情['平均手数']= {Public.计算均值(个股成交量[-n:]) for n in 个股量均参数} + + if self._个股行情有效: + # 分时数据 + 个股分时行情= 当日数据['个股分时行情'] + self._个股手数序列= 个股分时行情['手数序列'] + self._个股金额序列= 个股分时行情['金额序列'] + self._个股备注序列= 个股分时行情['备注序列'] + + # self._个股上涨标记= 个股分时行情['上涨标记'] + # self._个股下跌标记= 个股分时行情['下跌标记'] + # self._个股平盘标记= 个股分时行情['平盘标记'] + + self._个股买盘标记= [True if 备注.startswith('买') else False for 备注 in self._个股备注序列] + self._个股卖盘标记= [True if 备注.startswith('卖') else False for 备注 in self._个股备注序列] + self._个股中性标记= [True if 备注.startswith('中') else False for 备注 in self._个股备注序列] + + # 分时衍生数据(均线等) + self._个股坐标序列= parent._个股坐标序列 + self._个股平均手数= Public.计算均值(序列=[nr for nr in self._个股手数序列 if nr>0]) + + # TODO: 指数行情 + #=================================================================================================== + + + + # 横轴参数、纵轴参数 + #=================================================================================================== + self._横轴参数= parent._横轴参数 + self._纵轴参数= None + + # 平面对象,留待后面初始化 + #=================================================================================================== + self._个股布局参数= None + self._指数布局参数= None + + self._指数平面= None + self._指数横轴= None + self._指数纵轴= None + + self._个股平面= None + self._个股横轴= None + self._个股纵轴= None + + + + def 计算成交步进记录(self): + ''' + 本函数是 naive 的,只考虑本图。 + ''' + if self._个股行情有效: + # 决定个股步进值 + 个股平均手数= self._个股平均手数[0] + 个股步进= 25 # 步进代表主坐标的间隔距离 + + while 个股平均手数/个股步进 > 1.0: + 个股步进= 个股步进*2 + else: + 个股步进= 25 + + return {'个股步进': 个股步进} + + + + def 计算纵轴参数(self, 步进记录=None): + ''' + + ''' + 纵轴参数= {} + + # 大小固定 + #======================================================================================= + 纵轴尺寸= 4.0 + 纵轴倍率= 0.3 + 纵轴参数['纵轴倍率']= 纵轴倍率 + 纵轴参数['纵轴高度']= 纵轴尺寸 * 纵轴倍率 + + if 步进记录 is None: + 步进记录= self.计算成交步进记录() + + # 个股部分 + #======================================================================================= + 步进= 步进记录['个股步进'] + + # 坐标起点 与 坐标终点 + 个股坐标起点= 0.0 + 个股坐标终点= max(self._个股手数序列) if self._个股行情有效 else 步进*纵轴尺寸 + 纵轴参数['个股坐标起点']= 个股坐标起点 + 纵轴参数['个股坐标终点']= 个股坐标终点 + + 个股纵轴尺寸= max(个股坐标终点/步进, 纵轴尺寸) + 纵轴参数['个股纵轴尺寸']= 个股纵轴尺寸 + 纵轴参数['个股纵轴高度']= 个股纵轴尺寸 * 纵轴倍率 + + # 计算 坐标值 与 坐标点 + 个股主坐标值= [步进 * i for i in range(1, 4)] + 个股副坐标值= [(步进/2.0) + 步进*i for i in range(4)] + if 个股副坐标值[-1] > 纵轴参数['个股坐标终点']: del 个股副坐标值[-1] + + 纵轴参数['个股主坐标值']= 个股主坐标值 + 纵轴参数['个股副坐标值']= 个股副坐标值 + + self._纵轴参数= 纵轴参数 + + + + def 返回纵轴高度(self): + ''' + + ''' + return self._纵轴参数['纵轴高度'] + + + + def 返回指数平面(self): + ''' + + ''' + return self._指数平面 + + + + def 平面初始化(self, 图片对象, 子图偏移, 全图大小): + ''' + + ''' + 子图横移, \ + 子图纵移= 子图偏移 + + 全图宽度, \ + 全图高度= 全图大小 + + 本图宽度= self._横轴参数['横轴宽度'] + 指数平面高度= self._纵轴参数['纵轴高度'] # XXX: 以后指数平面可以有自己的高度 + 个股平面高度= self._纵轴参数['个股纵轴高度'] + + 指数布局参数= (子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 指数平面高度/全图高度) + 个股布局参数= (子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 个股平面高度/全图高度) + + self._指数布局参数= 指数布局参数 + self._个股布局参数= 个股布局参数 + + # 指数部分 + #======================================================================================= + 指数平面= 图片对象.add_axes(指数布局参数, axis_bgcolor='black') + 指数平面.set_frame_on(False) # XXX + 指数平面.set_axisbelow(True) # 网格线放在底层 + + 指数横轴= 指数平面.get_xaxis() + 指数纵轴= 指数平面.get_yaxis() + + self._指数平面= 指数平面 + self._指数横轴= 指数横轴 + self._指数纵轴= 指数纵轴 + + self.设置指数横轴() + self.设置指数纵轴() + + # 个股部分 + #======================================================================================= + + # XXX: 不用 twinx(),原因见分时价格子图。 + 个股平面= 图片对象.add_axes(个股布局参数, axis_bgcolor='black') + 个股平面.set_frame_on(False) # XXX + 个股平面.set_axisbelow(True) # 网格线放在底层 + + for 方位, 边框 in 个股平面.spines.items(): + 边框.set_color(None) + + 个股横轴= 个股平面.get_xaxis() + 个股纵轴= 个股平面.get_yaxis() + + self._个股平面= 个股平面 + self._个股横轴= 个股横轴 + self._个股纵轴= 个股纵轴 + + self.设置个股横轴() + self.设置个股纵轴() + + + + def 设置指数横轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 指数横轴= self._指数横轴 + 横轴参数= self._横轴参数 + + 指数横轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 指数横轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 指数横轴.set_ticks_position('none') + + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + + 指数平面.set_xlim(坐标起点, 坐标终点) + 指数横轴.set_major_locator(xMajorLocator) + 指数横轴.set_minor_locator(xMinorLocator) + + for 主坐标 in 指数平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 指数平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置指数纵轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 指数纵轴= self._指数纵轴 + + # 指数纵轴.set_label_position('right') # XXX: 不顶用 + 指数纵轴.set_ticks_position('none') + + for 主坐标 in 指数平面.get_yticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 指数平面.get_yticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置个股横轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 个股横轴= self._个股横轴 + 横轴参数= self._横轴参数 + + 个股横轴.set_ticks_position('none') + + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + xMajorFormatter= 横轴参数['xMajorFormatter'] + xMinorFormatter= 横轴参数['xMinorFormatter'] + + 个股平面.set_xlim(坐标起点, 坐标终点) + 个股横轴.set_major_locator(xMajorLocator) + 个股横轴.set_minor_locator(xMinorLocator) + + 个股横轴.set_major_formatter(xMajorFormatter) + 个股横轴.set_minor_formatter(xMinorFormatter) + + for 主坐标 in 个股平面.get_xticklabels(minor=False): + 主坐标.set_fontsize(5) + 主坐标.set_horizontalalignment('right') + 主坐标.set_rotation('45') + + for 副坐标 in 个股平面.get_xticklabels(minor=True): + 副坐标.set_fontsize(4) + 副坐标.set_color('blue') + 副坐标.set_horizontalalignment('right') + 副坐标.set_rotation('45') + + + + def 设置个股纵轴(self): + ''' + + ''' + 平面对象= self._个股平面 + 个股纵轴= self._个股纵轴 + 纵轴参数= self._纵轴参数 + + # 个股纵轴.set_label_position('right') # XXX: 不顶用 + 个股纵轴.set_ticks_position('none') + + 个股纵轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 个股纵轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 坐标起点= 纵轴参数['个股坐标起点'] + 坐标终点= 纵轴参数['个股坐标终点'] + + 平面对象.set_ylim(坐标起点, 坐标终点) + + # 主坐标点 + #====================================================================================== + 主坐标值= 纵轴参数['个股主坐标值'] + yMajorLocator= ticker.FixedLocator(numpy.array(主坐标值)) + 个股纵轴.set_major_locator(yMajorLocator) + + # def y_major_formatter(num, pos=None): + # return str(num) + # yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + # 个股纵轴.set_major_formatter(yMajorFormatter) + + for 主坐标 in 平面对象.get_yticklabels(minor=False): + 主坐标.set_visible(False) + + # 副坐标点 + #====================================================================================== + 副坐标值= 纵轴参数['个股副坐标值'] + yMinorLocator= ticker.FixedLocator(numpy.array(副坐标值)) + 个股纵轴.set_minor_locator(yMinorLocator) + + # def y_minor_formatter(num, pos=None): + # return str(num) + # yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + # 个股纵轴.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 平面对象.get_yticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 绘图(self): + ''' + + ''' + self.绘制辅助标记() + self.绘制个股手数() + + + + def 绘制辅助标记(self): + ''' + + ''' + 个股平面= self._个股平面 + + 标注位置组一= self._横轴参数['标注位置组一'] + 主标手数= self._纵轴参数['个股主坐标值'] + 副标手数= self._纵轴参数['个股副坐标值'] + + for 横标 in 标注位置组一: + for 纵标 in 主标手数: + 个股平面.text(横标+30.0, 纵标, str(int(纵标)), color='0.3', fontsize=5.0, zorder=0) + for 纵标 in 副标手数: + 个股平面.text(横标+30.0, 纵标, str(int(纵标)), color='0.3', fontsize=5.0, zorder=0) + + + + def 绘制个股手数(self): + ''' + + ''' + 个股平面= self._个股平面 + + if self._个股行情有效: + 坐标序列= numpy.array(self._个股坐标序列) + 手数序列= numpy.array(self._个股手数序列) + 序列长度= len(手数序列) + 起点序列= numpy.zeros(序列长度) + + # 正向标记= numpy.array(self._个股上涨标记) + # 负向标记= numpy.array(self._个股下跌标记) + # 中性标记= numpy.array(self._个股平盘标记) + + 正向标记= numpy.array(self._个股买盘标记) + 负向标记= numpy.array(self._个股卖盘标记) + 中性标记= numpy.array(self._个股中性标记) + + lwidth, alpha= (0.15, 1.0) + + if True in 正向标记: + 个股平面.vlines(坐标序列[正向标记], 起点序列[正向标记], 手数序列[正向标记], edgecolor='red', linewidth=lwidth, label='_nolegend_', alpha=alpha) + if True in 负向标记: + 个股平面.vlines(坐标序列[负向标记], 起点序列[负向标记], 手数序列[负向标记], edgecolor='green', linewidth=lwidth, label='_nolegend_', alpha=alpha) + if True in 中性标记: + 个股平面.vlines(坐标序列[中性标记], 起点序列[中性标记], 手数序列[中性标记], edgecolor='white', linewidth=lwidth, label='_nolegend_', alpha=alpha) + + # 绘制平均手数数值(直线) + 平均手数= self._个股平均手数[0] + 横轴参数= self._横轴参数 + 横标起点= 横轴参数['坐标起点'] + 横标终点= 横轴参数['坐标终点'] + + 个股平面.plot([横标起点, 横标终点], [平均手数, 平均手数], '-', color='yellow', linewidth=0.2, alpha=0.7) + + + + + + + + + + diff --git a/vn.training/SubPlot/实盘价格子图.py b/vn.training/SubPlot/实盘价格子图.py new file mode 100644 index 00000000..2a08a67c --- /dev/null +++ b/vn.training/SubPlot/实盘价格子图.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- + + + + + +class 实盘价格子图: + ''' + + ''' + + def __init__(self, parent, 绘图数据): + ''' + + ''' + pass + + + + + + + diff --git a/vn.training/SubPlot/实盘手数子图.py b/vn.training/SubPlot/实盘手数子图.py new file mode 100644 index 00000000..eca94e93 --- /dev/null +++ b/vn.training/SubPlot/实盘手数子图.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + + + + + +class 实盘手数子图: + ''' + + ''' + + def __init__(self, parent, 绘图数据, 价格子图): + ''' + + ''' + pass + + + + + + diff --git a/vn.training/SubPlot/日线价格子图.py b/vn.training/SubPlot/日线价格子图.py new file mode 100644 index 00000000..13c6a10c --- /dev/null +++ b/vn.training/SubPlot/日线价格子图.py @@ -0,0 +1,1327 @@ +# -*- coding: utf-8 -*- + + + + +import copy +import numpy +import datetime +import math + +import matplotlib.spines as spines +import matplotlib.ticker as ticker +import matplotlib.patches as patches +import matplotlib.font_manager as font_manager + + + +import Public.Public as Public + + + +__color_gold__= '#FDDB05' +__color_gray70__= '0.7' +__font_properties__= font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc') + +__横轴倍率__= 10.0 / 230.0 +__纵轴倍率__= 0.3 + + + + + +class 日线价格子图: + + def __init__(self, parent, 绘图数据): + ''' + + ''' + self._parent= parent + self._ilogger= Public.IndentLogger(logger=parent._ilogger._logger, indent=parent._ilogger._indent+1) + + # 原始数据 + self._公司信息= 绘图数据['公司信息'] + self._任务参数= 绘图数据['任务参数'] + self._日线数据= 绘图数据['日线数据'] + # self._实盘数据= 绘图数据['实盘数据'] + + self._个股代码= self._公司信息['个股代码'] + self._复权记录= self._日线数据['复权记录'] + + # 任务参数数据 + self._复权绘图= self._任务参数['复权绘图'] + self._绘制实盘= self._任务参数['绘制实盘'] + self._绘制个股均线= self._任务参数['绘制个股均线'] + self._绘制指数均线= self._任务参数['绘制指数均线'] + + # 全局日线数据 + self._全局指数日线= self._日线数据['指数日线'] + self._全局绘图日线= self._日线数据['绘图日线'] # 绘图日线 就是补全日线,根据任务参数已经选择了是否复权 + self._全局日期序列= self._全局指数日线['日期'] + + # 目标日线数据 + self._目标起始偏移= self._日线数据['目标起始偏移'] + self._目标结束偏移= self._日线数据['目标结束偏移'] + + self._目标指数日线= { 项目 : 序列[self._目标起始偏移:self._目标结束偏移+1] for 项目, 序列 in self._全局指数日线.items() } + self._目标绘图日线= { 项目 : 序列[self._目标起始偏移:self._目标结束偏移+1] for 项目, 序列 in self._全局绘图日线.items() } + + # 均线数据 + if self._绘制指数均线: + self._全局指数均线= self._日线数据['指数均线'] + self._目标指数均线= { 项目 : 序列[self._目标起始偏移:self._目标结束偏移+1] for 项目, 序列 in self._全局指数均线.items() } + if self._绘制个股均线: + self._全局绘图均线= self._日线数据['绘图均线'] + self._目标绘图均线= { 项目 : 序列[self._目标起始偏移:self._目标结束偏移+1] for 项目, 序列 in self._全局绘图均线.items() } + + self._目标日期序列= self._目标绘图日线['日期'] + + self._目标指数开盘= self._目标指数日线['开盘'] + self._目标指数收盘= self._目标指数日线['收盘'] + self._目标指数最高= self._目标指数日线['最高'] + self._目标指数最低= self._目标指数日线['最低'] + self._目标指数中线= self._目标指数日线['开收中'] + + self._目标绘图开盘= self._目标绘图日线['开盘'] + self._目标绘图收盘= self._目标绘图日线['收盘'] + self._目标绘图最高= self._目标绘图日线['最高'] + self._目标绘图最低= self._目标绘图日线['最低'] + self._目标绘图中线= self._目标绘图日线['开收中'] + + # 行情附加数据 + self._对数坐标底数= 1.1 + + self._横轴坐标序列= numpy.arange(self._目标起始偏移, self._目标结束偏移+1) + self._目标行情长度= len(self._目标日期序列) + + self._目标指数上涨= numpy.array( [True if po is not None and po < pc else False for po, pc in zip(self._目标指数开盘, self._目标指数收盘)] ) # 标示出该天股价日内上涨的一个序列 + self._目标指数下跌= numpy.array( [True if po is not None and po > pc else False for po, pc in zip(self._目标指数开盘, self._目标指数收盘)] ) # 标示出该天股价日内下跌的一个序列 + self._目标指数平盘= numpy.array( [True if po is not None and po == pc else False for po, pc in zip(self._目标指数开盘, self._目标指数收盘)] ) # 标示出该天股价日内走平的一个序列 + + self._目标绘图上涨= numpy.array( [True if po is not None and po < pc else False for po, pc in zip(self._目标绘图开盘, self._目标绘图收盘)] ) # 标示出该天股价日内上涨的一个序列 + self._目标绘图下跌= numpy.array( [True if po is not None and po > pc else False for po, pc in zip(self._目标绘图开盘, self._目标绘图收盘)] ) # 标示出该天股价日内下跌的一个序列 + self._目标绘图平盘= numpy.array( [True if po is not None and po == pc else False for po, pc in zip(self._目标绘图开盘, self._目标绘图收盘)] ) # 标示出该天股价日内走平的一个序列 + + # 行情衍生数据 + self._指数衍生行情= self._日线数据['指数衍生行情'] + self._个股衍生行情= self._日线数据['个股衍生行情'] + + # 平面对象数据 + self._布局参数= None + self._指数平面= None + self._指数横轴= None + self._指数纵轴= None + + self._个股平面= None + self._个股横轴= None + self._个股纵轴= None + + # 横轴参数、纵轴参数 + self._横轴参数= self.计算横轴参数() + self._纵轴参数= self.计算纵轴参数() + + # 行情衍生数据 + self._定制绘图函数= self._任务参数['定制绘图函数'] + self._定制绘图参数= self._任务参数['定制绘图参数'] + + + + def 返回目标日线数据(self): + ''' + 日线换手子图用 + ''' + 日线行情= {} + 日线行情['目标指数日线']= self._目标指数日线 + 日线行情['目标绘图日线']= self._目标绘图日线 + + return 日线行情 + + + + def 返回行情附加数据(self): + ''' + 日线换手子图用 + ''' + 附加行情= {} + + 附加行情['横轴坐标序列']= self._横轴坐标序列 + 附加行情['目标行情长度']= self._目标行情长度 + + 附加行情['目标指数上涨']= self._目标指数上涨 + 附加行情['目标指数下跌']= self._目标指数下跌 + 附加行情['目标指数平盘']= self._目标指数平盘 + + 附加行情['目标绘图上涨']= self._目标绘图上涨 + 附加行情['目标绘图下跌']= self._目标绘图下跌 + 附加行情['目标绘图平盘']= self._目标绘图平盘 + + return 附加行情 + + + + def 返回本图大小(self): + ''' + + ''' + return (self._横轴参数['横轴宽度'], self._纵轴参数['纵轴高度']) + + + + def 平面初始化(self, 图片对象, 子图偏移, 全图大小): + ''' + + ''' + + + # 计算布局参数 + #======================================================================================= + 子图横移, \ + 子图纵移= 子图偏移 + + 全图宽度, \ + 全图高度= 全图大小 + + 本图宽度= self._横轴参数['横轴宽度'] + 本图高度= self._纵轴参数['纵轴高度'] + + 布局参数= ( 子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 本图高度/全图高度 ) + + self._布局参数= 布局参数 + 对数坐标底数= self._对数坐标底数 + + # 指数部分 + #======================================================================================= + 指数平面= 图片对象.add_axes(布局参数, axis_bgcolor='black') + + 指数平面.set_axisbelow(True) # 网格线放在底层 + 指数平面.set_yscale('log', basey=对数坐标底数) # 使用对数坐标 + + # 改变坐标线的颜色 + for 方位, 边框 in 指数平面.spines.items(): # 方位: 'left' | 'right' | 'top' | 'bottom' + 边框.set_color(__color_gold__) + + 指数横轴= 指数平面.get_xaxis() + 指数纵轴= 指数平面.get_yaxis() + + # 指数纵轴.set_label_position('left') # XXX: 不顶用 + + self._指数平面= 指数平面 + self._指数横轴= 指数横轴 + self._指数纵轴= 指数纵轴 + + self.设置指数横轴() + self.设置指数纵轴() + + # 个股部分 + #======================================================================================= + 个股平面= 指数平面.twinx() + 个股平面.set_axis_bgcolor('black') + 个股平面.set_axisbelow(True) # 网格线放在底层 + 个股平面.set_yscale('log', basey=对数坐标底数) # 使用对数坐标 + + # for 方位, 边框 in 个股平面.spines.items(): # 方位: 'left' | 'right' | 'top' | 'bottom' + # 边框.set_color(__color_gold__) + + 个股横轴= 个股平面.get_xaxis() + 个股纵轴= 个股平面.get_yaxis() + + # 个股纵轴.set_label_position('right') # XXX: 不顶用 + + self._个股平面= 个股平面 + self._个股横轴= 个股横轴 + self._个股纵轴= 个股纵轴 + + self.设置个股横轴() + self.设置个股纵轴() + + + + def 返回指数平面(self): + ''' + + ''' + return self._指数平面 + + + + def 计算横轴余度(self, 横轴裕量): + ''' + 被 计算横轴参数() 调用 + ''' + 目标起始偏移= self._目标起始偏移 + 目标结束偏移= self._目标结束偏移 + + 坐标起点= 目标起始偏移 - 横轴裕量 + 坐标终点= 目标结束偏移 + 横轴裕量 + 横轴尺寸= 坐标终点 - 坐标起点 + + 横轴余度= {} + 横轴余度['横轴裕量']= 横轴裕量 + 横轴余度['坐标起点']= 坐标起点 + 横轴余度['坐标终点']= 坐标终点 + 横轴余度['横轴尺寸']= 横轴尺寸 + 横轴余度['横轴倍率']= __横轴倍率__ + 横轴余度['横轴宽度']= 横轴尺寸 * __横轴倍率__ + + return 横轴余度 + + + + def 计算纵轴余度(self): + ''' + 被 计算纵轴参数() 调用 + ''' + 对数坐标底数= self._对数坐标底数 + + 个股最高= [nr for nr in self._目标绘图最高 if nr is not None] + 个股最低= [nr for nr in self._目标绘图最低 if nr is not None] + 个股开盘= [nr for nr in self._目标绘图开盘 if nr is not None] + + 指数基点= self._目标指数开盘[0] + 个股基点= round(个股开盘[0], -1) # XXX: 对复权行情来说,基点价格可能不是整数,所以需要取整。 + + 指数极高= max(self._目标指数最高) + 指数极低= min(self._目标指数最低) + + 个股极高= max(个股最高) + 个股极低= min(个股最低) + + 指数修正极低= min(指数极低, 个股极低*指数基点/个股基点) + 指数修正极高= max(指数极高, 个股极高*指数基点/个股基点) + + 个股修正极低= min(个股极低, 指数极低*个股基点/指数基点) + 个股修正极高= max(个股极高, 指数极高*个股基点/指数基点) + + 指数坐标起点= 指数修正极低 / 对数坐标底数 + 指数坐标终点= 指数修正极高 * 对数坐标底数 + 个股坐标起点= 个股修正极低 / 对数坐标底数 + 个股坐标终点= 个股修正极高 * 对数坐标底数 + + # 计算 纵轴尺寸。 + # XXX: 注意,用个股坐标或指数坐标计算都可以,其中包含的倍数因子对结果无影响,即: + # log(base, n1) - log(base, n2) == + # log(base, n1/n2) == + # log(base, k*n1/k*n2) == + # log(base, k*n1) - log(base, k*n2) + # ,这是对数运算的性质。 + 纵轴尺寸= math.log(个股坐标终点, 对数坐标底数) - math.log(个股坐标起点, 对数坐标底数) + + 纵轴余度= { + '纵轴倍率': __纵轴倍率__, + '纵轴尺寸': 纵轴尺寸, + '纵轴高度': 纵轴尺寸 * __纵轴倍率__, + '个股基点': 个股基点, + '指数基点': 指数基点, + '个股坐标起点': 个股坐标起点, + '个股坐标终点': 个股坐标终点, + '指数坐标起点': 指数坐标起点, + '指数坐标终点': 指数坐标终点, + } + + return 纵轴余度 + + + + def 计算横轴参数(self): + ''' + + ''' + 全局日期序列= self._全局日期序列 + 目标起始偏移= self._目标起始偏移 + 目标结束偏移= self._目标结束偏移 + + 全局日期列表= [ datetime.date(int(ys), int(ms), int(ds)) for ys, ms, ds in [ dstr.split('-') for dstr in 全局日期序列 ] ] + 目标日期列表= 全局日期列表[目标起始偏移:目标结束偏移+1] + + # 确定 X 轴的 主要坐标点 + 每月首日偏移= [] # 每个月第一个交易日在所有日期列表中的 index + 所有年份= sorted(set([d.year for d in 目标日期列表])) + + for 年份 in 所有年份: + 当年月份= sorted(set([d.month for d in 目标日期列表 if d.year==年份])) # 当年所有的交易月份 + for 月份 in 当年月份: + 当月首日= min([d for d in 目标日期列表 if d.year==年份 and d.month==月份]) # 当月的第一个交易日 + 每月首日偏移.append(全局日期列表.index(当月首日)) + + xMajorLocator= ticker.FixedLocator(numpy.array(每月首日偏移)) + + # 确定 X 轴的 辅助坐标点 + 每周首日偏移= {} # value: 每周第一个交易日在所有日期列表中的 index; key: 当周的序号 week number(当周是第几周) + + for 日期 in 目标日期列表: + isoyear, weekno= 日期.isocalendar()[0:2] + dmark= isoyear*100 + weekno + if dmark not in 每周首日偏移: + 每周首日偏移[dmark]= 全局日期列表.index(日期) + + 每周首日偏移= sorted(每周首日偏移.values()) + + xMinorLocator= ticker.FixedLocator(numpy.array(每周首日偏移)) + + # 确定 X 轴的 MajorFormatter 和 MinorFormatter + def x_major_formatter(idx, pos=None): + return 全局日期列表[idx].strftime('%Y-%m-%d') + + def x_minor_formatter(idx, pos=None): + return 全局日期列表[idx].strftime('%m-%d') + + xMajorFormatter= ticker.FuncFormatter(x_major_formatter) + xMinorFormatter= ticker.FuncFormatter(x_minor_formatter) + + # 填充并返回 + 横轴参数= {} + + 横轴参数['xMajorLocator']= xMajorLocator + 横轴参数['xMinorLocator']= xMinorLocator + 横轴参数['xMajorFormatter']= xMajorFormatter + 横轴参数['xMinorFormatter']= xMinorFormatter + 横轴参数['每月首日偏移']= 每月首日偏移 + 横轴参数['每周首日偏移']= 每周首日偏移 + + 横轴裕量= 1 + 横轴余度= self.计算横轴余度(横轴裕量=横轴裕量) + 横轴参数.update(横轴余度) + + return 横轴参数 + + + + def 返回横轴参数(self): + ''' + + ''' + return self._横轴参数 + + + + def 计算纵轴参数(self): + ''' + 计算纵轴坐标点 + ''' + 步进倍率= 1.1 + + 纵轴余度= self.计算纵轴余度() + + 个股坐标起点= 纵轴余度['个股坐标起点'] + 个股坐标终点= 纵轴余度['个股坐标终点'] + + 个股基点= 纵轴余度['个股基点'] + 指数基点= 纵轴余度['指数基点'] + + # 计算个股主坐标值 + #===================================================================================================== + 个股主坐标值= [个股基点] + + # 往下拓展 + for i in range(1, 999): + 当前坐标= round(个股基点/(步进倍率**i), -1) # 坐标值 7890 表示 7.89 元 + if 当前坐标 <= 个股坐标起点: + break + 个股主坐标值.insert(0, 当前坐标) + # 往上拓展 + for i in range(1, 999): + 当前坐标= round(个股基点*(步进倍率**i), -1) # 坐标值 7890 表示 7.89 元 + if 当前坐标 >= 个股坐标终点: + break + 个股主坐标值.append(当前坐标) + + # 计算个股副坐标值 + #===================================================================================================== + 个股副坐标值= [round((curr*next)**0.5, -1) for curr, next in zip(个股主坐标值[:-1], 个股主坐标值[1:])] + 极值= round((个股主坐标值[0]*个股主坐标值[0]/步进倍率)**0.5, -1) + if 极值 > 个股坐标起点: 个股副坐标值.insert(0, 极值) + 极值= round((个股主坐标值[-1]*个股主坐标值[-1]*步进倍率)**0.5, -1) + if 极值 < 个股坐标终点: 个股副坐标值.append(极值) + + # 计算指数主、副坐标值 + #===================================================================================================== + 指数主坐标值= [数值*指数基点/个股基点 for 数值 in 个股主坐标值] + 指数副坐标值= [数值*指数基点/个股基点 for 数值 in 个股副坐标值] + + # 生成、返回 + #===================================================================================================== + 纵轴参数= { + '个股主坐标值': 个股主坐标值, + '个股副坐标值': 个股副坐标值, + '指数主坐标值': 指数主坐标值, + '指数副坐标值': 指数副坐标值, + } + 纵轴参数.update(纵轴余度) + + return 纵轴参数 + + + + def 设置指数横轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 横轴对象= self._指数横轴 + 横轴参数= self._横轴参数 + + 横轴裕量= 横轴参数['横轴裕量'] + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + xMajorFormatter= 横轴参数['xMajorFormatter'] + xMinorFormatter= 横轴参数['xMinorFormatter'] + + 横轴对象.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 横轴对象.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + 指数平面.set_xlim(坐标起点, 坐标终点) + 横轴对象.set_major_locator(xMajorLocator) + 横轴对象.set_minor_locator(xMinorLocator) + + for 主坐标 in 指数平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 指数平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置个股横轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 横轴对象= self._个股横轴 + 横轴参数= self._横轴参数 + + 横轴裕量= 横轴参数['横轴裕量'] + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + xMajorFormatter= 横轴参数['xMajorFormatter'] + xMinorFormatter= 横轴参数['xMinorFormatter'] + + # 横轴对象.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # 横轴对象.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + 个股平面.set_xlim(坐标起点, 坐标终点) + 横轴对象.set_major_locator(xMajorLocator) + 横轴对象.set_minor_locator(xMinorLocator) + + for 主坐标 in 个股平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + for 副坐标 in 个股平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置指数纵轴(self): + ''' + + ''' + 指数平面= self._指数平面 + 纵轴对象= self._指数纵轴 + 纵轴参数= self._纵轴参数 + 坐标起点= 纵轴参数['指数坐标起点'] + 坐标终点= 纵轴参数['指数坐标终点'] + + 纵轴对象.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 纵轴对象.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + 指数平面.set_ylim(坐标起点, 坐标终点) + + # 主坐标点 + #====================================================================================== + 主坐标值= 纵轴参数['指数主坐标值'] + yMajorLocator= ticker.FixedLocator(numpy.array(主坐标值)) + def y_major_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + + 纵轴对象.set_major_locator(yMajorLocator) + 纵轴对象.set_major_formatter(yMajorFormatter) + + for 主坐标 in 指数平面.get_yticklabels(minor=False): + 主坐标.set_fontsize(6) + + # 副坐标点 + #====================================================================================== + 副坐标值= 纵轴参数['指数副坐标值'] + yMinorLocator= ticker.FixedLocator(numpy.array(副坐标值)) + def y_minor_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + + 纵轴对象.set_minor_locator(yMinorLocator) + 纵轴对象.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 指数平面.get_yticklabels(minor=True): + 副坐标.set_fontsize(5) + 副坐标.set_color('blue') + + + + def 设置个股纵轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 纵轴对象= self._个股纵轴 + 纵轴参数= self._纵轴参数 + 坐标起点= 纵轴参数['个股坐标起点'] + 坐标终点= 纵轴参数['个股坐标终点'] + + # 纵轴对象.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # 纵轴对象.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + 个股平面.set_ylim(坐标起点, 坐标终点) + + # 主坐标点 + #====================================================================================== + 主坐标值= 纵轴参数['个股主坐标值'] + yMajorLocator= ticker.FixedLocator(numpy.array(主坐标值)) + def y_major_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + + 纵轴对象.set_major_locator(yMajorLocator) + 纵轴对象.set_major_formatter(yMajorFormatter) + + for 主坐标 in 个股平面.get_yticklabels(minor=False): + 主坐标.set_fontsize(6) + # 副坐标点 + #====================================================================================== + 副坐标值= 纵轴参数['个股副坐标值'] + yMinorLocator= ticker.FixedLocator(numpy.array(副坐标值)) + def y_minor_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + + 纵轴对象.set_minor_locator(yMinorLocator) + 纵轴对象.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 个股平面.get_yticklabels(minor=True): + 副坐标.set_fontsize(5) + 副坐标.set_color('blue') + + + + def 绘制指数行情走势(self): + ''' + 画 K 线 + ''' + 指数平面= self._指数平面 + 横轴坐标序列= self._横轴坐标序列 + + 开盘序列= copy.copy(self._目标指数开盘) + 收盘序列= copy.copy(self._目标指数收盘) + 最高序列= self._目标指数最高 + 最低序列= self._目标指数最低 + + 上涨序列= self._目标指数上涨 + 下跌序列= self._目标指数下跌 + 平盘序列= self._目标指数平盘 + + # 对开收盘价进行视觉修正 + for idx, (po, pc) in enumerate(zip(开盘序列, 收盘序列)): + if po is not None and po==pc: + 调整= int(po * 0.0005) + 开盘序列[idx]= po + 调整 + 收盘序列[idx]= pc - 调整 + + 开盘阵列= numpy.array(开盘序列) + 收盘阵列= numpy.array(收盘序列) + 最高阵列= numpy.array(最高序列) + 最低阵列= numpy.array(最低序列) + + if True in 上涨序列: + 指数平面.vlines(横轴坐标序列[上涨序列], 最低阵列[上涨序列], 最高阵列[上涨序列], edgecolor='0.7', linewidth=0.6, label='_nolegend_', alpha=0.5) + 指数平面.vlines(横轴坐标序列[上涨序列], 开盘阵列[上涨序列], 收盘阵列[上涨序列], edgecolor='0.7', linewidth=3.0, label='_nolegend_', alpha=0.5) + + if True in 下跌序列: + 指数平面.vlines(横轴坐标序列[下跌序列], 最低阵列[下跌序列], 最高阵列[下跌序列], edgecolor='0.3', linewidth=0.6, label='_nolegend_', alpha=0.5) + 指数平面.vlines(横轴坐标序列[下跌序列], 开盘阵列[下跌序列], 收盘阵列[下跌序列], edgecolor='0.3', linewidth=3.0, label='_nolegend_', alpha=0.5) + + if True in 平盘序列: + 指数平面.vlines(横轴坐标序列[平盘序列], 最低阵列[平盘序列], 最高阵列[平盘序列], edgecolor='1.0', linewidth=0.6, label='_nolegend_', alpha=1.0) + 指数平面.vlines(横轴坐标序列[平盘序列], 开盘阵列[平盘序列], 收盘阵列[平盘序列], edgecolor='1.0', linewidth=3.0, label='_nolegend_', alpha=1.0) + + + + def 绘制个股行情走势(self): + ''' + 画 K 线 + ''' + 个股平面= self._个股平面 + 横轴坐标序列= self._横轴坐标序列 + + 开盘序列= copy.copy(self._目标绘图开盘) + 收盘序列= copy.copy(self._目标绘图收盘) + 最高序列= self._目标绘图最高 + 最低序列= self._目标绘图最低 + + 上涨序列= self._目标绘图上涨 + 下跌序列= self._目标绘图下跌 + 平盘序列= self._目标绘图平盘 + + # 对开收盘价进行视觉修正 + for idx, (po, pc) in enumerate(zip(开盘序列, 收盘序列)): + if po is not None and po==pc: + 调整= int(po * 0.0005) + 开盘序列[idx]= po + 调整 + 收盘序列[idx]= pc - 调整 + + 开盘阵列= numpy.array(开盘序列) + 收盘阵列= numpy.array(收盘序列) + 最高阵列= numpy.array(最高序列) + 最低阵列= numpy.array(最低序列) + + if True in 上涨序列: + 个股平面.vlines(横轴坐标序列[上涨序列], 最低阵列[上涨序列], 最高阵列[上涨序列], edgecolor='red', linewidth=0.6, label='_nolegend_', alpha=0.5) + 个股平面.vlines(横轴坐标序列[上涨序列], 开盘阵列[上涨序列], 收盘阵列[上涨序列], edgecolor='red', linewidth=3.0, label='_nolegend_', alpha=0.5) + + if True in 下跌序列: + 个股平面.vlines(横轴坐标序列[下跌序列], 最低阵列[下跌序列], 最高阵列[下跌序列], edgecolor='green', linewidth=0.6, label='_nolegend_', alpha=0.5) + 个股平面.vlines(横轴坐标序列[下跌序列], 开盘阵列[下跌序列], 收盘阵列[下跌序列], edgecolor='green', linewidth=3.0, label='_nolegend_', alpha=0.5) + + if True in 平盘序列: + 个股平面.vlines(横轴坐标序列[平盘序列], 最低阵列[平盘序列], 最高阵列[平盘序列], edgecolor='0.7', linewidth=0.6, label='_nolegend_', alpha=0.5) + 个股平面.vlines(横轴坐标序列[平盘序列], 开盘阵列[平盘序列], 收盘阵列[平盘序列], edgecolor='0.7', linewidth=3.0, label='_nolegend_', alpha=0.5) + + + + def 绘制个股开收中线走势(self): + ''' + + ''' + 个股平面= self._个股平面 + + 横轴坐标序列= self._横轴坐标序列 + 目标行情长度= self._目标行情长度 + 个股衍生日期= self._个股衍生行情['日期'] + 全局日期序列= self._全局日期序列 + 目标日期序列= self._目标日期序列 + 目标绘图中线= self._目标绘图中线 + + 目标首日= 目标日期序列[0] + 目标尾日= 目标日期序列[-1] + + 中线阈值= 30 + 长线阈值= 90 + + # 绘制中线 + 中线阵列= numpy.array(目标绘图中线) + 个股平面.plot(横轴坐标序列, 中线阵列, 'o-', color='white', linewidth=0.2, label='个股中线', \ + markersize=0.3, markeredgecolor='white', markeredgewidth=0.1, alpha=1.0) + + # 计算拐点位置及关键度 + 走势拐点= self._个股衍生行情['开收中线走势拐点'] + + 顶点序列= [None] * 目标行情长度 + 底点序列= [None] * 目标行情长度 + + for 拐点 in 走势拐点: + 日期= 个股衍生日期[拐点['偏移']] + if 日期 < 目标首日 or 日期 > 目标尾日: + continue + + 偏移= 目标日期序列.index(日期) + 横标= 全局日期序列.index(日期) + 位置= 目标绘图中线[偏移] + if 拐点['类型'] == '^': + 顶点序列[偏移]= 位置 + 纵标= 位置 * 1.01 + valign= 'bottom' + else: + 底点序列[偏移]= 位置 + 纵标= 位置 / 1.01 + valign= 'top' + + 关键度= 拐点['关键度'] + + if 关键度 < 中线阈值: + continue + + if 关键度 < 中线阈值: + fsize= 2.3 + weight= 300 + elif 关键度 < 长线阈值: + fsize= 2.7 + weight= 600 + else: + fsize= 3.3 + weight= 900 + + 注释= 个股平面.text(横标, 纵标, str(关键度), weight=weight, fontsize=fsize, verticalalignment=valign, horizontalalignment='center', color='white', alpha=0.7) + + # 绘制拐点 + 底点阵列= numpy.array(底点序列) + 顶点阵列= numpy.array(顶点序列) + + 个股平面.plot(横轴坐标序列, 底点阵列, marker=6, color='white', label='个股中线底点', \ + markersize=2.3, markeredgecolor='white', markeredgewidth=0.0, alpha=0.7) + + 个股平面.plot(横轴坐标序列, 顶点阵列, marker=7, color='white', label='个股中线顶点', \ + markersize=2.3, markeredgecolor='white', markeredgewidth=0.0, alpha=0.7) + + + + def 绘制指数均线走势(self): + ''' + + ''' + 横轴坐标序列= self._横轴坐标序列 + 指数平面= self._指数平面 + 均线字典= self._目标指数均线 + + widthw= 0.2 + widthn= 0.1 + mksize= 0.3 + mkwidth= 0.1 + alpha= 1.0 + + for n, seq in 均线字典.items(): + rarray= numpy.array( seq ) + color= 'white' if n < 10 else \ + 'yellow' if n < 30 else \ + 'cyan' if n < 60 else \ + 'magenta' + + 指数平面.plot(横轴坐标序列, rarray, 'o-', color=color, linewidth=widthw, label='指数均线_'+str(n), \ + markersize=mksize, markeredgecolor=color, markeredgewidth=mkwidth, alpha=alpha) + + + + def 绘制个股均线走势(self): + ''' + + ''' + 横轴坐标序列= self._横轴坐标序列 + 个股平面= self._个股平面 + 均线字典= self._目标绘图均线 + + widthw= 0.2 + widthn= 0.1 + mksize= 0.3 + mkwidth= 0.1 + alpha= 0.7 + + for n, seq in 均线字典.items(): + rarray= numpy.array( seq ) + color= 'white' if n < 10 else \ + 'yellow' if n < 30 else \ + 'cyan' if n < 60 else \ + 'magenta' + + 个股平面.plot(横轴坐标序列, rarray, 'o-', color=color, linewidth=widthw, label='个股均线_'+str(n), \ + markersize=mksize, markeredgecolor=color, markeredgewidth=mkwidth, alpha=alpha) + + + + def 绘制复权提示(self): + ''' + + ''' + 个股平面= self._个股平面 + 纵轴参数= self._纵轴参数 + 目标日期序列= self._目标日期序列 + 全局日期序列= self._全局日期序列 + + 纵标起点= 纵轴参数['个股坐标起点'] + 纵标终点= 纵轴参数['个股坐标终点'] + 目标首日= 目标日期序列[0] + 目标尾日= 目标日期序列[-1] + + 复权记录= self._复权记录 + + fsize= 5.0 + ycoord= 纵标终点/1.1 + alpha= 0.7 + + for 日期, 比率 in 复权记录: + if 日期<目标首日 or 日期>目标尾日: continue + 横标= 全局日期序列.index(日期) + 个股平面.plot([横标, 横标], [纵标起点, 纵标终点], '-', color='purple', linewidth=0.1) + label= 个股平面.text( \ + 横标, ycoord, \ + '复权 ' + str(比率) + '\n' + 日期, \ + fontproperties=__font_properties__, \ + horizontalalignment='left', \ + verticalalignment='top', \ + color='purple', \ + alpha=alpha + ) + label.set_fontsize(fsize) + + + + def 绘制流通股本变更提示(self): + ''' + 注意两个问题: + 1. 流通股本变更提示中的日期可能不是交易日期 + 2. 流通股本变更提示涵盖个股的所有历史,有些内容可能在绘图目标区间以外 + ''' + 个股平面= self._个股平面 + 纵轴参数= self._纵轴参数 + 公司信息= self._公司信息 + 目标日期序列= self._目标日期序列 + 全局日期序列= self._全局日期序列 + + 纵标起点= 纵轴参数['个股坐标起点'] + 纵标终点= 纵轴参数['个股坐标终点'] + 目标首日= 目标日期序列[0] + 目标尾日= 目标日期序列[-1] + + 变更记录= [记录 for 记录 in 公司信息['流通股变更'] if 记录['变更日期']>=目标首日 and 记录['变更日期']<=目标尾日] + + fsize= 5.0 + ycoord= 纵标终点/1.05 + alpha= 0.7 + + for 记录 in 变更记录: + 变更日期= 记录['变更日期'] + 比率= 记录['变更比'] + 调整日期= [ds for ds in 目标日期序列 if ds >= 变更日期][0] + 横标= 全局日期序列.index(调整日期) + 个股平面.plot([横标, 横标], [纵标起点, 纵标终点], '-', color='yellow', linewidth=0.1) + label= 个股平面.text( \ + 横标, ycoord, \ + '流通股 ' + str(比率) + '\n' + 变更日期, \ + fontproperties=__font_properties__, \ + horizontalalignment='left', \ + verticalalignment='top', \ + color='yellow', \ + alpha=alpha + ) + label.set_fontsize(fsize) + + + + def 绘制首尾股本信息(self): + ''' + + ''' + def 寻找纵轴最大空隙(偏移): + ''' + 找出 X 轴某个位置图中最大的空隙 + ''' + 指数基点= 纵轴参数['指数基点'] + 指数目标= 全局指数开盘[偏移] + + 个股基点= 纵轴参数['个股基点'] + 个股目标= 全局绘图开盘[偏移] + + if 个股目标 is None: + try: + 个股目标= [nr for nr in 全局绘图开盘[:偏移] if nr is not None][-1] + except IndexError: + 个股目标= [nr for nr in 全局绘图开盘[偏移:] if nr is not None][0] + 调整目标= 指数目标 * 个股基点 / 指数基点 + + 比较列表= [坐标起点, 个股目标, 调整目标, 坐标终点] + 比较列表.sort() + 差值, 低点, 高点= max( [ (math.log(n2/n1, 对数坐标底数), n1, n2) for n1, n2 in zip(比较列表[:-1], 比较列表[1:]) ] ) + + return (低点*高点)**0.5 # 相乘再开平方,在 log 坐标系里看起来就是在中间位置。 + + + + def 整数分位表示(整数): + ''' + 123456789 --> '1,2345,6789' + ''' + if type(整数) is not int: return str(整数) + + if 整数 == 0: return '0' + + if 整数 < 0: + 整数= -整数 + 为负= True + else: + 为负= False + + intstr= str(整数) + 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 为负 else ','.join(intlist) + + 纵轴参数= self._纵轴参数 + 公司信息= self._公司信息 + 对数坐标底数= self._对数坐标底数 + + 全局指数开盘= self._全局指数日线['开盘'] + 全局绘图开盘= self._全局绘图日线['开盘'] + 目标日期序列= self._目标日期序列 + 目标起始偏移= self._目标起始偏移 + 目标结束偏移= self._目标结束偏移 + + 坐标起点= 纵轴参数['个股坐标起点'] + 坐标终点= 纵轴参数['个股坐标终点'] + 变更记录= 公司信息['股本变更记录'] + + 个股平面= self._个股平面 + + fsize= 5.0 + + # 首日总股本与流通股信息 + #==================================================================================================================================== + 目标首日= 目标日期序列[0] + 前期记录= [rec for rec in 变更记录 if rec['变更日期'] <= 目标首日] + + if 前期记录: + 总股本= 整数分位表示(前期记录[-1]['总股本']) + 流通股= 整数分位表示(前期记录[-1]['流通股']) + else: + 总股本= 'None' + 流通股= 'None' + + 显示字串= '总股本: ' + 总股本 + '\n' + '流通股: ' + 流通股 + + label= 个股平面.text(目标起始偏移, 寻找纵轴最大空隙(偏移=目标起始偏移), 显示字串, fontproperties=__font_properties__, color='0.7') + label.set_fontsize(fsize) + + # 尾日总股本与流通股信息 + #==================================================================================================================================== + 目标尾日= 目标日期序列[-1] + 前期记录= [rec for rec in 变更记录 if rec['变更日期'] <= 目标尾日] + + if 前期记录: + 总股本= 整数分位表示(前期记录[-1]['总股本']) + 流通股= 整数分位表示(前期记录[-1]['流通股']) + else: + 总股本= 'None' + 流通股= 'None' + + 显示字串= '总股本: ' + 总股本 + '\n' + '流通股: ' + 流通股 + + label= 个股平面.text(目标结束偏移, 寻找纵轴最大空隙(偏移=目标结束偏移), 显示字串, fontproperties=__font_properties__, horizontalalignment='right', color='0.7') + label.set_fontsize(fsize) + + + + def 绘制日期提示(self): + ''' + + ''' + 个股平面= self._个股平面 + 横轴参数= self._横轴参数 + 纵轴参数= self._纵轴参数 + 全局日期序列= self._全局日期序列 + + 每月首日偏移= 横轴参数['每月首日偏移'] + 每周首日偏移= 横轴参数['每周首日偏移'] + + 坐标起点= 纵轴参数['个股坐标起点'] + 坐标终点= 纵轴参数['个股坐标终点'] + + # 每月第一个交易日 + for iy in [坐标起点*1.2, 坐标终点/1.12]: + for ix in 每月首日偏移: + newlab= 个股平面.text(ix-1, iy, 全局日期序列[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('center') + # newlab.set_verticalalignment('bottom') + newlab.set_zorder(0) # XXX: 放在底层 + + # 每周第一个交易日,根据这个可以推算出全部确切的日期。 + for iy in [坐标起点*1.08, 坐标终点/1.03]: + for ix in 每周首日偏移: + newlab= 个股平面.text(ix-0.8, iy, 全局日期序列[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 绘制价格提示(self): + ''' + + ''' + + 个股平面= self._个股平面 + 横轴参数= self._横轴参数 + 纵轴参数= self._纵轴参数 + + 每月首日偏移= 横轴参数['每月首日偏移'] + + 个股主坐标值= 纵轴参数['个股主坐标值'] + 个股副坐标值= 纵轴参数['个股副坐标值'] + 指数主坐标值= 纵轴参数['指数主坐标值'] + 指数副坐标值= 纵轴参数['指数副坐标值'] + + for iy, iy2 in zip(sorted(个股主坐标值[:-1] + 个股副坐标值[1:-1]), sorted(指数主坐标值[:-1] + 指数副坐标值[1:-1])): + for ix in 每月首日偏移[1:-1:3]: + newlab= 个股平面.text( ix+6, iy*1.001, str(iy/1000.0) + ' / ' + str(round(iy2/1000.0, 3)) ) + newlab.set_font_properties(__font_properties__) + newlab.set_color('0.3') + newlab.set_fontsize(3) + newlab.set_zorder(0) # XXX: 放在底层 + + + + def 绘制常用辅助标记(self): + ''' + + ''' + + def 绘制指数均线拐点(): + ''' + + ''' + 均线走势标记= 指数衍生行情['均线走势标记集'] + + for n in 指数衍生均线.keys(): + 均线序列= 指数衍生均线[n] + 走势标记= 均线走势标记[n] + + 拐点序列= [ (目标日期序列.index(日期), 数值, 标记) for 日期, 数值, 标记 in zip(指数衍生日期, 均线序列, 走势标记) \ + if 日期>=目标首日 and 日期<=目标尾日 and 标记 in ('v', '^') ] + + 底点序列= [None] * 目标行情长度 + 顶点序列= [None] * 目标行情长度 + for 偏移, 数值, 标记 in 拐点序列: + if 标记=='v': + 底点序列[偏移]= 数值 + elif 标记=='^': + 顶点序列[偏移]= 数值 + + 底点阵列= numpy.array(底点序列) + 顶点阵列= numpy.array(顶点序列) + + color= 'white' if n < 10 else \ + 'yellow' if n < 30 else \ + 'cyan' if n < 60 else \ + 'magenta' + + 指数平面.plot(横轴坐标序列, 底点阵列, marker=6, color=color, label='指数均线底点_'+str(n), \ + markersize=2.3, markeredgecolor=color, markeredgewidth=0.0, alpha=0.5) + 指数平面.plot(横轴坐标序列, 顶点阵列, marker=7, color=color, label='指数均线顶点_'+str(n), \ + markersize=2.3, markeredgecolor=color, markeredgewidth=0.0, alpha=0.5) + + + + def 绘制三日线拐点关键度(): + ''' + + ''' + 三日线拐点= 个股衍生行情['三日线走势拐点'] + 中线阈值= 30 + 长线阈值= 90 + + for 拐点 in 三日线拐点: + 关键度= 拐点['关键度'] + if 关键度 < 中线阈值: + continue + + 偏移= 拐点['偏移'] + 日期= 个股衍生日期[偏移] + if 日期 < 目标首日 or 日期 > 目标尾日: + continue + + 类型= 拐点['类型'] + 位置= 三日线[偏移] if 复权绘图 else 三日线[目标日期序列.index(日期)] + + 横标= 全局日期序列.index(日期) + 纵标= 位置*1.01 if 类型=='^' else 位置/1.01 + + # 添加文字注释 + 文字= str(关键度) + # 文字= str(关键度) + '(' + str(偏移) + ')' + valign= 'bottom' if 类型=='^' else 'top' + if 关键度 < 中线阈值: + fsize= 2.3 + weight= 300 + elif 关键度 < 长线阈值: + fsize= 2.7 + weight= 600 + else: + fsize= 3.3 + weight= 900 + + 注释= 个股平面.text(横标, 纵标, 文字, weight=weight, verticalalignment=valign, horizontalalignment='left', color='white', alpha=0.7) + 注释.set_fontsize(fsize) + + + + def 绘制个股均线拐点(): + ''' + + ''' + 均线走势标记= 个股衍生行情['均线走势标记集'] + + for n in 个股衍生均线.keys(): + 走势标记= 均线走势标记[n] + + if 复权绘图: + 均线序列= 个股衍生均线[n] # 使用复权均线 + 拐点序列= [ (目标日期序列.index(日期), 数值, 标记) for 日期, 数值, 标记 in zip(个股衍生日期, 均线序列, 走势标记) \ + if 日期>=目标首日 and 日期<=目标尾日 and 标记 in ('v', '^') ] + 底点序列= [None] * 目标行情长度 + 顶点序列= [None] * 目标行情长度 + for 偏移, 数值, 标记 in 拐点序列: + if 标记=='v': + 底点序列[偏移]= 数值 + elif 标记=='^': + 顶点序列[偏移]= 数值 + else: + 均线序列= 目标绘图均线[n] # 使用未复权的均线 + 拐点序列= [ (目标日期序列.index(日期), 日期, 标记) for 日期, 标记 in zip(个股衍生日期, 走势标记) \ + if 日期>=目标首日 and 日期<=目标尾日 and 标记 in ('v', '^') ] + 底点序列= [None] * 目标行情长度 + 顶点序列= [None] * 目标行情长度 + for 偏移, 日期, 标记 in 拐点序列: + if 标记=='v': + 底点序列[偏移]= 均线序列[目标日期序列.index(日期)] + elif 标记=='^': + 顶点序列[偏移]= 均线序列[目标日期序列.index(日期)] + + 底点阵列= numpy.array(底点序列) + 顶点阵列= numpy.array(顶点序列) + + color= 'white' if n < 10 else \ + 'yellow' if n < 30 else \ + 'cyan' if n < 60 else \ + 'magenta' + + 个股平面.plot(横轴坐标序列, 底点阵列, marker=6, color=color, label='个股均线底点_'+str(n), \ + markersize=2.3, markeredgecolor=color, markeredgewidth=0.0, alpha=0.7) + + 个股平面.plot(横轴坐标序列, 顶点阵列, marker=7, color=color, label='个股均线顶点_'+str(n), \ + markersize=2.3, markeredgecolor=color, markeredgewidth=0.0, alpha=0.7) + + + + def 绘制三日线波段(): + ''' + + ''' + 阶梯记录= 个股衍生行情['三日线走势阶梯'] + + for 记录 in 阶梯记录: + 升幅= 记录['升幅'] + 升跨= 记录['升跨'] + 降幅= 记录['降幅'] + 降跨= 记录['降跨'] + + 前底偏移= 记录['前底偏移'] + 顶点偏移= 记录['顶点偏移'] + 后底偏移= 顶点偏移 + 降跨 + 前底日期= 个股衍生日期[前底偏移] + 顶点日期= 个股衍生日期[顶点偏移] + 后底日期= 个股衍生日期[后底偏移] + + # 上升波段 + if 前底日期>=目标首日 and 顶点日期<=目标尾日 and 升幅>=900.0: + 横标= 全局日期序列.index(前底日期) + 0.5 + 纵标= 三日线[前底偏移] if 复权绘图 else 三日线[目标日期序列.index(前底日期)] + 宽度= 全局日期序列.index(顶点日期) - 横标 + 0.5 + 高度= 三日线[顶点偏移]-纵标 if 复权绘图 else 三日线[目标日期序列.index(顶点日期)]-纵标 + rectobj= patches.Rectangle((横标, 纵标), 宽度, 高度, fill=True, edgecolor=None, facecolor='magenta', linewidth=0.3, alpha=0.1) + rectobj.set_zorder(-1) # 放在底层 + 个股平面.add_patch(rectobj) + + 文字 = str(round(升幅, 2)) + 文字 += '\n' + str(round(升幅/升跨, 2)) + 文字 += '\n' + str(升跨) + 注释= 个股平面.text(横标, 纵标, 文字, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left', color=__color_gray70__) + 注释.set_fontsize(2.5) + + # 下降波段 + if 顶点日期>=目标首日 and 后底日期<=目标尾日 and 降幅<=-900.0: + 横标= 全局日期序列.index(顶点日期) + 0.5 + 纵标= 三日线[后底偏移] if 复权绘图 else 三日线[目标日期序列.index(后底日期)] + 宽度= 全局日期序列.index(后底日期) - 横标 + 0.5 + 高度= 三日线[顶点偏移]-纵标 if 复权绘图 else 三日线[目标日期序列.index(顶点日期)]-纵标 + rectobj= patches.Rectangle((横标, 纵标), 宽度, 高度, fill=True, edgecolor=None, facecolor='cyan', linewidth=0.3, alpha=0.1) + rectobj.set_zorder(-1) # 放在底层 + 个股平面.add_patch(rectobj) + + 文字 = str(round(降幅, 2)) + 文字 += '\n' + str(round(降幅/降跨, 2)) + 文字 += '\n' + str(降跨) + 注释= 个股平面.text(横标, 纵标, 文字, fontproperties=__font_properties__, verticalalignment='top', horizontalalignment='left', color=__color_gray70__) + 注释.set_fontsize(2.5) + + + + 横轴坐标序列= self._横轴坐标序列 + 目标行情长度= self._目标行情长度 + 目标日期序列= self._目标日期序列 + 全局日期序列= self._全局日期序列 + 目标首日= 目标日期序列[0] + 目标尾日= 目标日期序列[-1] + + 指数衍生行情= self._指数衍生行情 + 个股衍生行情= self._个股衍生行情 + + 指数衍生日期= 指数衍生行情['日期'] + 个股衍生日期= 个股衍生行情['日期'] + + 复权绘图= self._复权绘图 + + # 三日线= 个股衍生均线[3] if 复权绘图 else 目标绘图均线[3] + + 指数平面= self._指数平面 + 个股平面= self._个股平面 + + if self._绘制指数均线: + 指数衍生均线= 指数衍生行情['均线集'] + 绘制指数均线拐点() + if self._绘制个股均线: + 目标绘图均线= self._目标绘图均线 + 个股衍生均线= 个股衍生行情['均线集'] + 绘制个股均线拐点() + + # 绘制三日线波段() + # 绘制三日线拐点关键度() + + + + def 绘图(self): + ''' + + ''' + self.绘制指数行情走势() + self.绘制个股行情走势() + if self._绘制指数均线: + self.绘制指数均线走势() + if self._绘制个股均线: + self.绘制个股均线走势() + + self.绘制个股开收中线走势() + + self.绘制流通股本变更提示() + self.绘制复权提示() + self.绘制首尾股本信息() + self.绘制日期提示() + self.绘制价格提示() + + self.绘制常用辅助标记() + + if self._定制绘图函数: + 用户参数= self._定制绘图参数 + + 用户参数.update( + { '日线子图': self, + 'ilogger': self._ilogger, + 'Public': Public, + } + ) + + self._定制绘图函数(**用户参数) + + + + # 日线活跃度.绘图(日线子图=self, 环境参数=环境参数) + # 短线组合形态.绘图(日线子图=self, 环境参数=环境参数) + # 三日线涨幅差值.绘图(日线子图=self, 环境参数=环境参数) + # 日线情势判断.绘图(日线子图=self, 环境参数=环境参数) + + + diff --git a/vn.training/SubPlot/日线换手子图.py b/vn.training/SubPlot/日线换手子图.py new file mode 100644 index 00000000..bd3cc01a --- /dev/null +++ b/vn.training/SubPlot/日线换手子图.py @@ -0,0 +1,488 @@ +# -*- coding: utf-8 -*- + + + +import itertools +import numpy + +import matplotlib.spines as spines +import matplotlib.ticker as ticker +import matplotlib.font_manager as font_manager + + + +import Public.Public as Public + + + +__color_gold__= '#FDDB05' +__font_properties__= font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc') + + + +__横轴倍率__= 10.0 / 230.0 +__纵轴倍率__= 0.3 + + + + + +class 日线换手子图: + + def __init__(self, parent, 绘图数据): + ''' + + ''' + self._parent= parent + self._ilogger= Public.IndentLogger(logger=parent._ilogger._logger, indent=parent._ilogger._indent+1) + + # 原始数据 + self._任务参数= 绘图数据['任务参数'] + + # 行情数据 + 日线价格子图= parent._日线价格子图 + + 日线行情= 日线价格子图.返回目标日线数据() + + self._目标指数日线= 日线行情['目标指数日线'] + self._目标绘图日线= 日线行情['目标绘图日线'] + + self._目标指数换手= self._目标指数日线['换手率'] + self._目标绘图换手= self._目标绘图日线['换手率'] + + # 行情衍生数据 + 附加行情= 日线价格子图.返回行情附加数据() + + self._横轴坐标序列= 附加行情['横轴坐标序列'] + self._目标行情长度= 附加行情['目标行情长度'] + + self._目标指数上涨= 附加行情['目标指数上涨'] + self._目标指数下跌= 附加行情['目标指数下跌'] + self._目标指数平盘= 附加行情['目标指数平盘'] + + self._目标绘图上涨= 附加行情['目标绘图上涨'] + self._目标绘图下跌= 附加行情['目标绘图下跌'] + self._目标绘图平盘= 附加行情['目标绘图平盘'] + + # 平面对象 + self._指数平面= None + self._指数横轴= None + self._指数纵轴= None + + self._个股平面= None + self._个股横轴= None + self._个股纵轴= None + + # 横轴参数、纵轴参数 + self._横轴参数= 日线价格子图.返回横轴参数() + self._纵轴参数= self.计算纵轴参数() + + + + def 计算纵轴参数(self): + ''' + + ''' + + def _compute_torange(maxto, tostep): + return int(round((maxto + tostep/2.0 - 1) / float(tostep), 0)) + + def _compute_tostep(maxto): + ''' + maxto 是 换手率 最大值。返回每格单位(最小 500, 代表 0.5%)以及格数 + ''' + for i in range(9): + if maxto > (4 * 500 * (2**i)): # 换手率最大是 100000, 代表 100% + continue + else: + tostep= 500 * (2**i) + torange= _compute_torange(maxto, tostep) + break + return (tostep, torange) + + 指数换手= self._目标指数换手 + 个股换手= self._目标绘图换手 + + 个股最大= max( [nr for nr in 个股换手 if nr is not None] ) + 个股步进, \ + 个股格数= _compute_tostep(个股最大) + + 指数最大= max(指数换手) + 指数步进, \ + 指数格数= _compute_tostep(指数最大) + + 纵轴格数= max(个股格数, 指数格数) + 纵轴尺寸= 纵轴格数 * 1.0 + + 指数坐标终点= 指数步进 * 纵轴格数 + 个股坐标终点= 个股步进 * 纵轴格数 + + 指数主坐标值= [指数步进*i for i in range(纵轴格数)] + 指数副坐标值= list( itertools.chain.from_iterable( mi for mi in [[ma + (指数步进/4.0)*i for i in range(1, 4)] for ma in 指数主坐标值] ) ) + + 个股主坐标值= [个股步进*i for i in range(纵轴格数)] + 个股副坐标值= list( itertools.chain.from_iterable( mi for mi in [[ma + (个股步进/4.0)*i for i in range(1, 4)] for ma in 个股主坐标值] ) ) + + 纵轴参数= { + '指数步进': 指数步进, + '个股步进': 个股步进, + '纵轴格数': 纵轴格数, + '纵轴尺寸': 纵轴尺寸, + '纵轴高度': 纵轴尺寸 * __纵轴倍率__, + '指数坐标终点': 指数坐标终点, + '个股坐标终点': 个股坐标终点, + '指数主坐标值': 指数主坐标值, + '指数副坐标值': 指数副坐标值, + '个股主坐标值': 个股主坐标值, + '个股副坐标值': 个股副坐标值, + } + + return 纵轴参数 + + + + def 返回纵轴高度(self): + ''' + + ''' + return self._纵轴参数['纵轴高度'] + + + + def 平面初始化(self, 图片对象, 子图偏移, 全图大小, sharex): + ''' + + ''' + # 计算 布局参数 + #================================================================================================================================================== + 子图横移, \ + 子图纵移= 子图偏移 + + 全图宽度, \ + 全图高度= 全图大小 + + 本图宽度= self._横轴参数['横轴宽度'] + 本图高度= self._纵轴参数['纵轴高度'] + + 布局参数= ( 子图横移/全图宽度, 子图纵移/全图高度, 本图宽度/全图宽度, 本图高度/全图高度 ) + + # 指数部分 + #================================================================================================================================================== + 指数平面= 图片对象.add_axes(布局参数, axis_bgcolor='black', sharex=sharex) + 指数平面.set_axisbelow(True) # 网格线放在底层 + + for 方位, 边框 in 指数平面.spines.items(): # 方位: 'left' | 'right' | 'top' | 'bottom' + 边框.set_color(__color_gold__) + + 指数横轴= 指数平面.get_xaxis() + 指数纵轴= 指数平面.get_yaxis() + + self._指数平面= 指数平面 + self._指数横轴= 指数横轴 + self._指数纵轴= 指数纵轴 + + self.设置指数横轴() + self.设置指数纵轴() + + # 个股部分 + #================================================================================================================================================== + 个股平面= 指数平面.twinx() + 个股平面.set_axis_bgcolor('black') + 个股平面.set_axisbelow(True) # 网格线放在底层 + + # for 方位, 边框 in 个股平面.spines.items(): # 方位: 'left' | 'right' | 'top' | 'bottom' + # 边框.set_color(__color_gold__) + + 个股横轴= 个股平面.get_xaxis() + 个股纵轴= 个股平面.get_yaxis() + + self._个股平面= 个股平面 + self._个股横轴= 个股横轴 + self._个股纵轴= 个股纵轴 + + self.设置个股横轴() + self.设置个股纵轴() + + + + def 设置指数横轴(self): + ''' + + ''' + 任务参数= self._任务参数 + + 指数平面= self._指数平面 + 指数横轴= self._指数横轴 + + 横轴参数= self._横轴参数 + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + 指数横轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 指数横轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 指数平面.set_xlim(坐标起点, 坐标终点) + + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + xMajorFormatter= 横轴参数['xMajorFormatter'] + xMinorFormatter= 横轴参数['xMinorFormatter'] + + 指数横轴.set_major_locator(xMajorLocator) + 指数横轴.set_minor_locator(xMinorLocator) + + 指数横轴.set_major_formatter(xMajorFormatter) + 指数横轴.set_minor_formatter(xMinorFormatter) + + if 任务参数['绘制实盘']: + # 设为不可见 + for 主坐标 in axes.get_xticklabels(minor=False): + 主坐标.set_visible(False) + + for 副坐标 in axes.get_xticklabels(minor=True): + 副坐标.set_visible(False) + else: + for 主坐标 in 指数平面.get_xticklabels(minor=False): + 主坐标.set_fontsize(4) + 主坐标.set_horizontalalignment('right') + 主坐标.set_rotation('45') + + for 副坐标 in 指数平面.get_xticklabels(minor=True): + 副坐标.set_fontsize(4) + 副坐标.set_color('blue') + 副坐标.set_horizontalalignment('right') + 副坐标.set_rotation('45') + + + def 设置指数纵轴(self): + ''' + + ''' + 平面对象= self._指数平面 + 指数纵轴= self._指数纵轴 + 纵轴参数= self._纵轴参数 + + 主坐标值= 纵轴参数['指数主坐标值'] + 副坐标值= 纵轴参数['指数副坐标值'] + 坐标终点= 纵轴参数['指数坐标终点'] + + 指数纵轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + 指数纵轴.grid(True, 'minor', color='0.3', linestyle='solid', linewidth=0.1) + + 平面对象.set_ylim(0, 坐标终点) + + yMajorLocator= ticker.FixedLocator(numpy.array(主坐标值)) + def y_major_formatter(num, pos=None): + return str(round(num/1000.0, 2)) + '%' + yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + + 指数纵轴.set_major_locator(yMajorLocator) + 指数纵轴.set_major_formatter(yMajorFormatter) + + for 主坐标 in 平面对象.get_yticklabels(minor=False): + 主坐标.set_font_properties(__font_properties__) + 主坐标.set_fontsize(5) # 这个必须放在前一句后面,否则作用会被覆盖 + + + yMinorLocator= ticker.FixedLocator(numpy.array(副坐标值)) + def y_minor_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + '%' + yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + + 指数纵轴.set_minor_locator(yMinorLocator) + 指数纵轴.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 平面对象.get_yticklabels(minor=True): + 副坐标.set_font_properties(__font_properties__) + 副坐标.set_fontsize(4) # 这个必须放在前一句后面,否则作用会被覆盖 + + + + def 设置个股横轴(self): + ''' + + ''' + 个股平面= self._个股平面 + 个股横轴= self._个股横轴 + + 横轴参数= self._横轴参数 + 坐标起点= 横轴参数['坐标起点'] + 坐标终点= 横轴参数['坐标终点'] + + # 个股横轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # 个股横轴.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.15) + + 个股平面.set_xlim(坐标起点, 坐标终点) + + xMajorLocator= 横轴参数['xMajorLocator'] + xMinorLocator= 横轴参数['xMinorLocator'] + + 个股横轴.set_major_locator(xMajorLocator) + 个股横轴.set_minor_locator(xMinorLocator) + + for 主坐标 in 个股平面.get_xticklabels(minor=False): + 主坐标.set_visible(False) + + for 副坐标 in 个股平面.get_xticklabels(minor=True): + 副坐标.set_visible(False) + + + + def 设置个股纵轴(self): + ''' + + ''' + + 平面对象= self._个股平面 + 个股纵轴= self._个股纵轴 + 纵轴参数= self._纵轴参数 + + 主坐标值= 纵轴参数['个股主坐标值'] + 副坐标值= 纵轴参数['个股副坐标值'] + 坐标终点= 纵轴参数['个股坐标终点'] + + # 坐标线 + # 个股纵轴.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # 个股纵轴.grid(True, 'minor', color='0.3', linestyle='solid', linewidth=0.1) + + # 设置坐标范围 + 平面对象.set_ylim(0, 坐标终点) + + # 主坐标点 + yMajorLocator= ticker.FixedLocator(numpy.array(主坐标值)) + def y_major_formatter(num, pos=None): + return str(round(num/1000.0, 2)) + '%' + yMajorFormatter= ticker.FuncFormatter(y_major_formatter) + + 个股纵轴.set_major_locator(yMajorLocator) + 个股纵轴.set_major_formatter(yMajorFormatter) + + for 主坐标 in 平面对象.get_yticklabels(minor=False): + 主坐标.set_font_properties(__font_properties__) + 主坐标.set_fontsize(5) # 这个必须放在前一句后面,否则作用会被覆盖 + + # 副坐标点 + yMinorLocator= ticker.FixedLocator(numpy.array(副坐标值)) + def y_minor_formatter(num, pos=None): + return str(round(num/1000.0, 3)) + '%' + yMinorFormatter= ticker.FuncFormatter(y_minor_formatter) + + 个股纵轴.set_minor_locator(yMinorLocator) + 个股纵轴.set_minor_formatter(yMinorFormatter) + + for 副坐标 in 平面对象.get_yticklabels(minor=True): + 副坐标.set_font_properties(__font_properties__) + 副坐标.set_fontsize(4) # 这个必须放在前一句后面,否则作用会被覆盖 + + + + def 绘制指数换手(self): + ''' + + ''' + 横轴坐标序列= self._横轴坐标序列 + 平面对象= self._指数平面 + + 行情长度= self._目标行情长度 + 上涨序列= self._目标指数上涨 + 下跌序列= self._目标指数下跌 + 平盘序列= self._目标指数平盘 + 换手序列= self._目标指数换手 + + 换手阵列= numpy.array(换手序列) + 起点阵列= numpy.zeros(行情长度) + + lwidth, alpha= (0.7, 0.5) + + if True in 上涨序列: + 平面对象.vlines(横轴坐标序列[上涨序列], 起点阵列[上涨序列], 换手阵列[上涨序列], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=alpha) + if True in 下跌序列: + 平面对象.vlines(横轴坐标序列[下跌序列], 起点阵列[下跌序列], 换手阵列[下跌序列], edgecolor='0.3', linewidth=lwidth, label='_nolegend_', alpha=alpha) + if True in 平盘序列: + 平面对象.vlines(横轴坐标序列[平盘序列], 起点阵列[平盘序列], 换手阵列[平盘序列], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=1.0) + + + + def 绘制个股换手(self): + ''' + + ''' + 横轴坐标序列= self._横轴坐标序列 + 平面对象= self._个股平面 + + 行情长度= self._目标行情长度 + 上涨序列= self._目标绘图上涨 + 下跌序列= self._目标绘图下跌 + 平盘序列= self._目标绘图平盘 + 换手序列= self._目标绘图换手 + + 横轴参数= self._横轴参数 + 横标起点= 横轴参数['坐标起点'] + 横标终点= 横轴参数['坐标终点'] + 每月首日偏移= 横轴参数['每月首日偏移'] + + 换手阵列= numpy.array(换手序列) + 起点阵列= numpy.zeros(行情长度) + + lwidth= 3.0 + + if True in 上涨序列: + 平面对象.vlines(横轴坐标序列[上涨序列], 起点阵列[上涨序列], 换手阵列[上涨序列], edgecolor='red', linewidth=lwidth, label='_nolegend_', alpha=0.5) + if True in 下跌序列: + 平面对象.vlines(横轴坐标序列[下跌序列], 起点阵列[下跌序列], 换手阵列[下跌序列], edgecolor='green', linewidth=lwidth, label='_nolegend_', alpha=0.5) + if True in 平盘序列: + 平面对象.vlines(横轴坐标序列[平盘序列], 起点阵列[平盘序列], 换手阵列[平盘序列], edgecolor='0.7', linewidth=lwidth, label='_nolegend_', alpha=1.0) + + # 绘制 平均换手数值(直线) + 个股换手= [nr for nr in 换手序列 if nr is not None] + 平均换手= sum(个股换手) / float(len(个股换手)) + 平面对象.plot([横标起点, 横标终点], [平均换手, 平均换手], '-', color='yellow', linewidth=0.2, alpha=0.7) + + # 平均换手率数值在图中间的显示 + for ix in 每月首日偏移[2:-1:3]: + newlab= 平面对象.text(ix+8, 平均换手, str(round(平均换手/1000.0, 2)) + '%') + newlab.set_font_properties(__font_properties__) + newlab.set_color('yellow') + newlab.set_fontsize(3) + + + + def 绘制换手提示(self): + ''' + + ''' + def formatter(nr): + return str(round(nr/1000.0, 2)) + '%' + + 平面对象= self._指数平面 + 纵轴参数= self._纵轴参数 + + 指数步进= 纵轴参数['指数步进'] + 个股步进= 纵轴参数['个股步进'] + 纵轴格数= 纵轴参数['纵轴格数'] + + shift= 7 + 横轴参数= self._横轴参数 + 每月首日偏移= 横轴参数['每月首日偏移'] + for iy, iy2 in zip( range(int(指数步进/2.0), 指数步进*纵轴格数, int(指数步进/2.0)), range(int(个股步进/2.0), 个股步进*纵轴格数, int(个股步进/2.0)) ): + for ix in 每月首日偏移[1:-1:3]: + newlab= 平面对象.text(ix+shift, iy, formatter(iy2) + ' / ' + formatter(iy)) + newlab.set_font_properties(__font_properties__) + newlab.set_color('0.3') + newlab.set_fontsize(3) + newlab.set_zorder(0) # XXX: 放在底层 + + + + def 绘图(self): + ''' + + ''' + self.绘制指数换手() + self.绘制个股换手() + self.绘制换手提示() + + + + + diff --git a/vn.training/TushareGetAllDay.py b/vn.training/TushareGetAllDay.py new file mode 100644 index 00000000..def00753 --- /dev/null +++ b/vn.training/TushareGetAllDay.py @@ -0,0 +1,22 @@ +#coding=utf8 + +import tushare as ts +import pymongo +import json +#import csv +import time + +#conn = pymongo.Connection('222.73.15.21', port=7017) + +i=0 + +while i < 10000: + print time.localtime + i=i+1 + code = "%06d" % i + print code + df = ts.get_hist_data(code) + + #conn.db.DayAll.insert(json.loads(df.to_json(orient='index'))) + + diff --git a/vn.training/moving_average.py b/vn.training/moving_average.py new file mode 100644 index 00000000..17cf29ca --- /dev/null +++ b/vn.training/moving_average.py @@ -0,0 +1,30 @@ +import numpy as np +import matplotlib.pyplot as plt + +#first generate some datapoint for a randomly sampled noisy sinewave +x = np.random.random(1000)*10 +noise = np.random.normal(scale=0.3,size=len(x)) +y = np.sin(x) + noise + +#plot the data +plt.plot(x,y,'ro',alpha=0.3,ms=4,label='data') +plt.xlabel('Time') +plt.ylabel('Intensity') + +#define a moving average function +def moving_average(x,y,step_size=.1,bin_size=1): + bin_centers = np.arange(np.min(x),np.max(x)-0.5*step_size,step_size)+0.5*step_size + bin_avg = np.zeros(len(bin_centers)) + + for index in range(0,len(bin_centers)): + bin_center = bin_centers[index] + items_in_bin = y[(x>(bin_center-bin_size*0.5) ) & (x<(bin_center+bin_size*0.5))] + bin_avg[index] = np.mean(items_in_bin) + + return bin_centers,bin_avg + +#plot the moving average +bins, average = moving_average(x,y) +plt.plot(bins, average,label='moving average') + +plt.show() \ No newline at end of file diff --git a/vn.training/quotation.py b/vn.training/quotation.py new file mode 100644 index 00000000..21bc9975 --- /dev/null +++ b/vn.training/quotation.py @@ -0,0 +1,560 @@ +# -*- coding: utf-8 -*- + + +#!/usr/bin/python + +import sys +import random +from PyQt4 import QtGui, QtCore,Qt + +import tushare as ts + +class report_painter: + '''绘制行情类''' + def __init__(self,parent): + + #初始化 + self.parent = parent + self.paint = QtGui.QPainter() + self.paint.begin(self.parent) + + #设置抗锯齿 + #self.paint.setRenderHint(QtGui.QPainter.Antialiasing) + #度量尺对象 + self.metrics = self.paint.fontMetrics() + + #设置字体库 + self.fonts = dict() + self.fonts['default'] = QtGui.QFont('Serif', 9, QtGui.QFont.Light) + self.fonts['yahei_14_bold']= QtGui.QFont('Serif',12,QtGui.QFont.Bold) + self.fonts['yahei_14']= QtGui.QFont('Serif',12,QtGui.QFont.Light) + self.setFont('default') + + #设置笔刷样式库 + self.pens = dict() + + #红色 1px粗 1px点 2px距 线条 + self.pens['red_1px_dashline'] = QtGui.QPen( QtCore.Qt.red, 1, QtCore.Qt.DashLine) + self.pens['red_1px_dashline'].setDashPattern([1,2]) + + #红色 1px粗 实线条 + self.pens['red'] = QtGui.QPen( QtCore.Qt.red, 1, QtCore.Qt.SolidLine) + #红色 3px粗 实线条 + self.pens['red_2px'] = QtGui.QPen( QtCore.Qt.red, 2, QtCore.Qt.SolidLine) + #红色 2px粗 实线条 + self.pens['red_3px'] = QtGui.QPen( QtCore.Qt.red, 3, QtCore.Qt.SolidLine) + #黄色 1px粗 实线条 + self.pens['yellow'] = QtGui.QPen( QtCore.Qt.yellow, 1, QtCore.Qt.SolidLine) + #白色 1px粗 实线条 + self.pens['white'] = QtGui.QPen( QtCore.Qt.white , 1, QtCore.Qt.SolidLine) + #灰色 1px粗 实线条 + self.pens['gray'] = QtGui.QPen( QtCore.Qt.gray, 1, QtCore.Qt.SolidLine) + #绿色 1px粗 实线条 + self.pens['green'] = QtGui.QPen( QtCore.Qt.green, 1, QtCore.Qt.SolidLine) + #绿色 3px粗 实线条 + self.pens['green_2px'] = QtGui.QPen( QtCore.Qt.green, 2, QtCore.Qt.SolidLine) + #亮蓝 1px粗 1px点 2px距 线条 + self.pens['cyan_1px_dashline'] = QtGui.QPen( QtCore.Qt.cyan, 1, QtCore.Qt.DashLine) + self.pens['cyan_1px_dashline'].setDashPattern([3,2]) + #获得窗口的长和宽 + size = self.parent.size() + self.w = size.width() + self.h = size.height() + + #设置grid的上下左右补丁边距 + self.grid_padding_left = 45 #左侧补丁边距 + self.grid_padding_right = 245 #右侧补丁边距 + self.grid_padding_top = 25 #顶部补丁边距 + self.grid_padding_bottom = 17 #底部补丁边距 + + #开始绘制 + self.start_paint() + + + self.paint.end() #结束 + '''绘制流程步骤''' + def start_paint(self): + self.PriceGridPaint() + self.rightGridPaint() + self.timelinePaint() + self.topInfoPaint() + self.rulerPaint() + self.VolumeGridPaint() + self.volumePaint() + self.pricePaint() + self.xyPaint() + '''设置使用的字体''' + def setFont(self,code='default'): + self.paint.setFont(self.fonts[code]) + + '''设置使用的笔刷''' + def setPen(self,code='default'): + self.paint.setPen(self.pens[code]) + + '''绘制股价走势表格''' + def PriceGridPaint(self): + self.setPen('red') + self.paint.setBrush(QtCore.Qt.NoBrush) + + sum_width = self.grid_padding_left+self.grid_padding_right + sum_height = self.grid_padding_top+self.grid_padding_bottom + + grid_height = self.h-sum_height + + #画边框 + self.paint.drawRect(self.grid_padding_left,self.grid_padding_top, + self.w-sum_width,self.h-sum_height) + #成交量和走势的分界线 + self.paint.drawLine(self.grid_padding_left,grid_height*0.7+self.grid_padding_top, + self.w-self.grid_padding_right,grid_height*0.7+self.grid_padding_top) + + #股票昨收中间线 + self.paint.drawLine(self.grid_padding_left+1, + grid_height*0.35+self.grid_padding_top, + self.w-self.grid_padding_right + ,grid_height*0.35+self.grid_padding_top) + + #其他线条 + self.paint.drawLine(0,self.h-self.grid_padding_bottom,self.w-self.grid_padding_right+44,self.h-self.grid_padding_bottom) + self.paint.drawLine(0,self.h-self.grid_padding_bottom+16,self.w,self.h-self.grid_padding_bottom+16) + + self.paint.drawLine(self.w-self.grid_padding_right,0, + self.w-self.grid_padding_right,self.h-self.grid_padding_bottom+16) + self.paint.drawLine(self.w-self.grid_padding_right+44,0, + self.w-self.grid_padding_right+44,self.h-self.grid_padding_bottom+16) + self.setPen('yellow') + self.paint.drawText(self.w-self.grid_padding_right+5,self.h-self.grid_padding_bottom-4,QtCore.QString(u'成交量')) + self.setPen('white') + #右下角文字 + self.paint.drawText(self.w-self.grid_padding_right+12,self.h-self.grid_padding_bottom+12,QtCore.QString(u'实时')) + '''绘制成交量走势表格''' + def VolumeGridPaint(self): + sum_width = self.grid_padding_left + self.grid_padding_right + sum_height = self.grid_padding_top + self.grid_padding_bottom + + grid_height = self.h-sum_height + max_volume = self.parent.stk_data['max_vol'] + + px_h_radio = max_volume/(grid_height*0.3) + + self.setPen('red_1px_dashline') + + + grid_num = 6 + x = grid_num + cnt = grid_height*0.3/grid_num + for i in range(0,grid_num): + self.setPen('red_1px_dashline') + #计算坐标 + y1 = self.grid_padding_top+(grid_height*0.7)+i*cnt + x1 = self.grid_padding_left + x2 = self.grid_padding_left+self.w-sum_width + + self.paint.drawLine(x1,y1,x2,y1) #画价位虚线 + + vol_int = int(cnt*x*px_h_radio) + vol_str = str(vol_int) + fw = self.metrics.width(vol_str) #获得文字宽度 + fh = self.metrics.height()/2 #获得文字高度 + self.setPen('yellow') + self.paint.drawText(x2+40-fw,y1+fh,vol_str) #写入文字 + self.setPen('white') + self.paint.drawText(x1-2-self.metrics.width(str(x)),y1+fh,str(x)) #写入文字 + x-=1 + + + '''绘制左侧信息栏和盘口等内容''' + def rightGridPaint(self): + self.setPen('red') + #绘制信息内容之间的分割线 + _h = 0 + _x = self.w-self.grid_padding_right+44 + self.paint.drawLine(self.w-1,0,self.w-1,self.h-self.grid_padding_bottom+16) + self.paint.drawLine(0,0,0,self.h-self.grid_padding_bottom+16) + self.paint.drawLine(0,_h,self.w,_h) + _h+=23 + self.paint.drawLine(_x,_h,self.w,_h) + _h+=24 + self.paint.drawLine(_x,_h,self.w,_h) + + _h+=93 + self.paint.drawLine(_x,_h,self.w,_h) + _h+=20 + self.paint.drawLine(_x,_h,self.w,_h) + _h+=93 + self.paint.drawLine(_x,_h,self.w,_h) + _h+=123 + self.paint.drawLine(_x,_h,self.w,_h) + _h+=23 + self.paint.drawLine(_x,_h,self.w,_h) + #股票名称和代码 + self.setFont('yahei_14_bold') + self.setPen('yellow') + name_str = QtCore.QString(u'%s %s'%(self.parent.stk_info['code'],self.parent.stk_info['name'])) + self.paint.drawText(_x+35,18,name_str) + #委比和委差 + self.setFont('yahei_14') + zx_str = QtCore.QString(u'最新') + self.paint.drawText(_x+3 ,156,zx_str) + self.setPen('gray') + wb_str = QtCore.QString(u'委比') + wc_str = QtCore.QString(u'委差') + xs_str = QtCore.QString(u'现手') + self.paint.drawText(_x+3 ,39,wb_str) + self.paint.drawText(_x+100,39,wc_str) + self.paint.drawText(_x+100,156,xs_str) + fh = self.metrics.height() + + left_field_list = [u'涨跌',u'涨幅',u'振幅',u'总手',u'总额',u'换手',u'分笔'] + i = 1 + for field in left_field_list: + field_str = QtCore.QString(field) + self.paint.drawText(_x+3,253+(i*17),field_str) + i+=1 + + right_field_list = [u'均价',u'前收',u'今开',u'最高',u'最低',u'量比',u'均量'] + + i = 1 + for field in right_field_list: + field_str = QtCore.QString(field) + self.paint.drawText(_x+100,253+(i*17),field_str) + i+=1 + + wp_str = QtCore.QString(u'外盘') + np_str = QtCore.QString(u'内盘') + self.paint.drawText(_x+3,395,wp_str) + self.paint.drawText(_x+100,395,np_str) + #卖①②③④⑤ + + i = 0 + sell_queue = [u'卖⑤',u'卖④',u'卖③',u'卖②',u'卖①'] + for sell in sell_queue: + sell_str = QtCore.QString(sell) + self.paint.drawText(_x+3,62+(i*18),sell_str) + i+=1 + #买①②③④⑤ + buy_queue = [u'买①',u'买②',u'买③',u'买④',u'买⑤'] + for buy in buy_queue: + buy_str = QtCore.QString(buy) + self.paint.drawText(_x+3,87+(i*18),buy_str) + i+=1 + + self.setPen('red_2px') + self.paint.drawLine(_x+1,377,_x+99,377) + self.paint.drawLine(_x+1,46,_x+65,46) + self.setPen('green_2px') + self.paint.drawLine(_x+102,377,_x+199,377) + self.paint.drawLine(_x+67,46,_x+199,46) + self.setFont('default') + + '''绘制左右侧的价格刻度''' + def rulerPaint(self): + + sum_width = self.grid_padding_left+self.grid_padding_right + sum_height = self.grid_padding_top+self.grid_padding_bottom + + grid_height = self.h-sum_height + + high = self.parent.stk_data['high'] + low = self.parent.stk_data['low'] + lastclose = self.parent.stk_data['lastclose'] + + top = high-lastclose + bottom = lastclose-low + if top>bottom: + padding = top + else: + padding = bottom + + limit_top = lastclose+padding + limit_low = lastclose-padding + + + px_h_radio = (grid_height*0.7)/((limit_top-limit_low)*100) + + + self.setPen('red_1px_dashline') + + grid_num = 16 + cnt = grid_height*0.7/grid_num + + for i in range(0,grid_num): + self.setPen('red_1px_dashline') + #计算坐标 + y1 = self.grid_padding_top+i*cnt + x1 = self.grid_padding_left + x2 = self.grid_padding_left+self.w-sum_width + + self.paint.drawLine(x1,y1,x2,y1) #画价位虚线 + + price_float = (limit_top - ((i*cnt)/px_h_radio/100)) #计算价格 + price = '%4.2f'%(price_float) #格式化价格成str + + fw = self.metrics.width(price) #获得文字宽度 + fh = self.metrics.height()/2 #获得文字高度 + + radio_float = (price_float/lastclose-1)*100 #计算波动百分比 + radio_str = "%2.2f%%"%(radio_float) #格式化百分比成str + + r_fw = self.metrics.width(radio_str) + r_fh = self.metrics.height()/2 + #判断文字使用的颜色 + if price_float == lastclose: + self.setPen('white') + if price_float < lastclose: + self.setPen('green') + + self.paint.drawText(x1-fw-2,y1+fh,price) #写入文字 + self.paint.drawText(x2+40-r_fw,y1+r_fh,radio_str) #写入文字 + '''绘制x,y准星''' + def xyPaint(self): + if self.parent.m_x >= self.grid_padding_left and self.parent.m_x<=self.w-self.grid_padding_right and self.parent.m_y>=self.grid_padding_top and self.parent.m_y<=self.h-self.grid_padding_bottom: + self.setPen('gray') + x1 = self.grid_padding_left + x2 = self.w-self.grid_padding_right + y1 = self.grid_padding_top + y2 = self.h-self.grid_padding_bottom + self.paint.drawLine(x1+1,self.parent.m_y,x2-1,self.parent.m_y) + self.paint.drawLine(self.parent.m_x,y1+1,self.parent.m_x,y2-1) + + + + '''绘制时间轴刻度''' + def timelinePaint(self): + + fw = self.metrics.width(u'00:00') #计算文字的宽度 + + sum_width = self.grid_padding_left+self.grid_padding_right + sum_height = self.grid_padding_top+self.grid_padding_bottom + + grid_width = self.w-sum_width-2 + + + y1 = self.grid_padding_top + y2 = y1+(self.h-sum_height) + + #时间轴中线 + self.setPen('red') + x_pos = grid_width/2+self.grid_padding_left + + self.paint.drawLine(x_pos,y1,x_pos,y2) + self.paint.drawText(x_pos-fw/2,y2+12,QtCore.QString(u'13:00')) + + #时间轴09点30分 + x_pos = self.grid_padding_left + self.paint.drawText(x_pos,y2+12,QtCore.QString(u'09:30')) + + #时间轴10点30分 + x_pos = grid_width*0.25+self.grid_padding_left + self.paint.drawLine(x_pos,y1,x_pos,y2) + self.paint.drawText(x_pos-fw/2,y2+12,QtCore.QString(u'10:30')) + + #时间轴14点00分 + x_pos = grid_width*0.75+self.grid_padding_left + self.paint.drawLine(x_pos,y1,x_pos,y2) + self.paint.drawText(x_pos-fw/2,y2+12,QtCore.QString(u'14:00')) + + #时间轴15点00分 + x_pos = grid_width+self.grid_padding_left + self.paint.drawText(x_pos-fw,y2+12,QtCore.QString(u'15:00')) + + #时间虚线 by 30min + self.setPen('red_1px_dashline') + x_pos_array = [0.125,0.375,0.625,0.875] + for i in x_pos_array: + x_pos = grid_width*i+self.grid_padding_left + self.paint.drawLine(x_pos,y1,x_pos,y2) + + + '''绘制表格上方的股票信息''' + def topInfoPaint(self): + self.setPen('yellow') + self.paint.drawText(4+self.grid_padding_left,self.grid_padding_top-4 + ,QtCore.QString(self.parent.stk_info['name'])) #股票名称 + self.paint.drawText(4+self.grid_padding_left+120,self.grid_padding_top-4 + ,QtCore.QString(u'均价线:')) #均价线 + lastclose = self.parent.stk_data['lastclose'] + close = self.parent.stk_data['close'] + mma = self.parent.stk_data['list']['mma'][-1] + + if lastclose>close: + self.setPen('green') + str_1 = '%.2f -%.2f'%(close,lastclose-close) + if lastclose==close: + self.setPen('white') + str_1 = '%.2f +%.2f'%(close,0.00) + if lastcloseclose: + self.setPen('green') + if mma==close: + self.setPen('white') + if mmabottom: + padding = top + else: + padding = bottom + + limit_top = lastclose+padding + limit_low = lastclose-padding + + h_radio = (grid_height*0.7)/((limit_top-limit_low)*100) + + w_radio = (self.w-sum_width-2)/240.00 + w = self.grid_padding_left + + self.setPen('white') + path = QtGui.QPainterPath() + path.moveTo(w,(limit_top-self.parent.stk_data['open'])*100*h_radio+self.grid_padding_top) + i = 1 + for price in self.parent.stk_data['list']['close']: + w = i*w_radio+self.grid_padding_left + y = (limit_top-price)*100*h_radio+self.grid_padding_top + path.lineTo(w,y) + i+=1 + self.paint.drawPath(path) + self.setPen('cyan_1px_dashline') + self.paint.drawLine(self.grid_padding_left+1,y,w-1,y) + self.setPen('yellow') + path = QtGui.QPainterPath() + w = self.grid_padding_left + path.moveTo(w,(limit_top-self.parent.stk_data['open'])*100*h_radio+self.grid_padding_top) + i = 1 + for price in self.parent.stk_data['list']['mma']: + w = i*w_radio+self.grid_padding_left + y = (limit_top-price)*100*h_radio+self.grid_padding_top + path.lineTo(w,y) + i+=1 + self.paint.drawPath(path) + + + '''绘制成交量''' + def volumePaint(self): + sum_width = self.grid_padding_left + self.grid_padding_right + sum_height = self.grid_padding_top + self.grid_padding_bottom + + max_volume = self.parent.stk_data['max_vol'] #最大分钟成交量 + + w_radio = (self.w-sum_width-2)/240.00 + h_radio = ((self.h-sum_height-2)*0.3)/max_volume + + y = (self.h-sum_height)+self.grid_padding_top + + self.setPen('yellow') + + + + for i in range(1,len(self.parent.stk_data['list']['vol'])+1): + x = i*w_radio+self.grid_padding_left + y2 = h_radio*self.parent.stk_data['list']['vol'][i-1] + self.paint.drawLine(x,y-1,x,y-y2) + +class Test(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + self.setMinimumSize(640, 430) #设置窗口最小尺寸 + self.setGeometry(300, 300, 960, 650) + self.setWindowTitle(QtCore.QString(u'超级狙击手[内部开发测试版]-行情实时走势')) + self.setStyleSheet("QWidget { background-color: black }") + self.setWindowIcon(QtGui.QIcon('ruby.png')) + self.setMouseTracking(True) + self.m_x = 0 #光标x轴位置 + self.m_y = 0 #光标y轴位置 + + self.stk_info = {} + + self.stk_info['name'] = u'浙江东方' + self.stk_info['code'] = u'600120' + self.stk_info['market'] = 'SH' + + self.stk_data = {} + self.stk_data['list'] = {} #股价序列 + self.stk_data['list']['time'] = [] #时间 + self.stk_data['list']['open'] = [] #开盘价 + self.stk_data['list']['high'] = [] #最高价 + self.stk_data['list']['low'] = [] #最低价 + self.stk_data['list']['close'] = [] #收盘价 + self.stk_data['list']['vol'] = [] #成交量 + self.stk_data['list']['amount']= [] #成交额 + self.stk_data['list']['mma']= [] #分时均价 + + self.stk_data['list']['buy_port'] = [(0.00,0),(0.00,0),(0.00,0),(0.00,0),(0.00,0)] #买盘前五 + self.stk_data['list']['sell_port'] = [(0.00,0),(0.00,0),(0.00,0),(0.00,0),(0.00,0)] #卖盘前五 + + #读取数据 + # f = open('SH600120.txt','r') + # data = f.readlines() + # f.close () + + data = ts.get_hist_data('600120') #一次性获取全部日k线数据 + + + + + #for row in data: + # vars = row.split(' ') + # self.stk_data['list']['time'].append(vars[1]) + # self.stk_data['list']['open'].append(float(vars[2])) + # self.stk_data['list']['high'].append(float(vars[3])) + # self.stk_data['list']['low'].append(float(vars[4])) + # self.stk_data['list']['close'].append(float(vars[5])) + # self.stk_data['list']['vol'].append(int(float(vars[6]))) + # self.stk_data['list']['amount'].append(int(float(vars[7]))) +# + # sum_vol = sum(self.stk_data['list']['vol']) + # sum_amt = sum(self.stk_data['list']['amount']) +# + # self.stk_data['list']['mma'].append(float(sum_amt)/(sum_vol*100.00)) + self.stk_data['list'] = data.datetime + self.stk_data['list'] + self.stk_data['list'] + self.stk_data['list'] + self.stk_data['list'] + self.stk_data['list'] + self.stk_data['list'] + + self.stk_data['lastclose'] = 10.12 #上一个交易日收盘价 + self.stk_data['open'] = self.stk_data['list']['open'][0] #开盘价 + self.stk_data['high'] = max(self.stk_data['list']['high']) #最高价 + self.stk_data['low'] = min(self.stk_data['list']['low']) #最低价 + self.stk_data['close']= self.stk_data['list']['close'][-1] #收盘价 + self.stk_data['max_vol'] = max(self.stk_data['list']['vol']) #当日最高成交量 + + + def mouseMoveEvent(self, event): + self.m_x = int(event.x()) + self.m_y = int(event.y()) + self.repaint() + def paintEvent(self, event): + report_painter(self) + +app = QtGui.QApplication(sys.argv) +dt = Test() +dt.show() +app.exec_() \ No newline at end of file diff --git a/vn.training/quotation_ext.py b/vn.training/quotation_ext.py new file mode 100644 index 00000000..d4357296 --- /dev/null +++ b/vn.training/quotation_ext.py @@ -0,0 +1,2597 @@ +# -*- 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): + ''' + 主营业务 + ''' + # 查找表: (<文字长度>, <每行字数>, <字体大小>, ) + 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 string,value 是相对复权因子(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 file,figpath 指定图片需存放的路径 + 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) \ No newline at end of file diff --git a/vn.training/qutation2.py b/vn.training/qutation2.py new file mode 100644 index 00000000..866f8f58 --- /dev/null +++ b/vn.training/qutation2.py @@ -0,0 +1,802 @@ +# -*- coding: utf-8 -*- + +import os +import sys +import pickle +import math +import datetime +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 FixedLocator, MultipleLocator, FuncFormatter, NullFormatter + + + +__font_properties__=font_manager.FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc') +__color_lightsalmon__= '#ffa07a' +__color_pink__= '#ffc0cb' +__color_navy__= '#000080' + + +def Plot(pfile, figpath): + """ + pfile 指明存放绘图数据的 pickle file,figpath 指定图片需存放的路径 + """ + + fileobj= open(name=pfile, mode='rb') + pdata= pickle.load(fileobj) + fileobj.close() + os.remove(pfile) + + # 计算图片的尺寸(单位英寸) + # 注意:Python2 里面, "1 / 10" 结果是 0, 必须写成 "1.0 / 10" 才会得到 0.1 + #================================================================================================================================================== + length= len(pdata[u'日期']) # 所有数据的长度,就是天数 + + open_price_pri= pdata[u'开盘'][0] # int 类型 + open_price_sec= pdata[u'开盘二'][0] # 同上 + + highest_price_pri= max( [phigh for phigh in pdata[u'最高'] if phigh != None] ) # 第一个行情的最高价 + highest_price_sec= max( [phigh for phigh in pdata[u'最高二'] if phigh != None] ) # 第二个行情的最高价 + highest_price= max(highest_price_pri, highest_price_sec*open_price_pri/open_price_sec) # 以第一个行情为基准修正出的总最高价 + + lowest_price_pri= min( [plow for plow in pdata[u'最低'] if plow != None] ) # 最低价 + lowest_price_sec= min( [plow for plow in pdata[u'最低二'] if plow != None] ) # 最低价 + lowest_price= min(lowest_price_pri, lowest_price_sec*open_price_pri/open_price_sec) # 以第一个行情为基准修正出的总最低价 + + + + yhighlim_price= int(highest_price * 1.1) # K线子图 Y 轴最大坐标 + ylowlim_price= int(lowest_price / 1.1) # K线子图 Y 轴最小坐标 + + + + xfactor= 10.0/230.0 # 一条 K 线的宽度在 X 轴上所占距离(英寸) + yfactor= 0.3 # Y 轴上每一个距离单位的长度(英寸),这个单位距离是线性坐标和对数坐标通用的 + + expbase= 1.1 # 底数,取得小一点,比较接近 1。股价 3 元到 4 元之间有大约 3 个单位距离 + + # 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) + # ,这是对数运算的性质。 + ymulti_price= math.log(yhighlim_price, expbase) - math.log(ylowlim_price, expbase) + + ymulti_vol= 3.0 # 成交量部分在 Y 轴所占的 “份数” + ymulti_top= 1.2 # 顶部空白区域在 Y 轴所占的 “份数” + ymulti_bot= 1.2 # 底部空白区域在 Y 轴所占的 “份数” + + xmulti_left= 12.0 # 左侧空白区域所占的 “份数” + xmulti_right= 12.0 # 右侧空白区域所占的 “份数” + + xmulti_all= length + xmulti_left + xmulti_right + xlen_fig= xmulti_all * xfactor # 整个 Figure 的宽度 + ymulti_all= ymulti_price + ymulti_vol + ymulti_top + ymulti_bot + ylen_fig= ymulti_all * yfactor # 整个 Figure 的高度 + + rect_1= (xmulti_left/xmulti_all, (ymulti_bot+ymulti_vol)/ymulti_all, length/xmulti_all, ymulti_price/ymulti_all) # K线图部分 + rect_2= (xmulti_left/xmulti_all, ymulti_bot/ymulti_all, length/xmulti_all, ymulti_vol/ymulti_all) # 成交量部分 + + + + # 建立 Figure 对象 + #================================================================================================================================================== + figfacecolor= __color_pink__ + figedgecolor= __color_navy__ + figdpi= 300 + figlinewidth= 1.0 + + figobj= pyplot.figure(figsize=(xlen_fig, ylen_fig), dpi=figdpi, facecolor=figfacecolor, edgecolor=figedgecolor, linewidth=figlinewidth) # Figure 对象 + + # 整个 figure 的标题 + title_pri= (pdata[u'代码'] + ' ' if u'代码' in pdata else '') + pdata[u'简称'] + title_sec= (pdata[u'代码二'] + ' ' if u'代码二' in pdata else '') + pdata[u'简称二'] + + figobj.suptitle(title_pri + ' / ' + title_sec, fontsize=12, fontproperties=__font_properties__) + + + + #================================================================================================================================================== + #================================================================================================================================================== + #======= + #======= XXX: 第一只:成交量部分 + #======= + #================================================================================================================================================== + #================================================================================================================================================== + + # 第一只:添加 Axes 对象 + #================================================================================================================================================== + axes_2= figobj.add_axes(rect_2, axis_bgcolor='black') + axes_2.set_axisbelow(True) # 网格线放在底层 + + # 第一只:改变坐标线的颜色 + #================================================================================================================================================== + for child in axes_2.get_children(): + if isinstance(child, matplotlib.spines.Spine): + child.set_color('lightblue') + + # 第一只:得到 X 轴 和 Y 轴 的两个 Axis 对象 + #================================================================================================================================================== + xaxis_2= axes_2.get_xaxis() + yaxis_2= axes_2.get_yaxis() + + # 第一只:设置两个坐标轴上的 grid + #================================================================================================================================================== + 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) + + + + #================================================================================================================================================== + #======= 第一只:成交量绘图 + #================================================================================================================================================== + xindex= numpy.arange(length) # X 轴上的 index,一个辅助数据 + + zipoc= zip(pdata[u'开盘'], pdata[u'收盘']) + up= numpy.array( [ True if po < pc and po != None else False for po, pc in zipoc] ) # 标示出该天股价日内上涨的一个序列 + down= numpy.array( [ True if po > pc and po != None else False for po, pc in zipoc] ) # 标示出该天股价日内下跌的一个序列 + side= numpy.array( [ True if po == pc and po != None else False for po, pc in zipoc] ) # 标示出该天股价日内走平的一个序列 + + + + if u'成交额' in pdata: + volume= pdata[u'成交额'] + else: + volume= pdata[u'成交量'] + + rarray_vol= numpy.array(volume) + volzeros= numpy.zeros(length) # 辅助数据 + + # XXX: 如果 up/down/side 各项全部为 False,那么 vlines() 会报错。 + if True in up: + axes_2.vlines(xindex[up], volzeros[up], rarray_vol[up], edgecolor='red', linewidth=3.0, label='_nolegend_') + if True in down: + axes_2.vlines(xindex[down], volzeros[down], rarray_vol[down], edgecolor='green', linewidth=3.0, label='_nolegend_') + if True in side: + axes_2.vlines(xindex[side], volzeros[side], rarray_vol[side], edgecolor='0.7', linewidth=3.0, label='_nolegend_') + + + + # 第一只:设定 X 轴坐标的范围 + #================================================================================================================================================== + axes_2.set_xlim(-1, length) + + + + # 第一只:设定 X 轴上的坐标 + #================================================================================================================================================== + datelist= [ datetime.date(int(ys), int(ms), int(ds)) for ys, ms, ds in [ dstr.split('-') for dstr in pdata[u'日期'] ] ] + + # 确定 X 轴的 MajorLocator + mdindex= [] # 每个月第一个交易日在所有日期列表中的 index + years= set([d.year for d in datelist]) # 所有的交易年份 + + for y in sorted(years): + months= set([d.month for d in datelist if d.year == y]) # 当年所有的交易月份 + for m in sorted(months): + monthday= min([dt for dt in datelist if dt.year==y and dt.month==m]) # 当月的第一个交易日 + 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) + + xMinorLocator= FixedLocator(numpy.array( sorted(wdindex.values()) )) + + # 第一只:确定 X 轴的 MajorFormatter 和 MinorFormatter + def x_major_formatter_2(idx, pos=None): + return datelist[idx].strftime('%Y-%m-%d') + + def x_minor_formatter_2(idx, pos=None): + return datelist[idx].strftime('%m-%d') + + xMajorFormatter= FuncFormatter(x_major_formatter_2) + xMinorFormatter= FuncFormatter(x_minor_formatter_2) + + # 第一只:设定 X 轴的 Locator 和 Formatter + xaxis_2.set_major_locator(xMajorLocator) + xaxis_2.set_major_formatter(xMajorFormatter) + + xaxis_2.set_minor_locator(xMinorLocator) + xaxis_2.set_minor_formatter(xMinorFormatter) + + # 第一只:设定 X 轴主要坐标点与辅助坐标点的样式 + for malabel in axes_2.get_xticklabels(minor=False): + malabel.set_fontsize(4) + malabel.set_horizontalalignment('right') + malabel.set_rotation('45') + + for milabel in axes_2.get_xticklabels(minor=True): + milabel.set_fontsize(4) + milabel.set_color('blue') + milabel.set_horizontalalignment('right') + milabel.set_rotation('45') + + + + # 第一只:设定成交量 Y 轴坐标的范围 + #================================================================================================================================================== + maxvol= max(volume) # 注意是 int 类型 + axes_2.set_ylim(0, maxvol) + + + + # 第一只:设定成交量 Y 轴上的坐标 + #================================================================================================================================================== + vollen= len(str(maxvol)) + + volstep_pri= int(round(maxvol/10.0+5000, -4)) + + yMajorLocator_2= MultipleLocator(volstep_pri) + + + + # 第一只:确定 Y 轴的 MajorFormatter + dimsuffix= u'元' if u'成交额' in pdata else u'股' + def y_major_formatter_2(num, pos=None): + if num >= 10**8: # 大于 1 亿 + return (str(round(num/10.0**8, 2)) + u'亿' + dimsuffix) if num != 0 else '0' + else: + return (str(num/10.0**4) + u'万' + dimsuffix) if num != 0 else '0' + + # def y_major_formatter_2(num, pos=None): + # return int(num) + yMajorFormatter_2= FuncFormatter(y_major_formatter_2) + + # 确定 Y 轴的 MinorFormatter + # def y_minor_formatter_2(num, pos=None): + # return int(num) + # yMinorFormatter_2= FuncFormatter(y_minor_formatter_2) + yMinorFormatter_2= NullFormatter() + + # 第一只:设定 X 轴的 Locator 和 Formatter + yaxis_2.set_major_locator(yMajorLocator_2) + yaxis_2.set_major_formatter(yMajorFormatter_2) + + # yaxis_2.set_minor_locator(yMinorLocator_2) + yaxis_2.set_minor_formatter(yMinorFormatter_2) + + # 第一只:设定 Y 轴主要坐标点与辅助坐标点的样式 + for malab in axes_2.get_yticklabels(minor=False): + malab.set_font_properties(__font_properties__) + malab.set_fontsize(4.5) # 这个必须放在前一句后面,否则作用会被覆盖 + + + + # 第一只:成交量数值在图中间的显示 + #================================================================================================================================================== + for iy in range(volstep_pri, maxvol, volstep_pri): + for ix in mdindex[1:-1:3]: + newlab= axes_2.text(ix+8, iy, y_major_formatter_2(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') + + + + #================================================================================================================================================== + #================================================================================================================================================== + #======= + #======= XXX: 第二条成交量图线 + #======= + #================================================================================================================================================== + #================================================================================================================================================== + + # 添加 Axes 对象 + #================================================================================================================================================== + axes_2_sec= axes_2.twinx() + # axes_2_sec.set_axisbelow(True) # 网格线放在底层 + + axes_2_sec.set_axisbelow(True) # 网格线放在底层 + + # 改变坐标线的颜色 + #================================================================================================================================================== + # for child in axes_2_sec.get_children(): + # if isinstance(child, matplotlib.spines.Spine): + # child.set_color('lightblue') + + # 得到 X 轴 和 Y 轴 的两个 Axis 对象 + #================================================================================================================================================== + xaxis_2_sec= axes_2_sec.get_xaxis() + yaxis_2_sec= axes_2_sec.get_yaxis() + + # 设置两个坐标轴上的 grid + #================================================================================================================================================== + # xaxis_2_sec.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # xaxis_2_sec.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1) + + # yaxis_2_sec.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + # yaxis_2_sec.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1) + + + + #================================================================================================================================================== + #======= 绘图 + #================================================================================================================================================== + + if u'成交额二' in pdata: + volume_sec= pdata[u'成交额二'] + else: + volume_sec= pdata[u'成交量二'] + + zipoc_sec= zip(pdata[u'开盘二'], pdata[u'收盘二']) + up_sec= numpy.array( [ True if po < pc and po != None else False for po, pc in zipoc_sec] ) # 标示出该天股价日内上涨的一个序列 + down_sec= numpy.array( [ True if po > pc and po != None else False for po, pc in zipoc_sec] ) # 标示出该天股价日内下跌的一个序列 + side_sec= numpy.array( [ True if po == pc and po != None else False for po, pc in zipoc_sec] ) # 标示出该天股价日内走平的一个序列 + + rarray_vol_sec= numpy.array(volume_sec) + volzeros_sec= numpy.zeros(length) # 辅助数据 + + # XXX: 如果 up_sec/down_sec/side_sec 各项全部为 False,那么 vlines() 会报错。 + if True in up_sec: + axes_2_sec.vlines(xindex[up_sec], volzeros_sec[up_sec], rarray_vol_sec[up_sec], edgecolor='pink', linewidth=1.0, label='_nolegend_', alpha=0.3) + if True in down_sec: + axes_2_sec.vlines(xindex[down_sec], volzeros_sec[down_sec], rarray_vol_sec[down_sec], edgecolor='lightgreen', linewidth=1.0, label='_nolegend_', alpha=0.3) + if True in side_sec: + axes_2_sec.vlines(xindex[side_sec], volzeros_sec[side_sec], rarray_vol_sec[side_sec], edgecolor='0.7', linewidth=1.0, label='_nolegend_', alpha=0.3) + + + + # 设定 X 轴坐标的范围 + #================================================================================================================================================== + # XXX: 不用了,与 axes_2 共用。 + + + # 设定 Y 轴坐标的范围 + #================================================================================================================================================== + maxvol_sec= max(volume_sec) # 注意是 int 类型 + axes_2_sec.set_ylim(0, maxvol_sec) + + + + # 设定 Y 轴上的坐标 + #================================================================================================================================================== + + volstep_sec= volstep_pri*maxvol_sec/float(maxvol) + yMajorLocator_2_sec= MultipleLocator(volstep_sec) + + # 确定 Y 轴的 MajorFormatter + dimsuffix_sec= u'元' if u'成交额二' in pdata else u'股' + def y_major_formatter_2_sec(num, pos=None): + if num >= 10**8: # 大于 1 亿 + print(('num= ' + str(num) + ', result= ' + str(round(num/10.0**8, 3)) + u'亿' + dimsuffix_sec).encode('utf8')) + + return (str(round(num/10.0**8, 3)) + u'亿' + dimsuffix_sec) if num != 0 else '0' + else: + return (str(round(num/10.0**4, 2)) + u'万' + dimsuffix_sec) if num != 0 else '0' + + # def y_major_formatter_2_sec(num, pos=None): + # return int(num) + yMajorFormatter_2_sec= FuncFormatter(y_major_formatter_2_sec) + + # 确定 Y 轴的 MinorFormatter + # def y_minor_formatter_2(num, pos=None): + # return int(num) + # yMinorFormatter_2_sec= FuncFormatter(y_minor_formatter_2) + yMinorFormatter_2_sec= NullFormatter() + + # 设定 X 轴的 Locator 和 Formatter + yaxis_2_sec.set_major_locator(yMajorLocator_2_sec) + yaxis_2_sec.set_major_formatter(yMajorFormatter_2_sec) + + # yaxis_2_sec.set_minor_locator(yMinorLocator_2_sec) + yaxis_2_sec.set_minor_formatter(yMinorFormatter_2_sec) + + # 设定 Y 轴主要坐标点与辅助坐标点的样式 + for malab in axes_2_sec.get_yticklabels(minor=False): + malab.set_font_properties(__font_properties__) + malab.set_fontsize(4.5) # 这个必须放在前一句后面,否则作用会被覆盖 + + + + + + #================================================================================================================================================== + #================================================================================================================================================== + #======= + #======= XXX: K 线图部分 + #======= + #================================================================================================================================================== + #================================================================================================================================================== + + # 添加 Axes 对象 + #================================================================================================================================================== + axes_1= figobj.add_axes(rect_1, axis_bgcolor='black', sharex=axes_2) + axes_1.set_axisbelow(True) # 网格线放在底层 + + axes_1.set_yscale('log', basey=expbase) # 使用对数坐标 + + # 改变坐标线的颜色 + #================================================================================================================================================== + for child in axes_1.get_children(): + if isinstance(child, matplotlib.spines.Spine): + child.set_color('lightblue') + + # 得到 X 轴 和 Y 轴 的两个 Axis 对象 + #================================================================================================================================================== + xaxis_1= axes_1.get_xaxis() + yaxis_1= axes_1.get_yaxis() + + # 设置两个坐标轴上的 grid + #================================================================================================================================================== + xaxis_1.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + xaxis_1.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1) + + yaxis_1.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2) + yaxis_1.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1) + + + + #================================================================================================================================================== + #======= 绘图 + #================================================================================================================================================== + + # 绘制 K 线部分 + #================================================================================================================================================== + + # 对开收盘价进行视觉修正 + for idx, poc in enumerate( zip(pdata[u'开盘'], pdata[u'收盘']) ): + if poc[0] == poc[1] and None not in poc: + variant= round((poc[1]+1000)/2000, 0) + pdata[u'开盘'][idx]= poc[0] - variant # 稍微偏离一点,使得在图线上不致于完全看不到 + pdata[u'收盘'][idx]= poc[1] + variant + + rarray_open= numpy.array(pdata[u'开盘']) + rarray_close= numpy.array(pdata[u'收盘']) + rarray_high= numpy.array(pdata[u'最高']) + rarray_low= numpy.array(pdata[u'最低']) + + # XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。 + # XXX: 可以使用 alpha 参数调节透明度 + if True in up: + axes_1.vlines(xindex[up], rarray_low[up], rarray_high[up], edgecolor='red', linewidth=0.6, label='_nolegend_') + axes_1.vlines(xindex[up], rarray_open[up], rarray_close[up], edgecolor='red', linewidth=3.0, label='_nolegend_') + + if True in down: + axes_1.vlines(xindex[down], rarray_low[down], rarray_high[down], edgecolor='green', linewidth=0.6, label='_nolegend_') + axes_1.vlines(xindex[down], rarray_open[down], rarray_close[down], edgecolor='green', linewidth=3.0, label='_nolegend_') + + if True in side: + axes_1.vlines(xindex[side], rarray_low[side], rarray_high[side], edgecolor='0.7', linewidth=0.6, label='_nolegend_') + axes_1.vlines(xindex[side], rarray_open[side], rarray_close[side], edgecolor='0.7', linewidth=3.0, label='_nolegend_') + + # 绘制均线部分 + #================================================================================================================================================== + if u'5日均' in pdata: + rarray_5dayave= numpy.array(pdata[u'5日均']) + axes_1.plot(xindex, rarray_5dayave, 'o-', color='white', linewidth=0.1, label='ave_5', \ + markersize=0.7, markeredgecolor='white', markeredgewidth=0.1) # 5日均线 + + if u'10日均' in pdata: + rarray_10dayave= numpy.array(pdata[u'10日均']) + axes_1.plot(xindex, rarray_10dayave, 'o-', color='yellow', linewidth=0.1, label='ave_10', \ + markersize=0.7, markeredgecolor='yellow', markeredgewidth=0.1) # 10日均线 + + if u'30日均' in pdata: + rarray_30dayave= numpy.array(pdata[u'30日均']) + axes_1.plot(xindex, rarray_30dayave, 'o-', color='cyan', linewidth=0.1, label='ave_30', \ + markersize=0.7, markeredgecolor='cyan', markeredgewidth=0.1) # 30日均线 + + + + # 绘制 复权提示 + #================================================================================================================================================== + if u'复权' in pdata: + adjdict= dict(pdata[u'复权']) + + for idx, dstr in enumerate(pdata[u'日期']): + if dstr in adjdict: + axes_1.plot([idx, idx], [ylowlim_price, yhighlim_price], '-', color='purple', linewidth=0.3) + + + + + + # 设定 X 轴坐标的范围 + #================================================================================================================================================== + axes_1.set_xlim(-1, length) + + + + # 先设置 label 位置,再将 X 轴上的坐标设为不可见。因为与 成交量子图 共用 X 轴 + #================================================================================================================================================== + + # 设定 X 轴的 Locator 和 Formatter + xaxis_1.set_major_locator(xMajorLocator) + xaxis_1.set_major_formatter(xMajorFormatter) + + xaxis_1.set_minor_locator(xMinorLocator) + xaxis_1.set_minor_formatter(xMinorFormatter) + + # 将 X 轴上的坐标设为不可见。 + for malab in axes_1.get_xticklabels(minor=False): + malab.set_visible(False) + + for milab in axes_1.get_xticklabels(minor=True): + milab.set_visible(False) + + # 用这一段效果也一样 + # pyplot.setp(axes_1.get_xticklabels(minor=False), visible=False) + # pyplot.setp(axes_1.get_xticklabels(minor=True), visible=False) + + + + # 设定 Y 轴坐标的范围 + #================================================================================================================================================== + axes_1.set_ylim(ylowlim_price, yhighlim_price) + + + + # 设定 Y 轴上的坐标 + #================================================================================================================================================== + + # XXX: 不用 LogLocator 了,因为不能控制坐标点的位置。 + + # 主要坐标点 + #---------------------------------------------------------------------------- + yticks_major_pri= [] + for i in range(1, 999): + newloc= ylowlim_price * (expbase**i) + if newloc <= yhighlim_price: + yticks_major_pri.append(newloc) + else: + break + + yMajorLocator_1= FixedLocator(numpy.array(yticks_major_pri)) + + # 确定 Y 轴的 MajorFormatter + def y_major_formatter_1(num, pos=None): + return str(round(num/1000.0, 2)) + + yMajorFormatter_1= FuncFormatter(y_major_formatter_1) + + # 设定 X 轴的 Locator 和 Formatter + yaxis_1.set_major_locator(yMajorLocator_1) + yaxis_1.set_major_formatter(yMajorFormatter_1) + + # 设定 Y 轴主要坐标点与辅助坐标点的样式 + for mal in axes_1.get_yticklabels(minor=False): + mal.set_fontsize(6) + + + + # 辅助坐标点 + #---------------------------------------------------------------------------- + yticks_minor_pri= [] + mtstart= ylowlim_price * (1.0+(expbase-1.0)/2) + for i in range(999): + newloc= mtstart * (expbase**i) + if newloc <= yhighlim_price: + yticks_minor_pri.append(newloc) + else: + break + + yMinorLocator_1= FixedLocator(numpy.array(yticks_minor_pri)) # XXX minor ticks 已经在上面一并设置,这里不需要了。 + + # 确定 Y 轴的 MinorFormatter + def y_minor_formatter_1(num, pos=None): + return str(round(num/1000.0, 2)) + + yMinorFormatter_1= FuncFormatter(y_minor_formatter_1) + + # 设定 X 轴的 Locator 和 Formatter + yaxis_1.set_minor_locator(yMinorLocator_1) + yaxis_1.set_minor_formatter(yMinorFormatter_1) + # 设定 Y 轴主要坐标点与辅助坐标点的样式 + for mal in axes_1.get_yticklabels(minor=True): + mal.set_fontsize(5) + mal.set_color('blue') + + + + # 第一只:价格数值在图中间的显示 + #================================================================================================================================================== + for iy in yticks_major_pri: + for ix in mdindex[1:-1:3]: + newlab= axes_1.text(ix+8, iy*1.001, y_major_formatter_1(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') + + + + # 第一只:日期在图中间的显示 + #================================================================================================================================================== + for iy in yticks_minor_pri[1:-1:5]: + for ix in mdindex: + newlab= axes_1.text(ix-1, iy, pdata[u'日期'][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_zorder(0) # XXX: 放在底层 + # newlab.set_verticalalignment('center') + + + + #================================================================================================================================================== + #================================================================================================================================================== + #======= + #======= XXX: 第二条 K 线图 + #======= + #================================================================================================================================================== + #================================================================================================================================================== + + # 添加 Axes 对象 + #================================================================================================================================================== + axes_1_sec= axes_1.twinx() + # axes_1_sec.set_axisbelow(True) # 网格线放在底层 + + axes_1_sec.set_yscale('log', basey=expbase) # 使用对数坐标 + + + # 得到 X 轴 和 Y 轴 的两个 Axis 对象 + #================================================================================================================================================== + xaxis_1_sec= axes_1_sec.get_xaxis() + yaxis_1_sec= axes_1_sec.get_yaxis() + + + + #================================================================================================================================================== + #======= 绘图 + #================================================================================================================================================== + + # 绘制 K 线部分 + #================================================================================================================================================== + + # 对开收盘价进行视觉修正 + for idx, poc in enumerate( zipoc_sec ): + if poc[0] == poc[1] and None not in poc: + pdata[u'开盘二'][idx]= poc[0] - 5 # 稍微偏离一点,使得在图线上不致于完全看不到 + pdata[u'收盘二'][idx]= poc[1] + 5 + + rarray_open= numpy.array(pdata[u'开盘二']) + rarray_close= numpy.array(pdata[u'收盘二']) + rarray_high= numpy.array(pdata[u'最高二']) + rarray_low= numpy.array(pdata[u'最低二']) + + # XXX: 如果 up_sec, down_sec, side_sec 里有一个全部为 False 组成,那么 vlines() 会报错。 + # XXX: 可以使用 alpha 参数调节透明度 + if True in up_sec: + axes_1_sec.vlines(xindex[up_sec], rarray_low[up_sec], rarray_high[up_sec], edgecolor='red', linewidth=0.6, label='_nolegend_', alpha=0.3) + axes_1_sec.vlines(xindex[up_sec], rarray_open[up_sec], rarray_close[up_sec], edgecolor='red', linewidth=3.0, label='_nolegend_', alpha=0.3) + + if True in down_sec: + axes_1_sec.vlines(xindex[down_sec], rarray_low[down_sec], rarray_high[down_sec], edgecolor='green', linewidth=0.6, label='_nolegend_', alpha=0.3) + axes_1_sec.vlines(xindex[down_sec], rarray_open[down_sec], rarray_close[down_sec], edgecolor='green', linewidth=3.0, label='_nolegend_', alpha=0.3) + + if True in side_sec: + axes_1_sec.vlines(xindex[side_sec], rarray_low[side_sec], rarray_high[side_sec], edgecolor='0.7', linewidth=0.6, label='_nolegend_', alpha=0.3) + axes_1_sec.vlines(xindex[side_sec], rarray_open[side_sec], rarray_close[side_sec], edgecolor='0.7', linewidth=3.0, label='_nolegend_', alpha=0.3) + + + + # 设定 X 轴坐标的范围 + #================================================================================================================================================== + axes_1_sec.set_xlim(-1, length) + + + + # 先设置 label 位置,再将 X 轴上的坐标设为不可见。因为与 成交量子图 共用 X 轴 + #================================================================================================================================================== + + # 设定 X 轴的 Locator 和 Formatter + xaxis_1_sec.set_major_locator(xMajorLocator) + xaxis_1_sec.set_major_formatter(xMajorFormatter) + + xaxis_1_sec.set_minor_locator(xMinorLocator) + xaxis_1_sec.set_minor_formatter(xMinorFormatter) + + # 将 X 轴上的坐标设为不可见。 + for malab in axes_1_sec.get_xticklabels(minor=False): + malab.set_visible(False) + + for milab in axes_1_sec.get_xticklabels(minor=True): + milab.set_visible(False) + + + + # 设定 Y 轴坐标的范围 + #================================================================================================================================================== + axes_1_sec.set_ylim(ylowlim_price*open_price_sec/open_price_pri, yhighlim_price*open_price_sec/open_price_pri) + + + + # 设定 Y 轴上的坐标 + #================================================================================================================================================== + + # 主要坐标点 + #---------------------------------------------------------------------------- + yticks_major_sec= [] + ylowlim_price_sec= ylowlim_price*open_price_sec/open_price_pri + yhighlim_price_sec= yhighlim_price*open_price_sec/open_price_pri + + for i in range(1, 999): + newloc= ylowlim_price_sec * (expbase**i) + if newloc <= yhighlim_price_sec: + yticks_major_sec.append(newloc) + else: + break + + yMajorLocator_1_sec= FixedLocator(numpy.array(yticks_major_sec)) + + # 确定 Y 轴的 MajorFormatter + def y_major_formatter_1_sec(num, pos=None): + return str(round(num/1000.0, 2)) + + yMajorFormatter_1_sec= FuncFormatter(y_major_formatter_1_sec) + + # 设定 X 轴的 Locator 和 Formatter + yaxis_1_sec.set_major_locator(yMajorLocator_1_sec) + yaxis_1_sec.set_major_formatter(yMajorFormatter_1_sec) + + # 设定 Y 轴主要坐标点与辅助坐标点的样式 + for mal in axes_1_sec.get_yticklabels(minor=False): + mal.set_fontsize(6) + + + + # 辅助坐标点 + #---------------------------------------------------------------------------- + yticks_minor_sec= [] + mtstart_sec= ylowlim_price_sec * (1.0+(expbase-1.0)/2) + for i in range(999): + newloc= mtstart_sec * (expbase**i) + if newloc <= yhighlim_price_sec: + yticks_minor_sec.append(newloc) + else: + break + + yMinorLocator_1_sec= FixedLocator(numpy.array(yticks_minor_sec)) # XXX minor ticks 已经在上面一并设置,这里不需要了。 + + # 确定 Y 轴的 MinorFormatter + def y_minor_formatter_1_sec(num, pos=None): + return str(round(num/1000.0, 2)) + + yMinorFormatter_1_sec= FuncFormatter(y_minor_formatter_1_sec) + + # 设定 X 轴的 Locator 和 Formatter + yaxis_1_sec.set_minor_locator(yMinorLocator_1_sec) + yaxis_1_sec.set_minor_formatter(yMinorFormatter_1_sec) + # 设定 Y 轴主要坐标点与辅助坐标点的样式 + for mal in axes_1_sec.get_yticklabels(minor=True): + mal.set_fontsize(5) + mal.set_color('blue') + + + + # 显示图片 + #================================================================================================================================================== + # pyplot.show() + + # 保存图片 + #================================================================================================================================================== + figobj.savefig(figpath, dpi=figdpi, facecolor=figfacecolor, edgecolor=figedgecolor, linewidth=figlinewidth) + + + +if __name__ == '__main__': + Plot(pfile=sys.argv[1], figpath=sys.argv[2]) \ No newline at end of file diff --git a/vn.training/t1.py b/vn.training/t1.py new file mode 100644 index 00000000..e9d83604 --- /dev/null +++ b/vn.training/t1.py @@ -0,0 +1,7 @@ +# encoding: UTF-8 +import matplotlib.pyplot as plt + +plt.plot([10,20,30]) +plt.xlabel('times') +plt.ylabel('numbers') +plt.show() \ No newline at end of file diff --git a/vnctptd.pyd b/vnctptd.pyd new file mode 100644 index 00000000..925f6add Binary files /dev/null and b/vnctptd.pyd differ diff --git a/vnpy.pyproj b/vnpy.pyproj new file mode 100644 index 00000000..21da358c --- /dev/null +++ b/vnpy.pyproj @@ -0,0 +1,178 @@ + + + + Debug + 2.0 + {5c9c864b-1736-4189-81b1-d32b67b54740} + + vn.event\eventEngine.py + + . + . + {888888a0-9f3d-457c-b088-3a5042f75d52} + Standard Python launcher + {2af0f10d-7135-4994-9156-5d01c9c11b7e} + 2.7 + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file