给定一个时间序列,我想计算最大亏损,我还想找到最大亏损的起点和终点,这样我就可以计算出持续时间。我想在这样的时间序列图上标记缩编的开始和结束:
a busy cat http://oi61.tinypic.com/r9h4er.jpg
到目前为止,我已经有了生成随机时间序列的代码,并且我已经有了计算最大亏损的代码。如果有人知道如何确定缩编开始和结束的地方,我真的很感激!
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# create random walk which I want to calculate maximum drawdown for:
T = 50
mu = 0.05
sigma = 0.2
S0 = 20
dt = 0.01
N = round(T/dt)
t = np.linspace(0, T, N)
W = np.random.standard_normal(size = N)
W = np.cumsum(W)*np.sqrt(dt) ### standard brownian motion ###
X = (mu-0.5*sigma**2)*t + sigma*W
S = S0*np.exp(X) ### geometric brownian motion ###
plt.plot(S)
# Max drawdown function
def max_drawdown(X):
mdd = 0
peak = X[0]
for x in X:
if x > peak:
peak = x
dd = (peak - x) / peak
if dd > mdd:
mdd = dd
return mdd
drawSeries = max_drawdown(S)
MaxDD = abs(drawSeries.min()*100)
print MaxDD
plt.show()
答案 0 :(得分:47)
找出运行最大减去当前值的最大值:
n = 1000
xs = np.random.randn(n).cumsum()
i = np.argmax(np.maximum.accumulate(xs) - xs) # end of the period
j = np.argmax(xs[:i]) # start of period
plt.plot(xs)
plt.plot([i, j], [xs[i], xs[j]], 'o', color='Red', markersize=10)
答案 1 :(得分:4)
在这背面我添加了unerwater分析,如果这有助于任何人...
def drawdowns(equity_curve):
i = np.argmax(np.maximum.accumulate(equity_curve.values) - equity_curve.values) # end of the period
j = np.argmax(equity_curve.values[:i]) # start of period
drawdown=abs(100.0*(equity_curve[i]-equity_curve[j]))
DT=equity_curve.index.values
start_dt=pd.to_datetime(str(DT[j]))
MDD_start=start_dt.strftime ("%Y-%m-%d")
end_dt=pd.to_datetime(str(DT[i]))
MDD_end=end_dt.strftime ("%Y-%m-%d")
NOW=pd.to_datetime(str(DT[-1]))
NOW=NOW.strftime ("%Y-%m-%d")
MDD_duration=np.busday_count(MDD_start, MDD_end)
try:
UW_dt=equity_curve[i:].loc[equity_curve[i:].values>=equity_curve[j]].index.values[0]
UW_dt=pd.to_datetime(str(UW_dt))
UW_dt=UW_dt.strftime ("%Y-%m-%d")
UW_duration=np.busday_count(MDD_end, UW_dt)
except:
UW_dt="0000-00-00"
UW_duration=np.busday_count(MDD_end, NOW)
return MDD_start, MDD_end, MDD_duration, drawdown, UW_dt, UW_duration
答案 2 :(得分:2)
您的max_drawdown已经跟踪了峰值位置。修改if
以在存储mdd和mdd_end
时存储结束位置return mdd, peak, mdd_end
。
答案 3 :(得分:2)
behzad.nouri解决方案非常干净,但这并不是最大的缺点(因为我刚开设帐户并且没有足够的atm信誉,所以无法发表评论)。
最终得到的是标称值的最大下降,而不是相对值的下降(百分比下降)。例如,如果将其应用于长期上升的时间序列(例如,股票市场指数S&P 500),则最新的价值下跌(较高的名义价值下跌)将优先于较早的价值下跌,因为只要名义值/点的下降更高。
例如标准普尔500:
通过在2000年之后使用此方法,您会看到Corona病毒危机,而不是2007-08年金融危机
下面的相关代码(来自behzad.nouri):
n = 1000
xs = np.random.randn(n).cumsum()
i = np.argmax(np.maximum.accumulate(xs) - xs) # end of the period
j = np.argmax(xs[:i]) # start of period
plt.plot(xs)
plt.plot([i, j], [xs[i], xs[j]], 'o', color='Red', markersize=10)
您只需要用标称值的下降除以最大累计量即可获得相对(%)的亏损。
( np.maximum.accumulate(xs) - xs ) / np.maximum.accumulate(xs)
答案 4 :(得分:-1)
此解决方案已经过测试并且可以工作,但是在这里我计算的是最大持续时间的缩水,而不是最大持续时间的缩水。该解决方案可以轻松地找到最大跌幅的持续时间。
def max_dur_drawdown(dfw, threshold=0.05):
"""
Labels all drawdowns larger in absolute value than a threshold and returns the
drawdown of maximum duration (not the max drawdown necessarily but most often they
coincide).
Args:
dfw (pd.DataFrame): monthly data, the pre-computed drawdowns or underwater.
threshold (float): only look at drawdowns greater than this in absolute value e.g. 5%
Returns:
dictionary containing the start, end dates and duration in months for the maximum
duration drawdowns keyed by column name.
"""
max_dur_per_column = {}
columns = dfw.columns.copy()
mddd_start = {}
mddd_end = {}
mddd_duration = {}
for col in columns:
# run the drawdown labeling algorithm
dfw['sign'] = 0
dfw['sign'].loc[dfw[col] == 0] = +1
dfw['sign'].loc[dfw[col] < 0] = -1
# find the sign change data points
dfw['change'] = dfw['sign'] != dfw['sign'].shift(1)
# the first change doesn't count
dfw['change'].iloc[0] = False
# demarcate the lef and right of the drawdowns
left = dfw[(dfw['change'] == True) & (dfw['sign'] == -1)].index.values
right = dfw[(dfw['change'] == True) & (dfw['sign'] == 1)].index.values
min_len = min(len(left), len(right))
intervals = pd.IntervalIndex.from_arrays(left[0:min_len], right[0:min_len])
# find the minimum value per drawdown interval so we label all data points to the left of it.
min_per_int = list(map(lambda i: (i.left, i.right, dfw[col][(dfw.index >= i.left) & (dfw.index < i.right)].min()), intervals))
# filter out drawdowns lower in absolute value than a threshold
min_per_int = list(filter(None.__ne__, list(map(lambda x: None if x[2] >= -threshold else x, min_per_int))))
# label only the negative part of the underwater NDD stands for negative-side drawdown.
dfw['NDD'] = 0
mddd_start[col] = None
mddd_end[col] = None
mddd_duration[col] = 0
for i in min_per_int:
# find the index of the data point that is minimum this is an argmin
min_idx = dfw[(dfw.index >= i[0]) & (dfw.index < i[1]) & (abs(dfw[col] - i[2]) < 1e-15)].index[0]
# compute the duration and update the maximum duration if needed
tmp_dur = int(np.round((min_idx - i[0]) / np.timedelta64(1, 'M')))
if tmp_dur > mddd_duration[col]:
mddd_start[col] = i[0].date()
mddd_end[col] = min_idx.date()
mddd_duration[col] = tmp_dur
return mddd_start, mddd_end, mddd_duration
用法示例:
# compute cumulative returns
dfc = pd.DataFrame(dfr['S&P500'] / dfr['S&P500'][0])
# compute drawdowns
dfw = dfc / dfc.cummax() - 1
print(max_dur_drawdown(dfw))