Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用python中的矢量化解决方案计算最大下拉_Python_Numpy_Pandas_Quantitative Finance - Fatal编程技术网

使用python中的矢量化解决方案计算最大下拉

使用python中的矢量化解决方案计算最大下拉,python,numpy,pandas,quantitative-finance,Python,Numpy,Pandas,Quantitative Finance,是量化金融中常用的风险度量,用于评估经历过的最大负回报 最近,我对使用循环方法计算最大压降的时间感到不耐烦 def max_dd_loop(returns): """returns is assumed to be a pandas series""" max_so_far = None start, end = None, None r = returns.add(1).cumprod() for r_start in r.index:

是量化金融中常用的风险度量,用于评估经历过的最大负回报

最近,我对使用循环方法计算最大压降的时间感到不耐烦

def max_dd_loop(returns):
    """returns is assumed to be a pandas series"""
    max_so_far = None
    start, end = None, None
    r = returns.add(1).cumprod()
    for r_start in r.index:
        for r_end in r.index:
            if r_start < r_end:
                current = r.ix[r_end] / r.ix[r_start] - 1
                if (max_so_far is None) or (current < max_so_far):
                    max_so_far = current
                    start, end = r_start, r_end
    return max_so_far, start, end

给定收益的时间序列,我们需要评估从起点到终点的每个组合的总收益

第一个技巧是将收益的时间序列转换为一系列收益指数。给定一系列回报指数,我可以计算任何子周期的回报,回报指数在开始的ri_0和结束的ri_1。计算结果为:ri_1/ri_0-1

第二个技巧是产生回报指数的第二系列倒数。如果r是我的收益指数系列,那么1/r是我的逆指数系列

第三个技巧是取r*(1/r)的矩阵积。转置

r是nx1矩阵。(1/r)。转置是一个1×n矩阵。生成的产品包含ri_j/ri_k的每个组合。只要减去1,我就得到了回报

第四个技巧是确保我限制分母在分子表示周期之前表示周期

下面是我的矢量化函数

import numpy as np
import pandas as pd

def max_dd(returns):
    # make into a DataFrame so that it is a 2-dimensional
    # matrix such that I can perform an nx1 by 1xn matrix
    # multiplication and end up with an nxn matrix
    r = pd.DataFrame(returns).add(1).cumprod()

    # I copy r.T to ensure r's index is not the same
    # object as 1 / r.T's columns object
    x = r.dot(1 / r.T.copy()) - 1
    x.columns.name, x.index.name = 'start', 'end'

    # let's make sure we only calculate a return when start
    # is less than end.
    y = x.stack().reset_index()
    y = y[y.start < y.end]

    # my choice is to return the periods and the actual max
    # draw down
    z = y.set_index(['start', 'end']).iloc[:, 0]
    return z.min(), z.argmin()[0], z.argmin()[1]
循环解决方案的相同测试如下:

10:   0.032 seconds
50:   0.044 seconds
100:  0.055 seconds
150:  0.082 seconds
200:  0.047 seconds
10:   0.153 seconds
50:   3.169 seconds
100: 12.355 seconds
150: 27.756 seconds
200: 49.726 seconds

编辑 亚历山大的回答提供了更好的结果。使用修改过的代码进行相同的测试

10:   0.000 seconds
50:   0.000 seconds
100:  0.004 seconds
150:  0.007 seconds
200:  0.008 seconds
我将他的代码修改为以下函数:

def max_dd(returns):
    """Assumes returns is a pandas Series"""
    r = returns.add(1).cumprod()
    dd = r.div(r.cummax()).sub(1)
    mdd = dd.min()
    end = dd.argmin()
    start = r.loc[:end].argmax()
    return mdd, start, end
def max_dd(returns):
    r = returns.add(1).cumprod()
    dd = r.div(r.cummax()).sub(1)
    mdd = drawdown.min()
    end = drawdown.argmin()
    start = r.loc[:end].argmax()
    return mdd, start, end

给定收益的时间序列,我们需要评估从起点到终点的每个组合的总收益

第一个技巧是将收益的时间序列转换为一系列收益指数。给定一系列回报指数,我可以计算任何子周期的回报,回报指数在开始的ri_0和结束的ri_1。计算结果为:ri_1/ri_0-1

第二个技巧是产生回报指数的第二系列倒数。如果r是我的收益指数系列,那么1/r是我的逆指数系列

第三个技巧是取r*(1/r)的矩阵积。转置

r是nx1矩阵。(1/r)。转置是一个1×n矩阵。生成的产品包含ri_j/ri_k的每个组合。只要减去1,我就得到了回报

第四个技巧是确保我限制分母在分子表示周期之前表示周期

下面是我的矢量化函数

import numpy as np
import pandas as pd

def max_dd(returns):
    # make into a DataFrame so that it is a 2-dimensional
    # matrix such that I can perform an nx1 by 1xn matrix
    # multiplication and end up with an nxn matrix
    r = pd.DataFrame(returns).add(1).cumprod()

    # I copy r.T to ensure r's index is not the same
    # object as 1 / r.T's columns object
    x = r.dot(1 / r.T.copy()) - 1
    x.columns.name, x.index.name = 'start', 'end'

    # let's make sure we only calculate a return when start
    # is less than end.
    y = x.stack().reset_index()
    y = y[y.start < y.end]

    # my choice is to return the periods and the actual max
    # draw down
    z = y.set_index(['start', 'end']).iloc[:, 0]
    return z.min(), z.argmin()[0], z.argmin()[1]
循环解决方案的相同测试如下:

10:   0.032 seconds
50:   0.044 seconds
100:  0.055 seconds
150:  0.082 seconds
200:  0.047 seconds
10:   0.153 seconds
50:   3.169 seconds
100: 12.355 seconds
150: 27.756 seconds
200: 49.726 seconds

编辑 亚历山大的回答提供了更好的结果。使用修改过的代码进行相同的测试

10:   0.000 seconds
50:   0.000 seconds
100:  0.004 seconds
150:  0.007 seconds
200:  0.008 seconds
我将他的代码修改为以下函数:

def max_dd(returns):
    """Assumes returns is a pandas Series"""
    r = returns.add(1).cumprod()
    dd = r.div(r.cummax()).sub(1)
    mdd = dd.min()
    end = dd.argmin()
    start = r.loc[:end].argmax()
    return mdd, start, end
def max_dd(returns):
    r = returns.add(1).cumprod()
    dd = r.div(r.cummax()).sub(1)
    mdd = drawdown.min()
    end = drawdown.argmin()
    start = r.loc[:end].argmax()
    return mdd, start, end

df_returns
被假定为一个返回数据框架,其中每列是一个单独的策略/经理/安全,每行是一个新日期(例如每月或每天)


df_returns
被假定为一个返回数据框架,其中每列是一个单独的策略/经理/安全,每行是一个新日期(例如每月或每天)


我第一次建议使用
.expansing()
窗口,但对于
.cumprod()
.cummax()
内置窗口,显然没有必要使用该窗口来计算任何给定点的最大压降:

df = pd.DataFrame(data={'returns': np.random.normal(0.001, 0.05, 1000)}, index=pd.date_range(start=date(2016,1,1), periods=1000, freq='D'))

df = pd.DataFrame(data={'returns': np.random.normal(0.001, 0.05, 1000)},
                  index=pd.date_range(start=date(2016, 1, 1), periods=1000, freq='D'))
df['cumulative_return'] = df.returns.add(1).cumprod().subtract(1)
df['max_drawdown'] = df.cumulative_return.add(1).div(df.cumulative_return.cummax().add(1)).subtract(1)


我第一次建议使用
.expansing()
窗口,但对于
.cumprod()
.cummax()
内置窗口,显然没有必要使用该窗口来计算任何给定点的最大压降:

df = pd.DataFrame(data={'returns': np.random.normal(0.001, 0.05, 1000)}, index=pd.date_range(start=date(2016,1,1), periods=1000, freq='D'))

df = pd.DataFrame(data={'returns': np.random.normal(0.001, 0.05, 1000)},
                  index=pd.date_range(start=date(2016, 1, 1), periods=1000, freq='D'))
df['cumulative_return'] = df.returns.add(1).cumprod().subtract(1)
df['max_drawdown'] = df.cumulative_return.add(1).div(df.cumulative_return.cummax().add(1)).subtract(1)


我最近遇到了一个类似的问题,但不是全局MDD,而是要求我查找每个峰值后间隔的MDD。另外,在我的例子中,我应该单独使用每个策略的MDD,因此不需要应用
cumprod
。我的矢量化实现也基于

以下是运行此代码后的示例:

        nw      max_peaks_idx       dd          mdd
0   10000.000       0           0.000000    0.000000
1   9696.948        0           -0.030305   -0.030305
2   9538.576        0           -0.046142   -0.046142
3   9303.953        0           -0.069605   -0.069605
4   9247.259        0           -0.075274   -0.075274
5   9421.519        0           -0.057848   -0.075274
6   9315.938        0           -0.068406   -0.075274
7   9235.775        0           -0.076423   -0.076423
8   9091.121        0           -0.090888   -0.090888
9   9033.532        0           -0.096647   -0.096647
10  8947.504        0           -0.105250   -0.105250
11  8841.551        0           -0.115845   -0.115845
这是应用于完整数据集的完整数据集的图像

虽然矢量化了,但这段代码可能比另一段代码慢,因为对于每个时间序列,都应该有许多峰值,每个峰值都需要计算,所以O(n_峰值*n_间隔)


PS:我本可以消除
dd
mdd
列中的零值,但我发现这些值有助于指示在时间序列中观察到新峰值的时间。

我最近遇到了类似的问题,但不是全局mdd,而是要求我查找每个峰值后间隔的mdd。另外,在我的例子中,我应该单独使用每个策略的MDD,因此不需要应用
cumprod
。我的矢量化实现也基于

以下是运行此代码后的示例:

        nw      max_peaks_idx       dd          mdd
0   10000.000       0           0.000000    0.000000
1   9696.948        0           -0.030305   -0.030305
2   9538.576        0           -0.046142   -0.046142
3   9303.953        0           -0.069605   -0.069605
4   9247.259        0           -0.075274   -0.075274
5   9421.519        0           -0.057848   -0.075274
6   9315.938        0           -0.068406   -0.075274
7   9235.775        0           -0.076423   -0.076423
8   9091.121        0           -0.090888   -0.090888
9   9033.532        0           -0.096647   -0.096647
10  8947.504        0           -0.105250   -0.105250
11  8841.551        0           -0.115845   -0.115845
这是应用于完整数据集的完整数据集的图像

虽然矢量化了,但这段代码可能比另一段代码慢,因为对于每个时间序列,都应该有许多峰值,每个峰值都需要计算,所以O(n_峰值*n_间隔)


PS:我本可以消除
dd
mdd
列中的零值,但我发现这些值有助于指示在时间序列中观察到新峰值的时间。

看看这个问题和答案是否提供了帮助:也可以看到(可能是重复的?)游戏进行得很晚,但我认为
r.loc[:end].argmax()
将给您带来一个问题。您需要
r.loc[:end]。排序索引(升序=False)。argmax()
。如果序列中有多个零(多个高水位线),则当前行按原样返回第一次而不是最后一次出现,并产生一个太早的开始日期。@除非我遗漏了什么,如果有多个相同的高水位线,这是一个关于何时出现最大水位下降的解释问题。此外,这不会影响回报率的计算。假设你有一个提款系列