Highstock + Data-Forge + Yahoo





5.00/5 (19投票s)
使用 Data-Forge 加载的来自 Yahoo 的金融数据的 Highstock 演示
本文已重新发布在The Data Wrangler上。
目录
- 引言
- 获取代码
- 屏幕截图
- Highstock
- 从雅虎获取数据
- DIY雅虎代理 (2016-04-04新增)
- Data-Forge
- 简单移动平均线
- 事件处理和调整大小以适应
- Azure部署 (2016-04-04新增)
- 结论
- 资源
- 历史
更新于2016-10-13
已更新至最新版本的Data-Forge。
更新于2016-04-04
我已移除之前使用的CORS服务器。事实证明该方法有问题,因此我不再推荐此技术。本文的代码现在包含一个简单的NodeJS代理服务器用于访问Yahoo金融API。实时演示现在已部署到Azure。有关此更新的详细信息,请参阅下方的新部分。
引言
本文旨在配套和解释我使用Yahoo金融API的数据来演示Highstock图表。它面向对金融数据处理和可视化感兴趣的开发人员。您应至少具备JavaScript和jQuery的基础知识。通过研究附带的示例代码,您将找到大部分有用的信息。在本文中,我将简要概述这项技术及其如何协同工作。
本文介绍了开源的Data-Forge库,该库仍处于原型和开发阶段。我是Data-Forge的主要开发者,并希望引起大家对其的关注并收集反馈。
我同时也想推广Highstock和Yahoo金融API。这两者我都没有参与,我纯粹是作为一名热情的用户来推广它们。我发现它们都非常成熟、稳定且极其有用。
相关的示例代码展示了如何在浏览器中使用Data-Forge(及各种插件)。通过稍作修改,所有Data-Forge和Yahoo的代码也可以在Node.js下运行。Highstock是为浏览器设计的,(至少不容易)在Node.js下运行。
此示例演示了以下内容:
- 异步加载OHLC数据到Highstock的K线图中。
- 生成简单移动平均线(SMA)并将其叠加在OHLC数据之上。
- 在主股价图下方显示交易量。
- 可以编辑变量(公司、SMA周期、时间间隔)并重新生成图表。
- 图表会调整大小以适应网页。
获取代码 (2016-04-04更新)
代码的zip文件已附加到Code Project文章中。
为了保持最新,我建议您克隆或forkGitHub仓库。您也可以从GitHub下载最新的代码zip。
要在本地运行服务器,您需要安装Node.js。安装NodeJS后,您可以运行以下命令:
cd highstock-yahoo-demo
npm install
cd client
bower install
cd ..
node index.js
现在您可以在浏览器中访问https://:8080来查看运行效果。
屏幕截图
万一实时演示出现问题...这里是截图。
Highstock
Highstock是一款纯JavaScript、客户端股票图表库,非商业用途免费。Highstock背后的公司Highsoft还拥有一款通用的图表库Highcharts。Highsoft拥有众多商业客户,其库非常稳定成熟。
对于此示例代码,我从Highstock异步加载演示开始。我还融入了K线图和交易量演示的元素。
还有许多其他Highstock的演示,可以很好地理解其全部功能。Highstock还拥有良好的文档和一个API参考。阅读这些文档以全面了解Highstock。
Highstock的基本设置相当简单。使用jQuery获取包含图表的元素,然后调用highcharts
函数。传入配置图表的选项并提供数据
var chartOptions = {
// ... options that defined the chart ...
};
$('#container').highcharts('StockChart', chartOptions);
图表选项允许我们设置图表类型、坐标轴选项和初始数据。
多个数据系列可以堆叠在一起。这就是SMA如何叠加在OHLC数据之上的方式。多个Y轴可以独立堆叠在一起,就像本例中OHLC/SMA图下方的交易量图那样。
在示例代码中,我使用了图表类型:K线图、折线图和柱状图。还有更多图表类型可用。OHLC图表类型是另一个您可能感兴趣的、与金融数据相关的类型。
此示例在用户缩放数据时异步加载数据。最初,必须加载完整数据,以便Highstock在其导航器中显示内容。导航器允许用户查看整个时间序列并放大其中的一部分。下载任何公司的全部每日金融数据成本都很高,因此最初只下载月度数据。然后,当用户放大以便更近距离查看时,会根据需要下载周度或日度数据。这有助于最大限度地减少数据加载并保持加载时间的响应性。
Highcharts通过afterSetExtremes事件支持异步数据下载。您也可以在Highstock异步加载演示中看到一个相当简单的示例。它看起来大致如下:
//
// Function called when the user changes the zoom level of the chart.
//
var afterSetExtremes = function (event) {
var fromDate = new Date(event.min);
var toDate = new Date(event.max);
// ... load new data in the requested date range ...
};
var chartOptions = {
// ...
xAxis: {
events: {
afterSetExtremes: afterSetExtremes, // Wire up the event handler.
},
// ...
},
// ...
};
从雅虎获取数据
Yahoo金融REST API非常棒,可能是唯一免费的金融API。它是现成的股票市场数据来源。不幸的是,入门可能有些困难,因为除了社区贡献的内容外,似乎文档很少。
基本的URL结构如下所示:
http://ichart.yahoo.com/table.csv?s=<公司代码>
例如,您可以使用此URL获取微软公司的完整数据(以CSV格式提供):http://ichart.yahoo.com/table.csv?s=MSFT(点击查看)。
下载的CSV文件可以在文本编辑器或Excel(或类似软件)中查看。
URL还可以包含一个interval参数
http://ichart.yahoo.com/table.csv?s=<公司代码>&g=<间隔代码>
有效的间隔代码包括:
- d表示每日;
- w表示每周;以及
- m表示每月。
例如,以月度间隔检索微软数据:http://ichart.yahoo.com/table.csv?s=MSFT&g=m
URL还可以包含一个参数,指定一个日期范围,将返回的数据限制在特定时期内
http://ichart.yahoo.com/table.csv?s=<公司代码>&a=<起始月份>&b=<起始日期>&c=<起始年份>&d=<结束月份>&e=<结束日期>&f=<结束年份>
请注意,Yahoo期望的是零基月份。这与JavaScript Date类相同。
例如,要检索2015年最后几个月的微软数据:http://ichart.yahoo.com/table.csv?s=MSFT&a=9&b=1&c=2015&d=11&e=31&f=2015
当然,示例代码并没有直接调用Yahoo API。Data-Forge有一个方便的插件data-forge-from-yahoo用于从Yahoo拉取数据。fromYahoo
函数返回一个Promise,该Promise解析为一个数据帧,其中包含从Yahoo返回的数据,看起来像这样:
var dataForge = require('data-forge');
dataForge.use(require('data-forge-from-yahoo'));
var options = {
baseUrl: location.protocol + '//' + location.hostname + ':' + location.port + '/yahoo',
};
dataForge.fromYahoo('MSFT', options)
.then(function (dataFrame) {
console.log(dataFrame.take(5).toString());
})
.catch(function (err) {
// ... error handling ...
});
输出
上面的代码旨在浏览器中运行。由于跨域资源共享(CORS)限制,我们无法直接访问Yahoo,这就是为什么我们必须使用代理。正如您所见,baseURL
被设置为指向代理服务器。相同的代码可以在Node.js下工作,但我们不需要设置baseURL
,因为没有CORS限制,我们可以直接从Node.js访问Yahoo API。
DIY雅虎代理 (2016-04-04新增)
在本文的原始版本中,我使用了公共CORS代理来绕过CORS限制。但效果不佳。存在各种问题,例如代理并不总是可用。
因此,我构建了自己的非常简单的代理服务器。它实际上有两个目的:服务示例Web应用程序以及作为检索Yahoo API数据的代理。
这是一个相当简单的Node.js应用程序,它提供静态页面和一个简单的REST API,该API被转发到Yahoo API。
var path = require('path');
var express = require('express');
var request = require('request-promise');
var E = require('linq');
var yahooBaseUrl = 'http://ichart.yahoo.com/table.csv';
var app = express();
// Serve static files.
var staticPath = path.join(__dirname, 'client');
console.log(staticPath);
app.use(express.static(staticPath));
// REST API that relays to the Yahoo API.
app.get('/yahoo', function (req, res) {
var queryParams = E.from(Object.keys(req.query))
.select(function (paramName) {
return paramName + '=' + req.query[paramName];
})
.toArray()
.join('&');
var url = yahooBaseUrl + '?' + queryParams;
request(url)
.then(function (result) {
res.set('Content-Type', 'text/csv');
res.send(result).end();
})
.catch(function (e) {
console.error(e)
});
});
// Start the server.
var server = app.listen(process.env.PORT || 3000, function () {
var host = server.address().address
var port = server.address().port
console.log('Example app listening at http://%s:%s', host, port)
});
Data-Forge
本示例使用了Data-Forge,这是一个开源的数据分析和转换工具包,其灵感来自Pandas和LINQ(我还在开发一个C#版本)。Data-Forge仍处于开发阶段,处于原型阶段,请谨慎使用。我是主要开发者,并希望在这个阶段引起大家对其的关注,以收集反馈并帮助明确其发展方向。我还在开发一个更大的金融分析、投资追踪和自动化系统,Market Tracker,这也是一个原型和开发中的项目。
您可以通过bower在浏览器中安装Data-Forge
bower install --save data-forge
或通过npm在Node.js中安装
npm install --save data-forge
此示例还安装了各种插件:
bower install --save data-forge-from-yahoo
bower install --save data-forge-to-highstock
或者
npm install --save data-forge-from-yahoo
npm install --save data-forge-to-highstock
要在浏览器中使用Data-Forge,只需在HTML文件中包含Data-Forge和插件的脚本文件即可。
<script src="bower_components/data-forge/data-forge.js"></script>
<script src="bower_components/data-forge-from-yahoo/from-yahoo.dist.js"></script>
<script src="bower_components/data-forge-to-highstock/to-highstock.dist.js"></script>
对于Node.js,请在主模块中require,然后使用插件。
var dataForge = require('data-forge');
dataForge.use(require('data-forge-from-yahoo'));
dataForge.use(require('data-forge-to-highstock'));
就像一把瑞士军刀一样,Data-Forge可以做很多事情,但在这个例子中Data-Forge为我们做了什么?
让我们来看看...
- data-forge-from-yahoo封装了从Yahoo获取数据并将其提供给我们一个数据帧的过程。
- Data-Forge解析Yahoo返回的CSV数据并将其转换为数据帧。
- data-forge-to-highstock将数据帧转换为Highstock所需的数据格式。
简单移动平均线
计算简单移动平均线(SMA)并将其作为折线图叠加在K线图上。简单移动平均线是一种基本的金融指标,它平滑了股票市场的频繁波动,以便识别更广泛的趋势。使用Data-Forge的rollingWindow
函数可以非常轻松地实现这一点。
var computeSmaSeries = function (series, period) {
return series.rollingWindow(period)
.asPairs()
.select(function (pair) {
var window = pair[1];
return [window.getIndex().last(), window.average()];
})
.asValues()
;
};
var dataFrame = ...
var smaPeriod = 30;
var close = dataFrame.getSeries('Close');
var sma = computeSMASeries(close, smaPeriod);
var dataFrameWithSMA = dataFrame.withSeries('SMA', sma);
console.log(dataFrameWithSMA.toString());
事件处理和调整大小以适应
示例代码主要依赖jQuery进行事件处理。例如,检测按钮点击和输入字段更改等基本操作。响应各种事件,Highstock图表会被更新并根据需要重新渲染。
最有趣的事件处理程序是窗口大小调整事件。如果我们可以为特定的HTML元素(例如图表的容器div
)处理事件,那将是很好的。然而,这似乎是不可能的,我们必须处理整个窗口的大小调整,并相应地更新图表。这不是最灵活的方法,但当您希望图表的大小与窗口大小(或接近)相匹配时,它会起作用。弄清楚如何做到这一点非常困难,而且感觉不是最优雅的解决方案,然而,就像Web开发中的许多其他决策一样,它最终归结为有效即可。
因此,我们得到一个简单的窗口resize事件处理程序:
$(window).resize(function() {
resizeChart();
});
resizeChart
函数更新Highstock图表的大小。
var resizeChart = function () {
var chart = $('#container').highcharts();
chart.setSize($(window).width(), $(window).height()-50);
};
resizeChart
在图表创建后也会被调用,以确保图表立即设置为正确的大小。这样做的意外后果是,图表数据在初始数据加载后立即重新加载(当图表大小在未来调整时不会发生)。我们不希望我们的数据加载两次,这会减慢应用程序的加载速度,并有点违背我们优化的异步加载的目的。为了对抗这种行为,我修改了resizeChart
,在调整大小时将resizingChart
设置为true
。
var resizeChart = function () {
try {
resizingChart = true; // Track that we are resizing.
var chart = $('#container').highcharts();
chart.setSize($(window).width(), $(window).height()-50);
}
finally {
// Finally clause ensures we never leave dangling state
// should an exception be thrown.
resizingChart = false; // Finished resizing.
}
};
现在我们有了resizingChart
来测试,并在调整图表大小时相应地中止数据加载。这不是最优雅的解决方案,但对于Highstock的奇怪行为来说是一个很好的变通方法。
更新于2016/01/19
最近意识到交易量图表存在渲染问题后,我联系了Highsoft寻求支持。他们很快回复了我一些建议,其中一个奏效了,所以我已将此更新的文章加入了新信息。
如上所述,我在创建图表后立即调用resizeChart
来强制其适应窗口大小。这其中有些东西导致了渲染问题。我当时已经弄清楚问题与调整大小有关。调整窗口大小(这会更新图表大小)会导致渲染问题消失。
根据Highsoft的建议,我添加了代码来在创建图表时设置其大小,而不是在创建后调整。这个改变很简单。图表的宽度和高度现在在图表选项中指定:
var chartOptions =
{
chart: {
width: $(window).width(),
height: $(window).height()-50
},
// ... all the other options ...
};
因此,图表的正确初始大小在创建时设置,我能够移除对resizeChart
的后续调用。问题解决了。
请注意,resizeChart
仍然用于在窗口大小调整后调整图表大小,但不再需要设置图表的初始大小。
Azure部署 (2016-04-04新增)
将公共CORS代理迁移到我自己的代理服务器后,我需要一个地方来托管实时演示。我决定将其部署为Azure的Web App,最低级别基本上是免费的,这对于这类演示很有用。
在我学习如何将Node.js应用程序部署到Azure的过程中,我构建了两个小型示例。第一个也是最简单的示例演示了如何将最简单的网页部署到Azure Web App。第二个示例展示了如何将一个简单的Node.js应用程序部署到Azure。
托管代码的GitHub仓库已设置为直接部署到Azure。
结论
通过本文,我介绍了Data-Forge和Market Tracker。Data-Forge是一个开源的数据分析和转换工具包。Market Tracker是金融分析和自动化平台。两者都是原型和开发中的项目,我将不胜感激建设性的批评。
对于Data-Forge,我正在寻找用户和贡献者。请尽您所能帮助推动这个项目向前发展!
在示例代码中,我演示了如何从Yahoo检索金融数据并使用Highstock进行可视化。我简要概述了各部分如何协同工作,并展示了Data-Forge如何让这一切变得更简单。
资源
- Highstock演示:http://www.highcharts.com/stock/demo
- Highstock文档:http://www.highcharts.com/docs
- Highstock API参考:http://api.highcharts.com/highstock
- Data-Forge:https://github.com/real-serious-games/data-forge-js
历史
- 2016年1月5日:初始版本