preview
Майнинг данных CFTC на Python и ИИ модель на их основе

Майнинг данных CFTC на Python и ИИ модель на их основе

MetaTrader 5Интеграция |
480 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko
Успешная торговля на валютном рынке требует не только технического анализа, но и учета фундаментальных факторов. Ценные, но часто игнорируемые источники — отчёты CFTC (COT и TFF), раскрывающие позиции крупнейших участников рынка и позволяющие оценить поведение институциональных инвесторов.

Рынок Forex — крупнейший в мире, но его высокая волатильность делает прогнозирование сложным. Отчёты COT/TFF дают понимание действий "умных денег" и помогают выявлять скрытые рыночные тренды.

Предложенный подход объединяет данные COT/TFF и рыночные котировки в единую модель на Python, с автоматической торговлей через MetaTrader 5. Это позволяет перейти от анализа к действию без задержек и человеческого вмешательства.



Теоретическая основа

Что такое отчеты COT и TFF

Представьте, что у вас есть возможность заглянуть в портфели крупнейших игроков на валютном рынке — хедж-фондов с миллиардными активами, пенсионных фондов, инвестиционных банков. Именно это и делают отчеты COT и TFF, публикуемые каждую пятницу американской Комиссией по торговле товарными фьючерсами (CFTC).

Эти отчеты появились после рыночных кризисов 1970-80х годов, когда регуляторы поняли: участникам рынка нужна информация о том, что делают крупные игроки. Теперь все, кто держит позиции выше определенного порога, обязаны раскрывать свои позиции. CFTC собирает эту информацию и публикует в агрегированном виде, показывая позиции на конец вторника.

Кто есть кто на рынке: участники и их мотивы

Коммерческие трейдеры — это реальный бизнес, который использует фьючерсы не для спекуляций, а для защиты от рисков. Авиакомпания покупает фьючерсы на нефть, чтобы зафиксировать цену топлива. Экспортер продает валютные фьючерсы, чтобы защититься от падения курса. Фермер заранее продает фьючерсы на пшеницу, чтобы знать, сколько получит за урожай.

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

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

Что показывают отчеты COT

Отчеты COT дают общую картину по всем фьючерсным рынкам. Для каждой группы участников показываются длинные позиции (ставки на рост), короткие позиции (ставки на падение) и чистые позиции (разница между длинными и короткими). Открытый интерес показывает общее количество активных контрактов на рынке.

Например, если по евро некоммерческие трейдеры держат чистую длинную позицию +120,000 контрактов, это означает, что спекулянты в целом ставят на рост EUR/USD. Если коммерческие трейдеры показывают -115,000 контрактов, то хеджеры либо ожидают ослабления евро, либо просто защищаются от валютных рисков.

Что добавляют отчеты TFF

Отчеты TFF фокусируются только на финансовых фьючерсах и показывают более детальную картину институциональных позиций. Здесь некоммерческие трейдеры разделены на подкaтeгории: фонды с кредитным плечом (агрессивные хедж-фонды, которые активно используют заемные средства) и управляющие активами (консервативные пенсионные фонды и страховые компании). Также выделяются дилеры и посредники — банки, которые обеспечивают ликвидность рынка.

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

Зачем это нужно трейдерам

Основная ценность отчетов в том, что они показывают, куда движутся "умные деньги" еще до того, как это отражается в ценах. Крупные игроки часто действуют на основе информации, недоступной рядовым участникам рынка.

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

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

Раннее обнаружение разворотов. Дивергенции между ценой и позициями часто предшествуют разворотам. Цена может продолжать расти, но если крупные игроки уже сокращают ставки на рост, разворот близко.

Практический пример

Допустим, отчет по EUR/USD показывает: фонды с кредитным плечом держат +85,000 контрактов, управляющие активами +25,000, а дилеры -95,000. Это означает, что и агрессивные спекулянты, и консервативные институциональные инвесторы ставят на рост евро. Дилеры занимают противоположную сторону, возможно, обслуживая клиентские заказы или хеджируя собственные риски. Общий сигнал — бычий по евро.

Важные нюансы

Данные публикуются с трёхдневной задержкой, что критично в быстро меняющихся рынках. Не все коммерческие позиции являются чистым хеджированием — некоторые банки могут спекулировать под видом коммерческой деятельности. Алгоритмическая торговля и машинное обучение меняют традиционные поведенческие модели участников.

Современные алгоритмы могут использовать сами отчеты COT/TFF для принятия решений, что создает самоисполняющиеся пророчества и усложняет интерпретацию данных.

Почему это работает

Отчеты COT и TFF работают, потому что показывают реальные денежные ставки, а не мнения или прогнозы. Когда хедж-фонд ставит миллиарды долларов на определенное направление рынка, это гораздо более значимый сигнал, чем любые публичные комментарии. Эти данные отражают коллективную мудрость наиболее информированных и ресурсообеспеченных участников рынка.

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

Прогнозирование цен с использованием данных COT/TFF

Прогнозирование цен основано на предположении, что позиции крупных игроков коррелируют с будущими ценовыми движениями. Например, увеличение длинных позиций некоммерческих трейдеров может сигнализировать о бычьем рынке, тогда как рост коротких позиций может указывать на медвежий настрой. Эти данные используются как признаки для модели машинного обучения, дополненные историческими ценами из MetaTrader 5 и техническими индикаторами, такими как волатильность и скользящие средние. Такой подход позволяет улавливать сложные зависимости между рыночными настроениями и ценовыми трендами.



Подготовка данных

Отчеты COT и TFF публикуются в формате Excel на сайте CFTC. Для их загрузки используется библиотека requests, а для обработки — pandas. Код ниже демонстрирует загрузку и предварительную обработку данных COT с кэшированием для повышения производительности:

import requests
import zipfile
import pandas as pd
import os
import logging
import MetaTrader5 as mt5
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import importlib.util
import glob
import asyncio

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = [10, 6]

COT_URL = "https://www.cftc.gov/files/dea/history/dea_fut_xls_2024.zip"
TFF_URL = "https://www.cftc.gov/files/dea/history/fut_fin_xls_2024.zip"
OUTPUT_DIR = "data"
os.makedirs(OUTPUT_DIR, exist_ok=True)

def load_cot_reports() -> pd.DataFrame:
    cache_path = os.path.join(OUTPUT_DIR, "cot_report.csv")
    if os.path.exists(cache_path):
        logger.info(f"Загрузка COT данных из кэша: {cache_path}")
        return pd.read_csv(cache_path)

    try:
        response = requests.get(COT_URL)
        response.raise_for_status()
        zip_path = os.path.join(OUTPUT_DIR, "cot_data.zip")
        with open(zip_path, "wb") as f:
            f.write(response.content)

        with zipfile.ZipFile(zip_path, 'r') as zip_ref:
            zip_ref.extractall(OUTPUT_DIR)
            excel_file = [f for f in zip_ref.namelist() if f.endswith('.xls') or f.endswith('.xlsx')][0]
        
        cot_data = pd.read_excel(os.path.join(OUTPUT_DIR, excel_file))
        relevant_columns = [
            "Market_and_Exchange_Names",
            "NonComm_Positions_Long_All",
            "NonComm_Positions_Short_All",
            "Comm_Positions_Long_All",
            "Comm_Positions_Short_All",
            "Open_Interest_All"
        ]
        cot_data = cot_data[relevant_columns]
        cot_data["Net_NonComm"] = cot_data["NonComm_Positions_Long_All"] - cot_data["NonComm_Positions_Short_All"]
        cot_data["Net_Comm"] = cot_data["Comm_Positions_Long_All"] - cot_data["Comm_Positions_Short_All"]
        cot_data.to_csv(cache_path, index=False)
        logger.info(f"Отчет COT сохранен в {cache_path}")
        return cot_data
    except Exception as e:
        logger.error(f"Ошибка при загрузке данных COT: {e}")
        return pd.DataFrame()

Для отчетов TFF применяется аналогичный процесс с фильтрацией по валютным фьючерсам и расчетом чистых позиций для фондов с кредитным плечом и управляющих активами. Кэширование данных минимизирует сетевые запросы, ускоряя повторную обработку.

Интеграция с MetaTrader 5

Исторические цены загружаются через библиотеку MetaTrader 5, обеспечивающую доступ к рыночным данным. Код ниже получает часовые цены за последние 30 дней:

def get_historical_prices(pair: str, days_history: int = 30) -> pd.DataFrame:
    if not mt5.initialize():
        logger.error("MT5 initialization failed")
        return pd.DataFrame()

    timeframe = mt5.TIMEFRAME_H1
    utc_from = datetime.now() - timedelta(days=days_history)
    rates = mt5.copy_rates_from(pair, timeframe, utc_from, 24 * days_history)
    
    if rates is None or len(rates) == 0:
        logger.warning(f"Нет исторических данных для {pair}")
        return pd.DataFrame()

    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    df['price_change_24h'] = df['close'].shift(-24) / df['close'] - 1
    df.dropna(inplace=True)
    return df[['open', 'high', 'low', 'close', 'tick_volume', 'price_change_24h']]

Обработка и объединение данных

Данные COT, TFF и исторические цены объединяются, добавляются производные признаки: волатильность (разница между максимальной и минимальной ценой, нормализованная на цену закрытия), 24-часовые скользящие средние для объема и цены, а также лаги и процентные изменения для позиций COT и TFF.

Код подготовки признаков:

def prepare_features(pair: str, cot_data: pd.DataFrame, tff_data: pd.DataFrame) -> pd.DataFrame:
    df_prices = get_historical_prices(pair)
    if df_prices.empty:
        logger.warning(f"Нет ценовых данных для {pair}")
        df = pd.DataFrame(index=[datetime.now()], columns=['close', 'price_change_24h'])
        df['close'] = 1.0
        df['price_change_24h'] = 0.0
    else:
        df = df_prices.copy()

    df['volatility'] = (df['high'] - df['low']) / df['close']
    df['volume_sma_24'] = df['tick_volume'].rolling(window=24).mean()
    df['price_sma_24'] = df['close'].rolling(window=24).mean()
    df['price_change_1h'] = df['close'].pct_change()

    market = map_pair_to_cot_tff(pair)
    if market and not cot_data.empty:
        cot_subset = cot_data[cot_data["Market_and_Exchange_Names"].str.contains(market, case=False, na=False)]
        if not cot_subset.empty:
            cot_features = cot_subset[['Net_NonComm', 'Net_Comm']].mean().to_frame().T
            for col in cot_features.columns:
                df[col] = cot_features[col].iloc[0]

    if market and not tff_data.empty:
        tff_subset = tff_data[tff_data["Market_and_Exchange_Names"].str.contains(market, case=False, na=False)]
        if not tff_subset.empty:
            tff_features = tff_subset[['Net_Lev_Money', 'Net_Asset_Mgr']].mean().to_frame().T
            for col in tff_features.columns:
                df[col] = tff_features[col].iloc[0]

    for col in ['Net_NonComm', 'Net_Comm', 'Net_Lev_Money', 'Net_Asset_Mgr']:
        if col in df.columns:
            df[f'{col}_lag1'] = df[col].shift(1)
            df[f'{col}_change'] = df[col].pct_change().fillna(0)

    df.dropna(inplace=True)
    return df

def map_pair_to_cot_tff(pair: str) -> str:
    mapping = {
        'EURUSD': 'EURO FX',
        'GBPUSD': 'BRITISH POUND',
        'USDJPY': 'JAPANESE YEN',
        'AUDUSD': 'AUSTRALIAN DOLLAR',
        'USDCAD': 'CANADIAN DOLLAR',
        'USDCHF': 'SWISS FRANC',
        'NZDUSD': 'NEW ZEALAND DOLLAR'
    }
    base_pair = pair.replace('.ecn', '')[:6]
    return mapping.get(base_pair, '')

Для повышения точности можно оптимизировать гиперпараметры, такие как количество деревьев или максимальная глубина, а также применять кросс-валидацию для оценки устойчивости модели. Важность признаков сохраняется в CSV-файл для анализа их влияния на прогнозы.



Прогнозирование и визуализация

Прогнозирование цен

Прогноз выполняется на основе последних данных, предсказывая процентное изменение цены через 24 часа, которое применяется к текущей рыночной цене. Код прогноза:

async def get_price_forecast(pair: str, model, scaler) -> dict:
    df = prepare_features(pair, cot_data, tff_data)
    if df.empty:
        logger.warning(f"Нет данных для прогнозирования для {pair}")
        return {'pair': pair, 'forecast_price': None, 'confidence': 0.0}

    X_latest = df.drop(columns=['price_change_24h']).iloc[-1:]
    X_scaled = scaler.transform(X_latest)
    
    price_change_pred = model.predict(X_scaled)[0]
    confidence = model.score(X_scaled, df['price_change_24h'].iloc[-1:]) if len(df) > 1 else 0.6
    
    tick = mt5.symbol_info_tick(pair)
    if not tick:
        logger.warning(f"Нет текущих данных для {pair}")
        return {'pair': pair, 'forecast_price': None, 'confidence': 0.0}
    
    current_price = (tick.bid + tick.ask) / 2
    forecast_price = current_price * (1 + price_change_pred)
    
    forecast_df = pd.DataFrame([{
        'forecast_price': forecast_price,
        'confidence': confidence,
        'current_price': current_price,
        'price_change_pred': price_change_pred,
        'timestamp': datetime.now()
    }])
    output_path = os.path.join(OUTPUT_DIR, f"forecast_{pair}.csv")
    forecast_df.to_csv(output_path, index=False)
    logger.info(f"Прогноз для {pair} сохранен в {output_path}")
    
    visualize_forecast(pair, current_price, forecast_price, confidence)
    
    return {
        'pair': pair,
        'forecast_price': forecast_price,
        'confidence': max(0.0, min(1.0, confidence))
    }

Визуализация результатов

Для анализа создаются графики текущей и прогнозируемой цены, а также чистых позиций COT и TFF. Код визуализации:

def visualize_cot_data(cot_data: pd.DataFrame):
    if cot_data.empty or "Net_NonComm" not in cot_data.columns:
        logger.warning("Нет данных COT для визуализации")
        return

    plt.figure(figsize=(14, 8))
    for market in cot_data["Market_and_Exchange_Names"].unique():
        market_data = cot_data[cot_data["Market_and_Exchange_Names"] == market]
        plt.plot(range(len(market_data)), market_data["Net_NonComm"], label=market, alpha=0.7)
    
    plt.title("Чистые позиции Non-Commercial для валютных фьючерсов")
    plt.xlabel("Запись")
    plt.ylabel("Чистые позиции")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.tight_layout()
    output_path = os.path.join(OUTPUT_DIR, "cot_net_positions.png")
    plt.savefig(output_path, dpi=150)
    plt.close()
    logger.info(f"График чистых позиций COT сохранен в {output_path}")

def visualize_tff_data(tff_data: pd.DataFrame):
    if tff_data.empty or "Net_Lev_Money" not in tff_data.columns:
        logger.warning("Нет данных TFF для визуализации")
        return

    plt.figure(figsize=(14, 8))
    for market in tff_data["Market_and_Exchange_Names"].unique():
        market_data = tff_data[tff_data["Market_and_Exchange_Names"] == market]
        plt.plot(range(len(market_data)), market_data["Net_Lev_Money"], label=market, alpha=0.7)

    plt.title("Чистые позиции Leveraged Funds для валютных фьючерсов (TFF)")
    plt.xlabel("Запись")
    plt.ylabel("Чистые позиции")
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.tight_layout()
    output_path = os.path.join(OUTPUT_DIR, "tff_net_positions.png")
    plt.savefig(output_path, dpi=150)
    plt.close()
    logger.info(f"График чистых позиций TFF сохранен в {output_path}")

def visualize_forecast(pair: str, current_price: float, forecast_price: float, confidence: float):
    plt.figure(figsize=(8, 5))
    plt.bar(['Current Price', 'Forecast Price'], [current_price, forecast_price], color=['blue', 'green'], alpha=0.7)
    plt.title(f"Прогноз цены через 24 часа для {pair} (Confidence: {confidence:.2f})")
    plt.ylabel('Цена')
    plt.tight_layout()
    output_path = os.path.join(OUTPUT_DIR, f"forecast_{pair}_plot.png")
    plt.savefig(output_path, dpi=150)
    plt.close()
    logger.info(f"График прогноза сохранен в {output_path}")

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


Оптимизация и обработка ошибок

Кэширование данных

Для минимизации сетевых запросов данные COT и TFF кэшируются в CSV-файлах, что позволяет повторно использовать актуальные данные и ускоряет обработку. Кэш проверяется перед загрузкой новых данных и, только при его отсутствии или устаревании, выполняется новый запрос.

Проверка зависимостей

Перед запуском проверяется наличие библиотек xlrd и openpyxl, необходимых для чтения Excel-файлов:

def check_dependencies():
    dependencies = ['xlrd', 'openpyxl']
    for dep in dependencies:
        if not importlib.util.find_spec(dep):
            logger.error(f"Missing dependency: {dep}. Install it using 'pip install {dep}'.")
            raise ImportError(f"Missing dependency: {dep}")

Обработка ошибок

Код включает обработку исключений для всех критических операций: загрузки данных, подключения к MetaTrader 5 и обучения модели. Это обеспечивает устойчивость системы к сбоям, например, при отсутствии данных или проблемах с соединением.



Практическое применение

Тестирование модели

Для оценки модели рекомендуется тестирование на исторических данных за 2024 год. Метрики R² и важность признаков сохраняются в CSV-файлах для анализа. Тестирование позволяет оценить точность прогнозов и выявить наиболее значимые признаки, такие как чистые позиции некоммерческих трейдеров или волатильность.

Интеграция в торговую стратегию

Прогнозы могут использоваться для автоматической торговли через библиотеку MetaTrader 5. Например, при прогнозном изменении цены более 0.5% и высокой уверенности модели (выше 0.7) можно открывать длинные или короткие позиции. Код для автоматической торговли:

def execute_trade(pair: str, forecast_price: float, confidence: float):
    if not mt5.initialize():
        logger.error("MT5 initialization failed")
        return

    symbol_info = mt5.symbol_info(pair)
    if not symbol_info:
        logger.warning(f"Символ {pair} не найден")
        return

    current_price = (mt5.symbol_info_tick(pair).bid + mt5.symbol_info_tick(pair).ask) / 2
    if confidence > 0.7 and forecast_price > current_price * 1.005:
        request = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": pair,
            "volume": 0.1,
            "type": mt5.ORDER_TYPE_BUY,
            "price": mt5.symbol_info_tick(pair).ask,
            "type_time": mt5.ORDER_TIME_GTC,
            "type_filling": mt5.ORDER_FILLING_IOC,
        }
        result = mt5.order_send(request)
        logger.info(f"Открыта длинная позиция для {pair}: {result}")
    elif confidence > 0.7 and forecast_price < current_price * 0.995:
        request = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": pair,
            "volume": 0.1,
            "type": mt5.ORDER_TYPE_SELL,
            "price": mt5.symbol_info_tick(pair).bid,
            "type_time": mt5.ORDER_TIME_GTC,
            "type_filling": mt5.ORDER_FILLING_IOC,
        }
        result = mt5.order_send(request)
        logger.info(f"Открыта короткая позиция для {pair}: {result}")

Расширение функциональности

Для улучшения модели, можно добавить технические индикаторы, такие как RSI или MACD, для учета дополнительных рыночных сигналов. Альтернативные алгоритмы, такие как градиентный бустинг (XGBoost) или нейронные сети, могут повысить точность прогнозов. Динамическое обновление данных COT и TFF можно реализовать через планировщик задач, например, библиотеку schedule, для автоматического получения новых отчетов по мере их публикации. Также возможно расширение для анализа других активов, таких как товары или индексы, с соответствующей адаптацией признаков.

Полная реализация: CurrencyForecastModule

Для удобства интеграции все функции объединены в класс CurrencyForecastModule, который инкапсулирует загрузку данных, обучение модели, прогнозирование и визуализацию:

class CurrencyForecastModule:
    def __init__(self, pairs: list, days_history: int = 30):
        self.pairs = pairs
        self.days_history = days_history
        self.models = {}
        self.scalers = {}
        self.forecasts = {}

        check_dependencies()
        if not mt5.initialize():
            logger.error("MT5 initialization failed. Ensure MT5 terminal is running and connected.")
            raise RuntimeError("MT5 initialization failed")

        self._validate_symbols()
        self._initialize_data()

    def _validate_symbols(self):
        available_symbols = [s.name for s in mt5.symbols_get()]
        logger.info(f"Доступные символы в MT5: {available_symbols}")
        self.symbol_mapping = {}
        for pair in self.pairs[:]:
            if pair in available_symbols:
                self.symbol_mapping[pair] = pair
            else:
                base_pair = pair.split('.')[0]
                if base_pair in available_symbols:
                    self.symbol_mapping[pair] = base_pair
                    logger.info(f"Сопоставлено: {pair} -> {base_pair}")
                else:
                    logger.warning(f"Символ {pair} не найден в MT5. Пропускается.")
                    self.pairs.remove(pair)

    def _initialize_data(self):
        logger.info("Инициализация данных для CurrencyForecastModule...")
        self.cot_data = load_cot_reports()
        self.tff_data = self._load_tff_reports()
        for pair in self.pairs:
            self._train_model(pair)

    def _load_tff_reports(self) -> pd.DataFrame:
        cache_path = os.path.join(OUTPUT_DIR, "tff_report.csv")
        if os.path.exists(cache_path):
            logger.info(f"Загрузка TFF данных из кэша: {cache_path}")
            return pd.read_csv(cache_path)

        try:
            response = requests.get(TFF_URL)
            response.raise_for_status()
            zip_path = os.path.join(OUTPUT_DIR, "tff_data.zip")
            with open(zip_path, "wb") as f:
                f.write(response.content)

            with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                zip_ref.extractall(OUTPUT_DIR)
                logger.info(f"Содержимое TFF архива: {zip_ref.namelist()}")

            excel_files = glob.glob(os.path.join(OUTPUT_DIR, "**", "*.xls*"), recursive=True)
            tff_files = [f for f in excel_files if 'FinFut' in f or 'fin' in f.lower()]
            if not tff_files:
                logger.error("Excel-файл TFF не найден в извлеченных файлах")
                return pd.DataFrame()

            excel_file = tff_files[0]
            logger.info(f"Обработка TFF файла: {excel_file}")

            relevant_columns = [
                "Market_and_Exchange_Names",
                "Lev_Money_Positions_Long_All",
                "Lev_Money_Positions_Short_All",
                "Asset_Mgr_Positions_Long_All",
                "Asset_Mgr_Positions_Short_All",
                "Open_Interest_All"
            ]
            tff_data = pd.read_excel(excel_file, engine='xlrd' if excel_file.endswith('.xls') else 'openpyxl')
            available_columns = [col for col in relevant_columns if col in tff_data.columns]
            if not available_columns:
                logger.error("Не найдены ожидаемые столбцы в данных TFF")
                return pd.DataFrame()

            tff_data = tff_data[available_columns]
            forex_markets = ["EURO FX", "JAPANESE YEN", "BRITISH POUND", "AUSTRALIAN DOLLAR",
                             "CANADIAN DOLLAR", "SWISS FRANC", "MEXICAN PESO", "NEW ZEALAND DOLLAR"]
            tff_data = tff_data[tff_data["Market_and_Exchange_Names"].str.contains('|'.join(forex_markets), case=False, na=False)]
            if "Lev_Money_Positions_Long_All" in tff_data.columns:
                tff_data["Net_Lev_Money"] = tff_data["Lev_Money_Positions_Long_All"] - tff_data["Lev_Money_Positions_Short_All"]
            if "Asset_Mgr_Positions_Long_All" in tff_data.columns:
                tff_data["Net_Asset_Mgr"] = tff_data["Asset_Mgr_Positions_Long_All"] - tff_data["Asset_Mgr_Positions_Short_All"]

            tff_data.to_csv(cache_path, index=False)
            logger.info(f"Отчет TFF сохранен в {cache_path}")
            visualize_tff_data(tff_data)
            return tff_data
        except Exception as e:
            logger.error(f"Ошибка при загрузке данных TFF: {e}")
            return pd.DataFrame()

    def _train_model(self, pair: str):
        df = prepare_features(pair, self.cot_data, self.tff_data)
        model, scaler = train_model(pair, df)
        if model and scaler:
            self.models[pair] = model
            self.scalers[pair] = scaler

    async def update_forecasts(self):
        logger.info("Обновление прогнозов цен...")
        for pair in self.pairs:
            forecast = await get_price_forecast(pair, self.models.get(pair), self.scalers.get(pair))
            logger.info(f"Прогноз для {pair}: Цена={forecast['forecast_price']}, Уверенность={forecast['confidence']:.2f}")

    def __del__(self):
        mt5.shutdown()

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

В результате, получаем прогнозы, и несколько графиков. Вот к примеру, график позиций COT vs TFF:

Результирующим итогом работы кода получаем прогноз цен группы символов через 24 часа:

Хотя, конечно, более логичным было бы использовать в качестве прогноза именно закрытие вечера пятницы — тогда это будет согласовано с периодичностью публикации отчетов COT и TFF. 



Заключение

Использование данных COT и TFF с историческими ценами через библиотеку MetaTrader 5 в Python позволяет создавать эффективные модели прогнозирования цен, интегрируемые в торговые стратегии. Реализованное решение автоматизирует загрузку данных, обучение модели и выполнение торговых операций, предоставляя трейдерам устойчивую основу для анализа и принятия решений на финансовых рынках. Код можно расширять, добавляя новые индикаторы или алгоритмы, а также, автоматизируя обновление данных для постоянной актуальности прогнозов.

Прикрепленные файлы |
COT_TFF_Predict.py (37.75 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Aliaksandr Kazunka
Aliaksandr Kazunka | 9 июн. 2025 в 04:07
А важность признаков не смотрели? Что-то мне подсказывает, что 
Net_NonComm,0.0,0.0
Net_Comm,0.0,0.0
Net_Lev_Money,0.0,0.0
Net_Asset_Mgr,0.0,0.0
Net_NonComm_lag1,0.0,0.0
Net_NonComm_change,0.0,0.0
Net_Comm_lag1,0.0,0.0
Net_Comm_change,0.0,0.0
Net_Lev_Money_lag1,0.0,0.0
Net_Lev_Money_change,0.0,0.0
Net_Asset_Mgr_lag1,0.0,0.0
Net_Asset_Mgr_change,0.0,0.0
От начального до среднего уровня: Определения (I) От начального до среднего уровня: Определения (I)
В этой статье мы будем делать такие вещи, которые многим покажутся странными и совершенно вырванными из контекста, но которые при правильном применении сделают ваше обучение гораздо более увлекательным и интересным: мы сможем построить довольно интересные вещи на основе показанного здесь, что позволит лучше усвоить синтаксис языка MQL5. Представленные здесь материалы предназначены только для обучения. Ни в коем случае не рассматривайте его как окончательное приложение, целью которого не является изучение представленных концепций.
Построение модели для ограничения диапазона сигналов по тренду (Часть 9): Советник с несколькими стратегиями (II) Построение модели для ограничения диапазона сигналов по тренду (Часть 9): Советник с несколькими стратегиями (II)
Количество стратегий, которые можно интегрировать в виде советника, практически безгранично. Однако каждая дополнительная стратегия увеличивает сложность алгоритма. Благодаря использованию нескольких стратегий советник может лучше адаптироваться к изменяющимся рыночным условиям, что потенциально повышает его прибыльность. Сегодня мы рассмотрим, как реализовать в MQL5 одну из выдающихся стратегий, разработанных Ричардом Дончианом, продолжая при этом совершенствовать функциональность нашего советника Trend Constraint.
Нейросети в трейдинге: Фреймворк кросс-доменного прогнозирования временных рядов (TimeFound) Нейросети в трейдинге: Фреймворк кросс-доменного прогнозирования временных рядов (TimeFound)
В этой статье мы шаг за шагом собираем ядро интеллектуальной модели TimeFound, адаптированной под реальные задачи прогнозирования временных рядов. Если вас интересует практическая реализация нейросетевых патчинг-алгоритмов в MQL5 — вы точно по адресу.
От начального до среднего уровня: Рекурсия От начального до среднего уровня: Рекурсия
В этой статье мы рассмотрим очень интересную и довольно интересную концепцию программирования, хотя к ней следует относиться с большой осторожностью, поскольку неправильное её использование или непонимание превращает относительно простые программы в нечто неоправданно сложное. Но правильное использование и идеальная адаптация в одинаково подходящих ситуациях делают рекурсию отличным союзником в решении вопросов, которые в другом случае были бы гораздо более трудоемкими и длительными. Представленные здесь материалы предназначены только для изучения. Ни в коем случае нельзя рассматривать это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.