投資組合優化

用於優化的 CVAR 替代方案

  • April 7, 2020

是否有一些用於投資組合優化的 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

(披露:我是包的維護者NMOFneighbours。)

我們從一個小數據集開始,這樣我們就可以查看目標函式和約束矩陣:只有 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)

引用自:https://quant.stackexchange.com/questions/51724