R/utils.R

#' 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))
}
purplezippo/quantler documentation built on May 15, 2019, 4:27 p.m.