4

I connect to several APIs that all use FXI4.2 but now I wish to connect to another that uses its own version of FIX4.4.

I have an router app that send orders to the various APIs and it would appear that I need to duplicate all my methods (e.g. the OnMessage(), NewSingleOrder etc) to cope with the 2 FIX protocols.

Is there a smarter way to do this to avoid this duplication?

Moderators: I know this is a little open now, but I will add some code snippets once I get some initial feedback.

public void OnMessage(QuickFix.FIX42.MarketDataIncrementalRefresh message, SessionID sessionID)
{
    int count = message.NoMDEntries.getValue();
    QuickFix.FIX42.MarketDataSnapshotFullRefresh.NoMDEntriesGroup repeatingMDItem = new QuickFix.FIX42.MarketDataSnapshotFullRefresh.NoMDEntriesGroup();

    DateTime sourceDT = DateTime.ParseExact(message.Header.GetField(52), "yyyyMMdd-HH:mm:ss.fff", ci);
    DateTime dt = TimeZoneInfo.ConvertTimeToUtc(sourceDT, utcZone);
    DateTime nowUTC = TimeZoneInfo.ConvertTime(DateTime.UtcNow, utcZone, utcZone);
    TimeSpan diffToUK = nowUTC - dt;

    for (int i = 1; i <= count; i++)
    {

        message.GetGroup(i, repeatingMDItem);

        String symbol = repeatingMDItem.GetField(55);

        int tickBandNoDecPlaces = int.Parse(repeatingMDItem.GetField(5071));
        masterForm.MDATA.AddData(symbol, tickBandNoDecPlaces, sourceDT);
    }
}

Question: Will FIX44 accept all previous FIX?

How can I make this agnostic about which FIX version?

        public void OnMessage(QuickFix.FIX42.MarketDataSnapshotFullRefresh message, SessionID sessionID)
        {
            OnMessageAgnostic(message, sessionID);
        }

        public void OnMessage(QuickFix.FIX44.MarketDataSnapshotFullRefresh message, SessionID sessionID)
        {
            OnMessageAgnostic(message, sessionID);
        }

        public int FixVersion(QuickFix.Message message)
        {
               switch (message.GetString(8)) // BeginString
                    {
                        case Values.BeginString_FIX42:
                            return 42;
                        case Values.BeginString_FIX44:
                            return 44;
                        default:
                            throw new NotSupportedException("This version of FIX is unsupported");
                    }
        }

        public void OnMessageAgnostic(QuickFix.Message message, SessionID sessionID)
        {

             int count;
             if (FixVersion(message)==44)
             {
                  count = ((QuickFix.FIX44.MarketDataSnapshotFullRefresh)message).NoMDEntries.getValue();
             }
        }
ManInMoon
  • 6,795
  • 15
  • 70
  • 133

1 Answers1

4

The problem is that is that FIX message types from different versions don't have any relationship except for their base class - at the lowest level, all FIX messages derive from Message. You need to take the information you need from a message, package it in such a way that it's version-agnostic (as far as is possible), and then write code against those version-agnostic data structures.

I suggest that you let the message cracker do the initial filtering for you, if you're OK to let it handle that, and then feed the message to a handler that can deal with that particular type of message:

public void OnMessage(QuickFix.FIX42.MarketDataIncrementalRefresh message, SessionID sessionID)
{
    this.marketDataIncrementalRefreshHandler.Handle(message);
}

public void OnMessage(QuickFix.FIX44.MarketDataIncrementalRefresh message, SessionID sessionID)
{
    this.marketDataIncrementalRefreshHandler.Handle(message);
}

... elsewhere ...

public interface FixMessageHandler
{
    void Handle(Message msg);
}

public class MarketDataIncrementalRefreshHandler : FixMessageHandler
{
    public void Handle(Message msg)
    {
        DateTime sourceDT = DateTime.ParseExact(message.Header.GetField(52), "yyyyMMdd-HH:mm:ss.fff", ci);
        DateTime dt = TimeZoneInfo.ConvertTimeToUtc(sourceDT, utcZone);
        DateTime nowUTC = TimeZoneInfo.ConvertTime(DateTime.UtcNow, utcZone, utcZone);
        TimeSpan diffToUK = nowUTC - dt;

        var noMDEntriesGroups = this.GetAllNoMDEntries(msg)
        foreach (var noMDEntriesGroup in noMDEntriesGroups)
        {
            masterForm.MDATA.AddData(
                noMDEntriesGroup.Symbol,
                noMDEntriesGroup.TickBandNoDecPlaces,
                sourceDT);
        }
    }

    private IEnumerable<NoMDEntriesGroup> GetAllNoMDEntries(Message msg)
    {
        switch (message.GetString(8)) // BeginString
        {
            case Values.BeginString_FIX42:
                return this.GetAllNoMDEntries((QuickFix.FIX42.MarketDataSnapshotFullRefresh)msg);
            case Values.BeginString_FIX44:
                return this.GetAllNoMDEntries((QuickFix.FIX44.MarketDataSnapshotFullRefresh)msg);
            default:
                throw new NotSupportedException("This version of FIX is unsupported");
        }
    }

    private IEnumerable<NoMDEntriesGroup> GetAllNoMDEntries(QuickFix.FIX42.MarketDataSnapshotFullRefresh msg)
    {
        int count = message.NoMDEntries.getValue();
        QuickFix.FIX42.MarketDataSnapshotFullRefresh.NoMDEntriesGroup repeatingMDItem = new QuickFix.FIX42.MarketDataSnapshotFullRefresh.NoMDEntriesGroup();
        for (int i = 1; i <= count; i++)
        {
            message.GetGroup(i, repeatingMDItem);

            yield return new NoMDEntriesGroup
            {
                Symbol = repeatingMDItem.GetField(55),
                TickBandNoDecPlaces = int.Parse(repeatingMDItem.GetField(5071)
            };
        }
    }

    private IEnumerable<NoMDEntriesGroup> GetAllNoMDEntries(QuickFix.FIX44.MarketDataSnapshotFullRefresh msg)
    {
        // Should be practically identical to the above version, with 4.4 subbed for 4.2
    }

    private class NoMDEntriesGroup
    {
        public string Symbol { get; set; }
        public int TickBandNoDecPlaces { get; set; }
    }
}
Chris Mantle
  • 6,595
  • 3
  • 34
  • 48
  • Yes I see. Also, instead of your class MarketDataIncrementalRefreshHandler(). I could just pass as Quickfix.Message and cast when I need specific version... – ManInMoon Apr 30 '15 at 14:07
  • You could, yes. That's going to make your `Application`-derived class very large, though. And you can only cast from `Quickfix.Message` after the message has been cracked. – Chris Mantle Apr 30 '15 at 14:15
  • see edit please. This allows me to only cast when I need to deal for version specific bits. The rest of code can be the same – ManInMoon Apr 30 '15 at 14:18
  • You could do that, but I would advise against it. You'll likely find that your code becomes difficult to read and maintain because you will have lots of checks for different versions, lots of casts, and lots of duplicated code. It's much better to present the data to your business logic in version-agnostic data structures, as in my answer. – Chris Mantle Apr 30 '15 at 14:30
  • 1
    Please help me to understand what you are saying. Your line this.GetAllNoMDEntries(msg) "hides" the switch and cast but it is still there. Yes it's easier to read but I could rewrite mine to be similar to yours. Am I missing the point? – ManInMoon Apr 30 '15 at 14:36
  • No, I think you understand. I think the answer to your original question is, you always have to have the `switch` with a cast if you use the QuickFix message classes, because they have no common root but `Message`. – Chris Mantle Apr 30 '15 at 15:47