用於優化的 CVAR 替代方案
是否有一些用於投資組合優化的 CVaR 度量的替代方法,這些方法更易於實施。使用線性程序?它們可以只是 CVaR 的近似值或代表類似概念的度量。
編輯:我更喜歡一些精確的(分析的)方法而不是(元)啟發式,因為它們似乎對變化非常敏感(就我而言)。如果沒有,也歡迎使用其他使啟發式算法更可靠/更穩健的方法。
編輯2 :如果沒有真正的 CVaR 近似值,或者可以提供優化 CVaR 的更詳細的解釋(所有步驟),這也是主題,因為這將允許我們使用標準和分析方法(例如唱片)。
在對問題的評論和編輯之後,我將嘗試展示如何最小化投資組合的條件風險價值(又名預期尾部損失)。我們從 Rockafellar/Uryasev 建議的實現開始:
@ARTICLE{, author = {R. Tyrrell Rockafellar and Stanislav Uryasev}, title = {Optimization of Conditional Value-at-Risk}, journal = {Journal of Risk}, year = 2000, volume = 2, number = 3, pages = {21--41}, doi = {10.21314/JOR.2000.038}
}
他們將模型描述為線性程序 (LP),在他們的論文中的公式 17 之後給出。我們從一個場景集開始,即一個可能的回報實現樣本。請注意,在本文中,Rockafellar/Uryasev 處理的是損失,即負回報。因此,一個“壞”的分位數是一個高分位數,比如第 90 個。選擇的分位數稱為 $ \beta $ 在論文中。我們將場景儲存在矩陣中 $ R $ . 它有 $ n_{\mathrm{A}} $ 列(每個資產一個)和 $ n_{\mathrm{S}} $ 行(每個場景一行)。
模型中的變數是實際的投資組合權重 $ x $ , 加上輔助變數 $ u $ (每個場景一個),加上 VaR 水平 $ \alpha $ . 求解器可以同時選擇 VaR 水平和權重,以使 CVaR 最小化。目標函式只有輔助變數和VaR水平;實際的投資組合權重僅通過約束進入模型模型。
目標函式中的權重如下所示:
$$ \begin{array}{ccccccc} \underbrace{\begin{matrix}\phantom{000}1\phantom{000} \end{matrix}}{\alpha}& \underbrace{\begin{matrix}0 & \cdots & 0\phantom{0}\end{matrix}}{x} & \underbrace{\begin{matrix}\frac{1}{(1-\beta) n_{\mathrm{S}}} & \cdots & \frac{1}{(1-\beta) n_{\mathrm{S}}}\end{matrix}}_{u}\ \end{array} $$
也就是一個1的向量,後跟 $ n_{\mathrm{A}} $ 零,然後 $ n_{\mathrm{S}} $ 乘以常數。
約束矩陣如下所示:
$$ \begin{array}{ccccccccc} 0 & 1 & \cdots & 1 & 0 & 0 & \cdots & 0 & = &1 \ 1 & r_{1,1} & \cdots & r_{1, n_\mathrm{A}} & 1 & 0 & \cdots & 0 & \geq & 0 \ 1 & r_{2,1} & \cdots & r_{2, n_\mathrm{A}} & 0 & 1 & \cdots & 0 & \geq & 0 \ \vdots \ \underbrace{\phantom{00}1\phantom{00}}{\alpha} & \underbrace{\phantom{00}r{n_{\mathrm{S}},1}\phantom{00}}{x_1} & \cdots & \underbrace{r{n_\mathrm{S}, n_\mathrm{A}}}{x{n_\mathrm{A}}} & \underbrace{0}{u_1} & \underbrace{0}{u_2} & \cdots & \underbrace{1}{u{n_\mathrm{S}}} & \geq & 0 \ \end{array} $$
請注意,第一行是預算約束。除了那條線,矩陣由一列 1(對於 VaR 變數)組成,情景矩陣 $ R $ , 和一個維度的單位矩陣 $ n_\mathrm{S} $ . 對所有人都有非負約束 $ x $ 和 $ u $ ,但未明確顯示。許多求解器會自動執行它們。
我們可以嘗試在 R 中實現。我們載入範例所需的包。
library("Rglpk") library("NMOF") ## https://github.com/enricoschumann/NMOF library("neighbours") ## https://github.com/enricoschumann/neighbours
(披露:我是包的維護者
NMOF
和neighbours
。)我們從一個小數據集開始,這樣我們就可以查看目標函式和約束矩陣:只有 3 個資產和 10 個場景。
ns <- 10 ## number of scenarios na <- 3 ## number of assets R <- randomReturns(na, ns, sd = 0.01) ## an array of size ns x na b <- 0.8 ## beta in the original paper
目標函式賦予 0 權重
x
。f.obj <- c(alpha = 1, x = rep(0, na), u = 1/rep((1 - b)*ns, ns)) f.obj ## alpha x1 x2 x3 u1 u2 u3 ... u9 u10 ## 1.0 0.0 0.0 0.0 0.5 0.5 0.5 ... 0.5 0.5
約束矩陣為每個場景增加一列。
C <- cbind(1, R, diag(nrow(R))) C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C) C alpha x1 x2 x3 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 [1,] 0 1.000000 1.000000 1.00000 0 0 0 0 0 0 0 0 0 0 [2,] 1 0.000183 0.029174 0.00293 1 0 0 0 0 0 0 0 0 0 [3,] 1 -0.001776 -0.001673 0.00225 0 1 0 0 0 0 0 0 0 0 [4,] 1 -0.009948 -0.007892 0.01129 0 0 1 0 0 0 0 0 0 0 [5,] 1 0.008299 -0.005601 -0.00144 0 0 0 1 0 0 0 0 0 0 [6,] 1 0.005766 0.000521 -0.00940 0 0 0 0 1 0 0 0 0 0 [7,] 1 -0.017110 0.016782 -0.00122 0 0 0 0 0 1 0 0 0 0 [8,] 1 -0.008334 0.017317 0.00498 0 0 0 0 0 0 1 0 0 0 [9,] 1 -0.004077 -0.009600 0.01568 0 0 0 0 0 0 0 1 0 0 [10,] 1 -0.000532 -0.000201 0.00267 0 0 0 0 0 0 0 0 1 0 [11,] 1 -0.005090 0.002318 0.00368 0 0 0 0 0 0 0 0 0 1
讓我們在更大的模型上執行它。
ns <- 5000 na <- 20 R <- randomReturns(na, ns, sd = 0.01, rho = 0.5) b <- 0.75 f.obj <- c(alpha = 1, x = rep(0, na), u = 1/rep(( 1 - b)*ns, ns)) C <- cbind(1, R, diag(nrow(R))) C <- rbind(c(alpha = 0, x = rep(1, na), u = rep(0, nrow(R))), C) const.dir <- c("==", rep(">=", nrow(C) - 1)) const.rhs <- c(1, rep(0, nrow(C) - 1)) sol.lp <- Rglpk_solve_LP(f.obj, C, const.dir, rhs = const.rhs, control = list(verbose = TRUE, presolve = TRUE)) ## GLPK Simplex Optimizer, v4.65 ## 5001 rows, 5021 columns, 110020 non-zeros ## ... ## OPTIMAL LP SOLUTION FOUND
我們將得到的權重儲存在一個變數
lp.weights
中。lp.weights <- sol.lp$solution[2:(1+na)]
現在讓我們用啟發式求解相同的模型。我們可以使用一種最簡單的方法,即(隨機)局部搜尋。而且我也會讓這裡的實現變得簡單(可以改進它以提高速度)。
本地搜尋很簡單:我們從投資組合開始,例如所有資產權重相同的投資組合。然後我們評估這個投資組合——看看它有多好。所以我們寫了一個目標函式,給定數據,將投資組合映射到一個數字——CVaR。
CVaR <- function(w, R, b) { Rw <- R %*% w ## compute portfolio loss under scenarios mean(Rw[Rw >= quantile(Rw, b)]) }
現在我們執行啟發式算法:它將遍歷可能的投資組合空間並接受比目前更好的投資組合,但拒絕那些更差的投資組合。對於這個循環,我們需要第二個成分(第一個是目標函式):鄰域函式。鄰域函式將投資組合作為輸入,並返回該投資組合的略微修改的副本。
nb <- neighbourfun(0, 1, type = "numeric", stepsize = 0.05)
舉個例子,假設我們的投資組合僅包含三種資產,每種資產的權重為三分之一。然後我們可以如下計算鄰居:
nb(rep(1/3, 3)) ## 0.3122 0.3544 0.3333 nb(rep(1/3, 3)) ## [1] 0.3272 0.3333 0.3394
現在我們有了目標函式和鄰域函式,我們可以執行本地搜尋。
sol.ls <- LSopt(CVaR, list(x0 = rep(1/na, na), neighbour = nb, nI = 1000), R = R, b = b)
我們可以比較目標函式值。請注意,目標函式定義並不完全等價,因為 LP 可能會同時選擇 VaR 和權重,而 Local Search 僅改變權重,然後在目標函式中強加一種計算 VaR 的方法。
CVaR(sol.ls$xbest, R, b) CVaR(lp.weights, R, b) ## [1] 0.00946 ## [1] 0.00955
所以這兩種實現都給出了非常相似的結果。
至於不穩定性:我們可以多次執行啟發式(我們總是應該;參見http://enricoschumann.net/R/remarks.htm)。讓我們執行 20 次。
sols.ls <- restartOpt(LSopt, n = 20, OF = CVaR, list(x0 = rep(1/na, na), neighbour = nb, nI = 1000), R = R, b = b) summary(sapply(sols.ls, `[[`, "OFvalue")) ## Min. 1st Qu. Median Mean 3rd Qu. Max. ## 0.00946 0.00946 0.00946 0.00946 0.00946 0.00946
所以我們在所有執行中得到非常相似的結果。
啟發式的一個好處是我們可以很容易地改變它。假設您不再需要 CVaR;但現在我們更喜歡最小化一個部分時刻,比如說。然後我們要做的就是編寫另一個目標函式。
PM <- function(w, R, exp = 2, ...) { Rw <- R %*% w ## compute portfolio loss under scenarios pm(Rw, xp = exp, lower = FALSE) ## we work with losses } sol.ls <- LSopt(PM, list(x0 = rep(1/na, na), neighbour = nb, nI = 1000), R = R, exp = 2)