• Version 1.7.1
Show / Hide Table of Contents

Gateway Emulator Sample

This sample shows how to use the Gateway Emulator.

Source code


using OnixS.SimpleBinaryEncoding;
using OnixS.Cme.ILink3;
using OnixS.Cme.ILink3.Testing;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace GatewayEmulatorSample
{
    internal class GatewayEmulatorSample
    {
        private static readonly NLog.Logger logger = NLog.LogManager.GetLogger("Sample");
        private static bool stopRequested = false;
        private static readonly Random random = new();
        private static int sequenceNumber = 1;
        private static readonly Dictionary <string, IMessage> orderStorage = new();

        private static int Main(string[] args)
        {
            logger.Info("CME iLink 3 Gateway Emulator Sample.");

            int port = 49152;

            if (args.Length > 1 && 0 != args[1].Length)
            {
                port = int.Parse(args[1]);
            }

            try
            {
                SessionSettings settings = new()
                {
                    LicenseStore = "../../../../../license|../../../../license|."
                };

                var emulatorTask = Task.Run(() =>
                {

                    Gateway emulator = new(port, settings)
                    {
                        SendReceiveTimeout = TimeSpan.FromMilliseconds(100)
                    };

                    logger.Info($"Waiting for the connection at port {port}..");

                    bool connectionAccepted = false;

                    while (!stopRequested && !connectionAccepted)
                        connectionAccepted = emulator.TryAcceptConnection(TimeSpan.FromMilliseconds(100));

                    if (connectionAccepted)
                    {
                        logger.Info("Connection accepted.");

                        emulator.AcceptNegotiate();
                        emulator.AcceptEstablish();

                        emulator.WaitUntilTerminate(TimeSpan.FromMinutes(60),
                            (IMessage message) => MessageHandler(message, emulator),
                            (IMessage message) => SendSequence(stopRequested, emulator)
                            , ref stopRequested);
                    }
                });

                WaitForKey();

                stopRequested = true;

                emulatorTask.Wait(TimeSpan.FromMinutes(1));
            }
            catch (Exception ex)
            {
                logger.Error("Exception: " + ex.ToString());

                return 1;
            }

            NLog.LogManager.Shutdown();

            return 0;
        }

        private static void MessageHandler(IMessage message, Gateway emulator)
        {
            logger.Info("Received: " + message.ToFixString());
            try
            {
                var replies = new List<IMessage>();
                switch (message.TemplateID)
                {
                    case TemplateId.NewOrderSingle:
                        string clOrdId = message.GetString(Tag.ClOrdID);
                        orderStorage.TryAdd(clOrdId, message);
                        replies.AddRange(OnNewOrderSingle(message, emulator));
                        break;

                    case TemplateId.OrderCancelRequest:
                        replies.AddRange(OnOrderCancelRequest(message, emulator));
                        break;
                }

                foreach (IMessage reply in replies)
                {
                    emulator.Send(reply, sequenceNumber);
                    ++sequenceNumber;
                    logger.Info("Sent to the counterparty:" + reply.ToFixString());
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.ToString());
            }
        }

        private static List<IMessage> OnNewOrderSingle(IMessage message, Gateway emulator)
        {
            var replies = new List<IMessage>();
            int x = random.Next(0, 4);
            switch (x)
            {
                case 0:
                    // Acknowledge the order, fill 100% (at limit price; if market order can generate a random price)
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.New, ExecType.New));
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.Filled, ExecType.Filled));
                    break;
                case 1:
                    // Reject the order
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.Rejected, ExecType.Reject));
                    break;
                case 2:
                    // Acknowledge the order, send Unsolicited Cancel
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.New, ExecType.New));
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.Cancelled, ExecType.Cancelled));
                    break;
                case 3:
                    // Acknowledge the order and send Reject
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.New, ExecType.New));
                    replies.Add(CreateExecutionReport(message, emulator, OrdStatus.Rejected, ExecType.Reject));
                    break;
            }
            return replies;
        }

        private static List<IMessage> OnOrderCancelRequest(IMessage message, Gateway emulator)
        {
            var replies = new List<IMessage>();
            int y = random.Next(0, 2);
            switch (y)
            {
                case 0: // Cancel the order
                    string clOrdId = message.GetString(Tag.ClOrdID);
                    if (orderStorage.TryGetValue(clOrdId, out IMessage order))
                    {
                        order.SetUnsignedLong(Tag.OrderRequestID, message.GetUnsignedLong(Tag.OrderRequestID));
                        replies.Add(CreateExecutionReport(order, emulator, OrdStatus.Cancelled, ExecType.Cancelled));
                    }
                    else
                    {
                        replies.Add(CreateOrderCancelReject(message, emulator));
                    }
                    break;

                case 1: // Reject the cancellation request
                    replies.Add(CreateOrderCancelReject(message, emulator));
                    break;
            }
            return replies;
        }

        private static IMessage CreateOrderCancelReject(IMessage msg, Gateway emulator)
        {
            var reply = emulator.Encoder().Wrap(TemplateId.OrderCancelReject);

            reply.SetUnsignedLong(Tag.UUID, emulator.Uuid)
                .SetString(Tag.ExecID, Guid.NewGuid().ToString())
                .SetString(Tag.SenderID, "Emulator")
                .SetString(Tag.ClOrdID, msg.GetString(Tag.ClOrdID))
                .SetUnsignedLong(Tag.PartyDetailsListReqID, msg.GetUnsignedLong(Tag.PartyDetailsListReqID))
                .SetUnsignedLong(Tag.OrderRequestID, msg.GetUnsignedLong(Tag.OrderRequestID))
                .SetString(Tag.Location, msg.GetString(Tag.Location))
                .SetByte(Tag.ManualOrderIndicator, msg.GetByte(Tag.ManualOrderIndicator));

            const ushort rejectionCode = 1003;
            reply.SetUnsignedShort(Tag.CxlRejReason, rejectionCode);

            return reply;
        }

        private static IMessage CreateExecutionReport(IMessage msg, Gateway emulator, OrdStatus ordStatus, ExecType execType)
        {
            int templateId;
            switch (execType)
            {
                case ExecType.New:
                    templateId = TemplateId.ExecutionReportNew;
                    break;

                case ExecType.Filled:
                case ExecType.PartiallyFilled:
                    templateId = TemplateId.ExecutionReportTradeOutright;
                    break;

                case ExecType.Reject:
                    templateId = TemplateId.ExecutionReportReject;
                    break;

                case ExecType.Cancelled:
                    templateId = TemplateId.ExecutionReportCancel;
                    break;

                default:
                    templateId = TemplateId.BusinessReject;
                    break;
            }
            var response = emulator.Encoder().Wrap(templateId);
            response.SetUnsignedLong(Tag.UUID, emulator.Uuid);

            if(templateId == TemplateId.BusinessReject) // Business Reject
            {
                response.SetString(Tag.Text, "BusinessReject generated.");
                if (msg.Contains(Tag.OrderRequestID))
                {
                    response.SetUnsignedLong(Tag.BusinessRejectRefID, msg.GetUnsignedLong(Tag.OrderRequestID));
                }
                response.SetUnsignedShort(Tag.BusinessRejectReason, (ushort)BusinessRejectReason.Other);
                return response;
            }
            string execId = Guid.NewGuid().ToString();
            ulong orderId = (ulong)random.Next(99999);
            response.SetString(Tag.ExecID, execId)
                .SetString(Tag.SenderID, "Emulator")
                .SetString(Tag.ClOrdID, msg.GetString(Tag.ClOrdID))
                .SetUnsignedLong(Tag.PartyDetailsListReqID, msg.GetUnsignedLong(Tag.PartyDetailsListReqID))
                .SetUnsignedLong(Tag.OrderID, orderId)
                .SetDecimal(Tag.Price, msg.GetDecimal(Tag.Price))
                .SetUnsignedLong(Tag.TransactTime, CalculateTimestamp())
                .SetUnsignedLong(Tag.OrderRequestID, msg.GetUnsignedLong(Tag.OrderRequestID))
                .SetString(Tag.Location, msg.GetString(Tag.Location))
                .SetInteger(Tag.SecurityID, msg.GetInteger(Tag.SecurityID))
                .SetUnsignedInteger(Tag.OrderQty, msg.GetUnsignedInteger(Tag.OrderQty));

            if(templateId == TemplateId.ExecutionReportReject) // Execution Report Reject
            {
                response.SetUnsignedShort(Tag.OrdRejReason, 7000)
                    .SetString(Tag.Text, ErrorCode.errorCodes[7000]);
            }
            else if(templateId == TemplateId.ExecutionReportCancel) // Execution Report Cancel
            {
                response.SetUnsignedInteger(Tag.CumQty, 0);
            }
            else if(templateId == TemplateId.ExecutionReportTradeOutright)
            {
                decimal lastPx;
                if (msg.Contains(Tag.Price))
                    lastPx = msg.GetDecimal(Tag.Price);
                else
                    lastPx = decimal.One;
                response.SetDecimal(Tag.LastPx, lastPx);
                uint ordQty = msg.GetUnsignedInteger(Tag.OrderQty);
                uint lastQty = (uint)new Random().Next((int)ordQty);
                response.SetUnsignedInteger(Tag.LastQty, lastQty)
                    .SetUnsignedInteger(Tag.CumQty, lastQty)
                    .SetUnsignedInteger(Tag.MDTradeEntryID, (uint)new Random().Next(Int32.MaxValue))
                    .SetUnsignedInteger(Tag.SideTradeID, (uint)new Random().Next(Int32.MaxValue))
                    .SetUnsignedInteger(Tag.LeavesQty, ordQty - lastQty)
                    .SetUnsignedShort(Tag.TradeDate, CalculateLocalMktDays(0));

                if(lastQty == ordQty)
                    response.SetByte(Tag.OrdStatus, (byte)OrdStatus.Filled);
                else
                    response.SetByte(Tag.OrdStatus, (byte)OrdStatus.PartiallyFilled);

                response.SetByte(Tag.AggressorIndicator, (byte)AggressorIndicator.No);

                IGroup noFills = response.SetGroup(Tag.NoFills, 1);
                noFills.MoveNext();
                noFills.SetDecimal(Tag.FillPx, lastPx)
                    .SetUnsignedInteger(Tag.FillQty, lastQty)
                    .SetString(Tag.FillExecID, CalculateLocalMktDays(0) + execId)
                    .SetByte(Tag.FillYieldType, (byte)FillYieldType.FIFO);
            }

            ushort expireDate;
            if(msg.Contains(Tag.ExpireDate))
                expireDate = msg.GetUnsignedShort(Tag.ExpireDate);
            else
                expireDate = CalculateLocalMktDays(7); // Set in a week from now.

            response.SetUnsignedShort(Tag.ExpireDate, expireDate)
                .SetByte(Tag.TimeInForce, (byte)msg.GetEnum<TimeInForce>(Tag.TimeInForce))
                .SetByte(Tag.Side, msg.GetByte(Tag.Side))
                .SetByte(Tag.ManualOrderIndicator, msg.GetByte(Tag.ManualOrderIndicator));

            if (msg.Contains(Tag.ExecutionMode))
                response.SetChar(Tag.ExecutionMode, msg.GetChar(Tag.ExecutionMode));

            return response;
        }

        private static ulong CalculateTimestamp()
        {
            DateTime now = DateTime.UtcNow;
            DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            long ticksSinceEpoch = now.Ticks - unixEpoch.Ticks;
            long nanosecondsSinceEpoch = ticksSinceEpoch * 100;
            return (ulong)nanosecondsSinceEpoch;
        }

        private static ushort CalculateLocalMktDays(int daysOffset)
        {
            DateTime now = DateTime.UtcNow.AddDays(daysOffset);
            DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            TimeSpan timeSinceEpoch = now - unixEpoch;
            double daysSinceEpoch = timeSinceEpoch.TotalDays;
            return (ushort)daysSinceEpoch;
        }

        private static void SendSequence(bool stopRequested, Gateway emulator)
        {
            if (!stopRequested)
            {
                emulator.SendSequence(sequenceNumber, KeepAliveLapsed.NotLapsed);
            }
        }

        private static void WaitForKey()
        {
            logger.Info("Press any key to close the application.");

            if (Console.IsInputRedirected)
                Console.In.Read();
            else
                Console.ReadKey();
        }
    }
}

In this article
Back to top Copyright © Onix Solutions.
Generated by DocFX