}//RequestStop() // // // //*********************************************** // **** RequestProductsToRecord() **** //*********************************************** // /// <summary> /// Threadsafe call to request products to recorded. /// /// Warning this need to be improved to take into account illiquid months! /// </summary> /// <param name="productRequest"></param> /// <returns></returns> public bool RequestProductsToRecord(ProductRequest productRequest) { DataHubRequest request = m_RequestFactory.Get(); request.Request = RequestCode.RequestProductsToRecord; request.Data.Add(productRequest); return(this.HubEventEnqueue(request)); }
// // /// <summary> /// Given a ttOrder try and create a UV style order attempting to use /// a recycle order each time. /// </summary> /// <param name="ttOrder"></param> /// <param name="orderRecycler"></param> /// <param name="uvOrder"></param> /// <returns></returns> public static bool TryConvert(Order ttOrder, RecycleFactory <UVOrder> orderRecycler, out UVOrder uvOrder) { bool isSuccess = true; try { uvOrder = orderRecycler.Get(); // try and use a recycled order if (ttOrder.BuySell == BuySell.Buy) { uvOrder.OriginalQtyConfirmed = ttOrder.OrderQuantity; uvOrder.ExecutedQty = ttOrder.FillQuantity; uvOrder.Side = UVOrder.BuySide; } else { uvOrder.OriginalQtyConfirmed = ttOrder.OrderQuantity * -1; uvOrder.ExecutedQty = ttOrder.FillQuantity * -1; uvOrder.Side = UVOrder.SellSide; } uvOrder.OrderTIF = ToUVTimeinForce(ttOrder.TimeInForce); uvOrder.IPriceConfirmed = ttOrder.LimitPrice.ToTicks() / ttOrder.InstrumentDetails.SmallestTickIncrement; uvOrder.TickSize = ToUVTickSize(ttOrder.InstrumentDetails); uvOrder.OrderStateConfirmed = ToUVOrderState(ttOrder.TradeState); } catch (Exception) { isSuccess = false; uvOrder = null; } return(isSuccess); }
}//TryCreateNewBook() // // // ************************************************************* // **** GetRequest() **** // ************************************************************* /// <summary> /// This utility method uses the RequestFactory for request to create /// and clear a request for new use. /// </summary> /// <param name="request"></param> /// <returns></returns> protected MarketHubRequest GetRequest(MarketHubRequest.RequestType request) { MarketHubRequest eventArg = m_RequestFactory.Get(); eventArg.Request = request; eventArg.Data.Clear(); return(eventArg); }//GetRequest()
// // // // // // #endregion//Public Methods #region Private Utility Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // /// <summary> /// This method gives the caller a eventarg object to use. It may be new /// or recycled from previous usage. /// </summary> /// <param name="requestType"></param> /// <returns></returns> private DropQueueWriterEventArgs GetRequest(DropQueueWriterRequestType requestType) { DropQueueWriterEventArgs eventArgs = m_Factory.Get(); eventArgs.Clear(); eventArgs.Request = requestType; return(eventArgs); }
// // // #endregion//Private Methods #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // // // // **** GetMessage() **** // protected Message GetMessage(MessageType type, MessageState state, params object[] args) { Message response = m_Messages.Get(); response.MessageType = type; response.State = state; if (args != null) { foreach (object o in args) { if (o is IStringifiable) { response.Data.Add((IStringifiable)o); } else if (o is List <IStringifiable> ) { response.Data.AddRange((List <IStringifiable>)o); } } } return(response); }//GetMessage()
// // #endregion//Constructors #region no Properties // ***************************************************************** // **** Properties **** // ***************************************************************** // // #endregion//Properties #region Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** // // // ************************************************* // **** Try Add() **** // ************************************************* /// <summary> /// Adds a copy of newOrder into this OrderPage. /// </summary> /// <param name="newOrder"></param> /// <returns>true, if successful.</returns> public bool TryAdd(Order newOrder) { bool isSuccess = true; lock (m_Lock) { // Validate newOrder if (m_OrdersById.ContainsKey(newOrder.Id)) // Check that we don't already have entry for this orderId { isSuccess = false; } if (this.OrderSide != newOrder.Side) // Check side of mkt is same as this book. { isSuccess = false; } // Accept order if (isSuccess) { Order myOrder = m_OrderRecycleFactory.Get(); newOrder.CopyTo(myOrder); int iPriceLevel = -this.OrderSign * myOrder.IPricePending; // Puts lowest (highest) priced sell (buy) orders at lowest price level if (!m_OrdersByPendingPrice.ContainsKey(iPriceLevel)) { if (m_RecyclingList.Count > 0) { List <int> recycledList = m_RecyclingList.Dequeue(); recycledList.Clear(); m_OrdersByPendingPrice.Add(iPriceLevel, recycledList); } else { m_OrdersByPendingPrice.Add(iPriceLevel, new List <int>()); } } newOrder.OrderStatePending = OrderState.Submitted; // set pending state to submitted. myOrder.OrderStatePending = OrderState.Submitted; m_OrdersByPendingPrice[iPriceLevel].Add(myOrder.Id); // add to price-list m_OrdersById.Add(myOrder.Id, myOrder); // add to id-list. } } // Exit return(isSuccess); }//Add().
}//CreateOrderBook() // // // ***************************************** // **** TryCreateOrder **** // ***************************************** /// <summary> /// The caller wants to create an order. After creation, the order must be submitted /// to the specific OrderBook to be managed. /// </summary> /// <param name="instrumentName"></param> /// <returns></returns> public virtual bool TryCreateOrder(InstrumentName instrumentName, int tradeSide, int iPrice, int qty, out Order newOrder) { newOrder = null; if ((UV.Lib.Utilities.QTMath.MktSideToMktSign(tradeSide) * qty) < 0) { // this means our signs are incorrect! Log.NewEntry(LogLevel.Error, "Attempt to Create Order For Instrument {0} Failed, Mismatched Sides and Qtys", instrumentName); return(false); } OrderInstrument orderInstrument; if (m_OrderInstruments.TryGetValue(instrumentName, out orderInstrument)) { newOrder = m_OrderRecycleFactory.Get(); newOrder.Instrument = instrumentName; newOrder.Id = Order.GetNextId(); newOrder.Side = tradeSide; newOrder.OriginalQtyPending = qty; newOrder.IPricePending = iPrice; newOrder.TickSize = orderInstrument.Details.TickSize; newOrder.OrderType = OrderType.LimitOrder; } // Exit. return(newOrder != null); }//CreateOrderBook()
}// ProcessSyntheticOrder() // // // // // #endregion//Public Methods #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // // // // // ********************************************************************* // **** DistributeFillsToQuoters() **** // ********************************************************************* /// <summary> /// This distibutes fills in the list to those quotes provided in the list quotes. /// In the process of distrubuting, the fills are removed from the first list, and the /// quote quantities are reduced. PricingEngines that will receive the fills are /// added to the "distributedFills" dictionary. Also, the net position[] array is updated and /// fill messages are added to the query. /// </summary> /// <param name="fillsToDistribute">fills that will be distributed</param> /// <param name="quotes">quotes that will be distributed to.</param> /// <param name="query"></param> /// <param name="distributedFills"></param> /// <param name="position"></param> protected void DistributeFillsToQuoters(ref List <Fill> fillsToDistribute, ref List <Quote> quotes, ref FillsQuery query, ref Dictionary <Quote, List <Fill> > distributedFills, ref int[] position) { DateTime localTime = ParentStrategy.StrategyHub.GetLocalTime(); int quoter = 0; while (quoter < quotes.Count && fillsToDistribute.Count > 0)// loop thru each quoter until fills gone. { Quote quote = quotes[quoter]; Log.AppendEntry(" [{0}]", quote); Fill fill = fillsToDistribute[0]; // consider first fill. if (fill.Qty == 0) { // this should never happen. Log.AppendEntry(" Removed empty fill {0}.", fill); fillsToDistribute.Remove(fill); continue; } int tradeSign = Math.Sign(fill.Qty); int tradeSide = QTMath.MktSignToMktSide(tradeSign); // // Determine how much to allocate to this quoter. // Fill allocatedFill = null; // fill to allocate to quoter. int remainingQty = fill.Qty; // qty to be added back to fills list. if (quote.Side != tradeSide) { // This is an error. Never allocate a fill to a quote for the other side of mkt! Log.AppendEntry(" wrong-side {0}.", QTMath.MktSideToLongString(quote.Side)); } //else if (forceFillAbsQty != 0) //{ // Here, we are forcing fills to be allocated even to quoters who have qty = 0. // remainingQty = tradeSign * Math.Max(0, Math.Abs(fill.Qty) - forceFillAbsQty); // force up to full qty. //} else if (quote.Reason != QuoteReason.Entry && quote.Qty != 0) { // Normal non-entry request. Try to fill this quote as much as possible completely. remainingQty = tradeSign * Math.Max(0, (fill.Qty - quote.Qty) * tradeSign); Log.AppendEntry(" taking {0}, remaining {1},", quote.Qty, remainingQty); } else if (quote.Reason == QuoteReason.Entry && quote.Qty != 0) { // Normal entry request. Don't allow entries to violet position limit! int allowedEntryQty = tradeSign * Math.Max(0, m_MaxPosition - Math.Abs(position[tradeSide])); //remainingQty = tradeSign * Math.Max(0, (allowedEntryQty - quote.Qty) * tradeSign); int qtyWeCanTake = Math.Min(Math.Abs(allowedEntryQty), Math.Abs(quote.Qty)) * tradeSign; remainingQty = tradeSign * Math.Max(0, (fill.Qty - qtyWeCanTake) * tradeSign); Log.AppendEntry(" taking {0}, remaining {1},", qtyWeCanTake, remainingQty); } // // Allocate the fill // if (remainingQty == 0) { // This fill is completely consumed by the quoter. fillsToDistribute.Remove(fill); // remove fill from list. allocatedFill = fill; } else if (remainingQty == fill.Qty) { // No qty was consumed at all!?! allocatedFill = null; } else { // This fill is only partially consumed. fillsToDistribute.Remove(fill); // remove original fill from list. Fill remainingFill = Fill.Create(fill); remainingFill.Qty = remainingQty; fillsToDistribute.Insert(0, remainingFill); // First, replace the unused portion back onto list. allocatedFill = Fill.Create(fill); allocatedFill.Qty = fill.Qty - remainingQty; // Allocate the consumed portion. } if (allocatedFill != null && allocatedFill.Qty != 0) { Log.AppendEntry(" filled {1}.", quote.PricingEngine.EngineName, allocatedFill); List <Fill> fills; if (distributedFills.TryGetValue(quote, out fills) == false) { // No fills had be distributed to this quote previously. fills = m_FillListRecycling.Get(); fills.Clear(); distributedFills.Add(quote, fills); } fills.Add(allocatedFill); position[quote.Side] += allocatedFill.Qty; quote.Qty = tradeSign * Math.Max(0, tradeSign * (quote.Qty - allocatedFill.Qty)); string msgStr = quote.FillAttribution(); query.AddItemToWrite(ParentStrategy.SqlId, -1, localTime, m_Services.User, quote.PricingEngine.EngineName, msgStr, allocatedFill.Qty, allocatedFill.Price); } else { Log.AppendEntry(" skipped."); } quoter++; // otherwise move to next quoter }// next quoter }//DistributeFillsToQuoters()
// #endregion//Properties #region Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** // // // // // ***************************************** // **** Quote() **** // ***************************************** /// <summary> /// Method called by PricingEngine to request a change in quoting /// for a particular, side, price and qty. /// Sending a quote with only tradeSide will remove quote from price list. /// /// Generally,s pricingEngines that don't want to quote a qty should still quote their target price. /// This allows overfills to be distributed to the pricing engine that can best use it. /// </summary> /// <param name="pricingEngine"></param> /// <param name="tradeSide"></param> /// <param name="price"></param> /// <param name="qty">signed integer</param> /// <param name="quoteReason">user chosen quote reason.</param> public virtual void Quote(PricingEngine pricingEngine, int tradeSide, double price = double.NaN, int qty = 0, QuoteReason quoteReason = QuoteReason.None) { // Compute quote variables. int tradeSign = UV.Lib.Utilities.QTMath.MktSideToMktSign(tradeSide); bool isPriceListed = (double.IsNaN(price) == false && double.IsInfinity(price) == false); int iPriceNew = 0; if (isPriceListed) { iPriceNew = tradeSign * (int)System.Math.Floor(tradeSign * price / m_QuoteTickSize); // integerized price! } // Get this engines quote object from our master list. Quote quote = null; if (!m_Quotes[tradeSide].TryGetValue(pricingEngine, out quote)) // we store quotes for each side of mkt. { // Apparently this is the first quote call from this engine. quote = new Quote(); m_Quotes[tradeSide].Add(pricingEngine, quote); quote.PricingEngine = pricingEngine; // set constant vars. quote.Side = tradeSide; if (m_FirstPriceEngine == null) { m_FirstPriceEngine = pricingEngine; } } bool isPriceListedPrev = quote.IsPriceListed; int iPricePrev = quote.IPrice; int qtyPrev = quote.Qty; // Test validity of qty value if (tradeSign * qty < 0) { qty = 0; } // Determine if this quote has changed. // isPriceChanged = should contain all reasons the quotes location in book might change. bool isPriceChanged = (isPriceListed != isPriceListedPrev) || (iPriceNew != iPricePrev) || (quote.Reason != quoteReason); bool doesThisChangeRequireQuoteUpdating = (qty != qtyPrev) || (isPriceChanged); m_IsQuoteSideUpdateRequired[tradeSide] = m_IsQuoteSideUpdateRequired[tradeSide] || doesThisChangeRequireQuoteUpdating; // true if ANY PricingEngines quotes have changed since last update. // If the price has changed, and it should have been in our price list, // we need to remove it from its old location in price list. if (isPriceChanged && isPriceListedPrev) { RemoveQuoteFromListing(tradeSide, quote); } // Update the quote with the new price/qty quote.IsPriceListed = isPriceListed; quote.IPrice = iPriceNew; quote.RawPrice = price; quote.Qty = qty; quote.Reason = quoteReason; quote.FillAttributeStr.Clear(); quote.FillAttributeStr.Append(pricingEngine.GetFillAttributeString()); // Now add quote to new price location, if necessary. if (isPriceChanged && isPriceListed) { // We need to add it since its price has changed, and it price is valid now. List <Quote> quoteList = null; int priceKey = -tradeSign * iPriceNew; if (!m_QuotesByPrice[tradeSide].TryGetValue(priceKey, out quoteList)) // get all quotes at this price { // There are no previous quotes at this price, create new list. quoteList = m_QuoteListRecycling.Get(); quoteList.Clear(); m_QuotesByPrice[tradeSide].Add(priceKey, quoteList); } // Add quote to list at this price. if (quote.Reason == QuoteReason.Exit || quote.Reason == QuoteReason.Stop) { quoteList.Insert(0, quote); // put these in the front of the list. } else { quoteList.Add(quote); // add these to end of list. } } }//Quote()
// // // // // // ********************************************************* // **** CreateBar() **** // ********************************************************* /// <summary> /// This is called each time a bar must be created from a snapshot of the current market. /// New bars are pushed into the BarEventArg.BarList queue and handed to the QueryBuilder /// for query creation and writing. /// Called by internal hub thread. /// </summary> private void CreateBar(DateTime barTime) { //Log.NewEntry(LogLevel.Minor, "DataHub: CreateBar - Bar Creation Started"); BarEventArgs eArg = m_BarEventFactory.Get(); // new version GetBarEventArg(); eArg.unixTime = (int)Utilities.QTMath.DateTimeToEpoch(barTime.ToUniversalTime()); //round this to the floor. Log.NewEntry(LogLevel.Minor, "CreateBar: Attempting to create bar for timestamp {0} - miliseconds = {1}", eArg.unixTime, barTime.Millisecond); if ((eArg.unixTime - m_LastBarTimeStamp) > 1) { // we have stepped through time in some interval greater than a second....create email alert for debugging purposes DatabaseWriterEventArgs emailEvent = new DatabaseWriterEventArgs(); emailEvent.Request = DatabaseWriterRequests.SendEmail; emailEvent.QueryBase.AppendFormat("Data Hub Missed Timestamp. Current timestamp={0} and last timestamp={1} difference is {2} seconds", eArg.unixTime, m_LastBarTimeStamp, eArg.unixTime - m_LastBarTimeStamp); m_DatabaseWriterHub.HubEventEnqueue(emailEvent); } m_LastBarTimeStamp = eArg.unixTime; // // Get markets now. // UV.Lib.BookHubs.Book aBook; if (m_Market.TryEnterReadBook(out aBook)) { foreach (KeyValuePair <int, UV.Lib.BookHubs.Market> aBookMarket in aBook.Instruments) { int mySQLID = -1; if (m_InstrumentsRequested.Contains(aBookMarket.Value.Name) && m_InstrToMySQLID.TryGetValue(aBookMarket.Value.Name, out mySQLID)) { // we would like to record data for this instrument if (aBookMarket.Value.Qty[(int)UV.Lib.Utilities.QTMath.BidSide][0] == 0 || aBookMarket.Value.Qty[(int)UV.Lib.Utilities.QTMath.AskSide][0] == 0) {// we have bad data this can happen sometimes in between sessions.. //Log.NewEntry(LogLevel.Major, "CreateBar: Bid Or Ask qty for {0} is equal To zero, skipping bar", aBookMarket.Value.Name); continue; } Bar aBar = m_BarFactory.Get(); // grab a bar! aBar.mysqlID = mySQLID; // set instrument id aBar.bidPrice = aBookMarket.Value.Price[(int)UV.Lib.Utilities.QTMath.BidSide][0]; // set best bid aBar.askPrice = aBookMarket.Value.Price[(int)UV.Lib.Utilities.QTMath.AskSide][0]; // set best ask aBar.bidQty = aBookMarket.Value.Qty[(int)UV.Lib.Utilities.QTMath.BidSide][0]; // set best bidqty aBar.askQty = aBookMarket.Value.Qty[(int)UV.Lib.Utilities.QTMath.AskSide][0]; // set best askqty aBar.lastTradePrice = aBookMarket.Value.LastPrice; aBar.sessionVolume = aBookMarket.Value.Volume[(int)UV.Lib.Utilities.QTMath.LastSide]; aBar.longVolume = aBookMarket.Value.Volume[(int)UV.Lib.Utilities.QTMath.BidSide]; aBar.shortVolume = aBookMarket.Value.Volume[(int)UV.Lib.Utilities.QTMath.AskSide]; aBar.totalVolume = aBar.longVolume + aBar.shortVolume + aBookMarket.Value.Volume[(int)UV.Lib.Utilities.QTMath.UnknownSide]; aBar.sessionCode = Convert.ToInt32(aBookMarket.Value.IsMarketGood); // flag for trading ==1 or not trading==0 eArg.BarList.Enqueue(aBar); } } m_Market.ExitReadBook(aBook); } else { // something went wrong here! Log.NewEntry(LogLevel.Error, " ********* CreateBar: FAILED TO OBTAIN READ FOR BOOK! *********"); } if (eArg.BarList.Count > 0 && !m_IsDebugMode) // do not write to db in debug mode. { m_QueryBuilderHub.HubEventEnqueue(eArg); } }//CreateBar().