//+------------------------------------------------------------------+
//|                                                       YsFann.mq4 |
//|                                                            YsEkU |
//+------------------------------------------------------------------+
#property copyright "YsEkU"
#property link      ""
//--
#include <Fann2MQL.mqh>


extern int capas_rn = 4;   // Numero total de capas. entrada + ocultas + salida.
extern int entrada_rn = 3; // Numero de perceptrones en la capa de entrada. 
extern int oculta1_rn = 8; // Numero de perceptrones en la primera capa oculta.
extern int oculta2_rn = 5; // Numero de perceptrones en la segunda capa oculta.
extern int salida_rn = 1;  // Numero de perceptrones en la capa de salida.

extern int maxEntrenamiento = 500; //Maximo numero de tiempo empleado para entrenar la red.
extern double objetivoECM = 0.002; //Error cuadratico medio minimo objetivo.
double datosEntrenamiento[][4];    //Contendrá los datos de nuestra RN. *[][t] -> t = entrada_rn + salida_rn

int ann;                   //Esta variable sera el identificador de nuestra red neuronal.

int b=0;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
//----   
  
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
//----Cuando cerramos, borramos los datos de memoria.
f2M_destroy_all_anns();
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
//----
if(b!=Bars)
   {
   b=Bars;
   int i;
   double ECM;
   ArrayResize(datosEntrenamiento,1);
   Print("=================================== RED NEURONAL ================================");
//----
   Print("##### INICIO #####");
   // Creamos la nueva red neuronal con los parametros deseados.
   ann = f2M_create_standard(capas_rn,entrada_rn,oculta1_rn,oculta2_rn,salida_rn);
   // Comprobamos si está bien creada. 0 = OK, -1 = error
   debug("f2M_create_standard()",ann);
   // Iniciamos la funcion de activacion. Protocolo.
	f2M_set_act_function_hidden (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
	f2M_set_act_function_output (ann, FANN_SIGMOID_SYMMETRIC_STEPWISE);
	// Iniciamos los pesos randomizados
	f2M_randomize_weights (ann, -0.77, 0.77);
	//Informacion sobre la red neuronal creada.
   debug("f2M_get_num_input(ann)",f2M_get_num_input(ann));
   debug("f2M_get_num_output(ann)",f2M_get_num_output(ann));	
//----  
   Print("##### REGISTRO DE DATOS #####");
         /* Ahora preparamos algunos datos de ejemplo (con la salida esperada) y los añadimos al entrenamiento.
         Una vez añadidos todos los datos, los enviamos a las neuronas para que puedan aprender. 
         Este es el patron de ejemplo que vamos a aprender.
         - Hay 3 numeros a,b y c: 
            si a < b && b < c -> salida = 1
            si a < b && b > c -> salida = 0
            si a > b && b > c -> salida = 0
            si a > b && b < c -> salida = 1
         */ 
   // si a < b && b < c -> salida = 1
   prepararDatos("entrenar",1,2,3,1);
   prepararDatos("entrenar",8,12,20,1);
   prepararDatos("entrenar",4,6,8,1);
   prepararDatos("entrenar",0,5,11,1);
   // si a < b && b > c -> salida = 0
   prepararDatos("entrenar",1,2,1,0);
   prepararDatos("entrenar",8,10,7,0);
   prepararDatos("entrenar",7,10,7,0);
   prepararDatos("entrenar",2,3,1,0);
   // si a > b && b > c -> salida = 0
   prepararDatos("entrenar",8,7,6,0);
   prepararDatos("entrenar",20,10,1,0);
   prepararDatos("entrenar",3,2,1,0);
   prepararDatos("entrenar",9,4,3,0);
   prepararDatos("entrenar",7,6,5,0);
   // si a > b && b < c -> salida = 1
   prepararDatos("entrenar",5,4,5,1);
   prepararDatos("entrenar",2,1,6,1);
   prepararDatos("entrenar",20,12,18,1);
   prepararDatos("entrenar",8,2,10,1);
   // Imprimimos los datos introducidos.
   prtArrayDatos();
//----      
   Print("##### ENTRENAMIENTO #####");
      /* La red necesita entrenar los perceptrones varias veces con los dataos de entrenamiento 
      para obtener buenos resultados, hasta que el ECM sea correcto o hasta el maximo de pases de entrenamiento.
      Un ECM  de 0.1 es suficiente para para reglas sencillas, 0.02 o inferior es mejor para patrones mas complejos 
      */  
   for (i=0;i<maxEntrenamiento;i++) 
   {
   ECM = aprender(); 
   if(ECM < objetivoECM)
      { 
      debug("Entrenamiento finalizado. Pases ",i+1); 
      i = maxEntrenamiento; 
      }
   }
   debug("ECM",f2M_get_MSE(ann));
//----   
   Print("##### EJECUTANDO #####");   
   // El último argumento se reserva a la salida esperada 
   // cuando usamos esta función para registrar ejemplos 
   // entonces lo dejamos a cero.   
   // si lo prefieres, puedes llamar directamente  a la funcion calcular()
   // En este caso, la estructura es calcular(inputVector []);
   // En vez de prepareData("calcular",1,3,1,0); sería:
   // declarar un nuevo array 
   // double inputVector [];     
   // redimensionamos el array 
   // ArrayResize(inputVector, f2M_get_num_input(ann)); 
   // introducimos los datos al array 
   // inputVector [0] = 1; 
   // inputVector [1] = 3;
   // inputVector [2] = 1;
   // llamar a la función calcular, con el array de entrada.
   // resultado = calcular (inputVector); 
   
   debug("1,3,1 -> 0.","");
   prepararDatos("calcular",1,3,1,0);
   
   debug("1,2,3 -> 1.","");
   prepararDatos("calcular",1,2,3,0);
   
   debug("3,2,1 -> 0.","");
   prepararDatos("calcular",3,2,1,0);
   
   debug("45,2,89 -> 1.","");
   prepararDatos("calcular",45,2,89,0);
   
   debug("1,3,23 -> 1.","");
   prepararDatos("calcular",1,3,23,0);
   
   debug("7,5,6 -> 1.","");
   prepararDatos("calcular",7,5,6,0);
   
   debug("2,8,9 -> 1.","");
   prepararDatos("calcular",2,8,9,0);
   
   Print("=============================== FIN DE LA EJECUCION ===========================");
   } 
//----
return(0);
}
  
//+------------------------------------------------------------------+
// Imprimir por consola
//+------------------------------------------------------------------+
void debug(string a, string b) 
{
Print(a+" ==> "+b);
}

//+------------------------------------------------------------------+
// Imprimir por consola los datos usados en el entrenamiento
//+------------------------------------------------------------------+
void prtArrayDatos() 
{
int i,j;
int bufferSize = ArraySize(datosEntrenamiento)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
string lineBuffer = "";
for (i=0;i<bufferSize;i++) 
   {
   for (j=0;j<(f2M_get_num_input(ann)+f2M_get_num_output(ann));j++) 
      {
      lineBuffer = StringConcatenate(lineBuffer, datosEntrenamiento[i][j], ",");
      }
   debug("ArrayDatos["+i+"]", lineBuffer);
   lineBuffer = "";
   }
}

//+------------------------------------------------------------------+
// Preparar los datos. Crea un array y lo envia a entrenar o calcular.
//+------------------------------------------------------------------+
void prepararDatos(string accion, double a, double b, double c, double salida) 
{
double vectorEntrada[];
double vectorSalida[];

//Ajustamos el tamaño de los Arrays.
ArrayResize(vectorEntrada,f2M_get_num_input(ann));
ArrayResize(vectorSalida,f2M_get_num_output(ann));
  
//Introducimos los datos.
vectorEntrada[0] = a;
vectorEntrada[1] = b;
vectorEntrada[2] = c;
vectorSalida[0]  = salida;

//Por último los procesamos.
if (accion == "entrenar") 
   {
   introDatEntr(vectorEntrada,vectorSalida);
   }
if (accion == "calcular") 
   {
   calcular(vectorEntrada);
   }
//Ajustar esta funcion en función de las entradas y salidas.
}

//+------------------------------------------------------------------+
// Añade un conjunto simple de datos de entrenamiento,(datos + salida 
// esperada), al conjunto de datos de entrenamiento.
//+------------------------------------------------------------------+
void introDatEntr(double arrayEntrada[], double arraySalida[]) 
{
int j;
int bufferSize = ArraySize(datosEntrenamiento)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;  
//register the input data to the main array
for (j=0;j<f2M_get_num_input(ann);j++) 
   {
   datosEntrenamiento[bufferSize][j] = arrayEntrada[j];
   }
for (j=0;j<f2M_get_num_output(ann);j++) 
   {
   datosEntrenamiento[bufferSize][f2M_get_num_input(ann)+j] = arraySalida[j];
   }  
ArrayResize(datosEntrenamiento,bufferSize+2);
}

//+------------------------------------------------------------------+
// Aprender. Realiza un unico entrenamiento con todos los datos aportados,
// Esta funcion se utiliza de forma reiterativa hasta que baje el ECM.
//+------------------------------------------------------------------+
double aprender() 
{
int i,j;
double ECM;
double vectorEntrada[];
double vectorSalida[];
ArrayResize(vectorEntrada,f2M_get_num_input(ann));
ArrayResize(vectorSalida,f2M_get_num_output(ann));
int call;
int bufferSize = ArraySize(datosEntrenamiento)/(f2M_get_num_input(ann)+f2M_get_num_output(ann))-1;
for (i=0;i<bufferSize;i++) 
   {
   for (j=0;j<f2M_get_num_input(ann);j++) 
      {
      vectorEntrada[j] = datosEntrenamiento[i][j];
      }
   vectorSalida[0] = datosEntrenamiento[i][3];
   call = f2M_train(ann, vectorEntrada, vectorSalida);
   }
ECM = f2M_get_MSE(ann);
return(ECM);
}

//+------------------------------------------------------------------+
// Realiza el calculo de un conjunto de datos y devuelve el resultado
//+------------------------------------------------------------------+
double calcular(double vectorEntrada[]) 
{
int j;
int out;
double salida;
ArrayResize(vectorEntrada,f2M_get_num_input(ann));
//Enviamos los datos nuevos.
out = f2M_run(ann, vectorEntrada);
//y devolvemos la salida.
salida = f2M_get_output(ann, 0);
debug("Calcular()",MathRound(salida));
return(salida);
}


