外汇EA编写教程:为 MetaTrader 5 创建自定义新闻递送

引言

MetaTrader 5 拥有许多有用功能, 无论交易者的交易风格如何都会需要它们, 其中包括可能的实时新闻订阅。 它为交易者提供了宝贵背景, 其内容可能会对市场产生一定影响。 唯一的限制是所提供新闻的范围。 我相信, 交易者可从更灵活的新闻递送中受益, 这种新闻递送不仅能够选择新闻种类, 还能够选择新闻来源。

内置新闻递送

根据您的交易经纪商, 实时新闻递送可能非常有用, 亦或完全无用。 您必须依赖经纪商在终端中如何实施它。 一些经纪商可提供从信誉卓著的新闻渠道访问相关新闻。 而有些其他经纪商则根本没有。 为此提供了可能性的机会, 探索建立满足这些需求的应用程序。

新闻 API

鉴于所有可能需要的新闻都可以在互联网上免费获得, 我们所需的只是一种获取期望新闻的直接方式。 实现这一目标的一种方法是使用 RSS 工具。 我写过一篇关于如何为 MetaTrader 5 的 RSS 阅读器 进行编码的文章。 顺此路线走下去的最大缺点是不得不重复手工输入每个 URL, 结合在终端中启用 Web 请求所需的额外设置, 这项工作可能变得乏味。

我相信, 最好的解决方案是使用 web API。 经过广泛搜索, 我偶然发现了一个可以访问多个新闻媒体的 API。 简称为 NewsAPI, 它通过 HTTP Get 请求进行访问, 并返回 json 元数据。 它允许检索由所选新闻渠道发布的新闻标题。 有各种各样的新闻网站可供选择, 他们声称将会增加更多。 也鼓励用户建议新的来源。 该服务似乎只缺乏语言多样性, 因为其内容主要由美国和欧洲的新闻媒体主导。 不过, 我认为, 这是可以接受的。 另一个强项是, 所有这些都可以通过一个网址进行访问。

NewsAPI 访问

若要使用 NewsAPI, 您需要获得授权才能访问完整服务。 为此, 请访问 官方网站 并点击获取 API 密钥。 注册您的电子邮件地址, 您将收到所有您需要访问功能的 API 密钥。

NewsAPI 网站

使用 API

整个 API 通过两个主要 URLs 发出请求来访问:

  1. https://newsapi.org/v1/sources? – 一个请求返回 API 上所有可用新闻资源的列表。
    此列表还包括发出最新新闻标题请求时, 每个资源指定的标识符信息。资源 URL 可由参数选项限定, 该参数指定列表返回的资源类型。
  2. https://newsapi.org/v1/articles? – articles URL 会返回来自特定资源的新闻标题和摘要。 该 URL 应包含两个强制参数。 首先是一个标识符, 表示所需资源的独有标识。 第二个是授权的 API 密钥。

参数 描述 可能的参数值 资源/文章  示例
category (选项) 您希望获取资源的分类 商业, 娱乐, 游戏, 日常, 音乐, 政治, 科学和自然, 体育, 技术 sources https://newsapi.org/v1/sources?category=business

language (选项) 您想要新闻资源的语言 en, de, fr sources https://newsapi.org/v1/sources?language=en
country (选项) 资源所在的国家/地区 au, de, gb, in, it, us sources https://newsapi.org/v1/sources?country=us
source (必须) 新闻资源的标识符
使用资源 URL 从请求返回的列表中选择任何一个
articles https://newsapi.org/v1/articles?source=cnbc&apiKey=API_KEY
api key (必须) 用户认证令牌 articles 参见上面的示例
 sortby 新闻标题按照它们在网站上出现的顺序和时间跨度进行排序, 即流行度  top, latest, popular  articles  https://newsapi.org/v1/articles?source=cnbc&sortBy=top&apiKey=API_KEY

上表显示了可以与 API 的两个 URL 一起使用的主要参数。 有关完整列表, 请参阅网站上的 文档

用于测试 API 的脚本

现在我们已经对如何使用 API 有了大致的了解, 是时候应用它了。 我们即将创建的脚本是让我们熟悉发出 API 请求后所得到的响应。 为了达成这个目的, 脚本需要能发出 web 请求, 并且将响应保存到文本文件中。 使用该脚本, 我们应该能够测试任何 API 请求并观察其响应。

这是 NewsAPI_test 脚本的代码。

//+------------------------------------------------------------------+
//|                                                 NewsAPI_test.mq5 |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property 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;
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                  |
//+------------------------------------------------------------------+
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("无法下载文件");
      return;
     }
   else Print("下载成功");

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

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

   if(hFile==INVALID_HANDLE)
     {
      Print("无效文件句柄");
      return;
     }

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

   return;
  }

首先是 define 指令, 它们表示构成 API 请求的 URL 的不同组件。 sFilename 作为用户的第一个输入, 其代表将用于缓存 API 响应的文件名。 Mode 参数是一个枚举, 允许在 API 的两个主要 URL 之间切换。

用户输入参数中所命名的参数是以需要包含在 API 请求中的附加强制或可选 URL 参数。 我们的脚本只有一个函数, 它根据所选的参数设置构建 URL API 调用。 如果 webrequest 函数成功, 元数据将被保存到文件中。

我们现在可以进行一些测试并研究响应。初始测试将使用默认参数运行脚本。 当我们打开文件时, 可以读取 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."  
}

另一个错误响应的示例, 这次请求是使用不正确的资源 ID 进行的。

{
 "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 上主动维护。 而作者没有在代码库当中更新可用的代码文件。

修复编译错误非常简单, 只需将哈希文件直接包含在 json.mqh 文件中即可。 警告都是由隐式类型转换造成的。 下面给出了更正后的 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
// 解析一个 JSON 字符串 - 适用于 mql4++, 来自我的 gawk 实现
// ( https://code.google.com/p/oversight/source/browse/trunk/bin/catalog/json.awk )

/*
   TODO 常量 true|false|null 可以表示为固化对象。
      为此, 删除 _hash 和 _array 必须跳过这些对象。

   TODO 测试 null

   TODO 解析 Unicode 转义
*/

/*
   参见 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();
     }

   // 由数组和对象调用的静态取值函数 当返回子类结果时。
   // 它们允许应用程序检查数值且检索时不必停止程序 
   // (有时候程序暂停是需要的 - 而非 EA 停止, 然后继续处理错误数据)
   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 的代码详述如下。 脚本简单地读取一个文件, 该文件包含早前测试时 API 调用后保存的 json 元数据。 当脚本运行时, 数据中包含的所有可用新闻渠道将输出到终端。

//+------------------------------------------------------------------+
//|                                                newstest_json.mq5 |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <json.mqh>
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   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("打开文件错误 "+sources_filename);
      return(false);
     }

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

   ::FileClose(hFile);

   Print("打开并读取文件成功");

   JSONParser *parser=new JSONParser();

   JSONValue *jv=parser.parse(pStream);

   if(jv==NULL) 
     {
      Print("错误:"+(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);
  }

使用更正后的文件, 该函数库功能良好。

函数库测试结果

现在我们可以看看第二个 json 函数库 JAson.mqh。 使用这个函数库进行测试没有发现任何错误。 它首次即完美运行。 脚本 newstest_JAson 用于测试这个函数库。

//+------------------------------------------------------------------+
//|                                               newstest_JAson.mq5 |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh>
//+------------------------------------------------------------------+
//| 脚本程序开始函数                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   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("打开文件错误 "+sources_filename);
      return(false);
     }

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

   ::FileClose(hFile);

   Print("打开并读取文件成功");

   CJAVal  srce;

   if(!srce.Deserialize(pStream))
     {
      ::Print("Json 析构错误");
      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 类

在之前的文章中, 我使用标准函数库来构建图形用户界面。 这次我希望体验采用 Anatoli Kazharski 的函数库。 随着作者定期提供更新并增加新功能, 函数库似乎持续处于测试阶段, 众多版本的函数库并存。 我选择使用文章 图形界面 XI: 重构函数库代码 中提供的版本。 我感觉, 它提供了我们所需要的一切, 且无需额外的东西。

我们的应用程序将非常基本, 不需要菜单栏或任何选项卡。 我们从包含用于创建应用程序主类的头文件开始。

//+------------------------------------------------------------------+
//|                                              NewsFeedProgram.mqh |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property 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 |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property 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;

主要类 NewsFeed 衍生自 CWndEvents。 它的私有属性是构成应用程序的控件组件, 即主窗口窗体, 附加到该窗体中的列表视图、文本框和状态栏。 包含一个时间计数器用于更新状态栏。 CJAVal 类型的私有属性的其余部分启用 Json 解析器, srce 和 js 属性将分别包含调用新闻资源时返回的 json 对象, 以及来自某些新闻渠道的特别新闻。 其余的属性是指向嵌套 json 对象的指针。

类的其余部分如下所示。

//+------------------------------------------------------------------+
//|                                              NewsFeedProgram.mqh |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property 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);
   //--- 填充 sources Json 对象
   bool              PrepareSources(void);
   //--- 根据 sources Json 对象中的选定元素填充 articles Json 对象
   bool              PrepareArticles(int listview_index);
   //--- 下载数据
   string            DownLoad(string url,string file_name);
  };

有关图形用户界面方法的详细描述可以在 Anatoli Kazharski 撰写的文章中找到。

修改 CTextBox 类

我只想讨论函数库的一部分, 因为我不得不做一些修改来启用我需要的功能。 尤其涉及到文本框控件。 在测试过程中, 我注意到当文本框更新内容时, update() 方法没有按预期做出反应。 通过将相同类中的一些私有方法添加到 update 方法解决了此问题, 如下所示。

刷新文本框中的内容也会带来其它问题。当最小化和最大化应用程序时, 就会出现这些问题。 罪魁祸首再次是文本框。 这些问题大多与自动调整有关。

//+------------------------------------------------------------------+
//| 更新控件                                                          |
//+------------------------------------------------------------------+
void CTextBox::Update(const bool redraw=false)
  {
//--- 如果指定的话, 重绘表格
   if(redraw)
     {
      //--- 绘图
      //ChangeTextBoxSize();
      WordWrap();
      Draw();
      CalculateTextBoxSize();
      ChangeScrollsSize();
      //--- 应用
      m_canvas.Update();
      m_textbox.Update();
      return;
     }
//--- 应用
   m_canvas.Update();
   m_textbox.Update();
  }

首先是滚动条的问题。 刷新文本框后, 水平和垂直滚动似乎部分失灵, 我之所以说部分, 是因为当达到某个临界点时滚动内容突然消失。 我注意到, 每当写入文本框的新内容超出初始化过程中设定的初始文本 “size” 参数 (即组成文本的最大行宽和行数) 时, 就会出现问题。 为了避免这些问题, 我启用了自动换行模式, 这样就不需要水平滚动条, 并用大量的空行初始化文本框。 列表视图和文本框的垂直面自动调整已禁用。

因此, 如果您想运行该应用程序, 并且已经下载了 Gui 函数库, 只需使用文章末尾所示的那个文件替换 TextBox.mqh 文件即可。

处理 Json 对象的方法

//+------------------------------------------------------------------+
//| Web 请求和缓存数据的方法                                          |
//+------------------------------------------------------------------+     
string CNewsFeed::DownLoad(string url,string file_name="")
  {

// 如果终端未连接 EA, 则假定没有连接  
   if(!(bool)::TerminalInfoInteger(TERMINAL_CONNECTED))return(NULL);

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

   ::ResetLastError();
   int timeout=5000;
// web 请求
   res=::WebRequest("GET",url,cookie,NULL,timeout,post,0,result,headers);

   if(res==-1)
     {
      ::Print("WebRequest 失败");
      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("无效文件句柄 - "+file_name+" - 无法保存数据至文件");
        }
      // 将下载的数据写入文件
      ::FileWriteString(hFile,pStream);
      ::FileClose(hFile);
     }
//Print("下载成功");
   return(pStream);
  }

Download() – 通过 webrequest 函数进行 API 调用, 并将响应作为字符串值返回。 如果指定了第二个字符串参数, 则字符串响应将保存到文件中。 如果发生错误, 则返回 NULL 值。

//+------------------------------------------------------------------+
//| 下载数据填充 sources json 对象                                    |
//+------------------------------------------------------------------+    
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("错误 : 所需文件不存在");
         return(false);
        }
      // 读取文件内容 
      hFile=::FileOpen(sources_filename,FILE_TXT|FILE_READ|FILE_UNICODE);
      if(hFile==INVALID_HANDLE)
        {
         ::Print("打开文件错误 "+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 析构错误");
      return(false);
     }
// 分配 json 对象至 sources 
   if(srce["status"].ToStr()=="ok")
     {
      sources=srce["sources"];
      return(true);
     }
   else
     {
      Print("错误 json api 访问禁止");
      return(false);
     }
  }

PrepareSources() 在 EA 初始化期间调用一次, 以便向可用新闻资源发出 API 请求。 响应被保存到一个文件中, 并使用 Json 解析器的 Deserialize 方法进行解析。 从这里, json 对象的 sources 数组被分配给 sources 指针。 如果没有连接, 且资源文件 .txt 文件尚未创建, 则 EA 不会初始化成功。

//+------------------------------------------------------------------+
//| 下载数据填充 articles json 对象                                   |
//+------------------------------------------------------------------+     
bool CNewsFeed::PrepareArticles(int listview_index)
  {
   string sStream,id;
   int iStringSize,hFile;
// 检查 sources json 对象 
   if(sources==NULL)
     {
      ::Print("无效指针访问");
      return(false);
     }

// 检查索引 
   if(listview_index>=::ArraySize(sources.m_e))
     {
      Print("无效数组索引引用");
      return(false);
     }
// json 对象分配给引用 sources 数组元素  
   sourcesArrayElement=sources[listview_index];
// 获取新闻资源的名称
   id=sourcesArrayElement["id"].ToStr();
// 重置 sourcesArrayElement json 对象
   sourcesArrayElement=NULL;

// 下载特定新闻资源的数据
   sStream=DownLoad(BASE_URL+ATCLE+id+API_KEY,id+".txt");
   if(sStream==NULL)
     {
      if(!::FileIsExist(id+".txt"))
        {
         ::Print("错误 : 所需文件不存在");
         return(false);
        }

      // 读取 json 数据文件 
      hFile=::FileOpen(id+".txt",FILE_TXT|FILE_READ|FILE_UNICODE);
      if(hFile==INVALID_HANDLE)
        {
         ::Print("打开文件错误 "+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 析构错误");
      return(false);
     }
// 将 json 对象分配给 articles 指针
   if(js["status"].ToStr()=="ok")
     {
      articles=js["articles"];
      return(true);
     }
   else
     {
      Print("错误 json api 访问禁止");
      return(false);
     }
  }

PrepareArticles() – 此函数用于主图表事件处理器方法。 在文本框中显示之前, 为来自指定新闻渠道的新闻发出 API 请求。 传递给函数的整数值表示所选列表视图条目的索引。 该索引用于识别所选新闻渠道, 因此可以构建适当的 API 发出 URL 请求。 API 响应的处理方式与 PrepareSources 方法中的处理方式相同。

还要注意, 只有当 API 请求与现有文件相对应时, 上述两种方法才能在没有连接的情况下正常工作。

刷新文本框

接下来, 我们看看 OnchartEvent 方法。 当单击列表视图条目时, 首先文本框将自动滚动到顶部。 这是避免新内容显示不正确所必需的。 列表视图对象 SelectedItemText() 和 SelectedItemIndex() 的方法用于获取单击列表视图条目的名称和索引, 在这种情况下, 该列表视图条目定义所选新闻渠道的名称以及在 srce json 对象的 sources 数组中的位置。 根据这些信息, 可以构建正确的 URL, 以便使用 PrepareArtilces() 方法为新闻标题发送 API 请求。 如果成功, 文本框将刷新最新的标题, 否则文本框将显示错误消息。

//+------------------------------------------------------------------+
//| 图标事件处理器                                                    |
//+------------------------------------------------------------------+
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 - articles 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 对象的 articles 数组的元素
            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 为一 
         for(g=0; g<asize;g++)
           {
            // 在文本框上显示错误消息
            m_text_box.AddLine("检索递送数据错误。");
           }
        }
      //-- 重绘文本框      
      m_text_box.Update(true);
      //-- 重置 articles 对象的值
      articles=NULL;
      //Print("点击的列表条目是 "+nameofobject);
      return;
     }
  }

CNewsFeed 类的定义结束了, 所以它可以包含在智能交易系统中。

NewsFeedProgram 智能交易系统

代码如下所示, 以及应用程序在运行时的外观截图。

//+------------------------------------------------------------------+
//|                                               NewsFeedExpert.mq5 |
//|                                            版权所有 2017, ufranco |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "版权所有 2017, ufranco"
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <NewsFeedProgram.mqh>
CNewsFeed program;
//+------------------------------------------------------------------+
//| 智能系统初始化函数                                                |
//+------------------------------------------------------------------+
int OnInit()
  {

   if(!program.OnInitEvent())
     {
      ::Alert("检查您的互联网连接并设置终端 /n"+
              "用来进行 Web 请求");
      return(INIT_FAILED);
     }

//--- 设置交易面板
   if(!program.CreateGUI())
     {
      ::Print("创建图形界面失败!");
      return(INIT_FAILED);
     }
//--- 初始化成功
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 智能系统逆初始化函数                                              |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 摧毁计时器
   program.OnDeinitEvent(reason);

  }
//+------------------------------------------------------------------+
//| 智能系统逐笔报价函数                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
//| 定时器函数                                                        |
//+------------------------------------------------------------------+
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);
  }

应用程序的初始外观

新闻显示

结束语

在本文中, 我们探讨了使用新闻 Web API 创建自定义新闻递送的可能性。 演示的 EA 非常基本, 可以通过启用自动更新功能进行扩展, 以便应用程序在可用时显示最新消息。 我希望它会对某些人有帮助。


请注意, 为了令 EA 正常工作, 请下载 GUI 函数库。 然后将 TextBox.mqh 文件替换为本文所附的文件。

文章中使用的程序和文件

名称  类型 描述
JAson.mqh 头文件 Json 序列化和逆序列化的原生 MQL 类
json.mqh 头文件 Json 解析类
TextBox.mqh 头文件 修改的文本框类, 用于在图表上显示格式化文本
NewsFeedProgram.mqh 头文件 新闻递送 EA 的主类
NewsFeedExpert.mq5 智能交易系统文件 实现主要新闻递送类的智能交易系统
NewsAPI_test.mq5 脚本文件 测试 API 调用的脚本
newstest_JAson.mq5 脚本文件 用于访问 JAson.mqh 函数库功能的测试脚本
newstest_json.mq5 脚本文件 用于访问 json.mq 函数库功能的测试脚本

由MetaQuotes Software Corp.从英文翻译成
原始文章: https://www.mql5.com/en/articles/4149

附加的文件 |

NewsFeedExpert.mq5
(2.48 KB)
NewsAPI_test.mq5
(2.52 KB)
NewsFeedProgram.mqh
(35.64 KB)
TextBox.mqh
(282.87 KB)
newstest_JAson.mq5
(2.03 KB)
newstest_json.mq5
(2.18 KB)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投