我正在建立一个方程,以获取股票组合的净值。
原始方程式:
股票净值*重量。库存/股票类型数量+债券*重量。债券/债券类型数量+现金*重量。现金/现金类型数量
获取日期序列。
匹配方法:例如,BackTest.table中日期在2008-7-15之前的任何行都将与Weight.table中的第一行(涵盖从2008-5-01到2008-7- 15。)
这里是BackTest.table的一部分,以简化想象。该表的标题为:日期,股票类型1,股票类型2,...,债券类型1,债券类型2,...,现金类型4。(这只是类型编号的示例)它实际上与重量表
Date s1 s2 s3 s4 s5 b1 b2 b3 b4 b5 b6 b7 c1 c2 c3 c4
2 2008-07-01 0 -3.0158124 -0.055652040 1 0 0 0 0 0 0 0 0 0 0 0 0
3 2008-07-02 0 0.3838345 -0.119046476 1 0 0 0 0 0 0 0 0 0 0 0 0
4 2008-07-03 0 2.7602604 0.009611965 1 0 0 0 0 0 0 0 0 0 0 0 0
5 2008-07-04 0 -0.5370067 -0.009611041 1 0 0 0 0 0 0 0 0 0 0 0 0
6 2008-07-05 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
7 2008-07-06 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
8 2008-07-07 0 5.1583803 0.032680681 1 0 0 0 0 0 0 0 0 0 0 0 0
9 2008-07-08 0 0.8500539 0.048044124 1 0 0 0 0 0 0 0 0 0 0 0 0
10 2008-07-09 0 3.6352579 0.048981473 1 0 0 0 0 0 0 0 0 0 0 0 0
11 2008-07-10 0 -1.5689846 0.052797297 1 0 0 0 0 0 0 0 0 0 0 0 0
12 2008-07-11 0 -0.6688334 0.045093882 1 0 0 0 0 0 0 0 0 0 0 0 0
13 2008-07-12 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
14 2008-07-13 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
15 2008-07-14 0 1.0436299 0.033565414 1 0 0 0 0 0 0 0 0 0 0 0 0
16 2008-07-15 0 -3.8589001 0.004793450 1 0 0 0 0 0 0 0 0 0 0 0 0
17 2008-07-16 0 -4.0513392 0.034511187 1 0 0 0 0 0 0 0 0 0 0 0 0
18 2008-07-17 0 -1.0070062 0.009583134 1 0 0 0 0 0 0 0 0 0 0 0 0
19 2008-07-18 0 3.5303394 0.014373323 1 0 0 0 0 0 0 0 0 0 0 0 0
20 2008-07-19 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
21 2008-07-20 0 0.0000000 0.000000000 1 0 0 0 0 0 0 0 0 0 0 0 0
这是Weight.table的另一块内容,用于简化想象。
Date Stock Numbers1 Bond Number2 Cash Number3
1 2008-04-30 0.0642 5 0.7858 2 0.1500 2
2 2008-07-15 0.0801 5 0.7699 2 0.1500 2
3 2008-07-31 0.0727 6 0.7773 2 0.1500 1
4 2008-10-31 0.1373 4 0.7127 2 0.1500 1
5 2008-11-30 0.1457 3 0.7144 2 0.1399 2
6 2009-01-31 0.1791 5 0.7242 2 0.0967 1
这是Weight.table和BackTest.table标头的输出
dput(head(Weight.table))
structure(list(Date = structure(c(13999, 14075, 14091, 14183,
14213, 14275), class = "Date"), Stock = c(0.0642, 0.0801, 0.0727,
0.1373, 0.1457, 0.1791), Numbers1 = c(5L, 5L, 6L, 4L, 3L, 5L),
Bond = c(0.7858, 0.7699, 0.7773, 0.7127, 0.7144, 0.7242),
Number2 = c(2L, 2L, 2L, 2L, 2L, 2L), 现金 = c(0.15, 0.15,
0.15, 0.15, 0.1399, 0.0967), Number3 = c(2L, 2L, 1L, 1L,
2L, 1L)), row.names = c(NA, 6L), class = "data.frame")
dput(head(BackTest.table))
structure(list(Date = structure(c(14061, 14062, 14063, 14064,
14065, 14066), class = "Date"), s1 = c(0, 0, 0, 0, 0, 0), s2 = c(-3.01581241943634,
0.383834486785705, 2.76026041158503, -0.537006711952127, 0, 0
), s3 = c(-0.0556520404148886, -0.119046476128297, 0.00961196497399089,
-0.00961104116408056, 0, 0), s4 = c(1, 1, 1, 1, 1, 1), s5 = c(0,
0, 0, 0, 0, 0), b1 = c(0, 0, 0, 0, 0, 0), b2 = c(0, 0, 0, 0,
0, 0), b3 = c(0, 0, 0, 0, 0, 0), b4 = c(0, 0, 0, 0, 0, 0), b5 = c(0,
0, 0, 0, 0, 0), b6 = c(0, 0, 0, 0, 0, 0), b7 = c(0, 0, 0, 0,
0, 0), c1 = c(0, 0, 0, 0, 0, 0), c2 = c(0, 0, 0, 0, 0, 0), c3 = c(0,
0, 0, 0, 0, 0), c4 = c(0, 0, 0, 0, 0, 0)), row.names = 2:7, class = "data.frame")
但是,要得到我想要的东西要花一些时间。所以我尝试使用sapply,但是结果却不同。似乎申请者没有经过IFELSE流程?
要获取值,每当Backtest.table中的日期与Weight.table中的日期匹配时,我就设置一个常数k,k = k + 1,因此它将移至下一行并使用新的权重进行计算净值。
有效的原始代码:
k <- 1
for (t in 1:nrow(BackTest.table)) {
if (BackTest.table[t, 1] %in% Weight.table[, 1] == FALSE) {
NetReturnPt.table[t, 2] <- sum(BackTest.table[t, 2: ncol(BackTest.table)]*
c(rep(Weight.table[k, 2]/ Weight.table[k, 3], Weight.table[k, 3]),
rep(Weight.table[k, 4]/ Weight.table[k, 5], Weight.table[k, 5]),
rep(Weight.table[k, 6]/ Weight.table[k, 7], Weight.table[k, 7])
), na.rm = TRUE)
}
else {NetReturnPt.table[t, 2] <- sum(BackTest.table[t, 2: ncol(BackTest.table)]*
c(rep(Weight.table[k, 2]/ Weight.table[k, 3], Weight.table[k, 3]),
rep(Weight.table[k, 4]/ Weight.table[k, 5], Weight.table[k, 5]),
rep(Weight.table[k, 6]/ Weight.table[k, 7], Weight.table[k, 7])
), na.rm = TRUE)
k <- k + 1
}
}
dput(head(NetReturnPt.table[, 2]))
[1] -0.026597604 0.016239878 0.048405161 0.005821428 0.012840000 0.012840000
dput(NetReturnPt.table[20:25, 2])
[1] 0.016020000 0.073282388 0.014539880 0.003858773 0.065490672 -0.003378064
APPLY函数在前几个数据之后没有给出正确的值:
k <- 1
TestApply <- function(t) {
if (BackTest.table[t, 1] %in% Weight.table[, 1] == FALSE) {
NetReturnPt.table[t, 2] <- sum(BackTest.table[t, 2: ncol(BackTest.table)] *
c(rep(Weight.table[k, 2]/ Weight.table[k, 3], Weight.table[k, 3]),
rep(Weight.table[k, 4]/ Weight.table[k, 5], Weight.table[k, 5]),
rep(Weight.table[k, 6]/ Weight.table[k, 7], Weight.table[k, 7])
), na.rm = TRUE)
}
else { NetReturnPt.table[t, 2] <- sum(BackTest.table[t, 2:ncol(BackTest.table)] *
c(rep(Weight.table[k, 2]/ Weight.table[k, 3], Weight.table[k, 3]),
rep(Weight.table[k, 4]/ Weight.table[k, 5], Weight.table[k, 5]),
rep(Weight.table[k, 6]/ Weight.table[k, 7], Weight.table[k, 7])
), na.rm = TRUE)
k <- k + 1
}
}
test.result <- sapply(1: nrow(BackTest.table), function(t) TestApply(t))
dput(head(test.result))
[1] -0.026597604 0.016239878 0.048405161 0.005821428 0.012840000 0.012840000
dput(test.result[20:25])
[1] 0.012840000 0.058735697 0.011653687 0.003092799 0.052490651 -0.002707512
您可以看到前几个值与使用FORLOOP的值相同。因此,我想知道它是否没有通过IFELSE流程。
感谢您的宝贵时间,我还要感谢史蒂文·李(Steven Lee)告诉我更好的方式来显示代码。
答案 0 :(得分:1)
OMG,这令人困惑。看来,如果您正确设置了数据结构,则根本不需要循环,而只需对适当数据帧的列进行除法或乘以运算即可。
您的代码无法正常工作的原因是,k现在是sapply
函数参数的内部变量。 sapply
对BackTest.table的每一行重复调用TestApply
,但是每次调用TestApply
时,k仍为1,因为k <- k + 1
在{之外不起作用{1}}。
一种解决方法是使用TestApply
,它在父环境(特别是定义了k <<- k + 1
变量的第一个父环境)中进行分配。虽然我认为这会起作用,但这既不是一个优雅的解决方案也不是一个安全的解决方案。通常,函数只能通过返回值来影响其环境(更改k
被称为“副作用”,通常不鼓励这样做)。
更好的方法是考虑如何清理,匹配和提取数据帧,然后以一种简单明了的方式将其用于计算。在这里,我无法为您提供帮助:您需要向我们提供有关您的表格的更多信息(也许在另一个问题中)。
另一种方法是从以下代码开始:
k
此代码是邪恶的。这很令人困惑,您不知道它的作用(它似乎在尝试获取加权均值,但是这样做的方法要快得多,例如sum(BackTest.table[t, 2: ncol(BackTest.table)] *
c(rep(Weight.table[k, 2]/ Weight.table[k, 3], Weight.table[k, 3]),
rep(Weight.table[k, 4]/ Weight.table[k, 5], Weight.table[k, 5]),
rep(Weight.table[k, 6]/ Weight.table[k, 7], Weight.table[k, 7])
),这取决于BackTest.table中的列数。我会努力先清理这个烂摊子。
还要注意,带有重复项的向量需要计算weighted.mean
次,但实际上可以只复制一次Weight.table的列一次,因为此过程对于每一行都是相同的。>
编辑:好的,所以现在借助数据,我可以仔细看看这个邪恶代码的作用。基本上,这些列是分组的,对于每个组,您都有一个权重,您应根据该权重将该组中列的值相除。因此,为节省空间,代码作者使用了一种游程长度编码(0.0642重复5次,0.7858重复2次,等等)。
但是,t
的行具有不同的重复次数。这真是一团糟,我真的会非常鼓励您考虑如何做以及如何做,以最终得到一个最佳的数据帧用于计算。
答案 1 :(得分:0)
这是一种建议的方法。正如一月份所建议的,如果您重组数据,这应该很简单。
在这里,我重塑了Weight.table
的形状,以便每一行显示每个班级和日期的权重和数字。请注意,我更改了示例数据中的前两个日期,以使其与另一个表匹配。
library(tidyverse)
Weight.table.tidy <- Weight.table %>%
# Renaming here so the first character represents the class, and the second
# character represents (w)eight or (n)number.
rename(s_w = "Stock", s_n = "Numbers1",
b_w = "Bond", b_n = "Number2",
c_w = "现金", c_n = "Number3") %>%
gather(col, val, -Date) %>%
separate("col", c("class", "stat")) %>%
spread(stat, val)
head(Weight.table.tidy)
# Date class n w
#1 2008-07-01 b 2 0.7858
#2 2008-07-01 c 2 0.1500
#3 2008-07-01 s 5 0.0642
#4 2008-07-04 b 2 0.7699
#5 2008-07-04 c 2 0.1500
#6 2008-07-04 s 5 0.0801
现在,我对BackTest.table
做类似的事情,重塑为长形,以便每一行包含一个Date
,class
和num
,并从原始表。然后,我们可以将其加入Weight.table.tidy
并根据每个类+数字组中的先前日期填写所有不适用的日期。现在,我们有了一长串的值,以及将用来加权每个值的变量。
BackTest.table.tidy <- BackTest.table %>%
gather(type, val, -Date) %>%
separate("type", c("class", "num"), sep = 1) %>%
left_join(Weight.table.tidy) %>%
group_by(class, num) %>%
fill(n, w) %>% ungroup()
head(BackTest.table.tidy)
## A tibble: 6 x 6
# Date class num val n w
# <date> <chr> <chr> <dbl> <dbl> <dbl>
#1 2008-07-01 b 1 0 2 0.786
#2 2008-07-02 b 1 0 2 0.786
#3 2008-07-03 b 1 0 2 0.786
#4 2008-07-04 b 1 0 2 0.770
#5 2008-07-05 b 1 0 2 0.770
#6 2008-07-06 b 1 0 2 0.770
一整形,我们可以使用一行来加权值,另一行可以对每个Date求和。作为矢量化解决方案,我希望它会比原始解决方案快得多。
BackTest.table.tidy %>%
mutate(val_wtd = val * w / n) %>%
count(Date, wt = val_wtd)