NBLOG_20230615使用MPLFINANCE 打造一個互動的顯示股票線圖工具(3)


 NBLOG_20230615使用MPLFINANCE 打造一個互動的顯示股票線圖工具(3)




首先。先跟大家說一個令人振奮的消息。在這一篇文章當中。我們終於即將完成K線圖形顯示程式。這個未來會常常使用的小工具完工啦!就說興不興奮,高不高興。這段時間總算沒有白費;我們真的給鼓搗出了一些東西,這下子應該不只可以面對家鄉的父老了;都有一種榮歸故里的感覺。

還是先拿出來讓大家看看完工的樣子。還有我們日以繼夜,精工細琢出來的程式碼。忍不住就直接先來一個三連拍。






到了吧?直接上豪華下拉式選單Dropdown。外加航空級線傳飛控絲滑數位顯示天數滑動條。這一套下來,就算是目前最招搖的F1賽車;不只看不到咱們的尾燈,連想品嚐廢氣棄的機會直接都不給。接下來就給大家直接展示,我們這段不到10萬行的精悍程式碼。


import yfinance as yf

import mplfinance as mpf

import ipywidgets as widgets

from IPython.display import display, clear_output


data = None


def download_data(code):

    global data

    data = yf.download(code, start='2023-01-01', end='2023-06-11')


#建立股票代碼下拉式選單widget.

code_dropdown = widgets.Dropdown(

    options=['AAPL', 'NVDA', 'TSLA',’2330.TW’],

    value='AAPL',

    description='Stock Code:',

    disabled=False,

)


# 下載初始資料(全域變數)

download_data(code_dropdown.value)


# 建立顯示天數的滑動條 IntSlider widget


days_slider = widgets.IntSlider(

    value=5,

    min=5,

    max=len(data),

    step=1,

    description='Days:',

    disabled=False,

    continuous_update=False,

    orientation='horizontal',

    readout=True,

    readout_format='d'

)


# 建立執行的按鍵 Button widget

submit_button = widgets.Button(

    description='Plot Chart',

    disabled=False,

    button_style='', # 'success', 'info', 'warning', 'danger' or ''

    tooltip='Click to plot chart',

    icon='check' # (FontAwesome names without the `fa-` prefix)

)


def on_submit_button_clicked(b):

    # Get the input values

    code = code_dropdown.value

    days = days_slider.value


    # Set the candlestick colors and style

    my_color = mpf.make_marketcolors(up='r', down='g', edge='inherit', wick='inherit', volume='inherit')

    my_style = mpf.make_mpf_style(marketcolors=my_color, figcolor='(0.82, 0.83, 0.85)', gridcolor='(0.82, 0.83, 0.85)')


    # Clear the previous chart

    clear_output(wait=True)

    

    # Plot the candlestick chart

    mpf.plot(data.tail(days), type='candle', volume=True, style=my_style, title=str(code), figsize=(8, 5))

    

    # Redisplay the input widgets and button

    display(code_dropdown, days_slider, submit_button)


submit_button.on_click(on_submit_button_clicked)


# 檢查輸入代碼是否更新

Define a function to handle stock code changes

def on_code_dropdown_change(change):

    if change['type'] == 'change' and change['name'] == 'value':

        download_data(change['new'])


# Attach the change event handler to the dropdown

code_dropdown.observe(on_code_dropdown_change)





在解釋全部的代碼之前,還是要先來介紹今天加入的兩個小夥伴。首先加入的就是下拉式選單功能的小部件Dropdown widget。在這一次,將原來文字輸入的小部件Text widget,升級成了這種下拉式的選單,方便可以先把聚焦要顯示的股票代碼,一次全部都放進去;比如我們未來篩選出來的股票池。如此;只要在下拉式選單中直接點選,就再也不需要借用鍵盤來輸入了。這個功能我是很有私心的;回臺灣之後,面對的這個花花綠綠的鍵盤,一下子中英文切換的;然後冷不防的又來一個全形半形,已經整個人都快扭曲了。怎麼也不會想到,40多年的友誼小船,才經過1000多個日子;竟然說翻就翻。所以,看到這個下拉式選單,可真是興奮他媽給興奮開門;興奮到家了。以下是這個下拉式選單小部件的示範程式碼。


import ipywidgets as widgets

widgets.Dropdown(

    options=['1', '2', '3'],

    value='2',

    description='Number:',

    disabled=False,

)

這裡比較值得一提的。就是options了。後面的數列['1', '2', '3']。在選單中的所有項目,都是放在這個數列當中。然後下面那一行的value,便是當選單工作的時候,顯示出來的內定值。當然這個必須要是options當中的其中一員,不要問我為什麼?

接著我們來認識一下滑動條這個小部件。直接上示範程式碼。


import ipywidgets as widgets


widgets.IntSlider(

    value=7,

    min=0,

    max=10,

    step=1,

    description='Test:',

    disabled=False,

    continuous_update=False,

    orientation='horizontal',

    readout=True,

    readout_format='d'

)

首先我們看到這個小部件;它的全名叫Intslider。顧名思義它是處理屬於整數intger部分的數值。在我們的小部件家族裡,也有可以處理浮點小數的滑動條FloatSlider。 orientation='horizontal' 沒錯!不是眼花,你沒看錯這個字是叫做水平horizontal。所以它一定還有一個失散兄弟叫做垂直vertical;就說你服不服。兩個新入伙的夥伴介紹完了,其他的時間就開始來整我們的程式碼,因為今天真的有蠻多東西要說的。


def download_data(code):

    global data

    data = yf.download(code, start='2023-01-01', end='2023-06-11')




程式一開頭這三行,應該就會把不少人整頭大了。不要急,聽我慢慢道來。這3行是一夥的,是我們Python裡面的副程式Function。副程式的名稱就叫做”Down load data”;顧名思義就是要下載資料。之後帶入的code,就是股票代碼。這行當中把data這個變數。宣告為global;在Python當中,就代表了這個data被宣告成了全域型的變數。至於為什麼要在這個地方把它宣告成全域型的變數呢?因為我在寫這段程式碼的當中,發現當使用滑動條改變了顯示天數。在原先的程式當中它都會再一次的去向Yahoo資料庫提出下載的需求;感覺上會拖慢了我們執行程式的效率。所以最終程式定案的時候,當下拉選單當中股票代碼沒有被更動或更新,我們便不需要再向資料庫提出下載的需求。而這個全域變數的宣告,就能解決我們這個問題。


#建立股票代碼下拉式選單widget.

code_dropdown = widgets.Dropdown(

    options=['AAPL', 'NVDA', 'TSLA',’2330.TW’],

    value='AAPL',

    description='Stock Code:',

    disabled=False,

)


# 下載初始資料(全域變數)

download_data(code_dropdown.value)


接下來的這部分程式碼,大夥應該知道;就是設定下拉式選單。然後緊接著程式的最後就呼叫了剛才的那個設定全域變數的資料下載副程式。首先解釋一下,以免大家困惑了。其實在模組化的程式當中,並不是依程式碼的排列次序,來想像它的執行順序。就比如這段下載資料的程式碼,它是一個副程式;所以他在全部的程式當中,可能在任何一個位置被呼叫。所以它上面的小部件宣告,也不是宣告完成後就開始去執行的。它是被display指定名稱呼叫時才會執行。所以在我們的程式碼當中。最後這一行 display(code_dropdown, days_slider, submit_button) 我們也可以把它想像成;這裡是所有程式碼執行的開端。因為把這些小部件顯示之後,它才會開始工作。然後我們才可以從這些部件當中接收到接下來所需要的工作變數。


# 建立顯示天數的滑動條 IntSlider widget


days_slider = widgets.IntSlider(

    value=5,

    min=5,

    max=len(data),

    step=1,

    description='Days:',

    disabled=False,

    continuous_update=False,

    orientation='horizontal',

    readout=True,

    readout_format='d'

這段應該說大家都知道。是在初始之後要顯示天數的滑動條。初值顯示為5天,最小值也是5天。它的增加單位step為一天。要注意的是。最大值的設定;在這一項中,我們並不是直接輸入一個固定的數字;而是使用了max=len(data)。這裡所帶入的是data這個資料的長度。也就是我們限制了顯示天數的最大值,為資料的總天數。如此來保證它不會出軌。好一個感情的守護天使。

def on_submit_button_clicked(b):

    # Get the input values

    code = code_dropdown.value

    days = days_slider.value

然後接下來這裡又有一段副程式,是在我們點了按鍵之後所呼叫的。這裡可以看到。從下拉式選單當中,取得我們選擇的股票代碼。以及在滑動條當中得到所想要的顯示天數。然後接下來就是再熟悉不過的一些顯示圖形的程式碼。

# Plot the candlestick chart

    mpf.plot(data.tail(days), type='candle', volume=True, style=my_style, title=str(code), figsize=(8, 5))

    

    # Redisplay the input widgets and button

    display(code_dropdown, days_slider, submit_button)


submit_button.on_click(on_submit_button_clicked)


接著我們此行完所有的K線圖形顯示之後。我們又在一次呼叫了顯示小部件的display。這個原因是因為原先程式碼如果沒有加上這一段新的程式敘述。則會在第一次顯示圖形之後。這些小部件便都會消失了。所以必須要再重新把它們喚醒。我們不必為它特別再去設計一些迴圈,因為下回程式在跑到顯示圖形這一部分的時候,便自動順道又會跑到重新喚醒這部分功能。所以這樣就會保證我們的這些小部件,一直都會顯示出來,而不會平白無故地消失。

這裡容我插一下話;我們這段程式說長也不長。但是其實在寫作的過程裡面,還是有幾個問題需要花一些時間去克服它的。最原先當每次選擇了不同的股票代碼,或者改變了顯示天數的時候,程式碼都會重複的去資料庫下載資料。以及新生成一個圖形的物件,來顯示所要的結果。所以一些時間下來,很快的就會發現;我們已經完成了一幅近代的清明上河圖。所以做了一些事情,包含了就是一次下載所有的資料,並宣告為全域變數;來讓它一直在同一個畫面里面顯示我們所要的結果。然後就是發現剛才所講的;當第一步克服之後,便發現了這些小部件,在第一次工作執行完以後就壽終正寢了。解決的方法就是剛才所提的重新再去呼叫,讓它顯示出來。以下這段程式碼,便是在檢查是否將顯示的股票代碼改變?如果股票代碼被改變;即便需要重新再到資料庫去下載全部的歷史資料。

# 檢查輸入代碼是否更新

Define a function to handle stock code changes

def on_code_dropdown_change(change):

    if change['type'] == 'change' and change['name'] == 'value':

        download_data(change['new'])


# Attach the change event handler to the dropdown

code_dropdown.observe(on_code_dropdown_change)

最後還有一點值得一提的。看看。以下的這段程式碼。

# Set the candlestick colors and style

    my_color = mpf.make_marketcolors(up='r', down='g', edge='inherit', wick='inherit', volume='inherit')

    my_style = mpf.make_mpf_style(marketcolors=my_color, figcolor='(0.82, 0.83, 0.85)', gridcolor='(0.82, 0.83, 0.85)')


    # Clear the previous chart

    clear_output(wait=True)

    

    # Plot the candlestick chart

    mpf.plot(data.tail(days), type='candle', volume=True, style=my_style, title=str(code), figsize=(8, 5))

特別注意。在我們顯示K線圖形的指令之前。有一個很特殊的指令;它也是這次排除的一個小問題。就是在大量的顯示圖形之後,電腦的記憶體將很容易遇到滿載。問題在於,之前顯示的這些圖形佔用的記憶體並沒有被順利的卸載乾淨。所以必須加入這條指令,來清除之前顯示圖形所佔用的記憶體。特別要注意的是;這行指令請務必加在顯示圖形之前。至於為什麼啊?你就當做服藥有分為飯前跟飯後就好了。好吧!不亂扯了。很高興,大家終於把這個圖形顯示的工具完成了。希望大伙看得愉快,也用的愉快。下一次,我們將直接進入更深入的怎麼建立股票池的問題?所以今天就先談到這裡了。祝各位早安午安晚安。



留言

熱門文章