From 3e558a2aee18db782c88c034ef1e6c7b57827dac Mon Sep 17 00:00:00 2001 From: Julio Biason Date: Mon, 10 Jan 2022 13:34:37 -0300 Subject: [PATCH] Translated the FAST post --- content/research/decoding-fast.md | 294 ++++++++------ content/research/decoding-fast.pt.md | 552 +++++++++++++++++++++++++++ 2 files changed, 721 insertions(+), 125 deletions(-) create mode 100644 content/research/decoding-fast.pt.md diff --git a/content/research/decoding-fast.md b/content/research/decoding-fast.md index fe48b18..036175b 100644 --- a/content/research/decoding-fast.md +++ b/content/research/decoding-fast.md @@ -39,8 +39,8 @@ defined by the protocol (there is a whole list [here](https://www.inforeachinc.com/fix-dictionary/index.html)) but each exchange can create their own IDs. -For example, if you have MsgType (ID 35) with value "`y`" and Security ID (ID -48) with value "`123456`", you'd get the message: +For example, if you have the field MsgType (ID 35) with value "`y`" and the +field Security ID (ID 48) with value "`123456`", the message received would be ``` 35=y^A48=123456 @@ -49,12 +49,13 @@ For example, if you have MsgType (ID 35) with value "`y`" and Security ID (ID # And Back to FAST One of the things FAST is designed for is removing duplicate and/or constant -content. For example, MsgType (ID 35) is the "SecurityList" message, which -contains information about all the symbols (the their security IDs) handled by -the exchange. Because the exchange is the same in all the symbols, FAST allows -defining the fields related to it (Source, field ID 22, and Exchange, field ID -207) to constant values, so they don't need to be transmitted and, when decoding -FAST back to FIX, the decoder simply add the constant value. +content. For example, MsgType (ID 35) identifies that the message is +"SecurityList" (symbol list), which contains information about all the symbols +(the their security IDs) available in the exchange. Because the exchange is the +same in all the symbols, FAST allows defining the fields related to it (Source, +field ID 22, and Exchange, field ID 207) to constant values, so they don't need +to be transmitted and, when decoding FAST back to FIX, the decoder simply add +the constant value. To know which fields are constant and which are not (and some other information), the protocol defines a template, which have a well defined schema, to report @@ -62,21 +63,20 @@ that information. # The Template -The template is, as mentioned before, a XML file (which the protocol definition -doesn't provide any default way to actually receive that field, and thus is left -for the exchange to find their way) which describes field types, names, IDs and -operators. +The template is a XML file which describes the fields, their types, names, IDs +and operators. The protocol itself doesn't provide any default way to actually +receive that field, and thus is left for the exchange to find their way. -Note that the template describe the field IDs and their types, which the -incoming data have only the values. If we use the FIX description above, the -template defines the left side of the pair, while the incoming have have only -the right side. +Note that the template describe the field IDs and their types, and the incoming +data have only the values. If we use the FIX description above, the template +defines the left side of the pair, while the incoming data have have only the +right side. ## Field Types The protocol have a few field types: Unsigned Ints of 32 and 64 bits, Signed Ints of 32 and 64 bits, ASCII strings, UTF-8 strings, sequences, decimals and a -type called "presence map". +special type called "Presence Map". One thing to note is that all fields use a "stop bit" format. This is quite similar to UTF8, although UTF8 uses a "continuation bit" instead of "stop bit", @@ -85,23 +85,25 @@ but the process of reading is the same: - Read a byte; - Does it have the high order by set to 0? - Yes: Keep reading; - - No: Stop reading the conclude the field value. + - No: Stop reading the field value. + +I'll show some examples later in this post. ## Field definitions -On the template, the fields have their type, name (optional), ID, a presence -indicator and an operator (optional). +The template defines the fields with their type, name (optional), ID, a +presence indicator and an operator (optional). -For example, if you have an unsigned int of 32 bits, named "MsgType" with ID -"35", that would be described in the template as +For example, to describe an unsigned int of 32 bits, named "MsgType" with ID +"35", it would be described in the template as ```xml ``` Because there is no indication of presence, it is assumed that the field is -"mandatory" and should always have a value. On the other hand, if you have a -field defined as +"mandatory" and should always have a value. On the other hand, if the field +definition was something like ```xml @@ -110,17 +112,34 @@ field defined as ... then the field may not not have a value. This is also referred as "nullable" field. +## Optional and Mandatory Fields + +The difference between optional and mandatory fields is that mandatory fields +*always* have a value, even if 0. Optional fields, on the other hand, can be +null and have no value at all. + +{% note() %} +I have the impression that this is done if the exchange wants to mark a field +as "do not appear in the FIX version"; so even if the field have a definition, +the resulting FIX version would not have the field. + +So it is more of a "backwards compatibility" than anything else. +{% end %} + +## Types + ### Types: Ints -To read an Int, you pick the 7 low order bits (everything except the high order -one) and move to the resulting variable. If the stop bit is there, you're done; -if it is not, you shift the result by 7 bits and add the 7 bits from the next -byte and so on, till you find a byte with the stop bit set. +To read an Int, the decoder would pick the 7 low order bits (everything except +the high order one) and move them to the resulting variable. If the stop bit is +there, the read is complete; if it is not, the result should be shifted by 7 +bits and add the 7 bits from the next byte and so on, till you find a byte with +the stop bit set. -The 32 and 64 bits only define the maximum value of the field and should not be -used as "number of bits to be read" -- because of the stop bit. If the value -exceeds 32 or 64 bits, that is considered an error and the processing should be -aborted. +The 32 and 64 bits only define the maximum value of the field and should not +necessarily be used as "number of bits to be read" -- because of the stop bit. +If the value exceeds 32 or 64 bits, that is considered an error and the +processing should be aborted. Signed Int work exactly the same, but as 2's complement. @@ -133,32 +152,32 @@ to make it easier to read): ``` ... the decoder will read the first byte and see that it doesn't have the high -order bit set, so it keep just the "1" for the value and shift everything by 7 -bits. Then the second byte is read; this one have the high order bit set, so +order bit set, so it keep just the "1" for the value and shift 7 bits to the +left. Then the second byte is read; this one have the high order bit set, so the remaining bits (in this case "001_0010") are added to the resulting value and get `1001_0010` -- or 146. -Negative numbers are represented using 2's so you'd get, for example: +Negative numbers are represented using 2's so if the decoder receives, for +example: ``` 0000_0011 0111_1110 1110_1110 ``` -... which, when you remove the high order bits and follow the high order to find -the stop bit, you get "`1111_1111 0110_1110`", which is -146 (in 16 bits, just -to make it shorter). +... which, when the high order bits are removed, it should generate "`1111_1111 +0110_1110`", which is -146 (in 16 bits, just to make it shorter). When an integer field is optional, the result must be decremented by 1. The -reason for this is that, when the field is marked as optional -- also called -"nullable" -- we need something to differentiate both 0 and Null. So, an -optional integer with value 0 is, actually, Null; if we have a value of 0, we -incoming data will have the value 1, which we'll decrement by 1 and become 0. +reason is that it should be something to differentiate both 0 and Null. So, an +optional integer with value 0 is, actually, Null; to get the value 0 for the +field, the incoming data will have value 1, which is decremented in 1 and goes +back to 0. ### Types: Strings -ASCII strings are pretty simple to read: Again, you keep reading the incoming -data till you find a byte with the high order bit set (again, the stop bit) and -just convert to their respective ASCII character. +ASCII strings are pretty simple to read: Again, the decoder keeps reading the +incoming data till you find a byte with the high order bit set (again, the stop +bit) and just convert to their respective ASCII character. For example @@ -166,9 +185,9 @@ For example 0100_1000 0110_0101 0110_1100 0110_1100 1110_1111 ``` -Would generate the bytes 72, 101, 108, 108 and 111, which using the values as -ASCII codes would result in "Hello". Note that the stop bit here represents "end -of string" and the bytes should not be grouped like in Ints. +Would generate the bytes 72, 101, 108, 108 and 111, which using the ASCII table +would result in "Hello". Note that the stop bit here represents "end of string" +and the bytes should not be grouped like in Ints. {% note() %} So far, I didn't find any UTF8 strings, so I'm not quite sure how to process @@ -187,22 +206,22 @@ Sequences are basically arrays. The first field of a sequence is the "length" present. Inside the sequence, you have a list of field definitions, which may even include more sequences. -Optional Sequences follow the same idea of optional Ints: You read the length -and, it is null, there is nothing in the sequence -- and mandatory Sequences can -be zero. +Optional sequences affect the way the length is read: If optional, the length +should be treated as an optional Integer and thus the size is decremented by 1. ### Types: Decimals Decimals are formed by two fields: Exponent and Mantissa. The way it works is -that if you have an Exponent of "-2" and a Mantissa of "1020", you'd do `1020 * -10 ^ -2` ("1020 times 10 to the power of -2"), and the actual value is "10.20". +that if you have an Exponent of "-2" and a Mantissa of "1020", the resulting +value is `1020 * 10 ^ -2` ("1020 times 10 to the power of -2"), or "10.20". Both Exponent and Mantissa are read as Signed Ints. An Optional Decimal means the Exponent is optional. The documentation says that -the Mantissa is always mandatory, but there is a catch: If the Exponent is null, -then the Mantissa is not present and shouldn't be read; otherwise, you read the -Mantissa and apply the conversion. +the Mantissa is always mandatory, but there is a catch: If the Exponent is +null, then the Mantissa is not present and the whole Decimal is Null; +otherwise, the decoder is expected to read the Mantissa and the formula above +should be applied. Also, because Exponent and Mantissa are two fields, they can have different operators. I'll show some examples after the Operator, mostly because I've seen @@ -210,18 +229,19 @@ both with different operators and they make a mess to read. ### Type: Presence Map -Presence Maps are used in conjunction with operators. They are read basically -like you'd read an unsigned int (read bytes till you find the one with the high -order bit) but do not have any conversion in themselves. Every time you need to -check if a field is present by checking the presence map, you consume the high +Presence Maps are used in conjunction with operators. They are read like +unsigned Ints are read (read bytes till you find the one with the high order +bit, remove the high order bits and put all the bits in sequence) but do not +have any conversion in themselves. Every time the decoder need to check if a +field is present in the presence map, the bit is consumed and the line +moves on. Presence Maps are not present in the template and their presence is implied if -there is the need for one. For example, in a pure mandatory sequence of fields, -there will be no presence map at all. -order bit from it, so it is never used again. +there is at least one field that requires it. For example, if all the fields in +a sequence are mandatory, there will be no Presence Map in the incoming data. -The bits in the Presence Map are in the order of the required fields. For -example, if a template with: +The bits in the Presence Map are in the order of the fields in the template. +For example, if a template with: 1. A mandatory field; 2. A field with an operator that requires the presence map (I'll mention those @@ -229,17 +249,16 @@ example, if a template with: 3. Another mandatory field; 4. And, finally, another field with operator. -You may receive a Presence Map as `1110_0000`, in which: +... it is possible to have a Presence Map as `1110_0000`, in which: 1. The first bit is the stop bit, so the decoder assumes this is the last byte of the presence map. 2. The second bit indicates that the first field with operator is present. It - does *not* represent the mandatory field, 'cause, well, it is mandatory and, + does *not* represent the mandatory field 'cause, well, it is mandatory and, thus, is always present. -3. The second bit indicates the second field with an operator. +3. The second bit indicates the second field with an operator is present. -Again, I'll mention which ones the decoder should be checked in the presence -map. +(Again, I'll mention which operators require the presence map.) ## Operators @@ -255,18 +274,18 @@ operators: ### Operator: No Operator -When there is no operator defined, you have a "no operator" operator. It means -there is no special way of dealing with the incoming value: You just capture it -and use it. +When there is no operator defined in the field definition in the template, then +the operator is the "no operator" operator. It means there is no special way of +dealing with the incoming value. When a field have No Operator, there will be no bit in the Presence Map. ### Operator: Constant -A field with the Constant operator will not appear in the incoming data and you -should assume that its value is the value in the constant. Previously I +A field with the Constant operator will not appear in the incoming data and the +decoder should assume that its value is the value in the constant. Previously I mentioned that a list of securities may have the field 22, "Source", and field -207, "Exchange", with constant values, they would be defined as +207, "Exchange", with constant values, so they would be defined as ```xml @@ -277,18 +296,25 @@ mentioned that a list of securities may have the field 22, "Source", and field ``` -There is a catch, though: When a constant can be Null (`presence="optional"`), -then the decoder needs to use the Presence Map bit; if it is set, the constant -value should be used; if it is not set, then the field value is Null. +There is a catch, though: When a Constant field can be Null +(`presence="optional"`), then the decoder needs to use the Presence Map bit; if +it is set, the constant value should be used; if it is not set, then the field +value is Null. The Presence Map should be use only if there is a field with a constant value that is optional. ### Operator: Default -The Default operator is similar to the Constant operator, but the decoder needs -to check the Presence Map; if the bit for the field is set, then you use the -default value; if it is not set, then the field is Null. +The Default operator is similar to the Constant operator, but if the bit for +the field is set in the Presence Map, then the value for the field should be +read in the incoming data; otherwise, the Default value should be used. + +{% note() %} +In a way, you can read this: Is the value present in the incoming data +(indicated in the Presence Map)? Then read the value in the incoming data; +otherwise, use the default value. +{% end %} ### Operator: Copy @@ -296,8 +322,11 @@ The copy operator indicates that the value for this record have the same value of the previous record; if it is the first record, then the value should be used. If the Presence Map bit is set for the field, then the decoder must read the value in the incoming data; if it is not set, then the previous value should -be used. In the data I saw, every first record have the bit set, so you get the -initial/previous value. +be used. + +{% note() %} +In the data I saw, every first record have the bit set. +{% end %} An example: You can have a template like @@ -307,11 +336,11 @@ An example: You can have a template like ``` -... and you have the following records and their Presence Maps: +... and the incoming data had the following records and their Presence Maps: 1. The first record have the bit set for this field in the Presence Map and the - strings reads "first". This record will have this field with the value - "first". + strings reads "first". The result for this field in this record will be + the value "first". 2. The second record doesn't have the bit set in the Presence Map. So the decoder reuses the previous value and this record will have the field with the value "first" (again). @@ -320,8 +349,7 @@ An example: You can have a template like 4. The fourth record doesn't have the bit set and the decoder reuses the value "second" for the field. -The Copy operator may have the initial value, so you don't need to read it. For -example +The Copy operator may have the initial value. For example ```xml @@ -330,7 +358,7 @@ example ``` This means that you should use "string" as previous value, even in the first -field. +record. As pointed, fields with the Copy operator appear in the Presence Map. @@ -338,7 +366,7 @@ As pointed, fields with the Copy operator appear in the Presence Map. Delta is an operator similar to Copy, but instead of using the value of the previous record in this field, the new value must be computed using the previous -value and the current one. Again, if you have no previous value, then there is +value and the current one. Again, if there is no previous value, then there is no operation to be done and the incoming value is the current one. An example: @@ -353,18 +381,20 @@ An example: field. 2. The second record comes with the value "2". That should be added in the previous value and used, so the field for the second record is "302". -3. The third record comes with the value "3". Again, you reuse the previous - value and add the current one. So the field for the third record have the - value "305". +3. The third record comes with the value "3". Again, the previous value should + be used and the current one should be added. So the field for the third + record have the value "305". Fields with the Delta operator do not appear in the Presence Map. ### Operator: Increment -Increment is another operator that works similar to the Copy operator, but if -its bit is set in the Presence Map, the decoder reads the field value from the -incoming data; if it is not set, the decoder does not read any data, but reuses -the previous value with an increment of 1. +Increment works like the Delta operator 'cause it always add a value to the +previous one, but the value to be added is always 1. + +If the bit for the field is set in the Presence Map, the value for the field is +the one in the incoming data; otherwise, the value will be the previous value +added by 1. Example: @@ -399,8 +429,8 @@ Map, [according to JetTek](https://jettekfix.com/education/fix-fast-tutorial/): Constant - No, the Constant value should be used. - Yes; if set, use the Constant value; otherwise the field is Null. + No, the Constant value should always be used. + Yes; if set, use the Constant value; otherwise the field value is Null. Copy @@ -417,7 +447,8 @@ Map, [according to JetTek](https://jettekfix.com/education/fix-fast-tutorial/): Delta - No; the value should always be added to the previous one. + No; the value in the incoming data should always be added to the + previous one. No; same as Mandatory fields. @@ -452,47 +483,60 @@ That seems simple. But there are a lot of moving pieces here: 1. The `presence="optional"` in the decimal means that the `exponent` can be Null and only that. -2. The `default` operator in the Exponent means the decoder must check if the - Exponent have a value or should use the default value of "0". +2. The Default operator in the Exponent means the decoder must check if the + Exponent have a value in the incoming data or should use the default value + of "0". - There is another issue here: If the Presence Map indicates that the value is - present and the read value is 0, because the Exponent is optional, it should - be considered Null and, thus, there is no Mantissa and everything is Null. -3. The `delta` operator in the Mantissa should be used applying the incoming - value to the previous one. But, if the Exponent is Null, then there is no - Mantissa, but the previous value is kept. + There is an issue here: If the Presence Map indicates that the value is + present and the read value is 0, because the field is optional, the decoder + should consider the Exponent Null and, thus, there is no Mantissa and + everything is Null. +3. The Delta operator in the Mantissa should be used applying the incoming + value to the previous one. But, if the Exponent is not in the presence map, + because it has a default value, that's its value and it shouldn't be read in + the incoming data and the read value is actually applied to the Mantissa. -This causes a bunch of weird, "exception of the rule" dealings: +Like this: 1. The first record have the field set in the Presence Map and it is read as - "-2". That's the Exponent, reading the mantissa gives the value "1020", so - the whole decimal is "10.20"; + "-2"; that's the Exponent. The next value is "1020", which is the Mantissa, + so the whole decimal is "10.20"; 2. The second record have the field set in the Presence Map and it is read as "0". Because the decimal is optional, the exponent is optional, and because - 0 is Null, there is no Exponent, and the next value is *not* the Mantissa. -3. The third record have the field set in the Presence Map and it is again, - "-2" for the Exponent and we read the Mantissa. The value read for the - Mantissa is "-20", but instead of assuming that the Mantissa was Null in the - previous record, it uses the first record value, so the Mantissa for this - record is "1000" and the value for the decimal is "10.00". - -Another weird thing I saw was related to the way the exchange was ordering the + 0 is Null for optional fields, there is no Exponent, and the next value is + *not* the Mantissa. The value for the field in this record is Null. +3. The third record have the field not set in the Presence Map. Because + Exponent has a default value, it becomes "0", and the Mantissa should be read. + If the incoming data have the value "10", the decimal becomes "10" (or + "10.00", if we use the same decimal places everywhere). + +A weird thing I saw was related to the way the exchange was ordering the results. It had a sequence of sell and buy orders in which -1. The first record was the sell order, with an Exponent of 0 and a Mantissa of - "5410". That meant the value is "5410" (pretty straight). +1. The first record was the sell order, with an Exponent of "0" and a Mantissa + of "5410". That meant the value is "5410" (pretty straight). 2. The second record was the buy order. It had an Exponent of "-2" and the - Mantissa had an incoming value of 526604. That gives the value of "532014", - but because the Exponent is "-2", the actual value is "5320.14". + Mantissa had an incoming value of "526604". With the Delta operador, that + gives the value of "532014", but because the Exponent is "-2", the actual + value is "5320.14". 3. The weird thing happened in the third record, which was again a sell order. The value should be exactly the same as the first, but the exchange sent an - Exponent of 0 and a Mantissa of "−526604". With the delta, that would bring + Exponent of "0" and a Mantissa of "−526604". With the delta, that would bring the value back to "5410". I found it weird that they kept jumping between two different Exponents instead of using a single one, and at the time I had some issues with the delta math in my code, so... +## Null Length Sequences + +Another strange thing I saw was the optional sequence: In practice there is no +difference between a sequence with 0 elements and one which the length is Null +-- specially if you think the protocol is, basically, a FIX generator. I have +no idea why it wasn't standardized that lengths are mandatory and a value of 0 +means there is no values on it instead of doing a dance around optional and +mandatory sequences. + --- ### Changelog: diff --git a/content/research/decoding-fast.pt.md b/content/research/decoding-fast.pt.md new file mode 100644 index 0000000..1cd261c --- /dev/null +++ b/content/research/decoding-fast.pt.md @@ -0,0 +1,552 @@ ++++ +title = "Decodificando o Protocolo FAST" +date = 2021-01-10 + +[taxonomies] +tags = ["finanças", "binário", "protocolo", "fix", "fast"] ++++ + +Recentemente tive que trabalhar com um FAST (FIX Adapted for Streaming) e +como a documentação está espalhada, decidi colocar as coisas que descobri em um +único lugar para (minha) referência futura. + +{% note() %} +Como isso é baseado na minha experiência pessoal e eu tive contato com uma +única instância disso até agora, existem algumas coisas que estão incompletas +e/ou erradas. Vou continuar atualizando este post enquanto descubro coisas +novas. + +O changelog está no final deste post. +{% end %} + +# O que é o FAST + +[FAST](https://en.wikipedia.org/wiki/FAST_protocol) é, basicamente, um método +de compressão para o protocolo FIX. + +# E o que é FIX + +[FIX](https://en.wikipedia.org/wiki/Financial_Information_eXchange) é um +protocolo criado para as instituições financeiras trocarem informações. Embora +não haja nada "financeiramente" relacionado a ele - você poderia usar o +protocolo para qualquer coisa, basicamente - a maioria das empresas financeiras +o usa. + +FIX é um protocolo muito simples: você tem pares de "campo ID" e "valor" +separados por um "`=`" (sinal de igual) e os pares são separados pelo caractere +ASCII com código 1 (que é representado por `^A` em alguns editores). Alguns IDs +de campo são definidos pelo protocolo (há uma lista completa +[aqui](https://www.inforeachinc.com/fix-dictionary/index.html)), mas cada +central pode criar seus próprios IDs. + +Por exemplo, se você tiver o campo MsgType (ID 35) com valor "`y`" e o campo +Security ID (ID 48) com valor " 123456", a mensagem recebida seria + +``` +35=y^A48=123456 +``` + +# E de Volta ao FAST + +Uma das coisas para que o FAST foi projetado é remover conteúdo duplicado e/ou +constante. Por exemplo, MsgType (ID 35) identifica que a mensagem é +"SecurityList" (lista de ativos), que contém informações sobre todos os +símbolos (seus IDs de segurança) disponíveis na exchange. Como a exchange é a +mesma em todos os símbolos, o FAST permite definir os campos relacionados a ela +(Source, campo ID 22, e Exchange, campo ID 207) para valores constantes, para +que não precisem ser transmitidos e, na decodificação FAST de volta a FIX, o +decodificador simplesmente adiciona o valor constante. + +Para saber quais campos são constantes e quais não (e algumas outras +informações), o protocolo define um modelo, que possui um esquema bem definido, +para relatar essas informações. + +# O Template + +O template é um arquivo XML que descreve os campos, seus tipos, nomes, IDs e +operatores. O protocolo em si não fornece nenhuma maneira padrão de realmente +receber esse campo e, portanto, é deixado para que a exchange defina a forma +dos clientes encontrar este arquivo. + +Note que o modelo descreve os IDs de campo e seus tipos, e os dados recebidos +possuem apenas os valores. Se usarmos a descrição FIX acima, o modelo define o +lado esquerdo do par, enquanto os dados de entrada têm apenas o lado direito. + +## Tipos de Campos + +O protocolo define arquivos tipos de dados: Inteiros de 32 e 64 bits sem sinal, +Inteiros de 32 e 64 bits com sinal, strings em ASCII, strings em UTF8, +sequências, decimais e um tipo especial chamando "Mapa de Presença" (Presence +Map). + +Uma coisa que o processamento de todos os campos é que estes usam um formato de +"bit de parada". O formato é bem parecido com o formato de UTF8, embora UTF8 +use um "bit de continuação" ao invés de "bit de parada", mas o processo de +leitura é o mesmo: + +- Ler um byte; +- O bit de mais alta ordem é 0? + - Sim: Continue lendo; + - Não: Pare de ler o valor do campo. + +Eu vou mostrar alguns exemplos mais a frente neste post. + +## Definição dos Campos + +O template define os campos com seus tipo nome (opcional), ID, +um indicador de presença e um operador (opcional). + +Por exemplo, para descrever um inteiro sem sinal de 32 bits, chamado "MsgType" +com ID "35", ele seria descrito no template como + +```xml + +``` + +Como não há indicador de presença, é assumido que o campo é "mandatório" e deve +sempre deve ter um valor. Por outro lado, se a definição do campo for algo do +tipo + +```xml + +``` + +... então o campo pode não ter valor. Isso também é definido como campo +"nulável" (nullable). + +## Campos Mandatórios e Opcionais + +A diferença entre campos opcionais e mandatórios é que campos mandatórios +*sempre* tem um valor, mesmo que seja 0. Campos opcionais, por outro lado, +podem ser null e não ter valor algum. + +{% note() %} +Eu tenho a impressão que isso foi feito para que a exchange possa marcar um +campo como "não parece na versão FIX"; então mesmo que o campo tenha uma +definição, o resultado em FIX não teria o campo. + +Então é algo mais como "compatibilidade com versões anteriores" do que qualquer +outra coisa. +{% end %} + +## Tipos + +### Tipos: Inteiros + +Para ler um inteiro, o decodificador pegaria os 7 bits mais baixos (tudo exceto +o bit de alta ordem) e mover os mesmos para o valor resultante. Se o bit de +parada estiver presente, a leitura está completa; se não estiver, deve ser +feito um "shift" de 7 bits no valor resultado e movidos os 7 bits do próximo +byte e assim por diante, até que seja encontrado um byte com o bit de parada. + +Inteiros de 32 e 64 bits definem apenas os valores máximos de um campo e não +necessariamente usados como "número de bits a serem lidos" -- justamente por +causa do bit de parada. Se o valor excede 32 ou 64 bits, isso é considerado um +erro e o processamento deve ser abortado. + +Inteiros com sinal funcionam exatamente igual, mas com complemento de 2. + +Por exemplo, se os dados de entrada tem os seguintes bytes (em binário, para +ficar mais fácil de ler; eu adicionei um underscore entre cada 4 +valores, também para ficar mais fácil de ler): + +``` +0000_0001 1001_0010 +``` + +... o decodificador irá ler o primeiro byte e ver que o bit de mais alta ordem +não está ligado, então ele mantém apenas o "1" do valor e move 7 bits à +esquerda. O segundo byte então é ligado; este tem o bit de alta ordem ligado, +então os bits restantes (no caso, "001_0010") são adicionados no valor final e +temos `1001_0010` -- ou 146. + +Números negativos são representados por complemento de 2, então se o +decodificador receber, por exemplo: + +``` +0000_0011 0111_1110 1110_1110 +``` + +... que, quando os bits de alta ordem são removidos, deve gerar "`1111_1111 +0110_1110`", que é -146 (em 16 bits, apenas para ficar mais fácil de ler). + +Quando um campo inteiro é opcional, o valor resultante deve se decrementado +em 1. O motivo é que deve haver uma forma de diferenciar entre 0 e Null. Assim, +um inteiro que seja decodificado para 0 é, na verdade, Null; para obter o valor +0 num inteiro, é enviado o valor 1, que é decrementando por 1 e volta a zero. + +### Tipos: Strings + +Strings em ASCII são bem simples de serem lidas: Novamente, o decodificador +fica lendo os dados de entrada até que encontre um byte com o bit de mais alta +ordem ligado (novamente, o bit de parada) e converte cada byte para o +respectivo caractere em ASCII. + +Por exemplo + +``` +0100_1000 0110_0101 0110_1100 0110_1100 1110_1111 +``` + +... iria gerar os bytes 72, 101, 108, 108 e 111, que usando a tabela ASCII +geraria o valor "Hello". Note que o bit de parada aqui representa "acabou a +string" e os bytes não devem ser agrupados como em Inteiros. + +{% note() %} +Até aqui eu não tive nenhum contato com strings UTF8, então eu não tenho +certeza de como estes devem ser lidos. Com certeza há documentação em como +devem ser lidos, mas como esta é a minha experiência pessoal com o protocolo, +eu decidi não comentar aqui. +{% end %} + +Strings opcionais são Null quando o primeiro byte tiver o bit de mais alta +ordem ligado e todos os demais desligados. + +### Tipos: Sequências + +Sequências são basicamente arrays. O primeiro campo de uma sequência é o +"tamanho" (com o tipo "``" no template) com o número de registros +presentes. Dentro da sequência, você tem uma lista de definições de campos, que +podem até mesmo incluir outras sequências. + +Sequências opcionais afetam a forma como o campo de tamanho é lido: Se a +sequência é opcional, o tamanho deve ser tratato como um inteiro opcional e, +portanto, decrementando em 1. + +### Tipos: Decimais + +Decimais são formados por dois campos: Expoente e Mantissa. A forma como isso +funciona é que se você tiver um Expoente de "-2" e uma Mantissa de "1020", o +valor resultante é `1020 * 10 ^ -2` ("1020 vezes 10 na potência -2") ou +"10.20". + +Tanto o Expoente quanto a Mantissa são lidos como Inteiros com sinal. + +Um Decimal opcional significa que o Expoente é opcional. A documentação diz que +a Mantissa é sempre mandatória, mas tem uma pegadinha: Se o Expoente é Null, +então a Mantissa não está presente nos dados de entrada e o Decimal inteiro é +Null; caso contrário, o decodificador deve ler a Mantissa e aplicar a forma +acima. + +Ainda, como o Expoente e a Mantissa são dois campos, eles podem ter operadores +diferentes. Eu vou mostrar exemplos depois de explicar os Operadores, +principalmente porque eu vi os dois com operadores diferentes e a leitura é +complicada. + +### Tipo: Mapa de Presença + +Mapas de Presença são usados em conjunto com operadores. Eles são lidos da +mesma forma como Inteiros sem sinal (ler os bytes até encontrar o byte com o +bit de mais alta ordem ligado, remover os bits de alta ordem e colocar os bits +em sequência) mas não há uma conversão em si. Cada vez que o decodificador +precisa verificar se um campo está presente no Mapa de Presença, o bit é +consumido e a linha avança. + +Mapas de Presença não estão presentes no template e a existência do Mapa ocorre +se pelo menos um dos campos precisa do Mapa de Presença. Por exemplo, se os campos +de uma sequência são todos mandatórios, não haverá Mapa de Presença nos dados +de entrada. + +Os bits do Mapa de Presença sequem a ordem dos campos no template. Por exemplo, +se um template tem + +1. Um campo mandatório; +2. Um campo com um operador que requer o Mapa de Presença (eu vou comentar + sobre estes mais pra frente); +3. Outro campo mandatório; +4. E, finalmente, outro campo com operador. + +... é possível ter um mapa de presença como `1110_0000`, no qual: + +1. O primeiro bit é o bit de parada, e o decodificar assume que este é o último + byte do Mapa de Presença. +2. O segundo bit indica que o primeiro com operador está presente. Ele *não* + representa o campo mandatório porque, bom, o campo é mandatório e, assim, + está sempre presente. +3. O segundo bit indica que o segundo campo com operador está presente. + +(De novo, eu vou mencionar quais operadores requerem o Mapa de Presença.) + +## Operadores + +Operadores definem a forma como alguns campos devem ser tratados. Eu vi 5 tipos +diferentes de Operadores: + +- Nenhum; +- Constant; +- Default; +- Copy; +- Delta; +- Increment. + +### Operador: Nenhum. + +Quando não há operador definido para o campo no template, então o operador e o +"Nenhum". Isto significa que não há forma especial de lidar com o valor de +entrada. + +Quando um campo tem o operador "Nenhum", não haverá bit para o mesmo no Mapa de +Presença. + +### Operador: Contant + +Um campo com o operador Constant (Constante) não irá ser enviado nos dados de +entrada e o decodificador deve assumir que o valor é o valor constante. +Anteriormente eu mencionei que uma lista de símbolos pode ter o campo 22, +"Source" e o campo 207 "Exchange", com valores contantes, e assim eles seriam +definidos como + +```xml + + + + + + +``` + +Existe uma pegadinha, no entanto: Se um campo contante pode ser Null +(`presence="optional"`), então o decodificador precisa verificar o Mapa de +Presença: Se estiver ligado, o valor contante deve ser usado; caso contrário, o +valor do campo é Null. + +O Mapa de Presença deve ser usado apenas se o campo com valor contante é +opcional. + +### Operador: Default + +O operador Default é parecido com o operador Constante, mas o decodificador +precisa verificar o bit do campo no Mapa de Presença; se estiver ligado, o +valor do campo deve ser lido dos dados de entrada; caso contrário, deve ser +usado o valor Default. + +{% note() %} +Uma forma de ler isso é: O valor está presente nos dados de entrada (indicado +pelo Mapa de Presença)? Leia o valor dos dados de entrada; caso contrário, use +o valor default. +{% end %} + +### Operador: Copy + +O operador Copy (Cópia) indica que o valor deste campo neste registro tem o +mesmo valor do registro anterior; se este é o primeiro registro, então o valor +atual deve ser usado. Se o bit no Mapa de Presença estiver ligado para este +campo, então o decodificador deve ler o campo dos dados de entrada; caso +contrário, o valor anterior deve ser usado. + +{% note() %} +Nos dados que eu vi, todo primeiro registro tinha o bit ligado. +{% end %} + +Por exemplo: Com um template como: + +```xml + + + +``` + +... e os dados de entrada tem os seguintes registros e Mapas de Presença: + +1. O primeiro registro tem o bit do campo ligado no Mapa de Presença e a string + é lida como "primeiro". O resultado para este campo neste registro será o + valor "primeiro". +2. O segundo registro não tem o bit ligado no Mapa de Presença. Assim, o + decodificador usa o valor anterior e este campo neste registro terá o valor + "primeiro" (de novo). +3. O terceiro registro tem o bit do Mapa de Presença ligado novamente, e é lido + o valor "segundo". Este é o valor para este campo neste registro. +4. O quatro registro não tem o bit ligado no Mapa de Presença e o decodificador + usa o valor "segundo" para o campo. + +O operador Copy pode indicar o valor inicial do campo. Por exemplo + +```xml + + + +``` + +Isso significa que deve usar "string" como valor anterior, mesmo no primeiro +registro. + +Conforme indicado, campos com o operador Copy sempre aparecem no Mapa de +Presença. + +### Operador: Delta + +Delta é um operador parecido com Copy, mas ao invés de usar o valor do registro +anterior neste campo, o novo valor deve ser calculado usando o valor anterior e +o atual. De novo, se não há valor anterior, então nenhuma operação é feita o +valor nos dados de entrada é o valor atual. + +Um exemplo: + +```xml + + + +``` + +1. O primeiro registro vem com o valor "300". Este é o valor do campo. +2. O segundo registro vem com o valor "2". Este valor deve ser adicionado no + valor anterior e usado, então o valor no segundo registro é "302". +3. O terceiro registro vem com o valor "3". Novamente, deve ser usado o valor + anterior e o valor atual deve ser adicionar. Assim, o campo no terceiro + registro terá o valor "305". + +### Operador: Increment + +Increment (Incremento) funciona de forma similar ao operador Delta por estar +sempre adicionando um valor ao valor anterior, mas o valor adicionar é sempre +1. + +Se o bit do campo estiver ligado no Mapa de Presença, o valor do campo será o +valor vindo dos dados de entrada; caso contrário, deverá ser usado o valor +anterior acrescido de 1. + +Exemplo: + +```xml + + + +``` + +1. O primeiro registro tem o bit ligado para o campo no Mapa de Presença e o + decodificador lê o valor "100" nos dados de entrada. Este é o valor do campo + neste registro. +2. O segundo registro não tem o bit ligado, então não há nada a ser lido dos + dados de entrada, mas o campo deve ter o valor "101" para este registro. + +Campos com o operador Increment devem aparecer no Mapa de Presença. + +## Mapa do Mapa de Presença + +Existe um mapa simples que indica se um campo aparece ou não no Mapa de +Presença, [de acordo com a JetTek](https://jettekfix.com/education/fix-fast-tutorial/): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperadorAparece para campos Mandatórios?Aparece para campos Opcionais?
NenhumNão.Não.
ConstantNão, o valor constante é sempre usado.Sim; se ligado, deve usar o valor Constante; caso contrario, o + valor do campo é Null.
CopySim; se ligado, deve usar o valor dos dados de entrada; caso + contrário, deve ser usado o valor anterior.Sim, mesma coisa que campos Mandatórios.
DefaultSim; se ligado, deve usar o valor vindo dos dados de entrada; caso + contrário, use o valor default.Sim; mesma coisa que campos Mandatórios.
DeltaNão; o valor vindo dos dados de entrada deve ser adicionado ao + anterior.Não; mesma coisa que campos Mandatórios.
IncrementSim; se ligado, deve usar o valor vindo dos dados de entrada; caso + contrário, adicionar 1 no valor anterior.Sim; mesma coisa que campos Mandatórios.
+ +# Anomalias + +Eu chamo "anomalia" qualquer coisa que eu levei muito tempo para entender. + +## Decimais com Operadores Diferentes + +Isso foi uma coisa meio complicada para entender inicialmente. Por exemplo: + +```xml + + + + + + + + +``` + +Parece simples, mas tem várias peças se movendo ao mesmo tempo aqui: + +1. O indicador `presence="optional"` no decimal significa que o `exponent` pode + ser Null e apenas isso. +2. O operador Default no Expoente significa que o decodificador deve verificar + se o expoente tem um valor presente nos dados de entrada ou se deve ser + usado o valor default de "0". + + Existe um problema aqui: Se o Mapa de Presença indica que o valor está + presente e é lido o valor 0, como o campo é opcional, o decodificador deve + considerada o Expoente Null e, portanto, não há Mantissa e todo o valor é + Null. +4. O operador Delta na Mantissa deve ser usado aplicando o valor de entrada com + o valor anterior. Mas, se o Expoente não estiver com o bit ligado no Mapa de + Presença, como há um valor default, este deve ser usado e não lido dos dados + de entrada, e o valor lido deve ser usado na Mantissa. + +Tipo isso: + +1. O primeiro registro tem o bit no Mapa de Presença ligado e o valor lido é + "-2"; este é o Expoente. O próximo valor valor é "1020", que é o valor da + Mantissa, e o decimal inteiro é "10.20". +2. O segundo registro tem o bit ligado no Mapa de Presença e o valor lido é + "0". Como o Decimal é opcional, o Expoente é opcional e como 0 é Null para + campos opcionais, não há Expoente e o próximo valor não é a Mantissa. O + valor do campo neste registro é Null. +3. O terceiro registro não tem o bit ligado no Mapa de Presença. Como o + Expoente tem um valor default, ele fica como "0" e a Mantissa deve ser lida. + Se vier o valor "10", o decimal se torna "10" (ou "10.00" para mantermos o + mesmo número de casas decimais). + +Uma coisa estranha que vi foi relacionada com a forma como a Exchange estava +ordenando os resultados. Era uma sequência de ordens de compra e venda, na +quais + +1. O primeiro registro era a ordem de venda, com um Expoente de "0" e Mantissa + de "5410". Isso significa que o valor era "5410". +2. O segundo registro era uma ordem de compra. Ele tinha o Expoente de "-2" e + Mantissa tinha o valor de "526604". Com o operador Delta, isso dá a Mantissa + o valor de "532014", mas como o Expoente é "-2", o valor real era de + "5420.14". +3. A coisa estranha aconteceu no terceiro registro, que era novamente uma ordem + de venda. O valor deveria ser o valor igual ao primeiro, mas a Exchange + mandou o valor "0" para o Expoente e Mantissa de "-526604". Com o Delta, + isso trouxe o valor de volta a "5410". + +Eu achei estranho que a Exchange ficou mudando entre dois Expoentes ao invés de +usar apenas um, e na época eu estava com problemas com o cálculo de Deltas no +meu código, então... + +## Sequências com Tamanho Null + +Outra coisa que eu vi foi a Sequência opcional: Na prática não há diferença +entre uma sequência com 0 elementos e uma com o tamanho Null -- especialmente +se você pensar que o protocolo é, basicamente, um gerador de FIX. Eu não faço +ideia porque não foi padronizado que tamanhos são mandatórios e um valor de 0 +significa que não há valores ao invés de ficar fazendo uma dança ao redor de +sequências mandatórias e opcionais. + +