02 สร้างโมเดลข้อมูล
สร้างตาราง market, tick, trade และ aggregate รายหนึ่งนาทีที่กำหนดชนิดข้อมูลใน ClickHouse Cloud
จุดเริ่มต้น
.env.polymarket ถูก source แล้ว และคุณรู้ว่าทำไม condition ID กับ token ID จึงต่างกัน
ทำไมต้องเป็นตารางเหล่านี้
คิวรีห้าชุดที่จะใช้ต่อไปอ่านช่วงเวลาล่าสุดจากทุกตลาดที่เฝ้าดู ดังนั้น key ของตารางเหตุการณ์
จึงเริ่มด้วย key ของชั่วโมง/เวลา ตามด้วย token หรือ condition ที่ใช้จัดกลุ่ม ฟิลด์ที่รู้ชนิดแน่ชัด
ใช้ชนิดข้อมูลตามธรรมชาติ: UInt256 สำหรับ token ID,
DateTime64 สำหรับเวลาเหตุการณ์ ทศนิยมแบบแม่นยำสำหรับราคาและขนาด และ enum สำหรับค่าเหตุการณ์
ที่มีขอบเขตจำกัด payload ต้นทางที่เป็นก้อนทึบยังเก็บเป็น string เพราะไม่มีคิวรีใดอ่านฟิลด์ในนั้น
ไม่มี partition key เวิร์กช็อปอายุสั้นนี้ยังไม่มีขอบเขตการเก็บรักษาข้อมูลที่พิสูจน์แล้ว การเพิ่ม partition ก่อนที่จะมีข้อกำหนดเรื่อง lifecycle จะสร้าง part เล็ก ๆ ขึ้นมาโดยไม่ได้ ประโยชน์อะไร
ขั้นที่ 1 — สร้างฐานข้อมูลและตารางข้อมูลดิบ
คัดลอกบล็อกทั้งก้อนไปวางในคอนโซล SQL ของ ClickHouse Cloud แล้วรัน:
CREATE DATABASE IF NOT EXISTS polymarket;
CREATE TABLE IF NOT EXISTS polymarket.markets
(
market_id UInt64,
condition_id FixedString(66),
token_id UInt256,
outcome LowCardinality(String),
question String,
slug String,
active Bool,
accepting_orders Bool,
volume_24h Decimal128(8),
observed_at DateTime64(3, 'UTC')
)
ENGINE = ReplacingMergeTree(observed_at)
ORDER BY (condition_id, token_id);
CREATE TABLE IF NOT EXISTS polymarket.price_ticks
(
event_id FixedString(64),
condition_id FixedString(66),
token_id UInt256,
event_at DateTime64(3, 'UTC'),
observed_at DateTime64(3, 'UTC'),
event_kind Enum8(
'book_snapshot' = 1,
'price_change' = 2,
'last_trade_price' = 3,
'best_bid_ask' = 4,
'rest_book' = 5
),
source Enum8('WEBSOCKET' = 1, 'CLOB_REST' = 2, 'FIXTURE' = 3),
price Decimal64(12),
size Decimal128(8),
side Enum8('UNKNOWN' = 0, 'BUY' = 1, 'SELL' = 2),
best_bid Decimal64(12),
best_ask Decimal64(12),
midpoint Decimal64(12),
source_hash String,
raw_payload String
)
ENGINE = MergeTree
ORDER BY (toStartOfHour(event_at), token_id, event_at, event_id);
CREATE TABLE IF NOT EXISTS polymarket.trades
(
trade_id FixedString(64),
condition_id FixedString(66),
token_id UInt256,
event_at DateTime64(3, 'UTC'),
observed_at DateTime64(3, 'UTC'),
proxy_wallet FixedString(42),
side Enum8('UNKNOWN' = 0, 'BUY' = 1, 'SELL' = 2),
price Decimal64(12),
size Decimal128(8),
outcome LowCardinality(String),
transaction_hash FixedString(66),
title String
)
ENGINE = ReplacingMergeTree(observed_at)
ORDER BY (toStartOfHour(event_at), condition_id, event_at, trade_id);
CREATE OR REPLACE VIEW polymarket.trades_clean AS
SELECT *
FROM polymarket.trades FINAL;ตัว collector กันข้อมูลซ้ำก่อนจะ insert ส่วน ReplacingMergeTree เป็นตาข่ายความปลอดภัย
ชั้นที่สอง view trades_clean ทำให้คิวรีขนาดเล็กของเวิร์กช็อปให้ผลแน่นอนขณะที่ merge
ยังดำเนินอยู่
ขั้นที่ 2 — สร้าง aggregate ของ midpoint รายหนึ่งนาที
CREATE TABLE IF NOT EXISTS polymarket.market_midpoints_1m
(
token_id UInt256,
minute DateTime('UTC'),
open AggregateFunction(argMin, Decimal64(12), Tuple(DateTime64(3, 'UTC'), FixedString(64))),
high AggregateFunction(max, Decimal64(12)),
low AggregateFunction(min, Decimal64(12)),
close AggregateFunction(argMax, Decimal64(12), Tuple(DateTime64(3, 'UTC'), FixedString(64))),
updates AggregateFunction(count)
)
ENGINE = AggregatingMergeTree
ORDER BY (minute, token_id);
CREATE MATERIALIZED VIEW IF NOT EXISTS polymarket.market_midpoints_1m_mv
TO polymarket.market_midpoints_1m
AS
SELECT
token_id,
toStartOfMinute(event_at) AS minute,
argMinState(midpoint, tuple(event_at, event_id)) AS open,
maxState(midpoint) AS high,
minState(midpoint) AS low,
argMaxState(midpoint, tuple(event_at, event_id)) AS close,
countState() AS updates
FROM polymarket.price_ticks
WHERE midpoint > 0
AND event_kind IN ('book_snapshot', 'price_change', 'best_bid_ask', 'rest_book')
GROUP BY token_id, minute;materialized view นี้รวมเฉพาะ quote midpoint เท่านั้น มันตัดราคาระดับคำสั่งซื้อขายที่เปลี่ยนแปลง และราคาการเทรดล่าสุดออกอย่างตั้งใจ เพื่อให้ชุดข้อมูล OHLC มีความหมายเดียว
ขั้นที่ 3 — ตรวจสอบทุกออบเจ็กต์
clickhouse client \
--host "$CLICKHOUSE_HOST" \
--port "$CLICKHOUSE_PORT" \
--user "$CLICKHOUSE_USER" \
--password "$CLICKHOUSE_PASSWORD" \
--secure \
--query "SHOW TABLES FROM polymarket"ชื่อที่ควรมีได้แก่:
market_midpoints_1m
market_midpoints_1m_mv
markets
price_ticks
trades
trades_cleanถือว่าเสร็จเมื่อ
SHOW TABLES คืนออบเจ็กต์ทั้งหกโดยไม่มีเซิร์ฟเวอร์ ClickHouse บนเครื่องรันอยู่เลย
ต่อไป: เริ่ม collector แบบสด