函式 def 把重複的動作包裝起來!

我們在寫程式的時候,往往會遇到同一段程式碼不斷被重複使用。比如說,我們在選股模型的程式會需要讀取價量資料,而在做回測的時候也會需要讀取一樣的資料。那我們是不是可以寫成一個公版函式,變成一個一般化的、大家都能呼叫來使用的「功能」?

題外話,為了讓非程式底子的讀者在看過一遍就能學會,我們這裡盡量使用生活化的用詞,會大量捨棄程式開發上的標準用語「特性」、「方法、「實例」等來做教學,對於用語要求非常精準的讀者,建議直接閱讀相關教科書即可。

函式 def 怎麼用?

def plus(x, y):
  z = x + y
  return z

以上方的函式為例,第一行 def以及一個半形空格後,接著的是函式名稱,這裡我取名叫做 plus。名稱後方的括號內,定義好我要傳入的兩個參數名稱,分別叫做 x 和 y。同一行最後再加個半形冒號。

第二行開始是定義函式「負責」的任務,相比於 def 第一行,要做一次 tab 縮排,表示是屬於這個函式的內容。上方的例子,函式 plus 的任務首先就是 把 x 和 y 相加的結果存到 z,很單純的加法。

接著,return z 把 z 值回傳回來。換句話說,如果你需要用到這個運算回傳回來的結果,在呼叫函式的時候,記得在等號左邊把回傳值接起來。

如下方程式碼:

z = plus(3, 5) #運算結果 z = 8

a1 = 10
a2 = 12
result = plus(a1, a2) #運算結果 result = 22

注意到了嗎?我可以直接把值打在 plus 後的括號內,用 z 把回傳值 8 接起來;我也可以另外定義兩個變數,把變數傳進去。

變數名稱 a1, a2 和接回傳值的變數名稱 result,不必遵循 plus 內寫的 x, y, z。函式內的x, y, z 只是區域變數,只在 def 內生效。

讀取價量資料的 def,怎麼寫比較好?

基本上先取一個好的函式名稱,例如 get_price 簡單易懂。再來是函式內容:讀取資料後,把每次讀取價量資料後的例行資料整理也寫進來。

接著考量一下,我也不是每一次都要把所有股票的資料都讀取進來,太沒效率了!因此,我們應該把想要讀取的股票代碼清單當作參數傳入函式中。此外,我們也只想取用指定日期區間的資料,不需要回傳一整份資料,太笨重了!

這樣的思路足夠清晰了!因此,依照這個思路,函式會長成這樣:

import pandas as pd
from datetime import datetime

def get_price(
  symbol_list: list = [],
  start_date: datetime = datetime(1970,1,1),
  end_date: datetime = datetime(1970,1,1) 
): 
  price = {}
  for symbol in symbol_list:
    path = f“./price/{symbol}.csv”
    data = pd.read_csv(path, index_col=”time”, parse_dates=True)
    price[symbol] = data.loc[start_date, end_date]   

  return price

這裡說明一下,函式 def 怎麼會變得如此讓人眼花撩亂。Python 對於換行、縮排有很嚴謹的定義,不過有一個規則是,在括號中間的換行視為同一行。

因此,當我們同一行的字元數太多的時候,建議是以上方的例子來做分行:左括號後和右括號前不放程式碼,而括號中間的傳入參數都 tab 縮排一次。基本上一些常見的 IDE 開發工具所提供的排版插件,大致上也是會排成類似這個樣式,相當方便事後維護。

這個例子裡,傳入參數後也多了一些東西。基本上它的格式是:

傳入參數名稱: 參數型態 = 預設值

以第一個傳入參數 symbol_list 來講,我指定它的型態是 list,預設值是一個空的 list。如此定義可以避免一些未預期的錯誤,嚴謹一些還是有好處的。

再來解釋一下函式內在做什麼:這裡開一個空的字典(dictionary)叫做 price,預計存入每一個股票代碼作為索引,來對應到它的價量資料。

因此,在 for 迴圈中,我們每一圈取出一個股票代碼,假設我們的價量資料 csv 存在 price 目錄下,csv 檔案名稱以股票代碼本身作為命名,我們透過 pd.read_csv 即可讀取相關資料。這裡各位讀者可以置換自己的檔案放置路徑。此外,關於 pd.read_csv 的參數細節,可以自行查詢官方文件,或是待之後有機會我們再來詳細解說。

最後,把蒐集好的 price 字典回傳回來,就大功告成了。

回想一下,我們前面說要打造一個一般化的函式,讓計算選股模型可以用、回測也可以用,這樣子確實是大功告成了!

當然,實務上還有一些細節需要處理,例如檢查 csv 檔案是否存在,或是再稍微整理一下回傳資料的長相,才比較方便取用。

關於 def 的其他建議

到目前為止,各位讀者應該能做一些 def 的基本操作了。上面有提到的其他套件例如 pandas 和 datetime,如果不熟的話,建議先查閱相關官方文件,我們之後也會把這部分的教學補齊,敬請期待。

但其實 def 要用得好還有一些小撇步:

  1. 這裡建議一個函式只負責做一件事,例如一個函式負責讀取整理價量資料,就不要再把回測的程式碼寫在這個函式裡。函式架構做得好,日後維護沒煩惱。
  2. 另外,名稱也要定義清楚,例如上方例子的 get_price 是指讀取本地端的價量資料,會不會和價量資料的爬蟲的函式命名存在衝突,需不需要再定義得更清楚?
  3. 關於函式命名格式,許多初學者依照自己的喜好命名,例如 getpricefromlocalfile,這是一個必須改掉的壞習慣。上方例子我們用的是 get_price 的蛇形命名,用底線分隔不同的單字。常見的還有駝峰式,之後有機會再來介紹一下。

量化通粉絲社群,定期分享實用資源
✅加入LINE匿名群組量化通QuantPass」無壓力討論與分享!
✅追蹤量化通的粉絲專頁量化通QuantPass」即時獲取實用的資源!

程式交易課程推薦
📣 Python 程式交易系列線上課程,手把手開始用程式交易打造自己的被動收入!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。