Python處理金融資料 p.1 – 用pandas與matplotlib來畫股價圖

https://www.youtube.com/watch?v=8s6HAGqLi68
https://www.youtube.com/watch?v=S7bdO6S9XMU

這篇文章裡,我們將會學習如何利用python來分析金融資料。我們主要會利用mpl_finance、pandas與matplotlib三個套件庫

我們先匯入需要用到的套件庫

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import dates as mdates
from matplotlib import ticker as mticker
from mpl_finance import candlestick_ohlc
from matplotlib.dates import DateFormatter
import datetime as dt

這邊我們使用yahoo finance的台積電股票資料,檔案可在這邊下載到
https://drive.google.com/open?id=189v3i7aAfC4zqF0h26-2QFtfmVWFc91f

建立一個python筆記本或是腳本後,將腳本放在與股票資料同一個資料夾。我們就可以用以下的程式碼將股票資料讀為pandas.DataFrame格式

# 匯入資料,把csv檔(連結下方)放到與程式檔同一資料夾
df = pd.read_csv('file.csv',parse_dates=['Date'],index_col=0).dropna()
df = df[(df.index > '2018-06-01')]
df_plot = df[['Open','High','Low','Close']]

這邊我們用read_csv(pandas提供的指令)讀入csv檔案,並選取2018年6月後的股票資料,為了等一下畫股價圖的需求,我們另外建立一個DataFrame,並取名為df_plot

接下來我們來計算MA與KD線,我們的計畫畫兩個子圖,一個子圖顯示日線與交易量,另一個子圖顯示KD線,其中MA線的計算相當簡單,我們使用pandas裡面內建的rolling方法即可回傳一個MA線的DataFrame. 我們將計算的過程包裝成一個函數。

#計算MA線
def moving_average(data,period):
    return data['Close'].rolling(period).mean()

另一方面,計算KD線的規則相對複雜,我們先看一下程式瑪

#計算KD線
'''
Step1:計算RSV:(今日收盤價-最近9天的最低價)/(最近9天的最高價-最近9天的最低價)
Step2:計算K: K = 2/3 X (昨日K值) + 1/3 X (今日RSV)
Step3:計算D: D = 2/3 X (昨日D值) + 1/3 X (今日K值)
'''
def KD(data):
    data_df = data.copy()
    data_df['min'] = data_df['Low'].rolling(9).min()
    data_df['max'] = data_df['High'].rolling(9).max()
    data_df['RSV'] = (data_df['Close'] - data_df['min'])/(data_df['max'] - data_df['min'])
    data_df = data_df.dropna()
    # 計算K
    # K的初始值定為50
    K_list = [50]
    for num,rsv in enumerate(list(data_df['RSV'])):
        K_yestarday = K_list[num]
        K_today = 2/3 * K_yestarday + 1/3 * rsv
        K_list.append(K_today)
    data_df['K'] = K_list[1:]
    # 計算D
    # D的初始值定為50
    D_list = [50]
    for num,K in enumerate(list(data_df['K'])):
        D_yestarday = D_list[num]
        D_today = 2/3 * D_yestarday + 1/3 * K
        D_list.append(D_today)
    data_df['D'] = D_list[1:]
    use_df = pd.merge(data,data_df[['K','D']],left_index=True,right_index=True,how='left')
    return use_df

第一部份:計算出RSV
我們需要先計算RSV,根據定義,我們要將最近9天的最低價與最高價計算出,這邊我們使用pandas的rolling方法配合max,min即可計算出,我們將其存到新的一欄。有了max以及min欄位,我們就可以計算出RSV欄位。
第二部份:計算K線
我們先令K的初始值為50,然後要根據定義去迭代。我想把計算的K值存到一個列表中,所以先建了一個列表K_list,並填入第一個元素為50,接下來我們對RSV欄位跑迴圈,因為每次進入這個迴圈我們都能得到當天的RSV值,我們從K_list中取得昨天的K值並能計算出今天的K值,我們最後再把計算出的K值存回DataFrame一個新的column中,計算D線也是用同樣的邏輯。

有了KD線的計算函數之後,我們要來畫圖了!在這邊,我們要用mpl_finance這個套件,這是原本的matplotlib finance裡面獨立出來的一個套件,可以使用pip安裝,我們將要使用mpl_finance裡面的candlestick_ohlc函數來畫蠟燭圖,為了滿足格式需要(有時間、開盤價、收盤價、最高價與最低價排列好的DataFrame),我們想要新生成一個DataFrame,所以我們寫下以下的函數

def prepare_data(data):
    data_df = data.copy()
    data_df['DateTime'] = data_df.index
    data_df = data_df.reset_index()
    data_df = data_df[['DateTime','Open','High','Low','Close']]
    data_df['DateTime'] = mdates.date2num(data_df['DateTime'].astype(dt.date))
    return data_df

這個函數會將我們的原本的DataFrame抓出DateTime、Open、High、Low以及Close這幾個欄位排列好,並將原本是日期格式的日期,轉為需要candlestick_ohlc使用mdates.date2num格式。接下來就是繪製的主程式。

# 畫股價圖
# 顏色:https://matplotlib.org/users/colors.html

#畫股價線圖與蠟燭圖
def plot_stock_price(data):
    Ma_10 = moving_average(data,10)
    Ma_50 = moving_average(data,50)
    Length = len(data['DateTime'].values[50-1:])
    fig = plt.figure(facecolor='white',figsize=(15,10))
    ax1 = plt.subplot2grid((6,4), (0,0),rowspan=4, colspan=4, facecolor='w')
    candlestick_ohlc(ax1, data.values[-Length:],width=0.6,colorup='red',colordown='green')
    Label1 = '10 MA Line'
    Label2 = '50 MA Line'
    ax1.plot(data.DateTime.values[-Length:],Ma_10[-Length:],'black',label=Label1, linewidth=1.5)
    ax1.plot(data.DateTime.values[-Length:],Ma_50[-Length:],'navy',label=Label2, linewidth=1.5)
    ax1.legend(loc='upper center', ncol=2)
    ax1.grid(True, color='black')
    ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
    ax1.yaxis.label.set_color("black")
    ax1.tick_params(axis='y', colors='black')
    ax1.tick_params(axis='x', colors='black')
    plt.ylabel('Stock price and Volume')
    plt.suptitle('Stock Code:2330',color='black',fontsize=16)
    #畫交易量
    ax1v = ax1.twinx()
    ax1v.fill_between(data.DateTime.values[-Length:],0, df.Volume.values[-Length:], facecolor='navy', alpha=.4)
    ax1v.axes.yaxis.set_ticklabels([])
    ax1v.grid(False)
    ax1v.set_ylim(0, 3*df.Volume.values.max())
    ax1v.tick_params(axis='x', colors='black')
    ax1v.tick_params(axis='y', colors='black')
    #加入KD線在下方
    ax2 = plt.subplot2grid((6,4), (4,0), sharex=ax1, rowspan=1, colspan=4, facecolor='white')
    ax2.plot(daysreshape.DateTime.values[-Length:], df.K[-Length:],color='black')
    ax2.plot(daysreshape.DateTime.values[-Length:], df.D[-Length:],color='navy')
    plt.ylabel('KD Value', color='black')
    

首先,我們利用我們寫好的計算移動平均的函數先計算出10天與50天的移動平均,接下來我們新增一張畫布(fig),然後畫第一張子圖(ax1),裡面我們先利用candlestick_ohlc將股價圖畫在ax1上,接下來在將兩條MA線畫上,我們也將x軸的日期格式在轉回日期表示。
接下來我們畫交易量,交易量因為要跟股價圖分享同一個x軸,所以我們輸入指令ax1v = ax1.twinx(),然後我們使用fill_between畫出交易量。
最後我們加入KD線的子圖(ax2),並將K值與D值畫在上面。

最後我們執行

daysreshape = prepare_data(df_plot)
plot_stock_price(daysreshape)
plt.show()

即可看到成品

Share

14 thoughts on “Python處理金融資料 p.1 – 用pandas與matplotlib來畫股價圖

  1. 當我輸入最後一行程式時

    daysreshape = prepare_data(df_plot)
    plot_stock_price(daysreshape)
    plt.show()

    —————————————————————————
    TypeError Traceback (most recent call last)
    in
    —-> 1 daysreshape = prepare_data(df_plot)
    2 plot_stock_price(daysreshape)
    3 plt.show()
    跑出來error
    TypeError: dtype ” not understood
    這大概是甚麼問題呢?
    謝謝!!

    1. 看起來是在prepare_data這邊有問題,感覺是執行mdates.date2num(data_df[‘DateTime’].astype(dt.date))這行時的類別有問題,可以試試看停在這行看看執行的情況。

        1. 1.我有點忘了,但應該是根據RSV的定義https://www.wikiwand.com/zh-tw/%E9%9A%8F%E6%9C%BA%E6%8C%87%E6%A0%87
          可以看到(close – min)/(max-min)之後還要乘100,但程式沒有乘,所以可以在data_df[‘RSV’] = (data_df[‘Close’] – data_df[‘min’])/(data_df[‘max’] – data_df[‘min’] 這行多乘100修改

          2.可以試試,原來是在讀資料就有問題? NameError代表找不到daysreshape這個東西,在原本的程式裡是這行控制daysreshape = prepare_data(df_plot)
          所以代表您把prepare_data這行省略了?

          1. 1.程式開始不是都將K、D值分配一個數字,那如果這樣乘上100的話,那K、D值要怎麼預設?是參考昨天的K、D值還是??

            2.
            最後程式是這樣
            daysreshape = prepare_data(df_plot)
            plot_stock_price(daysreshape)
            plt.show()
            我看有問題好像是plot_stock_price(daysreshape)這行,上面會出現驚嘆號
            NameError: name ‘daysreshape’ is not defined

          2. 1.因為第一天沒有昨天的資料,所以直接設起始值為50,但也沒一定要這麼做!
            2.恩恩,但是NameError代表的意思是程式找不到daysreshape,而定義daysreshape在daysreshape = prepare_data(df_plot)這行程式,所以可能是這行執行出了問題

    1. 可以看看直接執行daysreshape = prepare_data(df_plot)會發生什麼事
      如果沒執行錯誤的話,可以檢查一下拼寫有沒有錯,不然不應該有NameError

      1. 我想要請問一下就是假設我資料不要跟您影片的資料一樣,假設我想從2019/01/01開始畫圖,在這當中需要修改甚麼嗎? 或者是我寫程式從2019/01/01開始到今天為止呢,假如我已經寫好並產生CSV檔,這樣能嗎?

  2. K線圖上會出現非交易日的空白區域(像是週六週日沒有交易, 但那兩天還是會顯示在圖上, 而且是空白的), 導致K線圖不連續, 請問該如何畫出不含空白區域的K線圖呢

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *