Equal-Weighting Top-Probability Stocks with Periodic Rebalancing
Summary
The document shows how to turn daily stock probability rankings into an equal-weighted portfolio that holds the five highest-ranked names and rebalances every ten days. The example uses a table of dates, stock names, returns, and model probabilities. The answer filters for rebalance dates, groups rows by date, selects the top five probabilities, and assigns each selected stock one-fifth of the portfolio.
To apply those holdings between rebalance dates, the example reshapes weights into a date-by-stock table, fills non-rebalance dates with the last available weights, and returns the data to long form. It then joins weights to stock returns and sums weighted returns by date. This is a simple ranking-based allocation rather than an optimization method. The example does not discuss transaction costs, turnover, missing returns, ties at the selection cutoff, or whether the probabilities and returns are aligned without look-ahead bias.
Key ideas
- Select the highest-probability stocks on each scheduled rebalance date.
- Assign equal weights to the selected names by dividing the portfolio evenly.
- Carry each rebalance’s weights forward until the next rebalance date.
- Join weights to returns and sum weighted returns to calculate portfolio performance.
- The example leaves trading frictions and potential look-ahead bias unaddressed.
Tags
Full text
# R - Portfolio construction based on own calculations, with rebalancing of components
# R - Portfolio construction based on own calculations, with rebalancing of components
I have used random forest in R to get probabilities for stocks being in a certain class. With those probabilities i would like to construct portfolios containing the 5 stocks with the highest probabilities on the first date of the dataset, and then rebalance this every 10 days with the stocks with the highest ranking at that time. The portfolio should be equal weighted.
Here is some example data that i think is representable of my data.
```
Date <- rep(seq(as.Date("2009/01/01"), by = "day", length.out = 100), 10)
Name <- c(rep("Stock A", 100), rep("Stock B",100), rep("Stock C", 100), rep("Stock D", 100), rep("Stock E",100), rep("Stock F",100), rep("Stock G",100), rep("Stock H",100), rep("Stock I", 100), rep("Stock J", 100))
Return <- rnorm(1000)
Prob <- runif(1000)
DF <- data.frame(Date, Name, Return, Prob)
DF <- DF %>% arrange(Date, desc(Prob))
```
```
> head(DF)
Date Name Return Prob
1 2009-01-01 Stock F 0.52259644 0.8084277
2 2009-01-01 Stock A 0.57720376 0.7617348
3 2009-01-01 Stock B -0.09864981 0.7256358
4 2009-01-01 Stock E -1.26136381 0.6200346
5 2009-01-01 Stock G -1.37360527 0.5680765
6 2009-01-01 Stock D -0.04794049 0.4793370
```
So the portfolio would contain stock F, A, B, E, and G for the first 10 days, and then rebalance it with the stocks of the highest percentage.
I am not very good at coding and R, and have tried looking at options as to how i can do this with PortfolioAnalytics, PerformanceAnalytics and tidyquant, but am not able to find a solution where i understand how to do this, as i am not interested in using any form of optimizing. I need a simple portfolio determined by my calculated percentages, with rebalancing.
If anyone has any suggestions as to how i can do this, i would highly appreciate it. And if this is the wrong forum for posting this question, i am sorry and will post it elsewhere.
## Answer by Martin (score 1)
https://quant.stackexchange.com/a/53331
Using `dplyr::group_by` and `dplyr::top_n` to get the highest probabilities by date. Then expand to get a weight vector. See my comments in the code.
```
library(dplyr)
weights <- DF %>%
# filter the dates you want to rebalance on
filter(Date %in% seq(min(Date), max(Date), by = "10 days")) %>%
# group by Date and get the top 5 by Prob
group_by(Date) %>%
top_n(5, Prob) %>%
# ungroup removes the grouping again
ungroup() %>%
# select Date and Name and add the weight
select(Date, Name) %>%
mutate(weight = 1/5) %>%
# last step is to complete the weights over all dates
# (I transform it to wide format first, and locf "last observation carried forward" the NAs,
# there might be a more elegant way for this, I'm not sure)
pivot_wider(names_from = "Name", values_from = "weight", values_fill = list(weight = 0)) %>%
complete(Date = seq(as.Date("2009/01/01"), by = "day", length.out = 100)) %>%
mutate_at(vars(starts_with("Stock")), na.locf) %>%
pivot_longer(names_to = "Name", values_to = "weight", -"Date")
DF %>%
# join the weights to the original returns
left_join(weights, by = c("Date", "Name")) %>%
# calculate PF Returns
group_by(Date) %>%
summarise(Return = sum(Return*weight))
```
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.