用来画股票 K线图 的 Python 脚本


<本文的原始位置: http://bluegene8210.is-programmer.com/posts/24902.html>

 

    ---- 花了 20 个小时左右的时间才从新浪下载完复权日线数据,把复权日线表建起来。这速度也太慢了,还有首次下载网页失败的比例居然这么高,一定有问题,印象中以前不是 这么慢的,下载几千只股票的数据也只有几十个页面会首次下载失败吧。但昨天晚上更新最新数据的时候把下载任务之间的延迟扩大了一些,好像好一些,速度还可 以,而且失败率不高。我开的是 5 个线程,下载页面之间的间隔是 0.2 ~ 0.3 秒。

    ---- 另外,把那个画 K 线图的脚本贴出来。这个脚本是通过研究 Matplotlib 官网里的示例并且借助 Google,用大概 1 周的时间改出来的。简单介绍一下:

    1. 由两个子图(subplot)构成,上面一个显示价格(K 线),下面一个显示成交量。
   
    2. K 线子图可以使用线性坐标或者对数坐标(由 Plot() 函数第三个参数控制)。使用线性坐标的时候,每个单位价格区间所占高度是固定的;使用对数坐标的时候,每个单位涨幅区间(比如 10%)所占高度是固定的。成交量子图的高度总是固定,不论成交量数值大小。
   
    3. 对 X 轴来说,每根 K 线的宽度固定,整个图形的宽度决定于行情的天数。只要把行情数据文件作为参数传递过去就可以,图片尺寸由程序自主计算。
   
    4. 另外,figdpi 这个变量控制图片的分辨率(解析度),可以随意调大调小。上一篇文章里贴的图使用的 dpi 值是 300。另外,X 轴和 Y 轴上的坐标点也是程序自主决定的。

    ---- 整个脚本还是一个 work-in-progress,目前的局限主要在于使用对数坐标时,Y 轴坐标点的确定。前一篇里所贴的那个图,可以看见价格上限在 20 块左右,如果换一只价格 90 块上下的股票,或者用来画几千点的指数行情,那 Y 轴的坐标点就会太密集。解决办法是根据取值区间来自主选择合适的 Y 轴坐标间距,但是这个目前还没有做。

    ---- 任何意见或建议都许多欢迎 !

 

    ---- <补记>:已经有了大幅改进的版本,在下一篇里。

  1 # -*- coding: utf-8 -*-
  2  
  3 import sys
  4 import pickle
  5 import math
  6 import datetime
  7 import matplotlib
  8  
  9 matplotlib.use("WXAgg", warn=True)  # 这个要紧跟在 import matplotlib 之后,而且必须安装了 wxpython 2.8 才行。
 10  
 11 import matplotlib.pyplot as pyplot
 12 import numpy
 13 from matplotlib.ticker import FixedLocator, MultipleLocator, LogLocator, FuncFormatter, NullFormatter, LogFormatter
 14  
 15  
 16  
 17 def Plot(pfile, figpath, useexpo=True):
 18     '''
 19     pfile 指明存放绘图数据的 pickle file,figpath 指定图片需存放的路径
 20     '''
 21  
 22     fileobj= open(name=pfile, mode='rb')
 23     pdata= pickle.load(fileobj)
 24     fileobj.close()
 25  
 26     #   计算图片的尺寸(单位英寸)
 27     #   注意:Python2 里面, "1 / 10" 结果是 0, 必须写成 "1.0 / 10" 才会得到 0.1
 28     #==================================================================================================================================================
 29      
 30     length= len(pdata[u'日期'])       # 所有数据的长度,就是天数
 31  
 32     highest_price= max(pdata[u'最高'])    # 最高价
 33     lowest_price= min( [plow for plow in pdata[u'最低'] if plow != None] )    # 最低价
 34  
 35     yhighlim_price= round(highest_price+50, -2) # K线子图 Y 轴最大坐标
 36     ylowlim_price=  round(lowest_price-50, -2)  # K线子图 Y 轴最小坐标
 37  
 38     xfactor= 10.0/230.0 # 一条 K 线的宽度在 X 轴上所占距离(英寸)
 39     yfactor= 0.3    # Y 轴上每一个距离单位的长度(英寸),这个单位距离是线性坐标和对数坐标通用的
 40      
 41     if useexpo: # 要使用对数坐标
 42         expbase= 1.1    # 底数,取得小一点,比较接近 1。股价 3 元到 4 元之间有大约 3 个单位距离
 43         ymulti_price= math.log(yhighlim_price, expbase) - math.log(ylowlim_price, expbase)  # 价格在 Y 轴上的 “份数”
 44  
 45     else:
 46         ymulti_price= (yhighlim_price - ylowlim_price) / 100    # 价格在 Y 轴上的 “份数”
 47      
 48     ymulti_vol= 3.0     # 成交量部分在 Y 轴所占的 “份数”
 49     ymulti_top= 0.2     # 顶部空白区域在 Y 轴所占的 “份数”
 50     ymulti_bot= 0.8     # 底部空白区域在 Y 轴所占的 “份数”
 51  
 52     xmulti_left= 10.0   # 左侧空白区域所占的 “份数”
 53     xmulti_right= 3.0   # 右侧空白区域所占的 “份数”
 54  
 55     xmulti_all= length + xmulti_left + xmulti_right
 56     xlen_fig= xmulti_all * xfactor      # 整个 Figure 的宽度
 57     ymulti_all= ymulti_price + ymulti_vol + ymulti_top + ymulti_bot
 58     ylen_fig= ymulti_all * yfactor      # 整个 Figure 的高度
 59      
 60     rect_1= (xmulti_left/xmulti_all, (ymulti_bot+ymulti_vol)/ymulti_all, length/xmulti_all, ymulti_price/ymulti_all)    # K线图部分
 61     rect_2= (xmulti_left/xmulti_all, ymulti_bot/ymulti_all, length/xmulti_all, ymulti_vol/ymulti_all)   # 成交量部分
 62  
 63     #   建立 Figure 对象
 64     #==================================================================================================================================================
 65     figfacecolor= 'white'
 66     figedgecolor= 'black'
 67     figdpi= 600
 68     figlinewidth= 1.0
 69  
 70     figobj= pyplot.figure(figsize=(xlen_fig, ylen_fig), dpi=figdpi, facecolor=figfacecolor, edgecolor=figedgecolor, linewidth=figlinewidth) # Figure 对象
 71  
 72     #==================================================================================================================================================
 73     #==================================================================================================================================================
 74     #=======    成交量部分
 75     #==================================================================================================================================================
 76     #==================================================================================================================================================
 77  
 78     #   添加 Axes 对象
 79     #==================================================================================================================================================
 80     axes_2= figobj.add_axes(rect_2, axis_bgcolor='black')
 81     axes_2.set_axisbelow(True)  # 网格线放在底层
 82  
 83     #   改变坐标线的颜色
 84     #==================================================================================================================================================
 85     for child in axes_2.get_children():
 86         if isinstance(child, matplotlib.spines.Spine):
 87             child.set_color('lightblue')
 88  
 89     #   得到 X 轴 和 Y 轴 的两个 Axis 对象
 90     #==================================================================================================================================================
 91     xaxis_2= axes_2.get_xaxis()
 92     yaxis_2= axes_2.get_yaxis()
 93  
 94     #   设置两个坐标轴上的 grid
 95     #==================================================================================================================================================
 96     xaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
 97     xaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
 98  
 99     yaxis_2.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
100     yaxis_2.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
101  
102  
103  
104     #==================================================================================================================================================
105     #=======    绘图
106     #==================================================================================================================================================
107     xindex= numpy.arange(length)    # X 轴上的 index,一个辅助数据
108  
109     zipoc= zip(pdata[u'开盘'], pdata[u'收盘'])
110     up=   numpy.array( [ True if po < pc and po != None else False for po, pc in zipoc] )        # 标示出该天股价日内上涨的一个序列
111     down= numpy.array( [ True if po > pc and po != None else False for po, pc in zipoc] )        # 标示出该天股价日内下跌的一个序列
112     side= numpy.array( [ True if po == pc and po != None else False for po, pc in zipoc] )      # 标示出该天股价日内走平的一个序列
113  
114  
115  
116     volume= pdata[u'成交量']
117     rarray_vol= numpy.array(volume)
118     volzeros= numpy.zeros(length)   # 辅助数据
119  
120     # XXX: 如果 up/down/side 各项全部为 False,那么 vlines() 会报错。
121     if True in up:
122         axes_2.vlines(xindex[up], volzeros[up], rarray_vol[up], color='red', linewidth=3.0, label='_nolegend_')
123     if True in down:
124         axes_2.vlines(xindex[down], volzeros[down], rarray_vol[down], color='green', linewidth=3.0, label='_nolegend_')
125     if True in side:
126         axes_2.vlines(xindex[side], volzeros[side], rarray_vol[side], color='0.7', linewidth=3.0, label='_nolegend_')
127      
128  
129  
130     #   设定 X 轴坐标的范围
131     #==================================================================================================================================================
132     axes_2.set_xlim(-1, length)
133  
134  
135  
136     #   设定 X 轴上的坐标
137     #==================================================================================================================================================
138     datelist= [ datetime.date(int(ys), int(ms), int(ds)) for ys, ms, ds in [ dstr.split('-') for dstr in pdata[u'日期'] ] ]
139  
140     # 确定 X 轴的 MajorLocator
141     mdindex= [] # 每个月第一个交易日在所有日期列表中的 index
142     years= set([d.year for d in datelist])  # 所有的交易年份
143  
144     for y in sorted(years):    
145         months= set([d.month for d in datelist if d.year == y])     # 当年所有的交易月份
146         for m in sorted(months):
147             monthday= min([dt for dt in datelist if dt.year==y and dt.month==m])    # 当月的第一个交易日
148             mdindex.append(datelist.index(monthday))
149  
150     xMajorLocator= FixedLocator(numpy.array(mdindex))
151  
152     # 确定 X 轴的 MinorLocator
153     wdindex= [] # 每周第一个交易日在所有日期列表中的 index
154     for d in datelist:
155         if d.weekday() == 0: wdindex.append(datelist.index(d))
156  
157     xMinorLocator= FixedLocator(numpy.array(wdindex))
158  
159     # 确定 X 轴的 MajorFormatter 和 MinorFormatter
160     def x_major_formatter_2(idx, pos=None):
161         return datelist[idx].strftime('%Y-%m-%d')
162  
163     def x_minor_formatter_2(idx, pos=None):
164         return datelist[idx].strftime('%m-%d')
165  
166     xMajorFormatter= FuncFormatter(x_major_formatter_2)
167     xMinorFormatter= FuncFormatter(x_minor_formatter_2)
168  
169     # 设定 X 轴的 Locator 和 Formatter
170     xaxis_2.set_major_locator(xMajorLocator)
171     xaxis_2.set_major_formatter(xMajorFormatter)
172  
173     xaxis_2.set_minor_locator(xMinorLocator)
174     xaxis_2.set_minor_formatter(xMinorFormatter)
175  
176     # 设定 X 轴主要坐标点与辅助坐标点的样式
177     for malabel in axes_2.get_xticklabels(minor=False):
178         malabel.set_fontsize(3)
179         malabel.set_horizontalalignment('right')
180         malabel.set_rotation('30')
181  
182     for milabel in axes_2.get_xticklabels(minor=True):
183         milabel.set_fontsize(2)
184         milabel.set_horizontalalignment('right')
185         milabel.set_rotation('30')
186  
187  
188  
189     #   设定 Y 轴坐标的范围
190     #==================================================================================================================================================
191     maxvol= max(volume) # 注意是 int 类型
192     axes_2.set_ylim(0, maxvol)
193  
194  
195  
196     #   设定 Y 轴上的坐标
197     #==================================================================================================================================================
198     vollen= len(str(maxvol))
199      
200     yMajorLocator_2= MultipleLocator(10**(vollen-1))
201     yMinorLocator_2= MultipleLocator((10**(vollen-2))*5)
202  
203     # 确定 Y 轴的 MajorFormatter
204     #   def y_major_formatter_2(num, pos=None):
205     #       numtable= {'1':u'一', '2':u'二', '3':u'三', '4':u'四', '5':u'五', '6':u'六', '7':u'七', '8':u'八', '9':u'九', }
206     #       dimtable= {3:u'百', 4:u'千', 5:u'万', 6:u'十万', 7:u'百万', 8:u'千万', 9:u'亿', 10:u'十亿', 11:u'百亿'}
207     #       return numtable[str(num)[0]] + dimtable[vollen] if num != 0 else '0'
208  
209     def y_major_formatter_2(num, pos=None):
210         return int(num)
211     yMajorFormatter_2= FuncFormatter(y_major_formatter_2)
212  
213     # 确定 Y 轴的 MinorFormatter
214     #   def y_minor_formatter_2(num, pos=None):
215     #       return int(num)
216     #   yMinorFormatter_2= FuncFormatter(y_minor_formatter_2)
217     yMinorFormatter_2= NullFormatter()
218  
219     # 设定 X 轴的 Locator 和 Formatter
220     yaxis_2.set_major_locator(yMajorLocator_2)
221     yaxis_2.set_major_formatter(yMajorFormatter_2)
222  
223     yaxis_2.set_minor_locator(yMinorLocator_2)
224     yaxis_2.set_minor_formatter(yMinorFormatter_2)
225  
226     # 设定 Y 轴主要坐标点与辅助坐标点的样式
227     for malab in axes_2.get_yticklabels(minor=False):
228         malab.set_fontsize(3)
229  
230     for milab in axes_2.get_yticklabels(minor=True):
231         milab.set_fontsize(2)
232  
233  
234  
235     #==================================================================================================================================================
236     #==================================================================================================================================================
237     #=======    K 线图部分
238     #==================================================================================================================================================
239     #==================================================================================================================================================
240  
241     #   添加 Axes 对象
242     #==================================================================================================================================================
243     axes_1= figobj.add_axes(rect_1, axis_bgcolor='black', sharex=axes_2)
244     axes_1.set_axisbelow(True)  # 网格线放在底层
245      
246     if useexpo:
247         axes_1.set_yscale('log', basey=expbase) # 使用对数坐标
248  
249     #   改变坐标线的颜色
250     #==================================================================================================================================================
251     for child in axes_1.get_children():
252         if isinstance(child, matplotlib.spines.Spine):
253             child.set_color('lightblue')
254  
255     #   得到 X 轴 和 Y 轴 的两个 Axis 对象
256     #==================================================================================================================================================
257     xaxis_1= axes_1.get_xaxis()
258     yaxis_1= axes_1.get_yaxis()
259  
260     #   设置两个坐标轴上的 grid
261     #==================================================================================================================================================
262     xaxis_1.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
263     xaxis_1.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
264  
265     yaxis_1.grid(True, 'major', color='0.3', linestyle='solid', linewidth=0.2)
266     yaxis_1.grid(True, 'minor', color='0.3', linestyle='dotted', linewidth=0.1)
267  
268  
269  
270     #==================================================================================================================================================
271     #=======    绘图
272     #==================================================================================================================================================
273  
274     #   绘制 K 线部分
275     #==================================================================================================================================================
276     rarray_open= numpy.array(pdata[u'开盘'])
277     rarray_close= numpy.array(pdata[u'收盘'])
278     rarray_high= numpy.array(pdata[u'最高'])
279     rarray_low= numpy.array(pdata[u'最低'])
280  
281     # XXX: 如果 up, down, side 里有一个全部为 False 组成,那么 vlines() 会报错。
282     if True in up:
283         axes_1.vlines(xindex[up], rarray_low[up], rarray_high[up], color='red', linewidth=0.6, label='_nolegend_')
284         axes_1.vlines(xindex[up], rarray_open[up], rarray_close[up], color='red', linewidth=3.0, label='_nolegend_')
285     if True in down:
286         axes_1.vlines(xindex[down], rarray_low[down], rarray_high[down], color='green', linewidth=0.6, label='_nolegend_')
287         axes_1.vlines(xindex[down], rarray_open[down], rarray_close[down], color='green', linewidth=3.0, label='_nolegend_')
288     if True in side:
289         axes_1.vlines(xindex[side], rarray_low[side], rarray_high[side], color='0.7', linewidth=0.6, label='_nolegend_')
290         axes_1.vlines(xindex[side], rarray_open[side], rarray_close[side], color='0.7', linewidth=3.0, label='_nolegend_')
291  
292     #   绘制均线部分
293     #==================================================================================================================================================
294     rarray_1dayave= numpy.array(pdata[u'1日权均'])
295     rarray_5dayave= numpy.array(pdata[u'5日均'])
296     rarray_30dayave= numpy.array(pdata[u'30日均'])
297      
298     axes_1.plot(xindex, rarray_1dayave, 'o-', color='white', linewidth=0.1, markersize=0.7, markeredgecolor='white', markeredgewidth=0.1)   # 1日加权均线
299     axes_1.plot(xindex, rarray_5dayave, 'o-', color='yellow', linewidth=0.1, markersize=0.7, markeredgecolor='yellow', markeredgewidth=0.1) # 5日均线
300     axes_1.plot(xindex, rarray_30dayave, 'o-', color='green', linewidth=0.1, markersize=0.7, markeredgecolor='green', markeredgewidth=0.1)  # 30日均线
301  
302     #   设定 X 轴坐标的范围
303     #==================================================================================================================================================
304     axes_1.set_xlim(-1, length)
305  
306  
307  
308     #   先设置 label 位置,再将 X 轴上的坐标设为不可见。因为与 成交量子图 共用 X 轴
309     #==================================================================================================================================================
310  
311     # 设定 X 轴的 Locator 和 Formatter
312     xaxis_1.set_major_locator(xMajorLocator)
313     xaxis_1.set_major_formatter(xMajorFormatter)
314  
315     xaxis_1.set_minor_locator(xMinorLocator)
316     xaxis_1.set_minor_formatter(xMinorFormatter)
317  
318     # 将 X 轴上的坐标设为不可见。
319     for malab in axes_1.get_xticklabels(minor=False):
320         malab.set_visible(False)
321  
322     for milab in axes_1.get_xticklabels(minor=True):
323         milab.set_visible(False)
324  
325     # 用这一段效果也一样
326     #   pyplot.setp(axes_1.get_xticklabels(minor=False), visible=False)
327     #   pyplot.setp(axes_1.get_xticklabels(minor=True), visible=False)
328  
329  
330  
331     #   设定 Y 轴坐标的范围
332     #==================================================================================================================================================
333     axes_1.set_ylim(ylowlim_price, yhighlim_price)
334  
335  
336  
337     #   设定 Y 轴上的坐标
338     #==================================================================================================================================================
339      
340     if useexpo:
341         #   主要坐标点
342         #-----------------------------------------------------
343         yMajorLocator_1= LogLocator(base=expbase)
344          
345         yMajorFormatter_1= NullFormatter()
346  
347         # 设定 X 轴的 Locator 和 Formatter
348         yaxis_1.set_major_locator(yMajorLocator_1)
349         yaxis_1.set_major_formatter(yMajorFormatter_1)
350  
351         # 设定 Y 轴主要坐标点与辅助坐标点的样式
352         #   for mal in axes_1.get_yticklabels(minor=False):
353         #       mal.set_fontsize(3)
354  
355         #   辅助坐标点
356         #-----------------------------------------------------
357         minorticks= range(int(ylowlim_price), int(yhighlim_price)+1, 100)
358          
359         yMinorLocator_1= FixedLocator(numpy.array(minorticks))
360  
361         # 确定 Y 轴的 MinorFormatter
362         def y_minor_formatter_1(num, pos=None):
363             return str(num/100.0) + '0'
364  
365         yMinorFormatter_1= FuncFormatter(y_minor_formatter_1)
366  
367         # 设定 X 轴的 Locator 和 Formatter
368         yaxis_1.set_minor_locator(yMinorLocator_1)
369         yaxis_1.set_minor_formatter(yMinorFormatter_1)
370  
371         # 设定 Y 轴主要坐标点与辅助坐标点的样式
372         for mil in axes_1.get_yticklabels(minor=True):
373             mil.set_fontsize(3)
374  
375     else:   # 如果使用线性坐标,那么只标主要坐标点
376         yMajorLocator_1= MultipleLocator(100)
377  
378         def y_major_formatter_1(num, pos=None):
379             return str(num/100.0) + '0'
380  
381         yMajorFormatter_1= FuncFormatter(y_major_formatter_1)
382  
383         # 设定 Y 轴的 Locator 和 Formatter
384         yaxis_1.set_major_locator(yMajorLocator_1)
385         yaxis_1.set_major_formatter(yMajorFormatter_1)
386  
387         # 设定 Y 轴主要坐标点与辅助坐标点的样式
388         for mal in axes_1.get_yticklabels(minor=False):
389             mal.set_fontsize(3)
390  
391  
392     #   保存图片
393     #==================================================================================================================================================
394     figobj.savefig(figpath, dpi=figdpi, facecolor=figfacecolor, edgecolor=figedgecolor, linewidth=figlinewidth)
395  
396  
397  
398 if __name__ == '__main__':
399     Plot(pfile=sys.argv[1], figpath=sys.argv[2], useexpo=True)
单股K线

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM