#' Benchmark return
#' @param stock.price Vector of a stock price series, time level could be 1day,
#' 1hour or 1min.
#' @param cash.begin Cash used at begining of a trade.
bm_return_v <- function(stock.price, cash.begin){
bm.return <- cash.begin * stock.price / stock.price[1]
bm.return
}
#' Return rate
#' @param stock.price Vector of a stock price series, time level could be 1day,
#' 1hour or 1min.
#' @param type Value of type muste be one of 'raw','day' and 'year'. When type
#' equals to 'raw' then calculate the total return rate, when type equals to
#' 'day' then calculate daily return rate, when type equals to year then
#' calculate annual return rate.
#' @param days Time span of day of the input stock data. If type is set to
#' 'day' or 'year', this value must given.
return_rate <- function(stock.price, type = 'raw', days = NA) {
# 收益率, 期末收益,日收益,年化收益
if (is.vector(stock.price) && is.numeric(stock.price)) {
rate.raw <- dplyr::last(stock.price)/stock.price[1] - 1
} else {
stop('none-numeric data input')
}
if (!(type %in% c('raw', 'day','year'))) {
stop('value of type must be one of c(\'raw\', \'day\', \'year\')')
}
if (is.integer(days) && days > 0) {
rate.d <- (1 + rate.raw)^(1/days) - 1
} else {
if (type != 'raw') stop('value of days must be none-zero integer')
}
if (type == 'raw') {
rate <- rate.raw
} else if (type == 'day') {
rate <- rate.d
} else if (type == 'year') {
rate <- (1 + rate.d)^(250) - 1
}
rate
}
#' Beta value
#' @param stock.price Vector of a stock price series, time level could be 1day,
#' 1hour or 1min.
#' @param cash.begin Cash used at begining of a trade.
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy correspond to stock.price.
beta_stock <- function(stock.price, cash.begin, trade.value){
cov(diff(stock.price)*(cash.begin/stock.price[1]), diff(trade.value))/
var(diff(stock.price)*(cash.begin/stock.price[1]))
}
#' Alpha value
#' @param stock.price Vector of a stock price series, time level could be 1day,
#' 1hour or 1min.
#' @param cash.begin Cash used at begining of a trade.
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy correspond to stock.price.
#' @param rate Risk free rate.
#' @param trade.days Time span of day.
alpha_stock <- function(stock.price, cash.begin, trade.value, rate, trade.days){
temp.beta <- beta_stock(stock.price, cash.begin, trade.value)
benchmark.annual.return <- return_rate(stock.price, type = 'year', days = trade.days)
strategy.annual.return <- return_rate(trade.value, type = 'year', days = trade.days)
temp.alpha <- strategy.annual.return - (rate + temp.beta*(benchmark.annual.return - rate))
temp.alpha
}
#' Algorithm Volatility
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy computed in daily.
algo_vol <- function(trade.value){
# 用来测量策略的风险性,波动越大代表策略风险越高。
alv <- sqrt(250*var(diff(trade.value)/trade.value[-length(trade.value)]))
alv
}
#' Compute charpe ratio of the input trade.value
#' @param trade.value vector of a numeric series which represents the total
#' value generated by a trade strategy.
#' @param rate risk free rate
#' @param trade.days time span of day
sharpe_rate <- function(trade.value, rate, trade.days){
strategy.annual.return <- return_rate(trade.value, type = 'year', days = trade.days)
alg.vol <- algo_vol(trade.value)
sharpe <- (strategy.annual.return - rate)/alg.vol
sharpe
}
#' Compute information ratio.
#' @param stock.price Vector of a stock price series, time level could be 1day,
#' 1hour or 1min.
#' @param cash.begin Cash used at begining of a trade.
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy.
#' @param trade.days Time span of day.
info_rate <- function(stock.price, cash.begin, trade.value, trade.days){
# 策略与基准每日收益差值的年化标准差
strategy.benchmark.annual.sd <- sd(diff(stock.price)*(cash.begin/stock.price[1]) -
diff(trade.value))
benchmark.annual.return <- return_rate(stock.price, type = 'year', days = trade.days)
strategy.annual.return <- return_rate(trade.value, type = 'year', days = trade.days)
info.ratio <- (strategy.annual.return - benchmark.annual.return)/strategy.benchmark.annual.sd
info.ratio
}
#' Compute Benchmark Volatility
#' @param stock.price a numeric series in the form of vector, which consists of
#' daily data of stock price
bench_vol <- function(stock.price){
sqrt(250*var(diff(stock.price)/stock.price[-length(stock.price)]))
}
#' Compute max draw down of a input stock price series
#'
#' \code{maxdrawdown} returns a list with two elements (value ,loc ), which represents
#' value of max draw down and its location.
#'
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy.
#'
#' @return a list with two elements of 'value' and 'loc'.
#'
#' @examples
#' data(sh600000)
#' maxdd <- maxdrawdown(sh600000$close/10000)
#'
#' @export
maxdrawdown <- function(trade.value){
if(length(which(diff(trade.value) == 0)) == 0){
trade.value.uniq <- trade.value
}else{
trade.value.uniq <- trade.value[-(which(diff(trade.value) == 0) + 1)]
}
loc.raw <- which(diff(trade.value) != 0)
loc.dw.1 <- which(diff(trade.value.uniq) < 0) # 下降序列点坐标,极大值居于左端点, 极小值居于右端点
loc.dw.1.notcont <- loc.dw.1[c(1, which(diff(loc.dw.1) > 1) + 1)]
# trade.value.uniq[loc.dw.1.notcont] # 极大值点
loc.dw.2 <- which(diff(trade.value.uniq) > 0) # 上升序列点坐标,极小值居于左端点
loc.dw.2.notcont <- loc.dw.2[c(1, which(diff(loc.dw.2) > 1) + 1)]
# trade.value.uniq[loc.dw.2.notcont] # 极小值点
max.down.loc<-outer(loc.dw.1.notcont, loc.dw.2.notcont, function(x,y){x - y}) # 极大值点坐标小于极小值点的矩阵
max.down.value<-outer(loc.dw.1.notcont, loc.dw.2.notcont, function(x,y){trade.value.uniq[x] - trade.value.uniq[y]}) # 对应的股价差
# 找到满足条件的最大值
id.xlessy <- which(max.down.loc < 0)
max.id.loc <- which.max(as.vector(max.down.value)[id.xlessy])
max.id <- id.xlessy[max.id.loc]
if((max.id %% length(loc.dw.1.notcont))!=0){
id.2 <- max.id %/% length(loc.dw.1.notcont) + 1 # 列的位置,极小值点的位置
id.1 <- max.id %% length(loc.dw.1.notcont) # 除以行的余数,极大值点的位置
}else{
id.1 <- length(loc.dw.1.notcont) # 标记能整除,除以行的余数,极大值点的位置
id.2 <- max.id %/% length(loc.dw.1.notcont)
}
max.drawdown <- (trade.value.uniq[loc.dw.1.notcont[id.1]] - trade.value.uniq[loc.dw.2.notcont[id.2]])/
trade.value.uniq[loc.dw.1.notcont[id.1]]
max.drawdown.loc <- c(loc.raw[loc.dw.1.notcont[id.1]], loc.raw[loc.dw.2.notcont[id.2]])
return(list(value = max.drawdown, loc = max.drawdown.loc))
}
#' Compute win rate
#' @param strategy.signal trade signal components of 0,-1,1 which stardends
#' 'hold','sell','buy'.
#' @param trade.value Vector of a numeric series which represents the total
#' value generated by a trade strategy.
#' @param type Must be one of 'buy' or 'sell'.
#' @param weight.s Stocks holded at begining in the short position.
#' @param stockp.begin the first one of the correspond stock price
win_rate <- function(strategy.signal, trade.value, type = 'buy', weight.s = 0, stockp.begin = 0){
if(type == 'buy'){
trade.loc.2 <- which(strategy.signal == -1)
trade.loc.1 <- which(strategy.signal == 1)
if(length(trade.loc.2) < length(trade.loc.1)) trade.loc.1 <- trade.loc.1[-length(trade.loc.1)]
pertrade.winrate <- (trade.value[trade.loc.2] - trade.value[trade.loc.1])/trade.value[trade.loc.1]
win.rate <- sum(pertrade.winrate > 0)/length(trade.loc.2)
trade.times <- length(trade.loc.2)
} else if (type == 'sell'){
# browser()
trade.loc.2 <- which(strategy.signal == 1)
trade.loc.1 <- which(strategy.signal == -1)
# 空头交易收益率
# 由于价值变动只在,本期买入和下期卖出时发生变化,因此依然用买入-卖出计算收益
# 如果第一个时间点就做空,那么为了计算收益率就需要在最开始补入对应的买入操作(借股)
if(trade.loc.1[1] == 1){
trade.value <- c(weight.s*stockp.begin, trade.value)
trade.loc.1 <- trade.loc.1 + 1
trade.loc.2 <- c(1, trade.loc.2 + 1)
if(length(trade.loc.2) > length(trade.loc.1)) {trade.loc.2 <- trade.loc.2[-length(trade.loc.2)]}
}
# 如果最后一个操作为卖空,意味着卖空信号比买入信号多一个,则将第一个策略定义为买入股票(对齐)
if(length(trade.loc.1) > length(trade.loc.2)){
if(trade.loc.1[1] < trade.loc.2[1] ){
trade.loc.2 <- c(1, trade.loc.2)
} else {
stop('sell signal error')
}
} else if (length(trade.loc.1) < length(trade.loc.2)){
# 如果卖空信号个数小于买入,并且,第一组买卖信号中卖出晚于买入,则买入信号多一个删除
if(trade.loc.1[1] > trade.loc.2[1]){
trade.loc.2 <- trade.loc.2[-length(trade.loc.2)]
} else {
stop('sell signal error')
}
} else {
# 如果买卖信号长度相等,并且先执行卖的操作,则将第一个时间点的信号设置为买入,并删除最后一个买入信号。
if (trade.loc.2[1] > trade.loc.1[1]) {
trade.loc.2 <- c(1, trade.loc.2[-length(trade.loc.2)])
}
}
pertrade.winrate <- (trade.value[trade.loc.1] - trade.value[trade.loc.2])/trade.value[trade.loc.1]
# pertrade.winrate <- (-trade.value[trade.loc.1[-length(trade.loc.1)]] + trade.value[trade.loc.2[-1]])/trade.value[trade.loc.1[-length(trade.loc.1)]]
win.rate <- sum(pertrade.winrate > 0)/length(trade.loc.1)
trade.times <- length(trade.loc.1)
}
return(list('win.rate' = win.rate, 'trade.times' = trade.times, 'pertrade.winrate' = pertrade.winrate))
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.