UNPKG

@sotatech/nest-quickfix

Version:

A powerful NestJS implementation of the FIX (Financial Information eXchange) protocol. Provides high-performance, reliable messaging for financial trading applications with built-in session management, message validation, and recovery mechanisms.

185 lines 5.76 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Message = exports.Field = void 0; const fields_1 = require("../fields"); class Field { constructor(tag, value) { this.tag = tag; this.value = value; } toJSON() { return { tag: this.tag, value: this.value, }; } } exports.Field = Field; class Message { constructor(...fields) { this.fields = new Map(); fields.forEach((field) => this.setField(field.tag, field.value)); } static createMessage(...fields) { return new Message(...fields); } setField(field, value) { this.fields.set(field, value); } getField(field) { return this.fields.get(field); } hasField(field) { return this.fields.has(field); } getAllFields() { return new Map(this.fields); } setFields(...fields) { fields.forEach((field) => this.setField(field.tag, field.value)); } toString() { this.validateRequiredFields(); const SOH = '\x01'; let fixString = ''; fixString += `8=${this.getField(fields_1.Fields.BeginString)}${SOH}`; let bodyString = ''; bodyString += `35=${this.getField(fields_1.Fields.MsgType)}${SOH}`; const orderedFields = [ fields_1.Fields.SenderCompID, fields_1.Fields.TargetCompID, fields_1.Fields.MsgSeqNum, fields_1.Fields.SendingTime, ]; orderedFields.forEach((field) => { if (this.hasField(field)) { bodyString += `${field}=${this.getField(field)}${SOH}`; } }); this.fields.forEach((value, tag) => { if (tag !== fields_1.Fields.BeginString && tag !== fields_1.Fields.BodyLength && tag !== fields_1.Fields.CheckSum && tag !== fields_1.Fields.MsgType && !orderedFields.includes(tag)) { bodyString += `${tag}=${value}${SOH}`; } }); const bodyLength = bodyString.length; fixString += `9=${bodyLength}${SOH}`; fixString += bodyString; const checksum = this.calculateChecksum(fixString); fixString += `10=${checksum}${SOH}`; return fixString; } calculateChecksum(message) { let sum = 0; for (let i = 0; i < message.length; i++) { sum += message.charCodeAt(i); } return (sum % 256).toString().padStart(3, '0'); } clone() { const cloned = new Message(); this.fields.forEach((value, tag) => { cloned.setField(tag, value); }); return cloned; } removeField(field) { this.fields.delete(field); } clear() { this.fields.clear(); } get length() { return this.fields.size; } isEmpty() { return this.fields.size === 0; } toJSON() { const json = { fields: {} }; this.fields.forEach((value, tag) => { json.fields[tag] = value; }); return json; } static fromJSON(json) { const message = new Message(); Object.entries(json.fields).forEach(([tag, value]) => { message.setField(Number(tag), value); }); return message; } stringify() { return JSON.stringify(this.toJSON()); } static parse(jsonString) { try { const json = JSON.parse(jsonString); return Message.fromJSON(json); } catch (error) { throw new Error(`Failed to parse message from JSON: ${error.message}`); } } toObject() { const obj = {}; this.fields.forEach((value, tag) => { obj[tag.toString()] = value; }); return obj; } static fromObject(obj) { const message = new Message(); Object.entries(obj).forEach(([tag, value]) => { message.setField(Number(tag), value); }); return message; } validateRequiredFields() { const requiredFields = [ fields_1.Fields.BeginString, fields_1.Fields.MsgType, fields_1.Fields.SenderCompID, fields_1.Fields.TargetCompID, fields_1.Fields.MsgSeqNum, fields_1.Fields.SendingTime, ]; for (const field of requiredFields) { if (!this.hasField(field)) { throw new Error(`Missing required field: ${field}`); } } } createReverse() { const reversed = new Message(); this.fields.forEach((value, tag) => { reversed.setField(tag, value); }); const originalSender = this.getField(fields_1.Fields.SenderCompID); const originalTarget = this.getField(fields_1.Fields.TargetCompID); reversed.setField(fields_1.Fields.SenderCompID, originalTarget); reversed.setField(fields_1.Fields.TargetCompID, originalSender); return reversed; } toFieldNameObject(options = {}) { const result = {}; const getFieldName = (tag) => { const numericTag = Number(tag); if (options.customFields?.[tag]) { return options.customFields[tag]; } const enumKey = Object.keys(fields_1.Fields).find((key) => fields_1.Fields[key] === numericTag); return enumKey || tag.toString(); }; this.fields.forEach((value, tag) => { const fieldName = getFieldName(tag); result[fieldName] = value; }); return result; } } exports.Message = Message; //# sourceMappingURL=message.js.map