/// <summary>
        /// Add an option contract.  This method would not normally be
        /// invoked by a user application.  Rather,
        /// MamdaOptionChainListener would be most likely to call this
        /// method.
        /// </summary>
        /// <param name="contractSymbol">The option instrument symbol.</param>
        /// <param name="contract">The Mamda option contract representation.</param>
        public void addContract(
            string contractSymbol,
            MamdaOptionContract contract)
        {
            DateTime expireDate  = contract.getExpireDate();
            double   strikePrice = contract.getStrikePrice();
            string   exchange    = contract.getExchange();

            MamdaOptionContract.PutOrCall putCall = contract.getPutCall();

            mOptions.put(contractSymbol, contract);
            if (putCall == MamdaOptionContract.PutOrCall.Call)
            {
                mCallOptions.add(contract);
            }
            else
            {
                mPutOptions.add(contract);
            }

            // Add the contract to the expiration-by-strike set.
            MamdaOptionExpirationStrikes expireStrikes = (MamdaOptionExpirationStrikes)mExpirationSet.valueOf(expireDate);

            if (expireStrikes == null)
            {
                expireStrikes = new MamdaOptionExpirationStrikes();
                mExpirationSet.put(expireDate, expireStrikes);
            }
            MamdaOptionStrikeSet strikeSet = (MamdaOptionStrikeSet)expireStrikes.valueOf(strikePrice);

            if (strikeSet == null)
            {
                strikeSet = new MamdaOptionStrikeSet(expireDate, strikePrice);
                expireStrikes.put(strikePrice, strikeSet);
            }
            MamdaOptionContractSet contractSet = (putCall == MamdaOptionContract.PutOrCall.Call) ?
                                                 strikeSet.getCallSet() : strikeSet.getPutSet();

            if (MamdaOptionExchangeUtils.isBbo(exchange))
            {
                contractSet.setBboContract(contract);
            }
            else if (MamdaOptionExchangeUtils.isWombatBbo(exchange))
            {
                contractSet.setWombatBboContract(contract);
            }
            else
            {
                contractSet.setExchangeContract(exchange, contract);
                mExchanges.add(exchange);
            }
            mStrikePrices.add(strikePrice);
        }
        private MamdaOptionContract findContract(
            MamdaSubscription subscription,
            MamaMsg msg)
        {
            /*
             * NOTE: fields which are enums can be pubished as integers if feedhandler
             * uses mama-publish-enums-as-ints.  It may also be possible for a feed to
             * publish the numerical value as a string. All enumerated fields must be handled
             * by getting the value based on the field type.
             */

            // Look up the strike price and expiration date
            string contractSymbol = null;

            if (!msg.tryString(MamdaOptionFields.CONTRACT_SYMBOL, ref contractSymbol))
            {
                throw new MamdaDataException("cannot find contract symbol");
            }

            string fullSymbol            = contractSymbol;
            MamdaOptionContract contract = mChain.getContract(fullSymbol);

            if (contract == null)
            {
                string expireDateStr = String.Empty;
                double strikePrice   = 0.0;
                string putCall       = String.Empty;
                uint   openInterest  = 0;

                msg.tryString(MamdaOptionFields.EXPIRATION_DATE, ref expireDateStr);
                msg.tryF64(MamdaOptionFields.STRIKE_PRICE, ref strikePrice);

                if (msg.tryField(MamdaOptionFields.PUT_CALL, ref tmpfield_))
                {
                    putCall = getFieldAsString(tmpfield_);
                }

                int    symbolLen = fullSymbol.Length;
                string symbol    = null;
                string exchange  = null;
                int    dotIndex  = fullSymbol.LastIndexOf('.');
                if (dotIndex > 0)
                {
                    // Have exchange in symbol.
                    exchange = fullSymbol.Substring(dotIndex + 1);
                    symbol   = fullSymbol.Substring(0, dotIndex);
                }
                else
                {
                    exchange = "";
                    symbol   = fullSymbol;
                }

                DateTime expireDate = DateTime.MinValue;
                try
                {
                    expireDate = mDateFormat.Parse(expireDateStr);
                }
                catch (FormatException e)
                {
                    throw new MamdaDataException(
                              String.Format("cannot parse expiration date: {0}", expireDateStr));
                }

                MamdaOptionContract.PutOrCall putCallchar = extractPutCall(msg, fullSymbol);
                contract = new MamdaOptionContract(
                    symbol,
                    exchange,
                    expireDate,
                    strikePrice,
                    putCallchar);

                MamdaOptionContract.ExerciseStyle exerciseStyleChar = extractExerciseStyle(msg, fullSymbol);
                contract.setExerciseStyle(exerciseStyleChar);


                if (msg.tryU32(MamdaOptionFields.OPEN_INTEREST, ref openInterest))
                {
                    contract.setOpenInterest((long)openInterest);
                }

                mChain.addContract(fullSymbol, contract);

                mLastActionContract           = contract;
                mLastActionContractFieldState = MamdaFieldState.MODIFIED;
                mLastAction           = MamdaOptionAction.Add;
                mLastActionFieldState = MamdaFieldState.MODIFIED;
                foreach (MamdaOptionChainHandler handler in mHandlers)
                {
                    handler.onOptionContractCreate(subscription, this, msg, contract, mChain);
                }
            }
            return(contract);
        }
        private MamdaOptionContract.PutOrCall extractPutCall(
            MamaMsg msg,
            string fullSymbol)
        {
            MamdaOptionContract.PutOrCall putCall = MamdaOptionContract.PutOrCall.Unknown;
            int putCallInt = 0;

            if (!msg.tryField(MamdaOptionFields.PUT_CALL, ref tmpfield_))
            {
                Console.WriteLine("findContract:CANNOT find put/call in msg:" + fullSymbol + putCall);
            }
            else
            {
                switch (tmpfield_.getType())
                {
                case mamaFieldType.MAMA_FIELD_TYPE_I8:
                case mamaFieldType.MAMA_FIELD_TYPE_U8:
                case mamaFieldType.MAMA_FIELD_TYPE_I16:
                case mamaFieldType.MAMA_FIELD_TYPE_U16:
                    putCallInt = tmpfield_.getU16();
                    switch (putCallInt)
                    {
                    case 1:
                        putCall = MamdaOptionContract.PutOrCall.Put;
                        break;

                    case 2:
                        putCall = MamdaOptionContract.PutOrCall.Call;
                        break;

                    case 99:
                        putCall = MamdaOptionContract.PutOrCall.Unknown;
                        break;

                    default:
                        putCall = MamdaOptionContract.PutOrCall.Unknown;
                        Console.WriteLine("Unhandled value for wPutCall." + putCallInt);
                        break;
                    }
                    break;

                case mamaFieldType.MAMA_FIELD_TYPE_STRING:
                    string putCallStr = tmpfield_.getString();
                    switch (putCallStr[0])
                    {
                    case '1':
                    case 'P':
                        putCall = MamdaOptionContract.PutOrCall.Put;
                        break;

                    case '2':
                    case 'C':
                        putCall = MamdaOptionContract.PutOrCall.Call;
                        break;

                    default:
                        putCall = MamdaOptionContract.PutOrCall.Unknown;
                        if ((putCallStr == "99") && (putCallStr == "Z"))
                        {
                            Console.WriteLine("Unhandled value for wPutCall." + putCallStr);
                        }
                        break;
                    }
                    break;

                default:
                    putCall = MamdaOptionContract.PutOrCall.Unknown;
                    Console.WriteLine("Unhandled type for wPutCall. Expected string or integer but returned: " + tmpfield_.getType());
                    break;
                }
            }
            return(putCall);
        }