Descomposición en modos dinámicos aplicada a series temporales univariadas en MQL5
Introducción
La descomposición en modos dinámicos (DMD, por sus siglas en inglés) es una técnica utilizada para analizar sistemas dinámicos complejos. Los ingenieros la utilizan en el análisis del flujo de fluidos para extraer estructuras espaciotemporales de conjuntos de datos complejos. DMD funciona descomponiendo los datos de un sistema en representaciones más simples llamadas modos. Cada modo representa un patrón espacial distinto con su propia frecuencia de oscilación y una tasa de crecimiento o decaimiento. Esto permite a los ingenieros analizar la dinámica subyacente de un sistema de fluidos sin necesidad de resolver las ecuaciones que lo rigen, las cuales suelen ser difíciles.
Las estructuras espaciotemporales son patrones que mantienen su forma e identidad a lo largo del tiempo y el espacio. Imagina un anillo de humo flotando en el aire. El anillo de humo es una estructura que mantiene su forma y se mueve como una sola unidad, incluso cuando el aire a su alrededor se arremolina.

Los ingenieros utilizan técnicas como DMD para identificar y analizar dichas estructuras dentro de flujos de fluidos complejos. En este artículo exploramos la implementación de DMD en MetaTrader 5. Mediante demostraciones prácticas de código, los lectores aprenderán a utilizar el nuevo método matricial, DynamicModeDecomposition(). Analizaremos sus entradas, así como las utilidades de código esenciales necesarias para procesar su salida.
El algoritmo DMD
El algoritmo de descomposición en modos dinámicos fue desarrollado para el análisis de la dinámica de fluidos por Peter Schmid y Joern Sesterhenn. En esencia, DMD es una técnica de reducción de dimensionalidad que también puede identificar oscilaciones presentes en los datos. Es como un análisis de componentes principales combinado con una transformada de Fourier para la detección de señales. Una de las principales ventajas de este método es que no impone supuestos previos sobre el conjunto de datos estudiado. Funciona con series temporales no estacionarias, y las versiones modernas incluso pueden manejar datos recopilados en momentos no uniformes. El requisito principal para DMD es la estructura de los datos en sí: se recomienda que el conjunto de datos tenga una dimensionalidad suficientemente alta.
El método se basa en una serie de "instantáneas" de un sistema dinámico recopiladas a lo largo del tiempo. Estas instantáneas, que pueden ser cualquier dato relacionado con un sistema, están organizadas en una matriz donde cada columna corresponde a un momento específico.

Las columnas de esta matriz, a la que llamaremos X, son las instantáneas del sistema. En aplicaciones del mundo real, los conjuntos de datos DMD suelen ser de alta dimensionalidad, lo que significa que se recopilan cientos o incluso miles de variables medidas en cada momento. Esto da como resultado una matriz con más filas (variables o características) que columnas (instantáneas).

A partir de esta matriz X, obtenemos dos nuevas matrices, X1 y X2. En conjunto, estas dos matrices representan la progresión temporal del sistema dinámico. X1 contiene todas las columnas de X excepto la última, que representa el estado del sistema en los pasos de tiempo iniciales. Por el contrario, X2 contiene todas las columnas de X desde la segunda columna hasta la última, que representan el estado del sistema en los pasos de tiempo subsiguientes.

La base matemática de DMD es la relación supuesta entre las matrices X1 y X2, específicamente que existe una matriz A tal que AX1 puede aproximar suficientemente a X2. El objetivo de DMD es determinar la estructura espectral de A sin tener que calcular explícitamente la matriz en sí.
Para lograr esto, DMD utiliza un procedimiento similar al de Rayleigh-Ritz. Esta es una técnica estándar de álgebra lineal para aproximar los valores propios y los vectores propios de una matriz grande. Funciona proyectando el problema sobre un subespacio de menor dimensión, que se elige cuidadosamente en función de los datos. En DMD, este subespacio es una parte del espacio columna de la matriz de datos X1. Normalmente se construye utilizando los vectores singulares izquierdos principales de X1, que se obtienen mediante una descomposición en valores singulares (SVD).
El algoritmo forma una aproximación de A utilizando la matriz de datos X2 y los resultados de la SVD de X1. La truncación del rango se representa como k, a continuación.
Esta aproximación de la matriz del operador A es la matriz DMD o el cociente de Rayleigh. Los valores propios de esta matriz más pequeña se denominan valores de Ritz o valores propios DMD, y sus vectores propios correspondientes se utilizan para calcular los vectores de Ritz o los modos DMD proyectados.
Los valores y vectores de Ritz forman pares de Ritz, que representan la descomposición en modos dinámicos de un conjunto de datos. Los modos proporcionan una aproximación de bajo rango, basada en datos, de la dinámica del sistema.
La calidad de esta aproximación está directamente ligada a la elección del subespacio, que en DMD está intrínsecamente vinculado a los datos de las instantáneas. Los modos DMD describen patrones que evolucionan en el tiempo según los valores propios del DMD; es decir, los modos son las estructuras y los valores propios rigen cómo evolucionan esas estructuras en un momento determinado.
La evolución temporal de un modo puede visualizarse calculando su dinámica. Lo cual, a su vez, requiere el cálculo de la amplitud de dicho modo. Las amplitudes sirven como pesos o coeficientes iniciales, que muestran cuánto contribuye cada modo al estado inicial del sistema y a su evolución general. Un modo con una gran amplitud es una característica dominante de la dinámica del sistema, mientras que un modo con una pequeña amplitud tiene una menor influencia en el comportamiento. Al combinar los modos, los valores propios y las amplitudes, DMD puede construir una aproximación del sistema. Este modelo reconstruido también puede extrapolarse hacia el futuro para proporcionar pronósticos sobre el estado futuro del sistema.
Incrustación con retardo temporal
Más allá del algoritmo DMD clásico, existen otras variaciones, cada una desarrollada para adaptarse a tipos específicos de conjuntos de datos y casos de uso. La mayoría se diferencian del DMD clásico al incorporar procedimientos de preprocesamiento o postprocesamiento para mejorar el método principal. Por ejemplo, la incrustación con retardo temporal es una técnica que se utiliza para expandir el espacio de estados de un sistema, haciéndolo más adecuado para el análisis con DMD.
El método DMD estándar asume que la dinámica de un sistema puede aproximarse linealmente de una instantánea a la siguiente. Esta suposición puede fallar para sistemas con fuertes no linealidades o para datos con pocas variables espaciales (filas). Al incorporar observaciones pasadas al vector de estado actual, las coordenadas de retardo pueden desplegar dinámicas complejas. Esto permite que una aproximación lineal, como la utilizada en DMD, capture las características esenciales de un sistema no lineal o caótico. Esto implica que la aplicación de DMD puede ampliarse para incluir conjuntos de datos que quizás no tengan una dimensión suficientemente alta.
La idea es crear una matriz de datos aumentada. En lugar de utilizar una única instantánea en cada paso de tiempo, creamos un nuevo vector de "estado" que incluye la instantánea actual y varias instantáneas pasadas, cada una retrasada por un intervalo de tiempo o retardo específico. Esto es similar a la construcción de la matriz de trayectoria en el análisis de espectro singular. El código que aparece a continuación define una función, time_delay_embedding(), que se puede utilizar para aplicar una incrustación con retardo temporal a una matriz.
//+------------------------------------------------------------------+ //| pseudo hankelization | //+------------------------------------------------------------------+ matrix time_delay_embedding(matrix &data, ulong delay=2) { if(delay>=data.Cols() || delay<1) { Print(__FUNCTION__, " invalid input parameters "); return matrix::Zeros(0,0); } ulong rows = data.Rows(); matrix matrices; matrix out(rows*delay,data.Cols()-(delay-1)); for(ulong i = 0; i<delay; i++) { matrices = np::sliceMatrixCols(data,long(i),long(i+data.Cols()-(delay-1))); if(!np::matrixCopyRows(out,matrices,long(i*rows),long((i*rows)+matrices.Rows()))) { Print(__FUNCTION__, " failed copy operation "); return matrix::Zeros(0,0); } } return out;
La función time_delay_reconstruction() se puede utilizar en los resultados de una reconstrucción DMD o en pronósticos para un conjunto de datos preprocesado con incrustaciones de retardo de tiempo, lo que permite volver a la forma original del conjunto de datos.
//+------------------------------------------------------------------+ //| rebuild original shape of data after augmentation | //+------------------------------------------------------------------+ matrix time_delay_reconstruction(matrix &data, ulong delay) { ulong rows = data.Rows(); ulong cols = data.Cols(); if(rows<delay) { Print(__FUNCTION__," invalid inputs"); return data; } matrix out(rows/delay,cols + delay - 1); matrix splitted[]; ulong splits = data.Hsplit(delay,splitted); for(ulong i = 0; i<splits; i++) if(!np::matrixCopyCols(out,splitted[i],long(i),long(i+cols))) { Print(__FUNCTION__, " failed copy operation "); return data; } return out; }
Independientemente de la variante de DMD utilizada, todas producen en última instancia resultados similares: valores propios de DMD, modos de DMD y amplitudes de DMD. La clave del análisis DMD reside en la interpretación de estos resultados, específicamente de los valores propios del DMD. La mejor manera de demostrar cómo interpretar los resultados de DMD es con un ejemplo, y para ello necesitamos profundizar en el código.
DMD en MQL5
La creciente compatibilidad de MetaTrader con las rutinas OpenBLAS ha dado lugar a la introducción de dos métodos que implementan DMD. El primero, DynamicModeDecomposition(), implementa el algoritmo DMD estándar basado en SVD, mientras que DynamicModeDecompositionQR() utiliza una implementación basada en la factorización QR. Este texto se centrará en la versión basada en la descomposición en valores singulares (SVD). Analizaremos el método mientras descomponemos una serie artificial descrita en el siguiente fragmento de código.
//+------------------------------------------------------------------+ //| univariate process | //+------------------------------------------------------------------+ vector f(vector &in) { return cos(in)*sin(cos(in))*cos(in*0.2); }
El código describe una serie que es el producto de tres componentes oscilatorios distintos, lo que da como resultado una onda compleja con amplitud y frecuencia variables.
La combinación de los tres componentes crea un patrón complejo que es periódico pero no una simple armónica. Las oscilaciones rápidas de cos(x)*sin(cos(x)) se 'estiran' y 'comprimen' por el término más lento cos(0.2*x), lo que da lugar a la intrincada forma de onda que se muestra en el gráfico. La descomposición de esta serie se realiza en el script DMD_Demo.mq5. El proceso comienza por preparar la serie para su descomposición, colocándola en un contenedor adecuado.
//+------------------------------------------------------------------+ //| DMD_Demo.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //--- #include<np.mqh> #include<cmath.mqh> //--- input ENUM_DMD_SCALE jobs = DMDSCALE_N; input ENUM_DMD_EIGV jobz = DMDEIGV_V; input ENUM_DMD_RESIDUALS jobr = DMDRESIDUALS_R; input ENUM_DMD_REFINE jobf = DMDREFINE_R; input ENUM_SVD_ALG whtsvd = SVDALG_1; input long nrnk = -1; input double tol_ = 1.e-9;//tol input ulong Delay = 50;//apply time delay embedding //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- ulong series_len = 100; double dt = 0.1010101; double first_time_point = 0.0; vector t = np::arange(series_len,first_time_point,dt); vector F = f(t); matrix X(1,series_len); X.Row(F,0);
El método DynamicModeDecomposition() de MQL5 es un método matricial que devuelve un valor booleano para indicar si la descomposición se realizó correctamente o no. Al revisar la documentación, se observa que el método requiere varios datos de entrada, algunos de los cuales deben seleccionarse cuidadosamente, ya que influyen significativamente en los resultados finales. A continuación se presenta una descripción de los parámetros del método DynamicModeDecomposition().
//--- if(Delay) X = time_delay_embedding(X,Delay); //--- Print("X shape is ", X.Rows(),":",X.Cols()); //--- matrix X1 = np::sliceMatrixCols(X,0,X.Cols()-1); matrix X2 = np::sliceMatrixCols(X,1); //--- vectorc eigen_values; matrix W; matrix B; vector residuals; matrix left_vectors; matrix res_vectors; matrix Z,S; if(!X1.DynamicModeDecomposition(X2,jobs,jobz,jobr,jobf,whtsvd,nrnk,tol_,eigen_values,left_vectors,Z,residuals,res_vectors,B,W,S)) { Print(" DMD error ",GetLastError()); return;
La función DynamicModeDecomposition(), que comienza con las propias matrices, requiere dos matrices como entrada. La primera será la matriz de instantáneas iniciales, sobre la cual se invoca el método. La matriz de instantáneas sucesivas se pasa al método como primer parámetro. El único requisito para las matrices es que tengan una dimensión suficientemente alta. El método espera matrices "altas" o "delgadas". Si alguna de las matrices tiene más columnas que filas, se producirá un error durante la ejecución. En lo que respecta a la aplicación de una incrustación con retardo de tiempo, el resultado debe ser una matriz aumentada con una forma adecuada. La dimensión de la incrustación determina el número de filas de la matriz aumentada.
Considerando nuestro ejemplo, que al transformarse en una matriz tiene una sola fila y 100 columnas o instantáneas. Al aplicar una incrustación con retardo de tiempo de dimensión 50, se crea una matriz aumentada con 50 filas (número original de filas × dimensión de incrustación) y 51 columnas (número original de columnas - dimensión de incrustación + 1). Seleccionar una incrustación de retardo de tiempo adecuada supone un compromiso entre aumentar el espacio de estados y garantizar que quede un número suficiente de instantáneas para un análisis eficaz.
input ENUM_DMD_SCALE jobs = DMDSCALE_NEl segundo parámetro de DynamicModeDecomposition() es una enumeración que especifica un paso de preprocesamiento integrado. Permite seleccionar un procedimiento de escalado que se aplicará a los datos. Hay cuatro opciones, incluida la opción sin escalado. Al llevar todas las columnas a una escala similar, es menos probable que el algoritmo se vea dominado por unas pocas columnas con normas muy grandes, lo que puede conducir a un cálculo más equilibrado y numéricamente estable. Esto garantiza que cada instantánea contribuya por igual a la dinámica general, evitando que los resultados estén sesgados hacia unos pocos eventos de gran magnitud. Por lo tanto, el algoritmo puede identificar modos que sean relevantes a lo largo de toda la serie temporal, incluso aquellos asociados con fenómenos de menor escala.
input ENUM_SVD_ALG whtsvd = SVDALG_1;El parámetro whtsvd es una enumeración ENUM_SVD_ALG que representa cuatro implementaciones SVD que un usuario puede especificar para el procedimiento DMD. Las implementaciones de la descomposición en valores singulares (SVD) varían en cuanto a su precisión y pueden afectar a los resultados finales.
input long nrnk = -1;
El parámetro nrnk se puede utilizar para definir explícitamente el subespacio columna, que será la base del modelo reducido. Se puede especificar como un valor positivo que debe ser menor o igual al número de columnas del conjunto de datos. Alternativamente, este parámetro puede establecerse en -1 o -2, lo que aplica un enfoque basado en datos para la truncación de rangos, con la ayuda del parámetro tol.
input double tol_ = 1.e-9;//tol
La tolerancia es un umbral impuesto a los valores singulares de la operación SVD para determinar el rango óptimo del conjunto de datos. El resto de los parámetros de entrada del método se refieren a los resultados de la operación. Los analizaremos a medida que examinemos los resultados de la descomposición. Una vez definidos todos los parámetros, podemos ejecutar el programa y observar los resultados, comenzando con los valores propios del DMD.
Análisis de valores propios
Los valores propios de DMD, o valores de Ritz, se devuelven en el parámetro vectorial eigen_values. Los valores propios determinan el comportamiento temporal del sistema. Son números complejos que codifican la tendencia general de un modo correspondiente. El número de valores propios es equivalente al rango del modelo de orden reducido calculado por DMD. La parte imaginaria de un valor propio representa la frecuencia de una oscilación, mientras que la parte real define la estabilidad del modo.
La estabilidad de un modo se puede inferir evaluando la magnitud de su valor propio:
- Magnitud > 1: El modo es inestable y crece con el tiempo.
- Magnitud < 1: El modo se considera estable, aunque haya indicios de decaimiento.
- Magnitud = 1: El modo es estable y oscila sin crecimiento ni decaimiento. Esto es característico de un comportamiento puramente oscilatorio o en estado estacionario.
Otra forma de evaluar la estabilidad de los modos es mediante una gráfica de los valores de Ritz en el plano complejo. El objetivo de esta visualización es determinar dónde se encuentran los valores propios en relación con el círculo unitario. Lo que buscamos son modos que sean bastante estables, ya que esto indica estructuras o patrones consistentes. Los modos estables tienen valores propios que se sitúan más o menos sobre el círculo unitario. Aquí se muestra un gráfico de los valores propios para nuestro ejemplo.
En lo que respecta a las frecuencias, las oscilaciones de valor real se capturan como pares complejos conjugados de valores propios. Una única exponencial compleja no puede describir datos reales, que son una combinación de senos y cosenos. Según la fórmula de Euler, una función de valor real requiere un par de exponenciales complejas, razón por la cual DMD produce pares conjugados. Al observar nuestro conjunto de datos sintéticos, la función tiene múltiples componentes periódicas. Por lo tanto, se espera que el DMD genere múltiples pares de valores propios complejos conjugados que correspondan a estas oscilaciones. La parte imaginaria de los valores propios también puede utilizarse para caracterizar los componentes que representan los modos respectivos. Los valores imaginarios cercanos a cero representan tendencias o componentes de variación lenta.
IN 0 20:48:41.719 DMD_univariate (AUDUSD,D1) DMD eigenvalues or Ritz values NF 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.5094085277495803,0.8361736631055409) MR 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.5094085277495803,-0.8361736631055409) LH 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9997963823571733,0.02020000684364618) IG 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9997963823571733,-0.02020000684364618) CQ 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9835155147230954,0.1808172385894571) DN 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9835155147230954,-0.1808172385894571) RG 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9754097172453285,0.2203989043734001) QS 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9754097172453285,-0.2203989043734001) LL 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9272204223699192,0.3744766566706951) CE 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9272204223699192,-0.3744766566706951) MR 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9113505757240357,0.4116425317268657) FO 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.9113505757240357,-0.4116425317268657) LH 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.8329207590629218,0.5528761024511968) KP 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.8329207590629218,-0.5528761024511968) KN 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.810278335748567,0.5863411580059696) NI 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.810278335748567,-0.5863411580059696) CP 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.6775746857433339,0.740515480806191) JL 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.6775746857433339,-0.740515480806191) JH 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.6991117585413807,0.7090540641602167) QQ 0 20:48:41.719 DMD_univariate (AUDUSD,D1) (0.6991117585413807,-0.7090540641602167) CM 0 20:48:41.719 DMD_univariate (AUDUSD,D1) Eigenvalues shape - (20,)
La frecuencia de un modo oscilante solo se puede determinar si se conoce el intervalo entre los momentos de recopilación de datos de las instantáneas. Esto es algo que no se destaca en el método DynamicModeDecomposition(). El intervalo de tiempo entre instantáneas sucesivas es importante porque DMD calcula un operador de tiempo discreto, y el tiempo entre instantáneas de datos actúa como el puente que conecta los resultados discretos con la dinámica subyacente de tiempo continuo del sistema. Sin ello, no podemos traducir con precisión los valores propios de tiempo discreto en frecuencias de tiempo continuo y tasas de crecimiento/decrecimiento significativas. Las frecuencias y las tasas de crecimiento o decaimiento se calculan utilizando el tiempo transcurrido entre las instantáneas, con las siguientes fórmulas.
Los vectores singulares de la izquierda
La matriz devuelta en el parámetro left_vectors contiene todos los vectores singulares izquierdos de la SVD de las instantáneas iniciales en la matriz X1. Las columnas de la matriz left_vectors se conocen como los modos de descomposición ortogonal propia (POD) del conjunto de datos. Este término se utiliza en el ámbito de la ingeniería y es conceptualmente similar a la noción de Componentes Principales en estadística.
Left singular vectors -> left_vectors [[-0.2186571002130553,0.2532378192042484,-0.03145683319876429,-0.08784983133100939,0.3705873208513075,0.2206528031484838,0.1522505585095688,0.2508786634158028,0.4005453375455179,-0.2070914249820868,-0.256938973254042,0.1083123043478309,0.3743749436717269,-0.08325285604985129,0.2153162921311084,0.03514527602276374,0.2660964556173894,-0.02288502160553232,0.08420865568250724,-0.1244960454833032,-0.05915206418948179,0.1412740898453582,-0.06378450251683798,0.05225051446648973,0.04596520897629693,0.05533900119077329,-0.03035343487604242,0.01632904421933207,0.02488201223142909,0.009443994581533457,0.003233076671905151,-0.007000002498680975,-0.0003763343596269273,-0.002249960643275939,0.006083378336102527,0.002738028494735405,-0.003071412428518359,0.007630930463007241,0.0006780866248171729,-0.001537236018891231,0.001097628276266037,0.002549189417918068,-0.00121555935468654,0.0002914617225681344,-0.001329425380550676,-0.0008417462519557183,-0.0001925572219431676,0.0006672631970817051,1.658425254616608e-07,-0.003517246244801298] ,[-0.2120650337138182,0.2460711108749398,0.02614922665855949,-0.06095346519256405,0.316287819867838,0.1367332022924505,0.1608350411867044,0.08971817028514953,0.1591329520755127,0.016649392674401,-0.02435104895770137,-0.1166535735451681,-0.1350117043919009,0.1534878122543588,-0.2129032230923289,0.07705593536128043,-0.375021818402949,-0.07706813579203413,-0.1050339399444373,0.2860161516006371,0.08475158498594149,-0.3855235413551042,0.1862630616541108,-0.1991183017470892,-0.1509536720131326,-0.2375913171609213,0.1371371152602869,-0.0898699217072978,-0.1292195766965235,-0.06648667026234195,-0.01812981549697532,0.0417051043959161,0.009525456359058087,0.01011521692378288,-0.03803922672356531,-0.02106190474509607,0.01766810797476536,-0.04822406797849837,-0.003057210631146539,0.006231539022859201,-0.006288330102253714,-0.0156751476701437,0.009093777375705452,-0.003891906142160671,0.01113869460673238,0.005700478320541056,0.001323281173233992,-0.004963029289243439,0.002210166681645755,0.01885563684292829] ,[-0.2050754583292416,0.2276848333084614,0.08120826878905353,-0.0401725018668373,0.255713699260657,0.06031724967005543,0.1260872837922681,-0.05233055144856189,-0.02895470981613132,0.1414817752150981,0.1496548155010341,-0.1633919339675757,-0.2909072792943354,0.1170695606024898,-0.2104477264601786,-0.05761666740975601,-0.1642196356337907,0.09376106622320569,-0.09917019230808037,-0.0512314209072473,0.07533085329520638,0.2045397680225845,-0.1172476831676734,0.2373535055615748,0.1327391899523122,0.363322598165184,-0.2353759964165236,0.2001199525018012,0.2797917856739792,0.2087875622138012,0.03391328469051902,-0.1093628961879368,-0.04980659874835306,-0.009841138497083401,0.1071536601377198,0.07721338502591252,-0.04348696114962958,0.1359693579381065,0.005776723545380768,-0.00329638274325811,0.01197007184135679,0.04141926298403621,-0.03188888672341144,0.02029703016126579,-0.0411903406233533,-0.02103491458905859,-0.004064246582866178,0.01736432751085308,-0.01540381295312496,-0.04519633129179929] ,[-0.197864304332741,0.198939547987894,0.1313736616978487,-0.02558290784786304,0.1925995771445337,-0.006134276320507245,0.05852103373207649,-0.1566416078904816,-0.1489006796224137,0.1767605998850408,0.2187235587134117,-0.08102546015612813,-0.2025179789413045,-0.02467023236462294,0.007177707826474537,-0.1394599646494139,0.1774528338997285,0.1254318761473469,0.06991185536803274,-0.2322392434345509,-0.1022804195006251,0.2595084087385101,-0.1385151209344889,0.02231602330043558,0.08496345787013258,-0.1181672772615679,0.1251167503291135,-0.1942278207520568,-0.2798025229501522,-0.3656598349104803,0.01139657187065844,0.1552588902748942,0.1253960619854343,-0.03868294575932402,-0.1774709498310883,-0.172412195179344,0.05203395600508887,-0.2094389089818375,-0.004270301693629558,-0.03143829332941872,0.006497267040059443,-0.05389031613821053,0.06861563190685697,-0.0601060573479536,0.08699077151177441,0.05297098886227182,0.005414702197319015,-0.03549882813964893,0.05199258221374336,0.05778558739229585…] Left singular vectors shape - (50,50)
Modos DMD
Los modos DMD proyectados, o vectores Ritz, se devuelven en la matriz de salida Z. El parámetro jobz es una enumeración ENUM_DMD_EIGV que se utiliza para controlar la salida proporcionada en Z. Con él, los usuarios pueden optar por no calcular los modos si no son necesarios. Observando los modos obtenidos de la descomposición de nuestro conjunto de datos sintéticos. Los lectores perspicaces notarán que el parámetro Z es una matriz real, aunque los valores propios correspondientes sean complejos. ¿Cómo puede ser esto? Resulta que los modos son, de hecho, complejos. El método DynamicModeDecomposition() devuelve los valores complejos en formato compacto.
KN 0 20:54:02.688 DMD_univariate (AUDUSD,D1) Z MJ 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [[-0.2115833899347683,0.04066707935807452,0.1309479910169292,-0.05340607539953211,-0.1311407627226479,-0.05294578413949149,0.1307327748372062,-0.05393554251234634,0.1300831102003118,0.05561343290756637,0.05476143012438625,0.1303708466035617,-0.0602971275669671,0.1290171409159042,0.1325833779653352,-0.0474567460410165,0.03953679964167294,0.1224969410523646,-0.08648845700023616,0.1302991571594397] KL 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [-0.1410082298469801,-0.1561422002553993,0.1320001302525303,-0.05075005112167052,-0.1194054633670081,-0.07578550974758559,0.1394053542006602,-0.02379589291884476,0.09978976936668775,0.1002790127266047,-0.003759313795174276,0.1413556653516757,-0.1215537937113269,0.07412474322962503,0.1352548790995219,0.03928642939266112,-0.06394467904383667,0.1123039293425674,-0.1528368724559976,0.02980086078332629] KE 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.0571288895384227,-0.1979205129165305,0.1329984050115342,-0.0480733131719782,-0.1037338006863208,-0.09612679258491226,0.1412219243613748,0.007514043825130007,0.05497499123596727,0.1303496653103515,-0.06161407873237246,0.1272770978516157,-0.1422252875452086,-0.00546499036287681,0.08655973711477287,0.1111375957285514,-0.1264360522083525,0.02869970692204134,-0.1280036213687715,-0.0876039346033588] QM 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.1946438003924811,-0.0523269950779746,0.1339424057467836,-0.04537695566367969,-0.08464242182804624,-0.1132990516514234,0.1360931500665304,0.03845442944292374,0.002161029940774177,0.1414497110787759,-0.1085446923168975,0.09063109173113748,-0.1154409995132384,-0.08318535501859128,0.004972784147358847,0.1408055296621761,-0.1069381563322531,-0.07420079524911123,-0.02739821515064329,-0.1519997996523627] MD 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.1442714745004026,0.1362363903857534,0.1348317466067521,-0.04266207939022894,-0.06276071186776834,-0.1267361823950489,0.1242712682729774,0.06750360361706979,-0.05096588923209126,0.1319643397659119,-0.1362298611687498,0.03791506074287562,-0.0501630100812688,-0.133110472474709,-0.07853144874233632,0.1170082970122646,-0.01755245167388933,-0.1294221063050899,0.08865106243357559,-0.1256351416263412] FO 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [-0.03992077677928203,0.1893519332890936,0.1356660661019982,-0.03992979099864888,-0.03881004602829696,-0.1359952192100831,0.10633768314148,0.09323292091368418,-0.09667419061156554,0.1032745163734511,-0.139760607057441,-0.02152411345851591,0.0318114520997212,-0.1386035419997053,-0.1322392470005959,0.04876382240238289,0.08394555153666447,-0.1006538267573124,0.1510967437027885,-0.02495855075802432] IE 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [-0.1796559181414814,0.06272269832359235,0.1364450246963949,-0.03718120464898366,-0.01358000327434823,-0.1407709344456745,0.08317437492830956,0.1143770068564059,-0.1283121621102718,0.05955600024366348,-0.1185106827077816,-0.07714740932760773,0.103127741913013,-0.09785844890997622,-0.1357422735154563,-0.03802553162801222,0.1314496877308521,-0.006061975670165805,0.123318478664932,0.08964429318551075] ON 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [-0.145052625469143,-0.1177739752477081,0.1371683036826087,-0.03441744261256562,0.01209766591051641,-0.140905898452734,0.05592052522418876,0.1298959869971385,-0.1412759661384557,0.007171600964583176,-0.07624763897835488,-0.1190923437906344,0.1400016038285632,-0.02449262903763641,-0.08769262768666558,-0.1104036026020508,0.09357636250498216,0.09318079023848276,0.02260518070773477,0.1500697490161283] OD 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.02477722278466908,-0.1806550663349361,0.1378356065274133,-0.03163963382212003,0.03737645720044752,-0.1363956708335061,0.02591649044609354,0.1390266308270787,-0.1336795590605116,-0.04625490379734653,-0.02046485321417844,-0.1399216409042799,0.1301514645240822,0.05700280745582626,-0.006321464342295452,-0.1408756808015346,-0.00561461203492375,0.1324202843066922,-0.09062222999370595,0.1209556314953582] FO 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.1648424157584578,-0.07139540484283648,0.1384466607105591,-0.02884891198589165,0.06142301561190965,-0.1273889490658437,-0.005362119258359672,0.141319891110143,-0.1066290580133119,-0.09294834217094237,0.03894705870314489,-0.1359418964516609,0.0768895241650633,0.1194373735899043,0.0774784937512529,-0.1178541799925052,-0.1018942505428861,0.08561003773138477,-0.149086741625562,0.0203522730386399…] ID 0 20:54:02.688 DMD_univariate (AUDUSD,D1) Z shape - (50,20)
Para cada modo (una columna en Z), si el valor propio correspondiente tiene una parte imaginaria distinta de cero, los valores imaginarios para ese modo se encuentran en la siguiente columna de Z. Esto también implica la existencia de otro vector propio que es el conjugado complejo del vector propio complejo resultante. En caso contrario, si el valor propio no tiene parte imaginaria, la columna adyacente está relacionada con otro valor propio. El código que aparece a continuación define una función de utilidad, compact_to_complex(), que toma como entrada contenedores con números reales (números complejos en forma compacta) y un vector de números complejos que actúa como clave para decodificar la forma verdadera de los números complejos. La función devuelve un contenedor con los números complejos reales.
//+------------------------------------------------------------------+ //|process vector representing complex numbers in compact form | //+------------------------------------------------------------------+ vectorc compact_to_complex(vector& v,vectorc& bp) { vectorc v_out = vectorc::Zeros(v.Size()); for(ulong i = 0; i<v.Size() && i<bp.Size();) { if(bp[i].imag) { v_out[i].real = v[i]; if(i+1 < v.Size()) { v_out[i].imag = v[i+1]; v_out[i+1].real = v_out[i].real; v_out[i+1].imag = -1.0*v_out[i].imag; i+=2; } } else { v_out[i].real = v[i]; i+=1; } } return v_out; } //+------------------------------------------------------------------+ //| process matrix representing complex numbers in compact form | //+------------------------------------------------------------------+ matrixc compact_to_complex(matrix& m, vectorc& bp) { matrixc m_out = matrixc::Zeros(m.Rows(), m.Cols()); for(ulong i = 0; i<m.Rows(); i++) { vector row = m.Row(i); vectorc row_c = compact_to_complex(row,bp); m_out.Row(row_c,i); } return m_out; }
A continuación, podemos ver la diferencia entre los modos en forma compacta y su verdadera forma compleja.
CF 0 20:54:02.688 DMD_univariate (AUDUSD,D1) DMD modes or Ritz Vectors -> Z PS 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [[(-0.2115833899347683,0.04066707935807452),(-0.2115833899347683,-0.04066707935807452),(0.1309479910169292,-0.05340607539953211),(0.1309479910169292,0.05340607539953211),(-0.1311407627226479,-0.05294578413949149),(-0.1311407627226479,0.05294578413949149),(0.1307327748372062,-0.05393554251234634),(0.1307327748372062,0.05393554251234634),(0.1300831102003118,0.05561343290756637),(0.1300831102003118,-0.05561343290756637),(0.05476143012438625,0.1303708466035617),(0.05476143012438625,-0.1303708466035617),(-0.0602971275669671,0.1290171409159042),(-0.0602971275669671,-0.1290171409159042),(0.1325833779653352,-0.0474567460410165),(0.1325833779653352,0.0474567460410165),(0.03953679964167294,0.1224969410523646),(0.03953679964167294,-0.1224969410523646),(-0.08648845700023616,0.1302991571594397),(-0.08648845700023616,-0.1302991571594397)] OO 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [(-0.1410082298469801,-0.1561422002553993),(-0.1410082298469801,0.1561422002553993),(0.1320001302525303,-0.05075005112167052),(0.1320001302525303,0.05075005112167052),(-0.1194054633670081,-0.07578550974758559),(-0.1194054633670081,0.07578550974758559),(0.1394053542006602,-0.02379589291884476),(0.1394053542006602,0.02379589291884476),(0.09978976936668775,0.1002790127266047),(0.09978976936668775,-0.1002790127266047),(-0.003759313795174276,0.1413556653516757),(-0.003759313795174276,-0.1413556653516757),(-0.1215537937113269,0.07412474322962503),(-0.1215537937113269,-0.07412474322962503),(0.1352548790995219,0.03928642939266112),(0.1352548790995219,-0.03928642939266112),(-0.06394467904383667,0.1123039293425674),(-0.06394467904383667,-0.1123039293425674),(-0.1528368724559976,0.02980086078332629),(-0.1528368724559976,-0.02980086078332629)] MN 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [(0.0571288895384227,-0.1979205129165305),(0.0571288895384227,0.1979205129165305),(0.1329984050115342,-0.0480733131719782),(0.1329984050115342,0.0480733131719782),(-0.1037338006863208,-0.09612679258491226),(-0.1037338006863208,0.09612679258491226),(0.1412219243613748,0.007514043825130007),(0.1412219243613748,-0.007514043825130007),(0.05497499123596727,0.1303496653103515),(0.05497499123596727,-0.1303496653103515),(-0.06161407873237246,0.1272770978516157),(-0.06161407873237246,-0.1272770978516157),(-0.1422252875452086,-0.00546499036287681),(-0.1422252875452086,0.00546499036287681),(0.08655973711477287,0.1111375957285514),(0.08655973711477287,-0.1111375957285514),(-0.1264360522083525,0.02869970692204134),(-0.1264360522083525,-0.02869970692204134),(-0.1280036213687715,-0.0876039346033588),(-0.1280036213687715,0.0876039346033588)] OI 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [(0.1946438003924811,-0.0523269950779746),(0.1946438003924811,0.0523269950779746),(0.1339424057467836,-0.04537695566367969),(0.1339424057467836,0.04537695566367969),(-0.08464242182804624,-0.1132990516514234),(-0.08464242182804624,0.1132990516514234),(0.1360931500665304,0.03845442944292374),(0.1360931500665304,-0.03845442944292374),(0.002161029940774177,0.1414497110787759),(0.002161029940774177,-0.1414497110787759),(-0.1085446923168975,0.09063109173113748),(-0.1085446923168975,-0.09063109173113748),(-0.1154409995132384,-0.08318535501859128),(-0.1154409995132384,0.08318535501859128),(0.004972784147358847,0.1408055296621761),(0.004972784147358847,-0.1408055296621761),(-0.1069381563322531,-0.07420079524911123),(-0.1069381563322531,0.07420079524911123),(-0.02739821515064329,-0.1519997996523627),(-0.02739821515064329,0.1519997996523627)] CH 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [(0.1442714745004026,0.1362363903857534),(0.1442714745004026,-0.1362363903857534),(0.1348317466067521,-0.04266207939022894),(0.1348317466067521,0.04266207939022894),(-0.06276071186776834,-0.1267361823950489),(-0.06276071186776834,0.1267361823950489),(0.1242712682729774,0.06750360361706979),(0.1242712682729774,-0.06750360361706979),(-0.05096588923209126,0.1319643397659119),(-0.05096588923209126,-0.1319643397659119),(-0.1362298611687498,0.03791506074287562),(-0.1362298611687498,-0.03791506074287562),(-0.0501630100812688,-0.133110472474709),(-0.0501630100812688,0.133110472474709),(-0.07853144874233632,0.1170082970122646),(-0.07853144874233632,-0.1170082970122646),…] KG 0 20:54:02.688 DMD_univariate (AUDUSD,D1) DMD modes or Ritz Vectors shape (50,20)
Los residuos
Los dos siguientes parámetros de salida, residuos y vectores de residuos, están relacionados. Cada valor en el vector de residuos es la norma euclidiana de un vector (columna) correspondiente en la matriz res_vectors. El vector residual para cada instantánea de datos es la diferencia entre la instantánea real y la instantánea predicha por el modelo DMD. Es el vector de error para un paso de tiempo determinado. Las normas de los vectores residuales se utilizan habitualmente como medida cuantitativa del error. En términos generales, cuanto más cerca estén de cero, mejor será el modelo.
CD 0 20:54:02.689 DMD_univariate (AUDUSD,D1) Residual norms -> residuals FO 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0.008836676346169272,0.008836676346169272,9.235187494765224e-09,9.235187494765224e-09,1.742579533919419e-08,1.742579533919419e-08,1.653443663139743e-08,1.653443663139743e-08,2.597847624528849e-07,2.597847624528849e-07,2.320987987120818e-07,2.320987987120818e-07,9.788834713137558e-06,9.788834713137558e-06,8.123609086522145e-06,8.123609086522145e-06,0.0004010758671648529,0.0004010758671648529,0.0004371025994576595,0.0004371025994576595] EN 0 20:54:02.689 DMD_univariate (AUDUSD,D1) Residuals shape - (20,)
El parámetro de salida residuals es un vector de números reales, mientras que res_vectors es una matriz de números complejos en formato compacto.
CD 0 20:54:02.690 DMD_univariate (AUDUSD,D1) Residual vectors -> res_vectors OR 0 20:54:02.690 DMD_univariate (AUDUSD,D1) [[(0.0007788944957199118,6.210241253423732E-05),(0.0007788944957199118,-6.210241253423732E-05),(-5.316966866786288E-10,-4.560266955722092E-10),(-5.316966866786288E-10,4.560266955722092E-10),(9.00277352666734E-10,9.748073598325746E-10),(9.00277352666734E-10,-9.748073598325746E-10),(7.854966954656817E-10,-9.872156113421848E-10),(7.854966954656817E-10,9.872156113421848E-10),(-1.459522849800443E-08,1.377941803715199E-08),(-1.459522849800443E-08,-1.377941803715199E-08),(1.069276902784799E-08,-1.449544934084557E-08),(1.069276902784799E-08,1.449544934084557E-08),(-5.704307382975449E-07,5.291619083608312E-07),(-5.704307382975449E-07,-5.291619083608312E-07),(-4.031761563494385E-07,5.112246791380559E-07),(-4.031761563494385E-07,-5.112246791380559E-07),(-2.293253034875431E-05,2.549073482446818E-05),(-2.293253034875431E-05,-2.549073482446818E-05),(1.737165621398806E-05,3.217988337436348E-05),(1.737165621398806E-05,-3.217988337436348E-05)] PH 0 20:54:02.690 DMD_univariate (AUDUSD,D1) [(-0.001602312423460747,-0.0004729759518786458),(-0.001602312423460747,0.0004729759518786458),(9.344054696658333E-10,8.094462253249723E-10),(9.344054696658333E-10,-8.094462253249723E-10),(-1.519821102302643E-09,-1.798446391809705E-09),(-1.519821102302643E-09,1.798446391809705E-09),(-1.489875889326697E-09,1.679088970293896E-09),(-1.489875889326697E-09,-1.679088970293896E-09),(2.855568843884715E-08,-2.24272293236627E-08),(2.855568843884715E-08,2.24272293236627E-08),(-2.198151384524838E-08,2.429971562856181E-08),(-2.198151384524838E-08,-2.429971562856181E-08),(1.189711697907603E-06,-8.400539607544832E-07),(1.189711697907603E-06,8.400539607544832E-07),(8.892800195531292E-07,-8.493346006915869E-07),(8.892800195531292E-07,8.493346006915869E-07),(5.40418365579387E-05,-4.256796091403961E-05),(5.40418365579387E-05,4.256796091403961E-05),(-2.314522858359869E-05,-6.846121833149754E-05),(-2.314522858359869E-05,6.846121833149754E-05)] FG 0 20:54:02.690 DMD_univariate (AUDUSD,D1) [(4.593660886884066E-05,0.0007257275432220045),(4.593660886884066E-05,-0.0007257275432220045),(3.019065830667245E-10,2.421602848801108E-10),(3.019065830667245E-10,-2.421602848801108E-10),(-6.415215442201472E-10,-3.751031429910512E-10),(-6.415215442201472E-10,3.751031429910512E-10),(-2.171667012884626E-10,6.773338626087089E-10),(-2.171667012884626E-10,-6.773338626087089E-10),(2.210737475895341E-09,-1.156460674445192E-08),(2.210737475895341E-09,1.156460674445192E-08),(5.990476170669723E-10,1.068699011230745E-08),(5.990476170669723E-10,-1.068699011230745E-08),(-6.762525454895307E-08,-4.884491409673508E-07),(-6.762525454895307E-08,4.884491409673508E-07),(-1.490076587543424E-07,-3.929403302738166E-07),(-1.490076587543424E-07,3.929403302738166E-07),(-1.571073502396048E-05,-1.913611580105223E-05),(-1.571073502396048E-05,1.913611580105223E-05),(-2.530414087616173E-05,6.629116600515017E-06),(-2.530414087616173E-05,-6.629116600515017E-06)] DS 0 20:54:02.690 DMD_univariate (AUDUSD,D1) [(0.001363806542495044,0.0001361882900650091),(0.001363806542495044,-0.0001361882900650091),(-9.180490534443919E-10,-7.880395988535405E-10),(-9.180490534443919E-10,7.880395988535405E-10),(1.549437189662939E-09,1.690010908994566E-09),(1.549437189662939E-09,-1.690010908994566E-09),(1.36508163106619E-09,-1.700071097787692E-09),(1.36508163106619E-09,1.700071097787692E-09),(-2.543471129545782E-08,2.364815934741138E-08),(-2.543471129545782E-08,-2.364815934741138E-08),(1.871941607278771E-08,-2.493395216685013E-08),(1.871941607278771E-08,2.493395216685013E-08),(-1.000003475026823E-06,9.064440836814569E-07),(-1.000003475026823E-06,-9.064440836814569E-07),(-7.106895980740768E-07,8.787577454316686E-07),(-7.106895980740768E-07,-8.787577454316686E-07),(-4.07015728396895E-05,4.38344900164922E-05),(-4.07015728396895E-05,-4.38344900164922E-05),(2.940113570619463E-05,5.652142258574799E-05),(2.940113570619463E-05,-5.652142258574799E-05)] OK 0 20:54:02.690 DMD_univariate (AUDUSD,D1) [(0.0005033857628483629,-0.0006840539757191899),(0.0005033857628483629,0.0006840539757191899),(-6.779790617805759E-10,-5.646610123921647E-10),(-6.779790617805759E-10,5.646610123921647E-10),(1.278677420890606E-09,1.06406300437456E-09),(1.278677420890606E-09,-1.06406300437456E-09),…] PG 0 20:54:02.690 DMD_univariate (AUDUSD,D1) OJ 0 20:54:02.690 DMD_univariate (AUDUSD,D1) Residual vectors shape - (50,20)
El parámetro de enumeración ENUM_DMD_RESIDUALS, jobr, ofrece a los usuarios la opción de no calcular los residuos si no son necesarios.
Refinamiento de Ritz y modos DMD exactos
El parámetro jobf es una enumeración ENUM_DMD_REFINE que determina la salida de la matriz B.- Cuando jobf es DMDREFINE_N, la matriz B estará vacía.
- Si jobf se establece en DMDREFINE_E, B contendrá los modos DMD exactos como números complejos en formato compacto. La distinción entre los modos DMD exactos y los modos DMD proyectados radica en cómo se relacionan con el operador lineal subyacente que aproxima la dinámica del sistema. Si bien producen los mismos valores propios, sus estructuras espaciales (los modos en sí) difieren. Los modos DMD proyectados son el resultado de una proyección sobre un conjunto de vectores base, lo que subraya que son aproximaciones, no los verdaderos autovectores del operador lineal de mejor ajuste. La forma en que se calculan los modos DMD exactos es un enfoque alternativo para resolver el mismo problema. Este método omite el paso de proyección y encuentra directamente los vectores propios del operador lineal. Los modos resultantes se consideran exactos porque son los verdaderos vectores propios de la aproximación lineal óptima del sistema. Dado que no están obligados a estar dentro del rango de las instantáneas de datos iniciales.
- Si jobf es DMDREFINE_R, la matriz B devolverá valores que se pueden utilizar en un procedimiento de postprocesamiento llamado refinamiento de Ritz. Recordemos que los pares de Ritz se calculan a partir de una aproximación del operador lineal A. El refinamiento de Ritz es una forma de mejorar aún más los vectores de Ritz. La matriz B contendrá los valores necesarios para minimizar los residuos iniciales. El refinamiento de Ritz queda fuera del alcance de este artículo, pero es un paso que resulta muy útil para mejorar las previsiones.
El cociente de Rayleigh
El parámetro de salida W es otra matriz con valores complejos en forma compacta.
DQ 0 20:54:02.689 DMD_univariate (AUDUSD,D1) DMD matrix eigenvectors - W CQ 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [[(0.03397841954159109,0.03317980055719008),(0.03397841954159109,-0.03317980055719008),(-0.9002396504998079,0),(-0.9002396504998079,-0),(0.01119040053117125,0.1182840538877034),(0.01119040053117125,-0.1182840538877034),(-0.08933258155433688,-0.07445561873978643),(-0.08933258155433688,0.07445561873978643),(0.002252288602000291,-0.07268070968789277),(0.002252288602000291,0.07268070968789277),(0.06363962546273101,-0.06242908185135146),(0.06363962546273101,0.06242908185135146),(0.05937239512593095,0.01490455626039135),(0.05937239512593095,-0.01490455626039135),(-0.02589857176991502,-0.03197379959572326),(-0.02589857176991502,0.03197379959572326),(0.03403147435273887,-0.02518975096744712),(0.03403147435273887,0.02518975096744712),(0.04814500369424651,0.0006646006481806285),(0.04814500369424651,-0.0006646006481806285)] LK 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [(-0.03856865140041035,-0.04728745684412225),(-0.03856865140041035,0.04728745684412225),(-0.07418687400224905,-0.02671782636983063),(-0.07418687400224905,0.02671782636983063),(-0.6278925369875541,0),(-0.6278925369875541,-0),(0.6611296439031247,0),(0.6611296439031247,-0),(-0.01948319607116129,0.1638582051901045),(-0.01948319607116129,-0.1638582051901045),(-0.1160509917189585,0.04684553196764583),(-0.1160509917189585,-0.04684553196764583),(-0.06080699466607752,-0.007252509160005485),(-0.06080699466607752,0.007252509160005485),(0.04824148800646633,0.0544753020238296),(0.04824148800646633,-0.0544753020238296),(-0.02631424783289801,0.02396317634823539),(-0.02631424783289801,-0.02396317634823539),(-0.05371125803952446,-0.01772350219292688),(-0.05371125803952446,0.01772350219292688)] JS 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [(0.01927996329670138,-0.003323809768950481),(0.01927996329670138,0.003323809768950481),(-0.001092132447284393,-0.08813408560005521),(-0.001092132447284393,0.08813408560005521),(-0.01686362521399225,-0.6191005830078983),(-0.01686362521399225,0.6191005830078983),(0.02637345886634726,0.5944648032143058),(0.02637345886634726,-0.5944648032143058),(-0.1052128591626567,-0.008385210070686162),(-0.1052128591626567,0.008385210070686162),(-0.004565995041448841,-0.09830093201273357),(-0.004565995041448841,0.09830093201273357),(0.02484135400580122,-0.004386892506558514),(0.02484135400580122,0.004386892506558514),(-0.01604018998961423,0.01817361621091497),(-0.01604018998961423,-0.01817361621091497),(0.01513765754343997,-0.01442045062996068),(0.01513765754343997,0.01442045062996068),(0.02301961366729926,-0.02281949313848668),(0.02301961366729926,0.02281949313848668)] DQ 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [(0.01349178032453495,0.04031036542801018),(0.01349178032453495,-0.04031036542801018),(0.2800915230663114,0.2610589921860959),(0.2800915230663114,-0.2610589921860959),(0.07750855775646105,-0.2950535943034701),(0.07750855775646105,0.2950535943034701),(-0.2337105682364486,0.0040858569402831),(-0.2337105682364486,-0.0040858569402831),(0.06293226732327413,-0.1508577239678987),(0.06293226732327413,0.1508577239678987),(-0.03484731631897012,0.1006710384526284),(-0.03484731631897012,-0.1006710384526284),(-0.01073717194453397,-0.1026561241299194),(-0.01073717194453397,0.1026561241299194),(-0.08143509356008606,-0.07823035735671989),(-0.08143509356008606,0.07823035735671989),(-0.06786249180652332,-0.01131117739979555),(-0.06786249180652332,0.01131117739979555),(-0.01459517002905221,0.04968638335424509),(-0.01459517002905221,-0.04968638335424509)] DS 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [(-0.06312195367435448,-0.05788238216619907),(-0.06312195367435448,0.05788238216619907),(-0.106179393196619,-0.1067792237431208),(-0.106179393196619,0.1067792237431208),(0.2228105336536818,-0.09464986022387956),(0.2228105336536818,0.09464986022387956),(-0.1223565828897724,-0.2665717534473687),(-0.1223565828897724,0.2665717534473687),(0.0407625323894802,0.0608679763109657),(0.0407625323894802,-0.0608679763109657),(-0.0236347933639726,0.2167362736925585),(-0.0236347933639726,-0.2167362736925585),(-0.1397842805702028,0.002686549243340094),(-0.1397842805702028,-0.002686549243340094),(0.0494448535458599,0.07175076524193082),(0.0494448535458599,-0.07175076524193082),(-0.05089933082548433,0.06350343732496355),…] LP 0 20:54:02.689 DMD_univariate (AUDUSD,D1) FK 0 20:54:02.689 DMD_univariate (AUDUSD,D1) W shape - (20,20)
Contiene los vectores propios de la matriz resultante de la evaluación del cociente de Rayleigh. La matriz de salida S se utiliza para almacenar el resultado del cociente de Rayleigh correspondiente. El cociente de Rayleigh es una expresión que, en este contexto, tiene la siguiente forma.
El resultado es una matriz cuadrada que se utiliza como aproximación del operador lineal A.
RF 0 20:54:02.688 DMD_univariate (AUDUSD,D1) DMD matrix or Rayleigh Quotient -> S HR 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [[0.5094085277495803,-0.8604610082897919,0.0002006138033779966,0.04760027059805147,-0.002803306343440372,-0.0391415882078795,-0.0003134843372287616,0.07893996206869613,0.001757225544053047,0.05891514022091245,0.1021418500890567,0.0154878531271508,0.08161025402145326,-0.01322727684398736,0.05289807915719862,0.1366031199179701,0.09067443894366256,0.02387635894976379,-0.1337617534319445,0.3158438887995204] GG 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0.8125718517577054,0.5094085277495803,0.03212818037482736,0.07731373949333104,-0.004523461571676836,-0.06365727084560829,0.07166331416570983,0.1149560436517001,0.0417897026770658,0.0807703163043338,0.1168290328074077,-0.07784523729378179,0.08134266010864513,0.0457586690177342,-0.05281322289935021,0.1041166865025129,0.06016908821880863,-0.02262883866060445,0.01864963663274376,0.1592958144288718] QM 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0,0,0.9997963823571733,-0.06841114291664024,0.003677630227929817,0.05134906476950822,0.001010343646915456,-0.1036712695688504,-0.001982116115745312,-0.07741467176450138,-0.1344061700387433,-0.02117211346836873,-0.1074879052755059,0.01790950142871103,-0.07054528189524574,-0.180179648794732,-0.1196714016226133,-0.0318305136919558,0.177441519638216,-0.4172515456746314] PO 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0,0,0.005964529447791189,0.9997963823571733,-6.130430193796907e-05,0.0001608255296355939,-0.1417177910594132,0.02603215499459065,-0.07645245784783392,0.02929171223870932,0.09632863544056425,0.2022679146534209,0.1005224694321643,-0.1320544694433864,0.2724705351116244,0.2311524902397419,0.1709822160686499,0.1206265129749707,-0.4633910086326531,0.6944803173283216] RG 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0,0,0,0,0.9835155147230954,-0.1807872789366,-0.01287650437961382,0.002273054758487322,-0.006948726541217937,0.002592615695004415,0.008633127046368962,0.01836065518926932,0.009038166070419178,-0.01198352209799287,0.02469606203936868,0.02084314801005234,0.01542969758433841,0.01093290292806937,-0.04194970752430172,0.06273377057840418] OE 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0,0,0,0,0.1808472032071591,0.9835155147230954,0.1161226589750725,-0.02159149420945333,0.06263828964805583,-0.02419610245484403,-0.07926824162748283,-0.1657867613281642,-0.08263670791881386,0.1082473436056935,-0.2234334894897005,-0.1898550808858993,-0.1404004467922538,-0.09891860736477874,0.3801384435040523,-0.5700922000360884] QE 0 20:54:02.688 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0.9754097172453285,-0.3671559385031988,-0.005735330959573934,-0.1752131166062669,-0.3034665524406584,-0.0447757385532624,-0.2423096107440454,0.03850758043656835,-0.1556886673590776,-0.4051646386927485,-0.2688190825258632,-0.07028522559611267,0.3951927856107862,-0.935685263814825] ES 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0.1323025776105538,0.9754097172453285,-0.1277498034867074,0.01635682016747238,0.1038905065841927,0.3269719154788018,0.122074385755231,-0.211766280182049,0.4229381295746578,0.3086378423896532,0.2339698855185775,0.1869911903713443,-0.6953365989256011,0.9790102802912404] EJ 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0.9272204223699192,-0.4253756556605945,-0.1675761806365016,-0.0322689202475039,-0.1347517961361585,0.0260709867621285,-0.09491983678538948,-0.2278476057705441,-0.1518792160120434,-0.04275596295515771,0.2317849265989918,-0.532635511016381] FM 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0.3296680581625781,0.9272204223699192,0.05633171550556969,0.2408424337338481,0.07416894629162182,-0.1553200191742876,0.3047043287502109,0.2020029040523848,0.1558084075499993,0.1346159453957157,-0.4912332208887085,0.6651615829764952] HG 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0,0,0.9113505757240357,-0.2568972073981867,0.05055669367035209,-0.2566052078814798,0.4776439687989247,0.2195895146754944,0.1834173442058648,0.2105372198999828,-0.7236933009079901,0.8511605081274156] JD 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0,0,0.6596006848134373,0.9113505757240357,0.3140920520023247,-0.01324336135550369,0.1361348333013918,0.5052672912884386,0.3296699228616658,0.06220880832355302,-0.4176612853765882,1.116102030438296] JE 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0,0,0,0,0.8329207590629218,0.4624722437908844,0.355960803548531,0.1072881843791952,0.1013740688581597,0.1566216030021757,-0.5124168716630659,0.5231145883642944] LP 0 20:54:02.689 DMD_univariate (AUDUSD,D1) [0,0,0,0,0,0,0,0,0,0,0,0,-0.6609520652656545,0.8329207590629218,-0.07009147383110947,-0.3140620624168415,-0.2035214647743208,-0.03229751959115882…] NH 0 20:54:02.689 DMD_univariate (AUDUSD,D1) S shape - (20,20)
Con esto concluye la discusión sobre los parámetros de DynamicModeDecomposition(). A continuación, demostraremos cómo combinar estos resultados para la reconstrucción, el filtrado y la predicción. Pero antes de hacerlo, debemos abordar el problema del soporte limitado de MQL5 para números complejos. Varias funciones integradas no funcionan con argumentos complejos, por lo que tenemos que implementar nuestras propias utilidades. Para ello, presentamos la biblioteca cmath, definida en cmath.mqh.
Trabajar con números complejos en MQL5
El archivo cmath.mqh contiene definiciones de diversas utilidades para trabajar con números complejos. Disponemos de funciones para extraer las partes imaginaria y real de matrices, vectores y arreglos complejos.
//+------------------------------------------------------------------+ //| get real part from container of complex numbers | //+------------------------------------------------------------------+ void real(complex& data[],double& out[]) { ArrayResize(out,data.Size()); for(ulong i = 0; i<out.Size(); i++) out[i] = data[i].real; } //+-------------------------------------------------------------------+ //| get imaginary part from container of complex numbers | //+-------------------------------------------------------------------+ void imaginary(complex& data[],double& out[]) { ArrayResize(out,data.Size()); for(ulong i = 0; i<out.Size(); i++) out[i] = data[i].imag; } //+------------------------------------------------------------------+ //| get real part from container of complex numbers | //+------------------------------------------------------------------+ vector real(vectorc& data) { vector out=vector::Zeros(data.Size()); for(ulong i = 0; i<out.Size(); i++) out[i] = data[i].real; return out; } //+-------------------------------------------------------------------+ //| get imaginary part from container of complex numbers | //+-------------------------------------------------------------------+ vector imaginary(vectorc& data) { vector out=vector::Zeros(data.Size()); for(ulong i = 0; i<out.Size(); i++) out[i] = data[i].imag; return out; } //+------------------------------------------------------------------+ //| get real part from container of complex numbers | //+------------------------------------------------------------------+ matrix real(matrixc& data) { matrix out=matrix::Zeros(data.Rows(),data.Cols()); for(ulong i = 0; i<out.Rows(); i++) for(ulong j = 0; j<out.Cols(); j++) out[i,j] = data[i,j].real; return out; } //+-------------------------------------------------------------------+ //| get imaginary part from container of complex numbers | //+-------------------------------------------------------------------+ matrix imaginary(matrixc& data) { matrix out=matrix::Zeros(data.Rows(),data.Cols()); for(ulong i = 0; i<out.Rows(); i++) for(ulong j = 0; j<out.Cols(); j++) out[i,j] = data[i,j].imag; return out; }
También existen rutinas para convertir contenedores de números reales en números complejos y viceversa.
//+------------------------------------------------------------------+ //| create complex containers from real numbers | //+------------------------------------------------------------------+ vectorc as_complex(vector& re,vector& im) { vectorc out = vectorc::Zeros(MathMax(re.Size(),im.Size())); for(ulong i = 0; i<out.Size(); i++) { out[i].real = i<re.Size()?re[i]:0.0; out[i].imag = i<im.Size()?im[i]:0.0; } return out; } //+------------------------------------------------------------------+ //| create complex containers from real numbers | //+------------------------------------------------------------------+ matrixc as_complex(matrix& re,matrix& im) { matrixc out = matrixc::Zeros((ulong)MathMax(re.Rows(),im.Rows()),(ulong)MathMax(re.Cols(),im.Cols())); for(ulong i = 0; i<out.Rows(); i++) { for(ulong j = 0; j<out.Cols(); j++) { out[i,j].real = (i<re.Rows() && j<re.Cols())?re[i,j]:0.0; out[i,j].imag = (i<im.Rows() && j<im.Cols())?im[i,j]:0.0; } } return out; } //+------------------------------------------------------------------+ //| create complex containers from real numbers | //+------------------------------------------------------------------+ vectorc as_complex(vector& re) { vectorc out = vectorc::Zeros(re.Size()); for(ulong i = 0; i<out.Size(); i++) { out[i].real = re[i]; out[i].imag = 0.0; } return out; } //+------------------------------------------------------------------+ //| create complex containers from real numbers | //+------------------------------------------------------------------+ matrixc as_complex(matrix& re) { matrixc out = matrixc::Zeros(re.Rows(),re.Cols()); for(ulong i = 0; i<out.Rows(); i++) { for(ulong j = 0; j<out.Cols(); j++) { out[i,j].real = re[i,j]; out[i,j].imag = 0.0; } } return out;
Por último, la biblioteca incluye implementaciones para el exponente, la potencia, el valor absoluto y el logaritmo natural de números complejos y sus contenedores.
//+------------------------------------------------------------------+ //| power of complex number | //+------------------------------------------------------------------+ complex c_pow(complex base, complex exponent) { return c_exp(exponent*c_log(base)); } //+------------------------------------------------------------------+ //| simple power of complex number | //+------------------------------------------------------------------+ vectorc c_pow(vectorc &vect, complex exponent) { vectorc out(vect.Size()); for(ulong i = 0; i<vect.Size(); i++) out[i] = c_pow(vect[i],exponent); return out; } //+------------------------------------------------------------------+ //| simple power of complex number | //+------------------------------------------------------------------+ matrixc c_pow(matrixc &matrx, complex exponent) { matrixc out(matrx.Rows(),matrx.Cols()); for(ulong i = 0; i<out.Cols(); i++) out.Col(c_pow(matrx.Col(i),exponent),i); return out; } //+------------------------------------------------------------------+ //| power of complex number | //+------------------------------------------------------------------+ complex c_pow(complex value, double alpha) { complex out = value; double r = c_abs(value); double theta = atan2(value.imag,value.real); double n_r = pow(r,alpha); double n_theta = alpha*theta; out.real = n_r*cos(n_theta); out.imag = n_r*sin(n_theta); return out; } //+------------------------------------------------------------------+ //| simple power of complex number | //+------------------------------------------------------------------+ vectorc c_pow(vectorc &vect, double alpha) { vectorc out(vect.Size()); for(ulong i = 0; i<vect.Size(); i++) out[i] = c_pow(vect[i],alpha); return out; } //+------------------------------------------------------------------+ //| simple power of complex number | //+------------------------------------------------------------------+ matrixc c_pow(matrixc &matrx, double alpha) { matrixc out(matrx.Rows(),matrx.Cols()); for(ulong i = 0; i<out.Cols(); i++) out.Col(c_pow(matrx.Col(i),alpha),i); return out; } //+------------------------------------------------------------------+ //| Complex Signum Function | //+------------------------------------------------------------------+ complex signum(complex z) { double magnitude = sqrt((z.real*z.real) + (z.imag*z.imag)); if(!magnitude) return 0+0i; complex magz; magz.real = magnitude; magz.imag = 0.0; return z/magz; } //+------------------------------------------------------------------+ //| absolute value of a complex number (magnitude) | //+------------------------------------------------------------------+ double c_abs(complex z) { return sqrt((z.real*z.real) + (z.imag*z.imag)); } //+------------------------------------------------------------------+ //| absolute value of a complex number (magnitude) | //+------------------------------------------------------------------+ vector c_abs(vectorc &z) { CComplexVector v(z); return sqrt((v.re*v.re) + (v.im*v.im)); } //+------------------------------------------------------------------+ //| absolute value of a complex number (magnitude) | //+------------------------------------------------------------------+ matrix abs(matrixc &z) { CComplexMatrix v(z); return sqrt((v.re*v.re) + (v.im*v.im)); } //+------------------------------------------------------------------+ //| sqrt of complex number | //+------------------------------------------------------------------+ complex c_sqrt(complex z) { double mag_z = c_abs(z); mag_z=sqrt(mag_z); double theta = atan2(z.imag,z.real); complex out; out.real = mag_z*cos(theta/2.0); out.imag = mag_z*sin(theta/2.0); return out; } //+------------------------------------------------------------------+ //| sqrt of complex vector | //+------------------------------------------------------------------+ vectorc c_sqrt(vectorc &z) { vectorc out = z; for(ulong i = 0; i<out.Size(); i++) out[i] = c_sqrt(z[i]); return out; } //+------------------------------------------------------------------+ //| sqrt of complex vector | //+------------------------------------------------------------------+ matrixc c_sqrt(matrixc &z) { matrixc out = z; for(ulong i = 0; i<out.Cols(); i++) out.Col(c_sqrt(z.Col(i)),i); return out; } //+------------------------------------------------------------------+ //| log of complex number | //+------------------------------------------------------------------+ complex c_log(complex z) { complex out; out.real = c_abs(z); out.imag = atan2(z.imag,z.real); return out; } //+------------------------------------------------------------------+ //| log of complex number | //+------------------------------------------------------------------+ vectorc c_log(vectorc &z) { vectorc out = z; for(ulong i = 0; i<out.Size(); i++) out[i] = c_log(z[i]); return out; } //+------------------------------------------------------------------+ //| log of complex number | //+------------------------------------------------------------------+ matrixc c_log(matrixc &z) { matrixc out = z; for(ulong i = 0; i<out.Cols(); i++) out.Col(c_log(z.Col(i)),i); return out; } //+------------------------------------------------------------------+ //| exp function of complex number | //+------------------------------------------------------------------+ complex c_exp(complex z) { complex out; out.real = exp(z.real)*cos(z.imag); out.imag = exp(z.real)*sin(z.imag); return out; } //+------------------------------------------------------------------+ //| exp function of complex number | //+------------------------------------------------------------------+ vectorc c_exp(vectorc &z) { vectorc out = z; for(ulong i = 0; i<out.Size(); i++) out[i] = c_exp(z[i]); return out; } //+------------------------------------------------------------------+ //| exp function of complex number | //+------------------------------------------------------------------+ matrixc c_exp(matrixc &z) { matrixc out = z; for(ulong i = 0; i<out.Cols(); i++) out.Col(c_exp(z.Col(i)),i); return out; }
Amplitudes y dinámica modal
La reconstrucción en DMD consiste en la reproducción de los datos de entrada utilizando únicamente los modos deducidos mediante la descomposición. El filtrado con DMD consiste en la reconstrucción de los datos de entrada utilizando únicamente una selección de modos. La previsión simplemente extrapola una reconstrucción hacia el futuro. Para realizar cualquiera de estas operaciones, necesitamos calcular las amplitudes y la dinámica correspondientes de los modos. Las amplitudes no son un resultado directo del método DynamicModeDecomposition(); deben calcularse en un paso separado ajustando los modos a una instantánea. Esto se suele hacer mediante un ajuste por mínimos cuadrados o calculando la pseudoinversa de los modos.
Para encontrar la solución a un problema de mínimos cuadrados para una matriz compleja y no simétrica, podemos utilizar la descomposición LU de las ecuaciones normales. Las ecuaciones normales transforman el sistema sobredeterminado en un sistema cuadrado resoluble. Esto se puede lograr con utilidades de la biblioteca Alglib, como se muestra en el siguiente fragmento de código.
//+------------------------------------------------------------------+ //| compute the DMD amplitudes | //+------------------------------------------------------------------+ vectorc compute_amplitudes(matrix& snapshots, matrixc& modes) { vectorc amplitudes(0); vectorc x0 = as_complex(snapshots.Col(0)); matrixc A_h = modes.TransposeConjugate(); matrixc B = np::matmul(A_h,modes); vectorc c = np::matmul(A_h,x0); complex right_side[], solution[]; int pivots[]; CMatrixComplex lua = B; if(!np::vecAsArray(c,right_side)) { Print(__FUNCTION__, " vector as array failure "); return amplitudes; } vector check_pivots; ResetLastError(); CAlglib::CMatrixLU(lua,lua.Rows(),lua.Cols(),pivots); int info; check_pivots.Assign(pivots); if(check_pivots.Max() == check_pivots.Min()) { Print(__FUNCTION__," CAlglib::CMatrixLU() failure ", GetLastError()); return amplitudes; } CDenseSolverReportShell shell; CAlglib::CMatrixLUSolve(lua,pivots,int(pivots.Size()),right_side,info,shell,solution); if(info < 1) { Print(__FUNCTION__, " modes matrix is singular, or VERY close to singular. ", GetLastError()); return amplitudes; } amplitudes.Assign(solution); return amplitudes;
El código define una función que calcula las amplitudes a partir de una matriz de instantáneas y los modos. Tenga en cuenta que en este ejemplo se utiliza la primera instantánea, pero es posible utilizar una instantánea de cualquier momento. La solución al ajuste por mínimos cuadrados son las amplitudes, que son complejas. Las amplitudes también nos permiten calcular los modos dominantes, tal como los revela el procedimiento DMD. Lo hacemos calculando la magnitud de las amplitudes. Podemos utilizar esta información para filtrar.
//--- vector abs_dmd_amplitudes = c_abs(dmd_amplitudes); //--- long amp_indices[]; if(!np::arange(amp_indices,int(abs_dmd_amplitudes.Size())) || !np::quickSortIndices(abs_dmd_amplitudes,false,amp_indices,amp_indices[0],amp_indices[amp_indices.Size()-1])) { Print(" error "); return; } //---
Con las amplitudes en mano, podemos calcular la dinámica de los modos. La matriz de dinámica DMD es una matriz de Vandermonde donde cada fila representa la evolución temporal de un modo. El cálculo requiere los puntos de tiempo discretos de las instantáneas y los valores propios del DMD. Los valores propios se organizan en vectores columna y se elevan a la potencia de un punto temporal discreto en el conjunto de datos. Finalmente, cada vector columna se multiplica por las amplitudes. La función compute_dynamics() calcula la matriz de dinámica DMD, dados las amplitudes, los valores de Ritz y un vector de los puntos temporales de las instantáneas.
//+------------------------------------------------------------------+ //| calculate the mode dynamics | //+------------------------------------------------------------------+ matrixc compute_dynamics(vectorc& amplitudes,vectorc& eigs, vector& timesteps) { matrixc mat_eigs = np::repeat_vector_as_rows_cols(eigs,timesteps.Size(),false); vector tpow = (timesteps - timesteps[0])/(timesteps[1] - timesteps[0]); matrixc dyn = mat_eigs; for(ulong i = 0; i<mat_eigs.Cols(); i++) { vectorc column = mat_eigs.Col(i); column = c_pow(column,tpow[i]); column*=amplitudes; dyn.Col(column,i); } return dyn; }
A continuación se muestra un gráfico de la evolución temporal del modo más dominante para nuestro ejemplo. El gráfico fue generado por el script DMD_Mode_Dynamics.ex5.
Reconstrucción y pronóstico
Los modos, amplitudes y valores propios del DMD se pueden combinar para construir una aproximación de los datos de entrada. El conjunto de datos reconstruido será una representación de bajo rango que capturará la dinámica dominante de los datos originales. Esto se logra multiplicando los modos por la dinámica.
//+------------------------------------------------------------------+ //| reconstruct the input data based on the dominant dmd dynamics | //+------------------------------------------------------------------+ matrixc reconstructed_data(matrixc& dynamics, matrixc& modes) { return np::matmul(modes,dynamics); }
A continuación se muestra la serie reconstruida según lo define el modelo DMD.
Si queremos filtrar los datos, tenemos que "desactivar" los modos que deseamos descartar. Podemos hacerlo estableciendo los modos (columnas) descartados a cero y luego multiplicando por la dinámica.
//--- vector time_steps = np::arange(X1.Cols()+1,t[0],dt); matrixc dmd_dynamics = compute_dynamics(dmd_amplitudes,eigen_values,time_steps); matrixc masked_modes; //--- if(NumTop) { double min_amp = abs_dmd_amplitudes[amp_indices[NumTop]]; masked_modes = matrixc::Zeros(dmd_modes.Rows(),dmd_modes.Cols()); for(ulong i = 0; i<dmd_modes.Cols(); i++) { bool select = c_abs(dmd_amplitudes[i])>min_amp; if(select) masked_modes.Col(dmd_modes.Col(i),i); } } else masked_modes = dmd_modes; //--- matrixc reconstructed = reconstructed_data(dmd_dynamics,masked_modes); //--- matrix real_rec = real(reconstructed); //--- real_rec = time_delay_reconstruction(real_rec,Delay); //--
El gráfico que aparece a continuación muestra la reconstrucción filtrada utilizando únicamente los modos dominantes.
La predicción se realiza extendiendo los puntos de tiempo discretos más allá del final de los datos de entrada al calcular la dinámica. El modelo DMD extrapola entonces los modos hacia adelante en el tiempo para generar predicciones. La precisión del pronóstico depende de qué tan bien el modelo lineal capturado por DMD represente la dinámica real del sistema.
//--- vector time_steps = np::arange((X1.Cols())+Num_future_points+1,first_time_point,dt); //--- matrixc dmd_dynamics = compute_dynamics(dmd_amplitudes,eigen_values,time_steps); //--- matrixc reconstruction = reconstructed_data(dmd_dynamics,dmd_modes); //--
Aquí podemos ver el resultado de pronosticar 10 pasos hacia adelante.
Cabe señalar que nuestra demostración utiliza una serie con una señal limpia, sin ningún tipo de ruido. En la siguiente sección, veremos cómo se comportan las predicciones de DMD cuando los datos están plagados de ruido.
Aplicación de DMD a los datos de precios
El script DMD_Price_Decomposition.ex5 demuestra la aplicación de DMD a datos reales. Descarga una muestra de cotizaciones de precios y descompone la serie. El modelo DMD resultante se utiliza posteriormente para realizar un número de pronósticos definido por el usuario. A continuación, se visualiza la serie prevista comparándola con las cotizaciones de precios reales.
//+------------------------------------------------------------------+ //| DMD_Price_Decompostion.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs #include<dmd_utils.mqh> //--- input parameters input string Symbol_="AUDUSD"; input ENUM_TIMEFRAMES TimeFrame=PERIOD_D1; input datetime StartDate=D'2025.06.02'; input ENUM_DMD_SCALE jobs = DMDSCALE_N; input ENUM_DMD_EIGV jobz = DMDEIGV_V; input ENUM_DMD_RESIDUALS jobr = DMDRESIDUALS_R; input ENUM_DMD_REFINE jobf = DMDREFINE_R; input ENUM_SVD_ALG whtsvd = SVDALG_1; input long nrnk = 20; input double tol_ = 1.e-9;//tol input ulong Delay = 50;//apply time delay embedding input ulong Num_Future_steps = 10; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- vector prices; if(!prices.CopyRates(Symbol_,TimeFrame,COPY_RATES_CLOSE,StartDate,100+Num_Future_steps)) { Print(" failed to get close prices for ", Symbol_,". Error ", GetLastError()); return; } //--- matrix X = matrix::Zeros(1,100); vector input_series = np::sliceVector(prices,0,100); X.Row(input_series,0); np::matrix2csv("AUDUSD.csv",X.Transpose()); //--- CDmd dmd; if(!dmd.fit(X,1,tol_,nrnk,Delay,jobs,whtsvd,jobz,jobr,jobf)) return; else Print(" It worked! "); vectorc egs = dmd.dmd_eigs(); Print("DMD eigenvalues or Ritz values\n",egs); matrix forecast = dmd.reconstructed_data(Num_Future_steps); vector time = np::arange(prices.Size(),0.0,dmd.dmd_time_delta()); plot_forecast(prices,forecast.Row(0),time,Num_Future_steps," Price forecast",0,0,0,0,500,400,true,30); }
El programa utiliza la clase CDmd definida en dmd_utils.mqh. Esta clase encapsula la funcionalidad proporcionada por el método integrado DynamicModeDecomposition() con las características descritas en este texto. Su interfaz consta de un método fit() para realizar descomposiciones. Requiere una serie de variables de entrada que definen los parámetros del procedimiento DMD, incluida la capacidad de aplicar una incrustación de retardo de tiempo.
virtual bool fit(matrix &X,double time_delta, double tolerance, long svd_rank = -1,ulong embedding_dimension = 0,ENUM_DMD_SCALE scale = DMDSCALE_N,ENUM_SVD_ALG svd_method = SVDALG_1, ENUM_DMD_EIGV eigv = DMDEIGV_V,ENUM_DMD_RESIDUALS resid = DMDRESIDUALS_R,ENUM_DMD_REFINE refine = DMDREFINE_R) { reset(); matrix z,res_vectors,b,w,s; vector residuals; m_delay = embedding_dimension; matrix snapshots = m_delay>1?time_delay_embedding(X,m_delay):X; if(!snapshots.Cols()) return false; m_snapshots_x = np::sliceMatrixCols(snapshots,0,snapshots.Cols()-1); m_snapshots_y = np::sliceMatrixCols(snapshots,1); if(!m_snapshots_x.DynamicModeDecomposition(m_snapshots_y,scale,eigv,resid,refine,svd_method,svd_rank,tolerance,m_eigs,m_left_vectors,z,m_residuals,res_vectors,b,w,s)) { Print(__FUNCTION__, " DMD error ", GetLastError()); return false; } if(eigv != DMDEIGV_N) m_modes = compact_to_complex(z,m_eigs); m_eigenvectors = compact_to_complex(w,m_eigs); if(refine == DMDREFINE_E) { m_modes = compact_to_complex(b,m_eigs); m_exact_modes = true; } else if(refine == DMDREFINE_R) m_residual_vectors = compact_to_complex(res_vectors,m_eigs); m_og_first = m_dmd_first = 0.0; m_og_dt = m_dmd_dt = time_delta; m_og_timesteps = m_dmd_timesteps = np::arange(m_snapshots_x.Cols()+1,m_dmd_first,m_dmd_dt); m_og_last = m_dmd_last = m_dmd_timesteps.Size()-1; m_fitted = compute_amplitudes() ; return m_fitted; }
Una vez ajustado un modelo, se pueden recuperar varias características del mismo invocando uno de los métodos getter que se muestran a continuación.
matrixc dmd_modes(void) { return m_modes; } double dmd_time_delta(void) { return m_dmd_dt; } double dmd_first_time(void) { return m_dmd_first; } ulong dmd_last_time(void) { return m_dmd_last; } double og_first_time(void) { return m_og_first; } ulong og_last_time(void) { return m_og_last; } vector dmd_time_steps(void) { return m_dmd_timesteps; } vector og_time_steps(void) { return m_og_timesteps; } vectorc dmd_eigs(void) { return m_eigs; } vectorc dmd_amplitudes(void) { return m_amplitudes; } matrixc exact_dmd_modes(void) { if(m_exact_modes) return m_modes; else return matrixc::Zeros(0,0); } matrixc eigenvectors(void) { return m_eigenvectors; } vector growth_rate(void) { return real(m_eigs)/m_dmd_dt; } vector frequency(void) { vectorc f = c_log(m_eigs); return imaginary(f)/(2.0*M_PI*m_dmd_dt); } matrixc residual_vectors(void) { return m_residual_vectors; } vector residuals(void) { return m_residuals; }
Las reconstrucciones, las predicciones y las operaciones de filtrado se gestionan mediante los métodos sobrecargados reconstructed_data(). Ambos generan contenedores de valores reales con el formato original de los datos de entrada. La versión de reconstructed_data() con un parámetro opcional se utiliza principalmente para pronósticos y reconstrucciones sin ningún tipo de filtrado. Cuando el único parámetro opcional se establece en un valor distinto de cero, el método generará el número de pronósticos solicitado. De lo contrario, generará una reconstrucción de la entrada.
matrix reconstructed_data(ulong num_future_steps = 0) { if(num_future_steps) { m_dmd_last+=num_future_steps; m_dmd_timesteps = np::arange(m_dmd_last+1,m_dmd_first,m_dmd_dt); } matrixc d = dynamics(); matrixc data = np::matmul(m_modes,d); if(!num_future_steps) data = np::sliceMatrixCols(data,0,long(m_og_last+1)); matrix rdata = real(data); if(m_delay>1) rdata = time_delay_reconstruction(rdata,m_delay); return rdata; }
La segunda versión de reconstructed_data() toma un parámetro vectorial adicional. Este método requiere que el usuario proporcione un vector de unos y ceros, cuyo tamaño coincida con el número de modos. Un cero en el vector indica que se descartará el modo correspondiente. Esto no altera en absoluto los modos originales, pero permite el cálculo de pronósticos filtrados o reconstrucciones.
matrix reconstructed_data(vector& modes_mask, ulong num_future_steps = 0) { if(num_future_steps) { m_dmd_last+=num_future_steps; m_dmd_timesteps = np::arange(m_dmd_last+1,m_dmd_first,m_dmd_dt); } matrixc d = dynamics(); matrixc masked_modes = matrixc::Zeros(m_modes.Rows(),m_modes.Cols()); if(modes_mask.Size() == m_modes.Cols()) { for(ulong i = 0; i<masked_modes.Cols(); i++) if(modes_mask[i]) masked_modes.Col(m_modes.Col(i),i); } matrixc data = np::matmul(masked_modes,d); if(!num_future_steps) data = np::sliceMatrixCols(data,0,long(m_og_last+1)); matrix rdata = real(data); if(m_delay>1) rdata = time_delay_reconstruction(rdata,m_delay); return rdata; }
Al ejecutar el programa, obtenemos el siguiente resultado.
Podemos observar que DMD tiene dificultades para deducir la dinámica, probablemente debido al ruido en la serie. Las predicciones que van más allá de unos pocos pasos de la entrada son inútiles. Confirmando que el DMD solo es realmente útil para pronósticos a corto plazo. Si se avanza más de un par de pasos hacia el futuro, el modelo empieza a fallar.
Conclusión
El artículo demostró cómo aplicar la descomposición en modos dinámicos a series temporales univariadas en MQL5, utilizando el nuevo método matricial DynamicModeDecomposition(). Analizamos los datos de entrada de este método, así como la forma de interpretar y procesar sus resultados para su análisis. De este modo, se presentaron las herramientas esenciales necesarias para trabajar con números complejos. DMD puede ser una valiosa incorporación a MQL5, pero para sacarle el máximo partido, los usuarios necesitan un conocimiento considerable y específico del dominio. No solo de la DMD en sí, sino también del conjunto de datos que se está analizando. Todo el código al que se hace referencia en el artículo se enumera y se adjunta a continuación.| Nombre del archivo | Descripción |
|---|---|
| MQL5/include/np.mqh | Este es un archivo de cabecera de funciones de utilidad para vectores y matrices. |
| MQL5/include/cmath.mqh | Un archivo de cabecera con funciones de utilidad para trabajar con números complejos. |
| MQL5/include/dmd_utils.mqh | Un archivo de cabecera que contiene la definición de la clase CDmd y varias funciones útiles para el análisis de DMD. |
| MQL5/scripts/DMD_Demo.mq5 | Un script utilizado para generar la gráfica de los valores propios. |
| MQL5/scripts/DMD_Mode_Dynamics.mq5 | Un script que demuestra el cálculo y la visualización de la dinámica de un modo. |
| MQL5/scripts/DMD_Reconstruction.mq5 | Este script muestra la reconstrucción de un modelo DMD, así como el uso de DMD para el filtrado. |
| MQL5/scripts/DMD_Forecast_Demo.m5 | El script muestra cómo realizar pronósticos con un modelo DMD. |
| MQL5/scripts/DMD_Price_Decomposition.mq5 | Este script demuestra el uso de la clase CDmd y la aplicación de DMD a cotizaciones de precios reales. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19188
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Dominar los Fair Value Gaps: formación, lógica y automatización del trading con Breakers y Market Structure Shifts (MSS)
De novato a experto: Noticias animadas utilizando MQL5 (X) Vista multigráfico de múltiples símbolos para el trading de noticias
Particularidades del trabajo con números del tipo double en MQL4
De novato a experto: Dominando la generación de informes detallados de trading con Reporting EA
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso