Script is here.
This is a simple R script to keep track of crypto investment. The script enables user to record and track investments with two functions:
add_trade()
with which user can record transactions, e.g., deposit, withdraw or exchange. And
portfolio_value()
which evaluates real time portfolio value in user specified crypto or fiat currency. We'll create a function to make naive price prediction as well as the two functions mentioned.
First, check if required packages are installed. If not, install them:
if (!require("jsonlite")) install.packages("jsonlite")
if (!require("dplyr")) install.packages("dplyr")
if (!require("ggplot2")) install.packages("ggplot2")
if (!require("forecast")) install.packages("forecast")
if (!require("plotrix")) install.packages("plotrix")
It's a good practice save trade history in disk. We'll save it as .csv file which allows analysis using other software as well. The below code chunk checks if there's a file named trade_histoy.csv
in user's working directory. If not, the code will create one. Issue getwd()
to see current working directory and setwd(<directory path>)
to set working directory.
if(!file.exists("trade_history.csv")){
trade_history <- data.frame(type=factor(levels = c("deposit", "wtihdraw", "exchange")),
deposit_amount=double(),
deposit_currency=factor(),
withdraw_amount=double(),
withdraw_currency=factor(),
exchange_name=character(),
remark=character(),
date=character(), # year-month-day format eg 2017-07-18
stringsAsFactors=FALSE)
write.csv(x = trade_history, file = "trade_history.csv", sep = ",", row.names = FALSE)
}
We inspect what our old or newly created trade_history.csv contains. First load the data with read.csv()
function, store that data in an R variable named trade_history
and see top few rows of that R variable with head(trade_history
:
trade_history <- read.csv(file = "trade_history.csv", header = TRUE, sep = ",")
head(trade_history)
Output of head(trade_history)
is:
[1] type deposit_amount deposit_currency withdraw_amount withdraw_currency exchange_name remark date
<0 rows> (or 0-length row.names)
Since we haven't yet entered any trade/transaction, it's a dataset with 0 rows. Lets create the function named add_trade()
so we can add transactions easier:
add_trade <- function(type, deposit_amount = 0, deposit_currency = NA,
withdraw_amount = 0, withdraw_currency = NA, exchange_name = NA,
remark = NA, date = NA) {
new_trade <- data.frame(type, deposit_amount, deposit_currency,
withdraw_amount, withdraw_currency, exchange_name, remark, date)
read.csv(file = "trade_history.csv", header = TRUE, sep = ",") %>%
rbind(new_trade) -> "trade_history"
write.csv(x = trade_history, file = "trade_history.csv", sep = ",", row.names = FALSE)
assign(x = "trade_history", trade_history, envir = globalenv())
}
Now add a few transactions with the function add_trade()
we just created:
add_trade(type = "deposit", deposit_amount = 0.2, deposit_currency = "BTC", remark = "gift from brother", date = "2017-07-01")
add_trade(type = "deposit", deposit_amount = 5, deposit_currency = "XMR", remark = "purchased", exchange_name = "poloniex", date = "2017-07-02")
add_trade(type = "deposit", deposit_amount = 1, deposit_currency = "ETH", remark = "mining reward", date = "2017-07-05")
add_trade(type = "deposit", deposit_amount = 200, deposit_currency = "STEEM", remark = "Steemit rewarrd", date = "2017-07-06")
add_trade(type = "trade", deposit_amount = 1.1, deposit_currency = "ZEC", withdraw_amount = 0.1, withdraw_currency = "BTC", remark = "Exchanged BTC for ZEC", date = "2017-07-09")
add_trade(type = "trade", deposit_amount = 4.6, deposit_currency = "ETC", withdraw_amount = 2, withdraw_currency = "XMR", remark = "Exchanged XMR ETC", date = "2017-07-10")
add_trade(type = "trade", deposit_amount = 65, deposit_currency = "EOS", withdraw_amount = 0.5, withdraw_currency = "ETH", date = "2017-07-14")
add_trade(type = "withdraw", withdraw_amount = 0.5, withdraw_currency = "XMR", remark = "lost wallet key")
See our first 5 transactions (rows) of trade history by running head(trade_history, 8)
type deposit_amount deposit_currency withdraw_amount withdraw_currency exchange_name remark date
1 deposit 0.2 BTC 0.0 <NA> <NA> gift from brother 2017-07-01
2 deposit 5.0 XMR 0.0 <NA> poloniex purchased 2017-07-02
3 deposit 1.0 ETH 0.0 <NA> <NA> mining reward 2017-07-05
4 deposit 200.0 STEEM 0.0 <NA> <NA> Steemit rewarrd 2017-07-06
5 trade 1.1 ZEC 0.1 BTC <NA> Exchanged BTC for ZEC 2017-07-09
Note that we don't need to supply values for all the arguments. Empty argument values default to NA
. This, however, doesn't provide compact info about our portfolio. Let's create a function and name it portfolio()
that will:
portfolio <- function() {
deposit <- aggregate(trade_history[c("deposit_amount")],
by = trade_history[c("deposit_currency")], FUN=sum)
names(deposit) <- c("currency", "deposit_amount")
withdraw <- aggregate(trade_history[c("withdraw_amount")],
by = trade_history[c("withdraw_currency")], FUN=sum)
names(withdraw) <- c("currency", "withdraw_amount")
portfolio <- full_join(x = deposit, y = withdraw, by = "currency")
portfolio[is.na(portfolio)] <- 0
portfolio$available <- portfolio$deposit_amount - portfolio$withdraw_amount
assign(x = "portfolio", portfolio, envir = globalenv())
print(portfolio)
}
Unlike add_trade()
, this portfolio()
function doesn't take any argument. Just issue portfolio()
which in our case returns a compact table:
currency deposit_amount withdraw_amount available
1 BTC 0.2 0.1 0.1
2 EOS 65.0 0.0 65.0
3 ETC 4.6 0.0 4.6
4 ETH 1.0 0.5 0.5
5 STEEM 200.0 0.0 200.0
6 XMR 5.0 2.5 2.5
7 ZEC 1.1 0.0 1.1
If were to know how much the portfolio is worth in, say, USD, EUR, CNY or BTC? Let's create a function portfolio_value()
that will do our job:
portfolio_value <- function(priced_in) {
for(i in 1:nrow(portfolio)) {
url <- paste0("https://min-api.cryptocompare.com/data/price?fsym=", portfolio[i, 1], "&tsyms=", priced_in, collapse = "")
unit_price <- fromJSON(url)[[1]]
portfolio$value[i] <- unit_price * portfolio$available[i]
}
assign(x = "portfolio_value", portfolio, envir = globalenv())
print(portfolio_value)
print(paste("Total portfolio value in", priced_in, sum(portfolio_value$value)))
lbls <- paste0(portfolio$currency, " : ", # Create labels for plot
sprintf("%.2f", (portfolio$value / sum (portfolio$value))*100), "%")
pie3D(portfolio$value, labels = lbls,
explode=0.1, main="Portfolio value")
}
The portfolio_value()
function takes one argument - the currency in which we want to evaluate our portfolio. Standard symbols are accepted like, USD, EUR, BTC, ETH. I would like to know how much it's worth in Chinese Yuan symbled CNY:
So I execute portfolio_value("CNY")
which returns the below table as well as the pie chart:
currency deposit_amount withdraw_amount available value
1 BTC 0.2 0.1 0.1 1585.070
2 EOS 65.0 0.0 65.0 729.950
3 ETC 4.6 0.0 4.6 491.418
4 ETH 1.0 0.5 0.5 767.625
5 STEEM 200.0 0.0 200.0 1724.000
6 XMR 5.0 2.5 2.5 620.150
7 ZEC 1.1 0.0 1.1 1466.366
[1] "Total portfolio value in CNY 7384.579"

We might be interested in some predictive analysis of portfolio performance. Let's see how we can make prediction using simple method. I would like to know how BTC is going to perform in the next 30 days against USD. Let's create a function so we can do same thing for all currencies:
predict_currency <- function(currency, priced_in, period) {
url <- paste0("https://min-api.cryptocompare.com/data/histoday?fsym=", currency, "&tsym=", priced_in, "&allData=true", collapse = "")
histoday <- fromJSON(url)
ts <- ts(histoday$Data$close, start = histoday$Data$time[1])
fit_arima <- auto.arima(ts)
autoplot(forecast(fit_arima, period))
}
Now run predict_currency("BTC", "USD", 30)
. It generates the below graph in which colored region indicates the likely price of BTC in USD in the next 30 days:
Run for, example, predict currency("ETH", "BTC", 7)
to get estimated ETH price in BTC for the next 7 days. It would be nice to create a predictive function for our portfolio like:
predict_portfolio <- fuction (period) {
# do things
}
We'll do this in the next post.
I just uploaded the script in my git repo. Feel free to contribute and provide suggestion to improve code so it can be actually be used by others. Thanks!