Files
quantconnect--lean/Tests/Common/Data/TickQuoteBarConsolidatorTests.cs
2026-07-13 13:02:50 +08:00

292 lines
11 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.Collections.Generic;
using NUnit.Framework;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Market;
namespace QuantConnect.Tests.Common.Data
{
[TestFixture]
public class TickQuoteBarConsolidatorTests: BaseConsolidatorTests
{
[Test]
public void AggregatesNewQuoteBarProperly()
{
QuoteBar quoteBar = null;
using var creator = new TickQuoteBarConsolidator(4);
creator.DataConsolidated += (sender, args) =>
{
quoteBar = args;
};
var reference = DateTime.Today;
var tick1 = new Tick
{
Symbol = Symbols.SPY,
Time = reference,
BidPrice = 10,
BidSize = 20,
TickType = TickType.Quote
};
creator.Update(tick1);
Assert.IsNull(quoteBar);
var tick2 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddHours(1),
AskPrice = 20,
AskSize = 10,
TickType = TickType.Quote
};
var badTick = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddHours(1),
AskPrice = 25,
AskSize = 100,
BidPrice = -100,
BidSize = 2,
Value = 50,
Quantity = 1234,
TickType = TickType.Trade
};
creator.Update(badTick);
Assert.IsNull(quoteBar);
creator.Update(tick2);
Assert.IsNull(quoteBar);
var tick3 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddHours(2),
BidPrice = 12,
BidSize = 50,
TickType = TickType.Quote
};
creator.Update(tick3);
Assert.IsNull(quoteBar);
var tick4 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddHours(3),
AskPrice = 17,
AskSize = 15,
TickType = TickType.Quote
};
creator.Update(tick4);
Assert.IsNotNull(quoteBar);
Assert.AreEqual(Symbols.SPY, quoteBar.Symbol);
Assert.AreEqual(tick1.Time, quoteBar.Time);
Assert.AreEqual(tick4.EndTime, quoteBar.EndTime);
Assert.AreEqual(tick1.BidPrice, quoteBar.Bid.Open);
Assert.AreEqual(tick1.BidPrice, quoteBar.Bid.Low);
Assert.AreEqual(tick3.BidPrice, quoteBar.Bid.High);
Assert.AreEqual(tick3.BidPrice, quoteBar.Bid.Close);
Assert.AreEqual(tick3.BidSize, quoteBar.LastBidSize);
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.Open);
Assert.AreEqual(tick4.AskPrice, quoteBar.Ask.Low);
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.High);
Assert.AreEqual(tick4.AskPrice, quoteBar.Ask.Close);
Assert.AreEqual(tick4.AskSize, quoteBar.LastAskSize);
}
[Test]
public void DoesNotConsolidateDifferentSymbols()
{
using var consolidator = new TickQuoteBarConsolidator(2);
var reference = DateTime.Today;
var tick1 = new Tick
{
Symbol = Symbols.AAPL,
Time = reference,
BidPrice = 1000,
BidSize = 20,
TickType = TickType.Quote,
};
var tick2 = new Tick
{
Symbol = Symbols.ZNGA,
Time = reference,
BidPrice = 20,
BidSize = 30,
TickType = TickType.Quote,
};
consolidator.Update(tick1);
Exception ex = Assert.Throws<InvalidOperationException>(() => consolidator.Update(tick2));
Assert.IsTrue(ex.Message.Contains("is not the same"));
}
[Test]
public void LastCloseAndCurrentOpenPriceShouldBeSameConsolidatedOnCount()
{
QuoteBar quoteBar = null;
using var creator = new TickQuoteBarConsolidator(2);
creator.DataConsolidated += (sender, args) =>
{
quoteBar = args;
};
var reference = DateTime.Today;
var tick1 = new Tick
{
Symbol = Symbols.SPY,
Time = reference,
TickType = TickType.Quote,
AskPrice = 0,
BidPrice = 24,
};
creator.Update(tick1);
var tick2 = new Tick
{
Symbol = Symbols.SPY,
Time = reference,
TickType = TickType.Quote,
AskPrice = 25,
BidPrice = 0,
};
creator.Update(tick2);
// bar 1 emitted
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.Open);
Assert.AreEqual(tick1.BidPrice, quoteBar.Bid.Open);
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.Close);
Assert.AreEqual(tick1.BidPrice, quoteBar.Bid.Close);
var tick3 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddSeconds(1),
TickType = TickType.Quote,
AskPrice = 36,
BidPrice = 35,
};
creator.Update(tick3);
creator.Update(tick3);
// bar 2 emitted
// ask is from tick 2
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.Open, "Ask Open not equal to Previous Close");
// bid is from tick 1
Assert.AreEqual(tick1.BidPrice, quoteBar.Bid.Open, "Bid Open not equal to Previous Close");
Assert.AreEqual(tick3.AskPrice, quoteBar.Ask.Close, "Ask Close incorrect");
Assert.AreEqual(tick3.BidPrice, quoteBar.Bid.Close, "Bid Close incorrect");
}
[Test]
public void LastCloseAndCurrentOpenPriceShouldBeSameConsolidatedOnTimeSpan()
{
QuoteBar quoteBar = null;
using var creator = new TickQuoteBarConsolidator(TimeSpan.FromMinutes(1));
creator.DataConsolidated += (sender, args) =>
{
quoteBar = args;
};
var reference = DateTime.Today;
// timeframe 1
var tick1 = new Tick
{
Symbol = Symbols.SPY,
Time = reference,
TickType = TickType.Quote,
AskPrice = 25,
BidPrice = 24,
};
creator.Update(tick1);
var tick2 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddSeconds(1),
TickType = TickType.Quote,
AskPrice = 26,
BidPrice = 0,
};
creator.Update(tick2);
var tick3 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddSeconds(1),
TickType = TickType.Quote,
AskPrice = 0,
BidPrice = 25,
};
creator.Update(tick3);
// timeframe 2
var tick4 = new Tick
{
Symbol = Symbols.SPY,
Time = reference.AddMinutes(1),
TickType = TickType.Quote,
AskPrice = 36,
BidPrice = 35,
};
creator.Update(tick4);
//force the consolidator to emit DataConsolidated
creator.Scan(reference.AddMinutes(2));
// bid is from tick 2
Assert.AreEqual(tick2.AskPrice, quoteBar.Ask.Open, "Ask Open not equal to Previous Close");
// bid is from tick 3
Assert.AreEqual(tick3.BidPrice, quoteBar.Bid.Open, "Bid Open not equal to Previous Close");
Assert.AreEqual(tick4.AskPrice, quoteBar.Ask.Close, "Ask Close incorrect");
Assert.AreEqual(tick4.BidPrice, quoteBar.Bid.Close, "Bid Close incorrect");
}
protected override IDataConsolidator CreateConsolidator()
{
return new TickQuoteBarConsolidator(2);
}
protected override IEnumerable<IBaseData> GetTestValues()
{
var time = DateTime.Today;
return new List<Tick>()
{
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time, Value = 10, AskPrice = 10, BidPrice = 5 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(1), Value = 2, AskPrice = 10, BidPrice = 7 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(2), Value = 8, AskPrice = 11, BidPrice = 9 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(3), Value = 5, AskPrice = 15, BidPrice = 6 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(4), Value = 13, AskPrice = 15, BidPrice = 7 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(5), Value = 15 , AskPrice = 13, BidPrice = 8 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(6), Value = 10 , AskPrice = 14, BidPrice = 7 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(7), Value = 11 , AskPrice = 13, BidPrice = 8 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(8), Value = 11 , AskPrice = 14, BidPrice = 6 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(9), Value = 4 , AskPrice = 14, BidPrice = 9 },
new Tick(){Symbol = Symbols.SPY, TickType = TickType.Quote, Time = time.AddSeconds(10), Value = 7 , AskPrice = 13, BidPrice = 5 },
};
}
}
}