App-Chart

 view release on metacpan or  search on metacpan

lib/App/Chart/Series/Derived/ZigZag.pm  view on Meta::CPAN

  $self->{'filled'} = 1;

  my $parent = $self->{'parent'};
  my ($percent, $closes_flag) = @{$self->{'parameters'}};

  $hi = $self->hi;
  $parent->fill (0, $hi);
  my $p = $parent->values_array;
  my $ph = $closes_flag ? $p : $parent->array('highs');
  my $pl = $closes_flag ? $p : $parent->array('lows');

  my $s = $self->values_array;

  my $factor_increase = 1 + $percent / 100;
  my $factor_decrease = 1 / $factor_increase;
  my $direction = sub {};
  my $extreme;
  my $target;
  my $extreme_pos;

  my ($rising, $falling);
  $rising = sub {
    my ($pos, $high, $low) = @_;
    if (! defined $extreme || $high > $extreme) {
      $extreme = $high;
      $extreme_pos = $pos;
      $target = $extreme * $factor_decrease;
      return;
    }
    if ($low <= $target) {
      my $ret_pos = $extreme_pos;
      my $ret_val = $extreme;
      $direction = $falling;
      $extreme = $low;
      $extreme_pos = $pos;
      $target = $extreme * $factor_increase;
      return $ret_pos, $ret_val;
    }
    return;
  };
  $falling = sub {
    my ($pos, $high, $low) = @_;
    if (! defined $extreme || $low < $extreme) {
      $extreme = $low;
      $extreme_pos = $pos;
      $target = $extreme * $factor_increase;
      return;
    }
    if ($low >= $target) {
      my $ret_pos = $extreme_pos;
      my $ret_val = $extreme;
      $direction = $rising;
      $extreme = $high;
      $extreme_pos = $pos;
      $target = $extreme * $factor_decrease;
      return $ret_pos, $ret_val;
    }
    return;
  };

  # decide initial direction rising or falling
  {
    my $high;
    my $high_pos;
    my $low;
    my $low_pos;

    foreach my $i (0 .. $hi) {
      my $value = $p->[$i] // next;
      my $this_high = $ph->[$i] // $value;
      my $this_low  = $pl->[$i] // $value;

      if (! defined $high || $this_high > $high) {
        $high = $this_high;
        $high_pos = $i;
      }
      if (! defined $low || $this_low < $low) {
        $low = $this_low;
        $low_pos = $i;
      }

      if ($high >= $low * $factor_increase) {
        if ($high_pos > $low_pos) {
          $direction = $rising;
          $s->[0] = $s->[$low_pos] = $low;
          last;
        }
        if ($low_pos >= $high_pos) {
          $direction = $falling;
          $s->[0] = $s->[$high_pos] = $high;
          last;
        }
      }
    }
  }

  foreach my $i ($lo .. $hi) {
    my $value = $p->[$i] // next;

    my ($pos, $val) = $direction->($i,
                                   $ph->[$i] // $value,
                                   $pl->[$i] // $value);
    if (defined $pos) {
      $s->[$pos] = $val;
    }
  }
  if ($extreme_pos) {
    $s->[$extreme_pos] = $s->[$hi] = $extreme;
  }
}


1;
__END__

# =head1 NAME
# 
# App::Chart::Series::Derived::ZigZag -- zig zag indicator
# 
# =head1 SYNOPSIS
# 



( run in 2.117 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )