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, даже и неспортивно.
Да ещё с порезанными тиками и без стакана.
Кстати не понял смысла прорежать тики, они и в полном-то объёме не то чтобы совсем дохрена. Много, ну на то она и база чтобы хранить и обрабатывать большие объёмы и потоки.
А без тиков львиную долю задач вообще не решить. Спреды/синтетики/арбитражи отлетают
Стаканы конечно. Есть идеи как эффективно сохранять историю стакана, чтобы одновременно компактно, быстро и удобно работать ?
разочаровал ты меня.
честно говоря от публикации на форуме MQL ожидал интеграция с терминалом. И решений узкоспецифичных проблем именно между ними двумя (терминалом и базой)
а просто TimeSeries DB, даже и неспортивно.
Да ещё с порезанными тиками и без стакана.
Кстати не понял смысла прорежать тики, они и в полном-то объёме не то чтобы совсем дохрена. Много, ну на то она и база чтобы хранить и обрабатывать большие объёмы и потоки.
А без тиков львиную долю задач вообще не решить. Спреды/синтетики/арбитражи отлетают
Стаканы конечно. Есть идеи как эффективно сохранять историю стакана, чтобы одновременно компактно, быстро и удобно работать ?
Если бы я записывал все тики в базу, то у меня было бы 15.7 млн строк в день, вместо 4.2 млн.
Дело ещё и в железке под базой. Не каждый может себе позволить сортировать гигабайты.
Решение делалось скорее для статистического анализа на короткие и не очень дистанции. Типа найти все похожие графики за интервал времени.
На SQL такое конечно трудновато сделать (можно заморочиться с pgvector, искать косинусные расстояния).
Но есть другие способы – Python, R в связке с БД. Сейчас с ИИ это не очень сложно.
Для такого рода задач все тики не нужны и даже вредны, если цель сглаживать и обобщать. Даже 10 секундный буфер уменьшит базу в 10 раз без особых потерь в качестве анализа.
А так-то можно и не пропускать ничего. Из NATS идёт всё что тикает
тут
https://www.mql5.com/ru/quotes/currencies/forex-matrix
и тут
https://www.profinance.ru/quotes
Про стаканы не подскажу.
- www.mql5.com
хочу спросить у сообщества вот что.
1.
как думаете, что за данные мелькают тут:
https://www.mql5.com/ru/quotes/currencies/forex-matrix
это средняя температура по брокерской больнице?
2.
кто-считал корреляцию Пирсона по USDCNH-USDRMB?
у меня по вышеобозначенному источнику c 2026-07-24 00:00:00 по н.в. получается корреляция 0.9999995658 (что кажется нереалистично если только данные не синтетические)
3.
какие геополитические индексы вы используете / хотели бы иметь под рукой для анализа: например, посматриваете ли иногда VIX (Индекс волатильности S&P 500), индекс пиццы пентагона, кол-во сделок в секунду на Polymarket или что-то в этом роде?
- www.mql5.com
возможно кому-то будет интересно. abs(корреляция) >= 0.9 начиная с 2026-07-24 00:00:00
отрицательная - значит в противофазе.
где небольшой baskets_count скорей всего какая-то хрень из-за небольшого пересечения по времени торгов на рынке (не смотрите).
# |scode1|scode2|corr |baskets_count| ---+------+------+-------------+-------------+ 1 |USDCNH|USDRMB| 0.9999995710| 1650| 2 |EURUSD|USDDKK|-0.9999543610| 1650| 3 |HKDJPY|USDJPY| 0.9999222009| 1626| 4 |AUDDKK|EURAUD|-0.9998639124| 1624| 5 |EURHKD|USDDKK|-0.9998298676| 1650| 6 |EURHKD|EURUSD| 0.9998241269| 1651| 7 |GBPHKD|GBPUSD| 0.9997405177| 1614| 8 |AUDHKD|AUDUSD| 0.9996511160| 1651| 9 |CHFDKK|EURCHF|-0.9995834205| 1588| 10 |EURNZD|NZDDKK|-0.9995239199| 1593| 11 |EURGBP|GBPDKK|-0.9995127052| 1647| 12 |SGDHKD|USDSGD|-0.9992909159| 1627| 13 |DKKSEK|EURSEK| 0.9986675884| 1593| 14 |HKDJPY|TRYJPY| 0.9965799989| 1589| 15 |TRYJPY|USDJPY| 0.9961300870| 1589| 16 |CNHJPY|USDJPY| 0.9958730468| 1612| 17 |EURHKD|EURTRY| 0.9956606449| 1647| 18 |EURTRY|USDDKK|-0.9955731675| 1646| 19 |CNHJPY|HKDJPY| 0.9953925452| 1612| 20 |EURTRY|EURUSD| 0.9952130807| 1647| 21 |CNHJPY|SGDJPY| 0.9943471689| 1612| 22 |SGDJPY|USDJPY| 0.9921813218| 1651| 23 |HKDJPY|SGDJPY| 0.9919057944| 1626| 24 |GBPHKD|GBPTRY| 0.9895201707| 1614| 25 |GBPTRY|GBPUSD| 0.9878066483| 1651| 26 |EURCNH|EURUSD| 0.9874787464| 1614| 27 |EURCNH|USDDKK|-0.9871939526| 1613| 28 |EURCNH|EURHKD| 0.9870467054| 1614| 29 |CNHJPY|TRYJPY| 0.9865126099| 1575| 30 |SGDJPY|TRYJPY| 0.9864844713| 1589| 31 |CADJPY|SGDJPY| 0.9855501699| 1651| 32 |CNHJPY|MXNJPY| 0.9852217286| 1609| 33 |NZDSGD|NZDUSD| 0.9846261389| 1650| 34 |EURHKD|USDCZK|-0.9824911401| 1651| 35 |MXNJPY|SGDJPY| 0.9824392366| 1624| 36 |GBPUSD|USDCHF|-0.9820638952| 1650| 37 |EURUSD|USDCZK|-0.9819851650| 1651| 38 |CADJPY|CNHJPY| 0.9819389896| 1612| 39 |GBPTRY|USDPLN|-0.9819233999| 1651| 40 |GBPHKD|USDCHF|-0.9819034097| 1613| 41 |USDCZK|USDDKK| 0.9813850388| 1650| 42 |USDCZK|USDPLN| 0.9805707652| 1651| 43 |CADJPY|USDJPY| 0.9802030092| 1651| 44 |CADJPY|HKDJPY| 0.9792310523| 1626| 45 |CHFSGD|USDCHF|-0.9787975784| 1626| 46 |USDPLN|USDSEK| 0.9786847721| 1648| 47 |EURHKD|USDPLN|-0.9784531933| 1651| 48 |EURUSD|USDPLN|-0.9783152334| 1651| 49 |CADJPY|GBPJPY| 0.9782684104| 1651| 50 |GBPTRY|NZDUSD| 0.9780296910| 1651| 51 |MXNJPY|USDJPY| 0.9777017850| 1624| 52 |USDDKK|USDPLN| 0.9775671864| 1650| 53 |HKDJPY|MXNJPY| 0.9774829853| 1623| 54 |NZDUSD|SGDHKD| 0.9765946287| 1627| 55 |EURCNH|USDCAD|-0.9764328548| 1614| 56 |GBPUSD|SGDHKD| 0.9764045600| 1627| 57 |EURCNH|EURTRY| 0.9763431426| 1610| 58 |NZDUSD|USDPLN|-0.9761392640| 1651| 59 |GBPHKD|SGDHKD| 0.9761189749| 1614| 60 |EURUSD|GBPTRY| 0.9758569107| 1651| 61 |EURHKD|GBPTRY| 0.9751727040| 1651| 62 |GBPUSD|USDSGD|-0.9748816714| 1650| 63 |GBPTRY|USDDKK|-0.9746716292| 1650| 64 |GBPJPY|SGDJPY| 0.9739523436| 1651| 65 |NZDUSD|USDSEK|-0.9739416561| 1648| 66 |GBPHKD|USDSGD|-0.9738790502| 1614| 67 |NZDUSD|USDSGD|-0.9737849487| 1650| 68 |SGDHKD|USDCHF|-0.9737653795| 1626| 69 |USDCHF|USDSGD| 0.9734423124| 1649| 70 |GBPHKD|NZDUSD| 0.9731802454| 1614| 71 |EURCNH|USDCZK|-0.9727190523| 1614| 72 |GBPUSD|NZDUSD| 0.9724305071| 1651| 73 |GBPHKD|USDPLN|-0.9712766267| 1614| 74 |EURTRY|GBPTRY| 0.9709309591| 1647| 75 |EURTRY|USDPLN|-0.9707817305| 1647| 76 |CHFJPY|GBPJPY| 0.9707061620| 1648| 77 |EURTRY|USDCZK|-0.9704194306| 1647| 78 |EURUSD|USDSEK|-0.9684201376| 1648| 79 |GBPTRY|SGDHKD| 0.9683610082| 1627| 80 |GBPUSD|USDPLN|-0.9681920934| 1651| 81 |USDDKK|USDSEK| 0.9681609448| 1647| 82 |EURHKD|USDSEK|-0.9676981881| 1648| 83 |MXNJPY|TRYJPY| 0.9676153643| 1586| 84 |CADJPY|TRYJPY| 0.9676069494| 1589| 85 |EURTRY|USDSEK|-0.9664993209| 1644| 86 |GBPTRY|USDCHF|-0.9661859213| 1650| 87 |CADJPY|EURJPY| 0.9657031960| 1651| 88 |SGDHKD|USDPLN|-0.9656793704| 1627| 89 |EURSGD|USDCLP|-0.9639873311| 249| 90 |GBPTRY|USDSGD|-0.9639866116| 1650| 91 |GBPTRY|USDCZK|-0.9637528421| 1651| 92 |AUDNZD|AUDSEK| 0.9634016614| 1609| 93 |NZDCAD|NZDUSD| 0.9627745598| 1650| 94 |EURJPY|GBPJPY| 0.9624247317| 1651| 95 |GBPTRY|USDSEK|-0.9618052222| 1648| 96 |NZDUSD|USDCHF|-0.9615267943| 1650| 97 |NZDJPY|PLNJPY| 0.9605751768| 1626| 98 |USDPLN|USDSGD| 0.9600466613| 1650| 99 |EURCAD|USDSEK|-0.9593437674| 1648| 100|EURCAD|USDCNH|-0.9593239243| 1650| 101|EURCAD|USDRMB|-0.9593147406| 1650| 102|EURCAD|USDZAR|-0.9592323263| 1647| 103|CADJPY|MXNJPY| 0.9590659530| 1624| 104|EURCNH|GBPTRY| 0.9587039756| 1614| 105|AUDNZD|AUDPLN| 0.9574995934| 1626| 106|CNHJPY|GBPJPY| 0.9569530539| 1612| 107|AUDPLN|AUDSEK| 0.9568147897| 1609| 108|TRYJPY|USDCOP| 0.9567705422| 306| 109|USDCZK|USDSEK| 0.9566420457| 1648| 110|SGDHKD|USDSEK|-0.9565553468| 1624| 111|EURUSD|USDCAD|-0.9563881489| 1651| 112|EURHKD|USDCAD|-0.9562163277| 1651| 113|NZDSGD|USDSEK|-0.9559034857| 1647| 114|SGDHKD|TRYJPY|-0.9556901829| 1589| 115|AUDCAD|AUDSGD| 0.9555509792| 1627| 116|USDCHF|USDPLN| 0.9553681322| 1650| 117|CADCHF|USDCHF| 0.9553154644| 1650| 118|USDCAD|USDDKK| 0.9553106721| 1650| 119|AUDDKK|GBPAUD|-0.9552935092| 1624| 120|USDCOP|USDJPY| 0.9550350351| 313| 121|TRYJPY|USDSGD| 0.9548401334| 1589| 122|EURAUD|GBPAUD| 0.9547302410| 1651| 123|HKDJPY|USDCOP| 0.9547295979| 313| 124|EURUSD|GBPHKD| 0.9544868752| 1614| 125|GBPTRY|NZDSGD| 0.9541005579| 1650| 126|EURUSD|NZDUSD| 0.9539559095| 1651| 127|NZDSGD|USDPLN|-0.9537474104| 1650| 128|USDSEK|USDZAR| 0.9534244775| 1647| 129|EURCAD|EURTRY| 0.9533335500| 1647| 130|HKDJPY|USDSGD| 0.9532372588| 1626| 131|EURHKD|GBPHKD| 0.9530881525| 1614| 132|NZDUSD|USDDKK|-0.9529663475| 1650| 133|EURHKD|NZDUSD| 0.9529195208| 1651| 134|HKDJPY|SGDHKD|-0.9527744494| 1626| 135|GBPHKD|USDDKK|-0.9527222312| 1613| 136|USDJPY|USDSGD| 0.9523942115| 1650| 137|EURCNH|USDPLN|-0.9522064877| 1614| 138|EURJPY|PLNJPY| 0.9520756988| 1626| 139|USDSEK|USDSGD| 0.9520546194| 1647| 140|SGDHKD|USDJPY|-0.9518811496| 1627| 141|CNHJPY|USDCOP| 0.9510973623| 313| 142|NZDUSD|TRYJPY|-0.9510792762| 1589| 143|GBPHKD|USDCZK|-0.9503825645| 1614| ... abs(corr) between 0.9 and 0.95 (Длина сообщения не должна превышать 64000 символов)
сравните визуально графики на ТФ М15 и ниже, чтобы увидеть сходство или зеркальность.
- Бесплатные приложения для трейдинга
- 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 на сейчас можно запросом
Продолжение следует...