Let’s consider the situation in which we earn a certain amount of money from our job and at the beginning of the month we convert it all in BTC. Then during the month we will have to spend a certain proportion of our income. Hence, during a month, we randomly convert our bitcoin into dollars and we spent them.
Setup
library(tidyverse)library(ggbraid)# ==========================================# random seed for simulation of expensesseed <-1# Annual salaryyearly_salary <-30000# Monthly salary monthly_salary <- yearly_salary/12# Annual expenses yearly_expenses <-21000# Monthly expenses monthly_expenses <- yearly_expenses/12# Number of expenses. # Each occurs in a different day,n_expenses <-20# Amount for a single expense single_expense <- monthly_expenses/n_expenses# Start date of the backtestingstart_conversion <-as.Date("2020-01-01")
Dataset
# Dataset of prices (15m data)BTCUSDT <-read_csv("BTCUSDT-spot-15m.csv")data <-mutate(BTCUSDT, Day =as.Date(date), Month = lubridate::month(date), Year = lubridate::year(date))data <-select(data, Year, Month, Day, date, close) # End date of the backtesting. Occur at the beginning of the month.end_conversion <-as.Date(max(data$date)) - lubridate::day(max(data$date))# conversion of monthly salary happen every 1 monthindex <-seq.Date(start_conversion, end_conversion, by ="1 month")
Variable
Value
BTC data:
From 2020-01-01 to 2024-03-31
Yearly salary:
30000 USD
Yearly expenses:
21000 USD
:
70 %
Number monthly expenses:
20
Single expense:
87.5 USD
In practice, in this setup we earn 30000 USD each year, but we spent 21000 that is the 70 % of our annual income. Each month we convert our BTC 20 times for a mean value of 87.5 USD. Hence, our annual savings, the 30 % of the annual salary, will be stored in BTC instead of USD.
1 Can Bitcoin be used as an everyday currency?
In order to answer to this question, we simulate the conversion and expenses starting from 2020-01-01 up to 2024-03-31. The random expenses are simulated with a uniform sample of minutely data.
Expenses simulation
set.seed(seed)result <-list()all_result <-list()for(i in1:length(index)){# Number of the month (1-12) idx_month <- lubridate::month(index[i])# Number of year idx_year <- lubridate::year(index[i])# Conversion of salary occurs randomly on 1st of the month payment_data <- dplyr::filter(data, Day == index[i]) # Conversion price btc_conversion_price <- payment_data$close[sample(nrow(payment_data), 1)]# Conversion amount btc_conversion_amount <- monthly_salary/btc_conversion_price# Dataset for the month month_data <- dplyr::filter(data, Month == idx_month & Year == idx_year) # Add random conversion price month_data$btc_price <- btc_conversion_price# Add random conversion amount month_data$btc_amount <- btc_conversion_amount# Initialize expenses variables month_data$expenses <-0 month_data$btc_expenses <-0 month_data$expenses_cost <-0# Sample random expenses idx_expenses <-sample(nrow(month_data), n_expenses, replace =FALSE)# Expenses amount in BTC btc_expenses <- single_expense/month_data[idx_expenses,]$close month_data$btc_expenses[idx_expenses] <- btc_expenses# Sampled expenses costs in USD month_data$expenses_cost[idx_expenses] <- btc_expenses*btc_conversion_price# Add expenses amount in USD month_data$expenses[idx_expenses] <- single_expense# Save data all_result[[i]] <- month_data# Save monthly data result[[i]] <- dplyr::tibble(date = index[i], salary = monthly_salary, btc_amount = btc_conversion_amount, btc_price = btc_conversion_price,btc_vol =sd(month_data[idx_expenses,]$close),usd_monthly_expenses = monthly_expenses, btc_monthly_expenses =sum(month_data$btc_expenses[idx_expenses]),btc_monthly_cost =sum(month_data$expenses_cost[idx_expenses]))}result <- dplyr::bind_rows(result) result <- dplyr::mutate(result, net_expenses = usd_monthly_expenses - btc_monthly_cost, usd_savings = salary - usd_monthly_expenses,btc_savings = btc_amount - btc_monthly_expenses)# Cumulated expenses in USD result$cum_usd_expenses <-cumsum(result$usd_monthly_expenses)# Cumulated random expenses in BTC result$cum_btc_expenses <-cumsum(result$btc_monthly_cost)# Cumulated savings in USD result$cum_usd_savings_usd <-cumsum(result$usd_savings)# Cumulated random savings in BTC -> USDresult$cum_btc_savings_usd <-cumsum(result$btc_savings)*result$btc_price# Cumulated savings in USD -> BTCresult$cum_usd_savings_btc <-cumsum(result$usd_savings)/result$btc_price# Cumulated random savings in BTCresult$cum_btc_savings_btc <-cumsum(result$btc_savings)
We distinguish between two situations:
A positive expense occurs when the mean price at which we have converted the BTC during the month is above the buy price for that month and hence realize a gain.
A negative expense occurs when the mean price at which we have converted the BTC during the month is below the buy price price and hence realize a loss.
In Figure 1 we show the cumulated expenses in the two cases:
USD: where we we use only dollars for expenses.
BTC: where we use dollars for expenses, but we convert the salary in BTC and then BTC to USD only when an expense occurs.
In Figure 2 we show the same situation but on a monthly basis. Moreover, even in months where we spend more, the difference with respect to the USD spending is not evident.
Show the code
y_breaks <-c(seq(0, monthly_salary, by =250))y_labels <-format(round(y_breaks), digits =0)ggplot(result) +geom_bar(aes(date, btc_monthly_cost, fill = btc_monthly_cost < usd_monthly_expenses), stat ="identity", color ="black", alpha =0.7)+geom_line(aes(date, monthly_salary, color ="salary"), size =1)+geom_line(aes(date, usd_monthly_expenses, color ="expense"), size =1)+scale_fill_manual(values =c(`TRUE`="green", `FALSE`="red"),labels =c(`TRUE`="Positive expense", `FALSE`="Negative expense"))+scale_color_manual(values =c(expense ="black", salary = mycol$usd), labels =c(expense ="Expenses", salary ="Monthly salary"))+scale_y_continuous(breaks = y_breaks, labels = y_labels)+theme_bw()+theme(legend.position ="top")+labs(fill =NULL, color =NULL, y ="USD", x =NULL)
Figure 2: Monthly expenses in the two cases.
In fact, in Table 1, we compute the precise values of the cumulated expenses in both cases we can see that such difference is very small.
Table 1: Cumulated expenses at the end of the period considered.
BTC Expenses
USD Expenses
Global Difference
Monthly Difference
89250 $
89263 $
13 $
0 $
2 Saving in BTC or in USD?
Here we compare the value of the savings in both cases expressed in USD.
Show the code
last_cum_usd_savings <- result$cum_usd_savings_usd[nrow(result)]last_cum_btc_savings <- result$cum_btc_savings_usd[nrow(result)]result %>%ggplot() +geom_line(aes(date, cum_btc_savings_usd, color ="btc")) +geom_line(aes(date, cum_usd_savings_usd, color ="usd")) +geom_braid(method ="line", alpha =0.3,aes(x = date, ymin = cum_usd_savings_usd, ymax = cum_btc_savings_usd, fill = cum_btc_savings_usd > cum_usd_savings_usd)) +scale_color_manual(values =c(btc = mycol$btc, usd = mycol$usd), labels =c(btc ="BTCUSD", usd ="USD"))+scale_fill_manual(values =c(`TRUE`="green", `FALSE`="red"),labels =c(`TRUE`="Greater", `FALSE`="Lower"), )+annotate(geom ="label", size =2.5,x =max(result$date), y = last_cum_usd_savings, label =paste0(round(last_cum_usd_savings), " USD"))+annotate(geom ="label", size =2.5,x =max(result$date), y = last_cum_btc_savings, label =paste0(round(last_cum_btc_savings), " USD"))+labs(fill ="BTC vs USD: ", color ="Savings: ", y ="USD", x =NULL)+theme_bw()+theme(legend.position ="top")
Figure 3: Savings in both cases expressed in USD.
Here we compare the value of the savings in both cases expressed in BTC.
Show the code
last_cum_usd_savings <- result$cum_usd_savings_btc[nrow(result)]last_cum_btc_savings <- result$cum_btc_savings_btc[nrow(result)]result %>%ggplot() +geom_line(aes(date, cum_btc_savings_btc, color ="btc")) +geom_line(aes(date, cum_usd_savings_btc, color ="usd")) +geom_braid(method ="line", alpha =0.3,aes(x = date, ymin = cum_usd_savings_btc, ymax = cum_btc_savings_btc, fill = cum_btc_savings_btc > cum_usd_savings_btc)) +scale_color_manual(values =c(btc = mycol$btc, usd = mycol$usd), labels =c(btc ="BTC", usd ="USD"))+scale_fill_manual(values =c(`TRUE`="green", `FALSE`="red"),labels =c(`TRUE`="Greater", `FALSE`="Lower"), )+annotate(geom ="label", size =2.5,x =max(result$date)-10, y = last_cum_usd_savings, label =paste0(round(last_cum_usd_savings, 3), " BTC"))+annotate(geom ="label", size =2.5,x =max(result$date)-10, y = last_cum_btc_savings, label =paste0(round(last_cum_btc_savings, 3), " BTC"))+labs(fill ="BTC vs USD: ", color ="Savings: ", y ="BTC", x =NULL)+theme_bw()+theme(legend.position ="top")
Figure 4: Savings in both cases expressed in BTC.
3 Should I trade my BTC?
Let’s compute the mean price of the BTC that we convert at the beginning of the month.
Show the code
# Initialize result$mean_price <-0result$mean_price[1] <- result$btc_price[1]for(i in2:nrow(result)){# Compute the total btc converted btc_weight <- result$btc_amount[1:i]# BTC mean price btc_price <- result$btc_price[1:i]# Total btc btc_sum_weights <-sum(btc_weight) result$mean_price[i] <-sum(btc_weight*btc_price)/btc_sum_weights}ggplot()+geom_line(data = result, aes(date, mean_price, color ="mean_price"))+geom_line(data = result, aes(date, btc_price, col ="btc_price"))+scale_color_manual(values =c(mean_price ="black", btc_price = mycol$btc),labels =c(usd ="Savings USD", mean_price ="Mean price", btc_price ="BTC price"))+theme_bw()+theme(legend.position ="top")+labs(color =NULL, linetype =NULL, y ="USD", x =NULL)
Figure 5: BTC price and mean conversion price.
3.1 Fixed trading strategy
Let’s consider a simple trading strategy, where we sell a certain amount of our saved BTC if some conditions are met. Let’s imagine a reasonable setup for example: at the beginning of the month, when we convert our income, we will sell 5% of our savings if the price at the beginning of that is 50% greater than our average price.
Show the code
df_simple <- resultperc_sell <-0.05# 5%threshold <- df_simple$mean_price*1.5# 50% # =======================df_simple$trade <-"buy"df_simple$gain <-0df_simple$btc_sold <-0for(i in1:nrow(df_simple)){# Compute the net btc saved btc_savings <-sum(df_simple$btc_savings[1:i]) -sum(df_simple$btc_sold[1:i])# Evaluate sell conditionif (btc_savings >0) { sell_condition <- df_simple$btc_price[i] > threshold[i]if (sell_condition) {# Compute the amount to be sold btc_sell_amount <- btc_savings*perc_sell# Update trade variable df_simple$trade[i] <-"sell"# Update capital gains df_simple$gain[i] <- btc_sell_amount*df_simple$btc_price[i]# Update btc sold df_simple$btc_sold[i] <- btc_sell_amount } } else { sell_condition <-FALSE }}# Compute cumulated net saved btc df_simple$cum_btc_savings_btc <-cumsum(df_simple$btc_savings - df_simple$btc_sold)# Compute cumulated capital gain df_simple$cum_gain <-cumsum(df_simple$gain)ggplot()+geom_line(data = result, aes(date, cum_btc_savings_btc*btc_price, color ="usd"))+geom_line(data = df_simple, aes(date, mean_price, color ="mean_price"))+geom_line(data = df_simple, aes(date, threshold, linetype ="threshold"), color ="blue")+geom_line(data = df_simple, aes(date, cum_btc_savings_btc*btc_price + cum_gain, col ="usd_trade"))+geom_line(data = df_simple, aes(date, btc_price, col ="btc_price"))+geom_point(data = df_simple, aes(date, btc_price, color = trade))+scale_linetype_manual(values =c(threshold ="dashed"), labels =c(threshold ="Trigger"))+scale_color_manual(values =c(usd = mycol$usd, mean_price ="black", usd_trade ="purple", btc_price = mycol$btc,buy ="gray", sell ="red"),labels =c(usd ="Savings (no trade)", mean_price ="Mean price", usd_trade ="Savings (trade)", btc_price ="BTC price",buy ="Only convert", sell ="Sell"))+theme_bw()+theme(legend.position ="top")+labs(color =NULL, linetype =NULL, y ="USD", x =NULL)
Figure 6: Simple trading strategy.
3.2 Volatility trading strategy
Let’s consider the volatility of BTC price computed randomly at the expenses dates. The idea is to adapt the amount sold in our strategy with respect to the volatility. For example, if the price is above the trigger, and so we have to sell a certain amount, we want to sell adapting the amount with respect to the volatility in the past 5 months. In practice, in the month -th, we compute the volatility of the expenses in the past months, i.e. . Then, we normalize the values in order to have a number in . In this way the percentage of savings that we are going to sell in the month -th is given by .
Show the code
df_vol <- resultthreshold <- df_vol$mean_price*1.5# 50% # =======================df_vol$trade <-"buy"df_vol$gain <-0df_vol$btc_sold <-0for(i in6:nrow(df_vol)){# Compute the net btc saved btc_savings <-sum(df_vol$btc_savings[1:i]) -sum(df_vol$btc_sold[1:i])# Evaluate sell conditionif (btc_savings >0) { sell_condition <- df_vol$btc_price[i] > threshold[i]if (sell_condition) {# Compute the percentage of savings to sell sum_sigma <-sum(df_vol$btc_vol[(i-5):(i-1)]) perc_sell <- df_vol$btc_vol[i-1]/sum_sigma# Compute the amount to be sold btc_sell_amount <- btc_savings*perc_sell# Update trade variable df_vol$trade[i] <-"sell"# Update capital gains df_vol$gain[i] <- btc_sell_amount*df_vol$btc_price[i]# Update btc sold df_vol$btc_sold[i] <- btc_sell_amount } } else { sell_condition <-FALSE }}# Compute cumulated net saved btc df_vol$cum_btc_savings_btc <-cumsum(df_vol$btc_savings - df_vol$btc_sold)# Compute cumulated capital gain df_vol$cum_gain <-cumsum(df_vol$gain)ggplot()+geom_line(data = result, aes(date, cum_btc_savings_btc*btc_price, color ="usd"))+geom_line(data = df_vol, aes(date, mean_price, color ="mean_price"))+geom_line(data = df_vol, aes(date, threshold, linetype ="threshold"), color ="blue")+geom_line(data = df_vol, aes(date, cum_btc_savings_btc*btc_price + cum_gain, col ="usd_trade"))+geom_line(data = df_vol, aes(date, btc_price, col ="btc_price"))+geom_point(data = df_vol, aes(date, btc_price, color = trade))+scale_linetype_manual(values =c(threshold ="dashed"), labels =c(threshold ="Trigger"))+scale_color_manual(values =c(usd = mycol$usd, mean_price ="black", usd_trade ="purple", btc_price = mycol$btc,buy ="gray", sell ="red"),labels =c(usd ="Savings (no trade)", mean_price ="Mean price", usd_trade ="Savings (trade)", btc_price ="BTC price",buy ="Only convert", sell ="Sell"))+theme_bw()+theme(legend.position ="top")+labs(color =NULL, linetype =NULL, y ="USD", x =NULL)