Skip to main content

Contacting API with Protobuf

Import Compiled .proto Files

In order to import and use the compiled .proto files in our code, we need to install the corresponding Protobuf library in our programming langauge of choice.

npm i protobufjs

Establish Secure Connection

Encode Decode Messages

Putting It Altogether

var tls = require('tls');
var pb = require('./pb.compiled.js');
var creds = require('./credentials.json');

var { accountId: ctidTraderAccountId, accessToken } = creds;
var { log } = console;
var nameByType = constructConvenientMap();

const host = 'live.ctraderapi.com';
const socket = tls.connect(
{
host: host,
port: 5035,
servername: host,
},
() => {
log(
`connection open (${socket.authorized ? 'authorized' : 'unauthorized'})`,
);
},
);

socket.on('connect', () => {
log('connect');
var rdyBuf = constructMsg(pb.ProtoOAApplicationAuthReq, {
clientId: creds.clientId,
clientSecret: creds.clientSecret,
});
socket.write(rdyBuf);
});

socket.on('data', (buf) => {
log('data received');
var msg = deconstructMsg(buf);
log(msg);
});

socket.on('error', (error) => {
log('error:', error.message); // error.code, error.errno,
});

socket.on('close', (hadError) => log('close', { hadError }));

// async infinite loop
(async () => {
while (true) {
await new Promise((r) => setTimeout(r, 1000));
}
})();

function deconstructMsg(buf) {
var protoMsg = pb.ProtoMessage.decode(buf);
var PayloadMsg = pb[nameByType[protoMsg.payloadType]];
protoMsg.payload = PayloadMsg.decode(protoMsg.payload);
return protoMsg;
}

function constructMsg(Msg, payload) {
var payloadMsg = Msg.create(payload);
var payloadMsgEncoded = Msg.encode(payloadMsg);

var protoMsg = pb.ProtoMessage.create({
payloadType: payloadMsg.payloadType,
payload: payloadMsgEncoded.finish(),
});

var protoMsgEncoded = pb.ProtoMessage.encode(protoMsg);

var dataLen = protoMsgEncoded.len;
var data = protoMsgEncoded.finish();

var lengthBytes = Buffer.alloc(4);
lengthBytes.writeUInt32BE(dataLen, 0);

var totLen = lengthBytes.length + data.length;
var rdyBuf = Buffer.concat([lengthBytes, data], totLen);

return rdyBuf;
}

function constructConvenientMap() {
const chgcase = (s) =>
'ProtoOA' +
s
.split('PROTO_OA_')[1]
.split('_')
.map((i) => i.toLowerCase())
.map((i) => [i[0].toUpperCase(), ...i.slice(1)].join(''))
.join('');

var o = pb.ProtoOAPayloadType;
var payloadTypes = Object.keys(o).map((k) => [o[k], chgcase(k)]);
var nameByType = Object.fromEntries(payloadTypes);

return nameByType;
}