为人类提供的pythonic金融信息交换(fix)客户端。

wtfix的Python项目详细描述


wtfix

为人类提供的pythonic金融信息交换(fix)客户端。

Build statusCoverage StatusPyPI version shields.ioPyPI pyversionsPyPI licenseCode style:black

项目重点和目标

  • 为Python3从头开始构建。

  • 包括电池-提供连接到修复服务器并开始发送和接收所需的一切 以分钟为单位的消息。提供以下项的默认实现:

    • 身份验证
    • 保持心跳
    • 序列号管理和重新发送请求
    • 消息存储和检索
  • 基于现代async and await实现的快速、易于理解的消息处理管道。

  • 易于扩展的体系结构-模块化的“应用程序”可以添加到管道堆栈中以添加自定义消息处理 例程或新的应用程序功能。

    PIPELINE_APPS=["my_app.apps.SecretAlgoTradingRecipe",# <-- Your application logic"wtfix.apps.api.RESTfulServiceApp",# REST API for sending messages"wtfix.apps.brokers.RedisPubSubApp",# Redis Pub/Sub broker for sending / receiving 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.store.MessageStoreApp",# Store messages (caching or persistence)"wtfix.apps.utils.InboundLoggingApp",# Log inbound messages"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.OutboundLoggingApp",# Log 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]
  • 消息可以缓存在内存中,也可以保存到redis消息存储中供以后检索。或者您可以添加 您自己的消息存储解决方案使用提供的接口。

  • 直接从管道、通过使用rest api的第三方应用程序或通过将消息发布到 用于立即交付的redis发布/子频道。

  • 提供一个方便的@on装饰器,用于细粒度控制哪些应用程序将响应哪些类型的消息:

    fromwtfix.apps.baseimportMessageTypeHandlerApp,onfromwtfix.protocol.commonimportMsgTypefromwtfix.confimportsettingslogger=settings.loggerclassSecretAlgoTradingRecipe(MessageTypeHandlerApp):@on(MsgType.Logon)# Only invoked when 'Logon (type A)' messages are received.defon_logon(self,message):self.send_security_definition_request()returnmessagedefon_receive(self,message):# Invoked for every type of message.logger.info(f"Received message {message}!")
  • 提供自定义的FieldFieldMap类型,用于处理固定标记和字段值。这些类型是“Python型”, 实现许多标准协议,并在将它们集成到现有Python中时表现出人意料 代码。

  • 一种简单的消息标记语法,具有各种方便的方法,用于快速访问 已使用的消息属性。

    >>>fromwtfix.messageimportadmin>>>fromwtfix.protocol.commonimportTag# Instantiate a new 'Logon' message>>>logon_msg=admin.LogonMessage("my_username","my_password",heartbeat_int=30)# Short, concise string representation>>>str(logon_msg)'A: {(35, A) | (98, 0) | (108, 30) | (553, my_username) | (554, my_password)}'# Pretty print tag names by using the 't' formatting option>>>f"{logon_msg:t}"'Logon (A): {MsgType (35): A | EncryptMethod (98): 0 | HeartBtInt (108): 30 | Username (553): my_username | Password (554): my_password}'# Example of getting the message type>>>logon_msg.type'A'# Example of getting the message type name>>>logon_msg.name'Logon'# Look up the sequence number>>>logon_msg.seq_num1# 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 numberField(108,'30')>>>logon_msg[Tag.HeartBtInt]# Using the tag name as per the FIX specificationField(108,'30')>>>logon_msg.HeartBtInt# Using tag name shortcutField(108,'30')
  • 一种基于实用unicode sandwich的编译码方法 消息意味着您不需要直接处理字节序列。

    fromwtfix.message.fieldimportFieldfromwtfix.message.messageimportgeneric_message_factory# Create a new Message from a byte sequence received over the wire>>>fields=Field.fields_frombytes(b"35=A\x0198=0\x01108=30\x01553=my_username\x01554=my_password\x01")>>>logon_msg=generic_message_factory(*fields)>>>str(logon_msg)'A: {(35, A) | (98, 0) | (108, 30) | (553, my_username) | (554, my_password)}'# Fields are tuples of (tag, value) pairs>>>username=logon_msg.Username>>>username.tag553>>>username.value"my_username"# Fields behave just like Python's built-in types, and most operations can be performed directly# on a field's 'value' attribute.>>>username+="_123">>>usernameField(553,'my_username_123')
  • 需要时访问底层字节序列:

    >>>bytes(logon_msg)b'35=A\x0198=0\x01108=30\x01553=my_username\x01554=my_password\x01'
  • 向消息添加字段很容易:只需分配标记值即可:

    >>>logon_msg.PossDupFlag="Y">>>f"{logon_msg:t}"'Logon (A): {MsgType (35): A | EncryptMethod (98): 0 | HeartBtInt (108): 30 | Username (553): my_username | Password (554): my_password | PossDupFlag (43): Y}'# Most FIX field values can be cast to their corresponding Python built-in type>>>bool(logon_msg.PossDupFlag)isTrueTrue
  • 一种非常宽容的重复消息标记组的方法:

    fromwtfix.protocol.commonimportTag,MsgTypefromwtfix.message.messageimportgeneric_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.dataOrderedDict([(35,Field(35,'8')),(136,Group(Field(136,'2'),Field(137,'10.0'),Field(139,'2'),Field(137,'20.0'),Field(139,'A')))])# Get 'NoMiscFees' group>>>group=msg.NoMiscFees>>>f"{group:t}"'[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.size2# Retrieve the second group instance>>>group.instances[1]FieldList(Field(137,'20.0'),Field(139,'A'))# Without a pre-defined group template, WTFIX falls 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.data[Field(35,'8'),Field(136,'2'),Field(137,'10.0'),Field(139,'2'),Field(137,'20.0'),Field(139,'A')]

开始

  • 最好在python虚拟环境中安装项目的依赖项(例如pip install -r requirements/local.txt) 专门为此目的而创造的环境。

  • 使用pytest运行测试套件以验证安装。

  • 在项目的根目录中创建至少包含以下配置设置的.env文件:

    # Supports different configuration settings for local development, staging, or production environments.WTFIX_SETTINGS_MODULE=config.settings.localHOST=# Required. The FIX server hostname or IP addressPORT=# Required. The port on the FIX server to connect toSENDER=# 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.
  • 使用python runclient.py启动修复客户端。默认实现将登录到fix服务器并保持稳定的心跳。

  • 使用Ctrl-C退出。这将触发在管道终止之前发送的Logout消息。

项目资源

灵感来源于

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
如何加载。java文件到编译器?   java写入文件和I/O   java日历返回错误的当前日期安卓   if语句if变量等于1(java)   java很难让Joda有时间使用我的bukkit插件   json如何创建基于java的应用程序,该应用程序将从网站获取输入   java如何在多页中打印大型JPanel   java my spinner在添加从firebase数据库中选择的多个值时被覆盖   java用Play框架覆盖HTTP方法   试图获取所有用户时,java错误请求400。Dto和表格   java支持bean加载图像后javascript自动调用JS函数   java在MTOM的@StreamingAttachment(Metro)中,Parsely的意思是什么   具有可抢占线程队列的多线程java执行器   Java Opencv connectedComponentsWithStats   java如何向数组中添加2D数组的数目   eclipse为什么我的Java Zork克隆不能正确循环?   java在哪里放置定制的第二个web。xml   java如何更改最近应用列表中显示的快照?   java Browser#getText()返回空字符串