365 lines
14 KiB
C#
365 lines
14 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;
|
|
using QuantConnect.Indicators;
|
|
|
|
namespace QuantConnect.Tests.Common.Data
|
|
{
|
|
[TestFixture]
|
|
public class BaseDataConsolidatorTests: BaseConsolidatorTests
|
|
{
|
|
[Test]
|
|
public void AggregatesTickToNewTradeBarProperly()
|
|
{
|
|
TradeBar newTradeBar = null;
|
|
using var creator = new BaseDataConsolidator(4);
|
|
creator.DataConsolidated += (sender, tradeBar) =>
|
|
{
|
|
newTradeBar = tradeBar;
|
|
};
|
|
var reference = DateTime.Today;
|
|
var bar1 = new Tick
|
|
{
|
|
Symbol = Symbols.SPY,
|
|
Time = reference,
|
|
Value = 5,
|
|
Quantity = 10
|
|
};
|
|
creator.Update(bar1);
|
|
Assert.IsNull(newTradeBar);
|
|
|
|
var bar2 = new Tick
|
|
{
|
|
Symbol = Symbols.SPY,
|
|
Time = reference.AddHours(1),
|
|
Value = 10,
|
|
Quantity = 20
|
|
};
|
|
creator.Update(bar2);
|
|
Assert.IsNull(newTradeBar);
|
|
var bar3 = new Tick
|
|
{
|
|
Symbol = Symbols.SPY,
|
|
Time = reference.AddHours(2),
|
|
Value = 1,
|
|
Quantity = 10
|
|
};
|
|
creator.Update(bar3);
|
|
Assert.IsNull(newTradeBar);
|
|
|
|
var bar4 = new Tick
|
|
{
|
|
Symbol = Symbols.SPY,
|
|
Time = reference.AddHours(3),
|
|
Value = 9,
|
|
Quantity = 20
|
|
};
|
|
creator.Update(bar4);
|
|
Assert.IsNotNull(newTradeBar);
|
|
|
|
Assert.AreEqual(Symbols.SPY, newTradeBar.Symbol);
|
|
Assert.AreEqual(bar1.Time, newTradeBar.Time);
|
|
Assert.AreEqual(bar1.Value, newTradeBar.Open);
|
|
Assert.AreEqual(bar2.Value, newTradeBar.High);
|
|
Assert.AreEqual(bar3.Value, newTradeBar.Low);
|
|
Assert.AreEqual(bar4.Value, newTradeBar.Close);
|
|
Assert.AreEqual(bar4.EndTime, newTradeBar.EndTime);
|
|
|
|
// base data can't aggregate volume
|
|
Assert.AreEqual(0, newTradeBar.Volume);
|
|
}
|
|
|
|
[Test]
|
|
public void DoesNotConsolidateDifferentSymbols()
|
|
{
|
|
using var consolidator = new BaseDataConsolidator(2);
|
|
|
|
var reference = DateTime.Today;
|
|
|
|
var tb1 = new Tick
|
|
{
|
|
Symbol = Symbols.AAPL,
|
|
Time = reference,
|
|
Value = 5,
|
|
Quantity = 10
|
|
};
|
|
|
|
var tb2 = new Tick
|
|
{
|
|
Symbol = Symbols.ZNGA,
|
|
Time = reference,
|
|
Value = 2,
|
|
Quantity = 5
|
|
};
|
|
|
|
consolidator.Update(tb1);
|
|
|
|
Exception ex = Assert.Throws<InvalidOperationException>(() => consolidator.Update(tb2));
|
|
Assert.IsTrue(ex.Message.Contains("is not the same"));
|
|
}
|
|
|
|
[Test]
|
|
public void AggregatesTradeBarsProperly()
|
|
{
|
|
TradeBar newTradeBar = null;
|
|
using var creator = new TradeBarConsolidator(4);
|
|
creator.DataConsolidated += (sender, args) =>
|
|
{
|
|
newTradeBar = args;
|
|
};
|
|
|
|
var time = DateTime.Today;
|
|
var period = TimeSpan.FromMinutes(1);
|
|
var bar1 = new TradeBar
|
|
{
|
|
Time = time,
|
|
Symbol = Symbols.SPY,
|
|
Open = 1,
|
|
High = 2,
|
|
Low = 0.75m,
|
|
Close = 1.25m,
|
|
Period = period
|
|
};
|
|
creator.Update(bar1);
|
|
Assert.IsNull(newTradeBar);
|
|
|
|
var bar2 = new TradeBar
|
|
{
|
|
Time = time + TimeSpan.FromMinutes(1),
|
|
Symbol = Symbols.SPY,
|
|
Open = 1.1m,
|
|
High = 2.2m,
|
|
Low = 0.9m,
|
|
Close = 2.1m,
|
|
Period = period
|
|
};
|
|
creator.Update(bar2);
|
|
Assert.IsNull(newTradeBar);
|
|
|
|
var bar3 = new TradeBar
|
|
{
|
|
Time = time + TimeSpan.FromMinutes(2),
|
|
Symbol = Symbols.SPY,
|
|
Open = 1,
|
|
High = 2,
|
|
Low = 0.1m,
|
|
Close = 1.75m,
|
|
Period = period
|
|
};
|
|
creator.Update(bar3);
|
|
Assert.IsNull(newTradeBar);
|
|
|
|
var bar4 = new TradeBar
|
|
{
|
|
Time = time + TimeSpan.FromMinutes(3),
|
|
Symbol = Symbols.SPY,
|
|
Open = 1,
|
|
High = 7,
|
|
Low = 0.5m,
|
|
Close = 4.4m,
|
|
Period = period
|
|
};
|
|
creator.Update(bar4);
|
|
Assert.IsNotNull(newTradeBar);
|
|
Assert.AreEqual(bar1.Symbol, newTradeBar.Symbol);
|
|
Assert.AreEqual(1, newTradeBar.Open);
|
|
Assert.AreEqual(7, newTradeBar.High);
|
|
Assert.AreEqual(0.1m, newTradeBar.Low);
|
|
Assert.AreEqual(4.4m, newTradeBar.Close);
|
|
Assert.AreEqual(newTradeBar.Close, newTradeBar.Value);
|
|
Assert.AreEqual(bar4.EndTime, newTradeBar.EndTime);
|
|
Assert.AreEqual(TimeSpan.FromMinutes(4), newTradeBar.Period);
|
|
|
|
Assert.AreEqual(bar1.Volume + bar2.Volume + bar3.Volume + bar4.Volume, newTradeBar.Volume);
|
|
|
|
}
|
|
|
|
|
|
[Test]
|
|
public void AggregatesPeriodInCountModeWithHourlyData()
|
|
{
|
|
TradeBar consolidated = null;
|
|
using var consolidator = new BaseDataConsolidator(2);
|
|
consolidator.DataConsolidated += (sender, bar) =>
|
|
{
|
|
consolidated = bar;
|
|
};
|
|
|
|
var reference = new DateTime(2015, 04, 13);
|
|
consolidator.Update(new Tick { Time = reference });
|
|
Assert.IsNull(consolidated);
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddHours(1) });
|
|
Assert.IsNotNull(consolidated);
|
|
|
|
// The EndTime of the consolidated bar should match the EndTime of the last data point
|
|
Assert.AreEqual(reference.AddHours(1), consolidated.EndTime);
|
|
Assert.AreEqual(TimeSpan.FromHours(1), consolidated.Period);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddHours(2) });
|
|
Assert.IsNull(consolidated);
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddHours(3) });
|
|
Assert.IsNotNull(consolidated);
|
|
|
|
Assert.AreEqual(reference.AddHours(3), consolidated.EndTime);
|
|
Assert.AreEqual(TimeSpan.FromHours(1), consolidated.Period);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddHours(4) });
|
|
Assert.IsNull(consolidated);
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddHours(5) });
|
|
Assert.IsNotNull(consolidated);
|
|
|
|
Assert.AreEqual(reference.AddHours(5), consolidated.EndTime);
|
|
Assert.AreEqual(TimeSpan.FromHours(1), consolidated.Period);
|
|
}
|
|
|
|
[Test]
|
|
public void AggregatesPeriodInPeriodModeWithDailyData()
|
|
{
|
|
TradeBar consolidated = null;
|
|
using var consolidator = new BaseDataConsolidator(TimeSpan.FromDays(1));
|
|
consolidator.DataConsolidated += (sender, bar) =>
|
|
{
|
|
consolidated = bar;
|
|
};
|
|
|
|
var reference = new DateTime(2015, 04, 13);
|
|
consolidator.Update(new Tick { Time = reference });
|
|
Assert.IsNull(consolidated);
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(1) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(2) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(3) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
}
|
|
|
|
[Test]
|
|
public void AggregatesPeriodInPeriodModeWithDailyDataAndRoundedTime()
|
|
{
|
|
TradeBar consolidated = null;
|
|
using var consolidator = new BaseDataConsolidator(TimeSpan.FromDays(1));
|
|
consolidator.DataConsolidated += (sender, bar) =>
|
|
{
|
|
consolidated = bar;
|
|
};
|
|
|
|
var reference = new DateTime(2015, 04, 13);
|
|
consolidator.Update(new Tick { Time = reference.AddSeconds(45) });
|
|
Assert.IsNull(consolidated);
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(1).AddMinutes(1) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
Assert.AreEqual(reference, consolidated.Time);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(2).AddHours(1).AddMinutes(1).AddSeconds(1) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
Assert.AreEqual(reference.AddDays(1), consolidated.Time);
|
|
consolidated = null;
|
|
|
|
consolidator.Update(new Tick { Time = reference.AddDays(3) });
|
|
Assert.IsNotNull(consolidated);
|
|
Assert.AreEqual(TimeSpan.FromDays(1), consolidated.Period);
|
|
Assert.AreEqual(reference.AddDays(2), consolidated.Time);
|
|
}
|
|
|
|
[Test]
|
|
public void ConsolidatesWithRegisterIndicator()
|
|
{
|
|
using var consolidator = new BaseDataConsolidator(TimeSpan.FromMinutes(5));
|
|
consolidator.DataConsolidated += OnFiveMinutes;
|
|
|
|
indicator = new SimpleMovingAverage(2);
|
|
RegisterIndicator(indicator, consolidator);
|
|
|
|
var time = DateTime.Today.AddHours(9);
|
|
for (var i = 1; i < 100; i++)
|
|
{
|
|
consolidator.Update(new Tick(time.AddMinutes(i - 1), Symbols.SPY, i, i, i));
|
|
}
|
|
}
|
|
|
|
private SimpleMovingAverage indicator;
|
|
|
|
private void OnFiveMinutes(object sender, TradeBar e)
|
|
{
|
|
if (!indicator.IsReady) return;
|
|
|
|
var previous = e.Value - e.Period.Minutes;
|
|
var actual = (e.Value + previous) / indicator.Period;
|
|
Assert.AreEqual(indicator, actual);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Simplified version of QCAlgorithm.RegisterIndicator
|
|
/// </summary>
|
|
/// <param name="indicator">The indicator to receive data from the consolidator</param>
|
|
/// <param name="consolidator">The consolidator to receive raw subscription data</param>
|
|
public void RegisterIndicator(IndicatorBase<IndicatorDataPoint> indicator, IDataConsolidator consolidator)
|
|
{
|
|
consolidator.DataConsolidated += (sender, consolidated) =>
|
|
{
|
|
indicator.Update(consolidated.EndTime, consolidated.Value);
|
|
};
|
|
}
|
|
|
|
protected override IDataConsolidator CreateConsolidator()
|
|
{
|
|
return new BaseDataConsolidator(4);
|
|
}
|
|
|
|
protected override IEnumerable<IBaseData> GetTestValues()
|
|
{
|
|
var time = new DateTime(2015, 04, 13, 8, 31, 0);
|
|
return new List<TradeBar>()
|
|
{
|
|
new TradeBar(){ Time = time, Period = Time.OneMinute, Symbol = Symbols.SPY, High = 10 },
|
|
new TradeBar(){ Time = time.AddMinutes(1), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 12, Close = 5 },
|
|
new TradeBar(){ Time = time.AddMinutes(2), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 10, Close = 7 },
|
|
new TradeBar(){ Time = time.AddMinutes(3), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 5, Close = 2 },
|
|
new TradeBar(){ Time = time.AddMinutes(4), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 15 , Close = 2 },
|
|
new TradeBar(){ Time = time.AddMinutes(5), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 20 , Close = 2 },
|
|
new TradeBar(){ Time = time.AddMinutes(6), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 18 , Close = 8 },
|
|
new TradeBar(){ Time = time.AddMinutes(7), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 12 , Close = 4 },
|
|
new TradeBar(){ Time = time.AddMinutes(8), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 25 , Close = 5 },
|
|
new TradeBar(){ Time = time.AddMinutes(9), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 30 , Close = 4 },
|
|
new TradeBar(){ Time = time.AddMinutes(10), Period = Time.OneMinute, Symbol = Symbols.SPY, High = 26 , Close = 7 },
|
|
};
|
|
}
|
|
}
|
|
}
|