Сразу оговорюсь - тематика будет интересна не всем, просто хотел показать один из аспектов использования perl.
Если Вы занимаетесь разработкой ПО для торговых систем, иногда может возникнуть необходимость анализа логов FIX протокола.
Причин может быть несколько, самая простая - определить, насколько соответствуют показатели разрабатываемого приложения действительности. То-есть: Ваша база данных Вам говорит одно, мол, отправлено было столько-то ордеров, открыто столько-то, закрыто столько-то, а на самом деле все может быть иначе.
Если показатели, полученные при анализе базы данных будут отличаться от показателей, полученных при анализе логов FIX engine - пора бить тревогу. Кто-то врет, и скорее всего этот кто-то - Ваше приложение.
Лог-файл FIX сервера обычно представляет собой набор текстовых строк. Выбор инструмента для их разбора пришел сразу - конечно же, perl:). Одна из проблем состоит в том, что поддержки FIX для perl просто нет. Существует большое количество серверов, написанных на всевозможных языках(в основном, это Java или C++), среди которых можно встретить даже Ruby и Python(я так понимаю, дань моде), но горячо любимого нами Perl нет. Ладно, не проблема. Раз нет библиотек для работы с самим протоколом, написать разбор текстовых данных мы сможем.
Была задача анализа файла торгов с FXCM. Основное приложение с нашей стороны было написано на Java с использованием движка QuickFIX. Полученный данные торгов складируются в отдельном файле, растущем достаточно быстро. На момент запуска тестового скрипта размер лог файла превышал 1.5 GB. Достаточно быстро был разработан скрипт, пробегающийся по всем строкам, выбирающий все необходимые типы сообщения и анализирующий состояния ордеров.
Результат(отчет объема продаж за день) выводится на печать с помощью format(вот и живой пример к теме http://kiev.pm.org/node/215 :)).
Сам скрипт далеко не идеален ввиду жесткой привязки переменных, да и есть прямая зависимость от FXCM(они несколько расширили стандартный протокол своими типами переменных), но общая идея более-менее ясна.
В любом случае, скрипт сделал свою работу, и сделал ее быстро. Я думаю, что выбор perl в качестве инструмента анализа был более чем оправдан ввиду скорости разработки и общей удобочитаемости приложения. Выводы - я был очень доволен.
Код скрипта:
#!/usr/bin/perl -w use strict; use Order; my $log_file = $ARGV[0]; my %data; open (LOG, $log_file) or die "$!\n"; while() { $_ =~ /35=(\w\D)/; if($1 eq "AP") { #We are looking for PositionReport message only process_position_report(); } } print_volume_report(); sub process_position_report { my @tags = split(/\x01/); my %message; foreach (@tags) { my ($key, $value) = split(/=/); $message{$key} = $value; } #Get transaction date my $date = (split(/-/,$message{60}))[0]; my $symbols = $data{$date}; unless(defined($symbols)){ $symbols = {}; $data{$date} = $symbols; } #Get Symbol name my $symbol = $message{55}; my $orders = $symbols->{$symbol}; unless(defined($orders)) { $orders = {}; $symbols->{$symbol} = $orders; } #Get ClOrdID my $order_id = $message{11}; #Get FXCMPosID my $external_id = $message{9041}; my ($size, $side); #Here we get Size and Side of Position if(defined($message{705})) { $size = $message{705}; $side = "S"; }else{ $size = $message{704}; $side = "B"; } my $order = $orders->{$external_id}; unless(defined($order)) { $order = Order->new(); $order->id($order_id); $order->symbol($symbol); $order->open_date($message{60}); $order->external_id($external_id); $order->size($size); $order->side($side); $orders->{$external_id} = $order; }else{ if(defined($message{9052})) { $order->size(2*$order->size); } } } sub print_volume_report { my %volume_data; my ($symbol, $symbol_volume); my $total_volume = 0; format = @<<<<<<<< @>>>>>>>>>> $symbol, $symbol_volume . foreach(keys %data) { my $volume_symbols = {}; $volume_data{$_} = $volume_symbols; my $symbols = $data{$_}; foreach my $symbol (keys (%$symbols)) { my $volume = 0; my $orders = $symbols->{$symbol}; foreach(keys(%$orders)) { $volume+=$orders->{$_}->size; } $volume_symbols->{$symbol} = $volume; } } foreach(keys %volume_data) { my $day_volume = 0; print $_,"\n"; my $symbols = $volume_data{$_}; foreach(keys(%$symbols)) { $symbol = $_; $day_volume+=$symbols->{$_}; $symbol_volume = format_size($symbols->{$_}); write; } #Print footer. Day volume $symbol = "Total"; $symbol_volume = format_size($day_volume); write; } } sub format_size { my $size = shift; if($size < 999999) { return ($size/1000)."K"; }elsif($size < 999999999) { return ($size / 1000000)."M"; }else{ return ($size / 1000000000)."B"; } }
Код используемого класса Order:
package Order; use strict; use base 'Class::Accessor::Fast'; sub new { my $class = shift; my $self = bless {}, $class; return $self; } __PACKAGE__->mk_accessors(qw/ id symbol side size open_date open_price close_date close_price type external_id /); 1;
Пример вывода:
20080813 EUR/AUD 1.9M EUR/CHF 1.5M USD/CHF 2.1M EUR/USD 252.6M EUR/GBP 2M NZD/USD 2.7M EUR/NZD 2.9M AUD/USD 1.4M EUR/JPY 79.1M USD/JPY 32.9M USD/SGD 4.3M USD/HKD 1.3M USD/CAD 8.2M EUR/CAD 2.4M CAD/JPY 4.7M GBP/USD 2.4M Total 402.4M 20080811 CHF/JPY 1.2M EUR/CHF 26.7M EUR/USD 1.0603B GBP/JPY 1M NZD/USD 26.7M USD/SGD 25.9M USD/CAD 26.8M EUR/CAD 24.2M CAD/JPY 1.2M EUR/AUD 28.3M USD/CHF 30M EUR/GBP 24.8M EUR/NZD 26.1M EUR/JPY 691.9M AUD/USD 24.1M NZD/JPY 1.6M USD/JPY 461.5M USD/HKD 17.1M GBP/USD 29.9M Total 2.5293B 20080812 CHF/JPY 21.1M EUR/CHF 27M EUR/USD 1.3162B GBP/JPY 19.2M NZD/USD 24.9M USD/SGD 29.6M USD/CAD 47.9M CAD/JPY 32.3M EUR/CAD 37.3M EUR/AUD 33.3M USD/CHF 29.2M EUR/GBP 36.4M EUR/NZD 38.7M AUD/USD 24.6M EUR/JPY 332.1M NZD/JPY 22.7M USD/JPY 211.6M USD/HKD 14.3M GBP/USD 25.8M Total 2.3242B

килобайты, мегабайты...
Когда я тоже выводил размер в килобайтах и мегабайтах в зависимости от размера, то делил на 1024 и 1024**1024 соответственно :)
Это не
Это не килобайты:) Это тысячи и миллионы долларов:)
"Настоящий
"Настоящий программист считает что в килограмме 1024 грамма" :)