zl程序教程

您现在的位置是:首页 >  后端

当前栏目

使用python计算最大回撤

Python计算 最大 使用
2023-09-11 14:16:58 时间

使用python计算最大回撤

1. 单期简单收益率

R t = P t − P t − 1 P t − 1 R_{t}=\dfrac {P _{t}-P_{t-1}}{P_{t-1}} Rt=Pt1PtPt1
说明:

  1. R t R_t Rt 为单期简单收益率
  2. P t P_t Pt 为t期的价格
  3. P t − 1 P_{t-1} Pt1 为t-1期的价格
import datetime
import pandas as pd
pd.core.common.is_list_like = pd.api.types.is_list_like
price = pd.Series([3.42,3.51,3.68,3.43,3.56,3.67], index=[datetime.date(2015,7,x) for x in range(3,9)])
price
2015-07-03    3.42
2015-07-04    3.51
2015-07-05    3.68
2015-07-06    3.43
2015-07-07    3.56
2015-07-08    3.67
dtype: float64

利用ffn库计算单期简单收益

import ffn
r = ffn.to_returns(price)
r
2015-07-03         NaN
2015-07-04    0.026316
2015-07-05    0.048433
2015-07-06   -0.067935
2015-07-07    0.037901
2015-07-08    0.030899
dtype: float64

2. 最大回撤

最大回撤(Maximum Drawdown, MDD) 用来衡量投资(特别是基金)的表现,

2.1 回撤:某资产在时刻T的回撤是指资产在(0,T)的最高峰值与现在价值 P T P_T PT之间的回落值,用数学公式表达为:

D ( T ) = max ⁡ { 0 , max ⁡ t ∈ ( 0 , T ) P t − P T } D\left( T\right) =\max \left\{ 0,\max _{t\in (0,T)}P_t-P_{T}\right\} D(T)=max{0,maxt(0,T)PtPT}

2.2 对应的回撤率为:

d ( T ) = D ( T ) max ⁡ t ∈ ( 0 , T ) p t d\left(T\right) = \dfrac {D\left( T\right) }{\max _{t\in \left( 0,T\right) }p_{t}} d(T)=maxt(0,T)ptD(T)

知道回撤的含义之后,最大回撤就比较容易理解了,资产在T时刻的最大回撤MDD(T),就是资产在时段(0,T)内回撤的最大值,对应的数学公式为:

M D D ( T ) = max ⁡ τ ∈ ( 0 , T ) D ( τ ) = max ⁡ τ ∈ ( 0 , T ) [ max ⁡ t ∈ ( 0 , τ ) P t − P τ ] MDD\left(T\right) = {\max _{\tau \in \left(0,T\right)}} D\left(\tau\right) = {\max _{\tau \in \left(0,T\right)}} \begin{aligned} \left[ \max _{t\in \left( 0,\tau \right) }P_{t}-P_{\tau}\right] \end{aligned} MDD(T)=maxτ(0,T)D(τ)=maxτ(0,T)[t(0,τ)maxPtPτ]

相应的最大回撤率为:
m d d ( T ) = max ⁡ τ ∈ ( 0 , T ) d ( τ ) M D D ( T ) max ⁡ t ∈ ( 0 , T ) P t mdd\left(T\right) = {\max _{\tau \in \left(0,T\right)}} d\left(\tau \right) \dfrac {MDD\left(T\right)}{\max _{t \in \left(0,T\right)}P_t} mdd(T)=maxτ(0,T)d(τ)maxt(0,T)PtMDD(T)

直观的讲,MDD(T)对应的是在(0,T)时段内资产价值从最高峰回落到最低谷的幅度。最大回撤常用来描述投资者在持有资产是可能面临的最大亏损。

2.3 利用收益率计算最大回撤

如果某资产的收益率序列为R1,R2,…,RT,在初始时刻0时,我们投资1元在该资产上并一直持有到T时刻,则初始值为1元的资产价值就会随时间变化为:(1+R1),(1+R1)(1+R2),(1+R1)(1+R2)(1+R3),…, ∏ k = 1 T ( 1 + R k ) \prod _{k=1}^{T}\left( 1+R_{k}\right) k=1T(1+Rk)

时刻T对应的回撤值为:
D ( T ) = max ⁡ { 0 , max ⁡ t ∈ ( 0 , T ) ∏ k = 1 t ( 1 + R k ) − ∏ k = 1 T ( 1 + R k ) } D(T) = \max \left\{ 0, \max _{t \in (0,T)} \prod _{k=1}^{t}\left( 1+R_{k}\right) - \prod _{k=1}^{T}\left( 1+R_{k}\right) \right\} D(T)=max{0,maxt(0,T)k=1t(1+Rk)k=1T(1+Rk)}

相应的回撤率为:
d ( T ) = D ( T ) max ⁡ t ∈ ( 0 , T ) ∏ k = 1 t ( 1 + R k ) d(T) = \dfrac {D\left( T\right) }{\max _{t\in \left( 0,T\right) }\prod _{k=1}^{t}\left( 1+R_{k}\right) } d(T)=maxt(0,T)k=1t(1+Rk)D(T)

最大回撤为:
M D D ( T ) = max ⁡ τ ∈ ( 0 , T ) D ( τ ) = max ⁡ τ ∈ ( 0 , T ) [ max ⁡ t ∈ ( 0 , τ ) ∏ k = 1 t ( 1 + R k ) − ∏ k = 1 τ ( 1 + R k ) ] MDD(T) = \max _{\tau \in \left( 0,T\right) }D\left( \tau \right) = \max _{\tau \in \left( 0,T\right) } \begin{aligned} \left[\max _{t\in \left( 0,\tau \right) }\prod _{k=1}^{t}\left( 1+R_{k}\right) - \prod _{k=1}^{\tau}\left( 1+R_{k}\right) \right]\end{aligned} MDD(T)=maxτ(0,T)D(τ)=maxτ(0,T)[t(0,τ)maxk=1t(1+Rk)k=1τ(1+Rk)]

相应的最大回撤率为:
m d d ( T ) = max ⁡ τ ∈ ( 0 , T ) d ( τ ) = M D D ( T ) max ⁡ t ∈ ( 0 , T ) ∏ k = 1 t ( 1 + R k ) mdd(T) = \max _{\tau \in \left( 0,T\right) }d(\tau) = \dfrac {MDD(T)}{\max _{t \in \left( 0,T\right) }\prod _{k=1}^{t}\left( 1+R_{k}\right)} mdd(T)=maxτ(0,T)d(τ)=maxt(0,T)k=1t(1+Rk)MDD(T)

value = (1 + r).cumprod()
value
2015-07-03         NaN
2015-07-04    1.026316
2015-07-05    1.076023
2015-07-06    1.002924
2015-07-07    1.040936
2015-07-08    1.073099
dtype: float64
D = value.cummax() - value
D
2015-07-03         NaN
2015-07-04    0.000000
2015-07-05    0.000000
2015-07-06    0.073099
2015-07-07    0.035088
2015-07-08    0.002924
dtype: float64
d = D / (D + value)
d
2015-07-03         NaN
2015-07-04    0.000000
2015-07-05    0.000000
2015-07-06    0.067935
2015-07-07    0.032609
2015-07-08    0.002717
dtype: float64
MDD = D.max()
MDD
0.07309941520467844
mdd =d.max()
mdd
# 对应的最大回撤率值为
0.06793478260869572
# 采用ffn库计算收益率累积最大回撤
ffn.calc_max_drawdown(value)
-0.06793478260869568
from empyrical import max_drawdown
# 使用 empyrical 计算收益率序列最大回撤
max_drawdown(r)
-0.06793478260869572

3. Java版本的最大回测

import java.util.Arrays;
import java.util.List;

import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MaxDrawdownService {

	/**
	 * 计算最大回撤:某段时间内连续收益率之和最小的值为最大回撤
	 * 
	 * @Param rates:收益率
	 */
	@SuppressWarnings("all")
	public static double caculateMaxDrawdown(List<Double> rates) {
		double s = 0;
		double e = 0;
		double max = 0;
		double temp = 0;
		double ts = 0;
		// 收益率不为空
		if (!rates.isEmpty()) {
			for (int i = 0; i < rates.size(); i++) {
				// 获得收益率
				double r = rates.get(i);
				temp = temp + r;
				if (temp > 0) {
					ts = i + 1;
					e = i + 1;
					temp = 0;
				} else {
					if (temp < max) {
						s = ts;
						e = i;
						max = temp;
					}
				}
			}
		}
		log.info("最大回撤计算结果:maxsum={},start={},end={}", max, s, e);
		return max;
	}

	/**
	 * 按照资金计算最大回撤
	 * @param equityValues
	 * @return
	 */
	public static double calMaxDrawdown(List<Double> equityValues) {
		if (equityValues == null || equityValues.size() < 2)
			return 0;

		double maxDrawdown = 0; // 最大回撤
		double maxEquityValue = equityValues.get(0); // 当日之前的最大资产净值

		for (int i = 1; i < equityValues.size(); i++) {
			double currentEquityValue = equityValues.get(i); // 当日资产净值
			double drawDown = (1 - currentEquityValue / maxEquityValue);
			maxDrawdown = Math.max(maxDrawdown, drawDown);

			maxEquityValue = Math.max(currentEquityValue, maxEquityValue);
		}
		log.info("calMaxDrawdown最大回撤计算结果:{}", maxDrawdown);
		return maxDrawdown;
	}

	public static void main(String[] args) {
		Double[] c = {0.026316,0.048433,-0.067935,0.037901,0.030899};
		caculateMaxDrawdown(Arrays.asList(c));
		Double[] c2 = {3.42,3.51,3.68,3.43,3.56,3.67};
		calMaxDrawdown(Arrays.asList(c2));
	}
}