/// <summary> /// The AddField method adds an instance of a FIX field /// to the collection. Multiple fields with the same tag /// can be added to the collection, and will be stored in /// a list that is keyed by the field's FIX tag. /// </summary> /// <param name="field"> /// The FIX field to add to the collection. /// </param> public void AddField(FixField field) { // REC: Ensure that the field doesn't collide with // an existing group that has the same tag: if (!_mapGroups.ContainsKey(field.Tag)) { if (!_mapFields.ContainsKey(field.Tag)) { _mapFields.Add(field.Tag, new List <FixField>()); } _mapFields[field.Tag].Add(field); if (!_mapElements.ContainsKey(field.Tag)) { _mapElements.Add(field.Tag, new List <IFixElement>()); } _mapElements[field.Tag].Add(field); // REC: Add to the sequential list: _listElements.Add(field); } else { throw new ArgumentException(string.Format("Field's tag collides with an existing group.")); } }
/// <summary> /// The SetField method attempts to assign a value to /// a field in the collection. If the field is already /// present within the collection, the existing value is /// replaced with the supplied value. If the field is not /// already in the collection, the field is added to it. /// </summary> /// <param name="field"> /// The FIX field instance that is to be either assigned /// or added to the collection. If the operation results /// in an assigment, the Content property of the supplied /// field is copied into the corresponding property of the /// existing field, otherwise the entire field instance is /// added to the collection. /// </param> public void SetField(FixField field) { // REC: If the field is not already present in the // collection, the request can be forwarded to the // regular AddField method: if (!_mapFields.ContainsKey(field.Tag)) { AddField(field); } else { // REC: Ensure that the field doesn't collide // with a group that is already present: if (!_mapGroups.ContainsKey(field.Tag)) { // REC: The content of the supplied field can // be assigned directly to the content property // of the existing field, this ensures that the // field remains in the same location that it is // in within the sequential list, as well as the // map, of existing elements. _mapFields[field.Tag][0].Content = field.Content; } else { throw new ArgumentException("Field collides with an existing group."); } } }
/// <summary> /// The GetField method returns the first instance of /// a field that matches the specified FIX tag. /// </summary> /// <param name="tag"> /// The FIX tag of the field to retrieve. /// </param> /// <returns> /// The first instance of a FIX field in the collection /// that matches the specified tag, or null if there is /// no matching entry. /// </returns> public FixField GetField(int tag) { FixField result = null; if (_mapFields.ContainsKey(tag)) { result = _mapFields[tag][0]; } return(result); }
/// <summary> /// The Finalize method calculates the values for /// common session layer fields and assigns those /// values to their respective fields. /// </summary> /// <param name="msg"> /// The FIX message to be finalized. /// </param> public static void Finalize(FixMessage msg) { // REC: Calculate sending time and add it to the message: string strTimeStamp = DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss.fff"); FixField fldSendingTime = msg.Header.GetField(52); if (fldSendingTime != null) { fldSendingTime.Content = strTimeStamp; } else { msg.Header.AddField(new FixField(52, strTimeStamp)); } // REC: Calculate the body length and add it to the message: int fixBodyLength = FixCalculator.GetBodyLength(msg); FixField fldBodyLength = msg.Header.GetField(9); if (fldBodyLength != null) { fldBodyLength.Content = fixBodyLength.ToString(); } else { msg.Header.AddField(new FixField(9, fixBodyLength.ToString())); } // REC: Calculate the checksum and add it to the message: string strChecksum = string.Format("{0:000}", FixCalculator.GetChecksum(msg) % 256); FixField fldChecksum = msg.Trailer.GetField(10); if (fldChecksum != null) { fldChecksum.Content = strChecksum; } else { msg.Trailer.AddField(new FixField(10, strChecksum)); } }
/// <summary> /// The ParseHeader method attempts to parse the header of /// a FIX message from the buffer in the parser's context: /// </summary> /// <param name="ctx"> /// The parser's current context. /// </param> /// <param name="result"> /// The parser's result information. /// </param> private void ParseHeader(ParseContext ctx, VfxFixParserResult result) { // REC: Default to success, let the parse logic // override this if an error occurs: result.Status = VfxFixParserStatus.MsgComplete; // REC: Start extracting FIX fields from the buffer: while (ctx.Index < ctx.Buffer.Length) { // REC: Reset index value in case there // is an error parsing the buffer: int idxRst = ctx.Index; int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the header is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { result.Consumed = 0; result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag from its string // form into its corresponding integer value: int nTag = -1; if (int.TryParse(strTag, out nTag) == false) { // REC: If the field cannot be converted into // an integer, the message is malformed: result.Consumed = 0; result.Status = VfxFixParserStatus.MsgMalformed; return; } // REC: If the FIX BeginString has already been parsed // from the buffer, and is encountered again, then the // message was incomplete and cannot be parsed: if (nTag == 8) { if (result.Message.Header.GetField(8) != null) { // REC: Reset the index into the message: ctx.Index = idxRst; // REC: Adjust the result accordingly: result.Consumed = 0; result.Status = VfxFixParserStatus.MsgIncomplete; return; } } // REC: Determine whether or not the version of the // session protocol has been discovered yet: if (ctx.HdrElements != null) { IFixDxElement hdrElement = ctx.HdrElements.GetElement(nTag); if (hdrElement != null) { if (hdrElement is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.Header.AddField(field); ctx.Index = idxSoh + 1; } else if (hdrElement is FixDxResolvedGroup) { FixDxResolvedGroup groupEntry = hdrElement as FixDxResolvedGroup; // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: ParseGroupResult parseResult = ParseGroup(ctx, groupEntry, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.Header.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.Header.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: The parser doesn't support UDFs in the // header section of the message, so the result // is considered complete if we hit a field that // isn't in the session protocol dictionary: break; } } else { // REC: The session protocol isn't known yet, so the // field is considered a plain FIX field and added to // the message's header: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.Header.AddField(field); ctx.Index = idxSoh + 1; // REC: Attempt to determine the session layer protocol // based on the header elements that have been parsed: ctx.SxVersion = _vxMatcher.GetVersion(result.Message.Header, "session"); if (ctx.SxVersion == null) { // REC: If no session layer dictionary matched the // elements that have been parsed so far, check for // a combined version definition in case the session // is running FIX versions 4.0-4.4 which do not have // separate session and application layer versions: ctx.SxVersion = _vxMatcher.GetVersion(result.Message.Header, "combined"); } // REC: The elements in the message header correspond // to a specific data dictionary, so that should be // used for parsing the header and trailer elements: if (ctx.SxVersion != null) { InitCachedHdrElements(ctx.SxVersion); InitCachedTrlElements(ctx.SxVersion); // REC: Assign the cached sets of resolved header // and trailer elements to the parse context: ctx.HdrElements = _hdrElements[ctx.SxVersion]; ctx.TrlElements = _trlElements[ctx.SxVersion]; } } } // REC: Determine if the message belongs to the session // layer protocol as opposed to the application layer: if (ctx.SxVersion != null) { // REC: Retrieve the FIX message type: FixField msgType = result.Message.Header.GetField(35); if (!string.IsNullOrEmpty(msgType.Content)) { // REC: Check the version definition registry for the // corresponding protocol version definition: VfxFixVxRecord vxDefinition = this._vxRegistry.Get(ctx.SxVersion); if (vxDefinition != null) { // REC: Retrieve the associated dictionary from the // configured FIX dictionary registry: FixDictionary dxInstance = this._dxRegistry.GetEntry(vxDefinition.Dictionaries[0].Name); if (dxInstance != null) { // REC: Check if the message is defined within the // dictionary for the session layer: FixDxMessage sxMessage = dxInstance.GetMessageByType(msgType.Content); if (sxMessage != null) { if ((!_msgElements.ContainsKey(ctx.SxVersion)) || (!_msgElements[ctx.SxVersion].Elements.ContainsKey(msgType.Content))) { InitCachedMsgElements(ctx, msgType.Content); } // REC: Look in the message definition cache // for this particular message type: if (_msgElements[ctx.SxVersion].Elements.ContainsKey(msgType.Content)) { ctx.MsgElements = _msgElements[ctx.SxVersion].Elements[msgType.Content]; } // REC: The context is now initialized to parse the // session layer message's content, so we're done: return; } } } } } // REC: If an override version has been specified for the // application layer, use that instead of hunting for it: if (ctx.AxVersion == null) { // REC: Now that the entire header of the message has been // extracted from the buffer, test the header contents and // determine the application protocol version: ctx.AxVersion = _vxMatcher.GetVersion(result.Message.Header, "application"); if (ctx.AxVersion == null) { // REC: If the application layer version cannot be // determined, it may be a FIX 4.0-4.4 message, so // check the combined versions as well: ctx.AxVersion = _vxMatcher.GetVersion(result.Message.Header, "combined"); if(ctx.AxVersion == null) { // REC: If the application layer could not be // resolved, then switch to the default: ctx.AxVersion = ctx.AxDefault; } } } if (ctx.AxVersion != null) { // REC: Now that the application version has been // determined, retrieve the message elements for // the type of message being parsed, initializing // the local message elements cache if needed: FixField msgType = result.Message.Header.GetField(35); if (msgType != null) { if ((!_msgElements.ContainsKey(ctx.AxVersion)) || (!_msgElements[ctx.AxVersion].Elements.ContainsKey(msgType.Content))) { InitCachedMsgElements(ctx, msgType.Content); } // REC: Look in the message definition cache // for this particular message type: if (_msgElements[ctx.AxVersion].Elements.ContainsKey(msgType.Content)) { ctx.MsgElements = _msgElements[ctx.AxVersion].Elements[msgType.Content]; } } } }
/// <summary> /// The ParseContent method attempts to parse the message body /// from an instance of a FIX message. /// </summary> /// <param name="ctx"> /// The parser's context information. /// </param> /// <param name="result"> /// The parser's result information. /// </param> private void ParseContent(ParseContext ctx, VfxFixParserResult result) { // REC: Attempt to retrieve the FIX message type // from the header of the message: FixField msgType = result.Message.Header.GetField(35); while (ctx.Index < ctx.Buffer.Length) { int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the message is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag to an integer: int nTag; if (int.TryParse(strTag, out nTag) == false) { result.Status = VfxFixParserStatus.MsgMalformed; return; } // REC: Test for premature message termination: if (nTag == 8) { result.Status = VfxFixParserStatus.MsgMalformed; return; } if (ctx.MsgElements != null) { IFixDxElement element = ctx.MsgElements.GetElement(nTag); if (element != null) { if (element is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Create a new field to represent // the parsed field/value pair: FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } else if (element is FixDxResolvedGroup) { // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: FixDxResolvedGroup resolvedGroup = element as FixDxResolvedGroup; ParseGroupResult parseResult = ParseGroup(ctx, resolvedGroup, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { result.Status = parseResult.Status; return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: The tag wasn't found in the map of elements // that are known for this message type, so determine // whether it is a UDF or a field from the trailer: if (ctx.TrlElements.GetElement(nTag) != null) { // REC: The field is a trailer element // so content parsing is now complete: return; } else { // REC: The field is just a UDF so it can be added // to the message body: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } } } else { // REC: There is no cached set of elements for this // type of message, so the field is either a trailer // field or it is a user-defined field: if (ctx.TrlElements.GetElement(nTag) != null) { // REC: The field is a trailer element // so content parsing is now complete: return; } else { // REC: The field is just a UDF so it can be added // to the message body: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } } } }
private void ParseTrailer(ParseContext ctx, VfxFixParserResult result) { while (ctx.Index < ctx.Buffer.Length) { int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the message is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { // result.Consumed = 0; result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag to an integer: int nTag; if (int.TryParse(strTag, out nTag) == false) { result.Status = VfxFixParserStatus.MsgMalformed; return; } if (ctx.TrlElements != null) { IFixDxElement element = ctx.TrlElements.GetElement(nTag); if(element != null) { if (element is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Create a new field to represent // the parsed field/value pair: FixField field = new FixField(nTag, strVal); result.Message.Trailer.AddField(field); ctx.Index = idxSoh + 1; } else if (element is FixDxResolvedGroup) { FixDxResolvedGroup groupEntry = element as FixDxResolvedGroup; // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: ParseGroupResult parseResult = ParseGroup(ctx, groupEntry, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { result.Status = parseResult.Status; return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.Trailer.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.Trailer.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: If the element is not in the set of trailer // elements, then the parsing is complete. return; } } else { result.Status = VfxFixParserStatus.MsgUnkSxProtocol; return; } } }
/// <summary> /// The AddField method adds an instance of a FIX field /// to the body of the message. /// </summary> /// <param name="field"></param> public void AddField(FixField field) { _msgElements.AddField(field); }
/// <summary> /// The SetField method registers an instance of a field with /// the assembler. The assembler will use this field whenever /// it constructs a message that contains the field's tag. /// </summary> /// <param name="field"> /// The FIX field to register with the assembler. /// </param> public void SetField(FixField field) { if (!_mapFields.ContainsKey(field.Tag)) { _mapFields.Add(field.Tag, field); } else { _mapFields[field.Tag] = field; } }