https://github.com/dereckscompany/coinbase
R API wrapper to the Coinbase Advanced Trade API supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, spot trading, account management, and US futures (CFM), with helpers for tick-to-OHLCV aggregation.
https://github.com/dereckscompany/coinbase
pkgdown r rcpp testthat
Last synced: 5 days ago
JSON representation
R API wrapper to the Coinbase Advanced Trade API supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, spot trading, account management, and US futures (CFM), with helpers for tick-to-OHLCV aggregation.
- Host: GitHub
- URL: https://github.com/dereckscompany/coinbase
- Owner: dereckscompany
- License: other
- Created: 2026-05-30T18:14:38.000Z (28 days ago)
- Default Branch: master
- Last Pushed: 2026-05-30T18:44:54.000Z (28 days ago)
- Last Synced: 2026-05-30T20:12:51.315Z (28 days ago)
- Topics: pkgdown, r, rcpp, testthat
- Language: R
- Size: 73.2 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.Rmd
- License: LICENSE
Awesome Lists containing this project
README
---
output: github_document
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
warning = FALSE,
message = FALSE,
fig.path = "./man/figures/README-",
fig.align = "center",
fig.width = 12,
fig.height = 10,
dpi = 150,
results = "hold",
comment = "#>"
)
# Package symbols used throughout, plus the shared mock HTTP router.
box::use(
coinbase[
CoinbaseMarketData,
CoinbaseAccount,
CoinbaseTrading,
CoinbaseFutures,
trades_to_ohlcv,
coinbase_backfill_trades
],
coro,
later,
./tests/testthat/mock_router[mock_router]
)
# httr2 exposes a native global mock hook: this intercepts every
# req_perform / req_perform_promise call and answers from canned fixtures, so
# the document renders against deterministic data with no network, no real
# credentials, and no funds.
options(httr2_mock = mock_router)
# coinbase signs a JWT with the api_private_key BEFORE the request is performed,
# so a key must still be loadable. We generate a THROWAWAY ephemeral EC P-256
# key for this build: it signs fine, the mock ignores the Authorization header,
# it grants nothing, and it is regenerated on every render.
.sk <- openssl::ec_keygen("P-256")
KEYS <- list(
api_key_name = "organizations/00000000-0000-0000-0000-000000000000/apiKeys/00000000-0000-0000-0000-000000000000",
api_private_key = openssl::write_pem(.sk)
)
```
# coinbase
R API wrapper to the Coinbase Advanced Trade API supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, spot trading, account management, and US futures (CFM), with helpers for tick-to-OHLCV aggregation.
## Disclaimer
This software is provided for educational and research purposes. Trading cryptocurrency carries substantial risk. You are solely responsible for any orders placed through this package. Use the order **preview** methods (which execute nothing) before placing live orders.
## Design Philosophy
- **`data.table` everywhere, no list columns.** Every method returns a flat `data.table`; nested API objects are flattened into scalar columns.
- **Sync and async.** Every method works in both modes. `async = TRUE` returns a [promise][promises::promise]; otherwise results are returned directly. There is a single sync/async branch point.
- **Exact money values.** Prices, sizes, and amounts are transmitted with full precision (never rounded or in scientific notation).
- **Two hosts.** Authenticated trading/account endpoints use the Advanced Trade host (`api.coinbase.com`); deep public market data uses the Exchange host (`api.exchange.coinbase.com`).
## Installation
```{r install, eval = FALSE}
# install.packages("remotes")
remotes::install_github("dereckscompany/coinbase")
```
## Setup
Create API credentials at (download the JSON with a `name` and a `privateKey`). Store them as environment variables in `.Renviron` — the PEM newlines escaped as `\n` on a single line (see `.Renviron.example`):
```bash
COINBASE_API_KEY_NAME="organizations//apiKeys/"
COINBASE_API_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n"
```
Load them with `get_api_keys()` (reads the two environment variables by default):
```{r keys-example, eval = FALSE}
box::use(coinbase[ get_api_keys ])
keys <- get_api_keys()
```
Public market data needs no credentials.
## Quick Start — Market Data (no auth)
```{r market}
market <- CoinbaseMarketData$new()
# Best bid/ask
market$get_ticker("BTC-USD")
```
```{r market-ohlcv}
# OHLCV candles
market$get_ohlcv("BTC-USD", granularity = "1min")
```
```{r market-trades}
# Recent tick trades
market$get_trades("BTC-USD", limit = 100)
```
```{r market-book}
# Order book (top of book, aggregated)
market$get_orderbook("BTC-USD", level = 2)
```
```{r market-stats}
# 24h stats for every product in one call -- rank it yourself for a
# movers / most-active scanner
stats <- market$get_stats()
head(stats[order(-volume)], 5)
```
Deep tick history pages the trades endpoint backwards in time; aggregate the
result to OHLCV at any timeframe with `trades_to_ohlcv()`:
```{r market-history}
ticks <- market$get_trades("BTC-USD", limit = 100)
bars <- trades_to_ohlcv(ticks, interval = 60)
bars[]
```
## Account (auth)
```{r account}
account <- CoinbaseAccount$new()
```
```{r account-hidden, echo = FALSE}
account <- CoinbaseAccount$new(keys = KEYS)
```
```{r account-balances}
# Balances across all wallets (paginated)
account$get_accounts()
```
```{r account-fees}
# Maker/taker fee tier
account$get_fees()
```
```{r account-perms}
account$get_key_permissions()
```
## Trading
Always validate with a **preview** (which places nothing) before a live order.
```{r trading}
trading <- CoinbaseTrading$new()
```
```{r trading-hidden, echo = FALSE}
trading <- CoinbaseTrading$new(keys = KEYS)
```
```{r trading-preview}
# Dry run -- executes nothing
trading$preview_order(
"BTC-USD", "BUY",
list(market_market_ioc = list(quote_size = "10"))
)
```
```{r trading-add}
# Place an order (live -- against the mock here)
order <- trading$add_order(
"BTC-USD", "BUY",
list(limit_limit_gtc = list(base_size = "0.001", limit_price = "70000"))
)
order[]
```
```{r trading-orders}
trading$get_orders(product_ids = "BTC-USD", limit = 10)
```
```{r trading-fills}
trading$get_fills(product_ids = "ETH-USD")
```
```{r trading-edit}
# Edit an open order's price or size (preview first, then apply)
trading$preview_edit_order(order$order_id, price = "71000")
trading$edit_order(order$order_id, price = "71000")
```
```{r trading-cancel}
trading$cancel_orders(order$order_id)
```
## US Futures (CFM) — the short leg
US residents short via CFTC-regulated futures (Coinbase Financial Markets). Futures orders go through the same order endpoint with a futures `product_id`; `CoinbaseFutures` manages the account, positions, and margin.
```{r futures}
futures <- CoinbaseFutures$new()
```
```{r futures-hidden, echo = FALSE}
futures <- CoinbaseFutures$new(keys = KEYS)
```
```{r futures-balance}
futures$get_balance_summary()
```
```{r futures-positions}
futures$get_positions()
```
```{r futures-sweeps}
futures$get_sweeps()
```
```{r futures-short, eval = FALSE}
# Open a short on a futures product (live -- placed through CoinbaseTrading)
# CoinbaseTrading$new()$add_order(
# "BIT-31OCT26-CDE", "SELL",
# list(market_market_ioc = list(base_size = "1"))
# )
```
## Bulk Backfill
```{r backfill, eval = FALSE}
# Walks trades back to `from`, writes CSV incrementally, resumes if re-run.
coinbase_backfill_trades(
symbols = c("BTC-USD", "ETH-USD"),
from = lubridate::as_datetime("2026-05-01", tz = "UTC"),
file = "trades.csv"
)
```
## Asynchronous Use
The package is written around promises for non-blocking, event-loop use (à la
JavaScript). Pass `async = TRUE` to any class and its methods return a
[promise][promises::promise] instead of a `data.table`. Resolve it with
`$then()` chaining or, as recommended, `coro::async()` / `await()` for
sequential-looking code, and drive the event loop with
[later](https://r-lib.github.io/later/).
```{r async}
market_async <- CoinbaseMarketData$new(async = TRUE)
main <- coro$async(function() {
ticker <- await(market_async$get_ticker("BTC-USD"))
ohlcv <- await(market_async$get_ohlcv("BTC-USD", granularity = "1min"))
print(ticker)
print(ohlcv)
})
main()
# Drain the event loop until every promise has resolved.
while (!later$loop_empty()) {
later$run_now()
}
```
## Available Classes
| Class | Purpose | Auth |
|-------|---------|------|
| `CoinbaseMarketData` | products, OHLCV, trades, order book, ticker, deep history | No |
| `CoinbaseAccount` | balances, fees, portfolios, permissions | Yes |
| `CoinbaseTrading` | place / preview / edit / cancel / query orders and fills | Yes |
| `CoinbaseFutures` | US futures (CFM) balances, positions, margin, sweeps | Yes |
## License
MIT © Dereck Mezquita