Der letzte Kreuzzug
Einleitung
Im MetaTrader 5 Terminal (sowie in MetaTrader 4) gibt es drei Standardmittel zur Darstellung von Instrumentpreisen: Balken, Kerzen und Linien. Im Wesentlichen stellen alle drei das Gleiche dar – Zeitdiagramme. Zusätzlich zur herkömmlichen Methode der zeitlichen Darstellung des Preises gibt es auch andere, nicht mit der Zeit verbundene Mittel, die bei Investoren und Spekulanten ziemlich beliebt sind: Renko- und Kagi-Diagramme, Three-Line-Break- und Punkt- und Zeichendiagramme.
Ich werde ihre Vorteile gegenüber den Klassikern nicht erörtern, doch die Entfernung der Zeitvariable aus der Gesamtrechnung hilft einigen Händlern dabei, sich auf die Preisvariable zu konzentrieren. Ich schlage vor, dass wir Punkt- und Zeichendiagramme zusammen mit einem entsprechenden Algorithmus zum Zeichnen von Diagrammen betrachten, auf dem Markt weit verbreitete Produkte ansehen, die zur Erzeugung solcher Diagramme vorgesehen sind, und ein einfaches Script schreiben, in dem der Algorithmus umgesetzt wird. Das Buch "Point and Figure Charting: The Essential Application for Forecasting and Tracking Market Prices" von Thomas J. Dorsey dient uns als ABC.
Bull's-Eye Broker ist das beliebteste Softwarepaket für das Zeichnen von Offline-Diagrammen. Die Software steht für einen 21-tägigen Test zur Verfügung (es sind zahlreiche Tests möglich) und während der Beta-Periode ist die neue Beta-Version verfügbar. Dieses Softwarepaket wird verwendet, um die Ausführungsergebnisse unseres Scripts zu bewerten. Eine der besten Online-Ressourcen in puncto Punkt- und Zeichendiagramme ist StockCharts. Die Webseite ist börsenorientiert, deshalb gibt sie leider keine Preise von Forex-Instrumenten an.
Zum Vergleich der Ausführungsergebnisse des von uns geschriebenen Scripts werden Futures für Gold, und leichtes Rohöl und Diagramme für S&P-500-Differenzkontrakte mithilfe der Software und der Webseite erzeugt. Ein EURUSD-Preisdiagramm wird nur mithilfe von Bull's-Eye Broker gezeichnet (denken Sie an die Beschränkungen von StockChart).
Algorithmus für das Zeichnen von Punkt- und Zeichendiagrammen
Hier ist also der Algorithmus.
Für das Zeichnen von Punkt- und Zeichendiagrammen gibt es zwei zentrale Parameter:
- Box-Größe, die Mindeständerung des Instrumentpreises; Änderungen, die unter der Mindestpreisänderung liegen, wirken sich nicht auf das Diagramm aus;
- Umkehr, die Anzahl der Boxen, die die Preisbewegung in entgegengesetzter Richtung zur aktuellen Richtung des Diagramms darstellen, woraufhin diese Bewegung in der neuen Spalte angezeigt wird.
Da die Zeichnung von Diagrammen die Historie der in Form von Open-High-Low-Close-Preisen gespeicherten Gebote benötigt, nehmen wir Folgendes an:
- Das Diagramm wird basierend auf High-Low-Preisen gezeichnet;
- Der High-Preis wird auf die Box-Größe abgerundet (MathFloor), der Low-Preis wird auf die Box-Größe aufgerundet (MathCeil).
Lassen Sie es mich anhand eines Beispiels erklären. Nehmen wir an, wir wollen ein Diagramm für leichtes Rohöl mit einer Box-Größe gleich 1 (ein) $ und einer Box-Umkehr von 3 (drei) zeichnen. Das bedeutet, dass alle High-Preise auf den nächstniedrigeren 1 $ abgerundet und alle Low-Preise auf die gleiche Weise aufgerundet werden:
Datum | High | Low | XO High | XO Low |
---|---|---|---|---|
13.02.2012 | 100,86 | 99,08 | 100 | 100 |
14.02.2012 | 101,83 | 100,28 | 101 | 101 |
15.02.2012 | 102,53 | 100,62 | 102 | 101 |
16.02.2012 | 102,68 | 100,84 | 102 | 101 |
17.02.2012 | 103,95 | 102,24 | 103 | 102 |
X (Kreuze) werden zum Illustrieren einer Aufwärtsbewegung des Preises im Diagramm verwendet, O (Kreise) stellen eine Abwärtsbewegung des Preises dar.
Bestimmung der ursprünglichen Richtung des Preises (ob die erste Spalte X oder O ist):
Merken Sie sich die Werte XO High und XO Low von [Bars-1] und warten Sie bis:
- der Wert XO Low um die Umkehrmenge der Boxen im Vergleich zum ursprünglichen XO High sinkt (die erste Spalte ist O); oder
- der Wert XO High um die Umkehrmenge der Boxen im Vergleich zum ursprünglichen XO Low steigt (die erste Spalte ist X).
In unserem Beispiel mit leichtem Rohöl müssen wir uns merken: XO High[Bars-1]=100 und XO Low [Bars-1]=100.
Warten Sie weiter, um zu sehen, was vorher geschieht:
- Der Wert XO Low[i] des nächsten Balkens wird kleiner oder gleich 97 $, was darauf hindeutet, dass die erste Spalte O ist; oder
- Der Wert XO High[i] des nächsten Balkens wird größer oder gleich 103 $, was darauf hindeutet, dass die erste Spalte X ist.
Wir können die erste Spalte am 17. Februar bestimmen: Der Preis XO High hat 103 $ erreicht und die erste Spalte ist X. Stellen Sie sie durch vier X von 100$ bis 103 $ dar.
Bestimmung der weiteren Bewegungen des Diagramms:
Falls die aktuelle Spalte X ist, prüfen Sie, ob XO High des aktuellen Balkens im Vergleich zum aktuellen Preis XO um die Box-Größe gestiegen ist (d. h. wir prüfen am 20. Februar zuerst, ob XO High größer oder gleich 104 $ ist). Falls XO High[2012.02.20] 104 $ oder 105 $ oder größer ist, fügen wir die entsprechende Menge von X zur bestehenden Spalte aus X hinzu.
Falls XO High des aktuellen Balkens im Vergleich zum aktuellen Preis XO nicht um die Box-Größe gestiegen ist, prüfen Sie, ob XO Low des aktuellen Balkens um die Umkehrmenge der Boxen kleiner als XO High ist (in unserem Beispiel: Falls XO Low[2012.02.20] kleiner oder gleich 103 $ -3*1 $=100 $ ist oder 99 $ oder weniger). Falls er kleiner ist, zeichnen wir eine Spalte von O rechts neben der Spalte von X von 102 $ bis 100 $.
Falls die aktuelle Spalte O ist, gelten alle oben aufgeführten Überlegungen in umgekehrter Reihenfolge.
WICHTIG: Jede neue Spalte von O wird immer rechts und eine Box unterhalb vom High-Wert der vorhergehenden Spalte von X gezeichnet und jede neue Spalte von X wird immer rechts und eine Box oberhalb vom Low-Wert der vorhergehenden Spalte von O gezeichnet.
Die Prinzipien der Diagrammzeichnung sind nun klar. Fahren wir mit den Unterstützungs- und Widerstandslinien fort.
Unterstützungs- und Widerstandslinien in konventionellen Punkt- und Zeichendiagrammen haben immer einen Winkel von 45 Grad.
Die erste Linie hängt von der ersten Spalte ab. Falls die erste Spalte X ist, so ist die erste Linie eine Widerstandslinie, die eine Box über dem Maximalwert der ersten Spalte beginnt und um 45 Grad nach UNTEN und rechts geneigt ist. Falls die erste Spalte O ist, so ist die erste Linie eine Unterstützungslinie, die eine Box unter dem Minimalwert der ersten Spalte beginnt und um 45 Grad nach OBEN und rechts geneigt ist. Unterstützungs- und Widerstandslinien werden gezeichnet, bis sie das Preisdiagramm erreichen.
Sobald die Unterstützungs-/Widerstandslinie das Preisdiagramm erreicht, beginnen wir mit der Zeichnung einer entsprechenden Widerstands-/Unterstützungslinie. Das Schlüsselprinzip beim Zeichnen ist es, sicherzustellen, dass die gezeichnete Linie sich weiter rechts von der vorhergehenden Trendlinie im Diagramm befindet. Um also eine Unterstützungslinie zu zeichnen, identifizieren wir zuerst den Mindestwert des Diagramms unter der soeben gezeichneten Widerstandslinie und zeichnen die Unterstützungslinie ab einem Ausgangspunkt eine Box unterhalb des identifizierten Minimums nach OBEN und rechts, bis sie das Diagramm oder die letzte Spalte des Diagramms erreicht.
Falls die Unterstützungslinie, die ab dem Minimum unter der vorhergehenden Widerstandslinie beginnt, nach oben geht und unter derselben Widerstandslinie auf das Diagramm stößt, bewegen Sie sich nach rechts und finden Sie ein neues Preisminimum innerhalb des Bereichs zwischen dem niedrigsten Minimum unter der Widerstandslinie und dem Ende der Widerstandslinie. Fahren Sie fort, bis die so gezeichnete Trendlinie nach rechts hinter die vorhergehende Trendlinie verläuft.
Alle oben aufgeführten Überlegungen werden klarer sein, wenn sie anhand von realen Diagrammbeispielen erklärt werden, die weiter unten dargelegt sind.
Jetzt haben wir den Algorithmus für die Diagrammzeichnung geklärt. Erweitern wir unser Script um einige praktische Funktionen:
- Modusauswahl: Diagrammzeichnung nur für ein aktuelles Symbol oder für alle Symbole in MarketWatch;
- Timeframe-Auswahl (es erscheint logischer, Diagramme von 100 Pips auf Daily-Timeframes und Diagramme von 1-3 Pips auf M1 zu zeichnen);
- Festlegen der Box-Größe in Pips;
- Festlegen der Menge der Boxen für die Umkehrung;
- Festlegen der Menge der Zeichen für die Darstellung von Volumina (Tick-Volumina im Script, da ich noch auf keine Broker gestoßen bin, die reale Volumina anbieten) in Spalten und Zeilen (wie der Indikator MarketDepth);
- Festlegen der Tiefe der Historie, auf deren Basis das Diagramm gezeichnet wird;
- Auswahl des Ausgabeformats – die Ergebnisse lassen sich als Text- oder Grafikdateien speichern;
- Und zu guter Letzt eine Funktion für Neueinsteiger: automatische Diagrammzeichnung (Autocharting, legt die Box-Größe automatisch auf Basis der erforderlichen Höhe des Diagramms fest).
Da der Algorithmus nun beschrieben wurde und die Anforderungen klar sind, ist es höchste Zeit, das Script vorzustellen.
//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+
Abhängig vom Wert des Eingabeparameters pic werden die Ergebnisse des Scripts entweder in Form von Textdateien mit Grafikdateien (Datenverzeichnis_des_Terminals\MQL5\Images) oder nur in Form von Textdateien (Datenverzeichnis_des_Terminals\MQL5\Files) generiert.
Vergleich der Ergebnisse
Um die Ergebnisse zu vergleichen, zeichnen wir ein Diagramm für leichtes Rohöl mit den folgenden Parametern: Box-Größe ist 1 $, Umkehr beträgt 3 Boxen.
StockCharts.com:
Abb. 1. Von StockCharts.com generiertes Punkt- und Zeichendiagramm für leichtes Rohöl
Bull's-Eye Broker:
Abb. 2. Von Bull's-Eye Broker generiertes Punkt- und Zeichendiagramm für leichtes Rohöl
Ausführungsergebnisse unseres Scripts:
Abb. 3. Von unserem Script generiertes Punkt- und Zeichendiagramm für leichtes Rohöl
Alle drei Diagramme sind identisch. Glückwunsch! Wir haben uns das Zeichnen von Punkt- und Zeichendiagrammen angeeignet.
Typische Muster von Punkt- und Zeichendiagrammen
Wie können wir sie nutzen?
Sehen wir uns zunächst typische Muster an, insbesondere deshalb, weil sie sich an einer Hand abzählen lassen.
Diese sind:
Abb. 4. Preismuster: Doppelter Höchstpunkt, Dreifacher Höchstpunkt, Doppelte Basis, Dreifache Basis
außerdem:
Abb. 5. Preismuster: Bullen-Dreieck und Bären-Dreieck
und zu guter Letzt:
Abb. 6. Preismuster: Bullen-Katapult und Bären-Katapult
Jetzt noch ein paar Tipps.
- Öffnen Sie über der Unterstützungslinie nur lange Positionen und unterhalb der Widerstandslinie nur kurze. Zum Beispiel: Öffnen Sie nur lange Positionen für Futures für leichtes Rohöl ab Mitte Dezember 2011 nach dem Durchbrechen der Widerstandslinie, die sich seit Ende September 2011 geformt hat.
- Nutzen Sie Unterstützungs- und Widerstandslinien für das Trailing von Stop-Loss-Ordern.
- Zählen Sie vertikal, bevor Sie eine Position öffnen, um ein Verhältnis zwischen möglichem Gewinn und möglichem Verlust schätzen zu können.
Die vertikale Zählung lässt sich anhand des folgenden Beispiels besser veranschaulichen.
Im Dezember 2011 bewegte sich die Spalte von X vom Ausgangspreis von 76 $ nach oben, über die vorhergehende Spalte von X bei 85 $, durchbrach die Widerstandslinie bei 87 $ und erreichte 89 $. Gemäß der vertikalen Zählung deutet dies darauf hin, dass der Preis steigen und das Niveau von 76 $+(89 $-75 $)*3 (Umkehr von 3 Boxen)=118 $ erreichten könnte.
Die nächste Bewegung war korrektiv und brachte den Preis auf ein Niveau von 85 $. Spekulanten können eine Stop-Loss-Order für eine lange Position bei 1$ weniger platzieren, d. h. bei 84 $.
Der Eintritt in die lange Position kann nach einer abgeschlossenen korrektiven Bewegung eine Box höher als die vorherige Spalte von X, d. h. beim Preis von 90 $, geplant werden.
Lassen Sie uns den möglichen Verlust schätzen. Er könnte bei 90 $-84 $=6 $ liegen. Der mögliche Gewinn kann 118 $-90$=28 $ erreichen. Verhältnis möglicher Gewinn-möglicher Verlust: 28 $/6 $>4,5. Meiner Meinung nach eine gute Performance. Schon jetzt hätten wir einen Gewinn von 105 $-90 $=15 $ pro Future-Kontrakt.
Lizenzen
Das Script wurde unter BSD-Lizenz durch Roman Rich geschrieben und bereitgestellt. Den Lizenztext finden Sie in der Datei Lic.txt. Die Bibliothek cIntBMP wurde von Dmitry aka Integer erstellt. StockCharts.com und Bull's-Eye Broker sind eingetragene Schutzmarken und Eigentum ihrer jeweiligen Inhaber.
Fazit
In diesem Beitrag wurden ein Algorithmus und ein Script für das Zeichnen von Punkt- und Zeichendiagrammen ("X und O") vorgeschlagen. Es wurden diverse Preismuster betrachtet, deren praktische Nutzung in den bereitgestellten Empfehlungen umrissen wurde.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/368
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.