/// <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((OrderType)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((Side)field.Value); bodyFields += TagConverter.ConvertTo(field.Tag) + "=" + fieldValue + FixSeparator.ToString(); break; case Tag.TimeInForce: // TimeInForce Body Field fieldValue = TimeInForceConverter.ConvertTo((TimeInForce)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); }