65.9K
CodeProject 正在变化。 阅读更多。
Home

Highstock + Data-Forge + Yahoo

starIconstarIconstarIconstarIconstarIcon

5.00/5 (19投票s)

2016年1月5日

MIT

11分钟阅读

viewsIcon

55202

downloadIcon

601

使用 Data-Forge 加载的来自 Yahoo 的金融数据的 Highstock 演示

本文已重新发布在The Data Wrangler上。

目录

更新于2016-10-13

已更新至最新版本的Data-Forge。

更新于2016-04-04

我已移除之前使用的CORS服务器。事实证明该方法有问题,因此我不再推荐此技术。本文的代码现在包含一个简单的NodeJS代理服务器用于访问Yahoo金融API。实时演示现在已部署到Azure。有关此更新的详细信息,请参阅下方的新部分。

引言

本文旨在配套和解释我使用Yahoo金融API的数据来演示Highstock图表。它面向对金融数据处理和可视化感兴趣的开发人员。您应至少具备JavaScriptjQuery的基础知识。通过研究附带的示例代码,您将找到大部分有用的信息。在本文中,我将简要概述这项技术及其如何协同工作。

本文介绍了开源的Data-Forge库,该库仍处于原型和开发阶段。我是Data-Forge的主要开发者,并希望引起大家对其的关注并收集反馈。

我同时也想推广Highstock和Yahoo金融API。这两者我都没有参与,我纯粹是作为一名热情的用户来推广它们。我发现它们都非常成熟、稳定且极其有用。

相关的示例代码展示了如何在浏览器中使用Data-Forge(及各种插件)。通过稍作修改,所有Data-Forge和Yahoo的代码也可以在Node.js下运行。Highstock是为浏览器设计的,(至少不容易)在Node.js下运行。

实时演示直接从GitHub仓库部署到Azure。

此示例演示了以下内容:

获取代码 (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 ...
    }); 

输出

Click to enlarge image

上面的代码旨在浏览器中运行。由于跨域资源共享(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,这是一个开源的数据分析和转换工具包,其灵感来自PandasLINQ(我还在开发一个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代理迁移到我自己的代理服务器后,我需要一个地方来托管实时演示。我决定将其部署为AzureWeb App,最低级别基本上是免费的,这对于这类演示很有用。

在我学习如何将Node.js应用程序部署到Azure的过程中,我构建了两个小型示例。第一个也是最简单的示例演示了如何将最简单的网页部署到Azure Web App。第二个示例展示了如何将一个简单的Node.js应用程序部署到Azure。

托管代码的GitHub仓库已设置为直接部署到Azure。

结论

通过本文,我介绍了Data-ForgeMarket Tracker。Data-Forge是一个开源的数据分析和转换工具包。Market Tracker是金融分析和自动化平台。两者都是原型和开发中的项目,我将不胜感激建设性的批评。

对于Data-Forge,我正在寻找用户和贡献者。请尽您所能帮助推动这个项目向前发展!

在示例代码中,我演示了如何从Yahoo检索金融数据并使用Highstock进行可视化。我简要概述了各部分如何协同工作,并展示了Data-Forge如何让这一切变得更简单。

资源

历史

  • 2016年1月5日:初始版本
© . All rights reserved.