[Banking-pm] Parsing FIX messages
Holzman, Benjamin
BHolzman at ise.com
Wed Dec 19 06:38:58 PST 2007
banking-pm-bounces+bholzman=iseoptions.com at pm.org wrote:
> Does anyone know if a module is available for parsing FIX
> messages? I didnt see any on cpan just wondering if anyone
> here did one internally?
Here's what I posted the last time this came up:
> I would have assumed that FIX is so large that any generic
> implementation of it is likely to be incomplete.
Yes and no, I think. My experience with FIX has been that the
application-level messages tend to be domain-specific, but the
protocol-level is quite stable. I have built FIX interfaces to
allow
order entry and market data for our parimutuel matching engine.
I did
it using the quickfix open source FIX engine (actually C++
libraries) to
handle the protocol and wrote a minimal C++ bridge that shuttles
FIX
application messages back and forth to a perl process over a
socket.
I then parse the FIX messages in perl with a custom FixMessage
base
class, run all of my application logic there, creating
FixMessage
objects as responses and then turn them back into a string to
send back
to the C++ bridge. I store the FIX messages as a hash mapping
the fix
tag number to the value. I then have accessor methods named
after the
mnemonic for each tag number. I actually just have a hash
(%tagMapping)
in my base class with the mapping and use an AUTOLOAD to create
the
accessors on demand. Parsing the FIX message is as simple as
this code:
sub fromString {
my $string = shift;
my($class, %tags);
foreach my $field (split /\001/, $string) {
my($tag, $value) = split /=/, $field, 2;
if ($tag == $tagMapping{'MsgType'}) {
$class = $msgType_2_class{$value};
next;
}
next if defined $isHeaderTag{$tag};
$tags{$tag} = $value;
}
return $class->new(\%tags);
}
I have sub-classes of FixMessage for each MsgType that I handle;
the
%msgType_2_class hash has the mapping.
Actually, there's an additional complication to handle repeating
groups;
the value of a repeating group is an array ref with one element
for each
instance of the group; because tag order in repeating groups
matters,
each instance is represented with an array consisting of tag
number/value pairs. Something like this: [ [ 42 => 'value', 165
=>
'another value' ], [ 42 => 'value2', 165 => 'yet another' ], ...
]. So
my actual fromString function has more logic to handle this.
Anyway, converting an object back to a string isn't too hard;
the only
tricky parts are including the body length in the header and
computing
the checksum for the trailer. My code looks like this:
sub toString {
my $this = shift;
my $header = "8=FIX.4.4\0019=";
my $body = join("\001", "35=$class_2_msgType{ref
$this}",
map _toString($_, $this->{$_}), keys
%$this) .
"\001";
my $msg = $header . length($body) . "\001$body";
my $cksum = 0;
$cksum += ord($_) for split //, $msg;
$cksum = sprintf "%03d", $cksum %256;
return $msg . "10=$cksum\001";
}
sub _toString {
my($key, $value) = @_;
if (ref $value eq 'ARRAY') {
return unless @$value;
return join "\001",
"$key=" . @$value,
map { my $val = $_;
my @data;
for (my $i = 0; $i < $#$val;
$i+=2) {
push @data,
"$tagMapping{$val->[$i]}="
.
$val->[$i +
1];
}
@data;
} @$value;
} elsif ($value ne '') {
return "$key=$value";
} else {
return;
}
}
That's pretty much my whole FIX message base class. The
constructor
allows objects to be constructed from a string or from tag
mnemonic =>
value pairs.
I don't know if this helps you, but at least you see how simple
it is to
handle parsing and generation of FIX messages.
Benjamin Holzman
More information about the Banking-pm
mailing list