Pasar prediksi adalah domain yang menantai untuk membangun API: instrumen keuangan memiliki masa berlaku, harga merepresentasikan probabilitas secara real-time, peristiwa multi-hasil memiliki hubungan modal yang kompleks, dan pengguna mencakup manusia di UI hingga bot arbitrase. Dalam kondisi seperti ini, keputusan desain API langsung diuji oleh kebutuhan latensi, keamanan, dan konsistensi data.
Polymarket—platform pasar prediksi terbesar berdasarkan volume—menyediakan contoh arsitektur yang menarik untuk dipelajari. API-nya bukan sekadar lapisan CRUD di atas basis data, tetapi sistem berlapis yang memisahkan penemuan pasar, perdagangan, data pengguna, serta streaming real-time.
Berikut delapan pola yang dapat diterapkan saat merancang API untuk sistem keuangan, perdagangan, atau aplikasi real-time.
Pola 1: Pisahkan API Berdasarkan Domain, Bukan Entitas
Polymarket mengekspos tiga API dengan tanggung jawab yang berbeda:
-
Gamma API (
gamma-api.polymarket.com) — penemuan pasar, peristiwa, tag, dan pencarian. -
CLOB API (
clob.polymarket.com) — buku pesanan, harga, dan penempatan pesanan. -
Data API (
data-api.polymarket.com) — posisi pengguna, perdagangan, analitik, dan papan peringkat.
Pemisahan ini bukan hanya soal penamaan. Setiap domain memiliki karakteristik operasional berbeda:
| Domain | Konsumen utama | Kebutuhan utama |
|---|---|---|
| Penemuan pasar | UI, pencarian, integrasi | Data publik dan mudah dijelajahi |
| Perdagangan | Trader dan bot | Latensi rendah dan autentikasi |
| Analitik pengguna | Dashboard dan pelaporan | Query berbasis alamat dompet |
Pendekatan yang kurang ideal biasanya menggabungkan semuanya dalam endpoint seperti:
/markets
/orders
/users
Sebaliknya, tanyakan: untuk apa API ini digunakan?
- Penemuan memiliki pola akses baca yang tinggi.
- Perdagangan membutuhkan kontrol keamanan dan latensi.
- Analitik membutuhkan query historis serta agregasi.
Dengan URL dasar terpisah, setiap API dapat diskalakan, diamankan, dan dikembangkan secara independen.
Langkah implementasi:
- Petakan domain bisnis utama, bukan hanya tabel database.
- Identifikasi kebutuhan autentikasi dan latensi setiap domain.
- Pisahkan gateway, rate limit, cache, atau infrastruktur streaming jika kebutuhannya berbeda.
- Hindari endpoint “serba bisa” yang menggabungkan penemuan, eksekusi, dan analitik.
Pola 2: Jadikan Data Baca Publik Jika Tidak Membutuhkan Otorisasi
Data pasar Polymarket—harga, buku pesanan, metadata peristiwa, dan perdagangan historis—dapat diakses secara publik:
curl "https://gamma-api.polymarket.com/events?limit=5"
Tidak ada kunci API atau OAuth untuk data baca tersebut.
Pola ini relevan ketika nilai platform meningkat karena banyak pihak dapat membaca, menganalisis, dan membangun integrasi di atas data yang sama. Dalam konteks pasar, akses data publik dapat memperluas likuiditas dan ekosistem aplikasi.
Pisahkan dengan tegas operasi read dan write:
GET /markets → publik
GET /order-book/:tokenId → publik
POST /orders → terautentikasi
DELETE /orders/:orderId → terautentikasi
Prinsip implementasi:
- Jangan menerapkan autentikasi secara seragam hanya karena endpoint berada dalam produk yang sama.
- Lindungi operasi yang mengubah keadaan: membuat pesanan, membatalkan pesanan, memindahkan dana, atau memperbarui profil.
- Gunakan cache atau CDN untuk endpoint publik dengan trafik baca tinggi.
- Dokumentasikan batas penggunaan dan perilaku cache untuk data pasar yang cepat berubah.
Untuk platform dengan rasio pembaca jauh lebih besar daripada penulis, desain ini mengurangi gesekan bagi sebagian besar pengguna tanpa mengorbankan keamanan operasi penting.
Pola 3: Gunakan Otentikasi Bertingkat untuk Risiko yang Berbeda
Endpoint perdagangan Polymarket menggunakan dua tingkat autentikasi.
L1: Buktikan kepemilikan dompet
L1 memakai tanda tangan EIP-712 dari kunci privat pengguna. Tujuannya adalah membuktikan bahwa pengguna mengendalikan dompet terkait, biasanya saat membuat atau memperoleh kredensial API.
// L1: gunakan kunci privat untuk mendapatkan kredensial API
const credentials = await client.createOrDeriveApiKey();
// { key: "...", secret: "...", passphrase: "..." }
L2: Tanda tangani permintaan perdagangan rutin
Setelah memiliki kredensial API, permintaan perdagangan menggunakan HMAC-SHA256:
{
"POLY_ADDRESS": "0x...",
"POLY_SIGNATURE": "<hmac-sha256>",
"POLY_TIMESTAMP": "1716000000",
"POLY_API_KEY": "550e8400-...",
"POLY_PASSPHRASE": "..."
}
Pemisahan ini penting:
- L1 digunakan untuk tindakan berisiko tinggi: membuktikan identitas dan kontrol dompet.
- L2 digunakan untuk tindakan berulang: mengautentikasi permintaan perdagangan berfrekuensi tinggi.
Secara umum, pola ini dapat diterapkan di luar kripto:
L1 → “Buktikan Anda adalah pemilik identitas ini.”
L2 → “Buktikan permintaan ini dibuat oleh sesi atau kredensial yang sah.”
Langkah implementasi:
- Identifikasi tindakan yang memerlukan verifikasi dengan kredensial terkuat.
- Buat token atau kredensial turunan untuk operasi rutin.
- Tambahkan timestamp dan signature pada setiap request untuk mengurangi risiko replay attack.
- Terapkan masa berlaku, rotasi, dan pencabutan kredensial L2.
Jangan paksa pengguna menggunakan mekanisme keamanan paling berat untuk setiap request jika sistem juga membutuhkan throughput tinggi.
Pola 4: Perlakukan Pesanan sebagai Pesan Bertanda Tangan
Dalam desain API konvensional, POST /orders biasanya berarti:
“Server, tolong buat pesanan ini atas nama saya.”
Pada Polymarket, pesanan merupakan pesan EIP-712 yang ditandatangani secara kriptografis. Pesan tersebut adalah komitmen finansial yang dapat diverifikasi.
const response = await client.createAndPostOrder(
{
tokenID: "71321045679...",
price: 0.65,
size: 100,
side: Side.BUY,
},
{
tickSize: "0.01",
negRisk: false,
},
OrderType.GTC
);
SDK membangun struktur data bertipe, menandatanganinya dengan kunci privat, lalu mengirimkan pesanan dan tanda tangannya. Mesin pencocokan berjalan di luar rantai, sementara penyelesaian perdagangan dilakukan di Polygon berdasarkan otorisasi yang ditandatangani tersebut.
Maknanya berubah menjadi:
“Ini adalah instrumen yang ditandatangani dan mengotorisasi perdagangan ini.”
Untuk sistem berisiko tinggi, payload yang membawa otorisasi memiliki beberapa manfaat:
- Non-penolakan (non-repudiation).
- Verifikasi independen terhadap isi pesan.
- Jejak audit yang lebih kuat.
- Pengurangan ketergantungan pada kepercayaan penuh terhadap operator API.
Pola payload bertanda tangan:
type SignedOrder = {
payload: {
tokenId: string;
price: number;
size: number;
side: "BUY" | "SELL";
expiry: number;
nonce: string;
};
signature: string;
};
Tambahkan nonce, masa berlaku, dan domain pemisah tanda tangan agar pesan tidak dapat digunakan ulang pada konteks yang salah.
Pola 5: Modelkan Ontologi Domain Secara Eksplisit
Polymarket membedakan dua objek utama:
- Peristiwa (Event): pertanyaan atau konteks besar.
- Pasar (Market): hasil spesifik yang dapat diperdagangkan di dalam peristiwa tersebut.
Contohnya:
- Peristiwa: “Siapa yang akan memenangkan pemilihan Senat AS 2026 di Pennsylvania?”
- Pasar: “Apakah Bob Casey akan menang?”
Satu peristiwa dapat memuat beberapa pasar:
{
"id": "501",
"title": "2026 Pennsylvania Senate Race",
"negRisk": true,
"markets": [
{
"id": "2301",
"question": "Will Bob Casey win?",
"outcomePrices": "[\"0.42\", \"0.58\"]"
},
{
"id": "2302",
"question": "Will Dave McCormick win?",
"outcomePrices": "[\"0.35\", \"0.65\"]"
},
{
"id": "2303",
"question": "Will a third candidate win?",
"outcomePrices": "[\"0.23\", \"0.77\"]"
}
]
}
Model ini mengungkap hubungan konseptual yang penting bagi klien. Misalnya, harga direpresentasikan sebagai larik paralel:
outcomes[0] === outcomePrices[0]
outcomes[1] === outcomePrices[1]
Selain itu, negRisk berada pada tingkat peristiwa karena hubungan modal berlaku untuk pasar-pasar di dalam peristiwa tersebut.
Jangan sembunyikan aturan domain penting dalam dokumentasi saja.
Jika klien harus mengetahui hubungan antarobjek untuk menghitung posisi, risiko, atau validasi pesanan, tampilkan hubungan itu secara eksplisit di respons API.
Pola 6: Jadikan Invarian Domain sebagai Field API Bertipe
negRisk menunjukkan bahwa suatu peristiwa memiliki hubungan modal khusus. Pada peristiwa multi-hasil tertentu, tepat satu hasil dapat menang.
Dalam kondisi ini:
1 token “Tidak” pada hasil A ≡ 1 token “Ya” pada setiap hasil lainnya
Contoh konversi:
| Sebelum | Sesudah |
|---|---|
| 1× Tidak (Lainnya) | 1× Ya (Casey) + 1× Ya (McCormick) |
Polymarket mengekspos kondisi ini melalui negRisk: true, baik pada data pasar maupun saat membuat pesanan:
const order = await client.createAndPostOrder(
{
tokenID: "71321045679...",
price: 0.65,
size: 100,
side: Side.BUY,
},
{
tickSize: "0.01",
negRisk: true,
},
OrderType.GTC
);
Jika klien mengabaikan nilai tersebut, model posisi dapat salah dan pesanan dapat ditolak atau diproses secara tidak sesuai.
Pola yang dapat diterapkan:
{
"marketId": "2301",
"isMutuallyExclusive": true,
"settlementGroupId": "501",
"conversionRules": {
"enabled": true
}
}
Gunakan field bertipe untuk aturan yang memengaruhi perilaku sistem. Jangan hanya menulis catatan seperti:
Catatan: pasar tertentu mungkin memiliki hubungan khusus.
Aturan domain yang keras harus dapat dibaca dan divalidasi oleh mesin.
Pola 7: Perlakukan Konfigurasi Dinamis sebagai Keadaan Real-Time
Ukuran tick sering dianggap sebagai konfigurasi statis. Polymarket memperlakukannya sebagai keadaan pasar yang dapat berubah.
Saat harga berada di dekat ekstrem—di atas 0.96 atau di bawah 0.04—ukuran tick minimum berubah dari 0.01 menjadi 0.001:
{
"event_type": "tick_size_change",
"asset_id": "65818619657...",
"old_tick_size": "0.01",
"new_tick_size": "0.001",
"timestamp": "100000000"
}
Perubahan ini masuk akal secara finansial. Pada harga 0.04, pergeseran 0.01 ke 0.03 merepresentasikan perubahan probabilitas 25%. Tick yang lebih kecil memungkinkan penemuan harga lebih presisi, misalnya 0.973 alih-alih hanya 0.97.
Klien tidak boleh mengodekan ukuran tick secara permanen:
// Hindari hardcode seperti ini
const tickSize = 0.01;
Simpan keadaan tick per aset dan perbarui dari event real-time:
const tickSizeByAsset = new Map<string, string>();
function onTickSizeChange(event: {
asset_id: string;
new_tick_size: string;
}) {
tickSizeByAsset.set(event.asset_id, event.new_tick_size);
}
Sebelum membuat pesanan, ambil nilai terbaru:
const tickSize = tickSizeByAsset.get(tokenID) ?? "0.01";
Prinsipnya: jika parameter dapat berubah dan memengaruhi validitas request, parameter tersebut adalah state, bukan konstanta.
Pola 8: Pisahkan WebSocket Berdasarkan Profil Konsumen
Polymarket menjalankan dua sistem WebSocket dengan target penggunaan berbeda.
Market Channel
Market Channel:
wss://ws-subscriptions-clob.polymarket.com/ws/market
Channel ini ditujukan untuk kebutuhan perdagangan. Klien berlangganan berdasarkan ID token dan menerima data seperti:
- Snapshot buku pesanan.
- Perubahan harga.
- Eksekusi perdagangan.
- Perubahan ukuran tick.
{
"assets_ids": [
"65818619657568813474341868652308942079804919287380422192892211131408793125422"
],
"type": "market"
}
Real-Time Data Socket
Socket data real-time:
wss://ws-live-data.polymarket.com
Socket ini melayani kebutuhan berbeda, seperti komentar, harga kripto dari Binance dan Chainlink, harga ekuitas, serta peristiwa interaksi sosial.
{
"action": "subscribe",
"subscriptions": [
{
"topic": "crypto_prices",
"type": "update",
"filters": "btcusdt,ethusd"
}
]
}
Jangan paksa satu koneksi WebSocket untuk melayani semua kebutuhan real-time.
| Kebutuhan | Karakteristik |
|---|---|
| Bot perdagangan | Latensi rendah, delta buku pesanan, reliabilitas tinggi |
| UI sosial atau dashboard | Volume event beragam, toleransi latensi lebih besar |
| Feed harga eksternal | Langganan berbasis topik dan filter |
Langkah implementasi:
- Kelompokkan konsumen berdasarkan kebutuhan latensi, volume, dan keandalan.
- Pisahkan channel atau topik jika kontrak datanya berbeda jauh.
- Hindari mengirim event sosial ke klien yang hanya membutuhkan order book.
- Dokumentasikan strategi reconnect, snapshot awal, dan pemrosesan event yang datang tidak berurutan.
Endpoint WebSocket bersama yang menangani terlalu banyak tujuan biasanya menjadi lebih kompleks dan kurang optimal untuk semua konsumen.
Ringkasan: Buat Struktur Domain Terlihat di API
Pola desain API Polymarket berpusat pada satu prinsip: API harus merepresentasikan struktur domain yang sebenarnya, bukan menyembunyikannya di balik abstraksi generik.
Penerapannya terlihat pada:
- API terpisah untuk domain penemuan, perdagangan, dan data.
- Data pasar publik, tetapi operasi tulis yang terautentikasi.
- Otentikasi bertingkat untuk tindakan dengan tingkat risiko berbeda.
- Pesanan sebagai pesan bertanda tangan.
- Hierarki Peristiwa dan Pasar yang eksplisit.
-
negRisksebagai aturan domain yang dapat diproses mesin. - Ukuran tick sebagai state real-time.
- WebSocket terpisah untuk profil konsumen berbeda.
Ergonomi API tetap penting: penamaan konsisten, endpoint mudah dipanggil, dan error dapat diprediksi. Namun, untuk domain kompleks seperti sistem perdagangan atau keuangan, ergonomi saja tidak cukup.
Jika domain memiliki batasan, tampilkan batasan tersebut. Jika domain memiliki state yang berubah, siarkan perubahannya. Jika entitas memiliki hubungan penting, modelkan hubungan itu secara eksplisit.
Dengan begitu, klien yang berhasil mengintegrasikan API Anda bukan hanya dapat memanggil endpoint—mereka benar-benar memahami sistem yang sedang mereka gunakan.
Top comments (0)