I'm trying to establish a FIX 4.2 session to fix.gdax.com (docs: https://docs.gdax.com/#fix-api or https://docs.prime.coinbase.com/?python#logon-a) using Python 3.5 and stunnel. Everything is working apart from my logon message which is rejected and the session is closed by the server with no response making it difficult to debug what's going wrong. My Python code is as follows:
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))
The results of those two print statements are:
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''
There are a lot of variables here that could be wrong and the process of trial and error is getting a bit tedious. Can anyone see what is wrong with the logon message?
Nothing new to add just wanted to rephrase the above solution in a more function based way without tunneling:
I made some modifications to your code and put comments where it differs from yours (corrects your errors):
For me this corrected code now returns the Logon message from the server instead of just 0 bytes as it was in your case. Can you confirm that it also works for you and that you can successfully send other transactions after logon is done?