UNPKG

fixjs

Version:
158 lines (121 loc) 3.97 kB
/// fix message var moment = require('moment'); // convert a date object into a fix formatted timestamp var getUTCTimeStamp = function(date){ return moment(date).utc().format('YYYYMMDD-HH:mm:ss.SSS'); } var Msg = function() { var self = this; // map of field number (as a string) to field value self._fields = {}; self._define_field = function(field_id, name, opt) { var validator = (opt && opt.validator) ? opt.validator : function(v) { return v; }; Object.defineProperty(self, name, { get: function() { return self.get(field_id); }, set: function(value) { self.set(field_id, validator(value)); } }); }; self._define_field('49', 'SenderCompID'); self._define_field('56', 'TargetCompID'); self._define_field('50', 'SenderSubID'); self._define_field('35', 'MsgType'); self._define_field('34', 'MsgSeqNum'); self._define_field('52', 'SendingTime'); }; // constants Msg.kFieldSeparator = String.fromCharCode(1); Msg.prototype.get = function(field_id) { var self = this; return self._fields[field_id]; }; Msg.prototype.set = function(field_id, value) { var self = this; if (value instanceof Date) { value = getUTCTimeStamp(value); } self._fields[field_id] = value; } Msg.prototype.serialize = function() { var self = this; var header_arr = []; var body_arr = []; var fields = self._fields; header_arr.push('35=' + self.MsgType); header_arr.push('52=' + self.SendingTime); header_arr.push('49=' + self.SenderCompID); header_arr.push('56=' + self.TargetCompID); header_arr.push('34=' + self.MsgSeqNum); // manually inserted var ignore = ['8', '9', '35', '10', '52', '49', '56', '34']; for (var tag in fields) { if (fields.hasOwnProperty(tag) && ignore.indexOf(tag) === -1 && typeof fields[tag] !== 'undefined' && fields[tag] !== null) { body_arr.push(tag + '=' + fields[tag]); } } var headermsg = header_arr.join(Msg.kFieldSeparator); var bodymsg = body_arr.join(Msg.kFieldSeparator); var out = []; out.push('8=' + 'FIX.4.2'); // TODO variable // if there is no body, then only one separator will be added // if there is a body, then there will be another separator var sep_count = 1; if (bodymsg.length > 0) { sep_count += 1; } out.push('9=' + (headermsg.length + bodymsg.length + sep_count)); out.push(headermsg); if (bodymsg.length > 0) { out.push(bodymsg); } var outmsg = out.join(Msg.kFieldSeparator); outmsg += Msg.kFieldSeparator; return outmsg + '10=' + Msg.checksum(outmsg) + Msg.kFieldSeparator; }; Msg.checksum = function(str) { var chksm = 0; for (var i = 0; i < str.length; ++i) { chksm += str.charCodeAt(i); } chksm = chksm % 256; var checksumstr = ''; if (chksm < 10) { checksumstr = '00' + (chksm + ''); } else if (chksm >= 10 && chksm < 100) { checksumstr = '0' + (chksm + ''); } else { checksumstr = '' + (chksm + ''); } return checksumstr; }; Msg.parse = function(raw) { var Msgs = require('./msgs'); var fix = {}; var keyvals = raw.split(Msg.kFieldSeparator); keyvals.forEach(function(kv) { if (kv.length === 0) { return; } // a field could have an = in it, don't see why not var components = kv.split('='); var id = components.shift(); fix[id] = components.join('='); }); // TODO validate header var type = fix['35']; if (!type) { throw new Error('no MsgType in fix message'); } var msg_t = Msgs.types[type]; if (!msg_t) { throw new Error('no such message type: ' + type); } var msg = new msg_t(); msg._fields = fix; return msg; }; module.exports = Msg;