如何用R語言實現(xiàn)包含股票和債券的再平衡策略

引言

以下是使用 R 語言實現(xiàn)一個簡單的“股票-債券”再平衡策略的代碼示例。這里的策略基于閾值再平衡邏輯,即投資組合中的股票占比偏離目標(biāo)比例超過設(shè)定閾值時觸發(fā)再平衡調(diào)整。

策略邏輯

目標(biāo)配置

設(shè)定股票(如滬深300指數(shù)ETF)和債券(如國債ETF)的目標(biāo)比例,例如,股票60%,債券40%。

偏離閾值

當(dāng)股票實際占比偏離目標(biāo)比例±10%時,觸發(fā)再平衡(賣出超配資產(chǎn),買入低配資產(chǎn))。

再平衡規(guī)則

  • 若股票占比 > 目標(biāo)比例 + 閾值:賣出多余股票,買入債券;
  • 若股票占比 < 目標(biāo)比例 - 閾值:賣出部分債券,買入股票。

再平衡策略的R代碼實現(xiàn)

安裝并加載依賴包

# 安裝相關(guān)R包
# install.packages(c("quantmod", "xts", "TTR"))

# 加載包
library(quantmod)   # 數(shù)據(jù)獲取與處理
library(xts)        # 時間序列數(shù)據(jù)
library(TTR)        # 簡單指標(biāo)計算

數(shù)據(jù)準備(模擬或獲取真實數(shù)據(jù))

# 生成模擬的股票(滬深300)和債券(國債ETF)價格數(shù)據(jù)(400個交易日)
set.seed(123)
n_days <- 200
stock_returns <- cumsum(rnorm(n_days, mean = 0.001, sd = 0.02))
bond_returns <- cumsum(rnorm(n_days, mean = 0.0005, sd = 0.01))
stock_prices <- 100 * exp(stock_returns)
bond_prices <- 100 * exp(bond_returns)
dates <- as.Date("2024-01-01") + 0:(n_days-1)
prices <- as.xts(cbind(Stock = stock_prices, Bond = bond_prices), order.by = dates)

定義投資組合類和再平衡函數(shù)

Portfolio <- setRefClass(
  "Portfolio",
  fields = list(
    target_stock = "numeric",
    threshold = "numeric",
    initial_cash = "numeric",
    positions = "list",
    history = "list"
  ),
  
  methods = list(
    initialize = function(target_stock = 0.6, threshold = 0.1, initial_cash = 100000) {
      target_stock <<- target_stock
      threshold <<- threshold
      initial_cash <<- initial_cash
      positions <<- list(stock = 0, bond = 0, cash = initial_cash)
      history <<- list()
      invisible(.self)  # 使用 .self 代替 self
    },

    calculate_allocation = function(price) {
      stock_value <- positions$stock * price[, "Stock"]
      bond_value <- positions$bond * price[, "Bond"]
      total_value <- stock_value + bond_value + positions$cash
      list(
        stock_value = as.numeric(stock_value),
        bond_value = as.numeric(bond_value),
        cash = positions$cash,
        total_value = as.numeric(total_value),
        stock_ratio = as.numeric(stock_value / total_value),
        bond_ratio = as.numeric(bond_value / total_value)
      )
    },

    rebalance = function(price) {
      allocation <- calculate_allocation(price)
      stock_ratio <- allocation$stock_ratio

      if (abs(stock_ratio - target_stock) > threshold) {
        target_stock_value <- allocation$total_value * target_stock
        target_bond_value <- allocation$total_value * (1 - target_stock)
        adjust_stock <- target_stock_value - allocation$stock_value
        adjust_bond <- target_bond_value - allocation$bond_value

        # 使用 <<- 修改類字段
        positions$stock <<- positions$stock + adjust_stock / price[, "Stock"]
        positions$bond <<- positions$bond + adjust_bond / price[, "Bond"]
        positions$cash <<- positions$cash - adjust_stock - adjust_bond

        # 記錄歷史
        history <<- c(
          history,
          list(list(
            date = index(price),
            action = ifelse(adjust_stock > 0, "買入股票", "賣出股票"),
            stock_ratio = stock_ratio,
            target_ratio = target_stock,
            total_value = allocation$total_value
          ))
        )
      }
      invisible(allocation)
    },

   simulate = function(prices) {
  for (i in 1:nrow(prices)) {
    price_row <- prices[i, ]
    allocation <- calculate_allocation(price_row)
    rebalance(price_row)
    
    # 判斷是否觸發(fā)再平衡
    rebalanced_value <- if (length(history) > 0) {
      any(grepl("action", names(history[[length(history)]])))
    } else {
      FALSE
    }
    
    # 記錄每日狀態(tài)
    history <<- c(
      history,
      list(list(
        date = index(price_row),
        total_value = allocation$total_value,
        stock_ratio = allocation$stock_ratio,
        cash = allocation$cash,
        action = if (rebalanced_value) history[[length(history)]]$action else NA,
        rebalanced = rebalanced_value
      ))
    )
  }
  invisible(.self)
}
  )
)

初始化投資組合并運行回測

portfolio <- Portfolio$new(target_stock = 0.6, threshold = 0.1, initial_cash = 100000)

## 初始建倉
initial_price <- prices[1, ]
initial_stock_value <- portfolio$initial_cash * portfolio$target_stock
initial_bond_value <- portfolio$initial_cash * (1 - portfolio$target_stock)
portfolio$positions$stock <- initial_stock_value / as.numeric(initial_price[, "Stock"])
portfolio$positions$bond <- initial_bond_value / as.numeric(initial_price[, "Bond"])
portfolio$positions$cash <- 0  # 初始現(xiàn)金用盡

## 運行模擬
portfolio$simulate(prices)

## 查看歷史記錄
head(portfolio$history)

策略回測結(jié)果并可視化

# 整理歷史數(shù)據(jù)
library(dplyr)

# 合并歷史數(shù)據(jù)并計算凈值
history_df <- bind_rows(portfolio$history) %>%
  mutate(
    date = as.Date(date, format = "%Y-%m-%d"),  # 轉(zhuǎn)換日期格式
    net_value = total_value / first(total_value)  # 基準化為初始凈值
)
# 加載必要包
library(xts)
library(zoo)  # 用于時間序列處理

# 創(chuàng)建可復(fù)現(xiàn)示例數(shù)據(jù)(如果實際數(shù)據(jù)不存在)
# history_df <- structure(list(...)) 

# 設(shè)置圖形參數(shù)
# par(family = 'STHeiti')  # 中文字體支持(Windows用'SimHei',macOS用'STHeiti')
par(mar = c(5, 4, 4, 4) + 0.1)  # 調(diào)整圖形邊距

# 創(chuàng)建基礎(chǔ)圖形
plot(
  x = history_df$date, 
  y = history_df$net_value,
  type = "l", 
  lwd = 2,
  col = "steelblue",
  xlab = "Date",
  ylab = "net value",
  main = "rebanlance of stocks and bonds",
  xaxt = "n"  # 禁用默認x軸
)

# 自定義日期坐標(biāo)軸
axis.Date(
  side = 1,
  at = seq(min(history_df$date), max(history_df$date), by = "1 month"),
  format = "%Y-%m",
)

# 添加再平衡事件標(biāo)記
points(
  x = history_df$date[history_df$rebalanced],
  y = history_df$net_value[history_df$rebalanced],
  pch = 24,  # 三角形標(biāo)記
  col = "firebrick",
  bg = "gold",
  cex = 0.5
)

# 添加輔助網(wǎng)格線
grid(
  nx = NA, 
  ny = NULL,  # 僅橫向網(wǎng)格線
  col = "lightgray", 
  lty = "dotted"
)

# 添加圖例
legend(
  "topleft",
  legend = c("net value curve", "rebanlance"),
  col = c("steelblue", "firebrick"),
  lty = c(1, NA),
  pch = c(NA, 24),
  pt.bg = c(NA, "gold"),
  bty = "n"
)

# 可選:添加次坐標(biāo)軸(例如顯示原始總價值)
par(new = TRUE)
plot(
  x = history_df$date, 
  y = history_df$total_value,
  type = "l", 
  lty = 2,
  col = "darkgreen",
  axes = FALSE, 
  xlab = "", 
  ylab = ""
)
axis(4, col.axis = "darkgreen")
mtext("Total value", side = 4, line = 3, col = "darkgreen")
Rplot.png

代碼說明

數(shù)據(jù)準備:

  • 使用 rnorm 生成模擬的股票和債券價格(可替換為真實數(shù)據(jù),如用 getSymbols("000300.SH", from = "2010-01-01") 獲取滬深300指數(shù))。

  • 數(shù)據(jù)格式為 xts 時間序列,方便按日期處理。

投資組合類:

  • Portfolio 類包含目標(biāo)配置、閾值、初始現(xiàn)金、持倉和歷史記錄等字段。

  • calculate_allocation 計算當(dāng)前資產(chǎn)比例, rebalance 執(zhí)行再平衡邏輯, simulate 按日模擬交易。

再平衡邏輯:

  • 每次計算股票實際占比,若偏離目標(biāo)超過閾值(如±10%),則調(diào)整至目標(biāo)比例(通過賣出/買入資產(chǎn))。

  • 記錄每次再平衡的時間、操作和組合價值。

回測與可視化:

  • 歸一化凈值曲線展示策略表現(xiàn),紅色點標(biāo)記再平衡觸發(fā)點,直觀顯示調(diào)整對組合的影響。

擴展建議

加入交易成本

在 rebalance 函數(shù)中添加傭金、滑點等成本(如 cost <- abs(adjust_stock + adjust_bond) * 0.001,假設(shè)千分之一傭金)。

處理最小交易單位:

避免買入零碎股(如 floor(adjust_stock / price[1]) 取整)。

定期再平衡

增加按固定頻率(如每年12月31日)觸發(fā)再平衡的邏輯,而非僅依賴閾值。

多資產(chǎn)支持

擴展至股票、債券、黃金等多資產(chǎn)類別,通過循環(huán)遍歷資產(chǎn)實現(xiàn)通用再平衡。

通過以上代碼,可直觀理解再平衡策略在R中的實現(xiàn)邏輯,并根據(jù)需求調(diào)整參數(shù)(目標(biāo)比例、閾值、資產(chǎn)類別等)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容