//= "full" // = false func DownsideDeviation2(Ra *utils.SlidingWindow, MAR float64) (float64, error) { if Ra == nil || Ra.Count() <= 0 { return math.NaN(), errors.New("In DownsideDeviation2, Ra == nil || Ra.Count() <= 0") } newMAR, _ := utils.CreateList(MAR, Ra.Count()) return DownsideDeviation(Ra, newMAR) }
/// <summary> /// subset of returns that are /// more than the target (or Minimum Acceptable Returns (MAR)) returns and /// divide the length of this subset by the total number of returns. /// (超过MAR的频率) /// </summary> func UpsideFrequency(Ra *utils.SlidingWindow, MAR float64) (float64, error) { aboveMAR, err := utils.AboveValue(Ra, MAR) if err != nil { return math.NaN(), err } return float64(aboveMAR.Count()) / float64(Ra.Count()), nil }
/// <summary> /// Upside Potential Ratio,compared to Sortino, was a further improvement, extending the /// measurement of only upside on the numerator, and only downside of the /// denominator of the ratio equation. /// (分子只考虑超过MAR部分,分母只考虑DownsideDeviation的下跌风险) /// </summary> func UpsidePotentialRatio(Ra *utils.SlidingWindow, MAR float64) (float64, error) { //var r = Ra.Where<float64>(singleData => singleData > MAR).ToList<float64>(); r, err := utils.AboveValue(Ra, MAR) if err != nil { return math.NaN(), err } var length int method := "subset" switch method { case "full": length = Ra.Count() break case "subset": length = r.Count() break default: return math.NaN(), errors.New("In UpsidePotentialRatio, method is default !!!") } add_Sliding, err := utils.Add(-MAR, r) if err != nil { return math.NaN(), err } dd2Data, err := DownsideDeviation2(Ra, MAR) if err != nil { return math.NaN(), err } var result = (add_Sliding.Sum() / float64(length)) / dd2Data return result, nil }
/// <summary> /// calculate a traditional or modified Sharpe Ratio of Return over StdDev or /// VaR or ES /// /// The Sharpe ratio is simply the return per unit of risk (represented by /// variability). In the classic case, the unit of risk is the standard /// deviation of the returns. /// </summary> func SharpeRatio(Ra *utils.SlidingWindow, Rf_val float64, scale float64) (float64, error) { Rf, err := utils.CreateList(Rf_val, Ra.Count()) if err != nil { return math.NaN(), err } xR, err := Excess(Ra, Rf) if err != nil { return math.NaN(), err } numerator := 0.0 denominator := 0.0 annualize := 1 if annualize == 1 { denominator, err = StdDev_Annualized(Ra, scale) if err != nil { return math.NaN(), err } numerator, err = Annualized(xR, scale, true) if err != nil { return math.NaN(), err } } else { denominator, err = StdDev(Ra) if err != nil { return math.NaN(), err } numerator = xR.Average() } return numerator / denominator, nil }
/// <summary> /// Calculate the drawdown levels in a timeseries /// </summary> //= true func Drawdowns(Rb *utils.SlidingWindow) ([]float64, error) { Ra := Rb.Data() if Ra == nil || len(Ra) <= 0 { return nil, errors.New("In Drawdowns, Ra == nil") } geometric := 1 curReturn := 1.0 curMaxReturn := 1.0 + Ra[0] result := []float64{} if geometric == 1 { for _, r := range Ra { curReturn = curReturn * (1.0 + r) if curReturn > curMaxReturn { curMaxReturn = curReturn } result = append(result, curReturn/curMaxReturn-1.0) } } else { for _, r := range Ra { curReturn = curReturn + r if curReturn > curMaxReturn { curMaxReturn = curReturn } result = append(result, curReturn/curMaxReturn-1.0) } } return result, nil }
func Beta2(Ra, Rb *utils.SlidingWindow, Rf float64) (float64, error) { RfList, err := utils.CreateList(Rf, Ra.Count()) if err != nil { return math.NaN(), err } return Beta(Ra, Rb, RfList) }
/// <summary> /// 收益率序列的几何均值,非年化 /// </summary> func MeanGeometric(Ra *utils.SlidingWindow) (float64, error) { if Ra.Count() <= 0 { return math.NaN(), errors.New("In MeanGeometric, Ra.Count() <= 0") } add_Sliding, _ := utils.Add(1, Ra) log_Sliding, _ := utils.Log(add_Sliding) return math.Exp(log_Sliding.Average()) - 1.0, nil }
/// <param name="returns"></param> /// <returns></returns> func Centered(returns *utils.SlidingWindow) (*utils.SlidingWindow, error) { if returns == nil { return nil, errors.New("Centered Sliding window is nil") } if returns.Count() == 0 { return nil, errors.New("Centered Count is Zero !!!") } return utils.Add(-returns.Average(), returns) }
/// <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> /// Fama beta is a beta used to calculate the loss of diversification. It is made /// so that the systematic risk is equivalent to the total portfolio risk. /// </summary> func FamaBeta(Ra *utils.SlidingWindow, Rb *utils.SlidingWindow, Ra_sclae float64, Rb_scale float64) (float64, error) { var n1 = Ra.Count() var n2 = Rb.Count() var_Ra, err := Variance(Ra) if err != nil { return math.NaN(), err } var_Rb, err := Variance(Rb) if err != nil { return math.NaN(), err } var result = math.Sqrt(var_Ra*float64(n1-1)/float64(n1)) * math.Sqrt(float64(Ra_sclae)) / (math.Sqrt(var_Rb*float64(n2-1)/float64(n2)) * math.Sqrt(float64(Rb_scale))) return result, nil }
/// <summary> /// Prospect ratio is a ratio used to penalise loss since most people feel loss /// greater than gain /// (经验类型调整收益率,给损失赋予更大的权重) /// </summary> func ProspectRatio(Ra *utils.SlidingWindow, MAR float64) (float64, error) { var n = Ra.Count() SigD, err := DownsideDeviation2(Ra, MAR) if err != nil { return math.NaN(), err } positivevalues, negativevalues, err := utils.PosNegValues(Ra) if err != nil { return math.NaN(), err } var result = ((positivevalues.Sum()+2.25*negativevalues.Sum())/float64(n) - MAR) / SigD return result, nil }
/// <summary> /// downside frequency of the return distribution /// To calculate Downside Frequency, we take the subset of returns that are /// less than the target (or Minimum Acceptable Returns (MAR)) returns and /// divide the length of this subset by the total number of returns. /// </summary> func DownsideFrequency(Ra *utils.SlidingWindow, MAR *utils.SlidingWindow) (float64, error) { if Ra == nil { return math.NaN(), errors.New("In DownsideFrequency, Ra == nil") } if Ra.Count() <= 0 { return math.NaN(), errors.New("In DownsideFrequency, Ra.Count() <= 0") } len := 0.0 for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] < MAR.Data()[i] { len++ } } return len / float64(Ra.Count()), nil }
func Excess(returns, Rf *utils.SlidingWindow) (*utils.SlidingWindow, error) { result, err := utils.NewSlidingWindow(returns.Count()) if err != nil { return nil, err } for i := 0; i < returns.Count(); i++ { result.Add(returns.Data()[i] - Rf.Data()[i]) } return result, nil }
func PainRatio(Ra *utils.SlidingWindow, Rf float64, scale float64) (float64, error) { PI, err := PainIndex(Ra) if err != nil { return math.NaN(), err } n := Ra.Count() add_Sliding, err := utils.Add(1.0, Ra) if err != nil { return math.NaN(), err } prod_Sliding, err := utils.Prod(add_Sliding) if err != nil { return math.NaN(), err } Rp := math.Pow(prod_Sliding, float64(scale)/float64(n)) - 1.0 Rf = Rf * scale return (Rp - Rf) / PI, nil }
/// <param name="returns"></param> /// <param name="geometric"></param> /// <returns></returns> func Cumulative(returns *utils.SlidingWindow, geometric bool) (float64, error) { if returns == nil { return math.NaN(), errors.New("Cumulative Sliding window is Nil !!!") } if returns.Count() == 0 { return math.NaN(), errors.New("Cumulative Count == 0 !!") } if !geometric { return (returns.Sum()), nil } else { add_data, err := utils.Add(1.0, returns) if err != nil { return math.NaN(), err } prod_data, err := utils.Prod(add_data) if err != nil { return math.NaN(), err } return (prod_data - 1.0), 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> /// 峰度 /// </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 }
/// <param name="Ra"></param> /// <param name="Rb"></param> /// <returns></returns> func Relative(Ra, Rb *utils.SlidingWindow) (*utils.SlidingWindow, error) { res4Ra := 1.0 res4Rb := 1.0 result, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return nil, err } for i := 0; i < Ra.Count(); i++ { res4Ra = res4Ra * (1 + Ra.Data()[i]) res4Rb = res4Rb * (1 + Rb.Data()[i]) result.Add(res4Ra / res4Rb) } return result, nil }
/// <summary> /// To calculate Mean absolute deviation we take /// the sum of the absolute value of the difference between the returns and the mean of the returns /// and we divide it by the number of returns. /// (描述收益率偏离均值得一个指标) /// </summary> func MeanAbsoluteDeviation(Ra *utils.SlidingWindow) (float64, error) { if Ra.Count() <= 0 { return math.NaN(), errors.New("In MeanAbsoluteDeviation, Ra.Count() <= 0") } add_Sliding, _ := utils.Add(-Ra.Average(), Ra) ads_Sliding, _ := utils.Abs(add_Sliding) return ads_Sliding.Sum() / float64(Ra.Count()), nil }
/// <summary> /// M squared is a risk adjusted return useful to judge the size of relative /// performance between differents portfolios. With it you can compare portfolios /// with different levels of risk. /// (使得不同组合的收益率可比的调整措施) /// </summary> func MSquared(Ra *utils.SlidingWindow, Rb *utils.SlidingWindow, scale float64, Rf float64) (float64, error) { var n = Ra.Count() Rp, err := Annualized(Ra, scale, true) if err != nil { return math.NaN(), err } var_Ra_data, err := Variance(Ra) if err != nil { return math.NaN(), err } sigp := math.Sqrt(var_Ra_data*float64(n-1)/float64(n)) * math.Sqrt(float64(scale)) if err != nil { return math.NaN(), err } var_Rb_data, err := Variance(Rb) if err != nil { return math.NaN(), err } var sigm = math.Sqrt(var_Rb_data*float64(n-1)/float64(n)) * math.Sqrt(float64(scale)) //var result = (Rp-Rf)*sigp/sigm + Rf//Source Rf = Rf * scale var result = (Rp-Rf)*sigm/sigp + Rf return result, nil }
/// <param name="returns"></param> /// <param name="scale"></param> /// <param name="geometric"></param> /// <returns></returns> func Annualized(returns *utils.SlidingWindow, scale float64, geometric bool) (float64, error) { if returns == nil { return math.NaN(), errors.New("Returns Utils Sliding Window is nil") } if returns.Count() == 0 { return math.NaN(), errors.New("Returns Windows content is Zero") } n := returns.Count() if geometric { add_Sliding, err := utils.Add(1.0, returns) if err != nil { return math.NaN(), err } prod_Data, err := utils.Prod(add_Sliding) if err != nil { return math.NaN(), err } return math.Pow(prod_Data, float64(scale)/float64(n)) - 1.0, nil } else { return returns.Average() * float64(scale), nil } }
/// <summary> /// 方差 /// </summary> func Variance(Ra *utils.SlidingWindow) (float64, error) { if Ra == nil || Ra.Count() <= 1 { return math.NaN(), errors.New("In Variance, Ra == nil || Ra.Count() <= 1") } result := 0.0 mean := Ra.Average() for i := 0; i < Ra.Count(); i++ { result += (Ra.Data()[i] - mean) * (Ra.Data()[i] - mean) } return result / (float64)(Ra.Count()-1), nil }
/// <param name="prices"></param> /// <param name="method"></param> /// <returns></returns> func Calculate(prices *utils.SlidingWindow, method string) (*utils.SlidingWindow, error) { if prices == nil { return nil, errors.New("Prices Utils Sliding Window is nil") } if prices.Count() == 0 { return nil, errors.New("Returns Windows content is Zero") } lastPrice := prices.First() returns, err := utils.NewSlidingWindow(prices.Count()) if err != nil { return nil, errors.New("create a Sliding Window is Error !!") } switch method { case "simple": case "discrete": for i := 0; i < prices.Count(); i++ { price := prices.Data()[i] if lastPrice != 0.0 { returns.Add(price/lastPrice - 1.0) } else { returns.Add(0.0) } lastPrice = price } case "compound": case "log": for i := 0; i < prices.Count(); i++ { price := prices.Data()[i] if lastPrice != 0.0 { returns.Add(math.Log(price / lastPrice)) } else { returns.Add(0.0) } lastPrice = price } default: return nil, errors.New("The input Method is nil !!!") } return returns, 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> /// 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> /// 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> /// 只测试了默认参数 /// Calculate metrics on how the asset in R performed in up and down markets, /// measured by periods when the benchmark asset was up or down. /// Up (Down) Capture Ratio: this is a measure of an investment's compound /// return when the benchmark was up (down) divided by the benchmark's compound /// return when the benchmark was up (down). The greater (lower) the value, the /// better.(Up越大越好,Down越小越好) /// /// Up (Down) Number Ratio: similarly, this is a measure of the number of /// periods that the investment was up (down) when the benchmark was up (down), /// divided by the number of periods that the Benchmark was up (down).(Up越大越好,Down越小越好) /// /// Up (Down) Percentage Ratio: this is a measure of the number of periods that /// the investment outperformed the benchmark when the benchmark was up (down), /// divided by the number of periods that the benchmark was up (down). Unlike /// the prior two metrics, in both cases a higher value is better.(Up、Down均为越大越好) /// (当市场涨跌时,组合收益率涨跌所占比率,) /// </summary> func UpDownRatios(Ra *utils.SlidingWindow, Rb *utils.SlidingWindow) (float64, error) { var cumRa = 0.0 var cumRb = 0.0 var result = 0.0 method := "Capture" side := "Up" switch method { case "Capture": switch side { case "Up": UpRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } UpRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] > 0 { UpRa.Add(Ra.Data()[i]) UpRb.Add(Rb.Data()[i]) } } cumRa = UpRa.Sum() cumRb = UpRb.Sum() result = cumRa / cumRb return result, nil case "Down": DnRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } DnRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] <= 0 { DnRa.Add(Ra.Data()[i]) DnRb.Add(Rb.Data()[i]) } } cumRa = DnRa.Sum() cumRb = DnRb.Sum() result = cumRa / cumRb return result, nil default: return math.NaN(), errors.New("In UpDownRatios, method Default!!") } case "Number": switch side { case "Up": UpRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } UpRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] > 0 && Rb.Data()[i] > 0 { UpRa.Add(Ra.Data()[i]) } } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] > 0 { UpRb.Add(Rb.Data()[i]) } } cumRa = float64(UpRa.Count()) cumRb = float64(UpRb.Count()) result = cumRa / cumRb return result, nil case "Down": DnRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } DnRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] < 0 && Rb.Data()[i] < 0 { DnRa.Add(Ra.Data()[i]) } } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] < 0 { DnRb.Add(Rb.Data()[i]) } } cumRa = float64(DnRa.Count()) cumRb = float64(DnRb.Count()) result = cumRa / cumRb return result, nil default: return math.NaN(), errors.New("In UpDownRatios, method default 2 error !!!") } case "Percent": switch side { case "Up": UpRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } UpRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] > Rb.Data()[i] && Rb.Data()[i] > 0 { UpRa.Add(Ra.Data()[i]) } } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] > 0 { UpRb.Add(Rb.Data()[i]) } } cumRa = float64(UpRa.Count()) cumRb = float64(UpRb.Count()) result = cumRa / cumRb return result, nil case "Down": DnRa, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } DnRb, err := utils.NewSlidingWindow(Ra.Count()) if err != nil { return math.NaN(), err } for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] > Rb.Data()[i] && Rb.Data()[i] < 0 { DnRa.Add(Ra.Data()[i]) } } for i := 0; i < Ra.Count(); i++ { if Rb.Data()[i] < 0 { DnRb.Add(Rb.Data()[i]) } } cumRa = float64(DnRa.Count()) cumRb = float64(DnRb.Count()) result = cumRa / cumRb return result, nil default: return math.NaN(), errors.New("In UpDownRatios, method default 3 is Error !!!") } default: return math.NaN(), errors.New("In UpDownRatios, method default 4 is Error !!!") } return math.NaN(), nil }
/// <summary> /// d ratio of the return distribution /// The d ratio is similar to the Bernado Ledoit ratio but inverted and /// taking into account the frequency of positive and negative returns. /// </summary> func DRatio(Ra *utils.SlidingWindow) (float64, error) { if Ra == nil { return math.NaN(), errors.New("In DRatio, Ra == nil") } if Ra.Count() <= 0 { return math.NaN(), errors.New("In DRatio, Ra.Count() <= 0") } upList, _ := utils.NewSlidingWindow(Ra.Count()) downList, _ := utils.NewSlidingWindow(Ra.Count()) for i := 0; i < Ra.Count(); i++ { if Ra.Data()[i] < 0 { downList.Add(Ra.Data()[i]) } else if Ra.Data()[i] > 0 { upList.Add(Ra.Data()[i]) } } return -(downList.Sum() * float64(downList.Count())) / (float64(upList.Sum()) * float64(upList.Count())), nil }