La última cruzada
Introducción
Hay por defecto tres formas de presentación del precio de un instrumento disponibles en el terminal de Meta Trader 5 (así como en Meta Trader 4): barras, velas y líneas. Esencialmente, todas ellas representan lo mismo, gráficos de tiempo. Además del método tradicional de presentación del precio con relación al tiempo, hay aún otra forma no relativa al tiempo que es muy popular entre los inversores y especuladores: los gráficos Renko y Kagi, tres saltos de línea y los gráficos de punto y forma.
No voy a comentar sus ventajas respecto a los clásicos pero perder de vista la variable tiempo ayuda a los operadores a centrarse en la variable precio. Recomiendo aquí tener en cuenta los gráficos de punto y forma junto con el respectivo algoritmo de gráfico, echar un vistazo a los conocidos productos del mercado que sirven para generar dichos gráficos y escribir un simple script implementando el algoritmo. El libro de Thomas J. Dorsey "Point and Figure Charting: The Essential Application for Forecasting and Tracking Market Prices" será nuestro libro básico.
Bull's-Eye Broker es el paquete de software más popular para el dibujo de gráficos off-line. El software se encuentra disponible en un periodo de prueba de 21 días (son posibles numerosas pruebas) y la nueva versión Beta se encuentra disponible durante el periodo Beta. El paquete de software se usará para estimar los resultados de nuestro script. Uno de los mejores recursos online en términos de graficado de punto y forma son los StockCharts. Este sitio web está orientado al mercado de valores y por tanto no proporciona precios de instrumentos del mercado Forex.
Para comparar los resultados del script introduciríamos gráficos de futuros Gold, futuros Light Crude Oil y se generaráon S&P 500 CFD usando el software y el sitio web. Se dibujará un gráfico EURUSD usando solo el programa Bull's-Eye Broker (recuerde las limitaciones de StockChart).
Algoritmo para la realización de gráficos de punto y forma
Aquí está el algoritmo.
Los dos parámetros clave en la realización de gráficos de punto y forma son:
- El tamaño del cuadro que es el cambio de precio del instrumento mínimo, donde los cambios menores que el cambio de precio mínimo no afectan al gráfico.
- El inverso, que es el número de cuadros que representan el movimiento del precio en dirección opuesta a la dirección actual del gráfico tras el cual dicho movimiento se mostrará en la nueva columna.
Como la elaboración de gráficos requiere que el historial de cotizaciones se guarde en la forma de precios Abierto-Alto-Bajo-Cerrado, suponemos lo siguiente:
- El gráfico se dibuja en base a los precios Alto-Bajo.
- El precio Alto se redondea hacia abajo hasta el tamaño del cuadro (MathFloor), el precio Bajo se redondea hacia arriba hasta el tamaño del cuadro (MathCeil).
Vamos a ver un ejemplo. Supongamos que queremos dibujar un gráfico de Light Crude Oil con un tamaño de cuadro igual a $1 (uno) y a 3 (tres) cuadro inverso. Esto significa que todos los precios Alto se redondearán hacia abajo hasta el $1 más cercano y todos los precios Bajo se redondearán hacia arriba de la misma forma:
Fecha | Alto | Bajo | XO Alto | XO Bajo |
---|---|---|---|---|
2012.02.13 | 100,86 | 99,08 | 100 | 100 |
2012.02.14 | 101,83 | 100,28 | 101 | 101 |
2012.02.15 | 102,53 | 100,62 | 102 | 101 |
2012.02.16 | 102,68 | 100,84 | 102 | 101 |
2012.02.17 | 103,95 | 102,24 | 103 | 102 |
Las X (cruces) se usan para ilustrar un movimiento del precio hacia arriba en el gráfico, mientras que los 0 (ceros) representan un movimiento de caída de los precios.
Cómo determinar la dirección inicial del precio (si la primera columna es X o 0):
Recuerde que los valores de las [barras -1] X0 Alto y X0 Bajo y espere hasta que:
- el valor X0 Bajo disminuya en el número inverso de cuadros comparado con el X0 Alto inicial (la primera columna es 0); o
- el valor X0 Alto aumente en el número inverso de cuadros comparado con el X0 Bajo inicial (la primera columna es X).
En nuestro ejemplo de Light Crude Oil debemos tener presente que X0 Alto[Barras-1]=100 y X0 Bajo[Barras-1]=100.
Luego esperamos a ver qué ocurre antes:
- El valor X0 Bajo[i] de la siguiente barra es menor o igual a $97 lo que sugiere que la primera columna es 0, o
- El valor X0 Alto[i] de la siguiente barra es mayor o igual a $97 lo que sugiere que la primera columna es X.
Podemos determinar la primera columna del 17 de febrero: El precio X0 Alto ha alcanzado los $103 y la primera columna es X. Lo hacemos dibujando cuatro X desde $100 hasta $103.
Cómo determinar el movimiento del gráfico posterior
Si la columna actual es X, comprobamos si el X0 Alto de la barra actual se ha incrementado en el tamaño del cuadro en comparación con el precio actual X0 (es decir, el 20 de febrero primero comprobamos si X0 Alto es mayor o igual a $104). Si X0 Alto [2012.02.20] es $104 o $105 o mayor, añadiremos el número correspondiente de X a la columna existente de las X.
Si el X0 Alto de la barra actual no se ha incrementado en el tamaño del cuadro en comparación con el precio X0 actual, comprobamos si el X0 Bajo de la barra actual es menor que el X0 Alto en el número inverso de los cuadros (en nuestro ejemplo, si X0 Bajo [2012.02.20] es menor o igual a $103-3*$1=$100, o $99 o menor que esto). Si es menor, entonces dibujamos una columna de 0 a la derecha de la columna de X desde $102 a $100.
En caso de que la columna actual sea 0, todas las consideraciones anteriores se aplicarán a la inversa.
IMPORTANTE: cada nueva columna de 0 siempre se dibuja a la derecha y un cuadrado más abajo que el valor Alto de la columna precedente de X y cada nueva columna X se dibuja siempre a la derecha y un cuadro más alto que el valor Bajo de la columna precedente de 0.
Los principios de la elaboración de gráficos están ahora claros. Vamos ahora con las líneas de apoyo y resistencia.
Las líneas de apoyo y resistencia en los gráficos de punto y forma siempre están inclinadas 45 grados.
La primera línea depende de la primera columna. Si la primera columna es X, la primera línea será una línea de resistencia que comienza un cuadro más alto que el máximo de la primera columna, inclinado 45 grados HACIA ABAJO y hacia la derecha. Si la primera columna es 0, la primera línea será una línea de apoyo que comienza un cuadro más abajo que el mínimo de la primera columna, inclinado 45 grados HACIA ARRIBA y hacia la derecha. Las líneas de apoyo y resistencia se dibujan hasta que alcanzan el gráfico del precio.
En el momento en que las líneas de apoyo/resistencia alcanzan el gráfico del precio, comenzamos a dibujar un línea de apoyo/resistencia en consecuencia. Al dibujar, el principio clave es garantizar que la línea trazada está más a la derecha que la línea de tendencia precedente en el gráfico. De esta forma, para dibujar una línea de apoyo, primero identificamos el valor del gráfico mínimo bajo la línea de resistencia que acabamos de dibujar y trazamos la línea de apoyo comenzando un cuadro más abajo que el mínimo identificado ARRIBA a la derecha hasta que alcanza el gráfico o la última columna del gráfico.
Si la línea de apoyo dibujada comenzando en el mínimo bajo la línea de resistencia precedente va hacia arriba y tropieza con el gráfico bajo la misma línea de resistencia, se mueve a la derecha y encuentra un nuevo mínimo del precio dentro del rango desde el mínimo más bajo la resistencia hasta el final de la línea de resistencia. Continuamos hasta que la línea de tendencia dibujada de esta forma llegue a la derecha, detrás de la línea de tendencia precedente.
Todo lo anterior quedará más claro con los ejemplos gráficos reales que se proporcionan más adelante.
Por ahora ya hemos resuelto el algoritmo de creación de gráficos. Vamos a añadir algunas características nuevas a nuestro scrip:
- Selección del modo: hacer el gráfico para un símbolo actual solo o para todos los símbolos en la Observación del mercado;
- Elección de periodos de tiempo (parece más lógico dibujar gráficos de 100 puntos en periodos de tiempo diarios y gráficas de 1-3 puntos en M1);
- Establecer el tamaño del cuadro en puntos;
- Establecer el número de cuadros para la inversión;
- Establecer el número de caracteres para mostrar los volúmenes (en el script - los volúmenes de tick ya que no me he encontrado con brókeres que suministren volúmenes reales) en filas y columnas (como el indicador de la Profundidad del mercado);
- Establecer la profundidad del historial en base al cual se dibujará el gráfico;
- Elección del formato de salida, los resultados pueden guardarse como archivos de texto o de imagen;
- Y finalmente una característica para los principiantes: autograficado (automáticamente establece el tamaño del cuadro en base a la altura necesaria para el gráfico).
Ahora que se han proporcionado las descripciones y requisitos del algoritmo, es hora de presentar el script.
//+------------------------------------------------------------------+ //| Point&Figure text charts | //| BSD Lic. 2012, Roman Rich | //| http://www.FXRays.info | //+------------------------------------------------------------------+ #property copyright "Roman Rich" #property link "http://www.FXRays.info" #property version "1.00" #property script_show_inputs #include "cIntBMP.mqh" // Include the file containing cIntBMP class input bool mw=true; // All MarketWatch? input ENUM_TIMEFRAMES tf=PERIOD_M1; // Time frame input long box=2; // Box size in pips (0 - auto) enum cChoice{c10=10,c25=25,c50=50,c100=100}; input cChoice count=c50; // Chart height in boxes for autocharting enum rChoice{Two=2,Three,Four,Five,Six,Seven}; input rChoice reverse=Five; // Number of boxes for reversal enum vChoice{v10=10,v25=25,v50=50}; input vChoice vd=v10; // Characters for displaying volumes enum dChoice{Little=15000,Middle=50000,Many=100000,Extremely=1000000}; input dChoice depth=Little; // History depth input bool pic=true; // Image file? input int cellsize=10; // Cell size in pixels //+------------------------------------------------------------------+ //| cIntBMP class descendant | //+------------------------------------------------------------------+ class cIntBMPEx : public cIntBMP { public: void Rectangle(int aX1,int aY1,int aSizeX,int aSizeY,int aColor); void Bar(int aX1,int aY1,int aSizeX,int aSizeY,int aColor); void LineH(int aX1,int aY1,int aSizeX,int aColor); void LineV(int aX1,int aY1,int aSizeY,int aColor); void DrawBar(int aX1,int aY1,int aX2,int aY2,int aColor); void TypeTextV(int aX,int aY,string aText,int aColor); }; cIntBMPEx bmp; // cIntBMPEx class instance uchar Mask_O[192]= // The naughts { 217,210,241,111,87,201,124,102,206,165,150,221,237,234,248,255,255,255,255,255,255,255,255,255, 73,42,187,137,117,211,201,192,235,140,120,212,60,27,182,178,165,226,255,255,255,255,255,255, 40,3,174,250,249,253,255,255,255,255,255,255,229,225,245,83,54,190,152,135,216,255,255,255, 68,36,185,229,225,245,255,255,255,255,255,255,255,255,255,247,246,252,78,48,188,201,192,235, 140,120,212,145,126,214,255,255,255,255,255,255,255,255,255,255,255,255,188,177,230,124,102,206, 237,234,248,58,24,181,209,201,238,255,255,255,255,255,255,255,255,255,168,153,222,124,102,206, 255,255,255,199,189,234,63,30,183,186,174,229,247,246,252,204,195,236,60,27,182,204,195,236, 255,255,255,255,255,255,232,228,246,117,93,203,52,18,179,83,54,190,196,186,233,255,255,255 }; uchar Mask_X[192]= // The crosses { 254,252,252,189,51,51,236,195,195,255,255,255,255,255,255,235,192,192,248,234,234,255,255,255, 255,255,255,202,90,90,184,33,33,251,243,243,212,120,120,173,0,0,173,0,0,255,255,255, 255,255,255,254,252,252,195,69,69,192,60,60,178,15,15,233,186,186,253,249,249,255,255,255, 255,255,255,255,255,255,241,210,210,173,0,0,209,111,111,255,255,255,255,255,255,255,255,255, 255,255,255,255,255,255,205,99,99,192,60,60,181,24,24,241,210,210,255,255,255,255,255,255, 255,255,255,249,237,237,176,9,9,241,213,213,226,165,165,189,51,51,254,252,252,255,255,255, 255,255,255,230,177,177,185,36,36,255,255,255,255,255,255,189,51,51,222,153,153,255,255,255, 255,255,255,240,207,207,200,84,84,255,255,255,255,255,255,227,168,168,211,117,117,255,255,255 }; //+------------------------------------------------------------------+ //| Instrument selection | //+------------------------------------------------------------------+ void OnStart() { int mwSymb; string symb; int height=0,width=0; string pnfArray[]; if(mw==true) { mwSymb=0; while(mwSymb<SymbolsTotal(true)) { symb=SymbolName(mwSymb,true); ArrayFree(pnfArray); ArrayResize(pnfArray,0,0); PNF(symb,pnfArray,height,width,pic,cellsize); pnf2file(symb,pnfArray,0,height); mwSymb++; }; } else { symb=Symbol(); ArrayFree(pnfArray); ArrayResize(pnfArray,0,0); PNF(symb,pnfArray,height,width,pic,cellsize); pnf2file(symb,pnfArray,0,height); }; Alert("Ok."); } //+------------------------------------------------------------------+ //| Chart calculation and drawing | //+------------------------------------------------------------------+ void PNF(string sName, // instrument string& array[], // array for the output int& y, // array height int& z, // array width bool toPic, // if true-output and draw int cs) // set the cell size for drawing { string s,ps; datetime d[]; double o[],h[],l[],c[]; long v[]; uchar matrix[]; long VolByPrice[],VolByCol[],HVolumeMax,VVolumeMax; int tMin[],tMax[]; datetime DateByCol[]; MqlDateTime bMDT,eMDT; string strDBC[]; uchar pnf='.'; int sd; int b,i,j,k=0,m=0; int GlobalMin,GlobalMax,StartMin,StartMax,CurMin,CurMax,RevMin,RevMax,ContMin,ContMax; int height,width,beg=0,end=0; double dBox,price; int thBeg=1,thEnd=2,tv=0; uchar trend='.'; // --------------------------------- BMP ----------------------------------------- int RowVolWidth=10*cs; //--- shift for prices int startX=5*cs; int yshift=cs*7; // --------------------------------- BMP ----------------------------------------- if(SymbolInfoInteger(sName,SYMBOL_DIGITS)<=3) sd=2; else sd=4; b=MathMin(Bars(sName,tf),depth); ArrayFree(d); ArrayFree(o); ArrayFree(h); ArrayFree(l); ArrayFree(c); ArrayFree(v); ArrayFree(matrix); ArrayFree(VolByPrice); ArrayFree(VolByCol); ArrayFree(DateByCol); ArrayFree(tMin); ArrayFree(tMax); ArrayResize(d,b,0); ArrayResize(o,b,0); ArrayResize(h,b,0); ArrayResize(l,b,0); ArrayResize(c,b,0); ArrayResize(v,b,0); ArrayInitialize(d,NULL); ArrayInitialize(o,NULL); ArrayInitialize(h,NULL); ArrayInitialize(l,NULL); ArrayInitialize(c,NULL); ArrayInitialize(v,NULL); CopyTime(sName,tf,0,b,d); CopyOpen(sName,tf,0,b,o); CopyHigh(sName,tf,0,b,h); CopyLow(sName,tf,0,b,l); CopyClose(sName,tf,0,b,c); CopyTickVolume(sName,tf,0,b,v); if(box!=0) { dBox=box/MathPow(10.0,(double)sd); } else { dBox=MathNorm((h[ArrayMaximum(h,0,WHOLE_ARRAY)]-l[ArrayMinimum(l,0,WHOLE_ARRAY)])/count, 1/MathPow(10.0,(double)sd),true)/MathPow(10.0,(double)sd); }; GlobalMin=MathNorm(l[ArrayMinimum(l,0,WHOLE_ARRAY)],dBox,true)-(int)(reverse); GlobalMax=MathNorm(h[ArrayMaximum(h,0,WHOLE_ARRAY)],dBox,false)+(int)(reverse); StartMin=MathNorm(l[0],dBox,true); StartMax=MathNorm(h[0],dBox,false); ContMin=(int)(StartMin-1); ContMax=(int)(StartMax+1); RevMin=(int)(StartMax-reverse); RevMax=(int)(StartMin+reverse); height=(int)(GlobalMax-GlobalMin); width=1; ArrayResize(matrix,height*width,0); ArrayInitialize(matrix,'.'); ArrayResize(VolByPrice,height,0); ArrayInitialize(VolByPrice,0); ArrayResize(VolByCol,width,0); ArrayInitialize(VolByCol,0); ArrayResize(DateByCol,width,0); ArrayInitialize(DateByCol,D'01.01.1971'); ArrayResize(tMin,width,0); ArrayInitialize(tMin,0); ArrayResize(tMax,width,0); ArrayInitialize(tMax,0); for(i=1;i<b;i++) { CurMin=MathNorm(l[i],dBox,true); CurMax=MathNorm(h[i],dBox,false); switch(pnf) { case '.': { if(CurMax>=RevMax) { pnf='X'; ContMax=(int)(CurMax+1); RevMin=(int)(CurMax-reverse); beg=(int)(StartMin-GlobalMin-1); end=(int)(CurMax-GlobalMin-1); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; trend='D'; break; }; if(CurMin<=RevMin) { pnf='O'; ContMin=(int)(CurMin-1); RevMax=(int)(CurMin+reverse); beg=(int)(CurMin-GlobalMin-1); end=(int)(StartMax-GlobalMin-1); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; trend='U'; break; }; break; }; case 'X': { if(CurMax>=ContMax) { pnf='X'; ContMax=(int)(CurMax+1); RevMin=(int)(CurMax-reverse); end=(int)(CurMax-GlobalMin-1); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; break; }; if(CurMin<=RevMin) { pnf='O'; ContMin=(int)(CurMin-1); RevMax=(int)(CurMin+reverse); tMin[width-1]=beg-1; tMax[width-1]=end+1; beg=(int)(CurMin-GlobalMin-1); end--; width++; ArrayResize(matrix,height*width,0); ArrayResize(VolByCol,width,0); ArrayResize(DateByCol,width,0); ArrayResize(tMin,width,0); ArrayResize(tMax,width,0); SetMatrix(matrix,0,(int)(height-1),height,(int)(width-1),'.'); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=0; VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; tMin[width-1]=beg-1; tMax[width-1]=end+1; break; }; break; }; case 'O': { if(CurMin<=ContMin) { pnf='O'; ContMin=(int)(CurMin-1); RevMax=(int)(CurMin+reverse); beg=(int)(CurMin-GlobalMin-1); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; break; }; if(CurMax>=RevMax) { pnf='X'; ContMax=(int)(CurMax+1); RevMin=(int)(CurMax-reverse); tMin[width-1]=beg-1; tMax[width-1]=end+1; beg++; end=(int)(CurMax-GlobalMin-1); width++; ArrayResize(matrix,height*width,0); ArrayResize(VolByCol,width,0); ArrayResize(DateByCol,width,0); ArrayResize(tMin,width,0); ArrayResize(tMax,width,0); SetMatrix(matrix,0,(int)(height-1),height,(int)(width-1),'.'); SetMatrix(matrix,beg,end,height,(int)(width-1),pnf); SetVector(VolByPrice,beg,end,v[i]); VolByCol[width-1]=0; VolByCol[width-1]=VolByCol[width-1]+v[i]; DateByCol[width-1]=d[i]; tMin[width-1]=beg-1; tMax[width-1]=end+1; break; }; break; }; }; }; //--- credits s="BSD License, 2012, FXRays.info by Roman Rich"; k++; ArrayResize(array,k,0); array[k-1]=s; s=SymbolInfoString(sName,SYMBOL_DESCRIPTION)+", Box-"+DoubleToString(box,0)+",Reverse-"+DoubleToString(reverse,0); k++; ArrayResize(array,k,0); array[k-1]=s; // --------------------------------- BMP ----------------------------------------- if(toPic==true) { //-- BMP image size on the chart display int XSize=cs*width+2*startX+RowVolWidth; int YSize=cs*height+yshift+70; //-- creating a bmp image sized XSize x YSize with the background color clrWhite bmp.Create(XSize,YSize,clrWhite); //-- displaying cells of the main field for(i=height-1;i>=0;i--) for(j=0;j<=width-1;j++) { bmp.Bar(RowVolWidth+startX+cs*j,yshift+cs*i,cs,cs,clrWhite); bmp.Rectangle(RowVolWidth+startX+cs*j,yshift+cs*i,cs,cs,clrLightGray); } bmp.TypeText(10,yshift+cs*(height)+50,array[k-2],clrDarkGray); bmp.TypeText(10,yshift+cs*(height)+35,array[k-1],clrGray); } // --------------------------------- BMP ----------------------------------------- //--- calculating trend lines i=0; while(thEnd<width-1) { while(thBeg+i<thEnd) { if(trend=='U') { i=ArrayMinimum(tMin,thBeg,thEnd-thBeg); j=tMin[i]; } else { i=ArrayMaximum(tMax,thBeg,thEnd-thBeg); j=tMax[i]; } thBeg=i; tv=j; i=0; while(GetMatrix(matrix,j,height,(long)(thBeg+i))=='.') { i++; if(trend=='U') j++; else j--; if(thBeg+i==width-1) { thEnd=width-1; break; }; }; if(thBeg+i<thEnd) { thBeg=thBeg+2; i=0; }; }; thEnd=thBeg+i; if(thEnd==thBeg) thEnd++; for(i=thBeg;i<thEnd;i++) { SetMatrix(matrix,tv,tv,height,(long)(i),'+'); // --------------------------------- BMP ----------------------------------------- if(toPic==true) { //--- support and resistance lines if(trend=='U') { bmp.DrawLine(RowVolWidth+startX+i*cs,yshift+tv*cs, RowVolWidth+startX+(i+1)*cs,yshift+(tv+1)*cs,clrGreen); } if(trend=='D') { bmp.DrawLine(RowVolWidth+startX+i*cs,yshift+(tv+1)*cs, RowVolWidth+startX+(i+1)*cs,yshift+(tv)*cs,clrRed); } //--- broadening of support/resistance lines if(trend=='U') { bmp.DrawLine(RowVolWidth+1+startX+i*cs,yshift+tv*cs, RowVolWidth+1+startX+(i+1)*cs,yshift+(tv+1)*cs,clrGreen); } if(trend=='D') { bmp.DrawLine(RowVolWidth+1+startX+i*cs,yshift+(tv+1)*cs, RowVolWidth+1+startX+(i+1)*cs,yshift+(tv)*cs,clrRed); } } // --------------------------------- BMP ----------------------------------------- if(trend=='U') tv++; else tv--; }; if(trend=='U') trend='D'; else trend='U'; i=0; }; //--- displaying data in columns ArrayResize(strDBC,width,0); TimeToStruct(DateByCol[0],bMDT); TimeToStruct(DateByCol[width-1],eMDT); if((DateByCol[width-1]-DateByCol[0])>=50000000) { for(i=0;i<=width-1;i++) StringInit(strDBC[i],4,' '); for(i=1;i<=width-1;i++) { TimeToStruct(DateByCol[i-1],bMDT); TimeToStruct(DateByCol[i],eMDT); if(bMDT.year!=eMDT.year) strDBC[i]=DoubleToString(eMDT.year,0); }; for(i=0;i<=3;i++) { StringInit(s,vd,' '); s=s+" : "; for(j=0;j<=width-1;j++) s=s+StringSubstr(strDBC[j],i,1); s=s+" : "; k++; ArrayResize(array,k,0); array[k-1]=s; }; } else { if((DateByCol[width-1]-DateByCol[0])>=5000000) { for(i=0;i<=width-1;i++) StringInit(strDBC[i],7,' '); for(i=1;i<=width-1;i++) { TimeToStruct(DateByCol[i-1],bMDT); TimeToStruct(DateByCol[i],eMDT); if(bMDT.mon!=eMDT.mon) { if(eMDT.mon<10) strDBC[i]=DoubleToString(eMDT.year,0)+".0"+DoubleToString(eMDT.mon,0); if(eMDT.mon>=10) strDBC[i]=DoubleToString(eMDT.year,0)+"."+DoubleToString(eMDT.mon,0); } }; for(i=0;i<=6;i++) { StringInit(s,vd,' '); s=s+" : "; for(j=0;j<=width-1;j++) s=s+StringSubstr(strDBC[j],i,1); s=s+" : "; k++; ArrayResize(array,k,0); array[k-1]=s; }; } else { for(i=0;i<=width-1;i++) StringInit(strDBC[i],10,' '); for(i=1;i<=width-1;i++) { TimeToStruct(DateByCol[i-1],bMDT); TimeToStruct(DateByCol[i],eMDT); if(bMDT.day!=eMDT.day) { if(eMDT.mon<10 && eMDT.day<10) strDBC[i]=DoubleToString(eMDT.year,0)+".0" +DoubleToString(eMDT.mon,0)+".0"+DoubleToString(eMDT.day,0); if(eMDT.mon<10 && eMDT.day>=10) strDBC[i]=DoubleToString(eMDT.year,0)+".0" +DoubleToString(eMDT.mon,0)+"."+DoubleToString(eMDT.day,0); if(eMDT.mon>=10&&eMDT.day< 10) strDBC[i]=DoubleToString(eMDT.year,0)+"." +DoubleToString(eMDT.mon,0)+".0"+DoubleToString(eMDT.day,0); if(eMDT.mon>=10&&eMDT.day>=10) strDBC[i]=DoubleToString(eMDT.year,0)+"." +DoubleToString(eMDT.mon,0)+"." +DoubleToString(eMDT.day,0); } }; for(i=0;i<=9;i++) { StringInit(s,vd,' '); s=s+" : "; for(j=0;j<=width-1;j++) s=s+StringSubstr(strDBC[j],i,1); s=s+" : "; k++; ArrayResize(array,k,0); array[k-1]=s; }; }; }; StringInit(s,25+vd+width,'-'); k++; ArrayResize(array,k,0); array[k-1]=s; //--- displaying price chart price=GlobalMax*dBox; HVolumeMax=VolByPrice[ArrayMaximum(VolByPrice,0,WHOLE_ARRAY)]; s=""; for(i=height-1;i>=0;i--) { StringInit(ps,8-StringLen(DoubleToString(price,sd)),' '); s=s+ps+DoubleToString(price,sd)+" : "; for(j=0;j<vd;j++) if(VolByPrice[i]>HVolumeMax*j/vd) s=s+"*"; else s=s+" "; s=s+" : "; for(j=0;j<=width-1;j++) s=s+CharToString(matrix[j*height+i]); s=s+" : "+ps+DoubleToString(price,sd); k++; ArrayResize(array,k,0); array[k-1]=s; s=""; price=price-dBox; }; StringInit(s,25+vd+width,'-'); k++; ArrayResize(array,k,0); array[k-1]=s; //--- simple markup through 10 StringInit(s,vd,' '); s=s+" : "; for(j=0;j<=width-1;j++) if(StringGetCharacter(DoubleToString(j,0), StringLen(DoubleToString(j,0))-1)==57) s=s+"|"; else s=s+" "; s=s+" : "; k++; ArrayResize(array,k,0); array[k-1]=s; //--- displaying volume chart in columns VVolumeMax=VolByCol[ArrayMaximum(VolByCol,0,WHOLE_ARRAY)]; for(i=vd-1;i>=0;i--) { StringInit(s,vd,' '); s=s+" : "; for(j=0;j<=width-1;j++) if(VolByCol[j]>VVolumeMax*i/vd) s=s+"*"; else s=s+" "; s=s+" : "; k++; ArrayResize(array,k,0); array[k-1]=s; }; StringInit(s,25+vd+width,'-'); k++; ArrayResize(array,k,0); array[k-1]=s; //--- column history s=" | Start Date/Time | End Date/Time | "; k++; ArrayResize(array,k,0); array[k-1]=s; TimeToStruct(DateByCol[0],bMDT); s=" 1 | 0000/00/00 00:00:00 | "; s=s+DoubleToString(bMDT.year,0)+"/"; if(bMDT.mon >=10) s=s+DoubleToString(bMDT.mon ,0)+"/"; else s=s+"0"+DoubleToString(bMDT.mon ,0)+"/"; if(bMDT.day >=10) s=s+DoubleToString(bMDT.day ,0)+" "; else s=s+"0"+DoubleToString(bMDT.day ,0)+" "; if(bMDT.hour>=10) s=s+DoubleToString(bMDT.hour,0)+":"; else s=s+"0"+DoubleToString(bMDT.hour,0)+":"; if(bMDT.min >=10) s=s+DoubleToString(bMDT.min ,0)+":"; else s=s+"0"+DoubleToString(bMDT.min ,0)+":"; if(bMDT.sec >=10) s=s+DoubleToString(bMDT.sec ,0)+" | "; else s=s+"0"+DoubleToString(bMDT.sec ,0)+" | "; k++; ArrayResize(array,k,0); array[k-1]=s; for(i=1;i<=width-1;i++) { TimeToStruct(DateByCol[i-1],bMDT); TimeToStruct(DateByCol[i],eMDT); s=""; StringInit(ps,4-StringLen(DoubleToString(i+1,0)),' '); s=s+ps+DoubleToString(i+1,0)+" | "; s=s+DoubleToString(bMDT.year,0)+"/"; if(bMDT.mon >=10) s=s+DoubleToString(bMDT.mon ,0)+"/"; else s=s+"0"+DoubleToString(bMDT.mon ,0)+"/"; if(bMDT.day >=10) s=s+DoubleToString(bMDT.day ,0)+" "; else s=s+"0"+DoubleToString(bMDT.day ,0)+" "; if(bMDT.hour>=10) s=s+DoubleToString(bMDT.hour,0)+":"; else s=s+"0"+DoubleToString(bMDT.hour,0)+":"; if(bMDT.min >=10) s=s+DoubleToString(bMDT.min ,0)+":"; else s=s+"0"+DoubleToString(bMDT.min ,0)+":"; if(bMDT.sec >=10) s=s+DoubleToString(bMDT.sec ,0)+" | "; else s=s+"0"+DoubleToString(bMDT.sec ,0)+" | "; s=s+DoubleToString(eMDT.year,0)+"/"; if(eMDT.mon >=10) s=s+DoubleToString(eMDT.mon ,0)+"/"; else s=s+"0"+DoubleToString(eMDT.mon ,0)+"/"; if(eMDT.day >=10) s=s+DoubleToString(eMDT.day ,0)+" "; else s=s+"0"+DoubleToString(eMDT.day ,0)+" "; if(eMDT.hour>=10) s=s+DoubleToString(eMDT.hour,0)+":"; else s=s+"0"+DoubleToString(eMDT.hour,0)+":"; if(eMDT.min >=10) s=s+DoubleToString(eMDT.min ,0)+":"; else s=s+"0"+DoubleToString(eMDT.min ,0)+":"; if(eMDT.sec >=10) s=s+DoubleToString(eMDT.sec ,0)+" | "; else s=s+"0"+DoubleToString(eMDT.sec ,0)+" | "; k++; ArrayResize(array,k,0); array[k-1]=s; }; y=k; z=25+vd+width; // --------------------------------- BMP ----------------------------------------- if(toPic==true) { //--- displaying dates in YYYY/MM/DD format for(j=0;j<=width-1;j++) { string s0=strDBC[j]; StringReplace(s0,".","/"); bmp.TypeTextV(RowVolWidth+startX+cs*j,yshift+cs*(height-1)+5,s0,clrDimGray); } //--- volume cell support for(i=height-1;i>=0;i--) for(j=0;j<vd;j++) { bmp.Bar(cs+startX+cs*(j-1),yshift+cs*i,cs,cs,0xF6F6F6); bmp.Rectangle(cs+startX+cs*(j-1),yshift+cs*i,cs,cs,clrLightGray); } for(i=0; i>-7;i--) for(j=0;j<=vd;j++) { bmp.Bar(cs+startX+cs*(j-1),yshift+cs*i,cs,cs,clrWhite); bmp.Rectangle(cs+startX+cs*(j-1),yshift+cs*i,cs,cs,clrLightGray); } //--- exact volumes for(i=height-1;i>=0;i--) bmp.Bar(startX,yshift+cs*i,int(10*cs*VolByPrice[i]/HVolumeMax),cs,0xB5ABAB); //--- displaying naughts and crosses for(i=height-1;i>=0;i--) for(j=0;j<=width-1;j++) { int xpos=RowVolWidth+startX+cs*j+1; int ypos=yshift+cs*i+1; if(CharToString(matrix[j*height+i])=="X") ShowCell(xpos,ypos,'X'); else if(CharToString(matrix[j*height+i])=="O") ShowCell(xpos,ypos,'O'); } //--- volume underside support for(i=0;i<=60/cs;i++) for(j=0;j<=width-1;j++) { bmp.Bar(RowVolWidth+startX+cs*j,12+cs*i,cs,cs,0xF6F6F6); bmp.Rectangle(RowVolWidth+startX+cs*j,12+cs*i,cs,cs,clrLightGray); } //--- displaying volumes for(j=0;j<=width-1;j++) bmp.Bar(RowVolWidth+startX+cs*j,yshift-60, cs,int(60*VolByCol[j]/VVolumeMax),0xB5ABAB); //--- displaying the main field border bmp.Rectangle(RowVolWidth+startX+cs*0,yshift+cs*0,cs*(width),cs*(height),clrSilver); //--- displaying prices and scale bmp.LineV(startX,yshift,cs*height,clrBlack); bmp.LineV(RowVolWidth+startX+cs*width,yshift,cs*height,clrBlack); price=GlobalMax*dBox; for(i=height-1;i>=0;i--) { //-- prices on the left bmp.TypeText(cs,yshift+cs*i,DoubleToString(price,sd),clrBlack); bmp.LineH(0,yshift+cs*i,startX,clrLightGray); bmp.LineH(0+startX-3,yshift+cs*i,6,clrBlack); //-- prices on the right int dx=RowVolWidth+cs*width; bmp.TypeText(10+startX+dx,yshift+cs*i,DoubleToString(price,sd),clrBlack); bmp.LineH(startX+dx,yshift+cs*i,40,clrLightGray); bmp.LineH(startX+dx-3,yshift+cs*i,6,clrBlack); price=price-dBox; } //-- saving the resulting image in a file bmp.Save(sName,true); } // --------------------------------- BMP ----------------------------------------- } //+------------------------------------------------------------------+ //|Outputting as a text file | //+------------------------------------------------------------------+ void pnf2file(string sName, // instrument for the file name string& array[], // array of lines saved in the file int beg, // the line of the array first saved in the file int end) // the line of the array last saved in the file { string fn; int handle; fn=sName+"_b"+DoubleToString(box,0)+"_r"+DoubleToString(reverse,0)+".txt"; handle=FileOpen(fn,FILE_WRITE|FILE_TXT|FILE_ANSI,';'); for(int i=beg;i<end;i++) FileWrite(handle,array[i]); FileClose(handle); } //+------------------------------------------------------------------+ //| Adjusting the price to the box size | //+------------------------------------------------------------------+ int MathNorm(double value, // transforming any double-type figure into long-type figure double prec, // ensuring the necessary accuracy bool vect) // and if true, rounding up; if false, rounding down { if(vect==true) return((int)(MathCeil(value/prec))); else return((int)(MathFloor(value/prec))); } //+------------------------------------------------------------------+ //| Filling the array | //| Character one-dimensional array represented as a matrix | //+------------------------------------------------------------------+ void SetMatrix(uchar& array[], // passing the array in a link to effect a replacement long pbeg, // from here long pend, // up to here long pheight, // in the column of this height long pwidth, // bearing this number among all the columns in the array uchar ppnf) // with this character { long offset=0; for(offset=pheight*pwidth+pbeg;offset<=pheight*pwidth+pend;offset++) array[(int)offset]=ppnf; } //+------------------------------------------------------------------+ //| Getting an isolated value from the array | //| Character one-dimensional array represented as a matrix | //+------------------------------------------------------------------+ uchar GetMatrix(uchar& array[], // passing it in a link to obtain a character... long pbeg, // here long pheight, // in the column of this height long pwidth) // bearing this number among all the columns in the array { return(array[(int)pheight*(int)pwidth+(int)pbeg]); } //+------------------------------------------------------------------+ //|Filling the vector | //+------------------------------------------------------------------+ void SetVector(long &array[], // passing the long-type array in a link to effect a replacement long pbeg, // from here long pend, // up to here long pv) // with this value { long offset=0; for(offset=pbeg;offset<=pend;offset++) array[(int)offset]=array[(int)offset]+pv; } //+------------------------------------------------------------------+ //| Displaying a horizontal line | //+------------------------------------------------------------------+ void cIntBMPEx::LineH(int aX1,int aY1,int aSizeX,int aColor) { DrawLine(aX1,aY1,aX1+aSizeX,aY1,aColor); } //+------------------------------------------------------------------+ //| Displaying a vertical line | //+------------------------------------------------------------------+ void cIntBMPEx::LineV(int aX1,int aY1,int aSizeY,int aColor) { DrawLine(aX1,aY1,aX1,aY1+aSizeY,aColor); } //+------------------------------------------------------------------+ //| Drawing a rectangle (of a given size) | //+------------------------------------------------------------------+ void cIntBMPEx::Rectangle(int aX1,int aY1,int aSizeX,int aSizeY,int aColor) { DrawRectangle(aX1,aY1,aX1+aSizeX,aY1+aSizeY,aColor); } //+------------------------------------------------------------------+ //| Drawing a filled rectangle (of a given size) | //+------------------------------------------------------------------+ void cIntBMPEx::Bar(int aX1,int aY1,int aSizeX,int aSizeY,int aColor) { DrawBar(aX1,aY1,aX1+aSizeX,aY1+aSizeY,aColor); } //+------------------------------------------------------------------+ //| Drawing a filled rectangle | //+------------------------------------------------------------------+ void cIntBMPEx::DrawBar(int aX1,int aY1,int aX2,int aY2,int aColor) { for(int i=aX1; i<=aX2; i++) for(int j=aY1; j<=aY2; j++) { DrawDot(i,j,aColor); } } //+------------------------------------------------------------------+ //| Displaying the text vertically | //+------------------------------------------------------------------+ void cIntBMPEx::TypeTextV(int aX,int aY,string aText,int aColor) { SetDrawWidth(1); for(int j=0;j<StringLen(aText);j++) { string TypeChar=StringSubstr(aText,j,1); if(TypeChar==" ") { aY+=5; } else { int Pointer=0; for(int i=0;i<ArraySize(CA);i++) { if(CA[i]==TypeChar) { Pointer=i; } } for(int i=PA[Pointer];i<PA[Pointer+1];i++) { DrawDot(aX+YA[i],aY+MaxHeight+XA[i],aColor); } aY+=WA[Pointer]+1; } } } //+------------------------------------------------------------------+ //| Transforming components into color | //+------------------------------------------------------------------+ int RGB256(int aR,int aG,int aB) { return(aR+256*aG+65536*aB); } //+------------------------------------------------------------------+ //| Drawing X's or O's as an image | //+------------------------------------------------------------------+ void ShowCell(int x,int y,uchar img) { uchar r,g,b; for(int i=0; i<8; i++) { for(int j=0; j<8; j++) { switch(img) { case 'X': r=Mask_X[3*(j*8+i)]; g=Mask_X[3*(j*8+i)+1]; b=Mask_X[3*(j*8+i)+2]; break; case 'O': r=Mask_O[3*(j*8+i)]; g=Mask_O[3*(j*8+i)+1]; b=Mask_O[3*(j*8+i)+2]; break; }; int col=RGB256(r,g,b); bmp.DrawDot(x+i,y+j,col); } } } //+------------------------------------------------------------------+
Dependiendo del valor del parámetro de entrada pic, los resultados del script se generarán en forma de archivos de texto con archivos de imagen (terminal_data_directory\MQL5\Images) o solo archivos de texto (guardado en terminal_data_directory\MQL5\Files).
Comparando los resultados
Para comparar los resultados vamos a dibujar un gráfico Light Crude Oil con los siguientes parámetros: tamaño del cuadro $1, inversión de 2 cuadros.
StockCharts.com:
Fig. 1. Gráfico de punto y forma para un Light Crude Oil generado por StockCharts.com
Bull's-Eye Broker:
Fig. 2. Gráfico de punto y forma para un Light Crude Oil generado por el software Bull's-Eye Broker
Los resultados de nuestro script:
Fig. 3. Gráfico de punto y forma para un Light Crude Oil generado por nuestro script
Los tres gráficos son idénticos. ¡Enhorabuena! Ya conocemos la creación de gráficos de punto y forma.
Patrones de gráfico de punto y forma típicos
¿Cómo pueden usarse?
Vamos a ver primero los patrones típicos, especialmente porque pueden contarse con los dedos.
Estos son:
Fig. 4. Patrones de precio: The Double Top, The Triple Top, The Double Bottom Breakout y The Triple Bottoms
además:
Fig. 5. Patrones de precio: Bullish Triangle y Bearish Triangle
y finalmente:
Fig. 6. Patrones de precio: Bullish Catapult y Bearish Catapult
Y ahora algunos consejos.
- Abrimos solo posiciones largas sobre la línea de apoyo y solo posiciones cortas bajo la línea de resistencia. Por ejemplo, empezando a partir de mediados de diciembre de 2011, después de escapar a través de la línea de resistencia que se ha estado formando desde el final de septiembre de 2011, y abriendo solo posiciones largas en futuros de Light Crude Oil.
- Usamos las líneas de apoyo y resistencia para las órdenes trailing stop y loss.
- Usamos el recuento vertical antes de abrir una posición para estimar un ratio entre posibles beneficios y pérdidas.
El recuento vertical se muestra mejor con el siguiente ejemplo.
En diciembre de 2011 la columna de X se movió hacia arriba a partir del precio inicial de $76 más allá de la columna anterior de X a $85, se escapó a través de la resistencia en $87 y alcanzó $89. De acuerdo con el recuento vertical, esto sugiere que el precio puede subir hasta alcanzar el nivel de $76+($89-$75)*3 (3, inversión del cuadro)=$118.
El siguiente movimiento fue llevar el precio al nivel de $85. Los especuladores pueden colocar una orden stop loss en una posición larga en $1 menos, es decir a $84.
La entrada a la posición larga puede ser planificada después de un movimiento de corrección un cuadro mayor que la columna anterior de las X, es decir, al precio de $90.
Vamos a estimar las pérdidas posibles: pueden alcanzar los $90-$84=$6 por un contrato de futuros. El beneficio posible puede alcanzar $118-$90=$28. El ratio de beneficio-pérdida posible: $28/$6>4,5 buen rendimiento, en mi opinión. Por ahora, nuestro beneficio habría ascendido a $105-$90=$15 por cada contrato de futuros.
Licencias
El script fue escrito y suministrado bajo licencia BSD por el autor Roman Rich. El texto de la licencia se encuentra en el archivo Lix.txt. Se creó la librería cIntBMP por parte de Dmitry, también conocido como Integer. Las marcas comerciales StockCharts.com y Bull's-Eye Broker son propiedad de sus respectivos titulares.
Conclusión
El artículo ha propuesto un algoritmo y un script para la creación de gráficos de punto y forma ("ceros y cruces"). Se han tenido en cuenta varios patrones de precio cuyo uso práctico se describió en las recomendaciones que se han proporcionado.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/368
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso