Создание пользовательской новостной ленты в MetaTrader 5

Francis Dube | 21 марта, 2018


Введение

В терминале MetaTrader 5 есть множество полезных функций, которые могут пригодиться пользователю, независимо от его стиля торговли, включая и возможность доступа к ленте оперативных новостей. Это дает трейдеру торговый контекст, значимость которого сложно переоценить, ведь он может оказать влияние на рынки. Единственное ограничение — объем предоставляемых новостей. Я считаю, что трейдерам будет полезно получить доступ к более гибкой новостной ленте, которая предоставляет возможность выбирать не только вид новостей, но и их источник.


Встроенная новостная лента

В зависимости от того, с каким брокером вы работаете, лента новостей может быть либо очень полезной, либо, напротив, совершенно бесполезной. Ведь вы должны полагаться только на то, как эта возможность терминала реализована вашим брокером. Некоторые брокеры предоставляют доступ к актуальным новостным фидам от авторитетных источников, другие же этого не делают. Можно изучить создание приложения, которое удовлетворит этот запрос.

Новостное API

Поскольку все новости, которые могут нам понадобиться, свободно представлены в Сети, всё, что нам нужно — это получить прямой доступ к новостям, которые нам нужны. Один из способов этого добиться — использовать инструменты RSS. Я уже писал статью о том, как написать код RSS-ридера для MetaTrader 5. Самый большой недостаток этого способа состоит в том, что вы должны вручную вводить каждый URL. Это в сочетании с дополнительными настройками, необходимыми для разрешения веб-запросов в терминале, может быть утомительным.

По моему мнению, лучшее решение — использовать веб-API. После долгих поисков я наткнулся на бесплатный API, который предоставляет доступ к многочисленным новостным каналам. Он называется NewsAPI и доступен через HTTP Get-запросы, которые возвращают метаданные формата json. Этот интерфейс позволяет получать заголовки новостей, которые были опубликованы в выбранном канале. В нем уже есть множество новостных изданий на выбор, причем заявляется, что будет добавлено еще больше. Также приветствуется, если пользователи предлагают новые источники. Похоже, что сервису не хватает языкового разнообразия: в нем по большей части преобладают источники новостей из США и Европы. Тем не менее, я думаю, это вполне приемлемый вариант. Еще один плюс — доступ ко всем источникам с одного веб-адреса.

Доступ к NewsAPI

Для работы с NewsAPI вы должны быть авторизованы, чтобы получить доступ ко всем его сервисам. Чтобы это сделать, зайдите на официальный веб-сайт и кликните на кнопку Get API Key. Зарегистрируйте ваш e-mail, и вы получите ключ API, который понадобится вам для доступа ко всем функциям.

Веб-сайт NewsAPI


Использование API

Доступ ко всем возможностям API предоставляется по запросу с использованием двух основных URL:

  1. https://newsapi.org/v1/sources? — запрос, сделанный в таком виде, вернет список всех новостных ресурсов, доступных в API.
    Этот список также включает информацию об идентификаторе для каждого ресурса, который должен быть указан при запросе последних новостных заголовков. URL ресурса можно оптимизировать также необязательными параметрами, которые определяют, какой тип списка источников будет возвращен.
  2. https://newsapi.org/v1/articles? — URL статьи возвращает заголовки новостей и фрагменты из определенных источников. URL должен содержать два обязательных параметра. Первый — идентификатор, который однозначно указывает на требуемый источник. Второй — ключ API для авторизации.

Параметры
Описание параметров
Возможные значения параметров
Источники/Статьи
 Пример
Категория (необязательно)
Категория новостей, по которой вы хотели бы получить источники Бизнес, развлечения, игры, общее, музыка, политика, наука и природа, спорт, технологии... Источники
https://newsapi.org/v1/sources?category=business


Язык (необязательно)
Желаемый язык новостных источников
en, de, fr Источники
https://newsapi.org/v1/sources?language=en
Страна (необязательно)
Страна, в которй располагается источник
au, de, gb, in, it, us Источники
https://newsapi.org/v1/sources?country=us
Источник (обязательно)
Идентификатор новостного источника
Выберите любой из списка, возвращенного по запросу с использованием URL источника
Статьи
https://newsapi.org/v1/articles?source=cnbc&apiKey=API_KEY
Ключ API (обязательно)
Токен авторизации пользователя

Статьи
см. пример выше
 Сортировать по Способ, по которому будут сортироваться новостные заголовки: популярность, по порядку появления на сайте, в хронологическом порядке.
 top, latest, popular  Статьи  https://newsapi.org/v1/articles?source=cnbc&sortBy=top&apiKey=API_KEY

Вышеприведенная таблица показывает основные параметры, которые можно использовать в настройках API наряду с двумя URL. Чтобы ознакомиться с их полным списком, обратитесь к документации, которая доступна на веб-сайте.

Скрипт для тестирования API

Теперь, когда у нас есть общее представление о том, как использовать API, настало время опробовать его. Скрипт, который мы создадим, служит для ознакомления с ответами, которые дает API в ответ на запрос. Для этого потребуется, чтобы скрипт мог делать веб-реквест и сохранять ответы в текстовый файл. При использовании скрипта мы должны иметь возможность протестировать любой запрос API и пронаблюдать ответ.

Код скрипта NewsAPI_test:

//+------------------------------------------------------------------+ 
//|                                                 NewsAPI_test.mq5 |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#property script_show_inputs


#define BASE_URL "https://newsapi.org/v1/"
#define SRCE "sources?"
#define ATCLE "articles?source="
#define API_KEY "&apiKey=484c84eb9765418fb58ea936908a47ac"
//+------------------------------------------------------------------+ 
//|                                                                  |
//+------------------------------------------------------------------+ 
enum mode 
  {
   sources,
   articles
  };

input string sFilename="sorce.txt";
input mode Mode=sources;
input string parameters="";
int timeout=5000;
//+------------------------------------------------------------------+ 
//| Script program start function                                    |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//--- 
   TestWebRequest();
  }
//+------------------------------------------------------------------+ 
//| TestWebRequest                                                   |
//+------------------------------------------------------------------+ 
void TestWebRequest(void)
  {
   string cookie=NULL,headers;
   char post[],result[];
   int res;
   string _url;
//--- 
   switch(Mode)
     {
      case sources:
         _url=BASE_URL+SRCE+parameters;
         break;
      case articles:
         _url=BASE_URL+ATCLE+parameters+API_KEY;
         break;
     }
//--- 
   ResetLastError();
   res=WebRequest("GET",_url,cookie,NULL,timeout,post,0,result,headers);

   if(res==-1)
     {
      Alert("Could not download file");
      return;
     }
   else Print("Download success");

   string pStream=CharArrayToString(result,0,-1,CP_UTF8);

   int hFile=FileOpen(sFilename,FILE_BIN|FILE_WRITE);

   if(hFile==INVALID_HANDLE)
     {
      Print("Invalid file handle");
      return;
     }

   FileWriteString(hFile,pStream);
   FileClose(hFile);

   return;
  }

Во-первых, существуют директивы define, которые представляют различные компоненты URL, составляющие запрос API. В качестве первой пользовательской переменной у нас есть sFilename. В нее введем имя файла, в который будут помещены ответы API. Параметр Mode — перечисление, позволяющее переключаться между двумя основными URL-адресами API.

Пользовательский параметр parameters служит для ввода дополнительных параметров URL (обязательных или необязательных) для включения в запрос API. В нашем скрипте будет только одна функция, которая создает вызов URL API, в зависимости от выбранных настроек параметров. Если функция успешно выполнила запрос, метаданные будут сохранены в файл.

Сейчас мы можем провести несколько тестов и изучить ответы. Сначала протестируем запуск скрипта с использованием параметров по умолчанию. Затем откроем файл: ответ API может быть прочитан. Важно отметить структуру объекта json: это пригодится, когда нам нужно будет извлечь конкретную информацию из массива данных.

{
   "status":"ok","sources":
   [
     {"id":"abc-news-au","name":"ABC News (AU)","description":"Australia's most trusted source of local, national and world news. Comprehensive, independent, in-depth analysis, the latest business, sport, weather and more.","url":"http://www.abc.net.au/news","category":"general","language":"en","country":"au","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"al-jazeera-english","name":"Al Jazeera English","description":"News, analysis from the Middle East and worldwide, multimedia and interactives, opinions, documentaries, podcasts, long reads and broadcast schedule.","url":"http://www.aljazeera.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"ars-technica","name":"Ars Technica","description":"The PC enthusiast's resource. Power users and the tools they love, without computing religion.","url":"http://arstechnica.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"associated-press","name":"Associated Press","description":"The AP delivers in-depth coverage on the international, politics, lifestyle, business, and entertainment news.","url":"https://apnews.com/","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"bbc-news","name":"BBC News","description":"Use BBC News for up-to-the-minute news, breaking news, video, audio and feature stories. BBC News provides trusted World and UK news as well as local and regional perspectives. Also entertainment, business, science, technology and health news.","url":"http://www.bbc.co.uk/news","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"bbc-sport","name":"BBC Sport","description":"The home of BBC Sport online. Includes live sports coverage, breaking news, results, video, audio and analysis on Football, F1, Cricket, Rugby Union, Rugby League, Golf, Tennis and all the main world sports, plus major events such as the Olympic Games.","url":"http://www.bbc.co.uk/sport","category":"sport","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"bild","name":"Bild","description":"Die Seite 1 für aktuelle Nachrichten und Themen, Bilder und Videos aus den Bereichen News, Wirtschaft, Politik, Show, Sport, und Promis.","url":"http://www.bild.de","category":"general","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"bloomberg","name":"Bloomberg","description":"Bloomberg delivers business and markets news, data, analysis, and video to the world, featuring stories from Businessweek and Bloomberg News.","url":"http://www.bloomberg.com","category":"business","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"breitbart-news","name":"Breitbart News","description":"Syndicated news and opinion website providing continuously updated headlines to top news and analysis sources.","url":"http://www.breitbart.com","category":"politics","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"business-insider","name":"Business Insider","description":"Business Insider is a fast-growing business site with deep financial, media, tech, and other industry verticals. Launched in 2007, the site is now the largest business news site on the web.","url":"http://www.businessinsider.com","category":"business","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"business-insider-uk","name":"Business Insider (UK)","description":"Business Insider is a fast-growing business site with deep financial, media, tech, and other industry verticals. Launched in 2007, the site is now the largest business news site on the web.","url":"http://uk.businessinsider.com","category":"business","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"buzzfeed","name":"Buzzfeed","description":"BuzzFeed is a cross-platform, global network for news and entertainment that generates seven billion views each month.","url":"https://www.buzzfeed.com","category":"entertainment","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"cnbc","name":"CNBC","description":"Get latest business news on stock markets, financial & earnings on CNBC. View world markets streaming charts & video; check stock tickers and quotes.","url":"http://www.cnbc.com","category":"business","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"cnn","name":"CNN","description":"View the latest news and breaking news today for U.S., world, weather, entertainment, politics and health at CNN","url":"http://us.cnn.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"daily-mail","name":"Daily Mail","description":"All the latest news, sport, showbiz, science and health stories from around the world from the Daily Mail and Mail on Sunday newspapers.","url":"http://www.dailymail.co.uk/home/index.html","category":"entertainment","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"der-tagesspiegel","name":"Der Tagesspiegel","description":"Nachrichten, News und neueste Meldungen aus dem Inland und dem Ausland - aktuell präsentiert von tagesspiegel.de.","url":"http://www.tagesspiegel.de","category":"general","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["latest"]},
     {"id":"die-zeit","name":"Die Zeit","description":"Aktuelle Nachrichten, Kommentare, Analysen und Hintergrundberichte aus Politik, Wirtschaft, Gesellschaft, Wissen, Kultur und Sport lesen Sie auf ZEIT ONLINE.","url":"http://www.zeit.de/index","category":"business","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["latest"]},
     {"id":"engadget","name":"Engadget","description":"Engadget is a web magazine with obsessive daily coverage of everything new in gadgets and consumer electronics.","url":"https://www.engadget.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"entertainment-weekly","name":"Entertainment Weekly","description":"Online version of the print magazine includes entertainment news, interviews, reviews of music, film, TV and books, and a special area for magazine subscribers.","url":"http://www.ew.com","category":"entertainment","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"espn","name":"ESPN","description":"ESPN has up-to-the-minute sports news coverage, scores, highlights and commentary for NFL, MLB, NBA, College Football, NCAA Basketball and more.","url":"http://espn.go.com","category":"sport","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"espn-cric-info","name":"ESPN Cric Info","description":"ESPN Cricinfo provides the most comprehensive cricket coverage available including live ball-by-ball commentary, news, unparalleled statistics, quality editorial comment and analysis.","url":"http://www.espncricinfo.com/","category":"sport","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"financial-times","name":"Financial Times","description":"The latest UK and international business, finance, economic and political news, comment and analysis from the Financial Times on FT.com.","url":"http://www.ft.com/home/uk","category":"business","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"focus","name":"Focus","description":"Minutenaktuelle Nachrichten und Service-Informationen von Deutschlands modernem Nachrichtenmagazin.","url":"http://www.focus.de","category":"general","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"football-italia","name":"Football Italia","description":"Italian football news, analysis, fixtures and results for the latest from Serie A, Serie B and the Azzurri.","url":"http://www.football-italia.net","category":"sport","language":"en","country":"it","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"fortune","name":"Fortune","description":"Fortune 500 Daily and Breaking Business News","url":"http://fortune.com","category":"business","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"four-four-two","name":"FourFourTwo","description":"The latest football news, in-depth features, tactical and statistical analysis from FourFourTwo, the UK's favourite football monthly.","url":"http://www.fourfourtwo.com/news","category":"sport","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"fox-sports","name":"Fox Sports","description":"Find live scores, player and team news, videos, rumors, stats, standings, schedules and fantasy games on FOX Sports.","url":"http://www.foxsports.com","category":"sport","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"google-news","name":"Google News","description":"Comprehensive, up-to-date news coverage, aggregated from sources all over the world by Google News.","url":"https://news.google.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"gruenderszene","name":"Gruenderszene","description":"Online-Magazin für Startups und die digitale Wirtschaft. News und Hintergründe zu Investment, VC und Gründungen.","url":"http://www.gruenderszene.de","category":"technology","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"hacker-news","name":"Hacker News","description":"Hacker News is a social news website focusing on computer science and entrepreneurship. It is run by Paul Graham's investment fund and startup incubator, Y Combinator. In general, content that can be submitted is defined as \"anything that gratifies one's intellectual curiosity\".","url":"https://news.ycombinator.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"handelsblatt","name":"Handelsblatt","description":"Auf Handelsblatt lesen sie Nachrichten über Unternehmen, Finanzen, Politik und Technik. Verwalten Sie Ihre Finanzanlagen mit Hilfe unserer Börsenkurse.","url":"http://www.handelsblatt.com","category":"business","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["latest"]},
     {"id":"ign","name":"IGN","description":"IGN is your site for Xbox One, PS4, PC, Wii-U, Xbox 360, PS3, Wii, 3DS, PS Vita and iPhone games with expert reviews, news, previews, trailers, cheat codes, wiki guides and walkthroughs.","url":"http://www.ign.com","category":"gaming","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"independent","name":"Independent","description":"National morning quality (tabloid) includes free online access to news and supplements. Insight by Robert Fisk and various other columnists.","url":"http://www.independent.co.uk","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"mashable","name":"Mashable","description":"Mashable is a global, multi-platform media and entertainment company.","url":"http://mashable.com","category":"entertainment","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"metro","name":"Metro","description":"News, Sport, Showbiz, Celebrities from Metro - a free British newspaper.","url":"http://metro.co.uk","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"mirror","name":"Mirror","description":"All the latest news, sport and celebrity gossip at Mirror.co.uk. Get all the big headlines, pictures, analysis, opinion and video on the stories that matter to you.","url":"http://www.mirror.co.uk/","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"mtv-news","name":"MTV News","description":"The ultimate news source for music, celebrity, entertainment, movies, and current events on the web. It's pop culture on steroids.","url":"http://www.mtv.com/news","category":"music","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"mtv-news-uk","name":"MTV News (UK)","description":"All the latest celebrity news, gossip, exclusive interviews and pictures from the world of music and entertainment.","url":"http://www.mtv.co.uk/news","category":"music","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"national-geographic","name":"National Geographic","description":"Reporting our world daily: original nature and science news from National Geographic.","url":"http://news.nationalgeographic.com","category":"science-and-nature","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"new-scientist","name":"New Scientist","description":"Breaking science and technology news from around the world. Exclusive stories and expert analysis on space, technology, health, physics, life and Earth.","url":"https://www.newscientist.com/section/news","category":"science-and-nature","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"newsweek","name":"Newsweek","description":"Newsweek provides in-depth analysis, news and opinion about international issues, technology, business, culture and politics.","url":"http://www.newsweek.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"new-york-magazine","name":"New York Magazine","description":"NYMAG and New York magazine cover the new, the undiscovered, the next in politics, culture, food, fashion, and behavior nationally, through a New York lens.","url":"http://nymag.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"nfl-news","name":"NFL News","description":"The official source for NFL news, schedules, stats, scores and more.","url":"http://www.nfl.com/news","category":"sport","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"polygon","name":"Polygon","description":"Polygon is a gaming website in partnership with Vox Media. Our culture focused site covers games, their creators, the fans, trending stories and entertainment news.","url":"http://www.polygon.com","category":"gaming","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"recode","name":"Recode","description":"Get the latest independent tech news, reviews and analysis from Recode with the most informed and respected journalists in technology and media.","url":"http://www.recode.net","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"reddit-r-all","name":"Reddit /r/all","description":"Reddit is an entertainment, social news networking service, and news website. Reddit's registered community members can submit content, such as text posts or direct links.","url":"https://www.reddit.com/r/all","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"reuters","name":"Reuters","description":"Reuters.com brings you the latest news from around the world, covering breaking news in business, politics, entertainment, technology, video and pictures.","url":"http://www.reuters.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"spiegel-online","name":"Spiegel Online","description":"Deutschlands führende Nachrichtenseite. Alles Wichtige aus Politik, Wirtschaft, Sport, Kultur, Wissenschaft, Technik und mehr.","url":"http://www.spiegel.de","category":"general","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"t3n","name":"T3n","description":"Das Online-Magazin bietet Artikel zu den Themen E-Business, Social Media, Startups und Webdesign.","url":"http://t3n.de","category":"technology","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"talksport","name":"TalkSport","description":"Tune in to the world's biggest sports radio station - Live Premier League football coverage, breaking sports news, transfer rumours & exclusive interviews.","url":"http://talksport.com","category":"sport","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"techcrunch","name":"TechCrunch","description":"TechCrunch is a leading technology media property, dedicated to obsessively profiling startups, reviewing new Internet products, and breaking tech news.","url":"https://techcrunch.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"techradar","name":"TechRadar","description":"The latest technology news and reviews, covering computing, home entertainment systems, gadgets and more.","url":"http://www.techradar.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-economist","name":"The Economist","description":"The Economist offers authoritative insight and opinion on international news, politics, business, finance, science, technology and the connections between them.","url":"http://www.economist.com","category":"business","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-guardian-au","name":"The Guardian (AU)","description":"Latest news, sport, comment, analysis and reviews from Guardian Australia","url":"https://www.theguardian.com/au","category":"general","language":"en","country":"au","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"the-guardian-uk","name":"The Guardian (UK)","description":"Latest news, sport, business, comment, analysis and reviews from the Guardian, the world's leading liberal voice.","url":"https://www.theguardian.com/uk","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-hindu","name":"The Hindu","description":"The Hindu. latest news, analysis, comment, in-depth coverage of politics, business, sport, environment, cinema and arts from India's national newspaper.","url":"http://www.thehindu.com","category":"general","language":"en","country":"in","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-huffington-post","name":"The Huffington Post","description":"The Huffington Post is a politically liberal American online news aggregator and blog that has both localized and international editions founded by Arianna Huffington, Kenneth Lerer, Andrew Breitbart, and Jonah Peretti, featuring columnists.","url":"http://www.huffingtonpost.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"the-lad-bible","name":"The Lad Bible","description":"The LAD Bible is one of the largest community for guys aged 16-30 in the world. Send us your funniest pictures and videos!","url":"http://www.theladbible.com","category":"entertainment","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-new-york-times","name":"The New York Times","description":"The New York Times: Find breaking news, multimedia, reviews & opinion on Washington, business, sports, movies, travel, books, jobs, education, real estate, cars & more at nytimes.com.","url":"http://www.nytimes.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"the-next-web","name":"The Next Web","description":"The Next Web is one of the world’s largest online publications that delivers an international perspective on the latest news about Internet technology, business and culture.","url":"http://thenextweb.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["latest"]},
     {"id":"the-sport-bible","name":"The Sport Bible","description":"TheSPORTbible is one of the largest communities for sports fans across the world. Send us your sporting pictures and videos!","url":"http://www.thesportbible.com","category":"sport","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-telegraph","name":"The Telegraph","description":"Latest news, business, sport, comment, lifestyle and culture from the Daily Telegraph and Sunday Telegraph newspapers and video from Telegraph TV.","url":"http://www.telegraph.co.uk","category":"general","language":"en","country":"gb","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-times-of-india","name":"The Times of India","description":"Times of India brings the Latest News and Top Breaking headlines on Politics and Current Affairs in India and around the World, Sports, Business, Bollywood News and Entertainment, Science, Technology, Health and Fitness news, Cricket and opinions from leading columnists.","url":"http://timesofindia.indiatimes.com","category":"general","language":"en","country":"in","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-verge","name":"The Verge","description":"The Verge covers the intersection of technology, science, art, and culture.","url":"http://www.theverge.com","category":"technology","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"the-wall-street-journal","name":"The Wall Street Journal","description":"WSJ online coverage of breaking news and current headlines from the US and around the world. Top stories, photos, videos, detailed analysis and in-depth reporting.","url":"http://www.wsj.com","category":"business","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"the-washington-post","name":"The Washington Post","description":"Breaking news and analysis on politics, business, world national news, entertainment more. In-depth DC, Virginia, Maryland news coverage including traffic, weather, crime, education, restaurant reviews and more.","url":"https://www.washingtonpost.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top"]},
     {"id":"time","name":"Time","description":"Breaking news and analysis from TIME.com. Politics, world news, photos, video, tech reviews, health, science and entertainment news.","url":"http://time.com","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"usa-today","name":"USA Today","description":"Get the latest national, international, and political news at USATODAY.com.","url":"http://www.usatoday.com/news","category":"general","language":"en","country":"us","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"wired-de","name":"Wired.de","description":"Wired reports on how emerging technologies affect culture, the economy and politics.","url":"https://www.wired.de","category":"technology","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["top","latest"]},
     {"id":"wirtschafts-woche","name":"Wirtschafts Woche","description":"Das Online-Portal des führenden Wirtschaftsmagazins in Deutschland. Das Entscheidende zu Unternehmen, Finanzen, Erfolg und Technik.","url":"http://www.wiwo.de","category":"business","language":"de","country":"de","urlsToLogos":{"small":"","medium":"","large":""},"sortBysAvailable":["latest"]}
   ]
  }

При открытии файла мы видим, что ответ содержит массив объектов json, представляющий все новостные каналы, из которых можно запрашивать заголовки новостей.

Следующий тест — отправка API-запроса из выбранного источника. Снова откроем файл и рассмотрим ответ.

{
   "status":"ok","source":"cnbc","sortBy":"top","articles":
   [
     {"author":"Reuters","title":"'Singles Day' China shopping festival smashes record at the halfway mark","description":"Alibaba said its Singles Day sales surged past last year's total just after midday Saturday, hitting a record $18 billion.","url":"https://www.cnbc.com/2017/11/11/singles-day-china-shopping-festival-smashes-record-at-the-halfway-mark.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/11/10/104835461-RTS1JDT7-singles-day.1910x1000.jpg","publishedAt":"2017-11-11T10:50:08Z"},
     {"author":"The Associated Press","title":"Trump: Putin again denies meddling in 2016 election","description":"President Donald Trump said Saturday that Russia's Vladimir Putin again denied interfering in the 2016 U.S. elections.","url":"https://www.cnbc.com/2017/11/11/trump-putin-again-denies-meddling-in-2016-election.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/08/18/104661874-RTS19PQA-vladimir-putin.1910x1000.jpg","publishedAt":"2017-11-11T12:07:32Z"},
     {"author":"Jeff Cox","title":"GE limps into investor day with shareholders demanding answers on dividend and turnaround plan","description":"As General Electric limps into its investor day presentation Monday, it has gone from a paradigm of success to a morass of excess.","url":"https://www.cnbc.com/2017/11/10/ge-faces-investor-day-with-questions-about-its-past-and-future.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/10/20/104786151-JohnFlannery2.1910x1000.jpg","publishedAt":"2017-11-10T16:16:05Z"},
     {"author":"Sarah Whitten","title":"Here's where military service members can get freebies on Veterans Day","description":"Businesses across the country are saying \"thank you\" to Veterans on Friday by offering freebies to active and retired military members.","url":"https://www.cnbc.com/2016/11/10/heres-where-military-service-members-can-get-freebies-on-veterans-day.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2016/11/10/104097817-GettyImages-496717392.1910x1000.jpg","publishedAt":"2016-11-10T18:30:41Z"},
     {"author":"Morgan Brennan","title":"With an eye toward the North Korean threat, a 'missile renaissance' blooms in the US","description":"Raytheon is cranking out about 20 Standard Missile variants per month, as part of the effort to help repel a possible attack from North Korea.","url":"https://www.cnbc.com/2017/11/11/north-korea-threat-leads-to-a-us-missile-renaissance.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/08/03/104631031-RTS1A3SU.1910x1000.jpg","publishedAt":"2017-11-11T14:00:56Z"},
     {"author":"Larry Kudlow","title":"Larry Kudlow: A pro-growth GOP tax cut is on the way — this year","description":"One way or another, Congress will come up with a significant pro-growth bill, writes Larry Kudlow.","url":"https://www.cnbc.com/2017/11/11/larry-kudlow-pro-growth-gop-tax-cut-is-on-the-way--this-year.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/11/02/104816986-GettyImages-869498942.1910x1000.jpg","publishedAt":"2017-11-11T14:22:58Z"},
     {"author":"Reuters","title":"Trans-Pacific trade deal advances without United States","description":"Last-minute resistance from Canada had raised new doubts about its survival.","url":"https://www.cnbc.com/2017/11/11/trans-pacific-trade-deal-advances-without-united-states.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2016/11/22/104123278-GettyImages-624177112.1910x1000.jpg","publishedAt":"2017-11-11T10:23:03Z"},
     {"author":"Jacob Pramuk","title":"McConnell says he 'misspoke' about middle-class tax hikes","description":"Mitch McConnell told The New York Times that \"you can't guarantee that no one sees a tax increase.\"","url":"https://www.cnbc.com/2017/11/10/mitch-mcconnell-says-he-misspoke-about-republican-tax-plan.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/09/22/104726784-RTX3FY40-mcconnell.1910x1000.jpg","publishedAt":"2017-11-10T23:04:47Z"},
     {"author":"Erin Barry","title":"Start-up Dia&Co is catering the 70 percent of US women the fashion industry ignores","description":"There are more than 100 million plus-size women in the U.S., but finding fashionable clothes in their size can be a challenge.","url":"https://www.cnbc.com/2017/11/10/diaco-caters-to-the-70-percent-of-us-women-fashion-ignores.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/02/22/104298276-Lydia-Gilbert--Nadia-Boujarwah-2_credit-DiaCo_r.1910x1000.jpg","publishedAt":"2017-11-11T14:01:01Z"},
     {"author":"Elizabeth Gurdus","title":"Cramer shares a little-known investing concept critical to buying stocks","description":"Jim Cramer explained why the idea of suitability is crucial when it comes to individual investing.","url":"https://www.cnbc.com/2017/11/10/cramer-shares-an-investing-concept-critical-to-buying-stocks.html","urlToImage":"https://fm.cnbc.com/applications/cnbc.com/resources/img/editorial/2017/11/10/104835669-GettyImages-825493934.1910x1000.jpg","publishedAt":"2017-11-10T23:10:58Z"}
   ]
  }

В этом примере ответ API содержит массив json-объектов, в котором каждый объект — это заголовок новости.

Наконец, давайте выясним, какой ответ мы получим, отправив некорректный API-запрос. Вот такие результаты возвращаются при использовании некорректного ключа API:

{
  "status":"error",
  "code":"apiKeyInvalid",
  "message":"Your API key is invalid or incorrect. Check your key, or go to https://newsapi.org to create a free API
  key."  
}

Еще один пример ошибки ответа: здесь запрос был сделан с некорректным идентификатором источника.

{
 "status":"error",
 "code":"sourceDoesntExist",
 "message":"The news source you've entered doesn't exist. Check your spelling, or see /v1/sources for a list of valid sources."
}

После экспериментов с API мы можем взглянуть на то, как можно быстро извлечь полученные данные с помощью парсера json.

Парсим метаданные JSON

В библиотеке кодов MQL5.com доступны две библиотеки json. Первая, с которой мы попробуем поработать, — json.mqh. Компиляция тестового скрипта, использующего эту библиотеку, показала ряд ошибок и предупреждений. Эти ошибки заложены в самих файлах библиотеки. На странице, где опубликована библиотека, автор утверждает, что код активно поддерживается на GitHub. Однако автор не обновлял файл кода, доступный в библиотеке на mql5.com.

Исправить ошибки компиляции оказалось достаточно просто. Нужно было только подключить хэш-файл непосредственно к файлу json.mqh. Все предупреждения были вызваны неявным приведением типа. Исправленный файл библиотеки представлен ниже:

// $Id: json.mqh 102 2014-02-24 03:39:28Z ydrol $
#include "hash.mqh"
#ifndef YDROL_JSON_MQH
#define YDROL_JSON_MQH

// (C)2014 Andrew Lord forex@NICKNAME@lordy.org.uk
// Parse a JSON String - Adapted for mql4++ from my gawk implementation
// ( https://code.google.com/p/oversight/source/browse/trunk/bin/catalog/json.awk )

/*
   TODO the constants true|false|null could be represented as fixed objects.
      To do this the deleting of _hash and _array must skip these objects.

   TODO test null

   TODO Parse Unicode Escape
*/

/*
   Пример json_demo представлен ниже.

 Он требует, чтобы на компьютере был установлен файл hash.mqh ( http://codebase.mql4.com/9238 , http://lordy.co.nf/hash )

 */

enum ENUM_JSON_TYPE { JSON_NULL,JSON_OBJECT,JSON_ARRAY,JSON_NUMBER,JSON_STRING,JSON_BOOL };

class JSONString;
// Общий класс для всех типов JSON (Number, String, Bool, Array, Object )
class JSONValue : public HashValue 
  {
private:
   ENUM_JSON_TYPE    _type;

public:
                     JSONValue() {}
                    ~JSONValue() {}
   ENUM_JSON_TYPE getType() { return _type; }
   void setType(ENUM_JSON_TYPE t) { _type=t; }

   // Типы методов
   bool isString() { return _type==JSON_STRING; }
   bool isNull() { return _type==JSON_NULL; }
   bool isObject() { return _type==JSON_OBJECT; }
   bool isArray() { return _type==JSON_ARRAY; }
   bool isNumber() { return _type==JSON_NUMBER; }
   bool isBool() { return _type==JSON_BOOL; }

   // Переопределение в дочерних классах
   virtual string toString() 
     {
      return "";
     }

   // Некоторые вспомогательные методы для приведения к подтипам
   string getString()
     {
      return ((JSONString *)GetPointer(this)).getString();
     }
   double getDouble()
     {
      return ((JSONNumber *)GetPointer(this)).getDouble();
     }
   long getLong()
     {
      return ((JSONNumber *)GetPointer(this)).getLong();
     }
   int getInt()
     {
      return ((JSONNumber *)GetPointer(this)).getInt();
     }
   bool getBool()
     {
      return ((JSONBool *)GetPointer(this)).getBool();
     }

   // Статические методы для Array and Object, возвращающие дочерние результаты.
   // Они позволяют программе получить значение, не приостанавливая работу программы  
   // (иногда приостановка программы желательна — пусть лучше советник перестанет работать, чем продолжит работу с некорректными данными)
   static bool getString(JSONValue *val,string &out)
     {
      if(val!=NULL && val.isString()) 
        {
         out = val.getString();
         return true;
        }
      return false;
     }
   static bool getBool(JSONValue *val,bool &out)
     {
      if(val!=NULL && val.isBool()) 
        {
         out = val.getBool();
         return true;
        }
      return false;
     }
   static bool getDouble(JSONValue *val,double &out)
     {
      if(val!=NULL && val.isNumber()) 
        {
         out = val.getDouble();
         return true;
        }
      return false;
     }
   static bool getLong(JSONValue *val,long &out)
     {
      if(val!=NULL && val.isNumber()) 
        {
         out = val.getLong();
         return true;
        }
      return false;
     }
   static bool getInt(JSONValue *val,int &out)
     {
      if(val!=NULL && val.isNumber()) 
        {
         out = val.getInt();
         return true;
        }
      return false;
     }
  };
// -----------------------------------------
....

Код тестового скрипта newstest_json подробно разобран ниже. Скрипт просто читает файл, содержащий метаданные формата json, которые были сохранены в результате запроса API (его мы сделали в предыдущем тесте). После запуска скрипта список всех доступных источников новостей, содержащийся в файле с данными, будет выведен в терминал.

//+------------------------------------------------------------------+ 
//|                                                newstest_json.mq5 |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <json.mqh>
//+------------------------------------------------------------------+ 
//| Script program start function                                    |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//--- 
   Test();
  }
//+------------------------------------------------------------------+ 
//| Test                                                             |
//+------------------------------------------------------------------+ 
bool Test()
  {

   string pStream;
   string sources_filename="sources.txt";

   int hFile,iStringSize;

// read file contents 
   hFile=::FileOpen(sources_filename,FILE_TXT|FILE_READ|FILE_UNICODE);
   if(hFile==INVALID_HANDLE)
     {
      ::Print("error opening file "+sources_filename);
      return(false);
     }

   while(!::FileIsEnding(hFile))
     {
      iStringSize = ::FileReadInteger(hFile, INT_VALUE);
      pStream    += ::FileReadString(hFile, iStringSize);
     }

   ::FileClose(hFile);

   Print("success opening and reading file");

   JSONParser *parser=new JSONParser();

   JSONValue *jv=parser.parse(pStream);

   if(jv==NULL) 
     {
      Print("error:"+(string)parser.getErrorCode()+parser.getErrorMessage());
        } else {

      if(jv.isObject())
        {
         JSONObject *jo = jv;
         JSONArray  *jd =  jo.getArray("sources");

         for(int i=0;i<jd.size();i++)
           {
            Print(jd.getObject(i).getString("id"));
           }
        }
      delete jv;
     }
   delete parser;

   return(true);
  }

Когда используется корректный файл, библиотека работает правильно.

library test result


Теперь взглянем на вторую библиотеку для работы с json: JAson.mqh. Тестовый запуск библиотеки не показал ни одной ошибки, она сразу же отлично работает. Для тестирования этой библиотеки был использован скрипт newstest_JAson.

//+------------------------------------------------------------------+ 
//|                                               newstest_JAson.mq5 |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh>
//+------------------------------------------------------------------+ 
//| Script program start function                                    |
//+------------------------------------------------------------------+ 
void OnStart()
  {
//--- 
   Test();
  }
//+------------------------------------------------------------------+ 
//| Test                                                             |
//+------------------------------------------------------------------+ 
bool Test()
  {

   string pStream;
   string sources_filename="sources.txt";

   int hFile,iStringSize;

// чтение содержимого файла 
   hFile=::FileOpen(sources_filename,FILE_TXT|FILE_READ|FILE_UNICODE);
   if(hFile==INVALID_HANDLE)
     {
      ::Print("error opening file "+sources_filename);
      return(false);
     }

   while(!::FileIsEnding(hFile))
     {
      iStringSize = ::FileReadInteger(hFile, INT_VALUE);
      pStream    += ::FileReadString(hFile, iStringSize);
     }

   ::FileClose(hFile);

   Print("success opening and reading file");

   CJAVal  srce;

   if(!srce.Deserialize(pStream))
     {
      ::Print("Json deserialize error");
      return(false);
     }

   CJAVal *json_array=new CJAVal(srce["sources"]);

   for(int i=0;i<ArraySize(json_array.m_e);i++)
     {
      Print(json_array[i]["id"].ToStr());
     }

   delete json_array;

   return(true);
  }

Вот результаты тестирования:

Результаты тестирования библиотеки JAson.mqh

Если сравнивать эту библиотеку с рассмотренной выше (json.mqh), то можно заметить, что обе поддерживают все типы данных json. Основное отличие в том, что json.mqh реализует каждый тип данных json как класс и определяет несколько классов. В то же время в JAson.mqh типы данных json определяются общедоступным свойством класса, поэтому библиотека определяет только один класс.

Двигаемся дальше. Теперь мы можем написать приложение для MetaTrader 5, которое отображает новости. Приложение будет использовать библиотеку JAson.mqh. Реализуем его в виде советника, который будет отображать список новостных ресурсов. Когда элемент списка выбран, в соседнем текстовом поле будут отображены последние новостные фрагменты, доступные из источника.

Класс CNewsFeed

В предыдущей статье я использовал стандартную библиотеку для построения графического пользовательского интерфейса. На сей раз я хочу изучить использование библиотеки графических интерфейсов, разработанную Анатолием Кажарским. Поскольку библиотека, по-видимому, находится в состоянии бета-тестирования и регулярно обновляется автором (в нее добавляются новые функции), существует несколько ее расширенных версий. Я решил использовать версию, представленную в статье Графические интерфейсы XI: Рефакторинг кода библиотеки. Как мне представляется, в ней есть все, что нам нужно, и в то же время и нет ничего лишнего, что нам не пригодилось бы.

Наше приложение будет достаточно простым: в нем не понадобятся ни строка меню, ни вкладки. Начнем с файла include, который будет содержать основной класс для создания приложения.

//+------------------------------------------------------------------+ 
//|                                              NewsFeedProgram.mqh |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
#include <JAson.mqh>

#define BASE_URL "https://newsapi.org/v1/"
#define SRCE "sources?"
#define ATCLE "articles?source="
#define LATEST "&sortBy=latest"
#define API_KEY "&apiKey=484c84eb9765418fb58ea936908a47ac"
//+------------------------------------------------------------------+ 
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+ 
class CNewsFeed : public CWndEvents
  

NewsFeedprogram.mqh будет включать библиотеку GUI и библиотеку json. Как и в скрипте, компоненты URL хранятся в директивах.

//+------------------------------------------------------------------+ 
//|                                              NewsFeedProgram.mqh |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
#include <JAson.mqh>

#define BASE_URL "https://newsapi.org/v1/"
#define SRCE "sources?"
#define ATCLE "articles?source="
#define LATEST "&sortBy=latest"
#define API_KEY "&apiKey=484c84eb9765418fb58ea936908a47ac"
//+------------------------------------------------------------------+ 
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+ 
class CNewsFeed : public CWndEvents
  {
private:
   //--- Счетчик времени
   CTimeCounter      m_counter; // для обновления элементов в строке состояния
   //--- Главное окно
   CWindow           m_window;
   //--- Строка состояния
   CStatusBar        m_status_bar;
   //--- Списки
   CListView         m_listview;
   //--- Поля ввода
   CTextBox          m_text_box;
   //--- Основные объекты Json 
   CJAVal            srce;
   CJAVal            js;
   //--- Указатели json для ссылок на вложенные элементы
   CJAVal           *articles;
   CJAVal           *articlesArrayElement;
   CJAVal           *sources;
   CJAVal           *sourcesArrayElement;

Основной класс CNewsFeed наследуется от CWndEvents. Его приватные свойства — компоненты элементов управления, из которых составлено приложение, то есть: форма основного окна, к которой прикреплены список, текстовое поле и строка состояния. Временной счетчик включен для обновления строки состояния. Оставшиеся приватные свойства типа CJAVal включают парсер Json, свойства srce и js. Их будут содержать объекты json, возвращаемые после вызова новостных источников и определенных новостей из определенной ленты, соответственно. Остальные свойства — это указатели, которые ссылаются на вложенные объекты json.

Оставшаяся часть класса показана ниже.

//+------------------------------------------------------------------+ 
//|                                              NewsFeedProgram.mqh |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
#include <JAson.mqh>

#define BASE_URL "https://newsapi.org/v1/"
#define SRCE "sources?"
#define ATCLE "articles?source="
#define LATEST "&sortBy=latest"
#define API_KEY "&apiKey=484c84eb9765418fb58ea936908a47ac"
//+------------------------------------------------------------------+ 
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+ 
class CNewsFeed : public CWndEvents
  {
private:
   //--- Счетчики времени
   CTimeCounter      m_counter; // для обновления элементов в строке состояния
   //--- Основное окно
   CWindow           m_window;
   //--- Строка состояния
   CStatusBar        m_status_bar;
   //--- Списки
   CListView         m_listview;
   //--- Поля ввода
   CTextBox          m_text_box;
   //--- Основные объекты Json 
   CJAVal            srce;
   CJAVal            js;
   //--- Указатели json для ссылок на вложенные элементы
   CJAVal           *articles;
   CJAVal           *articlesArrayElement;
   CJAVal           *sources;
   CJAVal           *sourcesArrayElement;
public:
                     CNewsFeed(void);
                    ~CNewsFeed(void);
   //--- Инициализация/деинициализация
   bool              OnInitEvent(void);
   void              OnDeinitEvent(const int reason);
   //--- Таймер
   void              OnTimerEvent(void);
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

   //--- Создаёт графический интерфейс программы
   bool              CreateGUI(void);
private:
   //--- Основное окно
   bool              CreateWindow(const string text);
   //--- Строка состояния
   bool              CreateStatusBar(const int x_gap,const int y_gap);
   //--- Списки
   bool              CreateListView(const int x_gap,const int y_gap);
   //--- Поля ввода
   bool              CreateTextBox(const int x_gap,const int y_gap);
   //--- Заполнение объекта Json со списком источников
   bool              PrepareSources(void);
   //--- Заполнение объекта Json по статьям, расположенного в выбранном элементе списка источников Json 
   bool              PrepareArticles(int listview_index);
   //--- Загрузка данных
   string            DownLoad(string url,string file_name);
  };

Детальные описания методов, относящихся к графическому пользовательскому интерфейсу, можно найти в статьях Анатолия Кажарского.

Изменение класса CTextBox

Обсудим здесь только одну часть библиотеки, потому что я немного изменил ее, чтобы получить необходимый для нас функционал. Изменения коснулись элемента управления "текстовое поле". Во время тестирования я заметил, что метод update() текстового поля не реагирует, как ожидалось, при обновления поля ввода новым содержимым. Эта проблема была решена добавлением нескольких приватных методов из того же класса в метод обновления, как показано ниже.

Обновление содержимого в текстовом поле вызвало также и другие проблемы. Они возникают при сворачивании и разворачивании приложения. Виновником тоже тут было поле ввода. Проблемы эти связаны в основном с автоматическим изменением размера.

//+------------------------------------------------------------------+ 
//| Updating the control                                             |
//+------------------------------------------------------------------+ 
void CTextBox::Update(const bool redraw=false)
  {
//--- Перерисовывание таблицы, если указано
   if(redraw)
     {
      //--- Draw
      //ChangeTextBoxSize();
      WordWrap();
      Draw();
      CalculateTextBoxSize();
      ChangeScrollsSize();
      //--- Apply
      m_canvas.Update();
      m_textbox.Update();
      return;
     }
//--- Apply
   m_canvas.Update();
   m_textbox.Update();
  }


Сначала сталкиваемся с проблемой с полосами прокрутки. Горизонтальная и вертикальная прокрутка частично некорректно работают после обновления текстового поля. Я говорю "частично", поскольку по достижении определенной точки прокрученное содержимое внезапно исчезает. Я заметил, что проблемы возникают, когда новое содержимое, вписанное в поле ввода, имело больший размер (то есть, максимальную ширину строки и количество строк в тексте), чем исходный текст, отображаемый во время инициализации. Чтобы исправить эти проблемы, я включил режим переноса слов, так, чтобы отпала необходимость в горизонтальной полосе прокрутки, и инициализировал текстовое поле с большим количеством пустых строк. Формат списка и текстового поля в вертикальной плоскости был отключен.

Поэтому, если вы хотите запустить приложение и у вас есть загруженная библиотека Gui, просто поменяйте в ней файл TextBox.mqh на такой же файл из приложения к данной статье.

Методы для обработки объектов Json

//+------------------------------------------------------------------+ 
//| Метод для веб-запросов и кэширования данных                      |
//+------------------------------------------------------------------+      
string CNewsFeed::DownLoad(string url,string file_name="")
  {

// Если терминал не подключен, советник предполагает отсутствие подключения  
   if(!(bool)::TerminalInfoInteger(TERMINAL_CONNECTED))return(NULL);

   string cookie=NULL,headers,pStream;
   char post[],result[];
   int res,hFile;

   ::ResetLastError();
   int timeout=5000;
// Веб-реквест
   res=::WebRequest("GET",url,cookie,NULL,timeout,post,0,result,headers);

   if(res==-1)
     {
      ::Print("WebRequest failure");
      return(NULL);
     }

// Загруженный поток данных
   pStream=::CharArrayToString(result,0,-1,CP_UTF8);

   if(file_name!="")
     {

      hFile=::FileOpen(file_name,FILE_BIN|FILE_WRITE);

      if(hFile==INVALID_HANDLE)
        {
         return(pStream);
         ::Print("Invalid file handle - "+file_name+" - could not save data to file");
        }
      // Запись загруженных данных в файл
      ::FileWriteString(hFile,pStream);
      ::FileClose(hFile);
     }
//Print ("Загрузка успешна");
   return(pStream);
  }

Download() — используется для вызова API через функцию webrequest и возвращает ответ в виде строкового значения. Если указан второй строковый параметр, ответная строка будет сохранена в файл. Если возникает ошибка, возвращается значение NULL.

//+------------------------------------------------------------------+ 
//| downloads data to fill sources json object                       |
//+------------------------------------------------------------------+     
bool CNewsFeed::PrepareSources(void)
  {
   string sStream;
   int    iStringSize,hFile;

   string sources_filename="sources.txt";
// Загрузка данных
   sStream=DownLoad(BASE_URL+SRCE,sources_filename);

   if(sStream==NULL)
     {
      if(!::FileIsExist(sources_filename))
        {
         ::Print("error : required file does not exit");
         return(false);
        }
      // Считывание содержимого файла
      hFile=::FileOpen(sources_filename,FILE_TXT|FILE_READ|FILE_UNICODE);
      if(hFile==INVALID_HANDLE)
        {
         ::Print("error opening file "+sources_filename);
         return(false);
        }

      while(!::FileIsEnding(hFile))
        {
         iStringSize = ::FileReadInteger(hFile, INT_VALUE);
         sStream    += ::FileReadString(hFile, iStringSize);
        }

      ::FileClose(hFile);
     }
// Парсинг данных json 
   if(!srce.Deserialize(sStream))
     {
      ::Print("Json deserialize error");
      return(false);
     }
// Добавление объекта json к источникам
   if(srce["status"].ToStr()=="ok")
     {
      sources=srce["sources"];
      return(true);
     }
   else
     {
      Print("error json api access denied");
      return(false);
     }
  }

PrepareSources() вызывается во время инициализации советника один раз — чтобы сделать запрос API для получения доступных источников новостей. Ответ сохраняется в файле и парсится с использованием метода Deserialize парсера Json. Отсюда массив источников объектов json присваивается указателю на источники. Если нет возможности подключения и еще не создан файл источников .txt, советник не будет успешно инициализирован.

//+------------------------------------------------------------------+ 
//| downloads data to fill articles json object                      |
//+------------------------------------------------------------------+      
bool CNewsFeed::PrepareArticles(int listview_index)
  {
   string sStream,id;
   int iStringSize,hFile;
// Проверка источников объекта json 
   if(sources==NULL)
     {
      ::Print("Invalid pointer access");
      return(false);
     }

// Проверка индекса 
   if(listview_index>=::ArraySize(sources.m_e))
     {
      Print("invalid array index reference");
      return(false);
     }
// Создать ссылку объекта json на элементы массива источников 
   sourcesArrayElement=sources[listview_index];
// получение названий новостных источников
   id=sourcesArrayElement["id"].ToStr();
// сброс объекта json sourcesArrayElement
   sourcesArrayElement=NULL;

// загрузка данных для определенного новостного источника
   sStream=DownLoad(BASE_URL+ATCLE+id+API_KEY,id+".txt");
   if(sStream==NULL)
     {
      if(!::FileIsExist(id+".txt"))
        {
         ::Print("error : required file does not exit");
         return(false);
        }

      // чтение файла с данными json 
      hFile=::FileOpen(id+".txt",FILE_TXT|FILE_READ|FILE_UNICODE);
      if(hFile==INVALID_HANDLE)
        {
         ::Print("error opening file "+id+".txt");
         return(false);
        }

      while(!::FileIsEnding(hFile))
        {
         iStringSize = ::FileReadInteger(hFile, INT_VALUE);
         sStream    += ::FileReadString(hFile, iStringSize);
        }

      ::FileClose(hFile);
     }

// парсинг файла json 
   if(!js.Deserialize(sStream))
     {
      ::Print("Json deserialize error");
      return(false);
     }
// назначение объекта json указателям на статьи
   if(js["status"].ToStr()=="ok")
     {
      articles=js["articles"];
      return(true);
     }
   else
     {
      Print("error json api access denied");
      return(false);
     }
  }


PrepareArticles(): эта функция используется в методе обработчика событий основного графика. С ее помощью мы делаем запрос API на новости из определенного источника, прежде чем она сможет быть отображена в текстовом поле. Целочисленное значение, переданное функции, представляет индекс выбранного элемента списка. Этот индекс используется для идентификации выбранного источника новостей, чтобы мог быть создан правильный API-запрос URL. Ответ API обрабатывается таким же образом, как он обрабатывается в методе PrepareSources.

Также обратите внимание на то, что оба описанных метода могут работать без подключения — тогда и только тогда, когда запрос API соответствует существующему файлу.

Обновление текстового окна

Теперь рассмотрим метод OnchartEvent. Если кликнуть на элемент списка, текстовое окно сначала автоматически прокручивается к началу. Это необходимо во избежание некорректного отображения нового содержимого окна. Методы объекта списка SelectedItemText() и SelectedItemIndex() используются для получения имени и индекса элемента списка, на котором мы кликнули. В нашем случае этот элемент определяет название выбранного источника новостей и его позицию в массиве источников объекта json. Исходя из этой информации, можно построить правильный URL, чтобы сделать API-запрос новостных заголовков с использованием метода PrepareArtilces(). Если это произошло успешно, текстовое поле обновляется последними заголовками. В противном случае текстовое поле покажет сообщение об ошибке.

//+------------------------------------------------------------------+ 
//| Chart event handler                                              |
//+------------------------------------------------------------------+ 
void CNewsFeed::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
// если был клик на одном из новостных источников
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      string nameofobject="";        // название выбранного источника
      int selected,asize,g;          // selected - индекс выбранного элемента списка, asize - размер массива объектов json, представляющих статьи, g - индекс массива
      bool ar=false;                 // ar - значение, возвращаемое методом prepareArticles
                                     // сначала автопрокрутка текстового поля к началу, только если нужна вертикальная прокрутка
      if(m_text_box.GetScrollVPointer().IsScroll())
         m_text_box.VerticalScrolling(0);
      //--- 
      nameofobject=m_listview.SelectedItemText();
      //--- 
      selected=m_listview.SelectedItemIndex();
      //---  
      ar=PrepareArticles(selected);
      //--- 
      asize=(articles!=NULL)? ::ArraySize(articles.m_e):0;
      //---удаление текущего содержимого текстового поля
      m_text_box.ClearTextBox();
      //--- добавление заголовка для нового содержимого текстового поля
      m_text_box.AddText(0,nameofobject+" Top HeadLines:");
      //--- в зависимости от успешности выполнения метода PrepareArticles, текстовое поле заполняется новым содержимым
      if(asize>0 && ar)// если метод PrepareArticles выполнен успешно
        {
         string descrip,des;
         for(g=0; g<asize;g++)
           {
            // установить объект json для элемента из статейного массива объектов json 
            articlesArrayElement=articles[g];
            // получить значение
            des=articlesArrayElement["description"].ToStr();
            // установить дополнительный текст для отображения в зависимости от его доступности
            descrip=(des!="null" && des!="")? " -> "+des:".";
            // добавить новый текст в текстовое поле 
            m_text_box.AddLine(string(g+1)+". "+articlesArrayElement["title"].ToStr()+descrip);
           }
        }
      else // если метод PrepareArticles не сработал успешно
        {
         asize=1; // установить значение asize = 1 
         for(g=0; g<asize;g++)
           {
            // отобразить сообщение об ошибке в текстовом поле
            m_text_box.AddLine("Error retrieving data from feed.");
           }
        }
      //-- перерисовать текстовое поле     
      m_text_box.Update(true);
      //-- переустановить значение объекта со статьями
      articles=NULL;
      //Print("clicked listview item is "+nameofobject);
      return;
     }
  }

Этим завершается определение класса CNewsFeed, и теперь он может быть включен в советник.

Советник NewsFeedProgram

Ниже продемонстрирован код, а также некоторые скриншоты, отражающие внешний вид приложения при запуске. 

//+------------------------------------------------------------------+ 
//|                                               NewsFeedExpert.mq5 |
//|                                          Copyright 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <NewsFeedProgram.mqh>
CNewsFeed program;
//+------------------------------------------------------------------+ 
//| Expert initialization function                                   |
//+------------------------------------------------------------------+ 
int OnInit()
  {

   if(!program.OnInitEvent())
     {
      ::Alert("Check your internet connection and set up the terminal \n"+
              "for Web requests");
      return(INIT_FAILED);
     }

//--- Установим торговую панель
   if(!program.CreateGUI())
     {
      ::Print("Failed to create graphical interface!");
      return(INIT_FAILED);
     }
//--- Инициализация прошла успешно
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+ 
//| Функция деинициализации советника                                 |
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason)
  {
//--- удаление таймера
   program.OnDeinitEvent(reason);

  }
//+------------------------------------------------------------------+ 
//| Тиковая функция эксперта                                         |
//+------------------------------------------------------------------+ 
void OnTick()
  {
//--- 

  }
//+------------------------------------------------------------------+ 
//| Timer function                                                   |
//+------------------------------------------------------------------+ 
void OnTimer()
  {
//--- 
   program.OnTimerEvent();
  }
//+------------------------------------------------------------------+ 
//| Функция ChartEvent                                              |
//+------------------------------------------------------------------+ 
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- 
   program.ChartEvent(id,lparam,dparam,sparam);
  }

Вид приложения при запуске

Экран новостей


Заключение

В статье мы изучили возможности создания пользовательской новостной ленты с использованием новостного веб-API. Продемонстрированный советник очень прост и, вероятно, может быть расширен за счет включения функции автоматического обновления, чтобы приложение могло отображать последние новости по мере их появления. Надеюсь, это будет кому-нибудь полезно.

Обратите внимание: чтобы советник корректно работал, нужно загрузить к себе на компьютер библиотеку GUI. Затем надо заменить в ней файл TextBox.mqh на тот, который прикреплен к этой статье.

Программы и файлы, использованные в статье

Имя 
Тип
Описание параметров
JAson.mqh
Заголовочный файл
Cериализация и десериализация объекта JSON в MQL
json.mqh
Заголовочный файл
класс для парсинга Json
TextBox.mqh
Заголовочный файл
Измененный класс текстового поля для отображения форматированного текста на графике
NewsFeedProgram.mqh
Заголовочный файл
Основной класс для советника, отображающего новостную ленту
NewsFeedExpert.mq5
Файл эксперта
Имплементация класса для отображения новостной ленты в советнике
NewsAPI_test.mq5
Скрипт
Скрипт для тестирования вызовов API
newstest_JAson.mq5
Скрипт
Тестовый скрипт для доступа к возможностям библиотеки JAson.mqh
newstest_json.mq5
Скрипт
Тестовый скрипт для доступа к возможностям библиотеки json.mqh