diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..ffc1b85 --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,17 @@ +Package: IndexConstruction +Type: Package +Title: Index Construction for Time Series Data +Version: 0.1-1 +Date: 2019-03-25 +Author: Simon Trimborn +Maintainer: Simon Trimborn +LazyLoad: yes +LazyData: true +Depends: R (>= 2.10) +Imports: KernSmooth, fGarch, lubridate, xts, RcppBDT, zoo +Description: Derivation of indexes for benchmarking purposes. The methodology of the CRyptocurrency IndeX (CRIX) family with flexible number of constituents is implemented. Also functions for market capitalization and volume weighted indexes with fixed number of constituents are available. The methodology behind the functions provided gets introduced in Trimborn and Haerdle (2018) . +License: GPL (>= 3) +NeedsCompilation: no +Packaged: 2019-03-25 02:08:44 UTC; matsim +Repository: CRAN +Date/Publication: 2019-03-29 16:50:03 UTC diff --git a/MD5 b/MD5 new file mode 100644 index 0000000..818efdf --- /dev/null +++ b/MD5 @@ -0,0 +1,29 @@ +21ab0e60d738da9da2f830f0fc22e476 *DESCRIPTION +d49d84e3f91092578b3969a2c87e81e9 *NAMESPACE +70aaa902c6a9eae00ad8dd72f638b2cb *R/CRIXwrappers.R +50ac1ff9cab1ca95118c4e20c49b0743 *R/IndexComp.R +d2c0a5c2616d983f0a74d05371a4dd73 *R/IndexEval.R +06d40c34574ce39df85c386684fef54c *R/IndexLoop.R +3395066b42318dc5ce9cc10236785c0c *R/IndexMemberSelection.R +89fcbf034dc1f07bc01400e652c2daa9 *R/IndexMembersUpdate.R +80434123f2a18caf5d1607ba453f4d48 *R/IndexUpdate.R +2b93d31df319888b559b67f2de351fbc *R/RelativeWeights.R +662ac8867dd97f202e83e61febae4b75 *R/SwitchDates.R +199c301086baae8c18ffc79e45d811ac *data/CryptoData.RData +7c699b51ebd02eda5b76c33225e91189 *data/datalist +b09de006b611cb84739c53cfe3771b9f *inst/CITATION +639cf1149c43a615508f02ae89c905d2 *man/CRIX.Rd +eaefd014b392e7382cdb215b24afb9b9 *man/ECRIX.Rd +bc92a6715ac0a47f8ee2e3a0ffcc83b4 *man/EFCRIX.Rd +5b32face713cbc9c7998a8e2252feb92 *man/IndexComp.Rd +6c3260f597a4091052c749b32e99680c *man/IndexMemberSelection.Rd +985c9b7c49b875444dfc5100372cc47b *man/IndexMembersUpdate.Rd +e13db512b944559c526e7289abb7ac47 *man/IndexUpdate.Rd +7f88c7c4d4fe7478296ca611572e12ca *man/LCRIX.Rd +0c7f13adbbe3c9e49ba217f59088acf0 *man/LECRIX.Rd +22380196596f2b958d7b5a6110c604ac *man/LEFCRIX.Rd +7a4b6047e2578fc5631684bf9f399653 *man/RelativeWeights.Rd +d505b7e832d2daf78593cde418a85523 *man/SwitchDates.Rd +3035265cf9fedab107be5fc912d43f0c *man/marketData.Rd +b716fe2621ad924b1574155a8d306b86 *man/priceData.Rd +2dcba450227d1ef2a5baff32f3b7b7c8 *man/volData.Rd diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..8dda311 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,17 @@ +## regular functions: + +export("IndexComp", "SwitchDates", "IndexUpdate", "RelativeWeights", "IndexMemberSelection", "IndexMembersUpdate", +"CRIX", "ECRIX", "EFCRIX", "LCRIX", "LECRIX", "LEFCRIX" +) + +# imports: + +import(lubridate) +import(xts) +import(fGarch) +import(KernSmooth) + +importFrom(stats, lm, var) +importFrom(utils, tail) +importFrom(RcppBDT, getNthDayOfWeek) +importFrom(zoo, index, na.locf) \ No newline at end of file diff --git a/R/CRIXwrappers.R b/R/CRIXwrappers.R new file mode 100644 index 0000000..ff3e078 --- /dev/null +++ b/R/CRIXwrappers.R @@ -0,0 +1,41 @@ +CRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "market", weighting.all = "market", IC = "AIC", EvalSeq = "AllTogether", optimum = "local", + start.const = 5, steps = 5, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} + +ECRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "market", weighting.all = "market", IC = "AIC", EvalSeq = "AllTogether", optimum = "local", + start.const = 1, steps = 1, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} + +EFCRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "market", weighting.all = "market", IC = "AIC", EvalSeq = "AllTogether", optimum = "global", + start.const = 1, steps = 1, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} + +LCRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "volume", weighting.all = "volume", IC = "AIC", EvalSeq = "AllTogether", optimum = "local", + start.const = 5, steps = 5, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} + +LECRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "volume", weighting.all = "volume", IC = "AIC", EvalSeq = "AllTogether", optimum = "local", + start.const = 1, steps = 1, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} + +LEFCRIX = function(market, price, vol = NULL, days.line) { + IndexComp(market = market, price = price, vol = vol, weighting = "volume", weighting.all = "volume", IC = "AIC", EvalSeq = "AllTogether", optimum = "global", + start.const = 1, steps = 1, fixed.value = NULL, base.value = 1000, + derivation.period = 1, derivation.period.ic = 3, + days.line = days.line) +} diff --git a/R/IndexComp.R b/R/IndexComp.R new file mode 100644 index 0000000..ff4c028 --- /dev/null +++ b/R/IndexComp.R @@ -0,0 +1,219 @@ +IndexComp = function(market, price, vol = NULL, weighting = "market", weighting.all = "market", IC = "AIC", EvalSeq = c("Sequential", "AllTogether"), optimum = c("local", "global"), start.const = 1, steps = 1, fixed.value = NULL, base.value = 1000, derivation.period = 1, derivation.period.ic = 3, + days.line) { + + if (class(price)[1] != "xts" | class(market)[1] != "xts") { + stop("The data for 'price' and 'market' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if (class(vol)[1] != "xts" & is.null(vol) == FALSE) { + stop("The data for 'vol' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if ((weighting != "market" & weighting != "volume") | (weighting.all != "market" & weighting.all != "volume")) { + stop("The weighting scheme has to be either 'market' or 'volume'. Please chose either of the two options.") + } + if ((EvalSeq != "Sequential" & EvalSeq != "AllTogether")) { + stop("The evaluation scheme 'EvalSeq' has to be either 'Sequential' or 'AllTogether'. Please chose either of the two options.") + } + if ((optimum != "local" & optimum != "global")) { + stop("The optimal point 'optimum' has to be either 'local' or 'global'. Please chose either of the two options.") + } + if ((IC != "AIC" & IC != "GCV" & IC != "GFCV" & IC != "SH" & IC != "Cp" & IC != "FPE")) { + stop("The Information Criterion 'IC' has to be either 'AIC', 'GCV', 'GFCV', 'SH', 'Cp' or 'FPE'. Please chose either of the options.") + } + if ((weighting == "volume" | weighting.all == "volume") & is.null(vol)) { + stop("When weighting by trading volume is chosen, a data entry for volume is required.") + } + if (length(days.line) < derivation.period.ic*2) { + stop(paste("The number of periods to select the number of constituents for is not long enough. ", derivation.period.ic*2, " month of data are required for the derivation. Please provide a longer dataset or decrease the number of month over which the number of constituents shall be derived.", sep = "")) + } + index_periods = max(floor(derivation.period.ic / derivation.period), 1) + numb_aic1 = (length(days.line) - (derivation.period)) / derivation.period.ic + if ((numb_aic1%%1 == 0) == TRUE){ + numb_aic = numb_aic1 - 1 + } else { + numb_aic = floor(numb_aic1) + } + + index_comp_numb = seq(start.const, dim(price)[[2]], steps) + aic_matrix = matrix(NA, nrow = numb_aic, ncol = length(index_comp_numb)) + aic_compare = matrix(NA, nrow = numb_aic, ncol = length(index_comp_numb)) + max_coin_numb = c() + crix = crix_all = crix_all_comp = c() + plot_diff_values = rep(list(list()), numb_aic) + cryptos_available = list() + weights_list = list() + weights_list2 = list() + + ### start calculation index + for (per in 1:numb_aic){ + print(paste(per, " / ", numb_aic, sep = "")) + + ### in case no optimization is wished for, just a fixed value index + if (is.null(fixed.value)) { + + ### days to alter the index members for choice of optimal settings + current_lines = days.line[seq((1 + derivation.period.ic * + (per - 1)), (derivation.period.ic * per + (derivation.period + 1)), + derivation.period)] + 1 + begin_line = current_lines[2] + ### total market index + index_t_v_all = index.comp(market = market, price = price, vol = vol, weighting = weighting.all, index.const = "all", base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + max_coin_numb[per] = max(sapply(index_t_v_all[[2]], length)) + + if (EvalSeq == "AllTogether") { + index_t_v_numb = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_comp_numb[1], base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + } + + ### indices with different numbers of constituents + for (per1 in 1:(length(index_comp_numb)-1)){ + if (EvalSeq == "Sequential") { + index_t_v_numb = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_comp_numb[per1], base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + } + + if (is.null(index_t_v_numb)) {break} + d = c() + p = c() + for (l in 1:length(index_t_v_numb[[2]])) { + if (l == 1) { + for (k in 1:length(index_t_v_numb[[2]])) { + if (EvalSeq == "Sequential") { + p[k] = length(which(!is.na(index_t_v_numb[[2]][[k]][(index_comp_numb[per1] + 1):index_comp_numb[per1 + 1]]) == TRUE)) + } else if (EvalSeq == "AllTogether") { + p[k] = length(which(!is.na(index_t_v_numb[[2]][[k]][(index_comp_numb[1] + 1):index_comp_numb[per1 + 1]]) == TRUE)) + } + } + } + if (EvalSeq == "AllTogether" && any(p != per1*steps)) {break} + if (EvalSeq == "Sequential") { + a = price[paste(current_lines[l+1]-1, "::", current_lines[l+2] - 1, sep = ""), index_t_v_numb[[2]][[l]][(index_comp_numb[per1] + 1):index_comp_numb[per1 + 1]]] + } else if (EvalSeq == "AllTogether") { + a = price[paste(current_lines[l+1]-1, "::", current_lines[l+2] - 1, sep = ""), index_t_v_numb[[2]][[l]][(index_comp_numb[1] + 1):index_comp_numb[per1 + 1]]] + } + + a[a == 0] = NA + a = na.locf(a, na.rm = FALSE) + a = na.locf(a, fromLast = TRUE) + b = diff(log(a)) + # this part is only relevant for sequential + if (dim(b)[[2]] < max(p)) { + while(dim(b)[[2]] < max(p)) { + b = cbind(b, 0) + } + } + # + colnames(b) = 1:dim(b)[[2]] + d = rbind(d,b[-1,]) + } + if (EvalSeq == "AllTogether" && any(p != per1*steps)) {break} + if (per1 == 1) { + plot_diff_first = diff(log(c(base.value, index_t_v_all[[1]]))) - diff(log(c(base.value, index_t_v_numb[[1]]))) + } + plot_diff = diff(log(c(base.value, index_t_v_all[[1]]))) - diff(log(c(base.value, index_t_v_numb[[1]]))) + data_x = d + erg = lm(plot_diff ~ data_x - 1) + plot_diff_values[[per]][[per1]] = plot_diff + + ### evaluation of current index with an IC method + aic_compare[per, per1] = IndexEval(plot.diff = erg$residuals, index.numb = length(erg$coefficients), IC = IC, plot.diff.first = plot_diff_first) + aic_matrix[per, per1] = IndexEval(plot.diff = plot_diff, index.numb = 0, IC = IC, plot.diff.first = plot_diff_first) + + if (optimum == "local" && EvalSeq == "Sequential") { + # if (per1 >= 2){ + if (aic_matrix[per, per1] <= aic_compare[per, per1]){ + break + } + # } + } else if (optimum == "local" && EvalSeq == "AllTogether") { + if (per1 >= 2){ + if (aic_compare[per, per1-1] <= aic_compare[per, per1]){ + break + } + } else if (per1 == 1) { + if (aic_matrix[per, per1] <= aic_compare[per, per1]){ + break + } + } + } + } + + ### choice of optimal index members + if (optimum == "local" && EvalSeq == "Sequential") { + if (per1 > 1) { + index_members = index_comp_numb[per1 - 1] + } else if (per1 == 1) { + index_members = index_comp_numb[per1] + } + } else if (optimum == "local" && EvalSeq == "AllTogether") { + index_members = index_comp_numb[per1] + } else if (optimum == "global" && EvalSeq == "Sequential") { # works for Sequential and AllTogether + index_members = which.min(aic_compare[per,]) * steps + } else if (optimum == "global" && EvalSeq == "AllTogether") { # works for Sequential and AllTogether + index_members = which.min(c(aic_matrix[per, 1], aic_compare[per,])) * steps + } + + ### end if which controls fixed_value = NULL + } else { + index_members = fixed.value + } + + ### days to alter the index members for application of optimal settings + current_lines1 = days.line[seq((1 + derivation.period.ic * (per)), + (derivation.period.ic * (per + 1) + (derivation.period + 1)), + derivation.period)] + 1 + begin_line1 = current_lines1[2] + ### calculation of index under the chosen settings, means the chosen number of constituents + crix1 = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_members, base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines1, + begin.line.func = begin_line1, comp1 = TRUE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + + while (is.null(crix1)){ + index_members = index_members - 1 + crix1 = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_members, base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines1, + begin.line.func = begin_line1, comp1 = TRUE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + } + crix2 = crix1[[1]] + weights_list[[per]] = crix1[[4]] + weights_list2[[per]] = crix1[[5]] + ### total market index rebased at the total market index + crix_all2 = index.comp(market = market, price = price, vol = vol, weighting = weighting.all, index.const = "all", base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines1, + begin.line.func = begin_line1, comp1 = TRUE, comp = FALSE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + cryptos_available[[per]] = crix_all2[[2]] + + crix_all1 = crix_all2[[1]] + ### total market index rebased at the index + crix_all4 = index.comp(market = market, price = price, vol = vol, weighting = weighting.all, index.const = "all", base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines1, + begin.line.func = begin_line1, comp1 = TRUE, comp = TRUE, per = per, numb.aic = numb_aic, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + crix_all3 = crix_all4[[1]] + if (per == 1){ + crix = do.call(cbind,list(crix2)) + crix_all = do.call(cbind,list(crix_all1)) + crix_all_comp = do.call(cbind,list(crix_all3)) + } else { + crix = rbind(crix, do.call(cbind, list(crix2))) + crix_all = rbind(crix_all, do.call(cbind, list(crix_all1))) + crix_all_comp = rbind(crix_all_comp, do.call(cbind, list(crix_all3))) + } + } + + ### output + list(crix, crix_all, crix_all_comp, cryptos_available, weights_list) +} + + + + diff --git a/R/IndexEval.R b/R/IndexEval.R new file mode 100644 index 0000000..d8ea8b1 --- /dev/null +++ b/R/IndexEval.R @@ -0,0 +1,51 @@ +IndexEval = function(plot.diff, index.numb, IC = "AIC", plot.diff.first) { + switch(IC, AIC = { + logLik1 = c() + bw = dpik(plot.diff.first, kernel = "epanech", gridsize = 3201L) + for (cv1 in 1:length(plot.diff)){ + eva = function(x) { + eva_dat = (x - plot.diff.first)/bw + erg_dat = c() + for (i in 1:length(eva_dat)) { + if (abs(eva_dat[i]) <= sqrt(5)) { + erg_dat[i] = 3/(4*sqrt(5)) * (1 - (eva_dat[i]^2/5)) + } else { + erg_dat[i] = 1e-20 # computational 0, used to circumvent the problem of infinity after tacking log + } + } + erg_d = sum(erg_dat)/(bw*length(plot.diff.first)) + } + eva1 = eva(plot.diff[cv1]) + logLik1[cv1] = log(eva1) + } + + logLik = sum(logLik1) + aic = 2 * index.numb - 2 * logLik + return(aic) + }, + GCV = { + gcv = mean(plot.diff^2) / ((1 - index.numb/length(plot.diff))^2) + return(gcv) + }, + GFCV = { + gfcv = mean(plot.diff^2) * ((1 + index.numb/length(plot.diff))^2) + return(gfcv) + }, + SH = { + sh = ((length(plot.diff) + 2*index.numb) / (length(plot.diff)^2)) * sum(plot.diff^2) + return(sh) + }, + Cp = { + cp_var = try(sum(garchFit(data = plot.diff)@h.t), silent = TRUE) + if (class(cp_var) == "try-error"){ + cp_var = var(plot.diff) + } + cp = (sum(plot.diff^2)/cp_var) - length(plot.diff) + 2 * index.numb + return(cp) + }, + FPE = { + fpe = ((length(plot.diff) + index.numb) / (length(plot.diff) + index.numb)) * mean(plot.diff^2) + return(fpe) + } + ) # close switch +} \ No newline at end of file diff --git a/R/IndexLoop.R b/R/IndexLoop.R new file mode 100644 index 0000000..8687db4 --- /dev/null +++ b/R/IndexLoop.R @@ -0,0 +1,193 @@ +index.comp = function(market, price, vol, weighting, index.const, base.value, index.periods, order.derive, + current.lines.func, begin.line.func, comp, comp1, per, numb.aic, crix, crix.all, crix.all.comp){ + + index = list() + index_members_m = list() + index_members_t = list() + order_market_list = list() + weight_list = list() + weight_list2 = list() + divisor_list = list() + + if (per == 1){ + index_value = base.value + } else if (comp1 == T) { + if (comp == T){ + index_value = tail(crix[, 1], n = 1)[[1]] + } else { + index_value = tail(crix.all[, 1], n = 1)[[1]] + } + } else if (comp1 == F){ + index_value = base.value + } + ########################################################## + ############## for loop for index computation ############ + ########################################################## + for (i in 1:(index.periods)){ + if (i == 1){ + last_index_period_time = price[paste(current.lines.func[1],(current.lines.func[2] - 1), sep="::")] + last_index_period_market = market[paste(current.lines.func[1],(current.lines.func[2] - 1), sep="::")] + last_index_period_vol = vol[paste(current.lines.func[1],(current.lines.func[2] - 1), sep="::")] + if (is.null(vol) == TRUE | weighting == "market") { + omit_last = Reduce(intersect, list(which(apply(is.na( + last_index_period_time), 2, any) + == F), which(apply(is.na(last_index_period_market), 2, any) == F))) + } else { + omit_last = Reduce(intersect, list(which(apply(is.na( + last_index_period_time), 2, any) + == F), which(apply(is.na(last_index_period_market), 2, any) == F), + which(apply(is.na(last_index_period_vol), 2, any) == F))) + } + + last_index_period_time = last_index_period_time[, omit_last] + last_index_period_market = last_index_period_market[, omit_last] + last_index_period_vol = last_index_period_vol[, omit_last] + if (is.null(vol) == TRUE | weighting == "market") { + omit_last_zero = Reduce(intersect, list(which(apply( + last_index_period_time == 0, 2, any) == F), which(apply( + last_index_period_market == 0, 2, any) == F))) + } else { + omit_last_zero = Reduce(intersect, list(which(apply( + last_index_period_time == 0, 2, any) == F), which(apply( + last_index_period_market == 0, 2, any) == F), which(apply( + last_index_period_vol == 0, 2, any) == F))) + } + + last_index_period_time = last_index_period_time[, omit_last_zero] + last_index_period_market = last_index_period_market[, omit_last_zero] + last_index_period_vol = last_index_period_vol[, omit_last_zero] + } else { + if (is.null(vol) == TRUE | weighting == "market") { + omit_now = Reduce(intersect, list(which(apply(is.na( + index_period_time), 2, any) + == F), which(apply(is.na(index_period_market), 2, any) == F))) + } else { + omit_now = Reduce(intersect, list(which(apply(is.na( + index_period_time), 2, any) + == F), which(apply(is.na(index_period_market), 2, any) == F), + which(apply(is.na(index_period_vol), 2, any) == F))) + } + + last_index_period_time = index_period_time[, omit_now] + last_index_period_market = index_period_market[, omit_now] + last_index_period_vol = index_period_vol[, omit_now] + + if (is.null(vol) == TRUE | weighting == "market") { + omit_now_zero = Reduce(intersect, list(which(apply( + index_period_time == 0, 2, any) == F), which(apply( + index_period_market == 0, 2, any) == F))) + } else { + omit_now_zero = Reduce(intersect, list(which(apply( + index_period_time == 0, 2, any) == F), which(apply( + index_period_market == 0, 2, any) == F), which(apply( + index_period_vol == 0, 2, any) == F))) + } + + last_index_period_time = index_period_time[, omit_now_zero] + last_index_period_market = index_period_market[, omit_now_zero] + last_index_period_vol = index_period_vol[, omit_now_zero] + } + + if (dim(last_index_period_market)[2] <= index.const & index.const != "all"){ + return(NULL) + } + + last_line = if (i == 1){ + begin.line.func + } else { + next_line + } + break_loop = FALSE + if (per == numb.aic & is.na(current.lines.func[i + (2)]) == T) { + next_line = tail(index(price),n=1) + 1 + break_loop = TRUE + } else { + next_line = current.lines.func[i + 2] + } + last_index_time = price[(last_line - 1)] + last_index_market = market[(last_line - 1)] + + if (weighting == "volume") { + last_index_vol = vol[(last_line - 1)] + order_market = order(last_index_vol[,colnames(last_index_period_vol)], + decreasing = T) + } else if (weighting == "market") { + order_market = order(last_index_market[,colnames(last_index_period_market)], + decreasing = T) + } + + if (order.derive == T){ + order_market_list = append(order_market_list, list(colnames( + last_index_period_time[, order_market]))) + } + + if (length(order_market) >= index.const){ + order_market = order_market[1:index.const] + } + + index_members_m[[i]] = colnames(last_index_period_market)[order_market] + index_members_t[[i]] = colnames(last_index_period_time)[order_market] + last_index_period_time = last_index_period_time[, order_market] + last_index_period_market = last_index_period_market[, order_market] + last_index_period_vol = last_index_period_vol[, order_market] + + if (weighting == "volume") { + index_weights = colSums(last_index_period_vol) / colSums(last_index_period_market) + } else if (weighting == "market") { + index_weights = rep(1, length(order_market)) + } + + if (is.null(dim(last_index_period_market)) == T) { + if (i == 1){ + divisor = sum(index_weights * tail(last_index_period_market, n = 1)) / index_value + } else if (i > 1){ + divisor = sum(index_weights * last_index_market[,index_members_m[[i]]]) / + index[[i - 1]][length(index[[i - 1]])] + } + } else { + if (i == 1){ + divisor = sum(index_weights * tail(last_index_period_market[, index_members_m[[i]]], + n = 1)) / index_value + } else if (i > 1){ + divisor = sum(index_weights * last_index_market[,index_members_m[[i]]]) / + index[[i - 1]][length(index[[i - 1]])] + } + } + + index_period_time = price[paste(last_line,(next_line - 1),sep="::")] + index_period_market = market[paste(last_line,(next_line - 1),sep="::")] + index_period_vol = vol[paste(last_line,(next_line - 1),sep="::")] + + index_period_time1 = na.locf(price)[paste(last_line,(next_line - 1),sep="::")] + + index_old_amount = (last_index_market)[,index_members_m[[i]]] / + (last_index_time)[,index_members_t[[i]]] + if (is.null(dim(index_period_time1))) { + index_comp1 = t(t(index_period_time1[index_members_t[[i]]]) * (index_weights * as.vector(index_old_amount))) + index[[i]] = sum(index_comp1) + } else { + index_comp1 = t(t(index_period_time1[, index_members_t[[i]]]) * (index_weights * as.vector(index_old_amount))) + index[[i]] = apply(index_comp1, 1, sum) + } + index[[i]] = index[[i]] / divisor + + weight_list[[i]] = (index_weights * as.vector(index_old_amount)) + weight_list2[[i]] = t(t(index_period_time1[, index_members_t[[i]]]) * (index_weights * as.vector(index_old_amount))) + divisor_list[[i]] = divisor + + if (break_loop == T){ + break + } + } # end for loop + + ################### Index derivation done + # building whole index time series + plot_index = c() + for (i in 1:length(index)){ + plot_index = c(plot_index, index[[i]]) + } + + list(plot_index, order_market_list, index_weights, weight_list, weight_list2, divisor_list) +} + +# end function diff --git a/R/IndexMemberSelection.R b/R/IndexMemberSelection.R new file mode 100644 index 0000000..c7551f7 --- /dev/null +++ b/R/IndexMemberSelection.R @@ -0,0 +1,147 @@ +IndexMemberSelection = function(market, price, vol, weighting = "market", weighting.all = "market", IC = "AIC", + EvalSeq = c("Sequential", "AllTogether"), optimum = c("local", "global"), start.const = 1, steps = 1, + fixed.value = NULL, derivation.period = 1, derivation.period.ic = 3, base.value = 1000, + days.line) { + + if (class(price)[1] != "xts" | class(market)[1] != "xts") { + stop("The data for 'price' and 'market' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if (class(vol)[1] != "xts" & is.null(vol) == FALSE) { + stop("The data for 'vol' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if ((weighting != "market" & weighting != "volume") | (weighting.all != "market" & weighting.all != "volume")) { + stop("The weighting scheme has to be either 'market' or 'volume'. Please chose either of the two options.") + } + if ((EvalSeq != "Sequential" & EvalSeq != "AllTogether")) { + stop("The evaluation scheme 'EvalSeq' has to be either 'Sequential' or 'AllTogether'. Please chose either of the two options.") + } + if ((optimum != "local" & optimum != "global")) { + stop("The optimal point 'optimum' has to be either 'local' or 'global'. Please chose either of the two options.") + } + if ((IC != "AIC" & IC != "GCV" & IC != "GFCV" & IC != "SH" & IC != "Cp" & IC != "FPE")) { + stop("The Information Criterion 'IC' has to be either 'AIC', 'GCV', 'GFCV', 'SH', 'Cp' or 'FPE'. Please chose either of the options.") + } + if ((weighting == "volume" | weighting.all == "volume") & is.null(vol)) { + stop("When weighting by trading volume is chosen, a data entry for volume is required.") + } + if (length(days.line) < derivation.period.ic*2) { + stop(paste("The number of periods to select the number of constituents for is not long enough. ", derivation.period.ic*2, " month of data are required for the derivation. Please provide a longer dataset or decrease the number of month over which the number of constituents shall be derived.", sep = "")) + } + if (length(days.line) > derivation.period.ic*2) { + warning(paste("The number of periods to select the number of constituents for is larger than ", derivation.period.ic*2, " month. Is this intended?", sep = "")) + } + index_periods = max(floor(derivation.period.ic / derivation.period), 1) + + index_comp_numb = seq(start.const, dim(price)[[2]], steps) + aic_matrix = matrix(NA, nrow = 1, ncol = length(index_comp_numb)) + aic_compare = matrix(NA, nrow = 1, ncol = length(index_comp_numb)) + max_coin_numb = c() + crix = crix_all = crix_all_comp = c() + + current_lines = days.line[seq(1, (derivation.period.ic + (derivation.period + 1)), + derivation.period)] + 1 + begin_line = current_lines[2] + + ### total market index + index_t_v_all = index.comp(market = market, price = price, vol = vol, weighting = weighting.all, index.const = "all", base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = 1, numb.aic = 1, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + max_coin_numb[1] = max(sapply(index_t_v_all[[2]], length)) + + if (EvalSeq == "AllTogether") { + index_t_v_numb = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_comp_numb[1], base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = 1, numb.aic = 1, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + } + + ### indices with different numbers of constituents + for (per1 in 1:(length(index_comp_numb)-1)){ + if (EvalSeq == "Sequential") { + index_t_v_numb = index.comp(market = market, price = price, vol = vol, weighting = weighting, index.const = index_comp_numb[per1], base.value = base.value, + index.periods = index_periods, order.derive = TRUE, current.lines.func = current_lines, + begin.line.func = begin_line, comp1 = FALSE, comp = TRUE, per = 1, numb.aic = 1, + crix = crix, crix.all = crix_all, crix.all.comp = crix_all_comp) + } + + if (is.null(index_t_v_numb)) {break} + d = c() + p = c() + for (l in 1:length(index_t_v_numb[[2]])) { + if (l == 1) { + for (k in 1:length(index_t_v_numb[[2]])) { + if (EvalSeq == "Sequential") { + p[k] = length(which(!is.na(index_t_v_numb[[2]][[k]][(index_comp_numb[per1] + 1):index_comp_numb[per1 + 1]]) == TRUE)) + } else if (EvalSeq == "AllTogether") { + p[k] = length(which(!is.na(index_t_v_numb[[2]][[k]][(index_comp_numb[1] + 1):index_comp_numb[per1 + 1]]) == TRUE)) + } + } + } + if (EvalSeq == "AllTogether" && any(p != per1*steps)) {break} + if (EvalSeq == "Sequential") { + a = price[paste(current_lines[l+1]-1, "::", current_lines[l+2] - 1, sep = ""), index_t_v_numb[[2]][[l]][(index_comp_numb[per1] + 1):index_comp_numb[per1 + 1]]] + } else if (EvalSeq == "AllTogether") { + a = price[paste(current_lines[l+1]-1, "::", current_lines[l+2] - 1, sep = ""), index_t_v_numb[[2]][[l]][(index_comp_numb[1] + 1):index_comp_numb[per1 + 1]]] + } + + a[a == 0] = NA + a = na.locf(a, na.rm = FALSE) + a = na.locf(a, fromLast = TRUE) + b = diff(log(a)) + # this part is only relevant for sequential + if (dim(b)[[2]] < max(p)) { + while(dim(b)[[2]] < max(p)) { + b = cbind(b, 0) + } + } + # + colnames(b) = 1:dim(b)[[2]] + d = rbind(d,b[-1,]) + } + if (EvalSeq == "AllTogether" && any(p != per1*steps)) {break} + if (per1 == 1) { + plot_diff_first = diff(log(c(base.value, index_t_v_all[[1]]))) - diff(log(c(base.value, index_t_v_numb[[1]]))) + } + plot_diff = diff(log(c(base.value, index_t_v_all[[1]]))) - diff(log(c(base.value, index_t_v_numb[[1]]))) + data_x = d + erg = lm(plot_diff ~ data_x - 1) + + ### evaluation of current index with an IC method + aic_compare[1, per1] = IndexEval(plot.diff = erg$residuals, index.numb = length(erg$coefficients), IC = IC, plot.diff.first = plot_diff_first) + aic_matrix[1, per1] = IndexEval(plot.diff = plot_diff, index.numb = 0, IC = IC, plot.diff.first = plot_diff_first) + + if (optimum == "local" && EvalSeq == "Sequential") { + if (aic_matrix[1, per1] <= aic_compare[1, per1]){ + break + } + } else if (optimum == "local" && EvalSeq == "AllTogether") { + if (per1 >= 2){ + if (aic_compare[1, per1-1] <= aic_compare[1, per1]){ + break + } + } else if (per1 == 1) { + if (aic_matrix[1, per1] <= aic_compare[1, per1]){ + break + } + } + } + } + + ### choice of optimal index members + if (optimum == "local" && EvalSeq == "Sequential") { + if (per1 > 1) { + index_members = index_comp_numb[per1 - 1] + } else if (per1 == 1) { + index_members = index_comp_numb[per1] + } + } else if (optimum == "local" && EvalSeq == "AllTogether") { + index_members = index_comp_numb[per1] + } else if (optimum == "global" && EvalSeq == "Sequential") { # works for Sequential and AllTogether + index_members = which.min(aic_compare[1,]) * steps + } else if (optimum == "global" && EvalSeq == "AllTogether") { # works for Sequential and AllTogether + index_members = which.min(c(aic_matrix[1, 1], aic_compare[1,])) * steps + } + + return(index_members) +} diff --git a/R/IndexMembersUpdate.R b/R/IndexMembersUpdate.R new file mode 100644 index 0000000..35b4ab9 --- /dev/null +++ b/R/IndexMembersUpdate.R @@ -0,0 +1,93 @@ +IndexMembersUpdate = function(market, price, vol, weighting, index.const, last.value){ + + if (class(price)[1] != "xts" | class(market)[1] != "xts") { + stop("The data for 'price' and 'market' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if (class(vol)[1] != "xts" & is.null(vol) == FALSE) { + stop("The data for 'vol' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if ((weighting != "market" & weighting != "volume")) { + stop("The weighting scheme has to be either 'market' or 'volume'. Please chose either of the two options.") + } + if ((weighting == "volume") & is.null(vol)) { + stop("When weighting by trading volume is chosen, a data entry for volume is required.") + } + order_market_list = list() + + index_value = last.value + ########################################################## + ################ index computation ####################### + ########################################################## + last_index_period_time = price + last_index_period_market = market + last_index_period_vol = vol + omit_last = Reduce(intersect, list(which(apply(is.na( + last_index_period_time), 2, any) + == F), which(apply(is.na(last_index_period_market), 2, any) == F), + which(apply(is.na(last_index_period_vol), 2, any) == F))) + last_index_period_time = last_index_period_time[, omit_last] + last_index_period_market = last_index_period_market[, omit_last] + last_index_period_vol = last_index_period_vol[, omit_last] + omit_last_zero = Reduce(intersect, list(which(apply( + last_index_period_time == 0, 2, any) == F), which(apply( + last_index_period_market == 0, 2, any) == F), which(apply( + last_index_period_vol == 0, 2, any) == F))) + last_index_period_time = last_index_period_time[, omit_last_zero] + last_index_period_market = last_index_period_market[, omit_last_zero] + last_index_period_vol = last_index_period_vol[, omit_last_zero] + + + if (dim(last_index_period_market)[2] <= index.const & index.const != "all"){ + return(NULL) + } + + last_line = tail(index(price), n = 1) + + last_index_time = price[last_line] + last_index_market = market[last_line] + last_index_vol = vol[last_line] + + if (weighting == "volume") { + order_market = order(last_index_vol[,colnames(last_index_period_vol)], + decreasing = T) + } else if (weighting == "market") { + order_market = order(last_index_market[,colnames(last_index_period_market)], + decreasing = T) + } + + order_market_list = append(order_market_list, list(colnames( + last_index_period_time[, order_market]))) + + if (length(order_market) >= index.const){ + order_market = order_market[1:index.const] + } + + index_members_m = colnames(last_index_period_market)[order_market] + index_members_t = colnames(last_index_period_time)[order_market] + last_index_period_time = last_index_period_time[, order_market] + last_index_period_market = last_index_period_market[, order_market] + last_index_period_vol = last_index_period_vol[, order_market] + + if (weighting == "volume") { + index_weights = colSums(last_index_period_vol) / colSums(last_index_period_market) + } else if (weighting == "market") { + index_weights = rep(1, length(order_market)) + } + + if (is.null(dim(last_index_period_market)) == T) { + divisor = sum(index_weights * tail(last_index_period_market, n = 1)) / index_value + } else { + divisor = sum(index_weights * tail(last_index_period_market[, index_members_m], + n = 1)) / index_value + } + + + index_old_amount = (last_index_market)[,index_members_m] / + (last_index_time)[,index_members_t] + + weight = (index_weights * as.vector(index_old_amount)) + + list(index_members_m, index_weights, weight, divisor) +} + +# end function diff --git a/R/IndexUpdate.R b/R/IndexUpdate.R new file mode 100644 index 0000000..ebe93f2 --- /dev/null +++ b/R/IndexUpdate.R @@ -0,0 +1,4 @@ +IndexUpdate = function(price, index.weights, divisor) { + index_value = sum(price * index.weights) / divisor + return(index_value) +} \ No newline at end of file diff --git a/R/RelativeWeights.R b/R/RelativeWeights.R new file mode 100644 index 0000000..2b761d3 --- /dev/null +++ b/R/RelativeWeights.R @@ -0,0 +1,4 @@ +RelativeWeights = function(price, index.weights) { + relative_weights = (price * index.weights) / sum(price * index.weights) + return(relative_weights) +} \ No newline at end of file diff --git a/R/SwitchDates.R b/R/SwitchDates.R new file mode 100644 index 0000000..9695f5b --- /dev/null +++ b/R/SwitchDates.R @@ -0,0 +1,24 @@ +SwitchDates = function(price, specificDate = NULL, WeekDay = NULL, Appearance = 1) { + if (class(price)[1] != "xts") { + stop("The data for 'price' have to be in the format 'xts'. Please check the R library 'xts' to convert the data.") + } + if (is.null(specificDate) & is.null(WeekDay)) { + stop("Please chose either a 'specificDate' to search for or a 'WeekDay'.") + } + language = Sys.getenv("LANG") + Sys.setenv(LANG = "en") + all_days = index(price) + if (!is.null(specificDate)) { + days_line = which(specificDate == day(all_days)) - 1 + dates = all_days[days_line] + } else if (!is.null(WeekDay)) { + MonthYear = cbind(month(all_days), year(all_days)) + SelectDay = c() + for (i in 1:dim(MonthYear)[1]) { + SelectDay[i] = getNthDayOfWeek(Appearance, WeekDay, MonthYear[i,1], MonthYear[i,2]) + } + dates = as.Date(unique(SelectDay), origin = "1970-01-01") + } + Sys.setenv(LANG = language) + return(dates) +} diff --git a/data/CryptoData.RData b/data/CryptoData.RData new file mode 100644 index 0000000..4b1f706 Binary files /dev/null and b/data/CryptoData.RData differ diff --git a/data/datalist b/data/datalist new file mode 100644 index 0000000..caa9b7e --- /dev/null +++ b/data/datalist @@ -0,0 +1 @@ +CryptoData: market price vol diff --git a/inst/CITATION b/inst/CITATION new file mode 100644 index 0000000..c2651ad --- /dev/null +++ b/inst/CITATION @@ -0,0 +1,24 @@ +bibentry(bibtype = "Article", + title = "CRIX an Index for cryptocurrencies", + author = c(person("Simon", "Trimborn"), + person("Wolfgang Karl", "Haerdle")), + journal = "Journal of Empirical Finance", + year = "2018", + volume = "49", + pages = "107 - 122", + issn = "0927-5398", + doi = "10.1016/j.jempfin.2018.08.004", + url = "http://www.sciencedirect.com/science/article/pii/S0927539818300616", + header = "The R-package is based on this publication. Please reference to it, when using this software:" + ) + + +year <- sub("-.*", "", meta$Date) +note <- sprintf("R package version %s", meta$Version) + +bibentry(bibtype = "Manual", + title = "Index construction for time series data", + author = c(person("Simon", "Trimborn")), + year = year, + note = note, + header = "Building R-packages and therefore easing the access to research output for other researchers involves a lot of work. Please also reference to this version of the R-package when using it for your work:") \ No newline at end of file diff --git a/man/CRIX.Rd b/man/CRIX.Rd new file mode 100644 index 0000000..8bf4f2f --- /dev/null +++ b/man/CRIX.Rd @@ -0,0 +1,43 @@ +\name{CRIX} +\alias{CRIX} + +\title{Derivation of the CRIX index +} +\description{ +\code{CRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for CRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +CRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2015-01-31"] +vol = vol["2014-03-31::2015-01-31"] +days.line = SwitchDates(price, specificDate = "1") + +CRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/ECRIX.Rd b/man/ECRIX.Rd new file mode 100644 index 0000000..67e7b1b --- /dev/null +++ b/man/ECRIX.Rd @@ -0,0 +1,43 @@ +\name{ECRIX} +\alias{ECRIX} + +\title{Derivation of the ECRIX index +} +\description{ +\code{ECRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for ECRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +ECRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2015-01-31"] +vol = vol["2014-03-31::2015-01-31"] +days.line = SwitchDates(price, specificDate = "1") + +ECRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/EFCRIX.Rd b/man/EFCRIX.Rd new file mode 100644 index 0000000..c5ff0e4 --- /dev/null +++ b/man/EFCRIX.Rd @@ -0,0 +1,43 @@ +\name{EFCRIX} +\alias{EFCRIX} + +\title{Derivation of the EFCRIX index +} +\description{ +\code{EFCRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for EFCRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +EFCRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2014-10-31"] +vol = vol["2014-03-31::2014-10-31"] +days.line = SwitchDates(price, specificDate = "1") + +EFCRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/IndexComp.Rd b/man/IndexComp.Rd new file mode 100644 index 0000000..8afb2b6 --- /dev/null +++ b/man/IndexComp.Rd @@ -0,0 +1,81 @@ +\name{IndexComp} +\alias{IndexComp} + +\title{Index derivation for price and liquidity indices +} +\description{ +\code{IndexComp} derives an Index from the given price and market capitalization or liquidity data. The number of constituents can be fixed or being chosen based on the methodology from Trimborn and Haerdle (2018). +} +\usage{ +IndexComp(market, price, vol = NULL, weighting = "market", weighting.all = "market", +IC = "AIC", EvalSeq = c("Sequential", "AllTogether"), +optimum = c("local", "global"), start.const = 1, steps = 1, fixed.value = NULL, +base.value = 1000, derivation.period = 1, derivation.period.ic = 3, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} + \item{weighting}{ +The weighting scheme to be applied. \code{"market"} refers to weighting by market capitalization, \code{"volume"} refers to weighting by trading volume. +} +\item{weighting.all}{ +The weighting scheme to be applied to the full market index. \code{"market"} refers to weighting by market capitalization, \code{"volume"} refers to weighting by trading volume.} +\item{IC}{ +Information Criterion to be used for the evaluation of the appropriate index to be used. Possible entries are \code{"AIC"}, \code{"GCV"}, \code{"GFCV"}, \code{"SH"}, \code{"Cp"} and \code{"FPE"}. +} +\item{EvalSeq}{ +Indicates how the evaluation of the candidate indices by the IC shall be performed. \code{"AllTogether"} evaluates all indices against each other, \code{"Sequential"} evaluates always two consecutive indices against each other. +} +\item{optimum}{ +Define how to choose the optimal index. Either a \code{"local"} optimum is chosen, thus the derivation stops the first time the results become worse under the chosen IC, or a \code{"global"} optimum is chosen, thus all indices are derived and the best fitting one under the IC is chosen. +} +\item{start.const}{ +The number of constituents to start constructing the indices with. The default is \code{1}. +} +\item{steps}{ +The step width for the number of constituents to construct the next index from. The default is \code{1}. +} +\item{fixed.value}{ +In case no IC for the number of constituents for the index shall be applied, give the number of constituents the index shall contain. In that case, \code{"IC"}, \code{"EvalSeq"}, \code{"optimum"}, \code{"start.const"} and \code{"steps"} are inactive parameters. The default is \code{NULL}. +} +\item{base.value}{ +The starting value for the index. The default is \code{1000}. +} +\item{derivation.period}{ +The number of month after which the weights of the index are reallocated. The default is \code{1}. +} +\item{derivation.period.ic}{ +The number of month after which the composition of the index is derived again, thus the number of constituents is reevaluated. The default is \code{3}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2015-01-31"] +market = market["2014-03-31::2015-01-31"] +vol = vol["2014-03-31::2015-01-31"] +days.line = SwitchDates(price, specificDate = "1") + +IndexComp(market = market, price = price, vol = vol, weighting = "market", +weighting.all = "market", IC = "AIC", EvalSeq = "Sequential", optimum = "local", +start.const = 5, steps = 5, days.line = days.line) +} \ No newline at end of file diff --git a/man/IndexMemberSelection.Rd b/man/IndexMemberSelection.Rd new file mode 100644 index 0000000..077e774 --- /dev/null +++ b/man/IndexMemberSelection.Rd @@ -0,0 +1,82 @@ +\name{IndexMemberSelection} +\alias{IndexMemberSelection} + +\title{Number of Index Members Derivation +} +\description{ +\code{IndexMemberSelection} derives the number of index members for the coming period based on an Information Criterion, e.g. AIC. The methodology is according to Trimborn and Haerdle (2018). The method derives the new weights according to the specifications of the weight reevaluation. The function expects the data period provided to be twice the number of months specified in derivation.period.ic. In case of a mismatch, a warning is given. +} +\usage{ +IndexMemberSelection(market, price, vol, weighting = "market", +weighting.all = "market", IC = "AIC", EvalSeq = c("Sequential", "AllTogether"), +optimum = c("local", "global"), start.const = 1, steps = 1, fixed.value = NULL, +derivation.period = 1, derivation.period.ic = 3, base.value = 1000, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"volume"}. +} + \item{weighting}{ +The weighting scheme to be applied. \code{"market"} refers to weighting by market capitalization, \code{"volume"} refers to weighting by trading volume. +} +\item{weighting.all}{ +The weighting scheme to be applied to the full market index. \code{"market"} refers to weighting by market capitalization, \code{"volume"} refers to weighting by trading volume.} +\item{IC}{ +Information Criterion to be used for the evaluation of the appropriate index to be used. Possible entries are \code{"AIC"}, \code{"GCV"}, \code{"GFCV"}, \code{"SH"}, \code{"Cp"} and \code{"FPE"}. +} +\item{EvalSeq}{ +Indicates how the evaluation of the candidate indices by the IC shall be performed. \code{"AllTogether"} evaluates all indices against each other, \code{"Sequential"} evaluates always two consecutive indices against each other. +} +\item{optimum}{ +Define how to choose the optimal index. Either a \code{"local"} optimum is chosen, thus the derivation stops the first time the results become worse under the chosen IC, or a \code{"global"} optimum is chosen, thus all indices are derived and the best fitting one under the IC is chosen. +} +\item{start.const}{ +The number of constituents to start constructing the indices with. The default is \code{1}. +} +\item{steps}{ +The step width for the number of constituents to construct the next index from. The default is \code{1}. +} +\item{fixed.value}{ +In case no IC for the number of constituents for the index shall be applied, give the number of constituents the index shall contain. In that case, \code{"IC"}, \code{"EvalSeq"}, \code{"optimum"}, \code{"start.const"} and \code{"steps"} are inactive parameters. The default is \code{NULL}. +} +\item{base.value}{ +The starting value for the index. The default is \code{1000}. +} +\item{derivation.period}{ +The number of month after which the weights of the index are reallocated. The default is \code{1}. +} +\item{derivation.period.ic}{ +The number of month after which the composition of the index is derived again, thus the number of constituents is reevaluated. The default is \code{3}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +\code{IndexMemberSelection} derives the number of index members for the coming period based on an Information Criterion, e.g. AIC. The methodology is according to Trimborn and Haerdle (2018). The method derives the new weights according to the specifications of the weight reevaluation. The function expects the data period provided to be twice the number of months specified in derivation.period.ic. In case of a mismatch, a warning is given. The data from the first period are used to derived the likelihood, the second period is used for out-of-sample derivation of the number of constituents. Hence for a 3 month reevaluation period, 6 month of data are required by this function. For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +Returns the number of index members for application in the next period. +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2016-07-31::2017-01-31"] +market = market["2016-07-31::2017-01-31"] +vol = vol["2016-07-31::2017-01-31"] +days.line = SwitchDates(price, specificDate = "1") + +IndexMemberSelection(market = market, price = price, vol = vol, +weighting = "market", weighting.all = "market", IC = "AIC", EvalSeq = "Sequential", +optimum = "local", start.const = 5, steps = 5, days.line = days.line) + +} \ No newline at end of file diff --git a/man/IndexMembersUpdate.Rd b/man/IndexMembersUpdate.Rd new file mode 100644 index 0000000..0f31164 --- /dev/null +++ b/man/IndexMembersUpdate.Rd @@ -0,0 +1,48 @@ +\name{IndexMembersUpdate} +\alias{IndexMembersUpdate} + +\title{Reevaluation of Index constituents weights +} +\description{ +\code{IndexMembersUpdate} derives the new weights for the coming period. The methodology is according to Trimborn and Haerdle (2018). The method derives the new weights over the data period provided. The data input defines the length of the period, hence it can be different from full month. +} +\usage{ +IndexMembersUpdate(market, price, vol, weighting, index.const, last.value) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"volume"}. +} + \item{weighting}{ +The weighting scheme to be applied. \code{"market"} refers to weighting by market capitalization, \code{"volume"} refers to weighting by trading volume. +} + \item{index.const}{Number of Index constituents. The number can be derived from \code{IndexComp}, \code{IndexMemberSelection} or be chosen by alternative means.} + \item{last.value}{ +The last index value before rederivation. +} +} +\details{ +\code{IndexMembersUpdate} derives the new weights for the coming period. The methodology is according to Trimborn and Haerdle (2018). The method derives the new weights over the data period provided. The data input defines the length of the period, hence it can be different from full month. For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the ordered names of index members, entry 2 the respective consideration of the index constituents, entry 3 the weights of the index members which gives multiplied with entry 2 the actual weight and entry 4 the new divisor of the index. +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2017-01-01::2017-01-31"] +market = market["2017-01-01::2017-01-31"] +vol = vol["2017-01-01::2017-01-31"] +IndexMembersUpdate(market = market, price = price, vol = vol, +weighting = "market", index.const = 5, last.value = 1000) + +} \ No newline at end of file diff --git a/man/IndexUpdate.Rd b/man/IndexUpdate.Rd new file mode 100644 index 0000000..126c637 --- /dev/null +++ b/man/IndexUpdate.Rd @@ -0,0 +1,40 @@ +\name{IndexUpdate} +\alias{IndexUpdate} + +\title{Updating an existing index with new index values +} +\description{ +\code{IndexUpdate} derives the next values of an Index from the given price, weights and its divisor. +} +\usage{ +IndexUpdate(price, index.weights, divisor) +} +\arguments{ + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{index.weights}{ +A vector with the absolute weights expressed as number of shares of each asset. The weights are provided by \code{IndexComp}. They can be also easily derived from the market capitalization by dividing with the respective price. +} + \item{divisor}{ +The divisor required for the index derivation. The divisor is provided by \code{IndexComp}. For details on its derivation, see Trimborn and H{\"a}rdle (2018). +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +The next value(s) of the Index. +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +const.names = c("btc", "eth", "xrp", "ltc", "xmr") +index.weights = c(16136712, 88440036, 36856524148, 49589181, 13859864) +divisor = 17185084 + +IndexUpdate(price["2017-02-01", const.names], index.weights = index.weights, divisor = divisor) +} \ No newline at end of file diff --git a/man/LCRIX.Rd b/man/LCRIX.Rd new file mode 100644 index 0000000..2bad36c --- /dev/null +++ b/man/LCRIX.Rd @@ -0,0 +1,44 @@ +\name{LCRIX} +\alias{LCRIX} + +\title{Derivation of the LCRIX index +} +\description{ +\code{LCRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for LCRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +LCRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2014-10-31"] +market = market["2014-03-31::2014-10-31"] +vol = vol["2014-03-31::2014-10-31"] +days.line = SwitchDates(price, specificDate = "1") + +LCRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/LECRIX.Rd b/man/LECRIX.Rd new file mode 100644 index 0000000..5d05d6b --- /dev/null +++ b/man/LECRIX.Rd @@ -0,0 +1,44 @@ +\name{LECRIX} +\alias{LECRIX} + +\title{Derivation of the LECRIX index +} +\description{ +\code{LECRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for LECRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +LECRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2014-10-31"] +market = market["2014-03-31::2014-10-31"] +vol = vol["2014-03-31::2014-10-31"] +days.line = SwitchDates(price, specificDate = "1") + +LECRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/LEFCRIX.Rd b/man/LEFCRIX.Rd new file mode 100644 index 0000000..7a8708d --- /dev/null +++ b/man/LEFCRIX.Rd @@ -0,0 +1,44 @@ +\name{LEFCRIX} +\alias{LEFCRIX} + +\title{Derivation of the LEFCRIX index +} +\description{ +\code{LEFCRIX} is a wrapper function for \code{IndexComp} which derives the index according the methodology for LEFCRIX as described in Trimborn and Haerdle (2018) and visualized on \url{http://thecrix.de/}. +} +\usage{ +LEFCRIX(market, price, vol = NULL, days.line) +} +\arguments{ +\item{market}{ +An xts object with the market capitalization data. The default is \code{NULL}, an entry is necessary if weighting is set to \code{"market"}. +} + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{vol}{ +An xts object with the trading volume (liquidity) data. The default is \code{NULL}, an entry is necessary iw weighting is set to \code{"volume"}. +} +\item{days.line}{ +The days of the month to perform the recalculation on. Can be calculated from SwitchDates. +} +} +\details{ +For more details, please see the methodology section of the paper Trimborn and Haerdle (2018). +} +\value{ +A list, entry 1 is the optimal index, entry 2 the index of all constituents, entry 3 the index of all constituents rebased at the index (entry 1) each time after altering the number of index constituents which is useful for comparisons with the market, entry 4 the number of assets available for analysis in each period, entry 5 the absolute weight given to each assets price in the respective periods +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +price = price["2014-03-31::2014-10-31"] +market = market["2014-03-31::2014-10-31"] +vol = vol["2014-03-31::2014-10-31"] +days.line = SwitchDates(price, specificDate = "1") + +LEFCRIX(market = market, price = price, vol = vol, days.line = days.line) +} \ No newline at end of file diff --git a/man/RelativeWeights.Rd b/man/RelativeWeights.Rd new file mode 100644 index 0000000..b38fc86 --- /dev/null +++ b/man/RelativeWeights.Rd @@ -0,0 +1,33 @@ +\name{RelativeWeights} +\alias{RelativeWeights} + +\title{Retrieving the relative weights of the assets in the index +} +\description{ +\code{RelativeWeights} retrieves the relative weights of the assets in the index from the absolute weights expressed in shares of the assets. The latter is a direct output of \code{IndexComp}. +} +\usage{ +RelativeWeights(price, index.weights) +} +\arguments{ + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{index.weights}{ +A vector with the absolute weights expressed as number of shares of each asset. The weights are provided by \code{IndexComp}. They can be also easily derived from the market capitalization by dividing with the respective price. +} +} +\value{ +The relative weights of the assets in the Index. +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +const.names = c("btc", "eth", "xrp", "ltc", "xmr") +index.weights = c(16136712, 88440036, 36856524148, 49589181, 13859864) + +RelativeWeights(price = price["2017-02-01", const.names], index.weights = index.weights) +} \ No newline at end of file diff --git a/man/SwitchDates.Rd b/man/SwitchDates.Rd new file mode 100644 index 0000000..bdf721f --- /dev/null +++ b/man/SwitchDates.Rd @@ -0,0 +1,36 @@ +\name{SwitchDates} +\alias{SwitchDates} + +\title{Deriving the dates on which the index constituents are going to be reevaluated +} +\description{ +\code{SwitchDates} derives the dates on which the index constituents are going to be reevaluated. +} +\usage{ +SwitchDates(price, specificDate = NULL, WeekDay = NULL, Appearance = 1) +} +\arguments{ + \item{price}{ +An xts object with the price data. An entry is always required. +} + \item{specificDate}{ +A specific date of each month on which the index members get reevaluated. A common date would be the 1st of each month or the 15th of each month. \code{specificDate} is dominating \code{WeekDay}. +} +\item{WeekDay}{ +Only active when \code{specificDate} is \code{NULL}. A specific weekday of each month on which the index members get reevaluated. The input has to be a character describing the weekday in Engish. By default the first weekday with this appearancce is returned. The argument \code{Appearance} defines if it is the 1st, 2nd or another appearance of this weekday. E.g. the 3rd Friday of each month can be returned. +} + \item{Appearance}{ +Defines if the 1st, 2nd or another appearance of a weekday gets returned. E.g. the 3rd Friday of each month can be returned. Only active when \code{specificDate} is \code{NULL}. The argument works in combination with \code{WeekDay}. +} +} +\value{ +A vector of class date with the respective dates on which the index members become reevaluated. This is a necessary input to \code{IndexComp}. +} +\references{ +Trimborn, S. and Haerdle, W.K. (2018). CRIX an Index for cryptocurrencies, \emph{Journal of Empirical Finance} 49, pp. 107-122. \url{https://doi.org/10.1016/j.jempfin.2018.08.004} +} +\examples{ +data(CryptoData) + +SwitchDates(price, specificDate = "1") +} \ No newline at end of file diff --git a/man/marketData.Rd b/man/marketData.Rd new file mode 100644 index 0000000..cbe58d3 --- /dev/null +++ b/man/marketData.Rd @@ -0,0 +1,17 @@ +\name{market} +\docType{data} +\alias{market} + +\title{Market capitalization data for Cryptocurrencies. +} +\description{ +The dataset contains market capitalization information for cryptocurrencies. +} +\usage{ +data(CryptoData) +} +\format{A dataset with a xts matrix. Load the R library xts for proper visualization of the dataset. +} + +\source{The dataset was provided by CoinGecko. Up-to-date data are accessible via \url{https://www.coingecko.com/api}.} + diff --git a/man/priceData.Rd b/man/priceData.Rd new file mode 100644 index 0000000..db809d1 --- /dev/null +++ b/man/priceData.Rd @@ -0,0 +1,17 @@ +\name{price} +\docType{data} +\alias{price} + +\title{Pricing data for Cryptocurrencies. +} +\description{ +The dataset contains pricing information for cryptocurrencies. +} +\usage{ +data(CryptoData) +} +\format{A dataset with a xts matrix. Load the R library xts for proper visualization of the dataset. +} + +\source{The dataset was provided by CoinGecko. Up-to-date data are accessible via \url{https://www.coingecko.com/api}.} + diff --git a/man/volData.Rd b/man/volData.Rd new file mode 100644 index 0000000..a8ab4f2 --- /dev/null +++ b/man/volData.Rd @@ -0,0 +1,17 @@ +\name{vol} +\docType{data} +\alias{vol} + +\title{Volume data for Cryptocurrencies. +} +\description{ +The dataset contains trading volume information for cryptocurrencies. +} +\usage{ +data(CryptoData) +} +\format{A dataset with a xts matrix. Load the R library xts for proper visualization of the dataset. +} + +\source{The dataset was provided by CoinGecko. Up-to-date data are accessible via \url{https://www.coingecko.com/api}.} +