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