Files
2026-07-13 13:02:50 +08:00

294 lines
13 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.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using QuantConnect.Data;
using System.Globalization;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
namespace QuantConnect.Tests.Common.Data.Market
{
[TestFixture]
public class TickTests
{
[Test]
public void ConstructsFromLine()
{
const string line = "15093000,1456300,100,P,T,0";
var baseDate = new DateTime(2013, 10, 08);
var tick = new Tick(Symbols.SPY, line, baseDate);
var ms = (tick.Time - baseDate).TotalMilliseconds;
Assert.AreEqual(15093000, ms);
Assert.AreEqual(1456300, tick.LastPrice * 10000m);
Assert.AreEqual(100, tick.Quantity);
Assert.AreEqual("P", tick.ExchangeCode);
Assert.AreEqual("ARCA", tick.Exchange);
Assert.AreEqual("T", tick.SaleCondition);
Assert.AreEqual(false, tick.Suspicious);
}
[TestCase("18000677.3,3669.12,0.0040077,3669.13,3.40618718", "18000677.3", "3669.12", "0.0040077", "3669.13", "3.40618718")]
[TestCase("18000677.3111,3669.12,0.0040077,3669.13,3.40618718", "18000677.3111", "3669.12", "0.0040077", "3669.13", "3.40618718")]
public void ConstructsFromLineWithDecimalTimestamp(string line, string milliseconds, string bidPrice,
string bidSize, string askPrice, string askSize)
{
var config = new SubscriptionDataConfig(
typeof(Tick), Symbols.BTCUSD, Resolution.Tick, TimeZones.Utc, TimeZones.Utc,
false, false, false, false, TickType.Quote);
var baseDate = new DateTime(2019, 1, 15);
var tick = new Tick(config, line, baseDate);
var ms = (tick.Time - baseDate).TotalMilliseconds;
Assert.AreEqual( decimal.Parse(milliseconds, CultureInfo.InvariantCulture), ms);
Assert.AreEqual(decimal.Parse(bidPrice, CultureInfo.InvariantCulture), tick.BidPrice);
Assert.AreEqual(decimal.Parse(bidSize, CultureInfo.InvariantCulture), tick.BidSize);
Assert.AreEqual(decimal.Parse(askPrice, CultureInfo.InvariantCulture), tick.AskPrice);
Assert.AreEqual(decimal.Parse(askSize, CultureInfo.InvariantCulture), tick.AskSize);
}
[Test]
public void ReadsFuturesTickFromLine()
{
const string line = "86399572,52.62,5,usa,,0,False";
var baseDate = new DateTime(2013, 10, 08);
var symbol = Symbol.CreateFuture(Futures.Energy.CrudeOilWTI, QuantConnect.Market.NYMEX, new DateTime(2017, 2, 28));
var config = new SubscriptionDataConfig(typeof(Tick), symbol, Resolution.Tick, TimeZones.NewYork, TimeZones.NewYork, false, false, false);
var tick = new Tick(config, line, baseDate);
var ms = (tick.Time - baseDate).TotalMilliseconds;
Assert.AreEqual(86399572, ms);
Assert.AreEqual(52.62, tick.LastPrice);
Assert.AreEqual(5, tick.Quantity);
Assert.AreEqual("", tick.Exchange);
Assert.AreEqual("", tick.SaleCondition);
Assert.AreEqual(false, tick.Suspicious);
}
[TestCase(SecurityType.Crypto, TickType.Trade, "1234567,18000,0.0001,0")]
[TestCase(SecurityType.Crypto, TickType.Quote, "1234567,18000,10,18100,15,0")]
[TestCase(SecurityType.CryptoFuture, TickType.Trade, "1234567,18000,0.0001,0")]
[TestCase(SecurityType.CryptoFuture, TickType.Quote, "1234567,18000,10,18100,15,0")]
public void ReadsCryptoAndCryptoFuturesTickFromLine(SecurityType securityType, TickType tickType, string line)
{
var baseDate = new DateTime(2013, 10, 08);
var symbol = Symbol.Create("BTCUSDT", securityType, QuantConnect.Market.Binance);
var config = new SubscriptionDataConfig(typeof(Tick), symbol, Resolution.Tick, TimeZones.NewYork, TimeZones.NewYork, false, false, false,
tickType: tickType);
var tick = new Tick(config, line, baseDate);
var ms = (tick.Time - baseDate).TotalMilliseconds;
Assert.AreEqual(1234567d, ms);
Assert.AreEqual("", tick.Exchange);
Assert.AreEqual("", tick.SaleCondition);
Assert.AreEqual(false, tick.Suspicious);
if (tickType == TickType.Trade)
{
Assert.AreEqual(18000m, tick.Value);
Assert.AreEqual(0.0001m, tick.Quantity);
Assert.AreEqual("", tick.Exchange);
Assert.AreEqual("", tick.SaleCondition);
Assert.AreEqual(false, tick.Suspicious);
}
else
{
Assert.AreEqual((18000m + 18100m) / 2m, tick.Value);
Assert.AreEqual(18000m, tick.BidPrice);
Assert.AreEqual(10m, tick.BidSize);
Assert.AreEqual(18100m, tick.AskPrice);
Assert.AreEqual(15m, tick.AskSize);
}
}
[TestCase("14400135,0,0,1680000,400,NASDAQ,00000001,0", 0, 0, 168, 400)]
[TestCase("14400135,10000,10,0,0,NASDAQ,00000001,0", 1, 10, 0, 0)]
[TestCase("14400135,10000,10,20000,20,NASDAQ,00000001,0", 1, 10, 2, 20)]
public void EquityQuoteTick(string line, decimal bidPrice, decimal bidSize, decimal askPrice, decimal askSize)
{
var baseDate = new DateTime(2013, 10, 08);
var config = new SubscriptionDataConfig(typeof(Tick),
Symbols.SPY,
Resolution.Tick,
TimeZones.NewYork,
TimeZones.NewYork,
false,
false,
false,
false,
TickType.Quote);
var tick = new Tick(config, line, baseDate);
var expectedValue = (askPrice + bidPrice) / 2;
if (askPrice == 0 || bidPrice == 0)
{
expectedValue = askPrice + bidPrice;
}
var ms = (tick.Time - baseDate).TotalMilliseconds;
Assert.AreEqual(14400135, ms);
Assert.AreEqual(expectedValue, tick.Value);
Assert.AreEqual(expectedValue, tick.LastPrice);
Assert.AreEqual(0, tick.Quantity);
Assert.AreEqual(askPrice, tick.AskPrice);
Assert.AreEqual(askSize, tick.AskSize);
Assert.AreEqual(bidPrice, tick.BidPrice);
Assert.AreEqual(bidSize, tick.BidSize);
Assert.AreEqual("NASDAQ", tick.Exchange);
Assert.AreEqual("00000001", tick.SaleCondition);
Assert.IsFalse(tick.Suspicious);
}
[Test]
public void OptionWithUnderlyingEquityScaled()
{
var factory = new Tick();
var tickLine = "40560000,10000,10,NYSE,00000001,0";
var underlying = Symbol.Create("SPY", SecurityType.Equity, QuantConnect.Market.USA);
var optionSymbol = Symbol.CreateOption(
underlying,
QuantConnect.Market.USA,
OptionStyle.American,
OptionRight.Put,
4200m,
SecurityIdentifier.DefaultDate);
var config = new SubscriptionDataConfig(
typeof(Tick),
optionSymbol,
Resolution.Tick,
TimeZones.Chicago,
TimeZones.Chicago,
true,
false,
false,
false,
TickType.Trade,
true,
DataNormalizationMode.Raw);
using var stream = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(tickLine)));
var tickFromLine = (Tick)factory.Reader(config, tickLine, new DateTime(2020, 9, 22), false);
var tickFromStream = (Tick)factory.Reader(config, stream, new DateTime(2020, 9, 22), false);
Assert.AreEqual(new DateTime(2020, 9, 22, 11, 16, 0), tickFromLine.Time);
Assert.AreEqual(1m, tickFromLine.Price);
Assert.AreEqual(10, tickFromLine.Quantity);
Assert.AreEqual(new DateTime(2020, 9, 22, 11, 16, 0), tickFromStream.Time);
Assert.AreEqual(1m, tickFromStream.Price);
Assert.AreEqual(10, tickFromStream.Quantity);
}
[Test]
public void OptionWithUnderlyingFutureNotScaled()
{
var factory = new Tick();
var tickLine = "40560000,10000,10,CME,00000001,0";
var underlying = Symbol.CreateFuture("ES", QuantConnect.Market.CME, new DateTime(2021, 3, 19));
var optionSymbol = Symbol.CreateOption(
underlying,
QuantConnect.Market.CME,
OptionStyle.American,
OptionRight.Put,
4200m,
SecurityIdentifier.DefaultDate);
var config = new SubscriptionDataConfig(
typeof(Tick),
optionSymbol,
Resolution.Tick,
TimeZones.Chicago,
TimeZones.Chicago,
true,
false,
false,
false,
TickType.Trade,
true,
DataNormalizationMode.Raw);
using var stream = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(tickLine)));
var tickFromLine = (Tick)factory.Reader(config, tickLine, new DateTime(2020, 9, 22), false);
var tickFromStream = (Tick)factory.Reader(config, stream, new DateTime(2020, 9, 22), false);
Assert.AreEqual(new DateTime(2020, 9, 22, 11, 16, 0), tickFromLine.Time);
Assert.AreEqual(10000m, tickFromLine.Price);
Assert.AreEqual(10, tickFromLine.Quantity);
Assert.AreEqual(new DateTime(2020, 9, 22, 11, 16, 0), tickFromStream.Time);
Assert.AreEqual(10000m, tickFromStream.Price);
Assert.AreEqual(10, tickFromStream.Quantity);
}
[Test]
public void ExchangeSetterHandlesNonExpectedEncoding()
{
const string line = "15093000,1456300,100,P,T,0";
var baseDate = new DateTime(2013, 10, 08);
var tick = new Tick(Symbols.SPY, line, baseDate);
Assert.DoesNotThrow(()=> tick.ExchangeCode = "LL");
Assert.AreEqual(Exchange.UNKNOWN, tick.Exchange.GetPrimaryExchange(), "Failed at Exchange Property");
Assert.AreEqual((string)Exchange.UNKNOWN, tick.ExchangeCode, "Failed at ExchangeCode Property");
}
[Test]
public void ExchangeSetterHandlesDefinedExchanges()
{
var baseDate = new DateTime(2013, 10, 08);
const string line = "15093000,1456300,100,P,T,0";
var exchanges = typeof(Exchange)
.GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
.Where(p => p.PropertyType == typeof(Exchange))
.Select(propa => propa.GetValue(null))
.OfType<Exchange>()
.Where(exchange => exchange.Market == QuantConnect.Market.USA && exchange.SecurityTypes.Contains(SecurityType.Equity))
.ToList();
Assert.GreaterOrEqual(exchanges.Count, 20);
foreach (var exchange in exchanges)
{
{
var tick = new Tick(Symbols.SPY, line, baseDate);
Assert.DoesNotThrow(() => tick.ExchangeCode = exchange.Code);
Assert.AreEqual(exchange.Name, tick.Exchange, $"ExchangeCode: Failed at Exchange Property: {exchange}");
Assert.AreEqual(exchange.Code, tick.ExchangeCode, $"ExchangeCode: Failed at ExchangeCode Property: {exchange}");
}
{
var tick = new Tick(Symbols.SPY, line, baseDate);
Assert.DoesNotThrow(() => tick.Exchange = exchange);
Assert.AreEqual(exchange.Name, tick.Exchange, $"Exchange: Failed at Exchange Property: {exchange}");
Assert.AreEqual(exchange.Code, tick.ExchangeCode, $"Exchange: Failed at ExchangeCode Property: {exchange}");
}
}
}
}
}