/// <summary> /// Fired each time an event is read from file /// </summary> /// <param name="eventItem"></param> private void EventRecieved(Spark.Event eventItem) { //Raise event if (Spark.TimeToDateTime(eventItem.Time) > _latestMarketTime) { _latestMarketTime = Spark.TimeToDateTime(eventItem.Time); } RaiseEvent(new EventFeedArgs(eventItem, _latestMarketTime)); //Skip over waiting logic if quote event and ignoring quotes if ((IgnoreQuoteEvents) && (eventItem.Type == Spark.EVENT_QUOTE)) { return; } //Wait before playing next event while replay is pause while ((IsPaused) && (!_isSteppingForward)) { System.Threading.Thread.Sleep(100); } if (_isSteppingForward) { _isSteppingForward = false; } }
/// <summary> /// Open file for reading and then read one line at a time calling the event processing delegate (function pointer) to /// process the event /// </summary> /// <remarks> /// Streaming events sequentially from a file is slightly faster than loading all events into an array before processing. /// It also reduces the memory required when processing a large file. /// </remarks> /// <param name="fileName">Event file name</param> /// <param name="eventProcessingFunction">Delegate (function pointer) for the method that will process the events</param> internal void StreamFromFile(string fileName, ApiEventFeedReplay.EventReceivedHandler eventProcessingFunction) { StreamReader file = null; try { file = new StreamReader(fileName); string line; do { line = file.ReadLine(); if (line != null) { Spark.Event eventItem = ApiEventReaderWriter.Parse(line); eventProcessingFunction(eventItem); } } while (line != null); } finally { if (file != null) { file.Close(); //Close file stream even if there is an error } } }
/// <summary> /// Parses Spark event file string into Spark.Event struct /// </summary> /// <param name="eventItemText">Spark event in tab-delimited text format</param> public static Spark.Event Parse(string eventItemText) { //Parse Row string[] entryItems = eventItemText.Split('\t'); //Create new event Spark.Event eventItem = new Spark.Event { Code = entryItems[0], ConditionCodes = ulong.Parse(entryItems[1]), Count = int.Parse(entryItems[2]), Exchange = entryItems[3], Flags = int.Parse(entryItems[4]), Position = int.Parse(entryItems[5]), Price = int.Parse(entryItems[6]), QuoteBases = ulong.Parse(entryItems[7]), Reference = int.Parse(entryItems[8]), State = int.Parse(entryItems[9]), Time = int.Parse(entryItems[10]), TimeNsec = int.Parse(entryItems[11]), Type = int.Parse(entryItems[12]), Volume = int.Parse(entryItems[13]) }; return(eventItem); }
/// <summary> /// Update order list according to details in specified order change event /// </summary> /// <param name="eventItem">Spark event</param> public void SubmitEvent(Spark.Event eventItem) { switch (eventItem.Type) { case Spark.EVENT_NEW_DEPTH: //ENTER LimitOrder order = eventItem.ToLimitOrder(); if (Count == 0) { Add(order); } else { Insert(eventItem.Position - 1, order); } break; case Spark.EVENT_AMEND_DEPTH: //AMEND this[eventItem.Position - 1].Volume = (int)eventItem.Volume; break; case Spark.EVENT_DELETE_DEPTH: //DELETE RemoveAt(eventItem.Position - 1); break; default: break; } }
/// <summary> /// /// </summary> /// <param name="eventItem"></param> private void ProcessEvent(Spark.Event eventItem) { //Skip over waiting logic if quote event and ignoring quotes if ((IgnoreQuoteEvents) && (eventItem.Type == Spark.EVENT_QUOTE)) { return; } //Raise event DateTime sparkEventTime = ApiFunctions.DateTimeFromUnixTimestampSeconds(eventItem.Time); if (sparkEventTime > _latestMarketTime) { _latestMarketTime = sparkEventTime; } RaiseEvent(new EventFeedArgs(eventItem, _latestMarketTime)); //Wait before playing next event while replay is pause while ((IsPaused) && (!_isSteppingForward)) { System.Threading.Thread.Sleep(100); } if (_isSteppingForward) { _isSteppingForward = false; } }
/// <summary> /// Execute query /// </summary> /// <param name="exchange">Security exchange</param> public static List <Spark.Event> Execute(string exchange) { //Connect to Spark API if required ApiControl.Instance.Connect(); //Get exchange reference Spark.Exchange exchangeRef; List <Spark.Event> result = null; if (ApiFunctions.GetSparkExchange(exchange, out exchangeRef)) { //Wait to allow Spark API to prepare for large download (it will only return a small number of events without this) System.Threading.Thread.Sleep(1000); //Request stock events result = new List <Spark.Event>(); Spark.Event sparkEvent = new Spark.Event(); //Execute query using current day request method if (Spark.GetAllExchangeEvents(ref exchangeRef, ref sparkEvent)) { while (Spark.GetNextExchangeEvent(ref exchangeRef, ref sparkEvent, 1000)) { result.Add(sparkEvent); } } Spark.ReleaseCurrentEvents(); } return(result); }
/// <summary> /// Extension method for the Spark.Event struct that generates new trade from order event if there is an /// event type match, otherwise returns NULL. /// </summary> internal static Trade ToTrade(this Spark.Event eventItem, string symbol, string exchange, DateTime observationTime) { int price = eventItem.Price; int volume = (int)eventItem.Volume; DateTime timeStamp = DateTimeFromUnixTimestampSeconds(eventItem.Time); string conditionCodes = Spark.ConditionCodesToStr(eventItem.ConditionCodes, eventItem.Exchange); return(new Trade(timeStamp, symbol, exchange, price, volume, conditionCodes, observationTime)); }
/// <summary> /// Extension method for the Spark.Event struct that converts a Spark event into LimitOrder that can be submitted /// into the LimitOrderBook /// </summary> /// <param name="eventItem">Spark event</param> internal static LimitOrder ToLimitOrder(this Spark.Event eventItem) { MarketSide side = ApiFunctions.GetMarketSide(eventItem.Flags); int price = eventItem.Price; int volume = (int)eventItem.Volume; DateTime timeStamp = DateTimeFromUnixTimestampSeconds(eventItem.Time); return(new LimitOrder(side, price, volume, timeStamp)); }
/// <summary> /// EventFeedArgs constructor /// </summary> /// <param name="eventItem">Spark event</param> /// <param name="timestamp">Observation timestamp</param> internal EventFeedArgs(Spark.Event eventItem, DateTime timestamp) { Event = eventItem; TimeStamp = timestamp; var adjustedValues = ApiFunctions.GetSymbolExchange(eventItem.Code, eventItem.Exchange); Symbol = adjustedValues.Item1; Exchange = adjustedValues.Item2; }
/// <summary> /// Submit Spark.Event depth update event (thread-safe) /// </summary> /// <param name="eventItem">Spark event</param> public void SubmitEvent(Spark.Event eventItem) { LimitOrderList list = (ApiFunctions.GetMarketSide(eventItem.Flags) == MarketSide.Bid) ? Bid : Ask; lock (_lock) { list.SubmitEvent(eventItem); } }
/// <summary> /// /// </summary> /// <param name="symbol">Security symbol</param> /// <param name="exchange">Security exchange</param> /// <param name="date">Request date</param> public static List <Spark.Event> Execute(string symbol, string exchange, DateTime date) { //Connect to Spark API if required ApiControl.Instance.Connect(); //Determine what Spark considers to be the current date DateTime sparkDate = ApiFunctions.GetCurrentSparkDate(); Debug.WriteLine("\tCurrent Spark Date: " + date.ToShortDateString()); //Get instance to stock Spark.Stock stock; List <Spark.Event> result = null; if (ApiFunctions.GetSparkStock(symbol, exchange, out stock)) { //Request stock events Spark.Event sparkEvent = new Spark.Event(); result = new List <Spark.Event>(); if (date == sparkDate) { //Wait to allow Spark API to prepare for download (it may only return a small number of events without this) System.Threading.Thread.Sleep(1000); //NOTE: This is only returning 1 event when requested over a mobile wireless connection. It may require more delay or some other sort of test. //Execute query using current day request method Debug.WriteLine("\tSpark.GetAllStockEvents(" + symbol + ")"); if (Spark.GetAllStockEvents(ref stock, ref sparkEvent)) { while (Spark.GetNextStockEvent(ref stock, ref sparkEvent, 1000)) //Specifying 0 timeout will return events until there are no more in the queue { result.Add(sparkEvent); } } Spark.ReleaseStockEvents(ref stock); } else { //Execute query using historical request method Debug.WriteLine("\tSpark.GetPastStockEvents(" + symbol + ", " + date.ToShortDateString() + ")"); if (Spark.GetPastStockEvents(ref stock, ref sparkEvent, date)) { while (Spark.GetNextPastStockEvent(ref stock, ref sparkEvent)) { result.Add(sparkEvent); } } Spark.ReleasePastStockEvents(ref stock, (DateTime)date); } } return(result); }
/// <summary> /// Convert Spark.Event struct into tab-separted human readable string /// </summary> /// <param name="eventItem">Spark event item</param> internal static string ToOutputString(this Spark.Event eventItem) { System.Text.StringBuilder result = new StringBuilder(); result.Append(DateTimeFromUnixTimestampSeconds(eventItem.Time).ToMarketDateTime() + "\t"); result.Append(Spark.EventTypeToStr(eventItem.Type) + "\t"); result.Append(eventItem.Code + "\t"); result.Append(eventItem.Exchange + "\t"); result.Append(ApiFunctions.GetMarketSide(eventItem.Flags).ToString() + "\t"); result.Append(eventItem.Price.ToPriceString() + "\t"); result.Append(eventItem.Volume + "\t"); result.Append(Spark.ConditionCodesToStr(eventItem.ConditionCodes, eventItem.Exchange) + "\t"); result.Append(eventItem.Flags + "\t"); result.Append(Spark.StateToStr(eventItem.State) + "\t"); result.Append(eventItem.Position + "\t"); result.Append(Spark.QuoteBasesToStr(eventItem.QuoteBases, eventItem.Exchange) + "\t"); result.Append(eventItem.Reference + "\t"); result.Append(eventItem.Count + "\t"); return(result.ToString()); }
/// <summary> /// Convert Spark.Event struct into tab-separted string for file storage /// </summary> /// <param name="eventItem">Spark event item</param> public static string ToFileString(Spark.Event eventItem) { StringBuilder result = new StringBuilder(); result.Append(eventItem.Code + "\t"); result.Append(eventItem.ConditionCodes + "\t"); result.Append(eventItem.Count + "\t"); result.Append(eventItem.Exchange + "\t"); result.Append(eventItem.Flags + "\t"); result.Append(eventItem.Position + "\t"); result.Append(eventItem.Price + "\t"); result.Append(eventItem.QuoteBases + "\t"); result.Append(eventItem.Reference + "\t"); result.Append(eventItem.State + "\t"); result.Append(eventItem.Time + "\t"); result.Append(eventItem.TimeNsec + "\t"); result.Append(eventItem.Type + "\t"); result.Append(eventItem.Volume); return(result.ToString()); }
/// <summary> /// Initiate data feed /// </summary> public override void Execute() { //Connect to Spark API if required ApiControl.Instance.Connect(); //Get instance to stock Spark.Stock stock; if (ApiFunctions.GetSparkStock(Symbol, Exchange, out stock)) { //Request all events for current day Spark.Event sparkEvent = new Spark.Event(); if (Spark.GetAllStockEvents(ref stock, ref sparkEvent)) { while (Spark.GetNextStockEvent(ref stock, ref sparkEvent, -1)) //Specifying -1 timeout will keep it waiting until end of day { RaiseEvent(new EventFeedArgs(sparkEvent, Spark.TimeToDateTime(sparkEvent.Time))); } } //Release memory at end of day Spark.ReleaseStockEvents(ref stock); } }
/// <summary> /// Initiate data feed /// </summary> public override void Execute() { //Connect to Spark API if required ApiControl.Instance.Connect(); //Get instance to exchange Spark.Exchange exchangeRef; if (ApiFunctions.GetSparkExchange(Exchange, out exchangeRef)) { //Request all events for current day Spark.Event sparkEvent = new Spark.Event(); if (Spark.GetAllExchangeEvents(ref exchangeRef, ref sparkEvent)) { while (Spark.GetNextExchangeEvent(ref exchangeRef, ref sparkEvent, -1)) //Specifying -1 timeout will keep it waiting until end of day { RaiseEvent(new EventFeedArgs(sparkEvent, Spark.TimeToDateTime(sparkEvent.Time))); } } //Release memory at end of day Spark.ReleaseCurrentEvents(); } }
/// <summary> /// Processes the Spark update event provided in the EventFeedArgs and updates security properties based on the event type /// </summary> /// <param name="sender">Event sender</param> /// <param name="eventFeedArgs">Event feed args</param> internal void EventReceived(object sender, EventFeedArgs eventFeedArgs) { //Process event Spark.Event eventItem = eventFeedArgs.Event; switch (eventItem.Type) { //Depth update case Spark.EVENT_NEW_DEPTH: case Spark.EVENT_AMEND_DEPTH: case Spark.EVENT_DELETE_DEPTH: //Check if exchange order book exists and create if it doesn't LimitOrderBook orderBook; if (!OrderBooks.TryGetValue(eventFeedArgs.Exchange, out orderBook)) { orderBook = new LimitOrderBook(eventFeedArgs.Symbol, eventFeedArgs.Exchange); OrderBooks.Add(eventFeedArgs.Exchange, orderBook); } //Submit update to appropriate exchange order book orderBook.SubmitEvent(eventItem); if (OnDepthUpdate != null) { OnDepthUpdate(this, new GenericEventArgs <LimitOrderBook>(eventFeedArgs.TimeStamp, orderBook)); } break; //Trade update case Spark.EVENT_TRADE: //Create and store trade record Trade trade = eventItem.ToTrade(eventFeedArgs.Symbol, eventFeedArgs.Exchange, eventFeedArgs.TimeStamp); Trades.Add(trade); if (OnTradeUpdate != null) { OnTradeUpdate(this, new GenericEventArgs <Trade>(eventFeedArgs.TimeStamp, trade)); } break; //Trade cancel case Spark.EVENT_CANCEL_TRADE: //Find original trade in trade record and delete Trade cancelledTrade = eventItem.ToTrade(eventFeedArgs.TimeStamp); Trade originalTrade = Trades.Find(x => (x.TimeStamp == cancelledTrade.TimeStamp && x.Price == cancelledTrade.Price && x.Volume == cancelledTrade.Volume)); if (originalTrade != null) { Trades.Remove(originalTrade); } break; //Market state update case Spark.EVENT_STATE_CHANGE: State = ApiFunctions.ConvertToMarketState(eventItem.State); if (OnMarketStateUpdate != null) { OnMarketStateUpdate(this, new GenericEventArgs <MarketState>(eventFeedArgs.TimeStamp, State)); } break; //Market quote update (change to best market bid-ask prices) case Spark.EVENT_QUOTE: if (OnQuoteUpdate != null) { LimitOrderBook depth = OrderBooks[eventFeedArgs.Exchange]; MarketQuote quote = new MarketQuote(eventFeedArgs.Symbol, eventFeedArgs.Exchange, depth.BidPrice, depth.AskPrice, eventFeedArgs.TimeStamp); OnQuoteUpdate(this, new GenericEventArgs <MarketQuote>(eventFeedArgs.TimeStamp, quote)); } break; //IAP (Indicative Auction Price) Update case Spark.EVENT_AUCTION_PRICE: break; default: //Console.WriteLine(eventItem.ToOutputString()); break; } }
/// <summary> /// Extension method for the Spark.Event struct that generates new trade from order event if there is an /// event type match, otherwise returns NULL. /// </summary> internal static Trade ToTrade(this Spark.Event eventItem, DateTime observationTime) { var values = GetSymbolExchange(eventItem.Code, eventItem.Exchange); return(eventItem.ToTrade(values.Item1, values.Item2, observationTime)); }
/// <summary> /// Fired each time an event is read from file /// </summary> /// <param name="eventItem"></param> private void EventRecieved(Spark.Event eventItem) { _eventQueue.Add(eventItem); }