Ejemplo n.º 1
0
    def on_timer_tick_received(self):
        """ This is the Twisted timer callback when the heartbeat interval
            has elapsed.
        """
        if self.heartbeat <= 0:
            return

        now = datetime.datetime.now()

        # Have we received a testrequest response before we timed out?
        if (self._testrequest_id is not None and
                (now - self._testrequest_time).seconds > 2*self.heartbeat):
            raise FIXTimeoutError('testrequest response timeout')

        # if heartbeat seconds/2 have elapsed since the last time
        # a message was sent, send a heartbeat
        if (now - self._last_send_time).seconds > (self.heartbeat/2):
            self.transport.send_message(
                FIXMessage(source=[(35, FIX.HEARTBEAT),
                                   (49, self.link_config['sender_compid']),
                                   (56, self.link_config['target_compid'])]))

        # if heartbeat seconds + "some transmission time" have elapsed
        # since a message was received, send a TestRequest
        if (now - self._last_received_time).seconds > self.heartbeat:
            testrequest_id = "TR{0}".format(format_time(now))
            self._testrequest_time = now
            self._testrequest_id = testrequest_id
            self.transport.send_message(
                FIXMessage(source=[(35, FIX.TEST_REQUEST),
                                   (112, testrequest_id),
                                   (49, self.link_config['sender_compid']),
                                   (56, self.link_config['target_compid'])]))
Ejemplo n.º 2
0
    def test_to_binary(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[34] = 177
        mess[52] = '20090107-18:15:16'
        mess[98] = 0
        mess[108] = 30
        mess[10] = '---'

        data = mess.to_binary()

        # BodyLength(9) and Checksum(10) should be updated after
        # the to_binary() was called.
        self.assertEquals('65', mess[9])
        self.assertEquals('062', mess[10])

        self.assertEquals(to_fix('8=FIX.4.2',
                                 '9=65',
                                 '35=A',
                                 '49=SERVER',
                                 '56=CLIENT',
                                 '34=177',
                                 '52=20090107-18:15:16',
                                 '98=0',
                                 '108=30',
                                 '10=062'),
                          data)
Ejemplo n.º 3
0
 def test_nested_group_from_list(self):
     """ Call to_binary() on a nested grouped message from a list """
     # As its difficult to test this, convert the (unordered)
     # dict into an OrderedDict() before inserting (sorting by tag).
     # This makes it easier to do the comparison.
     mess = FIXMessage(header_fields=[8, 9],
                       source=to_ordered_dict([(8, 'FIX.4.2'),
                                               (100, [{
                                                   101: 'abc',
                                                   102: 'def'
                                               }, {
                                                   101: 'ghi',
                                                   103: 'jkl'
                                               }, {
                                                   101:
                                                   'mno',
                                                   200: [{
                                                       201: 'aaa',
                                                       202: 'bbb'
                                                   }]
                                               }]), (99, 'X')]))
     self.assertEquals(
         to_fix('8=FIX.4.2', '9=73', '100=3', '101=abc', '102=def',
                '101=ghi', '103=jkl', '101=mno', '200=1', '201=aaa',
                '202=bbb', '99=X', '10=034'), mess.to_binary())
Ejemplo n.º 4
0
    def test_header_fields(self):
        mess = FIXMessage()
        self.assertEquals(5, len(mess))

        self.assertTrue(8 in mess)
        self.assertTrue(9 in mess)
        self.assertTrue(35 in mess)
        self.assertTrue(49 in mess)
        self.assertTrue(56 in mess)

        items = [(k, v) for k, v in mess.items()]
        # 8,9 are required to be first and second fields, respectively
        self.assertEquals(8, items[0][0])
        self.assertEquals(9, items[1][0])

        # Custom required fields
        mess = FIXMessage(header_fields=[1024, 8, 9])
        self.assertTrue(8 in mess)
        self.assertTrue(9 in mess)
        self.assertTrue(35 not in mess)
        self.assertTrue(1024 in mess)

        items = [(k, v) for k, v in mess.items()]
        self.assertEquals(1024, items[0][0])
        self.assertEquals(8, items[1][0])
        self.assertEquals(9, items[2][0])
Ejemplo n.º 5
0
 def test_nested_group_from_list(self):
     """ Call to_binary() on a nested grouped message from a list """
     # As its difficult to test this, convert the (unordered)
     # dict into an OrderedDict() before inserting (sorting by tag).
     # This makes it easier to do the comparison.
     mess = FIXMessage(
         header_fields=[8, 9],
         source=to_ordered_dict([(8, 'FIX.4.2'),
                                 (100, [{101: 'abc', 102: 'def'},
                                        {101: 'ghi', 103: 'jkl'},
                                        {101: 'mno', 200: [
                                            {201: 'aaa', 202: 'bbb'}]}]),
                                 (99, 'X')]))
     self.assertEquals(to_fix('8=FIX.4.2',
                              '9=73',
                              '100=3',
                              '101=abc',
                              '102=def',
                              '101=ghi',
                              '103=jkl',
                              '101=mno',
                              '200=1',
                              '201=aaa',
                              '202=bbb',
                              '99=X',
                              '10=034'),
                       mess.to_binary())
Ejemplo n.º 6
0
    def test_msg_type(self):
        mess = FIXMessage()
        self.assertEquals('', mess[35])
        self.assertEquals('', mess.msg_type())

        mess[35] = FIX.LOGON
        self.assertEquals(FIX.LOGON, mess[35])
        self.assertEquals(FIX.LOGON, mess.msg_type())
Ejemplo n.º 7
0
    def test_msg_type(self):
        mess = FIXMessage()
        self.assertEquals('', mess[35])
        self.assertEquals('', mess.msg_type())

        mess[35] = FIX.LOGON
        self.assertEquals(FIX.LOGON, mess[35])
        self.assertEquals(FIX.LOGON, mess.msg_type())
Ejemplo n.º 8
0
 def test_group_from_list(self):
     """ Call to_binary() on a grouped message from a list """
     mess = FIXMessage(header_fields=[8, 9],
                       source=[(8, 'FIX.4.2'), (9, '25'), (49, 'SERVER'),
                               (56, 'CLIENT'), (99, 'X')])
     self.assertEquals(
         to_fix('8=FIX.4.2', '9=25', '49=SERVER', '56=CLIENT', '99=X',
                '10=239'), mess.to_binary())
Ejemplo n.º 9
0
    def test_to_binary_include(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[177] = 'hello'

        data = mess.to_binary(include=[8, 9, 35, 177])
        self.assertEquals(
            to_fix('8=FIX.4.2', '9=15', '35=A', '177=hello', '10=212'), data)
Ejemplo n.º 10
0
    def test_to_binary_binarydata(self):
        mess = FIXMessage(header_fields=[8, 9])
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[110] = 2
        mess[111] = '\x01\x02a\xbbbcd'

        data = mess.to_binary()

        self.assertEquals(
            to_fix('8=FIX.4.2', '9=18', '110=2', '111=\x01\x02a\xbbbcd',
                   '10=026'), data)
Ejemplo n.º 11
0
    def test_to_binary_exclude(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[99] = 'X'
        mess[177] = 'hello'

        data = mess.to_binary(exclude=[35, 177])
        self.assertEquals(
            to_fix('8=FIX.4.2', '9=25', '49=SERVER', '56=CLIENT', '99=X',
                   '10=239'), data)
Ejemplo n.º 12
0
 def test_group_from_list(self):
     """ Call to_binary() on a grouped message from a list """
     mess = FIXMessage(header_fields=[8, 9],
                       source=[(8, 'FIX.4.2'),
                               (9, '25'),
                               (49, 'SERVER'),
                               (56, 'CLIENT'),
                               (99, 'X')])
     self.assertEquals(to_fix('8=FIX.4.2',
                              '9=25',
                              '49=SERVER',
                              '56=CLIENT',
                              '99=X',
                              '10=239'),
                       mess.to_binary())
Ejemplo n.º 13
0
    def test_to_binary_binarydata(self):
        mess = FIXMessage(header_fields=[8, 9])
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[110] = 2
        mess[111] = '\x01\x02a\xbbbcd'

        data = mess.to_binary()

        self.assertEquals(to_fix('8=FIX.4.2',
                                 '9=18',
                                 '110=2',
                                 '111=\x01\x02a\xbbbcd',
                                 '10=026'),
                          data)
Ejemplo n.º 14
0
def logout_message(client):
    """ Generates a FIX logout message """
    return FIXMessage(
        source=[(35,
                 FIX.LOGOUT), (49,
                               client.sender_compid), (56,
                                                       client.target_compid)])
Ejemplo n.º 15
0
    def test_to_binary_include(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[177] = 'hello'

        data = mess.to_binary(include=[8, 9, 35, 177])
        self.assertEquals(to_fix('8=FIX.4.2',
                                 '9=15',
                                 '35=A',
                                 '177=hello',
                                 '10=212'),
                          data)
Ejemplo n.º 16
0
def new_order_message(client, **kwargs):
    """ Generates a new order message.

        Arguments:

        Returns:

        Raises:
            ValueError
    """
    # Required parameters
    for sym in ['symbol', 'side', 'order_type']:
        if sym not in kwargs:
            raise ValueError("{0} must have a value".format(sym))

    # optional parameters
    extra_tags = kwargs.get('extra_tags', [])

    return FIXMessage(source=[
        (35, FIX.NEWORDER_SINGLE),
        (49, client.sender_compid),
        (56, client.target_compid),
        (11, client.get_next_orderid()),
        (21, '1'),  # handlInst
        (55, kwargs['symbol']),
        (54, kwargs['side']),
        (60, format_time(datetime.datetime.now())),
        (40, kwargs['order_type']),
    ] + extra_tags)
Ejemplo n.º 17
0
    def test_to_binary_group(self):
        """ Call to_binary() on a grouped message """
        mess = FIXMessage(header_fields=[8, 9])
        tags = collections.OrderedDict()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        tags[110] = 2
        tags[111] = 'abcd'

        mess[100] = [
            tags,
        ]
        data = mess.to_binary()

        self.assertEquals(
            to_fix('8=FIX.4.2', '9=21', '100=1', '110=2', '111=abcd',
                   '10=086'), data)
Ejemplo n.º 18
0
def execution_report(client, prev_message, **kwargs):
    """ Generates an execution report

        Arguments:
            client
            prev_message
            exec_trans_type:
            exec_type:
            ord_status:
            leaves_qty:
            cum_qty:
            avg_px:

        Returns:
        Raises:
            ValueError
    """
    # Required parameters
    for sym in [
            'exec_trans_type', 'exec_type', 'ord_status', 'leaves_qty',
            'cum_qty', 'avg_px'
    ]:
        if sym not in kwargs:
            raise ValueError("{0} must have a value".format(sym))

    # optional parameters
    extra_tags = kwargs.get('extra_tags', [])
    exec_id = kwargs.get('exec_id', None) or client.get_next_orderid()

    message = FIXMessage(source=prev_message)
    message.update([
        (35, FIX.EXECUTION_REPORT),
        (49, client.sender_compid),
        (56, client.target_compid),
        (11, prev_message[11]),
        (37, client.get_next_orderid()),
        (17, exec_id),
        (20, kwargs['exec_trans_type']),
        (150, kwargs['exec_type']),
        (39, kwargs['ord_status']),
        (151, kwargs['leaves_qty']),
        (14, kwargs['cum_qty']),
        (6, kwargs['avg_px']),
    ] + extra_tags)
    return message
Ejemplo n.º 19
0
 def test_simple_send(self):
     """ test simple sending """
     self.assertEquals(0, self.transport.message_sent_count)
     self.transport.send_message(
         FIXMessage(source=[
             (8, 'FIX.4.2'),
             (35, 'A'),
         ]))
     self.assertEquals(1, self.transport.message_sent_count)
Ejemplo n.º 20
0
    def test_to_binary_exclude(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[99] = 'X'
        mess[177] = 'hello'

        data = mess.to_binary(exclude=[35, 177])
        self.assertEquals(to_fix('8=FIX.4.2',
                                 '9=25',
                                 '49=SERVER',
                                 '56=CLIENT',
                                 '99=X',
                                 '10=239'),
                          data)
Ejemplo n.º 21
0
def execution_report(client, prev_message, **kwargs):
    """ Generates an execution report

        Arguments:
            client
            prev_message
            exec_trans_type:
            exec_type:
            ord_status:
            leaves_qty:
            cum_qty:
            avg_px:

        Returns:
        Raises:
            ValueError
    """
    # Required parameters
    for sym in ['exec_trans_type', 'exec_type', 'ord_status',
                'leaves_qty', 'cum_qty',
                'avg_px']:
        if sym not in kwargs:
            raise ValueError("{0} must have a value".format(sym))

    # optional parameters
    extra_tags = kwargs.get('extra_tags', [])
    exec_id = kwargs.get('exec_id', None) or client.get_next_orderid()

    message = FIXMessage(source=prev_message)
    message.update([
        (35, FIX.EXECUTION_REPORT),
        (49, client.sender_compid),
        (56, client.target_compid),
        (11, prev_message[11]),
        (37, client.get_next_orderid()),
        (17, exec_id),
        (20, kwargs['exec_trans_type']),
        (150, kwargs['exec_type']),
        (39, kwargs['ord_status']),
        (151, kwargs['leaves_qty']),
        (14, kwargs['cum_qty']),
        (6, kwargs['avg_px']),
        ] + extra_tags)
    return message
Ejemplo n.º 22
0
    def test_to_binary_group(self):
        """ Call to_binary() on a grouped message """
        mess = FIXMessage(header_fields=[8, 9])
        tags = collections.OrderedDict()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        tags[110] = 2
        tags[111] = 'abcd'

        mess[100] = [tags, ]
        data = mess.to_binary()

        self.assertEquals(to_fix('8=FIX.4.2',
                                 '9=21',
                                 '100=1',
                                 '110=2',
                                 '111=abcd',
                                 '10=086'),
                          data)
Ejemplo n.º 23
0
 def test_send_seqno(self):
     """ test send sequence numbering """
     seqno = self.protocol._send_seqno
     self.assertEquals(0, self.transport.message_sent_count)
     self.transport.send_message(
         FIXMessage(source=[
             (8, 'FIX.4.2'),
             (35, 'A'),
         ]))
     self.assertEquals(1, self.transport.message_sent_count)
     self.assertEquals(seqno + 1, self.protocol._send_seqno)
Ejemplo n.º 24
0
    def test_header_fields(self):
        mess = FIXMessage()
        self.assertEquals(5, len(mess))

        self.assertTrue(8 in mess)
        self.assertTrue(9 in mess)
        self.assertTrue(35 in mess)
        self.assertTrue(49 in mess)
        self.assertTrue(56 in mess)

        items = [(k, v) for k, v in mess.items()]
        # 8,9 are required to be first and second fields, respectively
        self.assertEquals(8, items[0][0])
        self.assertEquals(9, items[1][0])

        # Custom required fields
        mess = FIXMessage(header_fields=[1024, 8, 9])
        self.assertTrue(8 in mess)
        self.assertTrue(9 in mess)
        self.assertTrue(35 not in mess)
        self.assertTrue(1024 in mess)

        items = [(k, v) for k, v in mess.items()]
        self.assertEquals(1024, items[0][0])
        self.assertEquals(8, items[1][0])
        self.assertEquals(9, items[2][0])
Ejemplo n.º 25
0
    def __init__(self, receiver, **kwargs):
        """ FIXParser initialization

            Args:
                receiver: This is an observer that receives the message and
                    error notifications from the parser.  There are two
                    callbacks:
                        on_message_received(message)
                        on_error_received(error)
                header_fields: A list of header tags.  This only affects
                    the sending of the message. The order of the input
                    fields is not validated.
                binary_fields: A list of tags indicating binary fields.
                    Note that binary fields come in pairs.  The first
                    field contains the length of the data and the second
                    field contains the actual data.  The convention is that
                    the IDs are sequential.  For example, if the length field
                    is tag 123, then tag 124 contains the data.  Note that
                    only the first field should be included in this list.
                group_fields: A dictionary of fields that belong to a group.
                    The key is the group ID field that maps to a list of
                    IDs that belong to the group.  When specifying the
                    field list for a group, include BOTH fields of a
                    binary field.
                max_length: Maximum length of a FIX message supported
                    (Default: 2048).
                debug: Set to True for more debugging output
        """
        self.is_parsing = False

        self._receiver = receiver
        self._header_fields = kwargs.get('header_fields', [8, 9, 35, 49, 56])
        self._binary_fields = kwargs.get('binary_fields', list())
        self._group_fields = kwargs.get('group_fields', list())
        self._max_length = kwargs.get('max_length', 2048)
        self._debug = kwargs.get('debug', False)

        self._buffer = b''
        self.is_receiving_data = False

        self._message = FIXMessage(header_fields=self._header_fields)
        self._checksum = 0
        self._message_length = 0

        # used for binary field processing
        self._binary_length = -1
        self._binary_tag = 0

        # used for groups processing
        self._level_stack = list()

        self._logger = logging.getLogger(__name__)
Ejemplo n.º 26
0
    def test_to_binary(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[34] = 177
        mess[52] = '20090107-18:15:16'
        mess[98] = 0
        mess[108] = 30
        mess[10] = '---'

        data = mess.to_binary()

        # BodyLength(9) and Checksum(10) should be updated after
        # the to_binary() was called.
        self.assertEquals('65', mess[9])
        self.assertEquals('062', mess[10])

        self.assertEquals(
            to_fix('8=FIX.4.2', '9=65', '35=A', '49=SERVER', '56=CLIENT',
                   '34=177', '52=20090107-18:15:16', '98=0', '108=30',
                   '10=062'), data)
Ejemplo n.º 27
0
    def on_message_received(self, message, message_length, checksum):
        """ This is the callback from the parser when a message has
            been received.
        """
        # verify required tags
        for tag in self.link_config['required_fields']:
            if tag not in message or len(str(message[tag])) == 0:
                raise FIXDataError(tag, 'missing field: id:{0}'.format(tag))

        # verify the protocol version
        if 'protocol_version' in self.link_config:
            if self.link_config['protocol_version'] != message[8]:
                raise FIXDataError(
                    8, 'version mismatch: expect:{0} received:{1}'.format(
                        self.link_config['protocol_version'],
                        message[8]
                        ))

        # verify the length and checksum
        if message_length != int(message[9]):
            raise FIXDataError(
                9, 'length mismatch: expect:{0} received:{1}'.format(
                    message_length, int(message[9])))

        if checksum != int(message[10]):
            raise FIXDataError(
                10, 'checksum mismatch: expect:{0} received:{1}'.format(
                    checksum, message[10]))

        self._last_received_time = datetime.datetime.now()

        # Have we received our testrequest response?
        if (message.msg_type() == FIX.HEARTBEAT and
                message.get(112, '') == self._testrequest_id):
            self._testrequest_time = None
            self._testrequest_id = None

        # We have received a testrequest and need to send a response
        if message.msg_type() == FIX.TEST_REQUEST:
            self.transport.send_message(
                FIXMessage(source=[(35, FIX.HEARTBEAT),
                                   (112, message[112]),
                                   (49, self.link_config['sender_compid']),
                                   (56, self.link_config['target_compid'])]))

        if (not self.filter_heartbeat or
                (message.msg_type() not in {FIX.HEARTBEAT, FIX.TEST_REQUEST})):
            self.transport.on_message_received(message)
Ejemplo n.º 28
0
    def test_send_with_missing_fields(self):
        """ send with missing fields """
        self.assertEquals(0, self.transport.message_sent_count)

        message = FIXMessage(source=[
            (8, 'FIX.4.2'),
            (35, 'A'),
        ])
        self.assertTrue(34 not in message)
        self.assertTrue(52 not in message)
        self.transport.send_message(message)
        self.assertEquals(1, self.transport.message_sent_count)

        # check for seqno(34) and sendtime(52)
        self.assertTrue(34 in self.transport.last_message_sent)
        self.assertTrue(52 in self.transport.last_message_sent)
Ejemplo n.º 29
0
    def test_verify(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[99] = 'X'
        mess[177] = 'hello'

        self.assertTrue(mess.verify(fields=[(8, 'FIX.4.2'), (35, 'A')]))
        self.assertFalse(mess.verify(fields=[(8, 'NOFIX')]))

        self.assertTrue(mess.verify(exists=[8, 35, 177]))
        self.assertFalse(mess.verify(exists=[9, 8, 2000]))

        self.assertTrue(mess.verify(not_exists=[2000, 20001]))
        self.assertFalse(mess.verify(not_exists=[177]))

        self.assertTrue(
            mess.verify(fields=[(99, 'X')],
                        exists=[56, 99, 177],
                        not_exists=[2001, 2002, 2003]))
Ejemplo n.º 30
0
    def reset(self, flush_buffer=False):
        """ Reset the protocol state so that it is ready to accept a
            new message.
        """
        self.is_parsing = False
        self._message = FIXMessage(header_fields=self._header_fields)
        self._checksum = 0
        self._message_length = 0

        # used for binary field processing
        self._binary_length = -1
        self._binary_tag = 0

        # used for groups processing
        self._level_stack = list()

        if flush_buffer:
            self._buffer = b''
Ejemplo n.º 31
0
    def test_verify(self):
        mess = FIXMessage()
        mess[8] = 'FIX.4.2'
        mess[9] = '---'
        mess[35] = 'A'
        mess[49] = 'SERVER'
        mess[56] = 'CLIENT'
        mess[99] = 'X'
        mess[177] = 'hello'

        self.assertTrue(mess.verify(fields=[(8, 'FIX.4.2'), (35, 'A')]))
        self.assertFalse(mess.verify(fields=[(8, 'NOFIX')]))

        self.assertTrue(mess.verify(exists=[8, 35, 177]))
        self.assertFalse(mess.verify(exists=[9, 8, 2000]))

        self.assertTrue(mess.verify(not_exists=[2000, 20001]))
        self.assertFalse(mess.verify(not_exists=[177]))

        self.assertTrue(mess.verify(fields=[(99, 'X')],
                                    exists=[56, 99, 177],
                                    not_exists=[2001, 2002, 2003]))
Ejemplo n.º 32
0
def logon_message(client):
    """ Generates a FIX logon message """
    return FIXMessage(source=[(35, FIX.LOGON), (
        49, client.sender_compid), (56, client.target_compid), (
            98, 0), (108, client.protocol.heartbeat)])