Uživatelská příručka: Veřejná tržní data
Revolut X poskytuje veřejné koncové body pro čtení konfigurací trhů, cenových tickerů, hloubky trhu (order book) a historických svíček (OHLCV) bez nutnosti autentizace.
Tickery a kniha objednávek
Získání tickerů
Můžete načíst tickery pro všechny aktivní měnové páry nebo pro jeden konkrétní:
from revolut_x import RevolutXClient
client = RevolutXClient()
# Ticker pro jeden pár
ticker = client.get_ticker("BTC-EUR")
print(f"Poslední cena: {ticker['last_price']} EUR")
print(f"Nejlepší Bid: {ticker['bid']} EUR")
print(f"Nejlepší Ask: {ticker['ask']} EUR")
# Všechny tickery
all_tickers = client.get_tickers()
for t in all_tickers[:3]:
print(f"{t['symbol']}: {t.get('last_price')}")
Jeden ticker na pár a region
get_tickers() vrací řádek pro každou kombinaci páru a regionu, nikoli
jeden na pár — většina symbolů je kotována v EEA i UK za odlišné ceny
a objemy, takže se v seznamu objeví dvakrát. Region poznáte z
t['region'] a nespoléhejte se na pořadí seznamu: burza ho mezi
jednotlivými voláními přehazuje.
get_ticker() tuto nejednoznačnost řeší tím, že vrátí nejlikvidnější
kotaci daného symbolu (nejvyšší volume_24h). Pokud potřebujete
konkrétní region, vyfiltrujte si ho z get_tickers() sami:
Získání knihy objednávek (Order Book)
Načte aktuální cenové hladiny nákupních (bids) a prodejních (asks) příkazů.
Každá hladina je slovník — cena je pod klíčem p, množství pod q:
book = client.get_order_book("BTC-EUR")
print("Bids (Kupující):")
for level in book["bids"]:
print(f" {level['p']} EUR x {level['q']} BTC")
print("\nAsks (Prodávající):")
for level in book["asks"]:
print(f" {level['p']} EUR x {level['q']} BTC")
Hloubka knihy je fixní
Burza aktuálně vrací 5 hladin na každou stranu a argument depth
ignoruje — nespoléhejte se tedy na vyžádání hlubší knihy.
Cenové svíčky (OHLCV)
Jak daleko do historie dosáhnete, určuje zvolený interval
Na svíčky se vztahují dva nezávislé limity a jen ten první umí knihovna obejít:
- 1000 svíček na požadavek — limit velikosti requestu.
iter_candles()okno rozsekává a řeší to za vás. - Retence historie — jak dlouho burza dané rozlišení vůbec drží. Jemná rozlišení se promazávají a žádné stránkování je nevrátí.
Dotaz mimo retenci není chyba. API odpoví HTTP 200 s prázdným seznamem,
takže iter_candles() poslušně nevyjede nic a 30denní minutový cyklus
prostě skončí s Streamed 0 candles.
Naměřeno na BTC-EUR s Interval.MIN_1, dotaz na 500 svíček v rostoucím
odstupu:
| Začátek okna | Vrácených svíček |
|---|---|
| 2 dny zpět | 500 |
| 7 dní zpět | 500 |
| 14 dní zpět | 500 |
| 30 dní zpět | 0 |
Minutová data tedy dosáhnou za 14 dní, ale ne na 30, zatímco
Interval.HOUR_1 zvládne 30 dní bez problémů — čím hrubší interval, tím
hlubší historie. Jde o naměřené chování, nikoli o garanci z dokumentace,
takže to berte jako řádový odhad. Pokud se stahování vrátí prázdné,
zkuste nejdřív hrubší interval, než začnete hledat chybu ve vlastním kódu.
Načtení jedné dávky
Burza podporuje časová rozlišení od 1 minuty do 4 týdnů:
from revolut_x import RevolutXClient, Interval
client = RevolutXClient()
# 1-hodinové svíčky
candles = client.get_candles(
"BTC-EUR",
interval=Interval.HOUR_1,
since=1700000000000
)
for c in candles[-5:]:
print(f"Čas: {c['start']} | Open: {c['open']} | High: {c['high']} | Close: {c['close']}")
Podporované formáty času
Každá časová hranice — since / until u svíčkových metod a
start_date / end_date u metod pro obchody — přijímá kteroukoli z těchto hodnot:
| Hodnota | Význam |
|---|---|
1700000000000 |
Unix timestamp v milisekundách, předá se beze změny. |
datetime(2024, 1, 1, tzinfo=timezone.utc) |
Převede se podle vlastního offsetu. |
datetime(2024, 1, 1) |
Naivní hodnota — chápe se jako UTC, nikdy jako lokální čas. |
date(2024, 1, 1) |
Midnight UTC daného dne. |
"2024-01-01T00:00:00Z" |
ISO-8601 string (koncové Z je v pořádku). |
"1700000000000" |
String z 12+ číslic — milisekundy, pro hodnoty přicházející jako text. |
from datetime import datetime, timedelta, timezone
candles = client.get_candles(
"BTC-EUR",
interval=Interval.HOUR_1,
since=datetime.now(timezone.utc) - timedelta(hours=500),
)
Čísla jsou milisekundy, ne sekundy
Čísla se nikdy nepřepočítávají, takže since=int(time.time()) neskončí
chybou — jen dostanete okno začínající v lednu 1970, tedy prázdný nebo
nepoužitelný rozsah. Vynásobte tisícem, nebo prostě předejte datetime a
nechte převod na knihovně. Totéž platí pro odpovědi: candle["start"] i
trade["timestamp"] jsou taky v milisekundách.
Potřebujete stejný převod i jinde ve svém kódu?
from revolut_x import to_timestamp_ms ten helper zpřístupňuje.
Hodnoty přicházející z requestu
Vstup od uživatele si převeďte sami, místo abyste posílali dál surový string — parser v knihovně je pohodlí, ne validační vrstva:
import re
from datetime import datetime
from zoneinfo import ZoneInfo
USER_TZ = ZoneInfo("Europe/Prague")
raw = request.args.get("from", "") # např. '2024-01-01' z <input type="date">
if raw and not re.fullmatch(r"\d{4}-\d{2}-\d{2}", raw):
abort(400, "from must be YYYY-MM-DD")
# Midnight v zóně *uživatele*, ne v UTC.
since = datetime.fromisoformat(raw).replace(tzinfo=USER_TZ) if raw else None
candles = client.get_candles("BTC-EUR", Interval.HOUR_1, since=since)
Tři věci, které kousnou, když hodnota přiteče přímo z prohlížeče:
- String bez offsetu je UTC.
"2024-01-01"je UTC midnight, ale uživatel v Praze, který vybral 1. ledna, myslí 31. prosince 23:00 UTC. Hranici proto postavte v jeho zóně a předejte awaredatetimejako výše. Z JavaScriptu jsou jednoznačnéd.getTime()(milisekundy) id.toISOString()(neseZ);"2024-01-01T13:30"z<input type="datetime-local">jednoznačné není — nemá offset, takže se přečte jako UTC. - Nevyplněné pole je prázdný string, ne chybějící hodnota. HTML formulář
pošle
?from=i u nedotčeného inputu a""vyhodíValueError— proto je v příkladuif raw. - Datum bez oddělovačů se odmítne, nehádá se.
"2024"a"20240101"vyhodíValueErrormísto toho, aby skončily v roce 1970 nebo se naparsovaly jako datum, protože krátká řada číslic je mezi těmi dvěma významy nerozhodnutelná. Za milisekundy se berou až 12+ číslic.
Streamování dlouhých časových oken pomocí iter_candles()
API omezuje jednotlivý požadavek na 1000 svíček. Pro stahování delších kontinuálních období použijte generátor iter_candles(), který automaticky dělí časová okna. Rozšiřuje ale jen to, kolik dat můžete vyžádat — ne to, jak daleko do minulosti data existují (viz varování o retenci výše):
import time
from revolut_x import RevolutXClient, Interval
client = RevolutXClient()
# Stažení 1-minutových dat za posledních 7 dní (10 080 svíček, tedy daleko
# za limitem 1000 na požadavek, ale stále v rámci retence)
start_ms = int(time.time() * 1000) - (7 * 24 * 3600 * 1000)
count = 0
for candle in client.iter_candles("BTC-EUR", Interval.MIN_1, since=start_ms):
count += 1
# Zpracování svíčky...
print(f"Staženo {count} svíček.")
Svíčka je slovník, ne objekt
iter_candles() vrací obyčejné slovníky. print(candle()) skončí chybou
TypeError: 'dict' object is not callable a candle.close vyhodí
AttributeError. Pro výpis celého záznamu použijte print(candle), pro
jednu hodnotu candle["close"]. Všechny OHLCV hodnoty jsou stringy, aby
se neztratila desetinná přesnost — před výpočty je převeďte na Decimal,
nikoli na float.
Čtení jednotlivých polí
from datetime import datetime, timezone
for candle in client.iter_candles("BTC-EUR", Interval.MIN_1, since=start_ms):
opened = datetime.fromtimestamp(candle["start"] / 1000, tz=timezone.utc)
print(
f"{opened:%Y-%m-%d %H:%M} "
f"O={candle['open']} H={candle['high']} "
f"L={candle['low']} C={candle['close']} V={candle['volume']}"
)
Omezení okna z obou stran
Parametrem until streamování ukončíte v pevném bodě místo v aktuálním čase:
from datetime import datetime, time as dtime, timedelta, timezone
midnight = datetime.combine(
datetime.now(timezone.utc).date(), dtime.min, tzinfo=timezone.utc
)
yesterday = midnight - timedelta(days=1)
candles = list(client.iter_candles(
"BTC-EUR",
Interval.MIN_1,
since=yesterday,
until=midnight,
))
print(f"{len(candles)} minutových svíček za {yesterday:%Y-%m-%d}") # ~1441, 2 požadavky
Oba okraje držte relativní k aktuálnímu času, ne na pevných kalendářních datech — fixní okno se s postupem času vysune mimo retenci a začne vracet prázdno.
Oba nepovinné parametry se chovají takto:
| Argumenty | Chování |
|---|---|
since + until |
Prostránkuje celé okno, nic po until se nevrátí. |
pouze since |
Stránkuje od since až do aktuálního času. |
žádný (nebo jen until) |
Bez stránkování — jediný požadavek s nejnovější dávkou. |
Předčasné ukončení
Generátor je líný: další požadavek pošle teprve ve chvíli, kdy si řeknete o další svíčku. Opuštění cyklu tedy zastaví i stahování.
import itertools
from decimal import Decimal
# Nahlédnutí na prvních 10 svíček — jeden požadavek, ne celé stahování
head = itertools.islice(client.iter_candles("BTC-EUR", Interval.MIN_5, since=start_ms), 10)
for candle in head:
print(candle["start"], candle["close"])
# Nebo ukončení podle podmínky
for candle in client.iter_candles("BTC-EUR", Interval.MIN_5, since=start_ms):
if Decimal(candle["high"]) > Decimal("100000"):
print("První průraz nad 100k v", candle["start"])
break
Agregace bez držení všeho v paměti
Deset tisíc minutových svíček je hodně slovníků na jednou. Zpracovávejte je průběžně:
from decimal import Decimal
count = 0
high = Decimal("-Infinity")
low = Decimal("Infinity")
volume = Decimal(0)
for candle in client.iter_candles("BTC-EUR", Interval.MIN_1, since=start_ms):
count += 1
high = max(high, Decimal(candle["high"]))
low = min(low, Decimal(candle["low"]))
volume += Decimal(candle["volume"])
print(f"{count} svíček | rozsah za 7 dní {low}–{high} EUR | objem {volume} BTC")
Zápis přímo do CSV
csv.DictWriter.writerows() přijme generátor rovnou, soubor se tedy zapisuje
průběžně:
import csv
with open("btc_eur_1m.csv", "w", newline="", encoding="utf-8") as fh:
writer = csv.DictWriter(
fh, fieldnames=["start", "open", "high", "low", "close", "volume"]
)
writer.writeheader()
writer.writerows(client.iter_candles("BTC-EUR", Interval.MIN_1, since=start_ms))
Načtení do pandas
import pandas as pd
df = pd.DataFrame(client.iter_candles("BTC-EUR", Interval.HOUR_1, since=start_ms))
df["start"] = pd.to_datetime(df["start"], unit="ms", utc=True)
df = df.set_index("start").astype(float)
df["sma_24"] = df["close"].rolling(24).mean()
print(df.tail())
Kolik je to požadavků a jak dlouho to trvá?
Jeden požadavek pokryje maximálně 1000 svíček a klient mezi požadavky čeká
request_delay sekund (výchozí 0,85 s) a zároveň za vás opakuje odpovědi
HTTP 429. 7 dní minutových dat je tedy 10 080 svíček ≈ 11 požadavků ≈ 10 s
reálného času. Pro dlouhé historie zvolte hrubší interval, nebo si výsledek
jednou uložte na disk a dál čtěte odtud.
Generátor lze projít jen jednou
Po dokončení cyklu je iterátor vyčerpaný — druhý for nad stejným objektem
nevrátí nic. Pokud potřebujete data projít vícekrát, převeďte je na seznam
pomocí list(...).