Apply BuyTheDip to historical bitcoin prices & make plots

source("fns.R")
suppressMessages(library(dplyr))
suppressMessages(library(Hmisc))
suppressMessages(library(lubridate))
suppressMessages(library(data.table))
suppressMessages(library(tidyr))
suppressMessages(library(knitr))

if(file.exists("bitcoin_zscores.csv")){
  dat <- fread("bitcoin_zscores.csv")
} else {
  dat <- data.table::fread("historicalprices_bitcoin.csv")
  dat <- do.call(rbind, pbmcapply::pbmclapply(1:nrow(dat), function(x) buythedip(x), mc.cores = 2))
  dat <- dat[!duplicated(dat$datetime),]
  write.csv(dat, "bitcoin_zscores.csv", row.names = F, quote = F)
}

cols = viridis::viridis(n=12)
p1 <- dat %>%
  select(coin_id, datetime, z.score, current.price) %>%
  mutate(log_price = (current.price)) %>%
  mutate(datetime = as.Date(datetime)) %>%
  mutate(buy2 = ifelse(z.score < -2, (log_price)+(log_price*0.075), NA)) %>%
  mutate(buy3 = ifelse(z.score < -3, (log_price)-(log_price*0.075), NA)) %>%
  # reshape2::melt(., id.var=c("coin_id","datetime")) %>%
  filter(!is.na(z.score)) %>%
  ggplot(aes(x=datetime, y=log_price)) +
  geom_line(size=0.25, col=cols[12], alpha=0.7, show.legend = F) +
  # geom_point(aes(x=datetime, y=buy2), pch=25, col="white",alpha=0.05) +
  # geom_point(aes(x=datetime, y=buy3), pch=24,  col="red",alpha=0.05) +
  scale_x_date(date_breaks = "3 months",limits = c(min(as.Date(dat$datetime)-5),NA),  
               date_minor_breaks = "1 month", date_labels = "%B\n%Y") +  
  ylab("bitcoin (USD)") + 
  theme(plot.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.grid.major = element_blank(),
        # panel.grid.major.x = element_line(colour="white"),
        axis.text.x = element_blank(),
        axis.text.y = element_text(colour = "white"),
        axis.title.y = element_text(colour = "white"),
        axis.title.x = element_blank(),
        panel.grid.minor = element_blank())
p2 <- dat %>%
  select(coin_id, datetime, z.score) %>%
  mutate(datetime = as.Date(datetime)) %>%
  # reshape2::melt(., id.var=c("coin_id","datetime")) %>%
  filter(!is.na(z.score)) %>%
  mutate(intercept1 = -2) %>%
  mutate(intercept2 = -3) %>%
  mutate(intercept3 = 2) %>%
  mutate(intercept4 = 3) %>%
  mutate(buy2 = ifelse(z.score < -2, min(z.score)-0.75, NA)) %>%
  mutate(buy3 = ifelse(z.score < -3, min(z.score)-2.1, NA)) %>%
  ggplot(aes(x=datetime, y=z.score)) +
  geom_hline(yintercept=0, na.rm=TRUE, linetype="dotted", col="white") +
  geom_hline(aes(yintercept=intercept1), na.rm=TRUE, linetype="dotted", col="red", alpha=0.5) +
  geom_hline(aes(yintercept=intercept2),  na.rm=TRUE,  col="red", alpha=0.5) +
  geom_hline(aes(yintercept=intercept3), na.rm=TRUE, linetype="dotted", col="green", alpha=0.5) +
  geom_hline(aes(yintercept=intercept4),  na.rm=TRUE,  col="green", alpha=0.5) +
  geom_line(size=0.25, col=cols[12], alpha=0.7, show.legend = F) +
  geom_point(aes(x=datetime, y=buy2),na.rm=TRUE, pch=24, col="white",alpha=0.05) +
  geom_point(aes(x=datetime, y=buy3),na.rm=TRUE, pch=24, col="red",alpha=0.05) +
  annotate("text", x=min(as.Date(dat$datetime))-10, y=-9.2, hjust=1, vjust=0, col="white", cex=3, fontface = "bold", label = "buys @ -2") +
  annotate("text", x=min(as.Date(dat$datetime))-10, y=-10.5, hjust=1, vjust=0, col="red", cex=3, fontface = "bold", label = "buys @ -3") +
  scale_x_date(date_breaks = "3 months",limits = c(min(as.Date(dat$datetime)-50),NA),  date_minor_breaks = "1 month", date_labels = "%B\n%Y", position = "top") +  
  ylab("      z-score") +
  scale_y_continuous(breaks=seq(-3,3,1))  +
  theme(plot.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.grid.major = element_blank(),
        # panel.grid.major.x = element_line(colour="white"),
        axis.text.x = element_text(colour = "white"),
        axis.text.y = element_text(colour = "white"),
        axis.title.y = element_text(colour = "white"),
        axis.title.x = element_blank(),
        panel.grid.minor = element_blank())
p3 <- dat %>%
  select(coin_id, datetime, z.score, current.price) %>%
  mutate(log_price = log10(current.price)) %>%
  mutate(datetime = as.Date(datetime)) %>%
  mutate(buy2 = ifelse(z.score < -2, (log_price)+(log_price*0.075), NA)) %>%
  mutate(buy3 = ifelse(z.score < -3, (log_price)-(log_price*0.075), NA)) %>%
  # reshape2::melt(., id.var=c("coin_id","datetime")) %>%
  filter(!is.na(z.score)) %>%
  ggplot(aes(x=datetime, y=log_price)) +
  geom_line(size=0.25, col=cols[12], alpha=0.7, show.legend = F) +
  geom_point(aes(x=datetime, y=buy2), pch=25, na.rm=TRUE, col="white",alpha=0.05) +
  geom_point(aes(x=datetime, y=buy3), pch=24, na.rm=TRUE, col="red",alpha=0.05) +
  scale_x_date(date_breaks = "3 months",limits = c(min(as.Date(dat$datetime)-50),NA),  
               date_minor_breaks = "1 month", date_labels = "%B\n%Y", position = "top") +  
  annotate("text", x=min(as.Date(dat$datetime))-10, y=4.05, hjust=1, vjust=0, col="white", cex=3, fontface = "bold", label = "buys @ -2") +
  annotate("text", x=min(as.Date(dat$datetime))-10, y=3.5, hjust=1, vjust=0, col="red", cex=3, fontface = "bold", label = "buys @ -3") +
  ylab("log10(bitcoin)") + 
  theme(plot.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.background = element_rect(fill = 'grey15', colour = 'grey15'),
        panel.grid.major = element_blank(),
        # panel.grid.major.x = element_line(colour="white"),
        axis.text.x = element_text(colour = "white"),
        axis.text.y = element_text(colour = "white"),
        axis.title.y = element_text(colour = "white"),
        axis.title.x = element_blank(),
        panel.grid.minor = element_blank())

Combined plots

ggpubr::ggarrange(p1, p2, p3,
          ncol = 1, nrow = 3,
          heights = c(1, 2, 2))

Investment scenario

days = (max(dat$datetime) - min(dat$datetime))/14
dca_dates <- seq.Date(from=as.Date(min(dat$datetime)), to = as.Date(max(dat$datetime)), length.out=days)

dca <- dat %>%
  arrange(datetime) %>%
  mutate(dca = as.character(as.Date(datetime))) %>%
  filter(dca %in% as.character(dca_dates)) %>%
  group_by(coin_id, dca) %>%
  summarise(mean_price = median(current.price, na.rm=TRUE), .groups = 'drop') %>%
  mutate(fiat=100, bitcoins = fiat/mean_price) %>%
  group_by(coin_id) %>%
  summarise(n=n(),total_bitcoin = sum(bitcoins), total_fiat = sum(fiat), latest_price = 60000, .groups = 'drop') %>%
  mutate(total_worth = total_bitcoin*latest_price, gain = total_worth-total_fiat, gain_per = gain/total_fiat*100) %>%
  mutate(scenario = "S1: Scheduled purchase ($100 every two weeks)")

btd2 <- dat %>%
  arrange(z.score) %>%
  filter(z.score <= -2.0) %>%
  mutate(fiat = dca$total_fiat/n(), bitcoins = fiat/current.price) %>%
  group_by(coin_id) %>%
  summarise(n=n(),total_bitcoin = sum(bitcoins), total_fiat = sum(fiat), latest_price = 60000) %>%
  mutate(total_worth = total_bitcoin*latest_price, gain = total_worth-total_fiat, gain_per = gain/total_fiat*100) %>%
  mutate(scenario = "S2: BuyTheDip @ z-score -2")


btd3 <- dat %>%
  arrange(z.score) %>%
  filter(z.score <= -3.0) %>%
  mutate(fiat = dca$total_fiat/n(), bitcoins = fiat/current.price) %>%
  group_by(coin_id) %>%
  summarise(n=n(),total_bitcoin = sum(bitcoins), total_fiat = sum(fiat), latest_price = 60000) %>%
  mutate(total_worth = total_bitcoin*latest_price, gain = total_worth-total_fiat, gain_per = gain/total_fiat*100)  %>%
  mutate(scenario = "S3: BuyTheDip @ z-score -3")

res <- rbind(dca,btd2,btd3) %>%
  select(-coin_id) %>%
  select(scenario, everything()) %>%
  mutate(total_fiat = format.money(total_fiat),
         latest_price = format.money(latest_price),
         total_worth = format.money(total_worth),
         gain = format.money((gain)),
         gain_per = paste0(round(gain_per,1),"%"))

kable(res)
scenario n total_bitcoin total_fiat latest_price total_worth gain gain_per
S1: Scheduled purchase ($100 every two weeks) 77 0.8605088 $7,700.00 $60,000.00 $51,630.53 $43,930.53 570.5%
S2: BuyTheDip @ z-score -2 1000 0.9195261 $7,700.00 $60,000.00 $55,171.56 $47,471.56 616.5%
S3: BuyTheDip @ z-score -3 139 1.0259890 $7,700.00 $60,000.00 $61,559.34 $53,859.34 699.5%

Outcome

donations
  • BTC: bc1qcay52dwm8h7qugj4tkzjcg0wrzhqf9jsh62sgn
  • ETH: 0xA1e608C40C88B83e3325a9f25E0523BE8bC977d2
  • ADA: Ae2tdPwUPEZMc9Jp6RVE4SfCKpQCQD4LMZKiSaoyCtSzTE5JNrZNrvBiyTT
  • SOL: 8oETiSiMJhR1iMSPqWQDZ24qhToxfdBZHG5AP9Jfimv6
  • XLM: GBC7BWLJ7LPGZGNSBOQQAGA4IQB3OINZ3WT7FHUQ3XRAWFJQC56X34XL
  • USDT: 0xA1e608C40C88B83e3325a9f25E0523BE8bC977d2
  • DOGE: DKwBg5xx946AMYrSQL1ZecGazetWHRe7Rs
  • SHIB: 0xA1e608C40C88B83e3325a9f25E0523BE8bC977d2
  • r/cc MOON: 0x1a5fce959e9e6574cba3893f9d3abcc127d8c82b