Lógica difusa en las estrategias comerciales

Maxim Dmitrievsky | 2 noviembre, 2017


Introducción

A menudo los traders se preguntan, ¿cómo se puede mejorar el sistema comercial o crear un sistema nuevo usando el aprendizaje automático? A pesar de la abundancia de las publicaciones, la cuestión sobre un modo simple e intuitivo de la creación de los modelos —los cuales es imposible calcular analíticamente sin usar los cálculos computacionales— está sin resolver. La lógica difusa (fuzzy logic) es una ventana al mundo del aprendizaje automático. En combinación con los algoritmos genéticos, esta lógica es capaz de ampliar las posibilidades de la creación de los sistemas comerciales autoenseñables o optimizables fácilmente. Al mismo tiempo, la lógica difusa es intuitiva ya que encapsula la información numérica nítida en los términos difusos (borrosos) de la misma manera como lo hace una persona durante el proceso de pensar.

Vamos a mostrar un ejemplo: en el marco de la lógica nítida, determinamos con exactitud la velocidad de un coche en movimiento usando los instrumentos, por ejemplo, 60 km/h. Pero si fuéramos unos observadores imparciales, sin aparatos de medida, podríamos evaluar la velocidad del coche sólo aproximadamente, basándose en nuestra experiencia y conocimientos. Por ejemplo, sabemos que el coche puede ir rápidamente, y «rápidamente» lo determinamos aproximadamente como 100 km/h y más. También sabemos que el coche puede ir lentamente, unos 5-10 km/h. Y finalmente, determinamos visualmente la velocidad como media (unos 60 km/h) si el tamaño del coche que se acerca se aumenta de una manera regular. Por tanto, podemos caracterizar 60 km/h con cuatro expresiones distintas:

  • como una velocidad media;
  • como una velocidad cercana a la media;
  • como una velocidad más bien media que rápida;
  • y por fin, como una velocidad más bien media que lenta. 

Así se encapsula la información en nuestra mente, permitiendo captar sólo la información necesaria en este momento. Por ejemplo, ¿me dará tiempo cruzar la carretera cuando el coche se mueve no muy rápido? Si pensáramos en todo a la vez entrando en detalles, tendríamos que gastar enormes recursos energéticos y de tiempo antes de tomar una u otra determinada decisión: cruzar la carretera o dejar pasar el coche. Al mismo tiempo, estudiaríamos a fondo la situación actual que a lo mejor nunca se repetiría en el futuro de la misma manera y que va a tener sólo características parecidas. En aprendizaje automático, este proceso se llama el sobreajuste (también es frecuente emplear el término en inglés overfitting).

No vamos a profundizarnos en la teoría de la lógica difusa en este artículo, ya que la información sobre ella está presentada ampliamente en el Internet y en la web MQL5. Pasaremos directamente a la práctica, la que vamos a aclarar con introducciones teóricas y con hechos interesantes.

Para la construcción del modelo, usamos la librería Fuzzy que forma parte de la entrega estándar del terminal MetaTrader 5.

Como resultado, obtendremos un Asesor Experto hecho a base de la lógica difusa, al que Usted podrá tomar como ejemplo para construir sus propios sistemas.

Creación del prototipo del sistema comercial

Pasaremos a la creación de la lógica nítida del sistema comercial en la que vamos a basarnos durante las investigaciones posteriores. Luego, podremos comparar dos sistemas idénticos, pero el segundo sistema será con la aplicación de la lógica difusa.

Como base, vamos a coger tres osciladores RSI con diferentes períodos:

hnd1 = iRSI(_Symbol,0,9,PRICE_CLOSE);
hnd2 = iRSI(_Symbol,0,14,PRICE_CLOSE);
hnd3 = iRSI(_Symbol,0,21,PRICE_CLOSE);

Formularemos las condiciones exactas de las señales y las determinaremos en la función:

double CalculateSignal()
{
 double res =0.5;
 CopyBuffer(hnd1,0,0,1,arr1);
 CopyBuffer(hnd2,0,0,1,arr2);
 CopyBuffer(hnd3,0,0,1,arr3);
 
 if(arr1[0]>70 && arr2[0]>70 && arr3[0]>70) res=1.0;                    //si todos los indicadores están en la zona de sobrecompra, vendemos
 if(arr1[0]<30 && arr2[0]<30 && arr3[0]<30) res=0.0;                    //si todos los indicadores están en la zona de sobreventa, compramos
 
 if(arr1[0]<30 && arr2[0]<30 && arr3[0]>70) res=0.5;                    //si 2 están sobrevendidos y uno está sobrecomprado, no hay señal
 if(arr1[0]<30 && arr2[0]>70 && arr3[0]<30) res=0.5;
 if(arr1[0]>70 && arr2[0]<30 && arr3[0]<30) res=0.5;
 
 if(arr1[0]>70 && arr2[0]>70 && arr3[0]<30) res=0.5;                    //si 2 están sobrecomprados y uno está sobrevendido, no hay señal
 if(arr1[0]>70 && arr2[0]<30 && arr3[0]>70) res=0.5;
 if(arr1[0]<30 && arr2[0]>70 && arr3[0]>70) res=0.5;
 
 if(arr1[0]<30 && arr2[0]<30 && (arr3[0]>40 && arr3[0]<60)) res=0.0;    //si 2 están sobrevendidos y el tercero está en la zona de 40 a 60, es una señal de compra
 if(arr1[0]<30 && (arr2[0]>40 && arr2[0]<60) && arr3[0]<30) res=0.0;
 if((arr1[0]>40 && arr1[0]<60) && arr2[0]<30 && arr3[0]<30) res=0.0;
 
 if(arr1[0]>70 && arr2[0]>70 && (arr3[0]>40 && arr3[0]<60)) res=1.0;    //si 2 están sobrecomprados y el tercero está en la zona de 40 a 60, es la señal de venta
 if(arr1[0]>70 && (arr2[0]>40 && arr2[0]<60) && arr3[0]>70) res=1.0;
 if((arr1[0]>40 && arr1[0]<60) && arr2[0]>70 && arr3[0]>70) res=1.0;
 
 return(res);
}

Luego, escribiremos el resto de las funciones de servicio y probaremos el Asesor Experto (EA) a partir del inicio del año 2017 en EURUSD, timeframes М15 y М5 (el código completo del EA se adjunta al artículo):

EURUSD M15

EURUSD M5

Se ve que aunque hemos descrito las condiciones claras para las combinaciones de tres indicadores y estas condiciones son lógicas y no contradictorias, este enfoque resultó ser demasiado estrecho y no flexible. Por término medio, el sistema pierde y no gana durante el período de 8 meses. Para hacer que gane, sería aconsejable repasar muchas combinaciones de las condiciones, tal vez, añadir más osciladores. Pero aquí no hay nada que optimizar, ya que las condiciones han sido establecidas de modo extremadamente concreto.

Intentaremos «extender por el plato» nuestras nociones sobre las condiciones en las que el sistema empezará a ganar aplicando la lógica difusa.

Creación de un modelo de la lógica difusa

Primero, hay que conectar la librería Fuzzy, o para ser más exacto, uno de dos modelos disponibles de la lógica difusa, Mamdanio Sugeno. La diferencia entre ellos consiste en que, en caso de Sugeno, en la salida se obtiene el modelo lineal sin crear la variable de salida en forma de un conjunto de términos borroso, mientras que en Mamdani este elemento está presente. Puesto que el artículo se escribe para los traders difusos, vamos a usar Mamdani. Pero eso no quiere decir que no le conviene el modelo Sugeno para algunas determinadas tareas: siempre se puede y hay que experimentar, basándose en la comprensión básica de la lógica difusa.

#include <Math\Fuzzy\MamdaniFuzzySystem.mqh>
CMamdaniFuzzySystem *OurFuzzy=new CMamdaniFuzzySystem();

La librería está conectada, la referencia a la clase Mamdani está declarada. Es todo lo que necesitamos para empezar a trabajar. 

Ahora hablaremos sobre las etapa principales de la construcción de una inferencia difusa. Ocupa un lugar central en los sistemas del modelado difuso. El proceso de la inferencia difusa representa un determinado procedimiento, o un algoritmo de la obtención de las inferencias difusas a base de las premisas borrosas con el uso de las operaciones principales de la lógica difusa. 

Se puede destacar 7 etapas de la construcción de la inferencia difusa:

  •  Determinación de la estructura del sistema de la inferencia difusa
  • En la etapa del diseño se determina el número de las entradas y salidas, así como de las funciones de pertenencia. En nuestro caso, habrá 3 entradas, 1 salida, y cada una de ellas tendrá 3 funciones de pertenencia.

  • Determinación de la base de las reglas del sistema de la inferencia difusa
  • En el proceso del desarrollo, vamos a crear sus propias reglas para la inferencia difusa basándose en nuestra evaluación experta de nuestro sistema comercial.

  • Fuzzificación de las variables de entrada
  • Es el establecimiento de la correspondencia entre el valor numérico de la variable de entrada del sistema de la inferencia difusa y el valor de la función de pertenencia del término correspondiente de la variable lingüística.

  • Agregación
  • Es el procedimiento de la determinación del grado de la veracidad de condiciones para cada una de las reglas de la inferencia difusa.

  • Activación
  • Es el proceso de la búsqueda del grado de la veracidad de cada una de las expresiones lógicas elementales (subconclusiones) que componen los consecuentes de los núcleos de todas las reglas difusas de producción.

  • Acumulación
  • Es el proceso de la búsqueda de la función de pertenencia para cada una de las variables lingüísticas de salida.

  • Defuzzificación
  • Es el proceso del tránsito de la función de pertenencia de la variable lingüística hacia su valor nítido (numérico). Este valor será nuestro valor de salida en el rango de o a 1.

Cabe destacar que es necesario ejecutar solamente el punto 1 y 2, el sistema se encargará de los demás sin nuestra intervención. Si le interesan los detalles del trabajo de la lógica difusa en todas las etapas, infórmese aquí.

Determinación de la estructura del sistema de la inferencia difusa

Continuamos la creación del modelo. Vamos a determinar los objetos de tres entradas y de una salida, así como los objetos auxiliares para un trabajo más cómodo con la lógica:

CFuzzyVariable *firstInput=new CFuzzyVariable("rsi1",0.0,1.0);
CFuzzyVariable *secondInput=new CFuzzyVariable("rsi2",0.0,1.0);
CFuzzyVariable *thirdInput=new CFuzzyVariable("rsi3",0.0,1.0);
CFuzzyVariable *fuzzyOut=new CFuzzyVariable("out",0.0,1.0);

CDictionary_Obj_Double *firstTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *secondTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *thirdTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *Output;

Como entradas, vamos a usar tres RSI con períodos diferentes. Puesto que el oscilador RSI siempre se encuentra en el diapasón 0-100, sería necesario crear una variable de la misma dimensionalidad para él. Pero por el tema de comodidad, vamos a normalizar los valores del indicador al rango 0-1. Simplemente no olvide que la variable creada debe tener la misma dimensionalidad que la dimensionalidad del vector de entrada, es decir, admitir todos los valores. En la salida, también establecemos el rango de 0 a 1.

Basándose en el punto 1 de la creación de la lógica difusa, también es necesario definir y configurar la funciones de pertenencia. Lo haremos en el handle de eventos OnInit():

firstInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
firstInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
firstInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
OurFuzzy.Input().Add(firstInput);
   
secondInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
secondInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
secondInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
OurFuzzy.Input().Add(secondInput);
   
thirdInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
thirdInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
thirdInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
OurFuzzy.Input().Add(thirdInput);
   
fuzzyOut.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
fuzzyOut.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(Gposition, Gsigma)));
fuzzyOut.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
OurFuzzy.Output().Add(fuzzyOut);

Ahora intentaremos comprender qué es una función de pertenencia y qué papel desempeña.

Hemos creado tres términos para cada una de las variables de entrada (y uno para la de salida): "buy", "neutral", "sell", cada uno de los cuales tiene su función de pertenencia. En otras palabras, ahora podemos dividir las indicaciones del oscilador en 3 grupos borrosos, y asignar a cada grupo un rango de valores usando la función de pertenencia. Hablando en la lengua de la lógica difusa, hemos creado 4 conjuntos de términos, cada uno de los cuales tiene 3 términos. Para demostrar lo dicho, vamos a escribir un simple script que puede usarse para la representación visual de los términos y sus funciones de pertenencia:

//+------------------------------------------------------------------+ 
//|                                      Our MembershipFunctions.mq5 | 
//|                        Copyright 2016, MetaQuotes Software Corp. | 
//|                                             https://www.mql5.com | 
//+------------------------------------------------------------------+ 
#include <Math\Fuzzy\membershipfunction.mqh> 
#include <Graphics\Graphic.mqh> 
//--- Create membership functions 
CZ_ShapedMembershipFunction func2(0.0, 0.6);
CNormalMembershipFunction func1(0.5, 0.2); 
CS_ShapedMembershipFunction func3(0.4, 1.0);

//--- Create wrappers for membership functions 
double NormalMembershipFunction1(double x) { return(func1.GetValue(x)); } 
double ZShapedMembershipFunction(double x) { return(func2.GetValue(x)); }
double SShapedMembershipFunction(double x) { return(func3.GetValue(x)); }
 
//+------------------------------------------------------------------+ 
//| Script program start function                                    | 
//+------------------------------------------------------------------+ 
void OnStart() 
  { 
//--- create graphic 
   CGraphic graphic; 
   if(!graphic.Create(0,"Our MembershipFunctions",0,30,30,780,380)) 
     { 
      graphic.Attach(0,"Our MembershipFunctions"); 
     } 
   graphic.HistoryNameWidth(70); 
   graphic.BackgroundMain("Our MembershipFunctions"); 
   graphic.BackgroundMainSize(16); 
//--- create curve 
   graphic.CurveAdd(NormalMembershipFunction1,0.0,1.0,0.01,CURVE_LINES,"[0.5, 0.2]"); 
   graphic.CurveAdd(ZShapedMembershipFunction,0.0,1.0,0.01,CURVE_LINES,"[0.0, 0.6]"); 
   graphic.CurveAdd(SShapedMembershipFunction,0.0,1.0,0.01,CURVE_LINES,"[0.4, 1.0]"); 
//--- sets the X-axis properties 
   graphic.XAxis().AutoScale(false); 
   graphic.XAxis().Min(0.0); 
   graphic.XAxis().Max(1.0); 
   graphic.XAxis().DefaultStep(0.1); 
//--- sets the Y-axis properties 
   graphic.YAxis().AutoScale(false); 
   graphic.YAxis().Min(0.0); 
   graphic.YAxis().Max(1.1); 
   graphic.YAxis().DefaultStep(0.1); 
//--- plot 
   graphic.CurvePlotAll(); 
   graphic.Update(); 
  }

Iniciamos el script en el gráfico:

Fig. 1. Funciones de pertenencia.

He elegido estas funciones de pertenencia porque tienen sólo 2 parámetros de entrada que pueden ser optimizados (de eso nos ocuparemos más adelante, en la etapa del testeo del sistema). Además, describen muy bien la posición extrema y central del sistema. Usted puede usar cualquier función de pertenencia disponible en el conjunto en la librería Fuzzy.

Vamos a tomar como principio que la posición del oscilador en los extremos indica en el próximo cambio de su dirección, y por consiguiente, en la próxima reversión de la tendencia. Por eso, la aproximación del oscilador a cero indicará en la posibilidad del inicio del crecimiento. El movimiento del oscilador a la marca 0,5 se acompaña de la degresión de CZ_ShapedMembershipFunction  o el término "Buy zone". Al mismo tiempo va a subir la incertidumbre de CNormalMembershipFunction "Neutral zone", la cual al final se cambiará por CS_ShapedMembershipFunction  o "Sell zone" a la medida de que el oscilador vaya acercándose a 1. A base de este principio, están organizadas todas las entradas y las salidas que van a devolver la pertenencia de la indicación del indicador a una u otra zona que tiene los límites borrosos.

No hay ninguna limitación en cuanto al número de las funciones de pertenencia para cada variable. Usted puede establecer 5, 7, 15 funciones en vez de tres, pero naturalmente dentro de los límites del sentido común y en nombre de la lógica difusa.

Formación de la base de las reglas del sistema de la inferencia difusa

En esta fase, vamos a añadir una base de conocimientos al sistema, a la que vamos a orientarnos a la hora de tomar decisiones borrosas.

   rule1 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is buy) then (out is buy)");
   rule2 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is sell) then (out is sell)");
   rule3 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is neutral) and (rsi3 is neutral) then (out is neutral)"); 
   
   rule4 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is sell) and (rsi3 is buy) then (out is neutral)");
   rule5 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is buy) then (out is neutral)");
   rule6 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is sell) then (out is neutral)"); 
   
   rule7 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is neutral) then (out is buy)");
   rule8 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is neutral) then (out is sell)");
   rule9 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is neutral) and (rsi3 is buy) then (out is buy)");
   rule10 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is neutral) and (rsi3 is sell) then (out is sell)");
   rule11 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is buy) and (rsi3 is buy) then (out is buy)");
   rule12 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is sell) and (rsi3 is sell) then (out is sell)");

   OurFuzzy.Rules().Add(rule1);
   OurFuzzy.Rules().Add(rule2);
   OurFuzzy.Rules().Add(rule3);
   OurFuzzy.Rules().Add(rule4);
   OurFuzzy.Rules().Add(rule5);
   OurFuzzy.Rules().Add(rule6);
   OurFuzzy.Rules().Add(rule7);
   OurFuzzy.Rules().Add(rule8);
   OurFuzzy.Rules().Add(rule9);
   OurFuzzy.Rules().Add(rule10);
   OurFuzzy.Rules().Add(rule11);
   OurFuzzy.Rules().Add(rule12);



La base de datos tiene que contener por lo menos una condición lógica: ella se considera incompleta si por lo menos un término no participa en las operaciones lógicas. El número total de las condiciones lógicas puede ser ilimitado.

En nuestro ejemplo, se establecen 12 condiciones lógicas cuya ejecución influye en la inferencia difusa. Por tanto, todos los términos participan en las operaciones lógicas. Por defecto, a todas las expresiones lógicas se les asignan los mismos coeficientes de peso iguales a 1, y en este ejemplo, no vamos a cambiarlos.

Si los 3 indicadores se encuentran en la zona borrosa de compras, en la salida habrá una señal borrosa de compra, lo mismo es cierto para las ventas y la señal neutral (las reglas 1-3).

Si dos indicadores muestran la compra y uno muestra la venta, los valores de salida van a ser neutrales, o sea, no estamos seguros (las reglas 4-6).

Si dos indicadores muestran la compra o la venta y uno es neutral, a la salida se le asigna la compra o la venta (las reglas 7-12).

Claro que no es la única versión de la creación de la base de reglas, Usted puede experimentar. Esta base de reglas se basa sólo en mi evaluación «de experto» y en la visión de cómo debe funcionar el sistema.

Obtención del valor nítido de la salida tras la defuzzificación

Nos queda calcular nuestro modelo y obtener un resultado en forma de los valores de 0 a 1. Los valores cercanos a 0 van a señalar en una fuerte señal de compra, los próximos a 0,5, en la neutralidad, y los cercanos a 1, en una fuerte señal de venta.

double CalculateMamdani()

{
 CopyBuffer(hnd1,0,0,1,arr1);
 NormalizeArrays(arr1);
   
 CopyBuffer(hnd2,0,0,1,arr2);
 NormalizeArrays(arr2);
    
 CopyBuffer(hnd3,0,0,1,arr3);
 NormalizeArrays(arr3);
     
 firstTerm.SetAll(firstInput,arr1[0]);
 secondTerm.SetAll(secondInput,arr2[0]);
 thirdTerm.SetAll(thirdInput,arr2[0]);
       
 Inputs.Clear(); 
 Inputs.Add(firstTerm);
 Inputs.Add(secondTerm);
 Inputs.Add(thirdTerm);
      
 CList *FuzzResult=OurFuzzy.Calculate(Inputs);
 Output=FuzzResult.GetNodeAtIndex(0);
 double res = Output.Value();
 delete FuzzResult;

 return(res);
}

En esta función, obtenemos los valores de tres osciladores RSI con diferentes períodos, los normalizamos en el rango de 0 a 1 (se puede dividir simplemente un valor por 100), actualizamos la lista con objetos del diccionario Fuzzy (con últimos valores de los indicadores), la enviamos para los cálculos, creamos la lista para la variable de salida y obtenemos el resultado en la variable res.

Adición de las funciones de servicio y optimización/simulación del sistema obtenido

Puesto que se trata del aprendizaje automático o por lo menos de sus gérmenes, vamos a sacar algunos parámetros en los inputs y optimizarlos.

input string Fuzzy_Setings;        //Fuzzy optimization settings
input double Gsigma = 0.5;         //sigma From 0.05 to 0.5 with 0.05 step
input double Gposition=0.5;        //position From 0.0 to 1.0 with 0.1 step
input double MinNeutralSignal=0.4; //MinNeutralSignal from 0.3 to 0.5 with 0.1 step
input double MaxNeutralSignal=0.6; //MaxNeutralSignal from 0.5 to 0.7 with 0.1 step

Se optimizarán los parámetros de la gaussiana (función de la pertenencia) en la salida de la lógica difusa. Vamos a desplazar su centro por el eje Х (parámetro Gposition), cambiar la sigma (estrechar y comprimir la campana, parámetro Gsigma). Así, obtendremos una configuración del sistema más fina si las señales RSI llegarán a ser asimétricas para las compras y las ventas.

Vamos a optimizar adicionalmente las condiciones para la apertura de las transacciones: el valor mínimo y máximo de la señal neutral (dentro del rango entre estos valores no vamos a abrir nuevas posiciones porque la señal no estará definida).

En el siguiente código, se muestra el procesamiento de la señal en la salida de la lógica difusa:

void OnTick()
  {
//---
   if(!isNewBar())
     {  
      return;
     }
     
   double TradeSignal=CalculateMamdani(); 
   
   if(CountOrders(0)!=0 || CountOrders(1)!=0)                                           //si hay posiciones abiertas
      {
       for(int b=OrdersTotal()-1; b>=0; b--)
         {
          if(OrderSelect(b,SELECT_BY_POS)==true)
           {
            if(OrderSymbol()==_Symbol && OrderMagicNumber()==OrderMagic)
             {
              if(OrderType()==OP_BUY && TradeSignal>=MinNeutralSignal)                  //tenemos seleccionada la orden de compra y la señal comercial supera el límite izquierdo de la señal neutral
               {                                                                        //es decir, existe una señal neutral o una señal de venta
                if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,Red))       //entonces, cerramos la posición de compra
                 {
                  if(TradeSignal>MaxNeutralSignal)                     //si la orden está cerrada y existe una señal de venta (ha salido fuera del límite derecho de la señal neutral), abrimos en seguida una posición de venta
                  {
                   lots = LotsOptimized(); 
                   if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0,0,NULL,OrderMagic,Red)){
                     };
                  }
                 }
               }
               if(OrderType()==OP_SELL && TradeSignal<=MaxNeutralSignal)                 //está seleccionada la orden de venta....///.... los mismo que para la posición de compra, pero todas las condiciones son contrarias
               {
                if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,Red))
                 {
                  if(TradeSignal<MinNeutralSignal)
                  {
                   lots = LotsOptimized(); 
                   if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0,0,NULL,OrderMagic,Green)){
                    };
                  }
                 }
               }
             }
            }
          }
         return;
        }
                 //si no ha posiciones, abrimos las posiciones según las señales
   lots = LotsOptimized(); 
   if(TradeSignal<MinNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_BUY))
     { 
      if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0,0,NULL,OrderMagic,Green)){
       };
     }
   else if(TradeSignal>MaxNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_SELL))
     {
      if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0,0,NULL,OrderMagic,Red)){
       };
     }
   return;
     
  }

Los cálculos van a realizarse en una barra nueva para acelerar el ejemplo demostrativo. Usted puede cambiar la lógica a su juicio (por ejemplo, tradear en los ticks, simplemente quitando la comprobación de la nueva barra).

Si hay posiciones abiertas y la señal contradice a la posición actual o no está definida, entonces la cerramos. Si hay una condición para la apertura de una posición opuesta, la abrimos.

El Stop Loss no se utiliza en este sistema porque es reverso, y el cierre/reapertura de las transacciones se realiza según las señales.

En el EA se utiliza la librería MT4Orders para un trabajo más fácil con las órdenes y para poder transmitir rápidamente el código en el lenguaje mql4.

Proceso de la simulación

Optimizamos el sistema con los ajustes mostrados en las capturas de pantalla, en el timeframe М15, para 8 meses por los precios de apertura, usando fast genetic based algorithm y el criterio de optimización Balance + max Profit.



Seleccionamos el mejor resultado de la optimización:

Comparamos con lo que había en el caso de la simulación del modelo nítido:

Las funciones de pertenencia obtenidas en la salida, tras la optimización (las de entrada han quedado sin cambios porque no las hemos optimizado):

Lo que era antes:

Ahora:


Optimizamos el sistema con los mismos ajustes, pero en M5:

Comparamos con lo que había en el caso de la simulación del modelo nítido:

Las funciones de pertenencia obtenidas en la salida, tras la optimización (las de entrada han quedado sin cambios porque no las hemos optimizado):

Lo que era antes:

Ahora:

En ambos casos, hemos obtenido el desplazamiento de la gaussiana (zona neutral) en dirección de las compras, y el número de las posiciones largas prevalece sobre el número de la cortas. Eso significa que las señales de compra y de venta han resultado ser asimétricas en este determinado intervalo del historial, de lo que no podíamos saber sin hacer este experimento. Probablemente, el sistema de tres indicadores RSI se encontraba con más frecuencia en la zona de sobrecompra (área 1) que en la zona de sobreventa (área 0), y la optimización de la gaussiana ayudó a suavizar este desbalance. En cuanto a la propia inferencia difusa, me es difícil imaginar analíticamente porqué esta configuración de salida contribuía a la mejora de las indicaciones del sistema comercial, ya que el proceso de la defuzzificación según el método del centro de gravedad, en combinación con todas las pertenencias de las entradas a los conjuntos difusos, por sí mismo ya es un sistema complejo.

El sistema resultó ser bastante estable a lo largo de 8 meses, aunque sólo 4 parámetros fueron optimizados. Incluso se puede reducirlos a dos (Gsigma y Gposition), porque los demás 2 han influido débilmente en el resultado, encontrándose siempre en la zona de 0,5. Vamos a considerar este resultado como positivo para un sistema experimental que tiene que mostrar cómo se puede reducir el número de los parámetros optimizados, al introducir el elemento de la lógica difusa en el sistema comercial. Como contrapeso, tendríamos que crear un gran número de los criterios de optimización para las reglas nítidas, lo que aumentaría la complejidad del desarrollo del sistema y el número de los parámetros a optimizar.

Cabe destacar que es un ejemplo bastante crudo de la construcción de un sistema comercial a base de la lógica difusa, porque he cogido una estrategia primitiva con los osciladores RSI, incluso sin usar los Stop Loss. Pero según mi opinión, eso es suficiente para comprender la aplicación de la lógica difusa a la creación de un sistema comercial.

Conclusión

La lógica difusa permite crear rápidamente los sistemas con las reglas difusas que se optimizan muy fácilmente. En este caso, el proceso complicado de la selección de los parámetros del sistema comercial se realiza mediante de la optimización genética, librando al desarrollador de la rutina de buscar una estrategia comercial, desarrollar y algoritmizar muchas reglas del sistema comercial. En combinación con otros modos del aprendizaje automático (por ejemplo, redes neuronales), este enfoque permite alcanzar unos resultados espectaculares. Reduce la posibilidad del sobreajuste (overfitting) y la dimensionalidad de los datos de entrada (había 3 indicadores RSI con diferentes períodos, y se consiguió una señal que describía la situación de mercado de manera más generalizada y completa, en comparación con cada uno de los indicadores por separado).

Si le queda la pregunta sobre cómo comprender el funcionamiento de la lógica difusa, pregúntese cómo piensa, con qué términos opera y de acuerdo con qué bases de reglas toma las decisiones.

Voy a mostrar otro ejemplo para consolidar los conocimientos. Supongamos que tiene 3 deseos: salir de marcha, ver una película o salvar el mundo. El término «ver una película en casa» tiene el mayor peso porque ya está en casa y no tiene que esforzarse. Se puede ir de fiesta si alguien le invita y le lleva, pero puesto que eso todavía no ha ocurrido, la probabilidad de salir es media. Y finalmente, para salvar el mundo, hay que movilizar todos sus capacidades sobrenaturales, poner el traje del superman y luchar contra un monstruo de otro planeta. Es poco probable que decida hacerlo hoy y no lo aplace para mañana, por eso las probabilidades son pocas.

La inferencia difusa será aproximadamente la siguiente: lo más probable Usted se quedará en casa, o tal vez vaya de fiesta, pero lo que es cierto es que no va a salvar el mundo hoy. Después de la defuzzificación, nosotros podríamos evaluar nuestras probabilidades según la escala de 10, donde, 0 es «quedarse en casa», 5 es «ir de fiesta», 10 es «luchar contra el monstruo». Está claro que la inferencia nítida estaría dentro de los límites de 0 a 3, o sea, lo más probable Usted se quedaría en casa. Nuestro sistema comercial funciona usando el mismo principio: compara las indicaciones de tres indicadores, y a través de las condiciones lógicas, determina qué opción es más conveniente en este momento, la compra la venta o quedarse en el banquillo.

Aquí tiene las opciones para mejorar este ejemplo (para el desarrollo personal):

  • Aumento del número de las entradas y condiciones lógicas. De esta manera, aumentamos la capacidad del sistema y le damos la posibilidad de adaptarse mejor al mercado.
  • Optimización no sólo de los parámetros de la gaussiana de salida, sino también de todas las funciones de la pertenencia de las entradas y salidas.
  • Optimización de la base de reglas.
  • Optimización de los pesos de las expresiones lógicas.
  • Creación del círculo de varios modelos difusos que se encargan de diferentes aspectos del sistema comercial.
  • Uso de las inferencias difusas como pronósticos (selección de rasgos, feature selection en inglés) y/o las variables objetivas para las redes neuronales.

Si le interesa este artículo y yo recibo una cantidad suficiente de comentarios, se podrá considerar la opción de escribir un nuevo artículo en el que trataremos de combinar la lógica difusa con la red neuronal.

Abajo se adjuntan los códigos fuente de los Asesores Expertos y el script de comprobación para las funciones de pertenencia. Para el funcionamiento y la compilación del EA, hay que descargar la librería MT4Orders y la librería actualizada Fuzzy.