/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using QuantConnect.Data.Market; using QuantConnect.Logging; using QuantConnect.Securities; using QuantConnect.Securities.Future; using QuantConnect.Util; namespace QuantConnect.ToolBox.AlgoSeekFuturesConverter { /// /// Enumerator for converting AlgoSeek futures files into Ticks. /// public class AlgoSeekFuturesReader : IEnumerator { private readonly Stream _stream; private readonly StreamReader _streamReader; private readonly HashSet _symbolFilter; private readonly Dictionary _symbolMultipliers; private readonly SymbolPropertiesDatabase _symbolProperties; private readonly int _columnTimestamp = -1; private readonly int _columnSecID = -1; private readonly int _columnTicker = -1; private readonly int _columnType = -1; private readonly int _columnSide = -1; private readonly int _columnQuantity = -1; private readonly int _columnPrice = -1; private readonly int _columnsCount = -1; /// /// Enumerate through the lines of the algoseek files. /// /// BZ File for AlgoSeek /// Symbol price multiplier /// Symbol filter to apply, if any public AlgoSeekFuturesReader(string file, Dictionary symbolMultipliers, HashSet symbolFilter = null) { var streamProvider = StreamProvider.ForExtension(Path.GetExtension(file)); _stream = streamProvider.Open(file).First(); _streamReader = new StreamReader(_stream); _symbolFilter = symbolFilter; _symbolMultipliers = symbolMultipliers.ToDictionary(); _symbolProperties = SymbolPropertiesDatabase.FromDataFolder(); // detecting column order in the file var headerLine = _streamReader.ReadLine(); if (!string.IsNullOrEmpty(headerLine)) { var header = headerLine.ToCsv(); _columnTimestamp = header.FindIndex(x => x == "Timestamp"); _columnTicker = header.FindIndex(x => x == "Ticker"); _columnType = header.FindIndex(x => x == "Type"); _columnSide = header.FindIndex(x => x == "Side"); _columnSecID = header.FindIndex(x => x == "SecurityID"); _columnQuantity = header.FindIndex(x => x == "Quantity"); _columnPrice = header.FindIndex(x => x == "Price"); _columnsCount = new[] { _columnTimestamp, _columnTicker, _columnType, _columnSide, _columnSecID, _columnQuantity, _columnPrice }.Max(); } //Prime the data pump, set the current. Current = null; MoveNext(); } /// /// Parse the next line of the algoseek future file. /// /// public bool MoveNext() { string line; Tick tick = null; while (tick == null && (line = _streamReader.ReadLine()) != null) { // If line is invalid continue looping to find next valid line. tick = Parse(line); } Current = tick; return Current != null; } /// /// Current top of the tick file. /// public Tick Current { get; private set; } /// /// Gets the current element in the collection. /// /// /// The current element in the collection. /// object IEnumerator.Current => Current; /// /// Reset the enumerator for the AlgoSeekFuturesReader /// public void Reset() { throw new NotImplementedException("Reset not implemented for AlgoSeekFuturesReader."); } /// /// Dispose of the underlying AlgoSeekFuturesReader /// public void Dispose() { _stream.Close(); _stream.Dispose(); _streamReader.Close(); _streamReader.Dispose(); } /// /// Parse a string line into a future tick. /// /// /// private Tick Parse(string line) { try { const int TradeMask = 2; const int QuoteMask = 1; const int OpenInterestMask = 11; const int MessageTypeMask = 15; // parse csv check column count var csv = line.ToCsv(); if (csv.Count - 1 < _columnsCount) { return null; } var ticker = csv[_columnTicker]; // we filter out options and spreads if (ticker.IndexOfAny(new [] { ' ', '-' }) != -1) { return null; } ticker = ticker.Trim('"'); if (string.IsNullOrEmpty(ticker)) { return null; } // ignoring time zones completely -- this is all in the 'data-time-zone' var timeString = csv[_columnTimestamp]; var time = DateTime.ParseExact(timeString, "yyyyMMddHHmmssFFF", CultureInfo.InvariantCulture); var symbol = SymbolRepresentation.ParseFutureSymbol(ticker, time.Year); if (symbol == null || !_symbolMultipliers.ContainsKey(symbol.ID.Symbol) || _symbolFilter != null && !_symbolFilter.Contains(symbol.ID.Symbol, StringComparer.InvariantCultureIgnoreCase)) { return null; } // detecting tick type (trade or quote) TickType tickType; bool isAsk = false; var type = csv[_columnType].ConvertInvariant(); if ((type & MessageTypeMask) == TradeMask) { tickType = TickType.Trade; } else if ((type & MessageTypeMask) == OpenInterestMask) { tickType = TickType.OpenInterest; } else if ((type & MessageTypeMask) == QuoteMask) { tickType = TickType.Quote; switch (csv[_columnSide]) { case "B": isAsk = false; break; case "S": isAsk = true; break; default: { return null; } } } else { return null; } // All futures but VIX are delivered with a scale factor of 10000000000. var scaleFactor = symbol.ID.Symbol == "VX" ? decimal.One : 10000000000m; var price = csv[_columnPrice].ToDecimal() / scaleFactor; var quantity = csv[_columnQuantity].ToInt32(); price *= _symbolMultipliers[symbol.ID.Symbol]; switch (tickType) { case TickType.Quote: var tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Value = price }; if (isAsk) { tick.AskPrice = price; tick.AskSize = quantity; } else { tick.BidPrice = price; tick.BidSize = quantity; } return tick; case TickType.Trade: tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Value = price, Quantity = quantity }; return tick; case TickType.OpenInterest: tick = new Tick { Symbol = symbol, Time = time, TickType = tickType, Exchange = symbol.ID.Market, Value = quantity }; return tick; } return null; } catch (Exception err) { Log.Error(err); Log.Trace("Line: {0}", line); return null; } } } }