动态规划计算最大回撤
定义
回撤用来衡量基金的抗风险能力。
最大回撤定义
是指在某一段时期内产品净值从最高点开始回落到最低点的幅度。
最大回撤率,不一定是(最高点净值-最低点净值)/最高点时的净值,也许它会出现在其中某一段的回落。
公式可以这样表达:
D为某一天的净值,i为某一天,j为i后的某一天,Di为第i天的产品净值,Dj则是Di后面某一天的净值
drawdown就是最大回撤率
drawdown=max((Di-Dj)/Di)= max(1- Dj/Di),其实就是对每一个净值进行回撤率求值,然后找出最大的。 这里的净值一般是复权净值,因为基金分红后净值会下跌,单纯的用单位净值计算会出现不准的情况。
计算
定义数据结构,包含回撤区间和最大回撤值
public static class MaxWithdrawal {
/**
* 最大回撤的数据列表
*/
private List<String> list;
/**
* 最大回撤的值
*/
private String maxValue;
public List<String> getList() {
return list;
}
public String getMaxValue() {
return maxValue;
}
}
动态规划方式,时间复杂度O(N),空间复杂度 O(3N),3个辅助数组
/**
* 动态规划方式计算最大回撤,依照定义,最大回撤值是一个负数
* D为某一天的净值,i为某一天,j为i后的某一天,Di为第i天的产品净值,Dj则是Di后面某一天的净值
* drawdown就是最大回撤率
* drawdown=max((Di-Dj)/Di),其实就是对每一个净值进行回撤率求值,然后找出最大的。
* @param list
* @return
*/
public static MaxWithdrawal maxWithdrawalDp(List<String> list) {
if (Preconditions.isNullOrEmpty(list))
return null;
int size = list.size();
double[] dpMax = new double[size]; //定义dpMax[i]为0到i的最大值
dpMax[0] = NumberHelper.getDouble(list.get(0), 0);
double[] dpMin = new double[size]; //定义dpMin[i]为0到i的最小值
dpMin[0] = NumberHelper.getDouble(list.get(0), 0);
double[] dp = new double[size]; //定义dp[i]为0到i的最大回撤
dp[0] = 0;
int maxIndex = size -1;
int minIndex = 0;
int i = 0; //i指针跟踪最大值
int j = 0; //j指针跟踪最小值
for (int k = 1; k< size; k++) {
if (NumberHelper.getDouble(list.get(k), 0) > dpMax[k-1]) {
dpMax[k] = NumberHelper.getDouble(list.get(k), 0);
i++;
j++;
maxIndex = k;
L.i("动规dpMax["+k+"]:"+dpMax[k]);
} else {
dpMax[k] = dpMax[k-1];
}
if (NumberHelper.getDouble(list.get(k), 0) < dpMin[k-1]) {
dpMin[k] = NumberHelper.getDouble(list.get(k), 0);
j++;
minIndex = k;
L.i("动规dpMin["+k+"]:"+dpMin[k]);
} else {
dpMin[k] = dpMin[k-1];
}
if (j > i) { //最小值在最大值之后回撤才有意义
//drawdown=(Di - Dj)/Di = 1 - Dj/Di
dp[k] = Math.max(1- dpMin[k] / dpMax[k], dp[k-1]);
L.i("动规dp["+k+"]:"+dp[k]);
} else {
dp[k] = dp[k-1];
}
}
L.i("动规dp["+(size-1)+"]:"+dp[size-1] + ",maxIndex:"+maxIndex+",minIndex:"+minIndex);
if (dp[size-1] == 0 || j == i || maxIndex >= minIndex) {
//找不到最大回撤
return null;
}
MaxWithdrawal data = new MaxWithdrawal();
data.maxValue = NumberHelper.parsePercent(String.valueOf(dp[size-1]), "0.00%", 2, true, false);
data.list = list.subList(maxIndex,minIndex+1);
return data;
}
压缩数组节省空间
/**
* 压缩数组节省空间
*
* @param list
* @return
*/
public static MaxWithdrawal maxWithdrawalDp2(List<String> list) {
if (Preconditions.isNullOrEmpty(list)) {
return null;
}
int size = list.size();
int i = 0; //i指针跟踪最大值
int j = 0; //j指针跟踪最小值
int maxIndex = size - 1;
int minIndex = 0;
double dpMax = NumberHelper.getDouble(list.get(0), 0); //当前遍历最大值
double dpMin = NumberHelper.getDouble(list.get(0), 0); //当前遍历最小值
double dp = 0; //当前计算得出回撤值
for (int k = 1; k < size; k++) {
if (NumberHelper.getDouble(list.get(k), 0) > dpMax) {
dpMax = NumberHelper.getDouble(list.get(k), 0);
i++;
j++;
maxIndex = k;
}
if (NumberHelper.getDouble(list.get(k).getOriginRange(), 0) < dpMin) {
dpMin = NumberHelper.getDouble(list.get(k), 0);
j++;
minIndex = k;
}
if (j > i) {
dp = Math.max(1 - dpMin / dpMax, dp); //dp = (dpMax - dpMin) / dpMax
}
}
if (j == i || minIndex <= maxIndex || dp == 0) {
return null;
}
MaxWithdrawal data = new MaxWithdrawal();
data.maxValue = NumberHelper.parsePercent(String.valueOf(dp), "0.00%", 2, true, false);
data.list = list.subList(maxIndex, minIndex + 1);
return data;
}
如果按照定义计算时间复杂度O(N^2),一般是不会采用这种方式的。