App-Chart
view release on metacpan or search on metacpan
lib/App/Chart/Series.pm view on Meta::CPAN
my $diff = CORE::abs ($highs->[$i] - $lows->[$i]);
if ($diff != 0) {
push @diffs, $diff;
}
}
}
}
# ENHANCE-ME: look at all parts of a multi-line like macd, bollinger,
# guppy, etc
# (if (= 2 (array-rank array))
# # macd, not quite right
# (set! array (make-shared-array-column array 0)))
# close to close ranges
{
my $prev;
foreach my $i ($lo .. $hi) {
my $value = $values->[$i];
if (! defined $value) { next; }
if (defined $prev) {
my $diff = CORE::abs ($value - $prev);
if ($diff != 0) {
push @diffs, $diff;
}
}
$prev = $value;
}
}
if (! @diffs) {
### no diffs for initial range: "$lo $hi"
my ($l, $h) = $self->range ($lo, $hi);
if (defined $l) {
# for just a single close value pretend 20% around the value
return $h * 0.8, $l / 0.8;
}
return;
}
# page will show 25x the median range
@diffs = sort {$a <=> $b} @diffs;
my $page = 25 * $diffs[CORE::int ($#diffs / 2)];
### initial page by: "25*median is $page"
# make page no more than twice upper,lower data range, so the
# data is not less than half the window
{ my ($l, $h) = $self->range ($lo, $hi);
if (defined $l) {
### series range: "$l to $h"
$page = min ($page, 2 * ($h - $l));
}
}
### shrink to use minimum half window: $page
# make page no smaller than last 1/2 of data, so that's visible
{ my ($l, $h) = $self->range (CORE::int (($lo + $hi) / 2), $hi);
if ($l) { $page = max ($page, $h - $l); }
}
### expand so last half data visible: $page
my ($l, $h);
my $accumulate = sub {
my ($value) = @_;
### accumulate: $value
if (! defined $value) { return 1; }
my $new_l = defined $l ? min ($value, $l) : $value;
my $new_h = defined $h ? max ($value, $h) : $value;
if ($new_h - $new_l <= $page) {
$l = $new_l;
$h = $new_h;
}
if (! defined $l) {
$l = $new_l;
$h = $new_l + $page;
}
return 0;
};
if ($latest) {
if (defined (my $quote_iso = $latest->{'quote_date'})) {
my $quote_t = $timebase->from_iso_floor ($quote_iso);
if ($quote_t >= $lo && $quote_t <= $hi) {
$accumulate->($latest->{'bid'});
$accumulate->($latest->{'offer'});
}
}
if (defined (my $last_iso = $latest->{'last_date'})) {
my $last_t = $timebase->from_iso_floor ($last_iso);
if ($last_t >= $lo && $last_t <= $hi) {
$accumulate->($latest->{'last'});
$accumulate->($latest->{'high'});
$accumulate->($latest->{'low'});
}
}
}
for (my $i = $hi; $i >= $lo; $i--) {
foreach my $value ($values->[$i], $highs->[$i], $lows->[$i]) {
$accumulate->($value);
}
}
my $extra = ($page - ($h - $l)) / 2;
$l -= $extra;
$h += $extra;
### initial range decided: "$l $h $self"
return ($l, $h);
}
# # don't go below `datatype-minimum' (if present), so long as
# # the actual data respects that minimum
# (and-let* ((minimum (datatype-minimum datatype))
# (actual-min (apply min-maybe lst-closes))
# ( (>= actual-min minimum)))
# (set! lower (max lower minimum)))
sub filled_low {
my ($self) = @_;
return $self->{'fill_low'};
}
sub filled_high {
( run in 1.076 second using v1.01-cache-2.11-cpan-5623c5533a1 )