Русский Português
preview
Minería de datos de la CFTC en Python y creación de un modelo de IA

Minería de datos de la CFTC en Python y creación de un modelo de IA

MetaTrader 5Integración |
116 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko
Para negociar con éxito en el mercado Forex es necesario no solo analizar técnicamente, sino también tener en cuenta factores fundamentales. Fuentes valiosas pero a menudo ignoradas son los informes de la CFTC (COT y TFF), que revelan las posiciones de los principales participantes del mercado y nos permiten estimar el comportamiento de los inversores institucionales.

El mercado Forex es el más grande del mundo, pero su alta volatilidad dificulta la realización de pronósticos. Los informes COT/TFF ofrecen información sobre los movimientos del dinero inteligente y ayudan a detectar tendencias subyacentes del mercado.

El enfoque propuesto combina datos COT/TFF y cotizaciones del mercado en un único modelo Python, con comercio automatizado a través de MetaTrader 5. Esto permite pasar del análisis a la acción sin demoras ni intervención humana.



Base teórica

¿Qué son los informes COT y TFF?

Imagínese poder analizar las carteras de los participantes más importantes del mercado de divisas: fondos de cobertura con miles de millones de dólares en activos, fondos de pensiones, bancos de inversión, etc. Esto es exactamente lo que hacen los informes COT y TFF, publicados todos los viernes por la Comisión de Comercio de Futuros de Productos Básicos de Estados Unidos (CFTC).

Estos informes surgieron después de las crisis del mercado de los años 1970 y 1980, cuando los reguladores se dieron cuenta de que los participantes del mercado necesitaban información sobre lo que estaban haciendo los principales participantes. A partir de entonces todos aquellos que ocupen posiciones por encima de un determinado umbral deberán revelar sus posiciones. La CFTC recopila esta información y la publica en forma agregada, mostrando las posiciones al final del martes.

Quién es quién en el mercado: los participantes y sus motivaciones

Los traders comerciales son empresas reales que utilizan futuros no para especular, sino para protegerse de riesgos. Las aerolíneas compran futuros de petróleo para fijar el precio del combustible. Los exportadores venden futuros de divisas para protegerse contra una caída del tipo de cambio. Los agricultores venden futuros de trigo por adelantado para saber cuánto recibirá por la cosecha.

Los traders no comerciales son especuladores que negocian para obtener ganancias de los movimientos de precios. Son fondos de cobertura, bancos de inversión y grandes sociedades de gestión. A menudo se les llama "dinero inteligente" porque tienen los recursos para analizar los mercados en profundidad.

Los traders pequeños son todos los demás participantes con posiciones por debajo de los umbrales de informe. Traders minoristas, pequeños fondos y especuladores individuales.

¿Qué muestran los informes del COT?

Los informes COT ofrecen una imagen general de todos los mercados de futuros. Para cada grupo de participantes, se muestran las posiciones largas (apuestas alcistas), las posiciones cortas (apuestas bajistas) y las posiciones netas (la diferencia entre largas y cortas). El interés abierto muestra el número total de contratos activos en el mercado.

Por ejemplo, si los traders no comerciales tienen una posición larga neta en euros de +120.000 contratos, esto significa que los especuladores generalmente apuestan a que el EUR/USD subirá. Si los traders comerciales muestran -115.000 contratos, entonces los coberturistas esperan que el euro se debilite o simplemente se protegen de los riesgos cambiarios.

Qué añaden los informes de TFF

Los informes de TFF se centran únicamente en los futuros financieros y ofrecen una imagen más detallada de las posiciones institucionales. Aquí, los traders no comerciales se dividen en subcategorías: fondos apalancados (fondos de cobertura agresivos que utilizan en gran medida fondos prestados) y gestores de activos (fondos de pensiones y compañías de seguros conservadoras). También hay distribuidores e intermediarios: bancos que ofrecen liquidez al mercado.

Este detalle es de importancia crítica. Si los fondos apalancados aumentan drásticamente sus posiciones largas en dólares, es una señal de interés especulativo a corto plazo. Si los administradores de activos hacen lo mismo, esto indicará un cambio a largo plazo en el sentimiento de los inversores institucionales.

¿Por qué los traders necesitan esto?

El principal valor de los informes es que muestran hacia dónde fluye el dinero inteligente antes de que se refleje en los precios. Los grandes actores a menudo actúan basándose en información que no está disponible para los participantes ordinarios del mercado.

Identificación de extremos. Cuando los especuladores toman posiciones largas récord, el mercado suele mostrar sobrecompra y está listo para una corrección. La situación opuesta señala un posible repunte. Esto funciona según el principio del trading contrario: actuar contra la multitud en momentos extremos.

Confirmación de tendencias. Si el precio sube y los traders no comerciales continúan aumentando sus posiciones largas, la tendencia es fuerte. Si comienzan a reducir posiciones cuando los precios suben, la tendencia se debilita.

Detección temprana de reversiones. Las divergencias entre el precio y las posiciones suelen preceder a las reversiones. El precio puede continuar subiendo, pero si los principales actores ya están reduciendo sus apuestas al crecimiento, se aproxima una reversión.

Ejemplo práctico

Digamos que el informe EUR/USD muestra que los fondos apalancados tienen +85.000 contratos, los gestores de activos +25.000 y los distribuidores -95.000. Esto significa que tanto los especuladores agresivos como los inversores institucionales conservadores están apostando al alza del euro. Los distribuidores adoptan el lado opuesto, tal vez ejecutando órdenes de clientes o cubriendo sus propios riesgos. La señal general es alcista para el euro.

Matices importantes a considerar

Los datos se publican con un retraso de tres días, lo que resulta fundamental en mercados de rápida evolución. No todas las posiciones comerciales son pura cobertura: algunos bancos pueden especular bajo la apariencia de actividad comercial. El trading algorítmico y el aprendizaje automático están cambiando los patrones de comportamiento tradicionales de los participantes.

Los algoritmos modernos pueden usar los propios informes COT/TFF para tomar decisiones, lo que crea profecías autocumplidas y complica la interpretación de los datos.

¿Por qué funciona esto?

Los informes COT y TFF funcionan porque muestran tasas monetarias reales, no opiniones ni pronósticos. Cuando un fondo de cobertura apuesta miles de millones de dólares en una dirección particular del mercado, esto supone una señal mucho más significativa que cualquier comentario público. Los datos reflejan la sabiduría colectiva de los participantes del mercado más informados y con más recursos.

Cuando se combina con el aprendizaje automático y el trading automatizado, el análisis de la posición institucional se convierte en una herramienta poderosa para crear estrategias de trading basadas en el comportamiento de quienes realmente mueven los mercados.

Pronóstico de precios usando los datos de COT/TFF

La previsión de precios se basa en el supuesto de que las posiciones de los principales actores están correlacionadas con los futuros movimientos de precios. Por ejemplo, un aumento en las posiciones largas de los traders no comerciales puede indicar un mercado alcista, mientras que un aumento en las posiciones cortas puede indicar un sentimiento bajista. Estos datos se usan como características para un modelo de aprendizaje automático, complementado con precios históricos de MetaTrader 5 e indicadores técnicos como volatilidad y promedios móviles. Este enfoque nos permite captar relaciones complejas entre el sentimiento del mercado y las tendencias de precios.



Preparación de los datos

Los informes COT y TFF se publican en formato Excel en el sitio web de la CFTC. La biblioteca de solicitudes se usa para cargarlos, mientras que pandas se utiliza para procesarlas. El código siguiente demuestra cómo cargar y preprocesar datos COT con almacenamiento en caché para mejorar el rendimiento:

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()

Para los informes TFF, se usa un proceso similar, filtrando por futuros de divisas y calculando posiciones netas para fondos apalancados y gestores de activos. El almacenamiento en caché de datos minimiza las solicitudes de red, lo que acelera el reprocesamiento.

Integración con MetaTrader 5

Los precios históricos se descargan a través de la biblioteca MetaTrader 5, que ofrece acceso a los datos del mercado. El código siguiente obtiene los precios por hora de los últimos 30 días:

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']]

Procesamiento y fusión de datos

Se combinan datos de precios históricos, COT y TFF, y se agregan características derivadas: volatilidad (la diferencia entre los precios máximos y mínimos, normalizada al precio de cierre), promedios móviles de 24 horas para volumen y precio, y retrasos y cambios porcentuales para posiciones COT y TFF.

Código de preparación de características:

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, '')

Para mejorar la precisión, se pueden optimizar hiperparámetros como el número de árboles o la profundidad máxima, y se puede usar la validación cruzada para evaluar la solidez del modelo. La importancia de las características se guarda en un archivo CSV para analizar su impacto en los pronósticos.



Previsión y visualización

Previsión de precios

El pronóstico se realiza en base a los últimos datos, prediciendo el cambio porcentual del precio en 24 horas, que se aplica al precio actual del mercado. Código de pronóstico:

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))
    }

Visualización de resultados

Para el análisis, se crean gráficos de los precios actual y proyectado, así como de las posiciones netas COT y TFF. Código de visualización:

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}")

Los gráficos ayudan a evaluar visualmente el sentimiento del mercado y la precisión del pronóstico ofreciendo a los traders una comprensión clara de los datos.


Optimización y procesamiento de errores

Almacenamiento en caché de datos

Para minimizar las solicitudes de red, los datos COT y TFF se almacenan en caché en archivos CSV, lo cual permite la reutilización de datos actualizados y un procesamiento más rápido. La caché se comprueba antes de cargar datos nuevos y solo si faltan o están desactualizados se realiza una nueva solicitud.

Comprobando dependencias

Antes del lanzamiento, se verifica la presencia de las bibliotecas xlrd y openpyxl necesarias para leer archivos 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}")

Procesamiento de errores

El código incluye el procesamiento de excepciones para todas las operaciones críticas: carga de datos, conexión a MetaTrader 5 y entrenamiento del modelo. Esto garantiza que el sistema sea resistente a fallas, como datos faltantes o problemas de conexión.



Uso práctico

Pruebas del modelo

Para valorar el modelo, recomendamos realizar pruebas con datos históricos del año 2024. Las métricas de R² y la importancia de las características se guardan en archivos CSV para su análisis. Las pruebas nos permiten estimar la precisión de los pronósticos e identificar las características más significativas, como las posiciones netas de los traders no comerciales o la volatilidad.

Integración en una estrategia comercial

Los pronósticos se pueden usar para el trading automatizado a través de la biblioteca MetaTrader 5. Por ejemplo, si el cambio de precio previsto es superior al 0,5% y el modelo tiene un alto nivel de confianza (por encima del 0,7), podemos abrir posiciones largas o cortas. Código para el trading automático:

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}")

Ampliación de la funcionalidad

Para mejorar el modelo, podemos agregar indicadores técnicos como RSI o MACD para considerar señales de mercado adicionales. Algoritmos alternativos como el aumento de gradiente (XGBoost) o las redes neuronales pueden mejorar la precisión del pronóstico. La actualización dinámica de los datos COT y TFF se puede implementar a través de un programador de tareas, como la biblioteca schedule, para recuperar automáticamente nuevos informes a medida que se publican. También se puede ampliar para analizar otros activos, como materias primas o índices, con la adaptación adecuada de las características.

Implementación completa: Módulo de pronóstico de divisas

Para facilitar la integración, todas las funciones se combinan en la clase CurrencyForecastModule, que encapsula la carga de datos, el entrenamiento del modelo, la previsión y la visualización:

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()

Esta clase ofrece una estructura modular para cargar datos, entrenar modelos y realizar pronósticos, simplificando la integración en los sistemas comerciales.

Como resultado, obtenemos pronósticos y varios gráficos. Aquí, por ejemplo, tenemos un gráfico con las posiciones COT vs TFF:

El resultado del código es un pronóstico de precios para un grupo de símbolos en 24 horas:

Aunque, obviamente, resultaría más lógico utilizar el cierre del viernes por la noche como pronóstico, entonces sería más coherente con la frecuencia de publicación de los informes COT y TFF. 



Conclusión

El uso de datos COT y TFF con precios históricos a través de la biblioteca Python de MetaTrader 5 permite crear modelos de pronóstico de precios efectivos que se pueden integrar en estrategias comerciales. La solución implementada automatiza la carga de datos, el entrenamiento de modelos y la ejecución de operaciones, ofreciendo a los traders una base estable para el análisis y la toma de decisiones en los mercados financieros. El código se puede ampliar agregando nuevos indicadores o algoritmos, además de automatizando las actualizaciones de datos para garantizar que los pronósticos estén siempre actualizados.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/18303

Archivos adjuntos |
COT_TFF_Predict.py (37.75 KB)
Aliaksandr Kazunka
Aliaksandr Kazunka | 9 jun 2025 en 04:07
¿Te has fijado en el significado de los signos? Algo me dice que
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
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Minería de datos de los balances de los bancos centrales y obtención de un panorama de la liquidez global Minería de datos de los balances de los bancos centrales y obtención de un panorama de la liquidez global
La minería de datos del balance de los bancos centrales ofrece una imagen de la liquidez global en el mercado Forex y en las divisas clave. Hoy combinaremos datos de la Fed, el BCE, el BOJ y el PBoC en un índice compuesto y utilizaremos el aprendizaje automático para descubrir patrones ocultos. Este enfoque convierte los datos sin procesar en señales comerciales reales combinando el análisis fundamental y técnico.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Creación de un Panel de administración de operaciones en MQL5 (Parte X): Interfaz basada en recursos externos Creación de un Panel de administración de operaciones en MQL5 (Parte X): Interfaz basada en recursos externos
Actualmente estamos aprovechando las capacidades de MQL5 para utilizar recursos externos, como imágenes en formato BMP, para crear una interfaz de inicio con un estilo único para el Panel de Administración de Operaciones. La estrategia que se muestra aquí resulta especialmente útil al empaquetar múltiples recursos, incluyendo imágenes, sonidos y más, para una distribución más eficiente. En este artículo exploramos cómo se implementan estas características para ofrecer una interfaz moderna y visualmente atractiva para nuestro New_Admin_Panel EA.