我有给定资产的
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%)
2 - 还有一种方法可以通过除以空头盈亏和多头盈亏来实现此目的。但这并不好,因为如果我因多头不良而下跌 50%,它会影响我下一笔交易的购买力(即下一笔交易上涨 50% 并不会让我实现收支平衡)
3 - 还有非矢量化路线,但这对我来说太慢了。
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