public event EventHandler HistoricFill; // Fired for each fill received (new or old). // // protected void OnHistoricFill(FillEventArgs eventArgs) { if (HistoricFill != null) { HistoricFill(this, eventArgs); } }// OnFilled()
private void TT_FillListDownLoad(object sender, TradingTechnologies.TTAPI.FillBookDownloadEventArgs eventArgs) { bool isWriteLog = false; if (Log != null) { isWriteLog = Log.BeginEntry(LogLevel.Minor, "FillListener.TT_FillDownLoad: {0} {1} fills from {2}: ", Name, eventArgs.Fills.Count.ToString(), System.Threading.Thread.CurrentThread.Name); } foreach (TradingTechnologies.TTAPI.Fill fill in eventArgs.Fills) { if (m_LogAllFillPropertiesFromTT) { WriteFillToLog(FillType.Historic.ToString(), fill); } FillEventArgs e = CreateFillEventArg(FillType.Historic, fill); if (isWriteLog) { Log.AppendEntry("[{0} {1} {2}]", e.TTInstrumentKey, e.Fill, e.FillKey); } m_HistoricFills.Add(e); // alt approach: StatusChanged is triggered at end of download, HistoricFills passed to subscribers OnHistoricFill(e); // otherwise, subscribers to this event can collect each historic fill. } if (Log != null) { Log.EndEntry(); } }
/// <summary> /// True if both instances seem to refer to the same fill event. /// </summary> /// <returns></returns> public bool IsSameAs(FillEventArgs other) { if (other == null) { return(false); } if (string.IsNullOrEmpty(this.FillKey) || string.IsNullOrEmpty(other.FillKey)) { // One or both are missing a unique fill key - bummer! if (!this.TTInstrumentKey.Equals(other.TTInstrumentKey)) { return(false); } if (this.Type != other.Type) { return(false); } if (!this.Fill.IsSameAs(other.Fill)) { return(false); } // They seem to be same! return(true); } else { return(this.FillKey.Equals(other.FillKey)); } }
} // TryAdd() // // // // ************************************************************* // **** Is Fill New() **** // ************************************************************* /// <summary> /// This method examines a fill event and tries to determine whether or not the fill /// is known to this fill book, or is new. If its not new, an explanation is returned as /// an RejectedFillEventArg. /// </summary> /// <param name="fillEventArgs"></param> /// <param name="rejectedEventArgs">Rejected fill: null when true, but can be null/not null when false</param> /// <returns>True if this fill seems to be new to our book.</returns> public new bool IsFillNew(FillEventArgs fillEventArgs, out RejectedFills.RejectedFillEventArgs rejectedEventArgs) { rejectedEventArgs = null; // Test purpose:(This should never appeared in the production code. //DateTime localAcceptDateTime = DateTime.Now.Date.AddHours(12); //if (fillEventArgs.Fill.LocalTime <= localAcceptDateTime) //{ // string msg = string.Format("Test purpose date time accepted only after {0}.", localAcceptDateTime); // rejectedEventArgs = TriggerRejectionEvent(fillEventArgs, RejectedFills.RejectionReason.ExcessiveLateness, msg); // return false; //} // Accept user adjusted fills always. if (fillEventArgs.Type == FillType.UserAdjustment) { return(true); } // Accept adjusted fills always. if (fillEventArgs.Type == FillType.Adjustment) { return(true); } // Exchange time stamp events. (Note some fills like UserAdjusted fills don't have Exchange time stamps.) if (fillEventArgs.Fill.ExchangeTime.CompareTo(DateTime.MinValue) > 0) { // Reject if we have actually seen fill key before. We don't do this test right off because its slow. if (fillEventArgs.FillKey != null && m_RecentKeys.Contains(fillEventArgs.FillKey)) { string msg = string.Format("{0} already in book {1}.", fillEventArgs.FillKey, this.Name); rejectedEventArgs = TriggerRejectionEvent(fillEventArgs, RejectedFills.RejectionReason.DuplicateKey, msg); return(false); } TimeSpan ts = (fillEventArgs.Fill.ExchangeTime).Subtract(this.ExchangeTimeLast); // time since last fill. if (ts.TotalMilliseconds >= 0) // fills after the last fill are always reasonable. { return(true); } else { // Reject if fill is old. That is, our book has more recent fills. string msg = string.Format("Bad fill time {0}. Book last {1}. Earlier by {2} hours.", fillEventArgs.Fill.ExchangeTime.ToString(Strings.FormatDateTimeZone), base.ExchangeTimeLast.ToString(Strings.FormatDateTimeZone), ts.TotalHours.ToString("0.0")); rejectedEventArgs = TriggerRejectionEvent(fillEventArgs, RejectedFills.RejectionReason.ExcessiveLateness, msg); return(false); } } else { // Reject if fill does not have valid exchange time. string msg = string.Format("Invalid fill time {0}.", fillEventArgs.Fill.ExchangeTime.ToString(Strings.FormatDateTimeZone)); rejectedEventArgs = TriggerRejectionEvent(fillEventArgs, RejectedFills.RejectionReason.ExcessiveLateness, msg); return(false); } // Can't find any reason to reject. //return true; }// IsFillAcceptable()
public event EventHandler Filled; // Fired for each fill received (new or old). // // public void OnFilled(FillEventArgs eventArgs) { Log.NewEntry(LogLevel.Major, "FillListener: {0}", eventArgs); if (Filled != null) { Filled(this, eventArgs); } }// OnFilled()
}// WriteFillToLog() // // // ************************************************************* // **** Create Fill EventArgs() **** // ************************************************************* private FillEventArgs CreateFillEventArg(FillType fillType, TradingTechnologies.TTAPI.Fill ttFill) { int qty = 0; if (ttFill.BuySell == BuySell.Buy) { qty = ttFill.Quantity.ToInt(); // qty > 0 --> buy; } else if (ttFill.BuySell == BuySell.Sell) { qty = -ttFill.Quantity.ToInt(); // qty < 0 --> sell; } Misty.Lib.OrderHubs.Fill aFill = Misty.Lib.OrderHubs.Fill.Create(qty, ttFill.MatchPrice.ToDouble(), Log.GetTime(), ttFill.TransactionDateTime); FillEventArgs e = new FillEventArgs(ttFill.InstrumentKey, fillType, aFill); e.FillKey = ttFill.FillKey; e.AccountID = ttFill.AccountName; return(e); }//CreateFillEventArg()
/// <summary> /// Creates a fill event from the pair-wise info in drop. /// Allowed keys: /// Time - the time the drop was made. /// LocalFillTime - local time fill was received. /// </summary> /// <param name="pairs"></param> /// <returns></returns> private FillEventArgs CreateFillEvent(Dictionary <string, string> pairs) { string sQty; string sPrice; if (pairs.TryGetValue("Qty", out sQty) && pairs.TryGetValue("Price", out sPrice)) { Misty.Lib.OrderHubs.Fill aFill = Misty.Lib.OrderHubs.Fill.Create(); aFill.Qty = Convert.ToInt32(sQty); aFill.Price = Convert.ToDouble(sPrice); // Extract fill times. if (pairs.ContainsKey("LocalFillTime")) { aFill.LocalTime = Convert.ToDateTime(pairs["LocalFillTime"]); // use fill time, if available. } else { aFill.LocalTime = Convert.ToDateTime(pairs["Time"]); // else use last drop time- legacy approach } // Extract TT's instrument key. InstrumentKey key; string sForeignKey; if (pairs.TryGetValue("ForeignKey", out sForeignKey) && TTConvert.TryCreateInstrumentKey(sForeignKey, out key)) { FillEventArgs e = new FillEventArgs(key, FillType.InitialPosition, aFill); return(e); // success! } else { Log.NewEntry(LogLevel.Error, "Failed to recreate instrument key from {0}.", sForeignKey); return(null); } } else { Log.NewEntry(LogLevel.Error, "Failed to create a fill event"); return(null); } }// CreateFillEvent()
}//IsFillExistByExchangeTime() #endregion//Public Methods #region Private Methods // ***************************************************************** // **** Private Methods **** // ***************************************************************** // // // // private RejectedFills.RejectedFillEventArgs TriggerRejectionEvent(FillEventArgs rejectedFill, RejectedFills.RejectionReason reason, string rejectionMessage) { // Make sure this rejection is new. bool isDuplicate = false; int n = 0; while ((!isDuplicate) && n < m_RejectedFills.Count) { isDuplicate = rejectedFill.IsSameAs(m_RejectedFills[n].OriginalFillEventArg); n++; } RejectedFills.RejectedFillEventArgs rejectedEventArgs = new RejectedFills.RejectedFillEventArgs(this.Name, rejectedFill, reason, rejectionMessage); if (!isDuplicate) { // Don't event if this is a duplicate rejection (previously rejected that is). //RejectedFills.RejectedFillEventArgs rejectedEventArgs = new RejectedFills.RejectedFillEventArgs(this.Name, rejectedFill, reason, rejectionMessage); this.m_RejectedFills.Add(rejectedEventArgs); // The storing of this rejection notification, is like triggering event. return(rejectedEventArgs); } else { return(rejectedEventArgs); // this is a rejection that we have already seen (in previous run, probably). } }
}//SetConfirmMode() // #endregion//Private Methods #region Event Handlers // ***************************************************************** // **** Event Handlers **** // ***************************************************************** // // private void buttonSubmitFill_Click(object sender, EventArgs e) { if (m_IsConfirmMode) { // The user has confirmed to send this. // Read numbers again bool isGoodToSendOrder = true; int qty = 0; if (!Int32.TryParse(textBoxQty.Text, out qty)) { isGoodToSendOrder = false; } double price = 0; if (!Double.TryParse(textBoxPrice.Text, out price)) { isGoodToSendOrder = false; } if (isGoodToSendOrder) { // Submit fill now Misty.Lib.OrderHubs.Fill aFill = Misty.Lib.OrderHubs.Fill.Create(); aFill.Price = price; aFill.Qty = qty; aFill.LocalTime = Log.GetTime(); /* * //Misty.Lib.Products.InstrumentBase instrBase = null; * TradingTechnologies.TTAPI.InstrumentDetails details; * if (m_CurrentInstrument != m_EmptyInstrument && m_Market.TryGetInstrumentDetails(m_CurrentInstrument, out details) && details != null && details.Key != null) * { // TODO: Somewhere around here a null exception was thrown. * if ( Log != null) * Log.NewEntry(Misty.Lib.Hubs.LogLevel.Major, "FormAddFills.SubmitFill_Click: Submitting fill request {0} or ForeignKey is null", m_CurrentInstrument); * TradingTechnologies.TTAPI.InstrumentKey instrKey = (TradingTechnologies.TTAPI.InstrumentKey)details.Key; * Ambre.TTServices.Fills.FillEventArgs fillEventArgs = new Ambre.TTServices.Fills.FillEventArgs(instrKey, Ambre.TTServices.Fills.FillType.UserAdjustment, aFill); * m_FillHub.HubEventEnqueue(fillEventArgs); * } * else if (Log != null) * Log.NewEntry(Misty.Lib.Hubs.LogLevel.Warning, "FormAddFills.SubmitFill_Click: Failed {0} or ForeignKey is null", m_CurrentInstrument); */ TradingTechnologies.TTAPI.InstrumentKey instrumentKey; if (m_CurrentInstrument != m_EmptyInstrument && m_FillHub.TryGetInstrumentKey(m_CurrentInstrument, out instrumentKey)) { // TODO: Somewhere around here a null exception was thrown. if (Log != null) { Log.NewEntry(Misty.Lib.Hubs.LogLevel.Major, "FormAddFills.SubmitFill_Click: Submitting fill request {0}.", m_CurrentInstrument); } Ambre.TTServices.Fills.FillEventArgs fillEventArgs = new Ambre.TTServices.Fills.FillEventArgs(instrumentKey, Ambre.TTServices.Fills.FillType.UserAdjustment, aFill); m_FillHub.HubEventEnqueue(fillEventArgs); } else if (Log != null) { Log.NewEntry(Misty.Lib.Hubs.LogLevel.Warning, "FormAddFills.SubmitFill_Click: Failed {0} or ForeignKey is null", m_CurrentInstrument); } // REset the colors textBoxQty.Text = string.Empty; // protection against user's adding fill twice. SetConfirmMode(buttonSubmitFill, false, 0); // reset colors and text on button } } else { bool isGood = true; int qty = 0; if (Int32.TryParse(textBoxQty.Text, out qty)) { textBoxQty.Text = string.Format("{0:+0;-0;0}", qty); } else { textBoxQty.Text = "0"; isGood = false; } double price = 0; if (Double.TryParse(textBoxPrice.Text, out price)) { textBoxPrice.Text = string.Format("{0}", price); } else { textBoxPrice.Text = ""; isGood = false; } // Set SetConfirmMode(buttonSubmitFill, isGood, qty); // set colors to "confirm" mode. } }
} // TryPeriodicBookDrop() // // ************************************************************* // **** Drop Raw Fill() **** // ************************************************************* public void DropFillEvent(FillEventArgs fillEventArg) { m_DropFills.RequestEnqueue(Misty.Lib.IO.Xml.Stringifiable.Stringify(fillEventArg)); }
public bool IsFillNew(FillEventArgs fillEventArgs, out RejectedFills.RejectedFillEventArgs rejectedEventArgs) { rejectedEventArgs = null; return(true); }
}//ToString() // // // public bool TryAdd(FillEventArgs eventArg, out RejectedFills.RejectedFillEventArgs rejection) { rejection = null; return(true); }
// ***************************************************************** // **** Try Add() **** // ***************************************************************** /// <summary> /// A fill event is presented to the book for acceptance. The fill event is checked for validity, /// and if good added to the book. If not, a rejection event argument is created for caller. /// </summary> /// <param name="eventArg">fill event argument</param> /// <param name="rejection">rejection event create if false is returned</param> /// <returns>true if fill can be accepted, false if not.</returns> public new bool TryAdd(FillEventArgs eventArg, out RejectedFills.RejectedFillEventArgs rejection) // Add a key workd override. { rejection = null; InstrumentKey key = eventArg.TTInstrumentKey; if (this.IsFillNew(eventArg, out rejection)) { // Add fill to book Misty.Lib.OrderHubs.Fill aFill = eventArg.Fill; base.Add(aFill); // Update fill identification tables if (m_RecentKeys == null) { m_RecentKeys = new RejectedFills.RecentKeyList(); } if (!string.IsNullOrEmpty(eventArg.FillKey)) { m_RecentKeys.Add(eventArg.FillKey); } // Process rejections that are now accepted. if (eventArg.Type == FillType.UserAdjustment) { int n = 0; while (n < m_RejectedFills.Count) { if (m_RejectedFills[n].OriginalFillEventArg.IsSameAs(eventArg)) { m_RejectedFills.RemoveAt(n); break; } n++; } } return(true); } else { return(false); } /* * if (eventArg.Type == FillType.Historic || eventArg.Type == FillType.InitialPosition) * { // If a fill is historic or initial, it needs to be verified that we have not already * // incorporated this fill into our book in the past. * // Other fills are blindly accepted since they should not have been seen before. * if (this.IsFillNew(eventArg, out rejection)) * { * base.Add(aFill); * if (m_RecentKeys == null) * m_RecentKeys = new RejectedFills.RecentKeyList(); * m_RecentKeys.Add(eventArg.FillKey); * } * else * { * return false; * } * } * else * { // Other kinds of fills; including "New" and "UserAdjustments" are always accepted as they * // come in. * base.Add(aFill); * if (m_RecentKeys == null) * m_RecentKeys = new RejectedFills.RecentKeyList(); * m_RecentKeys.Add(eventArg.FillKey); * } * return true; */ } // TryAdd()
/// <summary> /// Dropping for all Raw fill events. /// </summary> public void EnqueueArchiveFill(FillEventArgs fillEventArg) { m_FillWriter.RequestEnqueue(Stringifiable.Stringify(fillEventArg, m_StringifyOverrideTable)); }