對數回報實作:Lab 1 用 Python 計算累積回報|Quant Theory Lab 筆記

第 18 課・共 34 課

⚠️ 本站內容僅作教育用途,不構成投資建議。投資涉及風險,作出任何投資決定前請諮詢持牌專業人士。
目錄

這是 Liu Peng 大學級量化交易課程第一個 lab 的實作筆記,主題是對數回報 Log Return 的 Python 實作。講者在 Google Colab 上即場寫 code,用 yfinance 下載 Google 股價,把第 10 講推導過的三種累積回報算法逐行實現,並用程式驗證三者結果完全一致。

學習重點

詳細筆記

Lab 環境與數據準備

講者在 Google Colab 的 Jupyter Notebook 環境中示範,整個 lab 以儲存格(cell)為單位逐段執行。第一步是取得數據:用 yfinance 套件下載 Google(GOOG)的股票數據,日期範圍設為 2023 年 1 月 1 日至 1 月 8 日。

symbol = 'GOOG'
df = yf.download(symbol, start="2023-01-01", end="2023-01-08")
df

(以上 code 摘錄自原片教學)

執行後可見一個重要細節:雖然指定了八天的範圍,回傳的 DataFrame 只有四行——2023 年 1 月 3 日至 6 日。原因是 1 月 1 日與 2 日是週末及假期,並非交易日,自然沒有數據。這提醒初學者:日期範圍是查詢條件,不是結果保證,實際行數取決於交易日曆。

數據結構方面,日期被設為索引(index),欄位共六個:開市價(Open)、最高價(High)、最低價(Low)、收市價(Close)、調整後收市價(Adj Close)與成交量(Volume)。本 lab 全程只用 Close 一欄,即每個交易日的收市價。

用 yfinance 下載 GOOG 數據及 pct_change 計算單期回報的 Colab 畫面(截圖出自原片(Liu Peng))

步驟一:pct_change 計算單期簡單回報

取得價格序列後,講者先計算每個交易日的單期回報。pandas 的 Series 自帶百分比變化函數 pct_change(),對序列應用此方法,會回傳一條索引相同的新序列,每個數值是當期相對前期的百分比變化:

$$R_t = \frac{P_t - P_{t-1}}{P_{t-1}} = \frac{P_t}{P_{t-1}} - 1$$

這就是簡單回報 Simple Return,又稱百分比回報。以第二個交易日為例,函數取當日收市價減去前一日收市價,再除以前一日收市價,得出相對前一交易日的百分比變化。

# single-period percentage returns
returns = df.Close.pct_change()
returns

(以上 code 摘錄自原片教學)

輸出中第一期的值是 NaN(空值)。原因很直接:第一個交易日之前沒有價格點,無法計算差值,函數只能留空。這個 NaN 在後續計算會自動被累積函數略過,但實作時要意識到它的存在——例如計算平均回報前,通常要先移除空值,否則部分統計函數會回傳 NaN。

至此得到的 returns 序列,每一項代表該交易日的單日回報,每日數值各自獨立。問題來了:如果期初手持一股(示範中約 89 美元),持有到期末,整段期間的回報是多少?

步驟二:首尾價格法計終值回報

終值回報 Terminal Return 問的是:由期初持有到期末,總共經歷了幾多百分比的變化?最直接的算法只需兩個價格點——期末收市價除以期初收市價,再減 1:

$$R_{0,T} = \frac{P_T}{P_0} - 1$$

其中 $P_T / P_0$ 是 1+R 格式,代表期末資產相對期初本金的倍數;減 1 是把倍數還原成回報率。示範中該週股價下跌,算出的終值回報約為 $-1.72\%$,即持倉價值縮水了約 1.7%。價格跌,回報是負數,方向與價格變動一致。

這個方法勝在直觀,只需首尾兩個數據點;缺點是完全忽略了中間交易日的價格資訊,看不到資金在期間內的升跌軌跡。

步驟三:cumprod 累積回報與交叉驗證

第二種方法把終值回報視為複利 Compounding 過程:每日的回報連本帶利滾入下一日,逐日複利累積。程式上先把簡單回報轉成 1+R 格式(每項代表當日資金的倍數變化),再應用累積乘積函數 cumprod()

$$R_{0,t} = \prod_{i=1}^{t} (1 + R_i) - 1$$

cumprod() 的「累積」是指:每一期的輸出值,等於由期初到當期所有 $(1+R_i)$ 的連乘積。最後再減 1,還原成簡單回報口徑的累積回報 Cumulative Return。

# cumulative returns
cum_returns = (1+returns).cumprod() - 1
cum_returns

(以上 code 摘錄自原片教學)

與首尾價格法不同,cumprod 輸出的是一條完整序列:每一期都顯示由期初持有到該日的累積回報,可以看見資金中間的演變,而非只看終點。

cumprod 計算累積回報,並用 cum_returns.values[-1] == terminal_return 驗證兩法結果相等(截圖出自原片(Liu Peng))

接着講者做了一件值得學習的事——交叉驗證。他用 cum_returns.values[-1] 取出累積回報序列的最後一項(.values 屬性會捨棄索引、只取數值本身),與步驟二首尾價格法算出的終值回報比較,結果回傳 True:兩條路徑抵達同一終點。這與回報與風險的基礎計算|第 7 課的推導一致:多期回報連乘後,中間項全部對銷,理論上只剩 $P_T / P_0$。實作時養成「用兩種方法算同一樣東西、再驗證相等」的習慣,是發現 code 錯誤的有效手段。

步驟四:對數回報、cumsum 與化乘法為加法

第三種方法輪到本 lab 的主角登場。把 1+R 格式的單期回報取自然對數,就得到對數回報 Log Return:

$$r_t = \ln(1 + R_t) = \ln\left(\frac{P_t}{P_{t-1}}\right)$$

程式上用 NumPy 的 np.log() 對整條序列做變換。關鍵分別在下一步:原本簡單回報要用 cumprod() 連乘,對數回報改用 cumsum() 連加,最後以指數函數 np.exp() 還原、再減 1:

$$R_{0,t} = \exp\left(\sum_{i=1}^{t} r_i\right) - 1$$
# log returns (1+R format)
log_returns = np.log(1+returns)
# get cumulative returns using log returns
cum_return2 = np.exp(log_returns.cumsum()) - 1

(以上 code 摘錄自原片教學)

為何乘法可以變加法?講者在畫面上即場手寫推導:設兩個 1+R 格式的數量 $A$ 與 $B$,原本要計算乘積 $A \cdot B$;取對數後,利用對數函數的基本性質,乘積變成求和:

$$\ln(A \cdot B) = \ln A + \ln B$$

求和完畢,再施加指數變換——指數函數是對數函數的逆運算,正好把對數變換「對銷」,還原出原來的乘積:

$$\exp(\ln A + \ln B) = \exp(\ln(A \cdot B)) = A \cdot B$$

對數回報的 cumsum 算法與手寫推導 exp(log(a)+log(b))=a·b(截圖出自原片(Liu Peng))

換言之,整條路徑是「取對數 → 連加 → 取指數 → 減 1」,兜了一個圈,計算的仍是同一樣東西。講者再次交叉驗證:取 cum_return2 的最後一項與終值回報比較,結果同樣相等。三種算法——首尾價格法、cumprod 連乘法、對數回報 cumsum 法——殊途同歸。

那為何要兜這個圈?講者點明:連乘在數學推導上很難處理,加法比乘法容易操作得多。把問題轉到對數空間,累積運算由 cumprod 變 cumsum,之後做統計分析(均值、方差、回歸)都建基於可加變量,推導順暢;需要實際回報數字時,取指數轉回原來尺度即可。這正是第 10 講強調對數回報是量化研究標準口徑的原因,本 lab 則用 code 把這個抽象優點變成可以親手執行的具體操作。

Lab 總結:三條路徑一個終點

回顧整個 lab 的流程:下載數據 → 計算單期簡單回報 → 分別用三種方法計算累積與終值回報 → 逐項驗證相等。技術細節之外,有兩個實作態度值得記下。其一,每個新函數都要看清輸出結構pct_change() 保留原索引、第一期留 NaN;cumprod()cumsum() 輸出完整序列而非單一數值;.values 捨棄索引只取數值。其二,結果要用獨立方法驗證:講者三次使用相等性檢查(回傳 True),確認不同算法的輸出一致,這種「自己考自己」的習慣,在日後策略回測中會救回無數隱藏錯誤。

🙏 覺得內容有用?本站所有內容免費提供,使用推薦碼開戶就是支持我們繼續營運的最大鼓勵:

ZA Bank 邀請碼 P20923 —— 你享 HKD 2,000 迎新獎賞,本站獲得營運支持。 立即開戶 → (聯盟連結|投資涉及風險,受條款及細則約束,此資料僅可於香港境內分發)

欣利克的看法

名詞解釋

常見問題 FAQ

pct_change 計算出來的第一期為何是 NaN?

因為第一個交易日之前沒有價格點,無法計算「相對前期」的百分比變化,函數只能留空。這是正常行為而非錯誤;但後續計算統計量前應先移除空值,避免 NaN 向下游傳染。

cumprod 與 cumsum 算出的累積回報真的完全一樣嗎?

在數學上嚴格相等:由對數性質 $\ln(AB) = \ln A + \ln B$,連乘取對數後變連加,再取指數還原,結果與直接連乘相同。講者在原片以相等性檢查驗證兩者最後一期數值一致,回傳 True。

既然首尾價格相除一行就能算出終值回報,為何還要學對數回報?

首尾價格法只用兩個數據點,看不到中間軌跡,也無法做統計分析。對數回報把多期累積化為加法,均值、方差、回歸等分析工具才能順暢應用——這是量化研究普遍採用對數回報的根本原因,詳見第 10 講

yfinance 下載的日期範圍內,為何不是每日都有數據?

因為只有交易日才有價格記錄。週末與公眾假期休市,指定日期範圍只是查詢條件;原片示範下載 2023 年 1 月 1 日至 8 日的數據,實際只得 1 月 3 日至 6 日共四行。

原影片

本文整理自以下影片,版權屬原創作者所有,建議配合原片觀看:

來源:https://www.youtube.com/watch?v=F-rNvoTT13I

🏦 ZA Bank 開戶優惠

本站所有內容免費提供。多使用網站的推薦碼,就是對我們繼續製作優質內容的最大鼓勵和支持 🙏

去 ZA Bank 開戶,享價值 HKD 2,000 迎新獎賞,仲有機會享 USD 30 指定美股兌換券!

  • 💛 全線上開戶,無需前往分行
  • 💛 無最低存款要求,零賬戶管理費
  • 💛 24x7 銀行服務

專屬邀請碼:P20923

立即開戶 → (聯盟連結)