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|
бегло глянул Git, что-то незаметил "как именно забираются данные из mql то есть часть которая в терминале" и механику заполнения пропусков (а-ля терминал ушёл в аут и после рестарта надо заливать пропуски). Может правда плохо искал :-)
просто я подобное делал, но с Influx и Telegraph - идейно всё совпадает. Эвенты и данные льются в роутер, роутер отдаёт получателям (в том числе базе), кому нужна аналитика лезет к базе и не мешает терминалам. Модели push/poll разные, но не суть
в гите клиентская часть
а данные собираются из нескольких открытых онлайн источников.
терминал не используется
терминал не используется
разочаровал ты меня.
честно говоря от публикации на форуме MQL ожидал интеграция с терминалом. И решений узкоспецифичных проблем именно между ними двумя (терминалом и базой)
а просто TimeSeries DB, даже и неспортивно.
Да ещё с порезанными тиками и без стакана.
Кстати не понял смысла прорежать тики, они и в полном-то объёме не то чтобы совсем дохрена. Много, ну на то она и база чтобы хранить и обрабатывать большие объёмы и потоки.
А без тиков львиную долю задач вообще не решить. Спреды/синтетики/арбитражи отлетают
Стаканы конечно. Есть идеи как эффективно сохранять историю стакана, чтобы одновременно компактно, быстро и удобно работать ?
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Добрый день!
Сделал проект хранилища данных для анализа рынков на основе СУБД 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.
Проект только запустил на той неделе.
Сейчас по одной самой большой исторической таблице в моей бд статистика такая:
Гипертаблицы (таблицы фактов в терминах TimescaleDB) партицированны по дате evts с шагом 1 час. По умолчанию настроено так, что через 3 часа партиции сжимаются и переводятся в колоночный формат. Можно настроить retention policy, чтобы автоматически удалять данные старше определенного интервала.
Преимущества использования промышленной СУБД очевидны.
Клиент-сервер. Одна база на 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.
если предыдущая команда не найдет образ под вашу архитектуру, например, если вы на Mac, то надо будет сделать
3.
(перед этим можете поменять пароли, с которыми будут созданы пользователи бд: postgres, webdwh, viewer в файле ./timescaledb/.env.db.auth.txt)
подождите 5-10 сек, пока пустая база данных создастся
4.
это создаст схемы и таблицы, и заполнит справочники в БД.
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.
ну и финальный запуск:
=====
Подключиться можно вашим любимым sql клиентом на порту 5400.
логин/пароль = webdwh/webdwh по умолчанию (или postgrs/postgres (для администрирования), или viewer/viewer (только для просмотра данных)).
Посмотреть последний слепок рынка Forex на сейчас можно запросом
Продолжение следует...