You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
605 lines
40 KiB
605 lines
40 KiB
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
<meta http-equiv="content-type" content="text/html; charset=utf-8"> |
|
|
|
<!-- Enable responsiveness on mobile devices--> |
|
<!-- viewport-fit=cover is to support iPhone X rounded corners and notch in landscape--> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover"> |
|
|
|
<title>Julio Biason .Me 4.3</title> |
|
|
|
<!-- CSS --> |
|
<link rel="stylesheet" href="https://blog.juliobiason.me/print.css" media="print"> |
|
<link rel="stylesheet" href="https://blog.juliobiason.me/poole.css"> |
|
<link rel="stylesheet" href="https://blog.juliobiason.me/hyde.css"> |
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700|Abril+Fatface"> |
|
|
|
|
|
|
|
|
|
|
|
</head> |
|
|
|
<body class=" "> |
|
|
|
<div class="sidebar"> |
|
<div class="container sidebar-sticky"> |
|
<div class="sidebar-about"> |
|
|
|
<a href="https://blog.juliobiason.me"><h1>Julio Biason .Me 4.3</h1></a> |
|
|
|
<p class="lead">Old school dev living in a 2.0 dev world</p> |
|
|
|
|
|
</div> |
|
|
|
<ul class="sidebar-nav"> |
|
|
|
|
|
<li class="sidebar-nav-item"><a href="/">English</a></li> |
|
|
|
<li class="sidebar-nav-item"><a href="/pt">Português</a></li> |
|
|
|
<li class="sidebar-nav-item"><a href="/tags">Tags (EN)</a></li> |
|
|
|
<li class="sidebar-nav-item"><a href="/pt/tags">Tags (PT)</a></li> |
|
|
|
|
|
</ul> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="content container"> |
|
|
|
<div class="post"> |
|
<h1 class="post-title">Decodificando o Protocolo FAST</h1> |
|
<span class="post-date"> |
|
2022-01-10 |
|
|
|
<a href="https://blog.juliobiason.me/pt/tags/financas/">#finanças</a> |
|
|
|
<a href="https://blog.juliobiason.me/pt/tags/binario/">#binário</a> |
|
|
|
<a href="https://blog.juliobiason.me/pt/tags/protocolo/">#protocolo</a> |
|
|
|
<a href="https://blog.juliobiason.me/pt/tags/fix/">#fix</a> |
|
|
|
<a href="https://blog.juliobiason.me/pt/tags/fast/">#fast</a> |
|
|
|
</span> |
|
<p>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.</p> |
|
<span id="continue-reading"></span><div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>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.</p> |
|
<p>O changelog está no final deste post.</p> |
|
|
|
</div> |
|
<h1 id="o-que-e-o-fast">O que é o FAST</h1> |
|
<p><a href="https://en.wikipedia.org/wiki/FAST_protocol">FAST</a> é, basicamente, um método |
|
de compressão para o protocolo FIX.</p> |
|
<h1 id="e-o-que-e-fix">E o que é FIX</h1> |
|
<p><a href="https://en.wikipedia.org/wiki/Financial_Information_eXchange">FIX</a> é 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.</p> |
|
<p>FIX é um protocolo muito simples: você tem pares de "campo ID" e "valor" |
|
separados por um "<code>=</code>" (sinal de igual) e os pares são separados pelo caractere |
|
ASCII com código 1 (que é representado por <code>^A</code> em alguns editores). Alguns IDs |
|
de campo são definidos pelo protocolo (há uma lista completa |
|
<a href="https://www.inforeachinc.com/fix-dictionary/index.html">aqui</a>), mas cada |
|
central pode criar seus próprios IDs.</p> |
|
<p>Por exemplo, se você tiver o campo MsgType (ID 35) com valor "<code>y</code>" e o campo |
|
Security ID (ID 48) com valor " 123456", a mensagem recebida seria</p> |
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>35=y^A48=123456 |
|
</span></code></pre> |
|
<h1 id="e-de-volta-ao-fast">E de Volta ao FAST</h1> |
|
<p>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.</p> |
|
<p>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.</p> |
|
<h1 id="o-template">O Template</h1> |
|
<p>O template é um arquivo XML que descreve os campos, seus tipos, nomes, IDs e |
|
operadores. 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.</p> |
|
<p>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.</p> |
|
<h2 id="tipos-de-campos">Tipos de Campos</h2> |
|
<p>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).</p> |
|
<p>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:</p> |
|
<ul> |
|
<li>Ler um byte;</li> |
|
<li>O bit de mais alta ordem é 0? |
|
<ul> |
|
<li>Sim: Continue lendo;</li> |
|
<li>Não: Pare de ler o valor do campo.</li> |
|
</ul> |
|
</li> |
|
</ul> |
|
<p>Eu vou mostrar alguns exemplos mais a frente neste post.</p> |
|
<h2 id="definicao-dos-campos">Definição dos Campos</h2> |
|
<p>O template define os campos com seus tipo nome (opcional), ID, |
|
um indicador de presença e um operador (opcional).</p> |
|
<p>Por exemplo, para descrever um inteiro sem sinal de 32 bits, chamado "MsgType" |
|
com ID "35", ele seria descrito no template como</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">uInt32 </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MsgType</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">35</span><span>"/> |
|
</span></code></pre> |
|
<p>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</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">int32 </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">ImpliedMarketIndicator</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">1144</span><span>" </span><span style="color:#d08770;">presence</span><span>="</span><span style="color:#a3be8c;">optional</span><span>"/> |
|
</span></code></pre> |
|
<p>... então o campo pode não ter valor. Isso também é definido como campo |
|
"nulável" (nullable).</p> |
|
<h2 id="campos-mandatorios-e-opcionais">Campos Mandatórios e Opcionais</h2> |
|
<p>A diferença entre campos opcionais e mandatórios é que campos mandatórios |
|
<em>sempre</em> tem um valor, mesmo que seja 0. Campos opcionais, por outro lado, |
|
podem ser null e não ter valor algum.</p> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>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.</p> |
|
<p>Então é algo mais como "compatibilidade com versões anteriores" do que qualquer |
|
outra coisa.</p> |
|
|
|
</div> |
|
<h2 id="tipos">Tipos</h2> |
|
<h3 id="tipos-inteiros">Tipos: Inteiros</h3> |
|
<p>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.</p> |
|
<p>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.</p> |
|
<p>Inteiros com sinal funcionam exatamente igual, mas com complemento de 2.</p> |
|
<p>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):</p> |
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>0000_0001 1001_0010 |
|
</span></code></pre> |
|
<p>... 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 <code>1001_0010</code> -- ou 146.</p> |
|
<p>Números negativos são representados por complemento de 2, então se o |
|
decodificador receber, por exemplo:</p> |
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>0000_0011 0111_1110 1110_1110 |
|
</span></code></pre> |
|
<p>... que, quando os bits de alta ordem são removidos, deve gerar "<code>1111_1111 0110_1110</code>", que é -146 (em 16 bits, apenas para ficar mais fácil de ler).</p> |
|
<p>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.</p> |
|
<p>No template, os campos aparecem como</p> |
|
<ul> |
|
<li><code><uInt32/></code>: Inteiro sem sinal de 32 bits.</li> |
|
<li><code><uInt64/></code>: Inteiro sem sinal de 64 bits.</li> |
|
<li><code><int32/></code>: Inteiro com sinal de 32 bits.</li> |
|
<li><code><int64/></code>: Inteiro sem sinal de 64 bits.</li> |
|
</ul> |
|
<h3 id="tipos-strings">Tipos: Strings</h3> |
|
<p>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.</p> |
|
<p>Por exemplo</p> |
|
<pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>0100_1000 0110_0101 0110_1100 0110_1100 1110_1111 |
|
</span></code></pre> |
|
<p>... 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.</p> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>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.</p> |
|
|
|
</div> |
|
<p>Strings opcionais são Null quando o primeiro byte tiver o bit de mais alta |
|
ordem ligado e todos os demais desligados.</p> |
|
<p>No template, um campo de string aparece como <code><string></code>.</p> |
|
<h3 id="tipos-sequencias">Tipos: Sequências</h3> |
|
<p>Sequências são basicamente arrays. O primeiro campo de uma sequência é o |
|
"tamanho" (com o tipo "<code><length></code>" 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.</p> |
|
<p>Sequências opcionais afetam a forma como o campo de tamanho é lido: Se a |
|
sequência é opcional, o tamanho deve ser tratado como um inteiro opcional e, |
|
portanto, decrementando em 1.</p> |
|
<p>No template, a sequência aparece como <code><sequence></code>, com o tamanho logo depois. |
|
Um exemplo é</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">sequence </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">Sequence</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">length </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">NoSequence</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">123</span><span>"/> |
|
</span><span></</span><span style="color:#bf616a;">sequence</span><span>> |
|
</span></code></pre> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>Eu descrevo a maior parte dos campos de tamanho com um nome que começa com |
|
"No". Isso é porque a especificação do FIX define os tamanhos com esse prefixo.</p> |
|
|
|
</div> |
|
<h3 id="tipos-decimais">Tipos: Decimais</h3> |
|
<p>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 é <code>1020 * 10 ^ -2</code> ("1020 vezes 10 na potência -2") ou |
|
"10.20".</p> |
|
<p>Tanto o Expoente quanto a Mantissa são lidos como Inteiros com sinal.</p> |
|
<p>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.</p> |
|
<p>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.</p> |
|
<p>No template, um campo decimal aparece como <code><decimal></code>, com o expoente e a |
|
mantissa como campos internos.</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">decimal </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">ADecimal</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">123</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">exponent</span><span>/> |
|
</span><span> <</span><span style="color:#bf616a;">mantissa</span><span>/> |
|
</span><span></</span><span style="color:#bf616a;">decimal</span><span>> |
|
</span></code></pre> |
|
<h3 id="tipo-mapa-de-presenca">Tipo: Mapa de Presença</h3> |
|
<p>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.</p> |
|
<p>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.</p> |
|
<p>Os bits do Mapa de Presença sequem a ordem dos campos no template. Por exemplo, |
|
se um template tem</p> |
|
<ol> |
|
<li>Um campo mandatório;</li> |
|
<li>Um campo com um operador que requer o Mapa de Presença (eu vou comentar |
|
sobre estes mais pra frente);</li> |
|
<li>Outro campo mandatório;</li> |
|
<li>E, finalmente, outro campo com operador.</li> |
|
</ol> |
|
<p>... é possível ter um mapa de presença como <code>1110_0000</code>, no qual:</p> |
|
<ol> |
|
<li>O primeiro bit é o bit de parada, e o decodificar assume que este é o último |
|
byte do Mapa de Presença.</li> |
|
<li>O segundo bit indica que o primeiro com operador está presente. Ele <em>não</em> |
|
representa o campo mandatório porque, bom, o campo é mandatório e, assim, |
|
está sempre presente.</li> |
|
<li>O segundo bit indica que o segundo campo com operador está presente.</li> |
|
</ol> |
|
<p>(De novo, eu vou mencionar quais operadores requerem o Mapa de Presença.)</p> |
|
<h2 id="operadores">Operadores</h2> |
|
<p>Operadores definem a forma como alguns campos devem ser tratados. Eu vi 5 tipos |
|
diferentes de Operadores:</p> |
|
<ul> |
|
<li>Nenhum;</li> |
|
<li>Constant;</li> |
|
<li>Default;</li> |
|
<li>Copy;</li> |
|
<li>Delta;</li> |
|
<li>Increment.</li> |
|
</ul> |
|
<h3 id="operador-nenhum">Operador: Nenhum.</h3> |
|
<p>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.</p> |
|
<p>Quando um campo tem o operador "Nenhum", não haverá bit para o mesmo no Mapa de |
|
Presença.</p> |
|
<h3 id="operador-constant">Operador: Constant</h3> |
|
<p>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</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">Source</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">22</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">constant </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">123</span><span>"/> |
|
</span><span></</span><span style="color:#bf616a;">string</span><span>> |
|
</span><span><</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">Exchange</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">207</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">constant </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">EXCHANGE</span><span>"/> |
|
</span><span></</span><span style="color:#bf616a;">string</span><span>> |
|
</span></code></pre> |
|
<p>Existe uma pegadinha, no entanto: Se um campo contante pode ser Null |
|
(<code>presence="optional"</code>), 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.</p> |
|
<p>O Mapa de Presença deve ser usado apenas se o campo com valor contante é |
|
opcional.</p> |
|
<h3 id="operador-default">Operador: Default</h3> |
|
<p>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.</p> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>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.</p> |
|
|
|
</div> |
|
<p>Exemplo</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">uInt32 </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">Type</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">3</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">default </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">1</span><span>"/> |
|
</span><span></</span><span style="color:#bf616a;">uInt32</span><span>> |
|
</span></code></pre> |
|
<h3 id="operador-copy">Operador: Copy</h3> |
|
<p>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. </p> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>Nos dados que eu vi, todo primeiro registro tinha o bit ligado.</p> |
|
|
|
</div> |
|
<p>Por exemplo: Com um template como:</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MDReqID</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">262</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">copy</span><span>/> |
|
</span><span></</span><span style="color:#bf616a;">string</span><span>> |
|
</span></code></pre> |
|
<p>... e os dados de entrada tem os seguintes registros e Mapas de Presença:</p> |
|
<ol> |
|
<li>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".</li> |
|
<li>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).</li> |
|
<li>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.</li> |
|
<li>O quatro registro não tem o bit ligado no Mapa de Presença e o decodificador |
|
usa o valor "segundo" para o campo.</li> |
|
</ol> |
|
<p>O operador Copy pode indicar o valor inicial do campo. Por exemplo</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MDReqID</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">262</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">copy </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">string</span><span>"/> |
|
</span><span></</span><span style="color:#bf616a;">string</span><span>> |
|
</span></code></pre> |
|
<p>Isso significa que deve usar "string" como valor anterior, mesmo no primeiro |
|
registro.</p> |
|
<p>Conforme indicado, campos com o operador Copy sempre aparecem no Mapa de |
|
Presença.</p> |
|
<h3 id="operador-delta">Operador: Delta</h3> |
|
<p>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.</p> |
|
<p>Um exemplo:</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">uInt32 </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">NumberOfOrders</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">346</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">delta</span><span>/> |
|
</span><span></</span><span style="color:#bf616a;">uInt32</span><span>> |
|
</span></code></pre> |
|
<ol> |
|
<li>O primeiro registro vem com o valor "300". Este é o valor do campo.</li> |
|
<li>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".</li> |
|
<li>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".</li> |
|
</ol> |
|
<h3 id="operador-increment">Operador: Increment</h3> |
|
<p>Increment (Incremento) funciona de forma similar ao operador Delta por estar |
|
sempre adicionando um valor ao valor anterior, mas o valor adicionar é sempre |
|
1.</p> |
|
<p>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.</p> |
|
<p>Exemplo:</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">uInt32 </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">RptSeq</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">83</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">increment</span><span>/> |
|
</span><span></</span><span style="color:#bf616a;">uInt32</span><span>> |
|
</span></code></pre> |
|
<ol> |
|
<li>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.</li> |
|
<li>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.</li> |
|
</ol> |
|
<p>Campos com o operador Increment devem aparecer no Mapa de Presença.</p> |
|
<h2 id="mapa-do-mapa-de-presenca">Mapa do Mapa de Presença</h2> |
|
<p>Existe um mapa simples que indica se um campo aparece ou não no Mapa de |
|
Presença, <a href="https://jettekfix.com/education/fix-fast-tutorial/">de acordo com a JetTek</a>:</p> |
|
<table> |
|
<tr> |
|
<th>Operador</th> |
|
<th>Aparece para campos Mandatórios?</th> |
|
<th>Aparece para campos Opcionais?</th> |
|
</tr> |
|
<tr> |
|
<td>Nenhum</td> |
|
<td>Não.</td> |
|
<td>Não.</td> |
|
</tr> |
|
<tr> |
|
<td>Constant</td> |
|
<td>Não, o valor constante é sempre usado.</td> |
|
<td>Sim; se ligado, deve usar o valor Constante; caso contrario, o |
|
valor do campo é Null.</td> |
|
</tr> |
|
<tr> |
|
<td>Copy</td> |
|
<td>Sim; se ligado, deve usar o valor dos dados de entrada; caso |
|
contrário, deve ser usado o valor anterior.</td> |
|
<td>Sim, mesma coisa que campos Mandatórios.</td> |
|
</tr> |
|
<tr> |
|
<td>Default</td> |
|
<td>Sim; se ligado, deve usar o valor vindo dos dados de entrada; caso |
|
contrário, use o valor default.</td> |
|
<td>Sim; mesma coisa que campos Mandatórios.</td> |
|
</tr> |
|
<tr> |
|
<td>Delta</td> |
|
<td>Não; o valor vindo dos dados de entrada deve ser adicionado ao |
|
anterior.</td> |
|
<td>Não; mesma coisa que campos Mandatórios.</td> |
|
</tr> |
|
<tr> |
|
<td>Increment</td> |
|
<td>Sim; se ligado, deve usar o valor vindo dos dados de entrada; caso |
|
contrário, adicionar 1 no valor anterior.</td> |
|
<td>Sim; mesma coisa que campos Mandatórios.</td> |
|
</tr> |
|
</table> |
|
<h1 id="versionamento-de-templates">Versionamento de Templates</h1> |
|
<p>Eu não mencionei isso antes, mas agora que eu expliquei algumas coisas sobre os |
|
tipos e algumas informações sobre o template, eu posso dizer que o arquivo de |
|
template permite que existam múltiplas versões das mesmas mensagens.</p> |
|
<p>Por exemplo</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><?</span><span style="color:#bf616a;">xml </span><span style="color:#d08770;">version</span><span>="</span><span style="color:#a3be8c;">1.0</span><span>" </span><span style="color:#d08770;">encoding</span><span>="</span><span style="color:#a3be8c;">UTF-8</span><span>"?> |
|
</span><span><</span><span style="color:#bf616a;">templates </span><span style="color:#d08770;">xmlns</span><span>="</span><span style="color:#a3be8c;">http://www.fixprotocol.org/ns/fast/td/1.1</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">template </span><span style="color:#d08770;">xmlns</span><span>="</span><span style="color:#a3be8c;">http://www.fixprotocol.org/ns/fast/td/1.1</span><span>" </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">SomeRecordType</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">1</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MsgType</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">35</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">constant </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">Z</span><span>"/> |
|
</span><span> </</span><span style="color:#bf616a;">string</span><span>> |
|
</span><span> <</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">SomeField</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">1</span><span>"/> |
|
</span><span> </</span><span style="color:#bf616a;">template</span><span>> |
|
</span><span> |
|
</span><span> <</span><span style="color:#bf616a;">template </span><span style="color:#d08770;">xmlns</span><span>="</span><span style="color:#a3be8c;">http://www.fixprotocol.org/ns/fast/td/1.1</span><span>" </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">SomeRecordType</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">2</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MsgType</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">35</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">constant </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">Z</span><span>"/> |
|
</span><span> </</span><span style="color:#bf616a;">string</span><span>> |
|
</span><span> <</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">SomeField</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">1</span><span>"/> |
|
</span><span> <</span><span style="color:#bf616a;">string </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">SomeOtherField</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">2</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">default </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">A Value!</span><span>"/> |
|
</span><span> </</span><span style="color:#bf616a;">string</span><span>> |
|
</span><span> </</span><span style="color:#bf616a;">template</span><span>> |
|
</span><span></</span><span style="color:#bf616a;">templates</span><span>> |
|
</span></code></pre> |
|
<p>Uma coisa que você pode notar é que existem dois templates definidos, um com ID |
|
"1" e outro com ID "2". Ambos tem o mesmo nome e o mesmo campo com um valor |
|
constante, mas a informação inicial dos dados de entrada indica qual destes |
|
dois deve ser usada.</p> |
|
<p>Os dados de entrada começam com um Mapa de Presença. O primeiro bit deste mapa |
|
é o "Template ID". Com este Template ID, o decodificador pode encontrar a lista |
|
de campos que devem ser processados. Este mapa também tem os campos da |
|
sequência inicial -- no nosso exemplo, se o Template ID for "2", o outro bit no |
|
Mapa de Presença é o indicador do "SomeOtherField".</p> |
|
<div style="border:1px solid grey; margin:7px; padding: 7px"> |
|
<p>Até agora, eu não vi dados de entrada que não tivessem o bit indicador do |
|
template ID ligado, então eu não tenho muita certeza do que fazer caso isso |
|
aconteça.</p> |
|
|
|
</div> |
|
<h1 id="anomalias">Anomalias</h1> |
|
<p>Eu chamo "anomalia" qualquer coisa que eu levei muito tempo para entender.</p> |
|
<h2 id="decimais-com-operadores-diferentes">Decimais com Operadores Diferentes</h2> |
|
<p>Isso foi uma coisa meio complicada para entender inicialmente. Por exemplo:</p> |
|
<pre data-lang="xml" style="background-color:#2b303b;color:#c0c5ce;" class="language-xml "><code class="language-xml" data-lang="xml"><span><</span><span style="color:#bf616a;">decimal </span><span style="color:#d08770;">name</span><span>="</span><span style="color:#a3be8c;">MDEntryPX</span><span>" </span><span style="color:#d08770;">id</span><span>="</span><span style="color:#a3be8c;">270</span><span>" </span><span style="color:#d08770;">presence</span><span>="</span><span style="color:#a3be8c;">optional</span><span>"> |
|
</span><span> <</span><span style="color:#bf616a;">exponent</span><span>> |
|
</span><span> <</span><span style="color:#bf616a;">default </span><span style="color:#d08770;">value</span><span>="</span><span style="color:#a3be8c;">0</span><span>"/> |
|
</span><span> </</span><span style="color:#bf616a;">exponent</span><span>> |
|
</span><span> <</span><span style="color:#bf616a;">mantissa</span><span>> |
|
</span><span> <</span><span style="color:#bf616a;">delta</span><span>/> |
|
</span><span> </</span><span style="color:#bf616a;">mantissa</span><span>> |
|
</span><span></</span><span style="color:#bf616a;">decimal</span><span>> |
|
</span></code></pre> |
|
<p>Parece simples, mas tem várias peças se movendo ao mesmo tempo aqui:</p> |
|
<ol> |
|
<li> |
|
<p>O indicador <code>presence="optional"</code> no decimal significa que o <code>exponent</code> pode |
|
ser Null e apenas isso.</p> |
|
</li> |
|
<li> |
|
<p>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".</p> |
|
<p>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.</p> |
|
</li> |
|
<li> |
|
<p>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.</p> |
|
</li> |
|
</ol> |
|
<p>Tipo isso:</p> |
|
<ol> |
|
<li>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".</li> |
|
<li>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.</li> |
|
<li>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).</li> |
|
</ol> |
|
<p>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</p> |
|
<ol> |
|
<li>O primeiro registro era a ordem de venda, com um Expoente de "0" e Mantissa |
|
de "5410". Isso significa que o valor era "5410".</li> |
|
<li>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".</li> |
|
<li>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".</li> |
|
</ol> |
|
<p>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...</p> |
|
<h2 id="sequencias-com-tamanho-null">Sequências com Tamanho Null</h2> |
|
<p>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.</p> |
|
<hr /> |
|
<p>Changelog:</p> |
|
<ul> |
|
<li>2022-01-10: Primeira versão.</li> |
|
<li>2022-01-10: Adicionada informações sobre o versionamento de templates.</li> |
|
<li>2022-01-13: Adicionados exemplos das tags no template para os tipos de campos |
|
e exemplos dos operadores.</li> |
|
</ul> |
|
<!-- |
|
vim:spelllang=pt: |
|
--> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
</body> |
|
|
|
</html>
|
|
|