/*
* 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 NodaTime;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Packets;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using QuantConnect.Util;
using HistoryRequest = QuantConnect.Data.HistoryRequest;
using Timer = System.Timers.Timer;
using System.Threading;
namespace QuantConnect.ToolBox.IQFeed
{
///
/// IQFeedDataQueueHandler is an implementation of IDataQueueHandler and IHistoryProvider
///
public class IQFeedDataQueueHandler : HistoryProviderBase, IDataQueueHandler, IDataQueueUniverseProvider
{
private bool _isConnected;
private int _dataPointCount;
private readonly HashSet _symbols;
private readonly Dictionary _underlyings;
private readonly object _sync = new object();
private IQFeedDataQueueUniverseProvider _symbolUniverse;
//Socket connections:
private AdminPort _adminPort;
private Level1Port _level1Port;
private HistoryPort _historyPort;
private readonly IDataAggregator _aggregator = Composer.Instance.GetExportedValueByTypeName(
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"));
private readonly EventBasedDataQueueHandlerSubscriptionManager _subscriptionManager;
///
/// Gets the total number of data points emitted by this history provider
///
public override int DataPointCount => _dataPointCount;
///
/// IQFeedDataQueueHandler is an implementation of IDataQueueHandler:
///
public IQFeedDataQueueHandler()
{
_symbols = new HashSet();
_underlyings = new Dictionary();
_subscriptionManager = new EventBasedDataQueueHandlerSubscriptionManager();
_subscriptionManager.SubscribeImpl += (s, t) =>
{
Subscribe(s);
return true;
};
_subscriptionManager.UnsubscribeImpl += (s, t) =>
{
Unsubscribe(s);
return true;
};
if (!IsConnected) Connect();
}
///
/// Subscribe to the specified configuration
///
/// defines the parameters to subscribe to a data feed
/// handler to be fired on new data available
/// The new enumerator for this subscription request
public IEnumerator Subscribe(SubscriptionDataConfig dataConfig, EventHandler newDataAvailableHandler)
{
if (!CanSubscribe(dataConfig.Symbol))
{
return Enumerable.Empty().GetEnumerator();
}
var enumerator = _aggregator.Add(dataConfig, newDataAvailableHandler);
_subscriptionManager.Subscribe(dataConfig);
return enumerator;
}
///
/// Adds the specified symbols to the subscription: new IQLevel1WatchItem("IBM", true)
///
/// The symbols to be added keyed by SecurityType
public void Subscribe(IEnumerable symbols)
{
try
{
foreach (var symbol in symbols)
{
lock (_sync)
{
Log.Trace("IQFeed.Subscribe(): Subscribe Request: " + symbol.ToString());
if (_symbols.Add(symbol))
{
// processing canonical option symbol to subscribe to underlying prices
var subscribeSymbol = symbol;
if (symbol.ID.SecurityType == SecurityType.Option && symbol.IsCanonical())
{
subscribeSymbol = symbol.Underlying;
_underlyings.Add(subscribeSymbol, symbol);
}
if (symbol.ID.SecurityType == SecurityType.Future && symbol.IsCanonical())
{
// do nothing for now. Later might add continuous contract symbol.
return;
}
var ticker = _symbolUniverse.GetBrokerageSymbol(subscribeSymbol);
if (!string.IsNullOrEmpty(ticker))
{
_level1Port.Subscribe(ticker);
Log.Trace("IQFeed.Subscribe(): Subscribe Processed: {0} ({1})", symbol.Value, ticker);
}
else
{
Log.Error("IQFeed.Subscribe(): Symbol {0} was not found in IQFeed symbol universe", symbol.Value);
}
}
}
}
}
catch (Exception err)
{
Log.Error("IQFeed.Subscribe(): " + err.Message);
}
}
///
/// Removes the specified configuration
///
/// Subscription config to be removed
public void Unsubscribe(SubscriptionDataConfig dataConfig)
{
_subscriptionManager.Unsubscribe(dataConfig);
_aggregator.Remove(dataConfig);
}
///
/// Sets the job we're subscribing for
///
/// Job we're subscribing for
public void SetJob(LiveNodePacket job)
{
}
///
/// Removes the specified symbols to the subscription
///
/// The symbols to be removed keyed by SecurityType
public void Unsubscribe(IEnumerable symbols)
{
try
{
foreach (var symbol in symbols)
{
lock (_sync)
{
Log.Trace("IQFeed.Unsubscribe(): " + symbol.ToString());
_symbols.Remove(symbol);
var subscribeSymbol = symbol;
if (symbol.ID.SecurityType == SecurityType.Option && symbol.ID.StrikePrice == 0.0m)
{
subscribeSymbol = symbol.Underlying;
_underlyings.Remove(subscribeSymbol);
}
var ticker = _symbolUniverse.GetBrokerageSymbol(subscribeSymbol);
if (_level1Port.Contains(ticker))
{
_level1Port.Unsubscribe(ticker);
}
}
}
}
catch (Exception err)
{
Log.Error("IQFeed.Unsubscribe(): " + err.Message);
}
}
///
/// Initializes this history provider to work for the specified job
///
/// The initialization parameters
public override void Initialize(HistoryProviderInitializeParameters parameters)
{
}
///
/// Gets the history for the requested securities
///
/// The historical data requests
/// The time zone used when time stamping the slice instances
/// An enumerable of the slices of data covering the span specified in each request
public override IEnumerable GetHistory(IEnumerable requests, DateTimeZone sliceTimeZone)
{
foreach (var request in requests)
{
foreach (var slice in _historyPort.ProcessHistoryRequests(request))
{
yield return slice;
}
}
}
///
/// Indicates the connection is live.
///
public bool IsConnected => _isConnected;
///
/// Connect to the IQ Feed using supplied username and password information.
///
private void Connect()
{
try
{
// Launch the IQ Feed Application:
Log.Trace("IQFeed.Connect(): Launching client...");
if (OS.IsWindows)
{
// IQConnect is only supported on Windows
var connector = new IQConnect(Config.Get("iqfeed-productName"), "1.0");
connector.Launch();
}
// Initialise one admin port
Log.Trace("IQFeed.Connect(): Connecting to admin...");
_adminPort = new AdminPort();
_adminPort.Connect();
_adminPort.SetAutoconnect();
_adminPort.SetClientStats(false);
_adminPort.SetClientName("Admin");
_adminPort.DisconnectedEvent += AdminPortOnDisconnectedEvent;
_adminPort.ConnectedEvent += AdminPortOnConnectedEvent;
_symbolUniverse = new IQFeedDataQueueUniverseProvider();
Log.Trace("IQFeed.Connect(): Connecting to L1 data...");
_level1Port = new Level1Port(_aggregator, _symbolUniverse);
_level1Port.Connect();
_level1Port.SetClientName("Level1");
Log.Trace("IQFeed.Connect(): Connecting to Historical data...");
_historyPort = new HistoryPort(_symbolUniverse);
_historyPort.Connect();
_historyPort.SetClientName("History");
_isConnected = true;
}
catch (Exception err)
{
Log.Error("IQFeed.Connect(): Error Connecting to IQFeed: " + err.Message);
_isConnected = false;
}
}
///
/// Disconnect from all ports we're subscribed to:
///
///
/// Not being used. IQ automatically disconnect on killing LEAN
///
private void Disconnect()
{
if (_adminPort != null) _adminPort.Disconnect();
if (_level1Port != null) _level1Port.Disconnect();
_isConnected = false;
Log.Trace("IQFeed.Disconnect(): Disconnected");
}
///
/// Returns true if this data provide can handle the specified symbol
///
/// The symbol to be handled
/// True if this data provider can get data for the symbol, false otherwise
private static bool CanSubscribe(Symbol symbol)
{
var market = symbol.ID.Market;
var securityType = symbol.ID.SecurityType;
if (symbol.Value.IndexOfInvariant("universe", true) != -1) return false;
return
(securityType == SecurityType.Equity && market == Market.USA) ||
(securityType == SecurityType.Forex && market == Market.FXCM) ||
(securityType == SecurityType.Option && market == Market.USA) ||
(securityType == SecurityType.Future);
}
///
/// Admin port is connected.
///
private void AdminPortOnConnectedEvent(object sender, ConnectedEventArgs connectedEventArgs)
{
_isConnected = true;
Log.Error("IQFeed.AdminPortOnConnectedEvent(): ADMIN PORT CONNECTED!");
}
///
/// Admin port disconnected from the IQFeed server.
///
private void AdminPortOnDisconnectedEvent(object sender, DisconnectedEventArgs disconnectedEventArgs)
{
_isConnected = false;
Log.Error("IQFeed.AdminPortOnDisconnectedEvent(): ADMIN PORT DISCONNECTED!");
}
///
/// Method returns a collection of Symbols that are available at the data source.
///
/// String representing the name to lookup
/// Expected security type of the returned symbols (if any)
/// Include expired contracts
/// Expected security currency(if any)
/// Expected security exchange name(if any)
/// Symbol results
public IEnumerable LookupSymbols(string lookupName, SecurityType securityType, bool includeExpired, string securityCurrency = null, string securityExchange = null)
{
return _symbolUniverse.LookupSymbols(lookupName, securityType, includeExpired, securityCurrency, securityExchange);
}
///
/// Method returns a collection of Symbols that are available at the data source.
///
/// Symbol to lookup
/// Include expired contracts
/// Expected security currency(if any)
/// Symbol results
public IEnumerable LookupSymbols(Symbol symbol, bool includeExpired, string securityCurrency)
{
return LookupSymbols(symbol.ID.Symbol, symbol.SecurityType, includeExpired, securityCurrency);
}
///
/// Returns whether the time can be advanced or not.
///
/// The security type
/// true if the time can be advanced
public bool CanAdvanceTime(SecurityType securityType)
{
return _symbolUniverse.CanAdvanceTime(securityType);
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
public void Dispose()
{
_symbolUniverse.DisposeSafely();
}
}
///
/// Admin class type
///
public class AdminPort : IQAdminSocketClient
{
public AdminPort()
: base(80)
{
}
}
///
/// Level 1 Data Request:
///
public class Level1Port : IQLevel1Client
{
private int count;
private DateTime start;
private DateTime _feedTime;
private Stopwatch _stopwatch = new Stopwatch();
private readonly Timer _timer;
private readonly ConcurrentDictionary _prices;
private readonly ConcurrentDictionary _openInterests;
private readonly IQFeedDataQueueUniverseProvider _symbolUniverse;
private readonly IDataAggregator _aggregator;
private int _dataQueueCount;
public DateTime FeedTime
{
get
{
if (_feedTime == new DateTime()) return DateTime.Now;
return _feedTime.AddMilliseconds(_stopwatch.ElapsedMilliseconds);
}
set
{
_feedTime = value;
_stopwatch = Stopwatch.StartNew();
}
}
public Level1Port(IDataAggregator aggregator, IQFeedDataQueueUniverseProvider symbolUniverse)
: base(80)
{
start = DateTime.Now;
_prices = new ConcurrentDictionary();
_openInterests = new ConcurrentDictionary();
_aggregator = aggregator;
_symbolUniverse = symbolUniverse;
Level1SummaryUpdateEvent += OnLevel1SummaryUpdateEvent;
Level1TimerEvent += OnLevel1TimerEvent;
Level1ServerDisconnectedEvent += OnLevel1ServerDisconnected;
Level1ServerReconnectFailed += OnLevel1ServerReconnectFailed;
Level1UnknownEvent += OnLevel1UnknownEvent;
Level1FundamentalEvent += OnLevel1FundamentalEvent;
_timer = new Timer(1000);
_timer.Enabled = false;
_timer.AutoReset = true;
_timer.Elapsed += (sender, args) =>
{
var ticksPerSecond = count / (DateTime.Now - start).TotalSeconds;
int dataQueueCount = Interlocked.Exchange(ref _dataQueueCount, 0);
if (ticksPerSecond > 1000 || dataQueueCount > 31)
{
Log.Trace($"IQFeed.OnSecond(): Ticks/sec: {ticksPerSecond.ToStringInvariant("0000.00")} " +
$"Engine.Ticks.Count: {dataQueueCount} CPU%: {OS.CpuUsage.ToStringInvariant("0.0") + "%"}"
);
}
count = 0;
start = DateTime.Now;
};
_timer.Enabled = true;
}
private Symbol GetLeanSymbol(string ticker)
{
return _symbolUniverse.GetLeanSymbol(ticker, SecurityType.Base, null);
}
private void OnLevel1FundamentalEvent(object sender, Level1FundamentalEventArgs e)
{
// handle split data, they're only valid today, they'll show up around 4:45am EST
if (e.SplitDate1.Date == DateTime.Today && DateTime.Now.TimeOfDay.TotalHours <= 8) // they will always be sent premarket
{
// get the last price, if it doesn't exist then we'll just issue the split claiming the price was zero
// this should (ideally) never happen, but sending this without the price is much better then not sending
// it at all
double referencePrice;
_prices.TryGetValue(e.Symbol, out referencePrice);
var symbol = GetLeanSymbol(e.Symbol);
var split = new Split(symbol, FeedTime, (decimal)referencePrice, (decimal)e.SplitFactor1, SplitType.SplitOccurred);
Emit(split);
}
}
///
/// Handle a new price update packet:
///
private void OnLevel1SummaryUpdateEvent(object sender, Level1SummaryUpdateEventArgs e)
{
// if ticker is not found, unsubscribe
if (e.NotFound) Unsubscribe(e.Symbol);
// only update if we have a value
if (e.Last == 0) return;
// only accept trade and B/A updates
if (e.TypeOfUpdate != Level1SummaryUpdateEventArgs.UpdateType.ExtendedTrade
&& e.TypeOfUpdate != Level1SummaryUpdateEventArgs.UpdateType.Trade
&& e.TypeOfUpdate != Level1SummaryUpdateEventArgs.UpdateType.Bid
&& e.TypeOfUpdate != Level1SummaryUpdateEventArgs.UpdateType.Ask) return;
count++;
var time = FeedTime;
var last = (decimal)(e.TypeOfUpdate == Level1SummaryUpdateEventArgs.UpdateType.ExtendedTrade ? e.ExtendedTradingLast : e.Last);
var symbol = GetLeanSymbol(e.Symbol);
TickType tradeType;
switch (symbol.ID.SecurityType)
{
// the feed time is in NYC/EDT, convert it into EST
case SecurityType.Forex:
time = FeedTime.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
// TypeOfUpdate always equal to UpdateType.Trade for FXCM, but the message contains B/A and last data
tradeType = TickType.Quote;
break;
// for all other asset classes we leave it as is (NYC/EDT)
default:
time = FeedTime;
tradeType = e.TypeOfUpdate == Level1SummaryUpdateEventArgs.UpdateType.Bid ||
e.TypeOfUpdate == Level1SummaryUpdateEventArgs.UpdateType.Ask ?
TickType.Quote :
TickType.Trade;
break;
}
var tick = new Tick(time, symbol, last, (decimal)e.Bid, (decimal)e.Ask)
{
AskSize = e.AskSize,
BidSize = e.BidSize,
Quantity = e.IncrementalVolume,
TickType = tradeType,
DataType = MarketDataType.Tick
};
Emit(tick);
_prices[e.Symbol] = e.Last;
if (symbol.ID.SecurityType == SecurityType.Option || symbol.ID.SecurityType == SecurityType.Future)
{
if (!_openInterests.ContainsKey(e.Symbol) || _openInterests[e.Symbol] != e.OpenInterest)
{
var oi = new OpenInterest(time, symbol, e.OpenInterest);
Emit(oi);
_openInterests[e.Symbol] = e.OpenInterest;
}
}
}
private void Emit(BaseData tick)
{
_aggregator.Update(tick);
Interlocked.Increment(ref _dataQueueCount);
}
///
/// Set the interal clock time.
///
private void OnLevel1TimerEvent(object sender, Level1TimerEventArgs e)
{
//If there was a bad tick and the time didn't set right, skip setting it here and just use our millisecond timer to set the time from last time it was set.
if (e.DateTimeStamp != DateTime.MinValue)
{
FeedTime = e.DateTimeStamp;
}
}
///
/// Server has disconnected, reconnect.
///
private void OnLevel1ServerDisconnected(object sender, Level1ServerDisconnectedArgs e)
{
Log.Error("IQFeed.OnLevel1ServerDisconnected(): LEVEL 1 PORT DISCONNECTED! " + e.TextLine);
}
///
/// Server has disconnected, reconnect.
///
private void OnLevel1ServerReconnectFailed(object sender, Level1ServerReconnectFailedArgs e)
{
Log.Error("IQFeed.OnLevel1ServerReconnectFailed(): LEVEL 1 PORT DISCONNECT! " + e.TextLine);
}
///
/// Got a message we don't know about, log it for posterity.
///
private void OnLevel1UnknownEvent(object sender, Level1TextLineEventArgs e)
{
Log.Error("IQFeed.OnUnknownEvent(): " + e.TextLine);
}
}
// this type is expected to be used for exactly one job at a time
public class HistoryPort : IQLookupHistorySymbolClient
{
private bool _inProgress;
private ConcurrentDictionary _requestDataByRequestId;
private ConcurrentDictionary> _currentRequest;
private readonly string DataDirectory = Config.Get("data-directory", "../../../Data");
private readonly double MaxHistoryRequestMinutes = Config.GetDouble("max-history-minutes", 5);
private readonly IQFeedDataQueueUniverseProvider _symbolUniverse;
///
/// ...
///
public HistoryPort(IQFeedDataQueueUniverseProvider symbolUniverse)
: base(80)
{
_symbolUniverse = symbolUniverse;
_requestDataByRequestId = new ConcurrentDictionary();
_currentRequest = new ConcurrentDictionary>();
}
///
/// ...
///
public HistoryPort(IQFeedDataQueueUniverseProvider symbolUniverse, int maxDataPoints, int dataPointsPerSend)
: this(symbolUniverse)
{
MaxDataPoints = maxDataPoints;
DataPointsPerSend = dataPointsPerSend;
}
///
/// Populate request data
///
public IEnumerable ProcessHistoryRequests(HistoryRequest request)
{
// skipping universe and canonical symbols
if (!CanHandle(request.Symbol) ||
(request.Symbol.ID.SecurityType == SecurityType.Option && request.Symbol.IsCanonical()) ||
(request.Symbol.ID.SecurityType == SecurityType.Future && request.Symbol.IsCanonical()))
{
yield break;
}
// Set this process status
_inProgress = true;
var ticker = _symbolUniverse.GetBrokerageSymbol(request.Symbol);
var start = request.StartTimeUtc.ConvertFromUtc(TimeZones.NewYork);
DateTime? end = request.EndTimeUtc.ConvertFromUtc(TimeZones.NewYork);
// if we're within a minute of now, don't set the end time
if (request.EndTimeUtc >= DateTime.UtcNow.AddMinutes(-1))
{
end = null;
}
Log.Trace($"HistoryPort.ProcessHistoryJob(): Submitting request: {request.Symbol.SecurityType.ToStringInvariant()}-{ticker}: " +
$"{request.Resolution.ToStringInvariant()} {start.ToStringInvariant()}->{(end ?? DateTime.UtcNow.AddMinutes(-1)).ToStringInvariant()}"
);
int id;
var reqid = string.Empty;
switch (request.Resolution)
{
case Resolution.Tick:
id = RequestTickData(ticker, start, end, true);
reqid = CreateRequestID(LookupType.REQ_HST_TCK, id);
break;
case Resolution.Daily:
id = RequestDailyData(ticker, start, end, true);
reqid = CreateRequestID(LookupType.REQ_HST_DWM, id);
break;
default:
var interval = new Interval(GetPeriodType(request.Resolution), 1);
id = RequestIntervalData(ticker, interval, start, end, true);
reqid = CreateRequestID(LookupType.REQ_HST_INT, id);
break;
}
_requestDataByRequestId[reqid] = request;
while (_inProgress)
{
continue;
}
// After all data arrive, we pass it to the algorithm through memory and write to a file
foreach (var key in _currentRequest.Keys)
{
List tradeBars;
if (_currentRequest.TryRemove(key, out tradeBars))
{
foreach (var tradeBar in tradeBars)
{
// Returns IEnumerable object
yield return new Slice(tradeBar.EndTime, new[] { tradeBar });
}
}
}
}
///
/// Returns true if this data provide can handle the specified symbol
///
/// The symbol to be handled
/// True if this data provider can get data for the symbol, false otherwise
private bool CanHandle(Symbol symbol)
{
var market = symbol.ID.Market;
var securityType = symbol.ID.SecurityType;
return
(securityType == SecurityType.Equity && market == Market.USA) ||
(securityType == SecurityType.Forex && market == Market.FXCM) ||
(securityType == SecurityType.Option && market == Market.USA) ||
(securityType == SecurityType.Future && IQFeedDataQueueUniverseProvider.FuturesExchanges.Values.Contains(market));
}
///
/// Created new request ID for a given lookup type (tick, intraday bar, daily bar)
///
/// Lookup type: REQ_HST_TCK (tick), REQ_HST_DWM (daily) or REQ_HST_INT (intraday resolutions)
/// Sequential identifier
///
private static string CreateRequestID(LookupType lookupType, int id)
{
return lookupType + id.ToStringInvariant("0000000");
}
///
/// Method called when a new Lookup event is fired
///
/// Received data
protected override void OnLookupEvent(LookupEventArgs e)
{
try
{
switch (e.Sequence)
{
case LookupSequence.MessageStart:
_currentRequest.AddOrUpdate(e.Id, new List());
break;
case LookupSequence.MessageDetail:
List current;
if (_currentRequest.TryGetValue(e.Id, out current))
{
HandleMessageDetail(e, current);
}
break;
case LookupSequence.MessageEnd:
_inProgress = false;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch (Exception err)
{
Log.Error(err);
}
}
///
/// Put received data into current list of BaseData object
///
/// Received data
/// Current list of BaseData object
private void HandleMessageDetail(LookupEventArgs e, List current)
{
var requestData = _requestDataByRequestId[e.Id];
var data = GetData(e, requestData);
if (data != null && data.Time != DateTime.MinValue)
{
current.Add(data);
}
}
///
/// Transform received data into BaseData object
///
/// Received data
/// Request information
/// BaseData object
private BaseData GetData(LookupEventArgs e, HistoryRequest requestData)
{
var isEquity = requestData.Symbol.SecurityType == SecurityType.Equity;
try
{
switch (e.Type)
{
case LookupType.REQ_HST_TCK:
var t = (LookupTickEventArgs)e;
var time = isEquity ? t.DateTimeStamp : t.DateTimeStamp.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
return new Tick(time, requestData.Symbol, (decimal)t.Last, (decimal)t.Bid, (decimal)t.Ask) { Quantity = t.LastSize };
case LookupType.REQ_HST_INT:
var i = (LookupIntervalEventArgs)e;
if (i.DateTimeStamp == DateTime.MinValue) return null;
var istartTime = i.DateTimeStamp - requestData.Resolution.ToTimeSpan();
if (!isEquity) istartTime = istartTime.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
return new TradeBar(istartTime, requestData.Symbol, (decimal)i.Open, (decimal)i.High, (decimal)i.Low, (decimal)i.Close, i.PeriodVolume);
case LookupType.REQ_HST_DWM:
var d = (LookupDayWeekMonthEventArgs)e;
if (d.DateTimeStamp == DateTime.MinValue) return null;
var dstartTime = d.DateTimeStamp.Date;
if (!isEquity) dstartTime = dstartTime.ConvertTo(TimeZones.NewYork, TimeZones.EasternStandard);
return new TradeBar(dstartTime, requestData.Symbol, (decimal)d.Open, (decimal)d.High, (decimal)d.Low, (decimal)d.Close, d.PeriodVolume, requestData.Resolution.ToTimeSpan());
// we don't need to handle these other types
case LookupType.REQ_SYM_SYM:
case LookupType.REQ_SYM_SIC:
case LookupType.REQ_SYM_NAC:
case LookupType.REQ_TAB_MKT:
case LookupType.REQ_TAB_SEC:
case LookupType.REQ_TAB_MKC:
case LookupType.REQ_TAB_SIC:
case LookupType.REQ_TAB_NAC:
default:
return null;
}
}
catch (Exception err)
{
Log.Error("Encountered error while processing request: " + e.Id);
Log.Error(err);
return null;
}
}
private static PeriodType GetPeriodType(Resolution resolution)
{
switch (resolution)
{
case Resolution.Second:
return PeriodType.Second;
case Resolution.Minute:
return PeriodType.Minute;
case Resolution.Hour:
return PeriodType.Hour;
case Resolution.Tick:
case Resolution.Daily:
default:
throw new ArgumentOutOfRangeException("resolution", resolution, null);
}
}
}
}