{"id":22220609,"url":"https://github.com/fix8mt/ufeed_bindings_python","last_synced_at":"2025-03-25T07:41:38.210Z","repository":{"id":215362114,"uuid":"425228796","full_name":"fix8mt/ufeed_bindings_python","owner":"fix8mt","description":"Python language binding for UFEed.","archived":false,"fork":false,"pushed_at":"2021-11-16T06:38:45.000Z","size":190,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-30T07:12:44.136Z","etag":null,"topics":["fix","fix-engine","fix8","fix8-market","fix8pro","fixengine","fixprotocol","google-protocol-buffers","high-performance","low-latency","middleware","python","zeromq"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fix8mt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-11-06T11:39:52.000Z","updated_at":"2024-11-03T10:03:06.000Z","dependencies_parsed_at":"2024-01-04T02:16:11.616Z","dependency_job_id":"937c2465-5446-40ce-8108-fed70e249985","html_url":"https://github.com/fix8mt/ufeed_bindings_python","commit_stats":null,"previous_names":["fix8mt/ufeed_bindings_python"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fufeed_bindings_python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fufeed_bindings_python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fufeed_bindings_python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fix8mt%2Fufeed_bindings_python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fix8mt","download_url":"https://codeload.github.com/fix8mt/ufeed_bindings_python/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245422922,"owners_count":20612725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fix","fix-engine","fix8","fix8-market","fix8pro","fixengine","fixprotocol","google-protocol-buffers","high-performance","low-latency","middleware","python","zeromq"],"created_at":"2024-12-02T23:09:19.652Z","updated_at":"2025-03-25T07:41:38.181Z","avatar_url":"https://github.com/fix8mt.png","language":"Python","readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.fix8mt.com\"\u003e\u003cimg src=\"fix8mt_Master_Logo_Green_Trans.png\" width=\"200\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# UFEed Python Binding\n\n-   [Introduction](#introduction)\n-   [Getting Started](#getting-started)\n-   [Demo](#demo)\n    -   [Requests](#requests)\n    -   [Subscribing](#subscribing)\n    -   [Responding](#responding)\n    -   [Publishing](#publishing)\n    -   [Finishing Up](#finishing-up)\n-   [Message](#message)\n-   [Interface](#interface)\n    -   [UFEedClient](#ufeedclient)\n    -   [Message and Message Builder](#message-and-message-builder)\n-   [Constants](#constants)\n    -   [FIX variants constants](#fix-variants-constants)\n-   [Logging](#logging)\n\n------------------------------------------------------------------------\n\n# Introduction\n\nThe UFEed Python Adapter (`UFEed_Python`) provides a low level Pythonic interface\nto the UFEGW. Interactions with the UFEGW are based around a\n`UFEedClient` object which can be used to send and receive *Messages* to\nand from the UFEGW.\n\nUse the following [Universal FIX engine documentaion](https://fix8mt.atlassian.net/wiki/spaces/FMT/pages/634438/Universal|FIX|Engine|Home) for a reference.\n\nFeatures of `UFEedClient`:\n\n-   System API support (see [4. Implementation Guide - Section\n    1.3](https://fix8mt.atlassian.net/wiki/spaces/FMT/pages/628308/4.|Implementation|Guide))\n-   Business API support (eg. NewOrderSingle and standard FIX messages)\n-   Provides a 4-way communications API in order to make requests,\n    publish messages, receive responses and subscribe to broadcast\n    messages\n-   User defined functions to handle these PUB, SUB, REQ and REP message\n    events\n-   Dynamic configuration of PUB, SUB, REQ, REP addressing and topics\n-   Internal session management\n\nFeatures of a `Message`:\n\n-   A `Message` object is automatically mapped onto its internal fields,\n    with inspection via **message\\[tag\\] = value**\n-   An inheritance interface for implementers to enforce required\n    fields, as well as provide polymorphic/functional enhancements for\n    specialised `Message` types\n-   Smart field creation, rendering field value to *ival*, *sval* or\n    *fval* depending on context\n-   Named `Message` properties (*name, longname, seq, service_id,\n    subservice_id*)\n-   Pretty printing of messages\n\n# Getting Started\n\nThe `UFEed_Python` builds to a compiled Python module in both *.so* (Linux and\nMacOS) and *.pyd* (Windows) format. It has dependencies on the Python\n*pyzmq* and *protobuf* libraries. Within the distributed `UFEed_Python` archive we find the following directory structure (example is Linux / MacOS):\n\n```\npy\n├── README.md\n├── requirements.txt\n├── test_message.py\n├── test_ufeedclient.py\n└── ufeed_bindings_python\n    ├── __init__.py\n    ├── consts.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── message.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufeapi_pb2.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufeedclient.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix40.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix41.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix42.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix43.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix44.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix50.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    ├── ufe_py_fields_fix50sp1.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n    └── ufe_py_fields_fix50sp2.cpython-\u003cpython_version\u003e-\u003cplatform\u003e-\u003cOS\u003e.so\n```\n\nThe easiest way to run the `UFEed_Python` is within a Python virtual environment. If virtualenv is not installed on the machine, it can be installed via:\n\n**Linux**\n```shell\n$ sudo apt install virtualenv\n```\n**Windows / MacOS**\n```shell\n\u003e pip install virtualenv\n```\n\nAfter this is done, inside the `UFEed_Python` directory we enter the following\ncommands:\n\n**Linux / MacOS**\n```shell\n$ virtualenv -p python3 venv\n$ source venv/bin/activate\n$ pip install -r requirements.txt # installs pyzmq and protobuf\n```\n**Windows**\n```shell\n\u003e virtualenv venv\n\u003e ./venv/Scripts/activate\n\u003e pip install -r requirements.txt # installs pyzmq and protobuf\n```\n\nThe environment is now ready to execute the `UFEed_Python`.\n\n# Demo\n\n**Note that this tutorial is relatively detailed, and demonstrates some\nmore advanced concepts in `UFEed_Python` usage. From within the `UFEed_Python` directory, either in a Python script, or via a\nPython interpreter, we can import the `UFEed_Python` and all its necessary\nmachinery using:\n\n```python\nfrom UPA import *\n```\n\nThen we create a `UFEedClient` object:\n\n```python\nuc = UFEedClient()\n```\n\n\nWe now need to create user defined functions to handle the REQ and SUB\nmessages that the `UFEedClient` can make to and receive from the UFEGW.\nIn this simple case, we decide that when certain messages are received\nwe will print them, like so:\n\n```python\ndef subscriber_func(msg):\n    if msg.service_id == UFE_CMD_LOGIN or msg.longname == \"ExecutionReport\":\n        print(msg)\n\ndef requester_func(msg):\n    print(msg)\n```\n\nThese methods are the minimal amount of functional behaviour that must\nbe defined in order to use the `UFEed_Python`. For the subscriber function, we have applied a filter to only print login commands or execution reports\nbecause typically the UFEGW will have a plethora of messages coming\nthrough at any given time. You can try this out in the future by\ndefining `subscriber_func` as a simple `print(msg)`. For now though we\nproceed by starting the `UFEedClient` with the function definitions\nabove:\n\n```python\nuc.start(sub_func=subscriber_func, req_func=requester_func)\n```\n\n## Requests\n\nThe `UFEed_Python` manages its requests via the `REQUESTER` connection string, and its topic via the `REQUESTER_TOPIC`. In this run-through example we are using the defaults (\"tcp://127.0.0.1:55746\" and\n\"ufeedclient-responder\") by not specifying them explicitly.\n\nIf we want to specify these values we define them in a dictionary:\n\n```python\nconn_strs = {REQUESTER: 'tcp://127.0.0.1:55746',\n             REQUESTER_TOPIC: 'ufeedclient-responder'}\n```\n\nand pass them to the UFEedClient at instantiation:\n\n```python\nuc = UFEedClient(conn_strs)\n```\n\nWith the `UFEedClient` started, it is now ready to interact with the\nUFEGW. We should start by logging in:\n\n```python\nlogin = uc.create_message() \\\n    .set_long_name(\"login\") \\\n    .set_type(st_system) \\\n    .set_service_id(UFE_CMD_LOGIN) \\\n    .add_field(UFE_CMD, UFE_CMD_LOGIN) \\\n    .add_field(UFE_LOGIN_ID, \"user01\") \\\n    .add_field(UFE_LOGIN_PW, \"password123456\")\n# login is Message.Builder\nresponse = uc.request(login)\n```\n\nIf our user credentials are valid, we should receive back a `Message`\nfrom the UFEGW confirming our login. Because we are printing login\n`Message` responses automatically, a message similar to the following\nshould be printed in the Python console:\n\n```\nseq: 5\ntype: st_response\nservice_id: 73001\nfields {\n  location: fl_system\n  type: ft_status\n  tag: 72001\n  ival: 71013\n}\nfields {\n  location: fl_system\n  type: ft_int\n  tag: 80045\n  ival: 5\n}\nfields {\n  location: fl_system\n  type: ft_int\n  tag: 72010\n  ival: 73001\n}\nfields {\n  location: fl_system\n  type: ft_uuid\n  tag: 72005\n  sval: \"\\246[V\\275^\\006N\\254\\202\\336\\212\\312\\266\\3544\\217\"\n}\nfields {\n  location: fl_system\n  type: ft_string\n  tag: 80058\n  sval: \"logon success\"\n}\n```\n\nThis means we have now logged on successfully, and we can now perform\nboth System and Business API calls. Incidentally, we also now have a\nresponse from UFEGW that we can query. This message is stored in the\n`response` variable, and we could query (for example), the session token\nprovided by the UFEGW server using:\n\n```python\nresponse[UFE_SESSION_TOKEN]\n```\n\nIn this example we see the\nvalue **\\\"\\\\246\\[V\\\\275\\^\\\\006N\\\\254\\\\202\\\\336\\\\212\\\\312\\\\266\\\\3544\\\\217\\\"**,\nwhich is an *sval* binary format.\n\nNote that this is just an example of probing a message for a specific\nvalue - the `UFEedClient` will manage the session using this token on\nour behalf, so we don\\'t need to do anything specifically with it. For\nany additional messaging to the UFEGW, we don\\'t need to worry about\nexplicitly providing this token in any of our requests.\n\nWith our login out of the way, we can proceed with some more meaningful\ninteractions. Let\\'s try submitting an order:\n\n```python\nnos = uc.create_message()\n    .set_long_name(\"NewOrderSingle\")\n    .set_type(MsgType.st_fixmsg)\n    .set_service_id(6) # set service id manually\nnos.name = \"D\"\n# adds fields by list of tuple pairs\nnos.add_fields([\n    (COMMON_SYMBOL, \"BHP\"),\n    (COMMON_CLORDID, \"Ord01\"),\n    (COMMON_ORDERQTY, 100.),\n    (COMMON_PRICE, 10.25),\n    (COMMON_ORDTYPE, '1'),\n    (COMMON_SIDE, '0'),\n    (COMMON_TIMEINFORCE, '2'),\n    (COMMON_TRANSACTTIME, \"now\")])\nresponse = uc.request(nos)\n```\n\nSo long as our UFEGW environment is properly configured (see **4.\nImplementation Guide, section 7** for help with this), and we create a\n`Message` with a relevant service ID, our successful *NewOrderSingle*\nshould generate 1 to N Execution Reports. The UFEGW communications model\nis such that when we make our request via a request() function, we then\nreceive these reports via a UFEGW broadcast in our subscriber function\n(the UFEedClient started actively listening to these subscription\nbroadcasts on our behalf when we started it). When these messages\narrive, because of the way we defined our *subscriber_func* above, each\nExecution Report will now print. They will look something like this:\n\n```\nname: \"8\"\nlongname: \"ExecutionReport\"\nseq: 2889\nservice_id: 6\nfields {\n  location: fl_header\n  type: ft_string\n  tag: 35\n  sval: \"8\"\n}\nfields {\n  location: fl_header\n  type: ft_int\n  tag: 34\n  ival: 602\n}\nfields {\n  location: fl_header\n  type: ft_time\n  tag: 52\n  ival: 1557878840437000000\n}\nfields {\n  type: ft_string\n  tag: 37\n  sval: \"ord2\"\n}\nfields {\n  type: ft_string\n  tag: 11\n  sval: \"Ord01\"\n}\nfields {\n  type: ft_string\n  tag: 17\n  sval: \"exec7\"\n}\nfields {\n  type: ft_char\n  tag: 150\n  sval: \"0\"\n}\nfields {\n  type: ft_char\n  tag: 39\n  sval: \"0\"\n}\nfields {\n  type: ft_string\n  tag: 55\n  sval: \"ALLSYM:BHP\"\n}\n... and so on\n```\n\n## Subscribing\n\nThe `UFEed_Python` manages its subscriptions via the SUBSCRIBER connection string, and its topic via the `SUBSCRIBER_TOPIC`. In this run-through example we are using the defaults (\\\"tcp://127.0.0.1:55745\\\" and\n\\\"ufegw-publisher\\\") by not specifying them explicitly.\n\nIf we want to specify these values we define them in a dictionary:\n\n```python\nconn_strs = {SUBSCRIBER: 'tcp://127.0.0.1:55745',\n             SUBSCRIBER_TOPIC: 'ufegw-publisher'}\n```\n\nand pass them to the UFEedClient at instantiation:\n\n```python\nuc = UFEedClient(conn_strs)\n```\n\nThe UFEGW does not just provide responses to our `UFEed_Python` requests. It also publishes messages with UFEGW status updates that can be received by a\nsubscriber. The `UFEed_Python` actively subscribes to these messages after the\n`UFEedClient.start()` function has been called.\n\nWe already got a sense of the `UFEed_Python` subscriber in the previous section, when a *NewOrderSingle* request sent by the `UFEed_Python` resulted in the UFEGW publishing our Execution Reports in response. In that instance we simply printed the reports, but it is worth delving a little deeper into this subscription functionality and its possibilities.\n\nSubscribing to important messages from the UFEGW gives us an opportunity\nto gather, process and store information that is relevant to us via our\nclient. When we defined our subscription function\nthat we passed to start(), we specified a simple filter - to print login\ncommand responses or Execution Reports:\n\n```python\ndef subscriber_func(msg):\n    # msg is Message\n    if msg.service_id == UFE_CMD_LOGIN or msg.longname == \"ExecutionReport\":\n        print(msg)\n```\n\nHowever, we may wish to introduce additional functionality in the way we\nhandle messages, rather than just printing every single Execution Report\nmessage we receive on our subscription channel without discrimination.\n\nTo start with, rather than just printing these reports, it may be more\nuseful for us to store them programmatically. We could catalogue the\nExecution Reports in a simple list:\n\n```python\nexecution_reports = []\n\ndef subscriber_func(msg):\n    if msg.longname == \"ExecutionReport\":\n        execution_reports.append(msg)\n\n# ... program logic\n# ... do something with execution_reports\n```\n\nOr perhaps filter execution reports by a specific symbol:\n\n```python\nBHP_exec_reports = []\n\ndef subscriber_func(msg):\n    if msg.longname == \"ExecutionReport\" and msg[COMMON_SYMBOL] == \"ALLSYM:BHP\":\n        BHP_exec_reports.append(msg)\n```\n\n## Responding\n\nThe `UFEed_Python` manages its responses via the `RESPONDER` connection string, and its topic via the `RESPONDER_TOPIC`. In this run-through example we are using the defaults (\\\"tcp://\\*:55748\\\" and \\\"ufegw-responder\\\") by not\nspecifying them explicitly.\n\nIf we want to specify these values we define them in a dictionary:\n\n```python\nconn_strs = {RESPONDER: 'tcp://*:55748',\n             RESPONDER_TOPIC: 'ufegw-responder'}\n```\n\nand pass them to the UFEedClient at instantiation:\n\n```python\nuc = UFEedClient(conn_strs)\n```\n\nAs well as listening for published broadcasts, after its start()\nfunction is invoked the `UFEed_Python` will also start actively listen for requests\nsent to it via a requesting client elsewhere. Note that in order to do\nanything with this requests, we must define a *responder* function,\nsimilarly to our *requester* and *subscriber* functions. In this simple\nexample our UFEedClient instance acts to authenticate connecting\nsessions from another client on the network:\n\n```python\ndef authenticate(msg):\n    # ...perform authentication work...\n    return result\n\n# use object in responder function\ndef responder_func(msg):\n    if msg.longname == \"RequestAuthenticate\":\n        return authenticate(msg)\n\n# pass responder function to UFEedClient.start() function\nuc.start(rep_func=responder_func)\n```\n\nNote that without a responder function defined, the `UFEed_Python` will do nothing with the messages it receives. That is, if other sessions are sending\nrequests to a `UFEed_Python` instance that does not have a responder function, they will not receive anything in reply.\n\n## Publishing\n\nThe `UFEed_Python` manages its published messages via the `PUBLISHER` connection string, and its topic via the `PUBLISHER_TOPIC`. In this run-through example we are using the defaults (\\\"tcp://\\*:55747\\\" and \\\"ufeedclient-publisher\\\") by not specifying them explicitly.\n\nIf we want to specify these values we define them in a dictionary:\n\n```python\nconn_strs = {PUBLISHER: 'tcp://*:55747',\n             PUBLISHER_TOPIC: 'ufeedclient-publisher'}\n```\n\nand pass them to the UFEedClient at instantiation:\n\n```python\nuc = UFEedClient(conn_strs)\n```\n\nThe `UFEed_Python` also has the ability to publish any type of message to any\nnumber of subscribers, via the publish() function. In a case like this,\nit may be important to enforce an interface for published messages.\nPerhaps a published message will be expected to observe a very specific\nformat - maybe the programmer is creating a network of `UFEed_Python` clients, each\nnode with its own user, and wants to enforce consistency among them.\n\nIn this instance we are introduced to another aspect of `Message`\nspecialisation - enforcing required values. Such an interface would be\ndefined like:\n\n```python\nclass MessageFactory:\n    @staticmethod\n    def create_status_notification(self, # define the required fields\n                                   uc: UFEedClient,\n                                   seq_num: int,\n                                   status_id: str,\n                                   status_msg: str,\n                                   time: datetime = datetime.now()) -\u003e Message.Builder:\n        return uc.create_message() \\\n            .set_type(MsgType.st_system) \\\n            .set_long_name(\"status_notification\") \\\n            .add_fields([\n                (PRIVATE_SEQ_NUM, seq_num),\n                (PRIVATE_STAT_ID, status_id),\n                (PRIVATE_STAT_MSG, status_msg),\n                (PRIVATE_TRANSACTTIME, transact_time)\n            ])\n```\n\nIt would then be used within the context of a script or Python program\nlike so:\n\n```python\nnotification = MessageFactory.create_status_notification(uc, seq_num, status_id, status_msg)\nuc.publish(notification)\n\n# alternatively we could define a dictionary for our named arguments:\nnote = {\"uc\": uc,\n        \"seq_num\": 1,\n        \"status_id\": 0,\n        \"status_msg\": \"COMPLETE\"}\nnotification = MessageFactory.create_status_notification(*note)\nuc.publish(notification)\n```\n\n## Finishing Up\n\nWe have now successfully sent a *NewOrderSingle* message to the UFEGW,\nand received a relevant Execution Report in response. We have also\nlearnt how to build `Message` specialisations to handle this Execution\nReport in more specific, programmer-defined ways. We then learned about\nthe handling of requests to the `UFEed_Python` itself, and the `UFEed_Python`\\'s ability to\npublish messages to the broader network. In total, these requesting,\nsubscribing, responding and publishing actions encompass the 4-way\ninterface by which the `UFEed_Python` interacts with the UFEGW and also other\nclients on the network.\n\nTo clean up our session, we will now log out, like so:\n\n```python\nlogout = uc.create_message() \\\n    .set_long_name(\"logout\") \\\n    .set_type(MsgType.st_system) \\\n    .set_service_id(UFE_CMD_LOGOUT) \\\n    .add_field(UFE_CMD, UFE_CMD_LOGOUT)\nresponse = uc.request(logout)\n```\n\n# Message\n\nThe `UFEed_Python` can be used to send and receive any System or Business API\n`Message` between itself and the UFEGW. The typical format of a printed\n`Message` is:\n\n```\n# HEADER\nname: \"8\"\nlongname: \"ExecutionReport\"\nseq: 2889\nservice_id: 6\n\n# FIELDS\nfields {\nlocation: fl_header\ntype: ft_string\ntag: 35\nsval: \"8\"\n}\nfields {\nlocation: fl_header\ntype: ft_int\ntag: 34\nival: 602\n}\n...\n```\n\nThe creation of *Messages* can take 2 patterns:\n\n```python\n# 1. Recommended - create Message via a UFEedClient object\nlogin = uc.create_message() \\\n    .set_long_name(\"login\")\n    .set_type(MsgType.st_system)\n    .set_service_id(UFE_CMD_LOGIN) # MsgType.st_system means we are creating a SysMessage\n\n# 2. !Outdated! - Create Message specifically, in this case a SysMessage (a FIXMessage would be defined using FIXMessage())\nlogin = SysMessage.Builder()\nlogin.longname = \"login\"\nlogin.service_id = UFE_CMD_LOGIN\n\n# Add relevant Message fields (can also be added as a list of tuple pairs [(a,b)(c,d)(e,f)] )\nlogin.add_field(UFE_CMD, UFE_CMD_LOGIN) \\\n    .add_field(UFE_LOGIN_ID, \"user01\") \\\n    .add_field(UFE_LOGIN_PW, \"password123456\")\n\n# Send the Message request, and capture the response\nresponse = uc.request(login)\n```\nA message with groups can be created as (with generated FIX50 constants):\n\n```python\nfix50 = FIX50SP2_Fields\n\ndef test_message_with_groups():\n    # simple test\n    now = datetime.now()\n    g1 = Message.Builder.GroupRef()\n    g2 = Message.Builder.GroupRef()\n    msg = FIXMessage.Builder() \\\n        .set_long_name(\"NewOrderSingle\") \\\n        .set_name(fix50.MsgType.NEWORDERSINGLE) \\\n        .add_fields([(fix50.ClOrdID.tag, \"123\"),\n                     (fix50.TransactTime.tag, now),\n                     (fix50.ExecInst.tag, fix50.ExecInst.ALL_OR_NONE),\n                     (fix50.TimeInForce.tag, fix50.TimeInForce.FILL_OR_KILL),\n                     (fix50.Price.tag, 111.22),\n                     (fix50.OrderQty.tag, 33),\n                     (fix50.Side.tag, fix50.Side.BUY)]) \\\n        .add_field(fix50.OrdType.tag, fix50.OrdType.LIMIT) \\\n        .add_field(fix50.Account.tag, \"ACC1\") \\\n        .add_group(fix50.NoAllocs.tag, g1, lambda m, grp:\n            m.add_group_item(g1)\n                   .set_long_name(\"NoAlloc\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(1)\n                   .add_field(fix50.AllocAccount.tag, \"ALLOC1\")\n                   .add_field(fix50.AllocQty.tag, 50.) and\n            m.add_group_item(g1)\n                   .set_long_name(\"NoAlloc\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(3)\n                   .add_field(fix50.AllocAccount.tag, \"ALLOC2\")\n                   .add_field(fix50.AllocQty.tag, 60.)\n                   ) \\\n        .add_field(UFE_SESSION_TOKEN, uuid.UUID(\"ed391284-bb68-4e14-a786-3eb0387694d9\"), Location.fl_system) \\\n        .add_field(UFE_STATUS_CODE, Message.Status(1), Location.fl_system) \\\n        .add_field(fix50.PossDupFlag.tag, True) \\\n        .add_group(fix50.NoPartyIDs.tag, g1, lambda m, grp:\n                m.add_group_item(grp)\n                   .set_name(str(fix50.NoPartyIDs.tag))\n                   .set_long_name(\"NoPartyIDs\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(1)\n                   .add_field(fix50.PartyID.tag, \"Party1\")\n                   .add_group(fix50.NoCapacities.tag, g2, lambda m, grp:\n                        m.add_group_item(grp)\n                              .set_name(str(fix50.NoCapacities.tag))\n                              .set_long_name(\"NoCapacities\")\n                              .set_type(MsgType.st_fixmsg)\n                              .set_seq(1)\n                              .add_field(fix50.OrderQty.tag, 123)\n                              )\n                   ) \\\n        .build()\n    print(msg)\n```\nUFEGW provides a vast array of System and Business API `Message`\ninteractivity. To further explore these functions, please refer to **4.\nImplementation Guide - in particular sections 1.3 to 1.7**.\n\n# Interface\n\n## UFEedClient\n\nThe `UFEedClient` class is used as the interface to make both System and\nBusiness API calls to the UFEGW. Sessions between `UFEedClient` and the\nUFEGW are made up of ZeroMQ PUB/SUB and REQ/REP sockets. The network\naddresses and message topics inherent to these sockets are configurable\nvia *UFEedClient.* In addition, the `UFEedClient` manages these UFEGW\nsessions on behalf of the user (after the user has successfully logged\nin).\n\nThe default instantiation of a `UFEedClient` object is expressed like\nso:\n\n```python\nuc = UFEedClient()\n```\n\nDoing so will result in a `UFEedClient` object which utilises the\ndefault connection string configuration. (These default values can be\nfound under *UFEGW CONNECTION STRINGS* in the consts.py file in section\n1.6 below). If the user wishes to define these connection strings, they\ncan do so by passing a dictionary:\n\n```python\nconn_strs =    {\n  \"subscriber\": \"tcp://127.0.0.1:5000\",\n  \"requester\": \"tcp://127.0.0.1:5001\",\n  \"subscribertopic\": \"mygw-publisher\"\n}\n\nuc = UFEedClient(conn_strs)\n```\n\nAfter a UFEedClient object has been created, it is in a ready state to connect to the UFEGW. In order to actually establish the connection, we call the start() function.\n\n```python\nuc.start()\n# do work ...\nuc.stop()\n```\n\nThe `UFEedClient` will now go ahead and connect to the UFEGW. This means\nit will actively receive published messages, and can make requests to\nthe UFEGW via the request() method. Note that by default uc.start() does\nnot do anything to specifically handle messages received from the UFEGW.\nOftentimes the user may wish to define a handling function that will\nautomatically be called when messages are received. A simple example\nwould be to print messages as they are received:\n\n```python\ndef subscriber_func(msg):\n    print(msg)\n\ndef responder_func(msg):\n    print(msg)\n\nuc.start(sub_func=subscriber_func, rep_func=responder_func)\n# ...\nuc.stop()\n```\n\nAnother approach may be to store messages in a list automatically as\nthey are received, and do something with them later:\n\n```python\nsubmsgs = []\nrepmsgs = []\n\ndef subscriber_func(msg):\n    submsgs.append(msg)\n\ndef responder_func(msg):\n    repmsgs.append(msg)\n\nuc.start(sub_func=subscriber_func, rep_func=responder_func)\n\n# list comprehension calling do_something() on msg objects in repmsgs\nres = [do_something(msg) for msg in repmsgs]\n\nuc.stop()\n```\n\nThe `UFEedClient` communicates with the UFEGW via *Messages* (see\nsection 1.5.2). In order to create a *Message,* the caller must specify\nits header values, like so:\n\n```python\n# message often created with long name, message type and service id\nservice_list = uc.create_message() \\\n    .set_long_name(\"service_list\") \\\n    .set_msg_type(MsgType.st_system) \\\n    .set_service_id(UFE_CMD_SERVICE_LIST) \\\n```\n\nAfter a `Message` is created, the caller will add the necessary fields\nfor the body of the *Message:*\n\n```python\nservice_list.add_field(UFE_CMD, UFE_CMD_SERVICE_LIST)\n```\n\nWhen the necessary fields have been added to the `Message`,\nthe caller may make a request() with the *Message.* (In this example the\ncaller has also decided to capture the response of this request in a\n`Message` object called \\\"response\\\"):\n\n```python\nresponse = uc.request(service_list)\n```\n\nWhen the necessary fields have been added to the `Message` (see above),\nthe caller may make a publish() the `Message`:\n\n```python\nuc.publish(msg)\n```\n## Message and Message Builder\n\nMessage is built using \\\"builder\\\" pattern. Message.Builder class\nprovides write access to the message, while Message provides read-only\naccess to mapped message content, i.e. fields and groups.\n\n```python\nmsg_builder = uc.create_message() \\\n    .set_name(\"abc\") \\\n    .add_field(1, 2)\n    ...\nmsg = msg_builder.build()\nassert msg[1] == 2\n\nmsg.new_builder() \\\n    .add_field(2, \"abc\") \\\n    .build()\nassert msg[1] == 2 and msg[2] = \"abc\"\n```\n\nThe `Message` class provides some wrapping logic around the internal\nWireMessage format utilised by the UFEGW. *Messages* are objects with\nwhich requests are made from the UPA to the UFEGW.\n\n*Messages* can be created via the `UFEedClient` create_message()\nfunction (see section 1.5.1 above). For example, a NewOrderSingle\nmessage:\n\n```python\n# recommended\nnos = uc.create_message() \\\n    .set_long_name(\"NewOrderSingle\") \\\n    .set_name(\"D\") \\\n    .set_type(MsgType.st_fixmsg) \\\n    .set_service_id(6) # service id is 6\n```\n\nOr created directly:\n\n```python\n# !outdated!\nlogin = SysMessage()\nlogin.longname = \"login\"\nlogin.service_id = UFE_CMD_LOGIN\n```\n\nThe add_group()/add_group_item() methods allow the caller to add groups\nand group content to the `Message`:\n\n```python\ng1 = Message.Builder.GroupRef()\nmsg.add_group(fix50.NoAllocs.tag, g1, lambda m, grp:\n            m.add_group_item(g1)\n                   .set_long_name(\"NoAlloc\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(1)\n                   .add_field(fix50.AllocAccount.tag, \"ALLOC1\")\n                   .add_field(fix50.AllocQty.tag, 50.) and\n```\n\nExpose the underlying WireMessage stored in the `Message` object:\n\n```python\nwm = msg.wire_message\n```\n\n`Message` objects are provide a printable internal representation (via\n*\\_\\_repr\\_\\_* and *\\_\\_str\\_\\_*):\n\n```\nprint(msg)\n```\n\nCreates an immutable Message with mapped fields and groups. Searching by\ntag is available via \\[ \\]\n\n```python\nmsg = uc.create_message() \\\n    .set_type(MsgType.st_fix) \\\n    .set_long_name(\"NewOrderSingle\") \\\n    .set_name(fix50.MsgType.NEWORDERSINGLE) \\\n    .add_fields([(fix50.ClOrdID.tag, \"123\"), (fix50.TransactTime.tag, now)])\n    .build()\nassert msg[fix50.ClOrdID.tag] == \"123\"\nassert msg[fix50.OrderID.tag] is None\n```\n\n# Constants\n\nThe UPA maintains a list of constant values that translate to integer\ncodes in the UFEGW. These integer codes are used to identify System API\nservices as well as general FIX functionality. Constants could be regenerated\nvia `consts_gen.py`. Some examples of the use of these codes in the above examples are:\n\n```python\nservice_list.add_field(UFE_CMD, UFE_CMD_SERVICE_LIST)\n```\nand\n```python\nnos.add_fields([\n    (COMMON_SYMBOL, \"BHP\"),\n    (COMMON_CLORDID, \"Ord01\"),\n    (COMMON_ORDERQTY, 100.),\n    (COMMON_PRICE, 10.25),\n    (COMMON_ORDTYPE, '1'),\n    (COMMON_SIDE, '0'),\n    (COMMON_TIMEINFORCE, '2'),\n    (COMMON_TRANSACTTIME, \"now\")])\n```\nA full list of these constants is available at `consts.py`.\n\n## FIX variants constants\n\nThe UPA provides constants for all stock FIX variants:\n\n```python\n# FIX50SP2 NOS creation\nfix42 = FIX42_Fields\nfix50 = FIX50SP2_Fields\n\ndef create_fix50_nos():\n    now = datetime.now()\n    g1 = Message.Builder.GroupRef()\n    g2 = Message.Builder.GroupRef()\n    nos = uc.create_message() \\\n        .set_type(MsgType.st_fix) \\\n        .set_long_name(\"NewOrderSingle\") \\\n        .set_name(fix50.MsgType.NEWORDERSINGLE) \\\n        .add_fields([(fix50.ClOrdID.tag, \"123\"),\n                     (fix50.TransactTime.tag, now),\n                     (fix50.ExecInst.tag, fix50.ExecInst.ALL_OR_NONE),\n                     (fix50.TimeInForce.tag, fix50.TimeInForce.FILL_OR_KILL),\n                     (fix50.Price.tag, 111.22),\n                     (fix50.OrderQty.tag, 33),\n                     (fix50.Side.tag, fix50.Side.BUY)]) \\\n        .add_field(fix50.OrdType.tag, fix50.OrdType.LIMIT) \\\n        .add_field(fix50.Account.tag, \"ACC1\") \\\n        .add_group(fix50.NoAllocs.tag, g1, lambda m, grp:\n            m.add_group_item(g1)\n                   .set_long_name(\"NoAlloc\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(1)\n                   .add_field(fix50.AllocAccount.tag, \"ALLOC1\")\n                   .add_field(fix50.AllocQty.tag, 50.) and\n            m.add_group_item(g1)\n                   .set_long_name(\"NoAlloc\")\n                   .set_type(MsgType.st_fixmsg)\n                   .set_seq(3)\n                   .add_field(fix50.AllocAccount.tag, \"ALLOC2\")\n                   .add_field(fix50.AllocQty.tag, 60.)\n                   )\n    return nos\n```\n\n# Logging\n\nIn order to generate a debug log, the UPA requires a file called\n\\\"upa.log\\\" to be present in the root directory from which the UPA is\nrun. If this file is present, the UPA knows to sink debug messages into\nthis file during its execution. If the file is not present, the UPA will\nnot execute any logging function.\n\nBe careful to ensure this file is not present when you do not wish to\ncapture logs; the UPA will have better performance when not executing\nany logging functions.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Fufeed_bindings_python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffix8mt%2Fufeed_bindings_python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffix8mt%2Fufeed_bindings_python/lists"}