向量化回测——短期回报不正确?

问题描述 投票:0回答:1

问题陈述

我有给定资产的

position_signal
close
(收盘价)数据框。您可以将每一行视为一个特定的时间步长(分钟、小时等)。我想运行矢量化回测来计算每个时间点的总投资组合回报,无论我是多头、空头还是不持有。在这种情况下,
position_signal
列分别是
1
-1
0

我尝试过的事情

我已经尝试了很多...似乎是一个简单的问题。网上有很多教程声称可以做到这一点。然而,所有这些的问题在于它们要么:假设累积收益在交易期间(因此每天的收益都会乘以下一天的收益,如果您只是持有头寸,这是不正确的)或者它们不计算短期回报正确。

其中大多数的代码可以归结为:

df['log_returns'] = np.log(df['close']) - np.log(df['close'].shift(1)) df['strategy_returns'] = df['position_signal'] * df['log_returns'] df['cumulative_ret'] = df['strategy_returns'].cumsum().apply(np.exp)
现在,数学相当烦人,所以我不想在这里做……但本质上,这对于多头来说效果很好,但在尝试计算空头回报时就会崩溃。 

原因是因为它认为我们空头的回报只是我们同等多头头寸损失的负数。 哪一个是错误的

简单示例

假设我有一个数据框,代表进行的两笔交易:一笔交易是

long

,价格 = 
$1
 --> 
$2
,另一笔是 
$2
 -> 
$1
 做空。现在,如果我们要计算回报率,则很简单:第一笔交易为 100%,第二笔交易为 50%。

如果我们在交易开始时持有 100 美元的头寸,我们的第一笔交易 (+100%) 会将我们的 100 美元变成 200 美元,然后我们的第二笔交易 (+50%) 会将我们的 200 美元变成 300 美元。所以我们的最终头寸是 300 美元(或 300%)。

问题在于,这些教程中给出的代码执行了多头权利,但随后认为我们的空头回报率为 100%(因为我们的回报是多头回报的 -100% 的负数)。

证明代码

这是演示这一点的代码片段。需要注意的一点是,我们以第一行

position_signal = 1/-1 上方

的价格输入,这基本上意味着我们的 
position_signal
 移动了 1 以考虑前瞻偏差。这是一个语义问题,而不是真正的问题,只要返回相同,您就可以按照自己的意愿进行操作

df = pd.DataFrame([[0.0, 1.0], [0.0, 1.2], [0.0, 1.50], [0.0, 1], [1.0, 1.2], # We entered a LONG at the open of this timestep, which is the same as the close of the previous ($1.0) [1.0, 1.3], [1.0, 2.0], # We exit at close of this timestep, so $2 [0.0, 1.7], [0.0, 2], [-1.0, 1.798], # We entered a SHORT at the open of this timestep, which is the same as the close of the previous ($2.0) [-1.0, 0.50], [-1.0, 1.3], [-1.0, 1], # We exit at close of this timestep, so $1.0 [0.0, 1.5]], columns=['position_signal','close']) df['log_returns'] = np.log(df.close) - np.log(df.close.shift(1)) df['strategy_returns'] = df.log_returns * df.position_signal df['cumulative_returns'] = df.strategy_returns.cumsum().apply(np.exp) print(df)
代码输出:

position_signal close log_returns strategy_returns cumulative_returns 0 0.0 1.000 NaN NaN NaN 1 0.0 1.200 0.182322 0.000000 1.000000 2 0.0 1.500 0.223144 0.000000 1.000000 3 0.0 1.000 -0.405465 -0.000000 1.000000 4 1.0 1.200 0.182322 0.182322 1.200000 5 1.0 1.300 0.080043 0.080043 1.300000 6 1.0 2.000 0.430783 0.430783 2.000000 7 0.0 1.700 -0.162519 -0.000000 2.000000 8 0.0 2.000 0.162519 0.000000 2.000000 9 -1.0 1.798 -0.106472 0.106472 2.224694 10 -1.0 0.500 -1.279822 1.279822 8.000000 11 -1.0 1.300 0.955511 -0.955511 3.076923 12 -1.0 1.000 -0.262364 0.262364 4.000000 13 0.0 1.500 0.405465 0.000000 4.000000
如您所见,到最后它假设我们的回报率为 4.0 (400%),这不是我们想要的。应该是3.0(300%)



其他变化:

1 - 如果我们忽略中间价格变动,就有办法做到这一点。如果我们只将此 df 减少到四行:进入交易 1、退出交易 1、进入交易 2、退出交易 2,那么我们可以仅屏蔽仓位信号,并将每笔交易的收益作为单行计算,然后运行 cumprod那。我不想那样,我需要知道我的投资组合的中间价值。这就是为什么我为上面的示例添加了这些行。

2 - 还有一种方法可以通过除以空头盈亏和多头盈亏来实现此目的。但这并不好,因为如果我因多头不良而下跌 50%,它会影响我下一笔交易的购买力(即下一笔交易上涨 50% 并不会让我实现收支平衡)

3 - 还有非矢量化路线,但这对我来说太慢了。

python pandas dataframe algorithmic-trading trading
1个回答
0
投票
对数回报确实产生了 3.0 的总体回报,从对数回报到算术回报的转换公式不正确,因为您没有在指数化后减去 1。回顾一下核心公式是:

Let P1 = Initial Price, P2 = Exit Price Arithmetic Return R = (P2-P1)/P1 = P2/P1 - 1 Log Return r = ln(P2/P1) = ln(p2) - ln(p1)
因此,要将对数返回 r 转换为算术 R,公式为:

R = exp(r) - 1
举个例子:

import numpy as np np.exp(np.log(2/1) - np.log(1/2))-1
事实上,这也是相反的转换:

r = ln(1+R)
 非常常见,以至于 numpy 对这两者都有特定的功能:

np.log1p np.expm1
    
© www.soinside.com 2019 - 2024. All rights reserved.