A língua que corretoras e bolsas falam
Quando um sistema de trading envia uma ordem a uma corretora ou bolsa, muito frequentemente o faz em FIX, o protocolo Financial Information eXchange, em uso desde o início dos anos 1990. Uma mensagem FIX é texto simples: uma lista de campos tag=valor, cada um terminado por um caractere separador invisível, chr(1) (SOH). Cada tag é um número com significado combinado: 35 é o tipo de mensagem, 55 o símbolo, 54 o lado, 38 a quantidade, 44 o preço. O formato é antigo e pouco glamuroso, e toda a indústria o entende.
# A FIX message is tag=value fields each ended by a separator (shown here as |)
message = "8=FIX.4.2|9=60|35=D|11=ord-1|55=AAPL|54=1|38=100|40=2|44=184.50|10=123|"
fields = {}
for part in message.split("|"):
if part: # the last separator leaves an empty piece
tag, value = part.split("=", 1) # split at the FIRST = only
fields[int(tag)] = value
print(fields[35], fields[55], fields[38], fields[44]) # D AAPL 100 184.50
print(sorted(fields)) # [8, 9, 10, 11, 35, 38, 40, 44, 54, 55]A forma de uma mensagem
Uma mensagem tem três partes. O cabeçalho começa com 8=FIX.4.2 (a versão), depois 9= o comprimento do corpo, depois o tipo de mensagem 35=. O corpo leva o conteúdo: para um New Order Single (35=D), o id da ordem do cliente, o símbolo, o lado (1 compra, 2 venda), a quantidade, o tipo de ordem (40=2 limite) e o preço. O rodapé é 10=, um checksum: some os códigos dos caracteres de tudo o que vem antes e guarde o resto da divisão por 256, escrito com três dígitos. O receptor o recalcula para detectar corrupção e também verifica o comprimento do corpo. As sessões acrescentam o resto: logon (35=A), heartbeats (35=0) e números de sequência como os do último tópico.
# The checksum: add up the character codes, keep the remainder modulo 256, three digits
def checksum(text):
return f"{sum(ord(c) for c in text) % 256:03d}"
print(checksum("A")) # 065 the code of "A" is 65
print(checksum("aaa")) # 035 3 * 97 = 291 and 291 - 256 = 35
print(checksum("8=FIX.4.2|35=0|")) # a heartbeat header# Writing fields: tag=value and a separator after each one
def build_fix(fields, sep="|"):
return "".join(f"{tag}={value}{sep}" for tag, value in fields)
body = build_fix([(35, "D"), (11, "ord-1"), (55, "AAPL"), (54, 1), (38, 100)])
print(body) # 35=D|11=ord-1|55=AAPL|54=1|38=100|
print(len(body)) # 34 tag 9 holds this length# A complete order: header with the body length, the body, then the checksum trailer
def checksum(text):
return f"{sum(ord(c) for c in text) % 256:03d}"
def build_fix(fields, sep="|"):
return "".join(f"{tag}={value}{sep}" for tag, value in fields)
def new_order_message(cl_ord_id, symbol, side, quantity, price, sep="|"):
code = "1" if side == "buy" else "2"
body = build_fix([(35, "D"), (11, cl_ord_id), (55, symbol), (54, code), (38, quantity), (40, 2), (44, f"{price:.2f}")], sep)
head = build_fix([(8, "FIX.4.2"), (9, len(body))], sep) + body
return head + build_fix([(10, checksum(head))], sep)
message = new_order_message("ord-1", "AAPL", "buy", 100, 184.5)
print(message) # 8=FIX.4.2|9=...|35=D|11=ord-1|...|44=184.50|10=...|
print(message.split("|")[-2]) # the checksum field- Uma mensagem FIX são campos
tag=valorcada um terminado por um separador (chr(1)no fio); divida cada campo no primeiro=. - Cabeçalho
8(versão) e9(comprimento do corpo), corpo a partir de35(tipo), rodapé10(checksum). - O checksum é a soma dos códigos dos caracteres módulo 256, em três dígitos; use um motor testado em produção e registre cada mensagem.
A seguir: código limpo, nomes, funções pequenas e os hábitos que mantêm legível uma base de código de trading.
Lab: o que é avaliado
- Ler uma mensagem com
parse_fix25 pts - Selar uma mensagem com
checksum25 pts - Escrever campos com
build_fix25 pts - Enviar uma ordem com
new_order_message25 pts