File Coverage

blib/lib/Finance/FITF/Writer.pm
Criterion Covered Total %
statement 11 13 84.6
branch 1 2 50.0
condition 1 3 33.3
subroutine 5 5 100.0
pod n/a
total 18 23 78.2


line stmt bran cond sub pod time code
1             package Finance::FITF::Writer;
2 3     3   16 use strict;
  3         4  
  3         99  
3 3     3   23 use warnings;
  3         6  
  3         161  
4             BEGIN {
5 3 50 33 3   404 eval "use Class::XSAccessor::Compat 'antlers'; 1" or
  3     3   3188  
  3         31755  
  3         34  
6             eval "use Class::Accessor::Fast 'antlers'; 1" or die $@;
7             }
8 3     3   4664 use DateTime;
  0            
  0            
9             use Carp qw(croak);
10              
11             extends 'Finance::FITF';
12              
13             has current_prices => ( is => 'rw' );
14              
15             has bars_written => (is => "rw", isa => "Int");
16              
17             has ticks_written => (is => "rw", isa => "Int");
18             has last_index => (is => "rw", isa => "Int");
19              
20             has bar_index => (is => "rw", isa => "Int");
21              
22             sub new {
23             my $class = shift;
24             my $self = $class->SUPER::new(@_);
25              
26             $self->ticks_written(0);
27             $self->bars_written(0);
28             $self->last_index(0);
29              
30             $self->bar_index(0);
31              
32             return $self;
33             }
34              
35             sub add_session {
36             my ($self, $start_offset, $end_offset) = @_;
37             my $date_start = $self->{date_start};
38              
39             $self->{bar_ts} ||= [];
40             if (scalar @{$self->header->{start}} >= 3) {
41             croak "FITF supports up to 3 sessions only.";
42             }
43             push @{$self->header->{start}}, $date_start + $start_offset;
44             push @{$self->header->{end}}, $date_start + $end_offset;
45             push @{$self->{bar_ts}},
46             map { $date_start + $start_offset + $_ * $self->{header}{bar_seconds} }
47             (1..($end_offset - $start_offset) / $self->{header}{bar_seconds});
48             $self->nbars( scalar @{$self->{bar_ts}} );
49             }
50              
51             sub push_bar {
52             my $self = shift;
53             my $ts = shift;
54             my $frame = shift;
55             my $pos = sysseek($self->{fh}, 0, 1);
56             seek $self->{fh}, $self->header_sz + ($self->{bars_written}++) * $self->bar_sz, 0;
57             syswrite $self->{fh}, $self->bar_fmt->format({ %$frame, index => $self->{last_index}});
58              
59             seek $self->{fh}, $pos, 0;
60             $self->last_index( $self->ticks_written );
61             }
62              
63             sub push_price {
64             my ($self, $ts, $price, $volume) = @_;
65             $price *= $self->{header}{divisor};
66             unless ($self->ticks_written) {
67             seek $self->{fh}, $self->header_sz + $self->nbars * $self->bar_sz, 0;
68             }
69              
70             die unless @{$self->{bar_ts}};
71             my $cp = $self->current_prices;
72             my $last = $cp && $cp->{close} || 0;
73             while ($ts >= $self->{bar_ts}[$self->{bar_index}]) {
74             $self->push_bar($self->{bar_ts}[$self->{bar_index}++],
75             $cp || { open => $last, high => $last, low => $last, close => $last });
76             $cp = undef;
77             }
78              
79             if ($cp) {
80             ++$cp->{ticks}; $cp->{volume} += $volume;
81             $cp->{close} = $price;
82             $cp->{high} = $price if $price > $cp->{high};
83             $cp->{low} = $price if $price < $cp->{low};
84             }
85             else {
86             $self->current_prices({ open => $price, close => $price,
87             volume => $volume, ticks => 1,
88             high => $price, low => $price });
89             }
90             my $offset = $ts - $self->{date_start};
91             my $offset_min = int($offset/60);
92             my $offset_msec = int(($offset - $offset_min*60 ) * 1000);
93             syswrite $self->{fh}, $self->tick_fmt->format({ offset_min => $offset_min,
94             offset_msec => $offset_msec,
95             price => $price,
96             volume => $volume,
97             });
98             ++$self->{ticks_written};
99              
100             }
101              
102             sub end {
103             my $self = shift;
104              
105             my $cp = $self->current_prices;
106             my $last = $cp && $cp->{close} || 0;
107             while ($self->{bar_index} < $self->{nbars}) {
108             $self->push_bar($self->{bar_ts}[$self->{bar_index}++],
109             $cp || { open => $last, high => $last, low => $last, close => $last });
110             $cp = undef;
111             }
112              
113             $self->header->{start}[1] ||= 0;
114             $self->header->{start}[2] ||= 0;
115             $self->header->{end}[1] ||= 0;
116             $self->header->{end}[2] ||= 0;
117              
118             $self->header->{records} = $self->{ticks_written};
119             seek $self->{fh}, 0 ,0;
120             syswrite $self->{fh}, $self->header_fmt->format($self->header);
121             }
122              
123             1;
124              
125             __END__