Skip to content

OHLC equal open close cases fix #1655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions src/traces/ohlc/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,65 @@ exports.makeTransform = function(traceIn, state, direction) {
};

exports.getFilterFn = function(direction) {
return new _getFilterFn(direction);
};

function _getFilterFn(direction) {
var isPrevThisDirection = null;
var cPrev = null;
var fn;

switch(direction) {
case 'increasing':
return function(o, c) { return o <= c; };
fn = function(o, c) {
if(o === c) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't cast to numbers, have we? Looks like this will break if we mix strings and numbers (or use different string representations of the same number, like '2.0' and '2.00'

I guess a point should get dropped if any of o, h, l, c is not numeric?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked all to ensure they're numeric in 597adf9

if(c > cPrev) {
return true; // increasing
} else if(c < cPrev) {
return false; // decreasing
} else {
if(isPrevThisDirection === true) {
return true; // determine by last candle
} else if(isPrevThisDirection === false) {
return false; // determine by last candle
} else {
return true; // If we don't have previous data, assume it was increasing
}
}
}
return o < c;
};
break;

case 'decreasing':
return function(o, c) { return o > c; };
fn = function(o, c) {
if(o === c) {
if(c > cPrev) {
return false; // increasing
} else if(c < cPrev) {
return true; // decreasing
} else {
if(isPrevThisDirection === true) {
return true; // determine by last candle
} else if(isPrevThisDirection === false) {
return false; // determine by last candle
} else {
return false; // If we don't have previous data, assume it was increasing
}
}
}
return o > c;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of duplication with just a few true <-> false flips... looks like we could keep the perf up without the duplication if we made one function isIncreasing(o, c), which looks at a state var isPrevIncreasing (which would also simplify the logic there as we wouldn't need to deal with null, just initialize it to true), then move the switch logic into creating the wrapper function below:

function increasingFilter(o, c) {
    var isThisIncreasing = isIncreasing(o, c);
    isPrevIncreasing = isThisIncreasing; // why add !! to this?
    cPrev = c;
    return isThisIncreasing;
}

function decreasingFilter(o, c) {
    var isThisIncreasing = isIncreasing(o, c);
    isPrevIncreasing = isThisIncreasing; // why add !! to this?
    cPrev = c;
    return !isThisIncreasing; // the only difference...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson I vaguely remember you mentioning on slack that you had a commit implementing your solution on a branch. If that's not case, I'll try to push something up to get this PR in this week's 1.28.0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplified these filters (even more than ^^) in 9575ab6

break;
}
};

return function(o, c) {
var out = fn(o, c);
isPrevThisDirection = !!out;
cPrev = c;
return out;
};
}

exports.addRangeSlider = function(data, layout) {
var hasOneVisibleTrace = false;
Expand Down
39 changes: 39 additions & 0 deletions test/jasmine/tests/finance_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,45 @@ describe('finance charts calc transforms:', function() {
expect(out[1].x).toEqual([]);
expect(out[3].x).toEqual([]);
});

it('should handle cases where \'open\' and \'close\' entries are equal', function() {
var out = _calc([{
type: 'ohlc',
open: [0, 1, 0, 2, 1, 1, 2, 2],
high: [3, 3, 3, 3, 3, 3, 3, 3],
low: [-1, -1, -1, -1, -1, -1, -1, -1],
close: [0, 2, 0, 1, 1, 1, 2, 2],
tickwidth: 0
}, {
type: 'candlestick',
open: [0, 2, 0, 1],
high: [3, 3, 3, 3],
low: [-1, -1, -1, -1],
close: [0, 1, 0, 2]
}]);

expect(out[0].x).toEqual([
0, 0, 0, 0, 0, 0, null,
1, 1, 1, 1, 1, 1, null,
6, 6, 6, 6, 6, 6, null,
7, 7, 7, 7, 7, 7, null
]);
expect(out[1].x).toEqual([
2, 2, 2, 2, 2, 2, null,
3, 3, 3, 3, 3, 3, null,
4, 4, 4, 4, 4, 4, null,
5, 5, 5, 5, 5, 5, null
]);

expect(out[2].x).toEqual([
0, 0, 0, 0, 0, 0,
3, 3, 3, 3, 3, 3
]);
expect(out[3].x).toEqual([
1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2
]);
});
});

describe('finance charts updates:', function() {
Expand Down