/// <summary> /// Packs the FIX Message into FIX string. /// </summary> /// <returns>the FIX Message string.</returns> public override string ToString() { string beginStringField = string.Empty; string msgTypeField = string.Empty; string headerFields = string.Empty; string bodyFields = string.Empty; string fieldValue = string.Empty; if (!this.Contains(Tag.BeginString)) { throw new MissingFieldException("FIX BeginString field is missing"); } if (!this.Contains(Tag.MsgType)) { throw new MissingFieldException("FIX MsgType field is missing"); } // Create a stream of bytes from the fields in the FIX message. Remember that a hashtable -- the underlying data store // for the message -- iterates through the fields in a random order. This is fine for most FIX fields, but some in the // header and the trailer must be in a specified order in the packet, so these fields are pulled out of the message // during this loop and assembled later into the final message. foreach (Field field in this) { switch (field.Tag) { case Tag.BeginString: // The 'BeginString' must always be the first field in the message. beginStringField = TagConverter.ConvertTo(Tag.BeginString) + "=" + field.Value.ToString() + FixSeparator.ToString(); break; case Tag.BodyLength: case Tag.CheckSum: // these are calculated below once the FIX string is assembled. break; case Tag.MsgType: // The 'MsgType' field is always the third field in the message. string msgTypeValue = MsgTypeConverter.ConvertTo((MsgType)field.Value); msgTypeField = TagConverter.ConvertTo(Tag.MsgType) + "=" + msgTypeValue + FixSeparator.ToString(); break; ////////////////// // Header Fields ////////////////// case Tag.PossDupFlag: //case Tag.PossResend: // bool Header Fields headerFields += TagConverter.ConvertTo(field.Tag) + "=" + ((bool)field.Value ? "Y" : "N") + FixSeparator.ToString(); break; case Tag.SendingTime: //case Tag.OnBehalfOfSendingTime: case Tag.OrigSendingTime: // DateTime Header Fields. headerFields += TagConverter.ConvertTo(field.Tag) + "=" + ((DateTime)field.Value).ToString("yyyyMMdd-HH:mm:ss") + FixSeparator.ToString(); break; case Tag.MsgSeqNum: // MsgSeqNum Header Field. headerFields += TagConverter.ConvertTo(field.Tag) + "=" + field.Value.ToString() + FixSeparator.ToString(); break; case Tag.DeliverToCompID: //case Tag.DeliverToLocationID: case Tag.DeliverToSubID: case Tag.OnBehalfOfCompID: //case Tag.OnBehalfOfLocationID: case Tag.OnBehalfOfSubID: case Tag.SenderCompID: //case Tag.SenderLocationID: case Tag.SenderSubID: case Tag.TargetCompID: case Tag.TargetLocationID: case Tag.TargetSubID: // ID Header Fields. headerFields += TagConverter.ConvertTo(field.Tag) + "=" + field.Value.ToString() + FixSeparator.ToString(); break; ////////////////// // Body Fields ////////////////// // These are the Body Fields ( in alpha order) that require some conversion or business logic. // The 'default' case takes care of the rest. case Tag.Account: // the Account Body Field is not valid in a cancel message before FIX 4.2 if (!this.MsgType.Equals(MsgType.OrderCancelRequest) || (this.MsgType.Equals(MsgType.OrderCancelRequest) && !this.BeginString.Equals("FIX.4.0") && !this.BeginString.Equals("FIX.4.1"))) { if (field.Value != null) { bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + field.Value.ToString() + FixSeparator.ToString(); } } break; case Tag.BusinessRejectReason: // BusinessRejectReason Body Field fieldValue = BusinessRejectReasonConverter.ConvertTo((BusinessRejectReason)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.CommType: // CommType Body Field fieldValue = CommTypeConverter.ConvertTo((CommType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.CxlRejReason: // CxlRejReason Body Field. // Only FIX 4.2 and above allow CxlRejReason > 1. (1=CxlRejReason.UnknownOrder). if (this.BeginString.Equals("FIX.4.0") || this.BeginString.Equals("FIX.4.1")) { if ((CxlRejReason)field.Value > CxlRejReason.UnknownOrder) { // default to TooLateToCancel field.Value = CxlRejReason.TooLateToCancel; } } fieldValue = CxlRejReasonConverter.ConvertTo((CxlRejReason)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.CxlRejResponseTo: // the CxlRejResponseTo Body Field is not valid in a cancel-reject message before FIX 4.2 if (!this.MsgType.Equals(MsgType.OrderCancelReject) || (this.MsgType.Equals(MsgType.OrderCancelReject) && !this.BeginString.Equals("FIX.4.0") && !this.BeginString.Equals("FIX.4.1"))) { fieldValue = CxlRejResponseToConverter.ConvertTo((CxlRejResponseTo)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); } break; case Tag.CxlType: // CxlType Body Field Only for FIX 4.0 if (this.BeginString.Equals("FIX.4.0")) { fieldValue = CxlTypeConverter.ConvertTo((CxlType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); } break; case Tag.DiscretionInst: // DiscretionInst Body Field fieldValue = DiscretionInstConverter.ConvertTo((DiscretionInst)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.DKReason: // DKReason Body Field fieldValue = DKReasonConverter.ConvertTo((DKReason)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.EncryptMethod: // EncryptMethod Body Field fieldValue = EncryptMethodConverter.ConvertTo((EncryptMethod)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.ExecTransType: // ExecTransType Body Field fieldValue = ExecTransTypeConverter.ConvertTo((ExecTransType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.ExecType: // ExecType Body Field - Not valid in FIX 4.0 if (!this.BeginString.Equals("FIX.4.0")) { fieldValue = ExecTypeConverter.ConvertTo((ExecType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); } break; case Tag.ExpireTime: // ExpireTime Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((DateTime)field.Value).ToString("yyyyMMdd-HH:mm:ss") + FixSeparator.ToString(); break; case Tag.GapFillFlag: // GapFillFlag Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((bool)field.Value ? "Y" : "N") + FixSeparator.ToString(); break; case Tag.HandlInst: // HandlInst Body Field fieldValue = HandlInstConverter.ConvertTo((HandlInst)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.IOINaturalFlag: // IOINaturalFlag Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((bool)field.Value ? "Y" : "N") + FixSeparator.ToString(); break; case Tag.IOIQltyInd: // IOIQltyInd Body Field fieldValue = IOIQltyIndConverter.ConvertTo((IOIQltyInd)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.IoiQualifierGroup: // Expand the repeating group fields. IoiQualifierGroup ioiQualifierGroup = (IoiQualifierGroup)field.Value; if ((ioiQualifierGroup != null) && (ioiQualifierGroup.Count > 0)) { // The 'NoIOIQualifiers' field is only valid for FIX 4.1 and above. if (!this.BeginString.Equals("FIX.4.0")) { // the number of repeating instances goes in the 'NoIOIQualifiers' field. bodyFields += TagConverter.ConvertTo(Tag.NoIOIQualifiers) + "=" + ioiQualifierGroup.Count.ToString() + FixSeparator.ToString(); } // Add the qualifiers. foreach (IoiQualifierGroup.Entry entry in ioiQualifierGroup) { fieldValue = IoiQualifierConverter.ConvertTo(entry.IoiQualifier); bodyFields += TagConverter.ConvertTo(Tag.IOIQualifier) + "=" + fieldValue + FixSeparator.ToString(); // FIX 4.0 only supports 1 qualifier. if (this.BeginString.Equals("FIX.4.0")) { break; } } } break; // These fields should be part of the repeating group "IoiQualifierGroup", // and should NOT be used individually. case Tag.NoIOIQualifiers: case Tag.IOIQualifier: break; case Tag.IOITransType: // IOITransType Body Field fieldValue = IOITransTypeConverter.ConvertTo((IOITransType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.LastCapacity: // LastCapacity Body Field fieldValue = LastCapacityConverter.ConvertTo((LastCapacity)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.LeavesQty: // LeavesQty Body Field - Not valid in FIX 4.0 if (!this.BeginString.Equals("FIX.4.0")) { fieldValue = field.Value.ToString(); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); } break; case Tag.MaturityMonthYear: // MaturityMonthYear Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((DateTime)field.Value).ToString("yyyyMM") + FixSeparator.ToString(); break; case Tag.OpenClose: // OpenClose Body Field fieldValue = OptionPositionTypeConverter.ConvertTo((OptionPositionType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.OrdRejReason: // OrdRejReason Body Field fieldValue = OrdRejReasonConverter.ConvertTo((OrdRejReason)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.OrdStatus: // the OrdStatus Body Field is not valid in a cancel-reject message before FIX 4.1 if (!this.MsgType.Equals(MsgType.OrderCancelReject) || (this.MsgType.Equals(MsgType.OrderCancelReject) && !this.BeginString.Equals("FIX.4.0"))) { fieldValue = OrdStatusConverter.ConvertTo((OrdStatus)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); } break; case Tag.OrdType: // OrdType Body Field fieldValue = OrdTypeConverter.ConvertTo((OrderTypeCode)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.OrigClOrdID: // the OrigClOrdID Body Field is not valid in cancel-reject or execution report messages before FIX 4.1 if ((!this.MsgType.Equals(MsgType.OrderCancelReject) && !this.MsgType.Equals(MsgType.ExecutionReport)) || (this.MsgType.Equals(MsgType.OrderCancelReject) && !this.BeginString.Equals("FIX.4.0")) || (this.MsgType.Equals(MsgType.ExecutionReport) && !this.BeginString.Equals("FIX.4.0"))) { if (field.Value != null) { bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + field.Value.ToString() + FixSeparator.ToString(); } } break; case Tag.PutOrCall: // PutOrCall Body Field fieldValue = OptionTypeConverter.ConvertTo((OptionType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.RefMsgType: // RefMsgType Body Field fieldValue = MsgTypeConverter.ConvertTo((MsgType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.ResetSeqNumFlag: // ResetSeqNumFlag Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((bool)field.Value ? "Y" : "N") + FixSeparator.ToString(); break; case Tag.RoutingGroup: // Expand the Routing repeating group fields. RoutingGroup routingGroup = (RoutingGroup)field.Value; if ((routingGroup != null) && (routingGroup.Count > 0)) { // the number of repeating instances goes in the 'NoRoutingIDs' field. bodyFields += TagConverter.ConvertTo(Tag.NoRoutingIDs) + "=" + routingGroup.Count.ToString() + FixSeparator.ToString(); foreach (RoutingGroup.Entry entry in routingGroup) { // Add the routing type. fieldValue = RoutingTypeConverter.ConvertTo(entry.RoutingType); bodyFields += TagConverter.ConvertTo(Tag.RoutingType) + "=" + fieldValue + FixSeparator.ToString(); // Add the routing ID. bodyFields += TagConverter.ConvertTo(Tag.RoutingID) + "=" + entry.RoutingId + FixSeparator.ToString(); } } break; // These fields should be part of the repeating group "RoutingGroup", // and should NOT be used individually. case Tag.NoRoutingIDs: case Tag.RoutingID: case Tag.RoutingType: break; case Tag.SecurityType: // SecurityType Body Field fieldValue = SecurityTypeConverter.ConvertTo((SecurityType)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.SessionRejectReason: // SessionRejectReason Body Field fieldValue = SessionRejectReasonConverter.ConvertTo((SessionRejectReason)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.Side: // Side Body Field fieldValue = SideConverter.ConvertTo((SideCode)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.TimeInForce: // TimeInForce Body Field fieldValue = TimeInForceConverter.ConvertTo((TimeInForceCode)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.TransactTime: // the TransactTime Body Field is not valid before FIX 4.2 if (!this.BeginString.Equals("FIX.4.0") && !this.BeginString.Equals("FIX.4.1")) { bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((DateTime)field.Value).ToString("yyyyMMdd-HH:mm:ss") + FixSeparator.ToString(); } break; case Tag.ValidUntilTime: // ValidUntilTime Body Field bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + ((DateTime)field.Value).ToString("yyyyMMdd-HH:mm:ss") + FixSeparator.ToString(); break; default: // Default processing of these types for Tags not in the above list. // Int32 Body Fields // Decimal Body Fields // String Body Fields if (field.Value != null) { bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + field.Value.ToString() + FixSeparator.ToString(); } break; } } if (headerFields.Equals(string.Empty)) { throw new MissingFieldException("FIX Header fields are missing"); } // The 'BodyLength' fields must be calculated after the body and part of the header is assembled. int bodyPacketLength = msgTypeField.Length + headerFields.Length + bodyFields.Length; string bodyLengthField = TagConverter.ConvertTo(Tag.BodyLength) + "=" + bodyPacketLength.ToString() + FixSeparator.ToString(); // Assemble the packet from the component fields. string packet = beginStringField + bodyLengthField + msgTypeField + headerFields + bodyFields; // The final step in assembling a packet is the calculation of the checksum. This is the least significant byte of // the sum of all the byte values in the packet. int checksum = 0; foreach (byte messageByte in packet) { checksum += messageByte; } string checksumField = TagConverter.ConvertTo(Tag.CheckSum) + "=" + (checksum & 0xFF).ToString("000") + FixSeparator.ToString(); return(packet + checksumField); }
/// <summary> /// Parses a string to a properly typed value based on the FIX tag. /// </summary> /// <param name="tag">Determines the datatype of the conversion.</param> /// <param name="value">A string representing a value.</param> public static object Parse(Tag tag, string value) { // Use the FIX tag to determine the parsing method and target data type. switch (tag) { case Tag.GapFillFlag: case Tag.IOINaturalFlag: case Tag.PossDupFlag: case Tag.ResetSeqNumFlag: // Boolean Values return(value == "Y" ? true : false); case Tag.BeginSeqNo: case Tag.BodyLength: case Tag.CheckSum: case Tag.EncodedTextLen: case Tag.EndSeqNo: case Tag.HeartBtInt: case Tag.InternalRecordId: case Tag.MsgSeqNum: case Tag.NewSeqNo: case Tag.NextExpectedMsgSeqNum: case Tag.NoIOIQualifiers: case Tag.NoRoutingIDs: case Tag.RawDataLength: case Tag.RefSeqNum: // Int32 Values return(Int32.Parse(value)); case Tag.AvgPx: case Tag.Commission: case Tag.CxlQty: case Tag.CMSLeavesQty: case Tag.CumQty: case Tag.DiscretionOffset: case Tag.LastPx: case Tag.LastShares: case Tag.LeavesQty: case Tag.MaxFloor: case Tag.MinQty: case Tag.OrderQty: case Tag.PegDifference: case Tag.Price: case Tag.StopPx: case Tag.StrikePrice: // Decimal Values return(Decimal.Parse(value)); case Tag.ExpireTime: case Tag.OrigSendingTime: case Tag.SendingTime: case Tag.TransactTime: case Tag.ValidUntilTime: // DateTime Values int year = Int32.Parse(value.Substring(0, 4)); int month = Int32.Parse(value.Substring(4, 2)); int day = Int32.Parse(value.Substring(6, 2)); int hour = Int32.Parse(value.Substring(9, 2)); int minute = Int32.Parse(value.Substring(12, 2)); int second = Int32.Parse(value.Substring(15, 2)); return(new DateTime(year, month, day, hour, minute, second)); case Tag.MaturityMonthYear: int expYear = Int32.Parse(value.Substring(0, 4)); int expMonth = Int32.Parse(value.Substring(4, 2)); return(new DateTime(expYear, expMonth, 1)); //enums case Tag.BusinessRejectReason: return(BusinessRejectReasonConverter.ConvertFrom(value)); case Tag.CxlRejReason: return(CxlRejReasonConverter.ConvertFrom(value)); case Tag.CxlRejResponseTo: return(CxlRejResponseToConverter.ConvertFrom(value)); case Tag.CxlType: return(CxlTypeConverter.ConvertFrom(value)); case Tag.CommType: return(CommTypeConverter.ConvertFrom(value)); case Tag.DiscretionInst: return(DiscretionInstConverter.ConvertFrom(value)); case Tag.DKReason: return(DKReasonConverter.ConvertFrom(value)); case Tag.EncryptMethod: return(EncryptMethodConverter.ConvertFrom(value)); case Tag.ExecTransType: return(ExecTransTypeConverter.ConvertFrom(value)); case Tag.ExecType: return(ExecTypeConverter.ConvertFrom(value)); case Tag.HandlInst: return(HandlInstConverter.ConvertFrom(value)); case Tag.IOIQualifier: return(IoiQualifierConverter.ConvertFrom(value)); case Tag.IOIQltyInd: return(IOIQltyIndConverter.ConvertFrom(value)); case Tag.IOITransType: return(IOITransTypeConverter.ConvertFrom(value)); case Tag.LastCapacity: return(LastCapacityConverter.ConvertFrom(value)); case Tag.MsgType: return(MsgTypeConverter.ConvertFrom(value)); case Tag.RefMsgType: return(MsgTypeConverter.ConvertFrom(value)); case Tag.OpenClose: return(OptionPositionTypeConverter.ConvertFrom(value)); case Tag.PutOrCall: return(OptionTypeConverter.ConvertFrom(value)); case Tag.OrdRejReason: return(OrdRejReasonConverter.ConvertFrom(value)); case Tag.OrdStatus: return(OrdStatusConverter.ConvertFrom(value)); case Tag.OrdType: return(OrdTypeConverter.ConvertFrom(value)); case Tag.RoutingType: return(RoutingTypeConverter.ConvertFrom(value)); case Tag.SecurityType: return(SecurityTypeConverter.ConvertFrom(value)); case Tag.SessionRejectReason: return(SessionRejectReasonConverter.ConvertFrom(value)); case Tag.Side: return(SideConverter.ConvertFrom(value)); case Tag.TimeInForce: return(TimeInForceConverter.ConvertFrom(value)); default: // String Values return(value); } }