L'ultima crociata

Roman Zamozhnyy | 17 dicembre, 2021


Introduzione

Ci sono tre modalità predefinite di presentazione del prezzo dello strumento disponibili nel terminale МetaТrader 5 (così come su МetaТrader 4): barre, candele e linee. Essenzialmente, tutti rappresentano gli stessi grafici temporali. Oltre al metodo tradizionale di presentazione dei prezzi in base al tempo, esistono ancora altri mezzi non legati al tempo e che sono molto popolari tra investitori e speculatori: Renko e Kagi, interruzione di tre righe e punti e cifre dei grafici.

Non voglio affermare il loro vantaggio rispetto ai classici, ma togliere di vista la variabile tempo aiuta alcuni trader a concentrarsi sulla variabile prezzo. Suggerisco di considerare qui i grafici punti e cifre insieme a un algoritmo di creazione di grafici pertinente, dare un'occhiata a prodotti di mercato ben noti che servono a generare tali grafici e scrivere uno script chiaro e semplice che implementa l'algoritmo. Un libro di Thomas J. Dorsey "Point and Figure Charting: The Essential Application for Forecasting and Tracking Market Prices" sarà il nostro libro dell’ABC.

Bull's-Eye Broker è il pacchetto software più popolare per disegnare grafici offline. Il software è disponibile per 21 giorni di prova (numerose prove possibili) e la nuova versione Beta è disponibile durante il periodo Beta. Questo pacchetto software verrà utilizzato per stimare i risultati delle prestazioni degli script. Una delle migliori risorse online in termini di grafici a punti e cifre è StockCharts. Il sito web è orientato alla borsa e quindi, sfortunatamente, non fornisce i prezzi degli strumenti Forex.

Per confrontare i risultati delle prestazioni dello script che introdurremo, i future sull'oro, i future sul petrolio greggio leggero e i grafici CFD S&P 500 verranno generati utilizzando il software e il sito Web; un grafico dei prezzi EURUSD verrà disegnato utilizzando solo Bull's-Eye Broker (ricorda le limitazioni di StockChart).


Algoritmo per grafici a punti e cifre

Quindi, ecco l'algoritmo.

Ci sono due parametri chiave nei grafici a punti e cifre:

  1. Dimensione della scatola che è la variazione minima del prezzo dello strumento; variazioni inferiori alla variazione di prezzo minimo non influiscono sul grafico;
  2. Inversione che è il numero di caselle che rappresentano il movimento del prezzo nella direzione opposta all'attuale direzione del grafico a seguito del quale tale movimento verrà visualizzato nella nuova colonna.

Poiché la creazione di grafici richiede la cronologia delle quotazioni memorizzate sotto forma di prezzi Open-High-Low-Close, assumiamo quanto segue:

  1. Il grafico è disegnato in base ai prezzi High-Low;
  2. Il prezzo alto viene arrotondato per difetto alla dimensione della scatola (MathFloor), il prezzo basso viene arrotondato per eccesso alla dimensione della scatola (MathCeil).

Faccio un esempio. Supponiamo di voler disegnare un grafico del petrolio greggio leggero con una dimensione della scatola uguale a $ 1 (uno) e un'inversione di 3 (tre) scatola. Ciò significa che tutti i prezzi High vengono arrotondati per difetto al $1 più vicino e tutti i prezzi Low vengono arrotondati allo stesso modo:

Data High Low XO High XO Basso
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


Le X (croci) sono utilizzate per illustrare un movimento di prezzo al rialzo sul grafico, mentre le O (zero) rappresentano un movimento di prezzo in calo.

Come determinare la direzione iniziale del prezzo (se la prima colonna è X o O):

Tieni a mente i valori XO High e XO Low [Bars-1] e attendi fino a:

Nel nostro esempio di petrolio greggio, dovremmo tenere a mente XO High[Bars-1]=100 e XO Low[Bars-1]=100.

Quindi aspetta di vedere cosa succede prima:

Possiamo determinare la prima colonna il 17 febbraio: XO High price ha raggiunto $ 103 e la prima colonna è X. Fallo disegnando quattro X da $ 100 a $ 103.

Come determinare un ulteriore movimento del grafico:

Se la colonna corrente è X, controlla se XO High della barra corrente è aumentato della dimensione della casella rispetto al prezzo XO corrente (cioè il 20 febbraio controlleremo prima se XO High è maggiore o uguale a $ 104). Se XO High[2012.02.20] è $ 104 o $ 105 o superiore, aggiungeremo il numero rilevante di X alla colonna esistente di X.

Se XO High della barra corrente non è aumentato della dimensione della scatola rispetto al prezzo XO corrente, controlla se XO Low della barra corrente è inferiore a XO High del numero di inversione di scatole (nel nostro esempio, se XO Low[ 2012.02.20] è inferiore o uguale a $ 103-3* $ 1 = $ 100 o $ 99 o inferiore). Se è inferiore, tracciamo una colonna di O a destra della colonna di X da $ 102 a $ 100.

Nel caso in cui la colonna corrente sia O, tutte le considerazioni di cui sopra valgono viceversa.

IMPORTANTE: ogni nuova colonna di O è sempre disegnata a destra e a una casella più bassa del valore High della precedente colonna di X e ogni nuova colonna di X è sempre disegnata a destra e a una casella più alta del valore Low della precedente colonna di O.

I principi della creazione di grafici sono ora chiari. Passiamo alle linee di supporto e resistenza.

Le linee di supporto e resistenza nei grafici a punti e cifre convenzionali sono sempre angolate a 45 gradi.

La prima riga dipende dalla prima colonna. Se la prima colonna è X, la prima linea sarà una linea di resistenza che inizia un riquadro più in alto del massimo della prima colonna, con un'angolazione di 45 gradi GI e verso destra. Se la prima colonna è O, la prima linea sarà una linea di supporto che inizia un riquadro più in basso del minimo della prima colonna, con un'angolazione di 45 gradi verso l'alto e verso destra. Le linee di supporto e resistenza vengono tracciate fino a raggiungere il grafico dei prezzi.

Non appena la linea di supporto/resistenza raggiunge il grafico dei prezzi, iniziamo a tracciare una linea di resistenza/supporto, di conseguenza. Quando si disegna, il principio chiave è assicurarsi che la linea tracciata sia più a destra della precedente linea di tendenza sul grafico. Quindi, per tracciare una linea di supporto identifichiamo prima il valore minimo del grafico sotto la linea di resistenza che abbiamo appena disegnato e tracciamo la linea di supporto iniziando una casella più in basso del minimo identificato UP a destra fino a raggiungere il grafico o l'ultima colonna di il grafico.

Se la linea di supporto tracciata partendo dal minimo sotto la precedente linea di resistenza sale e inciampa sul grafico sotto la stessa linea di resistenza, spostati a destra e trova un nuovo minimo di prezzo all'interno dell'intervallo dal minimo più basso sotto la resistenza fino alla fine della linea di resistenza. Continua finché la linea di tendenza così tracciata non va a destra, oltre la linea di tendenza precedente.

Tutto quanto sopra sarà più chiaro se illustrato con esempi di grafici reali forniti più avanti.

Ormai abbiamo già risolto l'algoritmo di creazione di grafici. Aggiungiamo alcune belle funzionalità al nostro script:

Ora che sono state fornite le descrizioni dell'algoritmo e dei requisiti, è giunto il momento di presentare lo 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);
        }
     }
  }
//+------------------------------------------------------------------+

A seconda del valore del parametro di input pic, i risultati dello script verranno generati sotto forma di file di testo con file di immagine (directory_data_terminale\MQL5\Images) o solo file di testo (salvati in directory_dati_terminale\MQL5\Files).


Risultati a confronto

Per confrontare i risultati, disegniamo un grafico del petrolio greggio leggero con i seguenti parametri: la dimensione della scatola è $ 1, l'inversione è di 3 scatole.

StockCharts.com:

Fig.1. Grafico a punti e cifre per il petrolio greggio generato da StockCharts.com

Fig.1. Grafico a punti e cifre per il petrolio greggio generato da StockCharts.com

Bull's-Eye Broker:

Fig.2. Grafico a punti e cifre per il petrolio greggio leggero generato dal software Bull's-Eye Broker

Fig.2. Grafico a punti e cifre per il petrolio greggio generato dal software Bull's-Eye Broker


I nostri risultati delle prestazioni dello script:

Fig.3. Grafico a punti e cifre per il petrolio greggio leggero generato dal nostro script

Fig.3. Grafico a punti e cifre per il petrolio greggio leggero generato dal nostro script

Tutti e tre i grafici sono identici. Congratulazioni! Sappiamo come creare i grafici a punti e cifre.


Tipici modelli di grafici a punti e cifre

Come possono essere utilizzati?

Diamo prima un'occhiata agli schemi tipici, soprattutto perché si possono contare sulle dita.

Questi sono:

Fig.4. Andamento dei prezzi: il doppio massimo, il triplo massimo, il doppio minimo e il triplo minimo

Fig.4. Modelli di prezzo: Il doppio massimo, il triplo massimo, il doppio minimo di breakout e il triplo minimo

per di più:

Fig.5. Andamento dei prezzi: triangolo rialzista e triangolo ribassista

Fig.5. Modelli di prezzo: Triangolo rialzista e triangolo ribassista

e infine:

Fig.6. Andamento dei prezzi: catapulta rialzista e catapulta ribassista

Fig.6. Modelli di prezzo: Catapulta rialzista e catapulta ribassista

E ora qualche consiglio.

  1. Apri solo posizioni long sopra la linea di supporto e solo posizioni corte sotto la linea di resistenza. Ad esempio, a partire dalla metà di dicembre 2011, dopo aver superato la linea di resistenza che si è formata dalla fine di settembre 2011, apri solo posizioni long sui future sul petrolio greggio leggero.
  2. Utilizza le linee di supporto e resistenza per gli ordini trailing stop loss.
  3. Utilizza il conteggio verticale prima di aprire una posizione per stimare un rapporto tra possibile profitto e possibile perdita.

Il conteggio verticale è meglio dimostrato dal seguente esempio.

Nel dicembre 2011, la colonna di X è salita dal prezzo iniziale di $ 76 oltre la precedente colonna di X a $ 85, ha superato la linea di resistenza a $ 87 e ha raggiunto $ 89. Secondo il conteggio verticale, ciò suggerisce che il prezzo potrebbe salire fino a raggiungere il livello di $ 76+ ($ 89- $ 75)*3 (inversione di 3 scatole) = $ 118.

Il movimento successivo è stato correttivo, portando il prezzo al livello di 85 dollari. Gli speculatori possono piazzare un ordine di stop loss su una posizione long a $1 in meno, ovvero a $84.

L'ingresso nella posizione long può essere pianificato dopo un movimento correttivo completato di una casella in più rispetto alla precedente colonna di X, cioè al prezzo di $90.

Stimiamo la possibile perdita - può ammontare a $90-$84=$6 per un contratto future. Il possibile profitto può raggiungere $ 118- $ 90 = $ 28. Possibile rapporto di perdita tra profitti e perdite: $28/$6>4.5 Buone prestazioni, secondo me. A questo punto il nostro profitto sarebbe stato di $ 105- $ 90 = $ 15 per ogni contratto future.


Licenze

Lo script è stato scritto e fornito con licenza BSD dall'autore Roman Rich. Il testo della licenza si trova nel file Lic.txt. La libreria cIntBMP è stata creata da Dmitry, alias Integer. I marchi StockCharts.com e Bull's-Eye Broker sono di proprietà dei rispettivi proprietari.


Conclusione

Questo articolo ha proposto un algoritmo e uno script per la creazione di grafici a punti e cifre ("naught and cross"). È stata data considerazione a vari modelli di prezzo il cui uso pratico è stato delineato nelle raccomandazioni fornite.