English Русский Português
preview
Ecuación simbólica de previsión del precio con SymPy

Ecuación simbólica de previsión del precio con SymPy

MetaTrader 5 — Integración |
25 1
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Introducción

Imagina que has dedicado meses a crear un sistema de trading con una red neuronal. El modelo ofrece unos resultados fantásticos en el backtesting: una precisión de previsión del 78 %, un beneficio estable y una rentabilidad constante. Pero en el trading real sucede lo inesperado: el algoritmo empieza de repente a perder dinero y no tienes ni idea de por qué. Esto no es nada raro en el mundo del trading algorítmico. Muchos traders que utilizan modelos de aprendizaje automático se enfrentan a problemas similares debido a la falta de interpretabilidad.

Al abrir el código, se pueden ver miles de pesos, cientos de neuronas y funciones de activación incomprensibles. El modelo toma decisiones, pero es imposible explicar su razonamiento. Es una «caja negra» clásica: sabes cuáles son las entradas y las salidas, pero lo que ocurre en su interior sigue siendo un misterio. Como consecuencia, cuando el mercado cambia, ya sea por acontecimientos geopolíticos —como ocurrió en 2022 con la crisis energética— o por la inflación en 2023-2024, el modelo «se descarrila». No puedes corregirlo rápidamente porque no entiendes los mecanismos.

Este es el problema que afecta al 90 % de los traders algorítmicos. El aprendizaje automático aporta potencia, pero resta comprensión. Random Forest, LSTM, SVM: todos estos métodos funcionan como por arte de magia: hay un resultado, pero no hay explicación. En un momento crítico, cuando el mercado se comporta de forma extraña (por ejemplo, un flash crash como el de 2010 o la volatilidad de 2020 debida a la pandemia), te quedas solo con un algoritmo impredecible. ¿Deberías seguir confiando en él o dejar de operar? ¿Deberías ajustar los parámetros o volver a entrenar el modelo? No hay respuestas porque no hay comprensión. Esto provoca pérdidas.



Viaje: en busca de la verdad matemática

Dada la dificultad que entraña interpretar el funcionamiento interno de los modelos de aprendizaje automático, me pregunté: ¿y si pudiéramos crear un modelo que fuera igual de potente, pero totalmente transparente? Un modelo que no solo ofreciera una previsión, sino una fórmula matemática exacta: «Precio en 24 horas = f(RSI, MACD, volatilidad, ...)». Esto nos permitiría no solo predecir, sino también comprender el mercado a un nivel fundamental.

La investigación condujo a las matemáticas simbólicas, un ámbito en el que los ordenadores no trabajan con números, sino con expresiones matemáticas. Si el aprendizaje automático convencional dice «la respuesta es 1,4523», entonces las matemáticas simbólicas explican: «la respuesta es 1,4523 porque 0,003×RSI - 0,127×volatilidad² + …».

La biblioteca SymPy de Python fue todo un descubrimiento. Permite no solo entrenar un modelo, sino también extraer de él una ecuación matemática en su forma más pura. Imagina: en lugar de una misteriosa red neuronal, tienes una fórmula que puedes escribir en un papel, analizar y comprender cada uno de sus términos. SymPy se integra con scikit-learn, lo que permite transformar las regresiones en expresiones algebraicas. Esto no es nada nuevo: el concepto lleva desarrollándose desde los años 80 (con sistemas como Mathematica), pero en el ámbito del trading resulta revolucionario. Según mi experiencia con el sistema Midas, esto aumentó la estabilidad del sistema en un 25 %, ya que me permitió ajustar manualmente las fórmulas a las condiciones del mercado.



Psicología de la incertidumbre en el trading

La incertidumbre que genera el trading automatizado provoca un tipo especial de estrés que resulta familiar para cualquier trader serio. Cuando gestionas un capital considerable y tu modelo empieza de repente a comportarse de forma irracional, te enfrentas a un doloroso dilema a cada minuto. Detener el sistema supone perder beneficios potenciales si el algoritmo acierta en sus incomprensibles cálculos. Seguir operando conlleva el riesgo de sufrir pérdidas catastróficas si algo sale mal.

Veamos un ejemplo real: el modelo LSTM para el EURUSD arrojó excelentes resultados durante seis meses seguidos. La rentabilidad media mensual fue del 2,3 %; el drawdown máximo no superó el 8 %. El sistema procesó correctamente tanto los movimientos de tendencia como las correcciones laterales. Sin embargo, el viernes a las 14:30 GMT, el modelo comenzó a abrir posiciones cortas agresivas contra el EUR, aunque todos los factores fundamentales y técnicos apuntaban a que el crecimiento continuaría.

En esos momentos, los traders se ven atrapados en una trampa: cada segundo de vacilación puede costar dinero, pero una decisión equivocada costará aún más. Lo más doloroso es que no hay forma de comprender qué es exactamente lo que lleva al modelo a tomar esas decisiones. Quizás el algoritmo haya detectado un patrón sutil en los datos que escapa al análisis humano. O tal vez el sistema simplemente se haya sobreajustado a artefactos históricos y ahora se esté comportando de forma inadecuada.

Entre los enfoques clásicos para resolver este problema se encuentran la incorporación de filtros adicionales, la aplicación de stop loss y la imposición de límites de volumen. Pero todas estas medidas solo ocultan el problema principal sin resolverlo. Los filtros pueden bloquear señales válidas, los stop loss actúan a posteriori y los límites de volumen reducen la rentabilidad potencial del sistema. La verdadera solución reside en comprender la lógica de la toma de decisiones.



Evolución de los enfoques de trading

La historia del desarrollo de los sistemas de trading refleja la evolución de la comprensión humana de los mercados financieros. En la década de los 80, el trading se basaba principalmente en la intuición, la experiencia y el análisis técnico clásico. Los traders se basaban en patrones gráficos, niveles de soporte y resistencia y noticias fundamentales. Este enfoque permitía comprender a la perfección cada decisión tomada, pero limitaba la capacidad de procesar grandes volúmenes de información.

Una década más tarde, aparecieron los primeros sistemas algorítmicos basados en reglas sencillas. La lógica clásica «si... entonces» permitía crear estrategias como esta: si el RSI supera el 70 y el MACD cruza por debajo de su línea de señal, abrir una posición corta. Dichos sistemas seguían siendo totalmente interpretables: cada decisión podía explicarse mediante un conjunto de condiciones específicas. Sin embargo, no podían hacer frente a las complejas dependencias no lineales de los datos de mercado.

La década de los 2000 supuso una revolución del aprendizaje automático en el sector financiero. De repente, pasó a ser posible procesar terabytes de datos históricos, identificar patrones ocultos y predecir los movimientos de precios con una precisión sin precedentes. Las máquinas de vectores de soporte permitieron encontrar hiperplanos de separación óptimos en un espacio de características multidimensional. Random Forest combinó cientos de árboles de decisión para generar previsiones fiables. Las redes neuronales modelaban dependencias no lineales complejas.

Pero esta potencia de cálculo llegó a costa de la comprensión. Los modelos se volvieron tan complejos que ni siquiera sus creadores podían explicar los principios en los que se basaba la toma de decisiones. El sistema de trading pasó de ser una herramienta comprensible a convertirse en una caja negra.

Los modelos modernos de aprendizaje profundo han llevado este problema hasta el punto de lo absurdo. Las arquitecturas Transformer con miles de millones de parámetros demuestran una precisión extraordinaria en el análisis de series temporales, pero su lógica interna sigue siendo incomprensible incluso para los propios desarrolladores. Los intentos de explicar sus decisiones mediante mapas de atención y métodos de gradiente solo aportan información superficial.



Solución: Anatomía del modelo simbólico

La idea fundamental es sencilla: entrenar un modelo de aprendizaje automático convencional y, a continuación, extraer una fórmula matemática a partir de él. Es como hacer una radiografía de una red neuronal y ver su estructura interna. Utilizamos regresión polinómica con regularización Ridge para predecir el precio y regresión logística para predecir la dirección. A continuación, SymPy convierte los coeficientes en ecuaciones simbólicas.

Lo primero que tienes que hacer es recopilar toda la información posible sobre cada barra. Empezamos con la clase base de nuestro predictor. Aquí definimos las variables simbólicas que constituirán la base de las fórmulas. Esto nos permite trabajar con abstracciones, como en las matemáticas puras.

import sympy as sp
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge, LogisticRegression
from sklearn.model_selection import cross_val_score, train_test_split

class SymbolicPricePredictor:
    def __init__(self, symbol: str = "EURUSD"):
        self.symbol = symbol
        self.prediction_horizon = 24  # Forecast for 24 hours
        self.lookback_bars = 10000   # Use 10,000 historical bars
        
        # Create symbolic variables for mathematical equations
        self.t = sp.Symbol('t', real=True)    # time
        self.p = sp.Symbol('p', real=True)    # price
        self.r = sp.Symbol('r', real=True)    # return
        self.vol = sp.Symbol('vol', real=True) # volatility
        self.rsi = sp.Symbol('rsi', real=True) # RSI
        self.macd = sp.Symbol('macd', real=True) # MACD
        
        print(f"Predictor for {symbol} initialized")
        print(f"Forecast horizon: {self.prediction_horizon} hours")

Aquí sentamos las bases: las variables simbólicas de SymPy se convertirán en los elementos fundamentales de nuestras futuras fórmulas. Cada variable es un símbolo matemático con el que se pueden realizar operaciones algebraicas. Esto difiere del enfoque numérico: en lugar de aproximaciones, obtenemos expresiones exactas que pueden derivarse, simplificarse y analizarse analíticamente.

La filosofía que subyace a las variables simbólicas es que no estamos modelando valores numéricos concretos en momentos concretos, sino conceptos abstractos del mercado. El RSI deja de ser simplemente un número comprendido entre 0 y 100 para convertirse en un objeto matemático capaz de interactuar con otras fuerzas del mercado mediante operaciones algebraicas. Esto nos permite crear modelos que expresan leyes universales del mercado, en lugar de limitarse a memorizar patrones históricos.



Creación de un universo de mercado multidimensional

El siguiente paso consiste en transformar velas simples en un espacio de características multidimensional. Este es el núcleo de nuestro enfoque. Calculamos indicadores para distintos periodos con el fin de captar las escalas temporales: desde el corto plazo (5 barras) hasta el largo plazo (50 barras). Esto permite que el modelo identifique patrones fractales similares a los descritos en la «Teoría del caos» de Bill Williams.

def calculate_technical_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
    data = df.copy()
    
    # Basic calculations are the foundation for everything else
    data['returns'] = data['close'].pct_change()
    data['log_returns'] = np.log(data['close'] / data['close'].shift(1))
    data['price_change'] = data['close'] - data['close'].shift(1)
    
    # Volatility over different periods - each period has its own patterns
    periods = [5, 10, 20, 50]
    for period in periods:
        data[f'volatility_{period}'] = data['returns'].rolling(period).std() * np.sqrt(24)
        # Realized volatility through logarithmic returns
        data[f'realized_vol_{period}'] = data['log_returns'].rolling(period).std() * np.sqrt(24)
    
    # RSI on different periods - different time horizons
    for period in [7, 14, 21]:
        data[f'rsi_{period}'] = self._calculate_rsi(data['close'], period)
    
    # Add MACD and Momentum for completeness
    data['macd'] = data['close'].ewm(span=12, adjust=False).mean() - data['close'].ewm(span=26, adjust=False).mean()
    data['momentum_10'] = data['close'] - data['close'].shift(10)
    
    # Fractal dimension - to assess chaos
    def fractal_dimension(series):
        n = len(series)
        lags = np.arange(1, min(10, n//2))
        scales = [np.std(np.diff(series, lag)) for lag in lags]
        return np.polyfit(np.log(lags), np.log(scales), 1)[0] * -1 + 1
    
    data['fractal_dim'] = data['close'].rolling(50).apply(fractal_dimension)
    
    return data

def _calculate_rsi(self, prices, period=14):
    """Classic RSI calculation with each step explained"""
    delta = prices.diff()  # Price changes
    gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()  # Average gain
    loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean() # Average loss
    rs = gain / loss  # Relative Strength
    rsi = 100 - (100 / (1 + rs))  # Classic RSI formula
    return rsi

Este código crea una representación multidimensional de cada barra. No nos limitamos a tomar el precio de cierre, sino que elaboramos un «retrato» completo de las condiciones del mercado utilizando decenas de indicadores. La dimensión fractal muestra el grado de «fragmentación» del gráfico: los valores bajos indican un movimiento tendencial, mientras que los valores altos indican un movimiento caótico. En la práctica, esto ayuda a filtrar las señales: en mercados caóticos (fractal > 1,5), reducimos el tamaño de las posiciones.

El cálculo de los indicadores en distintos periodos temporales se basa en la teoría del análisis de mercado a múltiples escalas. Este concepto, desarrollado en los trabajos de Benoit Mandelbrot y Edgar Peters, sostiene que los mercados financieros presentan una estructura fractal: patrones similares que se repiten en diferentes escalas temporales. Los indicadores a corto plazo reflejan la dinámica intradía y las reacciones ante las noticias; los indicadores a medio plazo muestran las tendencias locales y los ciclos del sentimiento del mercado; y los indicadores a largo plazo revelan las tendencias globales y los factores fundamentales.

Se presta especial atención a los aspectos psicológicos del comportamiento del mercado. Los indicadores de momentum reflejan las emociones colectivas de los participantes del mercado, la volatilidad refleja el nivel de miedo e incertidumbre, y los indicadores de volumen muestran el grado de confianza en las decisiones tomadas. La combinación de estas métricas ofrece una visión multidimensional del sentimiento del mercado que, a menudo, resulta más predictiva que los patrones puramente técnicos.



Alquimia polinómica: transformar lo lineal en lo no lineal

Ahora bien, lo más interesante es convertir las características en combinaciones polinómicas. Este es el núcleo de nuestro enfoque. La expansión polinómica permite que el modelo refleje las no linealidades, como las dependencias cuadráticas de la volatilidad o las interacciones entre indicadores.

from sklearn.preprocessing import PolynomialFeatures, StandardScaler

def prepare_features_and_targets(self, data: pd.DataFrame):
    """Create features and target variables for training"""
    
    # Select all numerical columns as potential features
    numeric_columns = data.select_dtypes(include=[np.number]).columns.tolist()
    
    # Exclude the main OHLCV columns - we leave only the derivative indicators
    feature_columns = [col for col in numeric_columns 
                      if col not in ['open', 'high', 'low', 'close', 'tick_volume']]
    
    # Create target variables - what we want to predict
    data['target_price'] = data['close'].shift(-self.prediction_horizon)
    data['target_return'] = (data['target_price'] / data['close'] - 1) * 100
    
    # Binary target: Will the price be higher than the current one in 24 hours?
    data['target_direction'] = (data['target_price'] > data['close']).astype(int)
    
    # Remove rows with missing values
    data_clean = data.dropna()
    
    if len(data_clean) < 100:
        print("Insufficient data after cleaning")
        return None, None
    
    # Construct feature and target matrices
    X = data_clean[feature_columns].values
    y_price = data_clean['target_price'].values
    y_direction = data_clean['target_direction'].values
    
    # Normalization of features is critical for stability 
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)
    
    # Polynomial expansion (degree 2 for nonlinearities)
    poly = PolynomialFeatures(degree=2, include_bias=True)
    X_poly = poly.fit_transform(X_scaled)
    
    poly_feature_names = poly.get_feature_names_out(feature_columns)
    
    print(f"Prepared features: {X_poly.shape[1]} (after polynomials)")
    print(f"Training samples: {len(data_clean)}")
    print(f"Balance of directions: {np.mean(y_direction)*100:.1f}% growth")
    
    return {
        'X': X_scaled,
        'X_poly': X_poly,
        'y_price': y_price, 
        'y_direction': y_direction,
        'feature_names': feature_columns,
        'poly_feature_names': poly_feature_names,
        'scaler': scaler,
        'poly_transformer': poly,
        'data_clean': data_clean
    }

La expansión polinómica es magia matemática. Si disponemos de dos indicadores sencillos, el RSI y el MACD, la expansión polinómica da como resultado: RSI, MACD, RSI², MACD², RSI × MACD. Cada combinación puede revelar dependencias no lineales ocultas en los datos. En el ámbito del trading, esto resulta especialmente útil para modelar efectos como la «sobrecompra» (RSI² con un coeficiente negativo).

Los términos cuadráticos representan los efectos de la saturación y la aceleración. Un término RSI² con coeficiente negativo refleja el clásico efecto de «banda elástica»: cuanto más en sobrecompra o sobreventa esté el precio, mayor será la probabilidad de una reversión. La volatilidad² muestra efectos no lineales durante los periodos de crisis, en los que pequeños cambios en la volatilidad tienen consecuencias desproporcionadamente grandes para el precio.

Los términos de interacción revelan efectos sinérgicos entre los indicadores. RSI × MACD muestra cómo las señales de momentum se refuerzan o se debilitan en función de las condiciones de la tendencia. Volatilidad × Volumen muestra cómo el volumen de trading amplifica la volatilidad en momentos críticos. Estas interacciones suelen contener la información más valiosa sobre la dinámica del mercado.

Matemáticamente, una expansión polinómica puede expresarse como una transición de una función lineal f(x₁, x₂, ..., xₙ) = Σαᵢxᵢ a una función polinómica f(x₁, x₂, ..., xₙ) = Σαᵢxᵢ + Σβᵢⱼxᵢxⱼ + Σγᵢxᵢ². Cada coeficiente α, β y γ tiene una interpretación clara y puede relacionarse con los conceptos económicos de momentum, reversión a la media y agrupamiento de la volatilidad.



El nacimiento de una ecuación simbólica

Ahora llega el momento clave: convertir el modelo entrenado en una fórmula matemática legible. Utilizamos Ridge para el precio y LogisticRegression para la dirección, y a continuación creamos expresiones simbólicas.

def create_symbolic_equations(self, features_data: dict):
    """Creating symbolic equations is the heart of the entire system"""
    
    X_poly = features_data['X_poly']
    y_price = features_data['y_price']
    y_direction = features_data['y_direction']
    feature_names = features_data['feature_names']
    poly_feature_names = features_data['poly_feature_names']
    
    print("Create symbolic equations...")
    
    # === MODEL FOR PRICE ===
    
    # Train Ridge regression with optimal regularization
    alphas = [0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
    best_alpha = 1.0
    best_score = -np.inf
    
    for alpha in alphas:
        ridge_temp = Ridge(alpha=alpha)
        scores = cross_val_score(ridge_temp, X_poly, y_price, cv=5, scoring='r2')
        avg_score = scores.mean()
        
        if avg_score > best_score:
            best_score = avg_score
            best_alpha = alpha
    
    print(f"Optimal regularization alpha: {best_alpha}")
    print(f"Cross-validation R²: {best_score:.4f}")
    
    # Train the final model
    ridge = Ridge(alpha=best_alpha)
    ridge.fit(X_poly, y_price)
    
    final_score = ridge.score(X_poly, y_price)
    print(f"Final R² models: {final_score:.4f}")
    
    # Create symbolic variables for each basic feature
    symbols = {}
    for i, name in enumerate(feature_names):
        clean_name = name.replace('-', '_').replace(' ', '_')
        symbols[f"x{i}"] = sp.Symbol(f"x{i}", real=True)
        print(f"x{i} = {name}")
    
    # Construct symbolic polynomial terms
    symbol_list = list(symbols.values())
    poly_terms = []
    
    # Constant (bias term)
    poly_terms.append(sp.S(1))
    
    # Linear terms: x₀, x₁, x₂, ...
    for sym in symbol_list:
        poly_terms.append(sym)
    
    # Quadratic terms: x₀², x₁², x₂², ...
    for sym in symbol_list:
        poly_terms.append(sym**2)
    
    # Interaction terms: x₀×x₁, x₀×x₂, x₁×x₂, ...
    for i in range(len(symbol_list)):
        for j in range(i+1, len(symbol_list)):
            poly_terms.append(symbol_list[i] * symbol_list[j])
    
    # Limit the number of terms to the number of coefficients
    if len(poly_terms) > len(ridge.coef_):
        poly_terms = poly_terms[:len(ridge.coef_)]
    
    print(f"Created symbolic terms: {len(poly_terms)}")
    
    # MAGIC: Create a symbolic equation from model coefficients
    price_equation = sp.S(ridge.intercept_)
    significant_terms = 0
    
    for coef, term in zip(ridge.coef_, poly_terms[1:]):  # Skip 'bias', it is already added
        if abs(coef) > 1e-6:  # Take into account only significant coefficients
            price_equation += coef * term
            significant_terms += 1
    
    print(f"Significant coefficients: {significant_terms}")
    
    # Simplify the equation for readability
    price_equation = sp.simplify(price_equation)
    
    # === MODEL FOR DIRECTION ===
    
    # Separate data for binary classification
    X_train, X_test, y_train, y_test = train_test_split(
        X_poly, y_direction, test_size=0.2, random_state=42, stratify=y_direction
    )
    
    # Select the optimal regularization parameter for logistic regression
    C_values = [0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
    best_C = 1.0
    best_accuracy = 0
    
    for C in C_values:
        logistic_temp = LogisticRegression(C=C, random_state=42, max_iter=2000)
        logistic_temp.fit(X_train, y_train)
        accuracy = logistic_temp.score(X_test, y_test)
        
        if accuracy > best_accuracy:
            best_accuracy = accuracy
            best_C = C
    
    print(f"Optimal regularization C: {best_C}")
    print(f"Accuracy of the binary model: {best_accuracy:.4f}")
    
    # Train the final binary model
    logistic = LogisticRegression(C=best_C, random_state=42, max_iter=2000)
    logistic.fit(X_train, y_train)
    
    # Create a symbolic formula for logistic regression
    # P(y=1) = 1 / (1 + exp(-z)), where z = linear combination
    
    linear_combination = sp.S(logistic.intercept_[0])
    binary_significant_terms = 0
    
    for coef, term in zip(logistic.coef_[0][1:], poly_terms[1:]):  # Skip 'bias'
        if abs(coef) > 1e-6:
            linear_combination += coef * term
            binary_significant_terms += 1
    
    print(f"Significant terms in the binary model: {binary_significant_terms}")
    
    # Complete logistic function
    binary_equation = 1 / (1 + sp.exp(-linear_combination))
    binary_equation = sp.simplify(binary_equation)
    
    # Save all components
    self.price_equation = price_equation
    self.binary_equation = binary_equation
    self.symbols = symbols
    self.feature_names = feature_names
    self.ridge_model = ridge
    self.logistic_model = logistic
    self.scaler = features_data['scaler']
    self.poly_transformer = features_data['poly_transformer']
    
    # Create functions for fast calculations
    self.price_function = sp.lambdify(list(symbols.values()), price_equation, 'numpy')
    self.binary_function = sp.lambdify(list(symbols.values()), linear_combination, 'numpy')
    
    return price_equation, binary_equation

Tomamos una regresión Ridge entrenada, que «conoce» los coeficientes óptimos, y la convertimos en una expresión matemática simbólica. El resultado es una fórmula como esta:

P(t+24) = 1.4523 + 0.003×x₀ - 0.127×x₁² + 0.045×x₀×x₂ + ...

donde x₀ puede ser el RSI, x₁ la volatilidad, x₂ el MACD, y así sucesivamente. Esto nos permite interpretar que un coeficiente positivo para el RSI significa que las condiciones de sobrecompra contribuyen al aumento del precio.

El proceso de extracción de ecuaciones simbólicas constituye la culminación de todo el enfoque. El algoritmo comienza con la creación de una base simbólica: para cada característica inicial, se crea una variable simbólica mediante SymPy. A continuación, se construye un espacio polinómico en forma simbólica, generando todas las combinaciones posibles de variables simbólicas hasta un grado determinado. El número de términos debe coincidir exactamente con el número de coeficientes del modelo entrenado.



Análisis matemático de ecuaciones simbólicas

Una vez creadas las ecuaciones, se puede llevar a cabo un análisis matemático completo. Esto incluye el recuento de términos, el cálculo de derivadas para la sensibilidad y el análisis de puntos críticos mediante la matriz hessiana. Este análisis permite evaluar la estabilidad del modelo.

def analyze_symbolic_equations(self):
    """ Mathematical analysis of the generated equations"""
    
    if self.price_equation is None:
        print("Equations are not created")
        return
    
    print("=== PRICE EQUATION ANALYSIS ===")
    
    # Calculate terms by type
    linear_terms = 0
    quadratic_terms = 0
    interaction_terms = 0
    
    for arg in self.price_equation.args:
        if arg.is_number:
            continue
        elif len(arg.free_symbols) == 1:
            if any(sym**2 in str(arg) for sym in arg.free_symbols):
                quadratic_terms += 1
            else:
                linear_terms += 1
        elif len(arg.free_symbols) == 2:
            interaction_terms += 1
    
    print(f"Linear terms: {linear_terms}")
    print(f"Quadratic terms: {quadratic_terms}")
    print(f"Interaction terms: {interaction_terms}")
    
    # Analysis of derivatives to find critical points
    print("\n=== SENSITIVITY ANALYSIS ===")
    
    for i, (var_name, symbol) in enumerate(self.symbols.items()):
        derivative = sp.diff(self.price_equation, symbol)
        simplified_derivative = sp.simplify(derivative)
        
        print(f"∂P/∂{var_name} = {simplified_derivative}")
        
        # Estimate of average sensitivity (at average values of variables)
        if i < 5:  # Show only the first 5 for brevity
            try:
                # Substitute zero values (normalized data)
                sensitivity = float(simplified_derivative.subs([(s, 0) for s in self.symbols.values()]))
                print(f"Base sensitivity: {sensitivity:.6f}")
            except:
                print("Sensitivity: A nonlinear relationship")

Este análisis convierte unos coeficientes áridos en una lógica económica comprensible. Las derivadas muestran cómo las variaciones de cada indicador afectan a la previsión. La matriz hessiana ayuda a identificar las zonas de estabilidad e inestabilidad del modelo. En la práctica, esto permitió identificar «puntos de silla» en los que el modelo es inestable y añadir regularización.

Hay dos fragmentos de código adjuntos al artículo: uno se limita a realizar cálculos utilizando características polinómicas cuadráticas, y el segundo es un poco más sencillo y guarda la siguiente visualización de las previsiones:

Además, en la salida del programa veremos la siguiente interpretación simbólica del modelo entrenado:

1. PRICE PREDICTION (Linear):
P(t+24) = 0.00121530760092578*x0 - 0.00545884362231391*x1 - 0.00303560059895232*x2 - 0.000244633802972733*x3 + 0.00635698036389421*x4 + 0.0012835119213099*x5

2. DIRECTION PROBABILITY (Logistic):
Prob(UP) = 1/(0.962357919929742*exp(0.0334844928240356*x0 - 0.0606659913615301*x1 - 0.213695382829857*x2 + 0.141596646627179*x3 + 0.0790330083995048*x4 + 0.085224001367088*x5) + 1)


Limitaciones de los modelos y líneas de desarrollo

Los modelos simbólicos en el trading aportan transparencia e interpretabilidad, pero presentan una serie de limitaciones. La expansión polinómica aumenta de forma explosiva el número de características y requiere regularización; las operaciones con expresiones suponen un gran coste computacional, y la regresión Ridge en entornos de alta dimensión puede resultar difícil de ajustar y ser sensible a la proliferación de características. Los modelos son sensibles al ruido y a los valores atípicos, por lo que requieren sistemas rigurosos de limpieza de datos y control de calidad. A medida que las ecuaciones se vuelven más complejas, la interpretabilidad disminuye y las estrategias habituales pierden eficacia a medida que el mercado se adapta. En el caso de los activos líquidos, estos modelos funcionan de forma más fiable que con los instrumentos exóticos.

Entre las líneas de investigación futuras se incluyen los híbridos cuántico-simbólicos para la optimización acelerada, los autoencoders para la simplificación de fórmulas, los sistemas multimodales con noticias y redes sociales, el aprendizaje federado sin transferencia de datos y los algoritmos genéticos para el descubrimiento automático de nuevas dependencias. En la práctica, se recomienda empezar con modelos sencillos en un único instrumento líquido, escalar gradualmente y controlar estrictamente los riesgos mediante límites, paradas y pruebas de estrés. Para el sector, los modelos simbólicos suponen menores barreras de entrada, una normativa simplificada y nuevas oportunidades formativas, pero también exigen transparencia, una comunicación sincera de las restricciones y la prevención del abuso de mercado.



Conclusión

Las ecuaciones simbólicas en el trading algorítmico permiten pasar de las «cajas negras» a modelos transparentes en los que las decisiones pueden entenderse y verificarse. Esto cambia la filosofía del trading: el trader recibe una fórmula en lugar de un algoritmo oculto, al tiempo que conserva el control y la capacidad analítica. El futuro reside en sistemas que combinen la potencia de cálculo de las máquinas con la claridad del entendimiento humano, lo que facilitará la comprensión del mercado.

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

HeAic
HeAic | 17 oct 2025 en 17:15
No acabo de entender de qué se trata, pero tiene buena pinta :) Lo ejecuto en Spyder (Python 3.13) desde la colección WinPython 3.13.50. Para obtener los datos en la terminal, hay que pulsar Ctrl+U.
Desarrollo de un sistema de ruptura basado en la volatilidad Desarrollo de un sistema de ruptura basado en la volatilidad
El sistema de ruptura basado en la volatilidad identifica los rangos del mercado y, a continuación, realiza operaciones cuando el precio rompe al alza o a la baja dichos niveles, aplicando un filtro basado en medidas de volatilidad como el ATR. Este enfoque permite aprovechar los movimientos direccionales marcados.
Simplificación de bases de datos en MQL5 (Parte 2): Uso de la metaprogramación para crear entidades Simplificación de bases de datos en MQL5 (Parte 2): Uso de la metaprogramación para crear entidades
Hoy hemos analizado el uso avanzado de #define para la metaprogramación en MQL5, creando entidades que representan tablas y metadatos de columnas (tipo, clave primaria, autoincremento, posibilidad de valores nulos, etc.). Además, hemos centralizado estas definiciones en el archivo TickORM.mqh, automatizando la generación de clases de metadatos y allanando el camino para una manipulación eficiente de los datos por parte del ORM, sin necesidad de escribir SQL manualmente.
Desarrollo de estrategias de trading con los osciladores Parafrac y Parafrac V2: análisis del rendimiento de una única entrada Desarrollo de estrategias de trading con los osciladores Parafrac y Parafrac V2: análisis del rendimiento de una única entrada
Este artículo presenta el oscilador ParaFrac y su modelo V2 como herramientas de negociación. En él se describen tres estrategias de negociación desarrolladas a partir de estos indicadores. Cada estrategia se sometió a pruebas y se optimizó para identificar sus puntos fuertes y débiles. El análisis comparativo puso de manifiesto las diferencias de rendimiento entre el oscilador Parafrac original y su modelo V2.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 41): Creación de un EA estadístico de niveles de precio en MQL5 Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 41): Creación de un EA estadístico de niveles de precio en MQL5
La estadística siempre ha sido un elemento fundamental del análisis financiero. Por definición, la estadística es la disciplina que recopila, analiza, interpreta y presenta datos de forma significativa. Ahora imagine aplicar ese mismo marco a las velas japonesas: condensar la acción del precio bruta en información cuantificable. ¿En qué medida resultaría útil conocer, para un periodo de tiempo concreto, la tendencia central, la dispersión y la distribución del comportamiento del mercado? En este artículo presentamos precisamente ese enfoque y mostramos cómo los métodos estadísticos pueden transformar los datos de las velas japonesas en señales claras y aplicables.