fixparser
Version:
FIX.Latest / 5.0 SP2 Parser / AI Agent Trading
164 lines (152 loc) • 6.33 kB
text/typescript
import { randomInt } from 'node:crypto';
import { LicenseManager as clientLicense, FIXParser } from 'fixparser';
import {
EncryptMethod,
ExecType,
FIXServer,
Field,
Fields,
HandlInst,
type Message,
Messages,
OrdStatus,
OrdType,
ResetSeqNumFlag,
Side,
LicenseManager as serverLicense,
TimeInForce,
} from 'fixparser/FIXServer';
const RANDOMIZED_PORT = randomInt(9900, 12000);
let fixServer: FIXServer;
let fixParser: FIXParser;
const testTimeout: NodeJS.Timeout | null = setTimeout(() => {
console.log('Test timeout... exiting with error code 1');
if (fixParser) {
fixParser.close();
}
if (fixServer) {
fixServer.close();
}
process.exit(1);
}, 10000);
const clientSendLogon = () => {
const logon: Message = fixParser.createMessage(
new Field(Fields.MsgType, Messages.Logon),
new Field(Fields.MsgSeqNum, fixParser.getNextTargetMsgSeqNum()),
new Field(Fields.SenderCompID, 'CLIENT'),
new Field(Fields.SendingTime, fixParser.getTimestamp()),
new Field(Fields.TargetCompID, 'SERVER'),
new Field(Fields.ResetSeqNumFlag, ResetSeqNumFlag.Yes),
new Field(Fields.EncryptMethod, EncryptMethod.None),
new Field(Fields.HeartBtInt, 10),
);
const messages = fixParser.parse(logon.encode());
console.log('CLIENT sending message', messages[0].description, messages[0].messageString);
fixParser.send(logon);
};
const clientSendOrder = () => {
const order: Message = fixParser.createMessage(
new Field(Fields.MsgType, Messages.NewOrderSingle),
new Field(Fields.MsgSeqNum, fixParser.getNextTargetMsgSeqNum()),
new Field(Fields.SenderCompID, 'CLIENT'),
new Field(Fields.SendingTime, fixParser.getTimestamp()),
new Field(Fields.TargetCompID, 'SERVER'),
new Field(Fields.ClOrdID, '11223344'),
new Field(Fields.HandlInst, HandlInst.AutomatedExecutionNoIntervention),
new Field(Fields.OrderQty, '123'),
new Field(Fields.TransactTime, fixParser.getTimestamp()),
new Field(Fields.OrdType, OrdType.Market),
new Field(Fields.Side, Side.Buy),
new Field(Fields.Symbol, '700.HK'),
new Field(Fields.TimeInForce, TimeInForce.Day),
);
const messages = fixParser.parse(order.encode());
console.log('CLIENT sending message', messages[0].description, messages[0].messageString.replace(/\x01/g, '|'));
fixParser.send(order);
};
const serverSendExecutionReport = (message: Message) => {
const executionReport: Message = fixServer.createMessage(
new Field(Fields.MsgType, Messages.ExecutionReport),
new Field(Fields.MsgSeqNum, fixServer.getNextTargetMsgSeqNum()),
new Field(Fields.SenderCompID, 'SERVER'),
new Field(Fields.SendingTime, fixServer.getTimestamp()),
new Field(Fields.TargetCompID, 'CLIENT'),
new Field(Fields.AvgPx, message.getField(Fields.Price) ? message.getField(Fields.Price)?.value : 0),
new Field(Fields.ClOrdID, message.getField(Fields.ClOrdID) ? message.getField(Fields.ClOrdID)?.value : 'N/A'),
new Field(Fields.CumQty, message.getField(Fields.OrderQty) ? message.getField(Fields.OrderQty)?.value : 0),
new Field(Fields.Symbol, message.getField(Fields.Symbol) ? message.getField(Fields.Symbol)?.value : 'N/A'),
new Field(Fields.LastPx, message.getField(Fields.Price) ? message.getField(Fields.Price)?.value : 0),
new Field(Fields.OrderID, 55),
new Field(Fields.OrderQty, message.getField(Fields.OrderQty) ? message.getField(Fields.OrderQty)?.value : 0),
new Field(Fields.OrdStatus, OrdStatus.Filled),
new Field(Fields.Side, Side.Buy),
new Field(Fields.ExecType, ExecType.Trade),
new Field(Fields.LeavesQty, 0),
);
const messages = fixServer.parse(executionReport.encode());
console.log('SERVER sending message', messages[0].description, messages[0].messageString.replace(/\x01/g, '|'));
fixServer.send(executionReport);
};
const setupServer = async () => {
return new Promise<void>((resolve, reject) => {
void reject; // Reject unused for now
fixServer = new FIXServer({ logging: false });
fixServer.createServer({
host: '0.0.0.0',
port: RANDOMIZED_PORT,
sender: 'SERVER',
target: 'CLIENT',
onMessage: (message: Message) => {
if (message.messageType === Messages.NewOrderSingle) {
serverSendExecutionReport(message);
}
},
onReady: () => {
console.log('SERVER READY');
resolve();
},
});
});
};
const setupClient = async () => {
return new Promise<void>((resolve, _reject) => {
fixParser = new FIXParser({ logging: false });
fixParser.connect({
host: 'localhost',
port: RANDOMIZED_PORT,
protocol: 'tcp',
sender: 'CLIENT',
target: 'SERVER',
fixVersion: 'FIXT.1.1',
onOpen: () => {
clientSendLogon();
},
onMessage: (message: Message) => {
if (message.messageType === Messages.Logon) {
console.log('CLIENT received a Logon Acknowledge', message.messageString);
clientSendOrder();
} else if (message.messageType === Messages.ExecutionReport) {
console.log('CLIENT received a Execution Report', message.messageString);
resolve();
}
},
onClose: () => console.log('Disconnected'),
});
});
};
const runE2E = async () => {
try {
// NOTE: This feature requires a FIXParser Pro license
await serverLicense.setLicenseKey(process.env.FIXPARSER_LICENSE_KEY);
await clientLicense.setLicenseKey(process.env.FIXPARSER_LICENSE_KEY);
await setupServer();
await setupClient();
console.log('FIXServer E2E test was successfully completed.');
clearTimeout(testTimeout);
process.exit(0);
} catch (error) {
console.error('Error during server setup:', error);
process.exit(1);
}
};
void runE2E();