Cara Mendapatkan Acara Berimpak Tinggi dari ForexFactory.com untuk EA Anda

Mike 2016.05.21 02:37 48 0 0
Lampiran

Saya sedang membangunkan EA untuk minyak mentah dan Brent, jadi saya perlu mendapatkan tarikh dan waktu tepat laporan 'Inventori Minyak Mentah' dari ForexFactory.com. Laporan ini biasanya dikeluarkan pada hari Rabu jam 10:30 pagi waktu Timur, tetapi jika ada cuti, tarikh keluaran boleh berubah. Oleh kerana laporan ini sangat penting untuk EA saya, satu-satunya cara adalah memeriksa perkhidmatan dalam talian untuk mengesahkan tarikh keluaran.

Langkah pertama adalah menambah laman web yang akan digunakan untuk WebRequest di tab OPTIONS | EXPERT ADVISOR, yang adalah 'http://www.forexfactory.com/' (lihat gambar 1).

Seterusnya, kita perlu mendefinisikan struktur dalam kod anda untuk menyimpan acara-acara tersebut. Ini diletakkan di bahagian atas kod anda yang akan mengisytiharkan DailyEvents sebagai pemboleh ubah global dengan jumlah maksimum acara yang disimpan ditentukan oleh pemboleh ubah MaxDailyEvents.

// Definisikan struktur acara
struct EVENTS
{
   string   time;
   string   title;
   string   currency;
   bool     displayed;
};

#define     MaxDailyEvents       20    // Jika anda fikir akan ada lebih dari 20 acara berimpak tinggi, tingkatkan nombor ini.
EVENTS      DailyEvents[MaxDailyEvents];

Seterusnya, kita perlu mendapatkan kod HTML dari ForexFactory.com dan memparsenya. Jika anda tidak faham kod HTML, jangan risau, saya akan membantu anda :)

Kita perlu membina URL untuk WebRequest. Saya hanya ingin kalendar mereka untuk hari ini dan bukan yang default (seluruh minggu), jadi kita boleh melakukannya dengan menetapkan parameter 'day' permintaan kepada tarikh hari ini dan menghantar permintaan tersebut.

string   url="http://www.forexfactory.com/calendar.php?day="; url += MthName(Month()) + DoubleToStr(Day(), 0) + "." + DoubleToStr(Year(), 0);

Setelah itu, kita hantar permintaan, semak kod ralat (jika ada) dan tukar array watak yang dikembalikan kepada string. Ini memudahkan proses parsing kod HTML.

// Hantar permintaan web
   ResetLastError();
   res = WebRequest("GET", url, cookie, NULL, timeout, post, 0, result, headers);

   // Semak untuk ralat
   if(res == -1)
   {
      Print("Ralat dalam WebRequest. Kod ralat = ", GetLastError());
      MessageBox("Tambah alamat 'http://forexfactory.com/' dalam senarai URL yang dibenarkan di " +
        "tab 'Expert Advisors'", "Ralat", MB_ICONINFORMATION);
      return(false);
   }

Jika tidak ada ralat, kita akan menukarkan array watak result kepada string untuk parsing yang lebih baik.

// Tukar array watak kepada string
HTML = CharArrayToString(result);

Setelah kita mempunyai string, kita boleh menggunakan fungsi StringFind untuk mencari elemen HTML. Perkara pertama adalah memastikan HTML yang dikembalikan adalah untuk tarikh hari ini dan memotong apa-apa sebelum tag HTML ini. Fungsi GetHTMLElement digunakan untuk parsing kod HTML dan mengembalikan nilai antara tag HTML yang ditentukan. Lihat definisinya di bawah.

// Kalendar dimuat, pastikan ia untuk tarikh hari ini
int i = StringFind(HTML, "<span class=\"date\">");
if(i == -1) return(false);
HTML = StringSubstr(HTML, i);
string date = GetHTMLElement(HTML, "<span>", "</span>");
if(date != MthName(Month()) + " " + DoubleToStr(Day(), 0)) return(false);

Definisi fungsi GetHTMLElement.

//+------------------------------------------------------------------+
//| Ekstrak elemen HTML
//+------------------------------------------------------------------+
string   GetHTMLElement(string HTML, string ElementStart, string ElementEnd)
{
   string   data = NULL;
   
   // Cari posisi awal dan akhir untuk elemen
   int s = StringFind(HTML, ElementStart) + StringLen(ElementStart);
   int e = StringFind(StringSubstr(HTML, s), ElementEnd);
   
   // Kembalikan kandungan elemen
   if(e != 0) data = StringSubstr(HTML, s, e);
   return(data);
}

Baiklah, sekarang kita sudah memastikan kalendar yang dikembalikan adalah untuk tarikh hari ini, mari kita mula parsing setiap baris jadual dan ekstrak elemen yang kita perlukan. Yaitu waktu acara, mata wang acara, impak acara dan tajuk acara. Kita perlu melakukan ini untuk setiap baris jadual yang ada dalam kod HTML sehingga akhir kalendar atau maksimum MaxDailyEvents telah dicapai.

Setelah data untuk setiap baris diekstrak, kita tambahkannya ke dalam struktur DailyEvents.

// Sekarang dapatkan baris jadual untuk setiap acara
lasttime = NULL;
cnrt = 0;
date = DoubleToStr(Year(), 0) + "." + DoubleToStr(Month(), 0) + "." + DoubleToStr(Day(), 0) + " ";
do
{
   // Dapatkan maklumat acara
   time = GetHTMLElement(HTML, "<td class=\"calendar__cell calendar__time time\">", "</td>");
   if(StringFind(time, "<a name=\"upnext\"") == 0) time = GetHTMLElement(time, "class=\"upnext\">", "</span>");
   if(StringLen(time) != 0) lasttime = time;
   if(StringLen(time) == 0) time = lasttime; 
   time = date + time;
   currency = GetHTMLElement(HTML, "<td class=\"calendar__cell calendar__currency currency\">", "</td>");
   impact = GetHTMLElement(HTML, "<span title=\"", "\" class=\"");
   i = StringFind(impact, " Impact");
   if(i != -1) impact = StringSubstr(impact, 0, i);
   title = GetHTMLElement(HTML, "\"calendar__event-title\">", "</span>");
         
   // Adakah ini acara berimpak tinggi?
   if(StringFind(Symbol(), currency) != -1 && impact == "High")
   {
      // Tambah ke struktur acara harian
      DailyEvents[cntr].displayed = false;
      DailyEvents[cntr].time = time;
      DailyEvents[cntr].title = title;
      DailyEvents[cntr++].currency = currency;
   }
                  
   // Potong string HTML ke baris jadual seterusnya
   i = StringFind(HTML, "</tbody> </table> </td> </tr> ");
   if(i != -1) HTML = StringSubstr(HTML, i+30);
   if(StringFind(HTML, "</table> <div class=\"foot\">") == 0) i = -1;
} while(i != -1 || cntr == MaxDailyEvents);

Setelah kita memparsen semua baris jadual dan mencapai akhir kalendar, kita perlu memaparkan acara tersebut dalam carta. Jika acara tersebut di masa hadapan, saya ingin memaparkan garisan menegak dan jika di masa lalu, tiada garisan.

// Paparkan acara berimpak tinggi, jika ada
lasttime = NULL;
for(cntr = 0; cntr < MaxDailyEvents; cntr++)
{
   if(StringLen(DailyEvents[cntr].time) == 0) break;
      
   // Cipta penanda acara dalam carta jika pasaran terakhir bukan pada waktu yang sama
   if(lasttime != DailyEvents[cntr].time)
   {
      res = cntr;
      // Jika kita ada 'pm' dalam string, tambah 12 jam ke waktu
      if(StringFind(DailyEvents[cntr].time, "pm") != -1) DailyEvents[cntr].time = TimeToStr(StrToTime(DailyEvents[cntr].time) + 43200);
      if(ObjectCreate(0, Event + cntr, OBJ_EVENT, 0, StrToTime(DailyEvents[cntr].time), 0))
      {
         ObjectSetString(0, Event + cntr, OBJPROP_TEXT, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
         ObjectSetInteger(0, Event + cntr, OBJPROP_COLOR, Red);
         ObjectSetInteger(0, Event + cntr, OBJPROP_WIDTH, 2);
         ObjectSetInteger(0, Event + cntr, OBJPROP_BACK, true);
         ObjectSetInteger(0, Event + cntr, OBJPROP_SELECTABLE, false);
         ObjectSetInteger(0, Event + cntr, OBJPROP_SELECTED, false);
         ObjectSetInteger(0, Event + cntr, OBJPROP_HIDDEN, true);
         ObjectSetString(0, Event + cntr, OBJPROP_TOOLTIP, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
      }
      
      // Cipta garisan menegak jika acara di masa hadapan
      if(TimeCurrent() < TimeOffset(DailyEvents[cntr].time, 0))
      {
         if(ObjectCreate(0, VLine + cntr, OBJ_VLINE, 0, TimeOffset(DailyEvents[cntr].time, 0), 0))
       {
            ObjectSetInteger(0, VLine + cntr, OBJPROP_COLOR, Red);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_BACK, true);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_SELECTABLE, false);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_SELECTED, false);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_HIDDEN, true);
            ObjectSetString(0, VLine + cntr, OBJPROP_TOOLTIP, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
         }
      }
      else
         DailyEvents[cntr].displayed = true;
   }
   else
   {
      title = ObjectGetString(0, Event + res, OBJPROP_TOOLTIP);            
      title += "\n" + DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")";
      ObjectSetString(0, Event + res, OBJPROP_TOOLTIP, title);
      if(TimeCurrent() < TimeOffset(DailyEvents[cntr].time, 0)) ObjectSetString(0, VLine + res, OBJPROP_TOOLTIP, title);
   }
   lasttime = DailyEvents[cntr].time;
}

Jika acara yang ada di masa depan, saya ingin memberitahu pengguna tentang acara yang akan datang jika ia dalam 5 minit dari waktu semasa dan menghapus garisan menegak. Ini dilakukan dengan menambah beberapa kod dalam fungsi start() EA atau indikator anda.

//+------------------------------------------------------------------+
//| Fungsi permulaan Expert                                             |
//+------------------------------------------------------------------+
void start()
{
   string   event = NULL;
   
   // Adakah ada acara berimpak tinggi dalam 5 minit akan datang?
   for(int i = 0; i < MaxDailyEvents; i++)
   {
      if(StringLen(DailyEvents[i].time) == 0) break;
      if(TimeCurrent() >= StrToTime(DailyEvents[i].time) - 300 && TimeCurrent() < StrToTime(DailyEvents[i].time) && !DailyEvents[i].displayed)
      {
         // Acara dalam 5 minit...
         event += DailyEvents[i].title + " (" + DailyEvents[i].currency + "), ";
         DailyEvents[i].displayed = true;
         
         // Padam garisan menegak yang berkaitan dengan acara
         if(ObjectFind("VLine" + DoubleToStr(i, 0)) >= 0) ObjectDelete("VLine" + DoubleToStr(i, 0));
      }
   }
   
   // Ada apa-apa untuk dipaparkan?
   if(StringLen(event) != 0)
   {
      event += "dalam 5 minit.";
      Alert(event);
   }
}

Dan akhirnya, kita perlu mendapatkan acara harian. Ini dilakukan dengan menambah satu baris dalam fungsi OnInit() anda.

//+------------------------------------------------------------------+
//| Fungsi inisialisasi Expert                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Dapatkan acara hari ini
   GetHighImpactEvents();
   
   return(INIT_SUCCEEDED);
}

Senang dan ringkas. Anda tentu boleh mengubah kod untuk memaparkan semua acara untuk pasangan mata wang atau menambah parameter input pada indikator atau EA anda untuk menentukan impak yang ingin dipaparkan (tinggi, sederhana atau rendah) dan tentu saja menambah semakan untuk pertukaran tengah malam untuk mendapatkan senarai acara harian yang baru, tetapi saya akan biarkan anda bermain dengan itu :)


Cheers!

-Claude.

Senarai
Komen 0