Wskaźniki przerysowujące
Trzy rodzaje przerysowywania, z czego tylko jeden jest oszustwem. Plus gotowy detektor, który sam sprawdzi twój wskaźnik.
1 fragment kodu z tej strony przeszedł przez kompilator MetaEditor build 6090, 2026-08-05.
Wskaźnik przerysowujący to taki, który zmienia wartości już narysowane. Na wykresie historycznym wygląda znakomicie, bo widzisz wyłącznie wersję ostateczną — tę, której w chwili decyzji nie znałeś.
Nie każde przerysowanie jest oszustwem. Są trzy rodzaje i warto je odróżniać.
Rodzaj pierwszy: świeca w budowie
Każdy wskaźnik zmienia wartość na świecy o indeksie 0, bo ta świeca jeszcze się nie zamknęła. To nie jest przerysowywanie, tylko normalne działanie — i przestaje być problemem, gdy czytasz wyłącznie świecę 1 i starsze.
Opisaliśmy to osobno przy przewadze z przyszłości.
Rodzaj drugi: przerysowanie z definicji
Niektóre wskaźniki muszą przerysowywać, bo taka jest ich konstrukcja. ZigZag rysuje wierzchołek dopiero wtedy, gdy późniejsze świece potwierdzą, że to był wierzchołek. Wskaźniki oparte na ekstremum w oknie wokół danej świecy — fraktale, wykrywacze struktury rynku — działają tak samo.
Same w sobie są uczciwe. Nieuczciwe staje się użycie ich w warunku wejścia bez uwzględnienia opóźnienia. Fraktal potwierdzony po dwóch świecach jest sygnałem dostępnym dwie świece później, a nie w miejscu, w którym go widzisz na wykresie.
Rodzaj trzeci: przerysowanie ukryte
Najgorszy przypadek. Wskaźnik zmienia stare wartości bez żadnego widocznego powodu — bo autor przelicza cały bufor przy każdym ticku, korzystając z danych z całego okresu. Bywa to niezamierzone, bywa celowe, gdy wskaźnik ma się sprzedawać z ładnym wykresem.
Rozpoznasz go po tym, że wartość dla świecy sprzed tygodnia dziś jest inna niż tydzień temu.
Detektor
Nie trzeba czytać cudzego kodu ani mieć źródeł. Wystarczy zapisywać wartości i porównywać je po czasie.
input string NazwaWskaznika = "Examples\\MACD";
input int NumerBufora = 0;
int uchwyt = INVALID_HANDLE;
int OnInit()
{
uchwyt = iCustom(_Symbol, _Period, NazwaWskaznika);
if(uchwyt == INVALID_HANDLE)
return INIT_FAILED;
EventSetTimer(60);
return INIT_SUCCEEDED;
}
void OnDeinit(const int powod)
{
EventKillTimer();
if(uchwyt != INVALID_HANDLE)
IndicatorRelease(uchwyt);
}
//--- klucz zmiennej globalnej: wskaźnik, symbol i czas świecy
string Klucz(const datetime czasSwiecy)
{
return StringFormat("przerys_%s_%s_%I64d",
_Symbol, NazwaWskaznika, (long)czasSwiecy);
}
void OnTimer()
{
double bufor[];
ArraySetAsSeries(bufor, true);
//--- bierzemy dziesięć domkniętych świec, od indeksu 1
if(CopyBuffer(uchwyt, NumerBufora, 1, 10, bufor) < 10)
return;
for(int i = 0; i < 10; i++)
{
datetime czasSwiecy = iTime(_Symbol, _Period, i + 1);
string klucz = Klucz(czasSwiecy);
if(!GlobalVariableCheck(klucz))
{
GlobalVariableSet(klucz, bufor[i]); // pierwszy pomiar
continue;
}
double zapisana = GlobalVariableGet(klucz);
if(MathAbs(zapisana - bufor[i]) > _Point / 2.0)
PrintFormat("PRZERYSOWANIE | swieca %s | bylo %.8f | jest %.8f",
TimeToString(czasSwiecy, TIME_DATE | TIME_MINUTES),
zapisana, bufor[i]);
}
}Podepnij to na godzinę na wykresie M1. Jeśli w dzienniku pojawią się wpisy PRZERYSOWANIE dla świec starszych niż kilka okresów, wskaźnik zmienia przeszłość — i każdy backtest strategii na nim oparty jest bezwartościowy.
Co zrobić, gdy wskaźnik przerysowuje
Jeśli przerysowanie jest z definicji — dołóż opóźnienie. Sygnał brany z indeksu n uznaj za dostępny dopiero na świecy n minus liczba świec potwierdzenia, i przetestuj tak. Wynik spadnie, za to będzie prawdziwy.
Jeśli przerysowanie jest ukryte — wskaźnik nie nadaje się do niczego poza oglądaniem. Żadne opóźnienie tego nie naprawi, bo nie wiadomo, jak dużego opóźnienia potrzeba.