標籤:

MATLAB 高級數據結構連載 3:金融時間序列Financial Time Series (Part C) 跟蹤股票賬戶權益的變動

應用案例:使用 FINTS 跟蹤股票賬戶權益的變動

背景

15年初,小明聽從專家建議,決定長線投資 500 手中國人壽(股票代碼:601628)這支股票,持有期限為16 年的 3 月 3 日到 4 月 6 日。這段時間裡,小明常會做一些買入、賣出的短線操作,希望能夠賺一些額外的錢。

現在一年期滿,小明結束了投資(賣出所持有的所有股票)。小明想知道,這一年中,他的短線操作是否合理?

假設小明的賬戶有且僅持有一支股票,並且始終沒有出入金(出入金指:資金的轉入、轉出);於是,比較 3 月 3 日 至 4 月 6 日 的以下兩個數據:

  1. 小明的賬戶動態權益(賬面上的資產,通常=賬戶餘額+所持有股票的市值)
  2. 中國人壽的走勢

就可以對小明的「短線操作是否合理」有個比較直觀的概念。舉個簡單的例子,如果中國人壽的走勢是這樣的:

而小明的賬戶動態權益是這樣子的:

這就說明小明可能一直在「高買低賣」,增加了他持倉的成本;反過來,如果小明的賬戶動態權益是這樣子的,那就說明小明的確是短線高手:

問題

現在的問題是,怎樣回溯小明從開始建倉到清倉出場這一段時間的賬戶權益呢?

比較容易得到的素材是:這一年小明的交易記錄(小明可以向經紀商索取這一年的結算單),如下表所示(表格各欄位內容將在後文介紹):

(表一,小明的成交記錄,虛構)

和這一年中國人壽的 1 分鐘數據(可以從 windn或 tb 下載),如下表所示:

(表二,中國人壽的股票價格,1分鐘,收盤價)

(以上兩個表格均只顯示部分數據)

接下來,將分析如何利用以上兩個表格數據,來回溯賬戶動態權益。

分析

首先看一下「動態權益」怎麼算。動態權益的定義是:

動態權益 = 靜態權益(賬戶餘額) + 持倉市值

回顧表一:

(表一,小明的成交記錄,虛構)

記表第一列時刻序列為 n nt_1, t_2, ..., T,第二列為nDelta Pos(t),它表示小明的調倉操作n n,例如, Delta Pos(t_1) = 500,表示在 t_1 時刻小明買入 500 手, Delta Pos(t_2) = -200,表示在  t_2 時刻小明賣出 200 手。

Pos(t) 代表nn n t時刻小明所持有的倉位,顯然,它滿足如下遞推式:

Pos(t_{n+1}) = Pos(t_n) + Delta Pos(t_n)

記表第三列成交價格為 n nP(t),令 t nn n時刻小明的持倉成本為MV(t),則 MV(t) 可如此計算:

MV(t_{n+1})=left{nbegin{aligned}nMV(t_n),quad& Delta Pos(t_{n+1}) le 0nfrac {MV(t_n)*Pos(t_n)+P(t_{n+1}) times Delta Pos(t_{n+1})}{Pos(t_n)+Delta Pos(t_{n+1})} ,quad & Delta Pos(t_{n+1})>0nend{aligned}nright.

對上式的解釋:賣出股票時,持倉均價不變;買入股票時,新的持倉均價是原持倉均價和新買入均價的、按倉位的加權平均值。需要注意的是,這個結算方式和一般的股票軟體不同:一般的股票軟體傾向於將做 T 的成本反映在持倉均價上,即,調倉的損益將反映在持倉均價上,幫助用戶了解手頭倉位的當下盈虧。而本文,調倉的損益用「已實現損益」單獨列出。結算方式孰優孰劣,取決於個人習慣。

再記上表第四列手續費為 Fee(t) , 則n nt 時刻小明調倉造成的已實現損益 RPL(t) 為:

RPL(t_{n+1}) =left{nbegin{aligned}nleft[ P(t_{n+1})-MV(t_{n+1}) right] times Delta Pos(t_{n+1}) - Fee(t_{n+1}),quad& Delta Pos(t_{n+1}) le 0n0 ,quad & Delta Pos(t_{n+1})>0nend{aligned}nright.

對上式的解釋:「已實現損益」僅在平倉時出現。

於是,n nt 時刻小明的靜態權益 StaticEquity(t) 是:

StaticEquity(t_{n+1}) = StaticEquity(t_n)+RPL(t_{n+1})

另一方面,令t時刻中國人壽的價格為 S(t),則 n n t 時刻小明所持有倉位的市值(又名浮動損益) FPL可以按如下公式計算:

FPL(t_{n+1}) = left[S(t_{n+1})-MV(t_{n+1})right]times Pos(t_{n+1})

綜上,tn n 時刻小明賬戶的動態權益 DynamicEquity(t)就能計算了: nn

DynamicEquity(t_{n+1}) = StaticEquity(t_{n+1}) + FPL(t_{n+1})

如何將上述公式翻譯成代碼?注意到這些公式都以時刻 t 為索引,一個自然的做法是,把前文兩個表格的數據按時刻對齊,而後按時刻遍歷它們。我們已經知道,使用 FINTS 數據結構,可以方便地按時刻來對齊、補齊數據,並且所有數據一目了然;此外,FINTS 還擁有諸多針對金融數據的操作方法,因此,這裡我們用 FINTS 數據結構來解決這個動態權益跟蹤的問題。

MATLAB 解決方案

現在打開 MATLAB,開始做這個追蹤小明賬戶動態權益的工作。

step1. 導入數據

首先,需要用 FINTS 這一數據結構存儲前述兩個 excel 表格數據(實盤1分鐘數據、交易記錄)。用代碼做數據導入的方式參考這裡,本文介紹如何用 app 做這個數據導入的工作(具體參見這裡)。

首先導入成交記錄表格——ZGRS_TranR.xlsx(ZGRS 是 中國人壽拼音縮寫)。點擊 MATLAB 最上方工具欄的 APPS 選項卡,選擇右面那個小三角,展開眾多 app:

在裡面找到 computatinalnfinance 欄目下的 finacial time series 這個 app:

點擊它後,會跳出 FINTS 數據導入嚮導。選擇左側上方的 load,在跳出的對話框中找到小明的中國人壽成交記錄這一表格:

選擇打開該 excel 文件,將會跳出如下圖所示的選擇數據的對話框。如果對自己的數據所處的行列位置很清楚,可以直接在 Range 中填寫數據位置;否則的話,可以點擊 Select 按鈕,MATLAB將打開這個將要被 load 的 excel 文件,在該文件上,可直接用滑鼠框選數據範圍。一共有三個數據範圍要選擇,從上至下分別為:日期和時刻、數據、數據的表頭:

選擇完區域以後,點擊 OKn,就完成了數據導入。在 app 的左側可以看到這個新導入的數據,MATLAB 默認將之命名為 excel 表格文件名(ZGRS_TranR);在 app 的右側可以看到這個 FINTS 的數據內容,如圖所示:

同理,再導入收盤價表格:ZGRS_CLOSE,得到 ZGRS_CLOSEn這個 FINTS 對象。

step 2 合併數據

把這兩個 FINTS 合併起來,「中國人壽的股票價格1分鐘數據」和「小明的成交記錄數據」這兩者才可以按時刻對齊。合併可以用 merge 命令,這裡介紹在 app 上合併的方法。在 app 的左側下方點擊 Merge 選項卡,合併的方式選擇為 Union(這樣就不會丟失數據);然後,在 app 的左側上方同時選中兩個要合併的 FINTS,點擊 Merge 選項卡下的 create 按鈕,如圖所示:

於是,這兩個FINTS(ZGRS_TranR 和 ZGRS_CLOSE)就被合併成一個新的 FINTS了。在 workspace 里,可以找到這個新 create 出來的 FINTS 對象(在本例中,是ZGRS_CLOSE_1),將之改成合適的名字(右鍵 rename),如圖所示:

這裡,我將它重命名為 ZGRS。

至此,把成交記錄和收盤價這兩組數據導入到 FINTS 對象,並按時刻對齊的工作, 就完成了。將該 ZGRS 保存在硬碟里,留待後文備用,例如,本例將 ZGRS 這個 FINTS 對象保存為 ZGRS.mat:

>> save ZGRS ZGRS n

step3. 計算動態權益

把前文的一系列公式寫成 MATLAB 的代碼,即可計算動態權益。

首先從硬碟讀取 ZGRS 這個 FINTS 對象:

load ZGRS % 導入前文處理好的 FINTS 對象n

下載到的股票分鐘數據(在本例中,是 ZGRS.CLOSE),常會存在數據缺失的現象,即丟失部分分鐘的數據。而 step1 中,為避免丟失數據,採用了按時間並集(『Union』) 生成新 FINTS 對象的 merge 方式。這樣一來,ZGRS 這個 FINTS 對象中,就會存有不少 NaN,我們需要對這些 NaN 作預處理。以下分情況討論:

1. 交易記錄相關的欄位(DPos,Price,Fee)存在的 NaN,代表在相應時刻沒有交易,它們可以被 0 替代:

myFTS = fillts( ZGRS,0 ); % 用 0 代替原 FINTS 對象的 NaNn

注意以上語句新生成的 myFTS ,它的 CLOSE 欄位(myFTS.CLOSE),是把 ZGRS.CLOSE 的 NaN 替換成 0 得到的,這不是我們想要的結果:股票價格的分鐘數據,怎麼可能是 0 呢?為此,我們可以用 fillts 的插值功能,補齊 ZGRS.CLOSE 的缺失數據,並用 fts2mat,將它賦值給 myFTS.CLOSE。這裡,我們簡單地用線性插值來做數據補齊:

myFTS.CLOSE = fts2mat( fillts(ZGRS.CLOSE,linear )); % 平滑原 FINTS 對象的 CLOSE 欄位數據。n

成交記錄的數量是手數,實際計算時應當把手數這算成股數,為此引入 dMulti,代表每手股票是 100 股:

dMulti = 100; % 每手一百股n

後文將按時刻遍歷 myFTS 中的數據。在本文 PartA 的評論里,@George Feng 提到了對 FINTS 引用慢的問題,我建議,用 fts2mat 把它們轉換成 matrix,可以有效提高速度(本例計算量小,提速不明顯):

% 使用 fts2mat把 FINTS 型數據轉化成 matrix 型的,加速運算nCLOSE = fts2mat( myFTS.CLOSE );nDPos = fts2mat( myFTS.DPos );nFee = fts2mat( myFTS.Fee );nPrice = fts2mat( myFTS.Price );n

接下去,就是按時刻遍歷計算的工作了。

Pos,MV,RPL,FPL,StaticEquity,DynamicEquity(相關說明見前文公式)等這些需要在遍歷中計算的變數,應當先在內存中佔位:

nLength = length(myFTS) ;% 記錄 FINTS 的長度n% 初始佔位。變數說明見前文的公式nPos = zeros(nLength,1); % 所持有的倉位,nMV = zeros(nLength,1); % 持倉均價nRPL = zeros(nLength,1); % 已實現損益nFPL = zeros(nLength,1); % 浮動損益nStaticEquity = zeros(nLength,1); % 靜態權益nDynamicEquity = zeros(nLength,1); % 動態權益n

處理 iter = 1 時以上變數的取值:

% 處理第一筆數據。nMV(1) = Price(1);nPos(1) = DPos(1);nFPL(1) = ( CLOSE(1) - MV(1) ) * Pos(1) * dMulti;nStaticEquity(1) = 1.25e6; % 小明賬戶的初始資金nDynamicEquity(1) = StaticEquity(1) + FPL(1); % RPL(1) = 0;n

把前文的遞推公式寫成 MATLAB 的代碼(這裡為了簡單易懂,把公式直接翻譯成了MATLAB代碼,沒有做任何的優化):n

% 遍歷所有時刻,計算動態權益nfor iter = 2:nLengthn % 1. holded positionsn Pos(iter) = Pos(iter-1) + DPos(iter);n % 2. RPL StaticEquity and MVn if DPos(iter) % positions changedn if DPos(iter)>0 % enter positionn % MVn MV(iter) = ( MV(iter-1)*Pos(iter-1) + Price(iter)*DPos(iter) )...n / Pos(iter);n % RPL = 0 n StaticEquity(iter) = StaticEquity(iter-1);n else % close positionn RPL(iter) = ( Price(iter) - MV(iter-1) )...n * (- DPos(iter) )... n * dMulti;n if Pos(iter) == 0n MV(iter) = 0;n elsen MV(iter) = MV(iter-1);n endn endn else % position not changed n MV(iter) = MV(iter-1);n % RPL = 0 n endn % 3. FPLn FPL(iter) = ( CLOSE(iter) - MV(iter) ) ...n * Pos(iter) ...n * dMulti;n % 4. Static, DynamicEquityn StaticEquity(iter) = StaticEquity(iter-1) + RPL(iter) + Fee(iter);n DynamicEquity(iter) = StaticEquity(iter) + FPL(iter) ;nendn

如此一來,整個計算過程就完成了, 現在可以對比小明賬戶動態權益和中國人壽的走勢了。由於兩者不是一個數量級的——中國人壽股價在 20 多元附近波動,而小明的賬戶權益是百萬元級別的,故而把它們畫在同一張圖上,會導致數量級小的中國人壽股價走勢看起來香一條直線。把它們都摺合成相對初始值的百分比再畫圖,就可以避免這個問題:

plot( ( CLOSE - CLOSE(1) )/CLOSE(1) * 100); nhold onnplot( (DynamicEquity - DynamicEquity(1) )/DynamicEquity(1) * 100) ; nhold offnlegend(CLOSE,DynamicEquity)n

註:兩個圖都沒有設置橫坐標,這是因為如果用時刻做橫坐標的話,相鄰兩個收盤-開盤的時刻段對應的數據就會是一條直線。

運行以上代碼,得到下圖,圖中橙線是小明賬戶的動態權益,藍色是中國人壽的走勢:

可以看到,小明的動態權益曲線在中國人壽走勢曲線下方,這說明小明的短線操作總體上來說是不佳的。我們來分析下為什麼。用以下語句對比下小明的倉位和中國人壽的走勢:

>> plotyy( 1:1:length(Pos), Pos, 1:1:length(Pos), CLOSE ) n>> legend(Holded Position,CLOSE)n

得到下圖,圖中藍線是小明的倉位,橙色是中國人壽的走勢:

可以看到,小明經常在股價相對高的位置加倉、相對低的位置減倉,比如上圖中標記出的這兩對短線操作(紅色一對,紫色一對,圓圈圈出倉位,方框框出當時的市場價)。這種「高買低賣」的操作手法,是導致他賬戶權益走勢不如中國人壽股價走勢的原因。

至此,本案例結束。

擴展:

  • 算完的數據想要保存下來怎麼辦?可以保存為 FINTS 結構的數據
  • 事實上,FINTS 組織出的這種實盤數據+交易記錄,由於時間上已經全部對齊,因此,除了可以追溯賬戶的歷史權益變化(例如本例)外,還可以回溯其它諸如勝率、虧損收益比、潛在最大收益等評價策略「好壞」的指標。可以參考 matlabsky.com/thread-45
  • 本例的方法可以用來追溯期貨、期權賬戶的動態權益嗎?答案是可以的,但是由於期貨、期權除了買入開倉(多頭)以外,還有賣出開倉(空頭);相應的,也有買入平倉、賣出平倉。因此,單獨依賴Delta Pos來記錄交易動作是不夠的。可以引入買賣標記(IsBuy)、開平倉標記( IsOpen ),來輔助計算權益變化。(註:期權交易中,又稱多頭為權利倉,空頭為義務倉)
  • 如果不喜歡通過點滑鼠來導入數據怎麼辦? MATLAB中,凡是能用滑鼠解決的問題,必定是能用代碼解決的,找到相應代碼即可,比如前文的「合併兩個FINTS」這個動作,用代碼可以這樣做:

>> merge( FINTS1, FINTS2, DateSetMethod, Union )n

其它 FINTS 的操作方法,可以看這裡。

###########################################

如果對文中涉及的表格和代碼有興趣,可以給我發郵件:lhl_26@126.com

推薦閱讀:

The Trinity: MATLAB Mobile, MATLAB Online, and MATLAB Drive
MATLAB神經網路(一):BP神經網路
打開MATLAB時出現「Waring:could not read file classpath.txt」,怎麼辦?

TAG:MATLAB |