예제 #1
0
        // *****************************************************************
        // ****                     Members                             ****
        // *****************************************************************
        //
        #endregion// members


        #region Constructors
        // *****************************************************************
        // ****                     Constructors                        ****
        // *****************************************************************
        //
        //
        //
        #endregion//Constructors


        #region no Properties
        // *****************************************************************
        // ****                     Properties                          ****
        // *****************************************************************
        //
        //
        #endregion//Properties


        #region Public Methods
        // *****************************************************************
        // ****                     Public Methods                      ****
        // *****************************************************************
        //
        //
        /// <summary>
        /// Caller would like to check if a quote has been filled.  This implementation simply looks to see if
        /// we are on the market or have bettered it. If so, we then create a fill.
        /// </summary>
        /// <param name="quote"></param>
        /// <param name="fill"></param>
        /// <param name="tickRounding"></param>
        /// <returns></returns>
        public static bool TryFill(Quote quote, out Fill fill, double tickRounding = 0.0001)
        {
            fill = null;
            int    tradeSign      = QTMath.MktSideToMktSign(quote.Side);
            double sameSidePrice  = quote.PricingEngine.ImpliedMarket.Price[quote.Side][0];
            double otherSidePrice = quote.PricingEngine.ImpliedMarket.Price[QTMath.MktSideToOtherSide(quote.Side)][0];
            double orderPrice     = tickRounding * Math.Round(quote.RawPrice / tickRounding);

            if (tradeSign * (sameSidePrice - orderPrice) > 0)
            {   // We are outside market
                return(false);
            }
            else if (orderPrice >= otherSidePrice)
            {   // order has crossed market.  Fill it at market.
                fill              = new Fill();
                fill.LocalTime    = DateTime.Now;
                fill.ExchangeTime = DateTime.Now;
                fill.Price        = otherSidePrice;
                fill.Qty          = quote.Qty;
                return(true);
            }
            else
            {   // we are inside the market.
                fill              = new Fill();
                fill.LocalTime    = DateTime.Now;
                fill.ExchangeTime = DateTime.Now;
                fill.Price        = orderPrice;
                fill.Qty          = quote.Qty;
                return(true);
            }
        }
예제 #2
0
        // *****************************************************************
        // ****                     Constructors                        ****
        // *****************************************************************
        //
        //
        //
        #endregion//Constructors


        #region no Properties
        // *****************************************************************
        // ****                     Properties                          ****
        // *****************************************************************
        //
        //
        #endregion//Properties


        #region Public Methods
        // *****************************************************************
        // ****                     Public Methods                      ****
        // *****************************************************************
        //
        //
        //
        // *************************************************************
        // ****               TryFindLegIDWithBestQtyRatio          ****
        // *************************************************************
        /// <summary>
        /// Caller would like to find the leg with the current best qty ratio for a given
        /// side of the market.  If side == Order.BidSide then we are looking for the leg
        /// with the highest BidQty / AskQty Ratio (lowest AskQty/BidQty Ratio).  If we are
        /// looking at side == Order.AskSide then we are looking for the leg with the highest
        /// AskQty / BidQty ratio (lower BidQty / AskQty) ratio.
        /// </summary>
        /// <param name="legIDs"></param>
        /// <param name="side"></param>
        /// <param name="bestLegID"></param>
        /// <returns>False if a "best" leg id is not found.  This has to be a problem with our markets</returns>
        public bool TryFindLegIDWithBestQtyRatio(List <int> legIDs, int side, out int bestLegID)
        {
            bestLegID = -1;
            int bestQtyRatio = -1;
            int qtyRatio;
            int oppSide = QTMath.MktSideToOtherSide(side);

            for (int leg = 0; leg < legIDs.Count; leg++)
            {
                qtyRatio = m_CurveLegs[leg].m_Market.Qty[side][0] / m_CurveLegs[leg].m_Market.Qty[oppSide][0];
                if (qtyRatio > bestQtyRatio)
                {   // this leg is our current best qty ratio!
                    bestQtyRatio = qtyRatio;
                    bestLegID    = leg;
                }
            }
            return(bestLegID != -1);
        }
예제 #3
0
        // *****************************************************************
        // ****                     Private Methods                     ****
        // *****************************************************************
        //
        //
        // *********************************************************
        // ****                IsScratchNeededAtIPrice          ****
        // *********************************************************
        /// <summary>
        /// Caller would like to check if a position from a  specific level needs to
        /// be scratched based on the parameters and our current market state.
        /// Notes : This could be optimized a bit for speed up purposes!
        /// </summary>
        /// <param name="iPrice"></param>
        /// <returns></returns>
        private bool IsScratchNeededAtIPrice(int iPrice)
        {
            int currentPos;

            if (!m_IPriceToPosition.TryGetValue(iPrice, out currentPos))     // we have no position to manage here!
            {
                return(false);
            }

            int posSide = QTMath.MktSignToMktSide(currentPos);  // find out which side our position is from
            int marketIPrice;

            if (m_IsActive)
            {   // if we are actively scratching
                // If i am long from the bid, i only care when the bid changes
                int posSign = QTMath.MktSideToMktSign(posSide);
                marketIPrice = (int)(m_Market.Price[posSide][0] / m_InstrumentDetails.TickSize);

                if (iPrice * posSign > marketIPrice * posSign | (marketIPrice == iPrice & m_Market.Qty[posSide][0] < m_ScratchThreshold))
                {   // market has gone through us, or quantity is now less than our threshold
                    return(true);
                }
            }
            else
            {   // we are passively scratching
                // if i am long from the bid, i only care when the ask price changes ( i am going to join when price goes sellers)
                int oppSide = QTMath.MktSideToOtherSide(posSide);
                int oppSign = QTMath.MktSideToMktSign(oppSide);
                marketIPrice = (int)(m_Market.Price[oppSide][0] / m_InstrumentDetails.TickSize);

                if (marketIPrice * oppSign > iPrice * oppSign | (marketIPrice == iPrice & m_Market.Qty[oppSide][0] > m_ScratchThreshold))
                {   // market has gone through us, or quantity is now less greater than our threshold
                    return(true);
                }
            }
            return(false);
        }
예제 #4
0
        }//UpdateQuotes().

        //
        //
        // *****************************************************
        // ****         Process Synthetic Order()           ****
        // *****************************************************
        /// <summary>
        /// Process fills from Strategy to PricingEngines.
        /// </summary>
        /// <param name="syntheticOrder"></param>
        /// <param name="newFills"></param>
        /// <returns>True if update required</returns>
        public override bool ProcessSyntheticOrder(SyntheticOrder syntheticOrder, List <Fill> newFills)
        {
            if (newFills == null || newFills.Count == 0)
            {
                return(false);
            }

            // Collect all fills into work spaces.
            Log.BeginEntry(LogLevel.Major, "Quote.ProcessSynthOrder: {0}  Fills=", ParentStrategy.Name);
            w_NewFills[0].Clear();
            w_NewFills[1].Clear();
            foreach (Fill fill in newFills)
            {
                int tradeSide = QTMath.MktSignToMktSide(fill.Qty);
                w_NewFills[tradeSide].Add(fill);
                m_BuySellQty[tradeSide] += fill.Qty;                // this records the raw fills as they come in.
                Log.AppendEntry(" [{0}]", fill);
            }
            int[] position = new int[2];                            // this will be updated during allocation of fills.
            base.m_Position.CopyTo(position, 0);
            Log.AppendEntry(". ");


            // Try to cancel fills with undistributed fills.
            // TODO: Cancel undistributed fills, if any.

            /*
             * if ( m_UndistributedFills[0].Count + m_UndistributedFills[1].Count > 0)
             * {
             *  for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
             *  {
             *      int otherSide = QTMath.MktSideToOtherSide(tradeSide);
             *      if (w_NewFills[tradeSide].Count > 0 && m_UndistributedFills[otherSide].Count > 0 )
             *      {
             *          Log.AppendEntry(" Canceling with undistributed fills: Not implemented!");
             *      }
             *  }
             * }
             */

            // Prepare entry for database write.
            DateTime localTime = ParentStrategy.StrategyHub.GetLocalTime();

            UV.Lib.DatabaseReaderWriters.Queries.FillsQuery query = new Lib.DatabaseReaderWriters.Queries.FillsQuery();

            // -----------------------------------------------------
            // Pass: distribute fills to stops
            // -----------------------------------------------------
            for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
            {
                int exitingSide = QTMath.MktSideToActiveMktSide(tradeSide);
                if (w_NewFills[tradeSide].Count == 0 || base.m_FillQty[exitingSide].Count == 0)
                {
                    continue;
                }
                List <Quote> exitList = m_QuoteListRecycling.Get();      // get empty list.
                exitList.Clear();
                foreach (KeyValuePair <PricingEngine, int> kv in base.m_FillQty[exitingSide])
                {
                    Quote quote;
                    if (m_Quotes[tradeSide].TryGetValue(kv.Key, out quote) && quote.Reason == QuoteReason.Stop && quote.Qty != 0)
                    {
                        exitList.Add(quote);
                    }
                }
                if (exitList.Count > 0)
                {
                    Log.AppendEntry(" Distribute to {0} stop quoters:", exitList.Count);
                    DistributeFillsToQuoters(ref w_NewFills[tradeSide], ref exitList, ref query, ref w_DistributedFills, ref position);
                    Log.AppendEntry(". ");
                }
                exitList.Clear();
                m_QuoteListRecycling.Recycle(exitList);
            }//next tradeSide

            // -----------------------------------------------------
            // Pass: distribute fills to quoters who want them.
            // -----------------------------------------------------
            for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
            {
                if (w_NewFills[tradeSide].Count == 0)
                {
                    continue;
                }
                int          exitingSide = QTMath.MktSideToOtherSide(tradeSide);
                int          tradeSign   = QTMath.MktSideToMktSign(tradeSide);
                List <Quote> exitList    = m_QuoteListRecycling.Get();  // get empty lists for entry quotes.
                List <Quote> entryList   = m_QuoteListRecycling.Get();  // and for exit quoters...

                Log.AppendEntry(" Distribute to working quoters");
                List <int> iPriceKeys = new List <int>(m_QuotesByPrice[tradeSide].Keys);
                int        priceLevel = 0;
                while (w_NewFills[tradeSide].Count > 0 && priceLevel < iPriceKeys.Count)
                {
                    // On each interation, update our "pos" so we know the remaining qty.
                    int allowedEntryQty = tradeSign * Math.Max(0, m_MaxPosition - Math.Abs(position[tradeSide]));

                    // Load entry/exit quoters for this price level.
                    Log.AppendEntry(" lvl={0}/{1}:", priceLevel, iPriceKeys.Count);
                    entryList.Clear();
                    exitList.Clear();
                    List <Quote> quotes = null;
                    if (m_QuotesByPrice[tradeSide].TryGetValue(iPriceKeys[priceLevel], out quotes))
                    {
                        foreach (Quote quote in quotes)
                        {
                            if (allowedEntryQty != 0 && quote.Reason == QuoteReason.Entry && quote.Qty != 0)
                            {
                                entryList.Add(quote);
                            }
                            else if (base.m_FillQty[exitingSide].ContainsKey(quote.PricingEngine) && quote.Reason == QuoteReason.Exit && quote.Qty != 0)
                            {
                                exitList.Add(quote);
                            }
                        }
                    }

                    if (exitList.Count > 0)
                    {
                        Log.AppendEntry(" Exits ({0}):", exitList.Count);
                        DistributeFillsToQuoters(ref w_NewFills[tradeSide], ref exitList, ref query, ref w_DistributedFills, ref position);
                    }
                    if (entryList.Count > 0)
                    {
                        entryList.Sort(this.QuoteComparerByEngineId);  // To better match our backtest, consider sorting entryList by engine names...
                        Log.AppendEntry(" Entries ({0}):", entryList.Count);
                        DistributeFillsToQuoters(ref w_NewFills[tradeSide], ref entryList, ref query, ref w_DistributedFills, ref position);
                    }
                    //
                    priceLevel++;
                }// next price level
                // Clean up.
                entryList.Clear();
                exitList.Clear();
                m_QuoteListRecycling.Recycle(entryList);
                m_QuoteListRecycling.Recycle(exitList);
                Log.AppendEntry(" Finished.");
                if (w_NewFills[tradeSide].Count > 0)
                {
                    Log.AppendEntry(" {0} fills remaining.", w_NewFills[tradeSide].Count);
                }
                else
                {
                    Log.AppendEntry(" No fills remain.");
                }
            }//tradeSide

            // -----------------------------------------------------
            // Start emergency processing!
            // -----------------------------------------------------
            if (w_NewFills[0].Count > 0 || w_NewFills[1].Count > 0)
            {
                Log.AppendEntry(" Process unwanted fills!");
                ProcessUnwantedFills(ref w_NewFills, ref w_DistributedFills);
            }

            Log.EndEntry();                                             // end logging for us now, before we call other methods.
            // -----------------------------------------------------
            // Distribute these fills now.
            // -----------------------------------------------------
            if (query != null && query.Count != 0)
            {
                ParentStrategy.StrategyHub.RequestDatabaseWrite(query); // submit all the queries
            }
            foreach (KeyValuePair <Quote, List <Fill> > kv in w_DistributedFills)
            {
                int    fillQty   = 0;
                double fillPrice = 0;
                foreach (Fill fill in kv.Value)
                {
                    fillQty  += fill.Qty;
                    fillPrice = fill.Price;                             // TODO: this should be ave fill price
                }
                int tradeSide = QTMath.MktSignToMktSide(fillQty);
                int exitSide  = QTMath.MktSideToOtherSide(tradeSide);
                if (fillQty == 0)
                {
                    continue;
                }
                // Update our position counting.
                int openPos = 0;
                if (base.m_FillQty[exitSide].TryGetValue(kv.Key.PricingEngine, out openPos))
                {   // This is an exit (since this PricingEngine has open position on other side of mkt).
                    openPos += fillQty;

                    // update quoter's graph
                    if (m_IsGraphEnabled)
                    {
                        if (exitSide == 0)
                        {   // exit long position.
                            m_GraphEngine.AddPoint(m_GraphID, "Long Exit", fillPrice);
                            m_GraphEngine.AddText(m_GraphID, string.Format("{0}", kv.Key.PricingEngine.EngineName), fillPrice + m_TextOffsetTicks * m_QuoteTickSize);
                        }
                        else
                        {   // exit short position.
                            m_GraphEngine.AddPoint(m_GraphID, "Short Exit", fillPrice);
                            m_GraphEngine.AddText(m_GraphID, string.Format("{0}", kv.Key.PricingEngine.EngineName), fillPrice - m_TextOffsetTicks * m_QuoteTickSize);
                        }
                    }

                    // Update real position table.
                    if (openPos * fillQty <= 0)
                    {
                        base.m_FillQty[exitSide].Remove(kv.Key.PricingEngine);// complete exit, possibly a side flip
                    }
                    if (openPos != 0)
                    {   // There is a new position (on other side of mkt).
                        int posSide = QTMath.MktSignToMktSide(openPos);
                        base.m_FillQty[posSide][kv.Key.PricingEngine] = openPos;
                    }
                }
                else
                {   // This is an entry!
                    if (m_IsGraphEnabled)
                    {
                        if (tradeSide == 0)
                        {
                            m_GraphEngine.AddPoint(m_GraphID, "Long Entry", fillPrice);
                            m_GraphEngine.AddText(m_GraphID, string.Format("{0}", kv.Key.PricingEngine.EngineName), fillPrice - m_TextOffsetTicks * m_QuoteTickSize);
                        }
                        else
                        {
                            m_GraphEngine.AddPoint(m_GraphID, "Short Entry", fillPrice);
                            m_GraphEngine.AddText(m_GraphID, string.Format("{0}", kv.Key.PricingEngine.EngineName), fillPrice + m_TextOffsetTicks * m_QuoteTickSize);
                        }
                    }
                    // Update real position table.
                    if (base.m_FillQty[tradeSide].ContainsKey(kv.Key.PricingEngine))
                    {
                        base.m_FillQty[tradeSide][kv.Key.PricingEngine] += fillQty;  // add to this engines position.
                    }
                    else
                    {
                        base.m_FillQty[tradeSide].Add(kv.Key.PricingEngine, fillQty); // store this engines position.
                    }
                }
                // Trigger the pricing engine filled event!
                foreach (Fill fill in kv.Value)
                {
                    kv.Key.PricingEngine.Filled(fill);
                }
            }// next filled Quote.
            // Update total sum
            Log.BeginEntry(LogLevel.Major, "Quote.ProcessSynthOrder {0} Summary: ", ParentStrategy.Name);
            for (int tradeSide = 0; tradeSide < 2; tradeSide++)
            {
                // Add up the current position.
                int pos = 0;
                foreach (KeyValuePair <PricingEngine, int> kv in base.m_FillQty[tradeSide])
                {
                    pos += kv.Value;
                }
                base.m_Position[tradeSide] = pos;

                // Write some logging.
                Log.AppendEntry(" {0}-side:", QTMath.MktSideToLongString(tradeSide));
                Log.AppendEntry(" Pos={0:+0;-0;0}", base.m_Position[tradeSide]);
                foreach (KeyValuePair <PricingEngine, int> kv in base.m_FillQty[tradeSide])
                {
                    Log.AppendEntry(" [{1:+0;-0;0} {0}]", kv.Key.EngineName, kv.Value);
                }
                Log.AppendEntry(" TotalQty={0}", m_BuySellQty[tradeSide]);
                // Log undistributed fills too.
                if (m_UndistributedFills[tradeSide].Count > 0)
                {
                    Log.AppendEntry(" Undistributed {0}-fills:", QTMath.MktSideToLongString(tradeSide));
                    foreach (Fill fill in m_UndistributedFills[tradeSide])
                    {
                        Log.AppendEntry(" {0}", fill);
                    }
                }
            }// next tradeSide
            Log.AppendEntry(" |MaxPos|={0}.", m_MaxPosition);
            Log.EndEntry();

            //
            // Clean up work spaces
            //
            foreach (KeyValuePair <Quote, List <Fill> > kv in w_DistributedFills)
            {
                kv.Value.Clear();
                m_FillListRecycling.Recycle(kv.Value);
            }
            w_DistributedFills.Clear();                                 // Quoters and their fills to distribute.

            return(true);
        }// ProcessSyntheticOrder()
예제 #5
0
        //
        //
        //
        //
        #endregion//Constructors

        #region no Properties
        // *****************************************************************
        // ****                     Properties                          ****
        // *****************************************************************
        //
        //
        //
        //
        //
        #endregion//Properties

        #region Public Methods
        // *****************************************************************
        // ****                     Public Methods                      ****
        // *****************************************************************
        //
        // *****************************************
        // ****         UpdateQuotes            ****
        // *****************************************
        /// <summary>
        /// Called regularly by the StrategyHub after it has allowed PricingEngines to update
        /// and possibly change their quotes.  This method checks for quote changes, and
        /// sends them to the OrderEngine.
        /// Whenever the QuoteEngine's parameters are changed (and it would affect how we quote)
        /// we should call this method fith forceUpdate=true!
        /// <param name="forceUpdate">set to to force updating each side of quotes, as is done after fills.</param>
        /// </summary>
        public override void UpdateQuotes(bool forceUpdate = false)
        {
            if (m_IsQuoteEnabled == false)
            {   // TODO: We could check to exit undistributed fills.
                return;
            }
            ValidateQuoteTables();                              // Check whether QuoteTickSize has changed.

            if (ValidatePosition() == false)
            {
                this.IsQuoteEnabled = false;
                return;
            }

            DateTime     now              = ParentStrategy.StrategyHub.GetLocalTime();
            List <Fill>  fauxFills        = null;
            List <Quote> fauxQuotesFilled = null;

            // Update graph
            if (m_IsGraphEnabled && m_FirstPriceEngine != null)
            {
                double bid = m_FirstPriceEngine.ImpliedMarket.Price[0][0];
                double ask = m_FirstPriceEngine.ImpliedMarket.Price[1][0];
                m_GraphEngine.AddPoint(m_GraphID, "Bid", bid);
                m_GraphEngine.AddPoint(m_GraphID, "Ask", ask);
            }



            for (int tradeSide = 0; tradeSide < 2; tradeSide++)
            {
                if (m_IsQuoteSideUpdateRequired[tradeSide] == false && forceUpdate == false)
                {
                    continue;
                }

                //
                // Collect most aggressive non-zero quoters
                //
                int          tradeSign      = QTMath.MktSideToMktSign(tradeSide);
                int          exitSide       = QTMath.MktSideToOtherSide(tradeSide);
                bool         isEntryAllowed = tradeSign * base.m_Position[tradeSide] < m_MaxPosition; // true if new entries allowed.
                int          rawIPrice      = 0;
                int          realEntryQty   = 0;
                int          realExitQty    = 0;
                List <Quote> simQuotes      = m_QuoteListRecycling.Get();
                simQuotes.Clear();
                foreach (KeyValuePair <int, List <Quote> > kv in m_QuotesByPrice[tradeSide])
                {
                    foreach (Quote quote in kv.Value)
                    {
                        int qty;
                        if (isEntryAllowed && quote.Reason == QuoteReason.Entry)
                        {
                            realEntryQty += quote.Qty;          // collect real entry quote size
                        }
                        else if (base.m_FillQty[exitSide].TryGetValue(quote.PricingEngine, out qty) && qty != 0)
                        {
                            realExitQty += quote.Qty;           // collect real exit quantity
                        }
                        else if (quote.Qty != 0)
                        {
                            simQuotes.Add(quote);               // collect other quotes, which will be simulated filled.
                        }
                    }
                    rawIPrice = -tradeSign * kv.Key;            // price at this level
                    if (realEntryQty != 0 || realExitQty != 0)
                    {
                        break;                                  // stop, once we have found non-empty quote stop.
                    }
                }

                //
                // Send real quotes.
                //
                int  tradeId;
                bool isRealQuoteSent = false;
                int  realTotalQty    = realExitQty + realEntryQty;
                if (realTotalQty != 0)
                {                                                                                              // There is some non-zero (REAL) quantity to quote.
                    // Constrain REAL quotes inside max position allowed: s*qty <= MaxPos - s*Pos
                    int quoteQtyAllowed = Math.Max(0, m_MaxPosition - tradeSign * base.m_Position[tradeSide]); //always positive
                    realTotalQty = tradeSign * Math.Min(quoteQtyAllowed, Math.Abs(realTotalQty));
                    if (realTotalQty != 0)
                    {   // We want to quote a real qty.
                        int orderQty = realTotalQty + m_BuySellQty[tradeSide];
                        tradeId         = ParentStrategy.m_OrderEngine.Quote(tradeSide, rawIPrice * m_QuoteTickSize, orderQty, string.Empty);
                        isRealQuoteSent = true;
                        if (m_IsGraphEnabled)
                        {
                            m_GraphEngine.AddPoint(m_GraphID, QTMath.MktSideToLongString(tradeSide), rawIPrice * m_QuoteTickSize);
                        }
                    }
                }
                if (!isRealQuoteSent)
                {   // If in the above, we have not sent a real order, send a zero quote.
                    int orderQty = m_BuySellQty[tradeSide];
                    tradeId = ParentStrategy.m_OrderEngine.Quote(tradeSide, rawIPrice * m_QuoteTickSize, orderQty, string.Empty);
                    if (m_IsGraphEnabled)
                    {
                        m_GraphEngine.AddPoint(m_GraphID, QTMath.MktSideToLongString(tradeSide), double.NaN);
                    }
                }

                //
                // Simulate quoting.
                //
                if (simQuotes.Count > 0)
                {
                    foreach (Quote quote in simQuotes)
                    {
                        Fill fill;
                        if (quote.Qty != 0 && FillModels.FillModel.TryFill(quote, out fill))
                        {
                            if (fauxQuotesFilled == null)
                            {   // Only make these tables when we need them!
                                fauxQuotesFilled = m_QuoteListRecycling.Get();
                                fauxFills        = m_FillListRecycling.Get();
                            }
                            fill.LocalTime    = now;
                            fill.ExchangeTime = now;
                            fauxFills.Add(fill);
                            fauxQuotesFilled.Add(quote);
                        }
                    }
                }
                simQuotes.Clear();
                m_QuoteListRecycling.Recycle(simQuotes);

                m_IsQuoteSideUpdateRequired[tradeSide] = false;
            }//next side


            //
            // Report simulated fills.
            //
            if (fauxFills != null && fauxFills.Count > 0)
            {
                Log.BeginEntry(LogLevel.Minor, "Quote Simulating fills: ");
                for (int i = 0; i < fauxFills.Count; ++i)
                {
                    Fill  fill  = fauxFills[i];
                    Quote quote = fauxQuotesFilled[i];
                    Log.AppendEntry("[{0} filled {1}] ", quote.PricingEngine.EngineName, fill);
                }
                Log.EndEntry();
                UV.Lib.DatabaseReaderWriters.Queries.FillsQuery query = new Lib.DatabaseReaderWriters.Queries.FillsQuery();
                // Process sim fills.
                for (int i = 0; i < fauxFills.Count; ++i)
                {
                    Fill  fill  = fauxFills[i];
                    Quote quote = fauxQuotesFilled[i];
                    quote.Qty -= fill.Qty;

                    string msgStr = quote.FillAttribution();
                    query.AddItemToWrite(ParentStrategy.SqlId, -1, now, m_FauxUser, quote.PricingEngine.EngineName, msgStr, fill.Qty, fill.Price);
                    quote.PricingEngine.Filled(fill);
                }
                if (query != null)
                {
                    ParentStrategy.StrategyHub.RequestDatabaseWrite(query);
                }
                fauxFills.Clear();
                m_FillListRecycling.Recycle(fauxFills);
                fauxQuotesFilled.Clear();
                m_QuoteListRecycling.Recycle(fauxQuotesFilled);
            }
        }//UpdateQuotes().
예제 #6
0
        //
        //
        // *********************************************************
        // ****                  TryScratchPosition             ****
        // *********************************************************
        /// <summary>
        /// Called when a market state change triggers a scratch criteria to be met,
        /// Or after an order change triggers us to attempt and reprocess pending scratch orders
        /// </summary>
        /// <param name="iPrice"></param>
        /// <param name="isFirstAttempt">will be true if market state caused this attempt </param>
        /// <returns>true if a scratch order has been submitted and not queued</returns>
        private bool TryScratchPosition(int iPrice, bool isFirstAttempt)
        {
            Order scratchOrder;                                             // Our scratch order to be submit or queued
            int   currentPos;

            if (isFirstAttempt)
            {
                if (!m_IPriceToPosition.TryGetValue(iPrice, out currentPos))
                {
                    m_Log.NewEntry(LogLevel.Error, "Scratcher:{0} TryScratchPosition failed. iPrice {1} not found in dictionary", this.EngineName, iPrice);
                    return(false);
                }

                int qtyToScratch = currentPos * -1;                       // opposit of qty of position give us scratch qty
                int scratchSide  = QTMath.MktSignToMktSide(qtyToScratch); // opposite side is the scratch side

                if (m_IPriceToPendingScratchOrder.TryGetValue(iPrice, out scratchOrder))
                {   // we already have a pending scratch order waiting for submisssion, just add our quantity to it
                    m_ExecutionListener.TryChangeOrderQty(scratchOrder, scratchOrder.OriginalQtyPending + qtyToScratch);
                }
                else if (!m_ExecutionListener.TryCreateOrder(m_InstrumentDetails.InstrumentName, scratchSide, iPrice, qtyToScratch, out scratchOrder))
                {   // order creation failed for some reason, this has to be a logic mistake!
                    m_Log.NewEntry(LogLevel.Error, "Scratcher:{0} TryScratchPosition failed. Order Creation failed", this.EngineName);
                    return(false);
                }

                m_IPriceToPosition.Remove(iPrice);                              // Remove key from dictionary.
                scratchOrder.OrderReason = OrderReason.Scratch;                 // tag order with "reason"
            }
            else
            {     // this is a second call after we believe we are ready to scratch, check opposing orders again before we submit
                if (!m_IPriceToPendingScratchOrder.TryGetValue(iPrice, out scratchOrder))
                { // something went wrong, log the error
                    m_Log.NewEntry(LogLevel.Error, "Scratcher:{0} TryScratchPosition failed. Unable to find pending scratch order for IPrice {1}", this.EngineName, iPrice);
                    return(false);
                }
            }


            bool isOkayToSubmitScratch = true;

            if (m_IsActive)
            {                                 // if we are actively scratching, we could possibly cross ourselves in the market, need to be careful here
                m_OrderWorkSpaceList.Clear(); // clear order workspace
                m_OrderEngineOrderBook.GetOrdersByIPrice(QTMath.MktSideToOtherSide(scratchOrder.Side), iPrice, ref m_OrderWorkSpaceList);
                if (m_OrderWorkSpaceList.Count > 0)
                {   // we have opposing orders that need to be pulled prior to us submitting, arm ourselves and wait for delete ack
                    HashSet <int> orderIdsWaitingDelete;
                    if (!m_IPriceToOppositeOrderIDs.TryGetValue(iPrice, out orderIdsWaitingDelete))
                    {   // we don't have a list yet for this iPrice, create one
                        orderIdsWaitingDelete = new HashSet <int>();
                    }
                    for (int i = 0; i < m_OrderWorkSpaceList.Count; i++)
                    {                                                                   // iterate through all pending orders and save their order ids
                        orderIdsWaitingDelete.Add(m_OrderWorkSpaceList[i].Id);          // since this is a hash set it will only add unique ids
                        m_OppositeOrderIDToIPrice[m_OrderWorkSpaceList[i].Id] = iPrice; // make sure we map every order to the iprice we care about
                    }
                    m_IPriceToOppositeOrderIDs[iPrice] = orderIdsWaitingDelete;         // save our new hash set by iPrice
                }
                else
                {   // there is no opposing order, immediately send our scratch order
                    isOkayToSubmitScratch = true;
                    if (!isFirstAttempt)
                    {                                              // we need to cleanup our collection
                        m_IPriceToOppositeOrderIDs.Remove(iPrice); // remove this collection
                    }
                }
            }

            if (isOkayToSubmitScratch)
            {   // we are either passive (there is no possible way we can cross ourselves with a scratch order) or we are active but okay to submit
                TrySubmitScratchOrder(scratchOrder);
                return(true);
            }
            return(false);
        }
예제 #7
0
        }//DistributeFillsToQuoters()

        //
        //
        // *************************************************************
        // ****             Process Unwanted Fills()                ****
        // *************************************************************
        protected void ProcessUnwantedFills(ref List <Fill>[] newFills, ref Dictionary <Quote, List <Fill> > distributedFills)
        {
            //
            // Cancel off-setting fills.
            //
            if (newFills[0].Count > 0 && newFills[1].Count > 0)
            {
                Log.AppendEntry(" Remove offsetting fills:");
                while (newFills[0].Count > 0 && newFills[1].Count > 0)
                {
                    Fill fillLong = newFills[0][0];
                    newFills[0].RemoveAt(0);
                    Fill fillShort = newFills[1][0];
                    newFills[1].RemoveAt(0);
                    int netQty       = fillLong.Qty + fillShort.Qty; // cancel the first fill in each list.
                    int cancelledQty = Math.Min(Math.Abs(fillLong.Qty), Math.Abs(fillShort.Qty));
                    Log.AppendEntry(" PnL={0}", cancelledQty * (fillShort.Price - fillLong.Price));
                    if (netQty > 0)
                    {   // Long side will survive somewhat.
                        Fill remainder = Fill.Create(fillLong);
                        remainder.Qty = netQty;
                        newFills[0].Insert(0, remainder);
                    }
                    else if (netQty < 0)
                    {
                        Fill remainder = Fill.Create(fillShort);
                        remainder.Qty = netQty;
                        newFills[1].Insert(0, remainder);
                    }
                }
            }

            //
            // Pass: distribute fills to anyone with position: "forced exit"
            //
            if (newFills[0].Count > 0 || newFills[1].Count > 0)
            {
                for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
                {
                    int exitSide = QTMath.MktSideToOtherSide(tradeSide);
                    if (newFills[tradeSide].Count == 0 || m_FillQty[exitSide].Count == 0)
                    {
                        continue;
                    }
                    int tradeSign = QTMath.MktSideToMktSign(tradeSide);
                    foreach (KeyValuePair <PricingEngine, int> kv in m_FillQty[exitSide])
                    {
                        Quote       quote;
                        List <Fill> fills;
                        int         qtyToForce = 0;
                        if (m_Quotes[tradeSide].TryGetValue(kv.Key, out quote) && distributedFills.TryGetValue(quote, out fills))
                        {   // This strategy already has some fills.
                            int fillQty = 0;
                            foreach (Fill fill in fills)
                            {
                                fillQty += fill.Qty;
                            }
                            int finalQty = (kv.Value + fillQty);
                            qtyToForce = Math.Min(0, tradeSign * finalQty); // qty for fill he can still take.
                        }
                        else
                        {
                            qtyToForce = -kv.Value;
                        }
                        if (qtyToForce != 0)
                        {   // Pass to him extra fills
                            foreach (Fill fill in newFills[tradeSide])
                            {
                            }
                        }
                    }
                }// next side
            }

            //
            // Pass: distribute fills to anyone with a quote!
            //
            if (newFills[0].Count > 0 || newFills[1].Count > 0)
            {
                for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
                {
                    if (newFills[tradeSide].Count == 0)
                    {
                        continue;
                    }
                    Log.AppendEntry(" Forcing fills:");
                    int tradeSign = QTMath.MktSideToMktSign(tradeSide);
                    // Collect all the quotes
                    List <Quote> quotes = m_QuoteListRecycling.Get();
                    quotes.Clear();
                    foreach (KeyValuePair <int, List <Quote> > kv in m_QuotesByPrice[tradeSide])
                    {
                        quotes.AddRange(kv.Value);
                    }
                    int quoteID    = 0;
                    int qtyToForce = 1;                             // TODO: This can be dynamic
                    // Force fills now
                    while (newFills[tradeSide].Count > 0 && quotes.Count > 0)
                    {
                        Quote quote    = quotes[quoteID];
                        Fill  origFill = newFills[tradeSide][0];
                        newFills[tradeSide].RemoveAt(0);

                        Fill fillToDistribute = null;
                        int  fillQty          = tradeSign * Math.Min(qtyToForce, Math.Abs(origFill.Qty));
                        if ((origFill.Qty - fillQty) == 0)
                        {   // Entire fill is consumed.
                            fillToDistribute = origFill;
                        }
                        else
                        {
                            fillToDistribute     = Fill.Create(origFill);
                            fillToDistribute.Qty = fillQty;
                            Fill remainingFill = Fill.Create(origFill);
                            remainingFill.Qty = origFill.Qty - fillQty;
                            newFills[tradeSide].Insert(0, remainingFill);
                        }
                        List <Fill> fills;
                        if (!distributedFills.TryGetValue(quote, out fills))
                        {   // This is this quotes first fill, so create a fill list.
                            fills = new List <Fill>();
                            distributedFills.Add(quote, fills);
                        }
                        fills.Add(fillToDistribute);
                        Log.AppendEntry(" {0} filled {1}.", quote.PricingEngine.EngineName, fillToDistribute);
                        // increment the while loop!
                        quoteID = (quoteID + 1) % quotes.Count;
                    }//wend
                    // Cleanup.
                    quotes.Clear();
                    m_QuoteListRecycling.Recycle(quotes);
                } // next side
            }     // if fills to distribute.


            //
            // Failed to distribute fills.
            //
            if (newFills[0].Count > 0 || newFills[1].Count > 0)
            {
                Log.AppendEntry(" FAILED to distribute fills:");
                for (int tradeSide = 0; tradeSide < 2; ++tradeSide)
                {
                    foreach (Fill fill in newFills[tradeSide])
                    {
                        m_UndistributedFills[tradeSide].Add(fill);
                        Log.AppendEntry(" {0}", fill);
                    }
                }
            }
        }// ProcessUnwantedFills()