<本文的原始位置: 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)