2017-11-04 2 views
3

Ich versuche, eine FIX 4.2-Sitzung zu fixieren.gdax.com (docs: https://docs.gdax.com/#fix-api) mit Python 3.5 und Stunnel. Alles funktioniert abgesehen von meiner Anmeldemeldung, die zurückgewiesen wird, und die Sitzung wird vom Server ohne Antwort geschlossen, was es schwierig macht zu debuggen, was falsch läuft. Mein Python-Code ist wie folgt:Wie FIX-Anmeldemeldung mit Python an GDAX senden

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(("127.0.0.1", 4197)) # address and port specified in stunnel config file 

# generate a signature according to the gdax protocol for signing a message: 
timestamp = str(time.time()) 
message = [timestamp, "A", "0", "f3e85389ffb809650c367d42b37e0a80", "Coinbase", "password-goes-here"] # these are the components of the pre-hash string as specified in the docs for a logon message 
message = bytes("|".join(message), 'utf-8') # add the field separator 

hmac_key = base64.b64decode(r"api-secret-goes-here") 
signature = hmac.new(hmac_key, message, hashlib.sha256) 
sign_b64 = base64.b64encode(signature.digest()).decode() 
# in the above line the .decode() is not included when used to authenticate messages to the REST API and those are working successfully. 
#The reason I've included it here is to allow a string to be passed into the variable 'body' below: 

msgType = "A" 
t   = str(datetime.utcnow()).replace("-","").replace(" ", "-")[:-3] # format the timestamp into YYYYMMDD-HH:MM:SS.sss as per the FIX standard 

body  = '34=1|52=%s|49=f3e85389ffb809650c367d42b37e0a80|56=Coinbase|98=0|108=30|554=password-goes-here|96=%s|8013=Y|' % (t, sign_b64) 
bodyLength = len(body.encode('utf-8')) # length of the message in bytes 
header  = '8=FIX.4.2|9=%s|35=%s|' % (bodyLength, msgType) 
msg  = header + body 

# generate the checksum: 
def check_sum(s): 
    sum = 0 
    for char in msg: 
     sum += ord(char) 
    sum = str(sum % 256) 
    while len(sum) < 3: 
     sum = '0' + sum 
    return sum 

c_sum = check_sum(msg) 
logon = msg + "10=%s" % c_sum # append the check sum onto the message 
logon = logon.encode('ascii') # create a bytes object to send over the socket 
print(logon) 

s.sendall(logon) 
print(s.recv(4096)) 

Die Ergebnisse dieser beiden print-Anweisungen sind:

b'8=FIX.4.2|9=159|35=A|34=1|52=20171104-11:13:53.331|49=f3e85389ffb809650c367d42b37e0a80|56=Coinbase|98=0|108=30|554=password-goes-here|96=G7yeX8uQqsCEhAjWDWHoBiQz9lZuoE0Q8+bLJp4XnPY=|8013=Y|10=212' 
b'' 

Es gibt viele Variablen hier, die falsch sein könnte und der Prozess von Versuch und Irrtum wird immer ein bisschen langweilig. Kann jemand sehen, was mit der Anmeldungsnachricht falsch ist?

+1

Es sieht so aus, als ob Sie '|' als Feldtrennzeichen verwenden. Das FIX-Feldtrennzeichen ist ASCII-Zeichen 1 (ich glaube, das ist '\ x01' in Python), daher folgen Ihre Nachrichten nicht dem FIX-Protokoll, was die fehlende Antwort erklären kann. Ich würde sehr empfehlen, eine vorhandene FIX-Bibliothek zu verwenden, anstatt zu versuchen, Ihre eigene zu implementieren. – Iridium

+0

@Iridium Ich habe das auch versucht, ohne Erfolg :(Ich werde stattdessen eine Bibliothek versuchen, danke! – jp94

+0

Ich habe fast den gleichen Code wie deiner, und bekomme ein Ergebnis mit einem msgSeqNum (34) Wert> 1. Allerdings , es meldet sich immer noch nicht – bill0ute

Antwort

6

ich einige Änderungen an Ihrem Code vorgenommen und setzen Kommentare, wo es von Ihrem unterscheidet (korrigiert Ihre Fehler):

import socket 
import base64 
import time, datetime 
import hmac 
import hashlib 

PASSPHRASE = "your passphrase" 
API_KEY = "your api key" 
API_SECRET = "your secret" 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect(("127.0.0.1", 4197)) 

seq_num = "1" # Correction: using the same MsgSeqNum for signed text and for the field 34 


# Correction: t is the same in both signed RawData and in SendingTime (52) 
timestamp = str(time.time()) 
t = str(datetime.datetime.utcnow()).replace("-","").replace(" ", "-")[:-3] 

# Correction: '|' is not a valid separator for FIX, it must be '\u0001' 
message = "\u0001".join([t, "A", seq_num, API_KEY, "Coinbase", PASSPHRASE]).encode("utf-8") 

hmac_key = base64.b64decode(API_SECRET) 
signature = hmac.new(hmac_key, message, hashlib.sha256) 
sign_b64 = base64.b64encode(signature.digest()).decode() 

msgType = "A" 

body = "34={}|52={}|49={}|56=Coinbase|98=0|108=30|554={}|96={}|8013=Y|".format(seq_num, t, API_KEY, PASSPHRASE, sign_b64) # using the same time (t) and seq_num as in signed text 

# Correction: bodyLength is the number of characters, not bytes, also it must include everything after "8=FIX.4.2|9={}|" i.e. the "35=A|" part of the header 
bodyLength = len("35={}|".format(msgType)) + len(body) 
header  = "8=FIX.4.2|9={}|35={}|".format(bodyLength, msgType) 
msg  = header + body 

msg = msg.replace('|', '\u0001') # Correction: '|' is not a valid separator for FIX, it must be '\u0001' 

# generate the checksum: 
def check_sum(s): 
    sum = 0 
    for char in msg: 
     sum += ord(char) 
    sum = str(sum % 256) 
    while len(sum) < 3: 
     sum = '0' + sum 
    return sum 

c_sum = check_sum(msg) 

logon = msg + "10={}\u0001".format(c_sum) 
logon = logon.encode('ascii') 
print(logon) 

s.sendall(logon) 
print(s.recv(4096)) 

Für mich korrigierte Code nun die Logon-Nachricht vom Server anstatt nur 0 Bytes zurückgibt wie es in deinem Fall war. Können Sie bestätigen, dass es auch für Sie funktioniert und dass Sie nach der Anmeldung erfolgreich weitere Transaktionen senden können?

+1

Es gibt immer noch einen Fehler in diesem Skript - Python interpretiert 'u \ 0001' als 6-stellige Zeichenkette Sie benötigen au vor der Zeichenkette (u '\ u0001'), um die Single zu erhalten -char separator, das FIX benötigt Einfacher wäre es, in die hexadezimale Notation '\ x01' zu konvertieren, die in Nicht-Unicode-Kontexten funktioniert - zu diesem Zeitpunkt denke ich, dass keiner der ascii/utf-8-Encoding-Aufrufe notwendig sein würde – JHumphrey

+2

Danke Ich habe vergessen zu erwähnen, dass ich meinen Code in Python 3 getestet habe, wo Strings standardmäßig Unicode sind. Und da sie standardmäßig Unicode sind, muss man beim Senden/Empfangen von Strings immer über Sockets codieren/dekodieren Python 2, wahrscheinlich, es ist nicht notwendig, aber ich habe nicht getestet es. – some

+1

Ah, danke für den Kommentar. Ich bin auf Python 2, also muss das der Unterschied sein. – JHumphrey

Verwandte Themen