用來畫股票 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