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();
}
}
}