股票量化软件:从网络中获取债券收益率数据

自动交易几乎完全基于技术指标,这些指标使用过去的价格行为来预测未来的价格行为。然而,忽略市场运动的基本力量的交易者对将基本数据纳入其交易决策的交易者不利。基于自动收集的基础数据的指标可以提高专家顾问的效率。对汇率影响最大的基础数据是利率,它影响货币的感知价值,虽然央行的利率没有波动,但美国等政府债券的收益率、0年期国债,在全球债券市场的所有时间框架内波动。这些收益率反映了市场对未来央行利率走向的预期。债券收益率通常是利率和汇率的主要指标。在金融市场中,适用于货币对的度量标准是不同时间段的利率差,特别是delta或利率差变化本文介绍了如何从网络中收集债券收益率数据,并从利率差和增量的数据表达式中推导出债券收益率数据。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

抓取的基础

浏览器中显示的网页通常由许多元素组成:格式化的文本、图形、图像、声音和视频。所有这些元素都存在于Web服务器上的文件中,并由浏览器使用特定地址或URL按顺序下载以访问它们。但是,程序可以下载页面的一个元素,而忽略其余元素,因为该元素具有有用的信息。以这种方式获取信息被称为“抓取”。要做到这一点,抓取程序必须具有包含元素的文件的URL,该元素可能是显示在网页上的数字。它可以下载该文件,搜索代表数字的文本,并将该文本转换为数值。

编辑切换为居中

添加图片注释,不超过 140 字(可选)

取得 URL

抓取的第一个任务是获取包含要下载元素的文件的URL。如果元素嵌入到页面的HTML文本中,那么它可以是网页的URL,在这种情况下,可以从页面的HTML文本分析元素。或者,URL可以嵌入到页面上的链接中,浏览器使用该链接来获取元素,而scraper程序可以使用该链接来获取要分析元素的HTML文本。或者,可以通过链接到页面上的脚本将URL传递给浏览器,浏览器将在下载页面和脚本后运行该脚本。在这种情况下,scraper程序不必运行脚本,但可以使用脚本生成的URL,使用Internet Explorer或Google Chrome中提供的开发工具可以发现该URL。无论URL的来源是什么,抓取程序都会使用它从Web服务器下载一个文件,并对其进行解析以获取所需的信息。有几个金融网站报告债券收益率,赫兹股票量化首先应该看看 来创建抓取的例子。首先,让赫兹股票量化看看当单击上面的链接时浏览器从Web服务器下载的HTML文件。当页面显示在Chrome浏览器中时,单击右上角的“工具”按钮,将鼠标光标移动到“更多工具”,选择“将页面另存为”,下载HTML文件,并在文本编辑器(如记事本)中打开它。很明显,这个网站使抓取程序很容易获得报价,因为它包含在HTML文件头部的一系列元标记中。元数据不由浏览器显示,对显示的内容没有影响,但任何下载HTML文件的程序都可以访问它。报价出现在元标签<meta name=“price”content=“3.066”>中,距标签开头28个字符,与浏览器在页面上突出显示的值相同。抓取程序可以在文件中搜索文本字符串[<meta name=“price”content=],在meta标记开头的索引中增加28,并将结果位置的文本转换为浮点数字。为了避免混淆,本文中使用方括号引用HTML文本,HTML文本经常使用引号。

构建一个更好的程序

链接到上面的HTML文件从服务器下载时,包含大量样式表信息,总大小为295千字节。但是,感兴趣的meta标记与文件开头的距离仅为3千字节。一个表现较好的抓取程序下载的数据不会超过它所需要的数量,所以每次获取报价时只下载前四个千字节是合理的。不幸的是,mql 函数 WebRequest()无法限制要下载的数据量。包含服务器响应数据的数组必须是动态数组。如果使用指定大小的静态数组,程序将可以正常编译,但在运行时发生错误,导致终端崩溃。可以包含在对服务器的请求中包含 Range 头,但大多数服务器不支持 Range 头。所以,从Web服务器下载的数据量几乎总是所请求的HTML文件的大小。更好的方法是使用wininet.dll中的函数,wininet.dll是Windows的一个组件。其中一个函数 InternetReadFile()可以下载指定数量的字节,即使不支持范围头并且下载从文件开头开始。WinINet 函数可以导入到 MQL 脚本或 EA 交易中。本文所附的 ScraperBot01.mq5 文件是一个脚本,它下载HTML文件的前4千字节,定位所下载文本中感兴趣的元标记,查找该标记中表示10年 T-note上最后一次引用的收益率的文本,将该文本转换为浮点数,并将其值打印到终端。

ScraperBot 01

源代码文件 ScraperBot01.mq5 首先导入wininet.dll 并原型化将调用的函数,将所有参数声明为具有与 mql5 兼容的类型。

 
 

#import "wininet.dll" int InternetCheckConnectionW(string& lpszUrl, uint dwFlags, uint dwReserved); int InternetOpenW(string& lpszAgent, uint dwAccessType, string& lpszProxyName, string& lpszProxyBypass, uint dwFlags); int InternetOpenUrlW(int hInternetSession, string& lpszUrl, string& lpszHeaders, uint dwHeadersLength, uint dwFlags, uint dwContext); int InternetReadFile(int hFile, uchar& lpBuffer[], uint dwNumberOfBytesToRead, uint& lpdwNumberOfBytesRead); int InternetCloseHandle(int hInternet); #import uchar uc_Buffer[4096]; // InternetReadFile() 需要静态的缓冲区. float f_US;

静态数组 uc_Buffer 将用于接收从网络服务器上下载的 html 文本,而 f_US 变量用于设置从文本中分析到的数字值,它们在全局范围内声明。本文和本文附加的其他文件中的约定是通过类型说明符和名称之间的下划线来表示全局变量。uc_Buffer的大小设置为适应将要下载的特定字节数。

在 OnStart()的顶部声明了一些局部变量,其他变量则根据需要声明,以明确它们的用途。首先,赫兹股票量化检查一下互联网连接是否可用。此脚本中调用的函数的返回值将打印到终端,以指示成功或失败,如果发生错误,则关闭打开的句柄,并使用“返回”语句终止脚本。如果有可用的Internet连接,则初始化句柄 iNet1,以便随后调用 WinINet函数。有效的句柄值大于零。

 
 

void OnStart() { bool bResult; int i, iNet1, iNet2; string stURL = "http://www.msn.com"; bResult = InternetCheckConnectionW(stURL, 1, 0); // 1 == FLAG_ICC_FORCE_CONNECTION Print("InternetCheckConnectionW() returned ", bResult); if(!bResult) return; string stAgent = "Mozilla/5.0", stNull = ""; iNet1 = InternetOpenW(stAgent, // _In_ LPCTSTR lpszAgent 1, // 1 == INTERNET_OPEN_TYPE_DIRECT stNull, // _In_ LPCTSTR lpszProxyName stNull, // _In_ LPCTSTR lpszProxyBypass NULL); // _In_ DWORD dwFlags Print("iNet1 == ", iNet1); if(iNet1==0) return;

接下来建立到Web服务器的连接,初始化句柄 iNet2 以下载 html 文件。

 
 

stURL = "https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx"; string stHdr = "Accept: text/*"; iNet2 = InternetOpenUrlW(iNet1, // HINTERNET hInternet, stURL, // LPCWSTR lpszUrl, stHdr, // LPCWSTR lpszHeaders, StringLen(stHdr), // DWORD dwHeadersLength, 0x00080000, // DWORD dwFlags, 0x00080000 == INTERNET_FLAG_NO_COOKIES NULL); // DWORD_PTR dwContext Print("iNet2 == ", iNet2); if(iNet2==0) { InternetCloseHandle(iNet1); return; }

现在赫兹股票量化可以从Web服务器下载数据。

 
 

uint uGet, uGot; uGet = 4080; // 要下载的字节数 bResult = InternetReadFile(iNet2, // _In_ HINTERNET hFile uc_Buffer, // _Out_ LPVOID lpBuffer uGet, // _In_ DWORD dwNumberOfBytesToRead uGot); // _Out_ LPDWORD lpdwNumberOfBytesRead Print("InternetReadFile() returned ", bResult, ". Number of bytes read: ", uGot); InternetCloseHandle(iNet2); // download done if(!bResult) {InternetCloseHandle(iNet1); return;} uc_Buffer[uGot] = 0; // Terminate string in uc_Buffer by appending a null character.

现在赫兹股票量化在下载的文本中搜索感兴趣的meta标签,如果找到了它,在偏移量中添加28,这样我们就可以使用它作为 uc_Buffer 中表示数字的文本的索引。通过调用 StringSubstr()访问该文本,并在变量“i”中将索引传递给它。如果该索引处的文本不代表数字,则StringToDouble()将返回零,指示错误,除非债券收益率恰好为零。请注意,字符串中的引号被编码为\“,以将其与字符串开头和结尾的引号区分开来。

 
 

i = StringFind(CharArrayToString(uc_Buffer), "<meta name=\"price\" content=", 0); // 0 == 开始搜索的位置 Print("Offset of \'<meta name=\"price\" content=\' == ", i); if(i == -1) {Print("String not found."); InternetCloseHandle(iNet1); return;} i += 28; // 将索引提前到表示债券收益的文本的已知位置。 f_US = StringToDouble(StringSubstr(CharArrayToString(uc_Buffer), i, 8)); Print("US 10-year T-note yield, stored in variable f_US: ", f_US); InternetCloseHandle(iNet1); // wininet 完成. }//END void OnStart()

脚本 ScraperBot01可以在任何图表上运行,在终端中报告其进度,并打印从Web上获取的值。

另一种情况

既然已经演示了从网页中获取数据的过程,那么让我们考虑一下如果债券收益率数据不在元标记中,该怎么做。在这种情况下,赫兹股票量化将检查网页的HTML文件,以查找该网页上显示的数字的来源。了解债券收益率的最新报价后,我们可以使用文本编辑器的搜索功能来查找表示数字的文本字符串。在我们下载的HTML文件中,除了meta标签,还可以在其他三个位置找到报价。这三个元素中的第一个是 JSON-LD 结构化数据片段,它是一个HTML元素,旨在使搜索引擎和网络爬虫很容易访问网页信息。 这里是一部分数据,为了更清晰表示使用了分行显示。

 
 

<script type="application/ld+json"> { "@context":"http://schema.org/", "@type":"Intangible/FinancialQuote", "url":"https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx", "name":"U.S. 10 Year Treasury Note", "tickerSymbol":"TMUBMUSD10Y", "exchange":"Tullett Prebon", "price":"3.061", "priceChange":"0.007", "priceChangePercent":"0.22%", "quoteTime":"Sep 28, 2018 5:07 p.m.", "priceCurrency":"PERCENT" } </script>

算法将首先搜索标记的偏移量<script type=“application/ld+json”>,然后从该位置查找“[”price“:”]和的偏移量。如果[“price”:“]的偏移量小于</script>的偏移量,表明它在数据段内,则赫兹股票量化将9添加到[“price”:“]的偏移量中,以到达表示报价的数字的偏移量。该过程在附加的脚本 ScraperBot02.mq5中演示。

ScraperBot 02

此脚本下载的HTML文件的大小可以达到umax指定的大小。首次运行时,umax应设置为远大于预期下载大小的值,例如100万。脚本报告下载的字节数,如果该字节数位于或接近uMax,则应增加uMax的值。脚本还报告标记<script type=\“application/ld+json\”>文件中的偏移量,然后,可以将 uMax 的值设置为略高于标记的偏移量。在这种情况下,偏移量为166696,因此uMax设置为180224以下载足够的文件,以包含 JSON-LD 片段,而不是整个文件。该脚本使用静态数组下载16千字节的块,这些块被复制到动态数组中并累积到动态数组中,这些数组是用全局范围声明的。

 
 

uchar uc_Buffer[16400], uc_DynBuf[];

然后,ScraperBot02 与 ScraperBot01 相同,直到从Web服务器下载数据的部分,该部分将数据分块打包。在 do-while 循环中反复调用 InternetReadFile,直到下载所需的数据量。

 
 

uint uGet, uGot, uDst, uMax; uGet = 16384; // 每次调用InternetReadFile时要下载的字节数,必须至少比 uc_Buffer 大小小1个字节。 uGot = uDst = 0; // uGot是调用 InternetReadFile 时下载的字节数;uDst 是下载的字节总数。 uMax = 180224; // 最大下载字节数 do { bResult = InternetReadFile(iNet2, // _In_ HINTERNET hFile uc_Buffer, // _Out_ LPVOID lpBuffer uGet, // _In_ DWORD dwNumberOfBytesToRead uGot); // _Out_ LPDWORD lpdwNumberOfBytesRead uc_Buffer[uGot] = 0; // 使用空字符来结束 uc_Buffer 中的字符串。 ArrayCopy(uc_DynBuf, // 目标数组 uc_Buffer, // 源数组 uDst, // 开始写到目标数组的索引 0, // 开始从源数组复制的索引 uGot); // 要复制的元素数量 uDst += uGot; // 为了下一次循环调整索引 }while(bResult && uGot > 0 && uDst < uMax); Print("Size of uc_DynBuf == ", ArraySize(uc_DynBuf)); Print("Bytes downloaded == ", uDst);

ScraperBot02 现在定位标签<script type=\“application/ld+json\”> 并将偏移存储在索引变量i中,从该偏移量开始,它定位到[“price”:“]并将该偏移量存储在j中,然后它在代码段的末尾找到</script>并将该偏移量存储在k中。如果j小于k,则将9添加到j中,这将成为表示数字的文本的偏移量,然后在f_US中转换为浮点值并打印到终端。

 
 

int i, j, k; // 索引 i = StringFind(CharArrayToString(uc_DynBuf), "<script type=\"application/ld+json\">", 0); // 0 == 开始搜索的位置 Print("Offset of <script type=\"application/ld+json\"> == ", i); if(i == -1) {Print("<script type=\"application/ld+json\"> not found."); InternetCloseHandle(iNet1); return;} j = StringFind(CharArrayToString(uc_DynBuf), "\"price\":\"", i); // i == 开始搜索的位置 if(j == -1) {Print("\"price\":\" not found."); InternetCloseHandle(iNet1); return;} Print("Offset of \"price\":\" == ", j); k = StringFind(CharArrayToString(uc_DynBuf), "</script>", i); // i == 开始搜索的位置 Print("Offset of </script> == ", k); if(j > k) {Print("Offset of \"price\":\" is greater than offset of </script>"); InternetCloseHandle(iNet1); return;} j += 9; // 将索引提前到表示债券收益的文本的已知位置。 f_US = StringToDouble(StringSubstr(CharArrayToString(uc_DynBuf), j, 8)); Print("US 10-year T-note yield, stored in variable f_US: ", f_US); InternetCloseHandle(iNet1); // wininet 结束. }//END void OnStart()

使用开发者工具

使用Chrome浏览器中的开发人员工具可以找到该Web服务器报价的另一个来源。从右上角的菜单按钮,打开开发者工具并输入进入地址栏。选择中间窗格中的网络选项卡,并选择“XHR”作为要监视的事件类型。选择这些事件中的任何一个将打开右侧的窗格,其中显示诸如标题和响应等详细信息。标记为“quoteByDialect…”的事件有一个有趣的响应,右键单击该窗格并选择“全选”可突出显示该响应。按Ctrl+C将突出显示的文本复制到剪贴板,然后将其粘贴到文本编辑器中。在字符串 ["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":] 后的文本块中可以找到报价。可以在“标题”选项卡下找到获取该文本块的URL。在这种情况下它有些长: https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&dialects=Charting&id=Bond-BX-TMUBMUSD10Y,Bond-BX-TMBMKDE-1. 单击本文右侧的链接将在浏览器窗口中显示文本块。它实际上是两块文本,一块接一块,因为在URL的末尾有两个由逗号分隔的断续器符号字符串。第一个“Bond-BX-TMUBMUSD10Y”用于美国10年期国债,第二个“Bond-BX-TMBMKDE-10Y”用于德国10年期国债。从URL中删除第二个断续器符号字符串会将下载文本的大小从7.1千字节减少到3.6千字节。

随附的脚本 ScraperBot03 下载了滚动条符号“TMUBMUSD10Y”的文本块,找到字符串["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":],,在字符串开头的偏移量中添加61,将其用作表示数字的文本的索引,将文本转换为浮点数并把它打印到终端中。该脚本是根据ScrapterBot01建模的,因此此处不引用代码。这个资源的一个优点是下载的规模很小,由于URL所寻址的文件只有3.6千字节,因此可以使用mql5函数WebRequest(而不是wininet.dll中的函数)来下载该文件,而无需下载远远超过需要的数据。

ScraperBot 04

此脚本使用 WebRequest 而不是 WinINet函数下载与 ScraperBot 03相同的数据。为了使 WebRequest工作,服务器的基本URL(在本例中为“https://api.wsj.net”)需要包含在 MetaTrader平台的“工具\选项\专家顾问”下的允许服务器列表中。全局 char 数组 ch_Data 不会将任何数据传递给 WebRequest,它的存在只是为了满足对该类型参数的要求。

 
 

char ch_Buffer[], ch_Data[16]; float f_US; void OnStart() { int i; string stURL = "https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&" "MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&" "dialects=Charting&id=Bond-BX-TMUBMUSD10Y"; string stHdr = "Accept: text/*, User-Agent: Mozilla/5.0"; string stRspHdr; // 回应头 i = WebRequest("GET", // const string method, HTTP 方法 stURL, // const string url, URL stHdr, // const string headers, 1024, // int timeout, ch_Data, // const char &data[], HTTP 消息体的数组 ch_Buffer, // char &result[], 包含服务器回应数据的数组 stRspHdr); // string &result_headers Print("Server response code: ", i); if(i == -1) {Print("GetLastError == ", GetLastError()); return;} Print("Size of ch_Buffer (bytes downloaded) == ", ArraySize(ch_Buffer)); Print("Response header:\n", stRspHdr); string stSearch = "\"CompositeTrading\":{\"Last\":{\"Price\":{\"Iso\":\"PERCENT\",\"Value\":"; i = StringFind(CharArrayToString(ch_Buffer), stSearch, 0); // 0 == 开始搜索的位置 Print("Offset of ", stSearch, " == ", i); if(i == -1) {Print(stSearch, " not found."); return;} i += 61; // 将索引提前到表示债券收益的文本的已知位置。 f_US = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8)); Print("US 10-year T-note yield, stored in variable f_US: ", f_US); }//END void OnStart()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值