使用替代的、未公开的方法从 Google Finance 下载数据的简单 C# DLL
使用“历史价格”页面中“下载到电子表格”链接的替代方法,从 Google Finance 下载历史和实时价格。
引言
Google 于 2006 年启动了一项金融服务,目前可通过 http://www.google.com/finance 访问。它提供了相当有趣的数据,并包含一个选项,对于某些股票,可以通过位于股票“历史价格”页面上的“下载到电子表格”链接下载历史价格。文章 使用 Silverlight 可视化实时和历史股票数据 深入分析了如何自动执行此操作以自动检索历史数据。该想法包括参数化形式为 http://www.google.com/finance/historical?q={stockCode}&startdate={startDate}&enddate={endDate}&output=csv 的 URL。
但是,此选项并非适用于许多股票,例如属于某些非美国交易所的股票,或适用于货币。幸运的是,存在一种替代的(未公开的)方法,本文探讨的正是这种方法,它几乎可用于“列出”在 Google 上的任何证券。
其背后的想法是利用另一种历史报价来源,该来源可通过类型为 http://www.google.com/finance/getprices?q={code}&x={exchange}&i={interval}&p={period}&f={fields} 的 URL 访问。使用此方法,可以下载(据我所知)Google 上提供的任何证券的信息,只要 Google 为其提供了实时图表。事实上,我认为其主要目的是为该网站的交互式图表提供数据。
为了利用这种第二种方法,本文介绍了一个 C# .NET 2.0 类库,该库自动化了两个操作:
- 根据一些输入参数生成下载 URL。
- 将从 Google 收到的原始数据转换为更易读的格式。
构建 URL 并下载数据
如前所述,URL 匹配格式:http://www.google.com/finance/getprices?q={Code}&x={Exchange}&i={Interval}&p={Period}&f={Fields}。参数的含义是:
Code
。证券的代码。例如,Google 为 GOOG,欧元/美元货币对为 EURUSD。此参数区分大小写,必须大写才能识别。Exchange
。证券上市的交易所。例如,NASDAQ 用于 GOOG,CURRENCY 用于 EURUSD。交易所必须大写,并且可以为空以表示美国交易所。Interval
。Google 将数据分组为区间,其长度(以秒为单位)由此参数定义。其最小值是 60 秒。Period
。将返回数据的时期。Google 始终返回最新数据。此参数的示例包括 1d(一天)、1w(一周)、1m(一个月)或 1y(一年)。Fields
。要返回的字段。此参数似乎被 Google 忽略,因为它总是返回每个区间的日期、开盘价、最高价、最低价、收盘价和成交量。
例如,URL http://www.google.com/finance/getprices?q=LHA&x=ETR&i=60&p=1d&f=d,c,h,l,o,v 的意思是:下载日期、收盘价、最高价、最低价、开盘价和成交量 (f=d,c,h,l,o,v) 的字段,数据按 60 秒的间隔分组 (i=60),用于交易所“ETR” (x=ETR) 上市的证券 LHA (q=LHA) 的最后一天 (p=1d) 的数据。
调用该 URL 后,会下载类似以下内容的数据:
EXCHANGE=ETR
MARKET_OPEN_MINUTE=540
MARKET_CLOSE_MINUTE=1050
INTERVAL=60
COLUMNS=DATE,CLOSE,HIGH,LOW,OPEN,VOLUME
DATA=
TIMEZONE_OFFSET=120
a1306998060,14.84,14.95,14.83,14.93,54359
2,14.84,14.84,14.84,14.84,97
3,14.865,14.865,14.84,14.84,5584
4,14.875,14.875,14.875,14.875,1230
5,14.865,14.885,14.85,14.88,14962
6,14.845,14.86,14.84,14.86,7596
7,14.855,14.855,14.84,14.845,20912
8,14.845,14.85,14.845,14.85,9833
9,14.85,14.85,14.85,14.85,2358
...
如可见,返回的信息由两部分组成:
- 标题。以
TIMEZONE_OFFSET
条目结束。最有趣的字段是: COLUMNS
:定义数据行字段的出现顺序。INTERVAL
:每行数据代表的秒数。- 数据。这部分由几行组成,包含每行数据对应时间段的收盘价/最高价/最低价/开盘价/成交量值,长度为“interval”。日期有两种表示方式:
- 绝对。格式:“a” + 自 1970 年 1 月 1 日(UNIX 纪元的开始)以来的秒数。在示例中,只有第一行数据使用它('a1306713600')。
- 相对。格式:自最后一个“绝对日期”以来的区间数。
类库
类库包含两个类:
DownloadURIBuilder
。其任务是创建下载数据的 URL。它公开了以下构造函数和方法:
/// <summary>
/// Define here the exchange and the ticker.
/// </summary>
public DownloadURIBuilder(string exchange, string tickerName);
/// <summary>
/// Constructs the URI, using the object's exchange and ticker and the
/// given interval and period.
/// </summary>
public string getGetPricesUri(int interval, string period) {
/// <summary>
/// Calls getGetPricesUri with Interval = one day and Period = the number of years
/// since 1970.
///
/// The parameter 'lastDate' must be the current date. DateTime.Now isn't used to
/// avoid dependencies with the system time.
/// </summary>
public string getGetPricesUriToDownloadAllData(DateTime lastDate);
/// </summary>
/// Calls getGetPricesUri with Interval = 1 day and Period = the number of
/// days between 'startDate' and 'endDate'. The aim of this method is to return
/// at least the data between 'startDate' and today.
/// Although the ending date is fixed (Google
/// doesn't allow to define it), it is passed
/// as an argument in order to avoid dependencies
/// between the library code and the current system time.
///
/// Evidently, this method's URL returns data prior
/// to 'startDate'. To avoid this, things
/// like the number of holidays between 'startDate'
/// and 'endDate' should be taken into account.
/// It seems that that would overcomplicate things for this simple example.
/// </summary>
public string getGetPricesUriForRecentData(DateTime startDate, DateTime endDate);
/// <summary>
/// Calls getGetPricesUri with Interval = 1 minute (the minimum possible value)
/// and Period = 1 day.
/// </summary>
public string getGetPricesUriForLastQuote();
DataProcessor
。解释数据并将其转换为更易读的内容。此类包含两个公共方法:/// <summary>
/// Recovers the current (sometimes real-time) quote from the input stream.
/// To be used in conjunction with DownloadUriBuilder.getGetPricesUrlForLastQuote().
/// Extracts today's open, high, low, close and volume (OHLCV) from 'stream'.
/// To do this, it scans the returned data. The 'open' is the first value, the 'close'
/// the last, the high and the low are the highest and lowest of all of the values
/// returned and the volume is the sum of all the volume fields.
/// </summary>
public String processStreamMadeOfOneMinuteBarsToExtractMostRecentOHLCVForCurrentDay(
Stream stream, out string errorMessage) {
/// <summary>
/// To be used together with DownloadUriBuilder.getGetPricesUriToDownloadAllData() and
/// DownloadUriBuilder.getGetPricesUriForRecentData(). Transforms the input into a CSV
/// file with Date, Open, High, Low, Close and Volume headers and a line per day.
/// </summary>
public String processStreamMadeOfOneDayLinesToExtractHistoricalData(Stream str,
out string errorMessage)
例如,下载欧元/美元货币对所有历史数据的过程如下:
- 创建一个
DownloadUriBuilder
对象,将exchange
参数设置为 CURRENCY,将tickerName
参数设置为 EURUSD。两个参数都必须大写(也就是说,Google 不会识别 CuRRency 或 EurUSD)。类库不会自动将此参数设置为大写。这使其更具灵活性,因为它将能够支持未来可能的交易所或包含小写字符的股票代码。 - 调用类方法
getGetPricesUriToDownloadAllData()
,并使用返回的 URL(例如),通过框架的WebClient
类下载数据。 - 创建
DataProcessor
类型的对象,并调用方法processStreamMadeOfOneDayLinesToExtractHistoricalData(Stream str, out string errorMessage)
。可以使用WebClient
类的OpenRead()
方法获取流。如果一切顺利,errorMessage
为 null 或空,您可以使用返回的数据字符串。否则,您可以使用此输出参数显示出了什么问题。
演示工具
演示工具非常简单明了。它是一个 Windows 应用程序,有一个单一窗体,分为三个块:
- 第一个块允许用户选择 URL 构建参数:交易所、股票代码和下载间隔。
- 第二个块显示当前 URL。
- 第三个(也是最后一个)块在按下“下载”按钮后显示下载的数据。它包括通过调用
DataProcessor
公开的方法之一处理的数据,或者通过单击“原始数据”复选框从 Google 返回的数据。还有一个选项可以通过单击“保存”按钮来保存结果。
结论
本文介绍了一种使用 Google Finance 的未公开功能来下载金融数据的简单方法。希望您觉得它对您有所帮助。
历史
- 2011 年 7 月 4 日
- 初始版本。