Анализ рынка на SQL в реальном времени.

 

Добрый день!

Сделал проект хранилища данных для анализа рынков на основе СУБД TimescaleDB – расширении Postgres для работы с временными рядами. (в прошлом разрабатывал корпоративны DWH и ETL на Oracle, так что "немного" шарю в хранилищах)

Устанавливается через docker compose в несколько простых команд.

После первого запуска стека docker вы получите готовую базу данных (изначально заполнены только справочники), которая начнёт немедленно заполнять данными в реальном времени. База данных сохраняется между перезапусками, разумеется.

Как это работает на верхнем уровне.

При помощи особенного кунг-фу "real-time web pumps (parsers)" данные собираются из нескольких открытых онлайн источников (ask, bid, last) в реальном времени (средняя скорость 200+ тик/с, в пике свыше 1000 тик/с) и помещаются в брокер сообщений NATS.

Запущенные вами контейнеры подключаются к брокеру NATS, забирают данные, буферизуют/троттлят их, и с заданной периодичностью (по умолчанию 1 сек) скидывают в базу данных.

Например, за секунду из NATS пришло
USDZAR – 22 тика, USDNOK – 28 тиков, EURGBP – 8 тиков.
в конце секунды в БД будет сброшено по одному последнему значению, для каждой валютной пары (при этом кол-во прошедших тиков также записывается).

Это позволяет с одной стороны иметь приблизительную, но статистически точную картину рынка, с другой стороны, позволяет базе данных "дышать" и обслуживать пользовательские запросы даже с 1-2 CPU.


Проект только запустил на той неделе.

Сейчас по одной самой большой исторической таблице в моей бд статистика такая:

  

select date_trunc('day', q.evts) as day,

count(*) as row_cnt,

sum(q.tick_cnt) as tick_cnt

from mql.quotes q

group by date_trunc('day', q.evts)

order by day

#|day |row_cnt|tick_cnt| -+-----------------------------+-------+--------+ 1|2026-07-23 00:00:00.000 +0300|3843334|15473287| 2|2026-07-24 00:00:00.000 +0300|3919000|14680917| 3|2026-07-27 00:00:00.000 +0300|4162471|15687260| 4|2026-07-28 00:00:00.000 +0300|2561287| 8971931| select hypertable_size('mql.quotes')::numeric/1024/1024 AS mb; #|mb | -+--------------------+ 1|534.2734375000000000|

 

Гипертаблицы (таблицы фактов в терминах TimescaleDB) партицированны по дате evts с шагом 1 час. По умолчанию настроено так, что через 3 часа партиции сжимаются и переводятся в колоночный формат. Можно настроить retention policy, чтобы автоматически удалять данные старше определенного интервала.

select add_retention_policy(relation=>'qml.quotes', drop_after=>interval'3 month', schedule_interval=>interval'1 day');

 

Преимущества использования промышленной СУБД очевидны.

Клиент-сервер. Одна база на VPS или на постоянно включённом компьютере к которой можно подключаться откуда угодно большому кол-ву аналитиков.

SQL знают все, а коннекторов подключения к Postgres навалом в любом языке.

 

Как установить.

Вы должны уметь в командную строку, Docker и желательно в git (чтобы скачивать обновления в будущем).

 

Попробовать проще всего на Docker Desktop (windows).

Выберите папку, где будет располагаться проект (в проекте будет немного текстовых файлов, сама БД будет жить в docker volume)

1.

git clone https://github.com/pv14mn/webdwh_client.git

cd webdwh_client

или просто скачайте и распакуйте https://github.com/pv14mn/webdwh_client/archive/refs/heads/main.zip

и перейдите в командной строке папку webdwh_client

2.

docker compose pull --policy missing

если предыдущая команда не найдет образ под вашу архитектуру, например, если вы на Mac, то надо будет сделать

docker compose build

3.

docker compose up -d timescaledb

(перед этим можете поменять пароли, с которыми будут созданы пользователи бд: postgres, webdwh, viewer в файле ./timescaledb/.env.db.auth.txt)

подождите 5-10 сек, пока пустая база данных создастся

4.

docker compose run --rm db-init

это создаст схемы и таблицы, и заполнит справочники в БД.

5.

! теперь необходимо прописать параметры подключения к брокеру сообщений

откройте текстовый файл
./
node/.env.nats.auth.txt
и вставьте в него

NATS_URL=nats://104.171.138.245:4222
NATS_ENGINE=core
NATS_USER_PUBLIC=UACAUQJ4W2NDUHYQXONWPB4PSBB6UH3IEFB2S6U7CJA2Z5YTY7R4IPK5
NATS_USER_SECRET=SUAFWGEW4KKE4EFNPXA6YV6UAIUP3YTOK2A2DNDPS4Z4LYAP5GIUOTKTNM
NATS_CONS_SECRET=

6.

ну и финальный запуск:

docker compose up -d

 

=====

Подключиться можно вашим любимым sql клиентом на порту 5400.

логин/пароль = webdwh/webdwh по умолчанию (или postgrs/postgres (для администрирования), или viewer/viewer (только для просмотра данных)).

Посмотреть последний слепок рынка Forex на сейчас можно запросом

select t.description, t.scode, q.evts, q.ask, q.bid, q.tick_cnt
  from mql.quotes_last q
  join mql.tickers t
    on t.icode = q.icode
where 1=1
  --and t.scode = 'AUDUSD'
order by q.evts desc

#  |description                                 |scode |evts                         |ask      |bid      |tick_cnt|
---+--------------------------------------------+------+-----------------------------+---------+---------+--------+
1  |Новозеландский доллар / Канадский доллар    |NZDCAD|2026-07-28 16:30:14.779 +0300|  0.81429|  0.81424|       8|
2  |Евро / Гонконгский доллар                   |EURHKD|2026-07-28 16:30:14.779 +0300|  8.91475|  8.91458|       6|
3  |Евро / Австралийский доллар                 |EURAUD|2026-07-28 16:30:14.777 +0300|  1.63071|  1.63068|      13|
4  |Доллар США / Мексиканское песо              |USDMXN|2026-07-28 16:30:14.767 +0300| 17.46614| 17.46557|      26|
5  |Австралийский доллар / Новозеландский доллар|AUDNZD|2026-07-28 16:30:14.757 +0300|  1.20775|  1.20771|       9|
6  |Фунт стерлингов / Японская иена             |GBPJPY|2026-07-28 16:30:14.757 +0300|  217.922|  217.914|      18|
7  |Канадский доллар / Японская иена            |CADJPY|2026-07-28 16:30:14.757 +0300|  116.173|  116.165|       8|
8  |Австралийский доллар / Канадский доллар     |AUDCAD|2026-07-28 16:30:14.756 +0300|  0.98342|  0.98337|      11|
9  |Евро / Новозеландский доллар                |EURNZD|2026-07-28 16:30:14.738 +0300|  1.96948|   1.9694|       8|
10 |Фунт стерлингов / Новозеландский доллар     |GBPNZD|2026-07-28 16:30:14.737 +0300|  2.30386|  2.30377|       9|
11 |Австралийский доллар / Японская иена        |AUDJPY|2026-07-28 16:30:14.736 +0300|   114.24|  114.237|      12|
12 |Фунт стерлингов / Канадский доллар          |GBPCAD|2026-07-28 16:30:14.727 +0300|  1.87592|  1.87585|      10|
13 |Доллар США / Канадский доллар               |USDCAD|2026-07-28 16:30:14.726 +0300|  1.41069|  1.41067|       8|
14 |Норвежская крона / Японская иена            |NOKJPY|2026-07-28 16:30:14.721 +0300|   16.907|   16.905|       3|
15 |Доллар США / Южноафриканский рэнд           |USDZAR|2026-07-28 16:30:14.718 +0300| 16.75069|  16.7486|      23|
16 |Доллар США / Венгерский форинт              |USDHUF|2026-07-28 16:30:14.718 +0300|   317.37|  317.305|      11|
17 |Доллар США / Новый израильский шекель       |USDILS|2026-07-28 16:30:14.717 +0300|  3.05956|  3.05106|       3|
18 |Новозеландский доллар / Сингапурский доллар |NZDSGD|2026-07-28 16:30:14.705 +0300|  0.74648|  0.74642|       5|
19 |Канадский доллар / Швейцарский франк        |CADCHF|2026-07-28 16:30:14.705 +0300|  0.58079|   0.5807|       1|
20 |Евро / китайский офшорный юань              |EURCNH|2026-07-28 16:30:14.705 +0300|  7.69908|  7.69889|       4|
21 |US Dollar vs Brazilian Real                 |USDBRL|2026-07-28 16:30:14.691 +0300|   5.1541|   5.1526|       5|
22 |Фунт стерлингов / Швейцарский франк         |GBPCHF|2026-07-28 16:30:14.687 +0300|  1.08944|  1.08941|       5|
23 |Фунт стерлингов / Австралийский доллар      |GBPAUD|2026-07-28 16:30:14.687 +0300|  1.90759|   1.9075|      18|
24 |Доллар США / Норвежская крона               |USDNOK|2026-07-28 16:30:14.680 +0300|   9.6937|   9.6929|      16|
25 |Фунт стерлингов / Норвежская крона          |GBPNOK|2026-07-28 16:30:14.680 +0300|  12.8928|  12.8882|       3|
26 |Швейцарский франк / Венгерский форинт       |CHFHUF|2026-07-28 16:30:14.665 +0300|  387.477|   387.26|       1|
27 |Австралийский доллар / Доллар США           |AUDUSD|2026-07-28 16:30:14.657 +0300|  0.69712|  0.69711|       9|
28 |Евро / Южноафриканский рэнд                 |EURZAR|2026-07-28 16:30:14.654 +0300| 19.04584| 19.03826|      16|
29 |Евро / Мексиканское песо                    |EURMXN|2026-07-28 16:30:14.654 +0300| 19.85756| 19.85368|       6|
30 |Фунт стерлингов / Датская крона             |GBPDKK|2026-07-28 16:30:14.643 +0300|   8.7452|   8.7432|       2|
31 |Евро / Шведская крона                       |EURSEK|2026-07-28 16:30:14.643 +0300| 11.06448| 11.06278|      13|
32 |Australian Dollar vs Danish Krona           |AUDDKK|2026-07-28 16:30:14.628 +0300|  4.58787|  4.58037|      10|
33 |Фунт стерлингов / Мексиканское песо         |GBPMXN|2026-07-28 16:30:14.622 +0300|  23.2284|  23.2245|       3|
34 |Евро / Канадский доллар                     |EURCAD|2026-07-28 16:30:14.616 +0300|  1.60366|  1.60362|       6|
35 |Доллар США / Гонконгский доллар             |USDHKD|2026-07-28 16:30:14.607 +0300|   7.8417|  7.84162|       1|
36 |Доллар США / Японская иена                  |USDJPY|2026-07-28 16:30:14.604 +0300|  163.872|  163.869|       8|
37 |US Dollar vs South korean won               |USDKRW|2026-07-28 16:30:14.595 +0300|  1459.51|  1459.15|       4|
38 |Австралийский доллар / Сингапурский доллар  |AUDSGD|2026-07-28 16:30:14.583 +0300|  0.90155|  0.90144|       2|
39 |Australian Dollar vs Hong Kong Dollar       |AUDHKD|2026-07-28 16:30:14.580 +0300|  5.46689|  5.46656|       3|
40 |Chinese Yuan Renminbi vs Japanese Yen       |CNHJPY|2026-07-28 16:30:14.580 +0300|     24.2|   24.196|       1|
41 |Сингапурский доллар / Японская иена         |SGDJPY|2026-07-28 16:30:14.580 +0300|  126.731|  126.715|       1|
42 |Новозеландский доллар / Японская иена       |NZDJPY|2026-07-28 16:30:14.569 +0300|   94.594|   94.588|      10|
43 |Фунт стерлингов / Доллар США                |GBPUSD|2026-07-28 16:30:14.557 +0300|  1.32982|  1.32982|       6|
44 |Евро / Норвежская крона                     |EURNOK|2026-07-28 16:30:14.555 +0300|  11.0201|  11.0189|      11|
45 |Canadian Dollar vs Norwegian Krona          |CADNOK|2026-07-28 16:30:14.530 +0300|  6.87255|  6.87093|       4|
46 |Фунт стерлингов / Южноафриканский рэнд      |GBPZAR|2026-07-28 16:30:14.518 +0300|  22.2785|   22.271|      10|
47 |Швейцарский франк / Японская иена           |CHFJPY|2026-07-28 16:30:14.475 +0300|  200.033|  200.026|       4|
48 |Австралийский доллар / Швейцарский франк    |AUDCHF|2026-07-28 16:30:14.469 +0300|  0.57113|  0.57109|       6|
49 |Евро / Японская иена                        |EURJPY|2026-07-28 16:30:14.445 +0300|  186.294|  186.291|       4|
50 |Фунт стерлингов / Шведская крона            |GBPSEK|2026-07-28 16:30:14.443 +0300|  12.9437|  12.9395|       3|
51 |Фунт стерлингов / Гонконгский доллар        |GBPHKD|2026-07-28 16:30:14.419 +0300|  10.4285| 10.42758|       2|
52 |Доллар США / Чешская крона                  |USDCZK|2026-07-28 16:30:14.418 +0300|  21.2714|  21.2676|      12|
53 |Фунт стерлингов / Сингапурский доллар       |GBPSGD|2026-07-28 16:30:14.378 +0300|  1.71976|  1.71952|       2|
54 |Евро / Польский злотый                      |EURPLN|2026-07-28 16:30:14.367 +0300|  4.32435|  4.32335|       9|
55 |Евро / Доллар США                           |EURUSD|2026-07-28 16:30:14.354 +0300|  1.13682|  1.13682|       1|
56 |Доллар США / Датская крона                  |USDDKK|2026-07-28 16:30:14.308 +0300|  6.57579|  6.57551|       5|
57 |Новозеландский доллар / Доллар США          |NZDUSD|2026-07-28 16:30:14.302 +0300|  0.57721|  0.57721|       9|
58 |Евро / Швейцарский франк                    |EURCHF|2026-07-28 16:30:14.267 +0300|  0.93135|  0.93132|       5|
59 |US Dollar vs Indonesian Rupiah              |USDIDR|2026-07-28 16:30:14.266 +0300|18120.325|18120.325|       2|
60 |Фунт стерлингов / Польский злотый           |GBPPLN|2026-07-28 16:30:14.256 +0300|   5.0589|   5.0501|      16|
61 |Швейцарский франк / Польский злотый         |CHFPLN|2026-07-28 16:30:14.227 +0300|  4.64497|  4.64052|       1|
62 |Доллар США / Польский злотый                |USDPLN|2026-07-28 16:30:14.205 +0300|  3.80366|   3.8033|      13|
63 |US Dollar vs Indian Rupee                   |USDINR|2026-07-28 16:30:14.144 +0300|   96.046|   96.031|       4|
64 |US Dollar vs Chilean Peso                   |USDCLP|2026-07-28 16:30:14.110 +0300|    937.6|    936.9|       1|
65 |Новозеландский доллар / Швейцарский франк   |NZDCHF|2026-07-28 16:30:14.105 +0300|  0.47291|  0.47287|       6|
66 |Доллар США / Швейцарский франк              |USDCHF|2026-07-28 16:30:14.003 +0300|  0.81922|  0.81922|       1|
67 |Доллар США / Шведская крона                 |USDSEK|2026-07-28 16:30:13.993 +0300|   9.7324|   9.7317|       2|
68 |Норвежская крона / Шведская крона           |NOKSEK|2026-07-28 16:30:13.956 +0300|  1.00411|  1.00396|       2|
69 |Евро / Венгерский форинт                    |EURHUF|2026-07-28 16:30:13.869 +0300|  360.833|  360.721|      10|
70 |Канадский доллар / Сингапурский доллар      |CADSGD|2026-07-28 16:30:13.818 +0300|  0.91677|  0.91666|       1|
71 |Фунт стерлингов / Турецкая лира             |GBPTRY|2026-07-28 16:30:13.817 +0300| 63.05926| 63.04143|       1|
72 |Евро / Сингапурский доллар                  |EURSGD|2026-07-28 16:30:13.779 +0300|  1.47015|  1.47003|       4|
73 |Доллар США / китайский офшорный юань        |USDCNH|2026-07-28 16:30:13.729 +0300|  6.77235|  6.77223|       1|
74 |Доллар США / Китайский юань                 |USDRMB|2026-07-28 16:30:13.729 +0300|  6.77235|  6.77223|       1|
75 |Фунт стерлингов / Венгерский форинт         |GBPHUF|2026-07-28 16:30:13.614 +0300|  422.032|  421.807|       1|
76 |Шведская крона / Японская иена              |SEKJPY|2026-07-28 16:30:13.515 +0300|   16.845|   16.833|       1|
77 |Швейцарский франк / Сингапурский доллар     |CHFSGD|2026-07-28 16:30:13.061 +0300|  1.57858|  1.57839|       1|
78 |Швейцарский франк / Шведская крона          |CHFSEK|2026-07-28 16:30:12.981 +0300| 11.88216| 11.87624|       6|
79 |Доллар США / Сингапурский доллар            |USDSGD|2026-07-28 16:30:12.967 +0300|  1.29318|  1.29313|       4|
80 |Евро / Турецкая лира                        |EURTRY|2026-07-28 16:30:12.929 +0300| 53.90273| 53.89604|       2|
81 |Евро / Фунт стерлингов                      |EURGBP|2026-07-28 16:30:12.856 +0300|  0.85489|  0.85488|       4|
82 |Швейцарский франк / Датская крона           |CHFDKK|2026-07-28 16:30:12.784 +0300|  8.02738|  8.02564|       5|
83 |Доллар США / Тайский бат                    |USDTHB|2026-07-28 16:30:12.738 +0300|   33.556|   33.551|       4|
84 |Фунт стерлингов / Чешская крона             |GBPCZK|2026-07-28 16:30:10.995 +0300|  28.3143|  28.2525|       1|
85 |Мексиканское песо / Японская иена           |MXNJPY|2026-07-28 16:30:10.374 +0300|    9.385|    9.382|       1|
86 |Swiss Franc vs South African Rand           |CHFZAR|2026-07-28 16:30:10.339 +0300| 20.45003| 20.44309|       1|
87 |Датская крона / Шведская крона              |DKKSEK|2026-07-28 16:30:10.303 +0300|  1.48025|  1.47972|       1|
88 |Австралийский доллар / Шведская крона       |AUDSEK|2026-07-28 16:30:10.266 +0300|  6.78486|  6.78364|       1|
89 |Australian Dollar vs Norwegian Krona        |AUDNOK|2026-07-28 16:30:10.266 +0300|  6.75788|  6.75678|       1|
90 |Швейцарский франк / Норвежская крона        |CHFNOK|2026-07-28 16:30:09.711 +0300| 11.83271| 11.83073|       1|
91 |Новозеландский доллар / Датская крона       |NZDDKK|2026-07-28 16:30:09.340 +0300|  3.79633|    3.795|       4|
92 |Новозеландский доллар / Шведская крона      |NZDSEK|2026-07-28 16:30:09.304 +0300|  5.61866|  5.61663|       2|
93 |Евро / Датская крона                        |EURDKK|2026-07-28 16:30:08.659 +0300|  7.47547|  7.47534|       2|
94 |Zloty vs Japanese Yen                       |PLNJPY|2026-07-28 16:30:08.233 +0300|   43.104|   43.062|       1|
95 |Доллар США / Турецкая лира                  |USDTRY|2026-07-28 16:30:07.941 +0300|  47.3799|  47.3783|       1|
96 |Гонконгский доллар / Японская иена          |HKDJPY|2026-07-28 16:30:07.682 +0300|     20.9|   20.895|       2|
97 |US Dollar vs Colombian Peso                 |USDCOP|2026-07-28 16:30:07.386 +0300|  3218.28|   3211.2|       2|
98 |Евро / Чешская крона                        |EURCZK|2026-07-28 16:30:06.789 +0300|  24.1815|   24.178|       4|
99 |Сингапурский доллар / Гонконгский доллар    |SGDHKD|2026-07-28 16:30:05.523 +0300|  6.06462|  6.06348|       1|
100|Южноафриканский рэнд / Японская иена        |ZARJPY|2026-07-28 16:30:05.259 +0300|    9.784|    9.782|       2|
101|Турецкая лира / Японская иена               |TRYJPY|2026-07-28 16:30:04.520 +0300|    3.458|    3.455|       1|
102|Australian Dollar vs Zloty                  |AUDPLN|2026-07-28 16:30:03.637 +0300|  2.65583|  2.64733|       1|
103|Евро / Российский рубль                     |EURRUB|2026-07-28 16:25:22.947 +0300|    91.04|    88.62|       1|
104|US Dollar vs Nigerian Naira                 |USDNGN|2026-07-28 16:25:07.417 +0300| 1366.178| 1364.962|       1|
105|Доллар США / Российский рубль               |USDRUB|2026-07-28 16:09:38.445 +0300|   79.225|   77.725|       1|

   

Продолжение следует...
GitHub - pv14mn/webdwh_client
GitHub - pv14mn/webdwh_client
  • pv14mn
  • github.com
A turnkey Docker Compose solution for streaming real-time market indicators (Forex, etc.) directly into your local or VPS time-series database.
 

TimescaleDB предоставляет удобные функции для работы с временными рядами.

Например, вы зачем-то захотели построить 13 минутные свечи.

Для этого будет удобно воспользоваться функцией time_bucket или time_bucket_gapfill


select time_bucket('13 min', q.evts) AS tbucket,
       any_value(t.scode) as scode,
       first((ask + bid) / 2, evts) AS first_price,
       last((ask + bid) / 2, evts) AS last_price,
       min((ask + bid) / 2) AS min_price,
       max((ask + bid) / 2) AS max_price,
       sum(tick_cnt) AS total_ticks 
 from mql.quotes q
 join mql.tickers t
   on t.icode = q.icode
where 1 = 1
  and q.evts >= date_trunc('day', now())
  and t.scode = 'EURUSD'
group by t.scode, tbucket
order by t.scode, tbucket

# |tbucket                      |scode |first_price           |last_price            |min_price             |max_price             |total_ticks|
--+-----------------------------+------+----------------------+----------------------+----------------------+----------------------+-----------+
1 |2026-07-28 00:00:00.000 +0300|EURUSD|1.13677500000000000000|1.13719000000000000000|1.13677500000000000000|1.13753000000000000000|        325|
2 |2026-07-28 00:13:00.000 +0300|EURUSD|1.13719000000000000000|1.13733500000000000000|1.13717000000000000000|1.13741500000000000000|        270|
3 |2026-07-28 00:26:00.000 +0300|EURUSD|1.13739500000000000000|1.13708500000000000000|1.13696000000000000000|1.13739500000000000000|        830|
4 |2026-07-28 00:39:00.000 +0300|EURUSD|1.13708500000000000000|1.13727000000000000000|1.13699000000000000000|1.13734000000000000000|        695|
5 |2026-07-28 00:52:00.000 +0300|EURUSD|1.13726000000000000000|1.13702000000000000000|1.13692000000000000000|1.13732000000000000000|        829|
6 |2026-07-28 01:05:00.000 +0300|EURUSD|1.13704000000000000000|1.13709500000000000000|1.13688000000000000000|1.13715000000000000000|       1014|
7 |2026-07-28 01:18:00.000 +0300|EURUSD|1.13708500000000000000|1.13696000000000000000|1.13691000000000000000|1.13712000000000000000|        848|
8 |2026-07-28 01:31:00.000 +0300|EURUSD|1.13695000000000000000|1.13696500000000000000|1.13692500000000000000|1.13706000000000000000|        669|
9 |2026-07-28 01:44:00.000 +0300|EURUSD|1.13696500000000000000|1.13690000000000000000|1.13689500000000000000|1.13698000000000000000|        611|
10|2026-07-28 01:57:00.000 +0300|EURUSD|1.13691000000000000000|1.13702500000000000000|1.13682500000000000000|1.13705000000000000000|        757|
11|2026-07-28 02:10:00.000 +0300|EURUSD|1.13704000000000000000|1.13704000000000000000|1.13695000000000000000|1.13705500000000000000|        526|

С учётом описанного выше способа буферизации, данные, конечно, получатся приблизительными (например, за секунду вы можете пропустить реальный всплеск max/min)

 

бегло глянул Git, что-то незаметил "как именно забираются данные из mql то есть часть которая в терминале" и механику заполнения пропусков (а-ля терминал ушёл в аут и после рестарта надо заливать пропуски). Может правда плохо искал :-)

просто я подобное делал, но с Influx и Telegraph - идейно всё совпадает. Эвенты и данные льются в роутер, роутер отдаёт получателям (в том числе базе), кому нужна аналитика лезет к базе и не мешает терминалам. Модели push/poll разные, но не суть

 

найдём top роста/падения за последние 30 минут по наклону регрессионной линии (slope_norm)

with src as (
select t.scode,
       q.evts,
       extract(epoch from q.evts)/60/60 as unix_evts,
       (q.ask + q.bid)/2 as last_pr
  from mql.quotes q
  join mql.tickers t
    on t.icode = q.icode
 where q.evts >= now() - interval '30 minute'
   -- and t.scode not in ('USDIDR')
),
scaled as (
  select src.*,
         (last_pr - avg(last_pr) over (partition by scode))
         /
         stddev(last_pr) over (partition by scode) as last_pr_norm
    from src
)
select *
  from (
 select min(s.evts) as date_b,
        max(s.evts) as date_e,
        s.scode,
        round(regr_slope(s.last_pr_norm, s.unix_evts)::numeric, 6) as slope_norm,
        round(regr_slope(s.last_pr, s.unix_evts)::numeric, 6) as slope,
        -- stddev(s.last_pr_sc) as stddev_norm, == 1
        round(stddev(s.last_pr), 6) as stddev,
        count(*) as cnt
   from scaled s
  group by s.scode
 having count(*) >= 25
)
order by abs(slope_norm) desc
limit 10

# |date_b                   |date_e                   |scode |slope_norm|slope    |stddev  |cnt |
--+-------------------------+-------------------------+------+----------+---------+--------+----+
1 |'2026-07-28 21:41:53.922'|'2026-07-28 22:11:43.199'|CADNOK| -6.816649|-0.007877|0.001156|1041|
2 |'2026-07-28 21:41:47.540'|'2026-07-28 22:11:45.729'|AUDCAD|  6.657076| 0.000912|0.000137|1268|
3 |'2026-07-28 21:41:48.598'|'2026-07-28 22:11:46.295'|GBPJPY|  6.625107| 0.104702|0.015804|1145|
4 |'2026-07-28 21:41:48.598'|'2026-07-28 22:11:46.945'|NZDCAD|  6.381547| 0.000503|0.000079| 874|
5 |'2026-07-28 21:41:54.096'|'2026-07-28 22:11:21.675'|EURTRY|  6.166117| 0.032611|0.005289| 329|
6 |'2026-07-28 21:41:47.888'|'2026-07-28 22:11:46.956'|GBPCAD|  6.160634| 0.001347|0.000219|1373|
7 |'2026-07-28 21:41:47.888'|'2026-07-28 22:11:46.359'|USDMXN| -6.036042|-0.016140|0.002674|1463|
8 |'2026-07-28 21:42:00.444'|'2026-07-28 22:11:42.278'|USDBRL| -6.034108|-0.003886|0.000644| 458|
9 |'2026-07-28 21:41:54.117'|'2026-07-28 22:11:44.292'|USDTHB| -5.966338|-0.019541|0.003275| 168|
10|'2026-07-28 21:41:48.778'|'2026-07-28 22:11:41.875'|EURCAD|  5.944364| 0.001022|0.000172| 932|
 
Maxim Kuznetsov #:

бегло глянул Git, что-то незаметил "как именно забираются данные из mql то есть часть которая в терминале" и механику заполнения пропусков (а-ля терминал ушёл в аут и после рестарта надо заливать пропуски). Может правда плохо искал :-)

просто я подобное делал, но с Influx и Telegraph - идейно всё совпадает. Эвенты и данные льются в роутер, роутер отдаёт получателям (в том числе базе), кому нужна аналитика лезет к базе и не мешает терминалам. Модели push/poll разные, но не суть

в гите клиентская часть

а данные собираются из нескольких открытых онлайн источников.

терминал не используется

 
nakachu #:
терминал не используется

разочаровал ты меня.

честно говоря от публикации на форуме MQL ожидал интеграция с терминалом. И решений узкоспецифичных проблем именно между ними двумя (терминалом и базой)

а просто TimeSeries DB, даже и неспортивно.

Да ещё с порезанными тиками и без стакана.

Кстати не понял смысла прорежать тики, они и в полном-то объёме не то чтобы совсем дохрена. Много, ну на то она и база чтобы хранить и обрабатывать большие объёмы и потоки.
А без тиков львиную долю задач вообще не решить. Спреды/синтетики/арбитражи отлетают

Стаканы конечно. Есть идеи как эффективно сохранять историю стакана, чтобы одновременно компактно, быстро и удобно работать  ?