DotGram.Finance 0.2.0

dotnet add package DotGram.Finance --version 0.2.0
                    
NuGet\Install-Package DotGram.Finance -Version 0.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotGram.Finance" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotGram.Finance" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="DotGram.Finance" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotGram.Finance --version 0.2.0
                    
#r "nuget: DotGram.Finance, 0.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotGram.Finance@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotGram.Finance&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=DotGram.Finance&version=0.2.0
                    
Install as a Cake Tool

DotGram.Finance

Reads and checks finance protocols and formats wherever they are kept rather than traded: logs, archives, files, message buses. Today that is FIX 4.2, FIX 4.4 and FIX 5.0 SP2 tag-value messages; other protocols and formats join the package as they are added. For netstandard2.0 and net10.0; DotGram compiles the grammar at build time, so applications need no DotGram runtime, grammar files, schema XML, reflection configuration or initialization step.

dotnet add package DotGram.Finance
using System;

using DotGram.Finance.Fix;
using DotGram.Finance.Fix.Fix44;

// One message, with the separator written as a pipe so it can be read on a page.
var wire = ("8=FIX.4.4|9=65|35=D|11=ORDER|55=ABC|54=1|60=20260915-12:00:00|" +
            "38=100|40=2|44=12.50|10=000|").Replace('|', '/u0001');

if (FixParser.TryParseMessage(wire, out var message, out var error) && message is FixMessage.NewOrderSingle order)
{
    Console.WriteLine(order.Symbol?.Value);
    Console.WriteLine(order.OrderQty?.Value);
}
else
{
    Console.WriteLine(error);
}

What it is for

  • Logs and archives. Every FIX engine journals what it sent and received, often with | for the separator and spaces around it. Incident review, audit, trade reconstruction, regulatory reporting and execution analysis are read from those journals — gigabytes a day, read from a stream in bounded memory, pipe-delimited or not.
  • Checking messages against a counterparty's dictionary before a certification or in a test suite: apply their data dictionary, and Validate reports every finding of a message at once, each with its rule, tag, position and group entry, rather than the first.
  • Buses and stores. Data kept in Kafka, a database or a queue — drop copy, post-trade, clearing — and read by a consumer that has no session to hold.
  • Files and other transports. End-of-day allocation and confirmation files, FIX carried over a queue, an HTTP or a WebSocket gateway.
  • Tools. Simulators, load generators, test harnesses, anonymizers, converters to other formats.
  • An engine of your own. Where the session layer is written in house, this is the parser and the checks under it.

Capabilities

  • FIX 4.2, FIX 4.4, FIX 5.0 SP2. Tag-value messages: fields, messages, streams, pipe-delimited logs, validation, dictionaries, and custom fields and messages. See the FIX document for all of it.

What it is not

It is not a FIX engine. There is no socket, no session: no logon, sequence numbers, heartbeats, resend requests, message store or schedule; a live trading connection needs an engine, which reads with its own parser. Where you hold the bytes, this reads them: ReadMessages takes the Stream your transport hands you.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 117 10/1/2026
0.1.0 125 9/15/2026

0.2.0 replaces the public surface. This is what to write instead, line by line; both sides were read from the assemblies, 0.1.0 as published and 0.2.0 as built. THE NAMESPACES. DotGram.Finance.Fix stays, and holds what every version of FIX shares: the fields, FixTag, FixConvert, FixFinding and the context every version's derives from, FixContext. What is FIX 4.4's own -- FixParser, the messages, Fix44Context -- is in DotGram.Finance.Fix.Fix44. THERE IS ONE DOOR, AND IT IS NOT THE OLD ONE. 0.1.0 had Fix44 with Parse and TryParse over a string or a ReadOnlySpan<char>; 0.2.0 has FixParser, and the name of the call says what comes back: the verb says where the input is and what happens to it (Parse takes a buffer whole, Read consumes a reader or a stream and leaves it open), and the plural says how many. A buffer is a string, a byte array or a ReadOnlyMemory<byte>, and every call takes an optional Fix44Context. Fix44.Parse(wire, FixParseMode.Strict) -> FixParser.ParseMessage(wire) followed by message.Validate(Fix44Context.Default), which answers with everything wrong rather than throwing on the first. Fix44.Parse(wire, FixParseMode.Lenient) -> FixParser.ParseMessage(wire), which is now what parsing means. Fix44.TryParse(wire, out m, out e, mode) -> FixParser.TryParseMessage(wire, out m, out e). From a reader or a stream the same two are ReadMessage and TryReadMessage; ReadMessages walks a stream of them, ParseMessages reads every message of a buffer, and BuildMessage puts one together from fields already read. And there is an answer 0.1.0 did not have: ParseFields and ReadFields return the flat typed fields without assembling a message. THE MESSAGES. NewOrderSingle, ExecutionReport and the other 91 message classes -> FixMessage.NewOrderSingle and so on: nested in FixMessage, a closed set whose base stands in front of every arm of a switch. CustomFixMessage -> a class of your own derived from FixCustomMessage, which places its own fields (Place) and adds its own findings (OnValidate), built by the context's FixMessageFactory; a MsgType nothing builds is a FixMessage.Invalid. A message's properties keep their field names and are the typed fields themselves: order.ClOrdID is a FixField.Text, order.OrderQty a FixField.Decimal, where 0.1.0 had a string or a FixNumber?; Value is the CLR value and IsValid says whether the text fitted it. Header and Trailer -> the standard header's and trailer's fields are properties of every message (message.SenderCompID). AllFields -> Fields, every field in wire order. OriginalWire, GetField and GetGroup are gone. The 92 group-entry classes (PartiesEntry and the rest) -> groups named for their counter: order.Parties -> order.NoPartyIDsGroups, a list of IParties.NoPartyIDsGroup beside its counter order.NoPartyIDs. THE FIELD. 0.1.0's FixField, a readonly struct with Tag, Value, Wire, Position, ValuePosition, Length, TryGetDecimal and TryGetInt64 -> FixField, a class of the type of its value: FixField.Text, Character, Boolean, Integer, Decimal, Timestamp, Time, ZonedTime, Date, MonthYear, Multiple and Data, each with Value and TryGetValue, or FixField.Invalid. Tag is the tag's number, as it was, and FixTag holds the number of every tag as a constant named as the FIX repository names it: FixField.Decimal { Tag: FixTag.OrderQty }. The two FIX 4.4 fields its data dictionaries name otherwise keep those names on the messages (IOIid, LinesOfText) and are FixTag.IOIID and FixTag.NoLinesOfText. Position, ValuePosition and Length mean what they did; Wire is gone. Numbers are decimal and long; FixNumber is gone. THE CONTEXT. new FixParseOptions(mode, pairs) -> new Fix44Context { LengthDataPairs = pairs }, one immutable value that reads the input and holds the message to its schema, changed with `with`. The pairs are an IReadOnlyDictionary<int,int> from length tag to data tag rather than a FixDataPair[], and a supplied dictionary ADDS to the standard's sixteen pairs instead of replacing them. A pair is read as the two fields it is on the wire, the length and then the data, each with its own position. FixParseMode is gone, and with it FixParseOptions.Mode: reading the wire and holding it to a schema are two acts. Parse builds whatever it can read; message.Validate(context) reports everything wrong at once in message.InvalidFindings, each FixFinding naming its rule, its tag, the field, where in the source it begins and the entry of the repeating group it is in. So a message Strict used to turn away is now returned and reported on: a required field absent, a required component empty, a value outside its type or code set, a tag twice in one scope, a tag the schema does not place in that scope, a header field out of its place, a BodyLength or CheckSum the octets do not bear out (BodyLengthMismatch, CheckSumMismatch), a length not followed by its data, an unknown MsgType, and MessageEncoding absent beside an Encoded field. What still refuses is what the reader cannot defer: a message not ended by its CheckSum, one without a MsgType, a field it could not read, a data field its length does not measure, a NumInGroup that sizes an array past the fields left, and a group entry that does not begin with its delimiter. LOG FRAMING IS A VALUE. A pipe-delimited log is read by the same calls with Fix44Context.WithLogFraming, or context with { Framing = FixFraming.Log } for a context of your own. A streamed call is read through the context's BufferSize and bounded by its MaxRetained, and a field past the limit is an IOException. A COUNTERPARTY'S DICTIONARY. FixDictionary reads a data dictionary into a value (Parse, Read, LoadFile) that can be merged (Merge: a later dictionary's message type, component or field replaces an earlier one's whole) and edited in code, and context.With(dictionary) applies it, taking what it says at that moment; applying costs what reading does, and the checks are then compiled in the background, side by side; a message that arrives before its check is ready compiles it itself, and context.Prepare() waits until every check is ready. context.Load(text), LoadFile(path), or Load of a TextReader, a Stream or several texts merged in order, are the short forms. Either answers a new context with the dictionary's checks over the standard's: a message type, component, group entry or field the file describes has the file's whole check. The tags it adds are read, from then on, as the types it gives them; a tag nothing defines is a FixField.Invalid of its tag. A file that is not a dictionary this package can read, or that places a field where the model has no property for it, is refused whole. The context is a value, not a setting of the process, so two counterparties with two schemas are two contexts. WHAT IS NEW BESIDES. FIX 4.2, in DotGram.Finance.Fix.Fix42: the same FixParser, FixMessage and checks over its 46 message types, with Fix42Context. FIX 5.0 SP2 over FIXT 1.1, in DotGram.Finance.Fix.Fix50: its 116 message types with Fix50Context; MsgType f, SecurityStatus, is the class FixMessage.SecurityStatusMessage, since it carries a field of that name. Its zoned values: FixField.ZonedTime for TZTimeOnly, holding (TimeOnly Time, TimeSpan Offset), and TZTimestamp read into FixField.Timestamp at the offset it was written with, by FixConvert.ToZonedTime and ToZonedTimestamp; and MultipleStringValue, whose values are words rather than single characters, read by FixConvert.ToMultipleString into FixField.Multiple. FixConvert, public, holds the conversions a value is read by, each an extension of the span it reads (value.ToInteger()). Every public member is documented, and a skill for agents ships beside the README. Faster: a log field trailed by a very long run of padding before its `|` cost the square of the padding to read — a genuine value as much as one recovery had to skip past because nothing recognized it — and recovering past 4,096 spaces of it took on the order of a thousand times as long as the hand-written FIX parser takes to read the same line; both are linear now, and no field, value or message either one builds is different because of it.