Wykrywanie nowej świecy
Trzy sposoby na wykrycie nowej świecy, z czego dwa zawodzą. Plus wyjaśnienie, dlaczego to najważniejszy filtr w całym ekspercie.
3 fragmenty kodu z tej strony przeszły przez kompilator MetaEditor build 6090, 2026-08-05.
Większość strategii podejmuje decyzje raz na świecę, nie przy każdym ticku. Powodów jest trzy: sygnał liczony na niedomkniętej świecy zmienia się w trakcie jej trwania, wykonywanie ciężkich obliczeń kilkadziesiąt razy na sekundę jest marnotrawstwem, a wysyłanie zleceń przy każdym drgnięciu ceny to najkrótsza droga do kodu zwrotnego 10024 za zbyt wiele zapytań.
Sposób, który działa
Zapamiętujemy czas otwarcia bieżącej świecy. Jeśli przy kolejnym ticku jest inny — mamy nową świecę.
bool NowaSwieca()
{
static datetime czasOstatniej = 0;
datetime czasBiezacej = iTime(_Symbol, _Period, 0);
if(czasBiezacej == 0) // historia jeszcze się nie wczytała
return false;
if(czasBiezacej == czasOstatniej)
return false;
czasOstatniej = czasBiezacej;
return true;
}
void OnTick()
{
if(!NowaSwieca())
return;
//--- tu decyzje podejmowane raz na świecę
}Sposoby, które zawodzą
Zliczanie barów przez Bars. Wygląda kusząco: zapamiętać liczbę słupków i porównać. Problem w tym, że liczba barów zmienia się także wtedy, gdy terminal douczy historię wstecz albo gdy w danych była luka. Dostajesz wtedy fałszywy sygnał nowej świecy w środku poprzedniej — albo, co gorsza, nie dostajesz go wcale, gdy dwie zmiany zniosą się nawzajem.
Porównywanie minut w TimeCurrent. Zakłada, że świece zaczynają się równo z zegarem, co przestaje być prawdą przy okresach niestandardowych, przy przerwach sesyjnych i po zmianie czasu na serwerze brokera.
Świeca 0 i świeca 1
Kiedy już wiesz, że zaczęła się nowa świeca, twoje dane wejściowe leżą pod indeksem 1, a nie 0. Indeks 0 to świeca, która właśnie się otworzyła i ma zaledwie jeden tick — jej maksimum, minimum i zamknięcie równają się otwarciu i będą się zmieniać do końca okresu.
double zamkniecie = iClose(_Symbol, _Period, 1); // domknięta, ostateczna
double biezace = iClose(_Symbol, _Period, 0); // zmieni się jeszcze wiele razyCzytanie sygnału ze świecy 0 to najpowszechniejsze źródło wyników w teście, których nie da się powtórzyć na rachunku. Opisaliśmy to osobno: skąd się bierze przewaga z przyszłości.
Wersja dla wielu symboli
Jeśli ekspert obserwuje kilka instrumentów, jedna zmienna static nie wystarczy. Najprościej trzymać czasy w tablicy równoległej do listy symboli:
class CZegarSwiec
{
private:
string m_symbole[];
datetime m_czasy[];
public:
bool Nowa(const string symbol, const ENUM_TIMEFRAMES okres)
{
datetime biezacy = iTime(symbol, okres, 0);
if(biezacy == 0)
return false;
for(int i = 0; i < ArraySize(m_symbole); i++)
if(m_symbole[i] == symbol)
{
if(m_czasy[i] == biezacy)
return false;
m_czasy[i] = biezacy;
return true;
}
int rozmiar = ArraySize(m_symbole);
ArrayResize(m_symbole, rozmiar + 1);
ArrayResize(m_czasy, rozmiar + 1);
m_symbole[rozmiar] = symbol;
m_czasy[rozmiar] = biezacy;
return true;
}
};Zadanie
Dołóż do funkcji NowaSwieca parametr ENUM_TIMEFRAMES okres, żeby ekspert stojący na wykresie M5 mógł reagować na zamknięcie świecy H1. Sprawdź w dzienniku, czy wpis pojawia się dokładnie raz na godzinę.