[Banking-pm] FIX: Financial Information eXchange
Holzman, Benjamin
BHolzman at iseoptions.com
Sat Dec 2 07:51:18 PST 2006
> 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