Archive for enero 2023

h1

Bot de trading algorítmico para forex

21/01/2023

En este artículo plantearemos como crear un bot de trading en ProRealTime (manual) para la divisa DKK/EUR. Cabe recordar que la curva de esta moneda es practicamente plana debido a la alta correlación entre ambas economías.

El propósito de este bot consta de «cazar» esos movimientos abruptos del mercado marcados en el gráfico, ya que a corto plazo la divisa siempre retorna a valores previos (regresión a la media).

Para ello, el robot fijará desde el momento inicial (t0) dos órdenes de compra limitadas en extremos, a una distancia «distanciaOPT» de la media. En cada T recalibrará estos extremos a los que entrar en mercado. Utilizará una EMA para asumir cuál es la media de esa curva.

El código consta de 50 líneas, más otras 50 aprox. de comentarios para facilitar su legibilidad:

DEFPARAM CumulateOrders = True // Acumulación de posiciones false, desactivada: Si ya ha abierto posición, que no abra más hasta que no cierre ésta.
DEFPARAM PRELOADBARS =  301// to-do: que coincida con el valor de EMAA calibrado. Cargar 301 barras antes, para que las medias tengan sentido desde el primer momento.

// Bot que "caza" submarinos, esas spikes que hay en determinadas gráficas planas como la del EUR/DKK, y que tras la subida/caída brutal vuelve a la media. Al iniciarse el bot, y cada minuto, se pone tanto una orden limitada de compra como una de venta a niveles inusuales, fuera del rango habitual, en extremos. Estos niveles se pueden calibrar con backtesting (variable "distanciaOPT"). Para entender cuál es la media, se utiliza una EMA en cada momento, y en cada minuto se actualizan las órdenes limitadas, de modo que se evita caer en falsos positivos. Se sale del mercado con un trailing stop cuando se regresa a la EMA (lo cual suele ser a los pocos minutos).

//En TODO momento este bot tiene puestas dos ordenes limitadas (una de compra y otra de venta), por lo que para arrancarlo necesitará tener margin en el broken para abrir DOS operaciones al menos.

//Según el broker (IG), la distancia entre el precio de compra y el stop ha de ser superior a 35p.

//Futuros desarrollos:
//1) correr dos bots como éste, uno calibrado para que entre cuando detecte un 0.30% d diferera cuando detect un 0.50%
//2) aprovechar el CumulateOrders

//**********************************************************************************
saldoInicialDKK = 7400 //en moneda DKK. Esta cifra debe cuadrar con la caja "Capital inicial" del backtesting.
distanciaOPT = 0.002 // Distancia de la media a la que fijaremos las ordenes limitadas de compra (lejos de la media).
EMA = 300  //Valor para calcular cuál es la media de la curva. Calibrable.  
StartPerCentOPT = 0.0001 //Valor para inicial el trailing stop.
StepPerCentOPT = 0.007  //Valor para subir el trailing stop.
sizeLot = 10000 //Por definición del mercado, de la industria. // 1 minilote = 10.000 * 7 dkk = 70.000 dkk = 10.000€. Operamos en un gráfico "mini DKK/EUR".
//Lote estándar: un lote estándar contiene 100.000 unidades de la divisa base.
//Mini-lote. Un mini lote son 10.000 unidades de la divisa base.
//Micro-lote. Un micro lote son 1.000 unidades de la divisa base.

maximaCaida = 0.039 //Para evitar riesgos y que el broker ejecute las garantías por un eventual movto. del mercado (cisne negro), ésta es la volatilidad máxima histórica observada del EUY/DKK, en este caso el 22/6/18. Esto es lo máximo que se puede mover nuestra posición en 1 vela aprox. 

//**********************************************************************************

//Para calcular el apalancamiento máximo que puede hacerse sin riesgo, hay que conocer las garantías que pide el broker.
// éstas se ven en la web de IG, en la ficha de cada activo, https://deal.ig.com/wtp/#/workspace/NWUyY2E5YWE4YTdmMzg3YmFlNjk5NTI3
//[Franja] [Tamaño de la posición (Contracts)] [Depósito]
//     1                  0 - 30                   5%
//     2                  30 - 60                 10%
//     3                  60 - 240                15%
//     4                  240 +                   50%
margenTramo1 = 0.05
margenTramo2 = 0.10
margenTramo3 = 0.15
margenTramo4 = 0.50
// Esto significa que cuanto mayor sea el tamaño de la posicion, más depósito (garantías) se deben poner.

// ************************ CÁLCULOS ****************************
//Cálculo de precio al que entramos al mercado.
EMA  = ExponentialAverage[EMA](Close) //Conocer el precio habitual del activo.

precioLimitadaLargos = EMA * (1 - distanciaOPT) //Calcular a qué precio entraríamos si hubiese una spike bajista
precioLimitadaCortos = EMA * (1 + distanciaOPT) //Calcular a qué precio entraríamos si hubiese una spike alcista

// ************************ TAMAÑO DE POSICIÓN (NÚMERO DE LOTES) ****************************
// Calcular cuántos lotes podemos comprar con el saldo que tenemos, dejando margen de sobra para que el broker NO nos pueda barrer
// practicamente nunca.
//1º calcular cuántas posiciones como máximo podríamos abrir en base a las garantías que nos pide el broker.
capitalDKK = saldoInicialDKK + STRATEGYPROFIT  //el capital con el que empezamos, más lo que hayamos ganado o perdido.
garantiaRequeridaPorLoteEnDKK = 1 * sizeLot * precioLimitadaLargos * margenTramo1 // cuánta garantía nos exige un sólo lote.
maxNumLotesByInitialMargin = capitalDKK / garantiaRequeridaPorLoteEnDKK

//2º calcular cuántas posiciones como máximo podemos abrir aguantándolas vivas si el mercado cae hasta mínimos históricos
maxNumLotesByMaintenanceMargin = capitalDKK / (precioLimitadaLargos * sizeLot * maximaCaida  )
numLotes = floor(min(maxNumLotesByInitialMargin, maxNumLotesByMaintenanceMargin))


// ************************ DECISIONES ****************************
//según la pág 14. del manual de ProRealTime, las órdenes limitadas solo duran una vela, así que hay que reformularlas en cada vela.

IF NOT LONGONMARKET THEN
BUY numLotes LOT ROUNDEDDOWN AT precioLimitadaLargos LIMIT
ENDIF
IF NOT SHORTONMARKET THEN
SELLSHORT numLotes LOT ROUNDEDDOWN AT precioLimitadaCortos LIMIT
ENDIF

// ************************ TRAILING STOP FUNCTION ****************************
//Gestión de TS tomada de Prorealcode
StartPerCent  = StartPerCentOPT    //0.5 Trailing Stop will activate after 0.5% gain
StepPerCent   = StepPerCentOPT    //0.1 Trailing Steps will occur at any further 0.1% gain
trailingstart = PositionPrice * StartPerCent / 100 / PipSize
trailingstep  = PositionPrice * StepPerCent  / 100 / PipSize

IF NOT ONMARKET THEN //reset the stoploss value
newSL=0
ENDIF
 
//POSICIONES LARGAS **********
IF LONGONMARKET THEN
//first move (breakeven)
IF newSL=0 AND close-PositionPrice>=trailingstart*pipsize THEN
newSL = EMA // PositionPrice+trailingstep*pipsize //Funcionamiento habitual del TS en largos
ENDIF
//next moves
IF newSL>0 AND close-newSL>=trailingstep*pipsize THEN
newSL = max(newSL,newSL+trailingstep*pipsize)
ENDIF
ENDIF
 
//POSICIONES CORTAS **********
IF SHORTONMARKET THEN
//first move (breakeven)
IF newSL=0 AND PositionPrice-close>=trailingstart*pipsize THEN
newSL = EMA // PositionPrice-trailingstep*pipsize // Funcionamiento habtiaul del TS en cortos
ENDIF
//next moves
IF newSL>0 AND newSL-close>=trailingstep*pipsize THEN
newSL = min(newSL,newSL-trailingstep*pipsize)
ENDIF
ENDIF
 
//stop order to exit the positions
IF newSL>0 THEN
SELL AT newSL STOP
EXITSHORT AT newSL STOP
ENDIF



// ************************************ DEBUG *****************************************************************


// Para medir el riesgo que llevamos, ver cuántas garantías nos quedan antes de que nos barran.
//NoStopMargin = (CountOfPosition * sizeLot * low) * (margenTramo1)
//InitialMargin = Round(NoStopMargin * 100) / 100
//MaintenanceMargin =   (CountOfPosition * sizeLot * PositionPrice) - (CountOfPosition * sizeLot * low) //to-do: hacerlo tanto para cortos com para largos.

//GRAPH capitalDKK coloured (255,128,0) as "capitalDKK"
//GRAPH MaintenanceMargin coloured (255,128,0) as "MaintenanceMargin"
//GRAPH CountOfPosition coloured (128,128,128) as "CountOfPosition"
//GRAPH sizeLot coloured (128,0,0) as "sizeLot"
//GRAPH PositionPrice coloured (255,128,0) as "PositionPrice"
//GRAPH low coloured (255,128,0) as "low"su
//GRAPH numLotes coloured (255,128,0) as "numLotes"

//if newSL <> 0 THEN
//GRAPH newSL coloured (64,64,0) as "newSL"
//ENDIF

Como se indica en el código, para ponerlo en producción hay que tener suficiente margen (garantías) en la cuenta para poder abrir al menos 2 lotes, pues el bot desde t0 fija tanto una orden de compra en largo como otra en corto:

Nótese en la imagen las dos bandas que recorren el gráfico por encima y por debajo del precio. Son las dos ordenes de compra que están fijadas en T0, y que se actualizan en cada vela en función de las n-velas anteriores.

Las distintas variables se pueden calibrar realizando un backtesting o simulación del comportamiento del bot con datos pasados del mercado:

Tras hacer un backtesting, se puede estudiar los siguientes resultados, en función de las variables anteriormente tanteadas:

Se puede analizar en rendimiento de la estrategia en función de cada variable individual:

así como mostrar superficies para valorar la correlación entre dos variables. En el siguiente ejemplo vemos la ganancia que se obtiene mediante las distintas combinaciones de EMAs y distancias (en %) a fijar las ordenes limitadas de compra:

Con estas superficies es fácil estimar unas variables que faciliten que un movimiento del mercado que altere las variables, permita seguir situándonos en zonas de beneficios por el momento.

Eligiendo cualquier combinación de variables se puede ver en detalles los resultados financieros del backtesting:

E incluso se puede ver gráficamente las operaciones (entradas y salidas al mercado) que se realizan, así como el balance de la cuenta de trading:

ProRealTime es una herramienta potente para desarrollar bots de trading algorítmico. Dispone también de la ventaja de correr en una plataforma mantenida por el broker. Otra herramienta alternativa es MetaTrader4, que desarrollaremos en siguientes artículos.