File Coverage

blib/lib/FIX/Parser/FIX44.pm
Criterion Covered Total %
statement 56 98 57.1
branch 29 68 42.6
condition 6 33 18.1
subroutine 7 8 87.5
pod 0 4 0.0
total 98 211 46.4


line stmt bran cond sub pod time code
1             package FIX::Parser::FIX44;
2 2     2   196803 use 5.010;
  2         5  
3 2     2   6 use strict;
  2         3  
  2         27  
4 2     2   5 use warnings;
  2         3  
  2         39  
5 2     2   835 use POSIX qw(strftime);
  2         9587  
  2         8  
6              
7             our $VERSION = '0.02'; ## VERSION
8              
9             =for Pod::Coverage new add make_message parse_message
10              
11             =cut
12              
13             sub new {
14 2     2 0 266 my ($class) = @_;
15 2         10 return bless {_buf => ''}, $class;
16             }
17              
18             sub add {
19 3     3 0 9818 my ($self, $data) = @_;
20 3         18 $self->{_buf} .= $data;
21 3         5 my @msgs;
22              
23 3 50       10 if ($self->{_len}) {
24 0 0       0 return if $self->{_len} > length $self->{_buf};
25 0         0 push @msgs, parse_message(substr $self->{_buf}, 0, $self->{_len}, '');
26 0         0 delete $self->{_len};
27             }
28              
29 3         26 while ($self->{_buf} =~ s/^8=FIX.4.4\x{01}9=([0-9]+)\x{01}//) {
30 4         14 $self->{_len} = $1 + 7;
31 4 100       19 return @msgs if $self->{_len} > length $self->{_buf};
32 3         12 push @msgs, parse_message(substr $self->{_buf}, 0, $self->{_len}, '');
33 3         13 delete $self->{_len};
34             }
35              
36 2 50 33     7 if (length $self->{_buf} > 14
37             and $self->{_buf} !~ /^8=FIX.4.4\x{01}9=[0-9]/)
38             {
39 0         0 die "Invalid FIX message header: $self->{_buf}";
40             }
41              
42 2         20 return @msgs;
43             }
44              
45             sub parse_message {
46 3     3 0 7 my ($fix) = @_;
47 3         15 my @tags = map { [split /=/] } split /\x{01}/, $fix;
  36         92  
48 3 50       14 die "Message doesn't start with MsgType tag: $fix" unless $tags[0][0] == 35;
49              
50 3         5 my $message;
51 3 50       8 if ($tags[0][1] eq 'W') {
    0          
    0          
    0          
    0          
    0          
52 3         8 $message->{msg_type} = 'W';
53 3   66     17 while (@tags and $tags[0][0] != 10) {
54 21         24 my $tag = shift @tags;
55              
56 21         13 my $symbol;
57              
58 21 100       81 if ($tag->[0] == 52) {
    100          
    100          
59 3         14 $message->{msg_datetime} = $tag->[1];
60             } elsif ($tag->[0] == 55) {
61 3         11 $message->{symbol} = $tag->[1];
62             #$symbol = $tag->[1];
63             } elsif ($tag->[0] == 268) {
64 3         4 my ($date, $time);
65              
66 3         11 for (1 .. $tag->[1]) {
67 3         4 my ($price, $bprice, $aprice, $type);
68              
69 3         4 while (1) {
70 15         15 my $tag = shift @tags;
71 15 100 33     55 if ($tag->[0] == 269) {
    50 33        
    100          
    50          
    50          
    50          
    50          
72 6 0       14 $type =
    50          
    100          
73             $tag->[1] eq '0' ? 'bid'
74             : $tag->[1] eq '1' ? 'ask'
75             : $tag->[1] eq 'H' ? 'mid'
76             : $tag->[1];
77             } elsif ($tag->[0] == 55) {
78 0 0 0     0 die "symbols in different MDEntries do not match: $fix"
79             if $symbol and $symbol ne $tag->[1];
80 0         0 $symbol = $tag->[1];
81             } elsif ($tag->[0] == 270) {
82 6         9 $price = $tag->[1];
83 6 100       11 if ($type eq 'bid') { $bprice = $price; }
  3         4  
84 6 100       10 if ($type eq 'ask') { $aprice = $price; }
  3         6  
85             } elsif ($tag->[0] == 272) {
86 0 0 0     0 die "dates in differend MDEntries do not match: $fix"
87             if $date and $date ne $tag->[1];
88 0         0 $date = $tag->[1];
89             } elsif ($tag->[0] == 273) {
90 0 0 0     0 die "times in differend MDEntries do not match: $fix"
91             if $time and $time ne $tag->[1];
92 0         0 $time = $tag->[1];
93             } elsif ($tag->[0] == 167 and $tag->[1] ne 'FXSPOT') {
94 0         0 die "expected FXSPOT in MDEntries but found $tag->[1]: $fix";
95             } elsif ($tag->[0] == 279 or $tag->[0] == 10) {
96 3         5 last;
97             }
98              
99             #end of while loop
100             }
101              
102 3 50 33     12 die "MDEntry doesn't have price or type: $fix"
103             unless defined $price and $type;
104 3         5 $message->{bid} = $bprice;
105 3         5 $message->{ask} = $aprice;
106              
107 3         4 last;
108             }
109              
110 3         8 $message->{datetime} = $message->{msg_datetime};
111              
112             }
113              
114             }
115              
116             } elsif ($tags[0][1] eq '0') {
117              
118             # Heartbeat
119 0         0 $message->{msg_type} = '0';
120             } elsif ($tags[0][1] eq '1') {
121              
122             # Test request
123 0         0 $message->{msg_type} = '1';
124 0   0     0 while (@tags and $tags[0][0] != 10) {
125 0         0 my $tag = shift @tags;
126 0 0       0 if ($tag->[0] == 112) {
127 0         0 $message->{test_req_id} = $tag->[1];
128 0         0 last;
129             }
130             }
131             } elsif ($tags[0][1] eq '2') {
132              
133             # Resend request
134 0         0 $message->{msg_type} = '2';
135 0   0     0 while (@tags and $tags[0][0] != 10) {
136 0         0 my $tag = shift @tags;
137 0 0       0 if ($tag->[0] == 7) {
    0          
138 0         0 $message->{begin_seq_no} = $tag->[1];
139             } elsif ($tag->[0] == 16) {
140 0         0 $message->{end_seq_no} = $tag->[1];
141             }
142             }
143             } elsif ($tags[0][1] eq 'A') {
144              
145             # Logon message
146 0         0 $message->{msg_type} = 'A';
147             } elsif ($tags[0][1] eq '5') {
148              
149             # Logout message
150 0         0 $message->{msg_type} = '5';
151 0   0     0 while (@tags and $tags[0][0] != 10) {
152 0         0 my $tag = shift @tags;
153 0 0       0 if ($tag->[0] == 58) {
154 0         0 $message->{logout_message} = $tag->[1];
155             }
156             }
157             } else {
158 0         0 die "Don't know how to parse message of type $tags[0][1]: $fix";
159             }
160              
161 3         7 return $message;
162             }
163              
164             sub make_message {
165 0     0 0   my ($self, $type, $sender_id, $target_id, @tags) = @_;
166 0           $self->{_seq}++;
167 0           my $ts = strftime("%Y%m%d-%H:%M:%S.000", gmtime);
168 0           my $msg = join "\x{01}", "35=$type", "49=$sender_id", "52=$ts", "56=$target_id", "34=$self->{_seq}", @tags, '';
169 0           my $len = length $msg;
170 0           $msg = join "\x{01}", '8=FIX.4.4', "9=$len", $msg;
171 0           my $sum = 0;
172 0           $sum += ord $_ for split //, $msg;
173 0           $sum %= 256;
174 0           $msg .= '10=' . sprintf("%03d\x{01}", $sum);
175              
176 0           return $msg;
177             }
178              
179             1;