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.
Komen 0