Skip to main content

The Pythonic Financial Information eXchange (FIX) client for humans.

Project description

WTFIX

The Pythonic Financial Information eXchange (FIX) client for humans.

Build status Coverage Status PyPI version shields.io PyPI pyversions PyPI license Code style:black

Project Highlights

  • Pure Python3.

  • Batteries included - comes with everything that you need to connect to a FIX server and start sending and receiving messages in minutes. Provides default implementations for:

    • Authentication
    • Maintaining a heartbeat
    • Sequence number management and resend requests
  • Fast, easy to understand message processing pipeline based on a modern async and await implementation.

  • Easily extendable architecture - modular 'apps' can be added to the pipeline stack to add new application logic.

    PIPELINE_APPS = [
        "my_app.apps.SecretAlgoTradingRecipe",     # <-- Your application logic
        "wtfix.apps.api.RESTfulServiceApp",        # REST API for sending messages
        "wtfix.apps.admin.HeartbeatApp",           # Heartbeat monitoring and maintenance
        "wtfix.apps.admin.AuthenticationApp",      # Login / logout handling
        "wtfix.apps.admin.SeqNumManagerApp",       # Message gap detection and filling
        "wtfix.apps.parsers.RawMessageParserApp",  # Message parsing: Logon (A): {BeginString (8):FIX.4.4 | BodyLength (9):99 | MsgType (35):A | MsgSeqNum (34):1 | SenderCompID (49):SENDER | SendingTime (52):20190305-08:45:45.979 | TargetCompID (56):TARGET | EncryptMethod (98):0 | HeartBtInt (108):30 | Username (553):USERNAME | Password (554):PASSWORD | ResetSeqNumFlag (141):Y | CheckSum (10):94}
        "wtfix.apps.utils.LoggingApp",             # Log inbound and outbound messages
        "wtfix.apps.wire.WireCommsApp",            # Raw message encoding / decoding: b'8=FIX.4.4\x019=99\x0135=A\x0134=1\x0149=SENDER\x0152=20190305-08:42:32.793\x0156=TARGET\x0198=0\x01108=30\x01553=USERNAME\x01554=PASSWORD\x01141=Y\x0110=081\x01'
        "wtfix.apps.sessions.ClientSessionApp",    # HTTP session management
    ]
    
  • Provides a convenient @on decorator for fine-grained control over which apps will respond to which types of messages:

    from wtfix.apps.base import MessageTypeHandlerApp, on
    from wtfix.protocol.common import MsgType
    from wtfix.conf import logger
    
    class SecretAlgoTradingRecipe(MessageTypeHandlerApp):
    
        @on(MsgType.Logon)  # Only invoked when 'Logon (type A)' messages are received.
        def on_logon(self, message):
            self.send_security_definition_request()
            return message
    
        def on_receive(self, message):  # Invoked for every type of message.
          logger.info(f"Received message {message}!")
    
  • A simple message tag syntax that is human friendly, with various convenience methods for quick access to commonly used message attributes.

    >>> from wtfix.message import admin
    >>> from wtfix.protocol.common import Tag
    
    # Instantiate a new 'Logon' message
    >>> logon_msg = admin.LogonMessage("my_username", "my_password", heartbeat_int=b"30")
    
    # Readable string representation
    str(logon_msg)
    'Logon (A): {MsgType (35):A | EncryptMethod (98):0 | HeartBtInt (108):30 | Username (553):my_username | Password (554):my_password | 10222:345}
    
    # Example of getting the message type
    >>> logon_msg.type
    'A'
    
    # Example of getting the message type name
    >>> logon_msg.name
    'Logon'
    
    # Find the sequence number
    >>> logon_msg.seq_num
    1
    
    # Various ways for accessing the different fields that make up the message. Fields are just 
    # (tag, value) namedtuples.
    >>> logon_msg[108]  # Using old school tag number
    (108, b"30")
    
    >>> logon_msg[Tag.HeartBtInt]  # Using the tag name as per the FIX specification
    (108, b"30")
    
    >>> logon_msg.HeartBtInt  # Using tag name shortcut
    (108, b"30")
    
  • A pragmatic unicode sandwich based approach to encoding / decoding messages mean that you never need to deal with byte strings...

    # Duck typing for doing field value comparisons
    >>> logon_msg.HeartBtInt == 30
    True
    
    >>> logon_msg.HeartBtInt == "30"
    True
    
    >>> logon_msg.HeartBtInt == b"30"
    True
    
  • ...unless you want to:

    # Accessing the underlying byte string
    >>> logon_msg.HeartBtInt.value_ref.value
    b'30'
    
    >>> logon_msg.raw
    b'35=A\x0198=0\x01108=30\x01553=my_username\x01554=my_password\x01'
    
  • On the fly field conversions to commonly used types:

    >>> logon_msg.HeartBtInt.as_str
    '30'
    
    >>> logon_msg.HeartBtInt.as_int
    30
    
    >>> logon_msg.PossDupFlag = "Y"
    >>> logon_msg.PossDupFlag.as_bool
    True
    
    >>> logon_msg.PossDupFlag == True
    True
    
  • A very forgiving approach to repeating groups of message tags:

    from wtfix.message.message import generic_message_factory
    
    # If you provide a group template, then messages are stored in an 'OrderedDict' for fast lookups
    >>> msg = generic_message_factory((Tag.MsgType, MsgType.ExecutionReport), (Tag.NoMiscFees, 2), (Tag.MiscFeeAmt, 10.00), (Tag.MiscFeeType, 2), (Tag.MiscFeeAmt, 20.00), (Tag.MiscFeeType, "A"), group_templates={Tag.NoMiscFees: [Tag.MiscFeeAmt, Tag.MiscFeeType,]})
    >>> msg._fields
    OrderedDict([(35, (35, 8)), (136, [(136, 2)]:[(137, 10.0), (139, 2)], [(137, 20.0), (139, A)])])
    
    # Get 'NoMiscFees' group
    >>> group = msg.get_group(Tag.NoMiscFees)
    >>> str(group)
    '[NoMiscFees (136):2] | [MiscFeeAmt (137):10.0 | MiscFeeType (139):2] | [MiscFeeAmt (137):20.0 | MiscFeeType (139):A]'
    
    # Determine the number of instances in the group
    >>> group.size
    2
    
    # Retrieve the second group instance
    >>> group.instances[1]
    [(137, 20.0), (139, A)]
    
    # Without a pre-defined group template we fall back to using a (slightly slower) list structure for representing message fields internally
    >>> msg = generic_message_factory((Tag.MsgType, MsgType.ExecutionReport), (Tag.NoMiscFees, 2), (Tag.MiscFeeAmt, 10.00), (Tag.MiscFeeType, 2), (Tag.MiscFeeAmt, 20.00), (Tag.MiscFeeType, "A")
    >>> msg_.fields
    [(35, 8), (136, 2), (137, 10.0), (139, 2), (137, 20.0), (139, A)]
    

Getting Started

  • Install the project's dependencies (e.g. pip install -r requirements/local.txt), preferably in a Python virtual environment that has been created specifically for that purpose.

  • Run the test suite with pytest to verify the installation.

  • Create a .env file in the project's root directory that contains at least the following configuration settings:

    # Supports different configuration settings for local development, staging, or production environments.
    WTFIX_SETTINGS_MODULE=config.settings.local
    
    HOST=             # Required. The FIX server hostname or IP address
    PORT=             # Required. The port on the FIX server to connect to
    
    SENDER=           # Required. SENDER_COMP_ID (tag 49).
    TARGETD=          # Required. TARGET_COMP_ID (tag 56).
    
    USERNAME=         # Required. Username to use for Logon messages (tag 553).
    PASSWORD=         # Required. Password to use for logon messages (tag 554).
    
    PYTHONASYNCIODEBUG=0  # Set to '1' for detailed debugging messages.
    
  • Start the FIX client with python runclient.py. The default implementation will log in to the FIX server and maintain a steady heartbeat.

  • Use Ctrl-C to quit. This will trigger a Logout message to be sent before the pipeline is terminated.

Project Resources

Inspired By

  • slowbreak's message processing pipeline and @on decorator
  • simplefix's approach to raw message parsing

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wtfix-0.2.0.tar.gz (60.6 kB view hashes)

Uploaded Source

Built Distribution

wtfix-0.2.0-py3-none-any.whl (97.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page