A todos aquellos que usan sistemas de trading la historia que
actualmente les voy a contar les será bastante familiar.
Cuando comenzamos a interesarnos por el mundo de los sistemas,
comenzamos a programar nuestras primeras ideas, a probarlas, optimizarlas,
analizarlas, .... empezamos a descubrir un mundo totalmente nuevo
para nosotros y realmente, en un principio, nos encontramos como
bebes en pañales, con un sinfín de cosas por aprender.
Lo
más común es comenzar a probar sistemas de libre
distribución, del tipo de los que se ofrecen en la página
de Visual Chart Group o de los que algunos amables traders ponen
a nuestra disposición. Tras unas pocas horas de optimización
hemos conseguido el sistema millonario que nos retirará
de trabajar para el resto de la vida. Ya no tendremos que seguir
soportando a ese plasta de jefe y podré llevar a los niños
a Disneyworld. Las estadísticas de nuestra optimización
lo dicen y ese sistema es una máquina de hacer dinero.
Tras
convencer a nuestra familia para comenzar a operar con “la
máquina de hacer dinero”, empezamos no sin miedo,
a utilizar las señales de compra/venta que el sistema genera
y al cabo del tiempo (normalmente poco tiempo), nos damos cuenta
de que nuestros resultados no se parecen en nada a lo que había
conseguido tras mi optimización, las rachas de pérdidas
son extraordinariamente más grandes que las esperadas y
nos envuelve un sentimiento de frustración que acaba por
hacernos parar la operativa para recapacitar sobre lo que hemos
hecho mal y buscar soluciones.
Esta
historia tan típica y que todos hemos sufrido en nuestros
comienzos tiene fácil explicación: simplemente habíamos
comenzado a operar con un sistema sobreoptimizado, que se había
ajustado a la curva de precios histórica, dando unos resultados
muy buenos en el pasado, pero con poco valor predictivo futuro.
Para evitar este tipo de sobreoptimizaciones, ahorrándonos
así varios disgustos, lo más conveniente es aplicar
el análisis en prueba externa a nuestros sistemas. Veamos
en que consiste.
Supongamos
que hoy es día 31 de diciembre de 2000 y tenemos un sistema
muy bueno con el que queremos comenzar a operar al día
siguiente. El proceso habitual es optimizar nuestro sistema en
un conjunto de datos históricos y aplicar el conjunto de
parámetros óptimo a la operativa en mercado real
a partir del 1 de enero de 2001.
De esta manera podríamos saber como se hubiera comportado
nuestro sistema en un conjunto de datos en los que el sistema
no ha sido optimizado, o lo que es lo mismo, en un conjunto de
datos externos a la optimización. Por lo tanto, en el análisis
de prueba externa lo que hacemos es optimizar el sistema en un
conjunto de datos históricos determinados y aplicar los
parámetros optimizados a otros conjuntos de datos que no
habían sido tenidos en cuenta en nuestra optimización
inicial.
La
idea es dividir nuestro conjunto de datos históricos en
diversos “trozos” de datos, optimizando en cada uno
de los trozos y aplicando los parámetros óptimos
al trozo siguiente. Veámoslo con un ejemplo. Imaginemos
que tenemos un conjunto de datos que va desde el 1/1/1998 al 15/5/2003
y queremos analizar la validez de un determinado sistema de trading.
Para ello tenemos dos opciones:
1. Tomar el conjunto de datos entero, optimizarlo y comenzar a
operar con los parámetros optimizados a partir del día
16/5/2003. Esta manera, que es la que habitualmente se suele utilizar,
es totalmente errónea, ya que no nos informa de la bondad
del sistema en ningún momento. Simplemente nos dice que
ha sido posible obtener unos buenos resultados con nuestro sistema
en dicho periodo, pero no sabemos nada acerca del carácter
predictivo del sistema, ya que no hemos aplicado el sistema a
datos externos.
2. Dividir el conjunto de datos en diversos periodos. Por ejemplo,
de la siguiente manera:
Periodo
Optimizado |
Parámetros
aplicados al periodo |
1/1/1998
a 31/12/1998 |
1/1/1999
a 31/3/1999 |
1/4/1998
a 31/3/1999 |
1/4/1999
a 30/6/1999 |
1/7/1998
a 30/6/1999 |
1/7/1999
a 30/9/1999 |
... |
... |
1/4/2002
a 31/3/2003 |
1/4/2003
a 30/6/2003 |
En la tabla anterior hemos dividido nuestro conjunto de datos en
periodos de 12 meses, aplicando los parámetros optimizados
a los 3 meses siguientes, obteniendo de esta manera nuestra secuencia
de operaciones en prueba externa. Es importante por lo tanto, seleccionar
el periodo hacia atrás (el periodo que vamos a optimizar)
y el periodo hacia delante (el periodo “futuro” al que
vamos a aplicar los parámetros optimizados). De esta manera
podemos saber que hubiera ocurrido en nuestra cuenta si hubiésemos
comenzado a operar el 1/1/1999, por ejemplo, con los parámetros
optimizados hasta el 31/12/1998.
De esta manera obtendremos una secuencia de operaciones fruto de
nuestro análisis de prueba externa, siendo nuestras estadísticas
mucho más reales que las obtenidas por la mera optimización
del conjunto de datos global.
Además, con este tipo de prueba evitamos la sobreoptimización
del sistema y el ajuste a la curva de precios, porque la secuencia
de operaciones que hemos conseguido no es fruto de un conjunto de
datos optimizado. Posteriormente, sería conveniente aplicar
otro tipo de análisis a la secuencia de operaciones externas,
para poder determinar nuestro capital necesario, etc... No obstante,
esto será tratado en futuros boletines.
Si bien el hecho de que un sistema no haya superado los análisis
de prueba externa no es motivo suficiente para decir que ese sistema
no es bueno, si es cierto que con los análisis de prueba
externa conseguimos una imagen más real de lo que podemos
esperar de nuestro sistema, y sobre todo evitamos que nuestro
sistema se haya simplemente ajustado a la curva de precios histórica
como consecuencia de una sobreoptimización.
Aunque el proceso de realización de la prueba externa puede
llegar a convertirse en algo bastante tedioso, es recomendable
que realicemos este tipo de pruebas a nuestros sistemas antes
de comenzar a operar con ellos y estimar posteriormente los capitales
necesarios para comenzar en mercado real con nuestra operativa.
Espero
que les haya servido de ayuda este artículo. Para cualquier
duda pueden ponerse en contacto conmigo en chaptrader@yahoo.es
| ::
Stop de Pérdidas y Objetivo ::. |
Para
explicar la siguiente rutina vamos a utilizar un sistema muy simple,
de cruce de medias. Para ello, vemos inicialmente el código
vba del sistema original.
Como
sabemos, el sistema en vba se divide en 3 partes fundamentales:
1. La definición de parámetros y variables
2. La asignación de valores iniciales a las variables
3. Los cálculos que el sistema realizará
en cada nueva barra de datos
| ::
La definición de parámetros y variables ::.
|
Copiamos
a continuación el código vba de nuestro sistema
sin stops de pérdidas ni objetivos:
Código
vba 1 |
'¡¡
Parameters
Dim PeriodoMedia1 As Long '30
Dim PeriodoMedia2 As Long '100
Dim FinDia As Integer '0
'Parameters
!!
Dim
MediaMovil1 As Long
Dim MediaMovil2 As Long
Dim HoraCierre As Long
Option Explicit
Public APP As SysUserApp
Implements System
|
Como
podemos observar hemos definido tres parámetros:
- PeriodoMedia1, que será el número de barras o
periodos que utilicemos para calcular la media móvil corta.
-
PeriodoMedia2, que será el número de barras o periodos
que utilicemos para calcular la media móvil larga.
-
FinDia, que nos servirá para determinar si queremos hacer
intradía o continuo el sistema. Si le asignamos el valor
0, el sistema será continuo, es decir, dejará las
posiciones abiertas a fin de día, mientras que si es distinto
de 0 el sistema será intradía.
Además,
definimos dos variables a las que asignaremos posteriormente el
valor de cada una de las dos medias móviles, cuyos cruces
generarán nuestras señales de compra/venta. Y otra
variable auxiliar (HoraCierre) que utilizamos para realizar cálculos
intradía por si decidimos cerrar nuestras posiciones a
fin de día
A
continuación, pasamos a asignar los valores iniciales a
las variables.
| ::
La asignación de valores iniciales a las variables
(OnInitCalculate) ::. |
En
esta parte del código tenemos que asignar los valores iniciales
de las variables, o en caso de utilizar algún indicador,
como es nuestro caso, especificar que indicador es. Así
pues, nosotros asignaremos a las dos variables comentadas con
anterioridad el cálculo de una media móvil. Veamos
el código vba:
Código
vba 2 |
'Public
Sub System_OnInitCalculate()
With APP
MediaMovil1 = .GetIndicatorIdentifier(AvSimple, Data,
PeriodoMedia1, PriceClose)
MediaMovil2 = .GetIndicatorIdentifier(AvSimple, Data,
PeriodoMedia2, PriceClose)
HoraCierre = -1
.StartBar
= PeriodoMedia2
End
With
End Sub
|
Mediante
el proceso .GetIndicatorIdentifier le decimos a nuestro sistema
que a la variable MediaMovil1 le asigne el valor de un indicador,
que en este caso será una media móvil simple (AvSimple),
de nuestra fuente de datos principal (Data), utilizando para el
cálculo de la misma un número determinado de barras
o periodos (que viene expresado mediante el parámetro PeriodoMedia1)
y tomando los precios de cierre (PriceClose) de cada barra para
realizar el citado cálculo.
De la misma manera, asignamos el valor a la variable MediaMovil2.
Además,
asignamos al proceso .StartBar el valor del parámetro PeriodoMedia2
(el número de barras para el cálculo de la media
móvil larga). De esta manera, el sistema no podrá
realizar ningún cálculo adicional hasta que no hayan
transcurrido las barras suficientes para poder realizar correctamente
el cálculo de nuestros indicadores.
A
continuación pasamos a especificar los cálculos
que se realizarán en cada barra, indicando las señales
de compra/venta, etc...
| ::
Cálculos que el sistema realizará en cada nueva
barra de datos (OnCalculateBar) ::. |
Una
vez que hemos asignado los valores iniciales a nuestras variables
es necesario que especifiquemos la lógica del sistema que
determinará las entradas y salidas que realizamos. En nuestro
caso, queremos desarrollar un sistema con cruce de medias, que
seguirá la siguiente lógica:
-
comprar cuando la media móvil corta cruce hacia arriba
a la media móvil larga
-
vender cuando la media móvil corta cruce hacia abajo a
la media móvil larga
Esto se puede expresar de la siguiente manera, en el código
vba:
Código
vba 3 |
'If
HoraCierre <> -1 Then
If .Time < HoraCierre Then
If .Date <> .Date(1) Then
HoraCierre = .Time(1)
End If
If
.GetIndicatorValue(MediaMovil1) >= .GetIndicatorValue(MediaMovil2)
And .GetIndicatorValue(MediaMovil1, 1, 1) < .GetIndicatorValue(MediaMovil2,
1, 1) Then
.Buy AtMarket
End If
If
.GetIndicatorValue(MediaMovil1) <= .GetIndicatorValue(MediaMovil2)
And .GetIndicatorValue(MediaMovil, 1, 1) > .GetIndicatorValue(MediaMovil2,
1, 1) Then
.Sell AtMarket
End If
Else
If FinDia <> 0 Then
.ExitLong AtMarket
.ExitShort AtMarket
End If
End
If
Else
If .Date <> .Date(1) Then
HoraCierre = .Time(1)
End If
End
If
|
Con
esto tendríamos terminado nuestro sistema de cruce de medias,
que operaría según la lógica comentada con
anterioridad. Ahora bien, en ocasiones podemos estar interesados
en analizar el efecto de incorporar un stop de pérdidas
y un objetivo a nuestro sistema. Veamos como hacerlo.
Lo primero que necesitamos es definir dos nuevos parámetros
o variables, según queramos que nuestros stop de pérdidas
y objetivo sean fijos o variables. En este caso vamos a utilizar
stops fijos, por lo que definiremos dos nuevos parámetros.
Veamos el código completo al incorporarle los dos nuevos
parámetros:
Código
vba 4 |
''¡¡
Parameters
Dim PeriodoMedia1 As Long '30
Dim PeriodoMedia2 As Long '100
Dim FinDia As Integer '0
Dim
StopPerdidas As Double '50
Dim Objetivo As Double '50
'Parameters
!!
Dim MediaMovil1 As Long
Dim MediaMovil2 As Long
Dim HoraCierre As Long
Option
Explicit
Public APP As SysUserApp
Implements System
|
El
procedimiento OnInitCalculate lo dejamos tal cual está,
ya que seguimos teniendo las mismas variables y por lo tanto no
tenemos que asignar ningún nuevo valor inicial.
Pasamos por lo tanto a la parte del código que se repetirá
en cada barra. Aquí, tendremos que incorporar nuevas líneas
de códigos para que el sistema actúe teniendo en
cuenta el stop de pérdidas y el objetivo utilizados.
Para
ello, hemos de diferenciar entre posiciones largas o compradoras
y posiciones cortas o vendedoras. Veamos, como lo hacemos en el
código vba:
| Código
vba 5 |
Public
Sub System_OnCalculateBar(ByVal Bar As Long)
With APP
If HoraCierre <> -1 Then
If .Time < HoraCierre Then
If
.Date <> .Date(1) Then
HoraCierre = .Time(1)
End If
If
.GetIndicatorValue(MediaMovil1) >= .GetIndicatorValue(MediaMovil2)
And .GetIndicatorValue(MediaMovil1, 1, 1) < .GetIndicatorValue(MediaMovil2,
1, 1) Then
.Buy AtMarket
End If
If
.GetIndicatorValue(MediaMovil1) <= .GetIndicatorValue(MediaMovil2)
And .GetIndicatorValue(MediaMovil1, 1, 1) > .GetIndicatorValue(MediaMovil2,
1, 1) Then
.Sell AtMarket
End If
If
.GetMarketPosition = 1 Then 'Cuando estamos largos o comprados
.ExitLong AtStop, 1, .GetEntryPrice - StopPerdidas 'Fijamos
el stop de pérdidas
.ExitLong AtLimit, 1, .GetEntryPrice + Objetivo 'Fijamos
el objetivo de ganancias
End If
If
.GetMarketPosition = -1 Then 'Cuando estamos cortos o
vendidos
.ExitShort AtStop, 1, .GetEntryPrice + StopPerdidas 'Fijamos
el stop de pérdidas
.ExitShort AtLimit, 1, .GetEntryPrice - Objetivo 'Fijamos
el objetivo de ganancias
End If
Else
If
FinDia <> 0 Then
.ExitLong AtMarket
.ExitShort AtMarket
End If
End
If
Else
If
.Date <> .Date(1) Then
HoraCierre = .Time(1)
End If
End
If
End With
End Sub
|
En
rojo hemos resaltado la parte de código en la que incorporamos
el stop de pérdidas y el objetivo. Para ello utilizamos
la función .GetEntryPrice, que nos devuelve el precio de
entrada del negocio en vigor, sumando o restándole el stop
de pérdidas y el objetivo que definimos como parámetros,
dependiendo de que la posición sea larga o corta.
Utilizamos una orden AtStop o AtLimit, dependiendo de la posición
en la que se encuentre el precio actualmente respecto al stop
de pérdidas o el objetivo. Así pues, tendremos:
-
Si estamos comprados, estableceremos una orden en stop (AtStop)
si el precio llega al stop de pérdidas. Como el precio
actual es superior al precio de nuestro stop de pérdidas,
utilizamos una orden en stop.
-
Si estamos comprados, estableceremos una orden limitada (AtLimit)
si el precio llega al objetivo. Como el precio actual es inferior
al precio de nuestro objetivo, utilizamos una orden limitada.
En
el caso de que nos encontráramos en posición vendedora,
actuaríamos al contrario, es decir:
-
Si estamos vendidos, estableceremos una orden en stop (AtStop)
si el precio llega al stop de pérdidas. Como el precio
actual es inferior al precio de nuestro stop de pérdidas,
utilizamos una orden en stop.
-
Si estamos vendidos, estableceremos una orden limitada (AtLimit)
si el precio llega al objetivo. Como el precio actual es superior
al precio de nuestro objetivo, utilizamos una orden limitada.
En esta lección simplemente hemos querido mostrarles como
pueden incorporar a su sistema un stop de pérdidas y un
objetivo, sin entrar en valoraciones de la bondad o no del sistema
utilizado. Así mismo, es posible utilizar datos porcentuales
en lugar de absolutos tanto para el stop de pérdidas como
para el objetivo, aunque en este caso hemos preferido utilizar
datos absolutos, dejando otras opciones posibles a su discreción,
con la intención de que puedan practicar modificando el
presente código y desarrollando el suyo propio.
Esperamos
que esta lección les haya ayudado a aprender como incorporar
stops de pérdidas y ganancias a nuestro sistema en Visual
Chart, utilizando el lenguaje de programación VBA
Autor:
Equipo de Desarrollo de Sistemas deAutoTrading[bot]