Rbitcoin package can be used to create end-to-end trading engine in R.
The goal of the vignettes is to present a possible workflow based on the functions available in the package. Additionally few other handy utilies are also presented.
There is also simple shinyApp as GUI for Rbitcoin package shinyBTC which can be complementary to this vignette.
shiny::runGitHub("jangorecki/shinyBTC")
The core functionality of Rbitcoin is to communicate with cryptocurrency exchanges API directly from R, and to unify the structure of market API response across the different markets.
Lets see the full process.
market <- "kraken"
currency_pair <- c("BTC","EUR")
Public API calls do not require any authentication and you can query it without having an account on market.
ticker <- market.api.process(market, currency_pair, "ticker")
ticker
#> market base quote timestamp market_timestamp last vwap
#> 1: kraken BTC EUR 2016-10-25 15:04:52 <NA> 605.67 602.4755
#> volume ask bid
#> 1: 4904.718 605.713 605.68
trades <- market.api.process(market,currency_pair,"trades")
trades[["trades"]][,tail(.SD,10)] # print only last 10 trades
#> date price amount tid type
#> 1: 2016-10-25 15:01:43 604.031 1.17860364 NA ask
#> 2: 2016-10-25 15:01:48 604.031 0.03000000 NA ask
#> 3: 2016-10-25 15:02:14 605.710 0.16520000 NA bid
#> 4: 2016-10-25 15:02:16 605.710 0.07040000 NA bid
#> 5: 2016-10-25 15:02:54 604.211 0.02031671 NA ask
#> 6: 2016-10-25 15:03:08 605.670 0.11005000 NA bid
#> 7: 2016-10-25 15:03:30 604.231 0.49000000 NA ask
#> 8: 2016-10-25 15:03:30 604.211 0.03780000 NA ask
#> 9: 2016-10-25 15:03:33 605.689 0.03790000 NA bid
#> 10: 2016-10-25 15:04:13 605.670 0.53500000 1477407853130384595 bid
rbtc.plot(trades)
order_book <- market.api.process(market,currency_pair,"order_book")
rbtc.plot(order_book)
order_book[["asks"]][,head(.SD,10)] # print only first 10 asks
#> price amount value cum_amount cum_value avg_price
#> 1: 605.713 0.696 421.57625 0.696 421.5762 605.7130
#> 2: 605.715 1.627 985.49831 2.323 1407.0746 605.7144
#> 3: 605.900 0.421 255.08390 2.744 1662.1585 605.7429
#> 4: 605.969 0.928 562.33923 3.672 2224.4977 605.8000
#> 5: 605.970 4.000 2423.88000 7.672 4648.3777 605.8886
#> 6: 605.979 0.380 230.27202 8.052 4878.6497 605.8929
#> 7: 605.980 11.000 6665.78000 19.052 11544.4297 605.9432
#> 8: 606.165 0.020 12.12330 19.072 11556.5530 605.9434
#> 9: 606.266 0.020 12.12532 19.092 11568.6783 605.9438
#> 10: 606.300 1.800 1091.34000 20.892 12660.0183 605.9745
Private API calls requires authentication, user need to have an account on market and generate API keys (key
and secret
param pair).
Below examples will not be evaluated due to missing key
and secret
parameters in the vignette.
wallet <- market.api.process(market, action = "wallet", key = "", secret = "")
wallet[["wallet"]] # print currencies and their amount in the wallet
place_limit_order <- market.api.process(market, currency_pair, action = "place_limit_order",
req = list(type = "buy",
price = 500,
amount = 0.15)
key = "", secret = "")
open_orders <- market.api.process(market, action = "open_orders", key = "", secret = "")
oid
must be provided (oid can be obtained using open_orders method)cancel_order <- market.api.process(market, action = "cancel_order",
req = list(oid = "")
key = "", secret = "")
To avoid ban on API interface caused by a sequence of API calls, the market.api.process
(and any other function which query over web) will perform antiddos procedure behind the scene (to customize see ?antiddos
).
Because of the market.api.process
function do post-process and pre-process of API calls to common structure across markets, the function is limited to defined markets and currency pairs in the dictionary (?api.dict
). User can extend built-in dictionary for new markets (?query.dict
) or new currency pairs or new API methods (?api.dict
).
In case if user wants to use currency pair or method not defined in the dictionary it is possible using market.api.query
function which can handle any currency pair and any method but it requires appropriate format of method arguments and it will not post-process API response to common structure.
Built-in dictionary supports following market and currency pairs
api.dict <- getOption("Rbitcoin.api.dict")
api.dict[!is.na(base), .(market, currency_pair = paste0(base,quote))][,unique(.SD)]
#> market currency_pair
#> 1: bitmarket BTCEUR
#> 2: bitmarket BTCPLN
#> 3: bitmarket LTCBTC
#> 4: bitmarket LTCPLN
#> 5: bitstamp BTCUSD
#> 6: btcchina BTCCNY
#> 7: btcchina LTCBTC
#> 8: btcchina LTCCNY
#> 9: btce BTCUSD
#> 10: btce LTCBTC
#> 11: btce LTCUSD
#> 12: btce NMCBTC
#> 13: hitbtc BTCEUR
#> 14: hitbtc BTCUSD
#> 15: hitbtc LTCBTC
#> 16: hitbtc LTCUSD
#> 17: kraken BTCEUR
#> 18: kraken BTCGBP
#> 19: kraken BTCLTC
#> 20: kraken ETCBTC
#> 21: kraken ETCEUR
#> 22: kraken ETHBTC
#> 23: kraken ETHEUR
#> 24: kraken LTCEUR
#> market currency_pair
Simple conversion of fiat currencies to BTC (based on blockchain.info rates)
fromBTC(1) # current BTCUSD price
#> [1] 652.1115
toBTC(150, "GBP") # convert 150 GBP to BTC
#> [1] 0.2787416
Query blockchain.info API
# some first wallets btc address details
addr <- blockchain.api.process('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa')
str(addr)
#> Classes 'data.table' and 'data.frame': 1 obs. of 10 variables:
#> $ location : chr "blockchain"
#> $ action : chr "Single Address"
#> $ timestamp : POSIXct, format: "2016-10-25 15:05:13"
#> $ currency : chr "BTC"
#> $ hash : chr "62e907b15cbf27d5425399ebf6f0fb50ebb88f18"
#> $ address : chr "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
#> $ n_tx : int 1063
#> $ total_received: num 66.3
#> $ total_sent : num 0
#> $ final_balance : num 66.3
#> - attr(*, ".internal.selfref")=<externalptr>
# some transaction details
tx <- blockchain.api.process('e5c4de1c70cb6d60db53410e871e9cab6a0ba75404360bf4cda1b993e58d45f8')
str(tx, max.level=1)
#> List of 10
#> $ location : chr "blockchain"
#> $ action : chr "Single Transaction"
#> $ timestamp : POSIXct[1:1], format: "2013-12-22 21:22:14"
#> $ currency : chr "BTC"
#> $ double_spend: logi FALSE
#> $ inputs :Classes 'data.table' and 'data.frame': 1 obs. of 3 variables:
#> ..- attr(*, ".internal.selfref")=<externalptr>
#> $ hash : chr "e5c4de1c70cb6d60db53410e871e9cab6a0ba75404360bf4cda1b993e58d45f8"
#> $ tx_index : int 45125549
#> $ out :Classes 'data.table' and 'data.frame': 2 obs. of 3 variables:
#> ..- attr(*, ".internal.selfref")=<externalptr>
#> $ size : int 258
If you need to query any other data from blockchain API you can use blockchain.api.query
function which allows any methods but do not post-process response.
There is wise advice to do not store assets in a single location. This function assists in the management of assets distributed across different locations.
Wallet manager is quite complex function to track your cryptocurrency balances (and it’s values at a time) on different markets/accounts/addresses.
By default function do not archive it’s results, this must be setup using archive_write=TRUE
args but user should be aware that sensitive data (balance and its sources but not api keys) will be archived locally in working directory as wallet_archive.rds
file. Archive will allow rbtc.plot
to plot historical assets balance over time. Historical balances might be also important for user’s further analysis.
Below is the example of sources definition and wallet manager execution.
# example market.sources
market.sources <- list(
"john smith" = list(market='kraken', key='', secret=''),
"jane smith" = list(market='kraken', key='', secret=''),
"john smith" = list(market='btce', key='', secret=''),
"jane smith" = list(market='btce', key='', secret='')
)
# example blockchain.sources
blockchain.sources <- list(
"john smith" = list(address='')
)
# example manual.sources
manual.sources <- list(
"john smith" = list(location='bitfinex', location_type='market',
currency=c('BTC','USD'), amount=c(0.4,0)),
"john smith" = list(location='fidor', location_type='bank',
currency=c('EUR','USD'), amount=c(20,0)),
"jane smith" = list(location='fidor', location_type='bank',
currency=c('EUR','GBP'), amount=c(10,105))
)
# execute
wallet_dt <- wallet_manager(
market.sources = market.sources,
blockchain.sources = blockchain.sources,
manual.sources = manual.sources,
value_currency = 'USD', # your target currency
rate_priority = c('bitstamp','kraken','hitbtc','btce','bitmarket'), # value rates source priority
archive_write = TRUE # by default FALSE, read ?wallet_manager
)
Function gathers all the wallet balances from specified sources, calculates its values in specified value_currency and returns following structure (value_currency can be also cryptocurrency).
str(wallet_dt)
#> Classes 'data.table' and 'data.frame': 18 obs. of 11 variables:
#> $ wallet_id : int 1409529600 1409529600 1409529600 1409529600 1409529600 1409529600 1409529600 1409529600 1409529600 1409529600 ...
#> $ currency : chr "BTC" "BTC" "BTC" "BTC" ...
#> $ currency_type : chr "crypto" "crypto" "crypto" "crypto" ...
#> $ auth : chr "john smith" "john smith" "jane smith" "john smith" ...
#> $ timestamp : POSIXct, format: "2014-09-01" "2014-09-01" ...
#> $ location : chr "bitfinex" "kraken" "kraken" "btce" ...
#> $ location_type : chr "market" "market" "market" "market" ...
#> $ amount : num 0.4 0.1 0.6 0.4 0.55 10 20 0 0 105 ...
#> $ value_currency: chr "USD" "USD" "USD" "USD" ...
#> $ value_rate : num 475 475 475 475 475 ...
#> $ value : num 189.8 47.5 284.7 189.8 261 ...
#> - attr(*, "sorted")= chr "wallet_id" "currency"
#> - attr(*, ".internal.selfref")=<externalptr>
Simple plot of only recent balances, last wallet manager check on example dummy data.
rbtc.plot(wallet_dt) # type="recent" due to `archive_read=FALSE` so: wallet_dt[,length(unique(wallet_id))]==1
After multiple runs of wallet_manager(..., archive_write=TRUE)
we may plot historical balances.
Simple low-resolution (monthly, n=6) example of wallet manager dummy data over time.
# load archive only
wallet_dt <- wallet_manager(archive_write=FALSE, archive_read=TRUE)
rbtc.plot(wallet_dt) # type="value" due to `archive_read=TRUE`
## # in case of poor legend scaling it might be better to export plot to file
## svg("wallet_manager_value.svg")
## rbtc.plot(wallet_dt)
## dev.off()
Notice the graphs in this html are in svg format so are well zoom-able.