Własny wskaźnik od zera
Kompletny działający wskaźnik z wyjaśnieniem każdej właściwości: czym różni się bufor od linii, po co PLOT_DRAW_BEGIN i skąd biorą się linie spadające do zera.
7 fragmentów kodu z tej strony przeszło przez kompilator MetaEditor build 6090, 2026-08-05.
Wskaźnik różni się od eksperta trzema rzeczami: nie handluje, uruchamia się przy każdym przeliczeniu wykresu i komunikuje się ze światem przez bufory, a nie przez zlecenia.
Bufor to nie to samo co linia
To pierwsze nieporozumienie. Wskaźnik ma dwie różne liczby w nagłówku:
#property indicator_buffers 2 // ile tablic, łącznie z pomocniczymi
#property indicator_plots 1 // ile linii widzi użytkownikindicator_buffers to liczba tablic, które wskaźnik rezerwuje. indicator_plots to liczba rysowanych serii. Bufor pomocniczy — na przykład wynik pośredni obliczeń — zajmuje miejsce w pierwszej liczbie, ale nie w drugiej.
Podanie zbyt małego indicator_buffers daje błąd przy SetIndexBuffer. Podanie zbyt dużego indicator_plots daje ostrzeżenie o linii bez danych.
double BuforSredniej[]; // rysowany
double BuforZakresu[]; // tylko do obliczeń
SetIndexBuffer(0, BuforSredniej, INDICATOR_DATA);
SetIndexBuffer(1, BuforZakresu, INDICATOR_CALCULATIONS);Kolejność wywołań SetIndexBuffer wyznacza numerację, którą później podaje się w CopyBuffer. Bufory pomocnicze też są numerowane — dlatego wskaźnik z jedną widoczną linią może mieć ją pod numerem 0, a jego wewnętrzne obliczenia pod numerem 1.
Kompletny wskaźnik
//+------------------------------------------------------------------+
//| SredniZakres.mq5 |
//| Wskaźnik pokazujący średni zakres świecy w punktach. |
//| Napisany jako przykład do lekcji o budowie własnego wskaźnika. |
//| https://finanseodczarowane.pl/mql5/kurs/06-wlasny-wskaznik/ |
//| Licencja MIT |
//+------------------------------------------------------------------+
#property copyright "finanseodczarowane.pl/kod"
#property version "1.00"
#property indicator_separate_window
#property indicator_buffers 2 // bufory rysowane + pomocnicze
#property indicator_plots 1 // ile linii widać na wykresie
#property indicator_label1 "Średni zakres"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
input int Okres = 14; // liczba świec do uśrednienia
double BuforSredniej[]; // to widać
double BuforZakresu[]; // to tylko liczymy
//+------------------------------------------------------------------+
int OnInit()
{
if(Okres < 1)
{
Print("Okres musi wynosić co najmniej 1.");
return INIT_PARAMETERS_INCORRECT;
}
SetIndexBuffer(0, BuforSredniej, INDICATOR_DATA);
SetIndexBuffer(1, BuforZakresu, INDICATOR_CALCULATIONS);
//--- pierwsze Okres świec nie ma z czego policzyć średniej
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, Okres);
//--- 0.0 znaczy "brak wartości", inaczej linia spadłaby do zera
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
IndicatorSetString(INDICATOR_SHORTNAME,
StringFormat("Średni zakres (%d)", Okres));
IndicatorSetInteger(INDICATOR_DIGITS, 1);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
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[])
{
if(rates_total <= Okres)
return 0; // za mało danych, spróbujemy później
//--- liczymy od miejsca, w którym skończyliśmy poprzednio;
//--- minus jeden, bo ostatnia świeca wciąż się zmienia
int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
//--- zakres każdej świecy w punktach
for(int i = start; i < rates_total; i++)
BuforZakresu[i] = (high[i] - low[i]) / _Point;
//--- średnia krocząca z zakresu
int poczatek = MathMax(start, Okres - 1);
for(int i = poczatek; i < rates_total; i++)
{
double suma = 0.0;
for(int j = 0; j < Okres; j++)
suma += BuforZakresu[i - j];
BuforSredniej[i] = suma / Okres;
}
return rates_total;
}Pokazuje średni zakres świecy w punktach — ile średnio przechodzi instrument w jednym okresie. Prosta rzecz, za to dotyka wszystkich mechanizmów, które są w każdym wskaźniku.
Trzy właściwości, które robią różnicę
PLOT_DRAW_BEGIN mówi, od której świecy zaczyna się rysowanie. Bez tego wskaźnik o okresie 14 narysuje pierwsze trzynaście punktów z niekompletnych danych — i wykres zacznie się dziwnym garbem, który wygląda jak sygnał.
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, Okres);PLOT_EMPTY_VALUE to wartość oznaczająca „tu nie ma nic". Domyślnie jest nią EMPTY_VALUE, czyli ogromna liczba. Jeśli twój bufor zostawia miejsca niewypełnione, a wartością pustą jest zero, linia zjedzie do zera i utworzy pionowe kreski przez cały wykres.
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);To jest odpowiedź na najczęściej zadawane pytanie o własne wskaźniki: skąd te pionowe linie do dołu.
INDICATOR_SHORTNAME to nazwa w rogu okna wskaźnika i w oknie danych. Warto wstawić w nią parametry — inaczej przy trzech kopiach wskaźnika na wykresie nie odróżnisz, która ma jaki okres.
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("Średni zakres (%d)", Okres));OnCalculate: liczyć tylko to, co się zmieniło
Wskaźnik jest wywoływany przy każdym ticku. Przeliczanie całej historii za każdym razem działa, ale przy wykresie ze stu tysiącami świec zamula terminal i wydłuża każdy przebieg testera.
int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;Trzy przypadki, które ten jeden wiersz obsługuje:
- pierwsze wywołanie —
prev_calculatedwynosi zero, liczymy wszystko, - kolejny tick tej samej świecy — liczymy od ostatniej świecy, bo ona wciąż się zmienia,
- nowa świeca — liczymy od poprzedniej i nowej.
Minus jeden jest tu kluczowy. Bez niego ostatnia świeca zostaje policzona raz, przy swoim pierwszym ticku, i nigdy nie aktualizuje się do końca okresu.
Wskaźnik w oknie wykresu czy osobnym
#property indicator_chart_window // na wykresie, w cenach instrumentu
#property indicator_separate_window // w osobnym oknie, własna skalaReguła jest prosta: jeśli wartości wskaźnika są cenami — średnie, wstęgi, poziomy — idzie na wykres. Jeśli są czymkolwiek innym — punktami, procentami, oscylatorem — idzie do osobnego okna. Wskaźnik zwracający punkty umieszczony na wykresie cen po prostu nie będzie widoczny, bo jego wartości są o kilka rzędów wielkości mniejsze od ceny.
Jak to sprawdzić
Zadanie
Dołóż drugą linię pokazującą największy zakres z ostatnich Okres świec. Będzie to wymagało zwiększenia obu liczb w nagłówku, dodania właściwości indicator_label2 i indicator_type2 oraz trzeciego bufora. Sprawdź potem przez CopyBuffer z eksperta, czy nowa linia siedzi pod numerem, którego się spodziewasz.