Comercio Cuantitativo - página 33

 

Negociación de volatilidad bursátil con el proceso Ornstein-Uhlenbeck


Negociación de volatilidad bursátil con el proceso Ornstein-Uhlenbeck

A principios de 2020, el S&P 500 experimentó un aumento significativo de la volatilidad a medida que los precios bajaron considerablemente. En un lapso de un mes, el índice se desplomó en casi mil puntos. Al mismo tiempo, la expectativa de volatilidad futura, basada en las opciones de índice negociadas, también aumentó durante este período, alcanzando un máximo de 66. Se hizo evidente que durante los períodos de volatilidad del mercado cuando el valor del índice disminuyó, el VIX (Índice de Volatilidad) aumentó. El VIX sirve como una estimación futura de la volatilidad. Este fenómeno llevó a los creadores de mercado y a los profesionales del comercio a anticipar que la volatilidad observada persistiría.

En este video, nuestro objetivo es explicar las características del mercado de la volatilidad y discutir una metodología para modelar la volatilidad ajustando la fórmula de Ornstein-Uhlenbeck a un índice de volatilidad específico. Usaremos el método de estimación de máxima verosimilitud para calibrar los tres parámetros del modelo con los datos del mercado. Posteriormente, simularemos este proceso en Python, lo que nos permitirá comprender y analizar la dinámica de la volatilidad a lo largo del tiempo.

Para lograr esto, importaremos varias dependencias como time, math, numpy, pandas, datetime, scipy, matplotlib, pandas_datareader y la función plot_acf del módulo de estadísticas. Los datos que utilizaremos son los datos del S&P 500 desde 2003 en adelante. Para estudiar el agrupamiento de volatilidad y sus propiedades en las series temporales financieras, nos referiremos al trabajo de investigación "Clustering de volatilidad en los mercados financieros" de Ramacant (2005), que explora las propiedades estadísticas de las series temporales financieras. Las tres propiedades importantes en las que nos centraremos son el exceso de volatilidad, las colas pesadas y la agrupación de volatilidad.

El agrupamiento de volatilidad se refiere a la observación de que los grandes cambios en los precios tienden a ser seguidos por otros grandes cambios, independientemente de su dirección, mientras que los pequeños cambios a menudo son seguidos por pequeños cambios. Esta manifestación cuantitativa sugiere que, aunque los rendimientos pueden no estar correlacionados, los rendimientos absolutos o sus cuadrados muestran una pequeña correlación positiva que disminuye gradualmente con el tiempo. Para analizar esto, examinamos los rendimientos logarítmicos, que representan el logaritmo de los cambios de precios a lo largo del tiempo. Al examinar visualmente los rendimientos logarítmicos del S&P 500, podemos observar grupos de gran magnitud durante períodos específicos, como los grupos significativos en 2008-2009 y 2020.

A continuación, evaluamos la correlación entre los retornos de registro retrasados. En particular, no encontramos una autocorrelación estadísticamente significativa en los retornos de registro en el rango de datos especificado. Sin embargo, cuando elevamos el logaritmo al cuadrado volvemos a centrarnos en la magnitud absoluta, observamos una fuerte correlación positiva que se extiende incluso a los días y semanas retrasados. Esto implica que durante los períodos de alta volatilidad, es probable que persista, y durante los períodos de baja volatilidad, también es probable que la tendencia continúe. Este fenómeno se conoce como agrupamiento de volatilidad.

Para visualizar la volatilidad continua durante un número específico de días, seleccionamos una ventana de negociación y calculamos la desviación estándar sobre esa ventana. Para anualizar la volatilidad, tomamos la raíz cuadrada de la cantidad de días de negociación en un año, que normalmente es 252. Este enfoque nos permite observar aumentos significativos en la volatilidad realizada durante ciertos períodos.

Para modelar este proceso de volatilidad realizado, recurrimos a la fórmula de Ornstein-Uhlenbeck. Esta fórmula, también conocida como modelo de Vasicek en matemática financiera, considera tres parámetros: kappa, que representa la tasa de reversión media; theta, la volatilidad promedio alrededor de la cual fluctúan los precios; y sigma, la propia volatilidad. Nuestro objetivo es encontrar valores de parámetros que maximicen la probabilidad de que los datos observados se adhieran a esta distribución.

Para lograr esto, empleamos el método de estimación de máxima verosimilitud (MLE), que se aplica a muestras aleatorias y funciones de densidad de probabilidad. En el caso de la distribución normal, la función de verosimilitud es el producto de las probabilidades de las muestras individuales dados los parámetros. Tomando el logaritmo de la función de verosimilitud, podemos convertir

Ahora que hemos derivado la expectativa y la varianza del proceso de Ornstein-Uhlenbeck, podemos proceder a modelar la volatilidad utilizando este marco. Para ello, calibraremos los parámetros del modelo con los datos del mercado utilizando el método de estimación de máxima verosimilitud (MLE).

Primero, importamos las dependencias necesarias, incluidas bibliotecas como time, math, numpy, pandas, datetime, scipy, matplotlib, pandas_datareader y la función plot_acf del módulo de estadísticas. También importamos los datos del S&P 500 desde 2003, que servirán como nuestros datos de mercado.

A continuación, exploramos el concepto de agrupación de volatilidad en series temporales financieras. El agrupamiento de volatilidad se refiere al fenómeno en el que grandes cambios en los precios tienden a ser seguidos por otros grandes cambios, y pequeños cambios tienden a ser seguidos por pequeños cambios. Observamos visualmente este efecto de agrupamiento al trazar los rendimientos logarítmicos del S&P 500. Podemos ver que durante los períodos de volatilidad del mercado, la magnitud de los rendimientos logarítmicos se agrupa, lo que indica una correlación entre grandes movimientos de precios. Por ejemplo, podemos ver grupos durante la crisis financiera de 2008-2009 y el pico de volatilidad en 2020.

Para cuantificar la correlación entre los rendimientos de los registros, calculamos la función de autocorrelación (ACF). Mientras que los retornos logarítmicos no muestran una autocorrelación significativa, los retornos logarítmicos al cuadrado (que representan la magnitud absoluta) muestran una pequeña correlación positiva que decae lentamente con el tiempo. Esta autocorrelación de la magnitud absoluta confirma la presencia de agrupamiento de volatilidad, donde tienden a persistir períodos de alta volatilidad, mientras que también tienden a persistir períodos de baja volatilidad.

Para analizar más a fondo la volatilidad, calculamos la volatilidad móvil durante un número específico de días calculando la desviación estándar y anualizándola utilizando la raíz cuadrada del número de días de negociación en un año. Al graficar la volatilidad continua, podemos observar períodos de mayor volatilidad, indicados por aumentos significativos en la volatilidad realizada.

Ahora, presentamos la fórmula de Ornstein-Uhlenbeck (OU), que se utiliza para modelar la volatilidad. El modelo OU incorpora la reversión a la media, el nivel medio y la volatilidad en torno al precio medio. Los parámetros del modelo incluyen kappa (tasa de reversión media), theta (nivel promedio) y sigma (volatilidad). Para estimar estos parámetros, aplicamos el método de estimación de máxima verosimilitud (MLE), que implica encontrar los valores de los parámetros que maximizan la probabilidad de que los datos observados provengan de la distribución OU.

Comenzamos discutiendo la función de probabilidad, que es la función de densidad de probabilidad conjunta (pdf) de los datos observados dados los parámetros. En el caso de la distribución normal, la función de verosimilitud es el producto de los valores de pdf individuales. Tomar el logaritmo de la función de verosimilitud simplifica los cálculos, ya que transforma el producto de probabilidades en la suma de logaritmos. Al encontrar el estimador de máxima verosimilitud (MLE) de los parámetros, podemos determinar los valores que maximizan la verosimilitud de los datos observados.

En el caso del proceso OU, necesitamos usar métodos numéricos para encontrar las estimaciones de máxima verosimilitud debido a la no diferenciabilidad de la función de verosimilitud logarítmica. Utilizamos la función scipy.optimize.minimize para minimizar la probabilidad logarítmica negativa, ya que proporciona una solución numérica al problema de maximización. Al definir la función de log-verosimilitud, los parámetros iniciales y las restricciones, podemos estimar los parámetros que maximizan la probabilidad de los datos observados.

Una vez que hemos estimado los parámetros del proceso OU, podemos simular el proceso usando Python. Podemos simular el proceso discretizando los pasos de tiempo y obteniendo un camino en el tiempo o simulándolo como un proceso Itô de tiempo continuo. El último método proporciona una representación más precisa de la dinámica de la volatilidad en momentos específicos.

En conclusión, el texto analiza las características de volatilidad observadas en el S&P 500 durante los períodos de volatilidad del mercado. Introduce el concepto de agrupamiento de volatilidad y demuestra su presencia utilizando rendimientos logarítmicos y rendimientos logarítmicos cuadrados. Luego se presenta el modelo de Ornstein-Uhlenbeck (OU) como marco para modelar la volatilidad, y se usa el método de estimación de máxima verosimilitud (MLE) para estimar los parámetros del modelo. Finalmente, se explica la simulación del proceso OU, lo que permite el análisis y la comprensión de la dinámica de la volatilidad a lo largo del tiempo.

 

La fórmula mágica para negociar opciones sin riesgos



La fórmula mágica para negociar opciones sin riesgos

En este video, aprenderá a usar la fórmula Breeden-Litzenberger para derivar funciones de densidad de probabilidad neutrales al riesgo a partir de precios de opciones. Esta técnica es extremadamente útil cuando calcular los precios de las opciones requiere mucho tiempo y es intensivo en computación, especialmente para dinámicas complejas y escenarios de gran dimensión. La fórmula de Breeden-Litzenberger nos permite calcular derivados complejos una vez para diferentes valores de ejercicio y tiempo hasta el vencimiento, lo que da como resultado una función de distribución de probabilidad neutral al riesgo que simplifica el cálculo de varios derivados complejos.

Para comenzar, comprendamos el concepto de probabilidad neutral al riesgo. El análisis de Feynman-Kac nos permite definir la probabilidad neutral al riesgo como una medida (Q) de la probabilidad neutral al riesgo terminal en el tiempo (t). La función de distribución de probabilidad acumulada (F) representa la distribución de probabilidad neutral al riesgo. La fijación del precio de una opción de compra europea en el tiempo (t) con un ejercicio (k) y un tiempo hasta el vencimiento (tau) se puede hacer tomando la expectativa de pago descontada neutral al riesgo. Esto se puede expresar como la integral de (S_t - k) multiplicada por la función de densidad neutral al riesgo (pdf) entre el precio de ejercicio (k) y el infinito, descontada por la tasa libre de riesgo.

Para calcular la probabilidad neutral al riesgo directamente a partir de esta fórmula, podemos usar la fórmula de Breeden-Litzenberger de 1978. Establece que la primera derivada de la integral con respecto al strike (k) es igual a menos el factor de descuento exponencial multiplicado por (1 - F), donde F es la función de densidad acumulada. La segunda derivada de la integral centrada en el strike (k) extrae la pdf, que es el factor de descuento multiplicado por la pdf neutral al riesgo.

Ahora, analicemos cómo aplicar esta fórmula en Python. Necesitamos importar bibliotecas como NumPy, SciPy, Pandas y Matplotlib. Para el ejemplo, consideraremos una opción de compra europea con volatilidad estocástica bajo el modelo de Heston. El modelo de Heston proporciona la dinámica del activo subyacente y su volatilidad. Inicializamos los parámetros necesarios, como el precio de la acción, el precio de ejercicio, el tiempo hasta el vencimiento, la tasa libre de riesgo y los parámetros del modelo de Heston, como la tasa de reversión media, la varianza a largo plazo, la volatilidad inicial, la correlación y la volatilidad de la volatilidad.

Usando la fórmula de Breeden-Litzenberger, podemos determinar la función de distribución de probabilidad neutral al riesgo. Al aproximar la segunda derivada utilizando la aproximación de diferencias finitas, calculamos la distribución neutral al riesgo para diferentes valores de ejercicio y tiempo hasta el vencimiento. Construimos un pdf 2D para un tiempo de vencimiento particular.

Para calcular los precios de las opciones bajo el modelo de Heston, usamos la función característica y realizamos la integración numérica usando integración rectangular. Definimos la función característica y calculamos la integral compleja sobre un dominio específico usando integración rectangular. El tamaño de paso elegido para la integración afecta la precisión, especialmente para las opciones fuera del dinero.

Comparamos los resultados obtenidos utilizando la integración rectangular con la biblioteca QuantLib, que está implementada en C y proporciona una integración numérica más precisa. Aunque existen algunas diferencias entre los dos enfoques, el error cuadrático medio (MSE) es pequeño. Las discrepancias se deben principalmente a errores de redondeo causados por la representación binaria de valores decimales en Python.

Después de obtener el pdf aproximado discreto, lo multiplicamos por el factor directo. Usamos la interpolación para suavizar la curva y crear una función de distribución neutral al riesgo continua. Finalmente, podemos usar esta distribución neutral al riesgo para cotizar fácilmente varios derivados complejos.

En conclusión, la fórmula de Breeden-Litzenberger nos permite derivar funciones de densidad de probabilidad neutrales al riesgo a partir de los precios de las opciones. Al aproximar la segunda derivada utilizando la aproximación de diferencias finitas y realizando una integración numérica, podemos calcular la distribución neutral al riesgo para diferentes valores de ejercicio y tiempo hasta el vencimiento. Esto nos permite cotizar derivados complejos de manera eficiente.

 

Estrategia comercial de aprendizaje profundo desde el principio hasta la producción utilizando TensorFlow 2.0 y TFX


Estrategia comercial de aprendizaje profundo desde el principio hasta la producción utilizando TensorFlow 2.0 y TFX

Estoy encantado de presentarme como Denis, y le doy una calurosa bienvenida a ClosetoAlgotrading, su canal de referencia para todo lo relacionado con el comercio algorítmico.

Ha pasado bastante tiempo desde que la idea surgió por primera vez en mi mente: crear una estrategia comercial utilizando únicamente el poder del aprendizaje profundo. El concepto gira en torno al desarrollo de una red neuronal capaz de identificar de forma autónoma los parámetros necesarios para ejecutar operaciones rentables. Para embarcarme en este emocionante viaje, he decidido llevar a cabo un experimento en vivo, que espero cautive también su interés. En los próximos episodios, lo guiaré a través de cada paso involucrado en la implementación de un enfoque de ciencia de datos para elaborar una estrategia comercial sólida, desde las etapas iniciales hasta la producción final.

Mi objetivo es cubrir todos los pasos intermedios de manera integral, incluida la preparación de datos, las pruebas rigurosas y más. De cara al futuro, utilizaré TensorFlow 2.0 y exploraré las posibilidades que ofrece la tubería tfx. Tengo la intención de evitar cálculos o reglas complejos, confiando en cambio en el poder del aprendizaje profundo para determinar si podemos crear una empresa verdaderamente rentable. Si esta idea te suena, te animo a que te suscribas al canal y me acompañes en esta fascinante expedición. Juntos, navegaremos por los giros y vueltas del comercio algorítmico y nos esforzaremos por desbloquear su potencial oculto.

Para aquellos que sienten curiosidad por profundizar en el enfoque de la ciencia de datos, una simple búsqueda en Google arrojará varios recursos que aclaran el tema. Entre los resultados, puede encontrar artículos informativos y representaciones visuales que describen los pasos necesarios para seguir esta metodología.

El primer bloque crucial en nuestro viaje es la fase de comprensión empresarial. En esta coyuntura, debemos definir meticulosamente nuestras metas y establecer Indicadores Clave de Desempeño (KPI) que sirvan como valores medibles para evaluar la efectividad de nuestra estrategia en el logro de estos objetivos. Para embarcarse en esta fase, es crucial dedicar tiempo a comprender a fondo las complejidades de su dominio comercial específico. Al obtener una comprensión profunda de su objetivo, puede proceder con claridad y enfoque. Es imperativo discernir si su objetivo es predecir resultados específicos o clasificar fenómenos particulares.

Para determinar la naturaleza de nuestra tarea, debemos buscar respuestas a preguntas fundamentales. Si nuestra consulta gira en torno a "Cuánto" o "Cuántos", nos enfrentamos a una tarea de regresión. Por otro lado, si preguntamos sobre "qué categoría", nos estamos aventurando en el terreno de la clasificación, etc. Una vez que captamos el tipo de tarea que buscamos realizar, es esencial definir las métricas que significarán el éxito. En nuestro caso, estas métricas podrían incluir el retorno de la inversión (ROI) y la precisión. Después de todo, nuestro objetivo final es obtener ganancias del mercado, lo que requiere una comprensión firme de los futuros movimientos de precios.

Predecir efectivamente el movimiento futuro del precio implica no solo determinar la dirección sino también identificar el nivel de precio preciso y el momento en que se alcanzará. Sin embargo, simplemente conocer la dirección es insuficiente. Requerimos el nivel de precio exacto y el momento en que se alcanzará. Para evitar períodos de espera interminables para nuestro precio objetivo, podemos definir un precio objetivo mínimo esperado y un período de espera máximo. En otras palabras, buscamos determinar la dirección de los futuros movimientos de precios, asemejándose a una tarea de clasificación donde el precio puede subir, bajar o permanecer en el mismo nivel.

Para medir inicialmente el rendimiento de nuestro modelo en la predicción de la dirección, podemos emplear la precisión de clasificación. Esta métrica cuantifica el número de predicciones correctas dividido por el número total de predicciones, multiplicado por 100 para expresarlo en porcentaje. Ahora, ¿qué pasa con nuestro nivel de precio objetivo? Para nuestra estrategia comercial, podemos definir el nivel de ganancias como un porcentaje de nuestro capital invertido. Además, debemos determinar nuestra tolerancia al riesgo estableciendo un nivel de stop-loss como el riesgo máximo aceptable para una sola operación. Nuestros niveles de ganancias y stop sirven como valores de ROI para cada operación. Por ejemplo, supongamos que compramos acciones a $100 y las vendemos cuando llegan a $105. Este movimiento de precios del 5% generaría un retorno del 5% de nuestra inversión. Con nuestros niveles de salida definidos, que abarcan tanto el take profit como el stop loss, debemos abordar la cuestión del tiempo. No deseamos esperar indefinidamente a que el precio alcance los niveles deseados.

Por lo tanto, establecemos un período máximo de tenencia, aunque utilizando el volumen negociado en lugar de un marco de tiempo fijo. La razón detrás de esta elección se aclarará en el próximo episodio, donde explicaré la preparación de datos.

Para resumir nuestro enfoque hasta el momento: estamos desarrollando una estrategia intradía que emplea toma de ganancias, stop loss y un período de retención máximo para cerrar posiciones. Para iniciar operaciones, entrenaremos un modelo de clasificación capaz de predecir la dirección del precio futuro: hacia arriba, hacia abajo o plano. Inicialmente, emplearemos la precisión como medida de la eficacia de nuestro modelo. Con esta base establecida, concluimos nuestra discusión de hoy. Hemos definido nuestras metas, objetivos y métricas de desempeño. En el episodio siguiente, profundizaremos en nuestros datos, preparando conjuntos de datos y etiquetas para su posterior análisis y desarrollo.

No te pierdas la próxima entrega de nuestro viaje. Hasta entonces, cuídate, y espero ansiosamente nuestro próximo encuentro.

 

Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Parte II.


Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Parte II.

Estoy encantado de darle la bienvenida a la segunda parte de nuestro viaje cautivador en la creación de un sistema comercial. En el video anterior, discutimos nuestros objetivos y hoy nos enfocaremos en preparar y etiquetar nuestro conjunto de datos. ¡Así que vamos a sumergirnos!

Para comenzar, he definido un conjunto de funciones que nos ayudarán en la preparación de nuestro conjunto de datos. En primer lugar, necesitamos cargar nuestros datos. Para la demostración de hoy, usaré un pequeño conjunto de datos para mantener manejable el tiempo de ejecución del código. Como puede ver, el conjunto de datos comprende datos de ticks, incluida información como Hora, Precio, Volumen, Oferta y Pregunta. A los efectos de esta demostración, utilizaré datos de un período de un año. Comencemos examinando algunas estadísticas para comprender las características del conjunto de datos. Uno de los aspectos clave que observamos es el precio mínimo y máximo, que rondan los $100. Esto es ventajoso ya que permite calcular fácilmente los porcentajes de beneficio.

Además, he introducido un parámetro crucial para el conjunto de datos: el tamaño de la extensión. El spread se calcula como la diferencia entre los precios Ask y Bid. ¿Por qué es importante el tamaño de la extensión? Para ilustrar, consideremos un ejemplo en el que el precio de oferta es de $100 y el precio de venta es de $101. En este caso, el margen es igual a $1. Si tuviéramos que comprar y vender inmediatamente las acciones, siempre perderíamos el diferencial, que, en este ejemplo, asciende a $1. Para obtener información sobre el tamaño de la distribución, calculé la distribución media de todos los días por cada segundo. Como se muestra en el gráfico, el diferencial suele oscilar entre 1 y 2 centavos, con casos ocasionales de diferenciales ligeramente mayores. Según este análisis, podemos decidir ejecutar operaciones solo si el margen es inferior a 3 centavos.

Curiosamente, el gráfico muestra que los diferenciales más grandes tienden a ocurrir en los primeros minutos después de la apertura del mercado. En consecuencia, es prudente saltarse los 10-15 minutos iniciales al implementar una estrategia intradía. Ahora que hemos definido el período de tiempo y examinado el diferencial, podemos proceder a generar etiquetas para nuestro modelo, que predecirán la dirección del movimiento del precio. ¿Cómo generamos estas etiquetas? Como se mencionó en el video anterior, dado que carecemos de disparadores para abrir una posición, debemos generar etiquetas para cada barra en función del rendimiento esperado.

Para lograr esto, emplearemos el método de la ventana, donde las etiquetas se generan cuando los precios cruzan las barreras de la ventana. Así es como funciona: definimos una ventana de n barras de longitud y establecemos barreras de ventana superior e inferior en función de nuestro rendimiento esperado en porcentaje. A medida que deslizamos esta ventana sobre todas las barras, con un tamaño de paso de una barra, si el precio sale de la ventana, la primera barra de la ventana recibirá la etiqueta. Antes de continuar con la generación de etiquetas, establezcamos los parámetros para la ventana. Si bien la idea detrás de esto es simple, seleccionar el tamaño de ventana y los niveles de barrera óptimos puede ser un desafío. Personalmente, he pasado bastante tiempo lidiando con este problema y aún no he encontrado una solución definitiva.

Para enfrentar este desafío, calcularé la volatilidad histórica durante el día y todo el conjunto de datos. Por ejemplo, el gráfico presentado ilustra el cambio de precio en cada tick dentro de un día, junto con la volatilidad correspondiente. Además, podemos evaluar la volatilidad en todo el conjunto de datos. Como se muestra, la volatilidad media es de solo 0,003%, lo que equivale a aproximadamente 30 centavos del precio actual. Sin embargo, no pretendo usar una ventana que abarque todo el día. Para determinar la duración de la ventana, intenté generar 100 ventanas con tamaños aleatorios y evalué la volatilidad media dentro de cada ventana. El gráfico resultante representa la volatilidad media para ventanas de diferentes longitudes. Seleccionando un tamaño de ventana de 50 barras, por ejemplo, podemos anticipar una volatilidad en torno al 0,001%.

Este valor de volatilidad se vuelve útil para definir nuestro rendimiento mínimo esperado y calcular el tamaño de nuestro precio de stop-loss. Con esta información en la mano, podemos proceder a generar barras de volumen a partir de nuestros datos de ticks. El uso de barras en lugar de ticks nos permite calcular la longitud de la ventana más fácilmente, ya que una barra normalmente contiene un volumen similar, lo que garantiza condiciones estables. Para generar una barra de volumen, iteramos a través de las marcas y acumulamos el volumen hasta que supera o iguala un volumen objetivo predefinido (por ejemplo, 1000). Los ticks encontrados durante esta fase de acumulación representan una barra de volumen. Generemos barras de volumen para un solo día como ejemplo. Como se ilustra, obtenemos 179 barras para el día seleccionado.

En consecuencia, el gráfico de precios ahora consta de estas barras de volumen. Además, podemos calcular el cambio porcentual en cada barra y la volatilidad diaria utilizando el precio de cierre. Sin embargo, no tengo la intención de utilizar una ventana que abarque todo el día. Para determinar la longitud de la ventana, empleé la volatilidad media y las ventanas generadas aleatoriamente para todo el conjunto de datos. El gráfico resultante muestra la volatilidad de la ventana en todo el conjunto de datos.

Ahora que hemos completado estos pasos preparatorios, estamos listos para generar las etiquetas. Para esta demostración, he elegido un tamaño de ventana de 50 barras y una rentabilidad esperada de 0,003 %, lo que corresponde a aproximadamente 30 céntimos sobre la base del precio medio. Una vez que concluye el proceso de etiquetado, podemos encontrar varias etiquetas similares, conocidas como etiquetas cruzadas. Para evitar tener etiquetas idénticas para diferentes eventos, solo conservaremos etiquetas con la distancia más cercana entre la primera barra de la ventana y la barra donde el precio cruza la barrera de la ventana. Tras la inspección, observamos que tenemos alrededor de 700 etiquetas, distribuidas uniformemente entre las tres categorías (arriba, abajo y plano).

Ahora, guardemos nuestro conjunto de datos. Crearemos dos archivos: uno que contenga el conjunto de datos de la barra de volumen y otro archivo que contenga información de marca para cada barra. Este último puede resultar útil en nuestro modelo, por lo que vale la pena conservarlo. Con esto, haré una pausa en nuestra discusión por hoy. Creo que hemos cubierto un amplio terreno, y para aquellos interesados en profundizar en el etiquetado de datos, recomiendo explorar los capítulos 3 y 4 del libro de Marcos López de Prado, que brindan información valiosa.

Nuestro próximo paso implicará la ingeniería de funciones y ejecutar todo a través de la canalización tfx. Espero crear un nuevo episodio pronto para compartir más información intrigante.

Hasta entonces, cuídate y espero con ansias nuestro próximo video.

 

Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Parte III. Canalización TFX.


Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Parte III. Canalización TFX.

Estoy encantado de darle la bienvenida a otro episodio de "Cerca de AlgoTrading" conmigo, Denis. Hemos reanudado nuestro experimento después de las vacaciones de Año Nuevo y, aunque el progreso ha sido lento, todavía estamos avanzando. En el video de hoy, veremos más de cerca nuestros datos etiquetados y exploraremos la canalización de TFX. Entonces, ¡vamos a sumergirnos!

En el video anterior, creamos con éxito etiquetas para nuestros datos, pero olvidé mostrarles cómo aparecen en un gráfico. Como repaso rápido, almacenamos todos los datos en un nuevo marco de datos. Avancemos y leamos este marco de datos.

Dentro de nuestro marco de datos, la columna 'dir' contiene las etiquetas, mientras que la columna 'cross_idx' representa el número de tick cuando el precio cruza nuestra ventana definida. Para representar visualmente los eventos de posición de apertura y cierre basados en estas columnas, he creado una función simple. En el gráfico, un evento de posición abierta se indica con un triángulo relleno, mientras que un evento de posición cerrada se representa con un triángulo vacío.

Como puede ver, la mayoría de los eventos abiertos ocurren en puntos máximos o mínimos locales en el gráfico de precios. En el futuro, continuaremos trabajando con un pequeño conjunto de datos, similar al que hemos usado anteriormente. Además, dividiré el conjunto de datos en conjuntos de datos de entrenamiento, evaluación y prueba.

Ahora que tenemos una mejor comprensión de nuestras etiquetas, avancemos al siguiente paso y comencemos a trabajar con la canalización TFX. Para aquellos que no están familiarizados con TFX, significa Tensorflow Extended y es un marco poderoso diseñado específicamente para tareas de aprendizaje automático escalables y de alto rendimiento. La canalización de TFX consta de una secuencia de componentes que realizan varias etapas del flujo de trabajo de aprendizaje automático, como la ingesta de datos, el modelado, el entrenamiento, la inferencia de servicio y la gestión de implementación.

Para familiarizarnos con TFX, recomiendo explorar la página web oficial de Tensorflow TFX, que brinda información detallada sobre sus componentes y cómo se interconectan. Puede encontrar los enlaces relevantes en la descripción del video.

Como también soy nuevo en TFX, aprenderemos juntos en cada episodio. Hoy, nos centraremos en los primeros cuatro componentes de la tubería. Vamos a presentarlos brevemente:

  1. ExampleGen: este componente de entrada inicial de la canalización ingiere el conjunto de datos de entrada y, opcionalmente, lo divide en diferentes subconjuntos. En nuestro caso, no admite directamente divisiones de series temporales personalizadas, por lo que divido manualmente los datos en conjuntos de datos de entrenamiento, evaluación y prueba.

  2. StatisticsGen: este componente calcula estadísticas para el conjunto de datos, proporcionando información sobre la distribución de datos, la desviación estándar, los valores faltantes y más. Genera artefactos estadísticos para su posterior análisis.

  3. SchemaGen: después de examinar las estadísticas, el componente SchemaGen crea un esquema de datos basado en las características de los datos observados. El esquema describe la estructura y las propiedades de nuestros datos.

  4. ExampleValidator: este componente busca anomalías y valores faltantes en el conjunto de datos, utilizando las estadísticas y el esquema como referencias. Ayuda a identificar cualquier patrón de datos inesperado o inconsistente.

Para asegurarnos de que estamos en el camino correcto, usaré el ejemplo del taxi de Chicago proporcionado por el equipo de Tensorflow como plantilla. Este ejemplo demuestra el flujo de trabajo de extremo a extremo, incluido el análisis de datos, la validación, la transformación, el entrenamiento de modelos y el servicio.

Ahora, volvamos a centrarnos en nuestros propios datos. Después de importar los módulos necesarios y configurar las variables necesarias para las carpetas de datos de entrada y salida, podemos intentar cargar nuestros datos en la canalización TFX. La carpeta de entrada contiene subcarpetas para los conjuntos de datos de evaluación, entrenamiento y prueba.

Usando el componente ExampleGen, deberíamos poder cargar fácilmente nuestros datos en la canalización. Sin embargo, parece que ExampleGen no admite directamente divisiones de series temporales personalizadas. De forma predeterminada, divide los datos solo en conjuntos de entrenamiento y evaluación. Afortunadamente, podemos dividir manualmente los datos y configurar nuestra propia configuración de división de entrada, asegurando un mapeo uno a uno entre las divisiones de entrada y salida.

Como resultado, el componente ExampleGen produce dos artefactos: uno para el conjunto de datos de entrenamiento y otro para el conjunto de datos de evaluación. Examinemos los primeros tres elementos de nuestro conjunto de entrenamiento para verificar que coincida con nuestro conjunto de datos original. Continuando, pasamos la salida del componente ExampleGen al componente StatisticsGen. Este componente genera artefactos estadísticos para los conjuntos de datos de entrenamiento y evaluación. Con solo un comando, podemos representar visualmente las estadísticas del conjunto de datos, incluida la distribución de datos, la desviación estándar, los valores faltantes y más.

Aquí, podemos observar las estadísticas del conjunto de datos de entrenamiento, obteniendo información valiosa sobre las características de los datos. También podemos examinar el mismo conjunto de estadísticas para el conjunto de evaluación. Según las estadísticas, notamos que solo el 2% de nuestras etiquetas son distintas de cero, lo que sugiere que nuestros eventos de entrada para operaciones rentables pueden ser atípicos. Esto podría representar un desafío en el futuro debido al desequilibrio entre las clases.

A continuación, generamos el esquema de datos automáticamente utilizando el componente SchemaGen. Este esquema se deriva de las estadísticas observadas, pero también podemos definir nuestra propia descripción de datos si lo deseamos. El resultado es un esquema que proporciona una descripción completa de la estructura y las propiedades de nuestros datos. Finalmente, llegamos al componente ExampleValidator, que valida los datos en función de las estadísticas y el esquema generados. Comprueba si hay anomalías o inconsistencias en el conjunto de datos. Por ejemplo, en el ejemplo del taxi de Chicago, la función '_company' tenía un valor de cadena inesperado. Podemos usar ExampleValidator para detectar tales problemas en nuestro propio conjunto de datos.

En nuestro caso, afortunadamente, no encontramos ninguna anomalía o inconsistencia en nuestro conjunto de datos. Esta es una señal positiva que indica que nuestros datos están relativamente limpios y alineados con nuestras expectativas. Bueno, eso concluye nuestra rápida introducción a TFX. En el próximo episodio, profundizaremos en los componentes TFX restantes y exploraremos cómo transformar nuestros datos y entrenar nuestro modelo.

¡Gracias por mirar, y espero verte en el próximo video!

 

Parte IV. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Canalización TFX 2.


Parte IV. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción. Canalización TFX 2.

Bienvenidos a otro episodio de "Cerca de Algotrading". Soy Denis y hoy vamos a seguir explorando el canal TFX como parte de la construcción de nuestra estrategia comercial.

En el video anterior, cubrimos los primeros cuatro componentes de la tubería TFX: ExampleGen, StatisticsGen, SchemaGen y ExampleValidator. Estos componentes sentaron las bases para nuestra canalización, asegurando la validación y consistencia de los datos.

Ahora, profundicemos en los componentes restantes: Transformer, Trainer, Evaluator, ModelValidator y Pusher. Estos componentes nos permitirán transformar nuestros datos, entrenar nuestro modelo, evaluar su rendimiento, validar el modelo contra una línea de base y, finalmente, llevar el modelo validado a un entorno de producción.

Pero antes de continuar, permítanme abordar algunos puntos importantes. Si bien la tubería TFX ofrece un marco poderoso, vale la pena señalar que aún puede tener algunos errores y ciertos componentes pueden estar en construcción. Sin embargo, abordemos cada paso con cuidado y discutamos cualquier limitación en el camino. En el video anterior, validamos con éxito nuestros datos y ahora es el momento de pasar al paso de transformación. Para ello, utilizaremos el componente Transformer proporcionado por TFX.

El componente Transform es responsable de realizar transformaciones de datos e ingeniería de características que sean consistentes tanto para el entrenamiento como para el servicio. Esto significa que podemos usar la misma función de transformación de datos para entrenar el modelo y usarlo en producción. Esta funcionalidad es una de las principales razones por las que comencé a explorar TFX en primer lugar.

Para comenzar el proceso de transformación, necesitamos crear un par de archivos de Python. El primer archivo contendrá nuestras constantes, como una lista de características numéricas y etiquetas. El segundo archivo contendrá una función de preprocesamiento (preprocessing_fn), que es una función de devolución de llamada utilizada por tf.Transform para preprocesar los datos de entrada. En esta función, definiremos los pasos de transformación de datos y construcción de características. Por ahora, centrémonos en transformar todas las características de entrada numérica en puntuaciones z y cambiar los valores de etiqueta de -1, 0 y 1 a 0, 1 y 2, respectivamente. Esta transformación de etiquetas es necesaria porque el estimador de TensorFlow espera valores positivos.

Es importante tener en cuenta que en la versión actual de TFX, solo se admiten los estimadores de TensorFlow. Si prefiere usar un modelo de Keras, deberá convertirlo en un estimador usando la API model_to_estimator. Aunque la versión 0.21 de TFX afirma admitir modelos Keras para el componente Trainer, he encontrado algunos problemas con su funcionalidad de contexto interactivo. Con suerte, el equipo de TFX abordará estos problemas pronto y proporcionará una herramienta estable y totalmente funcional.

Ahora, procedamos con la transformación de datos. Como puede ver, el componente Transform espera los siguientes parámetros como entrada: los ejemplos generados, el esquema de datos y la ruta al archivo que contiene la función preprocessing_fn. Una vez completada la transformación, podemos pasar a crear y entrenar nuestro modelo. El componente Trainer se encargará de entrenar el modelo en base a las especificaciones que definamos.

En el archivo Python que contiene nuestro modelo y funciones de entrada, definimos una función trainer_fn que será llamada por el componente Trainer. Esta función debería devolver un diccionario con los siguientes elementos:

  • estimador: El estimador TensorFlow utilizado para entrenar el modelo.
  • train_spec: la configuración para la parte de entrenamiento de la llamada TensorFlow train_and_evaluate().
  • eval_spec: la configuración para la parte de evaluación de la llamada TensorFlow train_and_evaluate().
  • eval_input_receiver_fn: la configuración utilizada por el componente ModelValidator al validar el modelo.

Dentro de este archivo, definimos la función _input_fn, que genera las características y etiquetas de entrada para el entrenamiento y la evaluación. También tenemos funciones adicionales, como _example_serving_receiver_fn, que crea las entradas de servicio, y _eval_input_receiver_fn, que prepara las entradas necesarias para TensorFlow Model Analysis (TFMA).

Para crear nuestro estimador, definimos una función build_estimator. En esta función, configuramos el conjunto de características de entrada y creamos nuestro estimador. Vale la pena mencionar que usé DNNLinearCombinedEstimator porque DNNClassifier y DNNEstimator estaban causando errores relacionados con el paso de funciones de TensorFlow 1. Creo que este no es el caso, ya que estamos usando los métodos de TensorFlow 2. Desafortunadamente, no he encontrado una solución a este problema. Sin embargo, el estimador lineal parece funcionar bien.

Ahora que nuestro modelo está definido, podemos proceder a entrenarlo usando el componente Trainer. Como puede ver, el componente Trainer espera un module_file que contenga la función trainer_fn, junto con otros parámetros como transformer_examples, esquema de datos, gráfico de transformación y argumentos de entrenamiento y evaluación. Por ahora, solo hemos especificado el número de pasos para el entrenamiento y la evaluación. Una vez completada la capacitación del modelo, podemos pasar al análisis del modelo mediante TensorFlow Model Analysis (TFMA). Antes de continuar, asegúrese de haber instalado TFMA siguiendo el enlace proporcionado. TFMA nos permite realizar análisis en nuestro modelo, ya sea en todo el conjunto de datos o en segmentos de características específicas. En este caso, realizaremos un análisis en tres sectores: el conjunto de datos completo, la etiqueta y dos características específicas.

Al analizar el conjunto de datos completo, observamos una precisión notable del 98 por ciento. Sin embargo, al examinar el rendimiento del modelo basado en las etiquetas, notamos que predice consistentemente la etiqueta 0. Este resultado era esperado debido a las etiquetas desequilibradas y la ausencia de funciones útiles en nuestro modelo. Sin embargo, TFMA proporciona una forma conveniente de evaluar el rendimiento del modelo.

Más adelante, tenemos el componente ModelValidator, que nos ayuda a validar nuestros modelos exportados. Compara los nuevos modelos con una línea de base (como el modelo de servicio actual) y determina si cumplen con los criterios predefinidos. Esta validación incluye la evaluación de los modelos en un conjunto de datos eval y métricas informáticas como AUC y pérdida. Si las métricas del nuevo modelo cumplen los criterios especificados por el desarrollador en relación con la línea de base, el modelo se considera "suficientemente bueno" y se marca como tal.

Finalmente, tenemos el componente Pusher, que verifica si el modelo ha pasado la validación. Si el modelo cumple con los criterios de validación, se envía a un destino de archivo específico. Este paso garantiza que solo los modelos validados se implementen en el entorno de producción.

Para concluir nuestra canalización, podemos exportar todos los componentes a una canalización de Apache Beam. Luego podemos empaquetar todos los archivos necesarios en un archivo zip. Al desempaquetar los archivos del archivo en nuestro directorio de trabajo en el servidor, podemos ejecutar la canalización. Una vez completada la ejecución, tendremos un modelo entrenado listo para usar con TensorFlow Serving. Si está interesado en aprender cómo iniciar y usar TensorFlow Serving desde un contenedor Docker, puede encontrar un tutorial en video en mi canal.

Aunque hemos cubierto mucho terreno en esta serie, es importante tener en cuenta que la versión actual de TFX todavía tiene ciertas limitaciones. Según sus requisitos específicos, podría ser necesario implementar una canalización que dependa únicamente de la función de transformación con un modelo de Keras. Sin embargo, espero que esta exploración de TFX haya sido informativa y beneficiosa.

En el próximo video, regresaremos a nuestro objetivo principal de desarrollar una estrategia comercial. ¡Estén atentos para eso! No olvides comentar, suscribirte y darle me gusta a este video. ¡Gracias por mirar, y nos vemos en la próxima!

 

Parte V. GIGO. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción.


Parte V. GIGO. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción.

Bienvenido de nuevo a otro episodio de "Cerca de Algotrading". Mi nombre es Denis y estoy aquí para compartir mis experiencias y fallas en el desarrollo de un sistema de comercio de aprendizaje profundo.

He estado dedicando una cantidad significativa de tiempo a implementar todo el sistema con TensorFlow, pero debo admitir que estoy bastante decepcionado con la calidad de la biblioteca. Si bien aprecio el concepto, la documentación proporcionada por TensorFlow ha sido una fuente de frustración. Parece que incluso las mentes brillantes con sus doctorados luchan por escribir una documentación bien estructurada. Aunque ha mejorado en comparación con el pasado, sigue siendo confuso en varios puntos.

Sin embargo, la documentación es solo un problema menor en comparación con el problema mayor que encontré. Muchas funciones simplemente no funcionan como se esperaba cuando traté de usarlas, e incluso los ejemplos oficiales a veces no funcionan correctamente. Esto me ha dejado preguntándome si me he perdido algunos puntos centrales de este marco. Pero volvamos al tema principal de este video: mis fallas. Como recordará, decidí generar etiquetas para el movimiento de precios en función de un porcentaje específico. La mayoría de estas etiquetas resultaron ser mínimos o máximos locales en los datos.

Después de realizar un procesamiento y preparación de datos simples, creé una red LSTM simple y la puse a prueba. Mis expectativas no eran excepcionalmente altas, pero los resultados fueron realmente decepcionantes. Como puede ver en la imagen adjunta, la red no pudo aprender nada significativo.

Ahora bien, es importante recordar que una red neuronal no está destinada a realizar milagros. Tal vez sea hora de reevaluar nuestro enfoque. Los eventos que estamos tratando de predecir representan solo el 2% de todo el conjunto de datos. Estos eventos pueden considerarse atípicos, y nuestra red simple lucha por distinguirlos del resto de los datos. Además, existe una alta probabilidad de que estos eventos no estén relacionados y tengan causas distintas, lo que dificulta la construcción de un modelo que los prediga con precisión.

Dados estos desafíos, se hizo evidente que debemos centrarnos en comprender mejor los datos e identificar características con poder predictivo. Con eso en mente, decidí revisar una estrategia de promedio móvil simple. En nuestro enfoque inicial, carecíamos de una comprensión clara de por qué deberíamos entrar en una posición. Sin embargo, ahora tenemos una estrategia para abrir posiciones. Aunque no es perfecto, es una estrategia muy utilizada que podemos aprovechar. Agregué dos promedios móviles y un indicador RSI (Índice de fuerza relativa) a nuestro conjunto de datos y recopilé todos los eventos donde los promedios móviles se cruzaron entre sí. Esto nos permitió estimar las direcciones de movimiento de precios.

Luego, creé meta-etiquetas usando el mismo método de etiquetado que antes, pero esta vez, solo etiqueté las barras donde los promedios móviles se cruzaban entre sí. Aquí hay una diferencia importante: dado que ya conocemos la dirección de la posición (larga o corta), el significado de nuestras etiquetas será diferente. Una etiqueta de 1 indica una ganancia, -1 representa una pérdida y 255 se asigna para otros casos. Con el conjunto de datos y las etiquetas actualizados, entrené un modelo LSTM simple y obtuve una curva ROC (Receiver Operating Characteristic) perfecta. La curva ROC muestra el rendimiento de un modelo de clasificación en diferentes umbrales de clasificación. El valor AUC (Área bajo la curva) de la curva ROC nos ayuda a evaluar qué tan bien el modelo distingue entre clases.

Sin embargo, cuando examiné las predicciones del modelo, noté que predecía consistentemente la clase 0. Este resultado ocurrió porque, una vez más, usé todo el conjunto de datos en lugar de solo los eventos relevantes. Nuestro conjunto de datos permaneció desequilibrado y las características carecían de capacidad predictiva. Las curvas ROC no son ideales para conjuntos de datos desequilibrados, ya que pueden enmascarar el bajo rendimiento del modelo para predecir otras clases.

Para abordar el desequilibrio en los datos, hice ajustes y me centré solo en los datos relevantes para los eventos abiertos. Desafortunadamente, incluso después de estas modificaciones, mi modelo no mostró poder predictivo. Produjo los mismos resultados que un modelo aleatorio. También probé un modelo de avance simple, que mostró resultados ligeramente mejores pero aún insatisfactorios. En conclusión, como puede ver, no hay magia en el aprendizaje profundo si alimenta su modelo con datos de mala calidad. El principio de "Basura entra, basura sale" es válido en este contexto. Además, cuando se trata de conjuntos de datos muy desequilibrados y que carecen de características con un fuerte poder predictivo, un modelo de clasificación tenderá a predecir la clase que constituye la mayor parte del conjunto de datos.

Como mencioné anteriormente, es crucial para nosotros comprender nuestros datos e identificar procesos que puedan ayudarnos a encontrar una manera de vencer al mercado.

Eso concluye el episodio de hoy. Espero que lo hayas encontrado perspicaz e informativo. Cuídate, quédate en casa y mantente saludable.

 

Parte VI. Aprendizaje reforzado. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción.


Parte VI. Aprendizaje reforzado. Estrategia comercial de aprendizaje profundo desde el principio hasta la producción.

Soy Denis, y bienvenido a otro episodio de "Close to Alga Trading". En nuestros episodios anteriores, hemos estado trabajando para construir una estrategia comercial rentable utilizando el aprendizaje profundo. Recapitulemos nuestro progreso hasta ahora.

Comenzamos observando las condiciones del mercado en busca de ineficiencias. Una vez identificados, creamos reglas simples y preparamos los datos para nuestros modelos de aprendizaje profundo. Sin embargo, al implementar nuestro enfoque directo, descubrimos que no arrojó los resultados deseados. Esto nos llevó a darnos cuenta de que necesitamos repensar nuestro enfoque y considerar estrategias alternativas.

En el episodio de hoy, exploraremos una nueva dirección: el aprendizaje por refuerzo. El aprendizaje por refuerzo ha obtenido un éxito significativo en varios dominios, superando el rendimiento humano en juegos como el ajedrez, Dota y Go. También se utiliza en robótica de control y automóviles autónomos. Entonces, ¿por qué no aplicarlo al comercio? Si bien no puedo garantizar su éxito, es una vía intrigante para explorar.

Para comenzar, profundicemos en una breve introducción al aprendizaje por refuerzo y familiaricémonos con las definiciones clave.

El aprendizaje por refuerzo se puede describir como el aprendizaje a través de prueba y error para resolver problemas de control óptimo. En términos más simples, implica encontrar la mejor acción a realizar en un estado de entorno dado para maximizar una recompensa numérica final. Para entender mejor este concepto, visualicémoslo con una simple analogía.

Imagine a un comerciante sentado frente a un monitor, comprando y vendiendo acciones. Aquí, el comerciante representa al agente, mientras que el precio del gráfico y el corredor forman el entorno. El agente observa el estado actual del entorno y realiza acciones, como comprar una acción. A cambio, el agente recibe una recompensa, que podría ser los honorarios del corredor. A medida que cambia el entorno, el agente puede comprar más acciones, vender o no hacer nada. Al final del día de negociación, el agente recibe una recompensa final, que puede ser positiva o negativa. El objetivo del agente es maximizar esta recompensa final.

En el aprendizaje por refuerzo, nuestro objetivo es diseñar agentes que puedan aprender interactuando con un entorno. Ahora, vamos a familiarizarnos con las principales definiciones que encontraremos a lo largo de nuestro viaje.

  1. Agente: el algoritmo que toma decisiones, realiza acciones, observa el entorno, recibe retroalimentación y tiene como objetivo maximizar la recompensa.

  2. Entorno: El mundo en el que reside el agente. Engloba todos los datos disponibles para que nuestro agente tome decisiones.

  3. Estado: La configuración del entorno que percibe el agente.

  4. Recompensa: La retroalimentación que recibe el agente después de realizar una acción. Es un valor que el agente busca maximizar. Es importante destacar que, en el aprendizaje por refuerzo, la recompensa no significa necesariamente dinero o un trofeo físico. Es simplemente un valor numérico que también puede ser negativo, y nuestro objetivo es maximizar este valor.

  5. Acción: Cualquier cosa que el agente es capaz de hacer en el entorno dado. Para simplificar, consideremos tres acciones: comprar, vender o no hacer nada.

  6. Episodio: una ejecución completa de toda la tarea.

Estas son las principales definiciones que usaremos a lo largo de nuestro viaje. Hay términos importantes adicionales que cubriremos en videos futuros.

Con esta introducción al aprendizaje por refuerzo, procederemos a aprender sobre el entorno y a crear nuestro propio entorno usando TensorFlow en el siguiente video.

Gracias por acompañarme hoy, y espero seguir explorando el aprendizaje por refuerzo con usted. ¡Nos vemos pronto!

 

Parte VII. Aprendizaje reforzado. Entorno comercial.


Parte VII. Aprendizaje reforzado. Entorno comercial.

Soy Denis y estás viendo "Cerca de AlgoTrading". En nuestro último episodio, brindamos una breve introducción al aprendizaje por refuerzo. Hoy, nos sumergiremos en la creación de un entorno comercial simple para nuestro agente. Exploremos los detalles.

Primero, debemos considerar qué representa el entorno comercial. ¿Se basa únicamente en datos de precios o deberíamos incorporar factores adicionales como el libro de pedidos completo, noticias o incluso rumores de Twitter? Esta decisión dará forma a la complejidad de nuestro entorno.

A continuación, debemos determinar las acciones que nuestro agente puede realizar. ¿Debe limitarse a acciones de "comprar" y "vender", o también puede omitir ciertos pasos? Definir el espacio de acción es crucial para la toma de decisiones de nuestro agente.

Ahora, analicemos el objetivo y cómo definiremos las recompensas. En el mundo real, el entorno comercial tiene un espacio de estado continuo. Sin embargo, para simplificar, mantengamos el entorno lo más sencillo posible, lo que nos permitirá comprender su funcionamiento interno y su implementación.

Para empezar, en lugar de utilizar datos de precios reales, generaremos un proceso de recorrido aleatorio que consta de solo 20 valores. Estos precios diarios simulados nos servirán de entrada. El episodio comenzará desde la mitad de esta secuencia de precios, lo que significa que el estado inicial incluirá los 10 precios históricos. Los precios invisibles restantes se rellenarán con ceros. Con cada paso, se agregará un nuevo precio a la matriz. El episodio concluirá cuando los 20 precios sean visibles para el agente. Por lo tanto, tenemos un total de 10 pasos hasta la finalización del episodio.

Además de los datos de precios, el estado incluirá información sobre nuestra posición abierta. Representemos la posición usando una codificación one-hot: [0,0] para ninguna posición abierta, [1,0] para una posición larga y [0,1] para una posición corta.

Teniendo en cuenta las acciones comerciales típicas, incluiremos "comprar" y "vender" para los casos en que no haya una posición abierta. Sin embargo, si ya hay una posición abierta, el agente solo puede optar por omitir o cerrar la posición. Así, en este escenario, la acción de "vender" o "comprar" es equivalente a "saltar".

El objetivo de nuestro agente es maximizar las ganancias y pérdidas (PnL) durante el período de 10 días. Por lo tanto, definiremos la recompensa como el PnL diario.

Con una comprensión clara de la estructura y el comportamiento de nuestro entorno, ahora podemos pasar a la etapa de implementación. Como se mencionó anteriormente, utilizaremos el marco TensorFlow (tf-agent). El equipo de tf-agent ha proporcionado una guía completa sobre el desarrollo de un entorno adecuado para tf-agent, que recomiendo consultar para comprender mejor el código.

Para crear nuestro entorno, comenzaremos por heredar la clase PyEnvironment, ya que define la interfaz que todos los entornos de Python deben implementar. Dentro de la función Init, inicializaremos variables, estableceremos estados iniciales y, lo que es más importante, especificaremos las especificaciones de acción y observación. En nuestro caso, el espacio de acción constará de cuatro acciones diferentes, con valores mínimos y máximos establecidos en 0 y 3, respectivamente. La especificación de observación describirá el estado del entorno, con el precio a partir de 0 y sin límite máximo. El tipo de datos para los precios será flotante. Devolver las especificaciones de acción y observación es crucial para una integración perfecta con tf-agent.

La siguiente función significativa a implementar se reinicia. Esta función restablecerá el entorno a su estado inicial. Asegurar la correcta implementación de esta función es vital para el correcto funcionamiento del entorno.

Ahora, analicemos la función más crítica: el paso. La función de paso recibe una acción como parámetro de entrada y es responsable de gestionar las transiciones de estado del entorno. En esta función, manejaremos todas las acciones posibles y calcularemos el PnL (recompensa). La función devuelve un time_step, que consta de la observación (la parte del estado del entorno que el agente puede observar para elegir sus acciones en el siguiente paso), la recompensa (el objetivo de aprendizaje del agente), step_type (que indica si este paso de tiempo es el primero, intermedio o último en una secuencia) y descuento.

En la función de paso tenemos dos escenarios de retorno diferentes: uno para los pasos intermedios y otro para el último paso del episodio.

Después de crear el entorno, es esencial validarlo para identificar y corregir cualquier error. El paquete util proporciona una función llamada validate_py_environment, que se puede usar para este propósito. Además, verificar las especificaciones del entorno ayudará a garantizar que todo funcione como se espera.

Con nuestro entorno listo para usar, es buena idea probarlo con diferentes acciones para depurar y validar su comportamiento.

Realicé algunas pruebas con un simple agente tf de DQN y estos son los resultados. Después de 20 000 pasos, el agente demostró un rendimiento aceptable. Sin embargo, tenga en cuenta que solo teníamos una serie de tiempo para que el agente la aprendiera, lo que lo hace relativamente sencillo. Si introducimos una segunda serie temporal y ejecutamos 20 000 pasos, los resultados pueden no ser tan prometedores. Pero con alrededor de 100.000 pasos, el rendimiento del agente mejoró significativamente.

 

Parte VIII. Estrategia comercial de aprendizaje por refuerzo. DQN: Red Q, Red QRNN


Parte VIII. Estrategia comercial de aprendizaje por refuerzo. DQN: Red Q, Red QRNN

Hola, mi nombre es Denis y estás viendo "Cerca de AlgoTrading". En este video, proporcionaremos una breve actualización de nuestro entorno anterior para el agente de aprendizaje por refuerzo. También daremos una breve descripción de Q-Learning y el agente DQN. Luego procederemos a implementar los principales pasos de aprendizaje para un agente rDQN de la biblioteca tf-agents y analizaremos los resultados de la prueba.

Para comenzar, revisemos el entorno comercial que creamos en el video anterior. En ese entorno, utilizamos datos generados sintéticamente. Sin embargo, en esta versión actualizada, nuestro entorno recibirá un dataframe con datos históricos como parámetros de entrada. La especificación_observación para el entorno es un diccionario con dos claves: "Precio" y "Pos". La clave "Precio" contiene 20 elementos con datos de apertura, cierre, máximo, mínimo y volumen. La tecla "Pos" contiene información sobre nuestra posición abierta.

Al comienzo de cada episodio, seleccionamos aleatoriamente una porción de 20 precios de nuestros datos. Este cambio permite que nuestro agente de aprendizaje por refuerzo aprenda de datos históricos reales.

Continuando, analicemos Q-Learning y el concepto de Q-Table. Q-Learning implica asignar un valor Q a cada par de (estado, acción). El agente utiliza esta tabla, conocida como Q-Table, para seleccionar acciones con el valor Q máximo en el estado actual. El agente explora el entorno, recibe recompensas y actualiza los valores Q en función de las recompensas observadas.

Para actualizar los valores Q, usamos una fórmula que involucra el valor Q anterior y el valor Q futuro. Calculamos el valor Q futuro buscando el valor Q máximo para el siguiente estado en nuestra tabla Q. Con el valor Q futuro obtenido, actualizamos el valor Q asociado con el par inicial (estado, acción).

Sin embargo, en nuestro caso, el mercado financiero tiene un espacio de estados muy amplio, por lo que no es práctico utilizar una Q-Table. Para superar este desafío, podemos usar una red neuronal profunda para predecir los valores Q para un estado dado. Este enfoque, conocido como agente DQN (agente de red Q profunda), utiliza una red Q, donde los valores Q se actualizan minimizando la pérdida a través de la retropropagación. La función de pérdida utilizada en el agente DQN está dada por una ecuación específica.

Ahora que tenemos una buena comprensión de Q-Learning y el agente DQN, procedamos a implementar los principales pasos de aprendizaje para el agente rDQN utilizando la biblioteca tf-agents.

El algoritmo de entrenamiento general sigue estos pasos:

  1. Crea un entorno.
  2. Crea un agente.
  3. Recopile datos del entorno utilizando alguna política.
  4. Capacitar al agente utilizando los datos recopilados.
  5. Validar el desempeño del agente.
  6. Repita desde el paso 3.

Crear el entorno es una tarea sencilla. Envolvemos nuestro entorno comercial en TFPyEnvironment, lo que permite una integración perfecta con tf-agents.

A continuación, creamos una red Q para el agente DQN. La biblioteca tf-agents proporciona una clase Q-Network que podemos usar para definir nuestra Q-network. Definimos una red Q simple con una capa oculta completamente conectada que consta de 40 neuronas. Como nuestra observación es un diccionario, también definimos una capa de preprocesamiento simple para ella.

Con la Q-Network creada, procedemos a crear un agente DQN. Instanciamos la clase tf_agents.agents.dqn.dqn_agent, pasando nuestra Q-network como parámetro. También definimos el optimizador y la función de pérdida para entrenar el modelo.

Para entrenar al agente, necesitamos datos del entorno. Recopilamos estos datos mediante una política y, para este paso, podemos seleccionar aleatoriamente las acciones. El agente DQN tiene dos políticas: agent.policy, que se utiliza para la evaluación y la implementación, y agent.collect_policy, que se utiliza para la recopilación de datos.

La recopilación de datos implica tomar el estado actual, seleccionar una acción, recibir el siguiente estado y recompensa, y almacenar esta información en un búfer. Recogemos múltiples pasos o episodios, formando trayectorias. La biblioteca de tf-agents proporciona un controlador llamado DynamicEpisodeDriver, que recopila los pasos hasta el final de un episodio. El controlador actualiza los observadores, incluido un búfer de respuesta.

Para almacenar los datos, podemos usar el TFUniformReplayBuffer de uso común de la biblioteca tf-agents. Definimos las especificaciones de los elementos de datos que almacenará el búfer, el tamaño del lote y la longitud máxima de cada segmento del lote.

Una vez que se completa el paso de recopilación de datos, podemos capacitar a nuestro agente. El agente requiere acceso al búfer de reproducción. Creamos una canalización tf.data.Dataset para enviar datos al agente. Cada fila del búfer de reproducción almacena una sola trayectoria, pero el agente DQN necesita tanto las observaciones actuales como las siguientes para calcular la pérdida. Por lo tanto, configuramos el parámetro num_steps en 2, lo que permite que la canalización del conjunto de datos muestree dos filas para cada elemento del lote.

En este punto, tenemos todo listo para capacitar a dos agentes de DQN con los mismos datos y evaluar su desempeño. Un agente usa una red Q simple, mientras que el otro usa una red QRNN. Ambos agentes están capacitados utilizando 200 días de datos de precios históricos.

Después de 700 pasos de capacitación, observamos que el agente de Q-Network simple no aprendió mucho y en su mayoría muestra un rendimiento promedio negativo. Sin embargo, el agente QRNNNetwork en su mayoría muestra rendimientos promedio positivos. Este resultado se alinea con las expectativas, ya que el agente RNN puede capturar algunas dinámicas en los datos y aprender más rápido.

Si bien este sencillo experimento brinda cierta esperanza de utilizar el aprendizaje por refuerzo para crear un agente rentable, todavía hay otras métricas a considerar para evaluar el desempeño del agente. Los exploraremos en un video futuro.

Gracias por mirar, y nos vemos en el próximo episodio.

Razón de la queja: