• Version 1.16.0
Show / Hide Table of Contents

SellSide Sample Project

This sample waits for incoming connections on the pre-defined port (acts as a FIX Acceptor). If the incoming application-level message is 'SingleOrder - New'(MsgType='D') then the 'ExecutionReport'(MsgType='8') message is send to the counterparty. Otherwise the 'Email'(MsgType='C') message is sent back.

© Onix Solutions

Source code


using System;
using System.Collections.Generic;
using OnixS.Fix;
using System.Globalization;
using System.IO;
using OnixS.Fix.Fix44;
using System.Runtime.InteropServices;
using NLog.Extensions.Logging;

namespace SellSide
{
    /// <summary>
    /// Waits for incoming connections and processes incoming messages.
    /// </summary>
    internal class Acceptor
    {
        private const ProtocolVersion fixVersion = ProtocolVersion.Fix44;

        private static string GetLicenseStoreFolder()
        {
            string path = Path.Join(AppContext.BaseDirectory, "../../../../../license");

            if (Directory.Exists(path))
                return path;

            // expecting it run after dotnet publish using default paths
            return Path.Join(AppContext.BaseDirectory, "../../../../../../license");
        }

        private void Run()
        {
            Console.WriteLine("Sell Side Sample");

            var settings = new EngineSettings()
            {
                SendLogoutOnException = true,
                SendLogoutOnInvalidLogon = true, // E.g. to send a Logout when the sequence number of the incoming Logon (A) message is less than expected.
                LicenseStore = GetLicenseStoreFolder(),
                LoggerProvider = new NLogLoggerProvider()
            };
            settings.ListenPorts.Add(10450);

            Engine.Init(settings);

            Engine.Instance.Error += (object sender, EngineErrorEventArgs args) =>
            {
                Console.WriteLine("Engine Error: " + args.ToString());
            };

            Engine.Instance.Warning += (object sender, EngineWarningEventArgs args) =>
            {
                Console.WriteLine("Engine Warning: " + args.ToString());
            };

            var dictionary = fixVersion.ToDictionary();

            const string SenderCompID = "SellSide";
            const string TargetCompID = "BuySide";

            using var session = new Session(SenderCompID, TargetCompID, dictionary);

            session.InboundApplicationMessage += OnInboundApplicationMessage;

            session.StateChanged += (object sender, SessionStateChangeEventArgs e) =>
            {
                Console.WriteLine("Session state: " + e.NewState.ToString());
            };

            session.Error += (object sender, SessionErrorEventArgs e) =>
            {
                Console.WriteLine("Session Error: " + e.ToString());
            };

            session.Warning += (object sender, SessionWarningEventArgs e) =>
            {
                Console.WriteLine("Warning: " + e.ToString());
            };

            session.LogonAsAcceptor();

            Console.WriteLine("\nAwaiting session-initiator with "
                                  + "\n SenderCompID (49) = " + TargetCompID
                                  // Note: from the counterparty  point of view SenderCompID is TargetCompID
                                  + "\n TargetCompID (56) = " + SenderCompID
                                  + "\n FIX version = " + fixVersion
                                  + "\non port " + Engine.Instance.Settings.ListenPorts[0] + " ...");

            while (true)
            {
                Console.Write("Press 'X' to quit");

                // Start a console read operation. Do not display the input.
                ConsoleKeyInfo cki = Console.ReadKey(true);

                // Announce the name of the key that was pressed .
                Console.WriteLine($"  Key pressed: {cki.Key}\n");

                // Exit if the user pressed the 'X' key.
                if (cki.Key == ConsoleKey.X)
                    break;
            }

            Engine.Shutdown();
        }

        private void OnInboundApplicationMessage(object sender, InboundMessageEventArgs e)
        {
            Console.WriteLine("Received application-level message:\n" + e.Message.ToString());

            try
            {
                var replies = new List<Message>();
                switch (e.Message.Type)
                {
                    case MsgType.NewOrderSingle:
                        var rnd = new Random();
                        int x = rnd.Next(1, 5);
                        switch (x)
                        {
                            case 1:
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.New, ExecType.New));
                                break;

                            case 2:
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.New, ExecType.New));
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.Partial, ExecType.Trade));
                                break;

                            case 3:
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.New, ExecType.New));
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.Filled, ExecType.Trade));
                                break;

                            case 4:
                                replies.Add(CreateExecutionReport(e.Message, OrdStatus.Rejected, ExecType.Rejected));
                                break;
                        }
                        break;

                    case MsgType.OrderCancelRequest:
                        replies.Add(CreateCancelledExecutionReport(e.Message));
                        break;

                    default:
                        var email = new Message(MsgType.Email, ((Session)sender).Dictionary);
                        email[Tag.EmailThreadID] = "SellSide reply";
                        email[Tag.EmailType] = EmailType.New;
                        email[Tag.Subject] = "Message was received";

                        email[Tag.EmailType] = EmailType.New;
                        Group group = email.SetGroup(Tag.NoLinesOfText, 1);
                        group.Set(Tag.Text, 0, "The message with MsgType=" + e.Message[Tag.MsgType] + " was received");

                        replies.Add(email);
                        break;
                }

                var sn = (Session) sender;

                foreach (Message reply in replies)
                {
                    reply.Validate();
                    sn.Send(reply);
                    Console.WriteLine("Sent to the counterparty:\n" + reply);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception during the processing of the incoming message: " + ex);
            }
        }

        private Message CreateExecutionReport(Message order, string orderStatus, string executionType)
        {
            var report = new Message(MsgType.ExecutionReport, order.Dictionary);

            ++orderCounter;

            report[Tag.ClOrdID] = order[Tag.ClOrdID];
            report[Tag.OrderID] = "OrderID_ " + DateTime.Now.ToString("HHmmss", CultureInfo.InvariantCulture);
            report[Tag.ExecID] = "ExecID_" + orderCounter;
            report[Tag.OrdStatus] = orderStatus;
            report[Tag.ExecType] = executionType;
            report[Tag.Symbol] = order[Tag.Symbol];
            report[Tag.Side] = order[Tag.Side];
            report[Tag.OrdType] = order[Tag.OrdType];
            report[Tag.OrderQty] = order[Tag.OrderQty];
            report[Tag.LeavesQty] = order[Tag.OrderQty];
            report.Set(Tag.CumQty, 0)
                  .Set(Tag.AvgPx, 100.0);

            return report;
        }

        private Message CreateCancelledExecutionReport(Message cancelRequest)
        {
            var report = new Message(MsgType.ExecutionReport, cancelRequest.Dictionary);

            ++orderCounter;

            report[Tag.ClOrdID] = cancelRequest[Tag.ClOrdID];
            report[Tag.OrigClOrdID] = cancelRequest[Tag.OrigClOrdID];
            report[Tag.OrderID] = "OrderID_ " + DateTime.Now.ToString("HHmmss", CultureInfo.InvariantCulture);
            report[Tag.ExecID] = "ExecID_" + orderCounter;
            report[Tag.OrdStatus] = OrdStatus.Canceled;
            report[Tag.ExecType] = ExecType.Canceled;
            report[Tag.Symbol] = cancelRequest[Tag.Symbol];
            report[Tag.Side] = cancelRequest[Tag.Side];
            report[Tag.OrdType] = cancelRequest[Tag.OrdType];
            report[Tag.OrderQty] = cancelRequest[Tag.OrderQty];
            report[Tag.LeavesQty] = "0";
            report.Set(Tag.CumQty, 0)
                  .Set(Tag.AvgPx, 100.0);

            return report;
        }

        private int orderCounter;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        private static int Main()
        {
            try
            {
                var acceptor = new Acceptor();
                acceptor.Run();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex);

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && ex.Message.Contains("ErrorCode=10013"))
                {
                    Console.WriteLine("The socket is already in use, or access to the port is restricted on this OS. Please try to change the listen port in EngineSettings.ListenPorts to another one.");
                    Console.WriteLine("You can view a list of which ports are excluded from your user by running this command: 'netsh interface ipv4 show excludedportrange protocol=tcp'");
                }

                return 1;
            }
            finally
            {
                // From https://github.com/NLog/NLog/wiki/Tutorial:
                // NET Application running on Mono / Linux are required to stop threads / timers before entering application shutdown phase.
                // Failing to do this will cause unhandled exceptions and segmentation faults, and other unpredictable behavior.
                NLog.LogManager.Shutdown(); // Flush and close down internal threads and timers
            }

            return 0;
        }
    }
}

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