資料科學在量化金融中的應用:指數預測(上)
自2018年末以來,全球金融基本盤由早期的穩步上升變得起伏不定。由於投資者對市場走向和未來展望的不確定,這樣的市場情緒帶動著大盤和指數起起伏伏。同時中美貿易戰給零售帶來的巨大關稅壓力讓市場情緒再度低迷;2020年初,新冠肺炎疫情在全球肆虐蔓延,造成醫療資源緊張,疫情防控措施使商品和人員流動速度減慢,多地商店停業甚至倒閉,大大影響了人們的經濟生活,進一步導致全球各地股票市場出現熔斷甚至跌停的狀況。面對動盪的金融市場,政府和金融監管機構推行了各式各樣的政策以應對疫情造成的影響,穩住經濟與股票基本盤。
對於許多金融從業者而言,市場的動盪意味著新的投資機遇,而2008年金融危機的教訓也時刻提醒著人們,機遇背後往往伴隨著巨大的風險。著名的《巴塞爾協定III》就是在應對全球金融危機、強化金融監管的背景下應運而生的,它也是現階段被廣泛運用於各大金融機構的全球金融監管標準之一。如何識別風險、保證資產安全並保持其相應的流動性一直都是風險管理中重要的組成部分。在股票市場的風險管理中,金融從業者需要先檢視價格變化是否在預期範圍內,並判斷這樣的變化是由系統風險還是政策變化或重大事件所產生。如非系統風險,金融從業者則需要在對政策或重大事件充分的瞭解後,決定是否需要調整當下的策略,買入或出售相應的資產來保障機構金融層面的安全。判斷系統風險的方法有多種,其中較為流行的方法是通過歷史價格資訊來預測未來價格,並比較真實價格與預測結果的差距。在本篇中,我們將使用這樣的方法進行股價預測,並與真實價格進行對比。
解決方案結構
為了實現對歷史資料進行建模來預測未來價格,本篇採用如下結構的解決方案,同時大部分資料科學的專案也都可以使用類似的結構來完成:
- 確定預測目標
- 資料收集
- 探索性資料分析
- 資料預處理
- 特徵工程
- 模型選擇和訓練
- 模型評估
- 模型預測
確定預測目標
在股票市場中,波動較大的股票不佔少數。為了解市場狀況,金融從業人員會傾向於研究分析股票指數而非單個股票的價格。因為指數相對於單個股票來說更穩定,也更利於精準地建模。指數通常由多種股票構成,一般反映市場績效的是廣基指數,如道瓊斯指數、標普500指數、日經指數、恆生指數等。這樣的指數既可以反映出股票基本盤狀況,又可以反映出投資人對經濟現況的敏感度。在本篇中,我們選擇的目標預測指數是標普500指數,是全球最具標誌性的追蹤美國高市值公司的股票指數之一。
資料收集
收集金融資料的平臺眾多,其中像Bloomberg、Qliq、Quandl 這樣的國外市場平臺,抑或是如同花順、萬德這樣的國內平臺都受到大眾的喜愛。大部分企業為了保證資料的準確性、安全性和時效性,在生產環境下會使用成熟的收費平臺。在本篇中,我們使用的是 Yahoo Finance,一個免費的公開金融資料平臺,它不僅包含了大部分公開股票的實時資料,還提供了 Python API,可以通過股票程式碼和時間區間直接查詢歷史價格。
import yfinance as yf
df_sp = yf.download('^GSPC',start="2012-11-01",end="2022-11-01")
Pandas資料集“df_sp”包含了從2012年11月1日至2022年10月31日十年以來的標普500指數資料,記錄了每個日期所對應的開市價格、最高價格、最低價格、閉市價格、閉市調整價格和交易量。
探索性資料分析
在收集完需要的資料後,首先可以檢視資料集中可能存在的空缺記錄和需要調整的欄位。我們使用以下函式來了解資料集中包含多少條記錄、是否有空缺值以及每個欄位所對應的資料型別。
df_sp.info()
可以看到,資料集中沒有需要處理的空缺值,並且每個欄位的型別都是統一的。除日期以外(Python Datetime型別格式),其他欄位都是數字型別。價格相關的都為浮點數,交易量相關的為整數。接下來,我們通過以下函式檢視資料的統計類資訊。
df_sp.describe()
可以看到,指數資料都是正數,大部分以千為單位。唯一在單位上與其他列有所區別的是交易量資料。交易量的絕對數值與價格相比相差巨大,為了更有可比性,我們需要調整交易量的絕對數值大小。在本步驟中,我們選擇先將交易量除以1000000,使其變成以百萬為單位,那麼在數值上,交易量數值大小縮小至千,與價格相似。
初步分析資料後,我們可以將價格資訊的時間序列視覺化,更加直觀地瞭解價格趨勢。
通過影象,我們發現開市、閉市、最高、最低和閉市調整的價格在趨勢上非常相似;交易量和價格之間也有一定的聯絡。我們需要對交易量和價格做進一步的分析來明確兩者之間的關係。這裡,我們使用相關性矩陣(correlation matrix)來研究變數之間的關係。
由此可以看出,交易量與價格之間沒有非常高的相關性,但這並不能證明交易量不應該在價格預測中被考慮。從另一個角度來說,各價格變數之間的相關性非常高,我們只需挑選其中一個(閉市調整價格)進行預測即可。在下一步中,我們只保留需要用到的資訊即可。
df = df_sp[['Adj Close','Volume_in_M']]
另一個值得注意的特徵是,相比於2020年之前總體上升的趨勢,指數價格在2020至2021年間呈下降趨勢。我們需要放大這段時間的資料影象來了解具體情況。
2020年3月,由於疫情的蔓延,全球多個國家都開始實行封控政策。從結果可以看出市場對封控政策的反響巨大,但與此同時多地政府也採取了各式各樣的經濟政策來穩定市場,人們的生產生活在疫情後開啟了新的模式。在這裡,我們假設疫情的一系列政策會在短時間內持續下去。根據公共資訊的評估,大家可以酌情調整模型和假設。在本專案中,我們的目標是使用自2020年2月15日起的歷史資料,預測未來1個工作日的調整後閉市指數價格。
至此,我們完成了從原資料集中提取後續建模所需資料的全部步驟。為了發揮提取資料的最大價值,我們需要對現有的兩個變數進行一些處理,從而儘可能完整地展現資料的特性。特別是對於時間序列資料而言,季節性和週期性都是非常重要的資訊,而這些資訊是可以從時間序列本身提取的。必要的資料處理雖然會增加計算量,但可以換來更精準更完善的模型結果。
資料預處理
由於指數資料是時間序列,相比於大多數迴歸預測,我們不僅要考慮模型需要什麼樣的變數,也需要考慮時間順序。在本篇中,迴歸預測的數學公式如下,對於需要預測的未來時間為t+1:
即運用n條歷史記錄來預測未來的價格。
為了提取有效資訊輔助預測,我們將資料預處理大致分為四步:
- 將時間轉換為變數
- 更改價格資料
- 尋找週期和季節性
- 根據週期調整交易量資料
1. 將時間轉換為變數
首先,我們需要將時間資訊從索引中提取出來。
df_mod_forecast = df_mod.reset_index()
df_mod_forecast.info()
從日期資訊中,提取年、月、日和工作日資訊,經過處理後的資料如下:
df_mod_forecast.head()
2. 更改價格資料
本篇的預測目標是未來價格,對於指數而言,金融從業者相對看重的是價格變動而非是其數值的大小。我們可以選擇預測價格,也可以選擇預測價格變動。我們將根據資料本身的特性來決定二者當中誰更適合建模。
首先,我們可以將每日和前一個工作日的價格差百分比計算出來。
df_mod_forecast['AdjPricePctDelta'] = df_mod_forecast['Adj Close'].pct_change()
3. 尋找週期和季節性 — 解構時間序列與自相關分析
指數價格資料是以日期為單位的,且只有在工作日才有資料。我們在嘗試週期時可以有多種選擇,例如以周、月或者季度為單位。在本篇中,時間序列分解效果最好的是月份。從2020年2月到2022年11月大約有20個月的時間。合適的週期會幫助我們更好的尋找資料中的季節性。
注意,在之前的時間序列影象中我們注意到,全球疫情蔓延之後,股票市場表現出高於平常的波動。我們需要使用 multiplicative 模型來解決這個問題。我們首先嚐試分解價格的時間序列。
通過以上影象可以看出,每100天大約有5個週期,每個週期大約有20天。除此之外,價格資料殘差(residual)部分表現並不佳,徘徊在1左右。在這個基礎上,我們再進行自迴歸分析,價格的ACF和PACF影象如下:
ACF和PACF顯示近30天的歷史價格都對當下的價格有影響,而太多的資訊會影響模型的效率,甚至帶來過擬合的風險。我們再來看看每日價格變動的解構結果。注意,因為價格差有負值,multiplicative 模型並不適用,我們需要使用 additive 模型來進行時間序列分解。
價格百分比差也表現出類似的季節性。殘差比之前表現略有進步,徘徊在0左右,但是可以看出2月份左右的資料波動性遠高於平常。價格變動的ACF和PACF如下:
價格差的ACF和PACF中,我們可以看到近兩天的歷史價格變動對當天價格的影響比較大。在本專案中,前兩天的歷史資訊將納入模型中考慮。根據本節的發現,我們可以在原先的基礎上進一步處理資料,加入季節資訊。
我們先計算出大致需要多少個週期,以0-20的正整數為一週期填充數列,就可以得到代表週期性的變數。
n_seasonal_cycles = math.ceil(df_mod_forecast.shape[0]/20)
df_mod_forecast['seasonality'] = (list(range(0,20))*n_seasonal_cycles[:df_mod_forecast.shape[0]]
4. 根據週期調整交易量資料
時間序列較為傳統的模型為 ARIMA 模型,這類模型只需要一條時間序列,根據資料週期性等特徵除錯引數。時間序列也可以使用機器學習進行建模,這樣的模型可以輸入更多的變數,但需要對資料進行調整。和 ARIMA 型別的模型不同,我們需要使用 t-1 的價格波動和t-2的價格波動作為變數的一部分,輸入模型進行訓練。
在這裡我們需要運用Pandas的shift方程,將價格變動資料分別下移一個和兩個時間間隔。Shift方程需要索引作為時間的依據,因此我們需要將日期設為索引。根據日期,將價格變動資料下移。
df_model['t_1_PricePctDelta'] = df_model['AdjPricePctDelta'].shift(periods=1)
df_model['t_2_PricePctDelta'] = df_model['AdjPricePctDelta'].shift(periods=2)
以此類推,我們將交易量資料分別下移一個和兩個時間間隔,兩者求差即可得到每個交易日昨天(t-1)與前天(t-2)之間的交易量差。
df_model['t-1volume'] = df_model['Volume_in_M'].shift(periods=1)
df_model['t-2volume'] = df_model['Volume_in_M'].shift(periods=2)
df_model['t_1_VolumeDelta'] = df_model['t-1volume'] - df_model['t-2volume']
通過交易日前一天的價格變動,我們可以將其轉換為漲跌符號來作為模型輸入的一部分。
df_model['sign_t_1'] = np.where(df_model['t_1_PricePctDelta']>0,1,0*df_model['t_1_PricePctDelta'])
至此,我們完成了標普500指數資料從下載到轉換的全過程。
在下篇中我們將著手完成模型訓練和預測步驟,根據模型來預測未來一個工作日的價格變化,並將預測結果與真實情況進行比對。
參考資料:
- Time series into supervised learning problem
- Tuning Ada Boost
- S&P 500 historical data
- Bloomberg News
- 阿布(2021)。《量化交易之路 用Python做股票量化分析》。機械工業出版社。
- 資料科學在文字分析中的應用 :中英文 NLP(上)
- 『堅如磐石的 PieCloudDB』:透明加密模組的設計與實現
- 後疫情時代,資料科學賦能旅遊行業服務質量提升
- OpenPie 和 ChatGPT 聊聊雲上資料計算的那些事兒
- 正式上市丨拓數派發布eMPP存算分離軟硬體一體機
- 『Postgres.Live 技術沙龍回顧』揭祕 PieCloudDB Database eMPP 架構設計
- PieCloudDB Database 雲上商業智慧的最佳實踐
- 資料科學在量化金融中的應用:指數預測(下)
- 資料科學在量化金融中的應用:指數預測(上)
- 【DTCC 2022】雲原生資料庫PieCloudDB全新eMPP架構是如何煉成的
- 資料科學,為企業創造更大的資料價值
- 擁抱開放|OpenPie引領PostgreSQL中國程式碼貢獻力