}//CancelAllOrders() // // // ********************************************* // **** ProcessSyntheticOrder() **** // ********************************************* /// <summary> /// This function returns the "new" strategy-level synthetic fills. /// </summary> /// <param name="syntheticOrder"></param> /// <param name="syntheticFills"></param> /// <returns></returns> public bool ProcessSyntheticOrder(SyntheticOrder syntheticOrder, ref List <Fill> syntheticFills) { bool newFillsFound = false; SyntheticOrder prevOrder = null; if (m_FilledOrders.TryGetValue(syntheticOrder.OrderId, out prevOrder)) { // we have seen this order before, so lets just find the new fills and save them int n = 0; if (prevOrder.m_SyntheticFills != null) { n = prevOrder.m_SyntheticFills.Count; } for (int i = n; i < syntheticOrder.m_SyntheticFills.Count; ++i) { syntheticFills.Add(syntheticOrder.m_SyntheticFills[i]); } m_FilledOrders[syntheticOrder.OrderId] = syntheticOrder.Copy(); // save the newest order update newFillsFound = n != syntheticOrder.m_SyntheticFills.Count; } else { // We have never seen this order order before, add it to the dictionary. m_FilledOrders.Add(syntheticOrder.OrderId, syntheticOrder.Copy()); // this has to be a copy for us to be able to compare. foreach (SyntheticFill newSynthFill in syntheticOrder.m_SyntheticFills) { syntheticFills.Add(newSynthFill); } newFillsFound = syntheticOrder.m_SyntheticFills.Count > 0; } return(newFillsFound); }// ProcessSyntheticOrder()
}// ProcessParameterChangeRequest() // // // ***************************************** // **** ProcessSyntheticOrderRequest **** // ***************************************** /// <summary> /// A request for submission of a synthetic order. /// </summary> /// <param name="engineEventArg"></param> private void ProcessSyntheticOrderRequest(EngineEventArgs engineEventArg) { SyntheticOrder syntheticOrder = (SyntheticOrder)engineEventArg.DataObjectList[0]; ThreadContainer strategy = null; int strategyID = engineEventArg.EngineContainerID; if (strategyID < 0) { // This request is for all strategies Log.NewEntry(LogLevel.Error, "ProcessEngineEvent: Negative EngineContainerId not allowed in {0}.", syntheticOrder); engineEventArg.EngineHubName = this.ServiceName; engineEventArg.Status = EngineEventArgs.EventStatus.Failed; OnEngineChanged(engineEventArg); } else if (m_ExecutionContainers[DefaultHubName].TryGetValue(strategyID, out strategy)) { // Found the strategy, pass it the request now. // He is on another thread, so give him a thread safe copy. strategy.ProcessEvent(engineEventArg.Copy()); } else { // Unknown strategy Log.NewEntry(LogLevel.Error, "ProcessEngineEvent: Unknown EngineContainerId {0}", syntheticOrder); engineEventArg.EngineHubName = this.ServiceName; engineEventArg.Status = EngineEventArgs.EventStatus.Failed; OnEngineChanged(engineEventArg); } }
// ***************************************************************** // **** Public Methods **** // ***************************************************************** // // public override void ProcessEvent(EventArgs e) { base.ProcessEvent(e); if (e is EngineEventArgs) { EngineEventArgs engineEventArg = (EngineEventArgs)e; if (engineEventArg.MsgType == EngineEventArgs.EventType.SyntheticOrder) { SyntheticOrder syntheticOrder = (SyntheticOrder)engineEventArg.DataObjectList[0]; if (m_OpenSyntheticOrders[syntheticOrder.Side] == null) { // this is the first time we have seen this order m_OpenSyntheticOrders[syntheticOrder.Side] = syntheticOrder; Quote(syntheticOrder.Side, syntheticOrder.Price, syntheticOrder.Qty); } else { // we have seen this order before, check what do do...currently not implementing much // todo: how to save, check what user is requesting to change, etc m_OpenSyntheticOrders[syntheticOrder.Side].Qty = syntheticOrder.Qty; m_OpenSyntheticOrders[syntheticOrder.Side].Price = syntheticOrder.Price; m_OpenSyntheticOrders[syntheticOrder.Side].TradeReason = syntheticOrder.TradeReason; Quote(syntheticOrder.Side, syntheticOrder.Price, syntheticOrder.Qty); } } } }
// // ***************************************************** // **** ProcessSyntheticOrder() **** // ***************************************************** /// <summary> /// Called by the strategy hub to process a synthetic order for strategy. /// </summary> /// <param name="syntheticOrder"></param> /// <returns></returns> public bool ProcessSyntheticOrder(SyntheticOrder syntheticOrder) { bool isUpdateRequired = false; if (m_OrderEngine == null) { // This is an error. We need this engine! return(isUpdateRequired); } List <Fill> newFills = new List <Fill>(); m_OrderEngine.ProcessSyntheticOrder(syntheticOrder, ref newFills); if (m_QuoteEngine != null) { isUpdateRequired = m_QuoteEngine.ProcessSyntheticOrder(syntheticOrder, newFills); if (isUpdateRequired) { m_QuoteEngine.UpdateQuotes(true); } } /* * m_PricingEngine.Filled(syntheticOrder); // pass pricing engine raw event arg. * // Inform the pricing engine if there is a new * // synthetic fill. * if (m_PricingEngine != null && newFills != null) * { * // Prepare to write to fill database. Pre-fill snapshot. * string attributeString = string.Empty; * DateTime localTime = StrategyHub.GetLocalTime(); * UV.Lib.DatabaseReaderWriters.Queries.FillsQuery query = new Lib.DatabaseReaderWriters.Queries.FillsQuery(); * if (m_ModelSnapshots.TryGetValue(syntheticOrder.OrderId, out attributeString) == false) * attributeString = m_PricingEngine.GetFillAttributeString(); // get internal state of the pricing engine if it wants to send info with this fill. * query.AddItemToWrite(this.SqlId, -1, localTime, m_Services.User, attributeString, 0, 0); * * // Inform pricing engine * foreach (Fill f in newFills) * { * m_PricingEngine.Filled(f); // pass pricing engine synthetic fill. * attributeString = m_PricingEngine.GetFillAttributeString(); * if(syntheticOrder.TradeReason != null && syntheticOrder.TradeReason != string.Empty) * attributeString = String.Format("{0} TradeReason={1}", attributeString, syntheticOrder.TradeReason); * query.AddItemToWrite(this.SqlId, -1, localTime, m_Services.User, attributeString, f.Qty, f.Price); // here -1 means "strategy fill" * } * // Prepare a post-trade snapshot. * StrategyHub.RequestDatabaseWrite(query); // submit all the queries * } */ // Exit return(isUpdateRequired); }// ProcessTradeEventArg
// // // // #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()
}//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()
// // // ********************************************* // **** SendSyntheticOrderToRemote() **** // ********************************************* /// <summary> /// Called by an order engine who would like to send back to the strategy hub /// a synthetic order that has been updated. - This should probably be renamed. /// but just going with this for now. /// </summary> /// <param name="syntheticOrder"></param> public void SendSyntheticOrderToRemote(SyntheticOrder syntheticOrder) { m_ConfirmSynthOrderEventArg.DataObjectList.Clear(); // clear before each reuse. m_ConfirmSynthOrderEventArg.DataObjectList.Add(syntheticOrder); this.RemoteEngineHub.HubEventEnqueue(m_ConfirmSynthOrderEventArg.Copy()); }