Skip to content

Commit

Permalink
fix_parser: parse messages
Browse files Browse the repository at this point in the history
  • Loading branch information
SamDanielThangarajan committed Oct 8, 2024
1 parent 159277d commit 88bfa11
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 11 deletions.
10 changes: 9 additions & 1 deletion src/nasdaq_protocols/fix/parser/definitions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import Any

import attr
import attrs

from nasdaq_protocols.common import TypeDefinition
Expand All @@ -13,6 +12,7 @@
'Field',
'Group',
'Component',
'Message',
'Definitions'
]

Expand Down Expand Up @@ -51,9 +51,17 @@ class Component(EntryContainer):
name: str = attrs.field(kw_only=True)


@attrs.define
class Message(EntryContainer):
tag: str
name: str
category: str


@attrs.define
class Definitions:
fields: dict[str, FieldDef] = attrs.field(init=False, factory=dict)
components: dict[str, Component] = attrs.field(kw_only=True, factory=dict)
header: EntryContainer = attrs.field(kw_only=True, factory=EntryContainer)
trailer: EntryContainer = attrs.field(kw_only=True, factory=EntryContainer)
messages: list[Message] = attrs.field(kw_only=True, factory=list)
20 changes: 18 additions & 2 deletions src/nasdaq_protocols/fix/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
FieldDef,
Component,
Field,
EntryContainer, Group
EntryContainer,
Group,
Message
)
from .version_types import (
get_supported_types,
Expand Down Expand Up @@ -36,7 +38,8 @@ def parse(file: str) -> Definitions:
'fields': partial(_handle_fields, get_supported_types(version)),
'components': _handle_components,
'header': _handle_header,
'trailer': _handle_trailer
'trailer': _handle_trailer,
'messages': _handle_messages
}
definitions = Definitions()

Expand All @@ -58,6 +61,19 @@ def _handle_trailer(definitions: Definitions, root, element) -> None:
_handle_entry(definitions, definitions.trailer, root, entry)


def _handle_messages(definitions: Definitions, root, element) -> None:
LOG.debug('parsing <messages>')
for msg in element:
message = Message(
tag=msg.get('msgtype'),
name=msg.get('name'),
category=msg.get('msgcat')
)
for entry in msg:
_handle_entry(definitions, message, root, entry)
definitions.messages.append(message)


def _handle_fields(types: SupportedTypes,
definitions: Definitions,
_root,
Expand Down
39 changes: 31 additions & 8 deletions tests/test_fix_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,6 @@ def assert_component(name, *fields_defn):
assert field.required == field_defn[1]
assert field.field.name == field_defn[0]

def assert_group(name, group, *fields_defn):
assert name == group.name
assert len(group.entries) == len(fields_defn)
for i, field_defn in enumerate(fields_defn):
field = group.entries[i]
assert field.field.name == field_defn[0]
assert field.required == field_defn[1]

assert_component('InstrmtLegGrp', ('PossResend', False), ('QuoteStatus', False))
assert_component('Instrument', ('PossResend', True), ('QuoteStatus', True))
assert_component('InstrumentExtensionNoInstrAttribSubGroup', ('PossResend', True), ('QuoteStatus', False))
Expand Down Expand Up @@ -162,3 +154,34 @@ def test__fix_parser__trailer_is_parsed(fix_44_definitions):
assert fix_44_definitions.trailer.entries[0].field.tag == '10'
assert fix_44_definitions.trailer.entries[0].field.type == FixString
assert fix_44_definitions.trailer.entries[0].required


def test__fix_parser__message_is_parsed(fix_44_definitions):
assert len(fix_44_definitions.messages) == 1
message = fix_44_definitions.messages[0]

assert message.tag == 'A'
assert message.name == 'Logon'
assert message.category == 'Session'

assert len(message.entries) == 4
assert message.entries[0].field.name == 'HeartBtInt'
assert message.entries[0].required
assert message.entries[1].field.name == 'PossResend'
assert not message.entries[1].required
assert message.entries[2].field.name == 'QuoteStatus'
assert not message.entries[2].required
assert_group(
'Instrument_Group',
message.entries[3],
('PossResend', True), ('QuoteStatus', True)
)


def assert_group(name, group, *fields_defn):
assert name == group.name
assert len(group.entries) == len(fields_defn)
for i, field_defn in enumerate(fields_defn):
field = group.entries[i]
assert field.field.name == field_defn[0]
assert field.required == field_defn[1]
10 changes: 10 additions & 0 deletions tests/testdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,15 @@
<trailer>
<field name="CheckSum" required="Y"/>
</trailer>
<messages>
<message name="Logon" msgtype="A" msgcat="Session">
<field name="HeartBtInt" required="Y"/>
<component name="InstrmtLegGrp" required="Y"/>
<group name="Instrument_Group" required="Y">
<component name="Instrument" required="Y"/>
</group>
</message>
</messages>
<components>
<component name="InstrmtLegGrp">
<field name="PossResend" required="N"/>
Expand All @@ -223,6 +232,7 @@
</component>
</components>
<fields>
<field number="108" name="HeartBtInt" type="INT" />
<field number="10" name="CheckSum" type="STRING" />
<field number="8" name="BeginString" type="STRING" />
<field number="9" name="BodyLength" type="LENGTH" />
Expand Down

0 comments on commit 88bfa11

Please sign in to comment.