Files
quantconnect--lean/Tests/Engine/DataFeeds/IEXDataQueueHandlerTests.cs
Martin Molinero 0933da9303 Refactor previous commits
- Removing `AccountCurrency` from `Cash` and `Brokerage` classes.
`ICurrencyConverter` will now provide the `AccountCurrency`
- Adding new static `OrderFee.Zero` which will return a 0 order fee in
`NullCurrency`
- Adding static `Currencies.USD` value, replacing all "USD".
- Addin new static `Currencies.NullCurrency`
- Updating Bitfinex `FeeModel` so it return fees in quote currency.
Adding unit tests
2018-12-13 11:30:40 -03:00

308 lines
10 KiB
C#

/*
* 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.Threading;
using System.Threading.Tasks;
using NodaTime;
using NUnit.Framework;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Logging;
using QuantConnect.Securities;
using QuantConnect.ToolBox.IEX;
namespace QuantConnect.Tests.Engine.DataFeeds
{
[TestFixture]
[Explicit("Tests are dependent on network and are long")]
public class IEXDataQueueHandlerTests
{
private void ProcessFeed(IEXDataQueueHandler iex, Action<BaseData> callback = null)
{
Task.Run(() =>
{
foreach (var tick in iex.GetNextTicks())
{
try
{
if (callback != null)
{
callback.Invoke(tick);
}
}
catch (AssertionException)
{
throw;
}
catch (Exception err)
{
Console.WriteLine(err.Message);
}
}
});
}
[Test]
public void IEXCouldConnect()
{
var iex = new IEXDataQueueHandler();
Thread.Sleep(5000);
Assert.IsTrue(iex.IsConnected);
iex = null;
GC.Collect(2, GCCollectionMode.Forced, true);
Thread.Sleep(1000);
// finalizer should print disconnected message
}
/// <summary>
/// Firehose is a special symbol that subscribes to all IEX symbols
/// </summary>
[Test]
public void IEXCouldSubscribeToAll()
{
var iex = new IEXDataQueueHandler();
ProcessFeed(iex, tick => Console.WriteLine(tick.ToString()));
iex.Subscribe(null, new[]
{
Symbol.Create("firehose", SecurityType.Equity, Market.USA)
});
Thread.Sleep(30000);
iex.Dispose();
}
/// <summary>
/// Subscribe to multiple symbols in a single call
/// </summary>
[Test]
public void IEXCouldSubscribe()
{
var iex = new IEXDataQueueHandler();
ProcessFeed(iex, tick => Console.WriteLine(tick.ToString()));
iex.Subscribe(null, new[]
{
Symbol.Create("FB", SecurityType.Equity, Market.USA),
Symbol.Create("AAPL", SecurityType.Equity, Market.USA),
Symbol.Create("XIV", SecurityType.Equity, Market.USA),
Symbol.Create("PTN", SecurityType.Equity, Market.USA),
Symbol.Create("USO", SecurityType.Equity, Market.USA),
});
Thread.Sleep(10000);
iex.Dispose();
}
/// <summary>
/// Subscribe to multiple symbols in a series of calls
/// </summary>
[Test]
public void IEXCouldSubscribeManyTimes()
{
var iex = new IEXDataQueueHandler();
ProcessFeed(iex, tick => Console.WriteLine(tick.ToString()));
iex.Subscribe(null, new[]
{
Symbol.Create("MBLY", SecurityType.Equity, Market.USA),
});
iex.Subscribe(null, new[]
{
Symbol.Create("FB", SecurityType.Equity, Market.USA),
});
iex.Subscribe(null, new[]
{
Symbol.Create("AAPL", SecurityType.Equity, Market.USA),
});
iex.Subscribe(null, new[]
{
Symbol.Create("USO", SecurityType.Equity, Market.USA),
});
Thread.Sleep(10000);
Console.WriteLine("Unsubscribing from all except MBLY");
iex.Unsubscribe(null, new[]
{
Symbol.Create("FB", SecurityType.Equity, Market.USA),
});
iex.Unsubscribe(null, new[]
{
Symbol.Create("AAPL", SecurityType.Equity, Market.USA),
});
iex.Unsubscribe(null, new[]
{
Symbol.Create("USO", SecurityType.Equity, Market.USA),
});
Thread.Sleep(10000);
iex.Dispose();
}
[Test]
public void IEXCouldSubscribeAndUnsubscribe()
{
// MBLY is the most liquid IEX instrument
var iex = new IEXDataQueueHandler();
var unsubscribed = false;
ProcessFeed(iex, tick =>
{
Console.WriteLine(tick.ToString());
if (unsubscribed && tick.Symbol.Value == "MBLY")
{
Assert.Fail("Should not receive data for unsubscribed symbol");
}
});
iex.Subscribe(null, new[] {
Symbol.Create("MBLY", SecurityType.Equity, Market.USA),
Symbol.Create("USO", SecurityType.Equity, Market.USA)
});
Thread.Sleep(20000);
iex.Unsubscribe(null, new[]
{
Symbol.Create("MBLY", SecurityType.Equity, Market.USA)
});
Console.WriteLine("Unsubscribing");
Thread.Sleep(2000);
// some messages could be inflight, but after a pause all MBLY messages must have beed consumed by ProcessFeed
unsubscribed = true;
Thread.Sleep(20000);
iex.Dispose();
}
[Test]
public void IEXCouldReconnect()
{
var iex = new IEXDataQueueHandler();
var realEndpoint = iex.Endpoint;
Thread.Sleep(1000);
iex.Dispose();
iex.Endpoint = "https://badd.address";
iex.Reconnect();
Thread.Sleep(1000);
iex.Dispose();
iex.Endpoint = realEndpoint;
iex.Reconnect();
Thread.Sleep(1000);
Assert.IsTrue(iex.IsConnected);
iex.Dispose();
}
#region History provider tests
public TestCaseData[] TestParameters
{
get
{
return new[]
{
// valid parameters
new TestCaseData(Symbols.SPY, Resolution.Daily, TimeSpan.FromDays(15), true),
new TestCaseData(Symbols.SPY, Resolution.Minute, TimeSpan.FromDays(3), true),
// invalid resolution == empty result.
new TestCaseData(Symbols.SPY, Resolution.Tick, TimeSpan.FromSeconds(15), false),
new TestCaseData(Symbols.SPY, Resolution.Second, Time.OneMinute, false),
new TestCaseData(Symbols.SPY, Resolution.Hour, Time.OneDay, false),
// invalid period == empty result
new TestCaseData(Symbols.SPY, Resolution.Minute, TimeSpan.FromDays(45), false), // beyond 30 days
new TestCaseData(Symbols.SPY, Resolution.Daily, TimeSpan.FromDays(-15), false), // date in future
new TestCaseData(Symbols.SPY, Resolution.Daily, TimeSpan.FromDays(365*5.5), false), // beyond 5 years
// invalid symbol: XYZ
new TestCaseData(Symbol.Create("XYZ", SecurityType.Equity, Market.FXCM), Resolution.Daily, TimeSpan.FromDays(15), false)
.Throws("System.Net.WebException"),
// invalid security type, throws "System.ArgumentException : Invalid security type: Forex"
new TestCaseData(Symbols.EURUSD, Resolution.Daily, TimeSpan.FromDays(15), false)
.Throws("System.Net.WebException")
};
}
}
[Test, TestCaseSource("TestParameters")]
public void IEXCouldGetHistory(Symbol symbol, Resolution resolution, TimeSpan period, bool received)
{
var historyProvider = new IEXDataQueueHandler();
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null, null, null, null, null));
var now = DateTime.UtcNow;
var requests = new[]
{
new HistoryRequest(now.Add(-period),
now,
typeof(QuoteBar),
symbol,
resolution,
SecurityExchangeHours.AlwaysOpen(TimeZones.Utc),
DateTimeZone.Utc,
Resolution.Minute,
false,
false,
DataNormalizationMode.Adjusted,
TickType.Quote)
};
var history = historyProvider.GetHistory(requests, TimeZones.Utc);
foreach (var slice in history)
{
if (resolution == Resolution.Tick || resolution == Resolution.Second || resolution == Resolution.Hour)
{
Assert.IsNull(slice);
}
else if (resolution == Resolution.Daily || resolution == Resolution.Minute)
{
Assert.IsNotNull(slice);
var bar = slice.Bars[symbol];
Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}: {6}, {7}", bar.Time, bar.Symbol, bar.Open, bar.High, bar.Low, bar.Close, resolution, period);
}
}
Log.Trace("Data points retrieved: " + historyProvider.DataPointCount);
if (received)
{
Assert.IsTrue(historyProvider.DataPointCount > 0);
}
else
{
Assert.IsTrue(historyProvider.DataPointCount == 0);
}
}
#endregion
}
}