veloxyz-scraper 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- veloxyz_scraper-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- veloxyz_scraper-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +16 -0
- veloxyz_scraper-0.1.0/.github/workflows/ci.yml +41 -0
- veloxyz_scraper-0.1.0/.gitignore +17 -0
- veloxyz_scraper-0.1.0/.pre-commit-config.yaml +14 -0
- veloxyz_scraper-0.1.0/.python-version +1 -0
- veloxyz_scraper-0.1.0/CHANGELOG.md +36 -0
- veloxyz_scraper-0.1.0/CONTRIBUTING.md +45 -0
- veloxyz_scraper-0.1.0/LICENSE +21 -0
- veloxyz_scraper-0.1.0/PKG-INFO +398 -0
- veloxyz_scraper-0.1.0/README.md +368 -0
- veloxyz_scraper-0.1.0/examples/quickstart.py +73 -0
- veloxyz_scraper-0.1.0/pyproject.toml +76 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/__init__.py +46 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/_http.py +100 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/cli.py +105 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/client.py +747 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/metrics.py +201 -0
- veloxyz_scraper-0.1.0/src/veloxyz_scraper/py.typed +0 -0
- veloxyz_scraper-0.1.0/tests/test_cli.py +63 -0
- veloxyz_scraper-0.1.0/tests/test_client.py +651 -0
- veloxyz_scraper-0.1.0/tests/test_metrics.py +119 -0
- veloxyz_scraper-0.1.0/tests/test_time.py +108 -0
- veloxyz_scraper-0.1.0/uv.lock +599 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Something isn't working as expected
|
|
4
|
+
labels: bug
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**What happened**
|
|
8
|
+
A clear description of the bug.
|
|
9
|
+
|
|
10
|
+
**To reproduce**
|
|
11
|
+
The exact call or CLI command, e.g.:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
scraper.get_metric("BTCUSDT", "aggregated_open_interest", resolution=60, last=100)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Expected vs actual**
|
|
18
|
+
What you expected, and what you got (paste the error / traceback if any).
|
|
19
|
+
|
|
20
|
+
**Environment**
|
|
21
|
+
- veloxyz-scraper version:
|
|
22
|
+
- Python version:
|
|
23
|
+
- OS:
|
|
24
|
+
|
|
25
|
+
**Note**
|
|
26
|
+
Because this reads velo.xyz's undocumented chart endpoints, some issues are caused by an
|
|
27
|
+
upstream change or rate-limit rather than the library. If you can, include the failing
|
|
28
|
+
`/api/m/range?...` URL.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest a metric or capability
|
|
4
|
+
labels: enhancement
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
**What would you like?**
|
|
8
|
+
A clear description of the metric or feature.
|
|
9
|
+
|
|
10
|
+
**New metric?**
|
|
11
|
+
If you're requesting a metric, capture its request from a velo chart (browser dev tools →
|
|
12
|
+
Network → `/api/m/range?...`) and paste the full URL — that's what lets it be added and
|
|
13
|
+
verified.
|
|
14
|
+
|
|
15
|
+
**Why**
|
|
16
|
+
What you'd use it for.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
env:
|
|
16
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
enable-cache: true
|
|
23
|
+
- run: uv sync
|
|
24
|
+
- run: uv run ruff check
|
|
25
|
+
- run: uv run ruff format --check
|
|
26
|
+
- run: uvx basedpyright src/
|
|
27
|
+
- run: uv run pytest
|
|
28
|
+
- run: uv build
|
|
29
|
+
|
|
30
|
+
# Verify the declared dependency floors (pandas>=2.2, etc.) actually work.
|
|
31
|
+
lowest-deps:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
env:
|
|
34
|
+
UV_PYTHON: "3.11"
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: astral-sh/setup-uv@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.11"
|
|
40
|
+
- run: uv sync --resolution lowest-direct
|
|
41
|
+
- run: uv run pytest
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.9.6
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v5.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
- id: trailing-whitespace
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: check-yaml
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-07-09
|
|
9
|
+
|
|
10
|
+
Initial release.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `VeloScraper` client for velo.xyz free chart endpoints.
|
|
15
|
+
- Metrics: `price`, `aggregated_funding_rate`, `aggregated_premium`,
|
|
16
|
+
`aggregated_open_interest`, `aggregated_spot_volume_delta`, `aggregated_volume_delta`,
|
|
17
|
+
`aggregated_liquidations`, and the computed `coinbase_premium`.
|
|
18
|
+
- Chunked fetching (450-candle server cap), retry with exponential backoff and `Retry-After`
|
|
19
|
+
handling on 429/5xx, coins/dollars unit toggle, and `last` N-candle ranges.
|
|
20
|
+
- Checkpoint/resume as a bidirectional cache: a resume backfills a missing head range as well
|
|
21
|
+
as extending the tail, and the returned frame is always exactly the requested window.
|
|
22
|
+
- Checkpoint writes are atomic (temp file + `os.replace`); a sidecar `.meta.json` refuses a
|
|
23
|
+
checkpoint written for a different metric / resolution / exchange.
|
|
24
|
+
- `api_base` override on `VeloScraper` (proxies / tests); the HTTP session is per-thread so
|
|
25
|
+
one client is safe across threads.
|
|
26
|
+
- `METRICS` and `resolve_metric` are exported; a custom `MetricSpec` is the documented
|
|
27
|
+
extension path.
|
|
28
|
+
- `veloscraper` CLI (`fetch` command) and discovery helpers
|
|
29
|
+
(`list_coins`, `list_products`).
|
|
30
|
+
|
|
31
|
+
### Notes
|
|
32
|
+
|
|
33
|
+
- Naive dates/datetimes are consistently interpreted as UTC.
|
|
34
|
+
- `resolution <= 0` is rejected; the `datetime` column stays `datetime64` after a resume;
|
|
35
|
+
`coinbase_premium` rejects unsupported args; `premium_pct` guards against a zero price; and
|
|
36
|
+
an unexpected non-`arr` response surfaces a warning instead of an empty frame.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving veloxyz-scraper.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
The project uses [uv](https://docs.astral.sh/uv/).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uv sync # create the venv and install runtime + dev dependencies
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Checks
|
|
14
|
+
|
|
15
|
+
All of these must pass (CI runs them on every push/PR):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv run pytest # tests — the network is mocked, so no internet is needed
|
|
19
|
+
uv run ruff check # lint
|
|
20
|
+
uv run ruff format --check
|
|
21
|
+
uvx basedpyright src/ # type check
|
|
22
|
+
uv build # sdist + wheel build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Please add or update tests for any behavior change, and keep tests network-free
|
|
26
|
+
(`requests-mock` is available; a live call in the suite is a bug).
|
|
27
|
+
|
|
28
|
+
## Adding a metric
|
|
29
|
+
|
|
30
|
+
velo exposes metrics via a `#`-joined `symbol` payload. Capture the exact request from a
|
|
31
|
+
velo chart (browser dev tools → Network → `/api/m/range?...`), then either:
|
|
32
|
+
|
|
33
|
+
- pass a `MetricSpec(name, tail)` to `get_metric` (no code change needed), or
|
|
34
|
+
- to contribute it upstream, add a **verified** `MetricSpec` to the `METRICS` dict in
|
|
35
|
+
`src/veloxyz_scraper/metrics.py` with a test.
|
|
36
|
+
|
|
37
|
+
## Commit / PR
|
|
38
|
+
|
|
39
|
+
- Keep changes focused; update `CHANGELOG.md` under `## [Unreleased]`.
|
|
40
|
+
- Describe what you verified (which check passed, any live check you ran).
|
|
41
|
+
|
|
42
|
+
## Note
|
|
43
|
+
|
|
44
|
+
This is an unofficial client for velo.xyz's free chart endpoints (undocumented, may change).
|
|
45
|
+
Please keep it key-free and respect Velo's terms of service.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 0xAtasoy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: veloxyz-scraper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: velo.xyz market-data scraper (price, funding, open interest, premium, spot CVD)
|
|
5
|
+
Project-URL: Homepage, https://github.com/0xAtasoy/veloxyz-scraper
|
|
6
|
+
Project-URL: Repository, https://github.com/0xAtasoy/veloxyz-scraper
|
|
7
|
+
Project-URL: Issues, https://github.com/0xAtasoy/veloxyz-scraper/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/0xAtasoy/veloxyz-scraper/blob/main/CHANGELOG.md
|
|
9
|
+
Author: 0xAtasoy
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: crypto,funding-rate,market-data,open-interest,scraper,velo
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: pandas>=2.2
|
|
25
|
+
Requires-Dist: requests>=2.32
|
|
26
|
+
Requires-Dist: tenacity>=9.0.0
|
|
27
|
+
Requires-Dist: tqdm>=4.66.0
|
|
28
|
+
Requires-Dist: typer>=0.15.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# veloxyz-scraper
|
|
32
|
+
|
|
33
|
+
A **cross-exchange aggregate time-series** scraper for [velo.xyz](https://velo.xyz) market
|
|
34
|
+
data (aggregated funding rate, aggregated premium, aggregated open interest, and more). It
|
|
35
|
+
automatically splits long ranges into chunks, retries on transient errors, and resumes
|
|
36
|
+
interrupted fetches where they stopped.
|
|
37
|
+
|
|
38
|
+
> **Unofficial.** Not affiliated with Velo. It reads velo.xyz's free, undocumented chart
|
|
39
|
+
> endpoints (the ones the [charts](https://velo.xyz/chart) use) — these can change or
|
|
40
|
+
> rate-limit without notice. Please respect Velo's terms of service. Velo's official,
|
|
41
|
+
> supported, authenticated API is documented at [docs.velo.xyz](https://docs.velo.xyz/) and
|
|
42
|
+
> wrapped by the `velodata` package — a **different** API from the one used here.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Robust fetching:** shared `requests.Session`, timeout, and retry with exponential
|
|
47
|
+
backoff (429 / 5xx / timeout / connection error). Long ranges are split into chunks of
|
|
48
|
+
450 candles (the endpoint's hard per-request cap).
|
|
49
|
+
- **Discovery:** `list_coins()` and `list_products()` to see what velo supports.
|
|
50
|
+
- **Thread-safe:** one `VeloScraper` can be shared across threads (per-thread HTTP session).
|
|
51
|
+
- **Checkpoint + resume:** each chunk is written to disk; if a fetch is interrupted it
|
|
52
|
+
continues from where it stopped.
|
|
53
|
+
- **Generic metric support:** `price`, `aggregated_funding_rate`, `aggregated_premium`,
|
|
54
|
+
`aggregated_open_interest`, `aggregated_spot_volume_delta`, `aggregated_volume_delta`,
|
|
55
|
+
`aggregated_liquidations`, and the computed `coinbase_premium` (scalar / OHLC / OHLCV
|
|
56
|
+
columns detected automatically); extensible via a custom `MetricSpec`.
|
|
57
|
+
- **Coins or dollars:** `unit="dollars"` for `open_interest` / `*_volume_delta` /
|
|
58
|
+
`liquidations`.
|
|
59
|
+
- **Flexible resolution:** minutes (`1, 5, 15, 60, 240 ...`) and daily/weekly (`1D`, `1W`).
|
|
60
|
+
- **CLI + library:** both a `veloscraper` command and `import veloxyz_scraper`.
|
|
61
|
+
- Returns a `pandas.DataFrame`, with an optional `tqdm` progress bar.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
With [uv](https://docs.astral.sh/uv/):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uv sync # development environment (includes dev dependencies)
|
|
69
|
+
# or as a dependency in a project:
|
|
70
|
+
uv add veloxyz-scraper
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
With pip:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install .
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Library usage
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from veloxyz_scraper import VeloScraper
|
|
83
|
+
|
|
84
|
+
scraper = VeloScraper()
|
|
85
|
+
|
|
86
|
+
# OI-weighted, cross-exchange aggregate funding rate (hourly):
|
|
87
|
+
df = scraper.get_metric(
|
|
88
|
+
"BTCUSDT",
|
|
89
|
+
"aggregated_funding_rate",
|
|
90
|
+
resolution=60, # minutes; 60 = hourly
|
|
91
|
+
begin="2024-01-01",
|
|
92
|
+
end="2024-02-01",
|
|
93
|
+
)
|
|
94
|
+
print(df.head())
|
|
95
|
+
# datetime timestamp aggregated_funding_rate
|
|
96
|
+
# 0 2024-01-01 00:00:00 1704067200 0.000080
|
|
97
|
+
|
|
98
|
+
# Generic API — any metric:
|
|
99
|
+
oi = scraper.get_metric("ETHUSDT", "aggregated_open_interest", resolution=240,
|
|
100
|
+
begin="2024-06-01", end="2024-06-10")
|
|
101
|
+
# returns OHLC: datetime, timestamp, open, high, low, close
|
|
102
|
+
|
|
103
|
+
# Raw price candles (daily) — the exchange param picks which exchange's candles:
|
|
104
|
+
px = scraper.get_metric("BTCUSDT", "price", resolution="1D",
|
|
105
|
+
begin="2024-01-01", end="2024-06-01", exchange="binance-futures")
|
|
106
|
+
# returns price OHLCV: datetime, timestamp, open, high, low, close, volume
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`begin`/`end` accept a date `str` (`"2024-01-01"`), an epoch `int` (seconds or ms), or a
|
|
110
|
+
`datetime`. If omitted, `end=now` and `begin=end-30 days`. **Naive dates/datetimes are
|
|
111
|
+
interpreted as UTC** (so the same input gives the same result on any machine).
|
|
112
|
+
|
|
113
|
+
**Last N candles** — use `last` to compute `begin` automatically
|
|
114
|
+
(`begin = end - last*resolution`):
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
# The last 1000 hourly candles:
|
|
118
|
+
df = scraper.get_metric("BTCUSDT", "aggregated_funding_rate", resolution=60, last=1000)
|
|
119
|
+
|
|
120
|
+
# Scales with resolution: the last 500 four-hour candles
|
|
121
|
+
oi = scraper.get_metric("BTCUSDT", "aggregated_open_interest", resolution=240, last=500)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`last` is a **candle count** (int) and works together with `resolution`; it cannot be
|
|
125
|
+
combined with `begin`. velo data starts at **2021-01-01 00:00:00 UTC**; if `begin` (explicit,
|
|
126
|
+
derived from `last`, or the default) is earlier than that, it is clamped there automatically
|
|
127
|
+
(`DATA_START_MS`).
|
|
128
|
+
|
|
129
|
+
`resolution`: minutes as an `int` (`1, 5, 10, 15, 30, 60, 120, 240, 360, 720`) or `"1D"` /
|
|
130
|
+
`"1W"` for daily/weekly. `begin`/`end` are aligned to the resolution grid (begin floored,
|
|
131
|
+
end ceiled) so the bars containing them are included.
|
|
132
|
+
|
|
133
|
+
### Discovery
|
|
134
|
+
|
|
135
|
+
Look up what velo supports without hardcoding symbols:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
"BTC" in scraper.list_coins() # coins available for aggregation (list[str])
|
|
139
|
+
|
|
140
|
+
products = scraper.list_products() # futures products per exchange (DataFrame)
|
|
141
|
+
products[products["coin"] == "BTC"] # every BTC perp: exchange, product, coin, min
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Checkpoint / resume
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
df = scraper.get_metric(
|
|
148
|
+
"BTCUSDT", "aggregated_funding_rate", 60, "2021-01-01", "2026-01-01",
|
|
149
|
+
checkpoint="btc_funding.csv", # each chunk is written here
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
If the fetch is interrupted, calling it again with the same `checkpoint` path resumes from
|
|
154
|
+
where it stopped. The last bar of the previous run is **re-fetched and overwritten**, so a
|
|
155
|
+
still-forming / partial bar gets finalized on the next run; only the current (still-open) bar
|
|
156
|
+
is ever provisional.
|
|
157
|
+
|
|
158
|
+
## CLI
|
|
159
|
+
|
|
160
|
+
Every `fetch` command requires `--out` and `--metric`.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
veloscraper fetch --metric <METRIC> --out <FILE.csv>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`<METRIC>`: `price` · `aggregated_funding_rate` · `aggregated_premium` ·
|
|
167
|
+
`aggregated_open_interest` · `aggregated_spot_volume_delta` · `aggregated_volume_delta` ·
|
|
168
|
+
`aggregated_liquidations` · `coinbase_premium`
|
|
169
|
+
|
|
170
|
+
`--unit dollars` switches `open_interest` / `*_volume_delta` / `liquidations` to USD. See all
|
|
171
|
+
flags with `veloscraper fetch --help`.
|
|
172
|
+
|
|
173
|
+
### Basics
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# BTCUSDT hourly funding rate, last 30 days (default range)
|
|
177
|
+
veloscraper fetch --metric aggregated_funding_rate --out btc.csv
|
|
178
|
+
|
|
179
|
+
# A different symbol
|
|
180
|
+
veloscraper fetch --ticker ETHUSDT --metric aggregated_funding_rate --out eth.csv
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Metrics
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
veloscraper fetch --metric price --out btc_price.csv # OHLCV
|
|
187
|
+
veloscraper fetch --metric aggregated_open_interest --out btc_oi.csv # OHLC
|
|
188
|
+
veloscraper fetch --metric aggregated_premium --out btc_prem.csv
|
|
189
|
+
veloscraper fetch --metric aggregated_spot_volume_delta --out btc_spotdelta.csv
|
|
190
|
+
veloscraper fetch --metric aggregated_volume_delta --out btc_voldelta.csv
|
|
191
|
+
veloscraper fetch --metric aggregated_liquidations --out btc_liq.csv # short + long
|
|
192
|
+
veloscraper fetch --metric coinbase_premium --out btc_cbp.csv # computed
|
|
193
|
+
|
|
194
|
+
# Dollar-denominated (open_interest / *_volume_delta / liquidations):
|
|
195
|
+
veloscraper fetch --metric aggregated_open_interest --unit dollars --out btc_oi_usd.csv
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Time range
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# Explicit start/end
|
|
202
|
+
veloscraper fetch --metric aggregated_funding_rate --begin 2024-01-01 --end 2024-02-01 --out btc_jan.csv
|
|
203
|
+
|
|
204
|
+
# Only a start (end = now)
|
|
205
|
+
veloscraper fetch --metric aggregated_funding_rate --begin 2024-06-01 --out btc_since_jun.csv
|
|
206
|
+
|
|
207
|
+
# A begin that is too early is clamped to 2021-01-01 (where the data starts)
|
|
208
|
+
veloscraper fetch --metric aggregated_funding_rate --begin 2015-01-01 --out btc_all.csv
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Last N candles (`--last`, works with `--resolution`)
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# The last 1000 hourly candles
|
|
215
|
+
veloscraper fetch --metric aggregated_funding_rate --resolution 60 --last 1000 --out btc_1000h.csv
|
|
216
|
+
|
|
217
|
+
# The last 500 four-hour candles
|
|
218
|
+
veloscraper fetch --metric price --resolution 240 --last 500 --out btc_500x4h.csv
|
|
219
|
+
|
|
220
|
+
# The last 90 daily candles
|
|
221
|
+
veloscraper fetch --metric aggregated_open_interest --resolution 1D --last 90 --out btc_90d.csv
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Resolution
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Minutes: 1, 5, 10, 15, 30, 60, 120, 240, 360, 720
|
|
228
|
+
veloscraper fetch --metric price --resolution 15 --last 200 --out btc_15m.csv
|
|
229
|
+
|
|
230
|
+
# Daily / weekly
|
|
231
|
+
veloscraper fetch --metric aggregated_funding_rate --resolution 1D --begin 2021-01-01 --out btc_daily.csv
|
|
232
|
+
veloscraper fetch --metric aggregated_funding_rate --resolution 1W --begin 2021-01-01 --out btc_weekly.csv
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `price` + exchange (`--exchange` is only for `price`)
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
veloscraper fetch --metric price --exchange bybit --last 168 --out bybit_price.csv
|
|
239
|
+
veloscraper fetch --metric price --exchange coinbase --ticker BTC-USD --last 168 --out cb_price.csv
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Override the aggregation exchanges
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
veloscraper fetch --metric aggregated_funding_rate \
|
|
246
|
+
--exchanges binance-futures,bybit,okex-swap --last 168 --out btc_3exch.csv
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Checkpoint / resume
|
|
250
|
+
|
|
251
|
+
`--out` acts as a checkpoint by default (disable with `--no-resume`).
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# A long historical fetch — if it is interrupted, the SAME command resumes it
|
|
255
|
+
veloscraper fetch --metric aggregated_funding_rate --begin 2021-01-01 --resolution 60 --out btc_full.csv
|
|
256
|
+
|
|
257
|
+
# Disable resume (start over each time)
|
|
258
|
+
veloscraper fetch --metric price --last 500 --no-resume --out btc_fresh.csv
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Other
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
veloscraper fetch --metric price --last 100 --out btc.csv --quiet # silence progress/logs
|
|
265
|
+
veloscraper --version
|
|
266
|
+
veloscraper fetch --help
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Common mistakes
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# ✗ --metric is required
|
|
273
|
+
veloscraper fetch --out btc.csv
|
|
274
|
+
# -> Missing option '--metric'.
|
|
275
|
+
|
|
276
|
+
# ✗ begin and last cannot be used together
|
|
277
|
+
veloscraper fetch --metric price --begin 2024-01-01 --last 100 --out x.csv
|
|
278
|
+
# -> ValueError: 'begin' and 'last' cannot be used together.
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Examples
|
|
282
|
+
|
|
283
|
+
- [`examples/quickstart.py`](examples/quickstart.py) — fetch every metric and write a
|
|
284
|
+
separate CSV for each.
|
|
285
|
+
|
|
286
|
+
## Development
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
uv run pytest # tests (network is mocked, no internet needed)
|
|
290
|
+
uv run ruff check # lint
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Supported metrics
|
|
294
|
+
|
|
295
|
+
| Metric | Output shape | Columns | Exchanges |
|
|
296
|
+
|----------------------------|--------------|--------------------------------------------|-----------|
|
|
297
|
+
| `price` | OHLCV | `open`, `high`, `low`, `close`, `volume` | single (exchange param) |
|
|
298
|
+
| `aggregated_funding_rate` | scalar | `aggregated_funding_rate` | futures |
|
|
299
|
+
| `aggregated_premium` | scalar | `aggregated_premium` (OI-weighted) | futures |
|
|
300
|
+
| `aggregated_open_interest` | OHLC | `open`, `high`, `low`, `close` | futures |
|
|
301
|
+
| `aggregated_spot_volume_delta` | scalar | `aggregated_spot_volume_delta` (spot buy−sell) | spot |
|
|
302
|
+
| `aggregated_volume_delta` | scalar | `aggregated_volume_delta` (futures buy−sell) | futures |
|
|
303
|
+
| `aggregated_liquidations` | 2 values | `short_liquidations`, `long_liquidations` | futures |
|
|
304
|
+
| `coinbase_premium` | computed | `binance_close`, `coinbase_close`, `premium`, `premium_pct` | binance + coinbase spot |
|
|
305
|
+
|
|
306
|
+
Metric names follow the velo.xyz interface (`aggregated_*`). All are verified against the
|
|
307
|
+
real API.
|
|
308
|
+
|
|
309
|
+
**Coins vs dollars.** `open_interest`, `spot_volume_delta`, `volume_delta` and
|
|
310
|
+
`liquidations` accept `unit="coins"` (default) or `unit="dollars"`:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
oi_usd = scraper.get_metric("BTCUSDT", "aggregated_open_interest", "1D", unit="dollars")
|
|
314
|
+
liq = scraper.get_metric("BTCUSDT", "aggregated_liquidations", 60, last=168)
|
|
315
|
+
# liq columns: short_liquidations (green), long_liquidations (red)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The unit is not encoded in the column names or file name (like resolution), so track which
|
|
319
|
+
one you fetched. Rates (`funding_rate` / `premium`) and `price` have no coins/dollars unit.
|
|
320
|
+
|
|
321
|
+
- **`aggregated_premium`**: of velo.xyz's three premiums, this is the *aggregated Premium*
|
|
322
|
+
(OI-weighted, cross-exchange). The single-exchange premium is a separate metric (not added
|
|
323
|
+
yet).
|
|
324
|
+
- **`aggregated_open_interest`**: since OI is a *level* metric, the velo API returns
|
|
325
|
+
degenerate candles at low resolutions (e.g. hourly): `open == close`, with `high`/`low`
|
|
326
|
+
from the intra-hour range. To match velo's own chart, `open` is derived from the
|
|
327
|
+
**previous close** on degenerate rows. This fix applies only to rows where `open == close`
|
|
328
|
+
and only when `resolution < 120` (120+ is already real OHLC); the first row of the series
|
|
329
|
+
keeps the API value. `close`/`high`/`low` are never touched.
|
|
330
|
+
- **`aggregated_spot_volume_delta` / `aggregated_volume_delta`**: **per-bar** volume delta
|
|
331
|
+
(buy − sell), not a running total — velo's "Delta" and "Cumulative Delta" return the same
|
|
332
|
+
values; the cumulative line is a UI-only view. Take `.cumsum()` yourself for a running CVD.
|
|
333
|
+
- **`aggregated_liquidations`**: two values per row — `short_liquidations` (velo's green
|
|
334
|
+
series) then `long_liquidations` (red). Use `unit="dollars"` for USD notional.
|
|
335
|
+
- **`coinbase_premium`**: not a single API metric but a **computed** one — the spot price
|
|
336
|
+
difference between coinbase (`BTC-USD`) and binance (`BTCUSDT`). Two requests are made and
|
|
337
|
+
aligned on timestamp:
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
cp = scraper.get_coinbase_premium("BTCUSDT", resolution=60,
|
|
341
|
+
begin="2024-01-01", end="2024-02-01")
|
|
342
|
+
# datetime, timestamp, binance_close, coinbase_close, premium, premium_pct
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`get_metric("BTCUSDT", "coinbase_premium", ...)` routes to the same result.
|
|
346
|
+
|
|
347
|
+
### Custom metrics
|
|
348
|
+
|
|
349
|
+
You don't need to fork the library to add a metric — pass a `MetricSpec` (the symbol tail,
|
|
350
|
+
verified from a velo chart request) straight to `get_metric`:
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
from veloxyz_scraper import MetricSpec, VeloScraper
|
|
354
|
+
|
|
355
|
+
spec = MetricSpec("my_metric", "some#velo#tail") # tail after {ticker}#{exchanges}#
|
|
356
|
+
df = VeloScraper().get_metric("BTCUSDT", spec, resolution=60, last=100)
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The built-in presets are exported read-only as `METRICS` (`from veloxyz_scraper import
|
|
360
|
+
METRICS`) for discovery. To contribute a verified preset upstream, add it to the `METRICS`
|
|
361
|
+
dict in `src/veloxyz_scraper/metrics.py`.
|
|
362
|
+
|
|
363
|
+
## Per-exchange data availability
|
|
364
|
+
|
|
365
|
+
An aggregate combines several exchanges, but **each exchange only contributes from the date
|
|
366
|
+
its own data begins** — so the composition of the aggregate changes over time. The dates
|
|
367
|
+
below were measured (BTCUSDT) by requesting each exchange on its own
|
|
368
|
+
(`symbol=BTCUSDT#<exchange>#<metric-tail>`) and binary-searching the first day that returns
|
|
369
|
+
data.
|
|
370
|
+
|
|
371
|
+
**Futures exchanges** — same for `aggregated_funding_rate`, `aggregated_premium`,
|
|
372
|
+
`aggregated_open_interest`:
|
|
373
|
+
|
|
374
|
+
| Exchange | Included from |
|
|
375
|
+
|-----------------------|---------------|
|
|
376
|
+
| binance-futures | ≤ 2021-01-01 (data start) |
|
|
377
|
+
| bybit | ≤ 2021-01-01 |
|
|
378
|
+
| okex-swap | ≤ 2021-01-01 |
|
|
379
|
+
| deribit | ≤ 2021-01-01 |
|
|
380
|
+
| binance-coin-margin | ≤ 2021-01-01 |
|
|
381
|
+
| bybit-coin-margin | ≤ 2021-01-01 |
|
|
382
|
+
| okex-coin-margin | ≤ 2021-01-01 |
|
|
383
|
+
| **hyperliquid** | **2024-06-11** |
|
|
384
|
+
|
|
385
|
+
**Spot exchanges** — `aggregated_spot_volume_delta`:
|
|
386
|
+
|
|
387
|
+
| Exchange | Included from |
|
|
388
|
+
|-------------|---------------|
|
|
389
|
+
| binance | ≤ 2021-01-01 |
|
|
390
|
+
| coinbase | ≤ 2021-01-01 |
|
|
391
|
+
| **bybit-spot** | **2024-09-25** |
|
|
392
|
+
| **okex** | **2024-09-26** |
|
|
393
|
+
|
|
394
|
+
> **Heads-up for modeling:** because a new venue joins mid-series, the aggregate can show a
|
|
395
|
+
> **step change** when it is added (e.g. aggregated open interest jumps up around 2024-06-11
|
|
396
|
+
> when hyperliquid enters; spot CVD around 2024-09-25 when bybit-spot/okex enter). This is
|
|
397
|
+
> not a data error — it is a composition change. If a constant-composition series matters for
|
|
398
|
+
> your model, restrict `--exchanges` to venues present over your whole window.
|