Python For Algorithmic Trading - Español
Python For Algorithmic Trading - Español
Python
para algorítmica
Comercio
De la idea al despliegue en la nube
Yves Hilpisch
Machine Translated by Google
Machine Translated by Google
Yves Hilpisch
Publicado por O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.
Los libros de O'Reilly se pueden comprar para uso educativo, comercial o promocional de ventas. También hay ediciones en línea disponibles
para la mayoría de los títulos (http://oreilly.com). Para obtener más información, comuníquese con nuestro departamento de ventas corporativo/
institucional: 8009989938 o [email protected].
nSight, Inc.
El logotipo de O'Reilly es una marca registrada de O'Reilly Media, Inc. Python for Algorithmic Trading, la imagen de portada y la imagen
comercial relacionada son marcas comerciales de O'Reilly Media, Inc.
Las opiniones expresadas en este trabajo son las del autor y no representan las opiniones del editor.
Si bien el editor y el autor han realizado esfuerzos de buena fe para garantizar que la información y las instrucciones contenidas en este trabajo
sean precisas, el editor y el autor renuncian a toda responsabilidad por errores u omisiones, incluida, entre otras, la responsabilidad por los
daños resultantes del uso o confianza en este trabajo. El uso de la información y las instrucciones contenidas en este trabajo es bajo su propio
riesgo. Si algún ejemplo de código u otra tecnología que este trabajo contiene o describe está sujeto a licencias de código abierto o derechos
de propiedad intelectual de otros, es su responsabilidad asegurarse de que su uso cumpla con dichas licencias y/o derechos. Este libro no
pretende ser un consejo financiero. Por favor consulte a un profesional calificado si necesita asesoramiento financiero.
9781492053354
[LSI]
Machine Translated by Google
Tabla de contenido
Prefacio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
2. Infraestructura de Python. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Conda como administrador de paquetes 19
Instalación de miniconda 19
Operaciones básicas con Conda 21
Conda como gestor de entorno virtual 27
Usando contenedores Docker 30
Imágenes y contenedores de Docker 31
Creación de una imagen de Docker de Ubuntu y Python 31
Usar instancias en la nube 36
Claves públicas y privadas RSA 38
III
Machine Translated by Google
Usando TsTables 70
Entrando en lo básico 89
Generalizando el enfoque 97
Entrando en lo básico 99
i | Tabla de contenido
Machine Translated by Google
Uso de la regresión lineal para la predicción del movimiento del mercado 124
Uso del aprendizaje automático para predecir el movimiento del mercado 139
Uso del aprendizaje profundo para la predicción del movimiento del mercado 153
Uso de redes neuronales profundas para predecir la dirección del mercado 156
Tabla de contenidos | v
Machine Translated by Google
Empezando 251
vi | Tabla de contenido
Machine Translated by Google
Índice. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
Prefacio
El dataísmo dice que el universo consiste en flujos de datos, y el valor de cualquier fenómeno
o entidad está determinado por su contribución al procesamiento de datos... El dataísmo
colapsa así la barrera entre los animales [humanos] y las máquinas, y espera que los
algoritmos electrónicos eventualmente descifren y superan a los algoritmos bioquímicos.1
—Yuval Noé Harari
Encontrar el algoritmo adecuado para operar de forma automática y exitosa en los mercados financieros
es el santo grial en finanzas. No hace mucho tiempo, el comercio algorítmico sólo estaba disponible y
era posible para actores institucionales con mucho dinero y muchos activos bajo gestión. Los recientes
desarrollos en las áreas de código abierto, datos abiertos, computación en la nube y almacenamiento
en la nube, así como plataformas de comercio en línea, han nivelado el campo de juego para las
instituciones más pequeñas y los comerciantes individuales, haciendo posible iniciarse en esta
fascinante disciplina. mientras esté equipado únicamente con una computadora portátil o de escritorio
típica y una conexión a Internet confiable.
Hoy en día, Python y su ecosistema de potentes paquetes es la plataforma tecnológica elegida para el
comercio algorítmico. Entre otras cosas, Python permite hacer análisis de datos eficientes (con pandas,
por ejemplo), aplicar el aprendizaje automático a la predicción bursátil (con scikitlearn, por ejemplo), o
incluso hacer uso de la tecnología de aprendizaje profundo de Google con TensorFlow. .
Este es un libro sobre Python para el comercio algorítmico, principalmente en el contexto de estrategias
de generación de alfa (ver Capítulo 1). Un libro así, en la intersección de dos campos vastos y
apasionantes, difícilmente puede cubrir todos los temas relevantes. Sin embargo, puede cubrir en
profundidad una variedad de metatemas importantes.
1 Harari, Yuval Noah. 2015. Homo Deus: una breve historia del mañana. Londres: Harvill Secker.
ix
Machine Translated by Google
Datos financieros
Los datos financieros son el núcleo de todo proyecto de comercio algorítmico. Python y paquetes como NumPy
y pandas hacen un gran trabajo al manejar y trabajar con datos financieros estructurados de cualquier tipo
(final del día, intradiario, de alta frecuencia).
Backtesting No
debería haber operaciones algorítmicas automatizadas sin una prueba rigurosa de la estrategia comercial que
se implementará. El libro cubre, entre otras cosas, estrategias comerciales basadas en promedios móviles
simples, impulso, reversión de la media y predicción basada en aprendizaje automático/profundo.
El comercio algorítmico requiere trabajar con datos en tiempo real, algoritmos en línea basados en ellos y
visualización en tiempo real. El libro proporciona una introducción a la programación de sockets con ZeroMQ y
visualización de streaming.
Plataformas en línea
No se pueden realizar transacciones sin una plataforma de negociación. El libro cubre dos plataformas de
comercio electrónico populares: Oanda y FXCM.
Automatización
La belleza, así como algunos desafíos importantes, del comercio algorítmico son el resultado de la
automatización de la operación comercial. El libro muestra cómo implementar Python en la nube y cómo
configurar un entorno apropiado para el comercio algorítmico automatizado.
El libro ofrece una experiencia de aprendizaje única con las siguientes características y beneficios:
x | Prefacio
Machine Translated by Google
pasando, cómo utilizar los ejemplos de código, cómo cambiarlos, etc. No es necesario depender de
plataformas de terceros, por ejemplo, para realizar pruebas retrospectivas o conectarse a las
plataformas comerciales. Con este libro, el lector puede hacer todo esto por su cuenta a un ritmo
conveniente y tiene cada línea de código para hacerlo.
Foro de
usuarios Aunque el lector debería poder seguirlo sin problemas, el autor y The Python Quants están
ahí para ayudar. El lector puede publicar preguntas y comentarios en el foro de usuarios de la
plataforma Quant en cualquier momento (las cuentas son gratuitas).
Contenidos y estructura
A continuación se ofrece una descripción general rápida de los temas y contenidos presentados en cada capítulo.
Prefacio | xi
Machine Translated by Google
xiii | Prefacio
Machine Translated by Google
Apéndice
El apéndice proporciona una introducción concisa a los temas más importantes de Python,
NumPy y pandas en el contexto del material presentado en los capítulos principales. Representa
un punto de partida desde el cual uno puede ampliar su propio conocimiento de Python con el
tiempo.
La Figura P1 muestra las capas relacionadas con el comercio algorítmico que cubren los capítulos
de abajo hacia arriba. Necesariamente comienza con la infraestructura de Python (Capítulo 2) y
agrega datos financieros (Capítulo 3), estrategia y código de backtesting vectorizado (Capítulos 4 y
5). Hasta ese momento, los conjuntos de datos se utilizan y manipulan en su conjunto.
El backtesting basado en eventos introduce por primera vez la idea de que los datos del mundo real
llegan de forma incremental (Capítulo 6). Es el puente que conduce a la capa de código de conexión
que cubre la comunicación por socket y el manejo de datos en tiempo real (Capítulo 7). Además de
eso, se requieren plataformas comerciales y sus API para poder realizar pedidos (Capítulos 8 y 9).
Finalmente, se cubren aspectos importantes de la automatización y la implementación (Capítulo 10).
En ese sentido, los capítulos principales del libro se relacionan con las capas como se ve en la Figura
P1, que proporcionan una secuencia natural para los temas a cubrir.
Prefacio | xiii
Machine Translated by Google
Hilpisch, Yves. 2018. Python para finanzas: dominar las finanzas basadas en datos. 2da ed.
Sebastopol: O'Reilly.
. 2020. Inteligencia artificial en finanzas: una guía basada en Python. Sebastopol: O'Reilly.
McKinney, Wes. 2017. Python para análisis de datos: gestión de datos con Pandas, NumPy e
IPython. 2da ed. Sebastopol: O'Reilly.
Vander Plas, Jake. 2016. Manual de ciencia de datos de Python: herramientas esenciales para
trabajar con datos. Sebastopol: O'Reilly.
Puede encontrar información general sobre el comercio algorítmico, por ejemplo, en estos libros:
Chan, Ernesto. 2009. Comercio cuantitativo: cómo construir su propio comercio algorítmico.
ing Negocios. Hoboken y otros: John Wiley & Sons.
Narang, Rishi. 2013. Dentro de la caja negra: una guía sencilla para el comercio cuantitativo y de
alta frecuencia. Hoboken y otros: John Wiley & Sons.
Disfrute de su viaje por el mundo del comercio algorítmico con Python y póngase en contacto
enviando un correo electrónico a [email protected] si tiene preguntas o comentarios.
xiv | Prefacio
Machine Translated by Google
Cursiva
Indica nuevos términos, URL, direcciones de correo electrónico, nombres de archivos y extensiones de archivos.
Ancho constante Se
utiliza para listados de programas, así como dentro de párrafos, para hacer referencia a elementos del
programa como nombres de variables o funciones, bases de datos, tipos de datos, variables de entorno,
declaraciones y palabras clave.
Si tiene una pregunta técnica o un problema al utilizar los ejemplos de código, envíe un correo electrónico
a [email protected].
Este libro está aquí para ayudarle a realizar su trabajo. En general, si se ofrece código de ejemplo con este
libro, puede utilizarlo en sus programas y documentación. Tu no
Prefacio | xvi
Machine Translated by Google
Debe comunicarse con nosotros para obtener permiso a menos que esté reproduciendo una parte
importante del código. Por ejemplo, escribir un programa que utilice varios fragmentos de código
de este libro no requiere permiso. Vender o distribuir ejemplos de libros de O'Reilly requiere
permiso. Responder una pregunta citando este libro y citando código de ejemplo no requiere
permiso. Incorporar una cantidad significativa de código de ejemplo de este libro en la
documentación de su producto requiere permiso.
Apreciamos la atribución, pero generalmente no la exigimos. Una atribución suele incluir el título,
el autor, la editorial y el ISBN. Por ejemplo, este libro puede atribuirse como: “Python para el
comercio algorítmico por Yves Hilpisch (O'Reilly). Copyright 2021 Yves Hilpisch, 9781492053354.”
Si cree que su uso de ejemplos de código queda fuera del uso legítimo o del permiso otorgado
anteriormente, no dude en contactarnos en [email protected].
Cómo contactarnos
Por favor dirija sus comentarios y preguntas sobre este libro al editor:
Tenemos una página web para este libro, donde enumeramos erratas, ejemplos y cualquier
información adicional. Puede acceder a esta página en https://oreil.ly/py4at.
xvi | Prefacio
Machine Translated by Google
Envíe un correo electrónico a [email protected] para comentar o hacer preguntas técnicas sobre
este libro.
YouTube: http://youtube.com/oreillymedia
Expresiones de gratitud
Quiero agradecer a los revisores técnicos (Hugh Brown, McKlayne Marshall, Ramanathan Ramakrishnamoorthy
y Prem Jebaseelan) que proporcionaron comentarios útiles que condujeron a muchas mejoras en el contenido
del libro.
Como siempre, un agradecimiento especial a Michael Schwed, quien me apoya en todos los asuntos técnicos,
tanto simples como muy complejos, con su amplio y profundo conocimiento tecnológico.
Los delegados de los Programas de Certificación en Python para Finanzas Computacionales y Comercio
Algorítmico también ayudaron a mejorar este libro. Sus comentarios continuos me han permitido eliminar
errores y errores y perfeccionar el código y los cuadernos utilizados.
También me gustaría agradecer a todo el equipo de O'Reilly Media, especialmente a Michelle Smith, Michele
Cronin, Victoria DeRose y Danny Elfanbaum, por hacer que todo esto sucediera y ayudarme a perfeccionar
el libro de tantas maneras.
Además, también me gustaría agradecer al equipo de Refinitiv, en particular a Jason Ramchandani, por
brindar apoyo continuo y acceso a datos financieros. Los principales archivos de datos utilizados a lo largo
del libro y puestos a disposición de los lectores se recibieron de una forma u otra de las API de datos de
Refinitiv.
A mi familia con amor. Dedico este libro a mi padre Adolf, cuyo apoyo a mí y a nuestra familia abarca ya casi
cinco décadas.
Prefacio | xvii
Machine Translated by Google
Machine Translated by Google
CAPÍTULO 1
En Goldman [Sachs] el número de personas que se dedican a negociar acciones ha caído de un máximo de
600 en 2000 a sólo dos en la
actualidad.1 —The Economist
Este capítulo proporciona información general y una descripción general de los temas tratados en
este libro. Aunque Python para el comercio algorítmico es un nicho en la intersección de la
programación y las finanzas de Python, es un nicho de rápido crecimiento que aborda temas tan
diversos como la implementación de Python, el análisis financiero interactivo, el aprendizaje
automático y profundo, la programación orientada a objetos, comunicación por socket, visualización
de datos en streaming y plataformas comerciales.
Para un repaso rápido sobre temas importantes de Python, lea primero el Apéndice .
1
Machine Translated by Google
Python nunca estuvo dirigido específicamente a las comunidades científica y financiera, a muchas personas
de estos campos les gustó la belleza y concisión de su sintaxis. No hace mucho, se consideraba una
buena tradición explicar un algoritmo (financiero) y al mismo tiempo presentar algún pseudocódigo como
paso intermedio hacia su adecuada implementación tecnológica. Muchos sintieron que, con Python, el
paso del pseudocódigo ya no sería necesario. Y se demostró que en su mayoría tenían razón.
Consideremos, por ejemplo, la discretización de Euler del movimiento browniano geométrico, como en la
Ecuación 11.
Durante décadas, el compilador y el lenguaje de marcado LaTeX han sido el estándar de oro para la
creación de documentos científicos que contienen fórmulas matemáticas. En muchos sentidos, la sintaxis
de Latex es similar o ya se parece al pseudocódigo cuando, por ejemplo, se establecen ecuaciones, como
en la Ecuación 11. En este caso particular, la versión Latex queda así:
En Python, esto se traduce en código ejecutable, dadas las respectivas definiciones de variables, que
también está muy cerca de la fórmula financiera y de la representación de Latex:
Sin embargo, el problema de la velocidad persiste. Esta ecuación en diferencias, como una aproximación
numérica de la respectiva ecuación diferencial estocástica, se utiliza generalmente para fijar el precio de
los derivados mediante simulación de Monte Carlo o para realizar análisis y gestión de riesgos basados en
la simulación.2 Estas tareas, a su vez, pueden requerir millones de simulaciones que deben terminarse a
su debido tiempo, a menudo casi en tiempo real o al menos casi en tiempo cercano. Python, como lenguaje
de programación interpretado de alto nivel, nunca fue diseñado para ser lo suficientemente rápido como
para abordar tareas computacionalmente exigentes.
NumPy y vectorización
En 2006, Travis Oliphant lanzó la versión 1.0 del paquete NumPy Python .
NumPy significa Python numérico, lo que sugiere que apunta a escenarios que son
numéricamente exigente. El intérprete base de Python intenta ser lo más general posible.
en muchas áreas, lo que a menudo genera bastantes gastos generales en tiempo de ejecución.3 NumPy, en el
Por otro lado, utiliza la especialización como enfoque principal para evitar gastos generales y ser lo más
bueno y lo más rápido posible en ciertos escenarios de aplicación.
La clase principal de NumPy es el objeto de matriz regular, llamado objeto ndarray para una matriz de n
dimensiones. Es inmutable, lo que significa que no se puede cambiar de tamaño,
y sólo puede acomodar un único tipo de datos, llamado dtype. Esta especialización permite
para la implementación de código conciso y rápido. Un enfoque central en este contexto
es la vectorización. Básicamente, este enfoque evita bucles en el nivel de Python y elimina
abre el bucle al código NumPy especializado , generalmente implementado en C y allí.
por lo tanto bastante rápido.
En [1]: %%tiempo
importar aleatoriamente
de importación matemática exp, sqrt
S0 = 100
r = 0,05 T
= 1,0
sigma = 0,2
valores = []
para _ en rango(1000000): ST =
S0 * exp((r 0.5 * sigma ** 2) * T +
sigma * aleatorio.gauss(0, 1) * sqrt(T)) valores.append(ST)
3 Por ejemplo, los objetos de lista no sólo son mutables, lo que significa que pueden cambiarse de tamaño, sino que también pueden
También contienen casi cualquier otro tipo de objeto Python, como objetos int, float, tuple o los propios objetos de lista .
Con NumPy, puedes evitar completamente los bucles en el nivel de Python mediante el uso de
vectorización. El código es mucho más conciso, más legible y más rápido en un factor de aproximadamente
ocho:
En [2]: %%tiempo
importar numpy como np
S0 = 100r =
0,05
T = 1,0
sigma = 0,2
Tiempos de CPU: usuario 375 ms, sistema: 82,6 ms, total: 458 ms Tiempo de
pared: 160 ms
Esta única línea de código NumPy simula todos los valores y los almacena en un objeto ndarray .
Es seguro decir que NumPy ha contribuido significativamente al éxito de Python en ciencia y finanzas.
Muchos otros paquetes populares de Python de la llamada pila científica de Python se basan en NumPy
como una estructura de datos eficiente y funcional para almacenar y manejar datos numéricos. De hecho,
NumPy es una consecuencia del proyecto del paquete SciPy , que proporciona una gran cantidad de
funciones que se necesitan con frecuencia en la ciencia. El proyecto SciPy reconoció la necesidad de una
estructura de datos numéricos más poderosa y
Consolidó proyectos antiguos como Numeric y NumArray en esta área en uno nuevo y unificador en
forma de NumPy.
En el comercio algorítmico, una simulación de Monte Carlo puede no ser el caso de uso más importante
para un lenguaje de programación. Sin embargo, si ingresa al espacio del comercio algorítmico, la gestión
de conjuntos de datos de series temporales financieras más grandes, o incluso grandes, es un caso de
uso muy importante. Basta pensar en las pruebas retrospectivas de estrategias comerciales (intradiarias)
o en el procesamiento de flujos de datos de ticks durante el horario comercial. Aquí es donde entra en
juego el paquete de análisis de datos pandas .
pandas comenzó en 2008 por Wes McKinney, quien en aquel entonces trabajaba en AQR Capital
Management, un gran fondo de cobertura que operaba en Greenwich, Connecticut. Como ocurre con
cualquier otro fondo de cobertura, trabajar con datos de series temporales es de suma importancia para
AQR Capital Management, pero en aquel entonces Python no ofrecía ningún tipo de soporte atractivo
para este tipo de datos. La idea de Wes era crear un paquete que imitara las capacidades del lenguaje
estadístico R (http://rproject.org) en esta área. Esto se refleja, por ejemplo, al nombrar la clase principal
DataFrame, cuya contraparte en R se llama data.frame. Al no considerarse lo suficientemente cercano al
negocio principal de la administración del dinero, AQR Capital Management abrió el proyecto pandas en
2009, lo que marca el comienzo de una importante historia de éxito en análisis financiero y datos basados
en código abierto.
En parte debido a los pandas, Python se ha convertido en una fuerza importante en el análisis financiero
y de datos. Muchas personas que adoptan Python, provenientes de otros lenguajes diversos, citan a los
pandas como una de las principales razones de su decisión. En combinación con fuentes de datos
abiertos como Quandl, pandas incluso permite a los estudiantes realizar análisis financieros sofisticados
con las barreras de entrada más bajas jamás vistas: basta con un ordenador portátil normal con conexión
a Internet.
Supongamos que un operador algorítmico está interesado en operar con Bitcoin, la criptomoneda con
mayor capitalización de mercado. Un primer paso podría ser recuperar datos sobre el tipo de cambio
histórico en USD. Utilizando datos de Quandl y pandas, esta tarea se realiza en menos de un minuto. La
Figura 11 muestra el gráfico que resulta del siguiente código Python, que tiene (omitiendo algunas
parametrizaciones relacionadas con el estilo de trazado) solo cuatro líneas. Aunque pandas no se importa
explícitamente, el paquete contenedor Quandl Python devuelve por defecto un objeto DataFrame que
luego se usa para agregar un promedio móvil simple (SMA) de 100 días, así como para visualizar los
datos sin procesar junto con el SMA:
mpl.rcParams['font.family'] = 'serif'
Recupera datos diarios del tipo de cambio de Bitcoin y devuelve datos de pandas
Objeto de marco con una sola columna.
Figura 11. Tipo de cambio histórico de Bitcoin en USD desde principios de 2013 hasta mediados
de 2020
Comercio algorítmico
El término comercio algorítmico no está definido de forma única ni universal. En un nivel bastante
básico, se refiere a la negociación de instrumentos financieros basándose en algún algoritmo formal.
Un algoritmo es un conjunto de operaciones (matemáticas, técnicas) que se deben realizar en una
secuencia determinada para lograr un objetivo determinado. Por ejemplo, existen algoritmos
matemáticos para resolver un cubo de Rubik.4 Un algoritmo de este tipo puede resolver el problema
en cuestión mediante un procedimiento paso a paso, a menudo perfectamente. Otro ejemplo son los algoritmos para
4 Véase Matemáticas del cubo de Rubik o Algoritmos para resolver el cubo de Rubik.
Comercio algorítmico | 7
Machine Translated by Google
encontrar la(s) raíz(es) de una ecuación, si es que existe(n). En ese sentido, el objetivo de un algoritmo
matemático suele estar bien especificado y a menudo se espera una solución óptima.
Pero ¿qué pasa con el objetivo de los algoritmos de comercio financiero? Esta pregunta no es tan fácil de
responder en general. Podría ser útil dar un paso atrás por un momento y considerar los motivos generales
para comerciar. En Dorn et al. (2008) escribe:
La opinión expresada aquí es de naturaleza más técnica que económica y se centra principalmente en el
proceso en sí y sólo en parte en por qué las personas inician el comercio en primer lugar.
Para nuestros propósitos, una lista no exhaustiva de motivos de negociación financiera de personas e
instituciones financieras que administran dinero propio o para terceros incluye lo siguiente:
Negociación
beta Ganar primas de riesgo de mercado invirtiendo, por ejemplo, en fondos cotizados en bolsa (ETF)
que replican el rendimiento del S&P 500.
Generación alfa
Ganar primas de riesgo independientes del mercado, por ejemplo, vendiendo en corto acciones
cotizadas en el S&P 500 o ETF en el S&P 500.
Cobertura
estática Cobertura contra riesgos de mercado comprando, por ejemplo, opciones de venta fuera del
dinero en el S&P 500.
Cobertura dinámica
Cobertura contra riesgos de mercado que afectan a las opciones sobre el S&P 500, por ejemplo,
negociando dinámicamente futuros sobre el S&P 500 e instrumentos apropiados de efectivo, mercado
monetario o tasas.
Negociar acciones y ETF del S&P 500 para poder cubrir los pasivos resultantes de, por ejemplo, la
suscripción de pólizas de seguro de vida.
Creación de
mercado Proporcionar, por ejemplo, liquidez a opciones sobre el S&P 500 mediante la compra y venta
de opciones a diferentes precios de oferta y demanda.
Todos estos tipos de operaciones pueden implementarse mediante un enfoque discrecional, en el que los
comerciantes humanos toman decisiones principalmente por sí mismos, así como basándose en algoritmos
que apoyan al comerciante humano o incluso los reemplazan por completo en el futuro.
proceso de toma de decisiones. En este contexto, por supuesto, la informatización del comercio
financiero juega un papel importante. Mientras que al comienzo del comercio financiero, el comercio
en el piso con un gran grupo de personas gritándose entre sí ("grito abierto") era la única forma de
ejecutar operaciones, la informatización y la llegada de Internet y las tecnologías web han
revolucionado el comercio en el sector financiero. industria. La cita al comienzo de este capítulo
ilustra esto de manera impresionante en términos del número de personas que participaron
activamente en la negociación de acciones de Goldman Sachs en 2000 y en 2016. Es una
tendencia que se previó hace 25 años, como Solomon y Corso (1991). señalar:
La tecnología ha hecho posible que la información sobre los precios de las acciones se envíe a
todo el mundo en segundos. Actualmente, las computadoras enrutan órdenes y ejecutan pequeñas
operaciones directamente desde la terminal de la firma de corretaje a la bolsa. Actualmente, las
computadoras conectan varias bolsas de valores, una práctica que está contribuyendo a crear un
mercado global único para el comercio de valores. Las continuas mejoras en la tecnología harán
posible ejecutar operaciones a nivel mundial mediante sistemas de comercio electrónico.
5 Véase Hilpisch (2015) para un análisis detallado de las estrategias de cobertura delta para opciones europeas y americanas.
usando Python.
Comercio algorítmico | 9
Machine Translated by Google
Hay otras áreas en las que los algoritmos relacionados con el comercio desempeñan un papel
importante. Uno es el espacio del comercio de alta frecuencia (HFT), donde la velocidad suele ser la
disciplina en la que compiten los jugadores.6 Los motivos para el HFT son diversos, pero la creación de
mercado y la generación de alfa probablemente desempeñen un papel destacado. Otro es la ejecución
de operaciones, donde se implementan algoritmos para ejecutar de manera óptima ciertas operaciones no estándar.
Los motivos en esta área podrían incluir la ejecución (al mejor precio posible) de órdenes grandes o la
ejecución de una orden con el menor impacto posible en el mercado y en el precio.
Un motivo más sutil podría ser disfrazar una orden ejecutándola en varios intercambios diferentes.
Queda por abordar una pregunta importante: ¿hay alguna ventaja en utilizar algoritmos para el comercio
en lugar de investigación, experiencia y discreción humanas? Esta pregunta difícilmente puede
responderse de manera generalizada. Sin duda, hay operadores humanos y administradores de carteras
que han ganado, en promedio, más que su punto de referencia para los inversores durante períodos de
tiempo más largos. El ejemplo más destacado a este respecto es Warren Buffett. Por otro lado, los
análisis estadísticos muestran que la mayoría de los gestores de carteras activos rara vez superan de
forma consistente los índices de referencia relevantes. Refiriéndose al año 2015, Adam Shell escribe:
El año pasado, por ejemplo, cuando el índice bursátil Standard & Poor's 500 registró un rendimiento total
insignificante del 1,4% con dividendos incluidos, el 66% de los fondos de acciones de grandes empresas
"administrados activamente" registraron rendimientos menores que el índice... El panorama es igual de
sombrío: el 84% de los fondos de gran capitalización generaron rendimientos inferiores a los del S&P 500
en el último período de cinco años y el 82% se quedó por debajo de ellos en los últimos 10 años, según
el estudio.7
6 Consulte el libro de Lewis (2015) para obtener una introducción no técnica a HFT.
7 Fuente: “El 66% de los administradores de fondos no pueden igualar los resultados de S&P”. EE.UU. Hoy en día, 14 de marzo de 2016.
La tabla 10 reproduce los principales hallazgos cuantitativos del estudio de Harvey et al.
(2016).8 En la tabla, los factores incluyen los tradicionales (acciones, bonos, etc.),
(valor, impulso, etc.) y volatilidad (compra de opciones de compra y venta al precio del dinero).
El índice de evaluación del rendimiento ajustado divide alfa por la volatilidad del rendimiento ajustado. Para
Para más detalles y antecedentes, ver el estudio original.
Los resultados del estudio ilustran que los fondos de cobertura macro sistemáticos (“algorítmicos”) por
forman mejor como categoría, tanto en términos no ajustados como ajustados por riesgo. Generan un
alfa anualizado de 4,85 puntos% durante el período estudiado. Estos son fondos de cobertura
implementar estrategias que suelen ser globales, abarcan todos los activos y a menudo implican
elementos políticos y macroeconómicos. Los fondos de cobertura de acciones sistemáticos sólo superaron a sus
contrapartidas discrecionales sobre la base del ratio de valoración de rentabilidad ajustado (0,35
frente a 0,25).
factores
En comparación con el S&P 500, el rendimiento general de los fondos de cobertura fue bastante pobre durante el
año 2017. Mientras que el índice S&P 500 obtuvo un rendimiento del 21,8%, los fondos de cobertura solo obtuvieron un rendimiento del 8,5%
a inversores (ver este artículo en Investopedia). Esto ilustra lo difícil que es, incluso con
presupuestos multimillonarios para investigación y tecnología, para generar alfa.
8 Rendimiento anualizado (por encima de la tasa de interés a corto plazo) y medidas de riesgo para categorías de fondos de cobertura
que comprende un total de 9.000 fondos de cobertura durante el período comprendido entre junio de 1996 y diciembre de 2014.
Paquetes dedicados
Además de los paquetes estándar de análisis de datos, hay varios paquetes disponibles que
están dedicados al espacio comercial algorítmico, como PyAlgoTrade y Zipline para realizar
pruebas retrospectivas de estrategias comerciales y Pyfolio para realizar análisis de riesgo y
cartera.
Plataformas dedicadas
Quantopian, por ejemplo, ofrece un entorno de backtesting estandarizado como una plataforma
basada en web donde el lenguaje elegido es Python y donde las personas pueden intercambiar
ideas con otras personas con ideas afines a través de diferentes funciones de redes sociales.
Desde su fundación hasta 2020, Quantopian ha atraído a más de 300.000 usuarios.
En resumen, es bastante seguro decir que Python ya juega un papel importante en el comercio
algorítmico y parece tener un fuerte impulso para volverse aún más importante en el futuro. Por lo
tanto, es una buena opción para cualquiera que intente ingresar a este espacio, ya sea como un
comerciante "minorista" ambicioso o como un profesional empleado por una institución financiera
líder dedicada al comercio sistemático.
El foco de este libro está en Python como lenguaje de programación para el comercio algorítmico. El
libro supone que el lector ya tiene cierta experiencia con Python y los paquetes populares de Python
utilizados para el análisis de datos. Buenos libros introductorios son, por ejemplo, Hilpisch (2018),
McKinney (2017) y VanderPlas (2016), que se pueden consultar para construir una base sólida en
Python para el análisis de datos y las finanzas. También se espera que el lector tenga cierta
experiencia con las herramientas típicas utilizadas para el análisis interactivo con Python, como
IPython, a la que VanderPlas (2016) también proporciona una introducción.
Este libro presenta y explica el código Python que se aplica a los temas en cuestión, como realizar
pruebas retrospectivas de estrategias comerciales o trabajar con transmisión de datos. No puede
proporcionar una introducción completa a todos los paquetes utilizados en diferentes lugares. Sin
embargo, intenta resaltar aquellas capacidades de los paquetes que son centrales para la exposición
(como la vectorización con NumPy).
El libro tampoco puede proporcionar una introducción exhaustiva y una descripción general de todos
los aspectos financieros y operativos relevantes para el comercio algorítmico. En cambio, el enfoque
se centra en el uso de Python para construir la infraestructura necesaria para sistemas de comercio
algorítmico automatizados. Por supuesto, la mayoría de los ejemplos utilizados provienen del espacio
comercial algorítmico. Sin embargo, cuando se trata, por ejemplo, de estrategias de impulso o de
reversión a la media, se utilizan más o menos simplemente sin proporcionar verificación (estadística)
o una discusión en profundidad de sus complejidades. Siempre que parece apropiado, se dan
referencias que señalan al lector fuentes que abordan cuestiones que quedaron abiertas durante la
exposición.
Considerándolo todo, este libro está escrito para lectores que tienen cierta experiencia tanto con
Python como con el comercio (algorítmico). Para dicho lector, el libro es una guía práctica para la
creación de sistemas comerciales automatizados utilizando Python y paquetes adicionales.
Estrategias comerciales
A lo largo de este libro, se utilizan como ejemplos cuatro estrategias comerciales algorítmicas
diferentes. Se presentan brevemente en las siguientes secciones y con más detalle en el Capítulo 4.
Todas estas estrategias comerciales se pueden clasificar como principalmente búsqueda de alfa.
estrategias, ya que su principal objetivo es generar retornos positivos superiores a los del mercado
independientemente de la dirección del mercado. Los ejemplos canónicos a lo largo del libro, cuando
se trata de instrumentos financieros negociados, son un índice bursátil, una acción única o una
criptomoneda (denominada en moneda fiduciaria). El libro no cubre estrategias que involucran
múltiples instrumentos financieros al mismo tiempo (estrategias de negociación de pares, estrategias
basadas en cestas, etc.). También cubre solo estrategias cuyas señales comerciales se derivan de
datos estructurados de series de tiempo financieras y no, por ejemplo, de fuentes de datos no
estructurados como noticias o redes sociales. Esto mantiene las discusiones y las implementaciones
de Python concisas y más fáciles de entender, en línea con el enfoque (discutido anteriormente) de
centrarse en Python para el comercio algorítmico.9 El resto de este capítulo
ofrece una descripción general rápida de las cuatro estrategias comerciales utilizadas en este libro. .
tipo de estrategia comercial se basa en medias móviles simples (SMA) para generar señales
comerciales y posicionamientos en el mercado. Estas estrategias comerciales han sido popularizadas
por los llamados analistas técnicos o chartistas. La idea básica es que una SMA a corto plazo que
tenga un valor mayor que una SMA a más largo plazo indica una posición larga en el mercado y el
escenario opuesto indica una posición neutral o corta en el mercado.
Impulso
La idea básica detrás de las estrategias de impulso es que se supone que un instrumento financiero
se comportará de acuerdo con su desempeño reciente durante algún tiempo adicional. Por ejemplo,
cuando un índice bursátil ha tenido un rendimiento negativo en promedio durante los últimos cinco
días, se supone que su rendimiento también será negativo mañana.
Reversión media
En las estrategias de reversión a la media, se supone que un instrumento financiero revierte a algún
nivel medio o de tendencia si actualmente está lo suficientemente lejos de ese nivel. Por ejemplo,
supongamos que una acción cotiza 10 USD por debajo de su nivel SMA de 200 días de 100. Entonces
se espera que el precio de la acción vuelva a su nivel SMA pronto.
9 Consulte el libro de Kissel (2013) para obtener una descripción general de los temas relacionados con el comercio algorítmico, el libro de Chan
(2013) para una discusión en profundidad sobre las estrategias de impulso y reversión a la media, o el libro de Narang
(2013) para una cobertura del comercio cuantitativo y HFT en general.
los algoritmos de aprendizaje automático y profundo, generalmente se adopta un enfoque más de caja
negra para predecir los movimientos del mercado. Por simplicidad y reproducibilidad, los ejemplos de este
libro se basan principalmente en observaciones de rendimientos históricos como características para
entrenar algoritmos de aprendizaje profundo y automático para predecir los movimientos del mercado de valores.
Conclusiones
Python ya es una fuerza en las finanzas en general y está en camino de convertirse en una fuerza
importante en el comercio algorítmico. Hay varias buenas razones para usar Python para el comercio
algorítmico, entre ellas el poderoso ecosistema de paquetes que permite un análisis de datos eficiente
o el manejo de API modernas. También hay una serie de buenas razones para aprender Python para
el comercio algorítmico, la principal de ellas el hecho de que algunas de las instituciones de compra y
venta más importantes hacen un uso intensivo de Python en sus operaciones comerciales y buscan
constantemente profesionales experimentados en Python.
Este libro se centra en la aplicación de Python a las diferentes disciplinas del comercio algorítmico,
como realizar pruebas retrospectivas de estrategias comerciales o interactuar con plataformas
comerciales en línea. No puede reemplazar una introducción completa al propio Python ni al comercio en general.
Sin embargo, combina sistemáticamente estos dos mundos fascinantes para proporcionar una valiosa
fuente para la generación de alfa en los competitivos mercados financieros y de criptomonedas
actuales.
Black, Fischer y Myron Scholes. 1973. “La fijación de precios de opciones y pasivos corporativos”.
Revista de Economía Política 81 (3): 638659.
Conclusiones | 15
Machine Translated by Google
Dorn, Anne, Daniel Dorn y Paul Sengmueller. 2008. “¿Por qué la gente comercia?”
Revista de Finanzas Aplicadas (otoño/invierno): 3750.
Harvey, Campbell, Sandy Rattray, Andrew Sinclair y Otto Van Hemert. 2016.
"Hombre versus máquina: comparación del desempeño de los fondos de cobertura discrecionales
y sistemáticos". Libro blanco del Journal of Portfolio Management, Man Group.
Hilpisch, Yves. 2015. Análisis de derivados con Python: análisis de datos, modelos, simulación,
calibración y cobertura. Finanzas Wiley. Recursos en http://dawp.tpq.io.
. 2018. Python para finanzas: dominar las finanzas basadas en datos. 2da ed. Sebasto
Pol: O'Reilly. Recursos en https://py4fi.pqp.io.
. 2020. Inteligencia artificial en finanzas: una guía basada en Python. Sebastopol: O'Reilly.
Recursos en https://aiif.pqp.io.
Luis, Miguel. 2015. Flash Boys: descifrando el código del dinero. Nueva York, Londres: WW
Norton y compañía.
McKinney, Wes. 2017. Python para análisis de datos: gestión de datos con Pandas, NumPy e
IPython. 2da ed. Sebastopol: O'Reilly.
Merton, Roberto. 1973. “Teoría de la fijación de precios de opciones racionales”. Bell Journal de
Economía y Ciencias de la Gestión 4: 141183.
Narang, Rishi. 2013. Dentro de la caja negra: una guía sencilla para el comercio cuantitativo y de
alta frecuencia. Hoboken y otros: John Wiley & Sons.
Salomón, Lewis y Louise Corso. 1991. “El impacto de la tecnología en la negociación de valores: el
mercado global emergente y las implicaciones para la regulación”.
Revisión de la ley de John Marshall 24 (2): 299338.
Vander Plas, Jake. 2016. Manual de ciencia de datos de Python: herramientas esenciales para
trabajar con datos. Sebastopol: O'Reilly.
CAPITULO 2
Infraestructura de Python
Para alguien nuevo en Python, la implementación de Python puede parecer casi sencilla.
Lo mismo se aplica a la gran cantidad de bibliotecas y paquetes que se pueden instalar opcionalmente.
En primer lugar, no existe un solo Python. Python viene en muchas versiones diferentes, como CPython,
Jython, IronPython o PyPy. Luego todavía existe la división entre Python 2.7 y el mundo 3.x. Este capítulo
se centra en CPython, la versión más popular del lenguaje de programación Python, y en la versión 3.8.
Incluso cuando nos centramos en CPython 3.8 (en adelante simplemente “Python”), la implementación
se dificulta por varias razones:
• Los paquetes opcionales de Python deben instalarse por separado y hay cientos
de ellos.
• Compilar (“construir”) estos paquetes no estándar por su cuenta puede ser complicado
debido a dependencias y requisitos específicos del sistema operativo.
• Las actualizaciones y mejoras para ciertos paquetes pueden causar la necesidad de volver a compilar
una multitud de otros paquetes.
17
Machine Translated by Google
• Cambiar o reemplazar un paquete puede causar problemas en (muchos) otros lugares. • Migrar de
Administrador de
paquetes Los administradores de paquetes como pip o conda ayudan con la instalación, actualización
y eliminación de paquetes de Python. También ayudan con la coherencia de las versiones de
diferentes paquetes.
Los
contenedores Docker representan sistemas de archivos completos que contienen todas las piezas de
un sistema necesarias para ejecutar un determinado software, como código, tiempo de ejecución o
herramientas del sistema. Por ejemplo, puede ejecutar un sistema operativo Ubuntu 20.04 con una
instalación de Python 3.8 y los códigos Python respectivos en un contenedor Docker alojado en una
máquina que ejecuta Mac OS o Windows 10. Un entorno de contenedores de este tipo también se
puede implementar más adelante en el nube sin mayores cambios.
Instancia en la
nube La implementación de código Python para aplicaciones financieras generalmente requiere
alta disponibilidad, seguridad y rendimiento. Por lo general, estos requisitos solo pueden
cumplirse mediante el uso de infraestructura de almacenamiento y computación profesional que
hoy en día está disponible en condiciones atractivas en forma de instancias de nube desde
bastante pequeñas hasta realmente grandes y potentes. Una ventaja de una instancia en la
nube (servidor virtual) en comparación con un servidor dedicado alquilado a largo plazo es que
a los usuarios generalmente se les cobra solo por las horas de uso real. Otra ventaja es que
dichas instancias en la nube están disponibles literalmente en uno o dos minutos si es necesario,
lo que ayuda con un desarrollo ágil y escalabilidad.
1 Un proyecto reciente llamado pipenv combina las capacidades del administrador de paquetes pip con las del
administrador de entorno virtual virtualenv. Consulte https://github.com/pypa/pipenv.
El objetivo de este capítulo es tener una instalación adecuada de Python con las herramientas más
importantes, así como paquetes numéricos, de análisis de datos y visualización, disponibles en una
infraestructura profesional. Luego, esta combinación sirve como columna vertebral para implementar
e implementar los códigos Python en capítulos posteriores, ya sea código de análisis financiero
interactivo o código en forma de scripts y módulos.
Aunque conda se puede instalar solo, una forma eficiente de hacerlo es a través de Miniconda, una
distribución mínima de Python que incluye conda como paquete y administrador de entorno virtual.
Instalación de Miniconda
...
Solicitud HTTP enviada, esperando respuesta... 200 OK
Longitud: 93052469 (89M) [application/xsh]
Guardando en: 'miniconda.sh'
2 En Windows, también puede ejecutar exactamente los mismos comandos en un contenedor Docker (consulte https://oreil.ly/GndRR).
Trabajar directamente en Windows requiere algunos ajustes. Consulte, por ejemplo, el libro de Matthias y Kane (2018) para obtener
más detalles sobre el uso de Docker.
...
Última actualización 25 de febrero de 2020
[/root/miniconda3] >>>
PREFIX=/root/miniconda3
Desempaquetando carga útil...
Recopilación de metadatos del paquete (current_repodata.json): hecho
Entorno de resolución: hecho
## Plan de paquete ##
...
ningún cambio /root/miniconda3/etc/profile.d/conda.csh /root/.bashrc
modificado
==> Para que los cambios surtan efecto, cierre y vuelva a abrir su shell actual. <==
Después de eso, es posible que desees actualizar conda ya que el instalador de Miniconda en general no se
actualiza con tanta regularidad como el propio conda :
Después de este procedimiento de instalación bastante simple, ahora hay disponibles una instalación básica de
Python y conda . La instalación básica de Python ya viene con algunas buenas baterías incluidas, como el motor
de base de datos SQLite3 . Puedes probar si puedes iniciar Python en una nueva instancia de shell o después
de agregar la ruta relevante a la variable de entorno respectiva (como se hizo en el ejemplo anterior):
puede utilizar para manejar eficientemente, entre otras cosas, la instalación, actualización y eliminación de
paquetes de Python. La siguiente lista proporciona una descripción general de las funciones principales:
Instalación de Python xx
conda install python=xx
Actualizando Python
conda actualizar Python
Instalación de un paquete
Actualizando un paquete
Quitar un paquete
actualización de conda
Buscando paquetes
Dadas estas capacidades, instalar, por ejemplo, NumPy (como uno de los más importantes
paquetes de la llamada pila científica) es un único comando. Cuando la instalación
La operación se realiza en una máquina con procesador Intel, el procedimiento se realiza automáticamente.
instala la biblioteca Intel Math Kernel mkl, que acelera las operaciones numéricas no
sólo para NumPy en máquinas Intel pero también para algunos otros paquetes científicos de Python:3
## Plan de paquete ##
especificaciones agregadas/actualizadas:
entumecido
3 La instalación del metapaquete nomkl, como en conda install numpy nomkl, evita la instalación automática
y uso de mkl y otros paquetes relacionados.
Total: 134,5MB
blas paquetes/main/linux64::blas1.0mkl
intelopenmp mkl paquetes/main/linux64::intelopenmp2020.1217
mkl paquetes/main/linux64::mkl2020.1217
service mkl_fft paquetes/main/linux64::mklservice2.3.0py38he904b0f_0
mkl_random paquetes/main/linux64::mkl_fft1.1.0py38h23d657b_0
numpy numpy paquetes/main/linux64::mkl_random1.1.1py38h0573a6f_0
base paquetes/main/linux64::numpy1.19.1py38hbc911f0_0
paquetes/main/linux64::numpybase1.19.1py38hfa32c7d_0
¿Continuar ([sí]/n)? y
También se pueden instalar varios paquetes a la vez. El indicador y indica que todos (potencialmente)
cial) las preguntas se responderán con sí:
## Plan de paquete ##
especificaciones agregadas/actualizadas:
ipython
matplotlib
pandas
pytables
scikitaprende
picante
Total: 144,9MB
Tras el procedimiento de instalación resultante, además de las estándar, están disponibles algunas de las
bibliotecas más importantes para análisis financiero:
IPython
Un shell Python interactivo mejorado
matplotlib
La biblioteca de trazado estándar para Python
NumPy
Manejo eficiente de matrices numéricas.
pandas
Gestión de datos tabulares, como datos de series de tiempo financieras.
PyTables
Un contenedor de Python para la biblioteca HDF5
aprendizajescikit
ciencia ficción
Esto proporciona un conjunto de herramientas básico para el análisis de datos en general y el análisis
financiero en particular. El siguiente ejemplo usa IPython y dibuja un conjunto de números pseudoaleatorios
con NumPy:
En [2]: np.random.seed(100)
En [4]: salir
(base) root@pyalgo:~#
En caso de que un paquete ya no sea necesario, se elimina eficientemente con conda remove:
## Plan de paquete ##
especificaciones eliminadas:
matplotlib
ciclador0.10.0py38_0
...
tornado6.0.4py38h7b6447c_1
¿Continuar ([sí]/n)? y
conda como administrador de paquetes ya es bastante útil. Sin embargo, su potencia total sólo
se vuelve evidente cuando se agrega la gestión del entorno virtual a la mezcla.
Eliminar un entorno
conda env eliminar nombre $ENVIRONMENT_NAME
A modo de ilustración sencilla, el código de ejemplo que sigue crea un entorno llamado py27, instala IPython
y ejecuta una línea de código Python 2.7.x. Aunque el soporte para Python 2.7 ha finalizado, el ejemplo
ilustra cómo se puede ejecutar y probar fácilmente el código Python 2.7 heredado:
## Plan de paquete ##
especificaciones agregadas/actualizadas:
pitón=2.7
python2.7.18 |
9,9MB
herramientas de |
512KB
configuración44.0.0 rueda0.33.6 |
42KB
Total: 12,2MB
_libgcc_mutex paquetes/main/linux64::_libgcc_mutex0.1main
certificados ca paquetes/main/linux64::cacertificates2020.6.240
...
zlib paquetes/main/linux64::zlib1.2.11h7b6447c_3
¿Continuar ([sí]/n)? y
(base) raíz@pyalgo:~#
Observe cómo el mensaje cambia para incluir (py27) después de activar el entorno:
Como lo demuestra este ejemplo, conda como administrador de entorno virtual permite instalar
diferentes versiones de Python una al lado de la otra. También permite instalar diferentes versiones
de ciertos paquetes. La instalación predeterminada de Python no se ve influenciada por dicho
procedimiento, ni tampoco otros entornos que puedan existir en la misma máquina. Todos los
entornos disponibles se pueden mostrar a través de conda info envs:
(py27) raíz@pyalgo:~#
A veces es necesario compartir información del entorno con otros o utilizar información del
entorno en varias máquinas, por ejemplo. Para este fin, se puede exportar la lista de paquetes
instalados a un archivo con conda env export. Sin embargo, esto sólo funciona correctamente de
forma predeterminada para el mismo sistema operativo, ya que las versiones de compilación se
especifican en el archivo yaml resultante . Sin embargo, se pueden eliminar para especificar solo
la versión del paquete mediante el indicador nobuilds :
canales:
dependencias
predeterminadas:
_libgcc_mutex=0.1
backcall=0.2.0
blas=1.0
blosc=1.20.0
...
zlib=1.2.11
zstd=1.4.5
prefijo: /root/miniconda3
(base) root@pyalgo:~#
A menudo, los entornos virtuales, que técnicamente no son mucho más que una determinada estructura
de (sub)carpetas, se crean para realizar algunas pruebas rápidas.4 En tal caso, un entorno se elimina
fácilmente (después de la desactivación) mediante conda env eliminar:
(base) raíz@pyalgo:~#
Con esto concluye la descripción general de conda como administrador de entorno virtual.
Para nuestros propósitos, basta con pensar en un contenedor Docker como un sistema de archivos
separado (“en contenedores”) que incluye un sistema operativo (por ejemplo, Ubuntu 20.04 LTS para
servidor), un tiempo de ejecución (Python), un sistema adicional y herramientas de desarrollo. herramientas, y
4 En la documentación oficial encontrará la siguiente explicación: “Los entornos virtuales de Python permiten
Los paquetes de Python se instalarán en una ubicación aislada para una aplicación en particular, en lugar de instalarse
globalmente”. Consulte la página Creación de entornos virtuales.
más bibliotecas y paquetes (Python) según sea necesario. Un contenedor Docker de este tipo podría
ejecutarse en una máquina local con Windows 10 Professional de 64 bits o en una instancia en la
nube con un sistema operativo Linux, por ejemplo.
Esta sección profundiza en los interesantes detalles de los contenedores Docker. Es una ilustración
concisa de lo que la tecnología Docker puede hacer en el contexto de la implementación de Python.5
A un nivel más técnico, encontrarás la siguiente definición de imagen Docker en el glosario de Docker:
Las imágenes de Docker son la base de los contenedores. Una imagen es una colección ordenada de cambios en
el sistema de archivos raíz y los parámetros de ejecución correspondientes para su uso dentro de un tiempo de
ejecución de contenedor. Una imagen normalmente contiene una unión de sistemas de archivos en capas apilados
uno encima del otro. Una imagen no tiene estado y nunca cambia.
• Un entorno de ejecución
El concepto está tomado de los contenedores de envío, que definen un estándar para enviar mercancías a nivel
mundial. Docker define un estándar para enviar software.
subsección ilustra la creación de una imagen de Docker basada en la última versión de Ubuntu que
incluye Miniconda, así como algunos paquetes importantes de Python. En
5 Véase Matthias y Kane (2018) para obtener una introducción completa a la tecnología Docker.
Además, realiza algunas tareas domésticas de Linux actualizando el índice de paquetes de Linux,
actualizando los paquetes si es necesario e instalando ciertas herramientas adicionales del sistema.
Para ello se necesitan dos guiones. Uno es un script Bash que hace todo el trabajo a nivel de Linux.6
El otro es el llamado Dockerfile, que controla el procedimiento de construcción de la imagen misma.
El script Bash del Ejemplo 21, que realiza la instalación, consta de tres partes principales. La
primera parte se ocupa de la limpieza de Linux. La segunda parte instala Miniconda, mientras que la
tercera parte instala paquetes Python opcionales. También hay comentarios más detallados en línea:
#!/bin/bash#
#LINUX GENERAL
# INSTALAR MINICONDA #
descargas Miniconda wget https://
repo.anaconda.com/miniconda/Miniconda3latestLinuxx86_64.sh O \
Miniconda.sh
bash Miniconda.sh b # lo instala rm rf Miniconda.sh #
elimina el instalador export PATH="/root/miniconda3/bin:$PATH" # antepone
la nueva ruta
# PERSONALIZACIÓN
6 Consulte el libro de Robbins (2016) para obtener una introducción concisa y una descripción general rápida de las secuencias de comandos Bash .
Véase también ver GNU Bash.
cd /root/ wget
http://hilpisch.com/.vimrc # Configuración de Vim
El Dockerfile del Ejemplo 22 utiliza el script Bash del Ejemplo 21 para crear una nueva imagen de
Docker. También tiene sus partes principales comentadas en línea:
#
# Construyendo una imagen de Docker con
# la última versión de Ubuntu y
# Instalación básica de Python #
Si estos dos archivos están en una sola carpeta y Docker está instalado, entonces la creación de la
nueva imagen de Docker es sencilla. Aquí, la etiqueta pyalgo:basic se utiliza para la imagen. Esta
etiqueta es necesaria para hacer referencia a la imagen, por ejemplo, cuando se ejecuta un
contenedor basado en ella:
Las imágenes de Docker existentes se pueden enumerar mediante imágenes de Docker. La nueva imagen debe ser
en la parte superior de la lista:
IMAGEN pyalgo basic a0bb86daf9ad ubuntu último 4e2eef94cd6b (base) pro:Docker Hace 2 minutos 1,79GB
yves$ Hace 5 días 73,9MB
Habiendo construido la imagen pyalgo:basic con éxito, uno puede ejecutar una respectiva
Contenedor acoplable con ejecución acoplable. La combinación de parámetros ti es necesaria para
procesos interactivos que se ejecutan dentro de un contenedor Docker, como un proceso de shell de
En [2]: np.random.seed(100)
En [6]: gl
Fuera[6]:
a b C
01,749765 0,342680 1,153036
1 0,252436 0,981321 0,514219
2 0,221180 1,070043 0,189496
3 0,255001 0,458027 0,435163
40,583595 0,816847 0,672721
Al salir de IPython, también se saldrá del contenedor, ya que es la única aplicación en ejecución.
dentro del contenedor. Sin embargo, puede separarse de un contenedor mediante lo siguiente:
0 un 5 no nulo flotador64
1 b 5 no nulos 2 c 5 tipos no nulos: flotador64
float64(3) flotador64
El comando de salida finaliza IPython y con ello detiene el contenedor Docker, como
Bueno. Se puede eliminar mediante Docker rm:
En [8]: salir
(base) pro:Docker yves$ docker rm e93c
e93c
(base) pro:Docker yves$
De manera similar, la imagen de Docker pyalgo:basic se puede eliminar a través de Docker rmi si no
necesita más. Si bien los contenedores son relativamente livianos, las imágenes individuales pueden
consumir bastante espacio de almacenamiento. En el caso de la imagen pyalgo:basic , el tamaño es
cerca de 2 GB. Es por eso que es posible que desees limpiar periódicamente la lista de Docker.
imágenes:
...
Eliminado: sha256:40adf11b689fc778297c36d4b232c59fedda8c631b4271672cc86f505710502d (base) pro:Docker yves$
Por supuesto, hay mucho más que decir sobre los contenedores Docker y sus beneficios en ciertos
escenarios de aplicaciones. Para los propósitos de este libro, proporcionan un enfoque moderno para
implementar Python, realizar el desarrollo de Python en un entorno completamente separado (en
contenedores) y enviar códigos para el comercio algorítmico.
El objetivo de esta sección es configurar un Droplet en DigitalOcean que tenga una instalación de
Python 3.8 más los paquetes típicamente necesarios (como NumPy y pandas) en combinación con un
Jupyter Lab protegido con contraseña y cifrado con Secure Sockets Layer (SSL). instalación del
servidor.8 Como conjunto de herramientas basado en web, Jupyter Lab proporciona varias herramientas
que se pueden utilizar a través de un navegador normal:
Jupyter Notebook
Este es uno de los entornos de desarrollo interactivos basados en navegador más populares (si
no el más popular) que presenta una selección de diferentes núcleos de lenguaje como Python, R
y Julia.
7 Para aquellos que aún no tienen una cuenta con un proveedor de nube, en http://bit.ly/do_sign_up, los nuevos usuarios obtienen una
Crédito inicial de 10 USD para DigitalOcean.
8 Técnicamente, Jupyter Lab es una extensión de Jupyter Notebook. Sin embargo, ambas expresiones son a veces
usado indistintamente.
Consola Python
Esta es una consola basada en IPython que tiene una interfaz gráfica de usuario diferente de la apariencia
de la implementación estándar basada en terminal.
Terminal
Esta es una implementación de shell del sistema accesible a través del navegador que permite no solo
todas las tareas típicas de administración del sistema, sino también el uso de herramientas útiles como
Vim para edición de código o git para control de versiones.
Editor
Otra herramienta importante es un editor de archivos de texto basado en navegador con resaltado de
sintaxis para muchos lenguajes de programación y tipos de archivos diferentes, así como capacidades
típicas de edición de texto/código.
Administrador
de archivos Jupyter Lab también proporciona un administrador de archivos completo que permite
operaciones típicas de archivos, como cargar, descargar y cambiar el nombre.
Tener Jupyter Lab instalado en un Droplet permite realizar el desarrollo y la implementación de Python a través
del navegador, evitando la necesidad de iniciar sesión en la instancia de la nube a través del acceso Secure
Shell (SSH).
Script de configuración
del servidor Este script organiza todos los pasos necesarios, como copiar otros archivos al Droplet y
ejecutarlos en el Droplet.
La siguiente sección trabaja hacia atrás a través de esta lista de archivos, ya que aunque el script de instalación
se ejecuta primero, los demás archivos deben haberse creado previamente.
(base) pro:cloud yves$ openssl req x509 nodes days 365 newkey rsa:2048 \ > keyout mykey.key
out mycert.pem Generando una clave privada
RSA
.......+++++
.....+++++
+++++
escribiendo una nueva clave privada en 'mykey.key'
Está a punto de que se le solicite que ingrese información que se incorporará en su solicitud de
certificado.
Lo que está a punto de ingresar es lo que se llama un Nombre Distinguido o DN.
Hay bastantes campos pero puedes dejar algunos en blanco.
Para algunos campos habrá un valor predeterminado. Si
ingresa '.', el campo quedará en blanco.
Nombre del país (código de 2 letras) [AU]:DE
Nombre del estado o provincia (nombre completo) [Algún estado]:Saarland
Nombre de la localidad (p. ej., ciudad) []:Voelklingen
Nombre de la organización (p. ej., empresa) [Internet Widgits Pty Ltd ]:Nombre de la unidad
organizativa de TPQ GmbH (p. ej., sección) []:Nombre común de comercio algorítmico
(p. ej., FQDN del servidor o SU nombre) []:Dirección de correo electrónico de
Jupyter Lab []:[email protected] (base)
pro:cloud yves $
Los dos archivos mykey.key y mycert.pem deben copiarse en el Droplet y el archivo de configuración de
Jupyter Notebook debe hacer referencia a ellos. Este archivo se presenta proximamente.
Se puede implementar de forma segura un servidor público de Jupyter Lab , como se explica en los
documentos de Jupyter Notebook. Entre otras cosas, Jupyter Lab estará protegido con contraseña. Para
este fin, hay disponible una función de generación de código hash de contraseña llamada passwd()
9 Con un certificado autogenerado de este tipo, es posible que deba agregar una excepción de seguridad cuando se lo solicite el
navegador. En Mac OS, incluso puedes registrar explícitamente el certificado como confiable.
en el subpaquete notebook.auth . El siguiente código genera un código hash de contraseña donde jupyter
es la contraseña misma:
En [2]: contraseña('jupyter')
Fuera[2]: 'sha1:da3a3dfc0445:052235bb76e56450b38d27e41a85a136c3bf9cd7'
En [3]: salir
Este código hash debe colocarse en el archivo de configuración de Jupyter Notebook como se presenta
en el Ejemplo 23. El archivo de configuración supone que los archivos de clave RSA se han copiado en
el Droplet a la carpeta /root/.jupyter/ .
#
# Archivo de configuración de Jupyter Notebook #
# CIFRADO SSL
# reemplace los siguientes nombres de archivos (y archivos utilizados) por los archivos/su elección
c.NotebookApp.certfile = u'/root/.jupyter/mycert.pem' c.NotebookApp.keyfile = u'/
root/.jupyter/mykey. llave'
# DIRECCIÓN IP Y PUERTO
# establezca ip en '*' para vincular todas las direcciones IP de la instancia de nube c.NotebookApp.ip =
'0.0.0.0' # es una buena idea configurar un
puerto predeterminado fijo y conocido para el acceso al servidor c.NotebookApp.port = 8888
# PROTECCIÓN DE CONTRASEÑA
'sha1:da3a3dfc0445:052235bb76e56450b38d27e41a85a136c3bf9cd7'
# ACCESO RAÍZ
# permitir que Jupyter se ejecute desde el usuario root
c.NotebookApp.allow_root = True
El siguiente paso es asegurarse de que Python y Jupyter Lab estén instalados en el Droplet.
bash para instalar Python y Jupyter Lab es similar al presentado en la sección “Uso de
contenedores Docker” en la página 30 para instalar Python a través de Miniconda en un
contenedor Docker. Sin embargo, el script del ejemplo 24 también debe iniciar el servidor
de Jupyter Lab . Todas las partes y líneas de código principales están comentadas en línea.
Ejemplo 24. Script Bash para instalar Python y ejecutar el servidor Jupyter Notebook
#!/bin/bash
#
# Script para instalar
# Herramientas del sistema Linux y componentes básicos de Python # así
como a
# Inicie el servidor de Jupyter Lab
#
# Python para el comercio algorítmico # (c) Dr. Yves J.
Hilpisch # The Python Quants GmbH #
#LINUX GENERAL
aptget update # actualiza el caché del índice de paquetes aptget update y #
actualiza paquetes # instala herramientas del sistema apt
get install y buildessential git #
herramientas del sistema aptget install y screen htop vim wget # herramientas del
sistema apt get update y bash # actualiza bash si es necesario aptget clean # limpia
el caché del índice del paquete
# INSTALANDO MINICONDA
wget https://repo.anaconda.com/miniconda/Miniconda3latestLinuxx86_64.sh \
O Miniconda.sh bash
Miniconda.sh b # instala Miniconda rm rf Miniconda.sh # elimina
el instalador # antepone la nueva ruta para la exportación de la sesión
actual PATH="/root/miniconda3/bin:$PATH" # antepone el nuevo ruta
en la configuración del shell cat >> ~/.profile <<EOF export
PATH="/root/miniconda3/bin:$PATH"
EOF
install y jupyter # análisis de datos interactivos en el navegador conda install y jupyterlab # entorno de Jupyter Lab conda install y
numpy # paquete de computación numérica conda install y pytables # contenedor para almacenamiento
binario HDF5 conda install y pandas # paquete de análisis de datos conda install y scipy # paquete de
cálculos científicos conda install y matplotlib # biblioteca de trazado estándar conda install y seaborn # biblioteca de
trazado estadístico conda install y quandl # contenedor para la API de datos de Quandl conda
install y scikitlearn # biblioteca de aprendizaje automático conda install y openpyxl # paquete para la interacción
con Excel conda install y xlrd xlwt # paquetes para la interacción con Excel conda install y pyyaml # paquete
pip install upgrade pip # actualizando el administrador de paquetes pip install q # registro y depuración pip install
con pandas pip install tensorflow # biblioteca de aprendizaje profundo pip install keras
# biblioteca de aprendizaje profundo pip instalar eikon # contenedor de Python para Refinitiv Eikon Data
tpqoa
Este script debe copiarse en el Droplet y debe iniciarse mediante el script de orquestación,
como se describe en la siguiente subsección.
segundo script bash, que configura el Droplet, es el más corto (consulte el Ejemplo 25).
Básicamente copia todos los demás archivos al Droplet para los cuales se espera la
dirección IP respectiva como parámetro. En la última línea, inicia el script bash install.sh ,
que a su vez realiza la instalación e inicia Jupyter Lab.
servidor.
#!/bin/bash
#
IP_MAESTRA=$1
Ahora todo está junto para probar el código de configuración. En DigitalOcean, cree un nuevo Droplet con opciones similares a
estas:
Sistema operativo
Ubuntu 20.04 LTS x64 (la versión más reciente disponible al momento de escribir este artículo)
Tamaño
clave SSH
Agregue una (nueva) clave SSH para iniciar sesión sin contraseña10
Nombre de la gota
Finalmente, al hacer clic en el botón Crear se inicia el proceso de creación del Droplet, que generalmente demora
aproximadamente un minuto. El resultado principal al continuar con el procedimiento de configuración es la dirección IP, que
podría ser, por ejemplo, 134.122.74.144 cuando
10 Si necesita ayuda, visite Cómo usar claves SSH con DigitalOcean Droplets o Cómo usar SSH
Claves con PuTTY en DigitalOcean Droplets (usuarios de Windows).
ha elegido Frankfurt como ubicación de su centro de datos. Configurar Droplet ahora es tan fácil como
sigue:
Sin embargo, el proceso resultante puede tardar un par de minutos. Finaliza cuando aparece un mensaje
del servidor de Jupyter Lab que dice algo como lo siguiente:
https://134.122.74.144:8888
Después de tal vez agregar una excepción de seguridad, debería aparecer la pantalla de inicio de
sesión de Jupyter Notebook solicitando una contraseña (en nuestro caso, jupyter) . Ahora todo está
listo para iniciar el desarrollo de Python en el navegador a través de Jupyter Lab, a través de la
consola basada en IPython y a través de una ventana de terminal o el editor de archivos de texto.
También están disponibles otras capacidades de administración de archivos, como cargar archivos,
eliminar archivos o crear carpetas.
Conclusiones
Python es el lenguaje de programación y la plataforma tecnológica elegidos no sólo para este libro sino
también para casi todas las instituciones financieras líderes. Sin embargo, la implementación de Python
puede ser, en el mejor de los casos, complicada y, a veces, incluso tediosa y estresante.
Afortunadamente, hoy en día hay tecnologías disponibles (casi todas tienen menos de diez años) que
ayudan con el problema de la implementación. El software de código abierto conda ayuda tanto con el
paquete Python como con la gestión del entorno virtual. Los contenedores Docker van aún más lejos en
el sentido de que se pueden crear fácilmente sistemas de archivos completos y entornos de ejecución en
un “sandbox” técnicamente protegido, o el contenedor. Yendo incluso un paso más allá, los proveedores
de nube como DigitalOcean ofrecen capacidad de computación y almacenamiento en
Conclusiones | 43
Machine Translated by Google
Puede encontrar información sobre los contenedores Docker, entre otros lugares, en la página de inicio
de Docker, así como en lo siguiente:
• Matías, Karl y Sean Kane. 2018. Docker: en funcionamiento. 2da ed. Sebasto
Pol: O'Reilly.
Robbins (2016) proporciona una introducción concisa y una descripción general del lenguaje de
programación Bash :
• Robbins, Arnold. 2016. Referencia de bolsillo de Bash. 2da ed. Sebastopol: O'Reilly.
En The Jupyter Notebook Docs se explica cómo ejecutar de forma segura un servidor público de Jupyter
Notebook/Lab . También está disponible JupyterHub , que permite la gestión de múltiples usuarios para
un servidor Jupyter Notebook (consulte JupyterHub).
Para registrarse en DigitalOcean con un saldo inicial de 10 USD en su nueva cuenta, visite http://bit.ly/
do_sign_up. Esto paga por dos meses de uso para el Droplet más pequeño.
CAPÍTULO 3
Claramente, los datos superan a los algoritmos. Sin datos completos, se tiende a obtener
predicciones no completas.
—Rob Thomas (2016)
En el comercio algorítmico, generalmente hay que tratar con cuatro tipos de datos, como se ilustra
en la Tabla 31. Aunque simplifica el mundo de los datos financieros, distinguir entre datos históricos
versus en tiempo real y estructurados versus no estructurados a menudo resulta útil en entornos
técnicos.
Este libro se ocupa principalmente de datos estructurados (datos numéricos, tabulares) tanto de
tipo histórico como en tiempo real. Este capítulo en particular se centra en datos históricos
estructurados, como los valores de cierre al final del día para las acciones de SAP SE negociadas
en la Bolsa de Frankfurt. Sin embargo, esta categoría también incluye datos intradía, como los
datos de barra de 1 minuto de las acciones de Apple, Inc. que cotizan en la bolsa de valores
NASDAQ. El procesamiento de datos estructurados en tiempo real se trata en el Capítulo 7.
Un proyecto de comercio algorítmico generalmente comienza con una idea o hipótesis comercial
que debe probarse (retrospectivamente) en función de datos financieros históricos. Este es el
contexto de este capítulo, cuyo plan es el siguiente. “Lectura de datos financieros de diferentes
fuentes” en la página 46 utiliza pandas para leer datos de diferentes fuentes basadas en archivos
y web. “Trabajar con fuentes de datos abiertas” en la página 52 presenta a Quandl como una
plataforma popular de fuentes de datos abiertas. “API de datos Eikon” en la página 55 presenta el
contenedor Python para API de datos Eikon de Refinitiv. Finalmente, "almacenar datos financieros de manera eficiente"
45
Machine Translated by Google
en la página 65 se muestra brevemente cómo almacenar datos históricos estructurados de manera eficiente con
pan das basado en el formato de almacenamiento binario HDF5 .
El objetivo de este capítulo es tener datos financieros disponibles en un formato con el que se pueda implementar
de manera efectiva la prueba retrospectiva de ideas e hipótesis comerciales. Los tres temas principales son la
importación de datos, el manejo de los datos y su almacenamiento.
Este capítulo y los siguientes asumen una instalación de Python 3.8 con paquetes de Python instalados como se
explica en detalle en el Capítulo 2. Por el momento, aún no es relevante en qué infraestructura se proporciona
exactamente este entorno Python. Para obtener más detalles sobre operaciones eficientes de entrada y salida con
Python, consulte Hilpisch (2018, cap. 9).
El conjunto de datos
En esta sección, trabajamos con un conjunto de datos bastante pequeño para el precio de las acciones de Apple
Inc. (con símbolo AAPL y Reuters Instrument Code o RIC AAPL.O) recuperados de la API de datos de Eikon para
abril de 2020.
Dado que dichos datos financieros históricos se han almacenado en un archivo CSV en el disco, se puede utilizar
Python puro para leer e imprimir su contenido:
En [1]: fn = '../data/AAPL.csv'
Este enfoque permite una inspección simple de los datos. Uno aprende que hay una línea de encabezado y que los
puntos de datos individuales por fila representan Fecha, ABIERTO, ALTO,
BAJO, CERRAR, CONTAR y VOLUMEN, respectivamente. Sin embargo, los datos aún no están
disponibles en la memoria para su uso posterior con Python.
trabajar con datos almacenados como un archivo CSV, el archivo debe analizarse y los datos
deben almacenarse en una estructura de datos de Python. Python tiene un módulo integrado
llamado csv que admite la lectura de datos de un archivo CSV. El primer método produce un
objeto de lista que contiene otros objetos de lista con los datos del archivo:
En [6]: datos[:5]
Salida[6]: [['Fecha', 'ALTO', 'CERRAR', 'BAJO', ' ABRIR', 'CONTAR', 'VOLUMEN'],
['20200401',
'248.72',
'240.91',
'239.13',
'246.5',
'460606.0',
'44054638.0'],
['20200402',
'245.15',
'244.93' ,
'236.9',
'240.34',
'380294.0',
'41483493.0'],
['20200403',
'245.7',
'241.41' ,
'238.9741',
'242.8',
'293699.0',
'32470017.0' ],
['20200406',
'263.11',
'262.47',
'249.38',
'250.9',
'486681.0',
'50455071.0']]
Una lista por comprensión que agrega cada línea del archivo CSV como un objeto de lista al objeto de
lista resultante .
Trabajar con un objeto de lista anidado de este tipo (para el cálculo del precio de cierre promedio, por ejemplo)
es posible en principio, pero no es realmente eficiente ni intuitivo.
El uso de un objeto iterador csv.DictReader en lugar del objeto csv.reader estándar hace que estas tareas
sean un poco más manejables. Luego, cada fila de datos en el archivo CSV (aparte de la fila del encabezado)
se importa como un objeto dict para que se pueda acceder a los valores individuales a través de la clave
respectiva:
En [9]: datos[:3]
Salida[9]: [{'Fecha': '20200401', 'ALTO':
'248.72', 'CERRAR':
'240.91', 'BAJO':
'239.13', 'ABIERTO':
'246.5' , 'CONTAR':
'460606.0', 'VOLUMEN':
'44054638.0'},
{'Fecha': '20200402',
'ALTO': '245,15',
'CERRAR': '244,93',
'BAJO': '236,9',
'ABIERTO': '240,34',
'CONTAR': ' 380294.0',
'VOLUMEN': '41483493.0'},
{'Fecha': '20200403',
'ALTO': '245.7',
'CERRAR': '241.41',
'BAJO': '238.9741', '
ABIERTO': '242.8',
'CONTADOR': '293699.0',
'VOLUMEN': '32470017.0'}]
Aquí, se crea una instancia del objeto iterador csv.DictReader , que lee cada fila de datos en un objeto
dict , dada la información en la fila del encabezado.
Basado en objetos de dictado único , las agregaciones ahora son algo más fáciles de realizar.
plato. Sin embargo, todavía no se puede hablar de una forma conveniente de calcular la media de
el precio de cierre de las acciones de Apple al inspeccionar el código Python respectivo:
Primero, se genera un objeto de lista mediante una lista por comprensión con todos los valores de cierre;
en segundo lugar, se suman todos estos valores; tercero, la suma resultante se divide
por el número de valores de cierre.
Esta es una de las principales razones por las que los pandas han ganado tanta popularidad en el mundo.
Comunidad de Python. Hace que la importación de datos y el manejo de, por ejemplo,
conjuntos de datos de series de tiempo financieras más convenientes (y a menudo también considerablemente más rápidos)
que Python puro.
En [14]: datos.tail()
Fuera[14]: ALTO CIERRE BAJO ABIERTO CONTAR VOLUMEN
Fecha
20200424 283.01 282.97 277.00 277.20 306176.0 31627183.0 20200427 284.54 283.17
279.95 281.80 300771.0 29271893.0
20200428 285,83 278,58 278,20 285,08 285384,0 28001187,0
20200429 289,67 287,73 283,89 284,73 324890,0 34320204,0
20200430 294,53 293,80 288,35 289,96 471129,0 45765968,0
Esto importa los datos del archivo CSV, lo que indica que la primera columna se tratará como la
columna de índice y permitirá que las entradas de esa columna se interpreten como información
de fecha y hora.
El método data.tail() imprime de forma predeterminada las cinco filas de datos más recientes.
Calcular la media de los valores de cierre de las acciones de Apple ahora es solo una llamada al
método:
En [15]: datos['CLOSE'].media()
Fuera[15]: 272.38619047619056
también destaca en la exportación de datos almacenados en objetos DataFrame cuando estos datos
deben compartirse en un formato no específico de Python. Además de poder exportar a archivos CSV,
pandas también permite exportar en forma de archivos de hojas de cálculo de Excel y archivos JSON,
los cuales son formatos de intercambio de datos populares en la industria financiera. Un procedimiento
de exportación de este tipo normalmente necesita una única llamada a un método:
En [17]: data.to_json('datos/aapl.json')
En particular, cuando se trata de la interacción con archivos de hojas de cálculo de Excel, existen
formas más elegantes que simplemente realizar un volcado de datos en un archivo nuevo. xlwings, por ejemplo, es
un potente paquete Python que permite una interacción eficiente e inteligente
entre Python y Excel (visite la página de inicio de xlwings ).
En [20]: data_copy_1.head()
Fuera[20]: ALTO CIERRE BAJO CUENTA ABIERTA VOLUMEN
Fecha
20200401 248.72 240.91 239.1300 246.50 460606 44054638
20200402 245.15 244.93 236.9000 240.34 380294 41483493
20200403 245,70 241,41 238,9741 242,80 293699 32470017
20200406 263.11 262.47 249.3800 250.90 486681 50455071
20200407 271,70 259,43 259,0000 270,80 467375 50721831
En [22]: data_copy_2.head()
Fuera[22]: ALTO CIERRE BAJO CUENTA ABIERTA VOLUMEN
20200401 248.72 240.91 239.1300 246.50 460606 44054638
20200402 245.15 244.93 236.9000 240.34 380294 41483493
20200403 245,70 241,41 238,9741 242,80 293699 32470017
20200406 263.11 262.47 249.3800 250.90 486681 50455071
20200407 271,70 259,43 259,0000 270,80 467375 50721831
Esto lee los datos del archivo de hoja de cálculo de Excel en un nuevo objeto DataFrame .
Se imprimen las primeras cinco filas de la primera copia de los datos en memoria.
Esto lee los datos del archivo JSON en otro objeto DataFrame .
Luego, esto imprime las primeras cinco filas de la segunda copia en memoria de los datos.
pandas resulta útil para leer y escribir datos financieros desde y hacia diferentes tipos de archivos de datos.
A menudo, la lectura puede ser complicada debido a formatos de almacenamiento no estándar (como un
“;” en lugar de un “,” como separador), pero pandas generalmente proporciona el conjunto correcto de
combinaciones de parámetros para hacer frente a tales casos. Aunque todos los ejemplos de esta sección
utilizan únicamente un pequeño conjunto de datos, se puede esperar operaciones de entradasalida de
alto rendimiento de los pandas en los escenarios más importantes cuando los conjuntos de datos son
mucho más grandes.
La única excepción notable para los propósitos de este libro es Quandl, una plataforma que agrega una
gran cantidad de fuentes de datos abiertas y premium (es decir, de pago). Los datos se proporcionan a
través de una API unificada para la cual hay disponible un paquete contenedor de Python.
El paquete contenedor de Python para la API de datos de Quandl (consulte la página contenedora de
Python en Quandl y la página de GitHub del paquete) se instala con conda a través de conda install quandl.
El primer ejemplo muestra cómo recuperar los precios promedio históricos para el tipo de cambio BTC/
USD desde la introducción de Bitcoin como criptomoneda. Con Quandl, las solicitudes siempre esperan
una combinación de la base de datos y el conjunto de datos específico deseado. (En el ejemplo, BCHAIN
y MKPRU). Esta información generalmente se puede buscar en la plataforma Quandl. Por ejemplo, la
página relevante en Quandl es BCHAIN/MKPRU.
Si bien una gran parte de los conjuntos de datos de la plataforma Quandl son gratuitos, algunos de ellos
requieren una clave API. Esta clave es necesaria después de un cierto límite de API gratuita.
llamadas también. Cada usuario obtiene dicha clave registrándose para obtener una cuenta gratuita de Quandl en
la página de registro de Quandl. Las solicitudes de datos que requieren una clave API esperan que la clave sea
proporcionado como parámetro api_key. En el ejemplo, la clave API (que se encuentra en
la página de configuración de la cuenta) se almacena como una cadena en la variable quandl_api_key. El
El valor concreto de la clave se lee desde un archivo de configuración a través del configparser.
módulo:
En [27]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4254 entradas, 20090103 al 20200826
Columnas de datos ( 1 columna en total):
# Columna Tipo D de recuento no nulo
En [28]: datos['Valor'].resample('A').último()
Fuera[28]: Fecha
20091231 0.000000
20101231 0,299999
20111231 4.995000
20121231 13.590000
20131231 731.000000
20141231 317.400000
20151231 428.000000
20161231 952.150000
20171231 13215.574000
20181231 3832.921667
20191231 7385.360000
20201231 11763.930000
Frecuencia: ADEC, Nombre: Valor, tipo d: float64
Selecciona la columna Valor y vuelve a muestrearla, desde los valores diarios originales hasta los anuales.
valores y define la última observación disponible como la relevante.
Quandl también proporciona, por ejemplo, diversos conjuntos de datos para acciones individuales, como
precios de las acciones, fundamentos de las acciones o conjuntos de datos relacionados con opciones negociadas en un determinado
existencias:
En [30]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 579 entradas, 20180102 al 20200430
Columnas de datos (un total de 10 columnas):
# Columna Tipo D de recuento no nulo
La clave API también se puede configurar permanentemente con el contenedor de Python a través de
siguiente:
q.ApiConfig.api_key = 'TU_API_KEY'
La plataforma Quandl también ofrece conjuntos de datos premium para los cuales se requiere una suscripción o tarifa.
requerido. La mayoría de estos conjuntos de datos ofrecen muestras gratuitas. El ejemplo recupera la opción
volatilidades implícitas para las acciones de Microsoft Corp. El conjunto de datos de muestra gratuito es bastante
grande, con más de 4100 filas y muchas columnas (solo se muestra un subconjunto). El
Las últimas líneas de código muestran los valores de volatilidad implícita a 30, 60 y 90 días para los cinco
últimos días disponibles:
Con esto concluye la descripción general del paquete contenedor de Python quandl para Quandl.
API de datos. La plataforma y el servicio de Quandl están creciendo rápidamente y demuestran ser una
fuente valiosa de datos financieros en un contexto comercial algorítmico.
El software de código abierto es una tendencia que comenzó hace muchos años. Tiene
redujo las barreras de entrada en muchas áreas y también en algorítmicas
comercio. Una nueva tendencia que se refuerza en este sentido son las fuentes de datos abiertos.
ces. En algunos casos, como en el caso de Quandl, incluso proporcionan una alta
conjuntos de datos de calidad. No se puede esperar que los datos abiertos compitan
reemplazar completamente las suscripciones de datos profesionales en el corto plazo, pero
Representan un medio valioso para comenzar con algorítmica.
comerciar de manera rentable.
Las fuentes de datos abiertas son una bendición para los operadores algorítmicos que desean iniciarse en el
espacio y querer poder probar rápidamente hipótesis e ideas basadas en datos financieros reales.
conjuntos de datos especiales. Sin embargo, tarde o temprano, los conjuntos de datos abiertos ya no serán suficientes para
satisfacer las necesidades de comerciantes y profesionales más ambiciosos.
Refinitiv es uno de los mayores proveedores de noticias y datos financieros del mundo. Su actual
producto estrella de escritorio es Eikon, que es el equivalente al Terminal de Bloomberg, el principal
competidor en el campo de los servicios de datos. La Figura 31 muestra una captura de pantalla de
Eikon en la versión basada en navegador. Eikon proporciona acceso a petabytes de datos a través
de un único punto de acceso.
Si es suscriptor de Eikon y tiene una cuenta para las páginas de la comunidad de desarrolladores, encontrará
una descripción general de la biblioteca de secuencias de comandos Python Eikon en Inicio rápido.
Para utilizar la API de datos de Eikon, es necesario configurar Eikon app_key . Lo obtienes a través de la
aplicación App Key Generator (APPKEY) en Eikon o Workspace:
En [36]: ek.set_app_key(config['eikon']['app_key'])
En [37]: ayuda(ek)
Ayuda sobre el paquete eikon:
NOMBRE
Perfil
data_grid
eikonError
json_requests
news_request
streaming_session (paquete) simbología
herramientas
time_series
SUBMÓDULOS
caché
escritorio_sesión
istream_callback
flujo de elementos
sesión
arroyo
conexión_transmisión
precio de transmisión
streamingprice_callback
precios de streaming
VERSIÓN
1.1.5
ARCHIVO
/Users/yves/Python/envs/py38/lib/python3.8/sitepackages/eikon/__init__
.py
Establece app_key.
La recuperación de datos de series de tiempo financieras históricas es tan sencilla como con el
otros envoltorios utilizados antes:
En [41]: datos.claves()
Fuera[41]: MultiIndex([('AAPL.O', 'ALTO'),
('AAPL.O', 'CERRAR'),
('AAPL.O', 'BAJO'),
('AAPL.O', 'ABIERTO'),
('AAPL.O', 'CONTAR'),
('AAPL.O', 'VOLUMEN'),
('MSFT.O', 'ALTO'),
('MSFT.O', 'CERRAR'),
('MSFT.O', 'BAJO'),
('MSFT.O', 'ABIERTO'),
('MSFT.O', 'CONTAR'),
('MSFT.O', 'VOLUMEN'),
('GOOG.O', 'ALTO'),
('GOOG.O', 'CERRAR'),
('GOOG.O', 'BAJO'),
('GOOG.O', 'ABIERTO'),
('GOOG.O', 'CONTAR'),
('GOOG.O', 'VOLUMEN')],
)
En [42]: escriba(datos['AAPL.O'])
Fuera[42]: pandas.core.frame.DataFrame
En [43]: datos['AAPL.O'].info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 84 entradas, 20200102 al 20200501
Columnas de datos (un total de 6 columnas):
# Columna Tipo D de recuento no nulo
En [44]: datos['AAPL.O'].tail()
Fuera[44]: ALTO CIERRE RECUENTO ABIERTO BAJO VOLUMEN
Fecha
20200427 284,54 283,17 279,95 281,80 300771 29271893
20200428 285,83 278,58 278,20 285,08 285384 28001187
20200429 289,67 287,73 283,89 284,73 324890 34320204
20200430 294,53 293,80 288,35 289,96 471129 45765968
20200501 299,00 289,07 285,85 286,25 558319 60154175
Esto proporciona una descripción general de los datos almacenados en el objeto DataFrame .
La belleza de trabajar con una API de servicio de datos profesional se hace evidente cuando
uno desea trabajar con múltiples símbolos y en particular con un nivel granular diferente.
Cuidad de los datos financieros (es decir, otros intervalos de tiempo):
En [45]: %%tiempo
datos = ek.get_timeseries(symbols,
start_date='20200814',
end_date='20200815',
intervalo='minuto',
campos='*')
Tiempos de CPU: usuario 58,2 ms, sistema: 3,16 ms, total: 61,4 ms
Tiempo de pared: 2,02 s
AAPL.O
ALTO BAJO ABIERTO CERRAR VOLUMEN DE CONTEO
Fecha
20200814 19:01:00 457.1699 456.6300 457.14 456.83 1457 104693
20200814 19:02:00 456.9399 456.4255 456.81 456.45 1178 79740
20200814 19:03:00 456.8199 456.4402 456.45 456.67 908 68517
20200814 19:04:00 456.9800 456.6100 456.67 456.97 20200814 19:05:00 457.1900 665 53649
456.9300 456.98 457.00 679 49636
MSFT.O
ALTO BAJO ABIERTO CERRAR VOLUMEN DE CONTEO
Fecha
GOOG.O
ALTO BAJO ABRIR CERRAR CONTAR VOLUMEN
Fecha
20200814 19:01:00 1510.42 1509.3288 1509.5100 1509.8550 47 1577 20200814 19:02:00 1510.30
1508.8000 1509.7559 1508.8647 71 2950 20200814 19:03:00 1510.21 1508.7200 1508.7200 1509.8100 33
603
20200814 19:04:00 1510.21 1508.7200 1509.8800 1509.8299 41 934
20200814 19:05:00 1510.21 1508.7300 1509.5500 1509.6600 30 445
El intervalo de tiempo…
En [48]: %%tiempo
datos = ek.get_timeseries(símbolos[0],
start_date='20200814 15:00:00',
end_date='20200814 15:30:00', intervalo='tick',
campos=['*'])
Tiempos de CPU: usuario 257 ms, sistema: 17,3 ms, total: 274 ms Tiempo
de pared: 2,31 s
En [50]: datos.head()
Fuera[50]: VALOR VOLUMEN
Fecha
20200814 15:00:00.019 453.2499 20200814 60
15:00:00.036 453.2294 3
20200814 15:00:00.146 453.2100 5
20200814 15:00:00.146 453.2100 100
20200814 15:00:00.236 453.2100 2
En [52]: resampled.tail()
Fuera[52]: VALOR VOLUMEN
Fecha
20200814 15:28:00 453.9000 29746
20200814 15:28:30 454.2869 86441
20200814 15:29:00 454.3900 49513
20200814 15:29:30 454.7550 98520
20200814 15:30:00 454.6200 55592
El conjunto de datos de series temporales muestra longitudes de intervalos muy irregulares (heterogéneos)
entre dos garrapatas.
Los datos de tick se vuelven a muestrear en un intervalo de 30 segundos de duración (tomando el último valor
y la suma, respectivamente)…
El código que sigue recupera titulares de noticias durante un intervalo de tiempo fijo que incluye
Apple Inc. como empresa y "Macbook" como palabra. Los cinco éxitos más recientes son dis
jugado como máximo:
date_from='202041',
date_to='202051')
En [54]: titulares
Fuera[54]: versiónCreada \
20200420 21:33:37.332 20200420 21:33:37.332000+00:00
20200420 10:20:23.201 20200420 10:20:23.201000+00:00
20200420 02:32:27.721 20200420 02:32:27.721000+00:00
20200415 12:06:58.693 20200415 12:06:58.693000+00:00
20200409 21:34:08.671 20200409 21:34:08.671000+00:00
texto \
20200420 21:33:37.332 Apple dijo que lanzará nuevos AirPods, MacBook Pro 20200420 ...
10:20:23.201 Apple podría lanzar AirPods actualizados, M de 13 pulgadas ...
20200420 02:32:27.721 Apple supuestamente lanzará nuevos AirPods junto con...
20200415 12:06:58.693 Apple presenta una patente para iPhones, MacBook indu...
20200409 21:34:08.671 Apple lanza una nueva actualización de software para MacBoo...
ID de historia \
20200420 21:33:37.332 urn:newsml:reuters.com:20200420:nNRAble9rq:1
20200420 10:20:23.201 urna:newsml:reuters.com:20200420:nNRAbl8eob:1
20200420 02:32:27.721 urn:newsml:reuters.com:20200420:nNRAbl4mfz:1
20200415 12:06:58.693 urn:newsml:reuters.com:20200415:nNRAbjvsix:1
20200409 21:34:08.671 urn:newsml:reuters.com:20200409:nNRAbi2nbb:1
código fuente
20200420 21:33:37.332 NS:TIMIND
20200420 10:20:23.201 NS:BUSSTA
20200420 02:32:27.721 NS:HINDUT
20200415 12:06:58.693 NS:HINDUT
20200409 21:34:08.671 NS:TIMIND
En [56]: historia
Fuera[56]: versiónCreada 20200420 21:33:37.332000+00:00
texto Apple dijo que lanzará nuevos AirPods y MacBook Pro ...
storyId urna:newsml:reuters.com:20200420:nNRAble9rq:1
nombre del NS:TIMENTE
código fuente: 20200420 21:33:37.332000, tipo d: objeto
En [59]: HTML(texto_noticia)
Salida[59]: < objeto IPython.core.display.HTML >
NUEVA DELHI: Apple lanzó recientemente su tan esperado teléfono inteligente asequible iPhone SE. Ahora
parece que la empresa se está preparando para otro lanzamiento.
Se dice que Apple lanzará la próxima generación de AirPods y la nueva MacBook Pro de 13 pulgadas el
próximo mes.
En febrero, un informe en línea reveló que el gigante tecnológico con sede en Cupertino está trabajando en
AirPods Pro Lite. Ahora, un tweet del informante Job Posser ha revelado que Apple pronto presentará
nuevos AirPods y MacBook Pro.
Jon Posser tuiteó: "Los nuevos AirPods (que se suponía que estarían en el evento de marzo) ya
están listos para funcionar.
Probablemente junto con el MacBook Pro el próximo mes." Sin embargo, no hay muchos detalles disponibles
sobre los próximos productos en este momento. Se suponía que la compañía lanzaría estos productos
en el evento de marzo junto con el iPhone SE.
Este año la compañía ha decidido realizar un evento únicamente online debido al brote de coronavirus.
Los informes sugieren que este año la compañía planea lanzar los nuevos AirTags y un par de auriculares
Bluetooth premium en el evento. Con Apple AirTags, los usuarios podrán localizar elementos del mundo real,
como llaves o maletas, en la aplicación Find My.
Los AirTags también tendrán capacidades de búsqueda fuera de línea que la compañía introdujo en el núcleo
de iOS 13. Aparte de esto, también se dice que Apple presentará sus auriculares Bluetooth de alta gama. Se
espera que los auriculares Bluetooth ofrezcan una mejor calidad de sonido y batería de respaldo en comparación
con los AirPods.
Define el intervalo...
…y se muestra el story_id .
Con esto concluye la ilustración del paquete contenedor de Python para la API de datos de Refinitiv
Eikon.
En el comercio algorítmico, uno de los escenarios más importantes para la gestión de conjuntos de datos es
"recuperar una vez y utilizar varias veces". O desde una perspectiva de entradasalida (IO), es "escribir una vez,
leer varias veces". En el primer caso, los datos podrían recuperarse de un servicio web y luego usarse para
probar una estrategia varias veces basándose en una copia temporal en memoria del conjunto de datos. En el
segundo caso, los datos de ticks que se reciben continuamente se escriben en el disco y luego se usan
nuevamente varias veces para ciertas manipulaciones (como agregaciones) en combinación con un procedimiento
de backtesting.
Esta sección asume que la estructura de datos en memoria para almacenar los datos es un objeto Pandas
DataFrame , sin importar de qué fuente se adquieran los datos (de un archivo CSV, un servicio web, etc.).
Para tener disponible un conjunto de datos algo significativo en términos de tamaño, la sección utiliza un conjunto
de datos financieros de muestra generado mediante el uso de números pseudoaleatorios. “Secuencias de
comandos de Python” en la página 78 presenta el módulo de Python con una función llamada generate_sam
ple_data() que realiza la tarea.
En principio, esta función genera un conjunto de datos financieros de muestra en forma tabular de tamaño
arbitrario (la memoria disponible, por supuesto, establece un límite):
Imprime un conjunto de datos financieros de muestra con cinco filas y cuatro columnas.
En [63]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 5000000 entradas, 20210101 00:00:00 a 20300705
05:19:00
Frecuencia: T
0 No0 flotador64
1 número 1 flotador64
2 No2 flotador64
3 No3 flotador64
4 número 4 flotador64
5 No5 flotador64
6 No6 flotador64
7 No7 flotador64
8 No8 flotador64
9 número 9 flotador64
tipos de datos: float64(10)
uso de memoria: 419,6 MB
Se genera un conjunto de datos financieros de muestra con 5.000.000 de filas y diez columnas; el
La generación tarda un par de segundos.
El segundo paso es abrir un objeto HDFStore (es decir, un archivo de base de datos HDF5) en el disco.
y escribir el objeto DataFrame en él.1 El tamaño en disco de aproximadamente 440 MB es un poco
mayor que el del objeto DataFrame en memoria . Sin embargo, la velocidad de escritura es aproximadamente
cinco veces más rápido que la generación en memoria del conjunto de datos de muestra.
1 Por supuesto, también se pueden almacenar varios objetos DataFrame en un único objeto HDFStore .
Trabajar en Python con almacenes binarios como archivos de bases de datos HDF5 normalmente te hace escribir.
velocidades cercanas al máximo teórico del hardware disponible:2
En [66]: h5
Salida [66]: <clase 'pandas.io.pytables.HDFStore'>
Ruta del archivo: datos/datos.h5
En [67]: ls n datos/datos.*
rwrr@ 1 501 20 440007240 25 de agosto 11:48 data/data.h5
En [68]: h5.cerrar()
Esto abre el archivo de base de datos en el disco para escribir (y sobrescribe un archivo potencialmente
archivo existente con el mismo nombre).
El tercer paso es leer los datos del objeto HDFStore basado en archivos . Leyendo también
generalmente se produce cerca de la velocidad máxima teórica:
En [71]: copia_datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 5000000 entradas, 20210101 00:00:00 a 20300705
05:19:00
Frecuencia: T
0 No0 flotador64
1 número 1 flotador64
2 Todos los valores reportados aquí son del MacMini del autor con procesador Intel i7 hexa core (12 subprocesos), 32 GB
de memoria de acceso aleatorio (RAM DDR4) y una unidad de estado sólido (SSD) de 512 GB.
2 No2 flotador64
3 No3 flotador64
4 número 4 flotador64
5 No5 flotador64
6 No6 flotador64
7 No7 flotador64
8 No8 flotador64
9 número 9 flotador64
tipos de datos: float64(10)
uso de memoria: 419,6 MB
En [72]: h5.close()
Existe otra forma algo más flexible de escribir los datos desde un DataFrame.
objeto a un objeto HDFStore . Para este fin, se puede utilizar el método to_hdf() del
objeto DataFrame y establezca el parámetro de formato en tabla (consulte la referencia de la API to_hdf ).
página de referencia). Esto permite agregar nuevos datos al objeto de tabla en el disco y
también, por ejemplo, la búsqueda de datos en el disco, lo cual no es posible con el
primer enfoque. El precio a pagar son velocidades de escritura y lectura más lentas:
En [75]: ls n datos/datos.*
rwrr@ 1 501 20 446911563 25 de agosto 11:48 data/data.h5
En [77]: copia_datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 5000000 entradas, 20210101 00:00:00 a 20300705
05:19:00
Frecuencia: T
0 No0 flotador64
1 número 1 flotador64
2 No2 flotador64
3 No3 flotador64
4 número 4 flotador64
5 No5 flotador64
6 No6 flotador64
7 No7 flotador64
8 No8 flotador64
9 número 9 flotador64
tipos de datos: float64(10)
uso de memoria: 419,6 MB
Esto define que el formato de escritura será de tipo tabla. La escritura se vuelve más lenta
ya que este tipo de formato implica un poco más de gastos generales y conduce a un
aumento del tamaño del archivo.
En [80]: h5
Salida[80]: Archivo(nombre de archivo=datos/datos.h5, título='', modo='r', root_uep='/',
filtros = Filtros (nivel completo = 0, aleatorio = Falso, bitshuffle = Falso,
fletcher32=Falso, dígito_menos_significativo=Ninguno))
/ (Grupo raíz) ''
/datos (Grupo) ''
/datos/tabla (Tabla(5000000,)) ''
descripción := {
"índice": Int64Col(forma=(), dflt=0, pos=0),
"values_block_0": Float64Col(forma=(10,), dflt=0.0, pos=1)}
orden de bytes : = 'pequeño'
forma de trozo := (2978,)
índice automático := Verdadero
colíndices := {
"índice": Índice(6, medio, aleatorio, zlib(1)).is_csi=False}
En [81]: h5.root.data.table[:3]
Fuera[81]: matriz([(1609459200000000000, [100. 100. 100. , 100. , 100. , 100. ]), ,
100. , 100. , , , 100. , 100.
(1609459260000000000, [100.0752, 100.1164, 100.0224, 100.0073,
100.1142, 100.0474, 99.9329, 100.0254, 100.1009, 100.066 ]),
(1609459320000000000, [100.1593, 100.1721, 100.0519, 100.0933,
100.1578, 100.0301, 99.92 , 100.0965, 100.1441, 100.0717])],
dtype=[('índice', '<i8'), ('valores_bloque_0', '<f8', (10,))])
En [82]: h5.close()
Aunque este segundo enfoque proporciona más flexibilidad, no abre las puertas a todas las capacidades del paquete
PyTables . Sin embargo, los dos enfoques presentados en esta subsección son convenientes y eficientes cuando se
trabaja con conjuntos de datos más o menos inmutables que caben en la memoria. Hoy en día, sin embargo, el trading
algorítmico tiene que lidiar en general con conjuntos de datos en constante y rápido crecimiento, como por ejemplo,
datos de ticks sobre precios de acciones o tipos de cambio.
Para hacer frente a los requisitos de tal escenario, podrían resultar útiles enfoques alternativos.
Uso de TsTables El
paquete PyTables , con las tablas de nombres de importación, es un contenedor para la biblioteca de almacenamiento
binario HDF5 que también utiliza pandas para su implementación HDFStore presentada en la subsección anterior. El
paquete TsTables (consulte la página de GitHub para conocer el paquete) a su vez está dedicado al manejo eficiente
de grandes conjuntos de datos de series de tiempo financieras basados en la biblioteca de almacenamiento binario
HDF5. Es efectivamente una mejora del paquete PyTables y agrega soporte para datos de series de tiempo a sus
capacidades.
Implementa un enfoque de almacenamiento jerárquico que permite una recuperación rápida de subconjuntos de datos
seleccionados proporcionando fechas y horas de inicio y finalización, respectivamente. El escenario principal admitido
por TsTables es "escribir una vez, recuperar varias veces".
La configuración ilustrada en esta subsección es que los datos se recopilan continuamente de una fuente web, un
proveedor de datos profesional, etc. y se almacenan de forma provisional y en memoria en un objeto DataFrame .
Después de un tiempo o de una cierta cantidad de puntos de datos recuperados, los datos recopilados se almacenan
en un objeto de tabla TsTables en una base de datos HDF5.
En [84]: %%tiempo
datos = generar_muestra_datos (filas = 2.5e6, columnas = 5,
frecuencia='1s').ronda(4)
Tiempos de CPU: usuario 915 ms, sistema: 191 ms, total: 1,11 s
Tiempo de pared: 1,14 s
En [85]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2500000 entradas, 20210101 00:00:00 a 20210129
22:26:39
Frecuencia: S
0 No0 flotador64
1 número 1 flotador64
2 No2 flotador64
3 No3 flotador64
4 número 4 flotador64
tipos de datos: float64(5)
uso de memoria: 114,4 MB
Esto genera un conjunto de datos financieros de muestra con 2.500.000 filas y cinco columnas.
con una frecuencia de un segundo; los datos de la muestra se redondean a dos dígitos.
En segundo lugar, algunas importaciones más y la creación del objeto de tabla TsTables . El
La mayor parte es la definición de la clase desc , que proporciona la descripción del
estructura de datos del objeto de tabla :
No4 = tb.Float64Col(pos=5)
En [91]: h5
Salida[91]: Archivo(nombre de archivo=data/data.h5ts, título='', modo='w', root_uep='/',
filtros=Filtros(complevel=0, shuffle=False, bitsshuffle=Falso, fletcher32=Falso,
dígito_menos_significativo=Ninguno))
/ (Grupo raíz) '' /
data (Grupo/Serie temporal) '' /
data/y2020 (Grupo) '' /data/
y2020/m08 (Grupo) '' /data/
y2020/m08/d25 (Grupo) '' /data /
y2020/m08/d25/ts_data (Tabla(0,)) '' descripción :=
{ "marca de tiempo":
Int64Col(forma=(), dflt=0, pos=0), "No0":
Float64Col(forma=( ), dflt=0.0, pos=1), "No1":
Float64Col(forma=(), dflt=0.0, pos=2), "No2":
Float64Col(forma=(), dflt=0.0, pos=3) , "No3":
Float64Col(forma=(), dflt=0.0, pos=4), "No4":
Float64Col(forma=(), dflt=0.0, pos=5)} byteorder :=
'pequeño' fragmento de
forma := (1365,)
… Se importan PyTables .
La primera columna de la tabla es una marca de tiempo representada como un valor int .
La inspección del archivo de la base de datos revela el principio básico detrás de la estructuración jerárquica en años,
meses y días.
En tercer lugar está la escritura de los datos de muestra almacenados en un objeto DataFrame en el objeto de tabla en el disco.
Uno de los principales beneficios de TsTables es la conveniencia con la que se realiza esta operación, es decir, mediante una
simple llamada a un método. Aún mejor, aquí la comodidad va unida a la velocidad. Con respecto a la estructura de la base de
datos, TsTables divide los datos en subconjuntos de un solo día. En el caso de ejemplo donde el
La frecuencia se establece en un segundo, lo que se traduce en 24 x 60 x 60 = 86 400 filas de datos por día
completo de datos:
En [93]: #h5
El objeto de tabla muestra 86.400 filas por día después de la operación append() .
La lectura de subconjuntos de datos de un objeto de tabla TsTables generalmente es muy rápida, ya que
para eso está optimizado en primer lugar. En este sentido, TsTables apoya
porta aplicaciones comerciales algorítmicas típicas, como backtesting, bastante bien. Otro
factor que contribuye es que TsTables ya devuelve los datos como un objeto DataFrame , por
lo que en general no son necesarias conversiones adicionales:
En [102]: h5.cerrar()
El método read_range() toma las fechas de inicio y finalización como entrada; leer aquí
es solo cuestión de milisegundos.
Los datos nuevos que se recuperan durante un día se pueden agregar al objeto de tabla
TsTables , como se ilustra anteriormente. Por lo tanto, el paquete es una valiosa adición a la
Los datos de series de tiempo financieras también se pueden escribir directamente desde un objeto DataFrame a un
Base de datos relacional como SQLite3. El uso de una base de datos relacional podría resultar útil en
escenarios donde se aplica el lenguaje de consulta SQL para implementar soluciones más sofisticadas
análisis. En cuanto a la velocidad y también al uso del disco, las bases de datos relacionales no pueden, sin embargo,
nunca, compárelo con otros enfoques que se basan en formatos de almacenamiento binario como
HDF5.
En [109]: ls n datos/datos.*
rwrr@ 1 501 20 105316352 25 de agosto 11:48 data/data.sql
El conjunto de datos financieros de muestra tiene 1.000.000 de filas y cinco columnas; El uso de memoria es de
aproximadamente 46 MB.
Una fortaleza de las bases de datos relacionales es la capacidad de implementar tareas de análisis (sin memoria) basadas
en declaraciones SQL estandarizadas. Como ejemplo, considere una consulta
que selecciona para la columna No1 todas aquellas filas donde el valor en esa fila se encuentra entre
105 y 108:
En [110]: consulta = 'SELECCIONAR * DE datos DONDE No1 > 105 y No2 < 108'
En [112]: res[:5]
Fuera[112]: [('20210103 19:19:00', 103.6894, 105.0117, 103.9025, 95.8619, 93.6062),
('20210103
19:20:00', 103.6724, 105.0654, 10 3.9277 , 95.8915, 93.5673), ('20210103
19:21:00',
103.6213, 105.1132, 103.8598, 95.7606, 93.5618), ('20210103 19:22:00',
103.6724,
105.1896, 103.8704 , 95.7302, 93.4139), ('20210103 19:23:00', 103.8115,
105.1152,
103.8342, 95.706, 93.4436)]
En [113]: len(res)
Fuera[113]: 5035
En [114]: con.cerrar()
Es cierto que este tipo de consultas simples también son posibles con pandas si el conjunto de datos
cabe en la memoria. Sin embargo, el lenguaje de consulta SQL ha demostrado ser útil y poderoso
durante décadas y debería estar en el arsenal de armas de datos del comerciante algorítmico.
Conclusiones
Este capítulo cubre el manejo de datos de series de tiempo financieras. Ilustra la lectura de dichos
datos de diferentes fuentes basadas en archivos, como archivos CSV. También muestra cómo
recuperar datos financieros de servicios web, como el de Quandl, para datos de opciones y de fin de
día. Las fuentes abiertas de datos financieros son una valiosa adición al panorama financiero. Quandl
es una plataforma que integra miles de conjuntos de datos abiertos bajo el paraguas de una API
unificada.
Otro tema importante cubierto en este capítulo es el almacenamiento eficiente de objetos DataFrame
completos en disco, así como de los datos contenidos en dicho objeto en memoria en bases de datos.
Los tipos de bases de datos utilizados en este capítulo incluyen el estándar de base de datos HDF5 y
la base de datos relacional liviana SQLite3. Este capítulo sienta las bases para el Capítulo 4, que
aborda el backtesting vectorizado; Capítulo 5, que cubre el aprendizaje automático y el aprendizaje
profundo para la predicción del mercado; y el Capítulo 6, que analiza las pruebas retrospectivas de
estrategias comerciales basadas en eventos.
Conclusiones | 77
Machine Translated by Google
• http://quandl.org
La información sobre el paquete utilizado para recuperar datos de esa fuente se encuentra aquí:
Debe consultar las páginas de documentación oficial para obtener más información sobre los paquetes utilizados
en este capítulo:
Hilpisch, Yves. 2018. Python para finanzas: dominar las finanzas basadas en datos. 2da ed.
Sebastopol: O'Reilly.
McKinney, Wes. 2017. Python para análisis de datos: gestión de datos con Pandas, NumPy e IPython. 2da ed.
Sebastopol: O'Reilly.
Tomás, Rob. "Los malos pronósticos del día de las elecciones asestan un golpe a la ciencia de datos: los modelos
de predicción sufrieron datos limitados, algoritmos defectuosos y debilidades humanas".
Wall Street Journal, 9 de noviembre de 2016.
El siguiente script de Python genera datos de series de tiempo financieras de muestra basados en una simulación
de Monte Carlo para un movimiento browniano geométrico; para obtener más información, consulte Hilpisch (2018,
cap. 12):
Parámetros
==========
filas: int
número de filas para generar cols: int
número de
columnas para generar frecuencia: cadena
de frecuencia
str para DatetimeIndex
Devoluciones
=======
si __nombre__ == '__principal__':
filas = 5 # número de filas columnas =
3 # número de columnas frecuencia = 'D' #
frecuencia diaria
print(generate_sample_data(filas, columnas, frecuencia))
CAPÍTULO 4
[E]ron lo suficientemente tontos como para pensar que se puede mirar el pasado para predecir el futuro.1
El economista
Desarrollar ideas e hipótesis para un programa de comercio algorítmico es generalmente la parte más
creativa y, a veces, incluso divertida de la etapa de preparación. Probarlos exhaustivamente suele ser la
parte más técnica y que requiere más tiempo. Este capítulo trata sobre el backtesting vectorizado de
diferentes estrategias comerciales algorítmicas. Cubre los siguientes tipos de estrategias (consulte también
“Estrategias comerciales” en la página 13):
Estrategias de impulso
Estas son estrategias que se basan en la hipótesis de que el desempeño reciente persistirá durante
algún tiempo adicional. Por ejemplo, se supone que una acción que tiene una tendencia a la baja lo
hará durante más tiempo, razón por la cual dicha acción debe venderse en corto.
Estrategias de reversión a la
media El razonamiento detrás de las estrategias de reversión a la media es que los precios de las
acciones o de otros instrumentos financieros tienden a revertir a algún nivel medio o a algún nivel de
81
Machine Translated by Google
El objetivo principal de este capítulo es dominar el enfoque de implementación vectorizada, que permiten
paquetes como NumPy y pandas , como una herramienta de backtesting rápida y eficiente. Con este fin,
los enfoques presentados parten de una serie de suposiciones simplificadoras para centrar mejor la
discusión en el tema principal de la vectorización.
Estrategias comerciales
simples El enfoque de backtesting vectorizado claramente tiene límites cuando se trata del
modelado de estrategias comerciales algorítmicas. Sin embargo, muchas estrategias simples y
populares se pueden probar de forma vectorizada.
Python un bucle for o algo similar, como una lista de comprensión, que es simplemente
sintaxis diferente para un bucle for :
En [1]: v = [1, 2, 3, 4, 5]
En [2]: sm = [2 * i para i en v]
En [3]: sm
Fuera[3]: [2, 4, 6, 8, 10]
En principio, Python permite multiplicar un objeto de lista por un número entero, pero Python
El modelo de datos devuelve otro objeto de lista en el caso de ejemplo que contiene dos tiempos.
los elementos del objeto original:
En [4]: 2 * v
Fuera[4]: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
En [6]: a = np.array(v)
En [7]: un
Fuera[7]: matriz([1, 2, 3, 4, 5])
En [9]: 2 * a
Fuera[9]: matriz([ 2, 4, 6, 8, 10])
En [10]: 0,5 * un + 2
Salida[10]: matriz([2.5, 3. , 3.5, 4. , 4.5])
La transición de una matriz unidimensional (un vector) a una matriz bidimensional (un
matriz) es natural. Lo mismo se aplica a las dimensiones superiores:
En [12]: un
Fuera[12]: matriz([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
En [13]: 2 * a
Fuera[13]: matriz([[ 0, 2, 4],
[ 6, 8, 10],
[12, 14, 16],
[18, 20, 22]])
En [14]: un ** 2
Fuera[14]: matriz([[ 0, 1, 4],
[ 9, 16, 25],
[ 36, 49, 64],
[ 81, 100, 121]])
Además, la clase ndarray proporciona ciertos métodos que permiten operaciones vectorizadas.
ciones. A menudo también tienen su equivalente en forma de las llamadas funciones universales.
que NumPy proporciona:
En [15]: a.media()
Fuera[15]: 5,5
En [16]: np.media(a)
Fuera[16]: 5,5
En [17]: a.media(eje=0)
Fuera[17]: matriz([4.5, 5.5, 6.5])
Consulte el Apéndice para obtener más detalles sobre la vectorización con NumPy. Consulte Hilpisch
(2018) para conocer una multitud de aplicaciones de vectorización en un contexto financiero.
pandas y la clase central DataFrame hacen un uso intensivo de NumPy y la clase ndarray . Por lo tanto,
la mayoría de los principios de vectorización vistos en el contexto de NumPy se trasladan a los pandas.
La mecánica se explica mejor con un ejemplo concreto. Para empezar, primero defina un objeto ndarray
bidimensional :
En [19]: a = np.arange(15).reshape(5, 3)
En [20]: un
Fuera[20]: matriz([[ 0, 1, 2], [ 3, 4,
5], [ 6, 7, 8],
[ 9, 10, 11], [12,
13 , 14]])
Para la creación de un objeto DataFrame , genere un objeto de lista con nombres de columnas y un
objeto DatetimeIndex a continuación, ambos del tamaño apropiado dado el objeto ndarray :
En [23]: columnas
Fuera[23]: ['a', 'b', 'c']
En [25]: índice
En [27]: gl
Fuera[27]: aBC
20210701 0 1 2
20210702 3 4 5
20210705 6 7 8
20210706 9 10 11
20210707 12 13 14
Se crea un objeto pandas DatetimeIndex que tiene una frecuencia de "día hábil"
y abarca cinco períodos.
Se crea una instancia de un objeto DataFrame en función del objeto ndarray con una columna
etiquetas y valores de índice especificados.
En principio, la vectorización ahora funciona de manera similar a los objetos ndarray . Una diferencia es
que las operaciones de agregación de forma predeterminada muestran resultados en columnas:
En [28]: 2 * gl
Fuera[28]: aBC
20210701 0 2 4
20210702 6 8 10
20210705 12 14 16
20210706 18 20 22
20210707 24 26 28
En [29]: df.sum()
Fuera[29]: ab 30
35
C 40
tipo de letra: int64
En [30]: np.media(df)
Fuera[30]: ab 6.0
7.0
C 8.0
tipo de letra: float64
Calcula el producto escalar del objeto DataFrame (tratado como una matriz).
Las operaciones por columnas se pueden implementar haciendo referencia a la columna respectiva
nombres, ya sea mediante la notación entre corchetes o la notación con puntos:
De manera similar, las condiciones que producen vectores de resultados booleanos y selecciones tipo SQL basadas
en tales condiciones son fáciles de implementar:
20210706 Verdadero
20210707 Verdadero
Seleccione todas aquellas filas donde el elemento de la columna a sea mayor que cinco.
Para una prueba retrospectiva vectorizada de estrategias comerciales, comparaciones entre dos columnas
o más son típicos:
20210702 Verdadero
20210705 Verdadero
20210706 Verdadero
20210707 Verdadero
20210707 Verdadero
Condición que compara una combinación lineal de las columnas a y b con la columna c.
El término "análisis técnico" es un título general para una gran cantidad de técnicas comerciales.
preguntas….En este artículo, exploramos dos de las reglas técnicas más simples y populares:
oscilador de media móvil y ruptura del rango de negociación (niveles de resistencia y soporte). En
En el primer método, las señales de compra y venta se generan mediante dos medias móviles, una larga
período, y un período corto... Nuestro estudio revela que el análisis técnico ayuda a predecir los cambios
en las acciones.
básicos Esta subsección se centra en los conceptos básicos de las estrategias comerciales de
backtesting que utilizan dos SMA. El ejemplo siguiente funciona con datos de cierre de fin de día
(EOD) para el tipo de cambio EUR/USD, como se proporciona en el archivo csv en el archivo de
datos EOD. Los datos del conjunto de datos provienen de la API de datos de Refinitiv Eikon y
representan valores de EOD para los respectivos instrumentos (RIC):
El cálculo de las SMA se simplifica mediante el método Rolling() , en combinación con una operación de cálculo
diferido:
En [44]: datos.tail()
Fuera[44]: precio SMA1 SMA2
Crea una columna con 42 días de valores de SMA. Los primeros 41 valores serán NaN.
Crea una columna con 252 días de valores de SMA. Los primeros 251 valores serán NaN.
Una visualización de los datos de la serie temporal original en combinación con las SMA ilustra mejor los resultados
(consulte la Figura 41):
El siguiente paso es generar señales, o más bien posicionamientos de mercado, basados en la relación entre las
dos SMA. La regla es ir en largo siempre que la SMA más corta esté por encima de la más larga y viceversa. Para
nuestros propósitos, indicamos una posición larga con 1 y una posición corta con –1.
Ser capaz de comparar directamente dos columnas del objeto DataFrame hace que la implementación
de la regla sea una cuestión de una sola línea de código. El posicionamiento a lo largo del tiempo se
ilustra en la Figura 42:
En [48]: datos.dropna(inplace=True)
Implementa la regla comercial de forma vectorizada. np.where() produce +1 para filas donde la
expresión es Verdadera y 1 para filas donde la expresión es Falsa.
Elimina todas las filas del conjunto de datos que contienen al menos un valor NaN .
Para calcular el rendimiento de la estrategia, a continuación calcule los rendimientos logarítmicos basándose en la
serie de tiempo financiera original. El código para hacer esto vuelve a ser bastante conciso debido a la vectorización.
La Figura 43 muestra el histograma de los retornos del registro:
Para derivar los rendimientos de la estrategia, multiplique la columna de posición (desplazada un día de negociación)
por la columna de rendimientos . Dado que los rendimientos logarítmicos son aditivos, calcular la suma de las
columnas rendimientos y estrategia proporciona una primera comparación del rendimiento de la estrategia en
relación con la inversión base misma.
La comparación de los rendimientos muestra que la estrategia logra una victoria sobre el banco pasivo.
inversión en marcas:
Deriva los rendimientos logarítmicos de la estrategia dados los posicionamientos y los rendimientos del mercado.
Resume los valores de rendimiento logarítmicos únicos tanto para la acción como para la estrategia (para ilustrar).
ración únicamente).
Los rendimientos relativos al aplicar la función exponencial np.exp() dan una visión más completa.
imagen completa de cómo se compara la estrategia con el desempeño del sistema financiero base.
instrumento a lo largo del tiempo. La Figura 44 muestra los datos gráficamente e ilustra el resultado.
rendimiento en este caso particular:
Figura 44. Rendimiento bruto del EUR/USD en comparación con la estrategia basada en SMA
Las estadísticas promedio anualizadas de riesgorendimiento tanto para la acción como para la estrategia son fáciles
calcular:
Otras estadísticas de riesgo que suelen ser de interés en el contexto del desempeño de la estrategia comercial
son la reducción máxima y el período de reducción más largo. Una estadística de ayuda para
uso en este contexto es el rendimiento bruto máximo acumulado calculado por el
Método cummax() aplicado al rendimiento bruto de la estrategia. La figura 45 muestra
las dos series temporales para la estrategia basada en SMA:
Define una nueva columna, cumret, con el rendimiento bruto a lo largo del tiempo.
Define otra columna más con el valor máximo acumulado del bruto
actuación.
Figura 45. Rendimiento bruto y rendimiento máximo acumulado de la estrategia basada en SMA
La reducción máxima se calcula entonces simplemente como el máximo de la diferencia entre las dos
columnas relevantes. La reducción máxima en el ejemplo es de aproximadamente 18 puntos
porcentuales:
En [64]: reducción.max()
Fuera[64]: 0,17779367070195917
La determinación del período de reducción más largo es un poco más complicada. Requiere aquellas
fechas en las que el rendimiento bruto iguala su máximo acumulado (es decir, donde se fija un nuevo
máximo). Esta información se almacena en un objeto temporal. Luego se calculan las diferencias en
días entre todas esas fechas y se selecciona el período más largo. Estos períodos pueden durar sólo
un día o más de 100 días. En este caso, el período de retiro más largo dura 596 días, un período
bastante largo:2
En [67]: puntos[12:15]
Fuera[67]: matriz([datetime.timedelta(days=1), datetime.timedelta(days=1),
datetime.timedelta(días=10)], dtype=objeto)
En [68]: periodos.max()
Fuera[68]: fechahora.timedelta(días=596)
Calcula los valores delta de tiempo entre todos los valores del índice.
El backtesting vectorizado con pandas es generalmente una tarea bastante eficiente debido a las
capacidades del paquete y la clase principal DataFrame . Sin embargo, el enfoque interactivo ilustrado
hasta ahora no funciona bien cuando se desea implementar un programa de backtesting más amplio
que, por ejemplo, optimice los parámetros de una estrategia basada en SMA. Para ello es aconsejable
un enfoque más general.
2 Para obtener más información sobre los objetos de fecha , hora y delta de tiempo , consulte el Apéndice C de Hilpisch (2018).
backtesting de SMA” en la página 115 presenta un código Python que contiene una clase para el backtesting
vectorizado de estrategias comerciales basadas en SMA. En cierto sentido, es una generalización del enfoque
introducido en la subsección anterior. Permite definir una instancia de la clase SMAVectorBacktester proporcionando
los siguientes parámetros:
• SMA1: para la ventana de tiempo en días para la SMA más corta • SMA2:
La aplicación en sí se ilustra mejor con una sesión interactiva que haga uso de la clase. El ejemplo primero replica
el backtest implementado anteriormente basado en datos del tipo de cambio EUR/USD. Luego optimiza los
parámetros de SMA para obtener el máximo rendimiento bruto. Sobre la base de los parámetros óptimos, traza el
rendimiento bruto resultante de la estrategia en comparación con el instrumento base durante el período de tiempo
relevante:
En [71]: smabt.run_strategy()
Fuera[71]: (1,29, 0,45)
En [72]: %%tiempo
smabt.optimize_parameters((30, 50, 2),
(200, 300, 2))
Tiempos de CPU: usuario 3,76 s, sistema: 15,8 ms, total: 3,78 s Tiempo de
pared: 3,78 s
En [73]: smabt.plot_results()
Realiza una prueba retrospectiva de la estrategia basada en SMA, dados los parámetros durante la creación de instancias.
al menos durante un período de tiempo determinado. Jegadeesh y Titman (1993, 2001) y Chan et al.
(1996) estudian este tipo de estrategias comerciales y sus posibles fuentes de ganancias.
Las estrategias de impulso transversal tradicionalmente han funcionado bastante bien. Jegadeesh y
Titman (1993) escriben:
Este documento documenta que las estrategias que compran acciones que han tenido un buen desempeño
en el pasado y venden acciones que han tenido un mal desempeño en el pasado generan retornos positivos
significativos durante períodos de tenencia de 3 a 12 meses.
El segundo tipo son las estrategias de impulso de series temporales. Estas estrategias compran aquellos
instrumentos que recientemente han tenido un buen desempeño y venden aquellos que recientemente
han tenido un mal desempeño. En este caso, el punto de referencia son los rendimientos pasados del
propio instrumento. Moskowitz et al. (2012) analizan en detalle este tipo de estrategia de impulso en una
amplia gama de mercados. Escriben:
En lugar de centrarse en los rendimientos relativos de los valores en la sección transversal, el impulso de las
series de tiempo se centra exclusivamente en el rendimiento pasado del propio valor... Nuestro hallazgo del
impulso de las series de tiempo en prácticamente todos los instrumentos que examinamos parece desafiar la
hipótesis del “camino aleatorio”. lo que en su forma más básica implica que saber si un precio subió o bajó en
el pasado no debería ser informativo sobre si subirá o bajará en el futuro.
Entrando en lo básico
Considere los precios de cierre al final del día para el precio del oro en USD (XAU=):
La estrategia de impulso de series de tiempo más simple es comprar la acción si el último rendimiento fue
positivo y venderla si fue negativo. Con NumPy y pandas esto es fácil de formalizar; simplemente tome el
signo del último rendimiento disponible como posición de mercado.
La Figura 47 ilustra el desempeño de esta estrategia. La estrategia tiene un rendimiento significativamente
inferior al instrumento base:
Define una nueva columna con el signo (es decir, 1 o –1) del retorno de registro relevante; los
valores resultantes representan las posiciones de mercado (largas o cortas).
Calcula los rendimientos del registro de estrategia dadas las posiciones en el mercado.
Figura 47. Rendimiento bruto del precio del oro (USD) y estrategia de impulso (solo último retorno)
Utilizando una ventana de tiempo móvil, la estrategia de impulso de la serie temporal se puede generalizar a
algo más que el último rendimiento. Por ejemplo, se puede utilizar el promedio de los últimos tres retornos para
generar la señal para el posicionamiento. La Figura 48 muestra que la estrategia en este caso funciona mucho
mejor, tanto en términos absolutos como en relación con el instrumento base:
En [82]: datos[['devoluciones',
'estrategia']].dropna().cumsum( ).apply(np.exp).plot(figsize=(10, 6));
Esta vez se toma el rendimiento medio durante un período móvil de tres días.
Sin embargo, el rendimiento es bastante sensible al parámetro de la ventana de tiempo. Elegir, por ejemplo, los
dos últimos retornos en lugar de tres conduce a un desempeño mucho peor, como se muestra en la Figura 49.
Figura 48. Rendimiento bruto del precio del oro (USD) y estrategia de impulso (últimos tres
rendimientos)
Figura 49. Rendimiento bruto del precio del oro (USD) y estrategia de impulso (dos últimos
rendimientos)
También se podría esperar un impulso de las series temporales durante el día. En realidad, uno lo haría
Se espera que sea más pronunciado intradiario que interdiario. La figura 410 muestra el valor bruto
desempeño de cinco estrategias de impulso de series de tiempo para uno, tres, cinco, siete y
nueve observaciones de retorno, respectivamente. Los datos utilizados son datos de precios de acciones intradía para
Apple Inc., recuperado de Eikon Data API. La figura se basa en el código que
sigue. Básicamente, todas las estrategias superan a la acción durante el transcurso de este intradía.
ventana de tiempo, aunque algunas sólo ligeramente:
En [83]: fn = '../data/AAPL_1min_05052020.csv'
# fn = '../data/SPX_1min_05052020.csv'
En [89]: datos[to_plot].dropna().cumsum().apply(np.exp).plot(
title='AAPL intradía 05 de mayo de 2020',
figsize=(10, 6), estilo=['', '', '', '', '', '']);
Define un objeto de lista para seleccionar las columnas que se trazarán más adelante.
Traza todas las columnas relevantes para comparar el desempeño de las estrategias con el desempeño
del instrumento de referencia.
Figura 410. Rendimiento bruto intradía de las acciones de Apple y cinco estrategias de impulso (último,
tres, cinco, siete y nueve rendimientos)
La figura 411 muestra el desempeño de las mismas cinco estrategias para el índice S&P 500.
Nuevamente, las cinco configuraciones de estrategias superan al índice y todas muestran un rendimiento
positivo (antes de los costos de transacción).
Figura 411. Rendimiento intradiario bruto del índice S&P 500 y cinco estrategias de impulso (últimos
rendimientos uno, tres, cinco, siete y nueve)
backtesting de impulso” en la página 118 presenta un módulo de Python que contiene la clase MomVectorBacktester ,
que permite realizar un backtesting un poco más estandarizado de estrategias basadas en impulso. La clase tiene
los siguientes atributos:
En comparación con la clase SMAVectorBacktester , ésta introduce dos generalizaciones importantes: la cantidad
fija que se invertirá al comienzo del período de backtesting y los costos de transacción proporcionales para
acercarse a las realidades del mercado en términos de costos.
En particular, la adición de costos de transacción es importante en el contexto de estrategias de impulso de series
temporales que a menudo conducen a una gran cantidad de transacciones a lo largo del tiempo.
La aplicación es tan sencilla y cómoda como antes. El ejemplo primero replica los resultados de la
sesión interactiva anterior, pero esta vez con una inversión inicial de 10.000 USD. La Figura 412
visualiza el desempeño de la estrategia, tomando la media de los últimos tres retornos para generar
señales para el posicionamiento. El segundo caso cubierto es uno con costos de transacción
proporcionales del 0,1% por operación. Como ilustra la Figura 413 , incluso los costos de transacción
pequeños deterioran significativamente el desempeño en este caso. El factor determinante en este
sentido es la frecuencia relativamente alta de operaciones que requiere la estrategia:
En [92]: mombt.run_strategy(momentum=3)
Fuera[92]: (20797.87, 7395.53)
En [93]: mombt.plot_results()
En [94]: mombt = Mom.MomVectorBacktester('XAU=', '201011',
'20191231', 10000, 0,001)
En [95]: mombt.run_strategy(momentum=3)
Fuera[95]: (10749,4, 2652,93)
En [96]: mombt.plot_results()
Crea una instancia de un objeto de la clase de backtesting que define el capital inicial como 10
000 USD y los costos de transacción proporcionales como cero.
Realiza una prueba retrospectiva de la estrategia de impulso en función de una ventana de tiempo
de tres días: la estrategia supera la inversión pasiva de referencia.
Esta vez, se asumen costos de transacción proporcionales del 0,1% por operación.
Figura 412. Rendimiento bruto del precio del oro (USD) y la estrategia de impulso
(últimos tres retornos, sin costos de transacción)
Figura 413. Evolución bruta del precio del oro (USD) y la estrategia de impulso (últimos
tres retornos, costos de transacción del 0,1%)
La reversión a la media se refiere a una tendencia de los precios de los activos a volver a una tendencia.
Trabajando con una media móvil simple (SMA) como indicador de una “senda de tendencia”, una estrategia
de reversión a la media en, por ejemplo, el tipo de cambio EUR/USD se puede realizar una prueba
retrospectiva de manera similar a las pruebas retrospectivas de la SMA y el impulso. Estrategias basadas
en. La idea es definir un umbral para la distancia entre el precio actual de las acciones y la SMA, que indica
una posición larga o corta.
Entrando en lo básico
Los ejemplos que siguen son para dos instrumentos financieros diferentes para los cuales se esperaría una
reversión media significativa, ya que ambos se basan en el precio del oro:
• GLD es el símbolo de SPDR Gold Shares, que es el mayor fondo cotizado en bolsa (ETF) con respaldo
físico para oro (consulte la página de inicio de SPDR Gold Shares). • GDX es el símbolo
del ETF de VanEck Vectors Gold Miners, que invierte en productos de acciones para seguir el índice
NYSE Arca Gold Miners (consulte la página de descripción general de VanEck Vectors Gold Miners).
El ejemplo comienza con GDX e implementa una estrategia de reversión a la media sobre la base de una
SMA de 25 días y un valor umbral de 3,5 para que la desviación absoluta del precio actual se desvíe de la
SMA para señalar un posicionamiento. La Figura 414 muestra las diferencias entre el precio actual de
GDX y el SMA, así como el valor umbral positivo y negativo para generar señales de compra y venta,
respectivamente:
En [100]: SMA = 25
Figura 414. Diferencia entre el precio actual de GDX y SMA, así como los valores umbral para generar
señales de reversión a la media
A partir de las diferencias y de los valores umbral fijados se pueden obtener de nuevo posicionamientos de
forma vectorizada. La Figura 415 muestra las posiciones resultantes:
Si el valor de la distancia es mayor que el valor umbral, vaya corto (establezca –1 en la nueva posición de la
columna); de lo contrario, establezca NaN.
Si el valor de la distancia es inferior al valor del umbral negativo, vaya en largo (conjunto 1); de lo contrario,
mantenga la posición de la columna sin cambios.
Si hay un cambio en el signo del valor de la distancia, vaya al mercado neutral (establezca 0); de lo contrario,
mantenga la posición de la columna sin cambios.
Rellene hacia adelante todas las posiciones NaN con los valores anteriores; reemplace todos los valores de NaN
restantes por 0.
Trace las posiciones resultantes a partir de la posición del índice SMA en adelante.
Figura 415. Posicionamientos generados para GDX basados en la estrategia de reversión a la media
El último paso es derivar los rendimientos de la estrategia que se muestran en la Figura 416. La estrategia supera con
creces al ETF GDX , aunque la particular parametrización conduce a largos periodos con una posición neutral (ni larga
ni corta).
Estas posiciones neutrales se reflejan en las partes planas de la curva de estrategia en la Figura 416:
En [111]: datos[['devoluciones',
'estrategia']].dropna().cumsum( ).apply(np.exp).plot(figsize=(10, 6));
Figura 416. Rentabilidad bruta del ETF GDX y la estrategia de reversión a la media (SMA =
25, umbral = 3,5)
El ejemplo ahora utiliza GLD y establece los costos de transacción proporcionales en 0,1%.
El importe inicial a invertir vuelve a fijarse en 10.000 USD. La SMA es 43 esta vez y el valor
umbral está establecido en 7,5. La Figura 417 muestra el desempeño de la estrategia de
reversión a la media en comparación con el ETF GLD :
En [115]: mrbt.plot_results()
Realiza una prueba retrospectiva de la estrategia de reversión a la media con un valor SMA de 43 y un
valor umbral de 7,5.
Figura 417. Rendimiento bruto del ETF GLD y la estrategia de reversión a la media (SMA = 43,
umbral = 7,5, costos de transacción del 0,1%)
La capacidad de mostrar ejemplos con buenos resultados a menudo tiene como coste el espionaje de
datos. Según White (2000), el espionaje de datos se puede definir de la siguiente manera:
El espionaje de datos ocurre cuando un determinado conjunto de datos se utiliza más de una vez con fines
de inferencia o selección de modelos.
En otras palabras, un determinado enfoque podría aplicarse varias o incluso muchas veces al mismo
conjunto de datos para llegar a números y gráficos satisfactorios. Esto, por supuesto, es intelectualmente
deshonesto en la investigación de estrategias comerciales porque pretende que una estrategia comercial
tiene algún potencial económico que podría no ser realista en un contexto del mundo real. Dado que el
objetivo de este libro es el uso de Python como lenguaje de programación para el comercio algorítmico,
el enfoque de espionaje de datos podría estar justificado. Esto es una analogía con un libro de
matemáticas que, a modo de ejemplo, resuelve una ecuación que tiene una solución única que puede
identificarse fácilmente. En matemáticas, estos ejemplos sencillos son más bien la excepción que la
regla, pero aun así se utilizan con frecuencia con fines didácticos.
Otro problema que surge en este contexto es el sobreajuste. El sobreajuste en un contexto comercial se
puede describir de la siguiente manera (consulte el Man Institute on Overfitting):
El sobreajuste ocurre cuando un modelo describe ruido en lugar de señal. El modelo puede tener un buen
rendimiento con los datos sobre los que se probó, pero poco o ningún poder predictivo sobre nuevos datos
en el futuro. El sobreajuste puede describirse como encontrar patrones que en realidad no existen. Hay un
costo asociado con el sobreajuste: una estrategia sobreadaptada tendrá un rendimiento inferior en el futuro.
Incluso una estrategia simple, como la basada en dos valores de SMA, permite realizar pruebas
retrospectivas de miles de combinaciones de parámetros diferentes. Es casi seguro que algunas de
esas combinaciones mostrarán buenos resultados de rendimiento. Como señalan Bailey et al. (2015)
analizan en detalle, esto conduce fácilmente a un sobreajuste del backtesting y las personas responsables
del backtesting a menudo ni siquiera son conscientes del problema. Señalan:
Los avances recientes en la investigación algorítmica y la computación de alto rendimiento han hecho que
sea casi trivial probar millones y miles de millones de estrategias de inversión alternativas en un conjunto de
datos finito de series de tiempo financieras... [E]s una práctica común utilizar este poder computacional para
calibrar los parámetros de una estrategia de inversión para maximizar su rendimiento. Pero debido a que la
relación señalruido es tan débil, a menudo el resultado de dicha calibración es que los parámetros se eligen
para aprovechar el ruido pasado en lugar de la señal futura. El resultado es una prueba retrospectiva
sobreadaptada.
Existe una creciente preocupación de que en la investigación moderna, los hallazgos falsos puedan
constituir la mayoría o incluso la gran mayoría de las afirmaciones de las investigaciones publicadas. Sin
embargo, esto no debería sorprender. Se puede demostrar que la mayoría de los hallazgos de la
investigación son falsos... Como se ha demostrado anteriormente, la probabilidad de que un hallazgo de la
investigación sea realmente cierto depende de la probabilidad previa de que sea cierto (antes de realizar
el estudio), el poder estadístico del estudio y el nivel de significación estadística.
En este contexto, si se demuestra que una estrategia comercial en este libro funciona bien dado un determinado
conjunto de datos, una combinación de parámetros y tal vez un algoritmo de aprendizaje automático específico,
esto no constituye ningún tipo de recomendación para la configuración particular ni la permite. sacar conclusiones
más generales sobre la calidad y el potencial de desempeño de la configuración de la estrategia en cuestión.
Por supuesto, le animamos a utilizar el código y los ejemplos presentados en este libro para explorar sus propias
ideas de estrategias comerciales algorítmicas e implementarlas en la práctica basándose en los resultados,
validaciones y conclusiones de sus propias pruebas retrospectivas. Después de todo, lo que compensarán los
mercados financieros es una investigación estratégica adecuada y diligente, no el espionaje y el sobreajuste de
datos impulsados por la fuerza bruta.
Conclusiones
La vectorización es un concepto poderoso en la informática científica, así como en el análisis financiero, en el
contexto del backtesting de estrategias comerciales algorítmicas. Este capítulo presenta la vectorización tanto con
NumPy como con pandas y la aplica a tres tipos de estrategias comerciales de respaldo: estrategias basadas en
promedios móviles simples, impulso y reversión a la media. Es cierto que el capítulo hace una serie de
suposiciones simplificadoras, y una prueba retrospectiva rigurosa de las estrategias comerciales debe tener en
cuenta más factores que determinan el éxito comercial en la práctica, como cuestiones de datos, cuestiones de
selección, evitar el sobreajuste o elementos de microestructura del mercado. Sin embargo, el objetivo principal
del capítulo es centrarse en el concepto de vectorización y lo que puede hacer en el comercio algorítmico desde
un punto de vista tecnológico y de implementación. Con respecto a todos los ejemplos y resultados concretos
presentados, es necesario considerar los problemas de espionaje de datos, sobreajuste y significancia estadística.
McKinney, Wes. 2017. Python para análisis de datos. 2da ed. Sebastopol: O'Reilly.
Vander Plas, Jake. 2016. Manual de ciencia de datos de Python. Sebastopol: O'Reilly.
Conclusiones | 113
Machine Translated by Google
Para conocer el uso de NumPy y pandas en un contexto financiero, consulte estos libros:
Hilpisch, Yves. 2015. Análisis de derivados con Python: análisis de datos, modelos, simulación
calibración, calibración y cobertura. Finanzas Wiley.
. 2017. Derivados cotizados de volatilidad y varianza: una guía basada en Python. wiley
Finanzas.
. 2018. Python para finanzas: dominar las finanzas basadas en datos. 2da ed. Sebastopol: O'Reilly.
Para conocer los temas de espionaje y sobreajuste de datos, consulte estos artículos:
Bailey, David, Jonathan Borwein, Marcos López de Prado y Qiji Jim Zhu. 2015.
"La probabilidad de sobreajuste del backtest". Revista de Finanzas Computacionales 20, (4): 3969.
https://oreil.ly/sOHlf.
Ioánnidis, John. 2005. "Por qué la mayoría de los resultados de las investigaciones publicadas son falsos".
Medicina PLoS 2, (8): 696701.
Blanco, Halbert. 2000. "Una verificación de la realidad del espionaje de datos". Econométrica 68, (5):
10971126.
Para obtener más información general y resultados empíricos sobre estrategias comerciales basadas en
promedios móviles simples, consulte estas fuentes:
Brock, William, Josef Lakonishok y Blake LeBaron. 1992. “Reglas técnicas simples de negociación y
propiedades estocásticas de los rendimientos de las acciones”. Revista de Finanzas 47, (5): 17311764.
Droke, Clif. 2001. Medias móviles simplificadas. Columbia: Libros del mercado.
El libro de Ernest Chan cubre en detalle estrategias comerciales basadas en el impulso, así como en la
reversión a la media. El libro también es una buena fuente para conocer los peligros de las estrategias
comerciales de backtesting:
Estos artículos de investigación analizan las características y fuentes de ganancias de las estrategias de
impulso transversal, el enfoque tradicional del comercio basado en impulso:
Jegadeesh, Narasimhan y Sheridan Titman. 1993. “Rendimientos de los ganadores que compran y los
perdedores que venden: implicaciones para la eficiencia del mercado de valores”. Revista de Finanzas
48, (1): 6591.
Jegadeesh, Narasimhan y Sheridan Titman. 2001. "Rentabilidad de las estrategias de impulso: una evaluación
de explicaciones alternativas". Revista de Finanzas 56, (2): 599720.
El artículo de Moskowitz et al. proporciona un análisis de las llamadas estrategias de impulso de series
temporales:
Moskowitz, Tobias, Yao Hua Ooi y Lasse Heje Pedersen. 2012. “Serie temporal
Impulso." Revista de Economía Financiera 104: 228250.
Estos artículos analizan empíricamente la reversión media de los precios de los activos:
Balvers, Ronald, Yangru Wu y Erik Gilliland. 2000. “Reversión media en los mercados bursátiles
nacionales y estrategias de inversión paramétricas contrarias”. Journal of Finance 55, (2): 745772.
Kim, Myung Jig, Charles Nelson y Richard Startz. 1991. “¿Reversión media de los precios de las
acciones? Una reevaluación de la evidencia empírica”. Revista de Estudios Económicos 58:
515528.
Spierdijk, Laura, Jacob Bikker y Peter van den Hoek. 2012. “Reversión a la media en los mercados
bursátiles internacionales: un análisis empírico del siglo XX”. Revista de dinero y finanzas
internacionales 31: 228249.
Esta sección presenta los scripts de Python a los que se hace referencia y se utilizan en este capítulo.
A continuación se presenta el código Python con una clase para el backtesting vectorizado de
estrategias basadas en promedios móviles simples:
#
#
importar numpy como
np importar pandas como
pd desde scipy.optimize importar bruto
clase SMAVectorBacktester(objeto):
Atributos
==========
símbolo: cadena
Símbolo RIC con el que trabajar
SMA1:
ventana de tiempo int en días para SMA más corto
SMA2: entero
ventana de tiempo en días para un inicio más largo de
SMA: str
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base set_parameters:
establece uno o dos
nuevos parámetros SMA run_strategy:
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw =
pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True) raw['return'] = np.log(raw /
raw.shift(1)) raw['SMA1'] = raw['price'].rolling(self.SMA1).
media() cruda['SMA2'] = cruda['precio'].rolling(self.SMA2).media() self.data =
cruda
si SMA1 no es Ninguno:
self.SMA1 = SMA1
self.data['SMA1'] = self.data['price'].rolling( self.SMA1).mean()
si SMA2 no es Ninguno:
self.SMA2 = SMA2
self.data['SMA2'] = self.data['precio'].rolling(self.SMA2).mean()
def ejecutar_estrategia(yo):
''' Realiza una prueba retrospectiva de la estrategia comercial.
'''
def resultados_trama(yo):
''' Traza el rendimiento acumulado de la estrategia comercial en comparación con el
símbolo.
'''
si self.results es Ninguno:
print(' Aún no hay resultados para trazar. Ejecute una estrategia.')
título = '%s | SMA1=%d, SMA2=%d' % (símbolo propio,
auto.SMA1, auto.SMA2)
self.results[['creturns', 'cstrategy']].plot(title=title, figsize=(10, 6))
Parámetros
==========
SMA: tupla
Tupla de parámetros SMA
'''
Parámetros
==========
SMA1_range, SMA2_range: tupla tuplas
de la forma (inicio, fin, tamaño de paso)
'''
#
# Módulo Python con Clase # para
Backtesting Vectorizado # de Estrategias
Basadas en Momentum #
como pd
Atributos
==========
símbolo: cadena
RIC (instrumento financiero) para trabajar con inicio: str
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base
ejecutar_estrategia:
ejecuta la prueba retrospectiva para la estrategia basada en
el impulso
plot_results: traza el rendimiento de la estrategia en comparación con el símbolo
'''
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw =
pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True) raw['return'] = np.log(raw /
raw.shift(1)) self.data = raw
def resultados_trama(yo):
''' Traza el rendimiento acumulado de la estrategia comercial en comparación con el
símbolo.
'''
si self.results es Ninguno:
print(' Aún no hay resultados para trazar. Ejecute una estrategia.')
título = '%s | TC = %.4f' % (self.symbol, self.tc) self.results[['creturns',
'cstrategy']].plot(title=title, figsize=(10, 6))
print(mombt.run_strategy())
print(mombt.run_strategy(momentum=2)) mombt =
MomVectorBacktester('XAU=', '201011', '20201231', 10000, 0.001)
print( mombt.run_strategy(momento=2))
continuación se presenta el código Python con una clase para el backtesting vectorizado de
estrategias basadas en la reversión a la media:.
#
desde la importación de MomVectorBacktester *
Atributos
==========
símbolo: cadena
Símbolo RIC con el que trabajar
inicio: cadena
fecha de inicio para la recuperación de
datos fin: str
fecha de finalización de la cantidad de
recuperación de datos: int, float
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base
run_strategy:
ejecuta la prueba retrospectiva para la estrategia basada en reversión de
la media.
plot_results: traza el rendimiento de la estrategia en comparación con el símbolo.
'''
# señales de
compra data['position'] = np.where(data['distance'] < threshold, 1, data['position']) #
cruce del precio actual y los
datos SMA (distancia cero) ['position'] = np.where(datos['distancia'] *
datos['distancia'].shift(1) < 0, 0, datos['posición'])
datos['retorno'].cumsum().apply(np.exp)
datos['cstrategy'] = self.cantidad * \
data['strategy'].cumsum().apply(np.exp) self.results = data
# rendimiento absoluto de la
estrategia aperf = self.results['cstrategy'].iloc[1] #
rendimiento superior/bajo de estrategia operf = aperf
self.results['creturns'].iloc[1] return round(aperf, 2),
round(operf, 2)
CAPÍTULO 5
Skynet comienza a aprender a un ritmo geométrico. Se vuelve consciente de sí mismo a las 2:14 am, hora del
este, del 29 de agosto.
En los últimos años se han producido enormes avances en las áreas de aprendizaje automático,
aprendizaje profundo e inteligencia artificial. La industria financiera en general y los operadores
algorítmicos de todo el mundo en particular también intentan beneficiarse de estos avances tecnológicos.
Este capítulo presenta técnicas de estadística, como la regresión lineal, y de aprendizaje automático,
como la regresión logística, para predecir movimientos futuros de precios en función de rendimientos
pasados. También ilustra el uso de redes neuronales para predecir los movimientos del mercado de
valores. Este capítulo, por supuesto, no puede reemplazar una introducción exhaustiva al aprendizaje
automático, pero puede mostrar, desde el punto de vista de un profesional, cómo aplicar concretamente
ciertas técnicas al problema de predicción de precios. Para más detalles, consulte Hilpisch (2020).1
1 Los libros de Guido y Müller (2016) y VanderPlas (2016) proporcionan introducciones prácticas y generales a
Aprendizaje automático con Python.
123
Machine Translated by Google
El capítulo está organizado de la siguiente forma. “Uso de la regresión lineal para predecir el movimiento del mercado”
en la página 124 presenta la regresión lineal como una técnica para predecir los niveles del índice y la dirección de los
movimientos de precios. “Uso del aprendizaje automático para la predicción del movimiento del mercado” en la página
139 se centra en el aprendizaje automático e introduce scikitlearn sobre la base de la regresión lineal. Cubre
principalmente la regresión logística como un modelo lineal alternativo aplicable explícitamente a problemas de
clasificación. “Uso del aprendizaje profundo para la predicción del movimiento del mercado” en la página 153 presenta
a Keras para predecir la dirección de los movimientos del mercado de valores basándose en algoritmos de redes
neuronales.
El objetivo principal de este capítulo es proporcionar enfoques prácticos para predecir movimientos futuros de precios
en los mercados financieros basándose en rendimientos pasados. El supuesto básico es que la hipótesis del mercado
eficiente no se cumple universalmente y que, similar al razonamiento detrás del análisis técnico de los gráficos de
precios de acciones, la historia podría proporcionar algunas ideas sobre el futuro que pueden extraerse con técnicas
estadísticas. En otras palabras, se supone que ciertos patrones en los mercados financieros se repiten de modo que
las observaciones pasadas pueden aprovecharse para predecir movimientos futuros de precios. Se tratan más detalles
en Hilpisch (2020).
124 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
importar aleatoriamente
importar numpy como
np desde pylab importar mpl, plt
plt.style.use ('seaborn')
mpl.rcParams['savefig.dpi'] = 300
mpl.rcParams['font.family'] = 'serif'
os.environ['PYTHONHASHSEED'] = '0'
En [4]: y = x + np.random.standard_normal(len(x))
En [6]: reg
Salida [6]: matriz ([0.94612934, 0.22855261])
Importa NumPy.
Importa matplotlib.
Genera una cuadrícula de flotadores espaciados uniformemente para los valores de x entre 0 y 10.
Corrige los valores iniciales para todos los generadores de números aleatorios relevantes.
Uso de la regresión lineal para la predicción del movimiento del mercado | 125
Machine Translated by Google
Crea la leyenda.
126 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
La predicción de precios basada en datos de series temporales tiene que abordar una
característica especial: la ordenación de los datos en función del tiempo. Generalmente, el
orden de los datos no es importante para la aplicación de la regresión lineal. En el primer
ejemplo de la sección anterior, los datos sobre los que se implementa la regresión lineal
podrían haberse compilado en ordenes completamente diferentes, manteniendo constantes
los pares x e y . Independientemente del orden, los parámetros de regresión óptimos habrían sido los mismos.
Sin embargo, en el contexto de predecir el nivel del índice de mañana, por ejemplo, parece
ser de suma importancia tener los niveles históricos del índice en el orden correcto. Si este es
el caso, entonces se intentaría predecir el nivel del índice de mañana dado el nivel del índice
de hoy, ayer, anteayer, etc. El número de días utilizados como entrada generalmente se
denomina rezagos. Por lo tanto, utilizar el nivel del índice actual y los dos más anteriores se
traduce en tres rezagos.
El siguiente ejemplo vuelve a colocar esta idea en un contexto bastante simple. Los datos que
utiliza el ejemplo son los números del 0 al 11:
En [9]: x = np.arange(12)
En [10]: x
Fuera[10]: matriz([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Supongamos tres rezagos para la regresión. Esto implica tres variables independientes para
la regresión y una dependiente. Más concretamente, 0, 1 y 2 son valores de
Uso de la regresión lineal para la predicción del movimiento del mercado | 127
Machine Translated by Google
En [11]: rezagos = 3
En [14]: mT
Salida[14]: matriz([[ 0., 1., 2., 3.], [ 1., 2., 3.,
4.], [ 2., 3., 4 ., 5.], [ 3.,
4., 5., 6.], [ 4., 5., 6., 7.],
[ 5., 6., 7., 8.], [ 6 ., 7., 8.,
9.], [ 7., 8., 9., 10.], [ 8.,
9., 10., 11.]])
En el objeto ndarray transpuesto m, las primeras tres columnas contienen los valores de las tres
variables independientes. Juntos forman la matriz A. La cuarta y última columna representa el
vector b. Como resultado, la regresión lineal produce el vector faltante x. Como ahora hay más
variables independientes, polifit y polival ya no funcionan. Sin embargo, hay una función en el
subpaquete NumPy para álgebra lineal (linalg) que permite resolver problemas generales de
mínimos cuadrados: lstsq. Sólo se necesita el primer elemento de la matriz de resultados ya que
contiene los parámetros de regresión óptimos:
128 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [16]: reg
Salida [16]: matriz ([0.66666667, 0.33333333, 1.33333333])
Esta idea básica se traslada fácilmente a los datos de series temporales financieras del mundo real.
El siguiente paso es traducir el enfoque básico a los datos de series de tiempo para una situación financiera real.
instrumento, como el tipo de cambio EUR/USD:
Uso de la regresión lineal para la predicción del movimiento del mercado | 129
Machine Translated by Google
Recupera datos del final del día (EOD) y los almacena en un objeto DataFrame .
Los datos de la serie temporal para el símbolo especificado se seleccionan de los datos originales.
Marco.
Formalmente, apenas es necesario cambiar el código Python del ejemplo simple anterior para implementar el
enfoque de predicción basado en regresión. Sólo es necesario reemplazar el objeto de datos:
En [24]: rezagos = 5
datos.dropna(inplace=True)
En [27]: reg
Salida [27]: matriz ([ 0.98635864, 0.02292172, 0.04769849, 0.05037365,
0.01208135])
Los parámetros de regresión óptimos ilustran lo que normalmente se denomina hipótesis del paseo aleatorio.
Esta hipótesis establece que los precios de las acciones o los tipos de cambio, por ejemplo, siguen un camino
aleatorio con la consecuencia de que el mejor predictor del precio de mañana es el precio de hoy. Los
parámetros óptimos parecen apoyar tal hipótesis ya que el precio de hoy explica casi por completo el nivel de
precios previsto para mañana. Los otros cuatro valores apenas tienen peso asignado.
La Figura 53 muestra el tipo de cambio EUR/USD y los valores previstos. Debido a la gran cantidad de datos
para la ventana temporal de varios años, las dos series temporales son indistinguibles en el gráfico:
130 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Figura 53. Tipo de cambio EUR/USD y valores previstos basados en regresión lineal (cinco
rezagos)
Hacer zoom al trazar los resultados para una ventana de tiempo mucho más corta permite distinguir
mejor las dos series de tiempo. La Figura 54 muestra los resultados para un período de tres meses.
Este gráfico ilustra que la predicción para la tasa de mañana es aproximadamente la tasa de hoy.
La predicción es más o menos un desplazamiento del tipo original hacia la derecha un día de
negociación:
La aplicación de la regresión lineal MCO para predecir los tipos del EUR/USD
basándose en los tipos históricos respalda la hipótesis del paseo aleatorio. Los
resultados del ejemplo numérico muestran que la tasa de hoy es el mejor
predictor de la tasa de mañana en mínimos cuadrados.
sentido.
Uso de la regresión lineal para la predicción del movimiento del mercado | 131
Machine Translated by Google
Figura 54. Tipo de cambio EUR/USD y valores previstos basados en regresión lineal (cinco rezagos,
solo tres meses)
Hasta ahora, el análisis se basa en niveles de tipos absolutos. Sin embargo, los rendimientos (logarítmicos)
podrían ser una mejor opción para este tipo de aplicaciones estadísticas debido, por ejemplo, a su
característica de hacer que los datos de las series temporales sean estacionarios. El código para aplicar la
regresión lineal a los datos de retorno es casi el mismo que antes. Esta vez no sólo el rendimiento de hoy
es relevante para predecir el rendimiento de mañana, sino que los resultados de la regresión también son
de naturaleza completamente diferente:
En [32]: datos.dropna(inplace=True)
datos.dropna(inplace=True)
En [35]: registro
132 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
La Figura 55 muestra los datos de retorno y los valores de predicción. Como lo ilustra impresionantemente la
figura, la regresión lineal obviamente no puede predecir la magnitud de los rendimientos futuros en una medida
significativa:
Figura 55. Rentabilidad logarítmica del EUR/USD y valores previstos basados en regresión lineal (cinco
retrasos)
Desde el punto de vista comercial, se podría argumentar que lo relevante no es la magnitud del rendimiento
previsto, sino más bien si la dirección se pronostica correctamente o no. Para ello, basta con un simple cálculo
para obtener una visión general. Siempre que la regresión lineal toma la dirección correcta, lo que significa que
el signo del rendimiento previsto es correcto, el producto del rendimiento del mercado y el rendimiento previsto
es positivo y, en caso contrario, negativo.
Uso de la regresión lineal para la predicción del movimiento del mercado | 133
Machine Translated by Google
En el caso de ejemplo, la predicción es 1.250 veces correcta y 1.242 veces incorrecta, lo que
se traduce en una tasa de aciertos de alrededor del 49,9%, o casi exactamente el 50%:
Calcula la tasa de aciertos definida como el número de predicciones correctas dadas todas
predicciones
En [42]: registro
Fuera[42]: matriz([5.11938725, 2.24077248, 5.13080606, 3.03753232,
2.14819119])
En [44]: datos['predicción'].value_counts()
Out[44]: 1.0 1.0 1300
1205
Nombre: predicción, tipo d: int64
134 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [46]: visitas
Fuera[46]: 1,0 1,0 1301
1191
0.0 13
tipo de letra: int64
Esto utiliza directamente el signo del rendimiento que se va a predecir para la regresión.
Teniendo en cuenta los datos que ya están disponibles, el backtesting vectorizado se reduce a dos líneas
del código Python, incluida la visualización. Esto se debe al hecho de que el valor de predicción
Las cotizaciones ya reflejan las posiciones del mercado (largas o cortas). La Figura 56 muestra que, en la
muestra, la estrategia bajo los supuestos actuales supera al mercado.
significativamente (ignorando, entre otras cosas, los costos de transacción):
En [48]: datos.head()
Fuera[48]: precio retraso_1 retraso_2 retraso_3 retraso_4 retraso_5 \
Fecha
20100120 1.4101 0.005858 0.008309 0.000551 0.001103 0.001310
20100121 1.4090 0.013874 0.005858 0.008309 0.000551 0.001103
20100122 1.4137 0.000780 0.013874 0.005858 0.008309 0.000551
20100125 1.4150 0.003330 0.000780 0.013874 0.005858 0.008309
20100126 1.4073 0.000919 0.003330 0.000780 0.013874 0.005858
predicción devolver
Fecha
20100120 1,0 0,013874
20100121 1.0 0.000780
Uso de la regresión lineal para la predicción del movimiento del mercado | 135
Machine Translated by Google
Multiplica los valores de predicción (posicionamientos) por los retornos del mercado.
Traza el rendimiento bruto del instrumento base y la estrategia a lo largo del tiempo.
(en muestra, sin costos de transacción).
Figura 56. Rentabilidad bruta del EUR/USD y la estrategia basada en regresión (cinco rezagos)
136 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
backtesting de regresión lineal” en la página 167 presenta un módulo de Python que contiene una
clase para el backtesting vectorizado de la estrategia comercial basada en regresión en el espíritu
del Capítulo 4. Además de permitir una cantidad arbitraria para invertir y costos de transacción
proporcionales, permite el ajuste dentro de la muestra del modelo de regresión lineal y la evaluación
fuera de la muestra. Esto significa que el modelo de regresión se ajusta en función de una parte del
conjunto de datos, digamos para los años 2010 a 2015, y se evalúa en base a otra parte del conjunto
de datos, digamos para los años 2016 y 2019. Para todas las estrategias que implican un paso de
optimización o ajuste, esto proporciona una visión más realista del rendimiento en la práctica, ya que
ayuda a evitar los problemas que surgen del espionaje de datos y el sobreajuste de modelos (consulte
también “Espionaje y sobreajuste de datos” en la página 111).
La Figura 57 muestra que la estrategia basada en regresión basada en cinco rezagos supera al
instrumento base EUR/USD para la configuración particular también fuera de la muestra y antes de
contabilizar los costos de transacción:
En [56]: lrbt.plot_results()
Uso de la regresión lineal para la predicción del movimiento del mercado | 137
Machine Translated by Google
Utiliza dos conjuntos de datos diferentes para los pasos de capacitación y evaluación.
Figura 57. Rendimiento bruto del EUR/USD y la estrategia basada en regresión (cinco rezagos, fuera de la
muestra, antes de los costos de transacción)
Considere el ETF de GDX . La configuración de estrategia elegida muestra un rendimiento superior fuera de la
muestra y después de tener en cuenta los costos de transacción (ver Figura 58):
En [60]: lrbt.plot_results()
138 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Figura 58. Rendimiento bruto del ETF GDX y la estrategia basada en regresión (siete rezagos, fuera
de muestra, después de los costos de transacción)
Hoy en día, el ecosistema Python proporciona varios paquetes en el campo del aprendizaje automático.
El más popular de ellos es scikitlearn (consulte la página de inicio de scikitlearn ), que también es uno
de los paquetes mejor documentados y mantenidos. Esta sección presenta primero la API del paquete
basada en regresión lineal, replicando algunos de los resultados de la sección anterior. Luego pasa a
utilizar la regresión logística como algoritmo de clasificación para atacar el problema de predecir la
dirección futura del mercado.
API scikitlearn , es fructífero revisar la idea básica detrás del enfoque de predicción presentado en este
capítulo. La preparación de datos es la misma que con NumPy únicamente:
En [61]: x = np.arange(12)
En [62]: x
Fuera[62]: matriz([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
En [63]: rezagos = 3
Uso del aprendizaje automático para la predicción del movimiento del mercado | 139
Machine Translated by Google
Para aplicar la regresión lineal, esto se traduce en el siguiente código que hace uso de
el subpaquete linear_model para modelos lineales generalizados (ver scikitlearn lin
página de modelos de orejas). De forma predeterminada, el modelo LinearRegression se ajusta a un valor de intersección:
En [67]: lm = linear_model.LinearRegression()
En [69]: lm.coef_
Salida[69]: matriz([0.33333333, 0.33333333, 0.33333333])
En [70]: lm.intercept_
Salida [70]: 2,0
En [71]: lm.predict(m[:retrasos].T)
Fuera[71]: matriz([ 3., 4., 5., 6., 7., 8., 9., 10., 11.])
En [72]: lm = linear_model.LinearRegression(fit_intercept=False)
140 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [74]: lm.coef_
Salida[74]: matriz([0.66666667, 0.33333333, 1.33333333])
En [76]: lm.predict(m[:retrasos].T)
Fuera[76]: matriz([ 3., 4., 5., 6., 7., 8., 9., 10., 11.])
Este ejemplo ya ilustra bastante bien cómo aplicar scikitlearn al problema de predicción. Debido a su
diseño API consistente, el enfoque básico también se aplica a otros modelos.
problema de clasificación, hay que decidir a cuál de un conjunto limitado de categorías (“clases”)
pertenece una nueva observación. Un problema clásico estudiado en el aprendizaje automático es la
identificación de dígitos escritos a mano del 0 al 9. Tal identificación conduce a un resultado correcto,
digamos 3. O conduce a un resultado incorrecto, digamos 6 u 8, donde todos esos resultados
incorrectos están igualmente equivocados. En el contexto de un mercado financiero, predecir el precio
de un instrumento financiero puede llevar a un resultado numérico muy alejado del correcto o
eso está bastante cerca de eso. Al predecir la dirección del mercado de mañana, sólo puede haber un
resultado correcto o (“completamente”) incorrecto. Este último es un problema de clasificación con el
conjunto de categorías limitado a, por ejemplo, "arriba" y "abajo" o "+1" y "1" o "1" y "0". Por el
contrario, el primer problema es un problema de estimación.
En [77]: horas = np.array([0.5, 0.75, 1., 1.25, 1.5, 1.75, 1.75, 2., 2.25, 2.5, 2.75, 3., 3.25, 3.5,
4., 4.25, 4.5, 4.75, 5., 5.5])
Uso del aprendizaje automático para la predicción del movimiento del mercado | 141
Machine Translated by Google
El número de horas estudiadas por los diferentes estudiantes (la secuencia importa).
Traza el conjunto de datos tomando horas como valores de x y éxito como valores de y .
La pregunta básica que normalmente se plantea en tal contexto es: dada una cierta cantidad de horas estudiadas
por un estudiante (no en el conjunto de datos), ¿aprobará el examen o no? ¿Qué respuesta podría dar la regresión
lineal? Probablemente no sea uno que sea satisfactorio, como muestra la Figura 510 . Dado un número diferente
de horas estudiadas, la regresión lineal proporciona valores (de predicción) principalmente entre 0 y 1, así como
menores y mayores. Pero sólo puede haber fracaso o éxito como resultado de realizar el examen:
142 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Aquí es donde entran en juego los algoritmos de clasificación, como la regresión logística y las
máquinas de vectores de soporte. A modo de ilustración, la aplicación de la regresión logística
es suficiente (ver James et al. (2013, cap. 4) para obtener más información general). La clase
respectiva también se encuentra en el subpaquete linear_model . La Figura 511 muestra el
resultado del siguiente código Python. Esta vez, hay un valor claro (predicción) para cada valor
de entrada diferente. El modelo predice que los estudiantes que estudiaron de 0 a 2 horas
reprobarán. Para todos los valores iguales o superiores a 2,75 horas, el modelo predice que
un estudiante aprueba el examen:
En [82]: lm = linear_model.LogisticRegression(solver='lbfgs')
Uso del aprendizaje automático para la predicción del movimiento del mercado | 143
Machine Translated by Google
Cambia la forma del objeto ndarray unidimensional a uno bidimensional (requerido por scikitlearn).
Sin embargo, como muestra la Figura 511 , no hay garantía de que 2,75 horas o más conduzcan al éxito.
Simplemente es “más probable” tener éxito después de tantas horas que fracasar.
Este razonamiento probabilístico también se puede analizar y visualizar en función de la misma instancia del
modelo, como lo ilustra el siguiente código. La línea discontinua en la Figura 512 muestra la probabilidad de
tener éxito (aumentando monótonamente). La línea de puntos y guiones muestra la probabilidad de fallar
(decreciente monótonamente):
144 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
plt.ylim(0.2, 1.2)
plt.legend(loc=0);
Equipado con los conceptos básicos, el siguiente paso es aplicar la regresión logística al problema
de predecir la dirección del mercado.
Uso del aprendizaje automático para la predicción del movimiento del mercado | 145
Machine Translated by Google
El siguiente código presenta una alternativa a la creación de una “matriz de características” basada en
Pandas DataFrame a la que el procedimiento de tres pasos se aplica igualmente bien, si no de una
manera más pitónica. La matriz de características ahora es un subconjunto de las columnas del conjunto
de datos original:
En [93]: datos.dropna(inplace=True)
En [94]: rezagos = 3
En [96]: datos.dropna(inplace=True)
Crea una instancia de un objeto de lista vacío para recopilar nombres de columnas.
Agrega una nueva columna al objeto DataFrame con los datos de retraso respectivos.
146 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
La regresión logística mejora la tasa de aciertos en comparación con la regresión lineal en más de
un punto porcentual hasta alrededor del 54,5%. La Figura 513 muestra el desempeño de la estrategia.
basado en predicciones basadas en regresión logística. Aunque la proporción de aciertos es mayor, el
el rendimiento es peor que con la regresión lineal:
En [101]: datos['predicción'].value_counts()
Fuera[101]: 1,0 1983
1.0 529
Nombre: predicción, tipo d: int64
En [103]: visitas
Fuera[103]: 1,0 1338
1.0 1159
0.0 12
tipo de letra: int64
En [104]: exactitud_score(datos['predicción'],
np.sign(datos['retorno']))
Fuera[104]: 0,5338375796178344
Crea una instancia del objeto modelo usando un valor C que le da menos peso a la regularidad.
término de zación (consulte la página Modelos lineales generalizados).
Uso del aprendizaje automático para la predicción del movimiento del mercado | 147
Machine Translated by Google
Genera una nueva columna en el objeto DataFrame y escribe en ella los valores de predicción.
Figura 513. Rendimiento bruto de GLD ETF y la estrategia basada en regresión logística (3 rezagos, dentro de
la muestra)
Aumentar el número de rezagos utilizados de tres a cinco disminuye la tasa de aciertos, pero mejora en cierta
medida el desempeño bruto de la estrategia (en la muestra, antes de los costos de transacción). La Figura 514
muestra el rendimiento resultante:
En [111]: rezagos = 5
148 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [112]: columnas = []
para retraso en el rango (1, retrasos + 1):
col = 'lag_%d' % retraso
datos[col] = datos['precio'].shift(lag)
cols.append(col)
En [113]: datos.dropna(inplace=True)
En [116]: datos['predicción'].value_counts()
Fuera[116]: 1,0 2047
1.0 464
Nombre: predicción, tipo d: int64
En [118]: visitas
Fuera[118]: 1,0 1,0 1331
1163
0.0 12
tipo de letra: int64
En [119]: exactitud_score(datos['predicción'],
np.sign(datos['retorno']))
Fuera[119]: 0,5312624452409399
Uso del aprendizaje automático para la predicción del movimiento del mercado | 149
Machine Translated by Google
Figura 514. Rendimiento bruto de GLD ETF y la estrategia basada en regresión logística (cinco
rezagos, dentro de la muestra)
backtesting de algoritmos de clasificación” en la página 170 presenta un módulo de Python con una clase
para el backtesting vectorizado de estrategias basadas en modelos lineales de scikitlearn. Aunque sólo se
implementan regresión lineal y logística, el número de modelos se incrementa fácilmente. En principio, la
clase ScikitVectorBacktester podría heredar métodos seleccionados de LRVectorBacktester , pero se
presenta de forma autónoma. Esto hace que sea más fácil mejorar y reutilizar esta clase para aplicaciones
prácticas.
150 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
La Figura 515 ilustra que la estrategia supera al instrumento base durante el período fuera de la
muestra (que abarca el año 2019), sin embargo, sin considerar los costos de transacción como antes:
En [127]: scibt.plot_results()
Figura 515. Rendimiento bruto del S&P 500 y la estrategia basada en regresión logística fuera
de muestra (15 rezagos, sin costos de transacción)
Como otro ejemplo, considere la misma estrategia aplicada al ETF GDX , para el cual se muestra un
rendimiento superior fuera de la muestra (durante el año 2018) en la Figura 516 (antes de costos de
transacción):
Uso del aprendizaje automático para la predicción del movimiento del mercado | 151
Machine Translated by Google
En [130]: scibt.plot_results()
Figura 516. Rendimiento bruto de GDX ETF y la estrategia basada en regresión logística (10
retrasos, fuera de muestra, sin costos de transacción)
La Figura 517 muestra cómo el rendimiento bruto disminuye (llevando incluso a una pérdida neta)
cuando se tienen en cuenta los costos de transacción, manteniendo todos los demás parámetros.
constante:
En [133]: scibt.plot_results()
152 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Figura 517. Rendimiento bruto de GDX ETF y la estrategia basada en regresión logística (10
rezagos, fuera de muestra, con costos de transacción)
Uso del aprendizaje profundo para la predicción del movimiento del mercado
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 153
Machine Translated by Google
Las bibliotecas se instalan mejor mediante pip install tensorflow y pip install keras. scikitlearn
también ofrece clases para aplicar redes neuronales a problemas de clasificación.
Para obtener más información general sobre el aprendizaje profundo y Keras, consulte Goodfellow
et al. (2016) y Chollet (2017), respectivamente.
En [134]: horas = np.array([0.5, 0.75, 1., 1.25, 1.5, 1.75, 1.75, 2., 2.25, 2.5, 2.75, 3., 3.25, 3.5, 4., 4.25,
4.5, 4.75, 5., 5.5])
154 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
El siguiente código se ajusta al modelo, genera las predicciones y traza los resultados, como
como se muestra en la Figura 518:
En [142]: datos.tail()
Out[142]: horas de predicción de éxito
15 4.25 1 1
16 4,50 17 1
4,75 11 1
18 5.00 1 1
19 5,50 1 1
Este sencillo ejemplo muestra que la aplicación del enfoque de aprendizaje profundo es bastante
similar al enfoque con scikitlearn y el modelo LogisticRegression
objeto. La API es básicamente la misma; Sólo los parámetros son diferentes.
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 155
Machine Translated by Google
Figura 518. Base de datos y resultados de predicción con MLPClassifier para la clasificación simple.
ejemplo de ficación
El siguiente paso es aplicar el enfoque a los datos del mercado de valores en forma de rendimientos logarítmicos.
de una serie de tiempo financiera. Primero, es necesario recuperar y preparar los datos:
En [149]: rezagos = 5
En [150]: columnas = []
para retraso en el rango (1, retrasos
+ 1): col = f'lag_{lag}'
datos[col] = datos['retorno'].shift(lag)
cols.append(col)
datos.dropna(en el lugar=Verdadero)
156 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [151]: datos.round(4).tail()
Fuera[151]:
precio dirección de retorno lag_1 lag_2 lag_3 lag_4 lag_5
Fecha
20191224 1.1087 0.0001 20191226 1 0,0007 0,0038 0,0008 0,0034 0,0006 1 0,0001 0,0007
1.1096 0.0008 0,0038 0,0008 0,0034
20191227 1.1175 0.0071 1 0,0008 0,0001 0,0007 0,0038 0,0008
20191230 1.1197 0.0020 1 0,0071 0,0008 0,0001 0,0007 0,0038
20191231 1.1210 0.0012 1 0,0020 0,0071 0,0008 0,0001 0,0007
Calcula los retornos del registro y define la dirección como una columna binaria.
Crea nuevas columnas de DataFrame con los retornos de registro desplazados por el número respectivo de
retrasos.
Imprime las últimas cinco filas que indican los "patrones" que emergen en las cinco columnas de
características.
El siguiente código utiliza una red neuronal densa (DNN) con el paquete Keras4 , define subconjuntos de datos de
prueba y entrenamiento, define las columnas de características y etiqueta y ajusta el clasificador. En el backend,
Keras utiliza el paquete TensorFlow para realizar la tarea. La Figura 519 muestra cómo cambia la precisión del
clasificador DNN para los conjuntos de datos de entrenamiento y validación durante el entrenamiento. Como
conjunto de datos de validación, se utiliza el 20% de los datos de entrenamiento (sin barajar):
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 157
Machine Translated by Google
tf.random.set_seed(100)
En [155]: set_seeds()
modelo = Sequential()
model.add(Dense(64, activación='relu',
input_shape=(lags,)))
model.add(Dense(64, activación='relu'))
model.add(Dense(1, activación='sigmoid'))
model.compile(optimizer=optimizer,
pérdida = 'binary_crossentropy',
métricas = ['precisión'])
En [162]: %%tiempo
model.fit(training_data[cols],
datos_entrenamiento['dirección'],
épocas = 50, detallado = Falso,
validación_split=0.2, shuffle=Falso)
Tiempos de CPU: usuario 4,86 s, sistema: 989 ms, total: 5,85 s
Tiempo de pared: 3,34 s
158 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Figura 519. Precisión del clasificador DNN en datos de entrenamiento y validación por paso de entrenamiento
Equipado con el clasificador ajustado, el modelo puede generar predicciones sobre el conjunto de datos de entrenamiento. La
Figura 520 muestra el desempeño bruto de la estrategia en comparación con el instrumento base (en la muestra):
En [167]: pred[:30].aplanar()
Fuera[167]: matriz([0, 0, 0, 0, 0, 1 , 1 , 1, 1 , 0 , 0 , 0 , 1, 1, 1, 0 , 0 , 0, 1, 1 , 0, 0, 0, 1 , 0, 1, 0, 1, 0,
0])
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 159
Machine Translated by Google
Figura 520. Rendimiento bruto del EUR/USD en comparación con la estrategia basada en aprendizaje profundo
egy (en muestra, sin costos de transacción)
Desde el principio, la estrategia también supera al instrumento base fuera de la muestra, como se muestra en la Figura 521.
160 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
ilustra. Esto a pesar de que la precisión del clasificador es sólo ligeramente superior al 50% en el conjunto
de datos de prueba:
En [175]: test_data['predicción'].value_counts()
Fuera[175]: 1 1 368
135
Nombre: predicción, tipo d: int64
En [178]: test_data[['return',
'strategy']].cumsum( ).apply(np.exp).plot(figsize=(10, 6));
Figura 521. Rendimiento bruto del EUR/USD en comparación con la estrategia basada en aprendizaje
profundo (fuera de muestra, sin costos de transacción)
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 161
Machine Translated by Google
En [182]: datos.dropna(inplace=True)
En [184]: imprimir(datos.ronda(4).cola())
Los próximos pasos son redefinir los conjuntos de datos de entrenamiento y prueba para normalizar las características.
datos y actualizar el modelo para reflejar las nuevas columnas de características:
162 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [191]: %%tiempo
model.fit(training_data_[cols], Training_data['dirección'], detallado=False, épocas=25)
Tiempos de CPU: usuario 2,32 s, sistema: 577 ms, total: 2,9 s Tiempo de
pared: 1,48 s
En [197]: datos_entrenamiento[['retorno',
'estrategia']].cumsum( ).apply(np.exp).plot(figsize=(10, 6));
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 163
Machine Translated by Google
Figura 522. Rendimiento bruto del EUR/USD en comparación con la estrategia basada en aprendizaje profundo
(en la muestra, características adicionales)
El paso final es la evaluación del clasificador y la derivación del desempeño de la estrategia fuera de la muestra.
El clasificador también funciona significativamente mejor, ceteris paribus, en comparación con el caso sin
características adicionales. Como antes, el comienzo es un poco inestable (ver Figura 523):
En [201]: test_data['predicción'].value_counts()
Fuera[201]: 1 1 335
168
Nombre: predicción, tipo d: int64
164 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
En [204]: test_data[['return',
'strategy']].cumsum( ).apply(np.exp).plot(figsize=(10, 6));
Figura 523. Rendimiento bruto del EUR/USD en comparación con la estrategia basada en aprendizaje
profundo (fuera de muestra, características adicionales)
El paquete Keras , en combinación con el paquete TensorFlow como backend, permite utilizar los avances
más recientes en aprendizaje profundo, como los clasificadores de redes neuronales profundas (DNN), para
el comercio algorítmico. La aplicación es tan sencilla como aplicar otros modelos de aprendizaje automático
con scikitlearn. El enfoque ilustrado en esta sección permite una mejora sencilla con respecto a los diferentes
tipos de funciones utilizadas.
Como ejercicio, vale la pena codificar una clase de Python (en el espíritu
de la “Clase de backtesting de regresión lineal” en la página 167 y la “Clase
de backtesting de algoritmos de clasificación” en la página 170) que permita
un uso más sistemático y realista del Paquete Keras para la predicción de
los mercados financieros y el backtesting de las respectivas estrategias
comerciales.
Uso del aprendizaje profundo para la predicción del movimiento del mercado | 165
Machine Translated by Google
Conclusiones
Predecir los movimientos futuros del mercado es el santo grial en las finanzas. Significa encontrar la verdad.
Significa superar los mercados eficientes. Si uno puede hacerlo con una ventaja considerable, la consecuencia
serán excelentes retornos de inversión y operaciones. Este capítulo presenta técnicas estadísticas de los
campos de la estadística tradicional, el aprendizaje automático y el aprendizaje profundo para predecir la
dirección futura del mercado en función de rendimientos pasados o cantidades financieras similares. Algunos
primeros resultados dentro de la muestra son prometedores, tanto para la regresión lineal como para la logística.
Sin embargo, se obtiene una impresión más confiable cuando se evalúan dichas estrategias fuera de la muestra
y se tienen en cuenta las transacciones.
costos.
Este capítulo no pretende haber encontrado el santo grial. Más bien ofrece un vistazo a técnicas que podrían
resultar útiles en su búsqueda. La API unificada de scikitlearn también facilita la sustitución, por ejemplo, de
un modelo lineal por otro.
En ese sentido, ScikitBacktesterClass se puede utilizar como punto de partida para explorar más modelos de
aprendizaje automático y aplicarlos a la predicción de series de tiempo financieras.
La cita al comienzo del capítulo de la película Terminator 2 de 1991 es bastante optimista en cuanto a qué tan
rápido y en qué medida los ordenadores podrían aprender y adquirir conciencia. No importa si se cree que las
computadoras reemplazarán a los seres humanos en la mayoría de las áreas de la vida o no, o si algún día se
vuelven conscientes de sí mismas, han demostrado ser útiles para los seres humanos como dispositivos de
apoyo en casi cualquier área de la vida. Y algoritmos como los utilizados en el aprendizaje automático, el
aprendizaje profundo o la inteligencia artificial encierran al menos la promesa de permitirles convertirse en
mejores operadores algorítmicos en un futuro próximo. Una descripción más detallada de estos temas y
consideraciones se encuentra en Hilpisch (2020).
Hilpisch, Yves. 2020. Inteligencia artificial en finanzas: una guía basada en Python. Sebasto
Pol: O'Reilly.
Vander Plas, Jake. 2016. Manual de ciencia de datos de Python: herramientas esenciales para trabajar con
datos. Sebastopol: O'Reilly.
166 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Los libros de Hastie et al. (2008) y James et al. (2013) proporcionan una descripción matemática
exhaustiva de las técnicas y algoritmos populares de aprendizaje automático:
Hastie, Trevor, Robert Tibshirani y Jerome Friedman. 2008. Los elementos de estadística.
Aprendizaje tico. 2da ed. Nueva York: Springer.
James, Gareth, Daniela Witten, Trevor Hastie y Robert Tibshirani. 2013. Introducción
ción al aprendizaje estadístico. Nueva York: Springer.
Para obtener más información general sobre el aprendizaje profundo y Keras, consulte estos libros:
Chollet, Francois. 2017. Aprendizaje profundo con Python. Isla refugio: Manning.
Goodfellow, Ian, Yoshua Bengio y Aaron Courville. 2016. Aprendizaje profundo. Leva
puente: MIT Press. http://deeplearningbook.org.
Esta sección presenta los scripts de Python a los que se hace referencia y se utilizan en este capítulo.
#
importar numpy como np
importar pandas como pd
clase LRVectorBacktester(objeto):
''' Clase para el backtesting vectorizado de estrategias
comerciales basadas en regresión lineal.
Atributos
==========
símbolo: cadena
TR RIC (instrumento financiero) con el que trabajar
inicio: cadena
fecha de inicio para la selección de datos
fin: str
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base
select_data:
selecciona un subconjunto de datos
prepare_lags:
prepara los datos retrasados para la regresión
fit_model:
implementa el paso de regresión
ejecutar_estrategia:
ejecuta la prueba retrospectiva para la estrategia basada en
regresión
plot_results: traza el rendimiento de la estrategia en comparación con el símbolo
'''
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw
= pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True)
raw['devoluciones'] = np.log(raw / raw.shift(1))
self.data = raw.dropna()
168 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
self.reg = registro
self.lags = retrasos
self.fit_model(start_in, end_in) self.results =
self.select_data(start_out, end_out).iloc[lags:] self.prepare_lags(start_out, end_out) predicción
= np.sign(np.dot(self .lagged_data[self.cols], self.reg))
self.results['prediction'] = predicción self.results['strategy'] = self.results['prediction'] * \
self.results['returns'] # determina
cuándo se realiza una transacción trades =
self.results['prediction'].diff().fillna(0) != 0 # resta los costos de transacción de la
devolución cuando se realiza la transacción self.results ['estrategia'][operaciones] = self.tc
self.results['creturns'] = self.amount * \
self.resultados['devoluciones'].cumsum().apply(np.exp)
self.results['cstrategy'] = self.cantidad * \
self.resultados['estrategia'].cumsum().apply(np.exp)
# rendimiento bruto de la estrategia aperf =
self.results['cstrategy'].iloc[1] # rendimiento superior o inferior
de la estrategia operf = aperf
self.results['creturns'].iloc[1] return round( aperf, 2), redondo(operf, 2)
def resultados_trama(yo):
''' Traza el rendimiento acumulado de la estrategia comercial en comparación con el
símbolo.
'''
si self.results es Ninguno:
print(' Aún no hay resultados para trazar. Ejecute una estrategia.')
título = '%s | TC = %.4f' % (self.symbol, self.tc) self.results[['creturns',
'cstrategy']].plot(title=title, figsize=(10, 6))
si __nombre__ == '__principal__':
lrbt = LRVectorBacktester('.SPX', '201011', '20180629', 10000, 0.0) print(lrbt.run_strategy('201011',
'20191231' , '201011', '20191231')) print(lrbt.run_strategy('201011',
'20151231', '201611', '2019 1231'))
A continuación se presenta el código Python con una clase para el backtesting vectorizado de
estrategias basadas en regresión logística, como algoritmo de clasificación estándar, utilizado
para la predicción de la dirección de los movimientos del mercado:
#
importar numpy como np
importar pandas como pd
desde sklearn importar linear_model
Atributos
==========
símbolo: cadena
TR RIC (instrumento financiero) con el que trabajar
inicio: cadena
fecha de inicio para la selección de datos
fin: str fecha
de finalización para la selección de datos
monto: int, monto flotante
que se invertirá al principio tc: costos de transacción
proporcionales
flotantes (por ejemplo, 0,5% = 0,005) por operación
modelo: str
ya sea 'regresión' o 'logística'
170 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base
select_data:
selecciona un subconjunto de datos
prepare_features:
prepara los datos de características para el ajuste del
modelo
fit_model: implementa el paso de ajuste
ejecutar_estrategia:
ejecuta la prueba retrospectiva para la estrategia basada en
regresión
plot_results: traza el rendimiento de la estrategia en comparación con el símbolo
'''
def __init__(self, símbolo, inicio, fin, cantidad, tc, modelo): self.symbol = símbolo
self.start = inicio self.end =
final self.cantidad =
cantidad self.tc = tc
self.resultados = Ninguno
si modelo == 'regresión':
self.model = modelo_lineal.Regresiónlineal()
modelo elif == 'logístico':
self.model = modelo_lineal.Regresión logística (C = 1e6,
solucionador='lbfgs', multi_class='ovr', max_iter=1000)
else:
elevar ValueError('Modelo desconocido o aún no implementado.') self.get_data()
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw =
pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True) raw['devoluciones'] =
np.log(raw / raw.shift(1)) self.data = raw.dropna()
'''
Prepara las columnas de características para los pasos de regresión y predicción.
'''
self.data_subset = self.select_data(start, end)
self.feature_columns = [] para
el retraso en el rango(1, self.lags + 1): col
= 'lag_{}'.format(lag)
self.data_subset[col] = self.data_subset['devoluciones'].shift(lag)
self.feature_columns.append(col)
self.data_subset.dropna(inplace=True)
def resultados_trama(yo):
''' Traza el rendimiento acumulado de la estrategia comercial en
comparación con el símbolo.
'''
si self.results es Ninguno:
print(' Aún no hay resultados para trazar. Ejecute una estrategia.')
título = '%s | TC = %.4f' % (self.symbol, self.tc)
self.results[['creturns', 'cstrategy']].plot(title=title,
172 | Capítulo 5: Predecir los movimientos del mercado con aprendizaje automático
Machine Translated by Google
if __name__ == '__main__':
scibt = ScikitVectorBacktester('.SPX', '201011', '20191231', 10000, 0.0,
'regresión')
print(scibt.run_strategy('2010 11', '20191231', '201011',
'20191231'))
print(scibt.run_strategy('201011', '20161231 ', '201711',
'20191231')) scibt =
ScikitVectorBacktester('.SPX', '201011', '20191231', 10000, 0.0, 'logística ')
print(scibt.run_strategy('201011',
'20191231', '201011', '20191231'))
print(scibt.run_strategy('2010
11', '20161231', '201711', '20191231')) scibt =
ScikitVectorBacktester('.SPX',
'201011', '2019 1231', 10000, 0.001, 'logística') print(scibt.run_strategy('201011',
'20191231', '201011',
'20191231' , retrasos = 15)) print(scibt.run_strategy('201011',
'20131231', '201411', '20191231',
retrasos = 15))
CAPÍTULO 6
Las tragedias reales de la vida no guardan relación con las ideas preconcebidas de uno. Al final, uno
siempre queda desconcertado por su sencillez, la grandeza de su diseño y por ese elemento de
extrañeza que parece inherente a ellos.
—Jean Cocteau
Por un lado, el backtesting vectorizado con NumPy y pandas es generalmente conveniente y eficiente de
implementar debido al código conciso, y es rápido de ejecutar debido a que estos paquetes están optimizados
para tales operaciones. Sin embargo, el enfoque no puede hacer frente a todos los tipos de estrategias
comerciales ni a todos los fenómenos que la realidad comercial presenta a un operador algorítmico. Cuando
se trata de backtesting vectorizado, las posibles deficiencias del enfoque son las siguientes:
Sesgo de anticipación
Simplificación
Por ejemplo, los costos fijos de transacción no pueden modelarse mediante la vectorización, que se
basa principalmente en rendimientos relativos. Además, los montos fijos por operación o la no
divisibilidad de instrumentos financieros individuales (por ejemplo, una acción) no se pueden modelar
adecuadamente.
Los algoritmos de no
recursividad, que incorporan estrategias comerciales, pueden recurrir a variables estatales a lo largo del
tiempo, como pérdidas y ganancias hasta un determinado momento o estadísticas similares dependientes
de la trayectoria. La vectorización no puede hacer frente a tales características.
175
Machine Translated by Google
Por otro lado, el backtesting basado en eventos permite abordar estos problemas mediante un enfoque más
realista para modelar las realidades comerciales. En un nivel básico, un evento se caracteriza por la llegada de
nuevos datos. Al realizar una prueba retrospectiva de una estrategia comercial para las acciones de Apple Inc.
basada en los datos del final del día, un evento sería un nuevo precio de cierre para las acciones de Apple.
También puede ser un cambio en la tasa de interés o el logro de un nivel de límite de pérdidas.
Las ventajas del enfoque de backtesting basado en eventos generalmente son las siguientes:
Dependencia de la
ruta Es sencillo realizar un seguimiento de las estadísticas condicionales, recursivas o dependientes de
la ruta, como el precio máximo o mínimo visto hasta ahora, e incluirlas en el algoritmo de negociación.
Reusabilidad
Realizar pruebas retrospectivas de diferentes tipos de estrategias comerciales requiere una funcionalidad
básica similar que pueda implementarse y unificarse mediante programación orientada a objetos.
En lo que sigue, un nuevo evento generalmente se identifica mediante una barra, que representa una unidad
de datos nuevos. Por ejemplo, los eventos pueden ser barras de un minuto para una estrategia de negociación
intradía o barras de un día para una estrategia de negociación basada en precios de cierre diarios.
El capítulo está organizado de la siguiente forma. “Clase base de backtesting” en la página 177 presenta una
clase base para el backtesting de estrategias comerciales basado en eventos. La “Clase de backtesting de solo
largo” en la página 182 y la “Clase de backtesting de largo y corto” en la página 185 utilizan la clase base para
implementar clases de backtesting de solo largo y corto, respectivamente.
Los objetivos de este capítulo son comprender el modelado basado en eventos, crear clases que permitan un
backtesting más realista y tener una infraestructura de backtesting fundamental disponible como punto de
partida para futuras mejoras y refinamientos.
Colocación de
órdenes La clase base cubrirá la colocación de órdenes básicas de compra y venta. Para simplificar,
sólo se modelan las órdenes de compra y venta de mercado.
Cierre de posiciones Al
final de cualquier backtesting, es necesario cerrar todas las posiciones de mercado. La clase
base se encargará de este comercio final.
Si la clase base cumple con estos requisitos, las clases respectivas para probar estrategias basadas en
promedios móviles simples (SMA), impulso o reversión a la media (ver Capítulo 4), así como en predicción
basada en aprendizaje automático (ver Capítulo 5), se puede construir sobre él. “Clase base de backtesting”
en la página 191 presenta una implementación de dicha clase base llamada BacktestBase. A continuación
se muestra un recorrido por los métodos individuales de esta clase para obtener una descripción general de
su diseño.
Con respecto al método especial __main__, solo hay algunas cosas dignas de mención.
Primero, la cantidad inicial disponible se almacena dos veces, tanto en un atributo privado _cantidad que se
mantiene constante como en un atributo regular cantidad que representa el saldo corriente. El supuesto
predeterminado es que no hay costos de transacción:
self.ptc = ptc
self.unidades = 0
self.posición = 0
self.trades = 0
self.verbose = detallado
self.get_data()
Durante la inicialización, se llama al método get_data , que recupera datos EOD de un archivo CSV para el
símbolo proporcionado y el intervalo de tiempo dado. También calcula los retornos del registro. El código
Python que sigue se ha utilizado ampliamente en los Capítulos 4 y 5. Por lo tanto, no es necesario explicarlo
en detalle aquí:
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw
= pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True) raw['return']
= np.log(raw / raw.shift(1)) self.data = raw.dropna()
El método .plot_data() es simplemente un método auxiliar simple para trazar los valores (cercanos ajustados)
para el símbolo proporcionado:
Un método que se llama con frecuencia es .get_date_price(). Para una barra determinada, devuelve la fecha y
la información del precio:
'''
fecha = str(self.data.index[barra])[:10]
precio = self.data.price.iloc[barra]
fecha de regreso , precio
self.trades += 1 if
self.verbose:
print(f'{fecha} | vendiendo {unidades} unidades a {precio:.2f}') self.print_balance(bar)
self.print_net_wealth(bar)
…el número de unidades se calcula teniendo en cuenta el valor del importe. (Tenga en cuenta que uno
debe indicarse.) El cálculo no incluye los costos de transacción.
El saldo de caja corriente se reduce por los desembolsos de efectivo de las unidades del
instrumento a comprar más los costos de transacción fijos y proporcionales. Nota
que no se comprueba si hay suficiente liquidez disponible o no.
Si self.verbose es Verdadero...
En segundo lugar, el método .place_sell_order() , que solo tiene dos ajustes menores en comparación con
el método .place_buy_order() :
if self.verbose:
print(f'{fecha} | vendiendo {unidades} unidades a {precio:.2f}') self.print_balance(bar)
self.print_net_wealth(bar)
El saldo de efectivo actual se incrementa con el producto de la venta menos los costos de transacción.
No importa qué tipo de estrategia comercial se pruebe, la posición al final del período de prueba debe
cerrarse. El código de la clase BacktestBase supone que la posición no se liquida sino que se contabiliza
con su valor de activo para calcular e imprimir las cifras de rendimiento:
if self.verbose:
print(f'{fecha} | inventario {self.units} unidades a {price:.2f}') print('=' * 55)
El saldo final consiste en el saldo de caja actual más el valor de la posición comercial.
La parte final del script de Python es la sección __main__ , que se ejecuta cuando el archivo se
ejecuta como un script:
if __name__ == '__main__': bb =
BacktestBase('AAPL.O', '201011', '20191231', 10000) print(bb.data.info()) print(bb. datos.tail())
bb.plot_data()
Crea una instancia de un objeto basado en la clase BacktestBase . Esto conduce automáticamente a
la recuperación de datos para el símbolo proporcionado. La Figura 61 muestra el gráfico resultante.
El siguiente resultado muestra la metainformación para el objeto DataFrame respectivo y las cinco
filas de datos más recientes:
precio devolver
Fecha
20191224 284,27 0,000950
20191226 289,91 0,019646
20191227 289,80 0,000380 20191230
291,52 0,005918 20191231 293,65 0,007280
En 2]:
Figura 61. Gráfico de datos recuperados para el símbolo por la clase BacktestBase
Las dos secciones siguientes presentan clases para realizar pruebas retrospectivas de estrategias
comerciales largas y cortas. Dado que estas clases se basan en la clase base presentada en esta
sección, la implementación de las rutinas de backtesting es bastante concisa.
Ciertas preferencias o regulaciones de los inversores pueden prohibir las ventas en corto como parte
de una estrategia comercial. Como consecuencia, a un comerciante o administrador de cartera solo
se le permite ingresar posiciones largas o estacionar capital en forma de efectivo o activos similares
de bajo riesgo, como cuentas del mercado monetario. “Clase de backtesting de solo largo” en la
página 194 muestra el código de una clase de backtesting para estrategias de solo largo llamada
BacktestLongOnly. Dado que depende de la clase BacktestBase y la hereda , el código para
implementar las tres estrategias basadas en SMA, impulso y reversión a la media es bastante conciso.
Parámetros
==========
SMA: entero
media móvil simple en días
umbral: flotar
valor absoluto para la señal basada en desviación relativa a SMA
'''
'
msg = f'\n\nEjecución de estrategia de reversión a la media |
mensaje += f'SMA={SMA} & thr={umbral}'
'
msg += f'\ncostos fijos {self.ftc} | msg += f'costos
proporcionales {self.ptc}'
self.data['SMA'] = self.data['precio'].rolling(SMA).mean()
self.close_out(barra)
Al principio, este método imprime una descripción general de los parámetros principales para
el backtesting.
La posición se establece en mercado neutral, lo que se hace aquí para mayor claridad y
debería ser el caso de todos modos.
El saldo de efectivo actual se restablece al monto inicial en caso de que se realice otra prueba retrospectiva.
run ha sobrescrito el valor.
El valor inicial SMA garantiza que haya valores SMA disponibles para comenzar a implementar y
realizar pruebas retrospectivas de la estrategia.
Esto ejecuta la orden de compra por el monto del saldo de efectivo actual.
En tal caso, se realiza una orden de venta para todas las unidades del instrumento financiero.
Al final del período de backtesting, la posición de mercado se cierra si hay alguna abierta.
La ejecución del script de Python en la “Clase de backtesting de solo duración” en la página 194 produce
resultados de backtesting, como se muestra a continuación. Los ejemplos ilustran la influencia de los costos
de transacción fijos y proporcionales. Primero, afectan el desempeño en general. En cualquier caso, tener
en cuenta los costos de transacción reduce el rendimiento. En segundo lugar, sacan a la luz la importancia
del número de operaciones que una determinada estrategia desencadena a lo largo del tiempo. Sin costos
de transacción, la estrategia de impulso supera significativamente a la estrategia basada en SMA. Con los
costos de transacción, la estrategia basada en SMA supera a la estrategia de impulso ya que se basa en
menos operaciones:
self.place_buy_order(barra, unidades=unidades)
monto elif : si
monto == 'todos':
cantidad = auto.monto
self.place_buy_order(barra, monto=monto)
Además de la barra, los métodos esperan un número para las unidades del objeto negociado
instrumento o una cantidad de moneda.
En segundo lugar, el valor es un número que luego simplemente se toma para colocar el respectivo
orden de compra. Tenga en cuenta que no se comprueba si hay suficiente liquidez o no.
self.close_out(barra)
En tal caso, se vuelve a comprobar si el precio actual se encuentra nuevamente en el nivel SMA o por encima de él.
…se activa una orden de compra para todas las unidades cortas para cerrar la posición corta.
La ejecución del script Python en “Clase de backtesting largo y corto” en la página 197 produce resultados de rendimiento
que arrojan más luz sobre las características de la estrategia. Uno podría inclinarse a suponer que agregar flexibilidad para
vender en corto un instrumento financiero produce mejores resultados. Sin embargo, la realidad demuestra que esto no es
necesariamente cierto. Todas las estrategias funcionan peor sin y después de los costos de transacción. Algunas
configuraciones incluso acumulan pérdidas netas o incluso una posición de deuda. Aunque estos son sólo resultados
específicos, ilustran que en tal contexto es arriesgado sacar conclusiones demasiado pronto y no tener en cuenta los límites
para la acumulación de deuda:
Conclusiones
Este capítulo presenta clases para el backtesting de estrategias comerciales basado en eventos.
En comparación con el backtesting vectorizado, el backtesting basado en eventos hace un uso intensivo
y intencional de bucles e iteraciones para poder abordar cada evento nuevo (generalmente, la llegada de
nuevos datos) de forma individual. Esto permite un enfoque más flexible que puede, entre otras cosas,
hacer frente fácilmente a costos de transacción fijos o estrategias más complejas (y variaciones de las
mismas).
“Clase base de backtesting” en la página 177 presenta una clase base con ciertos métodos útiles para el
backtesting de una variedad de estrategias comerciales. La “Clase de backtesting solo largo” en la página
182 y la “Clase de backtesting largocorto” en la página 185 se basan en esta infraestructura para
implementar clases que permiten realizar pruebas retrospectivas de estrategias comerciales solo largas
y cortas. Principalmente por razones de comparación, las implementaciones incluyen las tres estrategias
presentadas formalmente en el Capítulo 4. Tomando las clases de este capítulo como punto de partida,
se pueden lograr mejoras y refinamientos fácilmente.
Hilpisch, Yves. 2018. Python para finanzas: dominar las finanzas basadas en datos. 2da ed.
Sebastopol: O'Reilly.
El ecosistema Python proporciona una serie de paquetes opcionales que permiten realizar pruebas
retrospectivas de estrategias comerciales algorítmicas. Cuatro de ellos son los siguientes:
• por cierto
• Operador atrasado
• PyAlgoTrade •
Tirolesa
Zipline, por ejemplo, impulsa la popular plataforma Quantopian para realizar pruebas retrospectivas de
estrategias comerciales algorítmicas, pero también se puede instalar y utilizar localmente.
Aunque estos paquetes podrían permitir una prueba retrospectiva más exhaustiva de las estrategias comerciales
algorítmicas que las clases bastante simples presentadas en este capítulo, el objetivo principal de este libro es capacitar
al lector y al comerciante algorítmico para implementar el código Python de forma autónoma. Incluso si posteriormente
se utilizan paquetes estándar para realizar las pruebas retrospectivas reales, una buena comprensión de los diferentes
enfoques y sus mecanismos es beneficiosa, si no necesaria.
Esta sección presenta los scripts de Python a los que se hace referencia y se utilizan en este capítulo.
#
# Script Python con clase base # para backtesting
basado en eventos #
Atributos
==========
símbolo: cadena
TR RIC (instrumento financiero) a utilizar
inicio: cadena
fecha de inicio para la selección de datos
fin: str fecha
de finalización para la selección de datos
monto: monto
flotante que se invertirá una vez o por operación ftc: costos de
transacción
fijos flotantes por operación (compra o venta) ptc: costos de transacción
proporcionales
flotantes por operación (compra o venta) )
Métodos
=======
get_data:
recupera y prepara el conjunto de datos base
plot_data:
traza el precio de cierre para el símbolo
get_date_price:
devuelve la fecha y el precio de la barra dada
print_balance:
imprime el saldo actual (en efectivo)
print_net_wealth:
imprime la riqueza neta actual
place_buy_order:
coloca una orden de
compra
place_sell_order: coloca
una orden
de venta close_out: cierra una posición larga o corta
'''
def get_data(yo):
'''
Recupera y prepara los datos.
'''
raw = pd.read_csv('http://hilpisch.com/pyalgo_eikon_eod_data.csv',
index_col=0, parse_dates=True).dropna() raw
= pd.DataFrame(raw[self.symbol]) raw =
raw.loc[self.start:self.end]
raw.rename(columns={self.symbol: 'precio'}, inplace=True) raw['return']
= np.log(raw / raw.shift(1)) self.data = raw.dropna()
'''
Fecha de devolución y precio de la barra.
'''
fecha = str(self.data.index[bar])[:10] precio =
self.data.price.iloc[bar] fecha de retorno ,
precio
self.units = 0
self.trades += 1 if
self.verbose:
print(f'{fecha} | inventario {self.units} unidades a {price:.2f}') print('=' * 55)
if __name__ == '__main__': bb =
BacktestBase('AAPL.O', '201011', '20191231', 10000) print(bb.data.info()) print(bb.
datos.tail()) bb.plot_data()
A continuación se presenta el código Python con una clase para el backtesting basado en eventos de
estrategias de solo largo plazo, con implementaciones para estrategias basadas en SMA, impulso y
reversión a la media:
#
# Script Python con clase larga únicamente # para
backtesting basado en eventos
#
# Python para el comercio algorítmico # (c) Dr. Yves J.
Hilpisch # The Python Quants GmbH #
Parámetros
==========
SMA1, SMA2: media
móvil simple int de corto y largo plazo (en días)
'''
elif auto.posición == 1:
if self.data['SMA1'].iloc[bar] < self.data['SMA2'].iloc[bar]: self.place_sell_order(bar,
unidades=self.units) self.position = 0 # mercado neutral
self.close_out(barra)
Parámetros
==========
impulso: int
número de días para el cálculo del rendimiento medio
'''
si self.data['momentum'].iloc[bar] > 0:
self.place_buy_order(barra, cantidad=self.amount) self.position = 1 #
posición larga
elif self.position == 1: si
self.data['momentum'].iloc[bar] < 0:
self.place_sell_order(bar, unidades=self.units) self.position = 0 #
mercado neutral
self.close_out(barra)
Parámetros
==========
SMA:
promedio móvil simple int en días umbral:
flotante
self.data['SMA'] = self.data['precio'].rolling(SMA).mean()
self.close_out(barra)
ejecutar_estrategias()
El siguiente código Python contiene una clase para el backtesting basado en eventos de
estrategias largas y cortas, con implementaciones para estrategias basadas en SMA, impulso y
reversión media:
#
# Script Python con clase largacorta # para backtesting basado
en eventos # # Python para comercio algorítmico
#
self.place_buy_order(barra, unidades=unidades)
monto elif : si
monto == 'todos': monto =
self.monto
self.place_buy_order(barra, monto=monto)
self.place_sell_order(barra, unidades=unidades)
monto elif : si
monto == 'todos': monto =
self.monto
self.place_sell_order(barra, monto=monto)
self.data['SMA'] = self.data['precio'].rolling(SMA).mean()
self.data['SMA'].iloc[bar] + umbral):
self.go_short(bar, cantidad=self.initial_amount)
self.position = 1 elif
self.position == 1:
if self.data['price'].iloc[bar] >= self.data['SMA'].iloc[bar]: self.place_sell_order(bar,
unidades=self.units) self.position = 0 elif self. posición
== 1: si
self.data['precio'].iloc[bar] <=
self.data['SMA'].iloc[bar]: self.place_buy_order(bar, unidades=self.units)
self .posición = 0
self.close_out(barra)
run_strategies() #
costos de transacción: 10 USD fijos, 1% variable lsbt
= BacktestLongShort('AAPL.O', '201011', '20191231', 10000, 10.0,
0.01, False)
ejecutar_estrategias()
CAPÍTULO 7
Si quieres encontrar los secretos del universo, piensa en términos de energía, frecuencia y vibración.
Nikola Tesla
Desarrollar ideas comerciales y probarlas es un proceso bastante asincrónico y no crítico durante el cual hay
múltiples pasos que pueden repetirse o no, durante el cual no hay capital en juego y durante el cual el
rendimiento y la velocidad no son los requisitos más importantes. Recurrir a los mercados para implementar una
estrategia comercial cambia las reglas considerablemente. Los datos llegan en tiempo real y normalmente en
cantidades masivas, lo que hace que sea necesario procesarlos en tiempo real y tomar decisiones en tiempo
real basadas en la transmisión de datos. Este capítulo trata sobre cómo trabajar con datos en tiempo real para
los cuales los enchufes son, en general, la herramienta tecnológica preferida.
Conector de red
Punto final de una conexión en una red informática, también abreviado simplemente conector.
Protocolo de socket
Un protocolo que define y maneja la comunicación de socket, como el Protocolo de control de transferencia
(TCP).
Par de sockets
Combinación de un socket local y uno remoto que se comunican entre sí.
API de socket
201
Machine Translated by Google
Este capítulo se centra en el uso de ZeroMQ como una biblioteca de programación de sockets liviana, rápida y
escalable. Está disponible en múltiples plataformas con contenedores para los lenguajes de programación más
populares. ZeroMQ admite diferentes patrones para la comunicación por socket. Uno de esos patrones es el llamado
patrón editorsuscriptor (PUBSUB) , donde un único socket publica datos y varios sockets los recuperan
simultáneamente. Esto es similar a una estación de radio que transmite su programa y es escuchado simultáneamente
por miles de personas a través de dispositivos de radio.
Este capítulo está organizado de la siguiente forma. “Ejecución de un servidor de datos de ticks simple” en la página
203 describe cómo implementar y ejecutar un servidor de datos de ticks para obtener datos financieros de muestra.
“Conexión de un cliente de datos de ticks simple” en la página 206 implementa un cliente de datos de ticks para
conectarse al servidor de datos de ticks. “Generación de señales en tiempo real” en la página 208 muestra cómo
generar señales comerciales en tiempo real basadas en datos del servidor de datos de ticks.
Finalmente, “Visualización de datos de transmisión con Plotly” en la página 211 presenta el paquete de trazado
Plotly como una manera eficiente de trazar datos de transmisión en tiempo real.
El objetivo de este capítulo es tener un conjunto de herramientas y enfoques disponibles para poder trabajar con
datos en streaming en el contexto del comercio algorítmico.
1 Cuando se habla de simultáneamente o al mismo tiempo, se entiende en un sentido teórico e idealizado. En aplicaciones
prácticas, las diferentes distancias entre los sockets de envío y recepción, las velocidades de la red y otros factores
afectan el tiempo de recuperación exacto por socket de suscriptor.
σ2
St = St − Δt ∙ exp r − Δt + σ Δtz
2
Haciendo uso de este modelo, “Servidor de datos de ticks de muestra” en la página 218 presenta un
script de Python que implementa un servidor de datos de ticks usando ZeroMQ y una clase llamada
Instrument Price para publicar datos de ticks nuevos y simulados de forma aleatoria. La publicación es
aleatoria de dos maneras. En primer lugar, el valor del precio de las acciones se basa en una simulación
de Monte Carlo. En segundo lugar está la duración del intervalo de tiempo entre dos eventos de
publicación que asignó al azar. El resto de esta sección explica en detalle las partes principales del guión.
La primera parte del siguiente script realiza algunas importaciones, entre otras cosas, para el contenedor
Python de ZeroMQ. También crea instancias de los objetos principales necesarios para abrir un socket
de tipo PUB :
importar zmq
importar
matemáticas
importar tiempo importar aleatorio
Se crea una instancia de un objeto Context . Es el objeto central de la comunicación del socket.
catión.
El socket se vincula a la dirección IP local (0.0.0.0 en Linux y Mac OS, 127.0.0.1 en Windows) y
al número de puerto 5555.
La clase InstrumentPrice es para la simulación de valores de precios de instrumentos a lo largo del tiempo.
Como atributos, existen los parámetros principales para el movimiento browniano geométrico, además del
símbolo del instrumento y el momento en que se crea una instancia. El único método .simulate_value()
genera nuevos valores para el precio de las acciones dado el tiempo transcurrido desde que se llamó por
última vez y un factor aleatorio:
def simular_valor(yo):
'''
Genera un precio de acción nuevo y aleatorio.
'''
t = tiempo.tiempo() dt
= (t self.t) / (252 * 8 * 60 * 60) dt *= 500
self.t = t
self.value *= math.exp((self.r 0.5 * self.sigma ** 2) * dt + self.sigma * math.sqrt(dt) *
random.gauss(0, 1) )
devolver valor propio
Para tener movimientos más grandes en el precio de los instrumentos, esta línea de código escala la
variable dt (por un factor arbitrario).
El atributo t se actualiza con la hora actual, que representa el punto de referencia para la próxima
llamada del método.
La parte principal del script consiste en la creación de instancias de un objeto de tipo InstrumentPrice y un
bucle while infinito . Durante el ciclo while , se simula el precio de un nuevo instrumento y se crea, imprime
y envía un mensaje a través del socket.
ip = PrecioInstrumento()
time.sleep(random.random() * 2)
El texto del mensaje se genera en función del atributo del símbolo y del valor del precio de las acciones
recientemente simulado.
La ejecución del bucle se pausa durante un período de tiempo aleatorio (entre 0 y 2 segundos), simulando la
llegada aleatoria de nuevos datos de ticks a los mercados.
SÍMBOLO 99.65
SÍMBOLO 99.28
SÍMBOLO 99.09
SÍMBOLO 98.76
SÍMBOLO 98.83
SÍMBOLO 98.82
SÍMBOLO 98,92
SÍMBOLO 98,57
SÍMBOLO 98.81
SÍMBOLO 98.79
SÍMBOLO 98.80
En este punto, aún no se puede verificar si el script también envía el mismo mensaje a través del socket vinculado a
tcp://0.0.0.0:5555 (tcp://127.0.0.1:5555 en Windows). Para este fin, se necesita otro socket que se suscriba al socket
de publicación para completar el par de sockets.
La parte inicial del siguiente script es casi simétrica al script del servidor de datos de ticks:
importar zmq
contexto = zmq.Context()
socket = contexto.socket(zmq.SUB)
socket.connect('tcp://0.0.0.0:5555')
socket.setsockopt_string(zmq.SUBSCRIBE, 'SYMBOL')
El bucle while se reduce a recuperar los mensajes enviados por el socket del servidor e imprimirlos:
mientras que
Esta es la línea principal de código donde se reciben los datos (mensaje basado en cadenas).
La salida del script Python para el cliente de socket es exactamente la misma que la del script Python
para el servidor de socket:
SÍMBOLO 99.28
SÍMBOLO 99.09
SÍMBOLO 98,76
SÍMBOLO 98,83
SÍMBOLO 98,82
SÍMBOLO 98.92
SÍMBOLO 98.57
SÍMBOLO 98,81
SÍMBOLO 98,79
SÍMBOLO 98,80
Recuperar datos en forma de mensajes basados en cadenas a través de comunicación por socket es solo un
requisito previo para las tareas que se deben realizar en función de los datos, como generar señales comerciales
en tiempo real o visualizar los datos. Esto es lo que cubren las dos siguientes secciones.
ZeroMQ también permite la transmisión de otros tipos de objetos. Por ejemplo, existe una opción para enviar
Para generar señales en tiempo real sobre la base de un algoritmo en línea, es necesario recopilar y procesar datos
a lo largo del tiempo. Consideremos, por ejemplo, una estrategia comercial basada en el impulso de la serie
temporal de los últimos tres intervalos de cinco segundos (consulte el Capítulo 4). Los datos de ticks deben
recopilarse y luego volverse a muestrear, y el impulso debe calcularse en función del conjunto de datos
remuestreado. Con el paso del tiempo se produce una actualización continua e incremental. “Algoritmo en línea de
impulso” en la página 219 presenta un script de Python que implementa la estrategia de impulso, como se describió
anteriormente como algoritmo en línea. Técnicamente, hay dos partes principales además de manejar la
comunicación del socket. Primero están la recuperación y almacenamiento de los datos de ticks:
df = pd.DataFrame() mamá =
3 min_length
= mamá + 1
datos = socket.recv_string() t =
datetime.datetime.now() sym, valor =
data.split() df =
df.append(pd.DataFrame({sym: float(valor)}, index=[t]) )
Crea una instancia de un DataFrame de pandas vacío para recopilar los datos de tick.
El mensaje basado en cadenas se divide en el símbolo y el valor numérico (aquí sigue siendo un objeto str ).
Esta línea de código primero genera un objeto DataFrame temporal con los nuevos datos y luego lo agrega al
objeto DataFrame existente .
En segundo lugar está el remuestreo y procesamiento de los datos, como se muestra en el siguiente código
Python. Esto sucede en función de los datos de ticks recopilados hasta un momento determinado. Durante
este paso, los retornos logarítmicos se calculan en función de los datos remuestreados y se deriva el
impulso. El signo del impulso define el posicionamiento a tomar en el instrumento financiero:
Los datos de tick se vuelven a muestrear en un intervalo de cinco segundos, tomando el último valor
de tick disponible como relevante.
Esto imprime las últimas cinco filas del objeto DataFrame remuestreado .
Un valor de impulso de 1,0 significa una posición larga en el mercado. En producción, la primera señal
o un cambio en la señal desencadena ciertas acciones, como realizar una orden con el corredor. Tenga
en cuenta que se utiliza el segundo pero último valor de la columna de impulso , ya que el último valor
se basa en esta etapa en datos incompletos para el intervalo de tiempo relevante (aún no terminado).
Técnicamente, esto se debe al uso del método pan das .resample() con la parametrización label='right' .
De manera similar, un valor de impulso de 1,0 implica una posición corta en el mercado y
potencialmente ciertas acciones que podrían desencadenarse, como una orden de venta con un corredor.
Nuevamente, se utiliza el segundo pero último valor de la columna de impulso .
Cuando se ejecuta el script, lleva algún tiempo, dependiendo de los parámetros elegidos, hasta que haya
suficientes datos (remuestreados) disponibles para generar la primera señal.
A continuación se muestra un ejemplo intermedio del script del algoritmo de comercio en línea:
==================================================== =
[4 filas x 3 columnas]
==================================================== =
[5 filas x 3 columnas]
==================================================== =
SÍMBOLO... impulso
20200523 11:33:20 98.53... Yaya
20200523 11:33:25 98,83... Yaya
20200523 11:33:30 99,33... 1.0
20200523 11:33:35 97,76... 20200523 11:33:40 1.0
98,51... 1.0
[5 filas x 3 columnas]
Los basicos
Una vez instalados los paquetes y la extensión necesarios, la generación de un gráfico de transmisión
es bastante eficiente. El primer paso es la creación de un widget de figura de Plotly:
Esto crea una instancia de un widget de figura Plotly dentro de Jupyter Notebook.
El segundo paso es configurar la comunicación del socket con el servidor de datos de ticks de muestra,
que debe ejecutarse en la misma máquina en un proceso Python separado. Los datos entrantes se
enriquecen con una marca de tiempo y se recopilan en objetos de lista . Estos objetos de lista, a su
vez, se utilizan para actualizar los objetos de datos del widget de figura (consulte la Figura 71):
En [6]: socket.connect('tcp://0.0.0.0:5555')
Actualiza el objeto de datos con los conjuntos de datos x (veces) e y (precios) modificados .
Figura 71. Gráfico de datos de precios en streaming, recuperados en tiempo real a través de una conexión de socket
Un gráfico de transmisión con Plotly puede tener múltiples objetos de gráfico. Esto viene muy bien
cuando, por ejemplo, dos promedios móviles simples (SMA) se visualicen en tiempo real
tiempo además de los ticks de precios. El siguiente código crea nuevamente una instancia de una figura.
widget, esta vez con tres objetos dispersos . Los datos de ticks de los datos de ticks de muestra.
El servidor se recopila en un objeto Pandas DataFrame . Los dos SMA se calculan después de cada
actualización del socket. Los conjuntos de datos modificados se utilizan para actualizar el objeto de
datos del widget de figura (consulte la Figura 72):
En [12]: df = pd.DataFrame()
Figura 72. Gráfico de datos de precios de transmisión y dos SMA calculados en tiempo real
En [17]: fig
Out[17]: FigureWidget({ 'data':
[{'name': 'SYMBOL', 'type': 'scatter', 'uid':
'c8db0cac…
En [19]: df = pd.DataFrame()
Figura 73. Transmisión de datos de precios, rendimientos de registros e impulso en diferentes subtramas
No todos los datos de transmisión se visualizan mejor como una serie de tiempo ( objeto disperso). Algunos
datos de transmisión se visualizan mejor como barras con altura cambiante. “Servidor de datos de muestra para
gráfico de barras” en la página 220 contiene un script de Python que proporciona datos de muestra adecuados
para una visualización basada en barras. Un único conjunto de datos (mensaje) consta de ocho puntos flotantes.
números. El siguiente código Python genera un diagrama de barras de transmisión (consulte la Figura 74).
En este contexto, los datos x normalmente no cambian. Para que funcione el siguiente código, el script
BarsServer.py debe ejecutarse en una instancia local separada de Python:
En [22]: socket.connect('tcp://0.0.0.0:5556')
Conclusiones
Hoy en día, el comercio algorítmico tiene que lidiar con diferentes tipos de datos de transmisión (en
tiempo real). El tipo más importante a este respecto son los datos de ticks para instrumentos financieros
que, en principio, se generan y publican las 24 horas del día.2 Los sockets son la herramienta tecnológica
preferida para manejar los datos en streaming. Una biblioteca poderosa y al mismo tiempo fácil de usar en
este sentido es ZeroMQ, que se utiliza en este capítulo para crear un servidor de datos de ticks simple que
emite sin cesar datos de ticks de muestra.
Se presentan y explican diferentes clientes de datos de ticks para generar señales comerciales en tiempo
real basadas en algoritmos en línea y para visualizar los datos de ticks entrantes mediante gráficos en
tiempo real usando Plotly. Plotly hace que la visualización de transmisiones dentro de Jupyter Notebook
sea un asunto eficiente, permitiendo, entre otras cosas, múltiples transmisiones al mismo tiempo, tanto en
una sola trama como en diferentes subtramas.
Con base en los temas tratados en este capítulo y en los anteriores, ahora puede trabajar tanto con datos
estructurados históricos (por ejemplo, en el contexto del backtesting de estrategias comerciales) como con
datos de transmisión en tiempo real (por ejemplo, en el contexto de la generación de señales comerciales
en tiempo real). Esto representa un hito importante en el esfuerzo por construir una operación comercial
algorítmica y automatizada.
2 No todos los mercados están abiertos las 24 horas, los 7 días de la semana y, desde luego, no todos los instrumentos
financieros se negocian las 24 horas del día. Sin embargo, los mercados de criptomonedas, por ejemplo el de Bitcoin,
funcionan las 24 horas del día, creando constantemente nuevos datos que los actores activos en estos mercados deben digerir en tiempo real.
Conclusiones | 217
Machine Translated by Google
El mejor punto de partida para una descripción general completa de ZeroMQ es la página de inicio de ZeroMQ.
La página del tutorial Aprendizaje de ZeroMQ con Python proporciona una descripción general del patrón PUBSUB
basado en el contenedor de Python para la biblioteca de comunicación de socket.
Un buen lugar para empezar a trabajar con Plotly es la página de inicio de Plotly y, en particular, la página de introducción
a Plotly para Python.
Esta sección presenta los scripts de Python a los que se hace referencia y se utilizan en este capítulo.
siguiente es un script que ejecuta un servidor de datos de ticks de muestra basado en ZeroMQ. Hace uso de la simulación
de Monte Carlo para el movimiento browniano geométrico:
#
# Script Python para simular un
# Servidor de datos de ticks financieros
#
# Python para el comercio algorítmico # (c) Dr. Yves J.
Hilpisch # The Python Quants GmbH #
importar zmq
importar
matemáticas
importar tiempo importar aleatorio
def simular_valor(yo):
'''
Genera un precio de acción nuevo y aleatorio.
'''
t = tiempo.tiempo() dt
= (t self.t) / (252 * 8 * 60 * 60) dt *= 500
self.t = t self.value
ip = PrecioInstrumento()
time.sleep(random.random() * 2)
El siguiente es un script que ejecuta un cliente de datos de ticks basado en ZeroMQ. Se conecta al servidor
de datos de ticks desde “Servidor de datos de ticks de muestra” en la página 218:
importar zmq
mientras que
un script que implementa una estrategia comercial basada en el impulso de series de tiempo como un
algoritmo en línea. Se conecta al servidor de datos de ticks desde “Servidor de datos de ticks de muestra”
en la página 218:
importar zmq
importar fecha y hora
importar numpy como np
importar pandas como pd
df = pd.DataFrame() mamá = 3
min_length = mamá + 1
mientras que
El siguiente es un script de Python que genera datos de muestra para un gráfico de barras de transmisión:
importar zmq
importar
matemáticas
importar tiempo importar aleatorio
contexto = zmq.Context()
conector = contexto.socket (zmq.PUB)
socket.bind('tcp://0.0.0.0:5556')
imprimir (mensaje)
socket.send_string(msj)
tiempo.dormir(aleatorio.aleatorio() * 2)
CAPÍTULO 8
Hoy en día, incluso las entidades pequeñas que comercian con instrumentos complejos o que reciben suficiente
apalancamiento pueden amenazar al sistema financiero global.
—Paul Cantante
Hoy en día, es más fácil que nunca empezar a operar en los mercados financieros. Existe una gran cantidad de
plataformas comerciales en línea (brokers) disponibles entre las que un operador algorítmico puede elegir. La
elección de una plataforma puede verse influenciada por múltiples factores:
Instrumentos
El primer criterio que me viene a la mente es el tipo de instrumento que le interesa negociar. Por ejemplo, uno
podría estar interesado en negociar acciones, fondos cotizados en bolsa (ETF), bonos, divisas, materias
primas, opciones o futuros.
Estrategias
Algunos operadores están interesados en estrategias únicamente largas, mientras que otros también requieren
ventas cortas. Algunos se centran en estrategias de un solo instrumento, mientras que otros se centran en
aquellas que involucran múltiples instrumentos al mismo tiempo.
Costos
Los costos de transacción fijos y variables son un factor importante para muchos comerciantes.
Incluso podrían decidir si una determinada estrategia es rentable o no (véanse, por ejemplo, los capítulos 4 y
6).
223
Machine Translated by Google
Tecnología La
tecnología se ha convertido en un factor importante en la selección de plataformas comerciales.
En primer lugar, están las herramientas que las plataformas ofrecen a los comerciantes. Las herramientas
comerciales están disponibles, en general, para computadoras de escritorio/portátiles, tabletas y teléfonos inteligentes.
En segundo lugar, están las interfaces de programación de aplicaciones (API) a las que los comerciantes pueden
acceder mediante programación.
Jurisdicción
El comercio financiero es un campo fuertemente regulado con diferentes marcos legales
vigentes para diferentes países o regiones. Esto podría prohibir a determinados comerciantes
utilizar determinadas plataformas y/o instrumentos financieros dependiendo de su residencia.
Este capítulo se centra en Oanda, una plataforma comercial en línea que es muy adecuada para implementar
estrategias comerciales algorítmicas y automatizadas, incluso para comerciantes minoristas. La siguiente es una breve
descripción de Oanda junto con los criterios descritos anteriormente:
Instrumentos
Oanda ofrece una amplia gama de productos denominados contratos por diferencia (CFD) (consulte también
“Contratos por diferencia (CFD)” en la página 225 y “Descargo de responsabilidad” en la página 249). Las
principales características de los CFD son que están apalancados (por ejemplo, 10:1 o 50:1) y se negocian con
margen, de modo que las pérdidas pueden exceder el capital inicial.
Estrategias
Oanda permite operar en largo (comprar) y en corto (vender) CFD. Hay diferentes tipos de órdenes disponibles,
como órdenes de mercado o de límite, con o sin objetivos de ganancias y/o stop loss (trailing).
Costos
No existen costos de transacción fijos asociados con la negociación de CFD en Oanda. Sin embargo, existe un
diferencial entre oferta y demanda que genera costos de transacción variables al operar con CFD.
Tecnología
Oanda proporciona la aplicación de negociación fxTrade (Practice), que recupera datos en tiempo real y permite
la negociación (manual, discrecional) de todos los instrumentos (consulte la Figura 81). También hay disponible
una aplicación comercial basada en navegador (consulte la Figura 82). Una fortaleza importante de la plataforma
son las API RESTful y de transmisión (consulte API Oanda v20) a través de las cuales los operadores pueden
acceder mediante programación a datos históricos y de transmisión, realizar órdenes de compra y venta o
recuperar información de la cuenta. Hay disponible un paquete contenedor de Python (consulte v20 en PyPi).
Oanda ofrece cuentas comerciales en papel gratuitas que brindan acceso completo a todas las capacidades
tecnológicas,
lo cual es realmente útil para comenzar a utilizar la plataforma. Esto también simplifica la transición del
comercio en papel al comercio real.
Jurisdicción
Dependiendo de la residencia del titular de la cuenta, la selección de CFD con los que se puede negociar
cambia. Los CFD relacionados con divisas están disponibles básicamente en todos los lugares donde
Oanda está activo. Por ejemplo, es posible que los CFD sobre índices bursátiles no estén disponibles en
determinadas jurisdicciones.
Esto conlleva ciertos riesgos que los comerciantes deben tener en cuenta. Un evento reciente que
ilustra este problema es el evento del franco suizo que provocó una serie de insolvencias en el espacio
de los corredores en línea. Véase, por ejemplo, el artículo Los corredores de divisas caen como fichas
de dominó tras la decisión del SNB sobre el franco suizo.
El capítulo está organizado de la siguiente forma. “Configuración de una cuenta” en la página 227 explica
brevemente cómo configurar una cuenta. “La API de Oanda” en la página 229 ilustra los pasos necesarios
para acceder a la API. Según el acceso a la API, “Recuperación de datos históricos” en la página 230
recupera y trabaja con datos históricos para un determinado CFD. “Trabajar con transmisión de datos” en la
página 236 presenta la API de transmisión de Oanda para la recuperación y visualización de datos.
“Implementación de estrategias comerciales en tiempo real” en la página 239 implementa una estrategia
comercial algorítmica y automatizada en tiempo real. Finalmente, “Recuperación de información de la
cuenta” en la página 244 trata de recuperar datos sobre la cuenta misma, como el saldo actual o las
operaciones recientes. En todo momento, el código utiliza una clase contenedora de Python llamada tpqoa
(consulte el repositorio de GitHub).
El objetivo de este capítulo es hacer uso de los enfoques y tecnologías presentados en capítulos anteriores
para operar automáticamente en la plataforma Oanda.
La API de Oanda
Después del registro, obtener acceso a las API de Oanda es fácil. Los principales ingredientes necesarios son el
número de cuenta y el token de acceso (clave API). Encontrará el número de cuenta, por ejemplo, en el área
Gestionar fondos. El token de acceso se puede generar en el área Administrar acceso a la API (consulte la Figura
86).1
De ahora en adelante, el módulo configparser se utiliza para administrar las credenciales de la cuenta. El módulo
espera un archivo de texto (con un nombre de archivo, digamos, pyalgo.cfg) en el siguiente formato para usar con
una cuenta de práctica de Oanda:
[oanda]
account_id = YOUR_ACCOUNT_ID
access_token = YOUR_ACCESS_TOKEN
account_type = práctica
Para acceder a la API a través de Python, se recomienda utilizar el paquete contenedor de Python tpqoa (ver
repositorio de GitHub) que a su vez depende del paquete v20 de Oanda (ver repositorio de GitHub).
1 La denominación de ciertos objetos no es completamente coherente en el contexto de las API de Oanda. Por ejemplo, la
clave API y el token de acceso se usan indistintamente. Además, el ID de cuenta y el número de cuenta se refieren al mismo
número.
Con estos requisitos previos, puedes conectarte a la API con una sola línea de código:
Este es un hito importante: estar conectado a la API de Oanda permite la recuperación de datos históricos, la
realización programática de pedidos y más.
descripción general de qué instrumentos se pueden negociar para una cuenta determinada, utilice el
método .get_instruments() . Solo recupera los nombres para mostrar y los instrumentos técnicos, nombres de la
API. Hay más detalles disponibles a través de la API, como el tamaño mínimo de la posición:
En [3]: api.get_instruments()[:15]
Salida[3]: [('AUD/CAD', 'AUD_CAD'),
('AUD/CHF', 'AUD_CHF'),
('AUD/HKD', 'AUD_HKD'),
('AUD/JPY', ' AUD_JPY'),
('AUD/NZD', 'AUD_NZD'),
('AUD/SGD', 'AUD_SGD'),
('AUD/USD', 'AUD_USD'),
("Australia 200", "AU200_AUD"),
('Petróleo crudo Brent', 'BCO_USD'),
('Bund', 'DE10YB_EUR'),
('CAD/CHF', 'CAD_CHF'),
('CAD/HKD', 'CAD_HKD'),
('CAD/JPY', 'CAD_JPY'),
('CAD/SGD', 'CAD_SGD'),
('CHF/HKD', 'CHF_HKD')]
El siguiente ejemplo utiliza el instrumento EUR_USD basado en el par de divisas EUR/USD. El objetivo
es realizar pruebas retrospectivas de estrategias basadas en el impulso en barras de un minuto. Los
datos utilizados son de dos días de mayo de 2020. El primer paso es recuperar los datos sin procesar
de Oanda:
Parámetros
==========
instrumento: cadena
nombre de instrumento válido
inicio, fin: fecha y hora, cadena
Objetos de cadena o fecha y hora de Python para granularidad inicial y final:
encadene una cadena como
'S5', 'M1' o 'D' precio: encadene uno de 'A'
(preguntar), 'B'
(oferta) o 'M' (medio )
Devoluciones
=======
datos: pd.DataFrame
pandas objeto DataFrame con datos
En [7]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2814 entradas, 20200810 00:00:00 a 20200811
23:59:00
Columnas de datos (un total de 6 columnas):
# Columna Tipo D de recuento no nulo
Para simplificar, el siguiente código utiliza valores cercanos (c) de precios medios únicamente:2
En [11]: columnas = []
2 Esto implícitamente ignora los costos de transacción en forma de diferenciales entre oferta y demanda al vender y comprar unidades del
instrumento, respectivamente.
Calcula los rendimientos logarítmicos en función de los valores de cierre de los precios medios.
Crea una instancia de un objeto de lista vacío para recopilar nombres de columnas.
En [16]: datos[strats].dropna().cumsum(
).apply(np.exp).plot(figsize=(10, 6));
Define otro objeto de lista para almacenar los nombres de las columnas que se trazarán más adelante.
Calcula los retornos de registro para las diferentes estrategias y los almacena como nuevos.
columnas.
Agrega los nombres de las columnas al objeto de lista para su posterior trazado.
Figura 87. Rendimiento bruto de diferentes estrategias de impulso para el instrumento EUR_USD (barras
de minutos)
En general, cuando se compra una acción por, digamos, 100 USD, los cálculos de pérdidas y ganancias
(P&L) son sencillos: si el precio de la acción aumenta 1 USD, se gana 1 USD (beneficio no realizado). ); si
el precio de las acciones cae 1 USD, usted pierde 1 USD (pérdida no realizada). Si compras 10 acciones,
simplemente multiplica el resultado por 10.
Operar con CFD en la plataforma de Oanda implica apalancamiento y margen. Esto influye significativamente
en el cálculo de P&L. Para obtener una introducción y una descripción general de este tema, consulte las
Reglas de márgenes de Oanda fxTrade. Un ejemplo sencillo puede ilustrar los aspectos más importantes
en este contexto.
Considere que un operador algorítmico basado en EUR quiere negociar el instrumento EUR_USD en la
plataforma Oanda y quiere obtener una exposición larga de 10.000 EUR a un precio de venta de 1,1. Sin
apalancamiento ni margen, el comerciante (o el programa Python) compraría
10.000 unidades del CFD.3 Si el precio del instrumento (tipo de cambio) sube a 1,105 (como tipo medio
entre los precios de oferta y demanda), el beneficio absoluto es 10.000 x 0,005 = 50 o 0,5%.
¿Qué impacto tienen el apalancamiento y el margen? Supongamos que el operador algorítmico elige un
ratio de apalancamiento de 20:1, lo que se traduce en un margen del 5% (= 100% / 20). Esto, a su vez,
implica que el operador sólo necesita aportar un margen inicial de 10.000 EUR x 5% = 500 EUR para
obtener la misma exposición. Si el precio del instrumento sube a 1,105, el beneficio absoluto permanece
igual en 50 EUR, pero el beneficio relativo aumenta a 50 EUR / 500 EUR = 10%. El rendimiento se amplifica
considerablemente en un factor de 20; Este es el beneficio del apalancamiento cuando las cosas van según
lo deseado.
¿Qué pasa si las cosas van mal? Supongamos que el precio del instrumento cae a 1,08 (como el punto
medio entre los precios de oferta y demanda), lo que genera una pérdida de 10.000 x (1,08 1,1) = 200
EUR. La pérdida relativa ahora es 200 EUR / 500 EUR = 40%. Si a la cuenta con la que opera el operador
algorítmico le quedan menos de 200 EUR en capital/efectivo, es necesario cerrar la posición ya que los
requisitos de margen (regulatorios) ya no se pueden cumplir. Si las pérdidas consumen el margen por
completo, es necesario asignar fondos adicionales como margen para mantener viva la operación.4 La
Figura 88 muestra el efecto amplificador
sobre el desempeño de las estrategias de impulso para una relación de apalancamiento de 20:1. El margen
inicial del 5% es suficiente para cubrir posibles pérdidas, ya que no se consume ni siquiera en el peor de
los casos descrito:
Multiplica los rendimientos logarítmicos por un factor de 20 según el ratio de apalancamiento supuesto.
3 Tenga en cuenta que para algunos instrumentos, una unidad significa 1 USD, como en el caso de los CFD relacionados con divisas. Para otros, como para
CFD relacionados con índices (por ejemplo, DE30_EUR), una unidad significa una exposición de divisas al precio (oferta/
demanda) del CFD (por ejemplo, 11.750 EUR).
4 Los cálculos simplificados no tienen en cuenta, por ejemplo, los costos financieros que podrían adeudarse por el comercio apalancado.
Figura 88. Rendimiento bruto de las estrategias de impulso para el instrumento EUR_USD con
apalancamiento de 20:1 (barras de minutos)
El parámetro de parada detiene la transmisión después de recuperar una cierta cantidad de ticks.
Parámetros
==========
instrumento: cadena
nombre de instrumento válido
unidades: int
número de unidades de instrumento que se comprarán ( int
positivo , por ejemplo, 'unidades = 50') o se
venderán ( int negativo, por ejemplo, 'unidades = 100')
precio: precio de
orden flotante límite, precio de orden táctil sl_distance:
precio de distancia de stop
loss flotante , obligatorio , por ejemplo, en Alemania tsl_distance: distancia de
stop loss flotante tp_price:
precio de ganancia flotante que se
utilizará para la
operación
comentario: str
toque
de cuerda: booleano
orden market_if_touched (requiere que se establezca el precio) suprimir:
booleano si se suprime
imprimir ret: booleano
Muestra todas las opciones para realizar órdenes de mercado, de límite y de mercado si se tocan.
Aunque la API de Oanda permite la realización de diferentes tipos de órdenes, este capítulo
y el siguiente capítulo se centra principalmente en las órdenes de mercado para ir instantáneamente en largo o en corto
cada vez que aparece una nueva señal.
importar tpqoa
importar numpy como np
importar pandas como pd
clase MomentumTrader(tpqoa.tpqoa):
def __init__(self, conf_file, instrumento, bar_length, impulso, unidades,
*argumentos, **kwargs):
super(MomentumTrader, self).__init__(conf_file)
self.position = 0
self.instrument = instrumento
self.momentum = impulso
self.bar_length = bar_length self.units =
unidades
self.raw_data = pd.DataFrame()
self.min_length = self.momentum + 1
Instrumento a negociar.
elif auto.posición == 1:
self.create_order(self.instrumento, self.units * 2) self.position = 1
Este método se llama cada vez que llegan nuevos datos de ticks.
Si es 1 (corto)…
Si es +1 (largo)…
Según esta clase, comenzar con el comercio algorítmico automatizado son solo cuatro
líneas de código. El código Python que sigue inicia una sesión comercial automatizada:
En [25]: mt = MT.MomentumTrader('../pyalgo.cfg',
instrumento=instrumento,
bar_length='10s',
impulso=6,
unidades=10000)
Se define el parámetro de impulso , que se aplica a los datos remuestreados entre sí.
vals.
Esto inicia la transmisión y con ello el comercio; se detiene después de 100 tics.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110 1 11 112 113 114 115 116 117 118 119 120
121 122 123 124 125 126 127 128 129 130 131
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
149 150 151 152 153
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
245 246 247 248 249 250 251 252 253 254 255 256 257 258
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
333 334 335 336 337 338 339 340 341 342 343
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
416 417 418 419 420 421 422 423 424 425 426 427 428 429
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
'guaranteedExecutionFee':
'0.0', 'accountBalance':
'98507.7425', 'tradesClosed': [{'tradeID':
'1731 ',
'unidades': '10000.0',
'precio': 1.19109,
'realizedPL': '0.1687',
'financiamiento': '0.0',
'guaranteedExecutionFee': '0.0',
'halfSpreadCost': '0.5037'}],
'halfSpreadCost': '0.5037'}
'comisión': '0.0',
'guaranteedExecutionFees': '0.0',
'marginRate': '0.0333',
'openTradeCount': 1,
'openPositionCount': 1,
'pendingOrderCount': 0,
'hedgingEnabled': False,
'unrealizedPL ': '929.8862', 'NAV':
'99437.6287',
'marginUsed': '377.76',
'marginAvailable': '99064.4945',
'positionValue': '3777.6',
'marginCloseoutUnrealizedPL': '935.8183',
'marginCloseoutNAV': '99443.5608',
'marginCloseoutMarginUsed': '377.76',
'marginCloseoutPercent': '0.0019',
'marginCloseoutPositionValue': '3777.6',
'withdrawalLimit': ' 98507.7425 ',
'marginCallMarginUsed': '377.76',
'marginCallPercent': ' 0.0038 ',
'últimaID de transacción': '1733'}
En [29]: api.get_transactions(tid=int(oo['id']) 2)
Salida[29]: [{'id': '1732', 'time':
'20200819T14:43:17.107985242Z', 'userID':
13834683, 'accountID':
'10100413834683001 ', 'batchID': '1732',
'requestID':
'42730658106750652', 'tipo':
'MARKET_ORDER',
'instrumento': 'EUR_USD',
'unidades': '10000.0',
'timeInForce': 'FOK' ,
'positionFill': 'DEFAULT', 'motivo':
'CLIENT_ORDER'}, {'id': '1733',
Para una descripción general concisa, también está disponible el método .print_transactions() :
Conclusiones
La plataforma Oanda permite una entrada fácil y directa al mundo del comercio algorítmico automatizado.
Oanda se especializa en los llamados contratos por diferencia (CFD). Dependiendo del país de
residencia del comerciante, existe una gran variedad de instrumentos con los que se puede negociar.
Una ventaja importante de Oanda desde un punto de vista tecnológico son las API modernas y potentes
a las que se puede acceder fácilmente a través de un paquete contenedor de Python dedicado (v20).
Este capítulo muestra cómo configurar una cuenta, cómo conectarse a las API con Python, cómo
recuperar datos históricos (barras de un minuto) para realizar pruebas retrospectivas, cómo recuperar
datos de transmisión en tiempo real, cómo operar automáticamente con un CFD basado en una
estrategia de impulso y cómo recuperar información de la cuenta y el historial detallado de transacciones.
Visite las páginas de ayuda y soporte de Oanda en Ayuda y soporte para obtener más información sobre
la plataforma de Oanda y aspectos importantes del comercio de CFD.
El portal para desarrolladores de Oanda Getting Started proporciona una descripción detallada de las API.
El siguiente script de Python contiene una clase de streaming personalizada de Oanda que intercambia
automáticamente una estrategia de impulso:
#
# Python Script # con
Momentum Trading Class # para Oanda v20 #
importar tpqoa
importar numpy como
np importar pandas como pd
clase MomentumTrader(tpqoa.tpqoa):
def __init__(self, conf_file, instrumento, bar_length, impulso, unidades, *args, **kwargs):
self.data['posición'] =
np.sign( self.data['returns'].rolling(self.momentum).mean())
elif auto.posición == 1:
self.create_order(self.instrumento, self.units * 2) self.position = 1
desconocida.')
CAPÍTULO 9
A las instituciones financieras les gusta llamar trading a lo que hacen. Seamos honestos. No es comercio;
es apostar.
—Graydon Carter
Este capítulo presenta la plataforma comercial de FXCM Group, LLC (“FXCM” en adelante), con
su interfaz de programación de aplicaciones (API) RESTful y streaming, así como el paquete
contenedor Python fcxmpy. Al igual que Oanda, es una plataforma muy adecuada para el
despliegue de estrategias comerciales algorítmicas y automatizadas, incluso para comerciantes
minoristas con posiciones de capital más pequeñas. FXCM ofrece a los comerciantes minoristas e
institucionales una serie de productos financieros que se pueden negociar tanto a través de
aplicaciones comerciales tradicionales como programáticamente a través de su API. Los productos
se centran en pares de divisas, así como contratos por diferencia (CFD) sobre, entre otros, los
principales índices bursátiles y materias primas. En este contexto, consulte también “Contratos por
diferencia (CFD)” en la página 225 y “Descargo de responsabilidad” en la página 249.
Descargo de responsabilidad
Operar con Forex/CFD con margen conlleva un alto nivel de riesgo y puede no ser adecuado para
todos los inversores, ya que podría sufrir pérdidas superiores a los depósitos. El apalancamiento
puede jugar en su contra. Los productos están destinados a clientes minoristas y profesionales.
Debido a ciertas restricciones impuestas por las leyes y regulaciones locales, los clientes minoristas
residentes en Alemania podrían sufrir una pérdida total de los fondos depositados, pero no están
sujetos a obligaciones de pago posteriores más allá de los fondos depositados. Sea consciente y
comprenda plenamente todos los riesgos asociados con el mercado y el comercio. Antes de
comercializar cualquier producto, considere cuidadosamente su situación financiera y su nivel de
experiencia. Cualquier opinión, noticia, investigación, análisis, precio u otra información se
proporciona como comentario general del mercado y no constituye asesoramiento de inversión. El
comentario del mercado no ha sido preparado de acuerdo con los requisitos legales diseñados para promover la independencia.
249
Machine Translated by Google
Con respecto a los criterios de plataforma discutidos en el Capítulo 8, FXCM ofrece lo siguiente:
Instrumentos
Productos FX (por ejemplo, la negociación de pares de divisas), contratos por diferencias (CFD) sobre índices
bursátiles, materias primas o productos de tasas.
Estrategias
FXCM permite, entre otras cosas, posiciones largas y cortas (apalancadas), órdenes de entrada al mercado y
órdenes de stop loss y objetivos de obtención de beneficios.
Costos
Además del diferencial entre oferta y demanda, generalmente se debe pagar una tarifa fija por cada operación
con FXCM. Hay diferentes modelos de precios disponibles.
Tecnología
FXCM proporciona al operador algorítmico una API RESTful moderna a la que se puede acceder, por ejemplo,
mediante el uso del paquete contenedor fxcmpy de Python. También están disponibles aplicaciones comerciales
estándar para computadoras de escritorio, tabletas y teléfonos inteligentes.
Jurisdicción
FXCM está activo en varios países a nivel mundial (por ejemplo, en el Reino Unido o Alemania). Dependiendo
del país, es posible que ciertos productos no estén disponibles/ofrecidos debido a regulaciones y restricciones.
Este capítulo cubre las funcionalidades básicas de la API comercial FXCM y el paquete fxcmpy Python necesarios
para implementar una estrategia comercial algorítmica y automatizada mediante programación. Está estructurado de
la siguiente manera. “Primeros pasos” en la página 251 muestra cómo configurar todo para que funcione con la API
REST de FXCM para operaciones algorítmicas.
“Recuperación de datos” en la página 251 muestra cómo recuperar y trabajar con datos financieros (hasta el nivel de
marca). “Trabajar con la API” en la página 256 es fundamental porque ilustra tareas típicas implementadas utilizando
la API RESTful, como recuperar datos históricos y de transmisión, realizar pedidos o buscar información de cuentas.
Empezando
Encontrará documentación detallada de la API de FXCM en https://oreil.ly/Df_7e. Para instalar el paquete contenedor
de Python fxcmpy, ejecute lo siguiente en el shell:
Para comenzar con la API comercial de FXCM y el paquete fxcmpy , es suficiente una cuenta de
demostración gratuita con FXCM. Se puede abrir una cuenta de este tipo en la cuenta demo de FXCM.
1 El siguiente paso es crear un token API único (por ejemplo, YOUR_FXCM_API_TOKEN) desde la
cuenta de demostración. A continuación se abre una conexión con la API, por ejemplo de la siguiente manera:
importar fxcmpy
api = fxcmpy.fxcmpy(access_token=YOUR_FXCM_API_TOKEN, log_level='error')
Alternativamente, puede usar el archivo de configuración creado en el Capítulo 8 para conectarse a la API.
El contenido de este archivo debe modificarse de la siguiente manera:
[FXCM]
log_level = error log_file
= PATH_TO_AND_NAME_OF_LOG_FILE access_token
= YOUR_FXCM_API_TOKEN
importar fxcmpy
api = fxcmpy.fxcmpy(config_file='pyalgo.cfg')
De forma predeterminada, el servidor se conecta al servidor de demostración. Sin embargo, mediante el uso del
parámetro del servidor , se puede realizar la conexión al servidor de operaciones en vivo (si existe dicha cuenta):
Recuperando datos
FXCM brinda acceso a conjuntos de datos históricos de precios de mercado, como datos de ticks, en una variante
preempaquetada. Esto significa que se pueden recuperar, por ejemplo, archivos comprimidos de los servidores FXCM
que contienen datos de ticks para el tipo de cambio EUR/USD de la semana.
1 Tenga en cuenta que las cuentas demo de FXCM solo se ofrecen para ciertos países.
Empezando | 251
Machine Translated by Google
Para varios pares de divisas, FXCM proporciona datos históricos de ticks. El paquete fxcmpy facilita la
recuperación de dichos datos de ticks y el trabajo con ellos. Primero, algunas importaciones:
El segundo es un vistazo a los símbolos disponibles (pares de divisas) para los cuales hay datos de tick
disponibles:
En [3]: imprimir(tdr.get_available_symbols())
('AUDCAD', 'AUDCHF', 'AUDJPY', 'AUDNZD', 'CADCHF', 'EURAUD', 'EURCHF',
'EURGBP', 'EURJPY', 'EURUSD', 'GBPCHF', 'GBPJPY', 'GBPNZD', 'GBPUSD',
'GBPCHF', 'GBPJPY', 'GBPNZD', 'NZDCAD', 'NZDCHF', 'NZDJPY', 'NZDUSD',
'USDCAD', 'USDCHF', 'USDJPY')
El siguiente código recupera los datos de ticks de una semana para un solo símbolo. El objeto pandas
DataFrame resultante tiene más de 4,5 millones de filas de datos:
En [7]: td.get_data().info()
<clase 'pandas.core.frame.DataFrame'> DatetimeIndex:
4504288 entradas, 20200322 21:12:02.256000 a
20200327 20:59:00.022000
Columnas de datos (un total de 2 columnas):
# Tipo de columna
0 Oferta flotante64
1 Pregunta a float64
tipos de datos: float64(2)
uso de memoria: 103,1 MB
En [8]: td.get_data().head()
Fuera[8]: Oferta Preguntar
Esto recupera el archivo de datos, lo descomprime y almacena los datos sin procesar en un DataFrame .
objeto (como un atributo del objeto resultante).
El método .get_raw_data() devuelve el objeto DataFrame con los datos sin procesar
para los cuales los valores de índice siguen siendo objetos str .
Dado que los datos de ticks se almacenan en un objeto DataFrame , es sencillo seleccionar un subconjunto de
datos e implementar tareas típicas de análisis financiero en él. Figura 91
muestra un gráfico de los precios medios derivados para el subconjunto y un promedio móvil simple
(AME):
En [10]: sub.head()
Fuera[10]: Oferta Preguntar
2 La conversión de DatetimeIndex lleva mucho tiempo, por lo que existen dos métodos diferentes relacionados con
Recuperación de datos de marca.
Además, FXCM proporciona acceso a datos históricos de velas (más allá de la API). Los datos de velas son datos
para ciertos intervalos de tiempo homogéneos (“barras”) con valores de apertura, máximo, mínimo y cierre para
precios de oferta y demanda.
Primero, un vistazo a los símbolos disponibles para los cuales se proporcionan datos de velas:
En [15]: imprimir(cdr.get_available_symbols())
('AUDCAD', 'AUDCHF', 'AUDJPY', 'AUDNZD', 'CADCHF', 'EURAUD', 'EURCHF',
'EURGBP', 'EURJPY', 'EURUSD', 'GBPCHF', 'GBPJPY', 'GBPNZD', 'GBPUSD',
'GBPCHF', 'GBPJPY', 'GBPNZD', 'NZDCAD', 'NZDCHF', 'NZDJPY', 'NZDUSD',
'USDCAD', 'USDCHF', 'USDJPY')
En segundo lugar, la propia recuperación de datos. Es similar a la recuperación de datos de ticks. La única diferencia
La diferencia es que es necesario especificar un valor de período , o la longitud de la barra (por ejemplo, m1
durante un minuto, H1 durante una hora o D1 durante un día):
En [20]: datos.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 600 entradas, 20200329 21:00:00 a 20200501 20:00:00
Columnas de datos (un total de 8 columnas):
# Columna Tipo D de recuento no nulo
En [21]: datos[datos.columnas[:4]].tail()
Fuera[21]: Oferta Oferta abierta Oferta alta Oferta baja Oferta cerrada
20200501 16:00:00 1.09976 1.09996 1.09850 1.09874
20200501 17:00:00 1.09874 1.09888 1.09785 1.09818
20200501 18:00:00 1.09818 1.09820 1.09757 1.09766
20200501 19:00:00 1.09766 1.09816 1.09747 1.09793
20200501 20:00:00 1.09793 1.09812 1.09730 1.09788
En [22]: datos[datos.columnas[4:]].tail()
Fuera[22]: PreguntarAbrir PreguntarAlto PreguntarBajo PreguntarCerrar
20200501 16:00:00 1.09980 1.09998 1.09853 1.09876
20200501 17:00:00 1.09876 1.09891 1.09786 1.09818
20200501 18:00:00 1.09818 1.09822 1.09758 1.09768
20200501 19:00:00 1.09768 1.09818 1.09748 1.09795
20200501 20:00:00 1.09795 1.09856 1.09733 1.09841
Para concluir esta sección, el código Python que sigue y calcula los precios de cierre medio, calcula
dos SMA y traza los resultados (consulte la Figura 92):
Calcula los precios de cierre medio a partir de los precios de oferta y cierre.
Calcula dos SMA: uno para un intervalo de tiempo más corto y otro para uno más largo.
Figura 92. Precios históricos de cierre medio por hora para EUR/USD y dos SMA
En [27]: fxcmpy.__version__
Fuera[27]: '1.2.6'
En [30]: imprimir(instrumentos)
['EUR/USD', 'USD/JPY', 'GBP/USD', 'USD/CHF', 'EUR/CHF', 'AUD/USD', 'USD/CAD',
'NZD/USD', ' EUR/GBP', 'EUR/JPY', 'GBP/JPY', 'CHF/JPY', 'GBP/CHF', 'EUR/AUD',
'EUR/CAD', 'AUD/CAD', 'AUD/ JPY', 'CAD/JPY', 'NZD/JPY', 'GBP/CAD', 'GBP/NZD',
'GBP/AUD', 'AUD/NZD', 'USD/SEK', 'EUR/SEK' , 'EUR/NOK', 'USD/NOK', 'USD/
MXN', 'AUD/CHF', 'EUR/NZD', 'USD/ZAR', 'USD/HKD', 'ZAR/JPY', ' USD/TRY', 'EUR/
TRY', 'NZD/CHF', 'CAD/CHF', 'NZD/CAD', 'TRY/JPY', 'USD/ILS', 'USD/CNH',
'AUS200' , 'ESP35', 'FRA40', 'GER30', 'HKG33', ' JPN225', 'NAS100', 'SPX500',
'UK100', 'US30', 'Cobre', 'CHN50', 'EUSTX50', ' USDOLLAR', 'US2000', 'USOil',
'UKOil', ' SOYF', 'NGAS', 'USOilSpot', 'UKOilSpot', 'WHEATF', 'CORNF', 'Bund', 'XAU/
USD', ' XAG/USD', 'EMBasket', 'JPYBasket', 'BTC/USD', 'BCH/USD', 'ETH/USD', 'LTC/
USD', 'XRP/USD', 'CryptoMajor', 'EOS/ USD', 'XLM/USD', 'ESPORTS', 'BIOTECH',
'CANNABIS', 'FAANG', 'CHN.TECH', 'CHN.ECOMM', 'USEquities']
Una vez conectado, la recuperación de datos para intervalos de tiempo específicos se logra mediante
una única llamada a un método. Cuando se utiliza el método .get_candles() , el período del parámetro
puede ser uno de m1, m5, m15, m30, H1, H2, H3, H4, H6, H8, D1, W1 o M1. La Figura 93 muestra
los precios de cierre de la barra de un minuto para el instrumento EUR/USD (par de divisas):
En [32]: velas[velas.columnas[:4]]
Fuera[32]: oferta oferta abierta oferta cerrada oferta alta oferta baja
fecha
20200807 21:00:00 105.538 105.898 106.051 105.452 20200809
21:00:00 105.871 105.846 105.871 105.844
20200810 21:00:00 105.846 105.914 106.197 105.702
20200811 21:00:00 105.914 106.466 106.679 105.870
20200812 21:00:00 106.466 106.848 107.009 106.434 20200813
21:00:00 106.848 106.893 107.044 106.560 20200814 21:00:00 106.893
106.535 107.033 106.429
20200817 21:00:00 106.559 105.960 106.648 105.937
20200818 21:00:00 105.960 105.378 106.046 105.277
20200819 21:00:00 105.378 105.528 105.599 105.097
En [33]: velas[velas.columnas[4:]]
Fuera[33]: preguntarabrir preguntarcerrar preguntarpreguntar altotick bajocantidad
fecha
20200807 21:00:00 105.557 105.969 106.062 105.484 253759 20200809 21:00:00
105.983 105.952 105.989 105.925 20
20200810 21:00:00 105.952 105.986 106.209 105.715 161841
Figura 93. Precios históricos de cierre de venta para EUR/USD (barras de minutos)
bien los datos históricos son importantes, por ejemplo, para realizar pruebas retrospectivas de estrategias
comerciales algorítmicas, se requiere acceso continuo a datos en tiempo real o en tiempo real (durante el horario
comercial) para implementar y automatizar estrategias comerciales algorítmicas. Al igual que la API de Oanda,
la API de FXCM también permite la suscripción a flujos de datos en tiempo real para todos los instrumentos. El
paquete contenedor fxcmpy admite esta funcionalidad porque permite proporcionar funciones definidas por el
usuario (las llamadas funciones de devolución de llamada) para procesar el flujo de datos en tiempo real suscrito.
El siguiente código Python presenta una función de devolución de llamada tan simple (solo imprime elementos
seleccionados del conjunto de datos recuperados) y la utiliza para procesar los datos recuperados en tiempo
real, después de una suscripción al instrumento deseado (aquí EUR/ USD):
En [41]: api.get_last_price('EUR/USD')
Salida[41]: Oferta 1.19318 Oferta Alta
Baja 1.19329
1.19534
1.19217 Nombre:
En [42]: api.unsubscribe_market_data('EUR/USD')
Esta es la función de devolución de llamada que imprime ciertos elementos de los datos recuperados.
colocar.
Aquí está la suscripción a un flujo de datos específico en tiempo real. Los datos se procesan de forma
asincrónica siempre que no exista un evento de "cancelación de suscripción".
Las funciones de devolución de llamada son una forma flexible de procesar datos de
transmisión en tiempo real basados en una función de Python o incluso en varias funciones de este tipo.
Se pueden utilizar para tareas simples, como la impresión de datos entrantes, o tareas
complejas, como generar señales comerciales basadas en algoritmos comerciales en línea.
Colocación de órdenes
La API de FXCM permite la colocación y gestión de todo tipo de órdenes que también están disponibles a
través de la aplicación comercial de FXCM (como órdenes de entrada u órdenes de trailing stop loss).3 Sin
embargo, el siguiente código ilustra la compra y venta básica del mercado. órdenes únicamente, ya que
generalmente son suficientes para al menos comenzar con el comercio algorítmico.
El siguiente código primero verifica que no haya posiciones abiertas y luego abre posiciones diferentes.
diferentes posiciones a través del método .create_market_buy_order() :
En [43]: api.get_open_positions()
Out[43]: Marco de datos vacío
Columnas: []
Índice: []
En [46]: api.get_open_positions()[sel]
Fuera[46]: cantidad de tradeIdK moneda brutaPL esComprar
0 169122817 100 EUR/USD 9,21945 Verdadero
En [48]: api.get_open_positions()[sel]
Fuera[48]: cantidad de tradeIdK moneda brutaPL esComprar
0 169122817 100 EUR/USD 8,38125 Verdadero
1 169122819 50 EUR/GBP 9,40900 Verdadero
En [51]: api.get_open_positions()[sel]
Fuera[51]: cantidad de tradeIdK moneda brutaPL esComprar
0 169122817 100 EUR/USD 7,54306 Verdadero
4 Las cantidades están en miles del instrumento para pares de divisas. Además, tenga en cuenta que diferentes cuentas pueden tener
diferentes ratios de apalancamiento. Esto implica que la misma posición podría requerir más o menos capital (margen)
dependiendo del ratio de apalancamiento relevante. Ajuste las cantidades del ejemplo a valores más bajos si es necesario. Ver
https://oreil.ly/xUHMP.
En [52]: api.close_all_for_symbol('EUR/GBP')
En [53]: api.get_open_positions()[sel]
Fuera[53]: cantidad de tradeIdK moneda brutaPL esComprar
0 169122817 1 100 EUR/USD 5,02858 Verdadero
169122834 25 EUR/USD 3,14257 Falso
En [54]: api.close_all()
En [55]: api.get_open_positions()
Out[55]: Marco de datos vacío
Columnas: []
Índice: []
Para el EUR/GBP ahora hay dos posiciones largas abiertas; al contrario del EUR/USD
posición, no se compensa.
De forma predeterminada, FXCM configura cuentas de demostración como cuentas de cobertura. Este
Información de la cuenta
Más allá, por ejemplo, de las posiciones abiertas, la API de FXCM permite recuperar más posiciones genéricas.
información general de la cuenta, también. Por ejemplo, se puede buscar la cuenta predeterminada.
(si hay varias cuentas) o una descripción general de la situación del capital y el margen:
En [56]: api.get_default_account()
Fuera[56]: 1233279
tasaPrecisión 0
cuentaId 1233279
saldo 47555.2
usdMr 0
mc norte
mcFecha
Nombre de cuenta 01233279
usdMr3 0
cobertura Y
utilizable Margen 3 47555.2
utilizable Margen Perc 100
utilizable Margen 3 Perc 100
capital 47555.2
utilizable Margen 47555.2
bus 1000
día PL 653.16
bruto PL 0
Conclusiones
Este capítulo trata sobre la API RESTful de FXCM para el comercio algorítmico y cubre
los siguientes temas:
Más allá de estos aspectos, la API FXCM y el paquete contenedor fxcmpy proporcionan, por supuesto
Por supuesto, más funcionalidad. Sin embargo, los temas de este capítulo son la construcción básica.
bloques necesarios para comenzar con el comercio algorítmico.
Con Oanda y FXCM, los operadores algorítmicos tienen dos plataformas comerciales (brokers)
disponibles que proporcionan un amplio espectro de instrumentos financieros y
API adecuadas para implementar estrategias comerciales algorítmicas y automatizadas. Algunas importantes
Estos aspectos se añaden a la mezcla en el Capítulo 10.
Conclusiones | 263
Machine Translated by Google
CAPÍTULO 10
A la gente le preocupa que las computadoras se vuelvan demasiado inteligentes y se apoderen del mundo,
pero el verdadero problema es que son demasiado estúpidas y ya se han apoderado del mundo.
—Pedro Domingos
"¿Ahora que?" tú puedes pensar. La plataforma comercial que permite recuperar datos históricos y
datos en tiempo real está disponible. Permite realizar órdenes de compra y venta y comprobar el
estado de la cuenta. En este libro se han introducido varios métodos diferentes para derivar
estrategias comerciales algorítmicas prediciendo la dirección de los movimientos de los precios del
mercado. Quizás se pregunte: "¿Cómo, después de todo, se puede combinar todo esto para que
funcione de forma automatizada?" Esto no puede responderse de manera generalizada. Sin embargo,
este capítulo aborda una serie de temas que son importantes en este contexto. El capítulo supone
que se implementará una única estrategia comercial algorítmica y automatizada. Esto simplifica, por
ejemplo, aspectos como la gestión del capital y del riesgo.
El capítulo cubre los siguientes temas. “Gestión de capital” en la página 266 analiza el criterio de
Kelly. Dependiendo de las características de la estrategia y del capital comercial disponible, el criterio
de Kelly ayuda a dimensionar las operaciones. Para ganar confianza en una estrategia comercial
algorítmica, es necesario realizar una prueba retrospectiva exhaustiva de la estrategia con respecto
a las características de rendimiento y riesgo. La “Estrategia comercial basada en ML” en la página
277 realiza una prueba retrospectiva de una estrategia de ejemplo basada en un algoritmo de
clasificación de aprendizaje automático (ML), como se presenta en “Estrategias comerciales” en la
página 13. Para implementar la estrategia comercial algorítmica para el comercio automatizado, es
necesario traducirse en un algoritmo en línea que funcione con datos de transmisión entrantes en
tiempo real. “Algoritmo en línea” en la página 291 cubre la transformación de un algoritmo fuera de
línea en un algoritmo en línea.
265
Machine Translated by Google
única opción viable desde el punto de vista de disponibilidad, rendimiento y seguridad en este
contexto. “Registro y monitoreo” en la página 297 cubre el registro y el monitoreo.
El registro es importante para poder analizar el historial y ciertos eventos durante el despliegue
de una estrategia comercial automatizada. La monitorización a través de comunicación por
socket, como se presentó en el Capítulo 7, permite observar eventos de forma remota en tiempo
real. El capítulo concluye con una “Descripción general visual paso a paso” en la página 299,
que proporciona un resumen visual de los pasos principales para la implementación automatizada
de estrategias comerciales algorítmicas en la nube.
forma común de introducir la teoría del criterio de Kelly en la inversión es sobre la base de un
juego de lanzamiento de moneda o, más generalmente, un entorno binomial (sólo son posibles
dos resultados). Esta sección sigue ese camino. Supongamos que un jugador está jugando al
lanzamiento de una moneda contra un banco o casino infinitamente rico. Supongamos además
que la probabilidad de obtener cara es algún valor p para el cual se cumple lo siguiente:
1
2 <p<1
1
q=1−p< 2
El jugador puede realizar apuestas b > 0 de tamaño arbitrario, por lo que gana la misma cantidad
si acierta y lo pierde todo si se equivoca. Dadas las suposiciones sobre las probabilidades, el
jugador, por supuesto, querría apostar a cara.
Por lo tanto, el valor esperado para este juego de apuestas B (es decir, la variable aleatoria que
representa este juego) en una configuración de una sola vez es el siguiente:
B=p∙b−q∙b=p−q∙b>0
A un jugador neutral al riesgo y con fondos ilimitados le gustaría apostar la mayor cantidad posible, ya
que esto maximizaría el beneficio esperado. Sin embargo, el comercio en los mercados financieros no
es un juego de una sola vez en general. Es un juego repetido. Por tanto, supongamos que bi representa
la cantidad que se apuesta el día i y que c0 representa el capital inicial. El capital c1 al final del día uno
depende del éxito de las apuestas ese día y podría ser c0 + b1 o c0 − b1. El valor esperado para una
apuesta que se repite n veces es el siguiente:
norte
Bn = c0 + ∑
yo = 1
p − q ∙ bi
En la teoría económica clásica, con agentes maximizadores de la utilidad esperada neutrales al riesgo,
un jugador intentaría maximizar la expresión anterior. Se ve fácilmente que se maximiza apostando
todos los fondos disponibles, bi = ci − 1, como en el escenario único.
Sin embargo, esto a su vez implica que una sola pérdida acabará con todos los fondos disponibles y
conducirá a la ruina (a menos que sea posible pedir préstamos ilimitados). Por tanto, esta estrategia no
conduce a una maximización de la riqueza a largo plazo.
Si bien apostar el máximo capital disponible puede conducir a una ruina repentina, no apostar nada
evita cualquier tipo de pérdida pero tampoco se beneficia de la ventajosa apuesta. Aquí es donde entra
en juego el criterio de Kelly ya que de él se deriva la fracción óptima f* del capital disponible para
apostar por ronda de apuestas. Supongamos que n = h + t donde h representa el número de caras
observadas durante n rondas de apuestas y donde t representa el número de cruces. Con estas
definiciones, el capital disponible después de n rondas es el siguiente:
h t
cn = c0 ∙ 1 + f ∙1−f
1/n
cn
r
gramo
= iniciar sesión
c0
h t 1/n
c0 ∙ 1 + f = ∙1−f
registro
c0
h t 1/n
∙
= registro 1 + f 1−f
h t
=
iniciar sesión 1 + f + iniciar sesión 1 f
norte norte
gr h t
=
iniciar sesión 1 + f + iniciar sesión 1 f
norte norte
קGf
pag − q
GRAMO
f=1 1−f
= + fp − pf − q − qf
1+f1−f
= pag q f
1+f1−f
!
Gf= 0 f * = pag − q
Si uno confía en que esto sea el máximo (y no el mínimo), este resultado implica que
lo óptimo es invertir una fracción f * = p − q por ronda de apuestas. Con, por ejemplo,
p = 0,55, se tiene f* = 0,55 0,45 = 0,1, o que la fracción óptima es el 10%.
En [2]: np.random.seed(1000)
plt.style.use('nacido en el mar')
mpl.rcParams['savefig.dpi'] = 300
mpl.rcParams['font.family'] = 'serif'
La idea es simular, por ejemplo, 50 series con 100 lanzamientos de moneda por serie. El
El código Python para esto es sencillo:
En [3]: p = 0,55
En [4]: f = p (1 p)
En [5]: f
Salida [5]: 0,10000000000000009
En [6]: I = 50
En [7]: n = 100
c[t, i] = (1 f) * c[t 1, i]
volver c
En [10]: c_1.round(2)
Fuera[10]: matriz([[100. , [ 90. 100. , 100. , ..., 100. , 100. , 100. ],
[ 99. , 110. , 90. , ..., 110. , 90. , 110. ],
, 121. , 99. , ..., 121. , 81. , 121. ],
...,
[226,35, 338,13, 413,27, ..., 123,97, 123,97, 123,97],
[248,99, 371,94, 454,6 , ..., 136,37, 136,37, 136,37],
[273.89, 409.14, 409.14, ..., 122.73, 150.01, 122.73]])
Crea una instancia de un objeto ndarray para almacenar los resultados de la simulación.
Si 1 o cara...
Si es 0 o cruz...
Figura 101. 50 series simuladas con 100 ensayos cada una (línea roja = promedio)
El siguiente código repite la simulación para diferentes valores de f . Como se muestra en la Figura
102, una fracción más baja conduce a una tasa de crecimiento más baja en promedio. Valores más
altos podrían llevar a un capital promedio más alto al final de la simulación (f =0,25) o a un capital
promedio mucho más bajo (f =0,5]). En ambos casos donde la fracción f es mayor, la volatilidad
aumenta considerablemente:
Figura 102. Capital promedio a lo largo del tiempo para diferentes valores de f
ahora un entorno de mercado de valores en el que la acción relevante (índice) sólo puede adquirir dos valores
después de un período de un año a partir de hoy, dado su valor conocido hoy.
El escenario es nuevamente binomial, pero esta vez un poco más cercano en el lado del modelado a las realidades
del mercado de valores.1 Específicamente, supongamos que lo siguiente es cierto:
S 1
pr =μ+σ=Pr S=μ−σ=
2
S
estándar de = μ > 0 es el rendimiento esperado de la acción durante un año, y σ > 0 es aquí, r la desviación
los rendimientos (volatilidad). En un escenario de un período, se obtiene lo siguiente para el capital disponible
después de un año (con c0 yf definidos como antes):
S
cf = c0 ∙ 1 + 1 − f ∙ r + f ∙ r
Aquí, r es la tasa corta constante obtenida con el efectivo no invertido en la acción. maximiz
Calcular la tasa de crecimiento geométrico significa maximizar el término:
cf
G f = log c0
Supongamos ahora que hay n días comerciales relevantes en el año, de modo que para cada uno de ellos
día de negociación i se cumple lo siguiente:
σ σ 1
S
= µ + S
= µ − =
pri norte norte
= Pri norte norte 2
Tenga en cuenta que la volatilidad aumenta con la raíz cuadrada del número de días de negociación. Bajo
estos supuestos, los valores diarios aumentan a los anuales de antes y uno
obtiene lo siguiente:
r
norte
S
cn f = c0 ∙ ∏ 1+1−f∙ + f∙ ri
yo = 1 norte
Ahora hay que maximizar la siguiente cantidad para lograr la máxima rentabilidad a largo plazo.
riqueza al invertir en acciones:
cn f
Gn f = registro
c0
r
norte
S
=∑ iniciar sesión 1 + 1 − f ∙ + f∙ ri
yo = 1 norte
1 r σ
norte
= ∑ µ +
2 yo = 1
iniciar sesión 1 + 1 − f ∙
norte
+f∙ norte norte
r µ −
σ
+ Iniciar sesión 1 + 1 − f ∙
norte
+f∙ norte norte
2
r µ F 2σ2
=
norte
−
2 iniciar sesión 1 + 1 − f ∙ norte
+f∙ norte norte
σ2 1
2+
Gn f = r + μ − r ∙ f − ∙ f 2 norte
O para una cantidad infinita de puntos de negociación en el tiempo (es decir, para una negociación
continua), se llega a lo siguiente:
σ2 2
G∞ f = r + μ − r ∙ f − ∙ f 2
La fracción óptima f * entonces viene dada por la condición de primer orden mediante la siguiente
expresión:
= μ−r
f*
σ2
Esto representa el exceso de rendimiento esperado de la acción sobre la tasa libre de riesgo dividido
por la varianza de los rendimientos. Esta expresión parece similar a la relación de Sharpe pero es
diferente.
Un ejemplo del mundo real ilustrará la aplicación de la fórmula anterior y su papel en el apalancamiento
del capital desplegado en las estrategias comerciales. La estrategia comercial considerada es
simplemente una posición larga pasiva en el índice S&P 500. Para ello, los datos base se recuperan
rápidamente y las estadísticas necesarias se derivan fácilmente:
En [20]: datos.dropna(inplace=True)
En [21]: datos.tail()
Fuera[21]: .SPX devolver
Fecha
20191223 3224.01 0.000866
20191224 3223.38 0.000195
20191227 3240.02 0.000034
20191230 3221.29 0.005798
20191231 3230.78 0.002942
Las propiedades estadísticas del índice S&P 500 durante el período cubierto sugieren que se invertirá
una fracción óptima de aproximadamente 4,5 en la posición larga del índice. Es decir, por cada dólar
disponible se invertirán 4,5 dólares, lo que implica un ratio de apalancamiento de 4,5 de acuerdo con la
fracción de Kelly óptima o, en este caso, el factor de Kelly óptimo.
En [23]: mu
Fuera [23]: 0.09992181916534204
En [25]: sigma
Fuera [25]: 0,14761569775486563
En [26]: r = 0,0
En [28]: f
Fuera [28]: 4.585590244019818
En [29]: ecus = []
datos[cap].loc[t_1] + \
datos[equ].loc[t_1]
datos.loc[t, cap] = datos[equ].loc[t] * f
En [33]: kelly_strategy(f)
En [34]: imprimir(datos[equs].tail())
equidad_2.29 equidad_3.03 equidad_4.59
Fecha
20191223 6.628865 9.585294 14.205748
20191224 6.625895 9.579626 14.193019
20191227 6.626410 9.580610 14.195229
20191230 6.538582 9.412991 13.818934
20191231 6.582748 9.496919 14.005618
…y f mismo.
Figura 103. Rendimiento bruto del S&P 500 en comparación con la posición accionaria dados diferentes
valores de f
Como ilustra la Figura 103 , la aplicación del apalancamiento óptimo de Kelly conduce a una evolución
bastante errática de la posición accionaria (alta volatilidad), lo cual es intuitivamente plausible, dado el
índice de apalancamiento de 4,59. Se esperaría que la volatilidad de la posición accionaria aumentara al
aumentar el apalancamiento. Por lo tanto, los profesionales a menudo no utilizan “Kelly completo” (4.6),
sino “medio Kelly” (2.3). En el ejemplo actual, esto se reduce a:
1
2 • f * ≈ 2,3
En este contexto, la Figura 103 también muestra la evolución de la posición patrimonial para valores
inferiores al “Kelly completo”. De hecho, el riesgo se reduce con valores más bajos de látexmatemáticas:
[$f$].
El Capítulo 8 presenta la plataforma comercial Oanda, su API RESTful y el paquete contenedor Python
tpqoa. Esta sección combina un enfoque basado en ML para predecir la dirección de los movimientos de
los precios del mercado con datos históricos de la API RESTful de Oanda v20 para probar una estrategia
comercial algorítmica para el par de divisas EUR/USD. Utiliza backtesting vectorizado, teniendo en cuenta
esta vez el diferencial de oferta y demanda como costos de transacción proporcionales. También agrega,
en comparación con el vectorizado simple
Backtesting vectorizado
El backtest se basa en datos intradía, más concretamente en barras de 10 minutos en
longitud. El siguiente código se conecta a la API de Oanda v20 y recupera 10 minutos
datos de la barra durante una semana. La Figura 104 visualiza los precios de cierre medio durante el período para
qué datos se recuperan:
En [40]: raw.tail()
Fuera[40]: oh h yo c volumen completo
tiempo
20200612 20:10:00 1.12572 1.12593 1.12532 1.12568 221 Verdadero
20200612 20:40:00 1.12544 1.12594 1.12528 1.12542 20200612 20:50:00 219 Verdadero
En [41]: raw.info()
<clase 'pandas.core.frame.DataFrame'>
DatetimeIndex: 701 entradas, 20200608 00:00:00 a 20200612 20:50:00
Columnas de datos (un total de 6 columnas):
# Columna Tipo D de recuento no nulo
float64(4), int64(1)
Uso de memoria: 33,5 KB
La estrategia basada en ML utiliza una serie de características de series temporales, como el retorno de registros.
y el mínimo y el máximo del precio de cierre. Además, las características
los datos están retrasados. En otras palabras, el algoritmo ML aprenderá de patrones históricos.
tal como lo reflejan los datos de características rezagadas:
En [48]: ventana = 20
datos['retorno'] = np.log(datos / datos.shift(1))
En [49]: datos.dropna(inplace=True)
En [50]: rezagos = 6
En [52]: columnas = []
para f en características:
para retraso en el rango (1, retrasos + 1):
col = f'{f}_lag_{lag}'
datos[col] = datos[f].shift(lag) cols.append(col)
En [53]: datos.dropna(inplace=True)
retorno_lag_5 retorno_lag_6
tiempo
20200608 04:20:00 0,000000 0.000009
20200608 04:30:00 0,000035 0.000000
20200608 04:40:00 0.000452 0.000035
20200608 04:50:00 0.000018 0.000452
20200608 05:00:00 0.000097 0.000018
20200608 05:10:00 0.000115 0.000097
Deriva el impulso de la serie temporal como la media de los rendimientos logarítmicos recientes.
Define los datos de las etiquetas como la dirección del mercado (+1 o arriba y 1 o abajo).
Dadas las características y los datos de las etiquetas, ahora se podrían utilizar diferentes algoritmos de aprendizaje supervisado.
se aplicado. A continuación se utiliza el algoritmo de clasificación denominado AdaBoost.
del paquete scikitlearn ML (consulte AdaBoostClassifier). La idea de impulsar
en el contexto de la clasificación es utilizar un conjunto de clasificadores base para llegar a una
predictor superior que se supone que es menos propenso al sobreajuste (consulte “Data Snoop
ing y sobreajuste” en la página 111). Como clasificador base, una clasificación de árbol de decisión.
Se utiliza el algoritmo de scikitlearn (consulte DecisionTreeClassifier).
El código entrena y prueba la estrategia comercial algorítmica basándose en una división secuencial de prueba de
tren. Las puntuaciones de precisión del modelo para los datos de entrenamiento y prueba son significativas.
significativamente por encima del 50%. En lugar de puntuaciones de precisión, también se hablaría en un sentido financiero.
contexto comercial de la proporción de aciertos de la estrategia comercial (es decir, el número de ganancias
operaciones en comparación con todas las operaciones). Dado que la tasa de aciertos es significativamente mayor que el 50%,
esto podría indicar, en el contexto del criterio de Kelly, una ventaja estadística en comparación
a una configuración de paseo aleatorio:
En [57]: n_estimadores=15
estado_aleatorio=100
profundidad_máxima=2
min_samples_leaf=15 submuestra=0,33
min_samples_leaf=min_samples_leaf)
Especifica los parámetros principales para el algoritmo ML (consulte las referencias para el
clases de modelo proporcionadas anteriormente).
Aplica la normalización gaussiana al conjunto de datos de características de prueba (utilizando los parámetros del
conjunto de datos de características de entrenamiento).
Muestra la precisión de las predicciones del modelo entrenado fuera de la muestra (conjunto de datos de prueba).
Es bien sabido que el índice de aciertos es sólo una cara de la moneda del éxito en el comercio financiero. El otro lado
comprende, entre otras cosas, realizar correctamente las operaciones importantes, así como los costos de transacción
implicados por la estrategia comercial.2 Con este fin, sólo un enfoque formal de backtesting vectorizado permite juzgar
la calidad de la estrategia comercial. El siguiente código tiene en cuenta los costos de transacción proporcionales según
el diferencial promedio entre oferta y demanda. La Figura 105 compara el desempeño de la estrategia comercial
algorítmica (sin y con costos de transacción proporcionales) con el desempeño de la inversión pasiva de referencia:
En [71]: suma(prueba['posición'].diff() != 0)
Fuera[71]: 77
2 Es un hecho empírico estilizado que es de suma importancia para el desempeño de la inversión y el comercio acertar los
mayores movimientos del mercado (es decir, los mayores movimientos de ganadores y perdedores). Este aspecto
se ilustra claramente en la Figura 105, que muestra que la estrategia comercial logra un gran movimiento a la baja en
el instrumento subyacente correcto, lo que lleva a un salto mayor para la estrategia comercial.
Deriva los retornos de registro para la estrategia comercial algorítmica basada en ML.
Calcula el número de operaciones que implica la estrategia comercial en función de los cambios en
la posición.
Cada vez que se realiza una operación, los costos de transacción proporcionales se restan del
rendimiento logarítmico de la estrategia ese día.
Figura 105. Rendimiento bruto del tipo de cambio EUR/USD y estrategia de negociación algorítmica
(antes y después de los costes de transacción)
El backtesting vectorizado tiene sus límites con respecto a qué tan cerca de
las realidades del mercado se pueden probar las estrategias. Por ejemplo,
no permite incluir directamente los costos de transacción fijos por operación.
Como aproximación, se podría tomar un múltiplo de los costos de
transacción proporcionales promedio (basados en el tamaño promedio de
las posiciones) para contabilizar indirectamente los costos de transacción
fijos. Sin embargo, esto no sería exacto en general. Si se requiere un mayor
grado de precisión, se deben aplicar otros enfoques, como el backtesting
basado en eventos (ver Capítulo 6) con bucles explícitos sobre cada barra
de los datos de precios.
Apalancamiento óptimo
Equipado con los datos de retorno del registro de la estrategia comercial, los valores de media y varianza
se puede calcular para derivar el apalancamiento óptimo según el criterio de Kelly.
río. El código que sigue escala los números a valores anualizados, aunque esto
no cambia los valores de apalancamiento óptimos según el criterio de Kelly ya que el
rentabilidad media y la escala de varianza con el mismo factor:
Variaciones anualizadas.
Volatilidades anualizadas.
Utilizando el criterio de "mitad Kelly", el apalancamiento óptimo para la estrategia comercial está por encima
50. Con una serie de corredores, como Oanda, y ciertos instrumentos financieros, como
como pares de divisas y contratos por diferencias (CFD), tales ratios de apalancamiento
son factibles, incluso para los comerciantes minoristas. La Figura 106 muestra, en comparación, el desempeño de la estrategia
Figura 106. Rendimiento bruto de la estrategia comercial algorítmica para diferentes valores de apalancamiento
Análisis de riesgo
Dado que el apalancamiento aumenta el riesgo asociado con una determinada estrategia comercial,
Por supuesto, parece necesario realizar un análisis de riesgos más profundo. El análisis de riesgo que sigue
asume un ratio de apalancamiento de 30. Primero, la reducción máxima y la reducción más larga
Se calculará el período de inactividad. La reducción máxima es la mayor pérdida (caída) después de un
máximo reciente. En consecuencia, el período de retiro más largo es el período más largo que el
La estrategia comercial debe volver a un máximo reciente. El análisis supone que el inicio
La posición inicial de capital es de 3.333 EUR, lo que lleva a un tamaño de posición inicial de 100.000 EUR para
un ratio de apalancamiento de 30. También se supone que no hay ajustes con respecto al
equidad a lo largo del tiempo, sin importar cuál sea el desempeño:
En [88]: riesgo['reducción'].max()
Fuera[88]: 511.38321383258017
El patrimonio inicial.
En [92]: periodos[20:30]
Fuera[92]: matriz([datetime.timedelta(segundos=600),
fechahora.timedelta(segundos=1200),
datetime.timedelta(segundos=1200), datetime.timedelta(segundos=1200)],
tipo d=objeto)
En [94]: t_per
Salida[94]: datetime.timedelta(segundos=26400)
…transformado en horas.
Otra medida de riesgo importante es el valor en riesgo (VaR). Se cotiza como moneda.
importe y representa la pérdida máxima que se puede esperar dado tanto un tiempo determinado
horizonte y un nivel de confianza.
El siguiente código deriva valores VaR basados en los rendimientos logarítmicos de la posición de capital.
para la estrategia comercial apalancada a lo largo del tiempo para diferentes niveles de confianza. El tiempo
El intervalo se fija en la duración del compás de diez minutos:
En [102]: imprimir_var()
Nivel de confianza Valor en riesgo
99,99 162.570
99,90 161.348
99.00 132.382
97,50 122.913
95,00 100.950
90.00 62.622
Traduce los valores percentiles a niveles de confianza y los valores VaR (negativos).
valores positivos) a valores positivos para la impresión.
Finalmente, el siguiente código calcula los valores VaR para un horizonte temporal de una hora mediante
remuestreo del objeto DataFrame original . En efecto, los valores del VaR aumentan para
todos los niveles de confianza:
En [106]: imprimir_var()
Nivel de confianza Valor en riesgo
99,99 252.460
99,90 251.744
99.00 244.593
97,50 232.674
95,00 125.498
90,00 61.701
Algoritmo en línea
El algoritmo comercial probado hasta ahora es un algoritmo fuera de línea. Estos algoritmos utilizan
un conjunto de datos completo para resolver un problema en cuestión. El problema ha sido entrenar
un algoritmo de clasificación AdaBoost basado en un árbol de decisión como clasificador base, varias
características de series de tiempo diferentes y datos de etiquetas direccionales. En la práctica, al
implementar el algoritmo comercial en los mercados financieros, debe consumir datos pieza por pieza
a medida que llegan para predecir la dirección del movimiento del mercado para el siguiente intervalo
de tiempo (barra). Esta sección utiliza el objeto de modelo persistente de la sección anterior y lo
incorpora en un contexto de transmisión de datos.
El código que transforma el algoritmo de comercio fuera de línea en un algoritmo de comercio en línea
aborda principalmente los siguientes problemas:
Datos de
tick Los datos de tick llegan en tiempo real y deben procesarse en tiempo real, por ejemplo, para
recopilarse en un objeto DataFrame .
Remuestreo
Los datos de tick se deben remuestrear a la longitud de barra adecuada según el algoritmo
comercial. A modo de ilustración, se utiliza una longitud de barra más corta para el remuestreo
que para el entrenamiento y el backtesting.
Predicción
El algoritmo comercial genera una predicción de la dirección del movimiento del mercado durante
el intervalo de tiempo relevante que por naturaleza se encuentra en el futuro.
Órdenes
Dada la posición actual y la predicción (“señal”) generada por el algoritmo, se realiza una orden o la posición se
mantiene sin cambios.
El Capítulo 8, y en particular “Trabajar con datos de streaming” en la página 236, muestra cómo
recuperar datos de ticks de la API de Oanda en tiempo real. El enfoque básico es redefinir el
método .on_success() de la clase tpqoa.tpqoa para implementar la lógica comercial.
En [111]: algoritmo['modelo']
Fuera[111]: AdaBoostClassifier(algoritmo='SAMME.R',
base_estimator=DecisionTreeClassifier(ccp_alpha=0.0,
class_weight=Ninguno,
criterio='gini',
max_profundidad=2,
max_features=Ninguno,
max_leaf_nodes=Ninguno,
min_impurity_decrease=0.0,
min_impurity_split=Ninguno,
min_samples_leaf=15,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort='obsoleto',
random_state=100,
splitter='mejor'),
learning_rate=1.0, n_estimators =15, estado_aleatorio=100)
self.posición = 0 self.bar
= '5s'
self.window = 2
self.lags = 6
self.min_length = self.lags + self.window + 1 self.features = ['return',
'sma', 'min', 'max', 'vol', 'mom '] self.raw_data = pd.DataFrame() def prepare_features(self):
self.data['return'] = np.log(self.data['mid'] /
self.data['mid'].shift( 1)) self.data['sma']
= self.data['mid'].rolling(self.window).mean() self.data['min'] =
self.data['mid'].rolling (self.window).min()
self.data['mamá'] = np.sign(
self.data['return'].rolling(self.window).mean()) self.data['max'] =
self.data['mid'].rolling(self.window).max() self. datos['vol'] = self.data['return'].rolling(
self.window).std()
self.data.dropna(inplace=True)
self.data[self.features] = self.mu self.data[self.features] /
= self.std self.cols = [] para f en self.features:
index=[pd.Timestamp(time).tz_localize(None)]) self.raw_data =
self.raw_data.append(df) self.data =
self.raw_data.resample(self.bar,
label='right').last().ffill() self.data =
self.data.iloc[:1] if len(self.data) >
self.min_length: self.min_length +=1 self.data[ 'mid']
= (self.data['oferta'] +
self.data['preguntar']) / 2
self.prepare_features()
características =
self.data[ self.cols].iloc[1].values.reshape(1, 1)
señal = self.model.predict(características)[0] print(f'NUEVA
SEÑAL: {señal}', end='\r') si self.position en [0, 1] y señal
== 1: imprimir ('*** VA LARGO ***')
self.create_order(self.stream_instrument,
unidades=(1 self.position) * self.units) self.position = 1
auto.posición = 1
Con la nueva clase MLTrader, el comercio automatizado se simplifica. Unas pocas líneas de código son suficientes en un
contexto interactivo. Los parámetros están configurados de tal manera que el primer pedido se realiza al poco tiempo. En
realidad, sin embargo, todos los parámetros deben, por supuesto, estar en
en línea con los originales de la fase de investigación y backtesting. Por ejemplo, también podrían persistir
en el disco y leerse con el algoritmo:
Infraestructura e implementación
Implementar una estrategia comercial algorítmica automatizada con fondos reales requiere una
infraestructura adecuada. Entre otras cosas, la infraestructura debe cumplir con lo siguiente:
Confiabilidad
La infraestructura en la que implementar una estrategia comercial algorítmica debe permitir
una alta disponibilidad (por ejemplo, 99,9% o más) y debe cuidar la confiabilidad (copias de
seguridad automáticas, redundancia de unidades y conexiones web, etc.).
Rendimiento
Dependiendo de la cantidad de datos que se procesen y la demanda computacional que
generen los algoritmos, la infraestructura debe tener suficientes núcleos de CPU, memoria de
trabajo (RAM) y almacenamiento (SSD). Además, las conexiones web deben ser lo
suficientemente rápidas.
Seguridad
El sistema operativo y las aplicaciones que se ejecutan en él deben estar protegidos con
contraseñas seguras, así como con cifrado SSL y cifrado del disco duro. El hardware debe
estar protegido contra incendios, agua y acceso físico no autorizado.
Básicamente, estos requisitos sólo se pueden cumplir alquilando una infraestructura adecuada a
un centro de datos profesional o a un proveedor de nube. Las propias inversiones en infraestructura
física para satisfacer los requisitos mencionados anteriormente sólo pueden ser justificadas por los
actores más grandes, o incluso los más grandes, de los mercados financieros.
Desde el punto de vista del desarrollo y las pruebas, incluso el Droplet (instancia de nube) más
pequeño de DigitalOcean (http://digitalocean.com) es suficiente para comenzar. Al momento de
escribir este artículo, un Droplet de este tipo cuesta 5 USD por mes y se factura por hora, se crea
en minutos y se destruye en segundos.3
3 Utilice el enlace http://bit.ly/do_sign_up para obtener un bono de 10 USD en DigitalOcean al registrarse para un nuevo
cuenta.
Registro y monitoreo
Supongamos ahora que la estrategia de comercio algorítmico automatizado se implementará en un
servidor remoto (instancia de nube virtual o servidor dedicado). Supongamos además que se han
instalado todos los paquetes de Python necesarios (consulte “Uso de instancias en la nube” en la página
36) y que, por ejemplo, Jupyter Lab se está ejecutando de forma segura (consulte Ejecución de un
servidor portátil). ¿Qué más se debe considerar desde el punto de vista de los traders algorítmicos si no
quieren sentarse todo el día frente a la pantalla mientras inician sesión en el servidor?
Esta sección aborda dos temas importantes a este respecto: el registro y el monitoreo en tiempo real.
El registro conserva la información y los eventos en el disco para su posterior inspección. Es una
práctica estándar en el desarrollo e implementación de aplicaciones de software. Sin embargo, aquí la
atención podría centrarse en el aspecto financiero, registrando datos financieros importantes e
información de eventos para su posterior inspección y análisis. Lo mismo se aplica a la monitorización
en tiempo real mediante comunicación por socket. A través de sockets, se puede crear un flujo
constante en tiempo real de aspectos financieros importantes que luego se pueden recuperar y procesar
en una computadora local, incluso si la implementación se realiza en la nube.
“Estrategia comercial automatizada” en la página 305 presenta una secuencia de comandos Python que
implementa todos estos aspectos y hace uso del código de “Algoritmo en línea” en la página 291. La
secuencia de comandos le da al código una forma que permite, por ejemplo, la implementación del
algoritmo. estrategia comercial rítmica, basada en el objeto algorítmico persistente, en un servidor
remoto. Agrega capacidades de registro y monitoreo basadas en una función personalizada que, entre
otras cosas, hace uso de ZeroMQ (ver http://zeromq.org) para la comunicación por socket. En
combinación con el breve script de “Monitoreo de estrategia” en la página 308, esto permite un monitoreo
remoto en tiempo real de la actividad en un servidor remoto.4
Cuando se ejecuta el script de "Estrategia comercial automatizada" en la página 305 , ya sea local o
remotamente, la salida que se registra y envía a través del socket tiene el siguiente aspecto:
4 El método de registro utilizado aquí es bastante simple y tiene la forma de un archivo de texto simple. Es fácil cambiar el registro y
la persistencia de, por ejemplo, los datos financieros relevantes en forma de una base de datos o formatos de almacenamiento
binario apropiados, como HDF5 (ver Capítulo 3).
20200615 17:04:14.298653
==================================================== ===============================
==================================================== ===============================
[5 filas x 36 columnas]
==================================================== ===============================
características:
[[0.02091774 0.06444794 0.07782834 0.04937258 0.02650799 0.12525265 2.06428556 1.96568848 2.16288147
2.08071843 1.9492569 22.19574189
0,92939697 0,92939697 1,07368691 0,92939697 1,07368691 1,07368691 1,41861822 1,42605902 1,4294412
1,42470615 1,4274119 1,42 470615 1.05508516 1.06879043 1.06879043 1.0619378 1.06741991 1.06741991
1.70580717 1.70707253 1.71339931 1.7108686 1.7108686 1.70580717]] posición: 1 señal: 1
20200615 17:04:14.402154
==================================================== ===============================
***FIN DE CICLO***
20200615 17:04:16.199950
==================================================== ===============================
==================================================== ===============================
{'id': '979', 'hora': '20200615T15:04:16.138027118Z', 'ID de usuario': 13834683, 'ID de cuenta': '10100413834683001',
'ID de lote': '978', 'requestID': '60721506683906591', 'tipo': 'ORDER_FILL', 'orderID':
'978', 'instrumento': 'EUR_USD', 'unidades': '100000.0', 'gainQuoteHomeConversionFactor': ' 0.882420762903',
'lossQuoteHomeConversionFactor': '0.891289313284', 'precio': 1.12751,
'fullVWAP': 1.12751, 'fullPrice': {'tipo': 'PRECIO', 'ofertas': [{'precio': 1.12751,
'liquidez ': '10000000'}], 'pregunta': [{'precio': 1.12765, 'liquidez': '10000000'}],
'liquidación': 1.12751, 'liquidez': 1.12765}, 'razón': 'MARKET_ORDER' , 'pl': '3.5652', 'financiación': '0.0',
'comisión': '0.0',
==================================================== ===============================
La ejecución local del script de “Monitoreo de estrategia” en la página 308 permite la recuperación y
el procesamiento en tiempo real de dicha información. Por supuesto, es fácil ajustar el registro y la
transmisión de datos a los propios requisitos.5 Además, el guión comercial y toda la lógica se pueden
ajustar para incluir elementos tales como detener pérdidas o objetivos de obtención de ganancias
mediante programación.
Operar con pares de divisas y/o CFD está asociado con una serie de riesgos
financieros. La implementación de una estrategia de negociación algorítmica
para dichos instrumentos conlleva automáticamente una serie de riesgos
adicionales. Entre ellos se encuentran fallas en la lógica de negociación y/o
ejecución, así como riesgos técnicos que incluyen problemas asociados con la
comunicación por socket, recuperación retrasada o incluso pérdida de datos
de ticks durante la implementación. Por lo tanto, antes de implementar una
estrategia comercial de manera automatizada, uno debe asegurarse de que
todos los riesgos asociados de mercado, de ejecución, operativos, técnicos y
de otro tipo hayan sido identificados, evaluados y abordados adecuadamente.
El código presentado en este capítulo es sólo para fines de ilustración técnica.
Esta sección final proporciona una descripción general paso a paso en capturas de pantalla. Si bien
las secciones anteriores se basan en la plataforma comercial FXCM, la descripción visual se basa en
la plataforma comercial Oanda.
El primer paso es configurar una cuenta con Oanda (o cualquier otra plataforma comercial para este
fin) y establecer el índice de apalancamiento correcto para la cuenta de acuerdo con el criterio de
Kelly y como se muestra en la Figura 108.
5 Tenga en cuenta que la comunicación por socket, tal como se implementa en los dos scripts, no está cifrada y envía texto sin formato
a través de la web, lo que podría representar un riesgo de seguridad en producción.
segundo paso es crear una gota de DigitalOcean, como se muestra en la Figura 109.
todo el software en el droplet (consulte la Figura 1010) para configurar la infraestructura. Cuando todo funcione bien,
podrá crear un nuevo Jupyter Notebook e iniciar su sesión interactiva de Python (consulte la Figura 1011).
paso es cargar los scripts de Python para el comercio automatizado y el monitoreo en tiempo real, como
se muestra en la Figura 1012. También es necesario cargar el archivo de configuración con las credenciales
de la cuenta.
paso es ejecutar el script Python para el comercio automatizado, como se muestra en la Figura 1013. La
Figura 1014 muestra una operación que ha iniciado el script Python.
paso es ejecutar el script de monitoreo localmente (siempre que haya configurado la IP correcta en el script
local), como se ve en la Figura 1015. En la práctica, esto significa que puede monitorear localmente en
tiempo real qué está sucediendo exactamente en su instancia en la nube.
Conclusiones
Este capítulo trata sobre el despliegue de una estrategia comercial algorítmica de forma automatizada,
basada en un algoritmo de clasificación del aprendizaje automático para predecir la dirección de los
movimientos del mercado. Aborda temas tan importantes como la gestión de capital (basada en el criterio
de Kelly), el backtesting vectorizado de rendimiento y riesgo, la transformación de algoritmos comerciales
fuera de línea a en línea, una infraestructura adecuada para la implementación y el registro y monitoreo
durante la implementación.
El tema de este capítulo es complejo y requiere un amplio conjunto de habilidades por parte del practicante
del comercio algorítmico. Por otro lado, tener disponibles API RESTful para trading algorítmico, como la de
Oanda, simplifica considerablemente la tarea de automatización, ya que la parte central se reduce
principalmente a hacer uso de las capacidades del paquete contenedor de Python tpqoa para la recuperación
de datos de ticks. y realización de pedidos. Alrededor de este núcleo se deberían agregar, en la medida de
lo apropiado y posible, elementos para mitigar los riesgos operativos y técnicos.
Rotando, Louis y Edward Thorp. 1992. “El criterio de Kelly y el mercado de valores”. El Mensual
Matemático Estadounidense 99 (10): 922931.
#
# Estrategia comercial automatizada basada en ML para Oanda
# Algoritmo en línea, registro, monitoreo
#
# Python para el comercio algorítmico # (c) Dr. Yves J.
Hilpisch #
importar zmq
importar tpqoa
importar pickle
importar numpy como
np importar pandas como
pd importar fecha y hora como dt
log_file = 'estrategia_automatizada.log'
# esto vincula la comunicación del socket a todas las direcciones IP de la máquina socket.bind('tcp://
0.0.0.0:5555')
self.position = 0 self.bar
= '2s' self.window = 2
self.lags = 6
self.min_length =
self.lags + self.window + 1 self.features = ['return', 'vol', 'mom ',
'sma', 'min', 'max'] self.raw_data = pd.DataFrame()
def prepare_features(yo):
self.data['return'] = np.log( self.data['mid'] /
self.data['mid'].shift(1)) self.data['vol'] = self.data['
return'].rolling(self.window).std() self.data['mamá'] = np.sign(
self.data['return'].rolling(self.window).mean()) self.data['sma'] =
self.data['mid'].rolling(self.window).mean() self. datos['min'] = self.data['mid'].rolling(self.window).min()
self.data['max'] = self.data['mid'].rolling(self.window) .max() self.data.dropna(inplace=True)
self.data[self.features] = self.mu self.data[self.features] /= self.std self.cols = [] para f en
self .características:
'''
Imprime, registra y envía datos comerciales.
'''
str(self.data[self.cols].tail()), Falso)
orden = self.create_order(self.stream_instrument,
unidades=(1 self.position) * self.units,
suprimir=True,
ret=True) self.report_trade('LONG',
orden) self.position = 1
Monitoreo de estrategia El
siguiente script de Python contiene código para monitorear de forma remota la ejecución del
script de Python de “Estrategia comercial automatizada” en la página 305.
#
# Estrategia comercial automatizada basada en ML para Oanda # Monitoreo de
estrategia a través de comunicación de socket # # Python para comercio
#
importar zmq
mientras es
Verdadero: msg = socket.recv_string()
print(msg)
APÉNDICE
pandas.pydata.org)
NumPy proporciona operaciones de matriz de alto rendimiento en conjuntos de datos numéricos grandes y homogéneos,
mientras que pandas está diseñado principalmente para manejar datos tabulares, como datos de series de tiempo
financieras, de manera eficiente.
Un apéndice introductorio de este tipo, que sólo aborda temas seleccionados relevantes para el resto del contenido de
este libro, no puede, por supuesto, reemplazar una introducción exhaustiva a Python y los paquetes cubiertos. Sin
embargo, si eres bastante nuevo en Python o en la programación en general, es posible que obtengas una primera
visión general y una idea de lo que se trata Python. Si ya tiene experiencia en otro lenguaje típicamente utilizado en
finanzas cuantitativas (como Matlab, R, C++ o VBA), verá cómo son las estructuras de datos, los paradigmas de
programación y los modismos típicos en Python.
Para obtener una descripción general completa de Python aplicado a las finanzas, consulte Hilpisch (2018).
Otras introducciones más generales al lenguaje con un enfoque científico y de análisis de datos son VanderPlas (2017)
y McKinney (2017).
309
Machine Translated by Google
Esta sección presenta los tipos y estructuras de datos básicos de Python, las estructuras de control,
y algunos modismos de Python.
Tipos de datos
Cabe señalar que Python es generalmente un sistema de tipo dinámico, lo que significa
que los tipos de objetos se infieren a partir de sus contextos. Empecemos con los números:
En [1]: a = 3
En [3]: a.bit_length()
Fuera[3]: 2
En [4]: b = 5.
Busca el tipo de a.
Python puede manejar números enteros arbitrariamente grandes, lo cual es bastante beneficioso para los números.
aplicaciones teóricas, por ejemplo:
En [6]: c = 10 ** 100
En [7]: c
Fuera[7]: 10000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000
En [8]: c.bit_length()
Fuera[8]: 333
En [9] : 3/5 .
Fuera[9]: 0,6
Entrada [10]: a * b
Salida [10]: 15,0
Entrada [11]: a b
Salida [11]: 2,0
Entrada [12]: b + a
Salida [12]: 8,0
Entrada [13]: a ** b
Salida [13]: 243,0
División.
Multiplicación.
Suma.
Diferencia.
Fuerza.
En [15]: math.log(a)
Fuera[15]: 1.0986122886681098
En [16]: matemática.exp(a)
Fuera[16]: 20.085536923187668
En [17]: matemáticas.sin(b)
Fuera[17]: 0,9589242746631385
En [19]: tipo(s)
Fuera[19]: str
En [20]: en inferior()
Out[20]: 'python para comercio algorítmico'.
En [21]: en superior()
Out[21]: 'PYTHON PARA COMERCIO ALGORITMICO.'
En [22]: s[0:6]
Fuera[22]: 'Python'
Estos objetos también se pueden combinar utilizando el operador + . El valor del índice –1 repre
envía el último carácter de una cadena (o el último elemento de una secuencia en general):
En [24]: imprimir(st)
Comercio de Python
Imprime el resultado.
En [27]: repl = 'Mi nombre es {:s}, tengo {:d} años y {:4.2f} m de altura.'
{altura:4.2f}m de altura.')
Mi nombre es Gordon Gekko, tengo 43 años y mido 1,78 m .
Hace uso de la llamada cadena f para el reemplazo de cadenas (introducida en Python 3.6).
Estructuras de datos
Los objetos tupla son estructuras de datos livianas. Estas son colecciones inmutables de otros objetos y están
construidas por objetos separados por comas, con o sin paréntesis:
En [32]: t1
Salida [32]: (3, 5.0, 'Comercio con Python')
En [34]: t2 = st, b, a
En [35]: t2
Salida [35]: ('Python Trading', 5.0, 3)
En [38]: t
Fuera[38]: ((3, 5.0, 'Comercio con Python'), ('Comercio con Python', 5.0, 3))
En [39]: t[0][2]
Fuera[39]: 'Comercio con Python'
Los objetos de lista son colecciones mutables de otros objetos y generalmente se construyen
proporcionando una colección de objetos separados por comas entre paréntesis:
En [41]: l
Fuera[41]: [3, 5.0, 'Comercio con Python']
En [43]: l.append(s.split()[3])
En [44]: l
Fuera[44]: [3, 5.0, 'Comercio con Python', 'Comercio'.
Ordenar es una operación típica en objetos de lista , que también se puede construir usando el constructor
de listas (aquí aplicado a un objeto tupla):
En [46]: l
Fuera[46]: ['Z', 'Q', 'D', 'J', 'E', 'H', '5.', 'a']
En [47]: l.sort()
En [48]: l
Fuera[48]: ['5.', 'D', 'E', 'H', 'J', 'Q', 'Z', 'a']
Ordena todos los elementos in situ (es decir, cambia el objeto mismo).
Los objetos de diccionario (dict) son los llamados almacenes de valores clave y generalmente se
construyen con llaves:
En [51]: d
Salida[51]: {'int_obj': 3, 'float_obj': 5.0, 'string_obj': 'Python Trading'}
En [52]: d['float_obj']
Fuera[52]: 5,0
En [53]: d['int_obj_long'] = 10 ** 20
En [54]: d
Fuera[54]: {'int_obj': 3,
'obj_flotante': 5.0,
'string_obj': 'Comercio con Python',
'int_obj_long': 100000000000000000000}
En [55]: d.claves()
Fuera[55]: dict_keys(['int_obj', 'float_obj', 'string_obj', 'int_obj_long'])
En [56]: d.valores()
Fuera[56]: dict_values([3, 5.0, 'Python Trading', 100000000000000000000])
Estructuras de Control
En [57]: rango(5)
Fuera[57]: rango(0, 5)
3 5 7 9 11 13
En [61]: l = ['a', 'b', 'c', 'd', 'e']
En [62]: para _ en l:
imprimir(_)
a
b
C
d
mi
En [64]: para c en s:
imprimir(c + '|', fin='')
P|y|t|h|o|n| |T|r|a|d|i|n|g|
En [65]: i = 0
…aumentar el valor de i en 1.
Modismos de Python
Python en muchos lugares se basa en una serie de modismos especiales. Comencemos con una
bastante popular, la lista de comprensión:
En [68]: lc
Fuera[68]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Crea un nuevo objeto de lista basado en la sintaxis de comprensión de la lista ( bucle for entre
paréntesis).
En [71]: f(5)
Fuera[71]: 0,2836621854632263
Asigna la función f a todos los elementos del objeto de rango y crea un objeto de lista con los
resultados, que se imprime.
En general, se trabaja con funciones regulares de Python (a diferencia de las funciones lambda), que
se construyen de la siguiente manera:
En [74]: f(5)
Fuera[74]: 148.4131591025766
regresar Ninguno
Devuelve algo: aquí, Ninguno; No es necesario para una función Python válida.
En [81]: número_decide(a)
Out[81]: 'El número es de tres dígitos'.
NumPy
Muchas operaciones en finanzas computacionales se llevan a cabo sobre grandes conjuntos de datos numéricos.
NumPy es un paquete de Python que permite el manejo y operación eficiente de dichas estructuras de datos.
Aunque es un paquete bastante poderoso con una gran cantidad de funcionalidades, para los propósitos de este
libro es suficiente cubrir los conceptos básicos de NumPy. Un interesante libro en línea que está disponible de
forma gratuita sobre NumPy es From Python to NumPy. Cubre muchos aspectos importantes en detalle que se
omiten en las siguientes secciones.
En [83]: a = np.array(rango(24))
En [84]: un
Fuera[84]: matriz([ 0, 1, 2, 3, 4, 5 , 6 , 7, 8, 9, 10, 11, 12, 13, 14,
15, 16,
17, 18, 19, 20, 21, 22, 23])
En [86]: b
Fuera[86]: matriz([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17], [18, 19,
20, 21, 22, 23]])
En [88]: c
Fuera[88]: matriz([[[ 0, 1, 2, 3], [ 4, 5, 6,
7], [ 8, 9, 10, 11]],
En [90]: b
Fuera[90]: matriz([[ 0., 1., 2., 3., 4., 5.], [ 6., 7., 8., 9., 10.,
11.], [12., 13., 14., 15., 16., 17.], [18.,
19., 20., 21., 22., 23.]])
Crea una instancia de un objeto ndarray a partir del objeto de rango ; np.arange también podría
usarse, por ejemplo.
…e imprime el resultado.
…e imprime el resultado.
Operaciones vectorizadas
En [91]: 2 * b
Fuera[91]: matriz([[ 0., 2., 4., 6., 8., 10.], [12., 14., 16., 18., 20 ., 22.],
[24., 26., 28., 30., 32., 34.], [36., 38., 40., 42.,
44., 46.]])
En [92]: b ** 2
Salida[92]: matriz([[ 0., 1., 4., 9., 16., 25.], [ 36., 49., 64.,
81., 100., 121.], [144., 169., 196., 225., 256., 289 .],
[324., 361., 400., 441., 484., 529.]])
En [94]: f(a)
Fuera[94]: matriz([ 0,5, 0,5, 0,5, 3,5, 8,5 , 15,5, 24,5, 35,5,
48,5,
63,5, 80,5, 99,5, 120,5, 143,5 , 168,5, 195,5, 224,5, 255,5,
288,5, 323,5, 360,5, 399,5, 440,5, 483,5])
En muchos escenarios, sólo una (pequeña) parte de los datos almacenados en un objeto ndarray es de
interés. NumPy admite cortes básicos y avanzados y otras funciones de selección:
En [95]: a[2:6]
Fuera[95]: matriz([2, 3, 4, 5])
En [96]: b[2, 4]
Fuera[96]: 16,0
Operaciones booleanas
En [98]: b > 10
Fuera[98]: matriz([[Falso, Falso, Falso, Falso, Falso, Falso],
[Falso, Falso, Falso, Falso, Falso, Verdadero],
[ Verdadero, Verdadero, Verdadero, Verdadero, Verdadero, Verdadero],
[ Verdadero, Verdadero, Verdadero, Verdadero, Verdadero, Verdadero]])
En [100]: a.suma()
Fuera[100]: 276
En [101]: b.media()
Fuera[101]: 11,5
En [102]: b.media(eje=0)
Fuera[102]: matriz([ 9., 10., 11., 12., 13., 14.])
En [103]: b.media(eje=1)
Fuera[103]: matriz([ 2.5, 8.5, 14.5, 20.5])
En [104]: c.std()
Fuera[104]: 6.922186552431729
De manera similar, existe una gran cantidad de las llamadas funciones universales que el paquete NumPy proporciona.
vídeos. Son universales en el sentido de que se pueden aplicar en general a NumPy.
objetos ndarray y tipos de datos numéricos estándar de Python. Para obtener más información, consulte Universidad
funciones sal (ufunc):
En [105]: np.sum(a)
Fuera[105]: 276
En [107]: np.sin(b).ronda(2)
Fuera[107]: matriz([[ 0. , 0,84, 0,91, 0,14, 0,76, 0,96],
[0,28, 0,66, 0,99, 0,41, 0,54, 1. ],
[0,54, 0,42, 0,99, 0,65, 0,29, 0,96],
[0,75, 0,15, 0,91, 0,84, 0,01, 0,85]])
En [108]: np.sin(4.5)
Fuera[108]: 0,977530117665097
El valor del seno para todos los elementos redondeado a dos dígitos.
Sin embargo, debe tener en cuenta que aplicar las funciones universales de NumPy al estándar
Los tipos de datos de Python generalmente conllevan una carga de rendimiento significativa:
Por otro lado, usar las operaciones vectorizadas de NumPy en objetos ndarray es más rápido que las dos
alternativas anteriores que dan como resultado objetos de lista . Sin embargo, la ventaja de la velocidad a
menudo tiene el costo de una huella de memoria mayor, o incluso enorme:
En [113]: sys.getsizeof(a)
Fuera[113]: 8000096
Cálculo vectorizado de los valores de los senos con NumPy, que es mucho más rápido en general.
Creación de ndarray
Aquí utilizamos el constructor de objetos ndarray np.arange(), que produce un objeto ndarray.
objeto de números enteros. El siguiente es un ejemplo sencillo:
En [115]: ai = np.arange(10)
En [116]: ai
Fuera[116]: matriz([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
En [119]: af
Fuera[119]: matriz([0.5, 1. 6.5, , 1,5, 2. , 2,5, 3. , 3.5, 4. , 4,5, 5. , 5.5, 6. ,
7. , 7,5, 8. , 8.5, 9. ])
Utiliza arange() nuevamente, pero esta vez con parámetros de inicio, fin y paso .
Números al azar
En análisis financiero, a menudo se necesitan números aleatorios1. NumPy proporciona muchas funciones
ciones para tomar muestras de diferentes distribuciones. Los que se necesitan regularmente en términos cuantitativos
Las finanzas son la distribución normal estándar y la distribución de Poisson. El
En [122]: np.random.standard_normal(10)
Fuera[122]: matriz([1.06384884, 0.22662171, 1.2615483 1.23231112, , 0.45626608,
En [124]: np.random.seed(1000)
En [127]: datos.media()
Fuera[127]: 0,02714981205311327
En [128]: datos.std()
Fuera[128]: 1.0016799134894265
En [130]: datos.media()
Fuera[130]: 3.552713678800501e18
En [132]: datos.std()
Fuera[132]: 1,0
Corrige el valor inicial del generador de números aleatorios para mayor repetibilidad.
1 Tenga en cuenta que las computadoras sólo pueden generar números pseudoaleatorios como aproximaciones a números verdaderamente aleatorios.
matplotlib
En este punto, tiene sentido introducir el trazado con matplotlib, el método de trazado
caballo de batalla en el ecosistema Python. Usamos matplotlib con la configuración de otro.
biblioteca en todas partes, a saber, seaborn. Esto da como resultado un estilo de trazado más moderno.
El siguiente código genera la Figura A1:
En [134]: plt.style.use('seaborn')
Primero calcula la suma acumulada de todos los elementos del objeto ndarray y
luego traza el resultado.
También es fácil generar múltiples gráficos de líneas en un objeto de una sola figura (consulte la Figura A2):
Traza cinco líneas calculando la suma acumulada a lo largo del primer eje y define
una etiqueta.
Otros tipos de trazado importantes son los histogramas y los gráficos de barras. En la Figura A3 se muestra un histograma
para los 500 valores del objeto de datos . En el código, el método .flatten() se utiliza para generar una matriz unidimensional
a partir de una bidimensional:
Finalmente, considere el gráfico de barras presentado en la Figura A4, generado por el siguiente
código:
Traza un gráfico de barras basado en un pequeño subconjunto del conjunto de datos original.
Para concluir la introducción a matplotlib, considere la regresión de mínimos cuadrados ordinarios (OLS) de los
datos de muestra que se muestran en la Figura A5. NumPy proporciona las dos funciones de conveniencia polifit
y polival para implementar OLS basado
En [142]: x = np.arange(len(data.cumsum()))
Regresión lineal.
Regresión cúbica.
pandas
pandas es un paquete con el que se pueden gestionar y operar datos de series temporales y
otras estructuras de datos tabulares de manera eficiente. Permite la implementación de soluciones incluso sofisticadas.
ted tareas de análisis de datos en conjuntos de datos bastante grandes en memoria. Si bien el foco está en
operaciones en memoria, también hay múltiples opciones para operaciones sin memoria (en disco)
operaciones. Aunque pandas proporciona varias estructuras de datos diferentes, incorpora
Compuesto por clases potentes, la estructura más utilizada es la clase DataFrame .
que se asemeja a una tabla típica de una base de datos relacional (SQL) y se utiliza para administrar,
por ejemplo, datos de series de tiempo financieras. Esto es en lo que nos centramos en esta sección.
En [149]: np.random.seed(1000)
En [154]: gl
Fuera[154]: no1 no2 Numero 3
Define un objeto de lista que contiene los nombres de las columnas (etiquetas).
Los objetos DataFrame han incorporado una multitud de funciones básicas, avanzadas y convenientes.
métodos, algunos de los cuales se ilustran en el código Python que aparece a continuación:
En [155]: df.head()
Fuera[155]: no1 no2 no3
20220131 0.804458 0.320932 0.025483
20220228 0.160134 0.020135 0.363992
20220331 0.267572 0.459848 0.959027
20220430 0.732239 0.207433 0.152912
20220531 1.928309 0.198527 0.029466
En [156]: df.tail()
Fuera[156]: no1 no2 Numero 3
En [157]: df.index
Salida[157]: DatetimeIndex(['20220131', '20220228', '20220331',
'20220430',
'20220531', '20220630', '20220731', '20220831',
'20220930', '20221031'],
dtype='fechahora64[ns]', frecuencia='M')
En [158]: df.columns
Fuera[158]: Índice(['no1', 'no2', 'no3'], dtype='objeto')
En [160]: df.describe()
Fuera[160]: no1 no2 Numero 3
Operaciones numéricas
Las operaciones numéricas son en general tan fáciles con objetos DataFrame como con NumPy
objetos ndarray . También son bastante parecidos en términos de sintaxis:
En [161]: imprimir(df * 2)
no1 no2 no3
20220131 1.608917 0.641863 0.050966
20220228 0.320269 0.040270 0.727983
20220331 0.535144 0.919696 1.918054
20220430 1.464479 0.414866 0.305823
20220531 3.856618 0.397054 0.058932
20220630 3.650232 0.673898 1.352453
20220731 1.106642 2.647393 0.682782
20220831 1.305605 1.833009 2.521557
20220930 0.681369 1.233314 1.421210
20221031 1.447664 0.412568 4.621376
En [162]: df.std()
Fuera[162]: no1 0,607430
número 2 0,578071
nº3 0,712430
tipo de letra: float64
En [163]: df.media()
Fuera[163]: no1 0.798847
no2 0.227665
Numero 3 0.672067
tipo de letra: float64
En [164]: df.media(eje=1)
Fuera[164]: 20220131 0.169670
20220228 0.074664
20220331 0.077202
20220430 0.123965
20220531 0.718767
20220630 0.495280
20220731 0.511875
20220831 0.102843
20220930 0.328859
20221031 0.460191
Frecuencia: M, tipo d: float64
En [165]: np.media(df)
Fuera[165]: no1 0.798847
no2 0.227665
nº3 0,672067
tipo de letra: float64
…y valor medio. Con los objetos DataFrame , las operaciones por columnas son las
por defecto.
Calcula el valor medio por valor de índice (es decir, por filas).
Selección de datos
En [166]: df['no2']
Fuera[166]: 20220131 0.320932
20220228 0.020135
20220331 0.459848
20220430 0.207433
20220531 0.198527
20220630 0.336949
20220731 1.323696
20220831 0.916504
20220930 0.616657
20221031 0.206284
Frecuencia: M, Nombre: no2, tipo d: float64
En [167]: df.iloc[0]
Fuera[167]: no1 0.804458
no2 0.320932
no3 0.025483
Nombre: 20220131 00:00:00, tipo d: float64
En [168]: df.iloc[2:4]
Fuera[168]: no1 no2 Numero 3
En [169]: df.iloc[2:4, 1]
Fuera[169]: 20220331 0.459848
20220430 0.207433
Frecuencia: M, Nombre: no2, tipo d: float64
En [170]: df.no3.iloc[3:7]
Fuera[170]: 20220430 0.152912
20220531 0.029466
20220630 0.676227
20220731 0.341391
Frecuencia: M, Nombre: no3, tipo d: float64
En [171]: df.loc['2022331']
Fuera[171]: no1 0.267572
no2 0.459848
Numero 3 0.959027
Nombre: 20220331 00:00:00, tipo d: float64
Operaciones booleanas
La selección de datos basada en operaciones booleanas también es un punto fuerte de los pandas:
20220331 Verdadero
20220430 FALSO
20220531 FALSO
20220630 Verdadero
20220731 FALSO
20220831 Verdadero
20220930 Verdadero
20221031 Verdadero
En [179]: a = 0,5
Combina dos condiciones con el operador & (bit a bit y) ; | es el operador bit a bit o .
Selecciona todas las filas con valores de índice mayores (posteriores) que '2020515' (aquí, según la
clasificación de objetos str ).
Utiliza el método .query() para seleccionar filas dadas las condiciones como objetos str .
bien integrado con el paquete de trazado matplotlib , lo que lo hace conveniente para trazar datos almacenados
en objetos DataFrame . En general, una sola llamada a un método ya es suficiente (consulte la Figura A6):
Traza los datos como un diagrama de líneas (por columnas) y fija el tamaño de la figura.
pandas se encarga del formato adecuado de los valores del índice, en este caso las fechas. Esto solo funciona
correctamente para un DatetimeIndex . Si la información de fecha y hora está disponible solo como objetos str , el
constructor DatetimeIndex() se puede utilizar para transformar la información de fecha y hora fácilmente:
En [183]: pd.DatetimeIndex(df.index)
Fuera[183]: DatetimeIndex(['20220131', '20220228', '20220331',
'20220430',
'20220531', '20220630', '20220731', '20220831',
'20220930', '20221031'],
dtype='datetime64[ns]', freq='M')
Los histogramas también se generan de esta manera. En ambos casos, pandas se encarga del manejo de las
columnas individuales y genera automáticamente líneas individuales (con las respectivas entradas de leyenda,
consulte la Figura A6) y genera las respectivas subgráficas con tres histogramas diferentes (como en la Figura
A7). ):
Operaciones de entradasalida
Otro punto fuerte de los pandas es la exportación e importación de datos hacia y desde
diversos formatos de almacenamiento de datos (ver también el Capítulo 3). Considere el caso de la separación por comas.
Archivos de valores almacenados (CSV):
En [185]: df.to_csv('datos.csv')
objetos.
Sin embargo, en general, almacenaría los objetos DataFrame en el disco de forma más eficiente.
formatos binarios como HDF5. pandas en este caso envuelve la funcionalidad de PyTables
paquete. La función constructora que se utilizará es HDFStore:
En [190]: h5['df'] = df
En [191]: h5
Fuera [191]: <clase 'pandas.io.pytables.HDFStore'>
Ruta del archivo: datos.h5
En [193]: h5.cerrar()
En [194]: from_h5.tail()
Fuera[194]: no1 no2 Numero 3
Caso de estudio
Cuando se trata de datos financieros, hay funciones útiles de importación de datos disponibles
en el paquete pandas (ver también el Capítulo 3). El siguiente código lee datos históricos
diarios para el índice S&P 500 y el índice de volatilidad VIX de un archivo CSV almacenado
en un servidor remoto usando la función pd.read_csv() :
Lee datos históricos del índice bursátil S&P 500 desde un archivo CSV (datos de Refinitiv
Eikon Data API).
Combinemos las respectivas columnas Cerrar en un solo objeto DataFrame . Son posibles
varias formas de lograr este objetivo:
En [202]: spxvix.info()
<clase 'pandas.core.frame.DataFrame'> DatetimeIndex: 2516
entradas, 20100104 a 20191231 Columnas de datos ( 2 columnas en total):
En [204]: spxvix.info()
<clase 'pandas.core.frame.DataFrame'> DatetimeIndex: 2516
entradas, 20100104 a 20191231 Columnas de datos ( 2 columnas en total):
memoria: 139,0 KB
En [206]: spxvix.info()
<clase 'pandas.core.frame.DataFrame'> DatetimeIndex: 2516
entradas, 20100104 a 20191231 Columnas de datos ( 2 columnas en total):
memoria: 139,0 KB
Tener disponibles los datos combinados en un solo objeto hace que el análisis visual sea sencillo.
adelante (ver Figura A8):
Figura A8. Valores históricos de cierre al final del día para el S&P 500 y VIX
En [210]: rets.head()
Fuera[210]: SPX VIX
Fecha
20100105 0.003111 0.035038
20100106 0.000545 0.009868
20100107 0.003993 0.005233
20100108 0.002878 0.050024
20100111 0.001745 0.032514
Calcula los rendimientos logarítmicos de las dos series temporales de forma totalmente vectorizada.
Elimina todas las filas que contienen valores NaN (“no es un número”).
Considere el gráfico de la Figura A9 que muestra los rendimientos del registro VIX frente a los
rendimientos del registro SPX en un diagrama de dispersión con una regresión lineal. Ilustra una
fuerte correlación negativa entre los dos índices:
Implementa una regresión lineal en los dos conjuntos de datos de retorno logarítmico.
Figura A9. Gráfico de dispersión de los rendimientos logarítmicos del S&P 500 y VIX con línea de regresión lineal
Tener datos de series de tiempo financieras almacenados en un objeto Pandas DataFrame hace que la
La cálculo de estadísticas típicas es sencillo:
En [214]: retirarse
Fuera[214]: SPX 0,104995
VIX 0.037526
tipo de letra: float64
En [216]: vol.
Fuera[216]: SPX 0,147902
VIX 1.229086
tipo de letra: float64
Calcula el ratio de Sharpe para una tasa corta libre de riesgo del 1%.
La reducción máxima, que sólo calculamos para el índice S&P 500, es un poco
más involucrado. Para su cálculo utilizamos el método .cummax() , que registra el
Máximo histórico en ejecución de la serie temporal hasta una fecha determinada. Considere lo siguiente
Código siguiente que genera el gráfico en la Figura A10:
Figura A10. Precios históricos de cierre del índice S&P 500 y máximo actual
La reducción máxima absoluta es la mayor diferencia entre el máximo actual y el nivel del índice
actual. En nuestro caso particular, se trata de 580 puntos de índice.
La reducción máxima relativa a veces puede ser un poco más significativa. Aquí hay un valor de
alrededor del 20%:
En [220]: arawdown.max()
Fuera[220]: 579.6500000000001
En [222]: rdrawdown.max()
Fuera[222]: 0.1977821376780688
El período de retiro más largo se calcula de la siguiente manera. El siguiente código selecciona todos
aquellos puntos de datos donde la reducción es cero (donde se alcanza un nuevo máximo). Luego
calcula la diferencia entre dos valores de índice consecutivos (fechas de negociación) para los cuales
la reducción es cero y toma el valor máximo. Dado el conjunto de datos que estamos analizando, el
período de retiro más largo es de 417 días:
En [225]: periodos_spx[50:60]
Fuera[225]: matriz([datetime.timedelta(días=67), datetime.timedelta(días=1),
datetime.timedelta(días=1), datetime.timedelta(días=1),
datetime.timedelta(días= 301), datetime.timedelta(días=3),
datetime.timedelta(días=1), datetime.timedelta(días=2),
datetime.timedelta(días=12), datetime.timedelta(días=2)], dtype
=objeto)
En [226]: máx(periodos_spx)
Fuera[226]: fechahora.timedelta(días=417)
Calcula los valores delta de tiempo entre todas esas posiciones de índice.
Conclusiones
Este apéndice proporciona una descripción general introductoria y concisa de temas seleccionados
relevantes para el uso de Python, NumPy, matplotlib y pandas en el contexto del comercio algorítmico. Por
supuesto, no puede sustituir una formación exhaustiva y una experiencia práctica, pero ayuda a aquellos
que quieren empezar rápidamente y que están dispuestos a profundizar en los detalles cuando sea
necesario.
Recursos adicionales
Una fuente valiosa y gratuita para los temas tratados en este apéndice son las notas de clase de Scipy
que están disponibles en múltiples formatos electrónicos. También está disponible gratuitamente el libro
en línea From Python to NumPy de Nicolas Rougier.
Hilpisch, Yves. 2018. Python para finanzas. 2da ed. Sebastopol: O'Reilly.
McKinney, Wes. 2017. Python para análisis de datos. 2da ed. Sebastopol: O'Reilly.
Vander Plas, Jake. 2017. Manual de ciencia de datos de Python. Sebastopol: O'Reilly.
Índice
operaciones booleanas
(ver también vectorización)
NumPy, 322
operaciones comerciales automatizadas, 265308
pandas, 337
administración de capital, 266277
configuración de la cuenta de Oanda, 299
configuración de hardware,
300 infraestructura e implementación, 296 registro Funciones de devolución de
Estrategia comercial basada en ML, 277290 comerciales automatizadas de gestión de capital y, 266277
351
Machine Translated by Google
negociación con Oanda, 223247 (ver Clase de marco de datos, 57, 49, 332335
también Oanda) Creación de objetos
problemas de clasificación DataFrame, 85
aprendizaje automático para, 141145 almacenamiento,
redes neuronales para, 154155 6670 datismo, ix
CSV con Python, 4749 método .cummax(), 347 pares de script para orquestar la configuración, 4143
divisas, 299 (ver también tipo de cobertura dinámica, 9
cambio EUR/USD)
comercio algorítmico riesgos, 299
E
352 | Índice
Machine Translated by Google
Índice | 353
Machine Translated by Google
JSON
exportando datos financieros a, 50
leyendo datos financieros de, 51 Problema de clasificación
Script de de aprendizaje automático M , 141145
instalación de Jupyter Lab para 4041 regresión lineal con scikitlearn, 139 predicción del
Claves públicas/privadas RSA para, 38 movimiento del mercado, 139153
herramientas incluidas, 36
Estrategia comercial basada en ML, 277290
Cuaderno Jupyter, 38 Scripts de Python, 167
estrategias comerciales y, 15
354 | Índice
Machine Translated by Google
Índice | 355
Machine Translated by Google
Script Python para algoritmo en línea de impulso, 219 tipos de datos, 310313
generación dificultades de implementación, 17
de señales en tiempo real, 208210 transformación modismos, 317319
del algoritmo fuera de línea a, 292 método .on_success(), NumPy y vectorización, 35 obstáculos
240, 291 fuentes de datos abiertas, 5255 para la adopción en la industria financiera,
mínimos cuadrados ordinarios 1
356 | Índice
Machine Translated by Google
Índice | 357
Machine Translated by Google
358 | Índice
Machine Translated by Google
Sobre el Autor
El Dr. Yves J. Hilpisch es fundador y director ejecutivo de The Python Quants, un grupo que se centra en
el uso de tecnologías de código abierto para la ciencia de datos financieros, la inteligencia artificial, el
comercio algorítmico y las finanzas computacionales. También es fundador y director ejecutivo de The AI
Machine, una empresa centrada en el comercio algorítmico impulsado por IA a través de una plataforma
patentada de ejecución de estrategias.
Colofón
El animal en la portada de Python for Algorithmic Trading es una culebra barrada común (Natrix helvetica).
Esta serpiente no venenosa se encuentra en o cerca de agua dulce en Europa occidental.
La culebra barrada común, originalmente miembro de Natrix natrix antes de su reclasificación como especie
distinta, tiene un cuerpo gris verdoso con bandas distintivas a lo largo de sus flancos y puede crecer hasta
un metro de largo. Es un nadador prodigioso y se alimenta principalmente de anfibios como sapos y ranas.
Debido a que necesitan regular su temperatura corporal como todos los reptiles, la culebra barrada común
normalmente pasa sus inviernos bajo tierra, donde la temperatura es más estable.
El aprendizaje automático se utiliza para predecir movimientos del mercado a través de algoritmos de regresión lineal y regresión logística. La regresión lineal se aplica para predecir rendimientos futuros basándose en datos de devolución históricos, mientras que la regresión logística se emplea como un algoritmo de clasificación para predecir la dirección del mercado. Scikit-learn es el paquete de Python principalmente utilizado para implementar estos algoritmos, facilitando tanto el entrenamiento de modelos como la evaluación de su rendimiento .
El libro presenta cuatro estrategias comerciales algorítmicas que se pueden clasificar como principalmente búsqueda de alfa: estrategias basadas en SMA (Simple Moving Average), de impulso, de reversión a la media y posiblemente otras configuraciones específicas. Estas estrategias suelen aplicarse en el contexto de pruebas retrospectivas utilizando Python y destacan por su simplicidad y reproducibilidad en un entorno algorítmico .
El libro cubre el backtesting principalmente a través de estrategias basadas en medias móviles simples (SMA), impulso y reversión a la media mediante un enfoque vectorizado que permite una exploración ágil y rápida de las estrategias con pocas líneas de código . Para implementar el backtesting en Python, se utilizan paquetes como NumPy y pandas para manejar los datos financieros y realizar operaciones vectorizadas, lo que facilita el análisis de datos y la prueba de estrategias . Esta implementación puede incluir la limpieza de datos, cálculo de retornos, eventos de cruce de medias móviles, y ejecución simulada con control de gestión de riesgos y apalancamiento . El uso de la programación orientada a objetos y la integración con plataformas de comercio como Oanda y FXCM también se recomienda para la conexión y manejo de datos en tiempo real .
Python es ventajoso para el comercio algorítmico debido a su ecosistema de potentes paquetes que facilitan el análisis de datos eficientes, como pandas para la gestión de datos y scikit-learn para aplicar aprendizaje automático . Además, Python permite el manejo de API modernas, lo cual es clave para plataformas de negociación en línea . La capacidad de Python para realizar pruebas retrospectivas de estrategias comerciales con un código más conciso y comprensible es otro beneficio significativo . Su amplia adopción en la industria financiera asegura una gran demanda de profesionales que dominan este lenguaje .
La regresión lineal se utiliza en la predicción de movimientos de mercado para extrapolar tendencias y prever la dirección futura de los precios basándose en datos pasados . Este método es parte de una estrategia más amplia basada en la idea de que los patrones históricos pueden predecir los movimientos futuros, asumiendo que el mercado no es totalmente eficiente . Sin embargo, presenta limitaciones significativas. Por un lado, la regresión lineal no siempre maneja adecuadamente la naturaleza secuencial y temporal de los datos financieros, ya que los modelos tradicionales no consideran la importancia del orden de los datos . Además, si bien puede predecir la dirección de los movimientos, no es eficaz para estimar la magnitud de los cambios . Por último, en muchos casos, simplemente predice que los precios de hoy son los mejores indicadores para los de mañana, lo que refleja la hipótesis de un paseo aleatorio en los precios .
La gestión del riesgo en el contexto de la automatización de operaciones comerciales algorítmicas se aborda a través de varios métodos y herramientas. Uno de los métodos comunes es el cálculo del Value at Risk (VaR), que estima la pérdida máxima esperada en un horizonte temporal particular y bajo un cierto nivel de confianza . Además, el backtesting riguroso de estrategias comerciales es fundamental para evaluar las características de rendimiento y riesgo antes de implementarlas en un entorno de producción . Por ejemplo, el backtesting vectorizado permite una representación concisa del código y un buen rendimiento, mientras que el backtesting basado en eventos proporciona un modelado más granular y realista de las características del mercado . También se menciona la importancia del criterio de Kelly para la asignación de capital, lo que ayuda a dimensionar adecuadamente las operaciones según el riesgo percibido . Estos enfoques se combinan con el uso de tecnologías como Python, que facilitan el manejo de datos financieros y API modernas para la interacción con plataformas de comercio algorítmico .
Para aquellos sin experiencia en Python pero interesados en el comercio algorítmico, se recomienda comenzar por consultar textos introductorios de Python que sean más básicos, como los de Hilpisch (2018), McKinney (2017) y VanderPlas (2016). Estos libros ofrecen una base sólida en Python para el análisis de datos y las finanzas. Es importante desarrollar habilidades en el manejo de paquetes como NumPy y pandas, que son esenciales para el tradeo algorítmico debido a sus capacidades de análisis de datos y manejo de series temporales . Además, aprender sobre automatización y backtesting es esencial. El comercio algorítmico requiere una infraestructura de Python y el uso de plataformas comerciales y sus APIs para realizar órdenes, lo cual se destaca en los materiales disponibles .
Para manejar la falta de datos completos en la implementación de estrategias basadas en machine learning, es recomendable utilizar técnicas de vectorización y programación de arrays para optimizar el uso de los datos disponibles. La vectorización permite procesar los datos de manera eficiente, reduciendo el tiempo y recursos necesarios para el análisis . Además, es crucial considerar la validación rigurosa de los modelos para evitar problemas como el sobreajuste y asegurar la robustez del modelo a pesar de la falta de datos. Se sugiere utilizar enfoques de validación cruzada y ajuste de parámetros para evaluar la efectividad de las estrategias, incluso cuando los datos son incompletos . Otro enfoque es emplear métodos de imputación para completar los datos faltantes, utilizando modelos predictivos para estimar los valores faltantes basándose en patrones del conjunto de datos disponible .
El módulo 'tpqoa' es crucial para la implementación de estrategias comerciales automatizadas en la plataforma Oanda. Sirve como un paquete contenedor que facilita el acceso y el manejo de la API de Oanda, permitiendo la ejecución de estrategias algorítmicas en tiempo real. Además, el módulo tpqoa se integra con el paquete v20 de Oanda, lo cual optimiza el proceso de conexión y operación con la plataforma . También es una herramienta esencial para desarrollar aplicaciones con Python que permiten interactuar con plataformas como Oanda y ofrecer capacidades tales como la recuperación de datos de cuenta y la interacción en tiempo real para el comercio con API .
La vectorización en las pruebas retrospectivas de estrategias comerciales se implementa utilizando paquetes como NumPy y pandas, que permiten la programación de matrices para generalizar operaciones en vectores y matrices . Esto facilita la exploración ágil e interactiva de estrategias, permitiendo probar una gran variedad de combinaciones de parámetros en un corto período de tiempo . La vectorización es adecuada para estrategias comerciales simples, como las basadas en promedios móviles simples, impulso y reversión a la media . A través de la vectorización, se pueden obtener resultados con solo unas pocas líneas de código Python, permitiendo también generar visualizaciones atractivas y reveladoras .