
SQL untuk Data Warehouse Finansial
Mengkueri, mengagregasi, dan menganalisis data finansial terstruktur
Mengapa SQL Adalah Bahasa Data Finansial
Setiap trade, setiap tick, setiap update order book — pasar finansial menghasilkan volume data terstruktur yang sangat besar. Dan selama lebih dari empat dekade, SQL (Structured Query Language) telah menjadi alat dominan untuk mengkueri, mentransformasi, dan menganalisis data itu. Tidak glamor. Tidak trendi. Tapi ketika seorang portfolio manager bertanya "Berapa rata-rata harga fill trading ETH-ku Selasa lalu antara jam 2 sore sampai 3 sore UTC?", jawabannya datang dari SQL.
Data finansial pada dasarnya bersifat relasional. Sebuah trade merujuk ke sebuah order, yang merujuk ke sebuah akun, yang dimiliki oleh seorang pengguna. Posisi berhubungan dengan instrumen; instrumen berhubungan dengan market. Model relasional — tabel dengan baris dan kolom, dihubungkan oleh foreign key — memetakan secara natural ke hubungan-hubungan ini. Inilah mengapa database relasional seperti PostgreSQL, MySQL, dan SQL Server tetap menjadi tulang punggung infrastruktur finansial, dari Goldman Sachs sampai GaiaEx.
Kekuatan SQL terletak pada sifat deklaratifnya: kamu mendeskripsikan apa yang kamu mau, bukan bagaimana mendapatkannya. Mesin database yang mencari tahu rencana eksekusi yang optimal. Ini sangat penting ketika tabel riwayat trading-mu punya 500 juta baris dan kamu butuh hasil dalam waktu kurang dari satu detik. Tulis kueri yang tepat, buat index yang tepat, dan SQL akan memberikannya — apakah kamu menjalankannya di instance PostgreSQL lokal atau data warehouse skala cloud.
SELECT, JOIN, dan WHERE: Mengkueri Data Trade
Mari mulai dengan sebuah schema konkret. Bayangkan sebuah tabel trades di platform seperti GaiaEx:
CREATE TABLE trades (
trade_id BIGINT PRIMARY KEY,
user_id INT NOT NULL,
symbol VARCHAR(20) NOT NULL,
side VARCHAR(4) NOT NULL, -- 'buy' or 'sell'
price NUMERIC(18,8) NOT NULL,
quantity NUMERIC(18,8) NOT NULL,
fee NUMERIC(18,8) DEFAULT 0,
executed_at TIMESTAMPTZ NOT NULL
);
Operasi paling mendasar adalah statement SELECT dengan filtering. Untuk mencari semua trade beli ETH-USD di atas $3.000 dalam 24 jam terakhir:
SELECT trade_id, price, quantity, executed_at
FROM trades
WHERE symbol = 'ETH-USD'
AND side = 'buy'
AND price > 3000
AND executed_at > NOW() - INTERVAL '24 hours'
ORDER BY executed_at DESC;
JOIN menghubungkan tabel-tabel yang berkaitan. Misalkan kamu punya sebuah tabel accounts dan ingin melihat volume trading per tier akun:
SELECT a.tier, COUNT(*) AS trade_count,
SUM(t.price * t.quantity) AS total_volume
FROM trades t
JOIN accounts a ON t.user_id = a.user_id
WHERE t.executed_at > NOW() - INTERVAL '30 days'
GROUP BY a.tier
ORDER BY total_volume DESC;
Kueri-kueri ini adalah makanan sehari-hari analis finansial, tim risiko, dan petugas compliance. Kuasai mereka dan kamu bisa menjawab pertanyaan apa pun yang terkandung dalam datamu.
Window Function: Senjata Rahasia Sang Analis
Window function adalah yang membedakan pemula SQL dari praktisi SQL di dunia finansial. Mereka membiarkanmu melakukan kalkulasi di sekumpulan baris yang berhubungan dengan baris saat ini — tanpa meruntuhkan hasilnya menjadi satu nilai teragregasi. Anggap saja mereka sebagai "kalkulasi berjalan" di seluruh datamu.
Window function yang paling umum dipakai dalam analisis finansial:
- ROW_NUMBER() — Memberikan integer berurutan ke setiap baris dalam sebuah partisi. Berguna untuk dedup atau memilih trade ke-N per symbol.
- LAG() dan LEAD() — Mengakses nilai baris sebelumnya atau selanjutnya. Penting untuk menghitung return antar-trade atau mendeteksi gap dalam data time-series.
- SUM() OVER () — Total berjalan (running total). Menghitung volume kumulatif, P&L kumulatif, atau ukuran posisi berjalan.
- AVG() OVER (ROWS BETWEEN) — Moving average langsung di SQL. Tidak perlu Python.
Ini contoh praktisnya — menghitung perubahan harga antar-trade dan posisi berjalan untuk BTC:
SELECT executed_at, price, quantity, side,
price - LAG(price) OVER (ORDER BY executed_at) AS price_change,
SUM(CASE WHEN side = 'buy' THEN quantity ELSE -quantity END)
OVER (ORDER BY executed_at) AS running_position
FROM trades
WHERE symbol = 'BTC-USD'
ORDER BY executed_at;
Window function tidak mengurangi jumlah barismu — tidak seperti GROUP BY, setiap baris input menghasilkan satu baris output. Ini membuat mereka sempurna untuk menambahkan kolom analitis ke log trade yang detail tanpa kehilangan granularitas. Anggap saja mereka sedang "memperkaya" datamu alih-alih "meringkas" data itu.
Untuk agregasi OHLCV — fondasi dari chart candlestick — kamu bisa mengombinasikan GROUP BY dengan pengelompokan waktu (time-bucketing) dan agregat standar:
SELECT date_trunc('hour', executed_at) AS bucket,
(ARRAY_AGG(price ORDER BY executed_at))[1] AS open,
MAX(price) AS high,
MIN(price) AS low,
(ARRAY_AGG(price ORDER BY executed_at DESC))[1] AS close,
SUM(quantity) AS volume
FROM trades
WHERE symbol = 'ETH-USD'
GROUP BY bucket
ORDER BY bucket;CTE: Menyusun Kueri Analitis yang Kompleks
Common Table Expression (CTE) membiarkanmu memecah sebuah kueri kompleks menjadi tahapan-tahapan bernama yang mudah dibaca — seperti function dalam pemrograman. Mereka diawali dengan kata kunci WITH dan bisa saling merujuk secara berurutan.
Misalkan kamu ingin mengidentifikasi 10 hari trading paling menguntungkan, tapi profitabilitas membutuhkan penghitungan net P&L per hari termasuk fee. Dengan CTE, kamu bisa membangun ini langkah demi langkah:
WITH daily_trades AS (
SELECT DATE(executed_at) AS trade_date,
SUM(CASE WHEN side = 'sell' THEN price * quantity
ELSE -price * quantity END) AS gross_pnl,
SUM(fee) AS total_fees
FROM trades
WHERE user_id = 42
GROUP BY DATE(executed_at)
),
daily_pnl AS (
SELECT trade_date,
gross_pnl - total_fees AS net_pnl,
SUM(gross_pnl - total_fees) OVER (ORDER BY trade_date) AS cumulative_pnl
FROM daily_trades
)
SELECT trade_date, net_pnl, cumulative_pnl
FROM daily_pnl
ORDER BY net_pnl DESC
LIMIT 10;
Setiap CTE terbaca seperti sebuah paragraf. daily_trades mengagregasi trade mentah menjadi ringkasan harian. daily_pnl menghitung net P&L dan total berjalan. SELECT terakhir memilih 10 hari teratas. Bandingkan ini dengan sebuah subquery monolitik tunggal — versi CTE lebih mudah dipelihara, dites, dan mendokumentasikan dirinya sendiri.
Dalam sistem finansial produksi, CTE dipakai untuk segala hal mulai dari pelaporan regulasi (mengagregasi kebutuhan margin lintas akun) sampai kueri dashboard real-time (menghitung volume 24 jam bergulir di GaiaEx). Mereka saling menyusun sama seperti kode yang tersusun rapi saling menyusun — setiap layer membangun di atas layer sebelumnya.
Indexing dan Partitioning untuk Performa Time-Series
Sebuah kueri secepat index yang mendukungnya. Tanpa index yang tepat, bahkan sebuah klausa WHERE yang sederhana memaksa sequential scan — membaca setiap baris di tabel itu. Pada 500 juta baris, itu berarti menit bukan milidetik.
Untuk data time-series finansial, pola index yang paling krusial adalah sebuah composite B-tree index pada (symbol, executed_at):
CREATE INDEX idx_trades_symbol_time
ON trades (symbol, executed_at DESC);
Satu index ini mengakselerasi mayoritas kueri analitis: "semua trade ETH di jam terakhir," "trade BTC antara dua timestamp," atau "trade terbaru per symbol." Urutan kolom itu penting — symbol di depan memungkinkan filtering kesetaraan, lalu executed_at memungkinkan range scan yang efisien di dalam partisi index itu.
Untuk tabel yang berkembang melebihi ratusan juta baris, table partitioning sangatlah penting. PostgreSQL mendukung partitioning deklaratif berdasarkan range — sempurna untuk time-series:
CREATE TABLE trades (
trade_id BIGINT, symbol VARCHAR(20),
price NUMERIC(18,8), executed_at TIMESTAMPTZ
) PARTITION BY RANGE (executed_at);
CREATE TABLE trades_2026_q1 PARTITION OF trades
FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');
CREATE TABLE trades_2026_q2 PARTITION OF trades
FOR VALUES FROM ('2026-04-01') TO ('2026-07-01');
Dengan partitioning, sebuah kueri untuk data Maret 2026 hanya memindai partisi Q1 — mesinnya melewatkan Q2, Q3, dan Q4 seluruhnya. Teknik ini, disebut partition pruning, memberikan peningkatan kecepatan berorde besaran (order-of-magnitude) pada dataset historis berskala besar.
PostgreSQL vs ClickHouse vs BigQuery: Memilih Mesin yang Tepat
Tidak semua database menangani analitik finansial secara setara. Pilihannya bergantung pada pola kuerimu, volume data, dan kebutuhan latensi.
PostgreSQL adalah kuda pekerja. Ia unggul pada beban kerja transaksional (mencatat trade secara real time), mendukung garansi ACID, menangani JOIN kompleks dengan mulus, dan bisa berskala sampai ratusan juta baris dengan indexing dan partitioning yang tepat. Ini pilihan yang tepat untuk database operasional — the system of record. GaiaEx, misalnya, mengandalkan infrastruktur yang kompatibel PostgreSQL untuk data trading intinya dengan garansi keandalan yang dituntut oleh sistem finansial.
ClickHouse adalah database kolumnar yang dirancang untuk kueri analitis pada miliaran baris. Di mana PostgreSQL menyimpan data baris demi baris (bagus untuk menyisipkan trade individual), ClickHouse menyimpan data kolom demi kolom (bagus untuk mengagregasi satu kolom di jutaan baris). Sebuah kueri seperti "rata-rata volume per jam untuk BTC selama 3 tahun" yang membutuhkan 30 detik di PostgreSQL mungkin selesai dalam 200 milidetik di ClickHouse. Trade-off-nya: ClickHouse tidak mendukung UPDATE atau DELETE secara efisien — ia bersifat append-only karena desain.
BigQuery (Google Cloud) adalah warehouse kolumnar serverless. Tanpa infrastruktur yang perlu dikelola, harga bayar-per-kueri, dan kapasitas skala petabyte. Ini ideal untuk analitik ad-hoc, riset, dan eksplorasi — tapi latensi kuerinya diukur dalam detik, bukan milidetik, membuatnya kurang cocok untuk aplikasi real-time.
- Pencatatan trade real-time dan manajemen order → PostgreSQL
- Analitik historis dan backtesting di atas miliaran baris → ClickHouse
- Eksplorasi ad-hoc dan berbagi data antar-tim → BigQuery
Banyak firma trading profesional memakai ketiganya dalam sebuah arsitektur bertingkat: PostgreSQL sebagai penyimpanan operasional yang hot, ClickHouse sebagai layer analitis yang warm, dan BigQuery sebagai arsip yang cold. Keterampilan menulis SQL yang baik berlaku di semuanya — sintaksnya 90% identik, dan pemikiran analitisnya 100% sama.