// #endregion//Constructors #region no Properties // ***************************************************************** // **** Properties **** // ***************************************************************** // // #endregion//Properties #region Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** // // /// <summary> /// This is the update method for this specialized stop book. /// The currently volatility must be passed in each time it is called. /// This is used for calculating the initial stop price from which we evolve towards /// the current price. /// </summary> /// <param name="currentVolatility"></param> public override void Update(double currentVolatility) { // do not call base class - this handles all needed functionality. base.StopBase = currentVolatility; if (m_StopSign != 0) { // We are stopped. // // Check blocking timeout. // if (m_IsBlockingTimeOut) { m_BlockingTimeOutRemaining -= 1; // decrease countdown to timeout. if (m_BlockingTimeOutRemaining <= 0) { if (IsLogWrite) { Log.NewEntry(LogLevel.Major, "StopRule: Blocking timed out. StopLoss for {1} returning to normal trading on {0} side." , QTMath.MktSignToString(m_StopSign), m_Name); } m_StopSign = 0; // return to normal trading. this.IsUpdateRequired = true; base.BroadcastParameters(); } } } }
// // ************************************************************* // **** Quote() ***** // ************************************************************* // /// <summary> /// This sets the inner market price and qty for the trade. /// For now it assumes the strategy has only one OrderInstrument, which is always /// the case when we have an "ExecutionStrategy" deployed; eg, an autospreader. /// /// qty must be signed, negative for sell qty's /// /// This will validate all prices prior to setting them. /// </summary> /// <param name="tradeSide"></param> /// <param name="price"></param> /// <param name="qty">Signed qty</param> public void Quote(int tradeSide, double price, int qty) { if (qty != 0 && tradeSide != QTMath.MktSignToMktSide(qty)) { // mismatch qty and sides Log.NewEntry(LogLevel.Warning, "Quote: tradeSide and side implied by qty sign do not match, rejecting quote update"); return; } if (!m_IsLegSetUpComplete) { // we cant't even validate prices yet. Log.NewEntry(LogLevel.Major, "Quote: Market has not been intialized yet. Order's will not be sent until market is intialized"); return; } price = price / m_PriceLeg.PriceMultiplier; // convert from strat price to instrument price qty = (int)(qty * Math.Abs(m_PriceLeg.Weight)); // convert from strat qty to instrument qty if (!QTMath.IsPriceEqual(m_StrategyWorkingPrice[tradeSide], price, m_InstrumentDetails.TickSize) || m_TotalDesiredQty[tradeSide] != qty) { // price is different. if (m_RiskManager.ValidatePrices(price, tradeSide, m_PriceLeg.InstrumentName) || qty == 0) { // our prices are valid so we can save variables m_StrategyWorkingPrice[tradeSide] = price; m_TotalDesiredQty[tradeSide] = qty; Log.NewEntry(LogLevel.Major, "Quote:{4} Working {1} {0} @ {2} in {3}", // while this may be silly to log first, it is here for readability m_OpenSyntheticOrders[tradeSide].TradeReason, // 0 qty, // 1 price, // 2 m_InstrumentDetails.InstrumentName, //3 m_ExecutionContainer.EngineContainerID); //4 Quote(); // go ahead an update our orders } } }//Quote()
}// CopyTo() // // // public override string ToString() { StringBuilder msg = new StringBuilder(); msg.AppendFormat("#{0}: {1} {2} @ {3} ExecQty = {4}", this.OrderId, QTMath.MktSideToString(this.Side), this.Qty, this.Price, this.ExecutedQty); return(msg.ToString()); }
// ***************************************************************** // **** 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); } }
// ***************************************************************** // **** Private Methods **** // ***************************************************************** // // // //********************************************************** //**** Update Market Prices() **** //********************************************************** /// <summary> /// Called whenever the mkt changes. Updates m_Prices[][] and m_Qtys[][]. /// returns True if inside market has changed prices /// </summary> /// TODO: THIS NEEDS TO BE FIXED private bool UpdateMarketPrices(UV.Lib.BookHubs.Market market, List <int>[] MarketDepthChangedList) { bool isChanged = false; for (int side = 0; side < 2; ++side) { if (MarketDepthChangedList[side].Count == 0 || MarketDepthChangedList[side][0] > m_LeanableDepth[side] || MarketDepthChangedList[side][0] > m_OrderBookDepthMax) { // if no change on this side || top change is past our leanable depth || top change is past the depth we care about //nothing to do here! } else { // this is a price or qty change that we need to consider if (MarketDepthChangedList[side][0] == 0 && !QTMath.IsPriceEqual(m_Market.Price[side][MarketDepthChangedList[side][0]], market.Price[side][0], InstrumentDetails.TickSize)) { // if this is a top of book price change! isChanged = true; } foreach (int level in MarketDepthChangedList[side]) { // Set new internal market from the depth changed back if (level < m_OrderBookDepthMax) { // we care about updates at this level m_Market.Price[side][level] = market.Price[side][level]; m_Market.Qty[side][level] = market.Qty[side][level]; } } } } return(isChanged); }
// // // // #endregion//Public Methods #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // // ***************************************************************** // **** RunHedgeLogic() **** // ***************************************************************** /// <summary> /// Called after Instrument_TickChanged to update hedge prices of /// orders under active management by the HedgeRuleManager. /// </summary> private void RunHedgeLogic() { double endingPrice; // price we end up with after our rules are applied. double userDefinedWorstPrice; // price the user has defined as the worst possible price if (m_OrderBook.Count() == 0) { // we have no orders to manage m_isManagerActive = false; m_QuoterLeg.m_Market.MarketChanged -= new EventHandler(Market_MarketChanged); // subscribe to orders state changes for this leg. m_Log.NewEntry(LogLevel.Minor, "HedgeRuleManager : {0} has no orders to manage, Unsunbscribing from market", m_QuoterLeg.InstrumentDetails.InstrumentName); return; } else // we have orders and need to manage them { for (int mktSide = 0; mktSide < 2; ++mktSide) { // each side of market if (m_OrderBook.Count(mktSide) != 0) { //we have orders on this side of the market m_ActiveOrders.Clear(); // these can not be recycled since the hedger could still be holding an order. m_OrderBook.GetOrdersBySide(mktSide, ref m_ActiveOrders); // populate all orders for this side of the market foreach (int id in m_ActiveOrders.Keys) { if (m_ActiveOrders.TryGetValue(id, out tmpOrder)) { // we can find the order int orderSign = QTMath.MktSideToMktSign(tmpOrder.Side); if (!m_OrderIdToUserDefinedWorstPrice.ContainsKey(id)) { userDefinedWorstPrice = tmpOrder.PricePending + (orderSign * tmpOrder.TickSize * m_MaxPayUpTicks); m_OrderIdToUserDefinedWorstPrice.Add(id, userDefinedWorstPrice); } else { userDefinedWorstPrice = m_OrderIdToUserDefinedWorstPrice[tmpOrder.Id]; } endingPrice = tmpOrder.PricePending; // assume we have no change to start. foreach (IHedgeRule rule in m_HedgeRules) { // apply our hedge rules bool isContinue = rule.ApplyHedgeRule(endingPrice, tmpOrder.Side, out endingPrice); // hand the function the ending price and let it update it if (!isContinue) // we want to execute our rule immediately { break; } } if ((endingPrice * orderSign) > userDefinedWorstPrice * orderSign) // if our ending price is worse than worse price, reassing it. { endingPrice = userDefinedWorstPrice; } if (!QTMath.IsPriceEqual(endingPrice, tmpOrder.PricePending, tmpOrder.TickSize)) { // our price has been changed m_Hedger.UpdateHedgerOrderPrice(tmpOrder, endingPrice); // call the hedger to change the order } } } // end foreach } // end if } // end mktside } // end else } // RunHedgeLogic()
}// AddBook() // // // ************************************************************* // **** Quote() **** // ************************************************************* // // /// <summary> /// This sets the inner market price and qty for the trade. /// For now it assumes the strategy has only one OrderInstrument, which is always /// the case when we have an "ExecutionStrategy" deployed; eg, an autospreader. /// /// qty must be signed, negative for sell qty's /// /// This will validate all prices prior to setting them. /// </summary> /// <param name="tradeSide"></param> /// <param name="price"></param> /// <param name="qty">Signed qty</param> /// <param name="aBook"></param> public void Quote(int tradeSide, double price, int qty, UV.Lib.BookHubs.Book aBook) { if (qty != 0 && tradeSide != QTMath.MktSignToMktSide(qty)) { // mismatch qty and sides Log.NewEntry(LogLevel.Warning, "Quote: tradeSide and side implied by qty sign do not match, rejecting quote update"); return; } if (!m_IsMarketReady) { // we cant't even validate prices yet. Log.NewEntry(LogLevel.Major, "Quote: Market has not been intialized yet. Order's will not be sent until market is intialized"); return; } if (!QTMath.IsPriceEqual(m_StrategyWorkingPrice[tradeSide], price, m_InstrumentDetails.TickSize)) { // price is different. if (m_RiskManager.ValidatePrices(price, tradeSide, m_Instrument, aBook)) { // our prices are valid so we can save variables m_StrategyWorkingPrice[tradeSide] = price; m_TotalDesiredQty[tradeSide] = qty; } } else { // price hasn't changed so if it was already set m_TotalDesiredQty[tradeSide] = qty; } Quote(); // go ahead an update our orders }//Quote()
}//RemoveQuoteFromListing() // // // // ************************************************* // **** ValidateQuoteTables() **** // ************************************************* /// <summary> /// /// </summary> protected void ValidateQuoteTables() { if (m_QuoteTickSize == ParentStrategy.m_OrderEngine.QuoteTickSize) { return; } // Accept new QuoteTickSize Log.NewEntry(LogLevel.Minor, "QuoteTickSize change detected for {0} {1} -> {2}. Rebuilding tables.", ParentStrategy.Name, m_QuoteTickSize, ParentStrategy.m_OrderEngine.QuoteTickSize); m_QuoteTickSize = ParentStrategy.m_OrderEngine.QuoteTickSize; // Rebuild the pricing Tables. for (int tradeSide = 0; tradeSide < 2; tradeSide++) { List <Quote> masterQuoteList = m_QuoteListRecycling.Get(); // get a list for all quotes! masterQuoteList.Clear(); // Empty all quotes into our masterList. List <int> keyList = new List <int>(); keyList.AddRange(m_QuotesByPrice[tradeSide].Keys); foreach (int priceKey in keyList) { List <Quote> quoteList; if (m_QuotesByPrice[tradeSide].TryGetValue(priceKey, out quoteList)) { foreach (Quote quote in quoteList) { if (!masterQuoteList.Contains(quote)) { masterQuoteList.Add(quote); } } m_QuotesByPrice[tradeSide].Remove(priceKey); quoteList.Clear(); m_QuoteListRecycling.Recycle(quoteList); } }// next priceKey // Resort all the quotes in the masterList. int tradeSign = QTMath.MktSideToMktSign(tradeSide); foreach (Quote quote in masterQuoteList) { quote.IPrice = tradeSign * (int)System.Math.Floor(tradeSign * quote.RawPrice / m_QuoteTickSize); List <Quote> quoteList; if (!m_QuotesByPrice[tradeSide].TryGetValue(quote.IPrice, out quoteList)) { quoteList = m_QuoteListRecycling.Get(); quoteList.Clear(); m_QuotesByPrice[tradeSide].Add(quote.IPrice, quoteList); } if (quote.Reason == QuoteReason.Stop || quote.Reason == QuoteReason.Exit) { quoteList.Insert(0, quote); } else { quoteList.Add(quote); } } } }// ValidateQuoteTables()
}//Quote() // // // // // #endregion//private Methods #region IOrderEngine Implementation // // // ************************************************************* // **** Filled() **** // ************************************************************* // /// <summary> /// Called by the StrategyHub when it received a fillEventArg from an OrderBook /// that this engine subscribed to. /// </summary> /// <param name="fillEventArgs"></param> /// <returns>null is no fill is generated.</returns> public Fill Filled(FillEventArgs fillEventArgs) { m_FillBook.TryAdd(fillEventArgs.Fill); int fillSide = QTMath.MktSignToMktSide(fillEventArgs.Fill.Qty); m_StrategyPosition[fillSide] += fillEventArgs.Fill.Qty; fillEventArgs.Fill.Price = fillEventArgs.Fill.Price * m_PriceLeg.PriceMultiplier; return(fillEventArgs.Fill); // pass fill through to other engines. }//Filled()
}//Quote() // // // // // #endregion//private Methods #region IOrderEngine Implementation // // // ************************************************************* // **** Filled() **** // ************************************************************* // /// <summary> /// Called by the StrategyHub when it received a fillEventArg from an OrderBook /// that this engine subscribed to. /// </summary> /// <param name="fillEventArgs"></param> /// <returns>null is no fill is generated.</returns> public Fill Filled(FillEventArgs fillEventArgs) { m_FillBook.TryAdd(fillEventArgs.Fill); int fillSide = QTMath.MktSignToMktSide(fillEventArgs.Fill.Qty); m_StrategyPosition[fillSide] += fillEventArgs.Fill.Qty; m_RiskManager.Filled(fillEventArgs); // make sure our risk manager knows return(fillEventArgs.Fill); // pass fill through to other engines. }//Filled()
// // ****************************************** // **** Get IPrice By Rank() **** // ****************************************** /// <summary> /// For a given "rank" of orders, try and get the corresponding iPrice /// for those orders. /// </summary> /// <param name="side"></param> /// <param name="rank"></param> /// <param name="iPrice"></param> /// <returns></returns> public bool TryGetIPriceByRank(int side, int rank, out int iPrice) { if (m_LiveOrders[side].TryGetIPriceByRank(rank, out iPrice)) { iPrice = -QTMath.MktSideToMktSign(side) * iPrice; return(true); } return(false); }
// // ************************************************* // **** Simulate Fills() **** // ************************************************* private void SimulateFills(List <int> marketInstrList) { Book aBook; if (m_Market.TryEnterReadBook(out aBook)) { foreach (int instrId in marketInstrList) { Market market = null; if (!aBook.Instruments.TryGetValue(instrId, out market)) { Log.NewEntry(LogLevel.Minor, "SimulateFills: Failed to obtain market for mkt instr ID {0}", instrId); continue; } InstrumentName instrName = market.Name; List <OrderBook> orderBooks = null; if (m_SimOrderBooks.TryGetValue(instrName, out orderBooks)) { double tickSize = m_ExecContainer.m_OrderInstruments[instrName].Details.TickSize; foreach (OrderBook orderBook in orderBooks) { // // Try to fill this order book. // for (int orderSide = 0; orderSide < 2; orderSide++) { int orderSign = QTMath.MktSideToMktSign(orderSide); int otherSide = (orderSide + 1) % 2; int ourMarketPrice = (int)Math.Round(market.Price[orderSide][0] / tickSize); int oppMarketPrice = (int)Math.Round(market.Price[otherSide][0] / tickSize); int orderRank = 0; // only fill top for now int orderPrice; if (orderBook.TryGetIPriceByRank(orderSide, orderRank, out orderPrice)) { if ((oppMarketPrice - orderPrice) * orderSign <= 0) { // filled by crossing market. m_OrdersWorkspace.Clear(); orderBook.GetOrdersByRank(orderSide, orderRank, ref m_OrdersWorkspace); FillTheseOrders(ref m_OrdersWorkspace); } else if ((ourMarketPrice - orderPrice) * orderSign > 0) { // We are below other orders in market } else { // We are in the middle of the market (or on the top of book). //m_OrdersWorkspace.Clear(); //orderBook.GetOrdersByRank(orderSide, orderRank, ref m_OrdersWorkspace); //FillTheseOrders( ref m_OrdersWorkspace ); } } } } } } m_Market.ExitReadBook(aBook); } // if MarketBook obtained. } // SimulateFills()
// // #endregion//Constructors #region no Properties // ***************************************************************** // **** Properties **** // ***************************************************************** // // #endregion//Properties #region no Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** /// <summary> /// This overloading creates an true implied market involving multiple instruments. /// </summary> /// public void SetMarket(Book marketBook, List <PriceLeg> legs) { // // Inner market // for (int spreadSide = 0; spreadSide < 2; ++spreadSide) // spread side, bid/ask { // bid = where we can sell the spread, ask = where we can buy it int spreadSign = QTMath.MktSideToMktSign(spreadSide); // bidside = +1, here! // // *** Inner market **** // int spreadLevel = 0; // inner market for spread int leg = 0; // First leg Market instr = marketBook.Instruments[legs[leg].MarketID]; int legSide = QTMath.MktSignToMktSide(legs[leg].Weight * spreadSign); // pos --> bidSide, neg --> askSide double spreadPrice = legs[leg].PriceMultiplier * instr.Price[legSide][spreadLevel]; int spreadQty = Convert.ToInt32(Math.Floor(instr.Qty[legSide][spreadLevel] / Math.Abs(legs[leg].Weight))); leg++; // Remaining legs > 0 while (leg < legs.Count) { instr = marketBook.Instruments[legs[leg].MarketID]; legSide = QTMath.MktSignToMktSide(legs[leg].Weight * spreadSign); spreadPrice += legs[leg].PriceMultiplier * instr.Price[legSide][spreadLevel]; spreadQty = Math.Min(spreadQty, Convert.ToInt32(Math.Floor(instr.Qty[legSide][spreadLevel] / Math.Abs(legs[leg].Weight)))); //spreadQtyImp = Math.Min(spreadQtyImp, Convert.ToInt32(Math.Floor(instr.QtyImp[legSide][spreadLevel] / Math.Abs(legs[leg].Weight)))); leg++; }//next leg // Set inner-market prices. this.Price[spreadSide][spreadLevel] = spreadPrice; this.Qty[spreadSide][spreadLevel] = Math.Max(1, Math.Abs(spreadQty)) * Math.Sign(spreadQty); // todo: this could be zero. //this.QtyImp[spreadSide][spreadLevel] = spreadQtyImp; //this.QtyImp[spreadSide][spreadLevel] = Math.Max(1, Math.Abs(spreadQtyImp)) * Math.Sign(spreadQtyImp); //this.QtyImp[spreadSide][spreadLevel] = Math.Max(1, Math.Abs(spreadQtyImp));// *spreadSign; // // **** Outer market **** // if (legs.Count == 1) { spreadLevel++; leg = 0; // First leg instr = marketBook.Instruments[legs[leg].MarketID]; while (spreadLevel < instr.DeepestLevelKnown) { this.Price[spreadSide][spreadLevel] = instr.Price[spreadSide][spreadLevel] * legs[leg].PriceMultiplier; this.Qty[spreadSide][spreadLevel] = instr.Qty[spreadSide][spreadLevel]; // todo: fix. spreadLevel++; }//next spreadLevel } if (spreadLevel > this.DeepestLevelKnown) { DeepestLevelKnown = spreadLevel; } } //next side } //SetMarket().
// protected string GetQueryForData(DatabaseInfo dataBase) { TableInfo.InstrumentsTableInfo instrumentTable = dataBase.Instruments; TableInfo.ExchangesTableInfo exchangeTable = dataBase.Exchanges; TableInfo.BarsTableInfo barsTable = dataBase.Bars; // Create a instrument expiry code. string expiryCode; if (!UV.Lib.Utilities.QTMath.TryConvertMonthYearToCodeY(InstrumentName.SeriesName, out expiryCode)) { return(string.Empty); } // Create sub selection string to get instr ID from InstrumentName. StringBuilder subQuery = new StringBuilder(); int instrSqlId = m_InstrumentQuery.Results[0].InstrumentId; subQuery.AppendFormat("{0}", instrSqlId); // Create the final query. string desiredFields = "*"; StringBuilder query = new StringBuilder(); query.AppendFormat("SELECT {0} FROM {1}", desiredFields, barsTable.TableNameFull); query.AppendFormat(" WHERE {0} = ({1})", barsTable.InstrumentID, subQuery); if (this.StartDate > DateTime.MinValue) { int timestamp = (int)QTMath.DateTimeToEpoch(this.StartDate); string s = timestamp.ToString(); query.AppendFormat(" AND {0} >= {1}", barsTable.TimeStamp, s); } if (this.EndDate < DateTime.MaxValue) { int timestamp = (int)QTMath.DateTimeToEpoch(this.EndDate); string s = timestamp.ToString(); query.AppendFormat(" AND {0} <= {1}", barsTable.TimeStamp, s); } query.AppendFormat(" AND {0} = 1", barsTable.SessionCode); // currently we only want sessionCode = 1 for when products are trading query.AppendFormat(" ORDER BY {0}", barsTable.TimeStamp); if (this.StartDate == DateTime.MinValue) { // since our date time is unnasigned, lets reverse the order of our timestamps for this query so we get the proper number of rows query.AppendFormat(" DESC"); m_IsReverseOrder = true; } if (this.MaxRows > 0) { query.AppendFormat(" LIMIT {0}", this.MaxRows); } query.Append(";"); // Exit return(query.ToString()); }// GetQuery();
}//Quote() // // // // // ***************************************** // **** 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 virtual 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(); 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 rawIPrice = 0; int totalQty = 0; foreach (KeyValuePair <int, List <Quote> > kv in m_QuotesByPrice[tradeSide]) { foreach (Quote quote in kv.Value) { totalQty = totalQty + quote.Qty; } rawIPrice = -tradeSign * kv.Key; // price at this level if (totalQty != 0) { break; } } // // Send quotes. // int tradeId; int quoteQtyAllowed = Math.Max(0, m_MaxPosition - tradeSign * m_Position[tradeSide]);//always positive totalQty = tradeSign * Math.Min(quoteQtyAllowed, Math.Abs(totalQty)); int orderQty = totalQty + m_BuySellQty[tradeSide]; tradeId = ParentStrategy.m_OrderEngine.Quote(tradeSide, rawIPrice * m_QuoteTickSize, orderQty, string.Empty); } }//UpdateQuotes().
}//SetupBegin(). // // ************************************************************* // **** SetupComplete() **** // ************************************************************* public override void SetupComplete() { base.SetupComplete(); // if we create order books here we know risk is already listening for the events. if (!QTMath.IsNearEqual(m_PriceLeg.Weight, 1, .01)) { Log.NewEntry(LogLevel.Error, "SingleLegExecutor: Does not have functionality for legs with weight greater than 1 implemented yet"); } m_OrderBook = m_ExecutionListener.CreateOrderBook(m_PriceLeg.InstrumentName, m_DefaultAccount); m_OrderBook.OrderFilled += new EventHandler(OrderBook_OrderFilled); m_OrderBook.OrderStateChanged += new EventHandler(OrderBook_OrderStateChanged); }
// ***************************************************************** // **** Public Methods **** // ***************************************************************** // // // // // ********************************************************* // **** Market_MarketChanged **** // ********************************************************* /// <summary> /// Called on every tick from the market. Attempt was made to optimize this /// by filtering out times when we don't have a position to manage. Additionally /// all calls that are not top of book are ignored. Checks are created to watch for when market /// quantity crosses thresholds as this is what triggers the scratch event. /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> public void Market_MarketChanged(object sender, EventArgs eventArgs) { if (m_IPriceToPosition.Count == 0) { // we are not actively managing a position, we dont care about market updates return; } if (m_Market.IsLastTickBestPriceChange) { // this update was a best price change! we need to check everything to see if we need to scratch m_IPriceWorkSpaceList.Clear(); // use this list since TryScratchPosition removes keys from m_IPricePosition, we cannot iterate on it! foreach (KeyValuePair <int, int> kvPair in m_IPriceToPosition) { // check each price we have a position at, if we need to scratch, then add it to our list of prices to scratch if (IsScratchNeededAtIPrice(kvPair.Key)) { m_IPriceWorkSpaceList.Add(kvPair.Key); } } for (int i = 0; i < m_IPriceWorkSpaceList.Count; i++) { // for every price we need to scratch TryScratchPosition(m_IPriceWorkSpaceList[i], true); } // update our state variables with new market quantities! for (int side = 0; side < 2; ++side) { m_WasInsideMarketQtyAboveThreshold[side] = m_Market.Qty[side][0] > m_ScratchThreshold; } } else { // this could be a top of book qty change, lets check bool isMarketQtyOverThreshold; for (int side = 0; side < 2; ++side) { if (m_Market.BestDepthUpdated[side] == 0) { // the last update on this side was top of market! isMarketQtyOverThreshold = m_Market.Qty[side][0] > m_ScratchThreshold; if ((m_IsActive & !isMarketQtyOverThreshold & m_WasInsideMarketQtyAboveThreshold[side]) | (!m_IsActive & isMarketQtyOverThreshold & !m_WasInsideMarketQtyAboveThreshold[side])) { // we are actively scratching, the market is now under our threshold and it was previously above! // OR we are passively scratching the market is now over our threshold and it was previously below! int iPrice = QTMath.RoundToSafeIPrice(m_Market.Price[side][0], side, m_InstrumentDetails.TickSize); if (IsScratchNeededAtIPrice(iPrice)) { // this call will complete the checks and if returns true we actually need to scratch TryScratchPosition(iPrice, true); } } m_WasInsideMarketQtyAboveThreshold[side] = isMarketQtyOverThreshold; // save new threshold state } } } }
// // // // #endregion//Public Methods #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // /// <summary> /// Override to allow us to set our "Theta" or multiplier of the volatility for our /// initial stop /// </summary> /// <param name="signedQty"></param> /// <param name="fillPrice"></param> /// <param name="preFillPosition"></param> protected override void UpdateNewFill(int signedQty, double fillPrice, int preFillPosition) { // // Initial stop price: // StopPrice[0] = FillPrice - StopSign*Theta*Vol[0] // base.StopBaseMultiplier = m_TrailingStopTheta[QTMath.MktSignToMktSide(signedQty)]; // set our multiplier for this side to theta for this fill! base.UpdateNewFill(signedQty, fillPrice, preFillPosition); if (Position != 0 && m_GraphEngine != null) { m_GraphEngine.AddPoint(m_GraphId, m_StopCurvveName, NearestStopPrice); m_PlotUpdateCount = 0; } }
}// ProcessUnwantedFills() // // // ************************************************* // **** RemoveQuoteFromListing() **** // ************************************************* protected void RemoveQuoteFromListing(int tradeSide, Quote quoteToRemove) { int tradeSign = QTMath.MktSideToMktSign(tradeSide); int priceKey = -tradeSign * quoteToRemove.IPrice; List <Quote> quoteList; if (m_QuotesByPrice[tradeSide].TryGetValue(priceKey, out quoteList) && quoteList.Contains(quoteToRemove)) { quoteList.Remove(quoteToRemove); quoteToRemove.IsPriceListed = false; if (quoteList.Count == 0) { // There are no remaining quotes at this price level, so clear out the list. m_QuotesByPrice[tradeSide].Remove(priceKey); m_QuoteListRecycling.Recycle(quoteList); } } else { ParentStrategy.StrategyHub.Log.BeginEntry(LogLevel.Major, "Quote: {0} could not find quote {1}. Searching for it...", ParentStrategy.Name, quoteToRemove); bool isFound = false; foreach (KeyValuePair <int, List <Quote> > kv in m_QuotesByPrice[tradeSide]) { if (kv.Value.Contains(quoteToRemove)) { isFound = true; Log.AppendEntry(" Found! Removed from list at priceKey={0} not QuotePriceKey={1}.", priceKey, quoteToRemove.IPrice); Log.EndEntry(); priceKey = kv.Key; kv.Value.Remove(quoteToRemove); quoteToRemove.IsPriceListed = false; break; } } if (isFound) { if (m_QuotesByPrice[tradeSide].TryGetValue(priceKey, out quoteList) && quoteList.Count == 0) { m_QuotesByPrice[tradeSide].Remove(priceKey); m_QuoteListRecycling.Recycle(quoteList); } } else { Log.AppendEntry(" Not Found! Marking it as such."); Log.EndEntry(); quoteToRemove.IsPriceListed = false; } } }//RemoveQuoteFromListing()
// protected string GetQueryForEconomicTickers(DatabaseInfo dataBase) { TableInfo.EconomicDataTableInfo economicTable = dataBase.EconomicEvents; TableInfo.EconomicTickersTableInfo economicTickers = dataBase.EconomicTickers; // Create a instrument expiry code. string expiryCode; if (!UV.Lib.Utilities.QTMath.TryConvertMonthYearToCodeY(InstrumentName.SeriesName, out expiryCode)) { return(string.Empty); } // Create sub selection string to get instr ID from InstrumentName. StringBuilder subQuery = new StringBuilder(); int prodFamilyId = m_InstrumentQuery.Results[0].ProdFamilyId; subQuery.AppendFormat("{0}", prodFamilyId); // Create the final query. //select * from bbg.economicData where unixT>=%d and unixT<=%d and tickerId in (select tickerId from bbg.productTickers where prodFamilyId in (-1,33)) order by unixT asc string desiredFields = "*"; StringBuilder query = new StringBuilder(); query.AppendFormat("SELECT {0} FROM {1}", desiredFields, economicTable.TableNameFull); if (this.StartDate > DateTime.MinValue) { int timestamp = (int)QTMath.DateTimeToEpoch(this.StartDate); string s = timestamp.ToString(); query.AppendFormat(" WHERE {0} >= {1}", economicTable.UnixTime, s); } if (this.EndDate < DateTime.MaxValue) { int timestamp = (int)QTMath.DateTimeToEpoch(this.EndDate); string s = timestamp.ToString(); query.AppendFormat(" AND {0} <= {1}", economicTable.UnixTime, s); } query.AppendFormat(" AND {0} IN", economicTable.TickerId); query.AppendFormat(" (SELECT {0} FROM {1} where {2}", economicTable.TickerId, economicTickers.TableNameFull, economicTickers.ProdFamilyId); query.AppendFormat(" IN (-1,{0})) ORDER BY {1} ASC", prodFamilyId, economicTable.UnixTime); if (this.MaxRows > 0) { query.AppendFormat(" LIMIT {0}", this.MaxRows); } query.Append(";"); // Exit return(query.ToString()); }// GetQuery();
// // // // #endregion//Properties #region Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** // // // ***************************************** // **** Quote() **** // ***************************************** /// <summary> /// Method to request that an order be sent to the Execution hub at /// a particular price and qty. /// </summary> /// <param name="orderSide"></param> /// <param name="price"></param> /// <param name="qty"></param> /// <param name="quoteReason"></param> /// <returns>TradeId</returns> public int Quote(int orderSide, double price, int qty, string quoteReason) { m_EngineEventArgsForOrders[orderSide].DataObjectList.Clear(); //clear before each use! // Locate TradeEventArg to update. SyntheticOrder syntheticOrder = null; if (m_ActiveOrders[orderSide] == null) { // There is currently no active trade, so create one. syntheticOrder = SyntheticOrder.RequestNewOrder(++m_LastOrderId); syntheticOrder.Side = orderSide; syntheticOrder.TradeReason = quoteReason; m_ActiveOrders[orderSide] = syntheticOrder; // store it as active trade } else { // There is already an active trade, grab it. syntheticOrder = m_ActiveOrders[orderSide]; } // Update quatities if they have changed. // TODO: implement various round-off schemes. int tradeSign = QTMath.MktSideToMktSign(orderSide); int iPrice = tradeSign * (int)System.Math.Floor(tradeSign * price / m_QuoteTickSize); // integerized price! bool isChangedFromPrev = qty != m_QuoteQtyPrev[orderSide] || (iPrice != m_QuoteIPricePrev[orderSide]) || quoteReason != m_QuoteReasonPrev[orderSide]; if (isChangedFromPrev) { m_QuoteQtyPrev[orderSide] = qty; m_QuoteIPricePrev[orderSide] = iPrice; m_QuoteReasonPrev[orderSide] = quoteReason; syntheticOrder.Price = iPrice * m_QuoteTickSize; syntheticOrder.Qty = qty; syntheticOrder.TradeReason = quoteReason; m_EngineEventArgsForOrders[orderSide].DataObjectList.Add(syntheticOrder); // add to event arg that is already set up. m_ExecutionHub.HubEventEnqueue(m_EngineEventArgsForOrders[orderSide].Copy()); // send off to remote exeuction m_StrategyHub.Log.NewEntry(Lib.Hubs.LogLevel.Major, "TradeEngine: {0} sent {1} ", m_Strategy.Name, syntheticOrder); } // Exit if (syntheticOrder == null) { return(-1); } else { return(syntheticOrder.OrderId); } }// Quote()
}//SetupBegin(). // // ************************************************************* // **** SetupComplete() **** // ************************************************************* public override void SetupComplete() { base.SetupComplete(); if (!QTMath.IsNearEqual(m_PriceLeg.Weight, 1, .01)) { Log.NewEntry(LogLevel.Error, "OrderEngine: Does not have functionality for legs with weight greater than 1 implemented yet"); } m_OrderBook = m_OrderHub.CreateOrderBook(m_PriceLeg.InstrumentName); List <InstrumentName> instrList = new List <InstrumentName> { m_PriceLeg.InstrumentName }; m_StrategyHub.SubscribeToMarketInstruments(instrList, m_Strategy); m_StrategyHub.SubscribeToFills(m_Strategy, m_OrderBook); m_StrategyHub.SubscribeToMajorOrderStatusEvents(m_Strategy, m_OrderBook); m_StrategyHub.SubscribeToOrderSubmitted(m_Strategy, m_OrderBook); }
// // // #endregion//Constructors #region no Properties // ***************************************************************** // **** Properties **** // ***************************************************************** // // #endregion//Properties #region Public Methods // ***************************************************************** // **** Public Methods **** // ***************************************************************** // // ************************************************* // **** Try Add() **** // ************************************************* // /// <summary> /// Add new Fill to the fill book /// </summary> /// <param name="aFill"></param> /// <returns>false if add failed</returns> public bool TryAdd(Fill aFill) { int remainingQty = aFill.Qty; // variable to subtract fills from bool isAddOkay = m_FillPages[QTMath.MktSignToMktSide(aFill.Qty)].TryAdd(aFill); // Store the Fill m_Volume += Math.Abs(aFill.Qty); // updated total volume // Allocate Fills against our open position/ if (m_FillsOpen.Count == 0 || remainingQty * m_FillsOpen[0].Qty >= 0) { // we have no opens fill OR the fill is on the same side so we adding to our position, or new position. Fill newFill = aFill.Clone(); // clone the fill so we can edit it m_FillsOpen.Add(newFill); // add the fill to the "open" list } else { // we have an opposing position so we need to cancel positions. double realPnL = 0.0; while (remainingQty != 0 && m_FillsOpen.Count > 0) { Fill oldFill = m_FillsOpen[m_FillsOpen.Count - 1]; if ((oldFill.Qty + remainingQty) * remainingQty >= 0) { // Incoming qty kills old fill qty "Q0" entirely; so the transacting amount is Q0 realPnL += -oldFill.Qty * oldFill.Price + oldFill.Qty * aFill.Price; // -Q0*P0 - (-Q0)*P remainingQty += m_FillsOpen[m_FillsOpen.Count - 1].Qty; // Q = Q + Q0 m_FillsOpen.RemoveAt(m_FillsOpen.Count - 1); // remove entire old fill [P0,Q0] } else { // Old fill completely absorbs new fill qty "Q"; transacting amount is Q realPnL += remainingQty * oldFill.Price - remainingQty * aFill.Price; // -pnl = -(-Q)*P1 - Q*P oldFill.Qty += remainingQty; // reduce oldQty by amount transacted. remainingQty = 0; // there is no quantity left. } } if (remainingQty != 0) { // After cancelling out all levels of old fills, we still have some left! Fill newFill = aFill.Clone(); newFill.Qty = remainingQty; // overwrite the quantity with remaining qty. m_FillsOpen.Add(newFill); } m_RealizedGain += realPnL * m_ContractMultiplier; } UpdatePositionInfo(); return(isAddOkay); }
// ***************************************************************** // **** 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); }
public double MinimumPriceTick = 1.0; // smallest price between two adjacent tick prices for orders. #endregion// members #region Constructors // ***************************************************************** // **** Constructors **** // ***************************************************************** public OrderBook(OrderHub parentOrderHub, InstrumentName instrumentName) { this.m_InstrumentName = instrumentName; // store required vars this.m_ParentHub = parentOrderHub; // Create Order tag format DateTime dt = DateTime.Now; string s1 = string.Format("{0:yy}{1}{2}{3}{4}{5}_", dt, QTMath.GetMonthCode(dt), QTMath.GetBaseSixtyCode(dt.Day), QTMath.GetBaseSixtyCode(dt.Hour), QTMath.GetBaseSixtyCode(dt.Minute), QTMath.GetBaseSixtyCode(dt.Second)); m_TagFormat = s1 + "{0}"; // Make certain this is unique. // Create books. m_ByPrice = new SortedDictionary <int, List <string> > [2]; // TODO: I think, for speed, use List<Order>? for (int side = 0; side < m_ByPrice.Length; ++side) { m_ByPrice[side] = new SortedDictionary <int, List <string> >(); } }//constructor
}//IsStopTriggered() // // // /// <summary> /// This must be called periodically to update things like volatility, blocking timeouts, /// etc. /// </summary> /// <param name="excessValue">The strategy excess value: (P - FairValue)</param> public virtual void Update(double excessValue) { if (m_StopSign != 0) { // We are stopped. // // Check blocking timeout. // if (m_IsBlockingTimeOut) { m_BlockingTimeOutRemaining -= 1; // decrease countdown to timeout. if (m_BlockingTimeOutRemaining <= 0) { if (IsLogWrite) { Log.NewEntry(LogLevel.Major, "StopRule: Blocking timed out. StopLoss for {1} returning to normal trading on {0} side." , QTMath.MktSignToString(m_StopSign), m_Name); } m_StopSign = 0; // return to normal trading. this.IsUpdateRequired = true; BroadcastParameters(); } } // // Crossing fair value can also reset blocking. // // Example: Consider the price trending is upward and that we stopped out on a short // position, so m_StopSign = -1. As the we continue to trend, the expectedReturn < 0. if (m_IsBlockingResetOnCrossing) { if (excessValue * m_StopSign > 0) // occurs when we cross to other side of fair value. { if (IsLogWrite) { Log.NewEntry(LogLevel.Major, "StopRule: Blocking halted because fair value crosssed. StopLoss for {1} returning to normal trading on {0} side." , QTMath.MktSignToString(m_StopSign), m_Name); } m_StopSign = 0; // return to normal trading. this.IsUpdateRequired = true; BroadcastParameters(); } } } // if stopped. } // Update().
// #endregion //IOrderEngine Implementation #region Event Handlers // ***************************************************************** // **** Event Handlers **** // ***************************************************************** // // // // ***************************************************************** // **** OrderBook_OrderFilled() **** // ***************************************************************** private void OrderBook_OrderFilled(object sender, EventArgs eventArgs) { FillEventArgs fillEventArgs = (FillEventArgs)eventArgs; m_FillBook.TryAdd(fillEventArgs.Fill); int fillSide = QTMath.MktSignToMktSide(fillEventArgs.Fill.Qty); m_StrategyPosition[fillSide] += fillEventArgs.Fill.Qty; fillEventArgs.Fill.Price = fillEventArgs.Fill.Price * m_PriceLeg.PriceMultiplier; Quote(); // this will update our orders now that we got filled, and resubmit if we are dripping etc.. // // Create a synthetic fill to pass back to the strategy. // if (m_OpenSyntheticOrders[fillSide] != null) { SyntheticFill newSyntheticFill = SyntheticFill.CreateSyntheticFillFromFill(fillEventArgs.Fill); m_OpenSyntheticOrders[fillSide].m_SyntheticFills.Add(newSyntheticFill); m_ExecutionContainer.SendSyntheticOrderToRemote(m_OpenSyntheticOrders[fillSide]); } }
// /// <summary> /// This override provides the implementation for the specialized EMA trailing /// stops. /// </summary> /// <param name="mktPrice"></param> protected override void UpdateTrailingStops(double mktPrice) { int posSign = Math.Sign(base.Position); int posSide = QTMath.MktSignToMktSide(posSign); m_PriceToDelete.Clear(); // place to store old stop price. m_PriceToAdd.Clear(); // place to store new stop price. // Check state of each stop price. for (int i = 0; i < m_StopPriceList.Count; ++i) // loop thru each stop-price in list. { // Compute the new stop price for this level. // Stop price evolution: // StopPrice[Time1] = Alpha*StopPrice[Time0] + (1-Alpha)*MidPrice[Time1] // double alpha = m_TrailingStopAlpha[posSide]; double newStopPrice = m_StopPriceList[i] * alpha + (1 - alpha) * mktPrice; m_PriceToDelete.Add(m_StopPriceList[i]); m_PriceToAdd.Add(newStopPrice); }//next stop level i // Update our stop lists. for (int i = 0; i < m_PriceToAdd.Count; ++i) { double newStopPrice = m_PriceToAdd[i]; if (!m_StopPriceList.Contains(newStopPrice)) { m_StopPriceList.Add(newStopPrice); m_StopPriceQty.Add(newStopPrice, 0); // put a zero qty space holder. } double oldStopPrice = m_PriceToDelete[i]; // original price to remove now. m_StopPriceList.Remove(oldStopPrice); m_StopPriceQty[newStopPrice] += m_StopPriceQty[oldStopPrice]; m_StopPriceQty.Remove(oldStopPrice); } m_PlotUpdateCount++; if (Position != 0 && m_GraphEngine != null && m_PlotUpdateCount % m_PlotUpdateFrequency == 0) { m_GraphEngine.AddPoint(m_GraphId, m_StopCurvveName, NearestStopPrice); } }
// // // // #endregion//Constructors #region Public Methods // ************************************* // **** AddItemToWrite() **** // ************************************* /// <summary> /// User calls this function to add a new signal item for later writing. /// TODO: /// 1) this is written like this so in future, we can recycle signal items /// without the user having access to them. /// </summary> public void AddItemToWrite(int strategyId, int instrId, DateTime localTime, UV.Lib.Application.UserInfo user, string pricingEngineName, string attributeString, int qty = 0, double price = 0) { FillsQueryItem newItem = new FillsQueryItem(); newItem.StrategyId = strategyId; newItem.InstrumentId = instrId; newItem.RunType = user.RunType.ToString().ToLower(); newItem.UserName = user.UserName; newItem.PricingEngineName = pricingEngineName; newItem.TimeStamp = localTime; double utc = QTMath.DateTimeToEpoch(localTime.ToUniversalTime()); newItem.UnixUTC = (int)Math.Floor(utc); newItem.UnixMicroSec = (int)((utc - newItem.UnixUTC) * 1000000); newItem.Qty = qty; newItem.Price = price; newItem.AttributeString = attributeString; this.m_ItemsToWrite.Add(newItem); }//AddItemToWrite()
// ***************************************************************** // **** 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); }