引言
以下是使用 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")

代碼說明
數(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)類別等)。