An open API service indexing awesome lists of open source software.

https://github.com/dereckscompany/hyperliquid

R API wrapper to the Hyperliquid decentralized exchange supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, perpetual and spot trading, account management, transfers, and staking, with Ethereum-style wallet signing for the exchange endpoint.
https://github.com/dereckscompany/hyperliquid

pkgdown r rcpp testthat

Last synced: 5 days ago
JSON representation

R API wrapper to the Hyperliquid decentralized exchange supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, perpetual and spot trading, account management, transfers, and staking, with Ethereum-style wallet signing for the exchange endpoint.

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(
hyperliquid[
HyperliquidMarketData,
HyperliquidAccount,
HyperliquidTrading,
HyperliquidTransfers,
HyperliquidStaking,
get_api_keys,
hyperliquid_backfill_klines,
hyperliquid_ohlcv
],
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)

# Signed /exchange actions sign the request body with the wallet key BEFORE the
# request is performed, so a key must still be loadable. We use a THROWAWAY,
# well-known test scalar here: it signs fine, the mock ignores the signature, it
# controls no real funds, and it never touches the network.
.sk_hex <- "0123456789012345678901234567890123456789012345678901234567890123"
KEYS <- get_api_keys(private_key = paste0("0x", .sk_hex))
```

# hyperliquid

[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![R-CMD-check](https://github.com/dereckscompany/hyperliquid/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/dereckscompany/hyperliquid/actions/workflows/R-CMD-check.yaml)

R API wrapper to the [Hyperliquid](https://hyperliquid.xyz) decentralised exchange supporting both synchronous and asynchronous (promise based) operations. Provides R6 classes for market data, perpetual and spot trading, account management, transfers, and staking. Every method returns a flat `data.table`. The `/exchange` endpoint is authenticated by an Ethereum wallet signature, computed in **pure R** via the companion [`ethsign`](https://github.com/dereckscompany/ethsign) package (no compiled code) and verified byte-for-byte against the official Hyperliquid SDKs.

## Disclaimer

This software is provided for educational and research purposes. Trading cryptocurrency carries substantial risk, and you are solely responsible for any orders, transfers, or withdrawals placed through this package. Test against testnet (`testnet = TRUE`) before signing anything on mainnet.

## Design

- **One method, one `data.table`, no list columns.** Every method returns a flat `data.table`; nested API objects are flattened into scalar columns, and heterogeneous rows are stacked with a discriminator column (`side`, `delta_type`, `status`, ...).
- **Sync and async.** Every method works in both modes. `async = TRUE` returns a [promise](https://rstudio.github.io/promises/); otherwise results are returned directly. There is a single sync/async branch point (`hyperliquid_build_request()`).
- **snake_case columns.** API `camelCase` fields become `snake_case` columns; prices and sizes are returned as numerics.
- **Pure-R Ethereum signing via [github.com/dereckscompany/ethsign](https://github.com/dereckscompany/ethsign).** secp256k1 ECDSA, Keccak-256, and EIP-712 come from our standalone pure-R `ethsign` package; the Hyperliquid-specific msgpack action hash lives here. No compiled code, and verified byte-identical to the test vectors of the official [Hyperliquid Python SDK](https://github.com/hyperliquid-dex/hyperliquid-python-sdk) (v0.24.0), which we used as the reference implementation.
- **One host, two paths.** The whole REST API lives behind one host: `/info` (public reads) and `/exchange` (signed writes). The network is chosen by an explicit `testnet` flag, never by URL sniffing, because the network changes the signature itself.

## Installation

```{r install, eval = FALSE}
renv::install("dereckscompany/hyperliquid")

# or, if you use remotes instead of renv:
# install.packages("remotes")
# remotes::install_github("dereckscompany/hyperliquid")
```

## Authentication

Public market-data and account reads (the `/info` endpoint) need no credentials. Signed `/exchange` actions (trading, transfers, staking writes) are authenticated by an **Ethereum wallet signature**: Hyperliquid uses no API keys, only your wallet's secp256k1 private key.

When you call a signed method, the package builds the action, hashes it, and signs it **locally** with your key, then sends only the resulting signature in the request body — your private key never leaves your machine and is never transmitted to Hyperliquid. The low-level signing primitives (secp256k1 ECDSA, Keccak-256, EIP-712) are factored into a small standalone package, [`ethsign`](https://github.com/dereckscompany/ethsign), which this package builds on; read it if you want the mechanics or to reuse Ethereum signing elsewhere. (This is not a cryptography library — it just uses one.)

Store the key as an environment variable in `.Renviron`:

```bash
HYPERLIQUID_PRIVATE_KEY="0x<64-hex-character-secp256k1-key>"
# Optional: the master account address when the key above is an AGENT (API)
# wallet approved to act on its behalf.
HYPERLIQUID_ACCOUNT_ADDRESS="0x"
```

`get_api_keys()` reads those variables (and derives the wallet address from the key):

```{r keys-example, eval = FALSE}
box::use(hyperliquid[get_api_keys])

keys <- get_api_keys()
```

The **same key serves both networks**. The network is selected by the explicit `testnet` flag at construction, not by the URL, because the network changes the signature itself (the phantom-agent source and the `hyperliquidChain` tag are part of what is signed):

```{r testnet-example, eval = FALSE}
# Sign and route against testnet
trading <- HyperliquidTrading$new(testnet = TRUE)
```

## Market Data (no auth)

`HyperliquidMarketData` covers every public `/info` read: perp and spot metadata, asset contexts, mids, the L2 book, candles, funding, and trades.

```{r market}
market <- HyperliquidMarketData$new()

# Perpetual universe metadata
market$get_meta()
```

```{r market-candles}
# OHLCV candles over a time range (sorted ascending by open time)
market$get_candles(
"BTC",
interval = "1h",
start = lubridate::now("UTC") - lubridate::days(1),
end = lubridate::now("UTC")
)
```

```{r market-book}
# L2 order book: both sides stacked long, with a `side` discriminator
market$get_l2_book("BTC")
```

## Account (no auth)

`HyperliquidAccount` reads the full state and history of any account from `/info` by its address. No key is required; pass the address you want to inspect (or let it default to your own wallet when a key is set).

```{r account}
account <- HyperliquidAccount$new()
addr <- "0x010461c14e146ac35fe42271bdc1134ee31c703a"
```

```{r account-positions}
# Open perpetual positions, one row per position
account$get_positions(addr)
```

```{r account-fills}
# Most recent fills
account$get_user_fills(addr)[, .(coin, side, px, sz, dir, closed_pnl, fee)]
```

## Trading (signed)

`HyperliquidTrading` places, modifies, and cancels orders. Every method signs an `/exchange` action with your wallet key. The calls below run against the mock; against the live API they would place and cancel real orders.

```{r trading}
trading <- HyperliquidTrading$new()
```

```{r trading-hidden, echo = FALSE}
trading <- HyperliquidTrading$new(keys = KEYS)
```

```{r trading-place}
# A good-til-cancelled limit bid. Returns one row per resulting status.
order <- trading$place_order(
"BTC",
is_buy = TRUE,
sz = 0.001,
limit_px = 50000,
order_type = list(limit = list(tif = "Gtc"))
)
order[]
```

```{r trading-cancel}
# Cancel a resting order by its order id
trading$cancel_order("BTC", oid = 77738308)
```

## Transfers (signed)

`HyperliquidTransfers` moves collateral between the spot and perp wallets, sends to other addresses, withdraws to the bridge, and moves funds to sub-accounts and vaults.

```{r transfers}
transfers <- HyperliquidTransfers$new()
```

```{r transfers-hidden, echo = FALSE}
transfers <- HyperliquidTransfers$new(keys = KEYS)
```

```{r transfers-class}
# Move $100 of USDC collateral from the spot wallet into the perp wallet
transfers$usd_class_transfer(100, to_perp = TRUE)
```

## Staking (no auth to read)

`HyperliquidStaking` reads a delegator's staking state and history, and (with a key) delegates or undelegates the native token.

```{r staking}
staking <- HyperliquidStaking$new()
staking$get_staking_summary("0x5ac99df645f3414876c816caa18b2d234024b487")
```

## Bulk Backfill

`hyperliquid_backfill_klines()` and `hyperliquid_backfill_funding()` download history for many coins/intervals, writing to a CSV incrementally so progress survives an interruption (re-running resumes each series from its last stored row).

```{r backfill, eval = FALSE}
hyperliquid_backfill_klines(
symbols = c("BTC", "ETH"),
intervals = c("1d", "1h"),
from = lubridate::as_datetime("2024-01-01", tz = "UTC"),
file = "klines.csv"
)
```

A bundled sample dataset, `hyperliquid_ohlcv`, ships daily candles for `"BTC"`, `"ETH"`, and `"SOL"` for examples:

```{r dataset}
head(hyperliquid_ohlcv)
```

## Asynchronous Use

The package is written around promises for non-blocking, event-loop use. Pass `async = TRUE` to any class and its methods return a [promise](https://rstudio.github.io/promises/) instead of a `data.table`. Resolve it with `coro::async()` / `await()` for sequential-looking code, and drive the event loop with [later](https://r-lib.github.io/later/).

```{r async}
market_async <- HyperliquidMarketData$new(async = TRUE)

main <- coro$async(function() {
mids <- await(market_async$get_all_mids())
book <- await(market_async$get_l2_book("BTC"))

print(mids)
print(book)
return(invisible(NULL))
})

main()

# Drain the event loop until every promise has resolved.
while (!later$loop_empty()) {
later$run_now()
}
```

## Available Classes

| Class | Purpose | Auth |
|-------|---------|------|
| `HyperliquidMarketData` | perp/spot meta, asset contexts, mids, L2 book, candles, funding, trades | No |
| `HyperliquidAccount` | positions, margin, spot balances, orders, fills, ledgers, portfolio, fees | No |
| `HyperliquidTrading` | place / modify / cancel orders, market open/close, leverage, margin, approvals | Yes |
| `HyperliquidTransfers` | collateral class transfer, sends, withdrawals, sub-account and vault transfers | Yes |
| `HyperliquidStaking` | delegator summary, delegations, rewards, history, delegate / undelegate | Mixed |

## Author

Dereck Mezquita — [ORCID: 0000-0002-9307-6762](https://orcid.org/0000-0002-9307-6762)

## License

MIT © Dereck Mezquita