• Version 1.16.0
Show / Hide Table of Contents

Sell Side Windows Service Sample Project

This sample illustrates how to running FIX Engine as a windows service.

Please pay attention that a windows service app returns the C:\WINDOWS\system32 folder as its current directory, so absolute paths for EngineSettings.LicenseStore and EngineSettings.Dictionary should be used.

© Onix Solutions

Source code


using Microsoft.Extensions.Hosting;
using OnixS.Fix;
using OnixS.Fix.Fix44;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
#if NET5_0_OR_GREATER
using System.Runtime.Versioning;
#endif
using System.Threading;
using System.Threading.Tasks;
using NLog.Extensions.Logging;

namespace SellSideWindowsService
{

#if NET5_0_OR_GREATER
    [SupportedOSPlatform("windows")]
#endif
    public class SellSideWindowsService : BackgroundService
    {
        private int OrderCounter;
        private const ProtocolVersion fixVersion = ProtocolVersion.Fix44;
        Session sn;

        void OnInboundApplicationMsg(object sender, InboundMessageEventArgs e)
        {
            ReportEvent("Incoming application-level message:\n" + e.Message);
            List<Message> replies = new();
            switch (e.Message.Type)
            {
                case MsgType.NewOrderSingle:
                    Random rnd = new();
                    int x = rnd.Next(1, 5);
                    switch (x)
                    {
                        case 1:
                            replies.Add(CreateExecutionReport(e.Message, OrdStatus.New));
                            break;

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

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

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

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

                default:
                    Message email = new(MsgType.Email, fixVersion);
                    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;
            }

            Session sn = (Session)sender;

            foreach (Message reply in replies)
            {
                reply.Validate();
                sn.Send(reply);
            }

        }

        private Message CreateExecutionReport(Message order, string status)
        {
            Message execReport = new(MsgType.ExecutionReport, fixVersion);

            ++OrderCounter;

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

            return execReport;
        }

        private Message CreateCancelledExecutionReport(Message cancelRequest)
        {
            Message execReport = new(MsgType.ExecutionReport, fixVersion);

            ++OrderCounter;

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

            return execReport;
        }


        void OnStateChange(object sender, SessionStateChangeEventArgs e)
        {
            ReportEvent("New session state: " + e.NewState);
        }

        static void ReportEvent(string msg)
        {
            EventLog.WriteEntry("FixClientAsServiceSample", msg, EventLogEntryType.Information);
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    EngineSettings settings = new();

                    settings.SendLogoutOnException = true;
                    settings.SendLogoutOnInvalidLogon = true; // E.g. to send a Logout when the sequence number of the incoming Logon (A) message is less than expected.

                    settings.ListenPorts.Add(10450);
                    //settings.LicenseStore = @"<the absolute path to the folder that contains the license file>";
                    //settings.Dictionary = @"<the absolute path to XML file with the description of FIX dictionary>";

                    settings.LoggerProvider = new NLogLoggerProvider();

                    Engine.Init(settings);

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

                    sn = new Session(SenderCompID, TargetCompID, fixVersion);

                    sn.InboundApplicationMessage += OnInboundApplicationMsg;
                    sn.StateChanged += OnStateChange;

                    sn.LogonAsAcceptor();

                    ReportEvent("FixClientAsServiceSample started");
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry("FixClientAsServiceSample", ex.ToString(), EventLogEntryType.Error);
                }
            }
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            try
            {
                if (sn != null)
                {
                    sn.Logout();
                    sn = null;
                }
                if (Engine.IsInitialized)
                    Engine.Shutdown();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("FixClientAsServiceSample", ex.ToString(), EventLogEntryType.Error);
            }
            return base.StopAsync(cancellationToken);
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            return Task.CompletedTask;
        }
    }
}

Host Run


using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
#if NET5_0_OR_GREATER
using System.Runtime.Versioning;
#endif

namespace SellSideWindowsService
{
#if NET5_0_OR_GREATER
    [SupportedOSPlatform("windows")]
#endif
    internal static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static void Main(string[] args)
        {
            using IHost host = Host.CreateDefaultBuilder(args)
                .UseWindowsService(options =>
                {
                    options.ServiceName = ".NET SellSide Service";
                })
                .ConfigureServices(services =>
                {
                    services.AddHostedService<SellSideWindowsService>();
                })
                .Build();

            host.RunAsync().Wait();
        }
    }
}

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