Skip to content

Commit 2dc6d7a

Browse files
authored
Merge pull request #1020 from plotly/finance
OHLC and Candlestick trace types
2 parents f7a8afa + c9de6c8 commit 2dc6d7a

33 files changed

+5058
-25
lines changed

lib/candlestick.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/traces/candlestick');

lib/index-finance.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var Plotly = require('./core');
12+
13+
Plotly.register([
14+
require('./bar'),
15+
require('./histogram'),
16+
require('./pie'),
17+
require('./ohlc'),
18+
require('./candlestick')
19+
]);
20+
21+
module.exports = Plotly;

lib/index.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,22 @@ Plotly.register([
2020
require('./histogram2dcontour'),
2121
require('./pie'),
2222
require('./contour'),
23+
require('./scatterternary'),
24+
2325
require('./scatter3d'),
2426
require('./surface'),
2527
require('./mesh3d'),
28+
2629
require('./scattergeo'),
2730
require('./choropleth'),
31+
2832
require('./scattergl'),
2933
require('./pointcloud'),
30-
require('./scatterternary'),
31-
require('./scattermapbox')
34+
35+
require('./scattermapbox'),
36+
37+
require('./ohlc'),
38+
require('./candlestick')
3239
]);
3340

3441
// transforms

lib/ohlc.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/traces/ohlc');

src/components/legend/draw.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,28 @@ function drawTexts(g, gd) {
367367
.call(textLayout)
368368
.on('edit', function(text) {
369369
this.attr({'data-unformatted': text});
370+
370371
this.text(text)
371372
.call(textLayout);
373+
372374
if(!this.text()) text = ' \u0020\u0020 ';
373-
Plotly.restyle(gd, 'name', text, traceIndex);
375+
376+
var fullInput = legendItem.trace._fullInput || {},
377+
astr;
378+
379+
// N.B. this block isn't super clean,
380+
// is unfortunately untested at the moment,
381+
// and only works for for 'ohlc' and 'candlestick',
382+
// but should be generalized for other one-to-many transforms
383+
if(['ohlc', 'candlestick'].indexOf(fullInput.type) !== -1) {
384+
var transforms = legendItem.trace.transforms,
385+
direction = transforms[transforms.length - 1].direction;
386+
387+
astr = direction + '.legenditem.name';
388+
}
389+
else astr = 'name';
390+
391+
Plotly.restyle(gd, astr, text, traceIndex);
374392
});
375393
}
376394
else text.call(textLayout);

src/lib/coerce.js

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var isNumeric = require('fast-isnumeric');
1313
var tinycolor = require('tinycolor2');
1414
var nestedProperty = require('./nested_property');
1515
var isPlainObject = require('./is_plain_object');
16+
var filterUnique = require('./filter_unique');
1617

1718
var getColorscale = require('../components/colorscale/get_scale');
1819
var colorscaleNames = Object.keys(require('../components/colorscale/scales'));
@@ -457,5 +458,17 @@ exports.findArrayAttributes = function(trace) {
457458

458459
exports.crawl(trace._module.attributes, callback);
459460

461+
// Look into the fullInput module attributes for array attributes
462+
// to make sure that 'custom' array attributes are detected.
463+
//
464+
// At the moment, we need this block to make sure that
465+
// ohlc and candlestick 'open', 'high', 'low', 'close' can be
466+
// used with filter ang groupby transforms.
467+
if(trace._fullInput) {
468+
exports.crawl(trace._fullInput._module.attributes, callback);
469+
470+
arrayAttributes = filterUnique(arrayAttributes);
471+
}
472+
460473
return arrayAttributes;
461474
};

src/lib/filter_unique.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
13+
/**
14+
* Return news array containing only the unique items
15+
* found in input array.
16+
*
17+
* IMPORTANT: Note that items are considered unique
18+
* if `String({})` is unique. For example;
19+
*
20+
* Lib.filterUnique([ { a: 1 }, { b: 2 } ])
21+
*
22+
* returns [{ a: 1 }]
23+
*
24+
* and
25+
*
26+
* Lib.filterUnique([ '1', 1 ])
27+
*
28+
* returns ['1']
29+
*
30+
*
31+
* @param {array} array base array
32+
* @return {array} new filtered array
33+
*/
34+
module.exports = function filterUnique(array) {
35+
var seen = {},
36+
out = [],
37+
j = 0;
38+
39+
for(var i = 0; i < array.length; i++) {
40+
var item = array[i];
41+
42+
if(seen[item] !== 1) {
43+
seen[item] = 1;
44+
out[j++] = item;
45+
}
46+
}
47+
48+
return out;
49+
};

src/lib/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ lib.error = loggersModule.error;
7575

7676
lib.notifier = require('./notifier');
7777

78+
lib.filterUnique = require('./filter_unique');
79+
7880
/**
7981
* swap x and y of the same attribute in container cont
8082
* specify attr with a ? in place of x/y

src/plot_api/plot_api.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ Plotly.plot = function(gd, data, layout, config) {
140140

141141
// generate calcdata, if we need to
142142
// to force redoing calcdata, just delete it before calling Plotly.plot
143-
var recalc = !gd.calcdata || gd.calcdata.length !== (gd.data || []).length;
143+
var recalc = !gd.calcdata || gd.calcdata.length !== (gd._fullData || []).length;
144144
if(recalc) Plots.doCalcdata(gd);
145145

146146
// in case it has changed, attach fullData traces to calcdata
@@ -1245,6 +1245,7 @@ function _restyle(gd, aobj, _traces) {
12451245
'histfunc', 'histnorm', 'text',
12461246
'x', 'y', 'z',
12471247
'a', 'b', 'c',
1248+
'open', 'high', 'low', 'close',
12481249
'xtype', 'x0', 'dx', 'ytype', 'y0', 'dy', 'xaxis', 'yaxis',
12491250
'line.width',
12501251
'connectgaps', 'transpose', 'zsmooth',
@@ -1287,7 +1288,8 @@ function _restyle(gd, aobj, _traces) {
12871288
// TODO: could we break this out as well?
12881289
var autorangeAttrs = [
12891290
'marker', 'marker.size', 'textfont',
1290-
'boxpoints', 'jitter', 'pointpos', 'whiskerwidth', 'boxmean'
1291+
'boxpoints', 'jitter', 'pointpos', 'whiskerwidth', 'boxmean',
1292+
'tickwidth'
12911293
];
12921294

12931295
// replotAttrs attributes need a replot (because different

src/plots/plots.js

+11-10
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ plots.supplyDefaults = function(gd) {
369369

370370
// then do the data
371371
newFullLayout._globalTransforms = (gd._context || {}).globalTransforms;
372-
plots.supplyDataDefaults(newData, newFullData, newFullLayout);
372+
plots.supplyDataDefaults(newData, newFullData, newLayout, newFullLayout);
373373

374374
// attach helper method to check whether a plot type is present on graph
375375
newFullLayout._has = plots._hasPlotType.bind(newFullLayout);
@@ -530,7 +530,7 @@ function relinkPrivateKeys(toContainer, fromContainer) {
530530
var isPlainObject = Lib.isPlainObject,
531531
isArray = Array.isArray;
532532

533-
var keys = Object.keys(fromContainer);
533+
var keys = Object.keys(fromContainer || {});
534534

535535
for(var i = 0; i < keys.length; i++) {
536536
var k = keys[i],
@@ -593,9 +593,9 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa
593593
}
594594
};
595595

596-
plots.supplyDataDefaults = function(dataIn, dataOut, layout) {
597-
var modules = layout._modules = [],
598-
basePlotModules = layout._basePlotModules = [],
596+
plots.supplyDataDefaults = function(dataIn, dataOut, layout, fullLayout) {
597+
var modules = fullLayout._modules = [],
598+
basePlotModules = fullLayout._basePlotModules = [],
599599
cnt = 0;
600600

601601
function pushModule(fullTrace) {
@@ -612,18 +612,18 @@ plots.supplyDataDefaults = function(dataIn, dataOut, layout) {
612612

613613
for(var i = 0; i < dataIn.length; i++) {
614614
var trace = dataIn[i],
615-
fullTrace = plots.supplyTraceDefaults(trace, cnt, layout);
615+
fullTrace = plots.supplyTraceDefaults(trace, cnt, fullLayout);
616616

617617
fullTrace.index = i;
618618
fullTrace._input = trace;
619619
fullTrace._expandedIndex = cnt;
620620

621621
if(fullTrace.transforms && fullTrace.transforms.length) {
622-
var expandedTraces = applyTransforms(fullTrace, dataOut, layout);
622+
var expandedTraces = applyTransforms(fullTrace, dataOut, layout, fullLayout);
623623

624624
for(var j = 0; j < expandedTraces.length; j++) {
625625
var expandedTrace = expandedTraces[j],
626-
fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, layout);
626+
fullExpandedTrace = plots.supplyTraceDefaults(expandedTrace, cnt, fullLayout);
627627

628628
// mutate uid here using parent uid and expanded index
629629
// to promote consistency between update calls
@@ -805,7 +805,7 @@ function supplyTransformDefaults(traceIn, traceOut, layout) {
805805
if(!_module) Lib.warn('Unrecognized transform type ' + type + '.');
806806

807807
if(_module && _module.supplyDefaults) {
808-
transformOut = _module.supplyDefaults(transformIn, traceOut, layout);
808+
transformOut = _module.supplyDefaults(transformIn, traceOut, layout, traceIn);
809809
transformOut.type = type;
810810
}
811811
else {
@@ -816,7 +816,7 @@ function supplyTransformDefaults(traceIn, traceOut, layout) {
816816
}
817817
}
818818

819-
function applyTransforms(fullTrace, fullData, layout) {
819+
function applyTransforms(fullTrace, fullData, layout, fullLayout) {
820820
var container = fullTrace.transforms,
821821
dataOut = [fullTrace];
822822

@@ -830,6 +830,7 @@ function applyTransforms(fullTrace, fullData, layout) {
830830
fullTrace: fullTrace,
831831
fullData: fullData,
832832
layout: layout,
833+
fullLayout: fullLayout,
833834
transformIndex: i
834835
});
835836
}

src/traces/candlestick/attributes.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
var Lib = require('../../lib');
13+
var OHLCattrs = require('../ohlc/attributes');
14+
var boxAttrs = require('../box/attributes');
15+
16+
var directionAttrs = {
17+
name: OHLCattrs.increasing.name,
18+
showlegend: OHLCattrs.increasing.showlegend,
19+
20+
line: {
21+
color: Lib.extendFlat({}, boxAttrs.line.color),
22+
width: Lib.extendFlat({}, boxAttrs.line.width)
23+
},
24+
25+
fillcolor: Lib.extendFlat({}, boxAttrs.fillcolor),
26+
};
27+
28+
module.exports = {
29+
x: OHLCattrs.x,
30+
open: OHLCattrs.open,
31+
high: OHLCattrs.high,
32+
low: OHLCattrs.low,
33+
close: OHLCattrs.close,
34+
35+
line: {
36+
width: Lib.extendFlat({}, boxAttrs.line.width, {
37+
description: [
38+
boxAttrs.line.width.description,
39+
'Note that this style setting can also be set per',
40+
'direction via `increasing.line.width` and',
41+
'`decreasing.line.width`.'
42+
].join(' ')
43+
})
44+
},
45+
46+
increasing: Lib.extendDeep({}, directionAttrs, {
47+
line: { color: { dflt: OHLCattrs.increasing.line.color.dflt } }
48+
}),
49+
50+
decreasing: Lib.extendDeep({}, directionAttrs, {
51+
line: { color: { dflt: OHLCattrs.decreasing.line.color.dflt } }
52+
}),
53+
54+
text: OHLCattrs.text,
55+
whiskerwidth: Lib.extendFlat({}, boxAttrs.whiskerwidth, { dflt: 0 })
56+
};

src/traces/candlestick/defaults.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
var Lib = require('../../lib');
13+
var handleOHLC = require('../ohlc/ohlc_defaults');
14+
var handleDirectionDefaults = require('../ohlc/direction_defaults');
15+
var helpers = require('../ohlc/helpers');
16+
var attributes = require('./attributes');
17+
18+
module.exports = function supplyDefaults(traceIn, traceOut) {
19+
helpers.pushDummyTransformOpts(traceIn, traceOut);
20+
21+
function coerce(attr, dflt) {
22+
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
23+
}
24+
25+
var len = handleOHLC(traceIn, traceOut, coerce);
26+
if(len === 0) {
27+
traceOut.visible = false;
28+
return;
29+
}
30+
31+
coerce('line.width');
32+
33+
handleDirection(traceIn, traceOut, coerce, 'increasing');
34+
handleDirection(traceIn, traceOut, coerce, 'decreasing');
35+
36+
coerce('text');
37+
coerce('whiskerwidth');
38+
};
39+
40+
function handleDirection(traceIn, traceOut, coerce, direction) {
41+
handleDirectionDefaults(traceIn, traceOut, coerce, direction);
42+
43+
coerce(direction + '.line.color');
44+
coerce(direction + '.line.width', traceOut.line.width);
45+
coerce(direction + '.fillcolor');
46+
}

0 commit comments

Comments
 (0)