2

I'm I'm trying to establish a FIX 4.2 session to fix.gdax.com (docs: https://docs.gdax.com/#fix-api) using C# with .Net Core 2.0. when i try to logon i receive 0 bytes as response as server. I really dont know what i have wrong, this is the code:

    private async Task ConnectToGdaxFix()
    {
        _socketTcpClient = new TcpClient();
        _socketTcpClient.NoDelay = true;
        _bufferEnd = 0;
        await _socketTcpClient.ConnectAsync(_gdaxFixGateway, _gdaxFixPort);
        _sslStream = new SslStream(_socketTcpClient.GetStream());
        await _sslStream.AuthenticateAsClientAsync(_gdaxFixGateway);
        byte[] buffer = new byte[2048];
        var message = await FixSendLoginMessage();
        var bytes = await _sslStream.ReadAsync(buffer, 0, buffer.Length);
    }

    private async Task<string> FixSendLoginMessage()
    {
        var sendTime = DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss.fff");
        string msgType = "A";
        var messageSeqNumber = (++messageSeqNum).ToString();
        var senderCompId = "apiKey";
        var targetCompId = "Coinbase";
        var password = "passphrase";
        var accessSign = CreateAccessSign(
                sendTime,
                msgType,
                messageSeqNumber,
                senderCompId,
                targetCompId,
                password
            );
        var logonMessage = $"98=0{'\x01'}108=30{'\x01'}554={password}{'\x01'}96={accessSign}{'\x01'}8013=Y";
        var headerAfter9Tag = $"35={msgType}{'\x01'}34={messageSeqNumber}{'\x01'}52={sendTime}{'\x01'}49={senderCompId}{'\x01'}56={targetCompId}";
        var length = logonMessage.Length + headerAfter9Tag.Length + 1;
        var messageHeader = $"8=FIX.4.2{'\x01'}9={length}{'\x01'}{headerAfter9Tag}";
        var messageWithHeader = $"{messageHeader}{'\x01'}{logonMessage}{'\x01'}";
        int sum = 0;
        int len = messageWithHeader.Length;
        for (int i = 0; i < len; i++)
        {
            sum += Convert.ToChar(messageWithHeader.Substring(i, 1));
        }
        sum = sum % 256;
        var messageWithChecksum = $"{messageWithHeader}10={sum.ToString("000")}{'\x01'}";
        byte[] buffer = Encoding.ASCII.GetBytes(messageWithChecksum);
        await _sslStream.WriteAsync(buffer, 0, buffer.Length);
        await _sslStream.FlushAsync();
        return messageWithChecksum;
    }

    private string CreateAccessSign(
            string sendingTime,
            string msgType,
            string msgSeqNumber,
            string senderCompId,
            string targetCompId,
            string password
        )
    {
        var stringToHash = string.Join('\x01', sendingTime, msgType, msgSeqNumber, senderCompId, targetCompId, password);
        var privateKeyAsBytes = Encoding.UTF8.GetBytes("privateKey");
        using (var hmac = new HMACSHA256(privateKeyAsBytes))
        {
            var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToHash));
            return Convert.ToBase64String(signature);
        }
    }

Can anyone please help me to see what is wrong with the logon message or ssl encryption?

This is the message sent to Gdax(the characters of the sensitive data have been replaced and the \x01 is the | to simplify the view): 8=FIX.4.2|9=161|35=A|34=1|52=20180124-00:50:34.083|49=426123d789fa8e5c3782c549kj9de06e|56=Coinbase|98=0|108=30|554=outswrt|96=qkE5KPMLjn+Ef9Zgk1/kvL0Etem6bK2llINwMjOkDy9=|8013=Y|10=028

Marco Levarato
  • 203
  • 4
  • 16
  • Zero bytes is not bad. You could be getting a error instead. You need to use a sniffer like wireshark or fiddler. Make sure the connection is not disconnecting which is a FIN. With TCP each message gets an ACK so make sure each message has an ACK. Each TCP message will have a sequence number. The sequence number is sequential and will be different from client to server than server to client. Also look for error messages in the data. – jdweng Jan 24 '18 at 01:00
  • Why not use a proven open source FIX engine? There are a few choices. What you're doing seems mad. – Grant Birchmeier Jan 24 '18 at 15:49
  • @GrantBirchmeier last time I used an open-source FIX engine for .NET (Quickfix/n) it was ported from Java resulting in bad .NET code, blocking calls and thread-*un*safe code, This is a question about .NET *Core* too. It may not be possible to use an existing library. Do you have something better in mind? – Panagiotis Kanavos Jan 25 '18 at 13:05
  • I am biased toward QF/n, as I work for the company that maintains it. – Grant Birchmeier Jan 25 '18 at 16:12

1 Answers1

3

CreateAccessSign is wrong, privateKeyAsBytes need to use Convert.FromBase64String:

    var privateKeyAsBytes = Convert.FromBase64String("privateKey");

FixSendLoginMessage is wrong, this is the correct implementation:

    private async Task<string> FixSendLoginMessage()
{
    var sendTime = DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss.fff");
    string msgType = "A";
    var messageSeqNumber = (++messageSeqNum).ToString();
    var senderCompId = "apiKey";
    var targetCompId = "Coinbase";
    var password = "passphrase";
    var accessSign = CreateAccessSign(
            sendTime,
            msgType,
            messageSeqNumber,
            senderCompId,
            targetCompId,
            password
        );
    var logonMessage = $"98=0{'\x01'}108=30{'\x01'}554={password}{'\x01'}96={accessSign}{'\x01'}8013=Y{'\x01'}";
    var headerAfter9Tag = $"35={msgType}{'\x01'}34={messageSeqNumber}{'\x01'}52={sendTime}{'\x01'}49={senderCompId}{'\x01'}56={targetCompId}{'\x01'}";
    var length = logonMessage.Length + headerAfter9Tag.Length;
    var messageHeader = $"8=FIX.4.2{'\x01'}9={length}{'\x01'}{headerAfter9Tag}";
    var messageWithHeader = $"{messageHeader}{logonMessage}";
    int sum = 0;
    int len = messageWithHeader.Length;
    for (int i = 0; i < len; i++)
    {
        sum += Convert.ToChar(messageWithHeader.Substring(i, 1));
    }
    sum = sum % 256;
    var messageWithChecksum = $"{messageWithHeader}10={sum.ToString("000")}{'\x01'}";
    byte[] buffer = Encoding.ASCII.GetBytes(messageWithChecksum);
    await _sslStream.WriteAsync(buffer, 0, buffer.Length);
    await _sslStream.FlushAsync();
    return messageWithChecksum;
}
Marco Levarato
  • 203
  • 4
  • 16