@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
JavaScript
;
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