English Русский Deutsch 日本語
preview
Características del Wizard MQL5 que debe conocer (Parte 19): Inferencia bayesiana

Características del Wizard MQL5 que debe conocer (Parte 19): Inferencia bayesiana

MetaTrader 5Sistemas comerciales | 27 septiembre 2024, 11:14
25 0
Stephen Njuki
Stephen Njuki

Introducción

Continuamos nuestra explotación del asistente MQL5 repasando la inferencia bayesiana, un método en estadística que procesa y actualiza las probabilidades con cada nuevo aporte de información. Es evidente que tiene un amplio espectro de posibles aplicaciones, sin embargo, para nuestro propósito como operadores, nos centraremos en su papel en la previsión de series temporales. Las series temporales abiertas a los operadores para su análisis son principalmente los precios de los valores negociados, pero como veremos en este artículo, estas series podrían «ampliarse» para considerar también alternativas como el historial de operaciones de los valores.

En teoría, la inferencia bayesiana debería mejorar la adaptabilidad al mercado de cualquier sistema comercial, ya que la reevaluación de cualquier hipótesis en inherente. Esto debería conducir a un menor ajuste de curvas cuando se pruebe con datos históricos y, posteriormente, se realicen recorridos hacia delante o ejercicios con cuentas reales. Pero eso es la teoría y en la práctica la implementación puede hacer naufragar una buena idea, por lo que intentaremos considerar más de una posible implementación de la inferencia bayesiana para este artículo.

Nuestro artículo, por tanto, está estructurado en un formato sencillo que abarca la definición de la inferencia bayesiana, ejemplos de aplicación que cubren ilustraciones en una clase de señal personalizada, una clase de gestión monetaria y una clase de trailing stop; informes de pruebas de estrategias y, por último, una conclusión.


Definición

La inferencia bayesiana (BI, Bayesian Inference) se sostiene mediante la fórmula P(H|E) = [P(E|H) * P(H)] / P(E), donde:

  • H significa la hipótesis, y
  • E la evidencia, tal que;
  • P(H) es la probabilidad a priori de la hipótesis, mientras que,
  • P(E) es la probabilidad de la evidencia, también conocida como verosimilitud marginal.
  • P(H|E) y P(E|H) son las respectivas probabilidades condicionales de lo anterior, y también se denominan probabilidad posterior y verosimilitud respectivamente.

La fórmula anterior, aunque sencilla y directa, presenta un pequeño problema del huevo y la gallina, a saber, cómo encontramos: P(E|H). Esto se debe a que de nuestra fórmula enumerada anteriormente se deduce que su solución:

P(E|H) = [P(H|E) * P(E)] / P(H).

Sin embargo, esto también podría reescribirse como P(E|H) = [P(EH)] / P(H). Lo que nos permitiría hacer algunos arreglos manuales en esta situación, como veremos a continuación.


Clase de señal

La clase de señal suele establecer la posición que debe tomar un Asesor Experto, ya sea larga o corta. Lo hace sumando las ponderaciones de los indicadores, y el valor de la suma oscila siempre entre 0 y 100. Al utilizar BI, nos enfrentamos a un amplio abanico de opciones de series temporales, sin embargo, este artículo, para la clase de señales, sólo proporcionará ilustración con la serie temporal de cambio de precio de cierre.

Para utilizar estas series temporales, o cualquier otro tipo, en primer lugar tenemos que encontrar una forma «sistemática» de clasificar los valores de las series temporales o agruparlos . Este paso obvio es importante porque no sólo normaliza nuestros datos de series temporales, sino que nos permite identificarlos correctamente al procesar su probabilidad.

La agrupación es no supervisada, y utilizamos un enfoque rudimentario de la misma, asignando un conglomerado a cada punto de datos en función del tipo de cambio de precios. A todos los valores positivos se les asigna un conglomerado, a los valores cero su propio conglomerado y a los valores negativos también el suyo. Hemos considerado enfoques de agrupación alternativos en el pasado dentro de estas series de artículos y el lector está invitado a experimentar con ellos, sin embargo, para este artículo, dado que la agrupación no es el tema principal, hemos considerado algo muy elemental.

Incluso con este enfoque básico de agrupación, queda claro que podemos «identificar» mejor los puntos de datos y, por tanto, evaluar sus probabilidades. Sin ella, dado que los datos son de coma flotante, cada uno habría sido único, lo que en esencia equivaldría a un tipo de conglomerado único y esto claramente frustraría nuestro propósito de obtener y calcular los valores de probabilidad. Nuestro sencillo planteamiento se implementa en la fuente que figura a continuación:

//+------------------------------------------------------------------+
//| Function to assign cluster for each data point                   |
//+------------------------------------------------------------------+
void CSignalBAYES::SetCluster(matrix &Series)
{  for(int i = 0; i < int(Series.Rows()); i++)
   {  if(Series[i][0] < 0.0)
      {  Series[i][1] = 0.0;
      }
      else if(Series[i][0] == 0.0)
      {  Series[i][1] = 1.0;
      }
      else if(Series[i][0] > 0.0)
      {  Series[i][1] = 2.0;
      }
   }
}

Una vez que tengamos los puntos de datos «identificados», procederíamos a calcular la probabilidad posterior tal y como se define en la ecuación de la fórmula anterior. Para ello, sin embargo, necesitaríamos un tipo de conglomerado específico que nos sirva de hipótesis. Este conglomerado debe ser único para las posiciones largas y las cortas, por lo que tenemos parámetros de entrada personalizados para cada una que sirven como índices que identifican el tipo de conglomerado a utilizar en cada caso. Se etiquetan como 'm_cluster_long' y 'm_cluster_short' respectivamente.

Así pues, para obtener la probabilidad posterior, se necesitarían como entradas este índice de conglomerados junto con las series temporales «identificadas» o conglomeradas. Nuestra función que calcula la probabilidad posterior obtiene la probabilidad de que se produzca el conglomerado de los tipos de posición dado el tipo de conglomerado actual. Puesto que estamos proporcionando una serie de puntos de datos recientes, cada uno con su índice de conglomerado en un formato matricial, esencialmente tenemos el punto de datos de índice cero como el conglomerado actual.

Con el fin de resolver nuestra posible situación del huevo y la gallina mencionada anteriormente, obtenemos como resultado P(E|H).

Desde los primeros principios. Dado que H está representado por el índice de posición respectivo como se ha explicado anteriormente, la prueba E es el conglomerado actual o el tipo de conglomerado en el índice cero dentro de la serie de entrada. Así pues, nuestra probabilidad posterior consiste en averiguar la probabilidad de que el tipo de conglomerado de una posición dada se produzca a continuación, dado que la última prueba (conglomerado en el índice 0), se ha producido.

Por lo tanto, para hallar P(E|H) a la inversa, volvemos a visitar la serie de entrada y hacemos una enumeración de cuándo se produjo el índice de posición H seguido del índice cero E (la prueba). Esto también es una probabilidad, por lo que primero enumeraríamos el espacio, es decir, encontraríamos las H ocurrencias y luego, dentro de ese espacio, hallaríamos cuántas veces se sucedieron las pruebas índice.

Esto implica claramente que nuestra serie de entrada tiene una longitud suficiente, sujeta al número de tipos de conglomerados considerados. En nuestro ejemplo, muy sencillo, tenemos 3 tipos de conglomerados (en realidad 2, teniendo en cuenta que el cambio a precio cero rara vez se producirá) y esto podría funcionar con una serie de entrada inferior a 50. Sin embargo, si se opta por un enfoque de agrupación más aventurado en el que se utilicen 5/6 o más tipos de conglomerados, entonces el tamaño por defecto de la serie de entrada debe ser lo suficientemente importante como para captar la aparición de todos estos tipos de conglomerados para que nuestra función posterior funcione. El listado de la función posterior se encuentra a continuación:

//+------------------------------------------------------------------+
//| Function to calculate the posterior probability for each cluster |
//+------------------------------------------------------------------+
double CSignalBAYES::GetPosterior(int Type, matrix &Series)
{  double _eh_sum = 0.0, _eh = 0.0, _e = 0.0, _h = 0.0;
   for(int i = 0; i < int(Series.Rows()); i++)
   {  if(Type == Series[i][1])
      {  _h += 1.0;
         if(i != 0)
         {  _eh_sum += 1.0;
            if(Series[i][1] == Series[i - 1][1])
            {  _eh += 1.0;
            }
         }
      }
      if(i != 0 && Series[0][1] == Series[i][1])
      {  _e += 1.0;
      }
   }
   _h /= double(Series.Rows() - 1);
   _e /= double(Series.Rows() - 1);
   if(_eh_sum > 0.0)
   {  _eh /= _eh_sum;
   }
   double _posterior = 0.0;
   if(_e > 0.0)
   {  _posterior += ((_eh * _h) / _e);
   }
   return(_posterior);
}


Una vez obtenida nuestra probabilidad posterior, ésta representaría la probabilidad de que se produzca el tipo de conglomerado óptimo de la posición (ya sea 'm_cluster_long' o 'm_cluster_short') dado el tipo de conglomerado actual (es decir, la evidencia o el tipo de conglomerado para el punto de datos en el índice cero). Sería un valor comprendido entre 0,0 y 1,0. Para que la hipótesis respectiva, ya sea para posiciones largas o cortas, sea probable, lo ideal sería que el valor devuelto fuera superior a 0,5, aunque el lector podría explorar situaciones especiales en las que un valor ligeramente inferior podría arrojar resultados interesantes.

Sin embargo, el valor decimal tendría que normalizarse al rango estándar de 0 - 100 que emiten las funciones de condición larga y condición corta. Para conseguirlo, simplemente lo multiplicamos por 100,0. A continuación figura la lista típica de una condición larga o corta:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalBAYES::LongCondition(void)
{  int result = 0;
   vector _s_new, _s_old, _s;
   _s_new.CopyRates(m_symbol.Name(), m_period, 8, 0, m_series_size);
   _s_old.CopyRates(m_symbol.Name(), m_period, 8, 1, m_series_size);
   _s = _s_new - _s_old;
   matrix _series;
   _series.Init(_s.Size(), 2);
   for(int i = 0; i < int(_s.Size()); i++)
   {  _series[i][0] = _s[i];
   }
   SetCluster(_series);
   double _cond = GetPosterior(m_long_cluster, _series);
   _cond *= 100.0;
   //printf(__FUNCSIG__ + " cond: %.2f", _cond);
   //return(result);
   if(_cond > 50.0)
   {  result = int(2.0 * (_cond - 50.0));
   }
   return(result);
}

Con esta clase de señal se puede ensamblar fácilmente en cualquier Asesor Experto a través del asistente MQL5 utilizando las guías que están aquí y aquí para los lectores que puedan ser nuevos en el Asistente MQL5.


Clase de gestión monetaria

También se puede implementar una clase de gestión monetaria (MM, Money Management) personalizada que utilice BI. Una vez más, para empezar tendríamos que seleccionar una serie temporal adecuada en la que basar nuestro análisis, pero como se ha aludido en la introducción nuestra elección para este con MM será el rendimiento histórico de las operaciones. Por lo tanto, dado que nuestro Asesor Experto montado por el asistente operará sólo con un símbolo, todo el historial de operaciones disponible para su selección, en la consulta, será aplicable al Asesor Experto.

Al utilizar las series temporales del historial de operaciones como base para el análisis, tomaremos ejemplo de una de las clases de gestión monetaria incorporada que es la «optimización del tamaño», en la que el tamaño del volumen de operaciones se reduce en proporción al número reciente de pérdidas consecutivas. En nuestro caso, sin embargo, reduciremos el lote de tamaño si la probabilidad de nuestro índice de conglomerados preferido (la hipótesis) cae por debajo de otro parámetro optimizable que llamaremos «m_condition».

Así pues, lo que estamos tratando de establecer en esencia es el índice de agrupación ideal en el que podemos utilizar un tamaño de lote regular en proporción al margen libre. Este índice de agrupación (cluster) es un identificador del tipo de curva de renta variable (ya que sólo se negocia un símbolo) en la que podemos escalar el tamaño del lote en proporción al margen libre. La referencia al «tipo de curva de renta variable» es un poco amplia, ya que nuestra agrupación sigue el formato simple que adoptamos en la clase de señales, entonces lo que se está señalando específicamente aquí es el tipo de resultado de la operación, es decir, si es ganadora o perdedora (a los resultados de beneficio nulo se les asigna un índice pero es poco probable que figuren materialmente en el análisis).

Esto significa, por ejemplo, que si el resultado de la operación favorable para el escalado del tamaño del lote con margen libre es un resultado de operación rentable, entonces estaríamos examinando la secuencia de resultados de operaciones anteriores e intentando establecer la probabilidad de tener otro resultado de operación rentable a la luz de las pruebas (el resultado de la operación en el índice cero de la serie de entrada).

Para ello se necesita otro parámetro optimizable en forma de umbral de probabilidad que calibre la probabilidad de que se repitan las condiciones favorables objetivo, de manera que si el resultado posterior no alcanza este umbral, el tamaño de la posición se reduzca en proporción al número de pérdidas contabilizadas, como ocurre en la clase de gestión monetaria original de «tamaño optimizado». El listado de la función optimizar se encuentra a continuación:

//+------------------------------------------------------------------+
//| Optimizing lot size for open.                                    |
//+------------------------------------------------------------------+
double CMoneyBAYES::Optimize(int Type, double lots)
{  double lot = lots;
//--- calculate number of losses orders without a break
   if(m_decrease_factor > 0)
   {  //--- select history for access
      HistorySelect(0, TimeCurrent());
      //---
      int       orders = HistoryDealsTotal(); // total history deals
      int       losses=0;                    // number of consequent losing orders
      //--
      int      size=0;
      matrix series;
      series.Init(fmin(m_series_size,orders), 2);
      series.Fill(0.0);
      //--
      CDealInfo deal;
      //---
      for(int i = orders - 1; i >= 0; i--)
      {  deal.Ticket(HistoryDealGetTicket(i));
         if(deal.Ticket() == 0)
         {  Print("CMoneySizeOptimized::Optimize: HistoryDealGetTicket failed, no trade history");
            break;
         }
         //--- check symbol
         if(deal.Symbol() != m_symbol.Name())
            continue;
         //--- check profit
         double profit = deal.Profit();
         //--
         series[size][0] = profit;
         size++;
         //--
         if(size >= m_series_size)
            break;
         if(profit<0.0)
            losses++;
      }
      //--
      series.Resize(size,2);
      SetCluster(series);
      double _cond = GetPosterior(Type, series);
      //--
      //---
      if(_cond < m_condition)
         lot = NormalizeDouble(lot - lot * losses / m_decrease_factor, 2);
   }
//--- normalize and check limits


...

//---

...

//---

...

//---
   return(lot);
}


Todos los demás parámetros de factor de disminución y porcentaje de margen invertido siguen siendo los mismos que en la clase MM original de «tamaño optimizado».

Para compararlo con el BI, podríamos considerar el Criterio de Kelly, que tiene en cuenta los resultados ganadores y la relación riesgo-recompensa, pero con una visión a largo plazo y no actualizando necesariamente los criterios de asignación a través de los resultados recientes o intermedios. Su fórmula viene dada como K = W - ((1 - W) / R)

Donde:

  • K es el porcentaje de asignación
  • W es el porcentaje de victorias, y
  • R es el factor de beneficio.

Al parecer, este enfoque ha sido adoptado por los gurús de la inversión debido a su perspectiva a largo plazo en la asignación del capital, sin embargo, se puede argumentar que es el posicionamiento el que debe adoptar un enfoque a largo plazo y no la asignación. A menudo se adoptan perspectivas a largo plazo en cuestiones de funcionamiento, sin embargo, cuando hay riesgo de por medio, el corto plazo tiende a ser más crítico, por lo que la ejecución es un tema aparte.

Así pues, las ventajas del BI sobre el Criterio de Kelly (KC, Kelly Criterion) podrían resumirse con el argumento de que el KC supone una ventaja constante en los mercados, lo que puede ser cierto en los casos en los que se tiene un horizonte muy largo. El hecho de ignorar los costes de transacción y el deslizamiento es otro argumento similar en contra de la KC y, aunque ambos podrían ignorarse a muy largo plazo, es justo decir que la forma en que están configurados la mayoría de los mercados es permitir que alguien opere en nombre de, o con el capital de, otra persona. Esto implica intrínsecamente que hay que aplicar un buen grado de sensibilidad a estas excursiones a corto plazo, ya que pueden determinar si al operador o inversor se le sigue confiando el capital en juego.


Clase Trailing Stop

Por último, examinamos una implementación de clase de arrastre personalizada que también utiliza BI. Nuestra serie temporal para esto tendrá que centrarse en el rango de barras de precios, ya que siempre es una buena aproximación a la volatilidad, una métrica clave para influir en cuánto debe ajustarse un nivel de stop loss para las posiciones abiertas. Hemos estado utilizando los cambios en los valores de las series temporales, ya que para la señal utilizamos los cambios en el precio de cierre, mientras que para la MM utilizamos el resultado de la operación (beneficios en contraposición a los niveles de capital de la cuenta), que también son cambios de facto en los niveles de capital de la cuenta. Los cambios aplicados a nuestro rudimentario método de agrupación nos proporcionan un conjunto muy básico pero viable de índices útiles para agrupar estos puntos de datos de coma flotante.

Un enfoque similar para una clase de arrastre experto se centraría en los cambios en el rango de barras de precios de alta a baja. Nuestra hipótesis con este enfoque sería que estamos buscando un índice de cluster ('m_long_cluster' o 'm_short_cluster' ambos podrían ser el mismo en esta situación de trailing) tal que cuando sea más probable que siga en la serie temporal, entonces necesitamos mover nuestro stop loss en una cantidad proporcional al rango de la barra de precios actual.

Hemos utilizado parámetros de entrada separados para las posiciones largas y cortas, pero en principio podríamos haber utilizado uno solo para servir tanto para el ajuste del stop loss de las posiciones largas como para el de las cortas. A continuación figura nuestra lista de aplicación para las posiciones largas:

//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for long position.          |
//+------------------------------------------------------------------+
bool CTrailingBAYES::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp)
  {
//--- check

...

//---
 
...

//---
   sl=EMPTY_VALUE;
   tp=EMPTY_VALUE;
   //
   
   vector _h_new, _h_old, _l_new, _l_old, _s;
   _h_new.CopyRates(m_symbol.Name(), m_period, COPY_RATES_HIGH, 0, m_series_size);
   _h_old.CopyRates(m_symbol.Name(), m_period, COPY_RATES_HIGH, 1, m_series_size);
   _l_new.CopyRates(m_symbol.Name(), m_period, COPY_RATES_LOW, 0, m_series_size);
   _l_old.CopyRates(m_symbol.Name(), m_period, COPY_RATES_LOW, 1, m_series_size);
   _s = (_h_new - _l_new) - (_h_old - _l_old);
   matrix _series;
   _series.Init(_s.Size(), 2);
   for(int i = 0; i < int(_s.Size()); i++)
   {  _series[i][0] = _s[i];
   }
   SetCluster(_series);
   double _cond = GetPosterior(m_long_cluster, _series);
   //
   delta=0.5*(_h_new[0] - _l_new[0]);
   if(_cond>0.5&&price-base>delta)
     {
      sl=price-delta;
     }
//---
   return(sl!=EMPTY_VALUE);
  }

En la sección de pruebas e informes que figura a continuación se ofrece una comparación de este método con otras clases de trailing stop alternativas, como las incorporadas en la librería MQL5.


Pruebas e informes

Realizamos pruebas sobre el EURJPY en el marco temporal de 4 horas para el año 2022. Dado que hemos desarrollado 3 clases personalizadas separadas utilizables en expertos asistentes, montaremos secuencialmente 3 asesores expertos separados con el primero teniendo sólo la clase de señal mientras que la gestión monetaria utiliza lotes fijos y no se utiliza trailing stop; el segundo tendrá la misma clase de señal pero con la adición de la clase de gestión monetaria que hemos codificado anteriormente y sin trailing stop; mientras que el Asesor Experto final tendrá las 3 clases que hemos codificado anteriormente. Las directrices para ensamblar estas clases mediante el asistente están disponibles aquí.

Si realizamos pruebas en los tres Asesores Expertos, obtendremos los siguientes informes y curvas de renta variable:

r0

c0

Informe y curva de renta variable del Asesor Experto sólo con la clase de señal BI.


r05

c05

Informe y curva de renta variable del Asesor Experto sólo con la clase de señal BI y la clase MM.


r1

c1

Informe y curva de renta variable del Asesor Experto con clase de señal BI, MM y trailing.

Parece que a medida que se realiza una mayor adaptación de la BI de la clase de señal a través de la MM a la clase de seguimiento, el rendimiento global tiende a correlacionarse positivamente. Estas pruebas se realizan con garrapatas reales, pero como siempre lo ideal es realizar pruebas independientes durante periodos más largos, y es algo que el lector debe tener en cuenta. Como control, podemos optimizar 3 Asesores Expertos separados que utilizan las clases de la librería. En todas estas pruebas no utilizamos objetivos de precio de salida y sólo nos basamos en la señal de apertura y cierre para controlar las salidas. Elegimos la clase de señal del indicador Awesome Oscillator, la clase de gestión monetaria de tamaño optimizado y las clases de arrastre de media móvil como lo que hay que utilizar en los Asesores Expertos de «control». Pruebas similares a las anteriores arrojan los siguientes resultados:

cr1

cc1

Informe y curva de equidad de Asesor Experto con sólo la clase del indicador Awesome Oscillator.


cr2

cc2

Informe y curva de equidad de control Asesor Experto con 2 de las clases seleccionadas.


cr3

cc3

Informe y curva de equidad de control Asesor Experto con las 3 clases seleccionadas.

El rendimiento de nuestro control sí va a la zaga del experto en BI, con la excepción del tercer recorrido. Nuestra elección de clases alternativas de señal, MM y trailing también influyó mucho en este «resultado», sin embargo, el objetivo general era establecer si existe una variación significativa en el rendimiento entre nuestro Asesor Experto BI y lo que está fácilmente disponible en la librería MQL5 y a eso la respuesta es clara.


Conclusión

Para concluir, hemos probado el papel de la inferencia bayesiana en la construcción de un Asesor Experto sencillo incorporando sus ideas básicas en las tres clases diferentes de pilares de los Asesores Expertos ensamblados con el Asistente MQL5. Nuestro enfoque aquí fue estrictamente introductorio y no cubrió un terreno importante, especialmente en lo que se refiere al uso de algoritmos de agrupación más elaborados o incluso conjuntos de datos multidimensionales. Todas estas son vías que pueden explorarse y que podrían proporcionarle a uno una ventaja si se prueban adecuadamente a lo largo de periodos históricos decentes, sobre datos de ticks de buena calidad. Se pueden realizar muchas más pruebas con la inferencia bayesiana y el lector está invitado a explorarlas, ya que los expertos reunidos por el Asistente siguen siendo una herramienta confiable para probar y crear prototipos de ideas.


Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14908

Archivos adjuntos |
bayes_3.mq5 (7.56 KB)
SignalWZ_19_.mqh (7.89 KB)
TrailingWZ_19.mqh (7.95 KB)
MoneyWZ_19.mqh (9.05 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
Red neuronal en la práctica: La primera neurona Red neuronal en la práctica: La primera neurona
En este artículo, comenzaremos a crear algo que muchos se sorprenden al ver funcionando: una simple y modesta neurona que lograremos programar con muy poco código en MQL5. La neurona funcionó perfectamente en las pruebas que realicé. Bueno, retrocedamos un poco en esta misma serie sobre redes neuronales, para que puedas entender de qué estoy hablando.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Red neuronal en la práctica: Esbozando una neurona Red neuronal en la práctica: Esbozando una neurona
En este artículo, vamos construir una neurona básica. Aunque parezca algo simple, y muchos piensen que el código es totalmente trivial y sin propósito, quiero que tú, querido lector y entusiasta del tema de redes neuronales, te diviertas explorando este sencillo esbozo de una neurona. No tengas miedo de modificar el código para entenderlo mejor.