Arthur Charpentier used R to denote a broken record of the CAC 40 when it went 11 consecutive days with negative returns.

Question: What happens to the market after runs of positive or negative returns? Will the market tank or soar after n days of gains/losses?

First, a little dissection of historical data (S&P 500 since 1980).

library(quantmod)
getSymbols("^GSPC", from='1980-01-01')
y <- diff(Cl(GSPC))
# looking at direction only
y[y < 0] <- -1
y[y > 0] <- 1
r <- rle(as.vector(y))
# losses
l <- r$lengths[which(r$values < 0)]
# gains
g <- r$lengths[which(r$values > 0)]
plot(as.factor(l), col='red'); title('Consecutive losing days for the S&P 500 since 1980')
plot(as.factor(g), col='green'); title('Consecutive gains for the S&P 500 since 1980')


So the record of consecutive gains is 12 days in a row, and we have a record of 9 losses in a row for the S&P 500.

# given a run index from the r table, get the day associated with the start of that particular run
getDayIndex <- function(i) {
        sum(r$lengths[1:i-1])
}
# getLossRuns(9) will give you the dates (index) of the start of 9 consecutive losing streak
getLossRuns <- function(i) {
        n <- length(r$lengths)
        l <- intersect(which(r$lengths == i), which(r$values < 0))
        l <- l[l < n - i - 30]
        unlist(lapply(l, getDayIndex))
}
getGainRuns <- function(i) {
        n <- length(r$lengths)
        l <- intersect(which(r$lengths == i), which(r$values > 0))
        l <- l[l < n - i - 30]
        unlist(lapply(l, getDayIndex))
}

Now, you can simply write:

> GSPC[getLossRuns(8)]
           GSPC.Open GSPC.High GSPC.Low GSPC.Close GSPC.Volume GSPC.Adjusted
1982-08-02    107.71    109.09   107.11     108.98    53460000        108.98
1991-08-28    393.06    396.64   393.05     396.64   169890000        396.64
1996-06-07    673.03    673.31   662.48     673.31   445710000        673.31
2008-09-30   1113.78   1168.03  1113.78    1166.36  4937680000       1166.36
> GSPC[getLossRuns(8) + 8 + 30]
           GSPC.Open GSPC.High GSPC.Low GSPC.Close GSPC.Volume GSPC.Adjusted
1982-09-24    123.79    123.80   123.11     123.32    54600000        123.32
1991-10-22    390.02    391.20   387.40     387.83   194160000        387.83
1996-08-01    639.95    650.66   639.49     650.02   439110000        650.02
2008-11-21    755.84    801.20   741.02     800.03  9495900000        800.03

to get the data on the start of 8-day losing streaks since 1980, and their data 30 days after the end of such streaks.

getAvgReturnsLoss <- function(i) {
        d2 <- as.vector(Cl(GSPC[getLossRuns(i) + i + 30]))
        d1 <- as.vector(Cl(GSPC[getLossRuns(i)]))
        (d2 - d1) / d1
}
boxplot(lapply(1:9, getAvgReturnsLoss))
getAvgReturnsGain <- function(i) {
        d2 <- as.vector(Cl(GSPC[getGainRuns(i) + i + 30]))
        d1 <- as.vector(Cl(GSPC[getGainRuns(i)]))
        (d2 - d1) / d1
}
boxplot(lapply(1:12, getAvgReturnsGain))


Each box in the boxplot represents the different returns after 30 days. For example, in the boxplot for returns after losing streak, the box labeled '7' represents the different returns of the S&P 500, 30 days following any run of 7 days with negative returns.

So far, this is just an exploration. One could determine whether the returns deviate significantly. One can also play with the number of days after the given run: analyze what happens to returns 15 days after such runs, etc. The amplitude of runs could also be considered in the analysis.

Photograph used with permission from mylittleshoebox.ca