
Қаржы деректер қоймалары үшін SQL
Құрылымдалған қаржы деректерін сұрау, жинақтау және талдау
Неге SQL қаржы деректерінің тілі
Әр мәміле, әр тик, әр тапсырыс кітабы жаңартуы — қаржы нарықтары құрылымдалған деректердің ауқымды көлемін өндіреді. Ал төрт онжылдықтан асқан уақыт бойы SQL (Structured Query Language) сол деректерге сұрақ жіберудің, түрлендірудің және талдаудың негізгі құралы болды. Ол жарқын емес. Ол сәнди емес. Бірақ портфель менеджері «Өткен сейсенбіде сағат 14:00-ден 15:00-ге дейін (UTC) ETH мәмілелерімнің орташа толтыру бағасы қандай болды?» деп сұрағанда, жауап SQL-ден келеді.
Қаржы деректері табиғатынан реляциялық. Мәміле тапсырысқа сілтейді, тапсырыс есептік жазбаға сілтейді, есептік жазба пайдаланушыға тиесілі. Позициялар құралдармен байланысады; құралдар нарықтармен байланысады. Реляциялық модель — сыртқы кілттер арқылы байланысатын жолдар мен бағаналары бар кестелер — осы байланыстарға табиғи түрде сай келеді. Дәл осы себептен PostgreSQL, MySQL және SQL Server сияқты реляциялық дерекқорлар Goldman Sachs-тан GaiaEx-ке дейін қаржы инфраструктурасының арқауы болып қалады.
SQL-дің күші оның декларативтік сипатында: сіз қалай алуды емес, не қалайтыныңызды сипаттайсыз. Дерекқор қозғалтқышы оптималды орындау жоспарын өзі табады. Бұл мәміле тарихы кестеңіз 500 миллион жолдан тұрғанда, ал нәтижелерді секундтан аз уақытта алу керек болғанда өте маңызды. Дұрыс сұрақ жазыңыз, дұрыс индекс құрыңыз, ал SQL жеткізеді — жергілікті PostgreSQL инстансында немесе бұлт-өлшемді деректер жиынында жұмыс істесе де.
SELECT, JOIN және WHERE: мәміле деректеріне сұрақ жіберу
Нақты схемадан бастайык. GaiaEx сияқты алаңда trades кестесін көз алдымызға келтірейік:
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
);
Ең негізгі амал — сүзу арқылы SELECT операторы. Соңғы 24 сағатта $3,000-нан жоғары барлық ETH-USD сатып алу мәмілелерін табу үшін:
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-дар байланысты кестелерді қосады. accounts кестеңіз бар деп, әрі есептік жазба деңгейі бойынша сауда көлемін көрсетуді қалайтыныңызды болжайык:
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;
Бұл сұрақтар — қаржы талдаушыларының, тәуекел командаларының және сәйкестік офицерлерінің күнделікті наны. Оларды меңгеріп алсаңыз, деректеріңіздегі кез келген сұрақтың жауабын бере аласыз.
Терезе функциялары: талдаушының жасырын қаруы
Терезе функциялары — қаржыда SQL жаңадан бастағандар мен SQL практиктерін айыратын нәрсе. Олар нәтижені бір агрегатталған мәнге бүкпей, ағымдағы жолға қатысты жолдар жинағы бойынша есептеулер жасауға мүмкіндік береді. Оларды деректеріңіз бойынша «жүретін есептеулер» деп ойлаңыз.
Қаржы талдауында ең жиі қолданылатын терезе функциялары:
- ROW_NUMBER() — бөлім ішіндегі әр жолға дәйекті бүтін сан тағайындайды. Қайталанымдарды жою немесе символ бойынша N-ші мәмілені таңдау үшін пайдалы.
- LAG() және LEAD() — алдыңғы немесе кейінгі жолдың мәніне қол жеткізеді. Мәміледен-мәмілеге дейінгі табысты есептеу немесе уақыт қатарынан ажырауларды анықтау үшін міндетті.
- SUM() OVER () — жүретін жиынтықтар. Кумулятивті көлем, кумулятивті PnL немесе жүретін позиция өлшемін есептейді.
- AVG() OVER (ROWS BETWEEN) — жылжымалы орташалар тікелей SQL-де. Python-ның қажеті жоқ.
Міне тәжірибелік мысал — 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;
Терезе функциялары жол санын азайтпайды — GROUP BY-дан айырмашылығы, әр кіріс жол бір шығыс жол шығарады. Бұл оларды детальді мәміле журналдарына тереңдікті жоғалтпай талдамалық баған қосу үшін тамаша жасайды. Оларды деректерді «жинақтайтын» емес, «байытатын» деп ойлаңыз.
OHLCV агрегациясы үшін — шам-график диаграммаларының негізі — GROUP BY-ды уақыт-бакеттеу және стандартты агрегаттармен біріктіруге болады:
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-лер: күрделі талдамалық сұрақтарды құрастыру
Жалпы кестелік өрнектер (CTE) күрделі сұрақты аталған, оқылатын кезеңдерге бөлуге мүмкіндік береді — программалаудағы функциялар сияқты. Олар WITH түйін сөзімен енгізіледі және бір-біріне дәйекті түрде сілтей алады.
Ең пайдалы 10 сауда күнін анықтағыңыз келетінін болжайык, бірақ пайдалылық комиссияларды қосқанда күніне таза PnL есептеуді қажет етеді. CTE-лермен бұл қадам-қадам құрылады:
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;
Әр CTE бір абзацтай оқылады. daily_trades шикі мәмілелерді күнделікті қорытпаларға агрегаттайды. daily_pnl таза PnL мен жүретін жиынтықты есептейді. Соңғы SELECT ең жақсы 10 күнді таңдап алады. Мұны бірыңғай монолитті ішкі-сұрақпен салыстырыңыз — CTE нұсқасы қолдауға оңай, тестелетін және өзін-өзі түсіндіретін.
Продукциялық қаржы жүйелерінде, CTE-лер реттеушілік есеп беруден (есептік жазбалар бойынша маржа талаптарын агрегаттау) нақты-уақыттағы дашборд сұрақтарына (GaiaEx-те жүретін 24-сағаттық көлемді есептеу) дейін барлық жерде қолданылады. Олар жақсы факторланған код құраластырылған түрде құраласады — әр қатлам алдыңғысының үстіне құрылады.
Уақыт қатары өнімділігі үшін индекстеу және бөліктеу
Сұрақ оны қолдайтын индекс қанша жылдам болса, соңша жылдам. Дұрыс индекс болмаса, қарапайым WHERE сөйлемі де дәйекті сканерлеуді мәжбүрлейді — кестедегі әр жолды оқиды. 500 миллион жолда, бұл миллисекунд орнына минуттар.
Қаржы уақыт қатары деректері үшін ең маңызды индекс үлгісі — (symbol, executed_at) бойынша құрама B-ағаш индексі:
CREATE INDEX idx_trades_symbol_time
ON trades (symbol, executed_at DESC);
Осы жалғыз индекс талдамалық сұрақтардың көбісін жылдамдатады: «соңғы сағаттағы барлық ETH мәмілелері», «екі мезгіл белгісі арасындағы BTC мәмілелері» немесе «символ бойынша соңғы мәміле». Баған реті маңызды — symbol алдымен теңдік сүзуді іске қосады, содан кейін executed_at индекстің сол бөлігі ішінде тиімді диапазон сканерлеуін іске қосады.
Жүздеген миллион жолдан асатын кестелер үшін кестені бөліктеу міндетті. PostgreSQL диапазон бойынша декларативті бөліктеуді қолдайды — уақыт қатарлары үшін тамаша:
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');
Бөліктеумен, 2026 жылдың наурыз деректеріне арналған сұрақ тек Q1 бөлігін сканерлейді — қозғалтқыш Q2, Q3 және Q4-ті толығымен өткізіп жібереді. Бөліктерді қырқу (partition pruning) деп аталатын осы техника ірі тарихи деректер жиынтықтарында реттік үлғайту жылдамдығын береді.
PostgreSQL vs ClickHouse vs BigQuery: дұрыс қозғалтқышты таңдау
Барлық дерекқор қаржы аналитикасын бірдей меңгермейді. Таңдау сұрақ үлгілеріңізге, деректер көлеміне және кідіріс талаптарына байланысты.
PostgreSQL — жұмысшы ат. Ол транзакциялық жүктемелерде (мәмілелерді нақты уақытта жазу) күшті, ACID кепілдіктерін қолдайды, күрделі JOIN-дарды сезімталдықпен басқарады және дұрыс индекстеу мен бөліктеумен жүздеген миллион жолға дейін масштабтайды. Ол операциялық дерекқорлар — жазба жүйесі — үшін дұрыс таңдау. GaiaEx, мысалы, қаржы жүйелері талап ететін сенімділік кепілдіктерімен өзінің негізгі сауда деректері үшін PostgreSQL-сәйкес инфраструктураға сүйенеді.
ClickHouse — миллиардтаған жолдардағы талдамалық сұрақтарға арналған бағаналық дерекқор. PostgreSQL деректерді жол-жол сақтайтын жерде (жеке мәмілелерді қосу үшін тамаша), ClickHouse деректерді баған-баған сақтайды (миллиондаған жолдар бойынша бір бағанды агрегаттау үшін тамаша). PostgreSQL-де 30 секунд алатын «BTC үшін 3 жыл бойынша сағаттық орташа көлем» сияқты сұрақ ClickHouse-те 200 миллисекундта аякталуы мүмкін. Алмасу: ClickHouse UPDATE немесе DELETE-ті тиімді қолдамайды — ол дизайн бойынша тек-қосу.
BigQuery (Google Cloud) — серверсіз бағаналық деректер қоймасы. Басқаруға тиісті инфраструктура жоқ, сұрақ бойынша төлем және петабайт-өлшемді сыйымдылық. Ол ad-hoc аналитика, зерттеу және зерттестіру үшін идеалды — бірақ сұрақ кідірісі секундпен өлшенеді, миллисекундпен емес, бұл оны нақты-уақыттағы қолданбалар үшін жарамсыз етеді.
- Нақты-уақыттағы мәміле жазу және тапсырыс басқару → PostgreSQL
- Миллиардтаған жолдар бойынша тарихи аналитика және бэктестинг → ClickHouse
- Ad-hoc зерттестіру және командалар аралық деректер алмасу → BigQuery
Көпшілік кәсіби сауда фирмалары қатламдалған архитектурада үшеуін де қолданады: ыстық операциялық қоймаға PostgreSQL, жылы талдамалық қатламга ClickHouse, және суық архивке BigQuery. Жақсы SQL жазу шеберлігі олардың барлығы арқылы тасымалданады — синтаксис 90% бірдей, ал талдамалық ой 100% бірдей.

