Блог им. RomellaAkumov

Как я написал скрипт для 24-часового прогноза рынка: корреляции, волатильность и вероятностная модель

Вступление: идея, цель, гипотеза

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

Гипотеза проекта:

Если агрегировать данные по разным классам активов (крипто, акции, золото), измерить их волатильность, тренд и взаимную корреляцию, можно получить осмысленную вероятностную оценку того, каким будет рынок в ближайшие 24 часа: рост, падение или консолидация.

Цель скрипта — не предсказать точную цену, а оценить состояние рынка в целом, получить вероятностный прогноз и использовать его как основу для торговых стратегий и автоматизированной торговли.

Общая архитектура проекта

На логическом уровне скрипт состоит из пяти ключевых блоков:

Данные → Индикаторы → Агрегация → Корреляции → Вероятностный прогноз

Код выложен на github.

Источники данных

Используются разные рынки:

  • криптовалюты (Binance, через ccxt),
  • фондовые индексы (S&P 500, NASDAQ, Dow Jones),
  • золото (через yfinance).

Все данные приводятся к единому таймфрейму (1час), единой временной зоне UTC, ну и само собой равномерно распределяю исходя из времени свечи, чтобы не было сдвигов данных на разных активов.

Это критично: без этого любые корреляции и агрегации будут искажены.

Конфигурация и управляемость

Все ключевые параметры вынесены в один конфиг:

<code>CONFIG = {
    "timeframe": "1h",
    "lookback_days": 30,
    "vol_low": 0.01,
    "vol_high": 0.05,
    "trend_thr": 0.02,
    "corr_weight": 0.30,
}</code>

Это позволить нам гибко использовать прогнозную модель в будущем, а также тестировать гипотезы и подгонять параметры для определенных торговых стратегий.

Получение данных

Получаем данные по криптовалютам

<code>def _fetch_crypto(self, symbol: str, days: int) -> pd.DataFrame:
    since = int((datetime.now(timezone.utc) - timedelta(days=days)).timestamp() * 1000)
    raw = self.exchange.fetch_ohlcv(symbol, "1h", since=since)

    df = pd.DataFrame(raw, columns=[
        'ts', 'open', 'high', 'low', 'close', 'volume'
    ])
    df['ts'] = pd.to_datetime(df['ts'], unit='ms', utc=True)
    df.set_index('ts', inplace=True)

    df = df.resample('1H').ffill()
    return df</code>

Что здесь происходит:

  • Загружаем OHLCV-данные с Binance через ccxt.
  • Переводим временные метки в UTC.
  • Приводим данные к равномерной часовой сетке.

Зачем это нужно: Корреляции, волатильность и агрегаты работают корректно только при одинаковом таймфрейме.

На выходе:DataFrame с чистыми часовыми свечами.

Получаем данные фондового рынка и золота (yFinance)

<code>def _fetch_yf(self, symbol: str, days: int) -> pd.DataFrame:
    end = datetime.now(timezone.utc)
    start = end - timedelta(days=days)

    df = yf.download(symbol, start=start, end=end, interval="1h")
    df = df[['Open', 'High', 'Low', 'Close', 'Volume']]
    df.columns = ['open', 'high', 'low', 'close', 'volume']

    df.index = pd.to_datetime(df.index, utc=True)
    df = df.resample('1H').ffill()
    return df</code>

Что происходит:

  • Загружаем индексы и золото.
  • Приводим названия колонок к единому формату.
  • Ресемплируем данные.

Почему не Binance: Индексы и золото удобнее и стабильнее получать через yfinance.

Расчёт индикаторов для одного актива

<code>def _asset_indicators(self, df: pd.DataFrame) -> dict:
    close = df['close']

    # Momentum (24h ROC)
    roc = (close.iloc[-1] - close.iloc[-25]) / close.iloc[-25]

    # ATR %
    atr = self._atr(df)
    atr_pct = atr.iloc[-1] / close.iloc[-1]

    # EMA trend
    ema12 = close.ewm(span=12).mean()
    ema26 = close.ewm(span=26).mean()
    trend = (ema12.iloc[-1] - ema26.iloc[-1]) / ema26.iloc[-1]

    # Volume ratio
    vol_ratio = df['volume'].iloc[-1] / df['volume'].rolling(24).mean().iloc[-1]

    return {
        "roc_24h": roc,
        "atr_pct": atr_pct,
        "trend_strength": trend,
        "volume_ratio": vol_ratio
    }
</code>

1. Momentum (ROC 24 часа)

roc = (price_now — price_24h_ago) / price_24h_ago

Интерпретация:

> 0 — рынок растёт

< 0 — рынок падает

Используется как базовый индикатор направления.

2. Волатильность (ATR в процентах)

atr_pct = ATR / price

Почему в процентах: Абсолютный ATR не сопоставим между BTC и, например, ADA. Процентная форма решает эту проблему.

3. Тренд через EMA

trend = (EMA12 — EMA26) / EMA26

Простая и устойчивая оценка:

  • положительное значение — восходящий тренд;
  • отрицательное — нисходящий.

Также мы можем использовать модели через прочие скользящие средние. Это может быть использование tema, линий macd и прочих.

4. Объём

volume_ratio = current_volume / avg_24h_volume

Объём позволяет выявить всплески интереса, подтвердить тренд и выявить фразу распределение накопления.

Агрегация индикаторов по рынку

<code>def aggregate(self) -> dict:
    rows = []

    for df in self.data.values():
        ind = self._asset_indicators(df)
        rows.append(ind)

    agg = pd.DataFrame(rows).mean().to_dict()
    return agg</code>

Что делаем:

  • рассчитываем индикаторы для каждого актива;
  • усредняем значения.

Почему усреднение работает: Мы оцениваем не конкретный актив, а состояние рынка в целом.

Корреляционный анализ

<code>def correlations(self) -> dict:
    btc = self.data["BTC/USDT"]['close'].tail(24)

    result = {}
    for symbol, df in self.data.items():
        if symbol == "BTC/USDT":
            continue

        series = df['close'].tail(24)
        corr, _ = pearsonr(btc, series)
        result[f"{symbol}_vs_BTC"] = corr

    result["avg_corr"] = np.mean(list(result.values()))
    return result</code>

Здесь нам необходимо получить данные о корреляциях.

Что здесь происходит:

  • берём BTC как якорный актив;
  • считаем корреляции с остальными рынками;
  • вычисляем среднюю корреляцию.

Зачем это нужно:

  • высокая корреляция → рынок движется синхронно;
  • низкая → повышенная неопределённость и флет.

Вероятностная модель прогноза

Ключевая особенность проекта — отказ от бинарных сигналов вида buy / sell. Вместо этого используется вероятностная модель, оценивающая три сценария поведения рынка на ближайшие 24 часа:

  • рост (upward);
  • падение (downward);
  • консолидация (consolidation).

Такой подход лучше отражает реальность рынка: в каждый момент времени существует несколько возможных сценариев, а не один “правильный”.

Базовая инициализация вероятностей

up = down = cons = 1 / 3

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

Это принципиально важно — модель не имеет смещения и не “верит” в рост или падение заранее.

Учет импульса (Momentum / ROC)

<code>roc = ind.get('roc_24h', 0.0)

if roc > CONFIG["trend_thr"]:
    up += 0.20
    down -= 0.10
    cons -= 0.10
elif roc < -CONFIG["trend_thr"]:
    down += 0.20
    up -= 0.10
    cons -= 0.10</code>

Экономический смысл:

  • сильное движение за последние 24 часа редко полностью исчезает на следующий день;
  • импульс увеличивает вероятность продолжения движения, но не гарантирует его.

Что делает модель:

  • усиливает сценарий по направлению импульса;
  • снижает альтернативные сценарии, но не обнуляет их.

Учет волатильности (ATR %)

<code>vol = ind.get('atr_pct', 0.0)

if vol < CONFIG["vol_low"]:
    cons += 0.20
    up -= 0.10
    down -= 0.10
elif vol > CONFIG["vol_high"]:
    if ind.get('trend_strength', 0.0) > 0:
        up += 0.15
    else:
        down += 0.15
    cons -= 0.15</code>

Интерпретация:

  • низкая волатильность → рынок не готов к сильному движению;
  • высокая волатильность → рынок уже “раскачан” и движение вероятнее.

При этом направление усиливается только при наличии тренда, что снижает шум.

Учет объёма

<code>vratio = ind.get('volume_ratio', 1.0)

if vratio > 1.5:
    if ind.get('trend_strength', 0.0) > 0:
        up += 0.10
    else:
        down += 0.10
    cons -= 0.10</code>

Почему объём важен:

  • объём — это подтверждение участия капитала;
  • движение без объёма статистически менее устойчиво.

Модель использует объём не как триггер, а как усилитель уже существующего направления.

Учет межрыночных корреляций

<code>avg_c = corr.get('avg_corr', 0.0)
c_adj = CONFIG["corr_weight"] * abs(avg_c)

if abs(avg_c) < 0.5:
    cons += c_adj
    up -= c_adj / 2
    down -= c_adj / 2
else:
    gold_roc = ind.get('gold_roc', 0.0)
    if gold_roc > 0 and avg_c > 0:
        up += c_adj / 2
    elif gold_roc < 0 and avg_c > 0:
        down += c_adj / 2</code>

Логика:

  • низкая корреляция между рынками → высокая неопределённость;
  • высокая корреляция → рынок следует глобальному макро-направлению.

Золото используется как макро-фильтр:

  • рост золота при высокой корреляции часто указывает на risk-off;
  • падение — на risk-on.

Нормализация вероятностей

<code>total = up + down + cons

up /= total
down /= total
cons /= total


</code>

После всех корректировок вероятности:

  • могут выходить за диапазон;
  • могут не суммироваться в 1.

Нормализация решает эту проблему и возвращает корректное распределение.

Математика модели: приоритеты и характеристики движения рынка

Приоритетность факторов

Факторы в модели имеют иерархию, а не равные веса:

  1. Импульс (Momentum) — определяет базовое направление.
  2. Волатильность — определяет возможность движения.
  3. Тренд — подтверждает устойчивость.
  4. Объём — подтверждает участие капитала.
  5. Корреляции — корректируют сценарий на уровне всего рынка.

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

Характеристики движения рынка

Модель различает три принципиально разных состояния рынка:

1. Трендовое движение

  • положительный ROC;
  • высокая волатильность;
  • подтверждённый EMA-тренд;
  • объём выше среднего.

2. Импульсное движение

  • высокий ROC;
  • скачок волатильности;
  • объём растёт, но тренд ещё нестабилен.

3. Консолидация

  • низкий ROC;
  • низкая ATR;
  • слабые корреляции;
  • отсутствие объёма.

Каждому состоянию соответствует разное распределение вероятностей, а не одинаковый сигнал.

Почему вероятности, а не сигналы

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

Модель может указать нам на состояние рынка в текущей момент — что при наличии определенного нарратива может помочь нам принять грамотное решение.

Выход в консоль

<code>Market forecast: {
  "timestamp_utc": "2025-12-22T09:30:23.229039+00:00",
  "probabilities": {
    "upward": 0.1333,
    "downward": 0.4333,
    "consolidation": 0.4333
  },
  "indicators": {
    "roc_24h": -0.02796597685998984,
    "atr_pct": 0.006690744224154546,
    "trend_strength": -0.005271170297204962,
    "volume_ratio": 0.394397730504669,
    "crypto_cnt": 10,
    "stock_cnt": 0,
    "gold_roc": 0.0
  },
  "correlations": {
    "ETH/USDT_vs_BTC": 0.9975288228710706,
    "SOL/USDT_vs_BTC": 0.9811768939562674,
    "ADA/USDT_vs_BTC": 0.9907696697577224,
    "DOT/USDT_vs_BTC": 0.823343645765213,
    "LINK/USDT_vs_BTC": 0.9543745729318979,
    "LTC/USDT_vs_BTC": 0.9508485363364672,
    "TRX/USDT_vs_BTC": 0.9222384210578161,
    "AVAX/USDT_vs_BTC": 0.9603413942455601,
    "DOGE/USDT_vs_BTC": 0.9765121574347357,
    "avg_corr": 0.9507926793729722
  }
}</code>

Тут скрипт нам выводит основные коэффициенты корреляций, индикаторы и самое главное сверху — рыночные вероятности. В этом примере вероятность роста 13%, падения 43% и консолидации 43%. Исходя из этих данных — сегодняшний день неплохой для открытия шортовых позиций, что сочетается и с моим ict анализом.

Вывод: возможности модели и практическое применение

В рамках проекта была реализована вероятностная модель оценки рыночного состояния, которая опирается не на один индикатор или источник данных, а на совокупность факторов: импульс, волатильность, тренд, объём и межрыночные корреляции.

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

Что даёт такая модель на практике

  1. Фильтрация торговых стратегий Модель может использоваться как верхнеуровневый фильтр: отключать трендовые стратегии в фазе консолидации, снижать риск при неопределённости
  2. Адаптивное управление риском Вероятностный выход позволяет управлять размером позиции: регулировать плечо и маржу, а также изменять тейки и стопы под состояние рынка.
  3. Контекст для алгоритмической торговли Модель хорошо ложится в архитектуру торговых ботов как внешний аналитический сервис.
  4. Кросс-рыночный анализ За счёт использования индексов и золота модель позволяет учитывать макроэкономические сдвиги, корреляции рынков и включать «режим risk on/risk off».Это особенно важно для крипторынка, который всё чаще реагирует на внешние макрофакторы.

Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.

455 | ★3

Читайте на SMART-LAB:
Фото
Блогерам рассчитали пенсию
По данным опроса RENI, чуть больше половины россиян полностью полагаются на госпенсию. Свыше трети респондентов ответили, что пока только...
🔔 Приглашаем на вебинар по результатам Займера в I квартале
В следующую пятницу, 15 мая, Займер представит финансовые результаты I квартала 2026 года по МСФО. Генеральный директор Роман Макаров и...
Фото
⚡ Получайте кэшбэк за сделки
Мы запустили акцию для тех, кто давно не пользовался нашим торговым терминалом — или только хочет попробовать.  Можно получать...
Фото
Россети Московский регион. Новая инвестпрограмма увеличивает прогноз по капитальным расходам!
Сегодня Минэнерго РФ на сайте опубликовал новую инвестиционную программы (ИПР) до 2030г. (публикуют здесь ) и что же там интересного:

теги блога Roman crypto_maniac

....все тэги



UPDONW
Новый дизайн