← Kembali ke artikel
March 4, 2026
5 menit baca

QuestDB untuk Perdagangan Algoritmik: Ekstensi SQL yang Mengubah Permainan

QuestDB untuk Perdagangan Algoritmik: Ekstensi SQL yang Mengubah Permainan
#QuestDB
#SQL
#ASOF JOIN
#SAMPLE BY
#time-series
#perdagangan algoritmik

Bagian 2 dari 3 — juga tersedia dalam RU · ZH

Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan pendidikan dan informasi serta tidak merupakan saran keuangan, investasi, atau perdagangan. Perdagangan mata uang kripto melibatkan risiko kerugian yang signifikan.


Selamat datang di Bagian 2 dari seri QuestDB kami. Di Bagian 1, kami membahas arsitektur penyimpanan tiga tingkat dan prinsip desain skema. Sekarang kita akan masuk ke kumpulan fitur yang benar-benar membedakan — ekstensi SQL yang membuat QuestDB terasa seolah dirancang oleh trader, untuk trader.

SQL standar dibuat pada tahun 1970-an untuk data relasional. SQL tidak mengenal waktu sebagai konsep kelas pertama. Setiap operasi time-series di PostgreSQL atau MySQL memerlukan solusi yang bertele-tele — fungsi window, lateral join, CTE yang ditumpuk tiga tingkat. Ekstensi QuestDB meruntuhkan kueri multi-paragraf ini menjadi pernyataan tunggal yang ekspresif.

Mari kita bahas satu per satu dengan contoh perdagangan nyata.

SAMPLE BY: perdagangan mentah diagregasikan menjadi lilin OHLCV

SAMPLE BY: Agregasi Time-Bucket Asli

Jika ada satu kueri yang dijalankan setiap sistem perdagangan lebih dari yang lain, itu adalah agregasi OHLCV — mengubah perdagangan mentah menjadi data candlestick. Dalam SQL standar, Anda akan menulis sesuatu seperti:

-- SQL Standar: verbose dan lambat
SELECT
  date_trunc('minute', timestamp) AS bucket,
  symbol,
  (array_agg(price ORDER BY timestamp))[1] AS open,
  max(price) AS high,
  min(price) AS low,
  (array_agg(price ORDER BY timestamp DESC))[1] AS close,
  sum(quantity) AS volume
FROM trades
WHERE timestamp >= now() - interval '1 hour'
GROUP BY bucket, symbol
ORDER BY bucket;

Di QuestDB, ini menjadi:

SELECT timestamp, symbol,
  first(price) AS open,
  max(price) AS high,
  min(price) AS low,
  last(price) AS close,
  sum(quantity) AS volume
FROM trades
WHERE timestamp IN today()
SAMPLE BY 1m;

Hanya itu. SAMPLE BY 1m memberi tahu QuestDB untuk mengelompokkan data ke dalam interval 1 menit, dan first() / last() adalah fungsi agregasi asli yang menghormati urutan waktu di dalam setiap bucket. Tidak ada date_trunc, tidak ada akrobat array_agg, tidak ada GROUP BY eksplisit.

Interval yang tersedia fleksibel: 1s, 5s, 15m, 1h, 1d, 7d — kombinasi unit waktu apa pun. Untuk pasar kripto yang tidak pernah tidur, Anda dapat menyelaraskan ke batas kalender dengan kesadaran zona waktu:

SAMPLE BY 1d ALIGN TO CALENDAR TIME ZONE 'UTC';

FILL: Menangani Celah dalam Data

Pasar nyata memiliki celah — pasangan berlikuiditas rendah mungkin tidak diperdagangkan selama beberapa menit atau jam. SAMPLE BY mendukung beberapa strategi FILL:

-- Isi dengan nilai sebelumnya (forward fill — umum dalam keuangan)
SAMPLE BY 15m FILL(PREV);

-- Isi dengan interpolasi linear
SAMPLE BY 15m FILL(LINEAR);

-- Isi dengan konstanta
SAMPLE BY 15m FILL(0);

-- Tanpa isi — kembalikan NULL (default)
SAMPLE BY 15m FILL(NONE);

FILL(PREV) adalah andalan untuk dashboard perdagangan — jika tidak ada perdagangan yang terjadi dalam bucket 15 menit, bawa maju harga terakhir yang diketahui. FILL(LINEAR) lebih tepat untuk sinyal kontinu seperti tingkat pendanaan atau suku bunga.

Penyelarasan temporal ASOF JOIN antara perdagangan dan kuotasi

ASOF JOIN: "Lakukan Apa yang Saya Maksud" dari Penyelarasan Data Pasar

Ini adalah mahkota QuestDB, dan jika Anda pernah bekerja dengan data pasar, Anda akan segera memahami alasannya.

Masalah mendasarnya: Anda memiliki perdagangan di satu tabel dan kuotasi (bid/ask) di tabel lain. Anda ingin mengetahui kuotasi yang berlaku pada saat tepat setiap perdagangan dieksekusi. Dalam basis data biasa, cap waktu hampir tidak pernah sejajar sempurna — perdagangan pada 12:00:00.123 perlu dicocokkan dengan kuotasi pada 12:00:00.098, bukan 12:00:00.201.

ASOF JOIN menyelesaikan ini dalam satu baris:

SELECT trades.*, quotes.bid, quotes.ask
FROM trades
ASOF JOIN quotes ON (symbol);

Untuk setiap baris dalam trades, QuestDB menemukan baris dalam quotes dengan cap waktu terbaru yang kurang dari atau sama dengan cap waktu perdagangan, dicocokkan pada kolom symbol. Tidak ada subkueri berkorelasi. Tidak ada fungsi window. Tidak ada logika tingkat aplikasi.

Ini adalah fondasi dari Transaction Cost Analysis (TCA) — membandingkan harga eksekusi Anda dengan pasar yang berlaku pada saat eksekusi. Di PostgreSQL, ekuivalen ini memerlukan LATERAL JOIN dengan ORDER BY dan LIMIT 1 untuk setiap baris, yang secara magnitudo lebih lambat pada dataset besar.

TOLERANCE: Mencegah Join Data Basi

Berikut adalah kehalusan yang memisahkan implementasi mainan dari sistem produksi. Bagaimana jika kuotasi untuk aset yang jarang diperdagangkan berumur 5 menit pada saat perdagangan? Di pasar yang bergejolak, kuotasi berumur 5 menit pada dasarnya adalah sampah. ASOF JOIN default masih akan menggunakannya — ia menemukan kecocokan terbaru, tidak peduli seberapa basi.

Klausul TOLERANCE QuestDB memperbaiki ini:

SELECT trades.*, quotes.bid, quotes.ask
FROM trades
ASOF JOIN quotes ON (symbol)
TOLERANCE 1s;

Sekarang, jika tidak ada kuotasi yang cocok dalam 1 detik dari perdagangan, join mengembalikan NULL sebagai ganti data basi. Ini sangat penting untuk TCA yang akurat dan untuk analitik apa pun di mana kesegaran data penting.

Sebagai bonus, TOLERANCE dapat secara signifikan meningkatkan kinerja kueri. Tanpanya, mesin mungkin memindai jauh ke belakang ke dalam tabel kuotasi mencari kecocokan. Dengan TOLERANCE, mesin dapat menghentikan pemindaian mundur lebih awal setelah rekaman terlalu lama untuk memenuhi syarat.

LT JOIN dan SPLICE JOIN

Dua variasi yang perlu diketahui: LT JOIN seperti ASOF JOIN tetapi cocok tepat sebelum cap waktu (tidak pernah sama). Ini berguna ketika Anda perlu menghindari bias look-ahead dalam backtesting — Anda ingin kuotasi yang ada sebelum perdagangan Anda, bukan yang tiba pada mikrodetik yang sama.

SPLICE JOIN adalah ASOF penuh di kedua arah: untuk setiap rekaman di tabel kiri ia menemukan rekaman tabel kanan yang berlaku, dan untuk setiap rekaman di tabel kanan ia menemukan rekaman tabel kiri yang berlaku. Hasilnya adalah garis waktu gabungan yang saling terjalin dari kedua sumber data. Ini sangat berguna untuk membuat garis waktu peristiwa terpadu dari beberapa aliran data.

HORIZON JOIN: Analisis Pasca-Perdagangan dalam Satu Kueri

Diperkenalkan di QuestDB 9.3.3, HORIZON JOIN dibuat khusus untuk analisis markout — landasan penilaian kualitas eksekusi dan penelitian mikrostruktur pasar.

Pertanyaan yang dijawabnya: "Setelah perdagangan dieksekusi, bagaimana harga berkembang selama N detik berikutnya?" Secara tradisional, ini memerlukan self-join, UNION ALL di beberapa kueri ASOF, atau mendorong logika ke kode aplikasi. HORIZON JOIN meruntuhkan semuanya:

SELECT h.offset / 1000000 AS offset_sec,
  avg(mid.price - fill.price) AS avg_markout
FROM fills
HORIZON JOIN mid_prices ON (symbol)
RANGE BETWEEN 0 AND 60s STEP 1s;

Ini menghitung pergerakan harga rata-rata pada interval 1 detik hingga 60 detik setelah setiap fill. Mesin menangani komputasi offset waktu, pencocokan ASOF di setiap titik horizon, dan agregasi — semuanya dalam satu lintasan.

Untuk horizon non-seragam — atau untuk melihat sebelum peristiwa — gunakan sintaks LIST:

HORIZON JOIN mid_prices ON (symbol)
LIST (-5s, -1s, 0, 1s, 5s, 30s, 60s);

Ini memberi Anda kurva markout sebelum dan sesudah perdagangan. Dikombinasikan dengan penyaringan berdasarkan venue, strategi, atau ukuran order, Anda dapat membangun kerangka kualitas eksekusi lengkap di dalam basis data.

Buku masak QuestDB mencakup lima pola analisis pasca-perdagangan yang dibangun di atas HORIZON JOIN: analisis slippage (eksekusi vs. harga mid), kurva markout, implementation shortfall (dekomposisi Perold), penilaian venue untuk smart order routing, dan deteksi toksisitas aliran (VPIN). Setiap satu berjalan pada demo langsung mereka dengan data nyata.

WINDOW JOIN: Menghubungkan Peristiwa dengan Data Sekitarnya

WINDOW JOIN diperkenalkan di QuestDB 9.3. Ini memungkinkan setiap baris dari tabel utama untuk digabungkan dengan jendela waktu baris dari tabel lain, dengan agregasi yang dihitung atas baris yang cocok.

Pertimbangkan skenario perdagangan FX di mana Anda ingin menghubungkan setiap perdagangan dengan harga bid dan ask rata-rata dalam 10 detik setelah perdagangan:

SELECT
  trades.timestamp,
  trades.symbol,
  trades.price,
  avg(quotes.bid) AS avg_bid,
  avg(quotes.ask) AS avg_ask
FROM trades
WINDOW JOIN quotes ON (symbol)
RANGE BETWEEN 0 AND 10s FOLLOWING
INCLUDE PREVAILING;

Klausul INCLUDE PREVAILING memastikan Anda mendapatkan harga terbaru bahkan jika tidak ada kecocokan tepat di batas jendela. Ini menghilangkan halaman subkueri yang biasanya diperlukan oleh analitik horizon pendek.

Kasus penggunaan dalam perdagangan: menghitung kondisi pasar rata-rata di sekitar setiap eksekusi, mendeteksi perilaku harga yang tidak biasa dalam jendela waktu sebelum order besar, menghubungkan peristiwa IoT/infrastruktur (lonjakan latensi jaringan) dengan kualitas eksekusi.

LATEST ON: Status Terkini Instan

Ekstensi yang tampak sederhana namun sangat berguna. LATEST ON mengembalikan baris terakhir untuk setiap nilai kolom partisi:

SELECT * FROM trades
LATEST ON timestamp PARTITION BY symbol, side
ORDER BY timestamp DESC;

Ini memberi Anda perdagangan terakhir untuk setiap pasangan (symbol, side) — pada dasarnya snapshot "status terkini" real-time. Dalam basis data tradisional, ini memerlukan subkueri berkorelasi atau fungsi window dengan ROW_NUMBER().

Untuk dashboard perdagangan yang menampilkan harga terbaru di ratusan simbol, LATEST ON bersifat instan. Dikombinasikan dengan materialized view (yang akan kita bahas di Bagian 3), ini menjadi fondasi untuk snapshot portofolio sub-milidetik.

TWAP: Rata-Rata Tertimbang Waktu Asli

Agregat twap(price, timestamp) ditambahkan di QuestDB 9.3.3. Tidak seperti VWAP yang memberi bobot berdasarkan volume, TWAP memberi bobot berdasarkan durasi — setiap harga bertahan hingga observasi berikutnya, dan hasilnya adalah area di bawah fungsi step dibagi dengan total waktu.

SELECT symbol,
  twap(price, timestamp) AS twap_value,
  vwap(price, quantity) AS vwap_value
FROM trades
WHERE timestamp IN today()
SAMPLE BY 1h;

TWAP adalah tolok ukur eksekusi standar untuk order algoritmik. Memilikinya sebagai agregat asli yang mendukung GROUP BY paralel dan SAMPLE BY dengan semua mode FILL berarti Anda tidak memerlukan integrasi sisi klien apa pun — komputasi berjalan sepenuhnya di dalam mesin kueri.

Fungsi Window: Fondasi Analisis Teknikal

QuestDB mendukung fungsi window SQL standar, yang membentuk tulang punggung komputasi indikator teknis:

-- Bollinger Bands menggunakan materialized view OHLC
WITH stats AS (
  SELECT timestamp, close,
    AVG(close) OVER (
      ORDER BY timestamp
      ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
    ) AS sma20,
    AVG(close * close) OVER (
      ORDER BY timestamp
      ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
    ) AS avg_close_sq
  FROM trades_OHLC_15m
  WHERE timestamp BETWEEN dateadd('h', -24, now()) AND now()
    AND symbol = 'BTC-USDT'
)
SELECT timestamp,
  sma20,
  sma20 + 2 * sqrt(avg_close_sq - sma20 * sma20) AS upper_band,
  sma20 - 2 * sqrt(avg_close_sq - sma20 * sma20) AS lower_band
FROM stats;

QuestDB 9.3.3 juga memperkenalkan klausul WINDOW standar SQL — definisikan spesifikasi window sekali, referensikan dengan nama di beberapa fungsi. Tidak perlu lagi mengulang PARTITION BY dan ORDER BY yang sama di setiap ekspresi:

SELECT timestamp, symbol,
  avg(price) OVER w AS avg_price,
  stddev(price) OVER w AS std_price,
  min(price) OVER w AS min_price,
  max(price) OVER w AS max_price
FROM trades
WINDOW w AS (PARTITION BY symbol ORDER BY timestamp ROWS BETWEEN 99 PRECEDING AND CURRENT ROW);

Kueri lebih bersih, lebih sedikit copy-paste, kinerja eksekusi yang sama.

Pola Kueri Dunia Nyata

Izinkan saya menunjukkan beberapa pola yang sering muncul dalam sistem perdagangan produksi:

Korelasi Lintas Aset

-- Korelasi bergulir per jam dan harian antara ETH dan aset lain
WITH data AS (
  SELECT ETHUSD.timestamp,
    corr(ETHUSD.price, asset.price) AS corr
  FROM ETHUSD
  ASOF JOIN asset
  SAMPLE BY 1m
)
SELECT timestamp,
  avg(corr) OVER (ORDER BY timestamp
    RANGE BETWEEN 1 HOUR PRECEDING AND CURRENT ROW) AS hourly_corr,
  avg(corr) OVER (ORDER BY timestamp
    RANGE BETWEEN 24 HOUR PRECEDING AND CURRENT ROW) AS daily_corr
FROM data;

RSI di Semua Simbol

-- RSI 14 hari untuk semua pasangan USDT
WITH gains_losses AS (
  SELECT timestamp, symbol,
    CASE WHEN close > lag(close) OVER (PARTITION BY symbol ORDER BY timestamp)
      THEN close - lag(close) OVER (PARTITION BY symbol ORDER BY timestamp)
      ELSE 0 END AS gain,
    CASE WHEN close < lag(close) OVER (PARTITION BY symbol ORDER BY timestamp)
      THEN lag(close) OVER (PARTITION BY symbol ORDER BY timestamp) - close
      ELSE 0 END AS loss
  FROM trades_latest_1d
  WHERE symbol LIKE '%-USDT'
)
SELECT timestamp, symbol,
  100 - 100 / (1 + avg_gain / NULLIF(avg_loss, 0)) AS rsi
FROM (
  SELECT timestamp, symbol,
    AVG(gain) OVER (PARTITION BY symbol ORDER BY timestamp
      ROWS BETWEEN 13 PRECEDING AND CURRENT ROW) AS avg_gain,
    AVG(loss) OVER (PARTITION BY symbol ORDER BY timestamp
      ROWS BETWEEN 13 PRECEDING AND CURRENT ROW) AS avg_loss
  FROM gains_losses
);

ATR (Average True Range)

-- ATR 14 periode dari bar OHLC 15 menit
WITH tr AS (
  SELECT timestamp, symbol,
    GREATEST(
      high - low,
      ABS(high - lag(close) OVER (PARTITION BY symbol ORDER BY timestamp)),
      ABS(low - lag(close) OVER (PARTITION BY symbol ORDER BY timestamp))
    ) AS true_range
  FROM trades_OHLC_15m
)
SELECT timestamp, symbol,
  AVG(true_range) OVER (PARTITION BY symbol ORDER BY timestamp
    ROWS BETWEEN 13 PRECEDING AND CURRENT ROW) AS atr_14
FROM tr;

Keunggulan SQL Dibandingkan Kode Kustom

Anda mungkin bertanya-tanya: mengapa menghitung indikator ini di dalam basis data ketika Anda bisa menarik data mentah ke Python dan menggunakan ta-lib atau pandas?

Ada tiga alasan. Pertama, lokalitas data — memindahkan terabyte data tick melalui jaringan untuk menghitung rata-rata bergulir adalah pemborosan. Komputasi harus berada di tempat data berada. Kedua, paralelisme — mesin QuestDB yang divektorkan dengan kompilasi JIT dapat memproses kueri ini di beberapa inti dengan instruksi SIMD, seringkali lebih cepat dari kode Python single-threaded. Ketiga, konsistensi — ketika beberapa dashboard, strategi, dan sistem pemantauan semua membutuhkan indikator yang sama, memiliki satu sumber kebenaran di basis data menghilangkan bug sinkronisasi.

Meskipun demikian, QuestDB tidak mencoba menggantikan seluruh tumpukan analitik Anda. Interop Parquet berarti Anda masih dapat menarik data historis langsung ke pipeline ML Anda tanpa melalui basis data untuk beban kerja batch.

Berikutnya di Bagian 3

Di bagian terakhir, kami akan membahas materialized view untuk OHLC real-time (termasuk view bertingkat di beberapa timeframe), array 2D untuk analitik order book asli, dan arsitektur referensi untuk platform perdagangan algoritmik bertenaga QuestDB yang lengkap.

Sitasi

@software{soloviov2025questdb_algotrading_p2,
  author = {Soloviov, Eugen},
  title = {QuestDB for Algorithmic Trading: SQL Extensions That Change the Game},
  year = {2025},
  url = {https://marketmaker.cc/id/blog/post/questdb-algotrading-sql},
  version = {0.1.0},
  description = {Selami mendalam ekstensi SQL time-series QuestDB: SAMPLE BY, ASOF JOIN, HORIZON JOIN, WINDOW JOIN, LATEST ON, dan pola kueri perdagangan dunia nyata.}
}
Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan edukasi dan informasi serta tidak merupakan nasihat keuangan, investasi, atau trading. Trading mata uang kripto mengandung risiko kerugian yang signifikan.

Penulis

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Selangkah Lebih Maju dari Pasar

Berlangganan newsletter kami untuk wawasan AI trading eksklusif, analisis pasar, dan pembaruan platform.

Kami menghormati privasi Anda. Berhenti berlangganan kapan saja.