Saya mencoba menggunakan fungsi optim() dalam R untuk meminimalkan nilai dengan operasi matriks. Dalam hal ini, saya mencoba meminimalkan volatilitas sekelompok saham yang masing-masing mengembalikan kovaria. Fungsi objektif yang diminimalkan adalah calculate_portfolio_variance.

library(quantmod)

filter_and_sort_symbols <- function(symbols)
{
  # Name: filter_and_sort_symbols
  # Purpose: Convert to uppercase if not
  # and remove any non valid symbols
  # Input: symbols = vector of stock tickers
  # Output: filtered_symbols = filtered symbols
  
  # convert symbols to uppercase
  symbols <- toupper(symbols)
  
  # Validate the symbol names
  valid <- regexpr("^[A-Z]{2,4}$", symbols)
  
  # Return only the valid ones
  return(sort(symbols[valid == 1]))
}

# Create the list of stock tickers and check that they are valid symbols
tickers <- filter_and_sort_symbols(c("AAPL", "NVDA", "MLM", "AA"))
benchmark <- "SPY"
# Set the start and end dates
start_date <- "2007-01-01"
end_date <- "2019-01-01"

# Gather the stock data using quantmod library
getSymbols(Symbols=tickers, from=start_date, to=end_date, auto.assign = TRUE)
getSymbols(benchmark, from=start_date, to=end_date, auto.assign = TRUE)

# Create a matrix of only the adj. prices
price_matrix <- NULL
for(ticker in tickers){price_matrix <- cbind(price_matrix, get(ticker)[,6])}
# Set the column names for the price matrix
colnames(price_matrix) <- tickers
benchmark_price_matrix <- NULL
benchmark_price_matrix <- cbind(benchmark_price_matrix, get(benchmark)[,6])

# Compute log returns
returns_matrix <- NULL
for(ticker in tickers){returns_matrix <- cbind(returns_matrix, annualReturn(get(ticker)))}
returns_covar <- cov(returns_matrix)
colnames(returns_covar) <- tickers
rownames(returns_covar) <- tickers
# get average returns for tickers and benchmark
ticker_avg <- NULL
for(ticker in tickers){ticker_avg <- cbind(ticker_avg, colMeans(annualReturn(get(ticker))))}
colnames(ticker_avg) <- tickers
benchmark_avg <- colMeans(annualReturn(get(benchmark)))

# create the objective function
calculate_portfolio_variance <- function(allocations, returns_covar, ticker_avg, benchmark_avg)
{
  # Name: calculate_portfolio_variance
  # Purpose: Computes expected portfolio variance, to be used as the minimization objective function
  # Input: allocations = vector of allocations to be adjusted for optimality; returns_covar = covariance matrix of stock returns
  #        ticker_avg = vector of average returns for all tickers, benchmark_avg = benchmark avg. return
  # Output: Expected portfolio variance
  
  # get benchmark volatility 
  benchmark_variance <- (sd(annualReturn(get(benchmark))))^2
  # scale allocations for 100% investment
  allocations <- as.matrix(allocations/sum(allocations))
  # get the naive allocations
  naive_allocations <- rep(c(1/ncol(ticker_avg)), times=ncol(ticker_avg))
  portfolio_return <-  sum(t(allocations)*ticker_avg)
  portfolio_variance <- t(allocations)%*%returns_covar%*%allocations
  
  # constraints = portfolio expected return must be greater than benchmark avg. return and
  #               portfolio variance must be less than benchmark variance (i.e. a better reward at less risk)
  if(portfolio_return < benchmark_avg | portfolio_variance > benchmark_variance)
  {
    allocations <- naive_allocations
  }
  
  portfolio_variance <- t(allocations)%*%returns_covar%*%allocations
  return(portfolio_variance)
}


# Specify lower and upper bounds for the allocation percentages
lower <- rep(0, ncol(returns_matrix))
upper <- rep(1, ncol(returns_matrix))

# Initialize the allocations by evenly distributing among all tickers
set.seed(1234)
allocations <- rep(1/length(tickers), times=length(tickers))

Ketika saya memanggil fungsi objektif secara manual, ia mengembalikan nilai seperti yang diharapkan:

> calculate_portfolio_variance(allocations, returns_covar, ticker_avg, benchmark_avg)
          [,1]
[1,] 0.1713439

Namun, ketika saya menggunakan fungsi optim() itu mengembalikan kesalahan:

> optim_result <- optim(par=allocations, fn=calculate_portfolio_variance(allocations, ticker_avg, benchmark_avg), lower=lower, upper=upper, method="L-BFGS-B")
Error in t(allocations) %*% returns_covar : non-conformable arguments

Saya tidak yakin alasannya, tetapi mungkin dengan cara optim() secara rekursif menggunakan variabel allocations. Apa yang bisa saya lakukan untuk memperbaikinya?

EDIT: FWIW, strategi optimasi lainnya bekerja (evolusi diferensial, menyimulasikan anil) tetapi saya lebih suka menggunakan keturunan gradien karena itu jauh lebih cepat

0
coolhand 4 April 2021, 05:11

1 menjawab

Jawaban Terbaik

Tidak ada kesalahan jika argumen pertama berganti nama menjadi par dan Anda beralih urutan di mana Anda menerapkan t () ke vektor parameter yang digunakan dalam operasi matriks-multiply yang mengapit:

cpv <- function(par, returns_covar=returns_covar, ticker_avg, benchmark_avg)
{
    # Name: calculate_portfolio_variance
    # Purpose: Computes expected portfolio variance, to be used as the minimization objective function
    # Input: allocations = vector of allocations to be adjusted for optimality; returns_covar = covariance matrix of stock returns
    #        ticker_avg = vector of average returns for all tickers, benchmark_avg = benchmark avg. return
    # Output: Expected portfolio variance
    
    # get benchmark volatility 
    benchmark_variance <- (sd(annualReturn(get(benchmark))))^2
    # scale allocations for 100% investment
    par <- as.matrix(par/sum(par))
    # get the naive allocations
    naive_allocations <- rep(c(1/ncol(ticker_avg)), times=ncol(ticker_avg))
    portfolio_return <-  sum(t(par)*ticker_avg);print(par)
    portfolio_variance <- t(par)%*%returns_covar%*%par
    
    # constraints = portfolio expected return must be greater than benchmark avg. return and
    #               portfolio variance must be less than benchmark variance (i.e. a better reward at less risk)
    if(portfolio_return < benchmark_avg | portfolio_variance > benchmark_variance)
    {
        par <- naive_allocations
    }
    
    portfolio_variance <- t(par)%*%returns_covar%*%par
    return(portfolio_variance)
}

Saya meninggalkan pencetakan debugging par dalam kode dan menunjukkan bagian atas hasil menjalankannya

optim_result <- optim(par=allocations, fn=cpv, lower=lower, upper=upper, returns_covar=returns_covar, ticker_avg=ticker_avg, benchmark_avg=benchmark_avg, method="L-BFGS-B")
     [,1]
[1,] 0.25
[2,] 0.25
[3,] 0.25
[4,] 0.25
          [,1]
[1,] 0.2507493
[2,] 0.2497502
[3,] 0.2497502
[4,] 0.2497502
          [,1]
[1,] 0.2492492
[2,] 0.2502503
[3,] 0.2502503
[4,] 0.2502503
#--- snipped output of six more iterations.

... dan hasilnya:

> optim_result 
$par
[1] 0.25 0.25 0.25 0.25

$value
[1] 0.1713439

$counts
function gradient 
       1        1 

$convergence
[1] 0

$message
[1] "CONVERGENCE: NORM OF PROJECTED GRADIENT <= PGTOL"

Seperti yang saya katakan dalam komentar kepada pertanyaan yang tidak terkait, fungsi Optim pertama kali mencoba untuk menaikkan kemudian menurunkan elemen pertama dalam par, kemudian mencoba melakukan hal yang sama untuk elemen kedua, ketiga dan keempat. Pada titik itu tidak menemukan peningkatan "memutuskan" itu konvergen pada minimum lokal dan menyatakan konvergensi.

Saya harus menunjukkan bahwa kode untuk optim adalah agak tua dan penulis algoritma asli, dr nash, telah menempatkan versi yang diperbarui pada cran dalam bentuk optimx Paket. Dia mengatakan optim baik pada zamannya, tetapi dia pikir prosedur lain harus dicoba jika itu tidak berhasil.

1
IRTFM 5 April 2021, 20:17