什么是FIX协议

FIX协议是由国际FIX协会组织提供的一个开放式协议,目的是推动国际贸易电子化的进程,在各类参与者之间,包括投资经理、经纪人,买方、卖方建立起实时的电子化通讯协议。

中文名

FIX协议

外文名

Financial Information eXchange (FIX®) Protocol

FIX协议的目标是把各类证券金融业务需求流程格式化,使之成为一个个可用计算机语言描述的功能流程,并在每个业务功能接口上统一交换格式,方便各个功能模块的连接。

1 简介

FIX会话协议与选择用于电子数据传递的物理介质(铜缆,光纤,卫星传输等)及传输协议规范(X.25,同步,TCP/IP等)无关。它提供了一个消息传递的可靠数据流。直到2006年10月,FIX会话协议与FIX应用协议一道,为用户提供了一个可靠的传输FIX应用消息的传输机制。

FIX会话层与数据传输相关,而FIX应用层则定义了商业相关的数据内容。

2006年10月,FPL’s Global Techenical Committee 引入了一个新的框架,将FIX会话层协议从FIX应用层协议分离开来。这就使应用协议消息可以使用任何适的会话传输技术进行传送,而FIX会话层协议是这些可选的协议中的一个。在新的框架下,GTC引入了一个新的别名,之后FIX会话层协议版本为FIXT.x.y,第一个版本为FIXT1.1。

2 FIXML及其它基于XML数据的传输

尽管FIX会话协议的标准头(Standard Header),标准尾部(Standard Trailer)和管理消息是基于“tag=value”语法的,但它能够支持传输FIXML及其它基于XML的数据。FIXML及其它XML数据被夹在FIX标准头与FIX标准尾部中间,并通过标准头的XmlDataLen域指定其内容长度,XmlData域包含其具体的数据。这样,FIX引擎可以通过多年使用的,可靠的,实时地异步传输机制传送FIXML及其它XML数据。当MsgType域值为’n’时,代表传输的数据为FIX未在MsgType中定义的XML数据。

3 FIX消息传送

3.1 Sequence Numbers 序列编号

所有的FIX消息都由一个唯一的序列号进行标示。序列号在每一个FIX会话开始时被初始化为1,并在整个会话期间递增。监控序列号可以使会话参与者识别和处理丢失的消息,当在一个FIX会话中重新连接时能够优雅地进行应用程序同步。

每个会话将建立一组互不依赖的接受和发送序列。会话参与者将维护一个赋予发送消息的序列和一个监控接受消息的消息块间隙序列号。

3.2 Heartbeats 心跳信号

在消息交互期间,FIX应用程序将周期性产生Heartbeat心跳消息。该心跳消息可以监控通信链路状态及识别接受序列号间隙。发送Heartbeat的周期间隔由会话发起者使用在Logon消息中HeartBtInt域进行定义。Heartbeat心跳消息的时间间隔应当在每一个消息发送后复位,即发送一个消息后,在间隔给定的时间内无其它消息发送则发送一个Heartbeat心跳消息。HeartBtInt的值应当被会话双方认同,由会话发起方定义并由会话接收者通过Logon消息进行确认。同一个HeartBtInt被会话双方——登录的发起者和登录的接受者共同使用。

3.3 Ordered Message Processing 消息序列处理

FIX协议假设消息在所有参与者间完全按照顺序进行传输。协议的实现者在设计消息间隙填充处理时应当考虑这个假设。有两种方式处理消息间隙。每一个都要求所有的消息时最后一个接收消息的后续消息或在维护一个所有新消息有序序列时,请求特定丢失消息。比如:接收方丢失了5个消息块中的第二个,程序能忽略第3到第5个消息,产生一个对消息2到消息5的重传请求,或者从消息2到无穷大消息编号的重传请求。另外的方式是暂时存储消息3到消息5,仅要求重传消息2。对于这两种方式,消息3到消息5都不应该先于消息2进行处理。

3.4 Possible Duplicates 可能的重复

当一个FIX引擎对一个消息是否成功地被指定的目标接收或者当对一个重传请求进行响应时,将会产生一个可能的消息复制。这个消息将用同样的序列进行重新传送,此时在头部的PossDupFlag域将会被设置为‘Y’。接收端程序负责处理该重发消息,可以作为一个新消息进行处理,或者根据实际情况忽略该消息。所有重传请求的响应消息都将包含其值为‘Y’的PossDupFlag域。没有PossDupFlag域或者PossDupFlag域为‘N’的消息应被当作初始传送消息。注意,一个PossDupFlag值为‘Y’的重传消息需要重新计算其CheckSum值。一个可能的复制消息里发生变化的域包括:CheckSum,OrigSendingTime,SendingTime,BodyLength和PossDupFlag。加密相关域(SecureDataLen和SecureData)也必须被重新构造。

3.5 Possible Resends 可能的重传

模糊的应用层消息可能随同PossResend标志被重传。当一个指令没有在规定时间长度内进行确认或者终端用户挂起该指令没有进行传送时这种方法非常有用。接收程序必须识别此标志,并质疑其内部域以确定该指令是否在之前已经被接收过。注意,可能的重传消息将包含与原始消息相同的数据体,但包含PossResend标志和一个新的序列号。此外,CheckSum和与加密相关的域值需要重构。

3.6 Data Integrity 数据完整性

消息数据内容的完整性可以参用两种方式来验证:消息长度和效验码检查。

程序通过计算BodyLength域到(并包含)在CheckSum标记(“10=”)后的分界符的字符数与在BodyLength中标示的消息长度进行比较来完成完整性效验。

ChekSum完整性检查,通过计算从域“8=” 中“8”开始,包括紧跟在CheckSum标记域的分界符每个字符的2进制和同CheckSum进行比较得到。

3.7 Message Acknowledgment 消息确认

FIX会话协议基于一个优化模型。普通的数据传送(无单个消息确认)被假设为通过消息序列间隙进行错误识别。每个消息由一个唯一的序列号进行标示。接收端应用程序负责监控接收消息序列号以识别消息间隙并产生重传请求。

FIX协议不支持单个消息的确认。然而,多数应用消息需要显示地应用层接收和拒绝。

3.8 Encryption 加密

敏感数据在公众网络上的传输建议采用数据加密技术来掩饰应用消息。

加密算法由连接双方共同协商。

一个消息的任何一个域可以被加密并放在SecureData域中。然而,一些显示的标志域必须采用明文进行传输。为确保完整性,明文域可以在SecureData域中重复。

当使用加密时,建议但不是必须,所有的消息体都进行加密。如果一个消息中的重复组数据中的部分数据要加密,这个重复组必须全部进行加密。

预先协商好的加密算法在Logon消息中进行声明。

4 SESSION PROTOCOL会话协议

一个FIX会话定义为一个在连接双方间的的带有连续序列号的有序消息双向传输流。 单个FIX会话能够跨越多个连续(不是并行的)的物理连接。在一个维持的,单独的FIX会话中,参与方能够多次连接和断开连接。连接的参与方必须根据单个系统及时间区域需求,公共协商会话的开始和结束。无论什么原因,重新设置接收和发送序列号为1,意味着一个新的FIX会话的开始。

建议一个新的FIX会话在每24小时期间建立一次。可以维持24小时的连接和通过设置在Logon消息中的ResetSeqNumFlag建立一套新的序列号。

FIX会话协议基于一个优化模型。普通的数据传送(无单个消息确认)被假设为通过消息序列间隙进行错误识别。这个部分详细介绍了FIX会话层和消息序列间隙的实现。

以下术语将在这部分使用:

Valid FIX Message有效FIX消息 是按照协议正确生成,包含有效消息体长度和效验域的消息。

Initiator发起者 建立通信连路,通过发送初始Logon消息发起会话的参与方。

Acceptor接收方 FIX会话的接收方。负责执行第一层次的认证和通过传输Logon消息的确认正式声明连接请求被接受。

FIX ConnectionFIX连接 由3部分组成:logon登录,message exchange消息传输,和logout注销。

FIX SessionFIX 会话 由一个或多个FIX Connection FIX连接组成。意思是一个FIX会话可以有多次登录。4.1

4.1 Logon 登录

建立一个FIX连接,分别包含3个操作:创建通信层链路,接收者认证/接受发起者和消息同步(初始化)。连接流程如下:

l会话发起者同会话接收者建立通信链路。

l发起者发送一个Logon消息。接收者检查Logon消息,认证发起者身份。Logon消息包含支持之前双方协商好的认证方法所必须的数据。如果发起者被成功认证,接收者用一个Logon消息进行响应。如果认证失败,会话接收者将关闭链接,之前可以选择发送一个Logout消息以提示认证失败的原因。这个Logout消息不是必须发送的,如果这样做将会占用该会话的一个序列号,在某些情况下会有问题,如:在会话期进行多次Logon时。发起者可以在Logon消息后紧接着开始发送消息。然而,接收者可能没有准备好接收它们。发起者必须等待接收者发送的Logon确认消息才能认为完全建立回话。

在发起者认证通过之后,接收者立即响应一个Logon确认消息。依据会话使用的加密方法,这个Logon消息可以,也可以不报还同样的会话密钥。发起者方将把接收方返回的Logon确认消息视为一个FIX会话建立的标志。如果会话接收方选择干边会话加密密钥,会话的发起方必须发送一个Logon消息到对方以确认密钥改变请求。这样,能让会话接收者知道发起者何时开始使用新的会话密钥。双方有责任诊断和避免在此阶段出现无限循环。

l认证完成之后,发起方和接收方必须在发送任何查询或新消息之前通过询问MsgSeqNum域来同步其消息。将Logon消息中的MsgSeqNum同内部监控的下一个希望的序列号进行比较可以指示任何的消息间隙。此外,发起方能通过比较确认Logon消息的MsgSeqNum及下一个期望值来侦测消息的间隙。后面的消息恢复部分文当将介绍如何进行消息间隙的处理。

l推荐引擎应当在一个临时的队列中存储发送消息系列,在消息间隙关闭时处理它们。这样可以阻止对n->m,n->m+1,n->m+2,…的重发请求,这些请求能导致许多PossDupFlag=’Y’的消息。

l当使用ResetSeqNumFlag来维持24小时连接和建立一套新的序列号时,应该按照下面的方式进行处理。双方应当协商一个复位时间以及此过程的发起方。注意,ResetSeqNum过程的发起方可以与Logon过程的发起方不是同一个。一方通过发送一个TestRequest初始化此过程,并等待一个Heartbeat的响应以确认没有序列号间隙。一旦收到Hearbeat,发起者应当发送一个带有ResetSeqNumFlag值为‘Y’和MsgSeqNum为1的Logon消息。接收方应当发送一个带有ResetSeqNumFlag值为‘Y’和MsgSeqNum为1的Logon消息作为响应。此时,任何一方发送的新的消息可以从MsgSeqNum编号为2开始计数。应当注意,一旦发起方发送设置了ResetSeqNumFlag的Logon消息,接收者必须遵守这个请求,并且作为最后一个序列号发送的消息的“yesterday”值不再可用。如果此过程的初始化未被正确执行,链接应当被关闭,手动关闭方式将会被介入。

4.2 Message exchange 消息交换

初始化过程之后,正常的消息交换将开始。所有有效的消息格式的细节将在“Adminitrative Message ”管理消息和“Application Messages”应用消息部分介绍。

4.3 Logout

正常的消息交换的终止将通过交换Logout消息来完成。换句话说,通信的终止应被看作异常并作为一个错误进行处理。没有接收到Logout消息的会话终止应当作参与方的注销。

推荐发送一个TestRequest消息强制请求从对方获取Heartbeat。这样可以帮助判断没有序列间隙。

在实际的会话关闭前,Logout的发起者应等待对方响应一个Logout确认消息。这种方式给接收者在需要时一个执行任何间隙填充错作的机会。一旦ResendRequest消息被接收,接收者应发起Logout操作。会话可以终止,如果接收者在适当的时间表内没有响应。

注意:注销不会影响任何指令的状态。左右活动的指令将继续有机会在注销后被执行。

4.4 Message Recovery 消息恢复

在FIX会话初始化过程及会话过程中,通过跟踪接收序列号,消息间隙的出现将会被侦测到。以下部分介绍了如何恢复消息。

如前所述,每个FIX参与方必须为FIX会话维护两个序列号,一个是接收序列号,一个是发送序列号,两者都在建立FIX会话开始时初始化为1。每个消息被赋予一个唯一的序列号值,并在消息发送后递增。此外,每个收到的消息都有一个唯一的序列号,接收序列号计数器在收到每个消息后将会被递增。

当接收序列号与所希望得到的的正确序列号不匹配时,必须采取纠错处理。注意,SeqReset-Reset消息(对比正常德重传请求处理,仅用于从严重灾难中进行恢复)对于这个规则来说是个例外,它应当被处理,而不用考虑其MsgSeqNum值。如果接收消息的序列号小于期望接收的序列号,并且PossDupFlag值未被设置,这表示出现一个严重错误。强烈推荐终止会话,启用手动干预。如果接收消息的序列号大于期望值,这表明有丢失消息,并通过Resend Request 重传请求这些消息的重新传输。

注意:后续的请求者(requester)指为请求发送方;重传者(resender)指请求的响应方。消息重传和消息同步叫做“间隙填充”(gap filling)。

再收到重传请求的情况下,重传者可以用三种方式之一进行响应:

1、按照原先序列号,顺序重新传输请求消息,并将PossDupFlag设置成‘Y’(除管理消息不被重传,并需要一个SeqReset-GapFill外)。

2、发出一个SeqReset-GapFill和一个PossDupFlag值为‘Y’的消息代替管理和应用消息的重传。

3、发出一个SeqReset-GapFill和一个PossDupFlag值为‘Y’的消息强制进行序列号的同步。

通常情况下使用1,2的组合。而3则仅用于从灾难中,且不能通过间隙填充模式进行数据恢复的场景。

在间隙填充过程中,一些管理消息应不被重传。取而代之的是一个特殊的SeqReset-GapFill消息将被产生。不能被重传的消息有:Logon,Logout,ResendRequest,Heartbeat,TestRequest和SeqReset-Reset,SeqRest-GapFill。

所有的FIX协议的实现,都必须监控接收消息以侦测管理消息的重传(PossDupFlag值标示冲传)。当接收到这些消息时,应当仅对序列号完整性进行处理,而商业/应用消息应被跳过(如,不处理基于ResendRequest请求的重传间隙填充)。

如果有连续的管理消息需要重发,推荐只发送一个SeqRest-GagFill消息。SeqRest-GapFill消息的序列号为下一个希望发送的序列号。GapFill消息的NewSeqNo域包含了这些管理消息中的最高序列号值加上1。如,在一个重传错作中,有7个顺序的管理消息等待重发。它们的序列号从9到15。替代7个间隙填充消息,一个SeqReset-GapFill消息将会被发送。此间隙填充消息的序列号被设置为9,因为远端把序列号9作为下一个期望接收的消息。GapFill消息的NewSeqNo的值为16,表示下一个发送消息的序列号。

序列号检查是FIX会话管理最重要的部分。然而,一些FIX消息的序列号处理存在一些不同,下表列出了接收序列号比期望接收序列号大时的处理方法。

注意:除了Reset消息外的所有情况,如果接收序列号小于期望接收序列号,且PossDupFlag未被设置,FIX会话应被终止。在关闭会话前,一个Logout消息和一些描述性文本应被发送给对端。

消息类型

序列号不必陪时的处理

Logon

必须是传送的第一个消息。认证和接受连接。如果一个消息间隙在logon序列号中被检测到,那么在返回一个Logon确认消息后,发送一个ResendRequest

Logout

如果一个消息间隙被检测到,在确认Logout消息发送之后再发起一个ResendRequest以补偿所有丢失消息。不要终止会话。Logout消息的发起方负责终止会话。这样可以允许Logout发起者对任何ResendRequest消息进行响应。

如果是Logout的发起方,那么这是一个Logout确认消息,必须在收到之后立即终止会话。

唯一一个“不终止会话”的例外是无效的Logout尝试。会话接收者有权发送一个Logout消息并立即终止会话。这样可以减少未经授权的连接尝试。

ResendRequest

首先执行重传处理,接着按照自己的顺序发送ResendRequest消息用以填充接收消息间隙。

SeqReset-Reset

忽略接收序列号。SeqReset消息的NewSeqNo将包含下一个传送消息的序列号。

SeqReset-GapFill

发送一个返回ResendRequest。间隙填充消息行为与SeqReset消息相似。然而,确认没有消息被连续地跳过是非常重要的。这意味着GapFill消息必须按顺序接收。一个无序的GapFill消息时则表明一种异常情况。

所有的其他类型消息

执行间隙填充操作。

4.5 Logon消息的NextExpectedMsgSeqNum处理

NextExpectedMsgSeqNum (789)域从FIX4.4开始加入到Logon消息中,用以支持一个FIX会话的重同步。这个新方法是可选的。其适用必须得到参与方的共同同意。

NextExpectedMsgSeqNum (789)的使用如下:

在Logout的请求阶段,会话发起者把期望从会话接收者的下一个MsgSeqNum(34)的值赋于NextExpectedMsgSeqNum (789)。通常,Logon请求发送头部MsgSeqNum(34)的值表示下一个序列号值。

会话接收者校验Logout请求,包括NextExpectedMsgSeqNum (789),确认没有间隙存在。然后,构建一个Logout响应,其NextExpectedMsgSeqNum (789)值包含了期望从会话发起者接收到的MsgSeqNum(34)值。如果那是一个期望的序列号,MsgSeqNum(34)会被递加。发送头部MsgSeqNum(34)按照常规进行构造。

会话发起者等待发送应用消息直到接收到Logon响应。当接收到Logon响应,发起者要校验该响应和NextExpectedMsgSeqNum (789)以确认没有消息间隙。

会话双方采用以下方式对收到的NextExpectedMsgSeqNum (789)进行处理:

1、如果等于下一个期望序列号值,则从该编号开始发送新消息。

2、如果小于下一个期望序列号值,从之前最后传送的消息到NextExpectedMsgSeqNum (789)按顺序恢复所有丢失消息,然后间隙填充将跨过Logon使用的序列号,使用比原Logon大1的序列号继续发送新的排队消息。

3、如果大于下一个期望序列号值,发送Logout终止会话。

除了希望自动进行那个间隙填充外,任何一方应给予接收的Logon消息的MsgSeqNum(34)产生一个ResendRequest。如果间隙由Logon消息的MsgSeqNum(34)导致,接收逻辑应希望自动填充间隙以恢复间隙的任何消息序列。

4.6 Standard Message Header标准消息头

任何管理和应用消息都紧跟在一个标准头部之后。头部标示了消息的类型、长度、目标、序列号,来源及创建时间。

两个域用于协助重发消息。PossDupFlag为‘Y’表明一个会话级事件导致的消息重传(如,使用同一个序列号重传一个消息)。PossResend为‘Y’表明使用新的序列号重新发出一个消息(如,重新发送一个Order指令)。接收程序应按下述方法进行处理:

PossDupFlag 如果一个消息的序列号表明之前已经收到,忽略该消息;如果没有,进行正常处理。

PossResend 将消息传递给应用程序以判断是否之前已经收到(如,验证Order的ID号和参数)。

Message Routing Details – One Firm-to-One Firm (point-to-point)

Message Routing Details – One Firm-to-One Firm(Point-to-point)消息路由-点对点

下表展示了使用SenderCompID,TargetCompID,DeliverToCompID,和OnBehalfOfCompID在两个企业间的一个单一的FIX会话。假设A为卖方,B为买方

SenderCompID

OnBehalfOfCompID

TartgetCompID

DelivrToCompID

A 到B

A

B

B 到 A

B

A

Message Routing Details-Third Party Message Routing消息路由-第三方消息路由

FIX会话协议具备支持一个FIX会话包含多个参与这者的能力。包括1对多,对对1,或者1对1的形式。此外,一些第三方可以与其它第三方连接,在消息发起者和最终的接收者间形成一个多跳的链。SenderCompID,TargetCompID,DeliverToCompID,和OnBehalfOfCompID域用于路由消息。

当一个第三方在另一个企业中间发送一个消息时(使用OnBehalfOfCompID),可以选择在NoHops重复组中加入它的细节信息。这个重复组构建了消息在第三方重新发送的的一个历史列表。NoHops重复组不用于协助路由,而是为接收消息方对参与的第三方进行审计时提供痕迹。当一个第三方转发一个消息给下一跳(可能是最终接收者,或另一个第三方)时,此第三方可以将其跳信息加到NoHops重复组中(如,将其SenderCompID作为HopCompID,其SendingTime作为HopSendingTime,将接收消息的MsgSeqNum或其他引用数据作为HopRefID )。

注意:如果OnBeHalfOfCompID或DeliverToCompID消息源识别/路由方法在一个FIX会话中使用,那么该方法必须在通过此会话传送的所有消息中使用。

下表提供了在单一FIX会话中表名多个企业参与时SenderCompID,TargetCompID,DeliverToCompID和OnBehalfOfCompID的使用方法。假设A=卖方,B和C表示买方,Q=第三方。

SenderCompID

OnBehalfOf

CompID

TargetCompID

DeliverTo

CompID

DeliverTo

CompID

HopSendingTime

从A通过Q到B

1 A到Q

A

Q

B

2 Q到B

Q

A

B

Q

A的发送时间

B通过Q响应A

1 B到Q

B

Q

A

2 Q到A

Q

B

A

Q

B的发送时间

A通过Q发送到B和C

1A到Q

A

Q

B

2Q到B

Q

A

B

Q

A的发送时间

3A到Q

A

Q

C

4Q到C

Q

A

C

Q

A的发送时间

B和C通过Q发送到A

1B到Q

B

Q

A

2Q到A

Q

B

A

Q

B的发送时间

3C到Q

C

Q

A

4Q到A

Q

C

A

Q

C的发送时间

注意,由于在一个给定的FIX会话中,一些域(如在一个新Order指令中的ClOrdID)必须是唯一的。因此,当使用OnBehalfOfCompI(或DeliverToCompID)进行寻址时,推荐的做法是在OnBehalfOfCompI(或DeliverToCompID)之后加上源地址。这样,如果A发送消息到Q的ClOrdID为“123”,那么Q将“A-123”作为ClOrdID的值发送到C,以确保唯一性。

标准消息头部如下

Standerd Message Header

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

8

BeginString

Y

FIXT.1.1(不能加密,必须是消息的第一个域)

9

BodyLength

Y

(不能加密,必须是消息的第二个域)

35

MsgType

Y

(不能加密,必须是消息的第三个域)

1128

ApplVerID

N

使用SP标示方法标明应用版本。ApplVerID用于一个特定消息的场景

1129

CstmApplVerID

N

用于支持客户共同协商认可的功能

49

SenderCompID

Y

(不能被加密)

56

TargetCompID

Y

(不能被加密)

115

OnBehalfOfCompID

N

通过第三方发送消息时交易参与者企业ID(可以内置于坚密数据部分)

128

DeliverToCompID

N

通过第三方发送消息时交易参与者企业ID(可以内置于坚密数据部分)

90

SecureDataLen

N

用于标示消息加密部分的长度时必选(不能加密)

91

SecureData

N

消息体加密时必选。总紧跟在SercureDataLen域之后

34

MsgSeqNum

Y

(可以内置于坚密数据部分)

50

SenderSubID

N

(可以内置于坚密数据部分)

142

SenderLocationID

N

发送者的LocationID(如,地理位置和或席位(desk))(可以内置于坚密数据部分)

57

TargetSubID

N

为管理消息保留,不针对一个特定用户。(可以内置于坚密数据部分)

143

TargetLocationID

N

交易参与者LocationID(如,地理位置和或席位(desk))(可以内置于坚密数据部分)

116

OnBehalfOfSubID

N

交易参与者SubID,用于通过第三方传送消息。(可以内置于坚密数据部分)

144

OnBehalfOfLocation

N

交易参与者的LocationID(如,地理位置和或席位(desk))(可以内置于坚密数据部分)

129

DeliverToSubID

N

交易参与者SubID,用于通过第三方传送消息。(可以内置于坚密数据部分)

145

DeliverToLocationID

N

交易参与者的LocationID(如,地理位置和或席位(desk))(可以内置于坚密数据部分)

43

PossDupFlag

N

当重传消息时总是必选,无论是由发送方系统提示,还是作为重传请求的响应结果。(可以内置于坚密数据部分)

97

PossResend

N

当消息可能是另一个消息的复制消息且使用一个不同的序列号时必选。(可以内置于坚密数据部分)

52

SendingTime

Y

(可以内置于坚密数据部分)

122

OrigSendingTime

N

当作为一个ResendRequest的返回结果重传消息时必选。如果数据不可用,则与SendingTime值相同(可以内置于坚密数据部分)

212

XmlDataLen

N

当标示XmlData消息体长度时必选。(可以内置于坚密数据部分)

213

XmlData

N

包含XML格式的消息块(如FIXML)。总紧跟在XmlDataLen后。(可以内置于坚密数据部分)

347

MessageEncoding

N

在消息的“Encode”域中使用的消息编码格式(非ASCII编码)。使用编码域时必选。

369

LastMsgSeqNumProcessed

N

FIX引擎到收到、下游应用(如交易系统、指令路由系统)处理过的的最后一个MsgSeqNum值。在每个消息发送时确定。用于参与方侦测消息积压(未被处理?)。

627

NoHops

N

-〉

628

HopCompID

N

629

HopSendingTime

N

-〉

630

HopRefID

N

4.7 Standard Message trailer标准消息尾部

每个消息,管理或应用消息,以一个标准尾部结束。该尾部用于分割消息并包含描述Checksum值的3个数字字符。

Standard Message Trailer

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

93

SignatureLength

N

如果尾部包含签名则必选。注意,不能包含在SecureData域中。

89

Signature

N

注意,不能包含在SecureData域中。

10

CheckSum

Y

(不能加密,总是消息的最后一个域)

5 ADMINISTRATIVE MESSAGES管理消息

管理消息强调协议的应用需求。以下部分内容描述了每个管理消息,提供消息的图表。

管理消息由连接各方产生。

5.1 Heartbeat 心跳消息

心跳消息监控通信链路的状态,用于识别一连串消息中最后没有收到的消息。

当如果一个FIX连接的任何一方在超过HeartBtInt规定的时间间隔后没有收到任何数据,将发送一个Hearbeat消息。当如果一个FIX连接的任何一方在超过HeartBtInt规定的时间间隔加上一些传输时间后没有收到任何数据,将发送一个Test Request测试请求消息。如果在超过HeartBtInt规定的时间间隔加上一些传输时间后仍然没有收到Heartbeat消息,那么应视为连接断开并应采取纠错处理。如果HearBtInt设置为0,则不会产生常规的Heartbeat消息。注意,一个测试请求消息能够不间隔HeartBtInt的值被发送,用于强制请求一个Heartbeat消息。

Heartbeat消息作为测试请求消息的响应,必须包含在请求测试消息中的TestReqID值,用于验证Heartbeat消息是测试请求消息的响应而不是常规超时的响应。

Heatbeat消息格式如下:

Heartbeat

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=0

112

TestReqID

N

当Heartbeat消息是Test Request消息的响应时必选

StandartTrailer

Y

5.2 Logon登陆消息

Logon消息认证一个连接到一个远程系统的用户。Logon消息必须是应用程序用于请求初始化一个FIX会话的第一个消息。

HeartBtInt(108)域用于申明产生心跳消息的时间间隔,连接双方使用形同的HeartBtInt值。其值应被双方企业一致同意,由Logon消息发起者初始化,并被Logon接收者回应。

当接收到Logon消息,会话接收者将认证参与者的连接请求,并发出一个Logon消息确认连接请求被接受。这个确认Logon消息也能用于发起者验证连接已经与对端正确建立。

在接收到Logon消息后会话接收者必须立即准备好开始处理消息。会话发起者可以选择在收到Logon确认消息前传输FIX消息,但推荐在收到返回的Logon消息后,完成加密秘要协商后进行正常的消息传输。

确认Logon消息可以用于加密秘要协商。如果一个会话密钥被认为是弱秘要,一个推荐的新的更加强状的会话秘要将在Logon消息中返回。这仅在加密协议允许秘要协商时有效。

Logon消息能用于确定支持的消息最大长度MaxMessageSize(能用于控制将大消息进行分节的规则)。也能用于确定双方接收和发送所支持的消息的类型MsgType。

下表是Logon消息的格式:

Logon

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=A

98

EncryptMethod

Y

不能加密

108

HearBtInt

Y

双方使用同一个值

95

RawDataLength

N

由一些认证方法使用

96

RawData

N

由一些认证方法使用

141

ResetSeqNumFlag

N

表示FIX会话双方应复位序列号

789

NextExpectedMsgSeqNum

N

可选,双方检测和恢复消息间隙的候选协商方法参照“Logon消息NextExpectedMsgSeqNum

383

MaxMessageSize

N

能用于指定所支持接收消息的最大字节数

384

NoMsgTypes

N

指定RefMsgTypes的重复次数

-〉

372 RefMsgType

N

制定一个特定的、被支持的MsgType。如果NoMsgTypes大于0时必选。从Logon消息的发送者角度应当被指定。

-〉

385 MsgDirection

N

表明所支持MsgType的接收或发送方向。当NoMsgTypes大于0时必选。从Logon消息的发送者角度应当被指定。

-〉

1130 RefApplVerID

N

在会话层指定一个消息的应用SP发行版本。SP发行时付值的枚举域。

-〉

1131 RefCstmApplVerID

N

再会话层指定一个用户扩展消息的应用。

464

TestMessageIndicator

N

用于指定此FIX会话将发送、接收 “Test” “production”消息。?

553

Username

N

554

Password

N

注意,没有传输层加密时存在最小安全性。

1137

DefaultApplVerID

Y

由FIXT承载的FIX的默认版本。

StandartTrailer

Y

5.3 Test Request 测试请求消息

测试请求消息强制对方发送一个Heartbeat消息。测试请求消息检查序列号或验证通信线路状态。对端应用程序响应一个包含TestReqID的Heartbeat消息。

TestReqID用于检查对端应用是依据测试请求消息产生的Heartbeat消息,而不是通常的超时。对端应用程序将TestReqID包含在响应Heartbeat消息中。任何字符串可以被用于TestReqID(一个建议是使用时间戳字符串)。

测试请求消息格式如下:

Test Request

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=A

112

TestReqID

Y

不能加密

StandartTrailer

Y

5.4 Resend Request 重传请求消息

重传请求消息由接收用用程序发送用于开始消息的重传。这个功能在序列号间隙被侦测到时,在接受应用程序丢失消息时,或者作为一个初始化处理功能时非常实用。

重传请求消息能用于请求一个单一消息,一定范围内的消息,或者一个特定消息的所有后续消息。

注意:发送应用程序可能希望考虑重传消息的消息类型。如:如果在重传序列中的一个新指令消息在其最初发送后经过一段相当长的时间,那么发送方可能不希望重传该消息以提供改变市场条件的潜在可能性。(Seqence Reset-GapFill消息用于发送方不希望发送二跳过这类消息。)

注意:接收程序必须按照顺序处理消息。如:如果收到消息8和9,消息7丢失,程序应忽略消息8和9,并要求重传消息7到9,或者最好重传消息7到0(0表示序列号无穷大)。后者,作为当序列号出现混乱,双方同时尝试恢复一个间隙时,从当前的某些竞争条件下快速恢复的推荐方法。

1.为请求一个单一消息, BeginSeqNo=EndSeqNo

2.为请求一定范围内的消息,BeginSeqNo=请求范围内第一个消息,EndSeqNo=请求范围内最后一个消息。

3.请求特定消息的所有后续消息:BeginSeqNo=请求范围内第一个消息,EndSeqNo=0。

重传请求消息格式如下:

Resend Request

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=2

7

BeginSeqNo

Y

不能加密

16

EndSeqNo

Y

StandartTrailer

Y

5.5 Reject(session-level)驳回消息

当一个接收消息由于违背会话层规则,不能被正确的处理,应发送驳回消息。一个例子是:当一个接收消息通过解密,效验和检查,及数据体长度检查后没有有效的基础数据(如,MsgType=&),将产生一个驳回消息。结果是,这些消息将传递给交易应用程序,如果需要,将产生商业逻辑级的驳回。

驳回消息应记录到日志中,且接收序列号应增加。

注意:接收应用程序应忽略任何干扰消息,不能被解析的及未通过数据完整性检查的消息。处理下一个FIX消息将导致检测到一个序列号间隙并产生一个Resend Request消息。FIX引擎应包含处理在此情况下的无限重传循环。

生成和接收到一个驳回消息表明一个接收或发送程序的逻辑错误导致的严重的错误。

如果发送程序选择重传驳回消息,该消息应赋予一个新的序列号值,且PossResend设置为‘Y’。

无论何时,强烈推荐将描述失败原因在Text 域中描述(如,INVALID DATA(35))。

如果接收到的一个应用级消息满足所有会话级规则,该消息应在商业消息级被处理。如果在处理过程中检测到规则冲突,将产生发送一个商业绩驳回。许多商业级消息都有自己特定的驳回消息。如果没有,则产生发送一个Business Message Reject消息。

注意,在收到一个商业消息,满足会话级规则,但不能被传送给商业级处理系统时,一个带有BusinessRjectReason=“Application not available at this time”的Business Message Reject消息将被发出。

会话级驳回消息场景:

SessionRejectReason

0=Invalid tag number 无效的tag编号

1=Required tag missing tag丢失

2=Tag not defined for this message type 这类消息的Tag没有被定义

3=Undefined Tag未知Tag

4=Tag specified without a value缺少Tag值

5=Value is incorrect (out of range) for this tagtag值错误(超界)

6=Incorrect data format for value错误值数据

7=Decryption problem解密错误

8=Signature problem签名错误

9=CompID problem企业ID错

10=SendingTime accuracy problem发送时间不正确

11=Invalid MsgType无效的MsgType

12=XML Validation error XML语法验证错误

13=Tag appears more than once Tag重复出现

14=Tag specificed out of required order 指定的Tag顺序错误

15=Repeating group fields out of order重复组域顺序错误

16=Incorrect NumInGroup count for repeating group 重复组NumInGroup错误

17=Non “data” value includes field delimiter(SOH character) 包含了SOH分界符的错误数据

99=Other其它错误

注意:其他的会话级规则冲突可能存在,SessionRejectReason值为99(Other),并在Text域中进行详细描述。

驳回消息格式:

Reject

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=3

45

RefSeqNum

Y

被驳回消息的MsgSeqNum

371

RefTagID

N

被参照的FIX域tag值

372

RefMsgType

N

被参照的FIX消息MsgType值

373

SessionRejectReason

N

会话级驳回消息错误代码

58

Text

N

解释驳回原因的文本信息

354

EncodedTextLen

N

如果使用EncodeText域,必选,且EncodeText必须紧跟在该域之后

355

EncodedText

N

使用MessageEncoding

域制定的编码规则对Text域内容的编码数据(非ASCII码)

StandartTrailer

Y

5.6 Sequence Reset(Gap Fill)序列号复位(间隙填充)消息

序列号复位消息有两种模式:Gap Fill模式和Reset模式。

5.6.1Gap Fill 模式

在下列情况下,Gap Fill模式用于响应当出现一个或多个消息必须被跳过时的Resend Request消息

1.在常规的重传处理过程中,发送应用程序可以选择不发送消息(如,一个过期的指令)。

2.在常规的重传处理过程中,大量的管理消息将跳过而不重传(如:Heartbeat, TestRequest 消息)。

GapFillFlag(123)域为‘Y’时,为Gap Fill模式。

如果GapFillFlag(123)域设置为‘Y’,MsgSeqNum应同标准消息序列号规则保持一致(如,序列号Reset GapFill模式消息的MsgSeqNum应为GapFIll消息的最开始的MsgSeqNum,因为其值为远端程序希望接收的消息序列号)。

5.6.2Reset Mode 复位模式

复位模式包括了指定一个任意的、数值更大的、接收者期望的Reset-Reset消息序列号,并用于在出现不可恢复的应用程序错误时重新建立一个FIX会话。

复位模式由GapFillFlag为‘N’时,或被忽略时被指定。

如果GapFillFlag没有出现,或其值为‘N’,可以认为Sequence Reset消息的目标是从序列号混乱时的恢复。在序列号的Reset-Reset模式中,在消息头中的MsgSeqNum将被忽略(如,当接受到带有错误的MsgSeqNum序列号的Sequence Reset-Reset模式消息时,不产生重传请求)。序列号复位的Reset消息不应被当作揖个重传请求的常规响应(使用序列号复位的Gap Fill模式)。序列号复位的Reset模式仅在当使用序列号复位的Gap Fill模式无法恢复时进行灾难恢复。注意,使用序列号复位模式时有可能造成消息丢失。

5.6.3Rules for processing all Sequence Reset messages处理所有序列号复位消息的规则

发送程序将启动序列号复位。所有情况下,这个消息指定NewSeqNo以作为消息接收方期望接收的下一个消息的序列号的复位,紧接在消息和/或被挑过的序列号?

序列号复位,近能增加序列号。如果一个序列号复位消息尝试减小期望接收消息的序列号时,应被当作揖个严重错误而被拒绝。多个Resend Request重传请求可能在接连着被发起(如,5道10,接着5到11)。如果序列号8,10,11是应用消息而5到7和9是管理消息,那么重传请求的结果为:序列号复位GapFill模式下的NewSeqNo为8,消息8;序列号复位GapFill模式下的NewSeqNo为10,消息10。也可以是序列号复位GapFill模式下的NewSeqNo为8,消息8;序列号复位GapFill模式下的NewSeqNo为10,消息10,和消息11。必须小心地忽略尝试减少期望序列号值的复制的序列号复位GapFill模式消息。可以通过检查MsgSeqNum是否小于期望值来检查。如果是,那么该序列号复位GapFill模式消息为复制消息,应被忽略。

序列号复位消息格式:

Sequence Reset

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=4

123

GapFillFlag

N

36

NewSeqNo

Y

StandartTrailer

Y

5.7 Logout注销消息

Logout消息发起或确认一个FIX会话的终止。没有Logout消息交换的连接断开应被视为一个异常情况。

在实际的关闭会话前,Logout发起者应等待对端的Logout确认消息的响应。这样可以给远端在必要时执行一些Gap Fill操作的机会。如果远端在规定的时间间隔后没有响应,会话可以终止。

在发送Logout消息后,注销发起者不应发送任何消息,除非注销的接收者通过ResendRequest消息请求发送消息。

注销消息格式如下:

Logout

Tag

(标记)

FieldName

(域名)

Req’d

(必选)

备注

StandardHeader标准头部

Y

MsgType=5

58

Text

N

354

EncodedTextLen

N

如果使用EncodeText域,必选,且EncodeText必须紧跟在该域之后

355

EncodedText

N

使用MessageEncoding

域制定的编码规则对Text域内容的编码数据(非ASCII码)

StandartTrailer

Y

5.8 CheckSum Calculation 校验和计算

一个FIX消息校验和通过计算到ChechSum域(但不包括)的消息的每个字节和得到。然后,校验和被转换为模256的数字用于传送和比较。校验和在所有加密操作之后被计算。

为了便于传输,校验和必须以可打印字符形式进行传输,因此,校验和被转换位3个ASCII数字。

比如:如果消息的校验和为274,则模256后为18(256+18 = 274)。这个值将采用|10=018|进行传输,其中“10=”是校验和域的标签。

产生校验和的代码示列如下:

char *GenerateCheckSum( char *buf, long bufLen )

{

static char tmpBuf[ 4 ];

long idx;

unsigned int cks;

for( idx = 0L, cks = 0; idx < bufLen; cks += (unsigned int)buf[ idx++ ] );

sprintf( tmpBuf, “%03d”, (unsigned int)( cks % 256 ) );

return( tmpBuf );

}

6 FIX 会话层测试用例和期望行为

6.1 Applicability 适用性

本文档在2002年9月20日最后被修订,当时的FIX协议的最新版本为带有20020930的扩展的FIX 4.3 。此文当适用于FIX4.X,除非特别说明。

6.2When to send a Logout vs. when to just disconnect何时发送Logout与仅断开连接

一般情况下,一个Logout消息应在关闭一个连接前发送。如果这个Logout消息是源于一个错误条件,Logout的Text域应提供错误原因的描述,为远端FIX系统提供问题诊断的操作支持。这里有2个例外,推荐不发送Logout消息:

1、在登陆阶段,如果会话发起者的SenderCompID,TargetCompID或IP其中一个无效时,推荐立即终止会话,不发送Logout消息。这个登陆尝试有可能使一个未经授权的破坏系统的未认证尝试,因此,企业不希望泄露其FIX系统的任何信息,如,哪个SenderCompID,TargetCompID是有效的,或所支持的FIX版本。

2、在登陆阶段,当一个有效的FIX会话已经被一个企业使用时,该企业的第2次连接尝试的Logon消息被接受时,推荐会话接收者立即中断该第2次连接尝试,不发送Logout消息。发送一个Logout消息将冒着妨碍和影响当前FIX连接的风险。例如:在一些FIX实现系统中,发送一个Logout消息可能会消耗一个序列号,这样将会导致在一个已经建立的FIX会话中的序列号混乱。

在其他情况下,如果发送一个Logout消息不产生风险和安全冲突,Logout消息应随同描述信息一起发送。

6.3 When to send a Session Reject vs. when to ignore the message 何时发送会话驳回与何时忽略消息

以下内容从FIX协议规范中的Reject消息定义中摘抄:

注意:接收程序应忽略任何文本混乱,不能被解析以及数据完整性检查失败的消息。处理下一个右下的FIX消息将导致检测到一个序列号间隙并产生一个重传请求消息Resend Request。这种情况下,FIX引擎应包含识别重传无限循环的逻辑。

FIX协议采取乐观的观点。它假设一个混乱的消息是由于传输中出现的错误,而不是FIX系统的问题。因此,如果发送一个重传请求消息(Resend Request),该混乱消息将被正确得重传。如果一个消息没被认为是混乱的,那么,推荐发送一个会话级驳回消息。

6.4 What constitutes a garbled message 什么样的情况认为是一个混乱消息

1.BeginString(tag#8)不是一个消息中的第一个tag或不是8=FIXT.n.m.的格式。

2.BogyLength(tag#9)不是一个消息中的第二个tag或没有包含正确的字节数。

3.MsgType(tag#35)不是一个消息中的第三个tag。

4.Checksum(tag#10)不适最后一个tag或没有包含正确的值。

如果丢失MsgSeqNum(tag#34),一个Logout消息将被发送以终止FIX连接。因为这种情况意味着一个严重的应用程序错误。

6.5 FIX Session-level State Matrix FIX会话层状态举证

Precedence

次序

State

状体

Initiator

发起者

Acceptor

接收者

Descriptioin

描述

1

断开 当天未连接

Y

Y

当前处于断开状态,当日未进行连接尝试。没有MsgSeqNum被使用(下一个当日的连接的MsgSeqNum值为1)。

2

断开 当日开始连接

Y

Y

当前处于断开状态,尝试建立当日连接,这样,MsgSeqNum开始被消耗(下一个当日连接时,MsgSeqNum将从其(last+1)开始)

3

检测到网络连接的中断

Y

Y

处于连接状态时,检测到一个网络连接中断(如TCP socket关闭)。断开网络连接并关闭该会话的配置。

4

等待连接

N

Y

会话登陆消息接收者等待对端的连接。

5

初始话(发起)连接

Y

N

会话登陆消息发起者同对端建立连接。

6

网络连接建立

Y

Y

双方建立网络连接。

7

发送Logon发起消息

Y

N

会话登陆发起者发送Logon

消息。

***异常:24小时会话

8

收到Logon发起消息

N

Y

会话登陆接收者收到对端的Logon消息

***异常:24小时会话

9

Logon发起消息响应

N

Y

会话登陆接收者使用Logon消息与对端握手,响应对端Logon消息。

10

处理ResendRequest

Y

Y

接收和响应对端发送的对消息的ResendRequeset消息,和(或)对MsgSeqNum所请求范围的SequenceReset-Gap Fill消息。修改以驳回接收到的MsgSeqNum小于LastSeqNum的Resend Request消息。

11

收到MsgSeqNum过大

Y

Y

从对端接收到过大的MsgSeqNum,将消息排队暂存,发送ResendRequest消息

12

等待/处理ResendRequest响应

Y

Y

处理请求的MsgSeqNum请求的、PossDupFlag=’Y’的消息,和/或从对端进行的SequenceRset-Gap Fill消息。将MsgSeqNu过高的接收消息排队暂存。

13

在实践间隔后未收到消息

Y

Y

没有非混乱消息在HeartBeatInt+响应时间内被接收,发送TestRequest消息。

14

等待/处理TestRequest消息响应

Y

Y

处理接收消息。当接收到非混乱消息后,复位心跳间隔时间。

15

接收Logout消息

Y

Y

从对端接收到发起注销/连接断开的Logout消息。如果MsgSeqNum过高,发送RsendRequest。如果发送,等待一定周期,以完成ResendRequest的响应处理。注意,依据Logout的原因,对端可能不能执行该请求。 发送Logout消息作为响应并等待一定时间让对端断开网络连接。注意,对端可能发送ResendRequest消息,如果Logout响应消息的MsgSeqNum过高并重新发起Logout操作。

16

发起Logout处理

Y

Y

识别优雅断开连接的条件和原因(如:日终,多个未响应的TestRequest消息发送后,MsgSeqNum过高等)。发送Logout效益给对端。等待一个时间周期以接收Logout响应。在这期间,如有可能,处理新接收的消息和/或ResendRequest。注意,一些注销/终止条件(如数据库/消息存储失败),可能要求紧接在初始沪Logout消息发送后立即止网络连接。断开该会话的网络连接并关闭该会配置。

17

活动/正常会话

Y

Y

网络连接建立后,Logon消息成果交换完成,接收和发送的MsgSeqNum都是所期望的顺序,并且Heartbeat 或其他消息在(HeartBeatInt +响应周期)内被接收。

18

等待Logon确认

Y

N

会话发起者等待会话接收者发送Logon确认消息。

6.6 FIX LogonProcess State Transition Diagram FIX登陆消息处理状态转换图

Session Initiator(e.g. buyside)Action 会话发起者(如,买方)行为

Session Acceptor(e.g. sellside)Action会话接收者(如,卖方)行为

Session Initiator(e.g.buyside)State会话发起者(如,买方)状态

Session Acceptor(e.g. sellside)State会话接收者(如,卖方)状态

开始

未连接-当日未连接

未连接-当日连接

等待连接

连接

发起连接

(可能)检测到网络连接中断

等待连接

接受连接

建立网络连接

建立网络连接

发起登陆

发送发起登陆消息Logout

建立网络连接

收到发起登陆消息Logout

发送发起登陆消息Logout

收到发起登陆消息Logout

发送发起登陆消息响应

发送发起登陆消息Logout

发起Logon的响应

(可能)发起 Logout处理

(可能)接收到的MsgSeqNum过高

(可能)发送ResendRequest

发起Logon响应

(可能)接收到的MsgSeqNum过高

接收发起登陆消息的响应

(可能)活动/正常的会话

(可能)发起 Logout处理(如,MsgSeqNum过高)

发起Logon的响应

(可能)发送RsendRequest

(可能)活动/正常的会话

(可能)接收的MsgSeqNum过高

(可能)活动/正常的会话

(可能)处理ResendRequest

活动/正常的会话

活动/正常的会话

6.7 FIX Logout Process State Transition Diagram FIX Logout处理状态转换图

Logout Initiator: Action

Logout发起者行为ie

Logout Acceptor Action

Logout接收者行为

Logout Initiator State

Logout发起者状态

Logout Acceptor State

Logout接收者状态

开始

1.活动/正常的会话

2.没有在时间间隔内收到消息

3.等待/处理响应TestRequest

7. 示例:

FIX消息示例如下:

8=FIX.4.29=7335=A34=149=CLIENT52=20181119-10:42:48.76856=SERVER98=0108=30141=Y10=208

消息长度:9=73,标识的消息体如下:

35=A34=149=CLIENT52=20181119-10:42:48.76856=SERVER98=0108=30141=Y
 

五、FIX Engine

(1) QuickFIX

QuickFIX是一款C++实现的开源FIX引擎,同时提供Python、Ruby语言实现。

(2)FIX 8

FIX8是一款C++实现的开源高性能FIX引擎。

https://www.fix8.org/

(3)OnixS FIX Engines

OnixS FIX Engines是OnixS公司的一款商业的FIX引擎,支持C#、C++、Python、Java。

(4)Chronicle FIX Engine

Chronicle FIX Engine是Chronicle公司的一款高性能的FIX引擎。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ztenv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值