Real-Time Market AnalyticsClickHouse Workshops

04 Executar as consultas de mercado

Sete consultas sobre 26,5 milhões de ticks, cada uma substituindo uma página de SQL: contagens aproximadas, topK, candles OHLC, percentis, combinadores -If, argMax e o desafio final da chave de ordenação.

Estas são as "funções que substituem uma página de SQL" do ClickHouse. Cole e execute cada bloco e leia a caixa verde para saber o que esperar. Todas as consultas usam a tabela forex que você acabou de carregar.

4.1 Aproximado ou exato — o grande truque

Quantos timestamps de cotação distintos existem em 26,5 milhões de ticks? Três funções respondem à mesma pergunta com diferentes compromissos. Execute uma de cada vez e observe o cronômetro nas estatísticas da consulta — executá-las separadamente é essencial para a comparação.

-- Approximate count of distinct timestamps (HyperLogLog)
SELECT uniq(datetime) AS distinct_ts FROM forex;
-- Exact count — precise, but scans every value
SELECT uniqExact(datetime) AS distinct_ts FROM forex;
-- Adaptive + tunable accuracy vs memory
SELECT uniqCombined(datetime) AS distinct_ts FROM forex;

Você deve ver

Cerca de 24,6 milhões de timestamps distintos nas três consultas. Porém, uniq retorna em aproximadamente 0,2 s; uniqExact leva cerca de 3 s (quase 17 vezes mais lento) para obter o número preciso; e uniqCombined fica a cerca de 0,4% do resultado em bem menos de um segundo. Em bilhões de linhas, essa diferença separa uma resposta imediata de uma pausa para o café.

4.2 Top-N com uma única função

Os pares com maior volume de cotações — sem precisar de GROUP BY / ORDER BY / LIMIT:

-- One row: an array of the 8 most actively quoted pairs (by tick count)
SELECT topK(8)(pair) AS most_active FROM forex;

Você deve ver

Uma única célula com um array de 8 pares, em ordem do mais cotado para o menos cotado (o ouro, XAU/USD, fica em primeiro). topK reduz toda a tabela a essa lista ordenada em uma passagem — substituindo um GROUP BY … ORDER BY count() DESC LIMIT 8.

4.3 Série temporal em uma passagem — candles OHLC

Abertura, máxima, mínima e fechamento diários do ouro — a consulta essencial do mercado:

-- One row per day: gold (XAU/USD) open, high, low, close — a candlestick chart
SELECT
    toDate(datetime)      AS day,
    argMin(bid, datetime) AS open,    -- bid at the day's first tick
    max(bid)              AS high,
    min(bid)              AS low,
    argMax(bid, datetime) AS close    -- bid at the day's last tick
FROM forex
WHERE base = 'XAU' AND quote = 'USD'
GROUP BY day
ORDER BY day;

Você deve ver

Uma linha por dia de janeiro de 2020. argMin(bid, datetime) é o bid do primeiro tick do dia (a abertura), enquanto argMax é o último (o fechamento) — sem funções de janela nem autojoins. Observe o ouro subir de cerca de 1.520 para 1.610 ao longo do mês.

4.4 Percentis sem malabarismo com janelas

O spread entre bid e ask indica liquidez — e a média esconde a cauda, então use percentis:

-- One row per pair: median and 99th-percentile bid/ask spread (tighter = more liquid)
SELECT
    pair,
    round(quantile(0.5)(ask - bid), 6)  AS median_spread,
    round(quantile(0.99)(ask - bid), 6) AS p99_spread
FROM forex
GROUP BY pair
ORDER BY median_spread ASC;

Você deve ver

Uma linha por par, começando pelo menor spread. EUR/USD aparece como o mais líquido (cerca de 0,00002, menos de um pip); o ouro tem o maior spread. quantile é calculado de forma aproximada em uma única passagem — sem ordenar a coluna inteira.

4.5 Combinadores — uma leitura, duas respostas

Compare lado a lado o spread médio durante o horário ativo e o período de menor atividade, em uma única leitura:

-- One row per pair: avg spread during the active window vs quiet hours
SELECT
    pair,
    count()                                                           AS ticks,
    round(avgIf(ask - bid, toHour(datetime) BETWEEN 7 AND 20), 6)     AS spread_active,
    round(avgIf(ask - bid, toHour(datetime) NOT BETWEEN 7 AND 20), 6) AS spread_quiet
FROM forex
GROUP BY pair
ORDER BY ticks DESC;

Você deve ver

Os spreads dos períodos ativo e menos movimentado no mesmo resultado. O combinador -If acrescenta uma condição a qualquer agregação: avgIf(x, cond) calcula a média de x apenas onde cond é verdadeiro — sem consulta em duas passagens e sem CASE WHEN. Quase toda agregação o aceita (countIf, sumIf, quantileIf, …).

4.6 O valor mais recente — argMax

A cotação mais recente de cada par, em uma única passagem:

-- One row per pair: the latest quoted bid and the timestamp it was seen
SELECT
    pair,
    argMax(bid, datetime) AS last_bid,
    max(datetime)         AS as_of
FROM forex
GROUP BY pair
ORDER BY pair;

Você deve ver

O bid mais recente de cada par. argMax(bid, datetime) retorna o bid da linha que contém o maior datetime — a consulta cotidiana de "último preço / status mais recente", sem função de janela ou autojoin.

4.7 O desafio final — com e sem chave de ordenação

As duas consultas têm o mesmo formato — contar os ticks que atendem a uma condição — mas um filtro usa a chave de ordenação e o outro não. Primeiro, execute as duas contagens:

-- Optimized: base+quote ARE the leading sort key — the index skips to that pair
SELECT count() FROM forex WHERE base = 'XAU' AND quote = 'USD';

-- Non-optimized: bid is NOT in the sort key — no index, scans all 26.5M ticks.
-- (cache off so the full scan shows on every run)
SELECT count() FROM forex WHERE bid > 1.5
SETTINGS use_query_condition_cache = 0;

Difícil perceber a diferença?

As duas contagens retornam em poucos milissegundos; olhando apenas o tempo, quase não há mudança — count() é rápido nos dois casos. A diferença real está em quantos dados cada consulta precisa acessar. Para enxergá-la, peça ao ClickHouse que mostre o plano com EXPLAIN indexes = 1, que informa quantos grânulos (blocos de aproximadamente 8.192 linhas) serão lidos.

-- Optimized: the primary index skips straight to the XAU/USD rows
EXPLAIN indexes = 1
SELECT count() FROM forex WHERE base = 'XAU' AND quote = 'USD';

-- Non-optimized: bid isn't in the sort key, so nothing can be skipped
EXPLAIN indexes = 1
SELECT count() FROM forex WHERE bid > 1.5;

Você deve ver

No plano otimizado, a seção Indexes → PrimaryKey mostra apenas uma parte dos grânulos selecionada — cerca de 492 / 3.234 (aproximadamente 16 mil linhas). No plano não otimizado, não há índice aplicável; ele lê 3.234 / 3.234 grânulos — todas as linhas. Mesmo formato de consulta, volume de trabalho totalmente diferente. A lição: coloque as colunas mais usadas nos filtros no início do seu ORDER BY.

Mantenha esta consulta à mão

Você entregará a consulta não otimizada WHERE bid > 1.5 à IA no próximo módulo.

Nesta página

Acompanhar seu progresso?

Opcional. Enviaremos um link por e-mail para confirmar seu endereço; o progresso será registrado depois que você o abrir.

Use seu e-mail corporativo, não um endereço pessoal.

O acompanhamento do progresso também exige a aceitação dos Termos de Serviço atuais nas Configurações de privacidade.

PT