English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Indicatore per grafici a point and figure

Indicatore per grafici a point and figure

MetaTrader 5Esempi | 11 gennaio 2022, 16:51
298 0
Dmitriy Zabudskiy
Dmitriy Zabudskiy

Introduzione

Esistono molti tipi di grafici che forniscono informazioni sulla situazione attuale del mercato. Molti di questi, come Point and Figure chart, sono l'eredità del passato remoto.

Questo tipo di grafico è noto dalla fine del XIX secolo. È stato menzionato per la prima volta da Charles Dow nel suo editoriale sul Wall Street Journal scritto il 20 luglio 1901 che lo ha etichettato come metodo del "libro". E sebbene Dow abbia fatto riferimento al metodo del "libro" fin dal 1886, è stato il primo a stabilirne ufficialmente l'uso fino ad oggi.

Nonostante il fatto che Dow abbia descritto questo metodo solo negli editoriali, ora puoi trovare molti libri che forniscono i dettagli di questo metodo. Uno dei libri che consiglierei ai trader alle prime armi è un libro di Thomas J. Dorsey intitolato "Point and Figure Charting: L'applicazione essenziale per la previsione e il monitoraggio dei prezzi di mercato".

 

Descrizione

Il grafico Point and Figure è un insieme di colonne verticali: le colonne di X sono prezzi in aumento e le colonne di O sono prezzi in calo. È unico in quanto è tracciato in base all'azione dei prezzi, non al tempo. Pertanto, dopo aver rimosso un valore dai dati dei grafici (tempo), otteniamo grafici con linee di tendenza tracciate con un angolo di 45 gradi.

I grafici a punti e figura vengono tracciati utilizzando due valori predefiniti:

  • Box Size è la quantità di movimento del prezzo richiesta per aggiungere una X o una O (originariamente, il valore era espresso come la quantità di dollari per azione ma nel tempo si è sviluppato in punti che è quello che useremo nel nostro indicatore).
  • Reversal Amount è l'importo dello storno di prezzo espresso in unità Box Size richiesto per cambiare le colonne da X a O o viceversa (es. un importo di storno di 3 e un box size di 10 punti corrisponderà a 30 punti).

Quindi selezioniamo un punto di partenza e mettiamo una X per un aumento di prezzo o una O per un calo, a condizione che il prezzo sia cambiato del valore pari al Box Size moltiplicato per l'importo di storno. Inoltre, se il prezzo continua a muoversi nella stessa direzione cambiando della dimensione della scatola, aggiungiamo una X nella parte superiore della colonna della X per un aumento o una O nella parte inferiore della colonna della O per una diminuzione, rispettivamente. Se il prezzo si è mosso nella direzione opposta per il valore di Box Size moltiplicato per Reversal Amount, mettiamo una X per un aumento del prezzo o una O per un calo del prezzo, iniziando così una nuova colonna di X o una nuova colonna di O, rispettivamente.

Per comodità, il grafico Point and Figure viene solitamente tracciato su carta a quadretti. Per una migliore comprensione, esaminiamo un piccolo esempio di grafici a point and figure. Supponiamo di avere i seguenti dati:

Data Prezzo Alto Prezzo basso
07.03.2013 12:00 - 07.03.2013 20:00 1.3117 1.2989
07.03.2013 20:00 - 08.03.2013 04:00 1.3118 1.3093
08.03.2013 04:00 - 08.03.2013 12:00 1.3101 1.3080
08.03.2013 12:00 - 08.03.2013 20:00 1.3134 1.2955

Tracciamo il grafico Point and Figure, dato che Box Size è uguale a 10 e Reversal Amount è uguale a 3:

  • All'inizio, vediamo un aumento del prezzo di 128 punti, da 1,2989 a 1,3117, quindi tracciamo 12 X.
  • Il prezzo quindi scende da 1,3118 a 1,3093 di 25 punti, il che non è sufficiente per l'inversione, quindi lo lasciamo lì.
  • Inoltre, possiamo vedere che il prezzo continua a scendere a 1,3080. Dato il valore precedente di 1.3118, ora è cambiato di 38 punti, quindi possiamo iniziare una nuova colonna aggiungendo due O (sebbene il movimento del prezzo abbia superato il valore di tre Dimensioni scatola, inseriamo solo due O come colonna successiva di O inizia sempre una dimensione della scatola inferiore).
  • Il prezzo sale quindi da 1,3080 a 1,3134 di 54 punti, per poi scendere a 1,2955, ovvero 179 punti. Quindi la colonna successiva è composta da quattro X, seguita da una colonna di O composta da 16 O.

Vediamolo raffigurato di seguito:

Fig. 1. Grafico a candele giapponesi (sinistra) e grafico Point and Figure (destra).

Fig. 1. Grafico a candele giapponesi (a sinistra) e grafico a point and figure (a destra).

L'esempio sopra di grafici di point and figure è molto approssimativo e viene fornito qui per aiutare i principianti a comprendere meglio il concetto.

 

Principio di creazione di grafici

Esistono diverse tecniche di creazione di grafici a point and figure, una delle quali è già stata descritta sopra. Queste tecniche di creazione di grafici differiscono nei dati che utilizzano. Ad esempio, possiamo utilizzare i dati giornalieri senza considerare i movimenti intraday, ottenendo così un grafico approssimativo. Oppure possiamo considerare i dati sul movimento dei prezzi infragiornalieri in modo da ottenere un grafico più dettagliato e fluido.

Per ottenere un grafico Point and Figure più fluido e accurato, è stato deciso di utilizzare i dati del minuto per i calcoli e la creazione di grafici poiché il movimento del prezzo in un minuto non è molto significativo e di solito è fino a sei punti, con due o tre punti non essendo raro. Pertanto, utilizzeremo i dati del prezzo di apertura su ogni barra dei minuti.

Il principio della creazione di grafici di per sé è abbastanza semplice:

  • Prendiamo un punto di partenza, ovvero il prezzo di apertura della barra del primo minuto.
  • Inoltre, se il prezzo si sposta di una distanza pari alla dimensione della scatola moltiplicata per l'importo di inversione, o più, disegniamo i rispettivi simboli (O per un movimento al ribasso e X per un movimento al rialzo). I dati sull'ultimo prezzo del simbolo vengono memorizzati per ulteriori grafici.
  • Nel caso in cui il prezzo si muova della Dimensione della Scatola nella stessa direzione, viene disegnato un simbolo corrispondente.
  • Inoltre, in caso di inversione del prezzo, il calcolo sarà basato sul prezzo dell'ultimo simbolo, piuttosto che sul prezzo più alto della coppia. In altre parole, se il movimento del prezzo non è superiore al 50% della dimensione della scatola, viene semplicemente ignorato.

Ora determiniamo lo stile dei grafici Point e Figure. Il linguaggio MQL5 supporta sette stili di tracciamento degli indicatori: linea, sezione (segmento), istogramma, freccia (simbolo), area piena (canale pieno), barre e candele giapponesi.

Le frecce (simboli) sarebbero perfette per una rappresentazione visiva ideale, ma questo stile richiede un numero variabile di buffer di indicatori (che semplicemente non è supportato in MQL5) o un numero enorme di essi poiché il tracciamento di ogni singola X o O in una colonna richiede un indicatore buffer separato. Ciò significa che se si decide di utilizzare questo stile, è necessario definire la volatilità e disporre di risorse di memoria sufficienti.

Quindi abbiamo deciso per le candele giapponesi come stile di creazione di grafici, per l'appunto, candele giapponesi colorate. Si suppone che vengano utilizzati colori diversi per differenziare le colonne di X dalle colonne di O. Pertanto, l'indicatore necessita solo di cinque buffer, consentendo un uso efficiente delle risorse disponibili.

Le colonne sono divise in Dimensioni casella utilizzando linee orizzontali. Il risultato che otteniamo è abbastanza decente:

Fig. 2. Grafici che utilizzano l'indicatore per EURUSD sul timeframe giornaliero.

Fig. 2. Grafici utilizzando l'indicatore per EURUSD sul timeframe giornaliero.

 

Algoritmo dell'indicatore

Innanzitutto, dobbiamo determinare i parametri di input dell'indicatore. Poiché il grafico Point and Figure non tiene conto del tempo e utilizziamo i dati per tracciare dalle barre dei minuti, dobbiamo determinare la quantità di dati da elaborare per non utilizzare inutilmente le risorse di sistema. Inoltre, non ha senso tracciare un grafico Point and Figure utilizzando l'intera cronologia. Quindi introduciamo il primo parametro: la Storia. Prenderà in considerazione il numero delle barre dei minuti per il calcolo.

Inoltre, dobbiamo determinare la dimensione della scatola e l'importo dello storno. A questo scopo, introdurremo rispettivamente le variabili Cell e CellForChange. Introdurremo anche il parametro color per X's, ColorUp, e per O's, ColorDown. E infine, l'ultimo parametro sarà il colore della linea - LineColor.

// +++ Program start +++
//+------------------------------------------------------------------+
//|                                                         APFD.mq5 |
//|                                            Aktiniy ICQ:695710750 |
//|                                                    ICQ:695710750 |
//+------------------------------------------------------------------+
#property copyright "Aktiniy ICQ:695710750"
#property link      "ICQ:695710750"
#property version   "1.00"
//--- Indicator plotting in a separate window
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1
//--- plot Label1
#property indicator_label1  "APFD"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_style1  STYLE_SOLID
#property indicator_color1  clrRed,clrGold
#property indicator_width1  1
//--- Set the input parameters
input int   History=10000;
input int   Cell=5;
input int   CellForChange=3;
input color ColorUp=clrRed;
input color ColorDown=clrGold;
input color LineColor=clrAqua;
//--- Declare indicator buffers
double CandlesBufferOpen[];
double CandlesBufferHigh[];
double CandlesBufferLow[];
double CandlesBufferClose[];
double CandlesBufferColor[];
//--- Array for copying calculation data from the minute bars
double OpenPrice[];
// Variables for calculations
double PriceNow=0;
double PriceBefore=0;
//--- Introduce auxiliary variables
char   Trend=0;      // Direction of the price trend
double BeginPrice=0; // Starting price for the calculation
char   FirstTrend=0; // Direction of the initial market trend
int    Columns=0;    // Variable for the calculation of columns
double InterimOpenPrice=0;
double InterimClosePrice=0;
double NumberCell=0; // Variable for the calculation of cells
double Tick=0;       // Tick size
double OldPrice=0;   // Value of the last calculation price
//--- Create arrays to temporary store data on column opening and closing prices
double InterimOpen[];
double InterimClose[];
// +++ Program start +++

Consideriamo ora la funzione OnInit(). Legherà i buffer degli indicatori a matrici unidimensionali. Imposteremo anche il valore dell'indicatore senza rendering per una visualizzazione più accurata e

calcolare il valore della variabile ausiliaria Tick (dimensione di un segno di spunta) per i calcoli. Inoltre, imposteremo la combinazione di colori e l'ordine di indicizzazione nei buffer degli indicatori come serie temporali. Ciò è necessario per calcolare convenientemente i valori dell'indicatore.

// +++ The OnInit function +++
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,CandlesBufferOpen,INDICATOR_DATA);
   SetIndexBuffer(1,CandlesBufferHigh,INDICATOR_DATA);
   SetIndexBuffer(2,CandlesBufferLow,INDICATOR_DATA);
   SetIndexBuffer(3,CandlesBufferClose,INDICATOR_DATA);
   SetIndexBuffer(4,CandlesBufferColor,INDICATOR_COLOR_INDEX);
//--- Set the value of the indicator without rendering
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//--- Calculate the size of one tick
   Tick=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE);
//--- Set the color scheme
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,ColorUp);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,ColorDown);
//--- Set the indexing order in arrays as time series
   ArraySetAsSeries(CandlesBufferClose,true);
   ArraySetAsSeries(CandlesBufferColor,true);
   ArraySetAsSeries(CandlesBufferHigh,true);
   ArraySetAsSeries(CandlesBufferLow,true);
   ArraySetAsSeries(CandlesBufferOpen,true);
//--- Check the input parameter for correctness
   if(CellForChange<2)
      Alert("The CellForChange parameter must be more than 1 due to plotting peculiarities");
//---
   return(0);
  }
// +++ The OnInit function +++

Siamo arrivati al vero "cuore" dell'indicatore, la funzione OnCalculate() dove verranno effettuati i calcoli. I calcoli dei valori degli indicatori sono suddivisi in sei funzioni di base che verranno chiamate da OnCalculate(). Diamo un'occhiata a loro:

1.  Funzione per la copia dei dati

Questa funzione copia i dati dalle barre dei minuti in una matrice per i calcoli. Innanzitutto, ridimensioniamo l'array di ricezione e quindi copiamo i prezzi di apertura in esso utilizzando la funzione CopyOpen().

//+------------------------------------------------------------------+
//| Function for copying data for the calculation                    |
//+------------------------------------------------------------------+
int FuncCopy(int HistoryInt)
  {
//--- Resize the array for copying calculation data
   ArrayResize(OpenPrice,(HistoryInt));
//--- Copy data from the minute bars to the array
   int Open=CopyOpen(Symbol(),PERIOD_M1,0,(HistoryInt),OpenPrice);
//---
   return(Open);
  }

2.  Funzione per il calcolo del numero di colonne

Questa funzione calcola il numero di colonne per i grafici a point and figure.

I calcoli vengono eseguiti in un ciclo iterando sul numero di barre sull'intervallo di tempo dei minuti che sono state copiate nella funzione sopra. Il ciclo stesso è costituito da tre blocchi principali per diversi tipi di trend:

  •  0 - andamento indefinito.
  •  1 - tendenza al rialzo.
  • -1 - Downtrend

Il trend indefinito verrà utilizzato una sola volta per determinare il movimento iniziale del prezzo. La direzione del movimento del prezzo sarà determinata quando il valore assoluto della differenza tra il mercato attuale e il prezzo iniziale supera il valore del Box Size moltiplicato per l'importo di storno.

In caso di breakout al ribasso, il trend iniziale verrà identificato come un trend ribassista e verrà inserita una corrispondente voce nella variabile Trend. Un trend rialzista viene identificato in modo esattamente opposto. Inoltre, verrà aumentato il valore della variabile per il numero di colonne, ColumnsInt.

Una volta identificato il trend attuale, impostiamo due condizioni per ogni direzione. Se il prezzo continua a muoversi nella direzione del trend attuale per Box Size, il valore della variabile ColumnsInt rimarrà invariato. Se il prezzo si inverte per il Box Size moltiplicato per l'Reversal Amount, apparirà una nuova colonna e il valore della variabile ColumnsInt aumenterà di uno.

E così via finché non vengono identificate tutte le colonne. 

Per arrotondare il numero di celle nel ciclo, utilizzeremo la funzione MathRound() che ci consente di arrotondare i valori risultanti agli interi più vicini. Facoltativamente, questa funzione può essere sostituita con la funzione MathFloor() (arrotondando per difetto all'intero più vicino) o la funzione MathCeil() (arrotondando per eccesso all'intero più vicino), a seconda del grafico richiesto.

//+------------------------------------------------------------------+
//| Function for calculating the number of columns                   |
//+------------------------------------------------------------------+
int FuncCalculate(int HistoryInt)
  {
   int ColumnsInt=0;

//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   FirstTrend=0;            // Direction of the initial market trend
   Columns=0;               // Variable for the calculation of columns
   InterimOpenPrice=0;
   InterimClosePrice=0;
   NumberCell=0;            // Variable for the calculation of cells
//--- Loop for the calculation of the number of main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
           }
         BeginPrice=InterimClosePrice;
         ColumnsInt++;
         FirstTrend=Trend;
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice+(Cell*Tick);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice-(Cell*Tick);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
     }
//---
   return(ColumnsInt);
  }

3.  Funzione per colorare le colonne

Questa funzione è pensata per colorare le colonne come richiesto utilizzando lo schema di colori preimpostato. A questo scopo, scriveremo un ciclo iterando sul numero di colonne e imposteremo i colori appropriati su colonne pari e dispari, prendendo in considerazione il valore del trend iniziale (colonna iniziale).

//+------------------------------------------------------------------+
//| Function for coloring columns                                    |
//+------------------------------------------------------------------+
int FuncColor(int ColumnsInt)
  {
   int x;
//--- Fill the buffer of colors for drawing
   for(x=0; x<ColumnsInt; x++)
     {
      if(FirstTrend==-1)
        {
         if(x%2==0) CandlesBufferColor[x]=1; // All even buffers of color 1
         if(x%2>0) CandlesBufferColor[x]=0;  // All odd buffers of color 0
        }
      if(FirstTrend==1)
        {
         if(x%2==0) CandlesBufferColor[x]=0; // All odd buffers of color 0
         if(x%2>0) CandlesBufferColor[x]=1;  // All even buffers of color 1
        }
     }
//---
   return(x);
  }

4.  Funzione per determinare la dimensione della colonna

Una volta determinato il numero di colonne da utilizzare e impostati i colori necessari, è necessario determinare l'altezza delle colonne. Per fare ciò, creeremo array temporanei, InterimOpen[] eInterimClose[], in cui memorizzeremo i prezzi di apertura e chiusura per ogni colonna. La dimensione di questi array sarà uguale al numero di colonne.

Quindi, avremo un ciclo che è quasi completamente identico al ciclo della funzione FuncCalculate(), la sua differenza è che oltre a tutto quanto sopra memorizza anche i prezzi di apertura e chiusura per ciascuna delle colonne. Questa separazione è implementata in modo da conoscere in anticipo il numero di colonne nel grafico. In teoria, potremmo inizialmente impostare un numero consapevolmente maggiore di colonne per l'allocazione della memoria dell'array e farlo solo con un solo ciclo. Ma in quel caso avremmo un maggiore utilizzo delle risorse di memoria.

Diamo ora uno sguardo più dettagliato alla determinazione dell'altezza della colonna. Dopo che il prezzo ha spostato la distanza pari al numero richiesto di Dimensioni scatola, calcoliamo il loro numero, arrotondandolo all'intero più vicino. Quindi aggiungiamo il numero totale di Dimensioni scatola della colonna corrente al prezzo di apertura della colonna, ottenendo così il prezzo di chiusura della colonna, che diventa anche l'ultimo prezzo utilizzato. Sarà coinvolto in tutte le ulteriori azioni.

//+------------------------------------------------------------------+
//| Function for determining the column size                         |
//+------------------------------------------------------------------+
int FuncDraw(int HistoryInt)
  {
//--- Determine the sizes of temporary arrays
   ArrayResize(InterimOpen,Columns);
   ArrayResize(InterimClose,Columns);
//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   NumberCell=0;            // Variable for the calculation of cells
   int z=0;                 // Variable for indices of temporary arrays
//--- Loop for filling the main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits()); // Normalize the number of decimal places
            Trend=1;
           }
         BeginPrice=InterimClose[z];
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice+(Cell*Tick);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice-(Cell*Tick);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
     }
//---
   return(z);
  }

5.  Funzione per l'inversione dell'array

La funzione inverte i dati dell'array di colonne ottenuti in modo da visualizzare ulteriormente il grafico a livello di codice da destra a sinistra. L'inversione dell'array viene eseguita in un ciclo, con i valori High e Low assegnati alle candele. Questo viene fatto perché l'indicatore viene visualizzato solo per i candelabri per i quali tutti i valori del buffer dell'indicatore non sono uguali a zero.

//+------------------------------------------------------------------+
//| Function for array reversal                                      |
//+------------------------------------------------------------------+
int FuncTurnArray(int ColumnsInt)
  {
//--- Variable for array reversal
   int d=ColumnsInt;
   for(int x=0; x<ColumnsInt; x++)
     {
      d--;
      CandlesBufferOpen[x]=InterimOpen[d];
      CandlesBufferClose[x]=InterimClose[d];
      if(CandlesBufferClose[x]>CandlesBufferOpen[x])
        {
         CandlesBufferHigh[x]=CandlesBufferClose[x];
         CandlesBufferLow[x]=CandlesBufferOpen[x];
        }
      if(CandlesBufferOpen[x]>CandlesBufferClose[x])
        {
         CandlesBufferHigh[x]=CandlesBufferOpen[x];
         CandlesBufferLow[x]=CandlesBufferClose[x];
        }
     }
//---
   return(d);
  }

6.  Funzione per disegnare linee orizzontali

Questa funzione crea una griglia di "scatole" utilizzando linee orizzontali (oggetti). All'inizio della funzione, determiniamo i valori di prezzo massimo e minimo dall'array dei dati di calcolo. Questi valori vengono ulteriormente utilizzati per tracciare gradualmente le linee su e giù dal punto di partenza.

//+------------------------------------------------------------------+
//| Function for drawing horizontal lines                            |
//+------------------------------------------------------------------+
int FuncDrawHorizontal(bool Draw)
  {
   int Horizontal=0;
   if(Draw==true)
     {
      //--- Create horizontal lines (lines for separation of columns)
      ObjectsDeleteAll(0,ChartWindowFind(),OBJ_HLINE); // Delete all old horizontal lines
      int MaxPriceElement=ArrayMaximum(OpenPrice);     // Determine the maximum price level
      int MinPriceElement=ArrayMinimum(OpenPrice);     // Determine the minimum price level
      for(double x=OpenPrice[0]; x<=OpenPrice[MaxPriceElement]+(Cell*Tick); x=x+(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      for(double x=OpenPrice[0]-(Cell*Tick); x>=OpenPrice[MinPriceElement]; x=x-(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      ChartRedraw();
     }
//---
   return(Horizontal);
  }

Ora che abbiamo descritto tutte le funzioni base, vediamo l'ordine con cui vengono chiamate in OnCalculate():

  • Avvia la funzione per copiare i dati per i calcoli (a condizione che non ci siano ancora barre calcolate).
  • Chiama la funzione per calcolare il numero di colonne.
  • Determina i colori delle colonne.
  • Determinare le dimensioni delle colonne.
  • Chiama la funzione per l'inversione dei dati negli array.
  • Chiama la funzione per tracciare linee orizzontali che divideranno le colonne in "riquadri".
// +++ Main calculations and plotting +++
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Reverse the array to conveniently get the last price value
   ArraySetAsSeries(close,true);
//---
   if(prev_calculated==0)
     {
      //--- Start the function for copying data for the calculation
      int ErrorCopy=FuncCopy(History);
      //--- In case of error, print the message
      if(ErrorCopy==-1)
        {
         Alert("Failed to copy. Data is still loading.");
         return(0);
        }
      //--- Call the function for calculating the number of columns
      Columns=FuncCalculate(History);
      //--- Call the function for coloring columns
      int ColorCalculate=FuncColor(Columns);
      //--- Call the function for determining column sizes
      int z=FuncDraw(History);
      //--- Start the function for array reversal
      int Turn=FuncTurnArray(Columns);
      //--- Start the function for drawing horizontal lines
      int Horizontal=FuncDrawHorizontal(true);
      //--- Store the value of the last closing price in the variable
      OldPrice=close[0];
     }
//--- If the price is one box size different from the previous one, 
//--- the indicator is recalculated
   if(fabs((OldPrice-close[0])/Tick)>Cell)
      return(0);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
// +++ Main calculations and plotting +++

Questa è la fine del codice di base dell'indicatore. Ma poiché l'indicatore ha i suoi svantaggi in quanto contiene array complessi, a volte deve essere ricaricato.

Per implementare ciò, utilizzeremo la funzione OnChartEvent() che gestisce gli eventi della pressione del tasto "С" - cancella e il tasto  "R" - ridisegna. Per azzerare, ad uno dei buffer indicatori viene assegnato un valore zero. La funzione per il ridisegno del grafico rappresenta una ripetizione dei calcoli precedenti e l'assegnazione dei valori ai buffer indicatori.

// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_KEYDOWN)
     {
      //--- 67 - The "C" key code clears the indicator buffer
      if(lparam==67)
        {
         for(int x=0; x<Bars(Symbol(),PERIOD_CURRENT); x++)
            CandlesBufferOpen[x]=0;
         ChartRedraw();
        }
      // 82 - The "R" key code redraws the indicator
      if(lparam==82)
        {
         //--- Start the copying function
         int ErrorCopy=FuncCopy(History);
         //--- In case of error, print the message
         if(ErrorCopy==-1)
            Alert("Failed to copy data.");
         //--- Call the function for calculating the number of columns
         Columns=FuncCalculate(History);
         //--- Call the function for coloring columns
         int ColorCalculate=FuncColor(Columns);
         //--- Call the function for determining column sizes
         int z=FuncDraw(History);
         //--- Start the function for array reversal
         int Turn=FuncTurnArray(Columns);
         //--- Start the function for drawing horizontal lines
         int Horizontal=FuncDrawHorizontal(true);
        }
     }
  }
//+------------------------------------------------------------------+
// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++

Ora possiamo fare un sospiro di sollievo perché abbiamo finito con la descrizione dell'algoritmo e del codice dell'indicatore e possiamo procedere a dare un'occhiata ad alcuni pattern grafici Point e Figure che generano segnali per l'esecuzione di operazioni.

 

Segnali Standard

Esistono due approcci nel trading e nell'analisi dei grafici Point e Figure: basati su pattern e basati su linee di supporto e resistenza. Quest'ultimo è peculiare in quanto le linee di supporto e resistenza sono tracciate con un angolo di 45 gradi (non è sempre così in un indicatore progettato in quanto è tracciato utilizzando candele giapponesi la cui dimensione cambia a seconda delle dimensioni del grafico principale, che può portare alla distorsione angolare).

Consideriamo ora i modelli in quanto tali:

  1. Motivi "Double Top" e "Double Bottom".

    Il "Double Top" si verifica quando il prezzo sale, poi scende formando una certa colonna di O e sale di nuovo, superando la precedente colonna di X di una dimensione della scatola. Questo modello è un segnale di acquisto.

    "Double Bottom" è l'esatto opposto del modello "Double Top". C'è un certo calo del prezzo (una colonna di O) seguita da una colonna di X e un'altra colonna di O che cade una casella sotto la precedente colonna di O, formando così un segnale di vendita.

    Fig. 3. Motivi "Double Top" e "Double Bottom".

    Fig. 3. Motivi "Double Top" e "Double Bottom".

  2. Motivi "Triple Top" e "Triple Bottom".

    Questi schemi sono meno frequenti ma rappresentano segnali molto forti. Essenzialmente, sono simili ai modelli "Double Top" e "Double Bottom", essendo la loro continuazione. Prima di trasmettere un segnale, ripetono i movimenti dei due schemi precedenti.

    Un "Triple Top" si verifica quando il prezzo raggiunge lo stesso livello di prezzo due volte e poi supera quel livello, rappresentando un segnale di acquisto.

    Il modello "Triple Bottom" è opposto a "Triple Top" e si verifica quando il prezzo scende allo stesso livello due volte e poi rompe al di sotto di quel livello, trasmettendo così un segnale di vendita.

    Fig. 4. Motivi "Triple Top" e "Triple Bottom".

    Fig. 4. Motivi "Triple Top" e "Triple Bottom".

  3. Pattern "Symmetrical Triangle Breakout": rialzo e ribasso.

    Tutti ricordiamo i modelli di analisi tecnica. Il pattern "Symmetrical Triangle Breakout" è simile al "Symmetrical Triangle" nell'analisi tecnica. Un breakout al rialzo (come mostrato sotto nella figura a sinistra) è un segnale di acquisto. Al contrario, una ripartizione al ribasso è un segnale di vendita (figura a destra).

    Fig. 5. "Triangolo simmetrico Breakout": rialzo e ribasso.

    Fig. 5. "Symmetrical Triangle Breakout": rialzo e ribasso.

  4. Modelli "Catapulta rialzista" e "Catapulta ribassista".

    Le "catapulte" sono in qualche modo simili ai modelli "triangolo ascendente" e "triangolo discendente" dell'analisi tecnica. I loro segnali sono essenzialmente simili: non appena il prezzo si rompe sopra o sotto il lato parallelo del triangolo, si verifica rispettivamente un segnale di acquisto o di vendita. Nel caso della "Catapulta rialzista", il prezzo scoppia sopra, che è un segnale di acquisto (figura a sinistra), mentre nel caso della "Catapulta ribassista" il prezzo scoppia sotto, che è un segnale di vendita ( figura a destra).

    Fig. 6. Schemi "Catapulta rialzista" e "Catapulta ribassista".

    Fig. 6. Modelli "Catapulta rialzista" e "Catapulta ribassista".

  5. Motivo "Linea di tendenza a 45 gradi".

    Il modello "Linea di tendenza a 45 gradi" crea un supporto o una linea di resistenza. Se c'è un breakout di quella linea, otteniamo un segnale di vendita (come mostrato nella figura a destra) o un segnale di acquisto (come nella figura a sinistra).

    Fig. 7. Schema "Linea di tendenza a 45 gradi".

    Fig. 7. Motivo "Linea di tendenza a 45 gradi".

Abbiamo esaminato i modelli e i segnali di grafici a point and figure standard. Vediamo ora alcuni di essi nel grafico degli indicatori fornito all'inizio dell'articolo:

Fig. 8. Identificazione dei pattern nel grafico Point and Figure.

Fig. 8. Identificazione dei modelli nel grafico Point and Figure.

 

Conclusione

Siamo giunti alla fase finale dell'articolo. Vorrei dire qui che il grafico Point and Figure è ancora utilizzato attivamente, il che dimostra ancora una volta il suo valore.

L'indicatore sviluppato, sebbene non privo di inconvenienti, come la creazione di grafici a blocchi al posto delle solite X e O e l'impossibilità di essere testato (o meglio, un funzionamento errato) nello Strategy Tester, fornisce risultati grafici abbastanza accurati.

Voglio anche far notare che questo algoritmo, in una versione leggermente modificata, può essere potenzialmente utilizzato per la creazione di grafici Renko, nonché per l'integrazione di entrambi i tipi di grafici in un unico codice con opzioni di menu, dandoti quindi la scelta, a seconda dei casi. Non escludo la possibilità di tracciare il grafico direttamente nella finestra principale, il che ancora una volta richiederà una leggera modifica del codice.

In generale, lo scopo di questo articolo era condividere le mie idee sullo sviluppo degli indicatori. Essendo questo il mio primo articolo, apprezzerò qualsiasi commento o feedback. Grazie per il tuo interesse per il mio articolo!

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/656

File allegati |
apfd.mq5 (18.85 KB)
MQL5 Cloud Network: Stai ancora calcolando? MQL5 Cloud Network: Stai ancora calcolando?
Presto sarà passato un anno e mezzo dal lancio di MQL5 Cloud Network. Questo evento all'avanguardia ha inaugurato una nuova era del trading algoritmico: ora con un paio di clic, i trader possono avere centinaia e migliaia di core di calcolo a loro disposizione per l'ottimizzazione delle loro strategie di trading.
Debug dei programmi MQL5 Debug dei programmi MQL5
Questo articolo è destinato principalmente ai programmatori che hanno già imparato il linguaggio ma non padroneggiano completamente lo sviluppo del programma. Rivela alcune tecniche di debug e presenta un'esperienza combinata dell'autore e di molti altri programmatori.
Manuale MQL5: Gestire gli eventi tipici dei grafici Manuale MQL5: Gestire gli eventi tipici dei grafici
Questo articolo prende in considerazione gli eventi tipici dei grafici e include esempi della loro elaborazione. Ci concentreremo su eventi del mouse, sequenze di tasti, creazione/modifica/rimozione di un oggetto grafico, clic del mouse su un grafico e su un oggetto grafico, spostamento di un oggetto grafico con il mouse, completamento della modifica del testo in un campo di testo, nonché sugli eventi di modifica del grafico. Viene fornito un campione di un programma MQL5 per ogni tipo di evento considerato.
Manuale MQL5: Ridurre l'effetto dell'overfitting e gestire la mancanza di preventivi Manuale MQL5: Ridurre l'effetto dell'overfitting e gestire la mancanza di preventivi
Qualunque sia la strategia di trading che utilizzi, rimarrà sempre la domanda: “quali parametri scegliere per garantire profitti futuri?” Questo articolo fornisce un esempio di Expert Advisor con la possibilità di ottimizzare più parametri di simboli contemporaneamente. Questo metodo ha lo scopo di ridurre l'effetto dei parametri di overfitting e gestire situazioni in cui i dati di un singolo simbolo non sono sufficienti per lo studio.