FlowscopeDocs

GET /v1/hl/levels

Hyperliquid take-profit, stop-loss and liquidation level runs of one coin over a time range.

Returns the measured take-profit, stop-loss and liquidation levels of one Hyperliquid coin as runs: how much USD notional sat in each price bucket, from when to when. Flowscope draws these as the Hyperliquid heatmaps.

Request

Text
GET /v1/hl/levels/<COIN>?from=<ms>&to=<ms>
HEAD /v1/hl/levels/<COIN>?from=<ms>&to=<ms>
ParameterInTypeDescription
COINPathstringA coin from /v1/hl/coins, case-sensitive (BTC, kPEPE)
fromQueryinteger, msStart of the range, inclusive
toQueryinteger, msEnd of the range, exclusive

Rules for the range:

  • to must be greater than from.
  • The range may span at most 60 days.
  • A missing or unparsable to defaults to a far-future sentinel, and a missing from to one day before to. Always pass both: from alone makes the range longer than 60 days and is rejected.

Response

HeaderValue
Content-Typeapplication/octet-stream
Cache-Controlno-cache

The body is one levels payload in the positions codec: a header with the coin, the bucket size, the newest sample time and the range answered, then the runs, then a checksum.

What the payload contains:

  • Every run that overlaps the range: start < to and start + duration > from.
  • Runs sorted by their end time.
  • Runs still open are included, ending at the newest sample (latest).
  • The payload’s from is the requested from, or later when nothing is recorded before that point. Use it to tell “no levels” from “not recorded yet”.

Each run has a kind:

KindValueMeaning
Long TP0Take-profit of a long: a sell above the price
Short TP1Take-profit of a short: a buy below the price
Long SL2Stop of a long: a sell below the price
Short SL3Stop of a short: a buy above the price
Long liquidation4Liquidation of a long, below the price
Short liquidation5Liquidation of a short, above the price

A run’s bucket covers prices [bucket × bucket_px, (bucket + 1) × bucket_px), and its notional is the total USD of the orders or positions in it.

Status codes

StatusBodyWhen
200Levels payloadThe coin has samples
400bad rangeto <= from, or the range is longer than 60 days
404coin not recordedThe coin is not in --hl-coins
404hyperliquid positions not recordedHyperliquid recording is off
503no sample yetThe coin is recorded but has not been sampled yet

Example

The last hour of BTC levels:

Terminal
TO=$(( $(date +%s) * 1000 ))
FROM=$(( TO - 3600000 ))
curl -fsS -o btc.levels "http://127.0.0.1:8787/v1/hl/levels/BTC?from=$FROM&to=$TO"
ls -l btc.levels

An hour is typically about 12 kB.

The start of the file, annotated:

Text
46 53 50 4c                 "FSPL"
01 00                       version 1
00 00                       reserved
00 00 00 00 00 00 34 40     bucket_px = 20.0
...                         latest, from, to (i64 each)
03 42 54 43                 coin: length 3, "BTC"
...                         run count (u32), runs (21 bytes each), checksum

Decoding in Rust

The flowscope-engine crate has the decoder:

Rust
use flowscope_engine::positions::codec::decode_levels;

let bytes = std::fs::read("btc.levels")?;
let p = decode_levels(&bytes)?;
println!("{} bucket {} from {} to {} latest {}", p.coin, p.bucket_px, p.from, p.to, p.latest);
for r in &p.runs {
    let lo = f64::from(r.bucket) * p.bucket_px;
    println!("{:?} {lo}..{} ${:.0} from {} for {} ms", r.kind, lo + p.bucket_px, r.notional, r.start, r.dur);
}

In any other language, follow the byte layout in Positions codec.

How answers are built

Runs that ended before what memory holds completely come from the coin’s .levels day files; the rest come from memory. Payloads in the day files with a different bucket size are skipped, so every answer uses one grid.

Levels are sampled every 10 seconds. A Flowscope client loads the last 3 hours, then asks for the tail every 10 seconds and for older history six hours at a time.