/// <summary> /// To calculate Burke ratio we take the difference between the portfolio /// return and the risk free rate and we divide it by the square root of the /// sum of the square of the drawdowns. To calculate the modified Burke ratio /// we just multiply the Burke ratio by the square root of the number of datas. /// (一种调整收益率的计算方式,调整是通过drawdown的平方和进行的) /// </summary> func BurkeRatio(Ra *utils.SlidingWindow, Rf float64, scale float64) (float64, error) { var len = Ra.Count() var in_drawdown = false var peak = 1 var temp = 0.0 drawdown, err := utils.NewSlidingWindow(len) if err != nil { return math.NaN(), err } for i := 1; i < len; i++ { if Ra.Data()[i] < 0 { if !in_drawdown { peak = i - 1 in_drawdown = true } } else { if in_drawdown { temp = 1.0 for j := peak + 1; j < i; j++ { temp = temp * (1.0 + Ra.Data()[j]) } drawdown.Add(temp - 1.0) //Source in_drawdown = false } } } if in_drawdown { temp = 1.0 for j := peak + 1; j < len; j++ { temp = temp * (1.0 + Ra.Data()[j]) } drawdown.Add(temp - 1.0) //Source //drawdown.Add((temp - 1.0) * 100.0) in_drawdown = false } //var Rp = Annualized(Ra, scale, true) - 1.0--->Source Rp, err := Annualized(Ra, scale, true) if err != nil { return math.NaN(), err } var result float64 if drawdown.Count() != 0 { pow_Sliding, err := utils.Power(drawdown, 2) if err != nil { return math.NaN(), err } Rf = Rf * scale result = (Rp - Rf) / math.Sqrt(pow_Sliding.Sum()) } else { result = 0 } modified := true if modified { result = result * math.Sqrt(float64(len)) } return result, nil }
/// <summary> /// Kappa is a generalized downside risk-adjusted performance measure. /// To calculate it, we take the difference of the mean of the distribution /// to the target and we divide it by the l-root of the lth lower partial /// moment. To calculate the lth lower partial moment we take the subset of /// returns below the target and we sum the differences of the target to /// these returns. We then return return this sum divided by the length of /// the whole distribution. /// (非年化的超MAR平均收益率通过l阶根的低于MAR的收益率序列的l阶矩) /// </summary> func Kappa(Ra *utils.SlidingWindow, MAR float64, l float64) (float64, error) { undervalues, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] < MAR { undervalues.Add(Ra.Data()[i]) } } var n = float64(Ra.Count()) var m = float64(Ra.Average()) neg_Sliding, err := utils.Negative(undervalues) if err != nil { return math.NaN(), err } add_Sliding, err := utils.Add(MAR, neg_Sliding) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, float64(l)) if err != nil { return math.NaN(), err } var temp = pow_Sliding.Sum() / n return (m - MAR) / math.Pow(temp, (1.0/float64(l))), nil }
/// <summary> /// 峰度 /// </summary> // = "sample" func Kurtosis(Ra *utils.SlidingWindow) (float64, error) { if Ra == nil || Ra.Count() <= 3 { return math.NaN(), errors.New("In Kurtosis, Ra == nil || Ra.Count() <= 3") } n := float64(Ra.Count()) method := "sample_excess" switch method { case "sample_excess": //kurtosis = sum((x-mean(x))^4/var(x)^2)*n*(n+1)/((n-1)*(n-2)*(n-3)) - 3*(n-1)^2/((n-2)*(n-3)) var_data, err := Variance(Ra) if err != nil { return math.NaN(), err } add_Sliding, err := utils.Add(-Ra.Average(), Ra) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, 4.0) if err != nil { return math.NaN(), err } multi_Sliding, err := utils.Multi(1.0/math.Pow(var_data, 2.0), pow_Sliding) if err != nil { return math.NaN(), err } return multi_Sliding.Sum()*n*(n+1.0)/((n-1.0)*(n-2.0)*(n-3.0)) - 3*(n-1.0)*(n-1.0)/((n-2.0)*(n-3.0)), nil default: return math.NaN(), errors.New("In Kurtosis, method is default") } return math.NaN(), nil }
/// <summary> /// 偏度 /// </summary> // default = "moment" func Skewness(Ra *utils.SlidingWindow) (float64, error) { if Ra == nil || Ra.Count() <= 2 { return math.NaN(), errors.New("In Skewness, Ra == nil || Ra.Count() <= 2") } n := float64(Ra.Count()) method := "moment" switch method { //"moment", "fisher", "sample" case "moment": //skewness = sum((x-mean(x))^3/sqrt(var(x)*(n-1)/n)^3)/length(x) var_data, err := Variance(Ra) if err != nil { return math.NaN(), err } add_Sliding, err := utils.Add(-Ra.Average(), Ra) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, 3.0) if err != nil { return math.NaN(), err } multi_Sliding, err := utils.Multi(1.0/math.Pow(var_data*(n-1.0)/n, 1.5), pow_Sliding) if err != nil { return math.NaN(), err } return multi_Sliding.Sum() / n, nil default: return math.NaN(), errors.New("In Skewness, method is default") } return math.NaN(), nil }
/// <summary> /// downside risk (deviation, variance) of the return distribution /// Downside deviation, semideviation, and semivariance are measures of downside /// risk. /// </summary> // = "full" // = false //func DownsideDeviation(Ra *utils.SlidingWindow, MAR *utils.SlidingWindow, method string, potential bool) float64 { func DownsideDeviation(Ra *utils.SlidingWindow, MAR *utils.SlidingWindow) (float64, error) { if Ra == nil { return math.NaN(), errors.New("In DownsideDeviation, Ra == nil") } if Ra.Count() <= 0 { return math.NaN(), errors.New("In DownsideDeviation, Ra.Count() <= 0") } r, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } newMAR, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } len := 0.0 result := 0.0 for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] < MAR.Data()[i] { r.Add(Ra.Data()[i]) newMAR.Add(MAR.Data()[i]) } } potential := false method := "subset" if method == "full" { len = float64(Ra.Count()) } else if method == "subset" { len = float64(r.Count()) } else { return math.NaN(), errors.New("In DownsideDeviation, method default !!!") } if newMAR.Count() <= 0 || r.Count() <= 0 || len <= 0 { return math.NaN(), errors.New("In DownsideDeviation, newMAR.Count() <= 0 || r.Count() <= 0 || len <= 0") } if potential { sub_Sliding, err := utils.Sub(newMAR, r) if err != nil { return math.NaN(), err } result = sub_Sliding.Sum() / len } else { sub_Sliding, err := utils.Sub(newMAR, r) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(sub_Sliding, 2.0) if err != nil { return math.NaN(), err } result = math.Sqrt(pow_Sliding.Sum() / len) } return result, nil }
/// <summary> /// Upside Risk is the similar of semideviation taking the return above the /// Minimum Acceptable Return instead of using the mean return or zero. /// (一般来说,非对称类的比较,单求此统计量意义有限) /// </summary> func UpsideRisk(Ra *utils.SlidingWindow, MAR float64, stat string) (float64, error) { r, err := utils.AboveValue(Ra, MAR) if err != nil { return math.NaN(), err } var length float64 method := "subset" switch method { case "full": length = float64(Ra.Count()) break case "subset": length = float64(r.Count()) break default: return math.NaN(), errors.New("In Upside Risk, method is default !!!") } if length <= 0 { return 0, nil } var result float64 switch stat { case "risk": add_Sliding, err := utils.Add(-MAR, r) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, 2.0) if err != nil { return math.NaN(), err } multi_Sliding, err := utils.Multi(1.0/length, pow_Sliding) if err != nil { return math.NaN(), err } result = math.Sqrt(multi_Sliding.Sum()) break case "variance": add_Sliding, err := utils.Add(-MAR, r) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, 2.0) if err != nil { return math.NaN(), err } multi_Sliding, err := utils.Multi(1.0/length, pow_Sliding) if err != nil { return math.NaN(), err } result = multi_Sliding.Sum() break case "potential": add_Sliding, err := utils.Add(-MAR, r) if err != nil { return math.NaN(), err } multi_Slding, err := utils.Multi(1.0/length, add_Sliding) if err != nil { return math.NaN(), err } result = multi_Slding.Sum() break default: return math.NaN(), errors.New("In UpSide Risk, method is default !!!") } return result, nil }
/// <summary> /// Appraisal ratio is the Jensen's alpha adjusted for specific risk. The numerator /// is divided by specific risk instead of total risk. /// </summary> func AppraisalRatio(Ra *utils.SlidingWindow, Rb *utils.SlidingWindow, scale float64, Rf float64, method string) (float64, error) { var result = 0.0 switch method { case "appraisal": be_data, err := Beta2(Ra, Rb, Rf) if err != nil { return math.NaN(), err } multi_Sliding, err := utils.Multi(be_data, Rb) if err != nil { return math.NaN(), err } sub_Sliding, err := utils.Sub(Ra, multi_Sliding) if err != nil { return math.NaN(), err } al_data, err := Alpha2(Ra, Rb, Rf) if err != nil { return math.NaN(), err } epsilon, err := utils.Add(-al_data, sub_Sliding) if err != nil { return math.NaN(), err } add_Sliding, err := utils.Add(-epsilon.Average(), epsilon) if err != nil { return math.NaN(), err } pow_Sliding, err := utils.Power(add_Sliding, 2) if err != nil { return math.NaN(), err } specifikRisk := math.Sqrt(pow_Sliding.Sum()/float64(epsilon.Count())) * math.Sqrt(float64(scale)) jsa_data, err := JensenAlpha2(Ra, Rb, Rf, scale) if err != nil { return math.NaN(), err } result = jsa_data / specifikRisk break case "modified": jsa2_data, err := JensenAlpha2(Ra, Rb, Rf, scale) if err != nil { return math.NaN(), err } be2_data, err := Beta2(Ra, Rb, Rf) if err != nil { return math.NaN(), err } result = jsa2_data / be2_data break case "alternative": jsa2_data, err := JensenAlpha2(Ra, Rb, Rf, scale) if err != nil { return math.NaN(), err } sr_data, err := SystematicRisk(Ra, Rb, scale, Rf) if err != nil { return math.NaN(), err } result = jsa2_data / sr_data break default: return math.NaN(), errors.New("In AppraisalRatio, method is default !!!") } return result, nil }