English Русский 中文 Deutsch 日本語 Português
Uso práctico de las redes neuronales de Kohonen en el trading algorítmico (Parte II) Optimización y previsión

Uso práctico de las redes neuronales de Kohonen en el trading algorítmico (Parte II) Optimización y previsión

MetaTrader 5Probador | 18 abril 2019, 14:09
1 937 0
Stanislav Korotky
Stanislav Korotky

En este artículo, continuamos considerando las redes de Kohonen como una herramienta del trader. En la primera parte, corregimos y mejoramos las clases de redes neuronales disponibles públicamente, completándolas con algoritmos necesarios. Ahora ha llegado el momento para aplicarlas en la práctica. En este artículo, usaremos los mapas de Kohonen para resolver los problemas, como la selección de los parámetros óptimos del Asesor Experto (EA) y la previsión de las series temporales.


Buscando parámetros óptimos del Asesor Experto

Principios generales

El problema de la optimización de los robots se soluciona en muchas plataformas comerciales, inclusive MetaTrader. El Simulador de Estrategias incorporado ofrece un amplio abanico de herramientas, algoritmos avanzados, cálculos distribuidos y una evaluación estadística muy detallada. Sin embargo, desde el punto de vista del usuario, siempre hay una última etapa crítica de la optimización, es decir, la etapa de la selección de los parámetros finales a base de los resultados del análisis de toda la avalancha de la información generada por el programa. En los artículos anteriores sobre los mapas de Kohonen publicados en este sitio web, fueron mostrados los ejemplos del análisis visual de los resultados de la optimización. Sin embargo, eso supone una evaluación experta realizada por el mismo usuario, pero, idealmente, nos gustaría obtener una recomendaciones más concretas de parte de la red neuronal. Al fin y al cabo, el trading algorítmico es una negociación realizada por el programa, sin la intervención del usuario.

Habitualmente, después de terminar la optimización, obtenemos un informe largo de parte del Simulador de Estrategias con una gran cantidad de opciones. Dependiendo del ordenamiento por una u otra columna, extraemos de sus entrañas las configuraciones absolutamente diferentes que suponen una optimalidad según el criterio correspondiente: beneficio, coeficiente de Sharp, etc. Y si incluso tenemos determinado un criterio en el que confiamos más, a menudo el sistema ofrece varias configuraciones con el mismo resultado. ¿Cómo se puede elegir?

Algunos traders practican su propio criterio sintético que incluye varios valores estándar en el informe: con este enfoque, es realmente menos probable recibir las líneas iguales en el informe. Sin embargo, él traspasa la cuestión en el área de meta optimización del criterio sintético mencionado (¿cómo podemos elegir correctamente su fórmula?). Es un tema absolutamente separado. Por eso, vamos a volver al análisis de los resultados de la optimización estándar.

Desde mi punto de vista, la selección de un conjunto ideal de los parámetros del EA no debe basarse en la búsqueda del máximo de alguna función objetiva, sino en la búsqueda de un «intervalo» más prolongado en un área de los valores de esta función, con el nivel mínimo predefinido de este «intervalo». En el contexto del trading, el nivel del «intervalo» puede ser comparado con la rentabilidad media, y su duración, con la fiabilidad, es decir, con la robustez y estabilidad del sistema.

Después de considerar previsiblemente algunas técnicas del análisis de los datos en la primera parte, surge una suposición de que la búsqueda de estos «intervalos» puede ser realizada a través del agrupamiento (Clusterización).

Lamentablemente, no hay un método unificado y universal para obtener los clústeres con características necesarias. En particular, si el número de los clústeres es «demasiado» grande, ellos se hacen pequeños y demuestran todos los indicios del sobreaprendizaje, es decir, generalizan la información de una manera mala. Si los clústeres son «demasiado» pocos, al contrario, no están enseñados suficientemente y absorben las instancias fundamentalmente diferentes. El concepto «demasiado» no tiene definiciones claras. Hay un límite especificado para cada tarea, cantidad y estructura de datos, por eso, normalmente se supone que se realizan varios experimentos.

El número de los clústeres está relacionado lógicamente con el tamaño del mapa y la tarea aplicada. En nuestro caso, el primer factor funciona sólo en una dirección, puesto que antes hemos decidido establecer el tamaño según la fórmula (7). Por tanto, sabiendo este tamaño, obtenemos un límite superior para el número de los clústeres; es poco probable que puedan ser más que el tamaño de un lado. Por otro lado, basándose en la tarea aplicada, tal vez nos convenga sólo un para de los clústeres: configuraciones «buenas» y «malas». Pues, éste es el intervalo dentro del cual se puede realizar los experimentos. Todo eso se aplica solamente a los algoritmos basados en una indicación explícita del número de los clústeres, como K-Means. Nuestro algoritmo alternativo no dispone de esta configuración, pero debido a la ordenación de los clústeres por la calidad, podemos simplemente excluir todos los clústeres con el número encima del establecido de nuestra consideración.

A continuación, intentaremos realizar el agrupamiento usando la red de Kohonen. Pero antes de pasar a las cuestiones prácticas, es necesario discutir un detalle.

La optimización de muchos robots se realiza en todo el espacio de parámetros de una dimensión muy grande, y por eso, se ejecuta usando el algoritmo genético. Él ahorra el tiempo y los recursos. Sin embargo, tiene una particularidad de «caer» en las áreas rentables. Al principio, está pensado así, pero eso no es bueno desde el punto de vista de los mapas de Kohonen. Es que los mapas de Kohonen son muy sensibles a la distribución de datos en el espacio de entrada, y prácticamente, la reflejan en la topología resultante. Debido al hecho de que las versiones malas de los parámetros se excluyen por la genética en las fases iniciales, ellas ocurren con menos frecuencia que las versiones buenas, que se analizan por la genética más detalladamente. Como resultado, la red de Kohonen puede omitir las caídas peligrosas de la función objetiva cerca de las versiones encontradas supuestamente buenas. Como las características del mercado fluctúan constantemente, es muy importante evitar estos parámetros que provocan las pérdidas un paso a la izquierda/derecha.

Estas son algunas maneras de resolver el problema:

  1. Desistir de la optimización genética a favor de la optimización completa; aunque no siempre es posible integralmente, conviene usar el enfoque jerárquico, es decir, ejecutar primero la optimización genética con un paso grande, localizar las zonas interesantes, y realizar la optimización completa en estas zonas (y luego analizar usando la red de Kohonen); además existe una opinión que la extensión excesiva de la lista de los parámetros a analizar ofrece al sistema un número de los grados de libertad, debido a los cuales el sistema primero se hace inestable y segundo, la optimización se convierte en una adaptación; por eso, se recomienda seleccionar los valores permanentes para la mayoría de los parámetros basándose en su significado físico y en el análisis fundamental (por ejemplo, seleccionar los períodos según el tipo de la estrategia: un día para la estrategia intradía, una semana para la estrategia a medio plazo, etc.); entonces, es posible reducir el espacio de la optimización y desistir de la genética;
  2. Repetir la optimización genética varias veces usando como criterio no sólo los máximos, sino también los mínimos y ceros de la función objetiva; por ejemplo, se puede realizar la optimización tres veces:
    • por factor de beneficio (PF), como de costumbre;
    • por su valor inverso 1/PF;
    • por factor (min(PF, 1/PF) / max(PF, 1/PF)), que recopilará la estadística cerca de 1;
    Después de eso, reunir los resultados de todas las optimizaciones y analizarlas como un todo único usando la red;
  3. Una medida paliativa, pero que vale la pena de ser estudiada, es construir los mapas de Kohonen en una métrica que excluye el índice optimizado (en general, todos los índices económicos que no son los parámetros del EA); en otras palabras, durante el aprendizaje de la red, la medida de la semejanza entre los pesos de las neuronas y los datos de entrada debe calcularse sólo por los componentes seleccionados correspondientes a los parámetros del EA; la métrica inversa también es interesante, cuando las medidas de la proximidad se calculan sólo por los índices económicos, y probablemente vemos la dispersión topológica en los planos de los parámetros, lo que indica en la inestabilidad del sistema; en ambos casos, los pesos de las neuronas se ajustan de manera completa, es decir, por todos los componentes;

La última versión N3 significa que la topología de la red va a reflejar sólo la distribución de los parámetros del EA (o de los índices económicos, dependiendo de la dirección). De hecho, cuando entrenamos la red en las líneas completas de la tabla de optimización, las columnas como el beneficio, reducción (drawdown), número de transacciones forman la distribución total de neuronas en la medida no menor que los parámetros del EA. Eso puede ser bueno para la tarea del análisis visual con el fin de formar una idea general y sacar conclusiones sobre qué parámetros hacen la mayor influencia en los determinados índices. Pero eso es malo para analizar los parámetros, ya que su distribución real está alterada por los índices económicos.

En principio, para el caso de analizar los resultados de la optimización, existe una división evidente del vector de entrada en dos componentes separados lógicamente: parámetros de entrada del EA y sus índices (de salida). Al entrenar la red de Kohonen en el vector completo, intentamos detectar la dependencia de las «entradas» y «salida» (una relación bidireccional incondicional). Cuando el aprendizaje se realiza sólo en una parte de las características, podemos intentar ver las relaciones direccionadas, como las «salidas» se agrupan dependiendo de las «entradas», o viceversa, como las «entradas» se agrupan dependiendo de las «salidas». Consideraremos las dos opciones.


Desarrollo adicional de SOM-Explorer

Para implementar este modo del aprendizaje (con el masqueramiento de una parte de recursos) necesitaremos realizar un desarrollo adicional de las clases CSOM y CSOMNode.

Las distancias se calculan en la clase CSOMNode. La exclusión de los componentes determinados desde el cálculo está unificada para todos los objetos de la clase. Por eso, hagamos que las variables correspondientes sean estáticas:

    static int dimensionMax;
    static ulong dimensionBitMask;

dimensionMax permite especificar la cantidad máxima de los pesos de las neuronas . Por ejemplo, si la dimensionalidad del espacio es 5, entonces dimensionMax igual a 4 significa que el último componente del vector se excluye del cálculo.

dimensionBitMask permite excluir los componentes organizados aleatoriamente usando la máscara de bits: si el inésimo bit es 1, entonces el inésimo componente se procesa, y si el bit es 0, entonces, no se procesa.

Para colocar las variables, añadimos el método estático:

static void CSOMNode::SetFeatureMask(const int dim = 0, const ulong bitmask = 0)
{
  dimensionMax = dim;
  dimensionBitMask = bitmask;
}

Ahora, alteramos el cálculo de la distancia aplicando nuevas variables:

double CSOMNode::CalculateDistance(const double &vector[]) const
{
  double distSqr = 0;
  if(dimensionMax <= 0 || dimensionMax > m_dimension) dimensionMax = m_dimension;
  for(int i = 0; i < dimensionMax; i++)
  {
    if(dimensionBitMask == 0 || ((dimensionBitMask & (1 << i)) != 0))
    {
      distSqr += (vector[i] - m_weights[i]) * (vector[i] - m_weights[i]);
    }
  }
  return distSqr;
}

Ahora, nos queda asegurarnos de que la clase CSOM coloca las limitaciones en las neuronas de manera correcta. Añadimos el método público semejante a la clase CSOM:

void CSOM::SetFeatureMask(const int dim, const ulong bitmask)
{
  m_featureMask = 0;
  m_featureMaskSize = 0;
  if(bitmask != 0)
  {
    m_featureMask = bitmask;
    Print("Feature mask enabled:");
    for(int i = 0; i < m_dimension; i++)
    {
      if((bitmask & (1 << i)) != 0)
      {
        m_featureMaskSize++;
        Print(m_titles[i]);
      }
    }
  }
  CSOMNode::SetFeatureMask(dim == 0 ? m_dimension : dim, bitmask);
}

En el EA de prueba, creamos el parámetro string FeatureMask, en el que el usuario podrá establecer la máscara de recursos, y vamos a analizarlo sintéticamente respecto a la disponibilidad de los símbolos '1' y '0':

    ulong mask = 0;
    if(FeatureMask != "")
    {
      int n = MathMin(StringLen(FeatureMask), 64);
      for(int i = 0; i < n; i++)
      {
        mask |= (StringGetCharacter(FeatureMask, i) == '1' ? 1 : 0) << i;
      }
    }
    KohonenMap.SetFeatureMask(0, mask);

Todo eso se hace directamente antes del inicio del método Train, y luego influye en el cálculo de las distancias tanto durante el aprendizaje, como en la fase del cálculo de U-Matrix y de los clústeres. Sin embargo, en algunas ocasiones, nos interesará realizar el agrupamiento de acuerdo con otras reglas: por ejemplo, entrenar la red sin máscara y colocarla sólo para encontrar los clústeres. Para eso, introducimos el parámetro adicional de control ApplyFeatureMaskAfterTraining, que será igual a false por defecto. Pero si está en true, llamamos a SetFeatureMask después de Train.

Si estamos mejorando nuevamente las herramientas, aclararemos otro punto más relacionado con el hecho de que vamos a usar los ajustes de robots comerciales como vectores de entrada.

Para el análisis, sería conveniente cargar los valores de los parámetros del EA en la red directamente desde el archivo set. Para eso, escribiremos la siguiente función:

bool LoadSettings(const string filename, double &v[])
{
  int h = FileOpen(filename, FILE_READ | FILE_TXT);
  if(h == INVALID_HANDLE)
  {
    Print("FileOpen error ", filename, " : ",GetLastError());
    return false;
  }
  
  int n = KohonenMap.GetFeatureCount();
  ArrayResize(v, n);
  ArrayInitialize(v, EMPTY_VALUE);
  int count = 0;

  while(!FileIsEnding(h))
  {
    string line = FileReadString(h);
    if(StringFind(line, ";") == 0) continue;
    string name2value[];
    if(StringSplit(line, '=', name2value) != 2) continue;
    int index = KohonenMap.FindFeature(name2value[0]);
    if(index != -1)
    {
      string values[];
      if(StringSplit(name2value[1], '|', values) > 0)
      {
        v[index] = StringToDouble(values[0]);
        count++;
      }
    }
  }
  
  Print("Settings loaded: ", filename, "; features found: ", count);
  
  ulong mask = 0;
  for(int i = 0; i < n; i++)
  {
    if(v[i] != EMPTY_VALUE)
    {
      mask |= (1 << i);
    }
    else
    {
      v[i] = 0;
    }
  }
  if(mask != 0)
  {
    KohonenMap.SetFeatureMask(0, mask);
  }
 
  FileClose(h);
  return count > 0;
}

En, ella analizamos sinteticamente (parsing) las líneas del archivo set y buscamos los nombres de los parámetros que corresponden los recursos de la red entrenada. Las configuraciones que coinciden se guardan en el vector, las que no coinciden, se omiten. La máscara de los recursos se rellena con unos solamente para los componentes disponibles. La función devuelve un recurso lógico del hecho de que las configuraciones han sido leídas.

Ahora, cuando tenemos esta función en nuestra disposición, para cargar los parámetros desde el archivo de las configuraciones, insertamos las siguientes líneas en la rama del operador if donde se realiza la verificación DataFileName != "":

      // process .set file as a special case of pattern
      if(StringFind(DataFileName, ".set") == StringLen(DataFileName) - 4)
      {
        double v[];
        if(LoadSettings(DataFileName, v))
        {
          KohonenMap.AddPattern(v, "SETTINGS");
          ArrayPrint(v);
          double y[];
          CSOMNode *node = KohonenMap.GetBestMatchingFeatures(v, y);
          Print("Matched Node Output (", node.GetX(), ",", node.GetY(),
            "); Hits:", node.GetHitsCount(), "; Error:", node.GetMSE(),
            "; Cluster N", node.GetCluster(), ":");
          ArrayPrint(y);
          KohonenMap.CalculateOutput(v, true);
          hasOneTestPattern = true;
        }
      }

Las configuraciones serán marcadas con SETTINGS.


Asesor Experto en funcionamiento

Ahora, nos acercamos al problema de la selección de los parámetros óptimos.

Para testear las teorías con agrupamiento de los resultados de la optimización, primero necesitamos crear un EA en funcionamiento. Lo generé usando el Asistente para MQL5 y algunos módulos estándar, y lo llamé WizardTest (el código fuente se adjunta al final del artículo). Aquí, tenemos la lista de los parámetros de entrada:

input string             Expert_Title                 ="WizardTest"; // Document name
ulong                    Expert_MagicNumber           =17897;        // 
bool                     Expert_EveryTick             =false;        // 
//--- inputs for main signal
input int                Signal_ThresholdOpen         =10;           // Signal threshold value to open [0...100]
input int                Signal_ThresholdClose        =10;           // Signal threshold value to close [0...100]
input double             Signal_PriceLevel            =0.0;          // Price level to execute a deal
input double             Signal_StopLevel             =50.0;         // Stop Loss level (in points)
input double             Signal_TakeLevel             =50.0;         // Take Profit level (in points)
input int                Signal_Expiration            =4;            // Expiration of pending orders (in bars)
input int                Signal_RSI_PeriodRSI         =8;            // Relative Strength Index(8,...) Period of calculation
input ENUM_APPLIED_PRICE Signal_RSI_Applied           =PRICE_CLOSE;  // Relative Strength Index(8,...) Prices series
input double             Signal_RSI_Weight            =1.0;          // Relative Strength Index(8,...) Weight [0...1.0]
input int                Signal_Envelopes_PeriodMA    =45;           // Envelopes(45,0,MODE_SMA,...) Period of averaging
input int                Signal_Envelopes_Shift       =0;            // Envelopes(45,0,MODE_SMA,...) Time shift
input ENUM_MA_METHOD     Signal_Envelopes_Method      =MODE_SMA;     // Envelopes(45,0,MODE_SMA,...) Method of averaging
input ENUM_APPLIED_PRICE Signal_Envelopes_Applied     =PRICE_CLOSE;  // Envelopes(45,0,MODE_SMA,...) Prices series
input double             Signal_Envelopes_Deviation   =0.15;         // Envelopes(45,0,MODE_SMA,...) Deviation
input double             Signal_Envelopes_Weight      =1.0;          // Envelopes(45,0,MODE_SMA,...) Weight [0...1.0]
input double             Signal_AO_Weight             =1.0;          // Awesome Oscillator Weight [0...1.0]
//--- inputs for trailing
input double             Trailing_ParabolicSAR_Step   =0.02;         // Speed increment
input double             Trailing_ParabolicSAR_Maximum=0.2;          // Maximum rate
//--- inputs for money
input double             Money_FixRisk_Percent=5.0;          // Risk percentage

Para nuestros estudios, no vamos a optimizar todos los parámetros, sino algunos de ellos.

Signal_ThresholdOpen
Signal_ThresholdClose
Signal_RSI_PeriodRSI
Signal_Envelopes_PeriodMA
Signal_Envelopes_Deviation
Trailing_ParabolicSAR_Step
Trailing_ParabolicSAR_Maximum

Ejecuté la optimización genética del EA desde el enero hasta el junio de 2018 (20180101-20180701) en EURUSD D1, M1 OHLC, usando el beneficio. El archivo Set con los ajustes de los parámetros para la optimización se adjunta al final del artículo (WizardTest-1.set). Los resultados de la optimización fueron guardados en el archivo Wizard2018plus.csv (también se adjunta) del que fueron excluidas las líneas outliers, en particular, las que tenían un número de transacciones menos de 5 y los coeficientes de Sharp excesivamente elevados. Además de eso, como la optimización genética produjo, por definición, un desplazamiento en los repasos rentables, decidí excluir completamente los repasos no rentables y dejé sólo las entradas donde el beneficio fue no menos de 10 000.

Aparte de eso, excluí dos columnas de los valores, puesto que ellas dependen prácticamente de las demás (es decir, el balance, Expected Payoff) Hay otros candidatos como Factor de beneficio, Factor de Recuperación y coeficiente de Sharp: con una alta correlación entre ellos. Además, el Factor de Recuperación es inversamente correlacionado con la reducción (Drawdown), pero los dejé para demostrar esta dependencia en los mapas.

La selección de la estructura de datos de entrada con un conjunto mínimo de los componentes independientes, que lleven el máximo de información, es una condición crítica para el uso de las redes neuronales.

Si abrimos el archivo csv con los resultados de la optimización, veremos aquella situación ambigua mencionada antes: una enorme cantidad de las líneas con los valores igualmente rentables y parámetros diferentes. Intentaremos realizar una selección razonable.

Para tener una referencia para evaluar su selección en el futuro, veremos cómo es la prueba forward para el conjunto de parámetros desde la primera línea de los resultados de la optimización (véase WizardTest-1.set). 01.07.2018-01.12.2018.

Informe del Simulador de Estrategias para la primera versión de los ajustes listados

Informe del Simulador de Estrategias para la primera versión de los ajustes listados

Recordamos estos valores, en realidad no tan buenos, y pasamos a la red.


Análisis de la red neuronal

El número de las entradas en el archivo csv es aproximadamente 2000, por eso, el tamaño de la red de Kohonen calculado según la fórmula (7) será 15.

Iniciamos CSOM-Explorer, introducimos el nombre del archivo con datos (Wizard2018plus.csv) en DataFileName; 15, en CellsX y CellsY, EpochNumber dejamos en 100. Para visualizar todos los planos simultáneamente, seleccionamos las imágenes pequeñas: 200 para las imágenes ImageW y ImageH, 6 para MaxPictures. Como resultado del trabajo del EA, obtenemos aproximadamente lo siguiente:

Entrenamiento de la red de Kohenen en los resultados de la optimización del EA

Entrenamiento de la red de Kohenen en los resultados de la optimización del EA

La primera línea se compone de los mapas de métricas económicas, la segunda línea y el primer mapa de la tercera línea representan los parámetros de trabajo del EA, y finalmente, los últimos 5 mapas son unos mapas especiales cuya construcción fue implementada en la primera parte.

Vemos como son estos mapas de cerca:

Beneficio, PF, RF y los tres primeros parámetros del EA

Beneficio, PF, RF y los tres primeros parámetros del EA

Ratio de Sharpe, drawdown, número de trades y el segundo conjunto de tres parámetros del EA

Ratio de Sharpe, drawdown, número de trades y el segundo conjunto de tres parámetros del EA

El último parámetro del EA, el contador de ocurrencias, U-Matrix, error de agrupamiento, clústeres y la salidas de la red

El último parámetro del EA, el contador de ocurrencias, U-Matrix, error de agrupamiento, clústeres y la salidas de la red

Vamos a verificar un análisis del EA de los mapas obtenidos, por decir así, manualmente.

En términos del beneficio, vemos dos áreas convenientes con unos valores aproximadamente iguales (rojo), en la esquina superior izquierda/derecha, siendo la esquina izquierda más grande y con el coloreado más fuerte. No obstante, el análisis paralelo de los planos con PF, RF y coeficiente de Sharp nos convence de que la esquina superior derecha es la mejor opción. Eso también se confirma por la menor reducción.

Vamos a pasar a los componentes, parámetros correspondientes del EA, prestando atención a la esquina superior derecha. En los mapas de los primeros cinco parámetros, este punto tiene un colorido estable, lo que nos permite nombrar con seguridad los valores óptimos (coloque el cursor en la neurona superior derecha en cada mapa para mostrar el valor correspondiente en la ayuda).

Signal_ThresholdOpen = 40
Signal_ThresholdClose = 85
Signal_RSI_PeriodRSI = 73
Signal_Envelopes_PeriodMA = 20
Signal_Envelopes_Deviation = 0.5

Los mapas de dos parámetros restantes cambian de forma activa su color en la esquina superior derecha, por eso no está claro qué valores hay que seleccionar. A juzgar por los planos con los contadores de las ocurrencias, la neurona superior derecha será la más conveniente. Además, tiene sentido asegurarse de que hay una «atmósfera silenciosa» en U-Matrix para esta neurona y en el mapa no hay errores. Puesto que todo está bien, seleccionamos estos valores:

Trailing_ParabolicSAR_Step = 0.15
Trailing_ParabolicSAR_Maximum = 1.59

Iniciamos el EA con estos parámetros (véase WizardTest-plus-allfeatures-manual.set) en el mismo período forward hasta el 1 de diciembre de 2018. Obtenemos el siguiente resultado:

Informe del Simulador de Estrategias para los ajustes seleccionados a través del análisis visual de los mapas de Kohonen

Informe del Simulador de Estrategias para los ajustes seleccionados a través del análisis visual de los mapas de Kohonen

El resultado es notablemente mejor que el seleccionado aleatoriamente desde la primera línea. No obstante, igualmente puede decepcionar a los profesionales. Por eso, hay que hacer una observación aquí.

Este es un EA de prueba, y no pretende a conseguir enormes beneficios. Su tarea es generar los datos iniciales para demostrar el funcionamiento de los algoritmos de redes neuronales. Tras la publicación del artículo, será posible testear las herramientas en una cantidad más grande de los sistemas reales, y tal vez, adaptarlas para su mejor uso.

Comparamos el resultado actual con los valores medios que podríamos obtener para todas las opciones de los ajustes desde el archivo csv en el mismo intervalo de fechas.

Resultados de todas las pruebas forward

Resultados de todas las pruebas forward

Estadística de la distribución del beneficio en las pruebas forward

Estadística de la distribución del beneficio en las pruebas forward

La estadística es la siguiente: la media es 1007, la desviación estándar es 1444, la mediana es 1085, el mínimo y el máximo son -3813.78 y 4202.82, respectivamente. De esta manera, usando la evaluación del EA, hemos obtenido el beneficio incluso más grande, que la media más desviación estándar.

Nuestra tarea consiste en aprender hacer automáticamente una selección igual o perecida en cuanto a la calidad. Para eso, se utiliza el agrupamiento. Puesto que los clústeres se enumeran en orden de su uso preferible, vamos a considerar el clúster 0 (aunque, básicamente, se puede usar los clústeres 1-5 para operar con una cartera de configuraciones).

Los colores de los clústeres en el mapa siempre corresponden a su número de orden conforme la lista siguiente:

{clrRed, clrGreen, clrBlue, clrYellow, clrMagenta, clrCyan, clrGray, clrOrange, clrSpringGreen, clrDarkGoldenrod}

Eso puede ayudar a identificarlos en las imágenes pequeñas cuando es difícil distinguir sus números.

SOM-Explorer muestra en el log las coordenadas de los centros de los clústeres, por ejemplo, para el mapa actual:

Clústeres [20]:
[ 0] "Profit"                        "Profit Factor"                 "Recovery Factor"               "Sharpe Ratio"                 
[ 4] "Equity DD %"                   "Trades"                        "Signal_ThresholdOpen"          "Signal_ThresholdClose"        
[ 8] "Signal_RSI_PeriodRSI"          "Signal_Envelopes_PeriodMA"     "Signal_Envelopes_Deviation"    "Trailing_ParabolicSAR_Step"   
[12] "Trailing_ParabolicSAR_Maximum"
N0 [3,2]
[0] 18780.87080     1.97233     3.60269     0.38653    16.76746    63.02193    20.00378
[7]    65.71576    24.30473    19.97783     0.50024     0.13956     1.46210
N1 [1,4]
[0] 18781.57537     1.97208     3.59908     0.38703    16.74359    62.91901    20.03835
[7]    89.61035    24.59381    19.99999     0.50006     0.12201     0.73983
...

Al principio, hay una leyenda de recursos, luego, para cada clúster se muestra su número, coordenadas X y Y los valores de los recursos.

Para el clúster 0, tenemos los siguientes valores de los parámetros del EA:

Signal_ThresholdOpen=20
Signal_ThresholdClose=66
Signal_RSI_PeriodRSI=24
Signal_Envelopes_PeriodMA=20
Signal_Envelopes_Deviation=0.5
Trailing_ParabolicSAR_Step=0.14
Trailing_ParabolicSAR_Maximum=1.46

Iniciamos la prueba forward con estos parámetros (WizardTest-plus-allfeatures-auto-nomasks.set) и получим отчет:

Informe del Simulador de Estrategias para los ajustes seleccionados automáticamente desde los mapas de Kohonen sin la máscara de recursos

Informe del Simulador de Estrategias para los ajustes seleccionados automáticamente desde los mapas de Kohonen sin la máscara de recursos

El resultado no es mucho mejor que la primera prueba aleatoria, y es notablemente peor que la pruebe del experto. Todo se resume en la calidad del agrupamiento. En este momento, se realiza según todos los recursos a la vez, inclusive los índices económicos y los parámetros del EA. Pero nosotros tenemos que buscar un «intervalo» exclusivamente en términos de los valores comerciales. Para el ordenamiento, vamos a aplicar la máscara de los recursos que consideran sólo a ellos, es decir, los primeros seis. Para eso, establecemos lo siguiente:

FeatureMask=1111110000000
FeatureMaskAfterTraining=true

Volvemos a iniciar el aprendizaje. El ordenamiento en SOM-Explorer se ejecuta como la fase final del aprendizaje, y los clústeres se guardan en el archivo de la red. Es necesario para que la red cargada posteriormente pueda ser usada inmediatamente para «reconocer» nuevas muestras. Estas muestras pueden contener no todas las índices (es decir, el vector puede ser incompleto), como, por ejemplo, analizar los archivos set. El incluye sólo los parámetros del EA, pero no hay índices económicos (y ellos no tienen sentido). Por tanto, para estas muestras, se construye su propia máscara de los recursos en la función LoadSettings. Esta máscara corresponde a los componentes del vector disponibles, luego ella se aplica a la red. Así, la máscara del agrupamiento tiene que encontrarse implícitamente en la red, para no entrar en conflicto con la máscara del vector a ser «reconocido».

Pero bueno, volvamos al aprendizaje usando la nueva máscara. Ella alterará el plano U-Matrix y de los clústeres. La imagen de los clústeres se cambiará de forma importante (a la izquierda antes de aplicar la máscara; a la derecha, después de aplicarla).

Clústeres en los mapas de Kohonen construidos sin (izquierda) y con (derecha) la aplicación de la máscara por indices económicos

Clústeres en los mapas de Kohonen construidos sin (izquierda) y con (derecha) la aplicación de la máscara por indices económicos

Ahora, los valores del centro cero del clúster son diferentes.

N0 [14,1] 
[0] 17806.57263     2.79534     6.78011     0.48506    10.70147    49.90295    40.00000
[7]    85.62392    73.51490    20.00000     0.49750     0.13273     1.29078

Si recuerda, en nuestra evaluación especializada, hemos seleccionado la neurona en la esquina superior derecha, es decir, con las coordenadas [14,0]. Ahora, el sistema nos ofrece la neurona vecina [14,1]. Sus pesos caso no se diferencian. Pasaremos los ajustes propuestos en los parámetros del EA de servicio.

Signal_ThresholdOpen=40
Signal_ThresholdClose=86
Signal_RSI_PeriodRSI=74
Signal_Envelopes_PeriodMA=20
Signal_Envelopes_Deviation=0.5
Trailing_ParabolicSAR_Step=0.13
Trailing_ParabolicSAR_Maximum=1.29

Obtenemos los siguientes resultados:

Informe del Simulador de Estrategias para los ajustes seleccionados automáticamente desde los mapas de Kohonen usando la máscara de índices económicos.

Informe del Simulador de Estrategias para los ajustes seleccionados automáticamente desde los mapas de Kohonen usando la máscara de índices económicos.

Son idénticos a los resultados del EA, a pesar de que algunos parámetros sean diferentes.

Para facilitar el tránsito de los resultados del agrupamiento en los ajustes del EA, escribiremos una función adicional que va a generar un archivo set con los nombres y valores de los recursos del clúster seleccionado (por defecto, 0). La función se llama SaveSettings y se hace operativa cuando el nuevo parámetro SaveClusterAsSettings contiene el índice del clúster a exportar. Por defecto, este parámetro contiene -1, lo que significa que no hace falta generar el archivo set. Está claro que esta función no «sabe» el sentido aplicado de los recursos y los guarda todos nombrados en el archivo set. Por eso, ahí también van a haber los índices comerciales como Profit, Profit Factor, etc. El usuario puede copiar desde el archivo generado sólo los recursos correspondientes a los parámetros reales del EA. Los valores de los parámetros se guardan en el formato de números reales, entonces habrá que corregirlos para los parámetros del tipo entero.

Ahora, cuando ya sabemos guardar los ajustes encontrados en formato portátil, creamos los ajustes para el clúster 0 (Wizard2018plusCluster0.set) y los cargamos de nuevo en SOM-Explorer (recordaremos que nuestra utilidad ya no sabe leer los archivos set). En el parámetro NetFileName, es necesario indicar el nombre de la red creada en la fase anterior del aprendizaje (tiene que ser Wizard2018plus.som, porque usábamos los datos de Wizard2018plus.csv — y después de cada aprendizaje de la red, ella se guarda en el archivo con el nombre correspondiente al de entrada, pero con la extensión som). Indicamos el nombre del archivo set generado en el parámetro DataFileName. La marca SETTINGS será sobrepuesta con el centro del cláster C0.

Renombramos el archivo som con la red para que no sea sobrescrito durante los siguientes experimentos.

El primer experimento será el siguiente. Vamos a entrenar la red con la máscara «reversa»: por los parámetros del EA excluyendo los valores comerciales. Para eso, indicamos de nuevo el archivo Wizard2018plus.csv en el parámetro DataFileName, limpiamos el parámetro NetFileName y definimos 

FeatureMask=0000001111111

Obsérvese que FeatureMaskAfterTraining sigue estando en true, es decir, la máscara influye sólo en el agrupamiento.

Después de terminar el aprendizaje, cargamos la red entrenada y probamos en ella nuestro archivo set. Para eso, pasamos el nombre del archivo creado de la red Wizard2018plus.som en el parámetro NetFileName, y copiamos de nuevo Wizard2018plusCluster0.set a DataFileName. El conjunto general de mapas permanecerá inalterado, pero U-Matrix, y portanto, los clústeres serán diferentes.

El resultado del agrupamiento se muestra en la siguiente imagen, a la izquierda:

Clústeres en los mapas de Kohonen construidos con la máscara por los parámetros del EA: solamente en la fase del agrupamiento (izuierda), y en la fase del aprendizaje de la red y el agrupamiento (derecha)

Clústeres en los mapas de Kohonen construidos con la máscara por los parámetros del EA: solamente en la fase del agrupamiento (izuierda), y en la fase del aprendizaje de la red y el agrupamiento (derecha)

La selección de los ajustes se confirma por el hecho de que ellos entran en el clúster cero, y él tiene un tamaño grande.

Como segundo experimento, vamos a entrenar la red de nuevo con la misma máscara (por los parámetros del EA). Pero ahora, también vamos a extenderla a la fase del aprendizaje:

FeatureMaskAfterTraining=false

El conjunto completo de los mapas se muestra más abajo, mientras que el cambio de los clústeres se muestra con una escala mayor en la imagen de arriba, a la derecha. Aquí, las neuronas se agrupan sólo por las semejanza de los parámetros. Hay que leer estos mapas de la siguiente manera, ¿qué índices económicos hay que esperar con los parámetros seleccionados? A pesar de que las configuraciones de prueba han caído en el clúster 2 (aunque es mejor que 0, pero no mucho), su tamaño es uno de más grandes, y eso es una buena característica.

Mapas de Kohonen construidos con la máscara por los parámetros del EA

Mapas de Kohonen construidos con la máscara por los parámetros del EA

Un lector atento notará que los mapas con índices económicos ahora no tienen un carácter topológico tan expresado. Pero en este momento, tampoco es tan necesario, ya que la esencia de la verificación consiste en identificar la estructura de los clústeres a base de los parámetros, y en garantizar que las configuraciones seleccionadas estén bien ubicadas en este sistema de los clústeres.

Una investigación «inversa» será un entrenamiento completo y el agrupamiento con la máscara por los índices económicos:

FeatureMask=1111110000000
FeatureMaskAfterTraining=false

El resultado será el siguiente:

Mapas de Kohonen construidos con la máscara por los índices económicos

Mapas de Kohonen construidos con la máscara por los índices económicos

Ahora, los colores de contraste están concentrados en las primeras seis planos de los índices económicos, y los mapas de los parámetros son más amorfos. En particular, se puede ver que los valores de los parámetros Trailing_ParabolicSAR_Step y Trailing_ParabolicSAR_Maximum prácticamente, no se alteran (los mapas son monótonos), entonces, se puede excluirlos de la optimización, teniendo como base algo medio, por ejemplo, 0.11 y 1.14 respectivamente. Además, entre los mapas de parámetros, Signal_ThresholdOpen se destaca por su contraste, se hace claro de este mapa que hay que seleccionar Signal_ThresholdOpen igual a 0.4 para un trading éxito. El mapa U-Matrix está dividida obviamente en dos «piscinas», superior e inferior (superior para el éxito, inferior para el fracaso). El mapa de ocurrencias es muy raro y heterogéneo (la mayor parte del espacio representa lagunas, y las neuronas activas tienen una valor grande del contador), puesto que los beneficios se agrupan por varios niveles explícitos en el EA de la investigación.

Hay que leer estos mapas de la siguiente manera, ¿qué parámetros hay que seleccionar con los valores seleccionados de los índices económicos?

Finalmente, para el último experimento, vamos a mejorar un poco SOM-Explorer para que pueda nombrar los clústeres de forma más razonable. Escribiremos la función SetClusterLabels que va a analizar determinados valores de los componentes de los vectores de código dentro de cada clúster, y compararlos con el rango de valores de estos componentes. Cuando el valor del peso en alguna neurona va a acercarse al máximo o al mínimo, eso será un motivo para marcarla con el nombre del componente correspondiente. Por ejemplo, si el peso de la relación 0 (corresponde al beneficio) excede los pesos en otros clústeres, entonces este clúster se caracteriza con un beneficio alto. Cabe mencionar que si el signo del extremo (máximo o mínimo) se determina por significado del índice: así, para el beneficio, cuanto más grande sea el valor, mejor; y para la reducción, viceversa. En este contexto, introducimos el parámetro de entrada especial FeatureDirection, en el que vamos a marcar los índices con el efecto positivo usando el signo '+', para el efecto negativo usaremos '-', y los efectos que no son importantes (o no tienen sentido) se omiten usando el signo ','. Nótese que en nuestro caso tiene sentido marcar sólo las indicaciones económicas porque los valores de los parámetros de entrada del EA pueden ser cualquieras y no se interpretan como buenos o malos, dependiendo de la proximidad a los límites del área de definición. Por eso, establecemos el valor de FeatureDirectionsólo para los primeros seis recursos:

FeatureDirection=++++-+

Así se ven las marcas para la red con el aprendizaje en el vector completo y el agrupamiento por los índices económicos (izquierda) y para la red con el aprendizaje y el agrupamiento con máscara por los parámetros del EA.

Marcas de los clústeres para los siguientes casos: el uso de la máscara de los índices económicos en la fase del agrupamiento (a la izquierda) y el uso de la máscara de los parámetros en las fases del aprendizaje y el agrupamiento (a la derecha)

Marcas de los clústeres para los siguientes casos: el uso de la máscara de los índices económicos en la fase del agrupamiento (a la izquierda) y el uso de la máscara de los parámetros en las fases del aprendizaje y el agrupamiento (a la derecha)

Vemos que en ambos casos, las configuraciones seleccionadas se encuentran en los clústeres de Profit Factor, así que se puede esperar la mayor eficacia precisamente en esta indicación. Sin embargo, hay que tener en cuenta que ahora tenemos todas las marcas positivas porque los datos de origen se obtienen de la optimización genética, que actúa como un filtro para la selección de las opciones aceptables. Si analizamos los datos de la optimización completa, podemos identificar también los clústeres que son negativos por su naturaleza.

Pues bien, en términos generales, hemos considerado la búsqueda de los parámetros óptimos del EA usando el agrupamiento en los mapas de Kohonen. Este enfoque es tanto flexible, como complejo. Eso implica el uso de diferentes métodos del agrupamiento y el aprendizaje de la red, el procesamiento previo de los datos de entrada. La búsqueda de las opciones óptimas también depende mucho del carácter específico del robot comercial. La herramienta presentada (SOM-Explorer) nos permite empezar a estudiar este apartado del procesamiento de datos, completar y mejorarlo.


Clasificación de color de los resultados de optimización

Como el método y la calidad del agrupamiento influye mucho en la búsqueda correcta de las configuraciones óptimas del EA, trataremos de solucionar el mismo problema usando un método simple, sin estimar la proximidad de las neuronas.

Mostraremos los pesos de los recursos en el mapa de Kohonen dentro del modelo de colores RGB y seleccionaremos el punto más claro. Puesto que la codificación RGB de color se compone de tres planos, podemos visualizar exactamente tres recursos de esta manera. Si hay más o menos de ellos, mostraremos el brillo en graduaciones del gris, en vez de colores RGB, y de cualquier manera, determinaremos un punto más claro.

Recordaremos que durante la selección de los recursos, es deseable seleccionar los más independientes (de lo que ya hablábamos más arriba)

Al realizar un nuevo enfoque, demostraremos al mismo tiempo la posibilidad de expandir las clases CSOM usando las clases propias. Creamos la clase CSOMDisplayRGB, en la que vamos a redefinir sólo algunos métodos virtuales de la clase padre CSOMDisplay. Gracias a eso, conseguiremos que el mapa RGB va a reflejarse en vez del último plano DIM_OUTPUT.

class CSOMDisplayRGB: public CSOMDisplay
{
  protected:
    int indexRGB[];
    bool enabled;
    bool showrgb;

    void CalculateRGB(const int ind, const int count, double &sum, int &col) const;
  
  public:
    void EnableRGB(const int &vector[]);
    void DisableRGB() { enabled = false; };
    virtual void RenderOutput() override;
    virtual string GetNodeAsString(const int node_index, const int plane) const override;
    virtual void Reset() override;
};

El código entero se adjunta.

Para usar esta versión, creamos la modificación SOM-Explorer-RGB y realizamos las siguientes modificaciones.

Añadimos el parámetro de entrada para activar el modo RGB:

input bool ShowRGBOutput = false;

El objeto del mapa se hará la clase derivada.

CSOMDisplayRGB KohonenMap;

La visualización del plano RGB será implementada en una rama separada del código:

  if(ShowRGBOutput && StringLen(FeatureDirection) > 0)
  {
    int rgbmask[];
    ArrayResize(rgbmask, StringLen(FeatureDirection));
    for(int i = 0; i < StringLen(FeatureDirection); i++)
    {
      rgbmask[i] = -(StringGetCharacter(FeatureDirection, i) - ',');
    }
    KohonenMap.EnableRGB(rgbmask);
    KohonenMap.RenderOutput();
    KohonenMap.DisableRGB();
  }

Ella utiliza el parámetro FeatureDirection, ya conocido para nosotros. Nos ayudará a seleccionar los recursos determinados (desde el conjunto total de recursos) para la inclusión en el espacio RGB. Por ejemplo, para nuestro ejemplo con Wizard2018plus basta con escribir:

FeatureDirection=++,,-

Para que los primeros dos recursos (beneficio y PF) entren directamente en el mapa (corresponde a los signos '+'), y el quinto recurso (reducción), es un valor invertido (corresponde a los signos “-”). Los recursos correspondientes a ',' se ignoran. Los recursos consiguientes tampoco se consideran. El archivo con los ajustes WizardTest-rgb.set se adjunta (se supone que el archivo de la red Wizard2018plus.som está disponible desde la fase anterior).

Así es como aparece el espacio RGB para estas configuraciones (izquierda).

Espacio RGB para los recursos (beneficio, PF, drawdown) y (PF, Drawdown, transacción)

Espacio RGB para los recursos (beneficio, PF, drawdown) y (PF, Drawdown, transacción)

Los colores corresponden a los recursos en secuencia de priridad: el beneficio es rojo, PF es verde, la reducción es azul. La neurona con el brillo máximo tiene la marca "RGB".

En el log se muestran las coordenadas de la neurona más «clara» y los valores de los recursos que pueden servir de una versión de los mejores ajustes.

Puesto que el beneficio y PF están fuertemente correlacionados, sustituiremos la máscara por otra:

FeatureDirection=,+,,-+

En este caso, el beneficio se ignora, pero el número de las transacciones ha sido añadido (algunos brókers dan los bonos por los volúmenes). El resultado se muestra en la imagen de arriba (a la derecha). Aquí, la correspondencia de colores es diferente: PF es rojo, la reducción es verde, el número de transacciones es azul.

Si seleccionamos sólo los recursos de PF y la reducción, obtenemos un mapa en tonos del gris (derecha):

Espacio RGB para los recursos (PF, reducción) y (beneficio)

Espacio RGB para los recursos (PF, reducción) y (beneficio)

Para verificar, podemos seleccionar sólo un recurso (por ejemplo, el beneficio) y asegurarse de que el modo negro y blanco corresponde al primer plano (véase arriba a la derecha).


Análisis de las pruebas forward

Como se sabe, el Simulador de MetaTrader permite completar la optimización del EA con las pruebas forward en las configuraciones seleccionadas. Sería absurdo no intentar usar esta información para el análisis a través de los mapas de Kohonen, y así seleccionar una determinada versión de los ajustes. Teóricamente, los clústeres o las áreas de color en la intersección de las ganancias elevadas en el pasado y en el futuro tienen que incluir las configuraciones que proporcionan el beneficio más estable.

Para eso, usamos los resultados de la optimización de WizardTest junto con las pruebas forward (recordaré que la optimización ha sido realizada para los beneficios en el intervalo 20180101-20180701, mientras que la prueba forward ha sido realizada en el intervalo 20180701-20181201), omitimos todas las indicaciones de ella salvo el beneficio en el pasado y en el futuro, y obtenemos un nuevo archivo para la red — Wizard2018-with-forward.csv (se adjunta al artículo). El archivo con las configuraciones WizardTest-with-forward.set se adjunta también.

El aprendizaje de la red por todas las características, inclusive los parámetros del EA, nos da el siguiente resultado:

Mapas de Kohonen con el análisis de las pruebas forward por todas las características (indicaciones de la rentabilidad y parámetros del EA)

Mapas de Kohonen con el análisis de las pruebas forward por todas las características (indicaciones de la rentabilidad y parámetros del EA)

Podemos observar que la mancha con el beneficio en el futuro es significativamente menor que el beneficio en el pasado, y el clúster cero (rojo) contiene este punto. Además de eso, he incluido el análisis de color por la máscara de dos indicaciones (FeatureDirection=++), y el punto más claro en el último mapa también entra en este clúster. Sin embargo, ni el centro del clúster ni el punto claro no incluyen las configuraciones super lucrativas. Aunque los resultados en el Simulador son positivos, son ellos son dos veces peores que los obtenidos anteriormente.

Intentaremos construir un mapa de Kohonen usando la máscara por dos primeras características, porque sólo ellas son indicaciones. Para eso, aplicamos otros ajustes con FeatureMask=110000000 (véase WizardTest-with-forward-mask11.set) y reentrenamos la red. El resultado es el siguiente.

Mapas de Kohonen con el análisis de las pruebas forward por las indicaciones de la rentabilidad

Mapas de Kohonen con el análisis de las pruebas forward por las indicaciones de la rentabilidad

Aquí podemos ver que los dos primeros planos han obtenido una estructura espacial más expresa, y disponen de la intersección de las áreas con valores altos (beneficios) en la esquina inferior izquierda, que también se resalta en el último mapa. Si tomamos los ajustes desde esta neurona, obtenemos el siguiente resultado:

Informe del Simulador de Estrategias para los ajustes seleccionados de acuerdo con el principio «estabilidad del beneficio»

Informe del Simulador de Estrategias para los ajustes seleccionados de acuerdo con el principio «estabilidad del beneficio»

Eso también es peor que el beneficio obtenido anteriormente.

De esta manera, este experimento práctico no ha permitido detectar los ajustes óptimos. Al mismo tiempo, la inclusión de los resultados de la prueba forward en el análisis de red neuronal parece ser correcto, y probablemente deba considerarse como un enfoque recomendado. Qué detalles nos han faltado exactamente para conseguir el éxito, y si el éxito está garantizado en todas las situaciones, se propone discutir en los comentarios para el artículo. Tal vez, el resultado obtenido es solamente una demostración de que nuestro EA no conviene para una negociación estable.


Previsión de series temporales

Visión general

Las redes de Kohonen se usan con más frecuencia para un análisis visual y el agrupamiento de datos, pero ellas pueden aplicarse también para la previsión. Este problema implica que el vector de los datos de entrada representa las marcas de tick también por su parte principal, normalmente con el tamaño (n - 1), donde n es el tamaño del vector, hay que predecir el final, que normalmente es la última marca de tick. Esencialmente, este problema es parecido al problema de recuperación de los datos con ruidos o perdidos parcialmente. Sin embargo, tiene su carácter específico.

Existen diferentes enfoques de la implementación de la previsión usando las redes de Kohonen. Aquí, tenemos algunos de ellos.

Una red de Kohonen A se entrena en los datos con los vectores incompletos con el tamaño (n - 1), es decir, sin el último componente. Para cada neurona i de la primera red, se entrena un mapa adicional de Kohonen B i para un conjunto reducido de los vectores completos con el tamaño n, que corresponden a aquellos vectores incompletos que han sido mostrados en esta neurona i de la red A. A base de las neuronas en la red Bi, se calcula la probabilidad de los valores determinados de la última coordenada n. Luego, en la fase de la utilización, cuando el vector pronosticado entra en la neurona i de la red A, se realiza la simulación de la futura marca de acuerdo con las probabilidades obtenidas desde la red Bi.

Otra opción. Una red de Kohonen A se entrena en los vectores completos. La segunda red de Kohonen B se entrena en los vectores modificados, en los cuales los incrementos se toman en vez de los valores, es decir,

yk = xk+1 - xk, k = 1 .. n - 1

donde yk y xk son los componentes del vector inicial y del vector modificado. Obviamente, el tamaño del vector modificado es menor que el vector inicial por 1. Luego, alimentamos la entrada de ambas redes de forma paralela con datos de aprendizaje, calculamos el número de las visualizaciones simultáneas del vector completo para la neurona i de la primera red y para la neurona j de la segunda red. Así, obtenemos las probabilidades condicionales pij de que el vector «y» entra en la neurona j (en la red B), a condición de que el vector «x» que le corresponde haya entrado en la neurona i (en la red A). En la fase de la utilización, la red A se alimenta con el vector incompleto x- (sin el último componente), se busca la neurona más próxima i* por componentes (n - 1), y luego se genera un incremento potencial de acuerdo con las probabilidades pi*. Este método, como algunos otros, está relacionado con el método de Monte Carlo que consiste en generar los valores aleatorios para el componente pronosticada, y en encontrar el resultado más probable de acuerdo con la estadística de poblar las neuronas con los datos de la muestra de aprendizaje.

Podemos continuar esta lista mucho tiempo, pues incluye así llamados SOM parametrizados y los modelos autoregresivos en las neuronas SOM, e incluso el agrupamiento de los atractores caóticos en el espacio de la incorporación.

Sin embargo, todos ellos se basan en cierta medida en el fenómeno del agrupamiento de los vectores que caracteriza las redes de Kohonen. A la medida del aprendizaje de la red, los pesos de las neuronas tienden asintóticamente a los valores medios de las clases (subconjuntos de los datos de entrada) que representan. Cada uno de estos valores medios ofrece la mejor estimación para los valores que faltan (en caso de la previsión, para los futuros) en los datos nuevos con la misma ley de distribución que la muestra de aprendizaje. Cuanto más compactas sean las clases y mejor divididas estén, más precisa será la estimación.

Vamos a usar el método de previsión más simple a base de la cuantización de los vectores, que incorpora la única instancia de la red de Kohonen. Pero incluso este método ofrece algunas dificultades.

Vamos a alimentar la entrada de la red con vectores completos de una serie temporal. No obstante, las distancias se calcularán sólo para (n - 1) los primeros componentes. Los pesos serán adaptados integramente para todos los componentes. Luego, para los propósitos de la previsión, alimentaremos la entrada de la red con un vector incompleto, encontraremos la mejor neurona para el componente (n - 1) y leeremos el valor del peso de la última enésima sinapsis. Ésta será la previsión.

Tenemos todo preparado para eso en la clase CSOM. Ahí, hay método SetFeatureMask que define la máscara en las dimensiones del espacio de los recursos que participan en el cálculo de las distancias. Pero antes de empezar con la implementación del algoritmo, tenemos que decidir qué es lo que vamos a prever.


Indicador de clúster Unity

El hecho de que las series de las cotizaciones representan un proceso temporal no trivial está reconocido por todos los traders. Incluye muchas cosas aleatorias, frecuentes cambios de la fase y una gran cantidad de factores influyentes, y es, en principio, un sistema abierto.

Para simplificar el problema, seleccionamos para el análisis uno de los mayores timeframes (diario). En este timeframe, la influencia del ruido es menor que en los timeframes más pequeños. Además, para la previsión, vamos a seleccionar un instrumento (instrumentos) para el que las noticias fundamentales fuertes capaces de mover el mercado de manera impredecible son relativamente raras. En mi opinión, los mejores candidatos son los metales (oro y plata). No siendo un medio de pago de un determinado país y desempeñando varios papeles (no sólo como materia prima, sino también como un activo de protección), por un lado, ellos son menos volátiles, y por otro, están relacionados inseparablemente con las divisas.

Teóricamente, sus futuros movimientos deben considerar las cotizaciones actuales, así como, reaccionar a las cotizaciones de Forex.

De esta manera, necesitamos una manera de recibir los cambios sincronos del coste de los metales y de las principales divisas en forma generalizada. En cada momento del tiempo, éste tiene que ser un vector con n componentes cada uno de los cuales corresponde al coste relativo de una divisa o de un metal. El objetivo de la previsión consiste en predecir el siguiente valor de uno de los componentes por tal vector.

Con este propósito, fue creado el indicador original de clúster llamado Unity (se adjunta). La esencia de su funcionamiento se describe por el siguiente algoritmo. Vamos a considerarlo ejemplificando de manera más simple posible: con EURUSD y el oro XAUUSD.

Cada marca de ticks (precios actuales para el inicio/fin del día) se describe usando las fórmulas obvias:

EUR / USD = EURUSD

XAU / USD = XAUUSD

donde las variables EUR, USD, XAU son determinados «costos» independientes de los activos, y EURUSD y XAUUSD son constantes (cotizaciones conocidas).

Para encontrar las variables, vamos a completar el sistema con una ecuación más, limitando la suma de los cuadrados de las variables con uno:

EUR*EUR + USD*USD + XAU*XAU = 1

Por eso, el indicador se llama Unity.

Usando una simple sustitución, obtenemos:

EURUSD*USD*EURUSD*USD + USD*USD + XAUUSD*USD*XAUUSD*USD = 1

De donde encontramos USD:

USD = sqrt(1 / (1 + EURUSD*EURUSD + XAUUSD*XAUUSD))

y luego, las demás variables.

O representando de forma más generalizada:

x0 = sqrt(1 / (1 + sum(C(xi, x0)**2))), i = 1..n

xi = C(xi, x0) * x0, i = 1..n

donde n es el número de las variables, C(xi,x0) es la cotización del inésimo par que incluye las variables correspondientes. Nótese que el número de las variables es mayor a 1 que el número de los instrumentos.

Puesto que los coeficientes C que participan en los cálculos son las cotizaciones que normalmente se diferencian mucho; en el indicador, se multiplican adicionalmente por el tamaño del contrato: así, se obtienen los valores más o menos comparables (por lo menos, son de la misma magnitud). Para verlos en la ventana del indicador (simplemente para la información), existe el parámetro de entrada AbsoluteValues que debe establecerse como true. Por defecto, está claro que es igual a false, y el indicador siempre calcula los incrementos de las variables:

yi = xi0 / xi1 - 1,

donde xi0 y xi1 son los valores en la última barra y en la penúltima, respectivamente.

No vamos a considerar aquí las particularidades técnicas. Usted puede estudiar su código fuente personalmente.

Así, obtenemos los siguientes resultados:

Indicador de clúster (multidivisas) Unity, XAUUSD

Indicador de clúster (multidivisas) Unity, XAUUSD

Las líneas de los activos que componen el instrumento de trabajo del gráfico actual (en este caso, XAU y USD) se muestran anchas, mientras que las demás tienen un grosor normal.

Entre otros parámetros de entrada del indicador, cabe destacar:

  • Instruments — línea con los nombres de los instrumentos de trabajo separados por comas; es necesario que la moneda base o la moneda de cotización sea la misma para todos los instrumentos;
  • BarLimit — número de las barras para el cálculo; cuando lleguemos al aprendizaje de la red, eso será el tamaño de la muestra de aprendizaje;
  • SaveToFile — nombre del archivo csv en el que el indicador exporta los valores que serán cargados posteriormente en la red neuronal; la estructura del archivo es simple: la primera columna es la fecha, todas las demás se usan para los valores de los búferes de indicador correspondientes;
  • ShiftLastBuffer — bandera para alterar el modo en el que se forma el archivo csv; cuando la opción es false, en cada línea del archivo se muestran los datos de la misma barra, el número de las columnas es igual al número de los instrumentos, más una de las divisiones de los tickers en componentes, más una columna, la primera es para la fecha, los nombres de las columnas corresponden a las monedas y metales; cuando la opción es true, se crea una columna adicional con el nombre FORECASTen el que se guardan los valores desde la columna con el último activo con el desplazamiento a un día hacia adelante; así, en cada línea, vemos no sólo todos los datos para el día actual, sino también el cambio de mañana para el último instrumento.

Por ejemplo, para preparar el archivo con los datos para la previsión del cambio en el precio del oro, es necesario especificar en el parámetro Instruments - "EURUSD,GBPUSD,USDCHF,USDJPY,AUDUSD,USDCAD,NZDUSD,XAUUSD" (es importante que XAUUSD se especifique como el último), y en ShiftLastBuffer debe haber true. Obtenemos el archivo csv aproximadamente con la siguiente estructura:

           datetime;      EUR;     USD;      GBP;      CHF;      JPY;      AUD;      CAD;      NZD;      XAU; FORECAST
2016.12.20 00:00:00; 0.001825;0.000447;-0.000373; 0.000676;-0.004644; 0.003858; 0.004793; 0.000118;-0.004105; 0.000105
2016.12.21 00:00:00; 0.000228;0.003705;-0.001081; 0.002079; 0.002790;-0.002885;-0.003052;-0.002577; 0.000105;-0.000854
2016.12.22 00:00:00; 0.002147;0.003368;-0.003467; 0.003427; 0.002403;-0.000677;-0.002715; 0.002757;-0.000854; 0.004919
2016.12.23 00:00:00; 0.000317;0.003624;-0.002207; 0.000600; 0.002929;-0.007931;-0.003225;-0.003350; 0.004919; 0.004579
2016.12.27 00:00:00;-0.000245;0.000472;-0.001075;-0.001237;-0.003225;-0.000592;-0.005290;-0.000883; 0.004579; 0.003232

Por favor, nótese que las 2 últimas columnas contienen los mismos números con un desplazamiento a una línea. Así, en la línea para el 20 de diciembre de 2016, vemos no sólo el incremento de XAU en este día, sino también su incremento para el día 21 de diciembre, en la columna FORECAST.


SOM-Forecast

Es la hora de implementar el mecanismo de la previsión a base de de la red de Kohonen. Para empezar, con el fin de comprender cómo funciona, vamos a usar como base el SOM-Explorer ya conocido, adaptándolo un poco para las tareas de la previsión.

En primer lugar, los cambios se centrarán en torno a las variables de entrada. Eliminamos todo lo que está relacionado con la configuración de las máscaras: FeatureMask, ApplyFeatureMaskAfterTraining, FeatureDirection, puesto que la máscara de la previsión ya se sabe, si el vector de tamaño n de las marcas está disponible, en el cálculo de la distancia deben participar sólo las primeras (n - 1). Pero nosotros vamos a añadir una opción lógica especial (ForecastMode) que permitirá desactivar esta máscara en caso de necesidad, y usar las posibilidades analíticas de la red de Kohonen en su forma clásica. Vamos a necesitar eso para explorar el mercado, o mejor dicho, el sistema de los instrumentos especificados en Unity, en el estado estático, es decir, ver las correlaciones dentro del mismo día.

Si ForecastMode es true, colocamos la máscara. Si es false, no hay máscara.

    if(ForecastMode)
    {
      KohonenMap.SetFeatureMask(KohonenMap.GetFeatureCount() - 1, 0);
    }

Cuando la red ya está entrenada y en el parámetro DataFileName se especifica el archivo csv con los datos de la prueba, verificamos la calidad de la previsión en el modo ForecastMode de la siguiente manera:

      if(ForecastMode)
      {
        int m = KohonenMap.GetFeatureCount();
        KohonenMap.SetFeatureMask(m - 1, 0);
        
        int n = KohonenMap.GetDataCount();
        double vector[];
        double forecast[];
        double future;
        int correct = 0;
        double error = 0;
        double variance = 0;
        for(int i = 0; i < n; i++)
        {
          KohonenMap.GetPattern(i, vector);
          future = vector[m - 1]; // preserve future
          vector[m - 1] = 0;      // make future unknown for the net (it's not used anyway due to the mask)
          KohonenMap.GetBestMatchingFeatures(vector, forecast);
          
          if(future * forecast[m - 1] > 0) // check if the directions match
          {
            correct++;
          }
          
          error += (future - forecast[m - 1]) * (future - forecast[m - 1]);
          variance += future * future;
        }
        Print("Correct forecasts: ", correct, " out of ", n, " => ", DoubleToString(correct * 100.0 / n, 2), "%, error => ", error / variance);
      }

Aquí, cada vector de prueba se presenta a la red a través de GetBestMatchingFeatures, y la respuesta es el vector de previsión «forecast». Su último componente se compara con el valor correcto del vector de prueba. La coincidencias de la dirección se computan en la variable correct, así como, se acumula un error total de la previsión «error» y luego se muestra en forma normalizada, respecto a la dispersión de los datos en sí.

Si para el aprendizaje tenemos establecido el conjunto de validación (parámetro ValidationSetPercent está establecido), y ReframeNumber es más de cero, se activa la nueva función de la clase CSOM, TrainAndReframe. Ella nos permite aumentar gradualmente el tamaño de la red y rastrear los cambios del error del aprendizaje en el conjunto de validación para interrumpir este proceso, cuando el error deja de caer y empieza a crecer. Es el momento cuando la adaptación de los pesos a los vectores determinados, debido al aumento de las capacidades computacionales de la red, lleva a la pérdida de su capacidad de generalizar y trabajar con los datos desconocidos.

Teniendo seleccionado el tamaño, reseteamos ValidationSetPercent y ReframeNumber a 0 y entrenamos la red, como es habitual, usando el método Train.

Finalmente, modificamos un poco la función de la marcación en los clústeres SetClusterLabels. Puesto que los activos servirán de las características en esta tarea, y cada uno de ellos puede mostrar tanto los extremos positivos, como negativos, vamos a incluir una señal del movimiento en la marca. De esta manera, la misma característica puede encontrarse en el mapa dos veces (con signo más y con signo menos).

void SetClusterLabels()
{
  const int nclusters = KohonenMap.GetClusterCount();

  double min, max, best;
  
  double bests[][3]; // [][0 - value; 1 - feature index; 2 - direction]
  ArrayResize(bests, nclusters);
  ArrayInitialize(bests, 0);
  
  int n = KohonenMap.GetFeatureCount();
  for(int i = 0; i < n; i++)
  {
    int direction = 0;
    KohonenMap.GetFeatureBounds(i, min, max);
    if(max - min > 0)
    {
      best = 0;
      double center[];
      for(int j = nclusters - 1; j >= 0; j--)
      {
        KohonenMap.GetCluster(j, center);
        double value = MathMin(MathMax((center[i] - min) / (max - min), 0), 1);
        
        if(value > 0.5)
        {
          direction = +1;
        }
        else
        {
          direction = -1;
          value = 1 - value;
        }
        
        if(value > bests[j][0])
        {
          bests[j][0] = value;
          bests[j][1] = i;
          bests[j][2] = direction;
        }
      }
    }
  }

  // ...
  
  for(int j = 0; j < nclusters; j++)
  {
    if(bests[j][0] > 0)
    {
      KohonenMap.SetLabel(j, (bests[j][2] > 0 ? "+" : "-") + KohonenMap.GetFeatureTitle((int)bests[j][1]));
    }
  }
}

Pues bien, vamos a considerar que SOM-Forecast está listo. Intentaremos alimentarlo con los valores del indicador Unity.


Análisis

Primero, analizaremos el mercado entero (conjunto de activos seleccionado) en el contexto estático (mejor dicho, estadístico), es decir, a base de los datos exportados por el indicador Unity estrictamente por días: cada línea corresponde a las indicaciones en una barra D1, sin la columna adicional del «futuro».

Para eso, especificaremos en el indicador un conjunto de instrumentos Forex, oro y plata, nombre del archivo en SaveToFile, la bandera de ShiftLastBuffer se establece en false. El ejemplo del archivo unity500-noshift.csv obtenido de esta manera se adjunta al artículo.

Después de entrenar la red a base de estos datos a través de SOM-Forecast (véase som-forecast-unity500-noshift.set), obtenemos el siguiente mapa:

Análisis visual del mercado Forex, oro y plata en los mapas de Kohonen por el indicador Unity para D1

Análisis visual del mercado Forex, oro y plata en los mapas de Kohonen por el indicador Unity para D1

Dos líneas superiores son los mapas de los activos. Su comparación permite identificar las vinculaciones permanentes que funcionan por lo menos durante los últimos 500 días. En particular, dos manchas de color en el centro saltan a los ojos: GBP de color azul, AUD de color amarillo. Eso significa que estos activos a menudo estaban en la fase inversa, y eso se refiere solamente a los movimientos fuertes, es decir, las ventas de GBPAUD prevalecían sobre al romper el nivel de la dispersión media del mercado. Esta tendencia puede persistir y ofrece una opción para colocar las órdenes pendientes en esta dirección.

El crecimiento de XAG se observa en la esquina superior derecha, mientras que CHF muestra la caída ahí. En la esquina inferior izquierda, la situación es contraria: CHF muestra la subida, XAG muestra la caída. Estos activos siempre se divergen en caso movimientos fuertes, de modo que podemos negociarlo usando la ruptura en ambas direcciones. Como es de esperar, EUR y CHF son parecidos.

Podemos encontrar algunas otras particularidades en los mapas. Así, resulta que antes de empezar a predecir, en cierto modo, tenemos la posibilidad de prever el futuro.

Vamos a fijarnos más detalladamente en los clústeres.

Clústeres del mercado Forex, oro y plata en los mapas de Kohonen por el indicador Unity para D1

Clústeres del mercado Forex, oro y plata en los mapas de Kohonen por el indicador Unity para D1

Ellos también proporcionan alguna información. Por ejemplo, muestran la información sobre lo que normalmente no ocurre (o ocurre con poca frecuencia), a saber: EUR no crece simultáneamente con la caída de JPY o CAD. A menudo GBP crece con la caída de CHF, y viceversa.

A juzgar por el tamaño de los clústeres, las divisas EUR, GBP y CHF son más volátiles (sus movimiento sustituyen los movimientos de otras monedas con más frecuencia), mientras que el dólar sorprendentemente pierde a este respecto ante JPY y CAD (recordaré que calculamos los cambios en por cientos). Si suponemos que los números de los clústeres también tienen importancia (los clústeres están ordenados), entonces los tres primeros, o sea, +CHF, -JPY, +GBP, aparentemente, describen los movimientos diarios más frecuentes (no se tiene en cuenta la tendencia, sino la frecuencia, pues incluso durante el movimiento lateral (flat), un gran número de los «pasos» hacia arriba puede ser compensado con un «paso» grande hacia abajo).

Ahora, por fin, pasamos al problema de la previsión.


Previsión

Especificaremos un conjunto de instrumentos en el indicador que incluye Forex y el oro ("EURUSD,GBPUSD,USDCHF,USDJPY,AUDUSD,USDCAD,NZDUSD,XAUUSD"), número de barras en BarLimit (por defecto, 500), nombre del archivo en SaveToFile (por ejemplo, unity500xau.csv) y la bandera ShiftLastBuffer la colocamos en true (modo de la creación de una columna adicional para la previsión). Puesto que es un indicador multidivisas, el número de datos disponibles se limita por el historial más corto entre todos los instrumentos. En el servidor MetaQuotes-Demo hay un número absolutamente suficiente de las barras, por lo menos 2000 barras, para el timeframe D1. Y aquí cabe considerar un detalle, ¿si tiene razón entrenar la red a una profundidad tan grande, ya que el mercado probablemente haya cambiado significativamente? Probablemente, un historial de 2-3 años, o incluso 1 año (250 barras en D1), sea más conveniente para detectar las regularidades actuales.

El ejemplo del archivo unity500xau.csv se adjunta al artículo.

Para cargarlo en SOM-Forecast, establecemos los siguientes parámetros de entrada: DataFileName — unity500xau, ForecastMode — true, ValidationSetPercent — 10, y lo que es importante: ReframeNumber — 10. Así, no sólo iniciamos el aprendizaje de la red del tamaño 10*10 (valores por defecto), sino también activamos la verificación del error en la muestra de validación y continuamos el aprendizaje aumentando gradualmente el tamaño de la red hasta que el error se disminuya. Hasta 10 aumentos de los tamaños ocurrirán en el método TrainAndReframe, 2 neuronas en cada dimensión. Así, determinaremos el tamaño óptimo de la red para los datos de entrada. El archivo con las configuraciones (som-forecast-unity500xau.set) se adjunta también.

Durante el proceso del aprendizaje gradual y el aumento de la red, en el registro, se muestra aproximadamente lo siguiente (se muestra de forma resumida):

FileOpen OK: unity500xau.csv
HEADER: (11) datetime;EUR;USD;GBP;CHF;JPY;AUD;CAD;NZD;XAU;FORECAST
Training 10*10 hex net starts
...
Exit by validation error at iteration 104; NMSE[old]=0.4987230270708455, NMSE[new]=0.5021707785446128, set=50
Training stopped by MSE at pass 104, NMSE=0.384537545433749
Training 12*12 hex net starts
...
Exit by validation error at iteration 108; NMSE[old]=0.4094350709134669, NMSE[new]=0.4238670029035179, set=50
Training stopped by MSE at pass 108, NMSE=0.3293719049246978
...
Training 24*24 hex net starts
...
Exit by validation error at iteration 119; NMSE[old]=0.3155973731785412, NMSE[new]=0.3177587737459486, set=50
Training stopped by MSE at pass 119, NMSE=0.1491464262340352
Training 26*26 hex net starts
...
Exit by validation error at iteration 108; NMSE[old]=0.3142964426509741, NMSE[new]=0.3156342534501801, set=50
Training stopped by MSE at pass 108, NMSE=0.1669971604289485
Exit map size increments due to increased MSE
...
Map file unity500xau.som saved

De esta manera, el proceso se ha detenido en el tamaño de la red 26*26. Aparentemente, tenemos que seleccionar 24*24, cuando el error era mínimo. Pero en realidad, cuando se trabaja con las redes neuronales, siempre operamos con casualidades. Si Usted recuerda, uno de los parámetros (RandomSeed) se encarga de la inicialización del generador de números aleatorios que se utiliza para definir los pesos en la red. Siempre que cambiemos RandomSeed, vamos a obtener una nueva red con características nuevas. Como todos otros factores son iguales, la red va a entrenarse mejor o peor que otras instancias. Por eso, para seleccionar el tamaño de la red, bien como otras configuraciones (incluyendo el tamaño de la muestra de aprendizaje), normalmente tenemos que realizar muchas pruebas y cometer muchas errores. Luego, siguiendo el mismo principio, reduciremos la muestra de los datos de aprendizaje y disminuiremos el tamaño de la red hasta 15*15. Además de eso, yo a veces practico una modificación no canónica del método descrito de la cuantización temporal de los vectores. La modificación consiste en entrenar la red con la bandera de ForecastMode desactivada, y activarla sólo durante la previsión. Entonces, a veces eso produce un efecto positivo. El trabajo con las redes neuronales nunca supone los «platos hechos», solamente las «recetas». Pues, no está prohibido experimentar.

Antes de empezar a entrenar, hay que dividir el archivo original (en este caso, unity500xau.csv) en 2. Es que después del aprendizaje, tendremos que comprobar la calidad de la previsión a base de algunos datos. No tiene sentido hacerlo a base de los mismos datos que se usaban por la red para el aprendizaje (para ser más exacto, tiene sentido solamente para asegurarse de que el porcentaje de las respuestas correctas es increíblemente alto, Usted puede comprobarlo). Por tanto, copiamos 50 vectores para la prueba a un archivo separado, los demás serán usados para entrenar una nueva red. Los archivos correspondientes unity500xau-training.csv y unity500xau-validation.csv se adjuntan.

Especificamos unity500xau-training en el parámetro DataFileName (la extensión .csv se supone), los valores de CellsX y CellsY deben ser de 24 cada uno, ValidationSetPercent es 0, ReframeNumber es 0, ForecastMode sigue estando en true (véase som-forecast-unity500xau-training.set). Como resultado del aprendizaje, obtenemos el siguiente mapa:

Red de Kohonen para la previsión del movimiento del oro en D1

Red de Kohonen para la previsión del movimiento del oro en D1

Todavía no una previsión, sino una confirmación visual de que la red ha aprendido algo. Para averiguar qué es exactamente ha aprendido, vamos a alimentar la red con nuestro archivo de prueba.

Para eso, introducimos las siguientes configuraciones. Ahora movemos el nombre unity500xau-training en el parámetro NetFileName (éste es el nombre del archivo de la red ya entrenada, pero con la extensión som que se supone). En el parámetro DataFileName, especificamos unity500xau-validation. La red va a hacer previsión para todos los vectores y mostrará la estadística en el log:

Map file unity500xau-training.som loaded
FileOpen OK: unity500xau-validation.csv
HEADER: (11) datetime;EUR;USD;GBP;CHF;JPY;AUD;CAD;NZD;XAU;FORECAST
Correct forecasts: 24 out of 50 => 48.00%, error => 1.02152123166208

Lamentablemente, la precisión de la previsión se encuentra en el nivel de una adivinación aleatoria. ¿Podríamos obtener un Grial de una escogida tan simple de los datos de entrada? Es poco probable. Además, hemos elegido la profundidad del historial al ojo, mientras que esta cuestión también tiene que ser investigada. Es más, tenemos que «jugar» con el conjunto de los instrumentos, como adicionar, por ejemplo, la plata, y en general, aclarecer cuál de los activos depende más de los demás para facilitar la previsión. Más abajo, vamos a considerar un ejemplo con plata.

Para introducir más contexto en los datos de entrada, he añadido al indicador Unity la posibilidad de formar un vector no sólo a base de un día, sino en varios últimos días. Para este fin, ha aparecido el parámetro BarLookback, por defecto, e igual a 1, lo que corresponde al modo anterior. Pero si introducimos, por ejemplo, un 5, el vector va a contener las marcas de todos los activos para 5 días (fundamentalmente, una semana es uno de los períodos principales). En caso de 9 activos (8 divisas y el oro), en cada línea del archivo csv se guardan 46 valores. Es un aumento bastante, los cálculos se hacen más lentos, el análisis de los mapas se hace más difícil: incluso los mapas pequeños caben con dificultad en la pantalla, mientras que los grandes pueden quedar sin historial suficiente en el gráfico.

Nota. Al mostrar un número tan grande de los mapas en el modo de los objetos tipo OBJ_BITMAP (MaxPictures = 0), puede que no haya barras suficiente en el gráfico (aunque están ocultos, pero se usan para la vinculación de las imágenes).

Para demostrar la funcionalidad del método, me he decidido con 3 días de la visualización y la red del tamaño 15*15. Los archivos unity1000xau3-training.csv y unity1000xau3-validation.csv se adjunta al artículo. En caso del aprendizaje en el primer archivo y la combrobación en el segundo, obtenemos 58% de las previsiones exactas.

Map file unity1000xau3-training.som loaded
FileOpen OK: unity1000xau3-validation.csv
HEADER: (29) datetime;EUR3;USD3;GBP3;CHF3;JPY3;AUD3;CAD3;NZD3;XAU3;EUR2;USD2;GBP2;CHF2;JPY2;AUD2;CAD2;NZD2;XAU2;EUR1;USD1;GBP1;CHF1;JPY1;AUD1;CAD1;NZD1;XAU1;FORECAST
Correct forecasts: 58 out of 100 => 58.00%, error => 1.131192104823076

Eso ya no está mal, pero no tenemos que olvidar del carácter aleatorio de los procesos en las redes: con otros datos de entrada y otra inicialización, obtendremos los resultados diferentes, y pueden ser peores. Todo el sistema tiene que ser verificado de nuevo y reconfigurado varias veces.. Se practica la generación de varias instancias y la selección de las mejores. Aparte de eso, se puede usar un conjunto de redes, en vez de una red, para tomar decisiones.


Unity-Forecast

Para la previsión, no es necesario mostrar los mapas en la pantalla. Por ejemplo, en los programas MQL, como los EAs e indicadores, podemos usar la clase CSOM, en vez de CSOMDisplay. Vamos a crear el indicador Unity-Forecast, que es parecido a Unity, pero que va a incluir un búfer adicional para visualizar la previsión. La red de Kohonen usada para obtener los «futuros» valores puede entrenarse separadamente (en SOM-Forecast) y luego cargarse en el indicador; o bien, se puede entrenar la red «sobre la marcha», directamente en el mismo indicador. Vamos a implementar ambos modos.

Para cargar la red, añadimos el parámetro de entrada NetFileName. Para entrenar la red, añadimos un grupo de parámetros semejantes al contenido de SOM-Forecast: CellsX, CellsY, HexagonalCell, UseNormalization, EpochNumber, ShowProgress, RandomSeed.

Aquí, no necesitamos el parámetro AbsoluteValues, lo reemplazamos por la constante false. ShiftLastBuffer tampoco tiene sentido, ya que el nuevo indicador siempre supone una previsión. La exportación al archivo csv está excluido, por tanto, eliminamos el parámetro SaveToFile. En vez de eso, añadimos la bandera SaveTrainedNetworks: si es true, el indicador va a guardar las redes entrenadas en los archivos para poder estudiar sus mapas en SOM-Forecast.

El parámetro BarLimit va a usarse no sólo como un número de las barras visualizadas durante el inicio, sino también como un número de las barras para el aprendizaje de la red.

El nuevo parámetro RetrainingBars permite establecer el número de las barras al cabo de las cuales habrá que entrenar la red de nuevo.

Incluimos el archivo de cabecera:

#include <CSOM/CSOM.mqh>

En el procesador de ticks, comprobamos la presencia de una barra nueva (hay que hacer una sincronización por las barras de todos los instrumentos, ella no se realiza en este proyecto demo), y si ha llegado el momento para cambiar la red, la cargamos desde el archivo NetFileName o la entrenamos a base de los datos del propio indicador en la función TrainSOM. Después de eso, vamos a prever el futuro usando la función ForecastBySOM.

  if(LastBarCount != rates_total || prev_calculated != rates_total)
  {
    static int prev_training = 0;
    if(prev_training == 0 || prev_calculated - prev_training > RetrainingBars)
    {
      if(NetFileName != "")
      {
        if(!KohonenMap.Load(NetFileName))
        {
          Print("Map loading failed: ", NetFileName);
          initDone = false;
          return 0;
        }
      }
      else
      {
        TrainSOM(BarLimit);
      }
      prev_training = prev_calculated > 0 ? prev_calculated : rates_total;
    }
    ForecastBySOM(prev_calculated == 0);
  }

Las funciones TrainSOM y ForecastBySOM se muestran a continuación (de forma simplificada). El código fuente completo se adjunta al artículo.

CSOM KohonenMap;

bool TrainSOM(const int limit)
{
  KohonenMap.Reset();

  LoadPatterns(limit);
  
  KohonenMap.Init(CellsX, CellsY, HexagonalCell);
  KohonenMap.SetFeatureMask(KohonenMap.GetFeatureCount() - 1, 0);
  KohonenMap.Train(EpochNumber, UseNormalization, ShowProgress);

  if(SaveTrainedNetworks)  
  {
    KohonenMap.Save("online-" + _Symbol + CSOM::timestamp() + ".som");
  }
  
  return true;
}

bool ForecastBySOM(const bool anew = false)
{
  double vector[], forecast[];
  
  int n = workCurrencies.getSize();
  ArrayResize(vector, n + 1);
  
  for(int j = 0; j < n; j++)
  {
    vector[j] = GetBuffer(j, 1); // 1-st bar is the latest completed
  }
  vector[n] = 0;
  
  KohonenMap.GetBestMatchingFeatures(vector, forecast);
  
  buffers[n][0] = forecast[n];
  if(anew) buffers[n][1] = GetBuffer(n - 1, 1);
  
  return true;
}

Nótese que nosotros prácticamente predecimos el precio del cierre de la barra actual 0 en el momento de su apertura, por tanto, en el indicador no hay ningún desplazamiento del «futuro» búfer respecto a los demás.

Esta vez, vamos a predecir el comportamiento de la plata, y visualizamos el proceso entero en el Simulador de Estrategias. El indicador va a entrenarse «sobre la marcha» en los últimos 250 días (1 año), y reentrenarse cada 20 días (1 mes). El archivo de los ajustes unity-forecast-xag.set se adjunta al final del artículo. Es importante de notar que el conjunto de los instrumentos ha sido ampliado: EURUSD,GBPUSD,USDCHF,USDJPY,AUDUSD,USDCAD,NZDUSD,XAUUSD,XAGUSD. De esta manera, hacemos la previsión de XAG no sólo a base de las cotizaciones de las divisas de Forex y la propia plata, sino también a base del oro.

Es la simulación en el período de 01.07.2018 a 01.12.2018.

Unity-Forecast: previsión de los movimientos de la plata por el clúster Forex y el oro en el Simulador visual de MetaTrader 5

Unity-Forecast: previsión de los movimientos de la plata por el clúster Forex y el oro en el Simulador visual de MetaTrader 5

A veces, la previsión alcanza un 60%. Podemos concluir que básicamente el método funciona, aunque requiere una selección fundamental del objeto de la previsión, preparación de los datos de entrada y un ajuste duradero y meticuloso.

¡Atención! El aprendizaje de la red «sobre la marcha» bloquea el indicador (y los demás indicadores con el mismo símbolo/timeframe), lo que no se recomienda. En realidad, habrá que ejecutar este código en el Asesor Experto. Aquí, ha sido colocado en el indicador con fines ilustrativos. Para actualizar la red en el indicador, se puede usar un EA auxiliar que genera los mapas según un horario, mientras que este indicador los va a recargar por el nombre especificado en NetFileName.

Como opciones de la mejora de la calidad de la previsión, se puede considerar la adición de los factores externos a los parámetros de entrada, por ejemplo, el número del día de la semana, implementación de los métodos más sofisticados a base de varias redes de Kohonen o en una red de Kohonen en combinación con las redes de otros tipos.


Conclusión

En este artículo hemos considerado el uso práctico de las redes de Kohonen en la solución de algunos problemas del trader. Las tecnologías de redes neuronales representan una herramienta potente y flexible que permite aplicar diferentes métodos del procesamiento de datos. Al mismo tiempo, requieren una escrupulosa selección de las variables de entrada, profundidad del historial, combinación de parámetros. Por eso, el éxito de su aplicación se determina en mayor grado por las habilidades profesionales del usuario. Las clases presentadas permiten probar las redes de Kohonen en acción, adquirir la experiencia necesaria de su uso y adaptarlas para sus propias tareas.


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

Archivos adjuntos |
Kohonen2MQL5.zip (302.62 KB)
El poder del ZigZag (Parte I). Desarrollando la clase base del indicador El poder del ZigZag (Parte I). Desarrollando la clase base del indicador
Muchos investigadores no prestan la atención suficiente a la definición del comportamiento de los precios. En este caso, además, se usan métodos complejos que con frecuencia son simplemente «cajas negras», tales como: aprendizaje de máquinas o redes neuronales. En estos casos, lo más importante es: «¿Qué datos suministrar a la entrada para el entrenamiento de este u otro modelo?»
Martingale como base de una estrategia comercial a largo plazo Martingale como base de una estrategia comercial a largo plazo
En este artículo, vamos a analizar con detalle el sistema martingale. Estudiaremos la posibilidad de aplicarlo, y cómo hacerlo de tal forma que reduzcamos los riesgos al mínimo. La principal desventaja de este sencillo sistema es la probabilidad de perder todo el depósito. Y usted debe tener este hecho en cuenta a la hora de comerciar, si es que finalmente decide usar dicho sistema de trading.
Utilidad para la selección y navegación en MQL5 y MQL4: añadiendo la búsqueda automática de patrones y visualización de símbolos encontrados Utilidad para la selección y navegación en MQL5 y MQL4: añadiendo la búsqueda automática de patrones y visualización de símbolos encontrados
En este artículo, seguiremos ampliando las capacidades de la utilidad para la selección y navegación por los instrumentos. Esta vez vamos a crear nuevas pestañas, con la apertura de las cuales van a abrirse sólo aquellos símbolos que correspondan a unos u otros parámetros nuestros. Asimismo, aprenderemos a incluir fácilmente nuestras propias pestañas con las reglas de filtración necesarias.
Aplicación práctica de las correlaciones en el trading Aplicación práctica de las correlaciones en el trading
En este artículo hablaremos sobre el concepto de correlación de magnitudes, y también analizaremos los métodos de cálculo de los coeficientes de correlación y su aplicación práctica en el comercio. La correlación es la interacción estadística de dos o más magnitudes aleatorias (o bien magnitudes que se pueden considerar tales con cierto nivel de precisión permisible). En este caso, además, el cambio de los valores de una o varias de estas magnitudes acompaña al cambio sistemático de los valores de otra u otras magnitudes.