martes, 5 de noviembre de 2019

SISTEMA DE BANDA PROHIBIDA CON AUTOTRADING Y MM

En las entradas anteriores de este blog hemos ido preparando el terreno para poder llegar a un sistema completo de trading que tenga un dimensionamiento basado en un objetivo de volatilidad y también tenga la posibilidad de ser ejecutado de forma completamente automática. Por fin estamos en condiciones de poder compartir un sistema COMPLETO de trading consistente en:

Lógica del sistema + Gestión de Capital + Algoritmo de ejecución con IBController. 

Es el sistema de Banda Prohibida. Un sistema de Banda Prohibida pone unas bandas alrededor del precio y solo opera cuando el precio está por fuera. Tiene características de reversión a la media y se llama así (banda prohibida) porque todas las operaciones se hacen por fuera de estas bandas, tanto las entradas como las salidas. Veamos un ejemplo:


Hay una banda rápida (azul) que consiste en una media de 20 sesiones y 2 ATRs por encima y por debajo. Hay una banda lenta (roja) que consiste en una media de 100 sesiones y 2 ATRs por encima y por debajo.  En esta imagen se puede ver cómo la tendencia alcista la define el hecho de que la banda rápida esté por encima de la lenta. Si estuviera por debajo asumiríamos que la tendencia es bajista.
Una vez alcistas el sistema compra cuando el cierre termina por debajo de la banda rápida. Y cierra los largos cuando supere la banda rápida.

A continuación vemos los resultados de aplicarlo a una cartera de materias primas con los siguientes tickers: AD, BP, GF, HE, JY, NQ, RTY, SM, TY, YM, QHG y NG. Como vemos el sistema parece ir bien con divisas, carnes, algunos granos (harina de soja), metales (cobre) y energías (gas natural).




El código que se muestra más abajo incluye una GESTIÓN DE CAPITAL de forma que las posiciones se dimensionan para una volatilidad anualizada en función del capital disponible (modelo de Carver explicado en entradas anteriores). Pero este capital "disponible" se reduce en un 10% por cada 5% de drawdown real. Es la estrategia de gestión de capital que utilizaban las tortugas de Dennis.

Al final del código se incluye LA PARTE DE AUTOTRADING que consiste en hacer la operativa a mercado en función de las señales del sistema. No obstante está mejorado y tras abrir una posición sitúa inmediatamente el Stop Loss con una orden Bracket. Asimismo comprueba que no se supere el máximo de 4 posiciones simultáneas abiertas. Recuerde que la ejecución automática tiene sus riesgo y "automático" no significa "desatendido".

El <include> con los multiplicadores de los futuros se encuentra en entradas anteriores de este blog:
https://onda4com.blogspot.com/2018/09/los-multiplicadores-de-los-futuros.html




CÓDIGO DEL SISTEMA:

//-------------------------------------------------------     
// SISTEMA DE BANDAS PROHIBIDAS
// OSCAR G. CAGIGAS   
// 5 NOVIEMBRE 2019
//-------------------------------------------------------

//VARIABLES OPTIMIZABLES// 
//--------------------------------------
nstop = 4; 
per = 40;//Optimize("per",40,20,50,5); //periodo lookback la desv estándar
OptimizerSetEngine("spso");
Len1 = Optimize("Len1",100,100,180,10);  //Long Lenta   
Len2 = Optimize("Len2",20,5,25,5); //Long Rapida
k = Optimize("k",2,1,2,0.2); //ancho bandas

//--------------------------------------

//MONEY MANAGEMENT (EJEMPLO CON 25.000 EUROS)
//********************************************************************************************************************
MaxNet = 25000; //EL MÁXIMO VALOR DEL NET ACC VALUE
Eq =  Nz(StaticVarGet("NAV")); //LO LEE DE IB (MAXNET SI NO ESTA CONECTADO)
//********************************************************************************************************************

Eq = MaxNet = 25000; //para pruebas

//CONTROL DE RIESGO. CADA 5% DE DRAWDOWN REDUCIMOS EL CAP OPERATIVO EN UN 10%  
dd = IIf(1 - Eq/MaxNet > 0, 1 - Eq/MaxNet, 0); 
Eq = Eq*(1 - 2*0.05*int(dd/0.05));  //equity operativo (ajustado por riesgo) en euros 

//PARAMETROS DEL FRAMEWORK DE CARVER
tavol = 0.5; //target volatility en ttp1  
idm = 2; //instrument diversification multiplier 
iw = 0.25;  //PESO EN LA CARTERA DE ESTE SISTEMA

//CAPITAL INICIAL Y COMISIONES//  
SetOption( "initialequity", 20000 ); // starting capital     
SetOption("PriceBoundChecking",1);  
SetOption("CommissionMode", 2);  
SetOption("commissionamount",25);  //COMISIÓN INDIVIDUAL (ENTRADA O SALIDA)  
SetOption("WarningLevel", 1 );    
  
//DIMENSIONAMIENTO//   
#include <futuros.afl>   //variables reservadas: MUL, TICK, DEC
SetOption("maxopenpositions",4);  
decim = 1 + dec/10; //decimales correctos
    
//DESVIACION STANDARD EXPONENCIAL    
desv_carver = sqrt( EMA( ( C-Ref(C,-1) )^2, 36 ) ) ;    
  
//----NÚMERO DE FUTUROS EN BASE AL FORECAST ANTERIOR, VOLATILIDAD Y EQUITY----    
dvt = Eq * tavol / 16; //daily volatility target    
ivv = desv_carver * PointValue; //instrument value volatility    
vs = Nz(dvt / ivv);  //volatility scalar    
pos = vs * iw * idm;  //forecast de 10 siempre  
numfut = round(pos);  //el número de contratos    
MarginDeposit = 1; PositionSize = NumFut;    

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

//REDONDEA AL TICK PARA FORMATO REAL DE TRADING
function Redondea( precio )
{
result = int(precio) + round((precio-int(precio))/TickSize)*TickSize;
return result;
}  

//+++++++++++ SISTEMA DE TRADING ++++++

//MOSTRAR INDICADORES// 
pintarmedias = 1;//ParamToggle("Medias", "No|Yes", 0);    

//BANDAS//
BLI = MA(C,Len1)-k*ATR(20); //Banda Lenta Inferior
BLS = MA(C,Len1)+k*ATR(20); //Banda Lenta Superior
BRI = MA(C,Len2)-k*ATR(20); //Banda Rápida Inferior
BRS = MA(C,Len2)+k*ATR(20); //Banda Rápida Superior

//SETUPS//
setupa = C < BRI AND C > BLS;
setupb = C > BRS AND C < BLI;

//ENTRY //   
BarraLarga = abs(C-O) > BBandTop(abs(C-O),per,1); //rango open-close mayor que 1 desv estándar

Buy =  setupa AND BarraLarga;
Short = setupb AND BarraLarga;

//EXIT//
Sell = C > BRS;
Cover = C < BRI;

//REMOVE EXTRA BUY and SHORT SIGNALS//   
Buy=ExRem(Buy,Sell OR Short);    
Short=ExRem(Short,Cover OR Buy);     
Sell=ExRem(Sell,Buy);     
Cover=ExRem(Cover,Short);    

//PANIC STOP// 
STOP = Nz(nstop * desv_carver );  
ApplyStop(stopTypeLoss,stopModePoint, STOP,1,0); 
Equity(1,0); 

//largo o corto//   
largo=Flip(Buy,Sell OR Short);    
corto=Flip(Short,Cover OR Buy);   

//SALIDA GRAFICA//    
color = IIf(setupa, colorLime, IIf(setupb, colorOrange, 0));
Plot(C,"PRICE",color,styleBar|styleThick);    
PlotShapes(IIf(Buy,shapeUpArrow,shapeNone),colorGreen,0,L,-15);    
PlotShapes(IIf(Buy,shapeHollowCircle,shapeNone),colorGreen,0,BuyPrice,0);    
PlotShapes(IIf(Sell AND NOT Corto,shapeDownArrow,shapeNone),colorRed,0,H,-15);    
PlotShapes(IIf(Sell AND NOT Corto,shapeHollowCircle,shapeNone),colorRed,0,SellPrice,0);    
PlotShapes(IIf(Short,shapeDownArrow,shapeNone),colorBrown,0,H,-15);    
PlotShapes(IIf(Short,shapeHollowCircle,shapeNone),colorBrown,0,ShortPrice,0);    
PlotShapes(IIf(Cover AND NOT Largo,shapeUpArrow,shapeNone),colorDarkGreen,0,L,-15);    
PlotShapes(IIf(Cover AND NOT Largo,shapeHollowCircle,shapeNone),colorDarkGreen,0,CoverPrice,0);    
  
//PINTA LAS MEDIAS// 
if (pintarmedias==1)    
{ Plot(BRS,"BRS",colorBlue,styleLine);  
  Plot(BRI,"BRI",colorBlue,styleLine); 
Plot(BLS,"BLS",colorRed,styleLine);  
  Plot(BLI,"BLI",colorRed,styleLine); 
  color1 = ColorBlend( colorRed, colorWhite, 0.95 );
    color2 = ColorBlend( colorBlue, colorWhite, 0.95 );
  PlotOHLC( BRS,BRS, BRI, BRI, "BT", color2, styleCloud | styleNoRescale, Null, Null, Null, -1 );
PlotOHLC( BLS,BLS, BLI, BLI, "BP", color1, styleCloud | styleNoRescale, Null, Null, Null, -1 );
}  
    
//PLOTTEXT//    
dist = 2*ATR(10);     
for( i = 0; i < BarCount; i++ )     
{     
if( Buy[i] ) PlotText( "Long "+NumToStr(ValueWhen(Buy[i],BuyPrice[i]),decim), i, L[ i ]-dist[i], colorGreen );     
if( Sell[i] AND NOT Short[i]) PlotText( "Exit " + NumToStr(ValueWhen(Sell[i],SellPrice[i]),decim), i, H[ i ]+dist[i], colorRed );     
if( Short[i] ) PlotText( "Short "+NumToStr(ValueWhen(Short[i],ShortPrice[i]),decim), i, H[ i ]+dist[i], colorBrown );     
if( Cover[i] AND NOT Buy[i]) PlotText( "Cover " + NumToStr(ValueWhen(Cover[i],CoverPrice[i]),decim), i, L[ i ]-dist[i], colorBlack );     
}   

//CHART// 
stopprice = Redondea(Largo*ValueWhen(Buy,BuyPrice-stop)+Corto*ValueWhen(Short,ShortPrice+stop));
color = IIf(numfut == 0, colorLavender, 0); 
SetChartOptions(0,chartShowDates);     
Plot(C,"PRICE",color,styleBar|styleThick,Null,Null,0,0,3);     
Title = StrFormat("{{NAME}} - {{INTERVAL}} {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%) {{VALUES}}", O, H, L, C, SelectedValue( ROC( C, 1 ) ) )      
+"\n"+ FullName()+"\n"+EncodeColor(colorBlue)+"SIST  BANDA PROHIBIDA + MM + AUTOTRADING"     
+"\n"+EncodeColor(0)+"STDEV = "+WriteVal(desv_carver,1.6)+" Pts ; x"   + WriteVal(PointValue,1.0) + " ; (" + TickSize +")"
+"\n"+EncodeColor(colorRed)+"VOLAT = $"+WriteVal(ivv,1.2)   
+"\n"+ EncodeColor(colorRed)+"SL = "+writeval(stopprice,decim)    
+" ; RISK = "+WriteVal(IIf(largo, C-stopprice,stopprice-c)*PointValue*NumFut,1.0)
+"\n"+EncodeColor(colorGrey40)+"Ref = " + WriteVal(Eq,1.0) + "; DD = " + WriteVal(dd*100,1.2) + "%"  
+"\n"+"daily volat target = " + WriteVal(dvt,1.0)   
+"\n"+"volat scalar = " + WriteVal(vs,1.2)   
+"\n"+"inst weight = " + WriteVal(100*iw,1.0) +" %"  
+"\n"+"ins div mul = " + WriteVal(idm,1.2) 
+"\n"+"pos = " + WriteVal(pos,1.2) 
+"\n"+EncodeColor(colorBlue)+"NumFut = " + WriteVal(numfut,1.0);

//LETRERO CON NOMBRE DEL SISTEMA
GfxSetOverlayMode(1);
GfxSelectFont("Tahoma", Status("pxheight")/16 );
GfxSetTextAlign( 6 );// center alignment
GfxSetTextColor( ColorRGB( 200, 200, 200 ) );
GfxSetBkMode(1); // transparent
GfxTextOut( "NF", Status("pxwidth")/24, Status("pxheight")/2.6 );

//PINTAMOS EL STOP//
stopline_l = IIf(largo OR Buy OR sell, ValueWhen(Buy, Buyprice-stop), Null); //solo largo
stopline = IIf(corto OR short OR cover, ValueWhen(short, ShortPrice+stop), stopline_l); //los dos
Plot(stopline, "SL", colorRed,styleDashed);

//EXPORTAMOS NUM CONTRATOS Y VOLATILIDAD
Filter = Status( "lastbarinrange" );
AddColumn(ivv,"ivv",1.0);
AddColumn(numFut,"num",1.0);

//*********************************************************************************************************
// CODIGO DE AUTOTRADING
// OSCAR G. CAGIGAS
// 5 NOVIEMBRE 2019
// La variable tib es el ticker de ib (viene del include).
// IMPORTANTE: Para autotrading activar los parámetros del fichero, no los del chart (son independientes) 
//**********************************************************************************************************

reset = ParamTrigger("RESET","RESETEAR VARIABLES");
if(reset)
{
StaticVarSetText("EntryID"+Name(), "");
StaticVarSetText("Entry2ID"+Name(), "");
}

//Arranca el interfaz con IB
autotrading = ParamToggle("Auto Trading","No|Yes",0);

if (autotrading)
{
ibc = GetTradingInterface("IB"); 
RequestTimedRefresh( 1 );  //refresca cada 1 seg

//POSICIONES ABIERTAS
PosAb = IIf(ibc.GetPositionList() == "", 0, 1 + StrCount( ibc.GetPositionList(), "," )); 

//CUATRO POSICIONES MÁXIMO
if (PosAb <= 4 AND ibc.IsConnected())
{
// coge el ID de la ejecución anterior (o cero en la primera)
EntryID = StaticVarGetText("EntryID"+Name()); 
ExitID = StaticVarGetText("ExitID"+Name()); 
StopID = StaticVarGetText("StopID"+Name());

//COMPRA A MERCADO, CON STOP LOSS
if( ibc.GetPositionSize( tib ) == 0 AND LastValue(Ref(Buy,-1)) AND EntryID == "" ) 
{
EntryID = ibc.PlaceOrder( tib, "BUY", LastValue(NumFut), "MKT", 0, 0, "DAY", False); //COMPRA A MERCADO
StopID = ibc.PlaceOrder( tib, "SELL", LastValue(NumFut), "STP", 0, LastValue(stopprice), "GTC", True, 1, "outsideRTH", EntryID); //stop loss
PlaySound("C:\\Windows\\Media\\Ring02.wav"); //Si suena es que entró el bracket
StaticVarSetText("EntryID"+Name(), EntryID); //almacenamos el ID de la entrada
StaticVarSetText("StopID"+Name(), StopID); //almacenamos el ID de la entrada
StaticVarSetText("ExitID"+Name(), ""); //RESETEAMOS ExitID
}

//CIERRA LARGOS A MERCADO
if( ibc.GetPositionSize( tib ) > 0 AND LastValue(Ref(Sell,-1)) AND ExitID == "" ) 
{
ExitID = ibc.PlaceOrder( tib, "SELL", ibc.GetPositionSize( tib ), "MKT", 0, 0, "DAY", False); //SALE A MERCADO
ibc.CancelOrder( StopID );
PlaySound("C:\\Windows\\Media\\Ring02.wav"); //Si suena es que entró el bracket
StaticVarSetText("ExitID"+Name(), ExitID); //almacenamos el ID de la entrada
}

//CORTO A MERCADO, CON STOP LOSS
if( ibc.GetPositionSize( tib ) == 0 AND LastValue(Ref(Short,-1)) AND EntryID == "" ) 
{
//BRACKET ORDER (CORTO A MERCADO Y SITÚA STOP Y PROFIT TARGET QUE UNO CANCELA EL OTRO)
EntryID = ibc.PlaceOrder( tib, "SELL", LastValue(NumFut), "MKT", 0, 0, "DAY", False); //CORTO A MERCADO
StopID = ibc.PlaceOrder( tib, "BUY", LastValue(NumFut), "STP", 0, LastValue(stopprice), "GTC", True, 1, "outsideRTH", EntryID); //stop loss
PlaySound("C:\\Windows\\Media\\Ring02.wav"); //Si suena es que entró el bracket
StaticVarSetText("EntryID"+Name(), EntryID); //almacenamos el ID de la entrada
StaticVarSetText("StopID"+Name(), StopID); //almacenamos el ID de la entrada
StaticVarSetText("ExitID"+Name(), ""); //RESETEAMOS ExitID
}

//CIERRA CORTOS A MERCADO
if( ibc.GetPositionSize( tib ) < 0 AND LastValue(Ref(Cover,-1)) AND ExitID == "" ) 
{
ExitID = ibc.PlaceOrder( tib, "BUY", ibc.GetPositionSize( tib ), "MKT", 0, 0, "DAY", False); //SALE A MERCADO
ibc.CancelOrder( StopID );
PlaySound("C:\\Windows\\Media\\Ring02.wav"); //Si suena es que entró el bracket
StaticVarSetText("ExitID"+Name(), ExitID); //almacenamos el ID de la entrada
}

//ACTUALIZAR NAV y MAXNET
NAV = StaticVarSet("NAV",StrToNum(ibc.GetAccountValue( "NetLiquidationByCurrency")));  

//visualizamos las variables en el chart
Title = Title + "\n" + EncodeColor(colorRed) + "---------------"
+ "\n" + "NetAccVal = " + StaticVarGet("NAV") 
+ "\n" + "EntryID = " + EntryID 
+ "\n" + "ExitID = " + ExitID
+ "\n" + "StopID = " + StopID  
+ "\n" + "pos size = " + ibc.GetPositionSize( tib )
+ "\n" + "Pos Abiertas = " + PosAb
+ "\n" + "POSITION LIST = " + ibc.GetPositionList();








3 comentarios:

ENTRADAS POPULARES