1
2library(zoo)
3
4# Default test objects
5data(managers, package = "PerformanceAnalytics")
6data(edhec, package = "PerformanceAnalytics")
7data(weights, package = "PerformanceAnalytics")
8
9
10## Varous periodicities of portfolio return contributions
11contribution <- PerformanceAnalytics::Return.portfolio(managers["2002::",1:5],
12                                                      weights=c(.05,.1,.3,.4,.15),
13                                                      rebalance_on = "quarters",
14                                                      verbose=TRUE)$contribution
15
16malformed_contribution <- coredata(contribution)
17
18minute_contribution <- contribution
19index(minute_contribution) <- seq(c(ISOdate(2000,1,1)), by="min", length.out=60)
20
21hourly_contribution <- contribution
22index(hourly_contribution) <- seq(c(ISOdate(2000,1,1)), by="hour", length.out=60)
23
24daily_contribution <- contribution
25index(daily_contribution) <- seq(c(ISOdate(2000,1,1)), by="day", length.out=60)
26
27weekly_contribution <- contribution
28index(weekly_contribution) <- seq(c(ISOdate(2000,1,1)), by="week", length.out=60)
29
30quaterly_contribution <- contribution
31index(quaterly_contribution) <- seq(c(ISOdate(2000,1,1)), by="quarter", length.out=60)
32
33yearly_contribution <- contribution
34index(yearly_contribution) <- seq(c(ISOdate(2000,1,1)), by="year", length.out=60)
35
36
37
38## Diferent time frames for edhec, weights,
39
40one_period_returns <- edhec[c("1999-12-31","2000-01-31"),c(1:11)]
41colnames(one_period_returns) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED", "FIA", "GM", "LSE", "MA", "RV")
42
43one_period_weights <- weights["2000-01-01",]
44colnames(one_period_weights) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED", "FIA", "GM", "LSE", "MA", "RV")
45
46multi_period_returns <- edhec[c("2000-01-31", "2000-02-29", "2000-03-31"),c(1:11)]
47colnames(multi_period_returns) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED", "FIA", "GM", "LSE", "MA", "RV")
48
49multi_period_weights <- c(weights["2000-01-01",], weights["2000-01-01",], weights["2000-01-01",])
50colnames(multi_period_weights) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED", "FIA", "GM", "LSE", "MA", "RV")
51index(multi_period_weights) <- as.Date(c("1999-12-31", "2000-01-31", "2000-02-29"))
52
53# Unit tests defaults
54epsilon <- 1e-10
55
56expect_error(to.period.contributions(),
57             info = "Missing 'Contributions' data handled correctly")
58
59  if(
60     expect_error(to.period.contributions(contribution, "daily"),
61                  info = "Invalid periodicity parameters are handled correctly")
62  ) { expect_error(to.period.contributions(x),
63                   info="Invalid periodicity parameters are handled correctly")
64    }
65
66  if(
67  expect_error(to.period.contributions(contribution, "weeks"),
68               info="Incompatible periodicity parameters are handled correctly")
69
70  ){   expect_error(to.period.contributions(contribution, "months"),
71                    info="Incompatible periodicity parameters are handled correctly")}
72
73  if(
74  expect_error(to.period.contributions(minute_contribution),
75               info="Data with too high a periodicity is handled correctly")
76  ){   expect_error(to.period.contributions(hourly_contribution),
77                    info="Data with too high a periodicity is handled correctly")}
78
79
80
81  expect_error(to.period.contributions(yearly_contribution),
82               info="Data with too low a periodicity is handled correctly")
83
84
85  expect_equivalent(periodicity(to.period.contributions(contribution))$scale,
86                    "yearly",
87                    info = "the main function and periodic versions do not throw an error")
88  expect_equivalent(periodicity(to.weekly.contributions(daily_contribution))$scale,
89                                "weekly",
90                                info = "the main function and periodic versions do not throw an error")
91  expect_equivalent(periodicity(to.monthly.contributions(daily_contribution))$scale,
92                    "monthly",
93                    info = "the main function and periodic versions do not throw an error")
94  expect_equivalent(periodicity(to.monthly.contributions(weekly_contribution))$scale,
95                    "monthly",
96                    info = "the main function and periodic versions do not throw an error")
97  expect_equivalent(periodicity(to.quarterly.contributions(weekly_contribution))$scale,
98                    "quarterly",
99                    info = "the main function and periodic versions do not throw an error")
100  expect_equivalent(periodicity(to.quarterly.contributions(contribution))$scale,
101                    "quarterly",
102                    info = "the main function and periodic versions do not throw an error")
103  expect_equivalent(periodicity(to.yearly.contributions(quaterly_contribution))$scale,
104                    "yearly",
105                    info = "the main function and periodic versions do not throw an error")
106  expect_equivalent(periodicity(to.yearly.contributions(contribution))$scale,
107                    "yearly",
108                    info = "the main function and periodic versions do not throw an error")
109  expect_equivalent(periodicity(to.quarterly.contributions(weekly_contribution))$scale,
110                    "quarterly",
111                    info = "the main function and periodic versions do not throw an error")
112
113
114# Check first API in underlying PerformanceAnalytics package
115expected_contribution_data <- coredata(one_period_weights)*coredata(one_period_returns[2,])
116computed_contribution_data <- PerformanceAnalytics::Return.portfolio(one_period_returns,
117                                                                     one_period_weights,
118                                                                     contribution = TRUE)
119
120expect_equal(coredata(expected_contribution_data),
121             coredata(computed_contribution_data[ ,colnames(expected_contribution_data)]),
122             tolerance=epsilon,
123             info = "one-period contributions are computed correctly")
124
125## Check alternate API in underlying PerformanceAnalytics package
126  computed_contribution_data <- PerformanceAnalytics::Return.portfolio(one_period_returns,
127                                                                       one_period_weights,
128                                                                       verbose = TRUE)$contribution
129
130  expect_equal(coredata(expected_contribution_data),
131               coredata(computed_contribution_data[,colnames(expected_contribution_data)]),
132               tolerance=epsilon,
133               info = "one-period contributions are computed correctly")
134
135### Now check the actual API for multi-period contribution but with only one period of data passed in
136  computed_multi_period_contribution_data <- to.period.contributions(computed_contribution_data)
137
138   expect_equal(coredata(expected_contribution_data),
139                coredata(computed_multi_period_contribution_data[,colnames(expected_contribution_data)]),
140                tolerance=epsilon,
141                info = "one-period contributions are computed correctly")
142
143
144   # Check API in underlying PerformanceAnalytics package
145  first_period_contribution_data <- coredata(multi_period_weights[1,])*coredata(multi_period_returns[1,])
146  second_period_contribution_data <- coredata(multi_period_weights[2,])*coredata(multi_period_returns[2,])
147  third_period_contribution_data <- coredata(multi_period_weights[3,])*coredata(multi_period_returns[3,])
148  expected_contribution_data <- first_period_contribution_data + second_period_contribution_data*(1+sum(first_period_contribution_data)) + third_period_contribution_data*(1+sum(first_period_contribution_data))*(1+sum(second_period_contribution_data))
149  computed_contribution_data <- PerformanceAnalytics::Return.portfolio(multi_period_returns, multi_period_weights, verbose = TRUE)$contribution
150
151  expect_equal(first_period_contribution_data,
152               coredata(computed_contribution_data[1,colnames(first_period_contribution_data)]),
153               tolerance=epsilon,
154               info = "multi-period contributions with a periodically rebalanced portfolio are computed correctly")
155
156  expect_equal(second_period_contribution_data,
157               coredata(computed_contribution_data[2,colnames(second_period_contribution_data)]),
158               tolerance=epsilon,
159               info = "multi-period contributions with a periodically rebalanced portfolio are computed correctly")
160
161  expect_equal(third_period_contribution_data,
162               coredata(computed_contribution_data[3,colnames(third_period_contribution_data)]),
163               tolerance=epsilon,
164               info = "multi-period contributions with a periodically rebalanced portfolio are computed correctly")
165
166   # Now check the actual API for multi-period contribution
167  computed_multi_period_contribution_data <- to.period.contributions(computed_contribution_data)
168  expect_equal(coredata(expected_contribution_data),
169               coredata(computed_multi_period_contribution_data[,colnames(expected_contribution_data)]), tolerance=epsilon)
170