惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
AI — Ассистент для торговли с крипто-биржами
Карпенко Сергей · 2026-06-22 · via Все публикации подряд на Хабре

Крипто-ассистент — самописный аналитический сервис под биржи (spot + USDT-perpetual, то есть и обычная торговля, и бессрочные фьючерсы).

Мой большой  проект, над которым очень долго работал, — 7 месяцев трудов. Статья будет очень большой, и это тоже моя первая статья на Хабре. Итак, вернёмся к сути.

Неторговый бот, который сам анализирует и не начинает мне ХХХХ — доход клепать, совсем нет, он мой советник. Он собирает рыночные данные, считывает индикаторы, прогоняет их через ML-модель (т.е. через обученный алгоритм, который пытается предсказать вероятность движения цены), проверяет кучей фильтров и присылает в Telegram карточку сигнала вида:

«BTCUSDT 15m LONG @ 64321, SL=…, TP=…, score=…»

Таким образом, приходит информация в Telegram:

А вот общая блок-схема, специально упрощённая, для трейдеров и непрофильных читателей. Чтобы стало понятно, о чём вообще речь, прежде чем начну погружать вас в технические детали:

Фичи — это индикаторы, на которых стоят столбы моего творения. Долго провозился над тем, чтобы для себя сформулировать, за что именно отвечает каждый из этих показателей. Вот итоговый набор:

  • EMA 9/20/50/200, SMA 20 - скользящие средние с разным периодом. Показывают, куда смотрит цена «в среднем» на разных горизонтах.

  • MACD (12, 26, 9), гистограмма — классический индикатор схождения/расхождения скользящих, помогает ловить смену импульса.

  • Bollinger (20, 2), ширина канала-границы «нормального» движения цены и насколько они сейчас сжаты.

  • ATR + ATR% к цене— средний истинный диапазон, по сути «насколько свечи большие». Дальше я использую его как универсальную меру волатильности.

  • RSI, ADX, OBV -индекс относительной силы, сила тренда и накопленный объём с учётом направления.

  • z-score по close и volume-отклонение цены и объёма от своего скользящего среднего в «сигмах». Помогает понять, насколько ситуация «нестандартная».

  • trend_score ∈ [-1; 1] мой сводный показатель тренда, собирается из структуры EMA и наклона EMA50. Простой способ ответить на вопрос «насколько уверенно идёт движение». Я использовал метод «тройного барьера» из книги Advances in Financial Machine Learning (2018)* Автор: Marcos López de Prado, если кому-то нужна эта книжка, я поделюсь. Идея была такая: Каждой свече мы задаём вопрос: «слышь, мне открывать сделку сейчас или подождать, пока цена не рухнет, или войти в сделку и она достигнет тейк-профита». А здесь моделька-мудмазелька сразу же ответ даёт: становится меткой «y», на которой потом учится модель классификации (предсказывать направление будущего движения). Представляю вашему вниманию часть кода.

def trend_score_from_emas(df: pl.DataFrame) -> pl.DataFrame: # 1) Флаговая структура EMA: +1 / -1 / 0 df = df.with_columns([ pl.when((pl.col(“ema_20”) > pl.col(“ema_50”)) & (pl.col(“ema_50”) > pl.col(“ema_200”))).then(1.0) .when((pl.col(“ema_20”) < pl.col(“ema_50”)) & (pl.col(“ema_50”) < pl.col(“ema_200”))).then(-1.0) .otherwise(0.0).alias(“ema_stack”) ])

# 2) Наклон EMA50 за 3 бара. 
# Делю на саму EMA50, чтобы было относительно цены, а не в абсолюте.
# Защита от деления на ноль обязательна, вспоминаю как спот возвращал мне нули, я вообще не понимаю что происходит
slope_raw = (pl.col("ema_50") - pl.col("ema_50").shift(3))
denom = pl.when(pl.col("ema_50").shift(3).abs() < 1e-12)\
          .then(1e-12)\
          .otherwise(pl.col("ema_50").shift(3))
df = df.with_columns([(slope_raw / denom).alias("ema50_slope3")])

# 3) Зажимаем наклон в окно ±2% и нормируем к [-1; +1]
# 2% за 3 четверти часа если резкое движение
df = df.with_columns([
    (_clip(pl.col("ema50_slope3"), -0.02, 0.02) / 0.02).alias("ema50_slope3_n")
])

# 4) Сводный балл: 70% структура EMA + 30% наклон. 
# Зажимаем в [-1; +1] на всякий случай могут вылезти кривые значения
df = df.with_columns([
    _clip(0.7 * pl.col("ema_stack") + 0.3 * pl.col("ema50_slope3_n"), -1.0, 1.0)
    .alias("trend_score")
])
return df

p.s простите, пожалуйста, модераторы, но у меня не получилось нормально вставить часть кода пришлось картинку прикрепить…

vol_regime: low/mid/high-режим волатильности по ATR. Типа, что с рынком? «Он спит?Умер? Бодр и свеж?»

squeeze play- флаг сжатия Боллинджера. Узкие BB часто предшествуют резкому движению.

ange_index- попытка отличить тренд от флэт (бокового движения).

Дополнительно для младшего таймфрейма 15m подмешиваю значение trend_score_4h (т.е тренд 4-часового таймфрейма). Делаю это через numpy.searchsorted- это просто быстрее и предсказуемее, чем join_asof из Polars. Так, модель видит контекст старшего ТФ, не теряя скорости.

Volume Profile - это профиль объёма вместо обычной свечной гистограммы строится распределение торгового объёма по уровням цены. Считаю его по 90-дневному окну, нахожу уровень с максимальным объёмом и диапазон, где прошло 70% торгов. Границы этого диапазона — вверх и вниз. Затем для каждой свечи записываю дистанции до этих уровней в единицах ATR -это потом используется и как фича, и как фильтр против входа «в стену».

Цель-мечта!

Избавиться от ручного мониторинга десятка пар на нескольких таймфреймах. Хотел получать свежую информацию из биржи и с возможностью успешно торговать — ручной труд не приносил мне нужного интереса, была усталость. В результате пришла в голову идея, почему бы не получать истории с биржи и не отправлять обработанные сигналы в Telegram? Тем самым видеть, в какие позиции сто́ит заходить, и играть на рынке более осознанно.

Что в итоге умеет система:

24/7 слушает рынок по WebSocket (это протокол постоянного соединения, через который биржа сама шлёт свежие свечи) и догружает историю по REST (одиночные HTTP-запросы для исторических данных).

Сама считывает фичи (EMA, RSI, MACD, ATR, BB, ADX, OBV, trend_score, режим волатильности). Выше немного описал, что это.

Оценивает вероятность движения вверх/вниз на горизонте H баров с помощью обученной модели - LightGBM (это градиентный бустинг, штука посерьёзнее) или логистическая регрессия + температурная калибровка (попроще, но быстрее).

Не лезет в каждое шевеление: есть фильтры (TrendAlign, SR-guard, Flip-guard, Volume Profile, кулдаун, Freeze на новостях/макро). Подробнее про каждый будет ниже.

Шлёт в Telegram только те карточки, где есть уровни SL/TP и пройдены все проверки. Считает ex-post по истории сигналов-winrate (доля прибыльных), RR (risk/reward, отношение прибыли к риску), ожидание. Чтобы я мог посмотреть, не врёт ли мой движок самому себе. А такое реально бывает, когда движок «в моменте» выглядит хорошо, а на истории сливает.

Отдельно есть Advisor- это наблюдатель/страж, который раз в N минут сканирует 4h/1d и ищет среднесрочные свинги (тренд 1d + swing 4h + удалённость от SR + брейкаут). Про него тоже расскажу подробно. И всё это из одной админки на Streamlit мне было лень каждый раз лазить в YAML-конфиг и вспоминать CLI-команды.

Стек Python 3.11, asyncio (асинхронный код, чтобы обрабатывать события с биржи без блокировок). Polars + PyArrow, для всего, что касается датафреймов и parquet-файлов.

aiohttp + websockets + tenacity, для общения с биржей (HTTP-запросы, WebSocket-подписки и автоматические повторы при сбоях).

scikit-learn + LightGBM-модели машинного обучения.

Pydantic v2 + YAML конфигурация. YAML - это просто текстовый формат с настройками, Pydantic проверяет, что все значения правильного типа.

Streamlit- для веб-админки (без неё пришлось бы всё делать через консоль, что для постоянного использования больно).

Prometheus client метрики, чтобы видеть состояние сервиса.

Хранилища как такового отсутствует, я специально не стал разворачивать базу данных, всё лежит в файлах формата parquet. Мне хватает.

Ядро системы

Ниже будет блок-схема. Она разделена на три части. Большая схема целиком не вмещалась по размерам (пробовал сжимать, не получилось без потери читаемости). Поэтому идём по этапам.

Часть 1. Сбор и подготовка данных

Описание по схеме:

Источник данных биржи. Свечи 15-минутного таймфрейма приходят двумя путями: в реальном времени по WebSocket и в виде истории через REST API. Слой ингеста (ws.py+ history.py) принимает их, проверяет на подтверждённость. Всё ок записываем, не ок не трогаем.

Хранение.Все свечи складываются в «partitioned parquet» это просто способ разложить файлы по папкам так, чтобы поиск нужного куска данных был мгновенным. Разбивка по символу, таймфрейму и категории (spot/linear). Даёт быстрый доступ и компактный размер на диске. Иногда думаешь, надо всё-таки базу.

Ресемплер.На основе 15-минуток автоматически собираются старшие таймфреймы: 30m, 1h, 4h и 1d. Никаких отдельных закачек — всё считается из базового ТФ. Это гарантирует, что данные между таймфреймами согласованы. Другие варианты (закачивать каждый ТФ отдельно) я перестал даже рассматривать после первого расхождения по нескольким барам.

Контекст деривативов. Параллельно тянутся данные с биржи v5: ставка финансирования (funding rate комиссия, которую лонги платят шортам или, наоборот), открытый интерес (OI - сколько всего открытых позиций) и ликвидации (когда чьи-то позы принудительно закрывают). Всё это складывается в отдельный кэш cache/context.

Volume Profile. По 90-дневному окну считается профиль объёма — уровни. Вверх, вниз и точка наибольшего объёма. Эти уровни потом используются и в фичах, и в фильтрах движка.

Расчёт фич.На каждом таймфрейме считаются индикаторы (EMA, MACD, BB, ATR, RSI, ADX, OBV), режимы волатильности, флаги сжатия Боллинджера, trend_score. Сюда же подмешивается контекст деривативов и дистанции до VP-уровней. Результат — единый датасет фич в cache/features.

Вывод этого этапа: для каждой свечи, каждого символа есть строка с 30+ признаками, готовая к ML.

Кстати, ингест данных, для тех, кто не сталкивался с термином, — это просто процесс сбора, обработки и импорта данных для анализа и хранения. Можно думать о нём как о «приёмка товара на склад» проверили, сверили, что не битый, разложили по полкам, теперь можно работать.

Часть 2. Обучение модели и движок принятия решений

Левая часть — схемы-обучение модели. Схема была большая, я постарался всё разбить, чтобы описать в статье по каждому блоку отдельно. Очень надеюсь, что получилось.

Сборка ML-таблицы. Из кэша фич собирается обучающий датасет. Каждой свече присваивается метка по методу triple-barrier, для каждого бара выставляются верхний тей профит и нижний стоп лос. Барьеры в ATR, и через H баров в будущем смотрим, какой барьер был пробит первым. Это даёт честную бинарную классификацию: «выгоднее был лонг» /«выгоднее был шорт». Если ни тот, ни другой не пробит за H баров — этот бар выбрасываем как «нежелательный».

Обучение. В зависимости от настроек, которые указываются в интерфейсе админки: LightGBM (тяжёлая модель с ранней остановкой по метрике AUC, точнее, но медленнее) или LogisticRegression + StandardScaler (лёгкая, быстрая, без зависимостей; подойдёт, если LightGBM почему-то не ставится).

Калибровка температурой T. После обучения подбирается коэффициент T, усиливающий «стойкость» модели (в мире ML называют логитами), чтобы предсказанные вероятности соответствовали реальной частоте событий. Замеряются специальной мерой так, насколько модель адекватна в своих оценках до и после, для этого. Без этого шага модель часто переуверена и начинает «галлюцинировать» — выдавать 90% уверенности там, где по факту 60%. У меня как-то упало с 0.12 до 0.04, и это сразу стало заметно по качеству сигналов.

Модель и метаданные (список фич, барьеры, метрики, T) сохраняются в папку models, потом можно запустить для обучения модельки.

Правая часть схемы — движок в реальном времени.

На каждом закрытом баре движок берёт последние фичи символа из кэша, прогоняет через сохранённую модель, получает рост и падение.

Контекст подмешивается тут же: данные деривативов и состояние Freeze (новости/макро) влияют на решение. Для новостей я использую API CryptoPanic - он отдаёт ленту по криптовалютам, и если попадает что-то критичное (взлом, делистинг, расследование), движок ставит «заморозку» на N минут и в это время никаких новых входов не делает.

Динамические пороги. Базовые лонг/шорты/gap умножаются на множители режима-волатильности, типа рынка (trend/flat) и торговой сессии (ASIA/EU/US). Это позволяет одной и той же модели адаптироваться к разным условиям. На спокойном азиатском рынке пороги мягче, но бывают не всегда, а вот открытие Нью-Йорка — ускоренно всё просто.

Цепочка фильтров. Она выглядит примерно так, Freeze — Cooldown — TrendAlign — SR-guard — VP-filter — Flip-guard — проверка score. Если хоть один фильтр сказал «нет» — сигнал превращается в ожидание с указанием причины. Жди тоже логируется, и в админке потом смотрю, какой фильтр чаще всего режет сигналы. Иногда вижу, что половину входов выбрасывает Flip-guard - и думаю, может, я его слишком жёстко настроил.

**Вывод **этого этапа:если все проверки пройдены — формируется карточка лонг/шорт с уровнями СЛ/ТП, рассчитанными по ATR, и отправляется в Telegram. Все события (и сигналы, и отказы) пишутся в журнал data/signals сразу в двух форматах:— parquet (для быстрой аналитики) и CSV (для быстрого открытия в Excel глазами).

Часть 3. Анализ результатов и Advisor

Эта схема про то, как система проверяет сама себя и работает на средних горизонтах. Описание, что имею в виду. Левая ветка — ex-post симулятор.

Ex-post мы берём уже выданные сигналы и симулируем, как было бы «если б стали миллионерами» или, наоборот, «хорошо, что не зашёл»

Источник. Берётся журнал сигналов из data/signals и исторические 15-минутные свечи из кеша.

Симуляция исполнения. Для каждого сигнала лонг/шорт симулирует реальный вход — по открытию следующего бара после сигнала (чтобы не подсматривать в будущее) — это Ex-post.

Метрики на выходе:winrate (% прибыльных сделок), средний RR (риск/ревард), ожидаемость в RR, медианное время удержания, разбивка по символам и действиям. Результаты сохраняются в data.

Зачем это вообще нужно? Без ex-post легко повестись на разводняк, что движок работает хорошо. С ex-post видишь правду, где модель ошибается, галлюцинация есть или её нет? Какие фильтры режут прибыль, а какие, наоборот, спасают от потерь. Это, наверное, самая отрезвляющая часть всего проекта. Это о чём выше написал.

Правая ветка — это Advisor(страж). Я о нём вышел, писал.

Параллельный сервис. Advisor работает независимо от основного движка(добавил службу, чтобы они не мешали друг друга) и сканирует фичи на старших таймфреймах (4h и 1d) с заданным интервалом (по умолчанию раз в 5 минут).

Логика поиска свингов. Необходимо найти сочетание тренда на днёвки, если достаточный свинг на 4ч, тогда необходимо создать свинг-карточку.

Вывод. Карточки уходят в Telegram (отдельным потоком от основных сигналов — я разделил, чтобы случайно не спутать) и в журнал data/advisor. Это нужно, чтобы основной движок ловил локальные входы на 15m, а Advisor-крупные свинги на днёвке. У них разные ритмы и разные цели, поэтому они разнесены по разным процессам.

Streamlit-админка связывает всё воедино. Из неё можно запустить и остановить движок и Advisor, посмотреть журналы, прогнать ex-post за нужный период, переобучить модель и применить новые настройки.

Ядро код как это выглядит Чтобы модель училась, ей нужны правильные ответы на вопросы: «Вот свеча/бар. Что надо тебе делать — Лонг или шорт?» Попробую ответить, что делает этот алгоритм. Берём каждую свечу из истории (здесь у нас большая будет история, 180 дней) и смотрим: — Цена растёт? Она на нужной величине — значит, надо лонговать — Цена упала на нужную величину — значит, надо было продавать Ни то, ни другое за отведённое время — ожидание, — ситуация неясна. Ждём точки входа. Вот и всё. Прохожусь так по всей истории, получаю миллион примеров с готовыми ответами. Скармливаю всю историю модели. Модель учится и потом на живых данных пытается угадать ответ сама. Угадала — молодец, получает развитие, если несколько раз промахнулась, переучивается. Почему не сделать проще — наблюдать выросла цена или нет? Потому что это обман. Цена могла сначала упасть и выбить стоп-лосс, а потом скакануть вверх, как они любят. По простой проверке получается «угадал». По факту — уже давно вышел с убытком. Способ модели на честность: смотрю, что происходило по истории, а не только в конечной точке.

def triple_barrier_label(df: pl.DataFrame, horizon: int, up_k: float, dn_k: float) -> pl.DataFrame: df = df.sort(["symbol", "ts"])

if "atr" not in df.columns:
    print("[triple_barrier] atr нет в df, барьеры будут нулевые")

parts = []
for g in _partition_by(df, "symbol"):
    n = len(g)
    if n < 2:
        continue

    close = g["close"].to_numpy()
    high = g["high"].to_numpy()
    low = g["low"].to_numpy()

    if "atr" in g.columns:
        atr = g["atr"].fill_null(strategy="forward").fill_null(0.0).to_numpy()
    else:
        atr = np.zeros(n)

    y = np.zeros(n, dtype=np.int8)
    tp_px = np.full(n, np.nan)
    sl_px = np.full(n, np.nan)

    for i in range(n - 1):
        a = atr[i]
        c = close[i]
        if a <= 0 or np.isnan(c):
            continue

        up = c + up_k * a
        dn = c - dn_k * a
        tp_px[i] = up
        sl_px[i] = dn

        # сколько баров реально доступно вперёд
        end = i + 1 + horizon
        if end > n:
            end = n

        up_j = -1
        dn_j = -1
        for j in range(i + 1, end):
            if up_j < 0 and high[j] >= up:
                up_j = j
            if dn_j < 0 and low[j] <= dn:
                dn_j = j
            if up_j >= 0 and dn_j >= 0:
                break

        # метод FIFO
        if up_j < 0 and dn_j < 0:
            continue  # y[i] уже 0
        if dn_j < 0:
            y[i] = 1
        elif up_j < 0:
            y[i] = -1
        else:
            y[i] = 1 if up_j < dn_j else -1

    parts.append(
        g.select(["ts", "symbol", "tf"]).with_columns(
            pl.Series("y", y),
            pl.Series("tp_price", tp_px),
            pl.Series("sl_price", sl_px),
        )
    )

if not parts:
    # тут пустой вход
    return df

labels = pl.concat(parts)
return df.join(labels, on=["symbol", "ts", "tf"], how="left")

Запомнил на всю жизнь: нельзя смешивать разные символы в один проход, если вдруг что-то смешалось, то метрики получатся ложные. Калибровка температурой уникальнейшая штука. Без неё вообще никак.

def sigmoid(x: np.ndarray) -> np.ndarray: x = np.asarray(x, dtype=np.float64) out = np.empty_like(x) pos = x >= 0 neg = ~pos out[pos] = 1.0 / (1.0 + np.exp(-x[pos])) ex = np.exp(x[neg]) out[neg] = ex / (1.0 + ex) return out def temperature_scale(logits: np.ndarray, T: float) -> np.ndarray: # делим логиты на T — при T>1 вероятности «сжимаются» к 0.5 # при T<1 наоборот, становятся более крайними return sigmoid(logits / T) def grid_search_temperature(logits_valid, y_valid, Tmin=0.6, Tmax=2.5, step=0.05): # Тупой перебор по сетке. На валидации, чтобы не подсматривать в трейн. best_T, best_ll = 1.0, float(“inf”) for T in np.arange(Tmin, Tmax + 1e-9, step): p = temperature_scale(logits_valid, T) ll = log_loss(y_valid, p, labels=[0, 1]) if ll < best_ll: best_ll, best_T = ll, T return float(best_T)

Интерфейс программы простой

Надеюсь, вы дочитали до конца! Цели на будущее: Модернизировать проект буду, на очереди биржи Binance и BINGX, кстати, вот что заметил, по некоторым монетам, цена у бирж отличается. Поэтому нужно доработать механизм, чтобы отображались разные цены на сделки.