From 44046e3a12e472547ef7599ce26960a64a039b4c Mon Sep 17 00:00:00 2001 From: msincenselee Date: Sat, 2 Jul 2016 10:20:07 +0800 Subject: [PATCH] commit before merge commit before merge --- vn.training/Public/Public.py | 480 +++++ vn.training/Public/__init__.py | 5 + vn.training/SubPlot/__init__.py | 3 + vn.training/SubPlot/公司信息子图.py | 284 +++ vn.training/SubPlot/分时价格子图.py | 580 ++++++ vn.training/SubPlot/分时手数子图.py | 436 +++++ vn.training/SubPlot/实盘价格子图.py | 23 + vn.training/SubPlot/实盘手数子图.py | 22 + vn.training/SubPlot/日线价格子图.py | 1327 ++++++++++++++ vn.training/SubPlot/日线换手子图.py | 488 +++++ vn.training/TushareGetAllDay.py | 22 + vn.training/moving_average.py | 30 + vn.training/quotation.py | 560 ++++++ vn.training/quotation_ext.py | 2597 +++++++++++++++++++++++++++ vn.training/qutation2.py | 802 +++++++++ vn.training/t1.py | 7 + vnctptd.pyd | Bin 0 -> 957952 bytes vnpy.pyproj | 178 ++ 18 files changed, 7844 insertions(+) create mode 100644 vn.training/Public/Public.py create mode 100644 vn.training/Public/__init__.py create mode 100644 vn.training/SubPlot/__init__.py create mode 100644 vn.training/SubPlot/公司信息子图.py create mode 100644 vn.training/SubPlot/分时价格子图.py create mode 100644 vn.training/SubPlot/分时手数子图.py create mode 100644 vn.training/SubPlot/实盘价格子图.py create mode 100644 vn.training/SubPlot/实盘手数子图.py create mode 100644 vn.training/SubPlot/日线价格子图.py create mode 100644 vn.training/SubPlot/日线换手子图.py create mode 100644 vn.training/TushareGetAllDay.py create mode 100644 vn.training/moving_average.py create mode 100644 vn.training/quotation.py create mode 100644 vn.training/quotation_ext.py create mode 100644 vn.training/qutation2.py create mode 100644 vn.training/t1.py create mode 100644 vnctptd.pyd create mode 100644 vnpy.pyproj 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 0000000000000000000000000000000000000000..925f6add6af997f055235c1d24c359b89608390e GIT binary patch literal 957952 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 z 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 z%0A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 literal 0 HcmV?d00001 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