Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/basiliscos/erl-erlyfix

FIX (Foreign Information Exchange) protocol implementation for Erlang
https://github.com/basiliscos/erl-erlyfix

elixir erlang fix protocol

Last synced: 7 days ago
JSON representation

FIX (Foreign Information Exchange) protocol implementation for Erlang

Awesome Lists containing this project

README

        

erlyfix
=====

[![Travis](https://img.shields.io/travis/basiliscos/erl-erlyfix.svg)](https://travis-ci.org/basiliscos/erl-erlyfix)
[![Hex.pm Version](http://img.shields.io/hexpm/v/erlyfix.svg?style=flat)](https://hex.pm/packages/erlyfix)
[![codecov](https://codecov.io/gh/basiliscos/erl-erlyfix/badge.svg)](https://codecov.io/gh/basiliscos/erl-erlyfix)
[![license](https://img.shields.io/github/license/basiliscos/erl-erlyfix.svg)](https://github.com/basiliscos/erl-erlyfix/blob/master/LICENSE)

FIX (Foreign Information eXchange) protocol implementation in erlang.

Versions
-----

Versions supported: R18 and up

Description
-----

This FIX protocol implementation based on XML FIX-protocol definitions provided by [quickfixengine.org](http://quickfixengine.org/). It is possible to have own/proprietary extenstion XML, which extends the quickfix's definition.

The library provides only serialization, deserialization and basic validation of FIX-messages and does not provides network layer. You have to build your own FIX-client/server.

Synopsis
-----

```erlang
% loading protocol
{ok, Protocol} = erlyfix_protocol:load("test/FIX44.xml"),
{ok, Protocol} = erlyfix_protocol:load("test/FIX44.xml", "test/extension-sample.xml"),

% message serialization
{ok, IoList} = erlyfix_protocol:serialize(Protocol, 'MarketDataSnapshotFullRefresh', [
{'SenderCompID', <<"me">>}, % field
{'TargetCompID', <<"you">>},
{'MsgSeqNum', 1},
{'SendingTime', <<"20171109-16:19:07.541">>},
{'Instrument', [{'Symbol', <<"EURCHF">>}] }, % component
{'MDReqID', <<"31955:1510225047.01637:EURCHF">>},
{'MDFullGrp', [{'NoMDEntries', [ % group
[{'MDEntryType', <<"BID">>}]
]}]}
]),

% message deserialization
BinaryMessage = <<"8=FIX.4.4", 1, "9=102", 1, "35=A", 1,
"212=1", 1, "213=", 1, 1,"49=me", 1, "56=you", 1,
"34=1", 1, "52=20090107-18:15:16", 1, "98=0", 1, "108=60", 1, "384=2", 1,
"372=abc", 1, "385=S", 1, "372=def", 1, "385=R", 1, "10=232", 1
>>,
{ok, 'Logon', TagsMarkup, <<>>} = erlyfix_parser:parse(BinaryMessage, Protocol),

```

The fields `BeginString`, `BodyLength`, `MsgType` and `CheckSum` are managed by the library. They **should not** be provided for messages serialization.

TagsMarkup is flat list of tags uplifted to fields/groups/components. It is expected to be processed by `lists:foldl/3`. Tagsmarkup is something like that:

```erlang
[
{start, header, undefined},
{field, 'BeginString', F_BeginString, <<"FIX.4.4">>},
{field, 'BodyLength', F_BodyLength, 102},
{field, 'MsgType', F_MsgType, <<"A">>},
{field, 'XmlDataLen', F_XmlDataLen, <<"1">>},
{field, 'XmlData', F_XmlData, <<1>>},
{field, 'SenderCompID', F_SenderCompID, <<"me">>},
{field, 'TargetCompID',F_TargetCompID, <<"you">>},
{field, 'MsgSeqNum', F_MsgSeqNum, <<"1">>},
{field, 'SendingTime', F_SendingTime, <<"20090107-18:15:16">>},
{finish,header,undefined},
{start,body,undefined},
{field, 'EncryptMethod', F_EncryptMethod, <<"0">>},
{field, 'HeartBtInt', F_HeartBtInt, <<"60">>},
{start,group,{'NoMsgTypes',2}},
{field, 'RefMsgType', F_RefMsgType, <<"abc">>},
{field, 'MsgDirection', F_MsgDirection, << "S">>},
{field, 'RefMsgType', F_RefMsgType, <<"def">>},
{field, 'MsgDirection', F_MsgDirection, <<"R">>},
{finish,group,{'NoMsgTypes',2}},
{finish,body,undefined},
{start,trailer,undefined},
{field, 'CheckSum', F_CheckSum, 232},
{finish,trailer,undefined}
],
```

where `F_*` is opaque field structure. Please note, that identations are for humans-only, the list itself is flat

Here is an example of folding tags markup:

```erlang

-record(quote, {
price,
volume,
source
}).
-record(tick, {
symbol,
bid,
ask
}).
...

{ok, IoList} = erlyfix_protocol:serialize(Protocol, 'MarketDataSnapshotFullRefresh', [
{'SenderCompID', <<"me">>},
{'TargetCompID', <<"you">>},
{'MsgSeqNum', 1},
{'SendingTime', <<"20171109-16:19:07.541">>},
{'Instrument', [{'Symbol', <<"EURCHF">>}] },
{'MDReqID', <<"31955:1510225047.01637:EURCHF">>},
{'MDFullGrp', [{'NoMDEntries', [
[{'MDEntryType', <<"BID">>}, {'MDEntryPx', <<"1.07509">>},
{'MDEntrySize', <<"200000">>}, {'QuoteCondition', <<"OPEN">>},
{'MDEntryOriginator', <<"PromoXM">>, {'QuoteEntryID', <<"82837831">>}}],
[{'MDEntryType', <<"OFFER">>}, {'MDEntryPx', <<"1.07539">>},
{'MDEntrySize', <<"100000">>}, {'QuoteCondition', <<"OPEN">>},
{'MDEntryOriginator', <<"PromoXM1">>, {'QuoteEntryID', <<"82837832">>}}]
]}]}
]),
Msg = iolist_to_binary(IoList),
{ok, 'MarketDataSnapshotFullRefresh', Markup, <<>>} = erlyfix_parser:parse(Msg, Protocol),

M2Q = fun(M) ->
#quote{
price = maps:get(price, M),
volume = maps:get(volume, M),
source = maps:get(source, M)
}
end,

F = fun(E, {Result, Stack} = Acc ) ->
case E of
{field, 'Symbol', _F, V} -> {ok, [ {symbol, V} | Stack ]};
{field, 'MDEntryType', F, V} ->
case erlyfix_fields:as_label(V, F) of
<<"BID">> -> {ok, [{bid, #{} } | Stack]};
<<"OFFER">> -> {ok, [{ask, #{}} | Stack]}
end;
{field, 'MDEntryPx', _F, V} ->
[{Type, Map0} | T] = Stack,
Price = binary_to_float(V),
{ok, [ {Type, Map0#{price => Price} } | T ] };
{field, 'MDEntrySize', _F, V} ->
[{Type, Map0} | T] = Stack,
Volume = binary_to_integer(V),
{ok, [ {Type, Map0#{volume => Volume} } | T ] };
{field, 'MDEntryOriginator', _F, V} ->
[{Type, Map0} | T] = Stack,
{ok, [ {Type, Map0#{source => V} } | T ] };
{start,group,{'NoMDEntries',Count}} ->
case Count of
2 -> {ok, [{group, 'NoMsgTypes' } | Stack]};
_ -> {error, Stack}
end;
{finish,group, {'NoMDEntries',2}} ->
[E1, E2, {group, 'NoMsgTypes' } | T] = Stack
{T1, M1} = E1,
Q1 = M2Q(M1),
{T2, M2} = E2,
Q2 = M2Q(M2),
case {T1, T2} of
{bid, ask} -> {ok, [Q1, Q2 | T]};
{ask, bid} -> {ok, [Q2, Q1 | T]}
end;
{finish,trailer,_} ->
case Result of
ok ->
[Bid, Ask, {symbol, Symbol}] = Stack,
Tick = #tick{ bid = Bid, ask = Ask, symbol = Symbol },
{ok, Tick};
_ -> Acc
end;
_ -> Acc
end
end,
{ok, Tick} = lists:foldl(F, {ok, []}, Markup),

```

It is possible to map field binary value to human-readable binary, i.e. when `F` points to `MDEntryType` field, and `V` contains `<<"1">>`, it is possible to map it to binary`<>`

```erlang
Label = erlyfix_fields:as_label(V, F).
```

(It is not possible to use atoms here, as the string description in XML-specification exceed possible atom length in Erlang).

In general it is assumed that you should use this library with tigth cooperation with XML-specifictions.

Build
-----

$ rebar3 compile

License
-----

Apache 2

See also
-----

https://github.com/maxlapshin/fix - FIX-client implementation as OTP-application, includes network layer.