English Русский Deutsch 日本語
preview
MetaTrader 5 ve MQL5 Ekonomik Takvim: Haberler Nasıl Tekrarlanabilir Bir Alım-Satım Sistemine Dönüştürülür?

MetaTrader 5 ve MQL5 Ekonomik Takvim: Haberler Nasıl Tekrarlanabilir Bir Alım-Satım Sistemine Dönüştürülür?

MetaTrader 5Örnekler |
16 8
MetaQuotes
MetaQuotes

Giriş

Haberlere dayalı işlem yapan yatırımcıların temel sorunları, dağınık araç seti ve sistematik, algoritmik bir alım-satım iş akışının olmamasıdır. İşlem yaparken dikkatinizi internet tarayıcınız (haber sitelerinde gezinmek) ve işlem terminaliniz arasında bölmek oldukça zordur.

Haber tabanlı işlem yapan bir yatırımcının iş akışı şuna benzer: Web tarayıcınızda haber takvimini hızlıca açma ve olaylardaki değişiklikleri kontrol etme → Yaklaşan olayları hızlıca değerlendirme ve ne ve nasıl işlem yapacağınıza karar verme → MetaTrader 5 terminaline gitme - bekleyen emirler yerleştirme veya terminal başında oturup karar vermek için haberlerin yayınlanmasını bekleme. Bu senaryoda, yatırımcı genellikle bağlam kaybı yaşar, bu da haberlere tepki vermede gecikmelere yol açar ve bu da kayıplara neden olur.

Haberlere dayalı alım-satımın net kurallar, tekrarlanabilir sonuçlar ve otomatik testlerle bir mühendislik problemi gibi çalışmasını ister misiniz? Bu makalenin amacı, MetaTrader 5 için bir haber katmanının çalışma mimarisini göstermektir: tek bir veri kaynağı, takvim API'sinin uygun kullanımı, bir filtreleme ve önbelleğe alma mekanizması, geçmiş olayların sınayıcı için bir kaynağa aktarılması ve Canlı ve Sınayıcı arasında otomatik geçiş - böylece aynı kod hem gerçek zamanlı hem de geçmiş verilerde deterministik sonuçlar üretir.

Yatırımcının ana sorunları

Şekil 1. Manuel bir yatırımcının temel sorunu araçların dağınık olmasıdır


Haberlere dayalı manuel alım-satımın artık modası geçti

Öncelikle, dış faktörlere bağlı bir haber stratejisi test edilemez. Alım-satımda başarının yarısı, stratejinin geçmişteki performansını test etmekle ilgilidir. Bir stratejinin test edilememesi, alınan kararlarda öznelliğe yol açar. Strateji bir hipotez olarak kalacak ve onu test etmek için aylar hatta yıllar boyunca çok zaman harcayacaksınız.

İkinci olarak, manuel alım-satım ölçeklendirilmesi çok zordur. Düzensiz kar getiren ve istikrarsız bir işletmeyi ölçeklendirmeye çalışın. Onu genişletmek sorunları daha da kötüleştirebilir. Net bir yapı, sistematikleştirme ve optimizasyon yoksa, ölçeklendirme hakkında düşünmek faydasızdır.

Üçüncüsü, haberlere verilen tepkilerdeki gecikmeler manuel alım-satımın en büyük sorunlarından biridir. Bir FOREX yatırımcısı olduğunuzu ve Tarım Dışı İstihdam (Non Farm Payrolls, NFP) verisine dayalı işlem yaptığınızı hayal edin. NFP verileri genellikle ayda bir kez açıklanmaktadır. Dikkatiniz dağıldı ve NFP'yi kaçırdınız, para kazanma fırsatını kaybettiniz. Bu, birçok yatırımcı için tanıdık bir durumdur.

Sonuç: Otomasyon, haberlere dayalı tekrarlanabilir alım-satımın tek yoludur.

Reaksiyon karşılaştırması

Şekil 2. Bir robot her zaman daha hızlıdır


MetaTrader 5'te yerleşik ekonomik takvim: Tek bir bilgi kaynağı

Haber tabanlı alım-satımı algoritmik hale getirmek ve dolayısıyla geçmiş testlerini mümkün kılmak için şirketimizin benzersiz ürününü kullanın - MQL5 API aracılığıyla haberlere erişim sağlayan, MetaTrader 5'te yerleşik bir ekonomik takvim. Haberlere dayalı alım-satımınızı doğaçlamadan, doğrulanabilir algoritmik bir sürece dönüştürecek olan şey budur. Çevrimdışı testler için hem gerçek zamanlı güncel olaylar hem de geçmiş olaylar mevcuttur.

Tablo 1:

MetaTrader 5'te yerleşik takvimi benzersiz kılan özellikler nelerdir?

Parametre
Erişim hızı
<100 ms - haberleri terminale yükledikten sonra
Entegrasyon Yerel - terminal çekirdek seviyesinde + MQL-API
Testler Tam destek - haberleri terminale indirdikten sonra (sınayıcıda dosyalar, kaynaklar veya SQLite üzerinden erişim)
Güvenilirlik Yüksek

MetaTrader 5 mükemmel bir strateji sınayıcıya sahiptir - hızlı, çok varlıklı ve test ve optimizasyon için yerel ve küresel ağlardaki bilgisayarları kullanabilir. Haber tabanlı alım-satım sisteminizi geçmiş veriler üzerinde çalıştırmayı deneyin. Bu, alım-satım yaklaşımınızı daha iyi hale getirebilir.


MetaTrader 5 ekonomik takvim fonksiyonları: MQL5 API'ye genel bakış

Manuel analizden algoritmik analize geçiş, veri mimarisini anlamakla başlar. MQL5'te ekonomik takvim sadece bir tablo değil, yerel bir API aracılığıyla erişilebilen yapılandırılmış bir veritabanıdır. Olayları doğru bir şekilde nasıl sorgulayacağımızı, Olay ve Değer arasındaki farkı ve başarılı bir strateji için zaman senkronizasyonunun neden gerekli olduğunu keşfedeceğiz.

MQL5 takviminin çekirdeği, her biri kendi görevini çözen birkaç temel fonksiyondan oluşur. Tüm Ekonomik Takvim verilerini aynı anda sağlayan evrensel bir fonksiyon olmadığını anlamak önemlidir. Kombine bir yaklaşım gerekmektedir.

  • CalendarValueHistory - ilk yükleme için ana araç. Belirli bir zaman aralığı için bir olay değerleri dizisi almayı sağlar. Bu, Uzman Danışmanın başlatılması sırasında önbelleği geçmiş verilerle veya önümüzdeki haftanın verileriyle doldurmak için kullanılan "ağır toplardır".
  • CalendarValueLast - gerçek zamanlı çalışan Uzman Danışmanlar için ana fonksiyon. Son talepten bu yana yalnızca değişen veya yeni değerleri geri döndürür (change_id mekanizması aracılığıyla). Bu, her tikte tüm veri dizisini talep etmeyerek trafikten ve sunucu kaynaklarından tasarruf edilmesini sağlar.
  • CalendarEventByCountry - ülkedeki tüm olayların açıklamalarını alır. ISO 3166-1 alpha-2 standardına göre kodla belirtilen belirli bir ülke için olay açıklamalarının bir listesini geri döndürür. Filtreler oluşturmak için gereklidir (örneğin, yalnızca "US" (ABD), "RU" (Rusya), "CA" (Kanada) vb. için olayları göstermek).
  • CalendarEventByCurrency - para birimine göre tüm olayların açıklamalarını alır. Kodla ("USD", "EUR" vb.) belirtilen belirli bir para birimi için olay açıklamalarının bir listesini geri döndürür.
  • CalendarCountryById - ID'ye göre ülke özelliklerini alır.
  • CalendarEventById - ID'ye göre olay özelliklerini alır.
  • CalendarValueById - ID'ye göre belirli bir değer alır.

Veri yapıları: Takvim MQL5 API'si ne geri döndürür?

Tüm ekonomik takvim API fonksiyonları ya bir yapı dizisi ya da tek bir yapı içeren bir değişken geri döndürür. Yapıları ve alanlarını tam olarak listeleyelim.

Olay açıklamaları - CalendarEventById, CalendarEventByCountry ve CalendarEventByCurrency fonksiyonlarında kullanılır:

struct MqlCalendarEvent
  {
   ulong                               id;                    // event ID
   ENUM_CALENDAR_EVENT_TYPE            type;                  // event type from the ENUM_CALENDAR_EVENT_TYPE enumeration
   ENUM_CALENDAR_EVENT_SECTOR          sector;                // sector an event is related to
   ENUM_CALENDAR_EVENT_FREQUENCY       frequency;             // event frequency (periodicity)
   ENUM_CALENDAR_EVENT_TIMEMODE        time_mode;             // event time mode
   ulong                               country_id;            // country ID
   ENUM_CALENDAR_EVENT_UNIT            unit;                  // economic indicator value's unit of measure
   ENUM_CALENDAR_EVENT_IMPORTANCE      importance;            // event importance
   ENUM_CALENDAR_EVENT_MULTIPLIER      multiplier;            // economic indicator value multiplier
   uint                                digits;                // number of decimal places
   string                              source_url;            // URL of a source where an event is published
   string                              event_code;            // event code
   string                              name;                  // event text name in the terminal language (in the current terminal encoding)
  };


Ülke açıklamaları - CalendarCountryById ve CalendarCountries fonksiyonlarında kullanılır:

struct MqlCalendarCountry
  {
   ulong                               id;                    // country ID (ISO 3166-1)
   string                              name;                  // country text name (in the current terminal encoding)
   string                              code;                  // country code name (ISO 3166-1 alpha-2)
   string                              currency;              // country currency code
   string                              currency_symbol;       // country currency symbol
   string                              url_name;              // country name used in the mql5.com website URL
  };


Olay değerleri - CalendarValueById, CalendarValueHistoryByEvent, CalendarValueHistory, CalendarValueLastByEvent ve CalendarValueLast fonksiyonlarında kullanılır.

struct MqlCalendarValue
  {
   ulong                               id;                    // value ID
   ulong                               event_id;              // event ID
   datetime                            time;                  // event date and time
   datetime                            period;                // event reporting period
   int                                 revision;              // revision of the published indicator relative to the reporting period
   long                                actual_value;          // actual value in ppm or LONG_MIN if the value is not set
   long                                prev_value;            // previous value in ppm or LONG_MIN if the value is not set
   long                                revised_prev_value;    // revised previous value in ppm or LONG_MIN if the value is not set
   long                                forecast_value;        // forecast value in ppm or LONG_MIN if the value is not set
   ENUM_CALENDAR_EVENT_IMPACT          impact_type;           // potential impact on the currency rate
   //--- functions for checking the values
   bool                         HasActualValue(void) const;   // returns 'true' if actual_value is set
   bool                         HasPreviousValue(void) const; // returns 'true' if prev_value is set
   bool                         HasRevisedValue(void) const;  // returns 'true' if revised_prev_value is set
   bool                         HasForecastValue(void) const; // returns 'true' if forecast_value is set
   //--- functions for getting values
   double                       GetActualValue(void) const;   // return actual_value or nan if the value is not set
   double                       GetPreviousValue(void) const; // return prev_value or nan if the value is not set
   double                       GetRevisedValue(void) const;  // returns revised_prev_value or nan if the value is not set
   double                       GetForecastValue(void) const; // returns forecast_value or nan if the value is not set
  };

Not:

MqlCalendarValue yapısının actual_value, forecast_value, prev_value ve revised_prev_value alanlarını kontrol etmek ve bu alanlardan değerler almak için metotlar sağladığını unutmayın. Listelenen alanların değerleri olmayabilir - örneğin, haber öğesi henüz yayınlanmadığı için actual_value alanı boş olabilir. Değerleri almanın en iyi yolu, kontrolü yapmak ve değerleri yapının kendi metotlarını kullanarak almaktır.

Yapılar birbirleriyle aşağıdaki ilişkilerle bağlantılıdır:

Takvim ilişkileri

Şekil 3. Takvim yapısı ilişkileri


MqlCalendarCountry yapısı, bir ülke ID'si aracılığıyla MqlCalendarEvent ile bağlantılıdır. İlişki biçimi "bire-çok" (1..*) şeklindedir.

MqlCalendarEvent yapısı, bir olay ID'si aracılığıyla MqlCalendarValue ile bağlantılıdır. İlişki biçimi "bire-çok" (1..*) şeklindedir.


Haber yayınlanma zamanı ve sunucu zamanı

Ekonomik takvimle çalışmaya yönelik tüm fonksiyonlar TimeTradeServer() işlem sunucusu zamanını kullanır. Bu, MqlCalendarValue yapısındaki zamanın ve CalendarValueHistoryByEvent() ve CalendarValueHistory() fonksiyonlarındaki zaman girdilerinin kullanıcının yerel zamanı yerine işlem sunucu saat dilimine ayarlandığı anlamına gelir.

Dönüştürmeye gerek yoktur: MqlCalendarValue::period olay zamanı, TimeCurrent() veya TimeTradeServer() fonksiyonları çağrıldığında elde edilen zamanla doğrudan karşılaştırılabilir. Sınayıcıda, TimeTradeServer() fonksiyonu geçmiş verilerdeki zamanla aynı olan bir model zamanı geri döndürür. Zaman pencereleriyle çalışma mantığı ("haberlerden 30 dakika önce") gerçek zamanda ve geçmişte aynı şekilde çalışır. Aracı kurum yaz/kış saati geçişini dikkate alırsa, takvim otomatik olarak bu geçişe göre ayarlanır.


Pratik örnek - bugün (geçerli gün) için olayların bir listesini alma:

//+------------------------------------------------------------------+
//| Get calendar values for the current day                          |
//+------------------------------------------------------------------+
void GetTodayUSD_Events()
 {
//--- define the period boundaries in server time
  datetime server_now = TimeTradeServer();
  datetime day_start  = server_now - (server_now % 86400);
  datetime day_end    = day_start + 86400;

  MqlCalendarValue    values[];
  MqlCalendarEvent    event;
  MqlCalendarCountry  country;

//--- request values only for USD
  if(CalendarValueHistory(values, day_start, day_end, NULL, "USD"))
   {
    Print("  Events received for USD: ", ArraySize(values));

    //--- iterate over the array of values
    for(int i = 0; i < ArraySize(values); i++)
     {
      //--- get event description
      if(CalendarEventById(values[i].event_id, event))
      {
        //--- get country description
        if(CalendarCountryById(event.country_id, country))
        {
          Print("✅ Event #", i);
          Print("Event ID:       ", values[i].event_id);

          Print("Event name: ", event.name);
          Print("Sector:           ", event.sector);
          Print("Source:         ", event.source_url);

          Print("Country name:  ", country.name);
          Print("Country URL:       ", country.url_name);

          Print("Time:            ", TimeToString(values[i].time, TIME_DATE | TIME_SECONDS));
          Print("Impact:          ", values[i].impact_type);

          // CHECK AND OUTPUT VALUES
          if(values[i].HasActualValue())
            Print("Actual:             ", values[i].GetActualValue());

          if(values[i].HasRevisedValue())
            Print("Revised:     ", values[i].GetRevisedValue());

          if(values[i].HasForecastValue())
            Print("Forecast:          ", values[i].GetForecastValue());

          if(values[i].HasPreviousValue())
            Print("Previous:       ", values[i].GetPreviousValue());
        }
      }
     }
   }
  else
   {
    int error = GetLastError();
    if(error == 0)
     {
      Print("❌ CalendarValueHistory: No Events");
     }
    else
     {
      Print("❌ Error CalendarValueHistory: ", error);
     }
   }
 }
//+------------------------------------------------------------------+

Fonksiyon, geçerli gün için USD para birimine ait olayların bir listesini ve takvimin MQL API'si aracılığıyla alınan yapıların ana alanlarının içeriğini görüntüler. Kodun tamamı makaleye ekli GetTodayEvents-S.mq5 dosyasında yer almaktadır.

Fonksiyonun çalışma sonuçları MetaTrader 5 terminalinin Araç Kutusu\Uzmanlar sekmesinde görülebilir. USD için iki olay alındığını görüyoruz. Talebin yapıldığı zamanda bu olaylar henüz gerçekleşmemişti (haberler yayınlanmamıştı). Bu nedenle, MqlCalendarValue::actual_value alanı bir değer içermez - HasActualValue() fonksiyonu 'false' geri döndürür.

Bu olay türleri için HasForecastValue() tarafından kontrol edilen MqlCalendarValue::forecast_value alanı yoktur (değer içermez). Diğer olaylar için dört alanın tümü (prev_value, actual_value, forecast_value ve revised_prev_value) eksik olabilir.

  Received events for USD: 2
✅ Event #0
Event ID:            840220005
Event name:      3-month treasury bills auction
Sector:              1
Source:              https://home.treasury.gov/
Country name:   USA
Country URL:     united-states
Time:                2026.04.20 18:30:00
Impact:             0
Previous:           3.62
✅ Event #1
Event ID:           840220006
Event name:      6-month treasury bills auction
Sector:              1
Source:              https://home.treasury.gov/
Country name:   USA
Country URL:     united-states
Time:                2026.04.20 18:30:00
Impact:             0
Previous:           3.61

Sağlanan kod için açıklamalar:

İlk olarak, "USD" para birimi filtresi ile belirli bir zaman aralığındaki tüm olaylar için bir değer dizisi elde ediyoruz. Bir döngü içinde, değer dizisi üzerinden geçiyoruz ve CalendarEventById() fonksiyonunu kullanarak MqlCalendarValue::event_id olay ID'sine göre olay açıklamasını talep ediyoruz. Ardından CalendarCountryById() fonksiyonunu kullanarak MqlCalendarEvent::country_id ID'sine göre bir ülke açıklaması talep ediyoruz.

CalendarValueHistory() fonksiyonu 'false' geri döndürür, ancak GetLastError() sıfır (hata yok) geri döndürürse, bu istenen ayarlara sahip hiçbir olay olmadığını gösterir.


Takvimle çalışırken hata işleme ve sınırlar

Uzak verilerle çalışmak her zaman bağlantı kesintileri veya erişim kısıtlamaları riski taşır. Takvim fonksiyonları başarısızlık durumunda false veya 0 geri döndürür. Nedenini anlamak için GetLastError() fonksiyonunu kullanmak gerekir. Dokümantasyonda takvim modülü için ayrı bir hata grubu vurgulanmaktadır.

Hata kodları:

  • ERR_CALENDAR_TIMEOUT (kod 5200) - sunucu yanıtı bekleme süresi doldu. Ağ arızası veya sunucu aşırı yüklenmesi. Çözüm: Talebi 5...10 saniyelik bir aradan sonra tekrarlayın.
  • ERR_CALENDAR_NO_DATA (kod 5201) - takvim verileri henüz yüklenmedi. Takvim eşzamansız olarak başlatılmıştır. Çözüm: 1...2 saniye bekleyin ve tekrarlayın.
  • ERR_CALENDAR_INVALID_DATE (kod 5202) - geçersiz tarih aralığı. Kod hatası (örneğin, başlangıç tarihi bitiş tarihinden daha ileridir). Çözüm: Mantığı düzeltin, talebi tekrarlamak işe yaramaz.
  • ERR_CALENDAR_INVALID_COUNTRY (kod 5203) - bilinmeyen ülke/para birimi kodu. Talep parametrelerinde hata. Çözüm: Kodu kontrol edin (örneğin, "USA" yerine "US").
  • ERR_CALENDAR_TOO_MANY_REQUESTS (kod 5204) - talep sınırı aşıldı. Kritik hata. Çözüm: Talepler arasındaki aralığı artırın.

Talep hızı sınırlaması:

MetaTrader 5 terminalinin bağlandığı sunucular, altyapıyı aşırı yükten korur. Uzman Danışman, CalendarValueHistory() fonksiyonunu her tikte veya gecikme olmaksızın bir döngü içinde çağırırsa, sunucu 5204 hatasını geri döndürür ve terminaliniz için takvime erişimi geçici olarak engeller. En iyi uygulama: başlangıçta OnInit() içinde gerekli aralık için verileri bir kez yükleyin.

Gerçek zamanlı güncellemeler için, geri döndürülen change_id değişkenini saklayarak CalendarValueLast() fonksiyonunu kullanın - bu, tüm veri dizisi yerine yalnızca değişikliklerin alınmasını sağlar. OnTimer() içindeki bir zamanlayıcıdaki verileri 5-10 dakikadan fazla olmayan aralıklarla güncelleyin.

Bir takvimi yüklerken ortaya çıkabilecek sorunlara yönelik bazı çözüm örneklerine bakalım:

Terminali ilk kez başlattığınızda ve Uzman Danışmanı çalıştırdığınızda takvim hemen hazır olmaz. Veriler arka planda sunucudan yüklenir. OnInit() fonksiyonunun ilk satırında CalendarValueHistory() fonksiyonunu çağırırsak, büyük olasılıkla 5201 - veri yok hatası alırız. Üstel veya sabit bir zaman aşımı ile bir durum sorgulama mekanizması uygulamak gerekir.

Pratik örnek - hata işleme ile takvim veri yükleme fonksiyonu:

//+------------------------------------------------------------------+
//| Calendar loading function                                        |
//+------------------------------------------------------------------+
bool LoadCalendar(MqlCalendarValue& values[], const datetime from, const datetime to, const string country_code = NULL, const string currency = NULL, const int max_retries = 5)
 {
  int retry_count = 0;

  while(retry_count < max_retries)
   {
    ResetLastError();
    //--- download attempt
    if(CalendarValueHistory(values, from, to, country_code, currency))
      return true; // Success

    int error = GetLastError();

    //--- in case of "No data" (5201) or "Timeout" (5200) error — wait and repeat
    if(error == 5201 || error == 5200)
     {
      retry_count++;
      Sleep(1000); // 1 second pause before repeating
      continue;
     }

    //--- if the error is critical (for example, an invalid date), interrupt the download immediately
    Print("❌ Critical Calendar Error: ", error);
    return false;
   }

  Print("❌ Failed to load calendar after ", max_retries, " attempts.");
  return false;
 }
//+------------------------------------------------------------------+

Sağlanan kod için açıklamalar:

"Veri yok" (5201) ve "Zaman aşımı" (5200) hataları olasılığını göz önünde bulundurun. Bunlar düzeltilebilir hatalardır. 1...5 saniye duraklama yaparak ve verileri tekrar talep ederek bu hataları yönetin. Düzeltilemeyen herhangi bir hata oluşursa, indirmeyi derhal durdurun. Düzeltilemeyen hatalar arasında geçersiz tarihler, yanlış para birimi veya ülke kodları vb. yer alır. Kodun tamamını bu makaleye ekli GetTodayEvents-S.mq5 dosyasında bulabilirsiniz.


Sınırlamalardan kaçınmak ve maksimum tepki hızını sağlamak için, OnTimer() içinde change_id mekanizmasını kullanmalıyız. Başlangıçta, tüm olay geçmişini yükleyin ve son change_id'yi hatırlayın. Zamanlayıcıda, CalendarValueLast() fonksiyonunu kullanarak yalnızca yeni verileri talep edin. Sunucu, eski verileri iletmek için kaynakları boşa harcamadan yalnızca değişenleri (veya herhangi bir değişiklik yoksa ‘false’) geri döndürecektir.

Pratik örnek - hata işleme ile takvim veri güncelleme fonksiyonu:

//+------------------------------------------------------------------+
//| Timer: incremental update                                        |
//+------------------------------------------------------------------+
void OnTimer()
 {
  if(!is_initialized)
    return;

  MqlCalendarValue updates[];
  ResetLastError();

//--- API automatically updates last_change_id by reference
  if(!CalendarValueLast(last_change_id, updates,
                        (InpCountryCode == "") ? NULL : InpCountryCode,
                        (InpCurrency    == "") ? NULL : InpCurrency))
   {
    int err = GetLastError();
    // 0 = SUCCESS/NO_NEW_DATA, 5402 = ERR_CALENDAR_NO_CHANGES
    if(err != 0 && err != 5402)
      Print("🔴️ CalendarValueLast error: ", err);
    return;
   }

  int cnt = ArraySize(updates);
  if(cnt == 0)
    return;

  Print("🟢 Received ", cnt, " updates. New change_id: ", last_change_id);
  if(InpPrintChanges)
    ArrayPrint(updates);
  SyncCache(updates);
 }
//+------------------------------------------------------------------+
//| Cache synchronization                                            |
//+------------------------------------------------------------------+
void SyncCache(const MqlCalendarValue &updates[])
 {
  int upd_cnt  = ArraySize(updates);
  int cache_sz = ArraySize(calendar_cache);

  for(int u = 0; u < upd_cnt; u++)
   {
    bool found = false;
    for(int c = 0; c < cache_sz; c++)
     {
      if(calendar_cache[c].id == updates[u].id)
       {
        calendar_cache[c] = updates[u];
        found = true;
        break;
       }
     }
    if(!found)
     {
      ArrayResize(calendar_cache, cache_sz + 1);
      calendar_cache[cache_sz] = updates[u];
      cache_sz++;
      total_events++;
     }
   }
 }
//+------------------------------------------------------------------+

Sağlanan kod için açıklamalar:

Olayların aşamalı güncellenmesi, OnTimer() zamanlayıcısındaki olay işleyicisi aracılığıyla gerçekleşir. Takvim API'si CalendarValueLast() fonksiyonu çağrılır - yeni olaylar ortaya çıkarsa, önceden yüklenmiş MqlCalendarValue yapı dizisindeki uygun öğeleri günceller ve terminal günlüğüne verileri yazdırır. Yüklenen dizide böyle bir olay bulunmazsa, diziye eklenir.

Kodun tamamı makaleye eklenmiş olan CalendarEventMonitor-EA.mq5 dosyasında yer almaktadır.


Olayları filtreleme: Genelden özele

Neden filtreleme:

Ekonomik takvim günlük 60-90 olay yayınlamaktadır. Bu, piyasa üzerinde hemen hiçbir etkisi olmayan yüzlerce küçük gösterge, tatil ve resmi konuşmanın sürekli akışını dinlemek gibidir. Her biri üzerinde işlem yapmak, aşırı işlem yapmanın ve bakiyenizi kaybetmenin kesin bir yoludur. Haberlere dayalı işlem yapan algoritmik bir yatırımcının görevi, piyasayı gerçekten hareket ettiren 3-5 olay bırakmaktır.

Haberlere dayalı algoritmik alım-satımda filtreleme, sinyal-gürültü oranını otomatik karar verme için uygun bir seviyeye çıkarmakla eşdeğerdir. MQL5 takvim API'sinde bu süreç, MqlCalendarEvent ve MqlCalendarValue yapı dizilerinin geçtiği çok seviyeli bir elek sistemi olarak uygulanır.

Filtreleme kriterleri:

Piyasa, haberlerin yayınlanmasının kendisine değil, açıklanan verilerin beklentilerden sapmasına ve makroekonomik etki düzeyine tepki verir. Düşük etkili olaylar (MqlCalendarValue::impact_type < 3) genellikle alım-satım algoritmaları ve piyasa yapıcılar tarafından göz ardı edilir.

Filtreleme olmadan şunları elde edersiniz:

  • Stratejinin ikincil istatistiklere dayalı yanlış tetiklemeleri;
  • Volatilite olmadan geniş makas dönemlerinde işlem;
  • Uzman Danışmanın mantığını gereksiz kontrollerle aşırı yükleme.

Filtrelemenin amacı, veri akışını %90 oranında azaltmak ve belirli bir zaman penceresinde yatırımcı tarafından seçilen hedef para birimi için yalnızca yüksek etkili olayları (ENUM_CALENDAR_EVENT_IMPORTANCE::CALENDAR_IMPORTANCE_HIGH) bırakmaktır. 

Hem gerçek zamanlı hem de test modunda eşit derecede güvenilir şekilde çalışacak güvenilir bir filtrenin nasıl oluşturulacağına bakalım.

Çok seviyeli filtreleme sistemi:

Terminalden indirilen "ham" olay verileri işlenmemiş cevher gibidir: önemsiz istatistiksel raporlardan algoritmik stratejiye hiçbir fayda sağlamayan tatil günlerine kadar binlerce girdi içerir.

Bu veri akışını temiz bir sinyale dönüştürmek için çok seviyeli bir filtreleme sistemine ihtiyacımız var. Olay filtreleme, ham verilerin içinden geçtiği bir "elek" görevi görür ve çıktıda yalnızca belirtilen kriterleri karşılayan olayları bırakır: para birimi, önem, kod ve zaman penceresi.

Etkili bir filtre hiyerarşik bir yapıya sahip olmalıdır: başlangıçta coğrafyaya göre kaba bir eleme işlemiyle başlar ve zaman penceresine göre ince ayar ile sona erer.

MQL5-API haberlerinin önemli bir özelliği - MqlCalendarValue yapısı currency_id ve country_id alanlarını içermez. Yalnızca event_id, zaman, gösterge değerleri ve etki türünü depolar. Bu filtrasyonu nasıl etkiler?

Para birimi filtreleme API talebi düzeyinde gerçekleştirilir. CalendarValueHistory(..., NULL, "USD") çağırdığınızda, terminalin kendisi USD dışındaki her şeyi göz ardı eder. Elde ettiğiniz dizi zaten para birimine göre filtrelenmiştir. Bu nedenle, currency_id'yi gerçek zamanlı olarak kontrol etmeye gerek yoktur - zamana, öneme ve beklenti varlığına/önemine göre filtrelemek yeterlidir.

Seviye 1: Para birimi filtresi

İlk bariyer olay coğrafyasıdır. EURUSD alım-satımı yaparken, Uzman Danışman yalnızca Euro Bölgesi (EUR) ve ABD'yi (USD) etkileyen olaylara tepki vermelidir. Filtreleme, işlem gören enstrümanın para birimi kodlarıyla olayların talep edilmesiyle gerçekleşir. Çapraz kurlar için (örneğin "GBPJPY"), her iki para birimi ("GBP" ve "JPY") ve "USD" için olayları talep etmemiz gerekir.

Seviye 2: Olayın önemi

Tüm haberler, ekonomi -aslında fiyatlar- üzerindeki etkileri bakımından farklı önemlere sahiptir. Tüketici Fiyat Endeksi (TÜFE) piyasayı 100 puan hareket ettirebilirken, ZEW Tüketici Güven Endeksi sadece 10 puan hareket ettirebilmektedir.

MQL5-API, önem derecesini ayarlamak için ENUM_CALENDAR_EVENT_IMPORTANCE numaralandırmasını kullanır:

enum ENUM_CALENDAR_EVENT_IMPORTANCE
{
   CALENDAR_IMPORTANCE_NONE,      // importance level is not set
   CALENDAR_IMPORTANCE_LOW,       // low importance
   CALENDAR_IMPORTANCE_MODERATE,  // medium importance
   CALENDAR_IMPORTANCE_HIGH       // high importance
};

Haber tabanlı Uzman Danışmanlar, likiditesi düşük haberlerde yanlış pozitifleri önlemek için YÜKSEK öneme sahip haberlerin altındaki her şeyi göz ardı etmelidir.

Seviye 3: Olay kodu

Yüksek öneme sahip olaylar arasında bile istisnalar vardır. Örneğin, bir ECB veya IMF konuşması önemli olarak işaretlenebilir, ancak algoritmik olarak işlenmesi belirli sayıların yayınlanmasından daha zordur. Bu nedenle, olay koduna göre filtrelemeye ihtiyacımız var: MqlCalendarEvent::event_code alanı benzersiz bir kimlik içerir (örneğin, "NONFARM", "CPI", "GDP"). Anketleri ve konuşmaları değil, yalnızca ekonomik sayıların yer aldığı olayları seçiyoruz.

Sadece ilk üç doğrulama seviyesini geçtikten sonra olay sonuç dizisine kopyalanır ve alım-satımda kullanılır.

Filtre seviyeleri

Şekil 4. Çok seviyeli filtreleme, haberlere dayalı alım-satımda başarının anahtarıdır

İlk üç filtreleme seviyesinden sonra Uzman Danışman, olayın yapılandırılan zaman penceresine denk gelip gelmediğini kontrol eder. Bu, Uzman Danışman çalışırken gerçekleşir.

Seviye 4: Zaman penceresi

Son aşama alaka düzeyinin kontrol edilmesidir. Bir ay sonra çıkacak haberlere ihtiyacımız yok. Dün çıkan haberler piyasa tarafından çoktan işlendi. Bu nedenle, TimeCurrent(), haberin yayınlanmasından önceki belirtilen süre ile haberin yayınlanmasından sonraki belirtilen süre arasındaki aralığa denk geliyorsa bir olay aktif olarak kabul edilir. Tipik olarak aşağıdaki ayarlar kullanılır: Pozisyonları kapatmak için haberden 15-30 dakika öncesi ve volatilite ve fiyat hareket yönünü analiz etmek için haberden 60 dakika sonrası.


MQL5 ikili kaynakları: Verimli önbelleğe alma için olayları çevrimdışı teste taşıma

Haber tabanlı algoritmik alım-satımda test yaparken karşılaşılan bir sorun, strateji sınayıcının internet erişimine sahip olmamasıdır. Bu çözüm testi hızlandırır ve deterministik olmayan faktörlere karşı koruma sağlar, ancak haber tabanlı stratejileri test etmek için bir sorun yaratır.

Sınayıcıdaki Uzman Danışman, CalendarValueHistory() fonksiyonunu çağırırsa bir hata ve boş bir dizi alır. Stratejiyi test etmek için, geçmiş haber verilerini .ex5 yürütülebilir dosyasına yerleştirmemiz gerekir. Bunun için MQL5 ikili kaynakları seçeneğini kullanıyoruz. Neden özellikle bu seçenek? Bu, yapılandırılmış verilere erişmenin en hızlı yoludur - erişim hızı yalnızca bilgisayarın dosya sisteminin performansı ile sınırlıdır.

MqlCalendarValue[] dizisinin kompakt bir ikili dosyaya nasıl dönüştürüleceğine ve sınayıcının RAM'den yıldırım hızında veri okuması için koda nasıl yerleştirileceğine bakalım. İlk adım, mevcut verileri takvimden indirecek ve ikili bir dosyaya kaydedecek bir dışa aktarıcı komut dosyası oluşturmaktır.

Pratik örnek - olayları ikili bir dosyaya aktarmak için bir komut dosyası:

Kodun tamamı makaleye ekli ExportCalendarForTester.mq5 dosyasında yer almaktadır.


1. Adım: Dizinin oluşturulması ve filtrelenmesi

Tüm küresel olayları kaynakta saklamayacağız. İkili bir dosyaya aktarma aşamasında, Uzman Danışmanda olduğu gibi aynı filtreleri uygularız: para birimi, önem ve olay türü. Komut dosyası girdileri para birimlerinin listesini, olayların yüklenmesi için zaman aralığını, olay kodlarını ve minimum önemi belirtir.

Olay kodları listesi (InpEventCodes parametresi) varsayılan olarak boştur - bu, tüm olay türlerinin yüklendiği anlamına gelir. Bu filtreyi daraltabiliriz. Örneğin, sadece Tarım Dışı İstihdam verileri - o zaman kod listesi "NONFARM" olacaktır.

InpUseCommonDir bayrağı ikili dosyanın nereye kaydedileceğini belirtir: true - tüm müşteri terminalleri için paylaşılan klasöre (\Terminal\Common\Files) kaydedilir.

//--- input parameters
input string                          InpCurrencies     = "USD";
input datetime                        InpDateFrom       = D'2025.01.01';
input datetime                        InpDateTo         = 0;
input string                          InpEventCodes     = "";
input ENUM_CALENDAR_EVENT_IMPORTANCE  InpMinImportance  = CALENDAR_IMPORTANCE_HIGH;
input string                          InpOutputFile     = "calendar_test_res.bin";
input bool                            InpUseCommonDir   = true;

Komut dosyasının, girdilerde belirtilen değerlerle olayları yükleyen ve filtreleyen kısmı aşağıda gösterilmiştir:

  Print("🔄 Calendar export:");
  Print("----------------------------------");
  Print("Filtering options:");
  Print(" 🟨 Interval = ", InpDateFrom, " — ", InpDateTo);
  Print(" 🟨 Currencies = ", InpCurrencies, "\n 🟨 Event Codes = ", InpEventCodes, "\n 🟨 Min Importance = ", EnumToString(InpMinImportance));
  Print("----------------------------------");

//--- initialize filter by currencies
  ArrayResize(currencies, 0);
  if(InpCurrencies == "")
    return;
  StringSplit(InpCurrencies, ',', currencies);
  currencies_size = ArraySize(currencies);
  for(int i = 0; i < currencies_size; i++)
    StringToUpper(currencies[i]);

//--- initialize the filter by event codes
  ArrayResize(event_codes, 0);
  if(InpEventCodes != "")
    StringSplit(InpEventCodes, ',', event_codes);
  event_codes_Size = ArraySize(event_codes);

//--- load calendar values with the CURRENCY FILTER (if specified)
  if(currencies_size > 0)
   {
    ArrayResize(values_size, currencies_size);
    ArrayFill(values_size, 0, currencies_size, 0);

    for(int i = 0; i < currencies_size; i++)
     {
      if(LoadCalendar(raw_values, InpDateFrom, InpDateTo, "", currencies[i]))
       {
        raw_values_size = ArraySize(raw_values);
        //--- load events with an IMPORTANCE AND EVENT CODE FILTER
        for(int k = 0; k < raw_values_size; k++)
         {
          //--- get event description
          if(CalendarEventById(raw_values[k].event_id, event))
           {
            //--- take indicators only
            if(event.type != CALENDAR_TYPE_INDICATOR)
              continue;                                   // nothing else to filter

            //--- check by IMPORTANCE
            if(event.importance < InpMinImportance)
              continue;                                   // nothing else to filter

            //--- check by EVENT CODE (if specified)
            if(event_codes_Size > 0)
             {
              bool code_allowed = false;
              for(int c = 0; c < event_codes_Size; c++)
               {
                StringToUpper(event.event_code);
                if(StringFind(event.event_code, event_codes[c]) >= 0)
                 {
                  code_allowed = true;
                  break;
                 }
               }
              if(code_allowed == false)
                continue;
             }

            //--- replenish the array of filtered events
            int event_index = ArraySize(values);
            ArrayResize(values, event_index + 1);
            values[event_index] = raw_values[k];
            values_size[i]++;
           }
         }
        Print("✅ Received values BY CURRENCY \"", currencies[i], "\": ", raw_values_size, " → Of these, filtered: ", values_size[i]);
       }
      else
       {
        int error = GetLastError();
        if(error == 0)
          Print("⚠ LoadCalendar Info: No Events for ", currencies[i]);
        else
          Print("❌ LoadCalendar Error: ", error, " for ", currencies[i]);
        return;                                     // nothing else to filter
       }
     }
   }

Sağlanan kod için açıklamalar:

  • values_size[] dizisi, girdilerde belirtilen her bir para birimi kodu için filtrelenen olay sayısını saklar.
  • MQL5 datetime türünün sıfır değeri 1970.01.01 00:00:00 tarihine karşılık gelir.
  • Olay türüne göre ek filtreleme gerçekleşir. Sadece ENUM_CALENDAR_EVENT_TYPE::CALENDAR_TYPE_INDICATOR türündeki olaylar ayrıştırılır - bunlar ekonomik olaylardır (konuşmalar değil, sayılar). Diğer tüm haberler göz ardı edilir. Bu, filtrelemenin en başında doğrulama ile yapılır:
//--- take only indicators
if(event.type != CALENDAR_TYPE_INDICATOR)
  continue;                                   // Nothing else to filter


Komut dosyasını farklı filtreleme parametreleriyle çalıştıralım ve olay yükleme/filtreleme komut dosyasının ne kadar hızlı çalıştığını görelim. Hızın bilgisayarın gücüne ve internet üzerinden veri sunucularına bağlantı hızına bağlı olduğu açıktır. Bununla birlikte, bir stres testi gerçekleştireceğiz. Sonuçlar aşağıda sunulmuştur.

Mevcut tüm tarih boyunca yüksek öneme sahip tüm USD olayları:

13:59:43.724    🔄 Calendar export:
13:59:43.724    ----------------------------------
13:59:43.724    Filtering options:
13:59:43.724      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
13:59:43.724      🟨 Currencies = USD
13:59:43.724      🟨 Event Codes = 
13:59:43.724      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
13:59:43.724    ----------------------------------
13:59:45.511    ✅ Received values BY CURRENCY "USD": 53346 → Of these, filtered: 9009
13:59:45.511    ----------------------------------
13:59:45.513    ✅ Saved: USD_calendar_test_res.bin   Size: 1153152 bytes  (9009 events)

Filtrelenmiş değerler (MqlCalendarValue yapıları) dizisinin elde edilme zamanı 1.80 sn (0.20 ms / filtreleme başına).


Mevcut tüm tarih boyunca yüksek öneme sahip tüm USD, EUR ve JPY olayları:

14:03:08.724    🔄 Calendar export:
14:03:08.724    ----------------------------------
14:03:08.724    Filtering options:
14:03:08.724      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:03:08.724      🟨 Currencies = USD,EUR,JPY
14:03:08.724      🟨 Event Codes = 
14:03:08.724      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:03:08.724    ----------------------------------
14:03:10.488    ✅ Received values BY CURRENCY "USD": 53346 → Of these, filtered: 9009
14:03:10.947    ✅ Received values BY CURRENCY "EUR": 45139 → Of these, filtered: 1102
14:03:11.404    ✅ Received values BY CURRENCY "JPY": 18907 → Of these, filtered: 937
14:03:11.404    ----------------------------------
14:03:11.406    ✅ Saved: USD_calendar_test_res.bin   Size: 1153152 bytes  (9009 events)
14:03:11.407    ✅ Saved: EUR_calendar_test_res.bin   Size: 141056 bytes  (1102 events)
14:03:11.408    ✅ Saved: JPY_calendar_test_res.bin   Size: 119936 bytes  (937 events)

Filtrelenmiş değerler (MqlCalendarValue yapıları) dizisinin elde edilme zamanı 2.68 sn (0.24 ms / filtreleme başına).


Mevcut tüm tarih boyunca yüksek öneme sahip tüm USD - NFP (Tarım Dışı İstihdam) olayları:

14:07:22.203    🔄 Calendar export:
14:07:22.203    ----------------------------------
14:07:22.203    Filtering options:
14:07:22.203      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:07:22.203      🟨 Currencies = USD
14:07:22.203      🟨 Event Codes = NONFARM
14:07:22.203      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:07:22.203    ----------------------------------
14:07:22.290    ✅ Received values BY CURRENCY "USD": 53346 → Of these, filtered: 473
14:07:22.290    ----------------------------------
14:07:22.291    ✅ Saved: USD_calendar_test_res.bin   Size: 60544 bytes  (473 events)

Filtrelenmiş değerler (MqlCalendarValue yapıları) dizisinin elde edilme zamanı 0.09 sn (0.18 ms / filtreleme başına).

Notlar:

  1. Filtrede olay kodunun tamamının belirtilmesi gerekli değildir. Kodu içeren dizgenin benzersiz bir bölümünü belirtmek yeterlidir. Örneğin, Tarım Dışı İstihdam verileri için tam kod "NONFARM-PAYROLLS" şeklindedir - ancak "NONFARM" belirtilmesi yeterlidir.
  2. MetaTrader 5 terminalinde gerçek zamanlı bir alım-satım sistemi çalıştırırken, tüm dönem için tüm olayları indirmemiz gerekmeyecektir. Tipik olarak, o gün ve gelecek hafta için güncel haberler indirilir. Bu nedenle, haber tabanlı Uzman Danışman algoritmasındaki yükleme süresi ihmal edilebilir.


2. Adım: Olay dizisini ikili bir dosyaya aktarma

SaveToBinary fonksiyonu filtrelenmiş olayları bir ikili dosyaya kaydeder. Dosya sayısı, girdilerde belirtilen para birimi kodlarının sayısına eşittir - her para birimi kendi ikili dosyasına aktarılır. Dosya adı para birimi kodu önekiyle başlar. Örneğin, "USD_calendar_test_res.bin". Bu şekilde, para birimi kodu ile buna ilişkin olayların listesi arasında bire bir eşleşme elde ederiz.

//+------------------------------------------------------------------+
//| Save an array to a binary file                                   |
//+------------------------------------------------------------------+
void SaveToBinary(MqlCalendarValue &values[], const int &vls_size[], const string filename, const string &currencies[])
 {
  if(ArraySize(values) == 0)
   {
    Print("⚠️ Nothing to save");
    return;
   }

  Print("----------------------------------");
  int offset = 0;
  for(int i = 0; i < ArraySize(vls_size); i++)
   {
    int file_handle = FileOpen(currencies[i] + "_" + filename, FILE_WRITE | FILE_BIN | (InpUseCommonDir ? FILE_COMMON : 0));

    if(file_handle == INVALID_HANDLE)
     {
      Print("❌ FileSave failed: ", GetLastError());
      return;
     }

    FileWriteArray(file_handle, values, offset, vls_size[i]);
    FileFlush(file_handle);
    FileClose(file_handle);

    Print("✅ Saved: ", currencies[i] + "_" + filename, "   Size: ", vls_size[i] * sizeof(MqlCalendarValue), " bytes", "  (", vls_size[i], " events)");

    offset += vls_size[i];
   }
 }

Olayları ikili dosyalara aktarmak için komut dosyasını başarıyla çalıştırdıktan sonra, MetaTrader 5'in Uzmanlar sekmesinde aşağıdakine benzer mesajlar görünür:

14:28:42.077    ----------------------------------
14:28:42.078    ✅ Saved: USD_calendar_test_res.bin   Size: 58240 bytes  (455 events)
14:28:42.079    ✅ Saved: EUR_calendar_test_res.bin   Size: 7680 bytes  (60 events)


3. Adım: Kaynağın Uzman Danışmana derlenmesi

ExportCalendarForTester.mq5 komut dosyasını MQL5/Files/ içinde başlattıktan sonra, USD_calendar_test_res.bin ve EUR_calendar_test_res.bin dosyaları görünür. Şimdi bunların Uzman Danışmana "yerleştirilmesi" gerekiyor. Uzman Danışman dosyasının başına (#property yönergelerinden sonra), aşağıdaki şekilde bir (tek bir para birimi için bir dosya kullanıyorsanız) veya birkaç satır ekleyin:

// Embed the binary file as a static resource at the compilation stage
#resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[]
#resource "\\Files\\EUR_calendar_test_res.bin" as MqlCalendarValue EUR_res_calendar_data[]

Not:

  1. #resource yolu MQL5/Files/ klasörüne göre belirtilir. \\Files\\ ön eki zorunludur.
  2. Uzman Danışman derlenirken, MetaEditor'ın Hatalar sekmesinde aşağıdaki gibi mesajlar görünmelidir:
  • 'USD_calendar_test_res.bin' as 'const MqlCalendarValue USD_res_calendar_data[455]' 
  • 'EUR_calendar_test_res.bin' as 'const MqlCalendarValue EUR_res_calendar_data[60]' 

Uzman Danışman başlatılırken, dışa aktarma aşamasında kaydedilen olayların yapıları USD_res_calendar_data[] ve EUR_res_calendar_data[] dizilerinde saklanır.


Uzman Danışmana entegrasyon: Otomatik mod değiştirme

Amaç, aynı Uzman Danışman kodunun hem gerçek zamanlı olarak hem de sınayıcıda veri kaynağını otomatik olarak seçerek çalışmasını sağlamaktır. MQLInfoInteger(MQL_TESTER) fonksiyonu, Uzman Danışman strateji sınayıcı veya optimize edicide çalışıyorsa 'true' geri döndürür.

//+------------------------------------------------------------------+
//| Initialization: select a data source                             |
//+------------------------------------------------------------------+
int OnInit()
 {
  Print("🔄 Initializing the news module:");

//--- initialize filter by currencies
  ArrayResize(currencies, 0);
  if(InpCurrencies == "")
    return INIT_FAILED;
  StringSplit(InpCurrencies, ',', currencies);
  currencies_size = ArraySize(currencies);
  for(int i = 0; i < currencies_size; i++)
    StringToUpper(currencies[i]);

//--- initialize the filter by event codes
  ArrayResize(event_codes, 0);
  if(InpEventCodes != "")
    StringSplit(InpEventCodes, ',', event_codes);
  event_codes_Size = ArraySize(event_codes);

//--- define the execution environment
  bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE);
  if(is_tester)
   {
    //--- TESTER MODE: Load from resource
    if(!LoadFromResource())
     {
      Print("❌ ERROR: Failed to load calendar from resource");
      return INIT_FAILED;
     }
    is_live_mode = false;
    Print("⚠️ Mode: TESTER (data from resource)");
   }
  else
   {
    //--- LIVE MODE: Load from API
    if(!LoadFromCalendarAPI())
     {
      int error = GetLastError();
      Print("❌ ERROR: Failed to load calendar from API - ", error);
      return INIT_FAILED;
     }
    is_live_mode = true;

    Print("⚠️ Mode: LIVE (data from API)");
   }

  return INIT_SUCCEEDED;
 }


Sağlanan kod için açıklamalar:

Ana kontrol OnInit() Uzman Danışman başlatma olay işleyicisinde yapılır:

   //--- define the execution environment
   bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE);
   if(is_tester)
    {
      //--- TESTER MODE: Load from resource
      if(!LoadFromResource())
       {
        ...
       }
    }
   else
    {
      //--- LIVE MODE: Load from API
      if(!LoadFromCalendarAPI())
       {
        ...
       }
    }

Sağlanan kod için açıklamalar:

Çalışan MQL5 programının (burada bizim Uzman Danışmanımızdır) hangi modda çalıştığını belirleriz. Aşağıdaki bayraklarla ilgileniyoruz:

  • MQL_TESTER - sınayıcıda başlatılan programın bayrağı;
  • MQL_OPTIMIZATION - optimizasyonda başlatılan programın bayrağı;
  • MQL_VISUAL_MODE - görsel test modunda başlatılan programın bayrağı.
Bu bayraklardan en az biri etkinse, olayların listesini kaynaktan yükleriz. Aksi takdirde, olay listesini takvim API'si aracılığıyla sunucudan olağan şekilde yükleriz.


14:42:18.894    🔄 Initializing news module:
14:42:18.905    ✅ Events from Server Loaded: 455 events for: USD
14:42:18.914    ✅ Events from Server Loaded: 60 events for: EUR
14:42:18.914    ⚠️ Mode: LIVE (data from API)



Veri dönüştürme zincirinin doğrulanması: Olaylar → İkili dosya → Kaynağa derleme → Kaynaktan olayları alma

Geriye kalan tek şey, sınayıcıda olayların alınmasını kontrol etmek ve bunların gerçek zamanlı olarak alınan listeyle tamamen eşleştiğinden emin olmaktır. Bu, haberlere dayalı alım-satım algoritmalarını otomatik olarak test etme ve optimize etme olanağını doğrulayacaktır.

Bunu yapmak için, sunucudan alınan/kaynaktan indirilen olayların çıktısını terminal/strateji sınayıcı günlüğüne ekleriz. Sınayıcı çalıştırma sonuçlarını içeren ImportTesterLog.txt tam metin dosyası aşağıya eklenmiştir. Aşağıda günlük dosyasının bir parçası yer almaktadır - her bir para birimi kodu için olay dizisinin başlangıcı ve sonu.

Strateji sınayıcıda görsel modda tek test sonucu:

16:48:22.527    EURUSD,M5: testing of Experts\ImportCalendarValidation-EA.ex5 from 2026.01.01 00:00 to 2026.03.07 00:00 started with inputs:
16:48:22.527      InpCurrencies=USD,EUR
16:48:22.527      InpDateFrom=1735689600
16:48:22.527      InpDateTo=1767225600
16:48:22.527      InpEventCodes=
16:48:22.527      InpMinImportance=3
16:48:22.546   🔄 Initializing news module:
16:48:22.546   ✅ Events from Resource Loaded: 455 events for: USD
16:48:22.546   ✅ Event #0
16:48:22.546   ID:                    230215
16:48:22.546   Event ID:          840140001
16:48:22.546   Time:               2025.01.02 16:30:00
16:48:22.546   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.546   Revision:          0
16:48:22.546   Actual:             211.0
16:48:22.546   Revised:           220.0
16:48:22.546   Forecast:          219.0
16:48:22.546   Previous:          219.0
16:48:22.546   ✅ Event #1
16:48:22.546   ID:                   230641
16:48:22.546   Event ID:          840500001
16:48:22.546   Time:               2025.01.02 17:45:00
16:48:22.546   Impact:            CALENDAR_IMPACT_NEGATIVE
16:48:22.546   Revision:          3
16:48:22.546   Actual:             49.4
16:48:22.546   Forecast:          50.5
16:48:22.546   Previous:          49.7
 ...
16:48:22.553   ✅ Event #453
16:48:22.553   ID:                   274419
16:48:22.553   Event ID:          840510001
16:48:22.553   Time:               2025.12.30 17:45:00
16:48:22.553   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:          0
16:48:22.553   Actual:             43.5
16:48:22.553   Forecast:          42.4
16:48:22.553   Previous:          36.3
16:48:22.553   ✅ Event #454
16:48:22.553   ID:                   274484
16:48:22.553   Event ID:          840140001
16:48:22.553   Time:               2025.12.31 16:30:00
16:48:22.553   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:          0
16:48:22.553   Actual:             199.0
16:48:22.553   Revised:           215.0
16:48:22.553   Forecast:          227.0
16:48:22.553   Previous:          214.0
...
16:48:22.553   ✅ Events from Resource Loaded: 60 events for: EUR
16:48:22.553   ✅ Event #0
16:48:22.553   ID:                   231322
16:48:22.553   Event ID:         999030013
16:48:22.553   Time:              2025.01.07 13:00:00
16:48:22.553   Impact:           CALENDAR_IMPACT_NA
16:48:22.553   Revision:         1
16:48:22.553   Actual:            2.4
16:48:22.553   Forecast:         2.4
16:48:22.553   Previous:         2.2
16:48:22.553   ✅ Event #1
16:48:22.553   ID:                  187384
16:48:22.553   Event ID:         999040007
16:48:22.553   Time:              2025.01.08 13:00:00
16:48:22.553   Impact:           CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:         0
16:48:22.553   Actual:            21.0
16:48:22.553   Revised:          17.8
16:48:22.553   Forecast:         15.7
16:48:22.553   Previous:         17.7
...
16:48:22.555   ✅ Event #58
16:48:22.555   ID:                  204746
16:48:22.555   Event ID:         999010006
16:48:22.555   Time:              2025.12.18 16:15:00
16:48:22.555   Impact:           CALENDAR_IMPACT_NA
16:48:22.555   Revision:         0
16:48:22.555   Actual:            2.0
16:48:22.555   Previous:         2.0
16:48:22.555   ✅ Event #59
16:48:22.555   ID:                  204762
16:48:22.555   Event ID:         999010007
16:48:22.555   Time:              2025.12.18 16:15:00
16:48:22.555   Impact:           CALENDAR_IMPACT_NA
16:48:22.555   Revision:         0
16:48:22.555   Actual:            2.15
16:48:22.555   Previous:         2.15
16:48:22.555   ⚠️ Mode: TESTER (data from resource)

Tüm olay dizilerinin kaynaktan yaklaşık 10 ms içinde yüklendiği görülüyor, bu şaşırtıcı değildir - kaynak Uzman Danışmanın koduna yerleştirilmiştir ve test başladığında onunla birlikte yüklenir.

MetaTrader 5 terminalinde Canlı modda bir Uzman Danışmanın aynı şekilde çalıştırılması (başlatılması), yüklenen olayların bir listesini içeren benzer bir günlük dosyası oluşturur. Canlı yükleme sonuçlarını içeren LoadLiveLog.txt dosyasının tamamı makaleye eklenmiştir. Aşağıda günlük dosyasının bir parçası yer almaktadır - her bir para birimi kodu için olay dizisinin başlangıcı ve sonu.

Olayların sunucudan canlı olarak indirilmesinin sonuçları:

17:27:25.250    🔄 Initializing news module:
17:27:25.258    ✅ Received values BY CURRENCY "USD": 3589 → Of these, filtered: 455
17:27:25.258    ✅ Events from Server Loaded: 455 events for: USD
17:27:25.258    ✅ Event #0
17:27:25.258    ID:                   230215
17:27:25.258    Event ID:          840140001
17:27:25.258    Time:               2025.01.02 16:30:00
17:27:25.258    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.258    Revision:          0
17:27:25.258    Actual:             211.0
17:27:25.258    Revised:           220.0
17:27:25.258    Forecast:          219.0
17:27:25.258    Previous:          219.0
17:27:25.258    ✅ Event #1
17:27:25.258    ID:                   230641
17:27:25.258    Event ID:          840500001
17:27:25.258    Time:               2025.01.02 17:45:00
17:27:25.258    Impact:            CALENDAR_IMPACT_NEGATIVE
17:27:25.258    Revision:          3
17:27:25.258    Actual:             49.4
17:27:25.258    Forecast:         50.5
17:27:25.258    Previous:          49.7
...
17:27:25.288    ✅ Event #453
17:27:25.288    ID:                   274419
17:27:25.288    Event ID:          840510001
17:27:25.288    Time:               2025.12.30 17:45:00
17:27:25.288    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.288    Revision:          0
17:27:25.288    Actual:             43.5
17:27:25.288    Forecast:          42.4
17:27:25.288    Previous:          36.3
17:27:25.288    ✅ Event #454
17:27:25.288    ID:                    274484
17:27:25.288    Event ID:          840140001
17:27:25.288    Time:               2025.12.31 16:30:00
17:27:25.288    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.288    Revision:          0
17:27:25.288    Actual:             199.0
17:27:25.288    Revised:           215.0
17:27:25.288    Forecast:          227.0
17:27:25.288    Previous:          214.0

17:27:25.301    ✅ Received values BY CURRENCY "EUR": 3116 → Of these, filtered: 0
17:27:25.301    ✅ Events from Server Loaded: 60 events for: EUR
17:27:25.301    ✅ Event #0
17:27:25.301    ID:                   231322
17:27:25.301    Event ID:          999030013
17:27:25.301    Time:               2025.01.07 13:00:00
17:27:25.301    Impact:            CALENDAR_IMPACT_NA
17:27:25.301    Revision:          1
17:27:25.301    Actual:             2.4
17:27:25.301    Forecast:          2.4
17:27:25.301    Previous:          2.2
17:27:25.301    ✅ Event #1
17:27:25.301    ID:                   187384
17:27:25.301    Event ID:          999040007
17:27:25.301    Time:               2025.01.08 13:00:00
17:27:25.301    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.301    Revision:          0
17:27:25.301    Actual:             21.0
17:27:25.301    Revised:           17.8
17:27:25.301    Forecast:         15.7
17:27:25.301    Previous:          17.7
...
17:27:25.303    ✅ Event #58
17:27:25.303    ID:                   204746
17:27:25.303    Event ID:          999010006
17:27:25.303    Time:               2025.12.18 16:15:00
17:27:25.303    Impact:            CALENDAR_IMPACT_NA
17:27:25.303    Revision:          0
17:27:25.303    Actual:             2.0
17:27:25.303    Previous:          2.0
17:27:25.303    ✅ Event #59
17:27:25.303    ID:                   204762
17:27:25.303    Event ID:          999010007
17:27:25.303    Time:               2025.12.18 16:15:00
17:27:25.303    Impact:            CALENDAR_IMPACT_NA
17:27:25.303    Revision:          0
17:27:25.303    Actual:            2.15
17:27:25.303    Previous:         2.15
17:27:25.303    ⚠️ Mode: LIVE (data from API)

Filtreleme ve günlük kaydı ile tam bir indirme yaklaşık 50 ms sürer - hala olayların indirilme/filtrelenme süresini göz ardı edecek kadar hızlıdır. İki günlük dosyasını karşılaştırdığımızda, olay listesinin hem olay sayısı hem de alanların değerleri açısından tamamen aynı olduğunu görüyoruz. Veri dönüştürme zinciri doğrulaması başarıyla tamamlandı.

Tipik hataların ve yanlış beklentilerin analizi

Geliştiricilerin MQL5 ekonomik takvimini entegre ederken yaptıkları en yaygın altı hataya bakalım. Bunların her biri gerçek hayatta test edilmiştir ve bir kişinin bakiyesini kaybetmesine neden olabilir.

1. "Takvim hareketi tahmin eder"

Yanlış beklenti:

YÜKSEK öneme sahip bir haber yayınlanırsa ve açıklanan veri beklentiden önemli ölçüde farklı olursa, fiyatın sapma yönünde hareket etmesi garantidir. Baz para birimi için açıklanan > beklenti ise basitçe alış yapılır.

Gerçek:

Ekonomik takvim bir veri kaynağıdır, işlem sinyali oluşturucu değildir. Size ne olduğunu söyler, ancak piyasanın nasıl tepki vereceğini söylemez. Fiyat neden "bariz" mantığa aykırı bir şekilde hareket edebilir?

  • Piyasa, haberin yayınlanmasından günler önce beklentileri fiyatlamıştır. Haberin yayınlandığı anda "haberin satılması" gerçekleşir.
  • Merkez bankası politikasının sıkılaştırıldığı bir dönemde güçlü enflasyon verileri para birimini güçlendirebilir, ancak gevşeme döneminde aşırı ısınma korkusu nedeniyle satışları tetikleyebilir.
  • Haber iyi olabilir, ancak önceki değer aşağı doğru revize edilirse, genel sinyal belirsizleşir.
  • Birden fazla para birimine ait verilerin aynı anda açıklanması, doğrusal olarak yorumlanamayacak çapraz etkiler yaratır.

Doğru yaklaşım, takvimi bir giriş tetikleyicisi olarak değil, bir volatilite filtresi olarak kullanmaktır:

//--- instead of
if(actual > forecast)
 OrderSend(...);

//--- use:
if(IsHighImpactNewsComingSoon(30)) 
 {
   //--- reduce the position size or temporarily suspend trading
   ReduceRiskExposure();
 }
Altın kural: Takvim, "Hangi yönde işlem yapmalı?" sorusuna değil, "Ne zaman volatilite artışı beklemeli?" sorusuna yanıt verir.


2. "Tüm YÜKSEK öneme sahip olaylar eşit derecede önemlidir"

Yanlış beklenti:

Önem == CALENDAR_IMPORTANCE_HIGH alanı, olayın piyasayı 50+ pip hareket ettireceğinin garanti olduğu anlamına gelir. Tüm bu haberlerde aynı algoritmayı kullanarak işlem yapabiliriz.

Gerçek:

ENUM_CALENDAR_EVENT_IMPORTANCE numaralandırmasındaki belirli değerler, takvim editörleri tarafından yapılan öznel bir değerlendirmedir, piyasa etkisinin nicel bir ölçütü değildir. Örneğin, YÜKSEK olarak etiketlenmiş, ancak farklı piyasa önemine sahip iki olayı ele alalım.

İlk olay olan Tarım Dışı İstihdam (ABD), kilit bir istihdam göstergesi olduğu ve Fed politikasını etkilediği için 80-150 puanlık bir volatilite tepkisi üretir. İkinci olay olan İmalat PMI (Euro Bölgesi), dar sektörlü bir gösterge olduğu ve ECB için ikincil olduğu için 10-30 puanlık bir tepki oluşturur.

Doğru yaklaşım, önem filtrelemesini bir öncelikli olay kodları listesiyle desteklemektir:

//--- a list of events that are really worth reacting to
bool IsGoodEvent(const string event_code)
 {
   static const string tier1_codes[] = 
   {
      "NONFARM", "CPI", "GDP", "RATE", "FOMC", "ECB_RATE", 
      "RETAIL_SALES", "UNEMPLOYMENT", "PMI_MANUFACTURING"
   };
   
   for(int i = 0; i < ArraySize(tier1_codes); i++)
      if(StringFind(event_code, tier1_codes[i]) != -1)
         return true;
   return false;
 }

//--- use in filter
if(event.importance == CALENDAR_IMPORTANCE_HIGH && IsGoodEvent(event.event_code))
 {
   //--- handle only truly significant news
 }

Öneri:

Geçmiş volatilite analizine dayalı olarak kendi olay sıralamanızı oluşturun - önceden ayarlanmış herhangi bir etiketten daha güvenilirdir.


3. "TimeTradeServer() yerine TimeLocal() kullandım"

Yanlış beklenti:

MqlCalendarValue::time yapısındaki olay zamanı benim yerel saat dilimimde (veya UTC) belirtiliyor, dolayısıyla TimeLocal() veya TimeGMT() ile karşılaştırabilirim.

Gerçek:

Tüm ekonomik takvim fonksiyonları işlem sunucusunun saat dilimindeki zamanı geri döndürür (TimeTradeServer()). Bu, manuel dönüştürme ihtiyacını ortadan kaldıran ancak geliştiricinin disiplinli olmasını gerektiren mimari bir karardır.

Bu hatanın sonuçları ciddidir - sunucunuz EET zaman dilimindeyse (UTC+2) ve TimeLocal() kullanıyorsanız (örneğin MSK, UTC+3), olayları 1 saatlik bir sapmayla kontrol edersiniz. Uzman Danışman haberleri kaçırabilir veya tam tersine, haberlere sonradan tepki verebilir.

Doğru yaklaşım, tüm zaman karşılaştırmaları için her zaman TimeTradeServer() kullanmaktır:

//--- WRONG — risk of desynchronization
datetime now = TimeLocal();
if(event.time - now < 1800)
 { ... }

//--- RIGHT — guaranteed synchronization
datetime now = TimeTradeServer();
if(event.time - now < 1800)
 { ... }

//--- in the tester, TimeTradeServer() returns the model time, so the logic works identically to live

Öneri:

TimeToString(TimeTradeServer(), TIME_MINUTES) çıktısını hata ayıklama günlüğüne ekleyin ve olay zamanıyla karşılaştırın - dönüştürme olmadan eşleşmeleri gerekir.


4. "Olay alanlarının boş olabileceğini unuttum"

Yanlış beklenti:

actual_value, forecast_value ve diğer alanlar her zaman doğru sayısal değerleri içerir. Güvenle 1,000,000'a bölebilir ve karşılaştırabilirim.

Gerçek:

Dokümantasyona göre, MqlCalendarValue yapısının sayısal alanları bir milyonla çarpılmış değerleri veya herhangi bir değer belirtilmemişse LONG_MIN sabitini depolar. Kontrolü göz ardı edersek ne olur:

//--- error
double actual = values[i].actual_value / 1000000.0; // if actual_value == LONG_MIN, result: -9223372036.854776

if(actual > forecast) // comparison with a "garbage" number causes a false alarm
  OpenBuy();

Doğru yaklaşım, değerleri güvenli bir şekilde elde etmek için MqlCalendarValue yapısının yerleşik metotlarını kullanmaktır:

//--- the right approach is built-in methods
if(values[i].HasActualValue() && values[i].HasForecastValue())
 {
  double actual = values[i].GetActualValue();
  double forecast = values[i].GetForecastValue();

  if(!MathIsNaN(actual) && !MathIsNaN(forecast))
   {
    double deviation = actual - forecast;
    // ... analysis logic
   }
 }
//+------------------------------------------------------------------+

Not:

GetActualValue(), GetForecastValue() ve diğer fonksiyonlar veri yoksa NaN geri döndürür. Hesaplamalarda kullanmadan önce sonucu her zaman MathIsValidNumber() veya MathIsNaN() ile kontrol edin.


5. "OnTick() içinde takvim API fonksiyonlarını çağırabilirim"

Yanlış beklenti:

Her zaman güncel verilere sahip olmak için her tikte CalendarValueHistory() fonksiyonunu çağıracağım. Bu, Uzman Danışmanın hiçbir son dakika haberini kaçırmamasını sağlar.

Gerçek:

Takvim fonksiyonları, MetaTrader 5'in bağlandığı uzak bir sunucu aracılığıyla çalışır ve katı talep oranı sınırlarına sahiptir. OnTick() çağrısı (saniyede düzinelerce kez ateşlenebilir) sorunlara neden olacaktır:

  • Hata 5204 (ERR_CALENDAR_TOO_MANY_REQUESTS) - takvime erişimin geçici olarak engellenmesi.
  • İşlem gerçekleştirme gecikmeleri - işlem döngüsündeki bir ağ talebi kaymayı artırır.
  • Aşırı trafik - aynı verinin tekrar tekrar indirilmesi.
Doğru yaklaşım, önbelleğe alma + aşamalı yenileme kullanmaktır:
//--- global variables
MqlCalendarValue calendar_cache[];
bool cache_initialized = false;
long last_change_id = 0;

//+------------------------------------------------------------------+
int OnInit()
 {
//--- initial history loading (once at startup)
  datetime from = TimeCurrent() - 7 * 86400;
  datetime to = TimeCurrent() + 30 * 86400;

  if(CalendarValueHistory(calendar_cache, from, to, "USD"))
   {
    cache_initialized = true;
   }

//--- set a timer for periodic updates (no more than 5-10 minutes)
  EventSetTimer(300);
  return INIT_SUCCEEDED;
 }
//+------------------------------------------------------------------+
void OnTimer()
 {
  if(!cache_initialized)
    return;

  MqlCalendarValue updates[];

//--- only request changes since the last update
  if(CalendarValueLast(last_change_id, updates, "USD") > 0)
   {
    if(ArraySize(updates) > 0)
     {
      //--- merge new data with the cache
      MergeUpdates(calendar_cache, updates);
      last_change_id = updates[ArraySize(updates) - 1].change_id;
     }
   }
 }
//+------------------------------------------------------------------+
void OnTick()
 {
//--- work only with local cache - no network delays
  if(cache_initialized)
    ProcessNewsSignals(calendar_cache);
 }

Öneri:

OnTick() yalnızca yerel verilerle çalışmalıdır. Ağ talepleri - yalnızca OnInit(), OnTimer() içinde veya kullanıcı olayı aracılığıyla.


6. "Yeni olaylarla test yapmak için Uzman Danışmanı yeniden derlemeyi unuttum"

Yanlış beklenti:

Dosyalar klasöründeki calendar_test_res.bin kaynak dosyasını güncelledim, artık sınayıcı yeni olayları otomatik olarak görecektir. Uzman Danışmanı yeniden derlemeye gerek yok.

Gerçek:

#resource yönergesi derleme sırasında .ex5 çalıştırılabilir dosyasına veri yerleştirir. Harici .bin dosyasındaki değişiklikler dinamik olarak alınmaz - Uzman Danışman, kaynağın son derleme zamanındaki halini kullanmaya devam eder.

Hata belirtileri:

  • Sınayıcı günlüğü, son derlemeden sonra kaynağa eklenen olayları içermez.
  • Strateji, dosyada olmasına rağmen önemli haberleri "görmez".
  • Kod mantıksal olarak doğru olmasına rağmen test sonuçları beklentilerden farklıdır.

Doğru yaklaşım, kaynağı güncelledikten sonra Uzman Danışmanı her zaman yeniden derlemektir:

//--- at the beginning of the EA file
#resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[]

//--- after USD_calendar_test_res.bin update:
// 1. Save changes to the resource file.
// 2. Press F7 in MetaEditor (or Compile in the menu).
// 3. Make sure there are no errors in the compilation log.
// 4. Run the test again.

Öneri:

.ex5'teki verilerin uygunluğunu görsel olarak izlemek için derleme günlüğüne kaynak sürümü çıktısı (örn. hash veya son güncelleme tarihi) ekleyin.

Notlar:

Bu bölümde tartışılan hatalar, MQL5 sözdizimi hakkındaki bilgi eksikliğinden değil, ekonomik verilerin (göstergelerin) doğasına ilişkin basitleştirilmiş bir anlayıştan kaynaklanmaktadır. Bu altı hatadan kaçınmayı öğrenen herkes sadece çalışan bir koda değil, piyasa değişikliklerine ve ekonomik düzenleyicilerin gereksinimlerine uyum sağlayabilen istikrarlı bir alım-satım sistemine sahip olacaktır. Algoritmik alım-satımda bir amatörü bir profesyonelden ayıran da bu yaklaşımdır.


Sonuç

MetaTrader 5 terminalindeki ekonomik takvim güçlü bir araçtır. Ancak her araç gibi bu da bağlamın anlaşılmasını, kullanım disiplininin sağlanmasını ve platform sınırlamalarının dikkate alınmasını gerektirir.

MetaTrader 5'te Uzman Danışman için haber API'sinin belirli bir uygulama ve kullanım yapısını ele aldık. Makale, bir araya getirildiğinde haberlere dayalı manuel alım-satımı tekrarlanabilir bir modüle dönüştüren bir dizi pratik unsur sunmaktadır:

  • Takvim API'sini anlama (Olay ve Değer, MqlCalendarEvent/MqlCalendarValue yapıları, sunucu zamanı);
  • Güvenli yükleme ve güncelleme yöntemleri (ilk yükleme için CalendarValueHistory, aşamalı güncellemeler için CalendarValueLast + change_id);
  • Tipik hataları işleme ve talep sınırlarına uyma;
  • Gürültüyü azaltmak ve 3-5 önemli olay bırakmak için para birimi, önem, olay kodu ve zaman pencerelerine göre çok seviyeli filtreleme;
  • Filtrelenmiş verilerin ikili bir kaynağa aktarılması ve veri kaynağının "gerçek zamanlı" ↔ "strateji sınayıcı" olarak otomatik olarak değiştirilmesi için bir mekanizma; bu sayede gerçek zamanlı olarak ve geriye dönük testlerde aynı davranış sağlanır.

Modül hazır olma kriterleri: belirtilen döneme ait olaylar başarıyla yüklenir; aşamalı güncellemeler change_id aracılığıyla sınırları aşmadan çalışır; sınayıcıda Uzman Danışman kaynağı okur ve aynı girdi verileriyle gerçek zamanlıda olduğu gibi aynı kararları üretir.

Önerilen sonraki adımlar: dışa aktarıcıyı uygulayın, .bin dosyasını #resource yönergesine ekleyin, OnInit/OnTimer güncelleme mantığını test edin ve kontrollü geriye dönük testler çalıştırın (örneğin, "30 dakika boyunca işlem yapma / 60 dakika sonra devam et" senaryosu).

Bu, hipotezlerden test edilebilir, ölçeklenebilir bir haber tabanlı alım-satım sistemine geçmenizi sağlayacaktır. Haber tabanlı Uzman Danışmanları geliştirmek sadece programlama değildir; makroekonomik teori ile piyasa pratiği arasında bir köprü kurmaktır. 

MetaTrader 5 Takvim API fonksiyonları ve bunların haberlere dayalı alım-satımda kullanımı hakkında derinlemesine bir çalışma için önerilen kaynaklar:


Makaleye ekli dosyaların listesi:

Dosya adı Açıklama
CalendarEventMonitor-EA.mq5  Takvim verisi güncelleme fonksiyonunu test etmek için test komut dosyası
ExportCalendarForTester-S.mq5 Belirtilen filtrelere sahip olayların ikili bir dosyaya aktarılmasını kontrol etmek için bir test komut dosyası
GetTodayEvents-S.mq5 Geçerli gün için olayların alınmasını kontrol etmek için bir test komut dosyası
ImportCalendarValidation-EA.mq5 Strateji sınayıcıda bir kaynaktan haber alımını kontrol etmek için bir test Uzman Danışmanı
ImportTesterLog.txt  ImportCalendarValidation-EA.mq5 Uzman Danışmanının sınayıcıda görsel modda tek testinin sonuçları
LoadLiveLog.txt  ImportCalendarValidation-EA.mq5 Uzman Danışmanında olayların Canlı yüklenme sonuçları

MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/22196

Son yorumlar | Tartışmaya git (8)
Stanislav Korotky
Stanislav Korotky | 1 May 2026 saat 13:32

Yazılan:

Если брокер учитывает переход на летнее/зимнее время — календарь автоматически подстраивается под этот переход

MQL5 API'sinde bir değişiklik olduysa beni düzeltin, ancak eskiden takvim, yaz saati uygulamasını (DST) dikkate alarak GEÇERLİ saat dilimine göre ayarlanıyordu; yani yaz aylarında işlev sorguları, örneğin UTC+3 saat diliminde zaman damgaları döndürürken, kışın aynı tarih aralığı için yapılan sorgu, sunucu yaz saati uygulamasına (DST) geçiş yapıp geri dönüyorsa, UTC+2 saat diliminde zaman damgalarına sahip olayları döndürürdü. Bu nedenle, yarım yıl veya daha uzun bir süreye ait takvim geçmişini doğru bir şekilde yüklemek için, brokerın saat dilimi geçiş geçmişini analiz etmek gerekir. Ayrıntılar kod tabanında yer almaktadır.

Ayrıca, takvim önbelleğini bir kaynak olarak bağlantı yoluyla kullanma yaklaşımı pek pratik görünmüyor ve özellikle makalede bahsedilen hatalara yol açıyor (örneğin, uzmanı yeniden derlemeyi unutmayın gibi). Takvim önbelleğini, tüm ajanlar tarafından erişilebilen Common klasöründeki bir dosya olarak giriş parametresinde neden belirtmiyoruz?

Maxim Kuznetsov
Maxim Kuznetsov | 3 May 2026 saat 17:44
MQL5 API’sinde bir değişiklik olduysa lütfen beni düzeltin, ancak eskiden takvim, yaz saati uygulamasını (DST) dikkate alarak GEÇERLİ saat dilimine göre ayarlanıyordu; yani yaz aylarında işlev çağrıları, örneğin UTC+3 saat diliminde zaman damgaları döndürürken, kışın aynı tarih aralığı için yapılan sorgu, sunucu yaz saati uygulamasına geçiş yapıyorsa ve geri dönüyorsa, UTC+2 saat diliminde zaman damgalarına sahip olayları alırdı. Bu nedenle, yarım yıl veya daha uzun bir süreye ait takvim geçmişini doğru bir şekilde yüklemek için, часового пояса ayrıntılar kod tabanında yer almaktadır.

Peki neden fiyatlar (genel olarak tüm zaman damgaları) örneğin UTC'de tutulmuyor?

Neden dealerların tikleri, aynı zaman sayımını farklı sayısal biçimlerde gösteriyor?

Felsefi bir soru :-) Gelenek böyle, ancak bu yanlış ve yok yere sorunlara yol açıyor.


Not: Bu nedenle "geçmiş haberleri" diğer kaynaklardan almak daha iyidir.

Ve asla "saat çevrim geçmişini" kendiniz "analiz etmemelisiniz" — bunların hepsi zaten mevcuttur; bu, işletim sisteminin bir işlevi ya da en azından sistem kütüphanelerinin bir parçasıdır. Google'da tzdata'yı arayın

Bisiklet ne kadar yapılabilir ki, üstelik de çarpık olanlardan

fxsaber
fxsaber | 3 May 2026 saat 19:30
Stanislav Korotky #:

Eskiden takvim, yaz saati uygulamasını (DST) dikkate alarak GEÇERLİ saat dilimine göre ayarlanırdı; yani yaz aylarında işlev çağrıları, örneğin UTC+3 saat diliminde zaman damgaları döndürürken, kışın aynı tarih aralığı için yapılan bir sorgu, sunucu yaz saati uygulamasına (DST) geçiş yapıp geri dönüyorsa, UTC+2 saat diliminde zaman damgalarına sahip olayları döndürür.

Ben de aynı kanıya vardım.
Stanislav Korotky
Stanislav Korotky | 4 May 2026 saat 16:09
Maxim Kuznetsov #:


Not: Bu nedenle "tarihsel haberler"i başka kaynaklardan almak daha iyidir.

Ve asla "saat çevirilerinin tarihçesini" kendiniz "analiz etmeye" kalkışmayın — bunların hepsi zaten mevcut, bu bir işletim sistemi işlevi ya da en azından sistem kütüphanelerinin bir parçasıdır. Google'da "tzdata" araması yapın.

Bisikletler ne kadar yapılabilir ki, üstelik de çarpık olanlar


Diğer kaynaklar sorunu çözmez, çünkü sorun MT5'te fiyatların saklanma biçiminde yatıyor.

Öncelikle kendi düz bisikletinizi gösterin, sonra da tavsiyelerde bulunun.

ilex044
ilex044 | 11 May 2026 saat 10:09
MetaQuotes:

Günümüz haber tabanlı yatırımcılarının başlıca sorunları, dağınık araç seti ve sistematik, algoritmik bir işlem akışının olmamasıdır. İşlem yaparken dikkatinizi internet tarayıcınız (haber sitelerini gezmek) ile işlem terminaliniz arasında bölmek oldukça zordur.

Bir haber tabanlı yatırımcının iş akışı şu şekildedir: Web tarayıcınızda haber takvimini hızlıca açıp olaylarda herhangi bir değişiklik olup olmadığını kontrol edin → Yaklaşan olayları hızlıca değerlendirin ve neyi nasıl işlem yapacağınıza karar verin → MetaTrader 5 terminaline gidin – ya bekleyen emirler verin ya da terminal başında oturup haberin yayınlanmasını bekleyerek karar verin. Bu senaryoda, yatırımcı genellikle bağlamı kaçırır; bu da haberlere tepki vermede gecikmelere yol açar ve sonuçta zarara neden olur.

Haber ticareti, net kurallar, tekrarlanabilir sonuçlar ve otomatik testler içeren bir mühendislik problemi gibi çalışsın ister misiniz? Bu makalenin amacı, MetaTrader 5 için bir haber katmanının çalışma mimarisini göstermektir: tek bir veri kaynağı, takvim API’sinin doğru kullanımı, filtreleme ve önbellekleme mekanizması, geçmiş olayların test cihazı için bir kaynağa aktarılması ve Canlı ile Test Cihazı arasında otomatik geçiş — böylece aynı kod hem gerçek zamanlı hem de geçmiş verilerde belirleyici sonuçlar üretir.

Evet, bu haber ticareti için gerçek bir sorun noktasıdır.

Tarayıcı, takvim ve terminal arasında geçiş yapmak konsantrasyonu bozar ve tepki süresini önemli ölçüde yavaşlatır. Haberleri doğrudan işlem ortamına çeken birleşik bir iş akışı veya sisteme sahip olmak, işlemlerin gerçekleştirilmesini kesinlikle daha hızlı ve tutarlı hale getirecektir.

Bunu, test ve otomasyon içeren yapılandırılmış, tekrarlanabilir bir “mühendislik tarzı” kurulum haline getirme fikri, duygusal veya gecikmeli kararları önlemek açısından aslında çok mantıklıdır.

Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler Yeni Raylara Adım Atın: MQL5'te Özel Göstergeler
Yeni terminalin ve dilin tüm yeni olanaklarını ve özelliklerini listelemeyeceğim. Bunlar sayısızdır ve bazı yenilikler ayrı bir makalede tartışılmaya değerdir. Ayrıca burada nesne yönelimli programlama ile yazılmış bir kod yoktur, geliştiriciler için ek avantajlar olarak bir bağlamda basitçe bahsedilemeyecek kadar ciddi bir konudur. Bu makalede, MQL4'e kıyasla göstergeleri, yapılarını, çizimlerini, türlerini ve programlama ayrıntılarını ele alacağız. Umarım bu makale hem yeni başlayanlar hem de deneyimli geliştiriciler için faydalı olacaktır, belki bazıları yeni bir şeyler bulacaktır.
Python + MetaTrader 5: Veriler, Özellikler ve Prototipler için Hızlı Araştırma Çerçevesi Python + MetaTrader 5: Veriler, Özellikler ve Prototipler için Hızlı Araştırma Çerçevesi
Makale, Python ve MetaTrader 5 entegrasyonunun araştırma esnekliğini ve alım-satım işlemleri gerçekleştirmeyi tek bir iş akışında nasıl birleştirdiğini göstermektedir. Python, veri analizi, özellik seçimi ve model eğitimi için kullanılırken, MetaTrader 5 test ve alım-satım otomasyonu için kullanılır. Bu yaklaşım, çözümlerin hayata geçirilmesini kolaylaştırır, tekrarlanabilirliği artırır ve alım-satım sistemlerinin geliştirilmesini daha hızlı ve daha yapılandırılmış hale getirir.
İşte Karışınızda Yeni MetaTrader 5 ve MQL5 İşte Karışınızda Yeni MetaTrader 5 ve MQL5
Bu MetaTrader 5 ile ilgili sadece kısa bir inceleme. Sistemin tüm yeni özelliklerini bu kadar kısa sürede açıklayamam, test süreci 09.09.2009’da başladı. Bu sembolik bir tarihtir ve şanslı sayı olacağına eminim. MetaTrader 5 terminalinin ve MQL5’in beta sürümünü beş gün önce aldım. Tüm özelliklerini deneme şansım olmadı ama şimdiden etkilendim.
Rutin İşler Olmadan Algoritmik Alım-Satım: SQLite ile MetaTrader 5'te Hızlı İşlem Analizi Rutin İşler Olmadan Algoritmik Alım-Satım: SQLite ile MetaTrader 5'te Hızlı İşlem Analizi
Makale, SQLite kullanılarak MQL5'te bir işlem günlüğü tutmak için temel bir çalışma seti sunmaktadır: işlemler, sinyaller ve olaylar için bir tablo yapısı, indeksler, hazırlanmış ifadeler ve işlemler ve standart analitik SQL sorguları. MetaTrader 5'te istatistik paneliyle entegrasyon ve MetaEditor aracılığıyla veritabanıyla çalışma gösterilmektedir. Bu yaklaşım, Uzman Danışman kodunu karmaşıklaştırmadan günlüğün otomatikleştirilmesine, hesaplamaların hızlandırılmasına ve analiz yapılmasına olanak tanır.