時間是阻止所有事情同時發生的力量。——雷•卡明斯
本節使用的是以CSV文件形式在本地存儲的金融數據集形式爲本地存儲的CSV文件。從技術上講,CSV文件是包含數據行結構的文本文件,其特徵是以逗號分隔單個值。在導入數據之前,導入一些軟件包並進行定製:
In [1]: import numpy as np import pandas as pd from pylab import mpl, plt plt.style.use('seaborn') mpl.rcParams['font.family'] = 'serif' %matplotlib inline
8.1.1 數據導入
pandas提供不同的函數和DataFrame方法,以導入不同存儲格式(CSV、SQL、Excel等)的數據,並將數據導出爲不同格式(詳見第9章)。下面的代碼通過pd.read_csv()函數導入CSV[1]文件中的時間序列數據:
In [2]: filename = '../../source/tr_eikon_eod_data.csv' ❶ In [3]: f = open(filename, 'r') ❷ f.readlines()[:5] ❷ Out[3]: ['Date,AAPL.O,MSFT.O,INTC.O,AMZN.O,GS.N,SPY,.SPX,.VIX,EUR=,XAU=,GDX, ,GLD\n', '2010-01-01,,,,,,,,,1.4323,1096.35,,\n', '2010-01-04,30.57282657,30.95,20.88,133.9,173.08,113.33,1132.99,20.04, ,1.4411,1120.0,47.71,109.8\n', '2010-01-05,30.625683660000004,30.96,20.87,134.69,176.14,113.63,1136.52, ,19.35,1.4368,1118.65,48.17,109.7\n', '2010-01-06,30.138541290000003,30.77,20.8,132.25,174.26,113.71,1137.14, ,19.16,1.4412,1138.5,49.34,111.51\n'] In [4]: data = pd.read_csv(filename, ❸ index_col=0, ❹ parse_dates=True) ❺ In [5]: data.info() ❻ <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2216 entries, 2010-01-01 to 2018-06-29 Data columns (total 12 columns): AAPL.O 2138 non-null float64 MSFT.O 2138 non-null float64 INTC.O 2138 non-null float64 AMZN.O 2138 non-null float64 GS.N 2138 non-null float64 SPY 2138 non-null float64 .SPX 2138 non-null float64 .VIX 2138 non-null float64 EUR= 2216 non-null float64 XAU= 2211 non-null float64 GDX 2138 non-null float64 GLD 2138 non-null float64 dtypes: float64(12) memory usage: 225.1 KB
❶ 指定路徑和文件名。
❷ 顯示原始數據(Linux/Mac)的前5行。
❸ 傳遞給pd.read_scv()函數的文件名。
❹ 指定第一列作爲索引處理。
❺ 指定索引值爲datetime類型。
❻ 結果爲DataFrame對象。
在這一階段,金融分析師可能首先觀察數據,對其進行檢查或者可視化(見圖8-1):
In [6]: data.head() ❶ Out[6]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX \ Date 2010-01-01 NaN NaN NaN NaN NaN NaN NaN NaN 2010-01-04 30.572827 30.950 20.88 133.90 173.08 113.33 1132.99 20.04 2010-01-05 30.625684 30.960 20.87 134.69 176.14 113.63 1136.52 19.35 2010-01-06 30.138541 30.770 20.80 132.25 174.26 113.71 1137.14 19.16 2010-01-07 30.082827 30.452 20.60 130.00 177.67 114.19 1141.69 19.06 EUR= XAU= GDX GLD Date 2010-01-01 1.4323 1096.35 NaN NaN 2010-01-04 1.4411 1120.00 47.71 109.80 2010-01-05 1.4368 1118.65 48.17 109.70 2010-01-06 1.4412 1138.50 49.34 111.51 2010-01-07 1.4318 1131.90 49.10 110.82 In [7]: data.tail() ❷ Out[7]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX.VIX \ Date 2018-06-25 182.17 98.39 50.71 1663.15 221.54 271.00 2717.07 17.33 2018-06-26 184.43 99.08 49.67 1691.09 221.58 271.60 2723.06 15.92 2018-06-27 184.16 97.54 48.76 1660.51 220.18 269.35 2699.63 17.91 2018-06-28 185.50 98.63 49.25 1701.45 223.42 270.89 2716.31 16.85 2018-06-29 185.11 98.61 49.71 1699.80 220.57 271.28 2718.37 16.09 EUR= XAU= GDX GLD Date 2018-06-25 1.1702 1265.00 22.01 119.89 2018-06-26 1.1645 1258.64 21.95 119.26 2018-06-27 1.1552 1251.62 21.81 118.58 2018-06-28 1.1567 1247.88 21.93 118.22 2018-06-29 1.1683 1252.25 22.31 118.65 In [8]: data.plot(figsize=(10, 12), subplots=True); ❸
圖8-1 以線圖表示的金融時間序列數據
❶ 前5行。
❷最後5行顯示。
❸ 這個語句通過多個子圖來可視化整個數據集。
這裏使用的數據來自Thomson Reuters (TR) Eikon Data API。TR金融工具代碼稱作路透金融工具代碼(RIC)。RIC表示的金融工具爲:
In [9]: instruments = ['Apple Stock', 'Microsoft Stock', 'Intel Stock', 'Amazon Stock', 'Goldman Sachs Stock', 'SPDR S&P 500 ETF Trust', 'S&P 500 Index', 'VIX Volatility Index', 'EUR/USD Exchange Rate', 'Gold Price', 'VanEck Vectors Gold Miners ETF', 'SPDR Gold Trust'] In [10]: for ric, name in zip(data.columns, instruments): print('{:8s} | {}'.format(ric, name)) AAPL.O | Apple Stock MSFT.O | Microsoft Stock INTC.O | Intel Stock AMZN.O | Amazon Stock GS.N | Goldman Sachs Stock SPY | SPDR S&P 500 ETF Trust .SPX | S&P 500 Index .VIX | VIX Volatility Index EUR= | EUR/USD Exchange Rate XAU= | Gold Price GDX | VanEck Vectors Gold Miners ETF GLD | SPDR Gold Trust
8.1.2 彙總統計
金融分析師採取的下一個步驟是觀察不同的數據集彙總統計,從而對其有個總體的“感覺”:
In [11]: data.info() ❶ <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 2216 entries, 2010-01-01 to 2018-06-29 Data columns (total 12 columns): AAPL.O 2138 non-null float64 MSFT.O 2138 non-null float64 INTC.O 2138 non-null float64 AMZN.O 2138 non-null float64 GS.N 2138 non-null float64 SPY 2138 non-null float64 .SPX 2138 non-null float64 .VIX 2138 non-null float64 EUR= 2216 non-null float64 XAU= 2211 non-null float64 GDX 2138 non-null float64 GLD 2138 non-null float64 dtypes: float64(12) memory usage: 225.1 KB In [12]: data.describe().round(2) ❷ Out[12]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX \ Count 2138.00 2138.00 2138.00 2138.00 2138.00 2138.00 2138.00 2138.00 mean 93.46 44.56 29.36 480.46 170.22 180.32 1802.71 17.03 std 40.55 19.53 8.17 372.31 42.48 48.19 483.34 5.88 min 27.44 23.01 17.66 108.61 87.70 102.20 1022.58 9.14 25% 60.29 28.57 22.51 213.60 146.61 133.99 1338.57 13.07 50% 90.55 39.66 27.33 322.06 164.43 186.32 1863.08 15.58 75% 117.24 54.37 34.71 698.85 192.13 210.99 2108.94 19.07 max 193.98 102.49 57.08 1750.08 273.38 286.58 2872.87 48.00 EUR= XAU= GDX GLD count 2216.00 2211.00 2138.00 2138.00 mean 1.25 1349.01 33.57 130.09 std 0.11 188.75 15.17 18.78 min 1.04 1051.36 12.47 100.50 25% 1.13 1221.53 22.14 117.40 50% 1.27 1292.61 25.62 124.00 75% 1.35 1428.24 48.34 139.00 max 1.48 1898.99 66.63 184.59
❶ info()給出DataFrame對象的相關元信息。
❷ describe()提供每列的實用標準統計量。
敏銳的洞察力
pandas提供了許多方法如info和describe(),可以獲得新導入的金融時間序列數據集的簡單概況。它們還能快捷地檢查導入程序是否按照要求進行(例如,DataFrame對象是否真正包含DatetimeIndex類型的索引)。
當然,pandas也提供自定義統計類型及顯示方式的選項:
In [13]: data.mean() ❶ Out[13]: AAPL.O 93.455973 MSFT.O 44.561115 INTC.O 29.364192 AMZN.O 480.461251 GS.N 170.216221 SPY 180.323029 .SPX 1802.713106 .VIX 17.027133 EUR= 1.248587 XAU= 1349.014130 GDX 33.566525 GLD 130.086590 dtype: float64 In [14]: data.aggregate([min, ❷ np.mean, ❸ np.std, ❹ np.median, ❺ max] ❻ ).round(2) Out[14]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX.VIX EUR= \ min 27.44 23.01 17.66 108.61 87.70 102.20 1022.58 9.14 1.04 mean 93.46 44.56 29.36 480.46 170.22 180.32 1802.71 17.03 1.25 std 40.55 19.53 8.17 372.31 42.48 48.19 483.34 5.88 0.11 median 90.55 39.66 27.33 322.06 164.43 186.32 1863.08 15.58 1.27 max 193.98 102.49 57.08 1750.08 273.38 286.58 2872.87 48.00 1.48 XAU= GDX GLD min 1051.36 12.47 100.50 mean 1349.01 33.57 130.09 std 188.75 15.17 18.78 median 1292.61 25.62 124.00 max 1898.99 66.63 184.59
❶ 每列均值。
❷ 每列最小值。
❸ 每列均值。
❹ 每列標準差。
❺ 每列中位數。
❻ 每列最大值。
使用aggregate方法還可以傳遞自定義函數。
8.1.3 隨時間推移的變化
統計分析方法往往基於隨時間推移的變化,而不是絕對值。計算時間序列中的隨時變化有多種選擇,包括絕對偏差、變化率和對數回報率。
首先介紹絕對偏差,pandas爲此提供了一個特殊的方法:
In [15]: data.diff().head() ❶ Out[15]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX EUR= \ Date 2010-01-01 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2010-01-04 NaN NaN NaN NaN NaN NaN NaN NaN 0.0088 2010-01-05 0.052857 0.010 -0.01 0.79 3.06 0.30 3.53 -0.69 -0.0043 2010-01-06 -0.487142 -0.190 -0.07 -2.44 -1.88 0.08 0.62 -0.19 0.0044 2010-01-07 -0.055714 -0.318 -0.20 -2.25 3.41 0.48 4.55 -0.10 -0.0094 XAU= GDX GLD Date 2010-01-01 NaN NaN NaN 2010-01-04 23.65 NaN NaN 2010-01-05 -1.35 0.46 -0.10 2010-01-06 19.85 1.17 1.81 2010-01-07 -6.60 -0.24 -0.69 In [16]: data.diff().mean() ❷ Out[16]: AAPL.O 0.064737 MSFT.O 0.031246 INTC.O 0.013540 AMZN.O 0.706608 GS.N 0.028224 SPY 0.072103 .SPX 0.732659 .VIX -0.019583 EUR= -0.000119 XAU= 0.041887 GDX -0.015071 GLD -0.003455 dtype: float64
❶ diff提供兩個索引值之間的絕對變化。
❷ 當然,還可以應用聚合運算。
從統計學角度講,絕對變化不是最優的,因爲它們與時間序列數據本身的比例相關。因此,我們通常更偏重變化率。下面的代碼可以計算金融環境裏的變動率或者回報率(也稱爲簡單回報率),並對其每列的平均值進行可視化(參見圖8-2):
In [17]: data.pct_change().round(3).head() ❶ Out[17]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX EUR= \ Date 2010-01-01 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2010-01-04 NaN NaN NaN NaN NaN NaN NaN NaN 0.006 2010-01-05 0.002 0.000 -0.000 0.006 0.018 0.003 0.003 -0.034 -0.003 2010-01-06 -0.016 -0.006 -0.003 -0.018 -0.011 0.001 0.001 -0.010 0.003 2010-01-07 -0.002 -0.010 -0.010 -0.017 0.020 0.004 0.004 -0.005 -0.007 XAU= GDX GLD Date 2010-01-01 NaN NaN NaN 2010-01-04 0.022 NaN NaN 2010-01-05 -0.001 0.010 -0.001 2010-01-06 0.018 0.024 0.016 2010-01-07 -0.006 -0.005 -0.006 In [18]: data.pct_change().mean().plot(kind='bar', figsize=(10, 6)); ❷
❶ pct_change()計算兩個索引值之間的變化率。
❷ 將結果的均值可視化爲一個柱狀圖。
圖8-2 變化率均值柱狀圖
對數回報率可作爲回報率的替代品。在某些情況下,它們更容易處理,因此在金融環境中往往優先使用對數回報率。[2]
圖8-3展示了單個金融時間序列的累計對數回報率。這種類型的圖表導致了某種形式的規範化:
圖8-3 一段時間的累計對數回報率
In [19]: rets = np.log(data / data.shift(1))❶ In [20]: rets.head().round(3) ❷ Out[20]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX EUR= \ Date 2010-01-01 NaN NaN NaN NaN NaN NaN NaN NaN NaN 2010-01-04 NaN NaN NaN NaN NaN NaN NaN NaN 0.006 2010-01-05 0.002 0.000 -0.000 0.006 0.018 0.003 0.003 -0.035 -0.003 2010-01-06 -0.016 -0.006 -0.003 -0.018 -0.011 0.001 0.001 -0.010 0.003 2010-01-07 -0.002 -0.010 -0.010 -0.017 0.019 0.004 0.004 -0.005 -0.007 XAU= GDX GLD Date 2010-01-01 NaN NaN NaN 2010-01-04 0.021 NaN NaN 2010-01-05 -0.001 0.010 -0.001 2010-01-06 0.018 0.024 0.016 2010-01-07 -0.006 -0.005 -0.006 In [21]: rets.cumsum().apply(np.exp).plot(figsize=(10, 6)); ❸
❶ 以向量的方式計算對數回報率。
❷ 結果的一個子集。
❸ 繪製一段時間的累計對數回報率圖表;首先調用cumsum()方法,然後對結果應用np.exp()。
8.1.4 重新採樣
重新採樣是金融時間序列數據的重要操作之一,通常採用向下採樣的形式,例如,分筆交易數據序列重新採樣的時間間隔爲一分鐘,也可以將每日觀察數據的時間序列重新採樣爲每週或者每月觀察數據(如圖8-4所示):
In [22]: data.resample('1w', label='right').last().head() ❶ Out[22]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX .VIX \ Date 2010-01-03 NaN NaN NaN NaN NaN NaN NaN NaN 2010-01-10 30.282827 30.66 20.83 133.52 174.31 114.57 1144.98 18.13 2010-01-17 29.418542 30.86 20.80 127.14 165.21 113.64 1136.03 17.91 2010-01-24 28.249972 28.96 19.91 121.43 154.12 109.21 1091.76 27.31 2010-01-31 27.437544 28.18 19.40 125.41 148.72 107.39 1073.87 24.62 EUR= XAU= GDX GLD Date 2010-01-03 1.4323 1096.35 NaN NaN 2010-01-10 1.4412 1136.10 49.84 111.37 2010-01-17 1.4382 1129.90 47.42 110.86 2010-01-24 1.4137 1092.60 43.79 107.17 2010-01-31 1.3862 1081.05 40.72 105.96 In [23]: data.resample('1m', label='right').last().head() ❷ Out[23]: AAPL.O MSFT.O INTC.O AMZN.O GS.N SPY .SPX \ Date 2010-01-31 27.437544 28.1800 19.40 125.41 148.72 107.3900 1073.87 2010-02-28 29.231399 28.6700 20.53 118.40 156.35 110.7400 1104.49 2010-03-31 33.571395 29.2875 22.29 135.77 170.63 117.0000 1169.43 2010-04-30 37.298534 30.5350 22.84 137.10 145.20 118.8125 1186.69 2010-05-31 36.697106 25.8000 21.42 125.46 144.26 109.3690 1089.41 .VIX EUR= XAU= GDX GLD Date 2010-01-31 24.62 1.3862 1081.05 40.72 105.960 2010-02-28 19.50 1.3625 1116.10 43.89 109.430 2010-03-31 17.59 1.3510 1112.80 44.41 108.950 2010-04-30 22.05 1.3295 1178.25 50.51 115.360 2010-05-31 32.07 1.2305 1215.71 49.86 118.881 In [24]: rets.cumsum().apply(np.exp). resample('1m', label='right').last( ).plot(figsize=(10, 6)); ❸
❶ 日終數據以一週爲時間間隔重新採樣。
❷ 以一月爲時間間隔重新採樣。
❸ 這就繪製了隨時間變化的累計對數回報率圖表:首先調用cumsun(),然後對結果應用np.exp,最後進行重新採樣。
圖8-4 重採樣的累計對數回報率圖表(每月)
避免預見偏差
在很多情況下,pandas在重新採樣時默認使用區間的左側標籤(或者索引值)。爲了在金融業務中保持一致,請確保使用右標籤(索引值)——一般是區間內最後一個可用數據點。否則,金融分析中可能潛藏着預見偏差。
本文摘自《Python金融大數據分析 第2版》
- 金融科技算法交易量化金融教程書籍
- 詳細講解使用Python分析處理金融大數據的專業圖書
- 將人工智能應用於金融開發的實戰指南,金融應用開發領域從業人員的常備讀物
《Python金融大數據分析 第2版》分爲5部分,共21章。第1部分介紹了Python在金融學中的應用,其內容涵蓋了Python用於金融行業的原因、Python的基礎架構和工具,以及Python在計量金融學中的一些具體入門實例;第2部分介紹了Python的基礎知識以及Python中非常有名的庫NumPy和pandas工具集,還介紹了面向對象編程;第3部分介紹金融數據科學的相關基本技術和方法,包括數據可視化、輸入/輸出操作和數學中與金融相關的知識等;第4部分介紹Python在算法交易上的應用,重點介紹常見算法,包括機器學習、深度神經網絡等人工智能相關算法;第5部分講解基於蒙特卡洛模擬開發期權及衍生品定價的應用,其內容涵蓋了估值框架的介紹、金融模型的模擬、衍生品的估值、投資組合的估值等知識。
《Python金融大數據分析 第2版》本書適合對使用Python進行大數據分析、處理感興趣的金融行業開發人員閱讀。