Analiza volatilitatii : de la previziunea volatilitatii la previziunea pretului
[English version] [MQLmagazine.com in english] [Editia romaneasca]
Volatilitate. Un cuvant care genereaza panica pentru investitori, ca si batai de inima mai accelerate pentru traderi. De cate ori inainte n-ai incercat sa tranzactionezi numai pietele in trend si sa prinzi momentum-ul ?
Dupa InvestorWords.com, volatilitatea este rata relativa la care pretul unei valori mobiliare se misca in sus si in jos. Volatilitatea este gasita calculand deviatia standard anualizata a schimbarilor zilnice de pret. Daca pretul unei actiuni se misca in sus sau in jos rapid in perioade scurte de timp, are volatilitate ridicata. Daca pretul aproape ca nu se schimba deloc, are volatilitate redusa. Deci volatilitatea este masurata de deviatia standard (notata cu litera greceasca sigma mica). Si care e cel mai popular indicator bazat pe deviatia standard? Ati ghicit, faimoasele Benzi Bollinger. Benzile Bollinger (BB) sunt printre primii indicatori pe care ai invatat sa-i folosesti in trading. Ti s-a spus ca, un semnal de cumparare sau de vanzare cand piata atinge una dintre benzi, este urmat de un posibil mers pe langa una dintre benzi, dupa care se intoarce, si va merge asa pana cand atinge media mobila. Regula asta nu spune prea mult. Nu exista nimic care sa spuna cat anume va merge pe langa banda pana cand se intoarce. Lucru care il vei descoperi foarte frustrant… pe contul propriu. Daca reprezinti grafic baza acestui indicator, deviatia standard, vei gasi un chart sinusoidal. Volatilitatea se misca in sus si in jos intr-o maniera relativ continua, fara miscari bruste. Din moment ce nu face miscari bruste, inseamna ca volatilitatea e mai predictibila decat pretul. Daca e predictibila, de ce sa nu folosim BB peste volatilitate ? Dar stai. Deviatia standard e relativa. Ea masoara dispersia pe perioada analizata. Daca adaugam BB peste deviatia standard, inseamna ca vom folosi deviatia standard a deviatiei standard pentru a prinde miscarile volatilitatii. De data asta nu e nevoie sa ne temem de
semnale false, pentru ca setul analizat de date, deviatia standard calculata asupra pretului, este o functie continua. In al doilea rand, chiar daca semnalul este gresit si volatilitatea se intoarce inapoi si se misca pe un trend descrescator, nu mai produce stricaciuni prea mari, pentru ca volatilitatea este deja jos cand un semnal este preluat si o miscare in jos a volatilitatii va insemna o miscare mai mica a pretului . Linia albastruie este deviatia standard a pretului de deschidere a ultimelor 20 de bare. Semnalele in care suntem interesati sunt cele in care volatilitatea intersecteaza propria bara Bollinger de sus. Bineinteles, nu suntem interesati in intersectiile in jos, pentru ca acestea inseamna ca volatilitatea se reduce mai repede decat ar fi normal. In timpul unui trend, o puternica contramiscare va fi reflectata printr-o miscare a volatilitatii in jos. Cele mai bune semnale apar atunci cand intersectiile cu BB de sus apar la o volatilitate istorica foarte redusa – sub 20% din intervalul de pe ecran. Daca e sa compari cu celelalte semnale de pe chart, BB peste pret intersectata cu pretul, vei vedea ca pot aparea intersectii chiar cand volatilitatea este in interiorul propriei ei BB, asadar acesta este un mod de a elimina afinitatea pentru erori a semnalelor date de intersectia pretului cu BB. Un criteriu bun intrare-iesire este atunci cand distanta dintre BB de sus a volatilitatii si volatilitate este sub 2% din intervalul de fluctuare a volatilitatii. Mai jos este versiunea MQL4 a indicatorului Benzi Bollinger peste Deviatia Standard.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | //+------------------------------------------------------------------+ //| BBoverSTD.mq4 | //| Bogdan Caramalac | //| mailto:fxeconomist@yahoo.com | //+------------------------------------------------------------------+ #property copyright "Bogdan Caramalac" #property link "mailto:fxeconomist@yahoo.com" #property indicator_separate_window #property indicator_buffers 4 #property indicator_color1 LightSeaGreen //STDDEV #property indicator_color2 Blue //MA #property indicator_color3 Yellow //Upper BB #property indicator_color4 Yellow //Lower BB //---- input parameters extern int STDLEN=20; extern int BBLEN=10; extern double BBMULT=2.0; extern string MATYPE="SMA"; extern int MALEN=0; extern int Logarithm=0; double ExtMapBuffer1[]; double ExtMapBuffer2[]; double ExtMapBuffer3[]; double ExtMapBuffer4[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- indicators SetIndexStyle(0,DRAW_LINE, 0, 1, indicator_color1); SetIndexBuffer(0, ExtMapBuffer1); SetIndexStyle(1,DRAW_LINE, 0, 1, indicator_color2); SetIndexBuffer(1, ExtMapBuffer2); SetIndexStyle(2,DRAW_LINE, 0, 1, indicator_color3); SetIndexBuffer(2, ExtMapBuffer3); SetIndexStyle(3,DRAW_LINE, 0, 1, indicator_color4); SetIndexBuffer(3, ExtMapBuffer4); //---- SetIndexDrawBegin(0,STDLEN); SetIndexDrawBegin(1,STDLEN+BBLEN-1); SetIndexDrawBegin(2,STDLEN+BBLEN-1); SetIndexDrawBegin(3,STDLEN+BBLEN-1); return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { if (Bars<=MathMax(STDLEN+BBLEN,MALEN)) return(0); int pos=Bars-1; ArraySetAsSeries(ExtMapBuffer1,true); while (pos>=0) { if (MATYPE=="SMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="EMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_EMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="SMMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } if (MATYPE=="LWMA") { ExtMapBuffer1[pos]=iStdDev(Symbol(),Period(),STDLEN,0,MODE_LWMA,MODE_OPEN,pos); if (Logarithm==1&&ExtMapBuffer1[pos]!=0.00) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); } pos--; } pos=Bars-1; while(pos>=0) { if (MATYPE=="SMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_SMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="EMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_EMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="SMMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_SMMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } if (MATYPE=="LWMA") { if (MALEN!=0) ExtMapBuffer2[pos]=iMAOnArray(ExtMapBuffer1,0,MALEN,0,MODE_LWMA,pos); else ExtMapBuffer2[pos]=EMPTY_VALUE; } pos--; } pos=Bars-1; while(pos>=0) { ExtMapBuffer3[pos]=EMPTY_VALUE; ExtMapBuffer4[pos]=EMPTY_VALUE; ExtMapBuffer3[pos]=iBandsOnArray(ExtMapBuffer1,0,BBLEN,BBMULT,0,MODE_HIGH,pos); ExtMapBuffer4[pos]=iBandsOnArray(ExtMapBuffer1,0,BBLEN,BBMULT,0,MODE_LOW,pos); pos--; } return(0); } |
Parametri:
STDLEN – perioada pentru care deviatia standard este calculata;
BBLEN – pentru care sunt calculate Benzile Bollinger;
BBMULT – multiplicatorul deviatiei standard pentru Benzile Bollinger;
MATYPE – o medie mobila ajutatoare este creata, de acest tip ; acelasi tip este aplicat la constructia deviatiei standard in primul moment;
MALEN – perioada mediei mobile ajutatoare (este dezactivata cu 0);
Logarithm – folosit logaritmarea deviatiei standard ; pentru 0, dezactivata, pentru 1, logaritm natural;
Multumiri lui Irtron si Ruptor de pe forumul mql4.com pentru reparatii aduse acestui indicator.
Functia iMAOnArray are probleme ; un indicator MQL4 trebuie sa contina mai mult decat un singur loop de calculatie daca foloseste aceasta functie.
Sa ne uitam la anatomia unui indicator MQL4.
Liniile 9 – 15 formeaza preambulul indicatorului ; este specificat daca indicatorul este intr-o fereastra separata , numarul de buffere si culoarea pentru fiecare.
Liniile 17 – 22 includ definitiile parametrilor ; ceea ce-i face sa fie parametri, spre deosebire de cele ce sunt cunoscute sub numele de variabile globale in alte limbaje de programare (de exemplu declaratiile Public in Visual Basic) este cuvantul extern in fata tipului ; cand indicatorul este instalat (tras cu mouse-ul pest chart) , acesti parametri pot fi modificati . De asemenea, acesti parametri poti fi modificati oricand de catre utilizator ; dar nici un expert sau script nu se poate atinge de parametri indicatorului care ruleaza intr-o fereastra.
Liniile 24 – 27 cuprind definitiile bufferelor ; intrucat 4 sunt specificate, 4 sunt definite. Bufferele indicatorului sunt numere care specifica tablouri in interiorul indicatorului, care contin date. De exemplu, daca un indicator ar copia datele OHLC in buffere, 0 ar fi pentru preturile de deschidere, 1 pentru high, 2 pentru low, 3 pentru inchidere. In cazul nostru, asa cum o sa vezi mai jos, 0 este pentru deviatia standard, 1 pentru media mobila ajutatoare, 2 pentru BB superioara si 3 pentru BB inferioara.
Liniile 31 – 49 constituie functia init() . Functia este apelata de fiecare data cand parametrii chartului sunt schimbati (simbol, periodicitate) sau conexiunea, intrerupta, revine. Functia defineste stilul de desenare pentru fiecare dintre buffere si asigneaza tablouri bufferelor indicatorului. De asemenea specifica unde incepe desenarea pentru fiecare buffer. Punctul de desenare trebuie sa fie ales cu grija, astfel incat datele care sunt la baza sa fie deja calculate la momentul respectiv. De exemplu, daca e sa calculezi media mobila pentru 10 elemente, punctul de incepere trebuie sa nu fie mai mare de 10 elemente sub numarul maxim de bare.
Liniile 54 – 131 constituie functia start() . Aceasta este apelata dupa functia la fiecare cotatie sosita. Incepe cu o conditie in care verifica daca sunt suficiente bare (Bars) ca sa deseneze indicatorul. Apoi seteaza pozitia de inceput a desenarii. Pozitia este un index de bara. Indicatorul este calculat de la o pozitie pozitiva care zero (bara curenta). Functia ArraySetAsSeries() altereaza modul de functionarea al unui tablou, facandu-l sa functioneze ca o serie temporala. Nu afecteaza tabloul per se. Ci spune unor functii speciale care lucreza cu tablouri sa-l trateze inversat. Functiile care calculeaza indicatori peste tablouri (ca iMAOnArray() , iStdDevOnArray() s.a.m.d.) traverseaza tabloul de la stanga spre dreapta, de la indecsi mai mici spre indecsi mai mari. Functia ArraySetAsSeries() aplicata unui tablou fac ca aceste functii sa traverseze tabloul de la dreapta la stanga, de la indecsi mai mari la indecsi mai mici, exact cum face bucla principala a unui indicator. Ce urmeaza apoi sunt buclele. Un indicator simplu calculeaza cam totul intr-o singura bucla, dar aici sunt necesare trei pentru a rezolva problemele. Primul loop va calcula deviatia standard ; al doilea va calcula media mobila ajutatoare, iar al treilea va calcula Benzile Bollinger.
Acelasi indicator, versiunea MQL5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | //+------------------------------------------------------------------+ //| BBoverSTD.mq5 | //| Copyright Bogdan Caramalac | //| mqlmagazine.com | //+------------------------------------------------------------------+ #property copyright "Bogdan Caramalac" #property link "mqlmagazine.com" #property version "1.02" #property indicator_separate_window #property indicator_buffers 4 #property indicator_applied_price PRICE_OPEN #property indicator_plots 4 #property indicator_type1 DRAW_LINE #property indicator_label1 "Stddev" #property indicator_color1 LightSeaGreen //STDDEV #property indicator_type2 DRAW_LINE #property indicator_label2 "Helper MA" #property indicator_color2 Blue //MA #property indicator_type3 DRAW_LINE #property indicator_label3 "Upper BB" #property indicator_color3 Yellow //Upper BB #property indicator_type4 DRAW_LINE #property indicator_label4 "Lower BB" #property indicator_color4 Yellow //Lower BB //---- input parameters input int STDLEN=20; input int BBLEN=10; input double BBMULT=2.0; input string MATYPE="SMA"; input int MALEN=2; input int Logarithm=0; double ExtMapBuffer1[]; double ExtMapBuffer2[]; double ExtMapBuffer3[]; double ExtMapBuffer4[]; #include <MovingAverages.mqh> int StdDevHandle; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, ExtMapBuffer1); SetIndexBuffer(1, ExtMapBuffer2); SetIndexBuffer(2, ExtMapBuffer3); SetIndexBuffer(3, ExtMapBuffer4); //---- PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,STDLEN); PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); PlotIndexSetInteger(3,PLOT_DRAW_BEGIN,STDLEN+BBLEN-1); //--- //we create iStdDev handler for use in main cycle; if (MATYPE=="SMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMA,PRICE_OPEN); if (MATYPE=="EMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_EMA,PRICE_OPEN); if (MATYPE=="LWMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_LWMA,PRICE_OPEN); if (MATYPE=="SMMA") StdDevHandle=iStdDev(Symbol(),Period(),STDLEN,0,MODE_SMMA,PRICE_OPEN); return(0); } //+------------------------------------------------------------------+ //| 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[]) { double res[]; //array to hold current iStdDev value; double buffer_helper[500]; double stddevonstddev=0.0; //current stddev on stddev double squaredsum=0.0; //current squared sum double bbma=0.0; //current ma for bollinger bands calculus double bbsum=0.0; //current sum for calculus of bbma double prevma=0.0; //previous helper moving average value, for use with SMMA or LWMA int buff_stddevs=0; //used length of stddevs buffer int buff_helper_len=0; //used length of helper buffer int startpos=MathRound(MathMin(prev_calculated,rates_total-1)); //this is the start position ; it goes UPWARD, we are on an array, not on series if (rates_total<STDLEN) return(rates_total); if (startpos<STDLEN) startpos=STDLEN; for (int pos=startpos;pos<rates_total;pos++) //main cycle, going from startpos to rates_total { //Reads 1 element from position rates_total-pos, buffer 0 of StdDevIndicator calculus designated by StdDevHandle //rates_total-pos to convert position ; buffers are time series (curent value has index 0) CopyBuffer(StdDevHandle,0,rates_total-pos,1,res); ExtMapBuffer1[pos]=res[0];//copy value from temporary res[0] array index to final ExtMapBuffer1 if (Logarithm==1) ExtMapBuffer1[pos]=MathLog(ExtMapBuffer1[pos]); if (Logarithm==2) ExtMapBuffer1[pos]=MathLog10(ExtMapBuffer1[pos]); if (pos>STDLEN+BBLEN) { //enough values to calculate Bollies over volatility squaredsum=0.0; bbsum=0.0; //calculating the moving average for bollinger bands for (int j=pos-BBLEN+1;j<=pos;j++) bbsum=bbsum+ExtMapBuffer1[j]; bbma=bbsum/BBLEN; //calculating the squared sum for the stddev over stddev needed for bollinger bands for (int j=pos-BBLEN+1;j<=pos;j++) squaredsum=squaredsum+(ExtMapBuffer1[j]-bbma)*(ExtMapBuffer1[j]-bbma); //stddev on stddev and bollinger bands stddevonstddev=MathSqrt(squaredsum/BBLEN); ExtMapBuffer3[pos]=bbma+BBMULT*stddevonstddev; ExtMapBuffer4[pos]=bbma-BBMULT*stddevonstddev; if (pos>MALEN+BBLEN+STDLEN) { if (MALEN!=0) //goes in only if helper moving average is indeed enabled { //making buffer for helper MA for (int j=1;j<=MALEN;j++) buffer_helper[MALEN-j+1]=ExtMapBuffer1[pos-j+1]; //calculating different kinds of moving averages, by calling moving average methods from MovingAverages.mqh if (MATYPE=="SMA") ExtMapBuffer2[pos]=SimpleMA(MALEN,MALEN,buffer_helper); if (MATYPE=="EMA") { ExtMapBuffer2[pos]=ExponentialMA(MALEN,MALEN,prevma,buffer_helper); prevma=ExtMapBuffer2[pos]; } if (MATYPE=="SMMA") { ExtMapBuffer2[pos]=SmoothedMA(MALEN,MALEN,prevma,buffer_helper); prevma=ExtMapBuffer2[pos]; } if (MATYPE=="LWMA") ExtMapBuffer2[pos]=LinearWeightedMA(MALEN,MALEN,buffer_helper); }//if (MALEN!=0) }//if (pos>MALEN+STDLEN) }//else if if (buff_stddevs<BBLEN) }//for (pos=startpos;pos<rates_total;pos++) return(rates_total); } //+------------------------------------------------------------------+ |
Ei bine, programarea in MQL5 este semnificativ mai dificila. MQL5 are un mod total diferit de gandire a indicatorilor, asa incat comparatiile cu MQL4 nu si-ar avea sensul. Indicatorul incepe intr-o maniera similara, cu un intro mai larg, liniile 10-26. Acest intro are un mult mai mare numar de setari comparativ cu MQL4. Apoi urmeaza parametrii, liniile 29-34, si in final bufferele, liniile 36-39. Observa in liniile 29-34 inlocuirea lui extern cu input , care-i ia locul in MQL5. extern ramane valabil in MQL5, dar are un alt rol.
Pana aici nu pare prea complicat, deoarece codul este similar cu MQL4. Dar de aici, ia o cu totul diferita turnura…
Mai intai, o includere a MovingAverages.mqh pe linia 41. Aceasta va fi necesara mai jos, deoarece diferitele calculatii de medii mobile sunt realizate prin apeluri la rutine din aceasta biblioteca. Apoi, pe linia 43, o declaratie publica a handlerului pentru indicatorul Standard Deviation (numai unul, pentru ca alegem in functie de tipul mediei mobile).. In MQL5, apelurile la indicatori nu intorc valori, asa cum faceau in MQL4. Ci, intorc un handler. Acest handler este necesar ulterior pentru a intoarce valorile indicatorului, prin apeluri la CopyBuffer(). OnInit(), intinzandu-se pe liniile 48-71, este similar cu init() din MQL4, cel putin pe liniile 50-59, unde incep asignarile bufferelor si setarile desenelor. OnInit() se termina cu apeluri la iStdDev ; indicatorul va fi folosit in bucla principala, dar handlerul este generat o singura data! Patru apeluri catre iStdDev, generand un handler unic in functie de tipul de mobila specificat de parametrul MATYPE.
Evenimentul OnCalculate() este practic noua bucla principala. Functia are un prototip destul de misterios. Detaliile de implementare nu sunt cunoscute complet, deci functia permite o serie larga de prototipuri. Cel pe care l-am folosit in indicator este cel pe care il obtii ca default creezi indicatorul. Spre deosebire de MQL4, unde barele sunt descendente de la Bars la 0, bara curenta, in MQL5, indecsii barelor sunt ascendenti de la prev_calculated-1 to rates_total-1.
Urmand declaratiile variabilelor, bucla incepe la linia 100. Primul lucru de facut, e sa primim valoarea curenta a deviatiei standard. Pentru ca avem handlerul, apelam CopyBuffer() ca sa obtinem prima valoare a deviatiei standard. Pozitia curenta este variabila de ciclu pos. Totusi, CopyBuffer() intelege pozitia ca o pozitie intr-o serie temporala, nu ca o pozitie intr-un tablou. De aceea, vom copia o valoare de la rates-total-pos, din prima serie, 0. Daca indicatorul ar fi returnat mai multe valori, de exemplu Heiken Ashi, de exemplu, care intoarce informatii de bare, am fi avut mai mult decat o serie, adresand seriile 0,1,2 si 3 pentru a obtine preturile barelor Heiken Ashi. Valorile pe care le obtinem cu CopyBuffer() sunt puse intr-un tablou. Dar avem nevoie de un singur numar, deci tabloul in care punem datele, res va detine o singura valoare, res[0] , care este copiata catre primul nostru buffer, ExtIndexBuffer1, si apoi logaritmarea este aplicata, in functie de parametrul Logarithm, liniile 106-109 (acum logaritmul zecimal este inclus si el, pe langa cel natural).
Ce urmeaza e destul de usor de urmarit. Numaram daca avem destule valori pentru a incepe calculul Benzilor Bollinger peste deviatia standard. Cand avem destule date (ramura de “if” de la linia 110) , ne intoarcem inapoi BBLEN valori si calculam media, apoi mai facem bucla o data si calculam deviatia standard peste deviatia standard, si incele din urma Benzile Bollinger, liniile 124-125.
La fel ca inainte, dar acum conditia este sa avem o deviatie standard peste deviatia standard calculata, verificam daca avem MALEN valori din acestea, pentru a calcula prima medie mobila ajutatoare. Dar spre deosebire de data trecuta, acum copiem valorile deviatiei standard initiale intr-un buffer. Bufferul se va intinde de la MALEN catre 1, cu indecsi mai mari pentru valorile mai vechi. Motivul pentru a face asta e ca vom transmite acest buffer drept parametru catre metodele de calcul ale mediilor mobile importate din MovingAverages.mqh, si implementarea lor interna este trecerea in acest fel prin tablouri. Acestea sunt functii simple. Spre deosebire de indicatori, rezultatele lor sunt raspunsuri la calculele interne, nu handlere. Si aceste rezultate sunt scrise in bufferul mediei mobile ajutatoare, ExtMapBuffer2. Dupa calculul mediei mobile ajutatoare, toate calculele sunt terminate si bucla se inchide.
Previziunea volatilitatii
De aceea am inclus in indicator o medie mobila ajutatoare : pentru a ajuta la previziunea volatilitatii. As pune accentul, totusi, ca anumite cerinte cheie sa fie intrunite inainte de a trece la analiza:
1. Distanta dintre Benzile Bollinger sa fie sub 2% din intervalul istoric al deviatiei standard – asta va asigura ca semnalul nu are loc accidental atunci cand Benzile Bollinger sunt prea departe una de alta.
2. Deviatia standard curenta sa fie sub 5% din maximul deviatiei standard istorice -asta va asigura ca e o sansa ridicata sa urmeze o explozie a volatilitatii dupa ce semnalul se produce.
Semnalul normal va fi o intersectie cu BB superioara. Semnalul de iesire va fi determinarea unei slabiri in viteza volatilitatii. Si aceasta se intampla cand volatilitatea isi atinge varful in timp ce BB superioara continua sa creasca. Pentru a determina aceasta foarte rapid, vom folosi o medie mobila rapida – cu o perioada de 2. Odata ce se intersecteaza cu volatilitatea, deja se poate considera semnalul de iesire. Dar poate fi determinat chiar si mai repede, cand exista o distanta prea mare intre BB superioara si deviatia standard. Una din cele mai bune interpretari ale chartului se va face convertind data – BB , deviatia standard, media mobila, etc. in echivalentele lor grafice – coordonate punct pe chart. Asta iti va spune de exemplu cand deviatia standard atinge BB superioara.
Intrucat calculezi BB pentru un numar fix de bare – poate WindowBarsPerChart() – care da numarul total de bare din fereastra unde ruleaza expertul sau indicatorul , sau un numar dat de bare, si extragi valoarea maxima a BB superioara , si valoarea minima a BB inferioara , sa le numim bbmax si bbmin – poti calcula o pozitie grafica a oricarei valori (fie ea BB, deviatie standard sau medie mobila ajutatoare) , folosind o inaltime a unui ecran ipotetic (sa zicem 300 de pixeli “virtuali”). Sa numim aceasta valoare testvalue. Atunci pozitia lui testvalue pe ecran va fi :
double pozitie=MathRound( inaltime*(testvalue-bbmin)/bbmax );
In acest fel, o conditie compozita ar fi ca, dupa ce conditiile 1. si 2. sunt indeplinite, sa existe o incrucisare orio atingere.
Criteriul de iesire va fi : deviatia standard este sub BB superioara si nu mai exista nici o atingere. Totusi, la primul semn de slabiciune a volatilitatii, semnalul de iesire va fi lansat, si s-ar putea intampla prea devreme. Ar trebui verificat statutul atingerii folosind o rezolutie mai mica.
Exista doua situatii posibile care contribuie la cresterea deviatiei standard : ori preturile se duc in sus sau in jos intr-o maniera accelerata ori preturile schimba directia de la sus la jos si viceversa cu o amplitudine crescatoare.
Daca volatilitatea ultimelor bare continua sa creasca, putem presupune prima conditie : preturile sunt in trend. In acest caz, verificam ultimele 2-3 preturi de deschidere pentru a vedea directia si a o tranzactiona, daca e stabila. Dar, indiferent daca directia e stabila sau nu, volatilitatea se poate duce in sus. Fluctuatiile de pret pot urmari o schema sus-jos cu o amplitudine crescatoare, pana cand directia se stabilizeaza. Totusi, volatilitatea este in continuare coborata. Fiind coborata, inseamna ca preturile nu se misca suficient de mult – ne putem permite sa pierdem . Nu conteaza ce directie tranzactionam, verificam la fiecare bara daca directia a fost corecta. Daca nu, taiem pierderile si tranzactionam din nou. Pentru ca daca semnalul e corect si volatilitatea va traversa intre 30% si 70% din intervalul sau istoric, ne asteptam la bani multi!
Previziunea volatilitatii e destul de simpla, si e mai mult manuala decat automata. Din moment ce ne asteptam ca volatilitatea sa “treaca prin acoperis”, putem face o previziune liniara sau parabolica. Modelul liniar e simplu. Daca avem deviatia curenta pe bara 0 (s0) si deviatia standard asteptata pentru bara m (sm), atunci avem volatilitatea previzionata per bara dupa functia f(bar)=so+(sm-so)*bar/m.
Modelul parabolic este mai dificil, pentru ca necesita un punct in plus pentru definirea curburii. Acesta este rezolvabil cu un sistem de ecuatii. Fiind date 3 puncte (b1,stddev1) , (b2,stddev2) , (b3,stddev3) unde b1, b2, b3 sunt indecsii barelor, iar stddev1,stddev2,stddev3 sunt deviatiile standard estimate :

Odata ce avem parametrii functiei de gradul al II-lea care descrie deviatia standard trecand prin punctele estimate, putem genera deviatia standard pe fiecare bara , vezi scriptul MQL4 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | double Determinant(double &origa[][],int l1,int c1,int l2,int c2,int jumpline,int jumpcol) { double a[50,50]; int degree,i,j,ip,jp,dets; double result=0; double reshere=0; double r; string image; int ipm,jpm; for (i=0;i<50;i++) { for (j=0;j<50;j++) a[i,j]=0.0; } if (l1==0) l1=1; if (c1==0) c1=1; degree=c2-c1+1; if (jumpcol!=0) degree=degree-1; ip=1; for (i=l1;i<=l2;i++) { ipm=ip-1; jp=1; if (i!=jumpline) { for (j=c1;j<=c2;j++) { jpm=jp-1; if (j!=jumpcol) { r=origa[i-1,j-1]*1.0; a[ipm,jpm]=r; jp=jp+1; } }//for (j=c1;j<=c2;j++c2) ip=ip+1; }//if (i!=jumpline) }//for (i=l1;i<=l2;i++) result=0; if (degree==1) result=0; if (degree==2) result=a[1-1,1-1]*a[2-1,2-1]-a[2-1,1-1]*a[1-1,2-1]; if (degree==3) result=a[1-1,1-1] * a[2-1,2-1] * a[3-1,3-1] + a[1-1,2-1] * a[2-1,3-1] * a[3-1,1-1] + a[1-1,3-1] * a[2-1,1-1] * a[3-1,2-1] - a[1-1,3-1] * a[2-1,2-1] * a[3-1,1-1] - a[1-1,2-1] * a[2-1,1-1] * a[3-1,3-1] - a[1-1,1-1] * a[2-1,3-1] * a[3-1,2-1] ; if (degree>3) { for (dets=1;dets<=degree;dets++) { reshere=Determinant(origa,1,1,degree,degree,1,dets); result=result + MathPow(-1,1+dets)*origa[1-1,dets-1]*reshere; } } return(result); } void EstablishStddevFunction(int b1,double stddev1,int b2,double stddev2,int b3,double stddev3,double &a, double &b, double &c) { double d,da,db,dc; double barray[3]; double stddev[3]; double det_d[3][3]; double det_da[3][3]; double det_db[3][3]; double det_dc[3][3]; barray[0]=b1*1.0; barray[1]=b2*1.0; barray[2]=b3*1.0; stddev[0]=stddev1; stddev[1]=stddev2; stddev[2]=stddev3; det_d[0][0]=barray[0]*barray[0]; det_d[0][1]=barray[0]; det_d[0][2]=1; det_d[1][0]=barray[1]*barray[1]; det_d[1][1]=barray[1]; det_d[1][2]=1; det_d[2][0]=barray[2]*barray[2]; det_d[2][1]=barray[2]; det_d[2][2]=1; det_da[0][0]=stddev[0]; det_da[0][1]=barray[0]; det_da[0][2]=1; det_da[1][0]=stddev[1]; det_da[1][1]=barray[1]; det_da[1][2]=1; det_da[2][0]=stddev[2]; det_da[2][1]=barray[2]; det_da[2][2]=1; det_db[0][0]=barray[0]*barray[0]; det_db[0][1]=stddev[0]; det_db[0][2]=1; det_db[1][0]=barray[1]*barray[1]; det_db[1][1]=stddev[1]; det_db[1][2]=1; det_db[2][0]=barray[2]*barray[2]; det_db[2][1]=stddev[2]; det_db[2][2]=1; det_dc[0][0]=barray[0]*barray[0]; det_dc[0][1]=barray[0]; det_dc[0][2]=stddev[0]; det_dc[1][0]=barray[1]*barray[1]; det_dc[1][1]=barray[1]; det_dc[1][2]=stddev[1]; det_dc[2][0]=barray[2]*barray[2]; det_dc[2][1]=barray[2]; det_dc[2][2]=stddev[2]; d=Determinant(det_d,1,1,3,3,0,0); da=Determinant(det_da,1,1,3,3,0,0); db=Determinant(det_db,1,1,3,3,0,0); dc=Determinant(det_dc,1,1,3,3,0,0); a=da/d; b=db/d; c=dc/d; } //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int start() { double aa,bb,cc; Print("Standard Deviation parabolic forecasting example"); EstablishStddevFunction(0,0.003,3,0.006,9,0.1,aa,bb,cc); Print("Forecasted standard deviations"); for (int bar=0;bar<10;bar++) { Print("bar ",DoubleToStr(bar,0)," stddev=",DoubleToStr(aa*bar*bar+bb*bar+cc,4)); } return(0); } //+------------------------------------------------------------------+ |
De observat ca functia Determinant are o aplicatie recursiva, pana la gradul 3, unde se aplica regula lui Sarrus. Astfel poti folosi functia pentru a calcula determinanti de ordinul n – dar atentie la timp si cerintele de alocare a memoriei. Un prototip MQL5 al functiei
Determinant va arata in felul urmator:
double Determinant(double &origa[][50],ushort l1,ushort c1,ushort l2,ushort c2,ushort jumpline,ushort jumpcol)
De observat ca a doua dimensiune este fixata la 50. MQL5 nu suporta dimensiuni mai mari decat unu cu marime nespecificata.
Toate acestea pot suna bine, dar nu uitati ca nu tranzactionam volatilitatea . Nu avem inca o directie a pretului.
Dar directia pretului poate fi extrasa din evolutia volatilitatii.
Previziunea pretului din previziunea volatilitatii
Trebuie sa consideram ca la fiecare bara, un pret iese din analiza si altul intra in loc. Daca avem Vechiul pentru cel mai vechi pret si Noul pentru pretul care intra, avem:
Noua Medie = Vechea Medie – Vechiul/N + Noul/N ;
Acum, dat fiind faptul ca am trasat deja deviatiile standard care vor veni, intrebarea este care este noul pret care va da noua deviatie standard la rand ?
Asa ca luam deviatia standard, obtinuta cu modelul de previziune, si o ridicam la patrat. Avem astfel dispersia. Inmultind dispersia cu N (numarul de preturi din analiza) , incepem sa formam ecuatia pentru membrul cel mai nou. Luand in consideratie n , elemente, de la P1 (elementul care va iesi din analiza) , la Pn (ultimul element), Pn+1 (noul pret care trebuie sa fie determinat) si media mobila curenta, formam ecuatia deviatiei standard, ridicand la patrat si inmultind-o cu n, care da suma patratelor Pi – noua medie, care este calculata ca mai sus, scotand influenta lui P1 si adaugand influenta lui Pn+1. Folosim o variabila ajutatoare, avgc , care este media curenta fara influenta lui P1: (Nu mai e nevoie sa o zicem, ecuatia este scrisa pentru cazul in care deviatia standard este calculata folosind media aritmetica simpla)

Deci ce se poate vedea e ceea ce era de asteptat. Fiecare deviatie standard apare ca rezultat al unui nou pret, care poate fi ori in sus, ori in jos, iar acestea sunt bineinteles radacinile ecuatiei de gradul al II-lea. Am grupat elementele ca sa se vada clar coeficientii lui Pn+1, Pn+1 la patrat si termenul liber. Fiind date m deviatii standard previzionate, avem 2^m preturi previzionate dupa m bare. Desigur, acestea nu pot fi generate direct ; ele formeaza un arbore . Astfel, in urma primei calculatii vor rezulta doua preturi ; fiecare dintre ele va fi baza unui nou calcul si asa mai departe. Astfel vom avea 2, 4, 8, 16… 2^m preturi pana la sfarsit. Cel mai probabil, pretul nu va avea o miscare sus-jos si viceversa cu o amplitudine in crestere pentru toate cele m bare. Ci, ne putem astept doar la cateva bare sa aiba o astfel de miscare, dupa care directia se va stabiliza. Putem avea cateva semnale directionale gresite la inceput, in timpul catorva balansari, dar dupa acestea miscarile pretului vor fi mult mai puternice. Sfatul meu este ca metoda sa fie folosita mai ales pentru timeframe-uri(perioada ocupata de o singura bara) mai mari decat patru ore.
Se poate lucra si cu timeframe-uri mai mici, dar semnalele de volatilitate prinse pe timeframe-uri mari pot produce rezultate spectaculoase.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void CalculateStats(double &data[],int lastindex,double &avg, double &stddev) { double sum=0; int i; for (i=0;i<=lastindex;i++) { sum=sum+data[i]; } avg=sum/(lastindex+1); sum=0; for (i=0;i<=lastindex;i++) { sum=sum+(data[i]-avg)*(data[i]-avg); } stddev=MathSqrt(sum/(lastindex+1)); } void CalculateNextPrices(double &p[],int lastindex,double newstddev,double &newprice1,double &newprice2) { double avg,avgc,stddev,suma2pi,sumapi2,sumapimavgc,sumapimavgc2,delta,a,b,c; int n,i; CalculateStats(p,lastindex,avg,stddev); n=lastindex+1; avgc=avg-p[0]/n; for (i=1;i<=lastindex;i++) { suma2pi=suma2pi+(2*p[i]); sumapi2=sumapi2+(p[i]*p[i]); sumapimavgc=sumapimavgc+(p[i]-avgc); sumapimavgc2=sumapimavgc2+(p[i]-avgc)*(p[i]-avgc); } a=1-(1.0/n); b=(2.0*avgc/n)-(2.0/n)*sumapimavgc-2.0*avgc; c=sumapimavgc2+avgc*avgc-newstddev*newstddev*n; delta=b*b-4.0*a*c; newprice1=(-b-MathSqrt(delta))/(2.0*a); newprice2=(-b+MathSqrt(delta))/(2.0*a); } |
Prima functie calculeaza parametrii statistici: media si deviatia standard . A doua functie calculeaza noile preturi care se potrivesc cu noua deviatie standard. Ambele tablouri pleaca de la indexul zero. Parametrul lastindex parameter este ultimul index care contine date. Se observa utilizarea constantelor intregi in formule care calculeaza variabile double. Aceste constante trebuie scrise intr-o maniera double : 1.0 in loc de 1, 2.0 in loc de 2 s.a.m.d., altfel rezultatele sunt expuse la erori.
Aveti grija cand folositi calculul parabolic pentru deviatia standard. Daca primul punct este ales inainte de varful parabolei, deviatia standard pe parcurs poate deveni zero sau negativa, iar ecuatia va avea o radacina sau niciuna – radacinile complexe nu au sens aici!

Am corectat o eroare afectand versiunea MQL4 a indicatorului, la calculul mediei mobile ajutatoare: am inlocuit BBLEN cu MALEN pe liniile 94,101,108 si 115.
Am corectat versiunea MQL5, facand indicatorul functional in Strategy Tester.
Am modificat versiunea MQL4, schimband tipul BBMULT in double.