5116 lines
258 KiB
C#
5116 lines
258 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 NodaTime;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Python.Runtime;
|
|
using NUnit.Framework;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Util;
|
|
using QuantConnect.Python;
|
|
using System.Globalization;
|
|
using QuantConnect.Packets;
|
|
using QuantConnect.Algorithm;
|
|
using QuantConnect.Securities;
|
|
using QuantConnect.Interfaces;
|
|
using QuantConnect.Data.Market;
|
|
using System.Collections.Generic;
|
|
using QuantConnect.Lean.Engine.Storage;
|
|
using QuantConnect.Lean.Engine.DataFeeds;
|
|
using QuantConnect.Tests.Engine.DataFeeds;
|
|
using QuantConnect.Lean.Engine.HistoricalData;
|
|
using HistoryRequest = QuantConnect.Data.HistoryRequest;
|
|
using QuantConnect.Data.Fundamental;
|
|
using QuantConnect.Data.UniverseSelection;
|
|
using QuantConnect.Tests.Common.Data.Fundamental;
|
|
using QuantConnect.Logging;
|
|
|
|
namespace QuantConnect.Tests.Algorithm
|
|
{
|
|
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
|
public class AlgorithmHistoryTests
|
|
{
|
|
private QCAlgorithm _algorithm;
|
|
private TestHistoryProvider _testHistoryProvider;
|
|
private IDataProvider _dataProvider;
|
|
private IMapFileProvider _mapFileProvider;
|
|
private IDataCacheProvider _cacheProvider;
|
|
private IFactorFileProvider _factorFileProvider;
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
_algorithm = new QCAlgorithm();
|
|
_algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(_algorithm));
|
|
_algorithm.HistoryProvider = _testHistoryProvider = new TestHistoryProvider();
|
|
}
|
|
|
|
[OneTimeSetUp]
|
|
public void OneTimeSetUp()
|
|
{
|
|
_dataProvider = TestGlobals.DataProvider;
|
|
_mapFileProvider = TestGlobals.MapFileProvider;
|
|
_factorFileProvider = TestGlobals.FactorFileProvider;
|
|
_cacheProvider = TestGlobals.DataCacheProvider;
|
|
FundamentalService.Initialize(_dataProvider, new NullFundamentalDataProvider(), false);
|
|
}
|
|
|
|
[TestCase(Language.Python)]
|
|
[TestCase(Language.CSharp)]
|
|
public void FundamentalHistory(Language language)
|
|
{
|
|
var start = new DateTime(2014, 04, 07);
|
|
_algorithm = GetAlgorithm(start);
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var result = _algorithm.History<Fundamental>(new[] { Symbols.SPY }, 10).ToList();
|
|
|
|
Assert.AreEqual(10, result.Count);
|
|
Assert.IsTrue(result.All(fundamentals =>
|
|
{
|
|
Assert.AreEqual(1, fundamentals.Count);
|
|
Assert.IsTrue(fundamentals.Values.All(x => !x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingFinancingActivities.HasValue));
|
|
return fundamentals.Values.All(x => double.IsNaN(x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingFinancingActivities));
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistory(algorithm, symbol):
|
|
return algorithm.History(Fundamental, symbol, 10)
|
|
").GetAttr("getHistory");
|
|
_algorithm.SetPandasConverter();
|
|
|
|
var result = getHistory.Invoke(_algorithm.ToPython(), Symbols.SPY.ToPython());
|
|
Assert.AreEqual(10, result.GetAttr("shape")[0].As<int>());
|
|
|
|
dynamic subDataFrame = result.GetAttr("loc")[Symbols.SPY.ID.ToString()];
|
|
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
var index = subDataFrame.index[i];
|
|
var series = subDataFrame.loc[index];
|
|
var cashFlow = (double)series.financialstatements.CashFlowStatement.CashFlowFromContinuingFinancingActivities.Value;
|
|
Assert.AreEqual(double.NaN, cashFlow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Resolution.Daily, Language.CSharp, 2)]
|
|
[TestCase(Resolution.Hour, Language.CSharp, 14)]
|
|
[TestCase(Resolution.Minute, Language.CSharp, 780)]
|
|
[TestCase(Resolution.Second, Language.CSharp, 45676)]
|
|
[TestCase(Resolution.Daily, Language.Python, 2)]
|
|
[TestCase(Resolution.Hour, Language.Python, 14)]
|
|
[TestCase(Resolution.Minute, Language.Python, 780)]
|
|
[TestCase(Resolution.Second, Language.Python, 45676)]
|
|
public void TickResolutionSubscriptionHistoryRequestOtherResolution(Resolution resolution, Language language, int expectedHistoryCount)
|
|
{
|
|
var start = new DateTime(2013, 10, 07);
|
|
_algorithm = GetAlgorithm(start.AddDays(2));
|
|
|
|
_algorithm.AddEquity(Symbols.SPY.Value, Resolution.Tick);
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// Trades and quotes
|
|
var result = _algorithm.History(new[] { Symbols.SPY }, start, _algorithm.Time, resolution).ToList();
|
|
|
|
var expectedSpan = resolution == Resolution.Daily ? TimeSpan.FromHours(6.5) : resolution.ToTimeSpan();
|
|
Assert.AreEqual(expectedHistoryCount, result.Count);
|
|
Assert.IsTrue(result.All(slice =>
|
|
{
|
|
foreach (var bar in slice.Bars.Values)
|
|
{
|
|
return (bar.EndTime - bar.Time) == expectedSpan;
|
|
}
|
|
foreach (var bar in slice.QuoteBars.Values)
|
|
{
|
|
return (bar.EndTime - bar.Time) == expectedSpan;
|
|
}
|
|
|
|
return false;
|
|
}));
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistory(algorithm, symbol, start, resolution):
|
|
return algorithm.History(symbol, start, algorithm.Time, resolution).loc[symbol]
|
|
").GetAttr("getHistory");
|
|
|
|
_algorithm.SetPandasConverter();
|
|
|
|
var result = getHistory.Invoke(_algorithm.ToPython(), Symbols.SPY.ToPython(), start.ToPython(), resolution.ToPython());
|
|
Assert.AreEqual(expectedHistoryCount, result.GetAttr("shape")[0].As<int>());
|
|
|
|
var times = result.GetAttr("index").GetAttr("tolist").Invoke().As<List<DateTime>>();
|
|
|
|
var prevDateTime = times[0].Subtract(resolution.ToTimeSpan());
|
|
Assert.IsTrue(times.Any(time =>
|
|
{
|
|
var timeSpan = time - prevDateTime;
|
|
prevDateTime = time;
|
|
return timeSpan == resolution.ToTimeSpan();
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetHistoryWithCustomDataAndUnsortedData()
|
|
{
|
|
var customDataKey = "CustomData/ExampleCustomData";
|
|
|
|
var customData = new string[] {
|
|
"2024-10-03 19:00:00,5894.5,5948.85,5887.95,5935.1,120195681,4882.29",
|
|
"2024-10-02 18:00:00,5991.2,6038.2,5980.95,6030.8,116275729,4641.97",
|
|
"2024-10-01 17:00:00,6000.5,6019,5951.15,6009,127707078,6591.27",
|
|
"2024-09-30 11:00:00,6000.5,6019,5951.15,6009,127707078,6591.27",
|
|
"2024-09-27 10:00:00,5894.5,5948.85,5887.95,5935.1,120195681,4882.29",
|
|
"2024-09-26 09:00:00,5869.9,5879.35,5802.85,5816.7,117516350,4820.53",
|
|
"2024-09-25 08:00:00,5834.6,5864.95,5834.6,5859,110427867,4661.55",
|
|
"2024-09-24 07:00:00,5833.15,5833.85,5775.55,5811.55,127624733,4823.52",
|
|
"2024-09-23 06:00:00,5889.95,5900.45,5858.45,5867.9,123586417,4303.93",
|
|
"2024-09-20 05:00:00,5794.75,5848.2,5786.05,5836.95,151929179,5429.87",
|
|
"2024-09-19 04:00:00,5811.95,5815,5760.4,5770.9,160523863,5219.24",
|
|
"2024-09-18 03:00:00,5885.5,5898.8,5852.3,5857.55,145721790,5163.09",
|
|
"2024-09-17 02:00:00,5834.1,5904.35,5822.2,5898.85,144794030,5405.72",
|
|
"2024-09-16 01:00:00,5749.5,5852.95,5749.5,5842.2,214402430,8753.33",
|
|
"2024-09-13 16:00:00,5894.5,5948.85,5887.95,5935.1,120195681,4882.29",
|
|
"2024-09-12 15:00:00,5984.7,6051.1,5974.55,6038.05,171728134,7774.83",
|
|
"2024-09-11 14:00:00,5972.25,5989.8,5926.75,5973.3,191516153,8349.59",
|
|
"2024-09-10 13:00:00,5930.8,5966.05,5910.95,5955.25,151162819,5915.8",
|
|
"2024-09-09 12:00:00,5991.2,6038.2,5980.95,6030.8,116275729,4641.97"
|
|
};
|
|
|
|
var endDateAlgorithm = new DateTime(2024, 10, 4);
|
|
var algorithm = GetAlgorithm(endDateAlgorithm.AddDays(1));
|
|
|
|
ExampleCustomDataWithSort.CustomDataKey = customDataKey;
|
|
|
|
var customSymbol = algorithm.AddData<ExampleCustomDataWithSort>("ExampleCustomData", Resolution.Daily).Symbol;
|
|
|
|
algorithm.ObjectStore.Save(customDataKey, string.Join("\n", customData));
|
|
|
|
var history = algorithm.History<ExampleCustomDataWithSort>(customSymbol, algorithm.StartDate, algorithm.EndDate, Resolution.Daily).ToList();
|
|
|
|
Assert.IsNotEmpty(history);
|
|
Assert.That(history.Count, Is.EqualTo(customData.Length));
|
|
|
|
for (int i = 0; i < history.Count - 1; i++)
|
|
{
|
|
if (history[i].EndTime > history[i + 1].EndTime)
|
|
{
|
|
Assert.Fail($"Order failure: {history[i].EndTime} > {history[i + 1].EndTime} at index {i}.");
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void TickResolutionHistoryRequest(Language language)
|
|
{
|
|
var start = new DateTime(2013, 10, 07);
|
|
_algorithm = GetAlgorithm(start.AddDays(1));
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var historyStart = start.AddHours(9.5);
|
|
var historyEnd = historyStart.AddSeconds(5);
|
|
var result = _algorithm.History(new[] { Symbols.SPY }, historyStart, historyEnd, Resolution.Tick).ToList();
|
|
var result2 = _algorithm.History<Tick>(Symbols.SPY, historyStart, historyEnd, Resolution.Tick).ToList();
|
|
|
|
Assert.IsNotEmpty(result);
|
|
Assert.IsNotEmpty(result2);
|
|
|
|
Assert.IsTrue(result2.Any(tick => tick.TickType == TickType.Trade));
|
|
Assert.IsTrue(result2.Any(tick => tick.TickType == TickType.Quote));
|
|
|
|
var resultTickCount = result.Sum(slice => slice.Ticks[Symbols.SPY].Count);
|
|
Assert.AreEqual(resultTickCount, result2.Count);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var pythonModule = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getTradesAndQuotesHistory(algorithm, symbol, start):
|
|
return algorithm.History([symbol], start + timedelta(hours=12), start + timedelta(hours=12.2), Resolution.Second).loc[symbol].to_dict()
|
|
|
|
def getTradesOnlyHistory(algorithm, symbol, start):
|
|
history_start = start + timedelta(hours=9.5)
|
|
history_end = history_start + timedelta(seconds=5)
|
|
return algorithm.History(Tick, symbol, history_start, history_end, Resolution.Tick).loc[symbol].to_dict()
|
|
");
|
|
using var getTradesAndQuotesHistory = pythonModule.GetAttr("getTradesAndQuotesHistory");
|
|
using var getTradesOnlyHistory = pythonModule.GetAttr("getTradesOnlyHistory");
|
|
_algorithm.SetPandasConverter();
|
|
using var pySymbol = Symbols.SPY.ToPython();
|
|
using var pyAlgorithm = _algorithm.ToPython();
|
|
using var pyStart = start.ToPython();
|
|
|
|
using var dict = getTradesAndQuotesHistory.Invoke(pyAlgorithm, pySymbol, pyStart);
|
|
var result = GetDataFrameDictionary<dynamic>(dict);
|
|
using var dict2 = getTradesOnlyHistory.Invoke(pyAlgorithm, pySymbol, pyStart);
|
|
var result2 = GetDataFrameDictionary<dynamic>(dict2);
|
|
|
|
Assert.IsNotEmpty(result);
|
|
Assert.IsNotEmpty(result2);
|
|
|
|
CollectionAssert.AreNotEquivalent(result.Keys, result2.Keys);
|
|
CollectionAssert.IsNotSubsetOf(result2.Keys, result.Keys);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ImplicitTickResolutionHistoryRequestTradeBarDoesNotThrowsException()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 1));
|
|
algorithm.SetStartDate(2013, 10, 8);
|
|
var spy = algorithm.AddEquity("SPY", Resolution.Tick).Symbol;
|
|
Assert.AreEqual(100, algorithm.History(spy, 100).Count());
|
|
}
|
|
|
|
[Test]
|
|
public void ExplicitTickResolutionHistoryRequestTradeBarApiThrowsException()
|
|
{
|
|
var spy = _algorithm.AddEquity("SPY", Resolution.Tick).Symbol;
|
|
Assert.Throws<InvalidOperationException>(() => _algorithm.History(spy, 1, Resolution.Tick).ToList());
|
|
}
|
|
|
|
[TestCase(Resolution.Tick, Resolution.Tick, true)]
|
|
[TestCase(Resolution.Tick, Resolution.Second, true)]
|
|
[TestCase(Resolution.Tick, Resolution.Minute, true)]
|
|
[TestCase(Resolution.Tick, Resolution.Hour, true)]
|
|
[TestCase(Resolution.Tick, Resolution.Daily, true)]
|
|
[TestCase(Resolution.Second, Resolution.Tick, true)]
|
|
[TestCase(Resolution.Second, Resolution.Second, true)]
|
|
[TestCase(Resolution.Second, Resolution.Minute, true)]
|
|
[TestCase(Resolution.Second, Resolution.Hour, true)]
|
|
[TestCase(Resolution.Second, Resolution.Daily, true)]
|
|
[TestCase(Resolution.Minute, Resolution.Tick, true)]
|
|
[TestCase(Resolution.Minute, Resolution.Second, true)]
|
|
[TestCase(Resolution.Minute, Resolution.Minute, true)]
|
|
[TestCase(Resolution.Minute, Resolution.Hour, true)]
|
|
[TestCase(Resolution.Minute, Resolution.Daily, true)]
|
|
[TestCase(Resolution.Hour, Resolution.Tick, false)]
|
|
[TestCase(Resolution.Hour, Resolution.Second, false)]
|
|
[TestCase(Resolution.Hour, Resolution.Minute, false)]
|
|
[TestCase(Resolution.Hour, Resolution.Hour, false)]
|
|
[TestCase(Resolution.Hour, Resolution.Daily, false)]
|
|
[TestCase(Resolution.Daily, Resolution.Tick, false)]
|
|
[TestCase(Resolution.Daily, Resolution.Second, false)]
|
|
[TestCase(Resolution.Daily, Resolution.Minute, false)]
|
|
[TestCase(Resolution.Daily, Resolution.Hour, false)]
|
|
[TestCase(Resolution.Daily, Resolution.Daily, false)]
|
|
public void VerifyReceivedDataBasedOnHistoryResolutionOnly(Resolution historyResolution, Resolution equityResolution, bool expected)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 1));
|
|
algorithm.SetStartDate(2013, 10, 8);
|
|
var spy = algorithm.AddEquity("SPY", Resolution.Minute).Symbol;
|
|
var ibm = algorithm.AddEquity("IBM", equityResolution).Symbol;
|
|
|
|
// Flags to check if there's Quote data for SPY and IBM
|
|
var spyFlag = false;
|
|
var ibmFlag = false;
|
|
|
|
// If the history resolution is Tick, check for Quote-type ticks
|
|
if (historyResolution == Resolution.Tick)
|
|
{
|
|
var start = new DateTime(2013, 10, 7, 9, 30, 0);
|
|
var allHistory = algorithm.History(new[] { spy, ibm }, start, start.AddSeconds(5), historyResolution).SelectMany(slice => slice.AllData);
|
|
// Filter the data to get only the Quote-type ticks
|
|
var ticks = allHistory.OfType<Tick>().Where(e => e.TickType == TickType.Quote).ToList();
|
|
spyFlag = ticks.Any(e => e.Symbol == spy);
|
|
ibmFlag = ticks.Any(e => e.Symbol == ibm);
|
|
}
|
|
else
|
|
{
|
|
var period = historyResolution != Resolution.Second ? TimeSpan.FromDays(1) : TimeSpan.FromHours(12);
|
|
var allHistory = algorithm.History(new[] { spy, ibm }, period, historyResolution).SelectMany(slice => slice.AllData).ToList();
|
|
// Checking for QuoteBar data for SPY and IBM
|
|
var quoteBars = allHistory.Where(e => e.DataType == MarketDataType.QuoteBar).ToList();
|
|
spyFlag |= quoteBars.Any(e => e.Symbol == spy);
|
|
ibmFlag |= quoteBars.Any(e => e.Symbol == ibm);
|
|
}
|
|
|
|
// Assert that the flags match the expected value
|
|
Assert.AreEqual(expected, spyFlag);
|
|
Assert.AreEqual(expected, ibmFlag);
|
|
}
|
|
|
|
[Test]
|
|
public void VerifyHistorySupportsSpecificDataTypes()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
algorithm.SetStartDate(2013, 10, 10);
|
|
var spy = algorithm.AddEquity("SPY", Resolution.Minute).Symbol;
|
|
var ibm = algorithm.AddEquity("IBM", Resolution.Hour).Symbol;
|
|
|
|
var tradeBarHistory = algorithm.History<TradeBar>(new[] { spy, ibm }, TimeSpan.FromDays(1), Resolution.Minute).ToList();
|
|
var generalHistory = algorithm.History(new[] { spy, ibm }, TimeSpan.FromDays(1), Resolution.Minute).ToList();
|
|
|
|
// Extract all TradeBars
|
|
var tradeBars = tradeBarHistory.SelectMany(slice => slice.Values).ToList();
|
|
|
|
// Filter and extract only TradeBars from the general history
|
|
var filteredTradeBars = generalHistory.SelectMany(slice => slice.AllData).Where(e => e.DataType == MarketDataType.TradeBar).ToList();
|
|
|
|
// Assert that the count of TradeBars in both methods is consistent
|
|
Assert.AreEqual(filteredTradeBars.Count, tradeBars.Count);
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void TickResolutionPeriodBasedHistoryRequestThrowsException(Language language)
|
|
{
|
|
var spy = _algorithm.AddEquity("SPY", Resolution.Tick).Symbol;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
Assert.Throws<InvalidOperationException>(() => _algorithm.History<Tick>(spy, 1).ToList());
|
|
Assert.Throws<InvalidOperationException>(() => _algorithm.History<Tick>(spy, 1, Resolution.Tick).ToList());
|
|
Assert.Throws<InvalidOperationException>(() => _algorithm.History<Tick>(new[] { spy }, 1).ToList());
|
|
Assert.Throws<InvalidOperationException>(() => _algorithm.History<Tick>(new[] { spy }, 1, Resolution.Tick).ToList());
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
|
|
foreach (var testCase in new[] { "return algorithm.History(Tick, symbol, 1)", "return algorithm.History(Tick, symbol, 1)",
|
|
"return algorithm.History(Tick, [ symbol ], 1)", "return algorithm.History(Tick, [ symbol ], 1, Resolution.Tick)" })
|
|
{
|
|
dynamic getTickHistory = PyModule.FromString("testModule",
|
|
@"from AlgorithmImports import *
|
|
|
|
def getTickHistory(algorithm, symbol):
|
|
" + testCase).GetAttr("getTickHistory");
|
|
|
|
Assert.Throws<ClrBubbledException>(() => getTickHistory(_algorithm, spy));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TickResolutionHistoryRequestTradeBarApiThrowsException()
|
|
{
|
|
Assert.Throws<InvalidOperationException>(
|
|
() => _algorithm.History(Symbols.SPY, 1, Resolution.Tick).ToList());
|
|
|
|
Assert.Throws<InvalidOperationException>(
|
|
() => _algorithm.History(Symbols.SPY, TimeSpan.FromSeconds(2), Resolution.Tick).ToList());
|
|
|
|
Assert.Throws<InvalidOperationException>(
|
|
() => _algorithm.History(Symbols.SPY, DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, Resolution.Tick).ToList());
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void GetsTickResolutionHistoricalDataWithoutATickSubscription(Language language)
|
|
{
|
|
var spy = _algorithm.AddEquity("SPY", Resolution.Daily).Symbol;
|
|
var ibm = _algorithm.AddEquity("IBM", Resolution.Daily).Symbol;
|
|
_algorithm.SetStartDate(2014, 6, 10);
|
|
var start = new DateTime(2013, 10, 7);
|
|
var end = new DateTime(2013, 10, 8);
|
|
_algorithm.SetStartDate(2013, 10, 8);
|
|
|
|
_testHistoryProvider.Slices = new[]
|
|
{
|
|
new Slice(start, new[] { new Tick(start, spy, 100, 100) { TickType = TickType.Trade } }, start),
|
|
new Slice(start, new[] { new Tick(start, ibm, 200, 200) { TickType = TickType.Trade } }, start),
|
|
new Slice(end, new[] { new Tick(end, spy, 110, 110) { TickType = TickType.Trade }, new Tick(end, ibm, 210, 210) { TickType = TickType.Trade } }, end)
|
|
}.ToList();
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var spyHistory = _algorithm.History<Tick>(spy, start, end, Resolution.Tick);
|
|
Assert.AreEqual(2, spyHistory.Count());
|
|
|
|
var ibmHistory = _algorithm.History<Tick>(ibm, start, end, Resolution.Tick);
|
|
Assert.AreEqual(2, ibmHistory.Count());
|
|
|
|
var allHistory = _algorithm.History<Tick>(new[] { spy, ibm }, start, end, Resolution.Tick);
|
|
Assert.AreEqual(3, allHistory.Count());
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getTickHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getTickHistory(algorithm, symbol, start, end):
|
|
history = algorithm.History(Tick, symbol, start, end, Resolution.Tick)
|
|
history = history if isinstance(symbol, list) else history.loc[symbol]
|
|
|
|
return history.values.tolist()
|
|
|
|
").GetAttr("getTickHistory");
|
|
|
|
_algorithm.SetPandasConverter();
|
|
using var pyAlgorithm = _algorithm.ToPython();
|
|
using var pySpy = spy.ToPython();
|
|
using var pyIbm = ibm.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySpy, pyIbm });
|
|
using var pyStart = start.ToPython();
|
|
using var pyEnd = end.ToPython();
|
|
|
|
var spyHistory = getTickHistory.Invoke(pyAlgorithm, pySpy, pyStart, pyEnd).As<List<dynamic>>();
|
|
Assert.AreEqual(2, spyHistory.Count);
|
|
|
|
var ibmHistory = getTickHistory.Invoke(pyAlgorithm, pyIbm, pyStart, pyEnd).As<List<dynamic>>();
|
|
Assert.AreEqual(2, ibmHistory.Count);
|
|
|
|
var allHistory = getTickHistory.Invoke(pyAlgorithm, pySymbols, pyStart, pyEnd).As<List<dynamic>>();
|
|
Assert.AreEqual(4, allHistory.Count);
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Resolution.Second, Language.CSharp, true)]
|
|
[TestCase(Resolution.Minute, Language.CSharp, true)]
|
|
[TestCase(Resolution.Hour, Language.CSharp, true)]
|
|
[TestCase(Resolution.Daily, Language.CSharp, true)]
|
|
[TestCase(Resolution.Second, Language.Python, true)]
|
|
[TestCase(Resolution.Minute, Language.Python, true)]
|
|
[TestCase(Resolution.Hour, Language.Python, true)]
|
|
[TestCase(Resolution.Daily, Language.Python, true)]
|
|
[TestCase(Resolution.Second, Language.CSharp, false)]
|
|
[TestCase(Resolution.Minute, Language.CSharp, false)]
|
|
[TestCase(Resolution.Hour, Language.CSharp, false)]
|
|
[TestCase(Resolution.Daily, Language.CSharp, false)]
|
|
[TestCase(Resolution.Second, Language.Python, false)]
|
|
[TestCase(Resolution.Minute, Language.Python, false)]
|
|
[TestCase(Resolution.Hour, Language.Python, false)]
|
|
[TestCase(Resolution.Daily, Language.Python, false)]
|
|
public void TimeSpanHistoryRequestIsCorrectlyBuilt(Resolution resolution, Language language, bool symbolAlreadyAdded)
|
|
{
|
|
_algorithm.SetStartDate(2013, 10, 07);
|
|
_algorithm.Settings.SeedInitialPrices = false;
|
|
|
|
var symbol = Symbols.SPY;
|
|
if (symbolAlreadyAdded)
|
|
{
|
|
// it should not matter
|
|
_algorithm.AddEquity("SPY");
|
|
}
|
|
if (language == Language.CSharp)
|
|
{
|
|
_algorithm.History(symbol, TimeSpan.FromSeconds(2), resolution);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
_algorithm.History(symbol.ToPython(), TimeSpan.FromSeconds(2), resolution);
|
|
}
|
|
}
|
|
|
|
Resolution? fillForwardResolution = null;
|
|
if (resolution != Resolution.Tick)
|
|
{
|
|
fillForwardResolution = resolution;
|
|
}
|
|
|
|
var expectedCount = resolution == Resolution.Hour || resolution == Resolution.Daily ? 1 : 2;
|
|
Assert.AreEqual(expectedCount, _testHistoryProvider.HistryRequests.Count);
|
|
Assert.AreEqual(symbol, _testHistoryProvider.HistryRequests.First().Symbol);
|
|
Assert.AreEqual(resolution, _testHistoryProvider.HistryRequests.First().Resolution);
|
|
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IncludeExtendedMarketHours);
|
|
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IsCustomData);
|
|
Assert.AreEqual(fillForwardResolution, _testHistoryProvider.HistryRequests.First().FillForwardResolution);
|
|
Assert.AreEqual(DataNormalizationMode.Adjusted, _testHistoryProvider.HistryRequests.First().DataNormalizationMode);
|
|
Assert.AreEqual(expectedCount == 1 ? TickType.Trade : TickType.Quote, _testHistoryProvider.HistryRequests.First().TickType);
|
|
}
|
|
|
|
private static IEnumerable<TestCaseData> BarCountHistoryRequestTestCases
|
|
{
|
|
get
|
|
{
|
|
var spyDate = new DateTime(2013, 10, 07);
|
|
|
|
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, true, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, false, spyDate, null, false);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, false, spyDate, null, false);
|
|
|
|
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, true, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Second, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Minute, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Hour, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Second, Language.Python, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Minute, Language.Python, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Hour, Language.Python, Symbols.SPY, false, spyDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, Symbols.SPY, false, spyDate, null, true);
|
|
|
|
var spxCanonicalOption = Symbol.CreateCanonicalOption(Symbols.SPX);
|
|
var spxDate = new DateTime(2021, 01, 12);
|
|
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, spxCanonicalOption, true, spxDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, spxCanonicalOption, true, spxDate, null, true);
|
|
yield return new TestCaseData(null, Language.CSharp, spxCanonicalOption, true, spxDate, Resolution.Daily, true);
|
|
yield return new TestCaseData(null, Language.Python, spxCanonicalOption, true, spxDate, Resolution.Daily, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.CSharp, spxCanonicalOption, false, spxDate, null, true);
|
|
yield return new TestCaseData(Resolution.Daily, Language.Python, spxCanonicalOption, false, spxDate, null, true);
|
|
yield return new TestCaseData(null, Language.CSharp, spxCanonicalOption, false, spxDate, Resolution.Daily, true);
|
|
yield return new TestCaseData(null, Language.Python, spxCanonicalOption, false, spxDate, Resolution.Daily, true);
|
|
}
|
|
}
|
|
|
|
[TestCaseSource(nameof(BarCountHistoryRequestTestCases))]
|
|
public void BarCountHistoryRequestIsCorrectlyBuilt(Resolution? resolution, Language language, Symbol symbol,
|
|
bool symbolAlreadyAdded, DateTime dateTime, Resolution? defaultResolution, bool multiSymbol)
|
|
{
|
|
_algorithm.SetStartDate(dateTime);
|
|
_algorithm.Settings.SeedInitialPrices = false;
|
|
|
|
if (symbolAlreadyAdded)
|
|
{
|
|
// it should not matter
|
|
_algorithm.AddSecurity(symbol);
|
|
}
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
if (multiSymbol)
|
|
{
|
|
_algorithm.History(new[] { symbol }, 10, resolution);
|
|
}
|
|
else
|
|
{
|
|
_algorithm.History(symbol, 10, resolution);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
if (multiSymbol)
|
|
{
|
|
using var pySymbols = new[] { symbol }.ToPyListUnSafe();
|
|
_algorithm.History(pySymbols, 10, resolution);
|
|
|
|
pySymbols[0].Dispose();
|
|
}
|
|
else
|
|
{
|
|
using var pySymbol = symbol.ToPython();
|
|
_algorithm.History(pySymbol, 10, resolution);
|
|
}
|
|
}
|
|
}
|
|
|
|
Resolution? fillForwardResolution = null;
|
|
if (resolution != Resolution.Tick)
|
|
{
|
|
fillForwardResolution = resolution ?? defaultResolution;
|
|
}
|
|
|
|
if (symbol.SecurityType == SecurityType.Equity)
|
|
{
|
|
var expectedCount = resolution == Resolution.Hour || resolution == Resolution.Daily ? 1 : 2;
|
|
Assert.AreEqual(expectedCount, _testHistoryProvider.HistryRequests.Count);
|
|
var request = _testHistoryProvider.HistryRequests.First();
|
|
Assert.AreEqual(symbol, request.Symbol);
|
|
Assert.AreEqual(resolution, request.Resolution);
|
|
Assert.IsFalse(request.IncludeExtendedMarketHours);
|
|
Assert.IsFalse(request.IsCustomData);
|
|
Assert.AreEqual(fillForwardResolution, request.FillForwardResolution);
|
|
Assert.AreEqual(DataNormalizationMode.Adjusted, request.DataNormalizationMode);
|
|
|
|
Assert.AreEqual(expectedCount == 1 ? TickType.Trade : TickType.Quote, request.TickType);
|
|
}
|
|
else if (symbol.SecurityType == SecurityType.IndexOption)
|
|
{
|
|
Assert.AreEqual(1, _testHistoryProvider.HistryRequests.Count);
|
|
var request = _testHistoryProvider.HistryRequests.Single();
|
|
Assert.AreEqual(symbol, request.Symbol);
|
|
Assert.AreEqual(resolution ?? defaultResolution, request.Resolution);
|
|
Assert.AreEqual(typeof(OptionUniverse), request.DataType);
|
|
Assert.IsFalse(request.IncludeExtendedMarketHours);
|
|
Assert.IsFalse(request.IsCustomData);
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp, true)]
|
|
[TestCase(Language.Python, true)]
|
|
[TestCase(Language.CSharp, false)]
|
|
[TestCase(Language.Python, false)]
|
|
public void TickHistoryRequestIgnoresFillForward(Language language, bool symbolAlreadyAdded)
|
|
{
|
|
_algorithm.SetStartDate(2013, 10, 07);
|
|
_algorithm.Settings.SeedInitialPrices = false;
|
|
|
|
var symbol = Symbols.SPY;
|
|
if (symbolAlreadyAdded)
|
|
{
|
|
// it should not matter
|
|
_algorithm.AddEquity("SPY");
|
|
}
|
|
if (language == Language.CSharp)
|
|
{
|
|
_algorithm.History(new[] { symbol }, new DateTime(1, 1, 1, 1, 1, 1), new DateTime(1, 1, 1, 1, 1, 2), Resolution.Tick,
|
|
fillForward: true);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { symbol.ToPython() });
|
|
_algorithm.History(symbols, new DateTime(1, 1, 1, 1, 1, 1), new DateTime(1, 1, 1, 1, 1, 2),
|
|
Resolution.Tick, fillForward: true);
|
|
}
|
|
}
|
|
|
|
Assert.AreEqual(2, _testHistoryProvider.HistryRequests.Count);
|
|
Assert.AreEqual(symbol, _testHistoryProvider.HistryRequests.First().Symbol);
|
|
Assert.AreEqual(Resolution.Tick, _testHistoryProvider.HistryRequests.First().Resolution);
|
|
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IncludeExtendedMarketHours);
|
|
Assert.IsFalse(_testHistoryProvider.HistryRequests.First().IsCustomData);
|
|
Assert.AreEqual(null, _testHistoryProvider.HistryRequests.First().FillForwardResolution);
|
|
Assert.AreEqual(DataNormalizationMode.Adjusted, _testHistoryProvider.HistryRequests.First().DataNormalizationMode);
|
|
Assert.AreEqual(TickType.Quote, _testHistoryProvider.HistryRequests.First().TickType);
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPriceOfIlliquidAsset_RealData()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 6, 6, 11, 0, 0));
|
|
|
|
//20140606_twx_minute_quote_american_call_230000_20150117.csv
|
|
var optionSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 23, new DateTime(2015, 1, 17));
|
|
var option = algorithm.AddOptionContract(optionSymbol);
|
|
|
|
var lastKnownPrice = algorithm.GetLastKnownPrice(option);
|
|
Assert.IsNotNull(lastKnownPrice);
|
|
|
|
// Data gap of more than 15 minutes
|
|
Assert.Greater((algorithm.Time - lastKnownPrice.EndTime).TotalMinutes, 15);
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPriceOfIlliquidAsset_TestData()
|
|
{
|
|
// Set the start date on Tuesday
|
|
_algorithm.SetStartDate(2014, 6, 10);
|
|
|
|
var optionSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 23, new DateTime(2015, 1, 17));
|
|
var option = _algorithm.AddOptionContract(optionSymbol);
|
|
|
|
// The last known price is on Friday, so we missed data from Monday and no data during Weekend
|
|
var barTime = new DateTime(2014, 6, 6, 15, 0, 0, 0);
|
|
_testHistoryProvider.Slices = new[]
|
|
{
|
|
new Slice(barTime, new[] { new TradeBar(barTime, optionSymbol, 100, 100, 100, 100, 1) }, barTime)
|
|
}.ToList();
|
|
|
|
var lastKnownPrice = _algorithm.GetLastKnownPrice(option);
|
|
Assert.IsNotNull(lastKnownPrice);
|
|
Assert.AreEqual(barTime.AddMinutes(1), lastKnownPrice.EndTime);
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPricesEquity()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
var equity = algorithm.AddEquity("SPY");
|
|
|
|
var lastKnownPrices = algorithm.GetLastKnownPrices(equity.Symbol).ToList();
|
|
Assert.AreEqual(2, lastKnownPrices.Count);
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(TradeBar)));
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(QuoteBar)));
|
|
}
|
|
|
|
[TestCase(Resolution.Daily)]
|
|
[TestCase(Resolution.Minute)]
|
|
[TestCase(Resolution.Hour)]
|
|
public void GetLastKnownPricesUsesCorrectResolution(Resolution resolution)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
algorithm.SetSecurityInitializer(security =>
|
|
{
|
|
var lastKnownPrices = algorithm.GetLastKnownPrices(security).ToList();
|
|
var data = lastKnownPrices.Where(x => x.GetType() == typeof(TradeBar)).Single().ConvertInvariant<TradeBar>();
|
|
var expectedPeriod = new TimeSpan();
|
|
switch (resolution)
|
|
{
|
|
case Resolution.Daily:
|
|
expectedPeriod = TimeSpan.FromHours(6.5);
|
|
break;
|
|
case Resolution.Minute:
|
|
expectedPeriod = TimeSpan.FromMinutes(1);
|
|
break;
|
|
case Resolution.Hour:
|
|
expectedPeriod = TimeSpan.FromHours(1);
|
|
break;
|
|
}
|
|
|
|
Assert.AreEqual(expectedPeriod, data.Period);
|
|
});
|
|
|
|
algorithm.AddEquity("SPY", resolution);
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void GetLastKnownPricesCustomData(Language language)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
Symbol symbol;
|
|
if (language == Language.CSharp)
|
|
{
|
|
symbol = algorithm.AddData<CustomData>("SPY").Symbol;
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
|
|
var customDataType = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests import *
|
|
|
|
class Test(PythonData):
|
|
def GetSource(self, config, date, isLiveMode):
|
|
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType)
|
|
source = f'{Globals.DataFolder}equity/usa/minute/spy/{fileName}'
|
|
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
|
|
|
def Reader(self, config, line, date, isLiveMode):
|
|
|
|
data = line.split(',')
|
|
|
|
result = Test()
|
|
result.DataType = MarketDataType.Base
|
|
result.Symbol = config.Symbol
|
|
result.Time = date + timedelta(milliseconds=int(data[0]))
|
|
result.Value = 1
|
|
|
|
return result
|
|
").GetAttr("Test");
|
|
symbol = algorithm.AddData(customDataType, "SPY").Symbol;
|
|
}
|
|
}
|
|
|
|
var lastKnownPrices = algorithm.GetLastKnownPrices(symbol).ToList();
|
|
Assert.AreEqual(1, lastKnownPrices.Count);
|
|
if (language == Language.CSharp)
|
|
{
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(CustomData)));
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(PythonData)));
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPriceEquity()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
var equity = algorithm.AddEquity("SPY");
|
|
|
|
var lastKnownPrice = algorithm.GetLastKnownPrice(equity);
|
|
Assert.AreEqual(typeof(TradeBar), lastKnownPrice.GetType());
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPriceOption()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 06, 09));
|
|
|
|
var option = algorithm.AddOptionContract(Symbols.CreateOptionSymbol("AAPL", OptionRight.Call, 250m, new DateTime(2016, 01, 15)));
|
|
|
|
var lastKnownPrice = algorithm.GetLastKnownPrice(option);
|
|
Assert.AreEqual(typeof(QuoteBar), lastKnownPrice.GetType());
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPricesOption()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 06, 09));
|
|
|
|
var option = algorithm.AddOptionContract(Symbols.CreateOptionSymbol("AAPL", OptionRight.Call, 250m, new DateTime(2016, 01, 15)));
|
|
|
|
var lastKnownPrices = algorithm.GetLastKnownPrices(option).ToList();
|
|
Assert.AreEqual(3, lastKnownPrices.Count);
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(TradeBar)));
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(QuoteBar)));
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(OpenInterest)));
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPriceFuture()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
var future = algorithm.AddSecurity(Symbols.CreateFutureSymbol(Futures.Indices.SP500EMini, new DateTime(2013, 12, 20)));
|
|
|
|
var lastKnownPrice = algorithm.GetLastKnownPrice(future);
|
|
Assert.AreEqual(typeof(QuoteBar), lastKnownPrice.GetType());
|
|
}
|
|
|
|
[Test]
|
|
public void GetLastKnownPricesFuture()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
var future = algorithm.AddSecurity(Symbols.CreateFutureSymbol(Futures.Indices.SP500EMini, new DateTime(2013, 12, 20)));
|
|
|
|
var lastKnownPrices = algorithm.GetLastKnownPrices(future).ToList();
|
|
Assert.AreEqual(3, lastKnownPrices.Count);
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(TradeBar)));
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(QuoteBar)));
|
|
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(OpenInterest)));
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void TickResolutionOpenInterestHistoryRequestIsNotFilteredWhenRequestedExplicitly(Language language)
|
|
{
|
|
var start = new DateTime(2014, 6, 05);
|
|
var end = start.AddDays(10);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetStartDate(start);
|
|
_algorithm.SetDateTime(end);
|
|
|
|
_algorithm.UniverseSettings.FillForward = false;
|
|
var optionSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 45, new DateTime(2015, 1, 17));
|
|
var historyResolution = Resolution.Minute;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var openInterests = _algorithm.History<OpenInterest>(optionSymbol, start, end, historyResolution).ToList();
|
|
|
|
Assert.AreEqual(2, openInterests.Count);
|
|
Assert.AreEqual(new DateTime(2014, 06, 05, 6, 31, 0), openInterests[0].Time);
|
|
Assert.AreEqual(optionSymbol, openInterests[0].Symbol);
|
|
Assert.AreEqual(new DateTime(2014, 06, 06, 6, 30, 0), openInterests[1].Time);
|
|
Assert.AreEqual(optionSymbol, openInterests[1].Symbol);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getOpenInterestHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getOpenInterestHistory(algorithm, symbol, start, end, resolution):
|
|
return algorithm.History(OpenInterest, symbol, start, end, resolution).reset_index().to_dict()
|
|
").GetAttr("getOpenInterestHistory");
|
|
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { optionSymbol.ToPython() });
|
|
using var dict = getOpenInterestHistory
|
|
.Invoke(_algorithm.ToPython(), symbols, start.ToPython(), end.ToPython(),
|
|
historyResolution.ToPython());
|
|
var openInterestsDataFrameDict = GetDataFrameDictionary<PyObject>(dict);
|
|
|
|
Assert.That(openInterestsDataFrameDict, Does.ContainKey("openinterest"));
|
|
Assert.That(openInterestsDataFrameDict, Does.ContainKey("time"));
|
|
|
|
var openInterests = openInterestsDataFrameDict["openinterest"].ConvertToDictionary<int, decimal>();
|
|
var times = openInterestsDataFrameDict["time"].ConvertToDictionary<int, DateTime>();
|
|
|
|
Assert.That(openInterests, Has.Count.EqualTo(2));
|
|
Assert.That(times, Has.Count.EqualTo(2));
|
|
Assert.That(times[0], Is.EqualTo(new DateTime(2014, 06, 05, 6, 31, 0)));
|
|
Assert.That(times[1], Is.EqualTo(new DateTime(2014, 06, 06, 6, 30, 0)));
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void TickResolutionOpenInterestHistoryRequestIsFilteredByDefault_SingleSymbol(Language language)
|
|
{
|
|
var start = new DateTime(2014, 6, 05);
|
|
var end = start.AddDays(2);
|
|
var historyResolution = Resolution.Minute;
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetStartDate(start);
|
|
_algorithm.SetDateTime(end);
|
|
|
|
var optionSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 23, new DateTime(2015, 1, 17));
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var result = _algorithm.History(new[] { optionSymbol }, start, end, historyResolution, fillForward: false).ToList();
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.AreEqual(53, result.Count);
|
|
Assert.IsTrue(result.Any(slice => slice.ContainsKey(optionSymbol)));
|
|
|
|
var openInterests = result.Select(slice => slice.Get(typeof(OpenInterest)) as DataDictionary<OpenInterest>).Where(dataDictionary => dataDictionary.Count > 0).ToList();
|
|
|
|
Assert.AreEqual(0, openInterests.Count);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getOpenInterestHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getOpenInterestHistory(algorithm, symbol, start, end, resolution):
|
|
return algorithm.History(symbol, start, end, resolution)
|
|
").GetAttr("getOpenInterestHistory");
|
|
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { optionSymbol.ToPython() });
|
|
using var openInterests = getOpenInterestHistory.Invoke(_algorithm.ToPython(), symbols, start.ToPython(), end.ToPython(),
|
|
historyResolution.ToPython());
|
|
Assert.AreEqual(780, openInterests.GetAttr("shape")[0].As<int>());
|
|
|
|
using var dict = openInterests.GetAttr("to_dict").Invoke();
|
|
var dataFrameDict = GetDataFrameDictionary<dynamic>(dict);
|
|
Assert.That(dataFrameDict, Does.Not.ContainKey("openinterest"));
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void TickResolutionOpenInterestHistoryRequestIsFilteredByDefault_MultipleSymbols(Language language)
|
|
{
|
|
if (language == Language.Python)
|
|
{
|
|
// required for python so we can assert the resulting DF symbols without other tests affecting us
|
|
SymbolCache.Clear();
|
|
}
|
|
var start = new DateTime(2014, 6, 05);
|
|
var end = start.AddDays(2);
|
|
var historyResolution = Resolution.Minute;
|
|
_algorithm = GetAlgorithm(start.AddDays(1));
|
|
_algorithm.SetStartDate(start);
|
|
_algorithm.SetDateTime(start.AddDays(2));
|
|
|
|
var optionSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 23, new DateTime(2015, 1, 17));
|
|
var optionSymbol2 = Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Call, 500, new DateTime(2015, 1, 17));
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var result = _algorithm.History(new[] { optionSymbol, optionSymbol2 }, start, end, historyResolution, fillForward: false).ToList();
|
|
|
|
Assert.AreEqual(415, result.Count);
|
|
Assert.IsTrue(result.Any(slice => slice.ContainsKey(optionSymbol)));
|
|
Assert.IsTrue(result.Any(slice => slice.ContainsKey(optionSymbol2)));
|
|
|
|
var openInterests = result.Select(slice => slice.Get(typeof(OpenInterest)) as DataDictionary<OpenInterest>).Where(dataDictionary => dataDictionary.Count > 0).ToList();
|
|
|
|
Assert.AreEqual(0, openInterests.Count);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var getOpenInterestHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getOpenInterestHistory(algorithm, symbol, start, end, resolution):
|
|
return algorithm.History(symbol, start, end, resolution)
|
|
").GetAttr("getOpenInterestHistory");
|
|
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { optionSymbol.ToPython(), optionSymbol2.ToPython() });
|
|
var result = getOpenInterestHistory
|
|
.Invoke(_algorithm.ToPython(), symbols, start.ToPython(), end.ToPython(), historyResolution.ToPython());
|
|
Assert.AreEqual(1170, result.GetAttr("shape")[0].As<int>());
|
|
|
|
var dataFrameDict = GetDataFrameDictionary<PyObject>(result
|
|
.GetAttr("reset_index").Invoke()
|
|
.GetAttr("to_dict").Invoke());
|
|
var dataFrameSymbols = dataFrameDict["symbol"].ConvertToDictionary<int, string>().Values.ToHashSet();
|
|
CollectionAssert.AreEquivalent(dataFrameSymbols, new[] { optionSymbol.ID.ToString(), optionSymbol2.ID.ToString() });
|
|
|
|
Assert.That(dataFrameDict, Does.Not.ContainKey("openinterest"));
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void SubscriptionHistoryRequestWithDifferentDataMappingMode(Language language)
|
|
{
|
|
var dataMappingModes = GetAllDataMappingModes();
|
|
var historyStart = new DateTime(2013, 10, 6);
|
|
var historyEnd = new DateTime(2014, 1, 1);
|
|
var resolution = Resolution.Daily;
|
|
_algorithm = GetAlgorithm(historyEnd);
|
|
var symbol = _algorithm.AddFuture(Futures.Indices.SP500EMini, resolution, dataMappingMode: dataMappingModes.First(),
|
|
extendedMarketHours: true).Symbol;
|
|
var expectedHistoryCount = 61;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var historyResults = dataMappingModes
|
|
.Select(x => _algorithm.History(new[] { symbol }, historyStart, historyEnd, resolution, dataMappingMode: x).ToList())
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
|
|
// Check that all history results have a mapping date at some point in the history
|
|
HashSet<DateTime> mappingDates = new HashSet<DateTime>();
|
|
for (int i = 0; i < historyResults.Count; i++)
|
|
{
|
|
var underlying = historyResults[i].First().Bars.Keys.First().Underlying;
|
|
int mappingsCount = 0;
|
|
|
|
foreach (var slice in historyResults[i])
|
|
{
|
|
var dataUnderlying = slice.Bars.Keys.First().Underlying;
|
|
if (dataUnderlying != underlying)
|
|
{
|
|
underlying = dataUnderlying;
|
|
mappingsCount++;
|
|
mappingDates.Add(slice.Time.Date);
|
|
}
|
|
}
|
|
|
|
if (mappingsCount == 0)
|
|
{
|
|
throw new Exception($"History results for {dataMappingModes[i]} data mapping mode did not contain any mappings");
|
|
}
|
|
}
|
|
|
|
if (mappingDates.Count < dataMappingModes.Length)
|
|
{
|
|
throw new Exception("History results should have had different mapping dates for each data mapping mode");
|
|
}
|
|
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { symbol.ToPython() });
|
|
var historyResults = dataMappingModes
|
|
.Select(x => _algorithm.History(symbols, historyStart, historyEnd, resolution, dataMappingMode: x))
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(DataNormalizationMode.BackwardsRatio, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.BackwardsPanamaCanal, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.ForwardPanamaCanal, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.BackwardsRatio, Language.Python)]
|
|
[TestCase(DataNormalizationMode.BackwardsPanamaCanal, Language.Python)]
|
|
[TestCase(DataNormalizationMode.ForwardPanamaCanal, Language.Python)]
|
|
|
|
public void HistoryThrowsForUnsupportedDataNormalizationMode_Equity(DataNormalizationMode dataNormalizationMode, Language language)
|
|
{
|
|
_algorithm = GetAlgorithmWithEquity(new DateTime(2014, 6, 6));
|
|
Assert.AreEqual(2, _algorithm.SubscriptionManager.Subscriptions.ToList().Count);
|
|
var equity = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
Assert.AreEqual(SecurityType.Equity, equity.SecurityType);
|
|
|
|
var start = _algorithm.Time.AddDays(-1);
|
|
var end = _algorithm.Time;
|
|
TestDelegate historyCall;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
_algorithm.History(new[] { equity.Symbol }, start, end, equity.Resolution,
|
|
dataNormalizationMode: dataNormalizationMode).ToList();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
var symbols = new PyList(new[] { equity.Symbol.ToPython() });
|
|
_algorithm.History(symbols, start, end, equity.Resolution, dataNormalizationMode: dataNormalizationMode);
|
|
}
|
|
};
|
|
}
|
|
|
|
Assert.Throws<ArgumentOutOfRangeException>(historyCall);
|
|
}
|
|
|
|
[TestCase(DataNormalizationMode.Adjusted, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.SplitAdjusted, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.TotalReturn, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.Adjusted, Language.Python)]
|
|
[TestCase(DataNormalizationMode.SplitAdjusted, Language.Python)]
|
|
[TestCase(DataNormalizationMode.TotalReturn, Language.Python)]
|
|
public void HistoryThrowsForUnsupportedDataNormalizationMode_Future(DataNormalizationMode dataNormalizationMode, Language language)
|
|
{
|
|
_algorithm = GetAlgorithmWithFuture(new DateTime(2014, 1, 1));
|
|
Assert.IsNotEmpty(_algorithm.SubscriptionManager.Subscriptions);
|
|
var future = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
Assert.AreEqual(SecurityType.Future, future.SecurityType);
|
|
|
|
var start = _algorithm.StartDate;
|
|
var end = _algorithm.EndDate;
|
|
TestDelegate historyCall;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
_algorithm.History(new[] { future.Symbol }, start, end, future.Resolution,
|
|
dataNormalizationMode: dataNormalizationMode).ToList();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
var symbols = new PyList(new[] { future.Symbol.ToPython() });
|
|
_algorithm.History(symbols, start, end, future.Resolution, dataNormalizationMode: dataNormalizationMode);
|
|
}
|
|
};
|
|
}
|
|
|
|
Assert.Throws<ArgumentOutOfRangeException>(historyCall);
|
|
}
|
|
|
|
[TestCase(DataNormalizationMode.Raw, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.Adjusted, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.SplitAdjusted, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.TotalReturn, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.Raw, Language.Python)]
|
|
[TestCase(DataNormalizationMode.Adjusted, Language.Python)]
|
|
[TestCase(DataNormalizationMode.SplitAdjusted, Language.Python)]
|
|
[TestCase(DataNormalizationMode.TotalReturn, Language.Python)]
|
|
public void HistoryDoesNotThrowForSupportedDataNormalizationMode_Equity(DataNormalizationMode dataNormalizationMode, Language language)
|
|
{
|
|
_algorithm = GetAlgorithmWithEquity(new DateTime(2014, 6, 6));
|
|
Assert.AreEqual(2, _algorithm.SubscriptionManager.Subscriptions.ToList().Count);
|
|
var equity = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
Assert.AreEqual(SecurityType.Equity, equity.SecurityType);
|
|
|
|
var start = _algorithm.Time.AddDays(-1);
|
|
var end = _algorithm.Time;
|
|
TestDelegate historyCall;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
_algorithm.History(new[] { equity.Symbol }, start, end, equity.Resolution,
|
|
dataNormalizationMode: dataNormalizationMode).ToList();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
var symbols = new PyList(new[] { equity.Symbol.ToPython() });
|
|
_algorithm.History(symbols, start, end, equity.Resolution, dataNormalizationMode: dataNormalizationMode);
|
|
}
|
|
};
|
|
}
|
|
|
|
Assert.DoesNotThrow(historyCall);
|
|
}
|
|
|
|
[TestCase(DataNormalizationMode.Raw, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.BackwardsRatio, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.BackwardsPanamaCanal, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.ForwardPanamaCanal, Language.CSharp)]
|
|
[TestCase(DataNormalizationMode.Raw, Language.Python)]
|
|
[TestCase(DataNormalizationMode.BackwardsRatio, Language.Python)]
|
|
[TestCase(DataNormalizationMode.BackwardsPanamaCanal, Language.Python)]
|
|
[TestCase(DataNormalizationMode.ForwardPanamaCanal, Language.Python)]
|
|
public void HistoryDoesNotThrowForSupportedDataNormalizationMode_Future(DataNormalizationMode dataNormalizationMode, Language language)
|
|
{
|
|
_algorithm = GetAlgorithmWithFuture(new DateTime(2014, 1, 1));
|
|
Assert.IsNotEmpty(_algorithm.SubscriptionManager.Subscriptions);
|
|
var future = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
Assert.AreEqual(SecurityType.Future, future.SecurityType);
|
|
|
|
var start = _algorithm.StartDate;
|
|
var end = _algorithm.Time;
|
|
TestDelegate historyCall;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
_algorithm.History(new[] { future.Symbol }, start, end, future.Resolution,
|
|
dataNormalizationMode: dataNormalizationMode).ToList();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
historyCall = () =>
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
var symbols = new PyList(new[] { future.Symbol.ToPython() });
|
|
_algorithm.History(symbols, start, end, future.Resolution, dataNormalizationMode: dataNormalizationMode);
|
|
}
|
|
};
|
|
}
|
|
|
|
Assert.DoesNotThrow(historyCall);
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void SubscriptionHistoryRequestWithDifferentDataNormalizationModes_Equity(Language language)
|
|
{
|
|
var dataNormalizationModes = new DataNormalizationMode[]{
|
|
DataNormalizationMode.Raw,
|
|
DataNormalizationMode.Adjusted,
|
|
DataNormalizationMode.SplitAdjusted
|
|
};
|
|
_algorithm = GetAlgorithmWithEquity(new DateTime(2014, 6, 6));
|
|
var equity = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
dynamic symbol = language == Language.CSharp ? equity.Symbol : equity.Symbol.ToPython();
|
|
CheckHistoryResultsForDataNormalizationModes(_algorithm, symbol, _algorithm.Time.AddDays(-1), _algorithm.Time, equity.Resolution,
|
|
dataNormalizationModes, expectedHistoryCount: 390);
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void SubscriptionHistoryRequestWithDifferentDataNormalizationModes_Future(Language language)
|
|
{
|
|
var dataNormalizationModes = new DataNormalizationMode[]{
|
|
DataNormalizationMode.Raw,
|
|
DataNormalizationMode.BackwardsRatio,
|
|
DataNormalizationMode.BackwardsPanamaCanal,
|
|
DataNormalizationMode.ForwardPanamaCanal
|
|
};
|
|
_algorithm = GetAlgorithmWithFuture(new DateTime(2014, 1, 1));
|
|
var future = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
dynamic symbol = language == Language.CSharp ? future.Symbol : future.Symbol.ToPython();
|
|
CheckHistoryResultsForDataNormalizationModes(_algorithm, symbol, new DateTime(2013, 10, 6), _algorithm.Time, future.Resolution,
|
|
dataNormalizationModes, expectedHistoryCount: 61);
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void SubscriptionHistoryRequestForContinuousContractsWithDifferentDepthOffsets(Language language)
|
|
{
|
|
var start = new DateTime(2013, 10, 6);
|
|
var end = new DateTime(2014, 1, 1);
|
|
_algorithm = GetAlgorithmWithFuture(end);
|
|
var future = _algorithm.SubscriptionManager.Subscriptions.First();
|
|
var expectedHistoryCount = 61;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
Func<int, List<Slice>> getHistoryForContractDepthOffset = (contractDepthOffset) =>
|
|
{
|
|
return _algorithm.History(new[] { future.Symbol }, start, end, future.Resolution, contractDepthOffset: contractDepthOffset).ToList();
|
|
};
|
|
|
|
var frontMonthHistory = getHistoryForContractDepthOffset(0);
|
|
var backMonthHistory1 = getHistoryForContractDepthOffset(1);
|
|
var backMonthHistory2 = getHistoryForContractDepthOffset(2);
|
|
|
|
Func<List<Slice>, HashSet<Symbol>> getHistoryUnderlyings = (history) =>
|
|
{
|
|
HashSet<Symbol> underlyings = new();
|
|
foreach (var slice in history)
|
|
{
|
|
var underlying = slice.Keys.Single().Underlying;
|
|
underlyings.Add(underlying);
|
|
}
|
|
|
|
Assert.GreaterOrEqual(underlyings.Count, 2, "History result did not contain any mappings");
|
|
|
|
return underlyings;
|
|
};
|
|
|
|
var frontMonthHistoryUnderlyings = getHistoryUnderlyings(frontMonthHistory);
|
|
var backMonthHistory1Underlyings = getHistoryUnderlyings(backMonthHistory1);
|
|
var backMonthHistory2Underlyings = getHistoryUnderlyings(backMonthHistory2);
|
|
|
|
Assert.AreNotEqual(frontMonthHistoryUnderlyings, backMonthHistory2Underlyings);
|
|
Assert.AreNotEqual(frontMonthHistoryUnderlyings, backMonthHistory2Underlyings);
|
|
Assert.AreNotEqual(backMonthHistory1Underlyings, backMonthHistory2Underlyings);
|
|
|
|
var historyResults = new List<List<Slice>> { frontMonthHistory, backMonthHistory1, backMonthHistory2 };
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { future.Symbol.ToPython() });
|
|
|
|
Func<int, PyObject> getHistoryForContractDepthOffset = (contractDepthOffset) =>
|
|
{
|
|
return _algorithm.History(symbols, start, end, future.Resolution, contractDepthOffset: contractDepthOffset);
|
|
};
|
|
|
|
var frontMonthHistory = getHistoryForContractDepthOffset(0);
|
|
var backMonthHistory1 = getHistoryForContractDepthOffset(1);
|
|
var backMonthHistory2 = getHistoryForContractDepthOffset(2);
|
|
|
|
Assert.Greater(frontMonthHistory.GetAttr("shape")[0].As<int>(), 0);
|
|
Assert.Greater(backMonthHistory1.GetAttr("shape")[0].As<int>(), 0);
|
|
Assert.Greater(backMonthHistory2.GetAttr("shape")[0].As<int>(), 0);
|
|
|
|
var historyResults = new List<PyObject> { frontMonthHistory, backMonthHistory1, backMonthHistory2 };
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each contract depth offset at each time");
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void GetHistoryWithCustomDataType(Language language)
|
|
{
|
|
var end = new DateTime(2013, 10, 8);
|
|
var algorithm = GetAlgorithm(end);
|
|
var start = end.AddDays(-1);
|
|
var span = end - start;
|
|
var periods = (int)span.TotalMinutes;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var symbol = algorithm.AddData<CustomData>("SPY").Symbol;
|
|
|
|
var historyResults = new[]
|
|
{
|
|
algorithm.History<CustomData>(symbol, start, end, Resolution.Minute),
|
|
algorithm.History<CustomData>(symbol, span, Resolution.Minute),
|
|
algorithm.History<CustomData>(symbol, periods, Resolution.Minute)
|
|
};
|
|
|
|
foreach (var history in historyResults)
|
|
{
|
|
AssertCustomDataTypeHistory(history.ToList());
|
|
}
|
|
|
|
var historyResults2 = new[]
|
|
{
|
|
algorithm.History<CustomData>(new[] { symbol }, start, end, Resolution.Minute),
|
|
algorithm.History<CustomData>(new[] { symbol }, span, Resolution.Minute),
|
|
algorithm.History<CustomData>(new[] { symbol }, periods, Resolution.Minute)
|
|
};
|
|
|
|
foreach (var history in historyResults2)
|
|
{
|
|
AssertCustomDataTypeHistory(history.ToList());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from typing import Union
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests import *
|
|
|
|
class TestCustomMarketData(PythonData):
|
|
def GetSource(self, config, date, isLiveMode):
|
|
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType)
|
|
source = f'{Globals.DataFolder}equity/usa/minute/spy/{fileName}'
|
|
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
|
|
|
def Reader(self, config, line, date, isLiveMode):
|
|
|
|
data = line.split(',')
|
|
|
|
result = TestCustomMarketData()
|
|
result.DataType = MarketDataType.Base
|
|
result.Symbol = config.Symbol
|
|
result.Time = date + timedelta(milliseconds=int(data[0]))
|
|
result.Value = 1
|
|
|
|
return result
|
|
|
|
def getDateRangeHistory(algorithm: QCAlgorithm, symbol: Union[Symbol, List[Symbol]], start: datetime, end: datetime):
|
|
return list(algorithm.History[TestCustomMarketData](symbol, start, end, Resolution.Minute))
|
|
|
|
def getTimeSpanHistory(algorithm: QCAlgorithm, symbol: Union[Symbol, List[Symbol]], span: Union[timedelta, int]):
|
|
return list(algorithm.History[TestCustomMarketData](symbol, span, Resolution.Minute))
|
|
");
|
|
var customDataType = testModule.GetAttr("TestCustomMarketData");
|
|
var symbol = algorithm.AddData(customDataType, "SPY").Symbol;
|
|
|
|
dynamic getDateRangeHistory = testModule.GetAttr("getDateRangeHistory");
|
|
dynamic getTimeSpanHistory = testModule.GetAttr("getTimeSpanHistory");
|
|
|
|
var historyResults = new[]
|
|
{
|
|
getDateRangeHistory(algorithm, symbol, start, end),
|
|
getTimeSpanHistory(algorithm, symbol, span),
|
|
getTimeSpanHistory(algorithm, symbol, periods)
|
|
};
|
|
|
|
foreach (var history in historyResults)
|
|
{
|
|
AssertCustomDataTypeHistory(history.As<List<PythonData>>());
|
|
}
|
|
|
|
var historyResults2 = new[]
|
|
{
|
|
getDateRangeHistory(algorithm, new[] { symbol }, start, end),
|
|
getTimeSpanHistory(algorithm, new[] { symbol }, span),
|
|
getTimeSpanHistory(algorithm, new[] { symbol }, periods)
|
|
};
|
|
|
|
foreach (var history in historyResults2)
|
|
{
|
|
AssertCustomDataTypeHistory(history.As<List<DataDictionary<PythonData>>>());
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert.That(_testHistoryProvider.HistryRequests, Has.All.Property("IsCustomData").True);
|
|
}
|
|
|
|
[Test]
|
|
public void GetHistoryFromPythonWithCSharpCustomDataType()
|
|
{
|
|
var end = new DateTime(2013, 10, 8);
|
|
var algorithm = GetAlgorithm(end);
|
|
var start = end.AddDays(-1);
|
|
var span = end - start;
|
|
var periods = (int)span.TotalMinutes;
|
|
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from typing import Union
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests import *
|
|
from QuantConnect.Tests.Algorithm import AlgorithmHistoryTests
|
|
|
|
def getDateRangeHistory(algorithm: QCAlgorithm, symbol: Union[Symbol, List[Symbol]], start: datetime, end: datetime):
|
|
return list(algorithm.History[AlgorithmHistoryTests.CustomData](symbol, start, end, Resolution.Minute))
|
|
|
|
def getTimeSpanHistory(algorithm: QCAlgorithm, symbol: Union[Symbol, List[Symbol]], span: Union[timedelta, int]):
|
|
return list(algorithm.History[AlgorithmHistoryTests.CustomData](symbol, span, Resolution.Minute))
|
|
");
|
|
var symbol = algorithm.AddData<CustomData>("SPY").Symbol;
|
|
|
|
dynamic getDateRangeHistory = testModule.GetAttr("getDateRangeHistory");
|
|
dynamic getTimeSpanHistory = testModule.GetAttr("getTimeSpanHistory");
|
|
|
|
var historyResults = new[]
|
|
{
|
|
getDateRangeHistory(algorithm, symbol, start, end),
|
|
getTimeSpanHistory(algorithm, symbol, span),
|
|
getTimeSpanHistory(algorithm, symbol, periods)
|
|
};
|
|
|
|
foreach (var history in historyResults)
|
|
{
|
|
AssertCustomDataTypeHistory(history.As<List<CustomData>>());
|
|
}
|
|
|
|
var historyResults2 = new[]
|
|
{
|
|
getDateRangeHistory(algorithm, new[] { symbol }, start, end),
|
|
getTimeSpanHistory(algorithm, new[] { symbol }, span),
|
|
getTimeSpanHistory(algorithm, new[] { symbol }, periods)
|
|
};
|
|
|
|
foreach (var history in historyResults2)
|
|
{
|
|
AssertCustomDataTypeHistory(history.As<List<DataDictionary<CustomData>>>());
|
|
}
|
|
}
|
|
|
|
Assert.That(_testHistoryProvider.HistryRequests, Has.All.Property("IsCustomData").True);
|
|
}
|
|
|
|
[Test]
|
|
public void GetHistoryWithCustomDataAndNormalizationMode()
|
|
{
|
|
var dataNormalizationModes = new DataNormalizationMode[]{
|
|
DataNormalizationMode.Raw,
|
|
DataNormalizationMode.Adjusted,
|
|
DataNormalizationMode.SplitAdjusted
|
|
};
|
|
var start = new DateTime(2014, 6, 5);
|
|
var end = start.AddDays(1);
|
|
var algorithm = GetAlgorithm(end);
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var getHistoryForDataNormalizationMode = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistoryForDataNormalizationMode(algorithm, symbol, start, end, resolution, dataNormalizationMode):
|
|
return algorithm.History(TradeBar, symbol, start, end, resolution, dataNormalizationMode=dataNormalizationMode)
|
|
").GetAttr("getHistoryForDataNormalizationMode");
|
|
|
|
algorithm.SetPandasConverter();
|
|
var symbol = algorithm.AddEquity("AAPL", Resolution.Minute).Symbol.ToPython();
|
|
var pyAlgorithm = algorithm.ToPython();
|
|
var pyStart = start.ToPython();
|
|
var pyEnd = end.ToPython();
|
|
var pyResolution = Resolution.Minute.ToPython();
|
|
var historyResults = dataNormalizationModes
|
|
.Select(dataNormalizationMode =>
|
|
getHistoryForDataNormalizationMode.Invoke(pyAlgorithm, symbol, pyStart, pyEnd, pyResolution, dataNormalizationMode.ToPython()))
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount: 390);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data normalization mode at each time");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetHistoryWithCustomDataAndDataMappingMode()
|
|
{
|
|
var dataMappingModes = GetAllDataMappingModes();
|
|
var historyStart = new DateTime(2013, 10, 6);
|
|
var historyEnd = new DateTime(2014, 1, 1);
|
|
var resolution = Resolution.Daily;
|
|
var algorithm = GetAlgorithm(historyEnd);
|
|
var symbol = algorithm.AddFuture(Futures.Indices.SP500EMini, resolution, dataMappingMode: dataMappingModes.First(),
|
|
extendedMarketHours: true).Symbol;
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var getHistoryForDataMappingMode = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistoryForDataMappingMode(algorithm, symbol, start, end, resolution, dataMappingMode):
|
|
return algorithm.History(TradeBar, symbol, start, end, resolution, dataMappingMode=dataMappingMode)
|
|
").GetAttr("getHistoryForDataMappingMode");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var symbols = symbol.ToPython();
|
|
var pyAlgorithm = algorithm.ToPython();
|
|
var pyStart = historyStart.ToPython();
|
|
var pyEnd = historyEnd.ToPython();
|
|
var pyResolution = resolution.ToPython();
|
|
var historyResults = dataMappingModes
|
|
.Select(dataMappingMode =>
|
|
getHistoryForDataMappingMode.Invoke(pyAlgorithm, symbols, pyStart, pyEnd, pyResolution, dataMappingMode.ToPython()))
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount: 61);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void GetHistoryWithCustomDataAndContractDepthOffset()
|
|
{
|
|
var start = new DateTime(2013, 10, 6);
|
|
var end = new DateTime(2014, 1, 1);
|
|
var algorithm = GetAlgorithmWithFuture(end);
|
|
var future = algorithm.SubscriptionManager.Subscriptions.First();
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var getHistoryForContractDepthOffset = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistoryForContractDepthOffset(algorithm, symbol, start, end, resolution, contractDepthOffset):
|
|
return algorithm.History(QuoteBar, symbol, start, end, resolution, contractDepthOffset=contractDepthOffset)
|
|
").GetAttr("getHistoryForContractDepthOffset");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var symbols = new PyList(new[] { future.Symbol.ToPython() });
|
|
var pyAlgorithm = algorithm.ToPython();
|
|
var pyStart = start.ToPython();
|
|
var pyEnd = end.ToPython();
|
|
var pyResolution = future.Resolution.ToPython();
|
|
|
|
var frontMonthHistory = getHistoryForContractDepthOffset.Invoke(pyAlgorithm, symbols, pyStart, pyEnd, pyResolution, 0.ToPython());
|
|
var backMonthHistory1 = getHistoryForContractDepthOffset.Invoke(pyAlgorithm, symbols, pyStart, pyEnd, pyResolution, 1.ToPython());
|
|
var backMonthHistory2 = getHistoryForContractDepthOffset.Invoke(pyAlgorithm, symbols, pyStart, pyEnd, pyResolution, 2.ToPython());
|
|
|
|
Assert.Greater(frontMonthHistory.GetAttr("shape")[0].As<int>(), 0);
|
|
Assert.Greater(backMonthHistory1.GetAttr("shape")[0].As<int>(), 0);
|
|
Assert.Greater(backMonthHistory2.GetAttr("shape")[0].As<int>(), 0);
|
|
|
|
var historyResults = new List<PyObject> { frontMonthHistory, backMonthHistory1, backMonthHistory2 };
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount: 61);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each contract depth offset at each time");
|
|
}
|
|
}
|
|
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void GetsHistoryWithGivenBarType(Language language)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 6, 6));
|
|
var ibmSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var twxSymbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 45, new DateTime(2015, 1, 17));
|
|
|
|
var ibmHistoryStart = new DateTime(2013, 10, 7);
|
|
var ibmHistoryEnd = new DateTime(2013, 10, 8);
|
|
|
|
var ibmTickHistoryStart = new DateTime(2013, 10, 7, 9, 30, 0);
|
|
var ibmTickHistoryEnd = ibmTickHistoryStart.AddSeconds(10);
|
|
|
|
var twxHistoryStart = new DateTime(2014, 6, 5);
|
|
var twxHistoryEnd = new DateTime(2014, 6, 6);
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var tradeHistory = algorithm.History<TradeBar>(ibmSymbol, ibmHistoryStart, ibmHistoryEnd);
|
|
Assert.AreEqual(390, tradeHistory.Count());
|
|
|
|
var quoteHistory = algorithm.History<QuoteBar>(ibmSymbol, ibmHistoryStart, ibmHistoryEnd);
|
|
Assert.AreEqual(390, quoteHistory.Count());
|
|
|
|
var tickHistory = algorithm.History<Tick>(ibmSymbol, ibmTickHistoryStart, ibmTickHistoryEnd, Resolution.Tick);
|
|
Assert.AreEqual(46, tickHistory.Count());
|
|
|
|
var openInterestHistory = algorithm.History<OpenInterest>(twxSymbol, twxHistoryStart, twxHistoryEnd);
|
|
Assert.AreEqual(391, openInterestHistory.Count());
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getTradeBarHistory(algorithm, symbol, start, end):
|
|
return algorithm.History(TradeBar, symbol, start, end)
|
|
|
|
def getQuoteBarHistory(algorithm, symbol, start, end):
|
|
return algorithm.History(QuoteBar, symbol, start, end)
|
|
|
|
def getTickHistory(algorithm, symbol, start, end):
|
|
return algorithm.History(Tick, symbol, start, end, Resolution.Tick)
|
|
|
|
def getOpenInterestHistory(algorithm, symbol, start, end):
|
|
return algorithm.History(OpenInterest, symbol, start, end)
|
|
");
|
|
|
|
dynamic getTradeBarHistory = testModule.GetAttr("getTradeBarHistory");
|
|
dynamic getQuoteBarHistory = testModule.GetAttr("getQuoteBarHistory");
|
|
dynamic getTickHistory = testModule.GetAttr("getTickHistory");
|
|
dynamic getOpenInterestHistory = testModule.GetAttr("getOpenInterestHistory");
|
|
|
|
algorithm.SetPandasConverter();
|
|
|
|
dynamic tradeHistory = getTradeBarHistory(algorithm, ibmSymbol, ibmHistoryStart, ibmHistoryEnd);
|
|
Assert.AreEqual(390, tradeHistory.shape[0].As<int>());
|
|
|
|
dynamic quoteHistory = getQuoteBarHistory(algorithm, ibmSymbol, ibmHistoryStart, ibmHistoryEnd);
|
|
Assert.AreEqual(390, quoteHistory.shape[0].As<int>());
|
|
|
|
dynamic tickHistory = getTickHistory(algorithm, ibmSymbol, ibmTickHistoryStart, ibmTickHistoryEnd);
|
|
Assert.AreEqual(46, tickHistory.shape[0].As<int>());
|
|
|
|
dynamic openInterestHistory = getOpenInterestHistory(algorithm, twxSymbol, twxHistoryStart, twxHistoryEnd);
|
|
Assert.AreEqual(391, openInterestHistory.shape[0].As<int>());
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void HistoryCallsGetSameTickCount()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 6, 6));
|
|
var ibmSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
|
|
var start = new DateTime(2013, 10, 7, 9, 30, 0);
|
|
var end = start.AddSeconds(5);
|
|
|
|
var history = algorithm.History(new[] { ibmSymbol }, start, end, Resolution.Tick);
|
|
var tickCountInSliceHistoryCall = history.Sum(x => x.Ticks[ibmSymbol].Count);
|
|
Assert.AreEqual(40, tickCountInSliceHistoryCall);
|
|
|
|
var tickHistory = algorithm.History<Tick>(ibmSymbol, start, end, Resolution.Tick).ToList();
|
|
var tickCountInTickHistoryCall = tickHistory.Count;
|
|
Assert.AreEqual(tickCountInSliceHistoryCall, tickCountInTickHistoryCall);
|
|
}
|
|
|
|
[Test]
|
|
public void PricesAreProperlyAdjustedForScaledRawHistoryRequest()
|
|
{
|
|
var start = new DateTime(2000, 01, 01);
|
|
var end = new DateTime(2016, 01, 01);
|
|
var algorithm = GetAlgorithm(end.AddDays(1));
|
|
var aaplSymbol = Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
|
|
|
var rawHistory = algorithm.History(new[] { aaplSymbol }, start, end, Resolution.Daily, dataNormalizationMode: DataNormalizationMode.Raw).ToList();
|
|
var scaledRawHistory = algorithm.History(new[] { aaplSymbol }, start, end, Resolution.Daily, dataNormalizationMode: DataNormalizationMode.ScaledRaw).ToList();
|
|
|
|
Assert.IsNotEmpty(rawHistory);
|
|
Assert.AreEqual(rawHistory.Count, scaledRawHistory.Count);
|
|
|
|
var factorFile = _factorFileProvider.Get(aaplSymbol);
|
|
var factorDates = new List<DateTime>();
|
|
var factors = new List<decimal>();
|
|
var prevFactor = 0m;
|
|
for (var date = start; date <= end; date = date.AddDays(1))
|
|
{
|
|
var factor = factorFile.GetPriceFactor(date, DataNormalizationMode.ScaledRaw);
|
|
if (factor != prevFactor)
|
|
{
|
|
factorDates.Add(date);
|
|
factors.Add(factor);
|
|
prevFactor = factor;
|
|
}
|
|
}
|
|
var lastFactorDate = factorDates[factorDates.Count - 1];
|
|
var lastFactor = factors[factors.Count - 1];
|
|
factorDates.RemoveAt(0);
|
|
var currentFactorIndex = 0;
|
|
|
|
for (var i = 0; i < rawHistory.Count; i++)
|
|
{
|
|
var rawBar = rawHistory[i].Bars[aaplSymbol];
|
|
var scaledRawBar = scaledRawHistory[i].Bars[aaplSymbol];
|
|
|
|
if (currentFactorIndex < factorDates.Count && rawBar.Time > factorDates[currentFactorIndex])
|
|
{
|
|
currentFactorIndex++;
|
|
}
|
|
|
|
if (rawBar.Time <= lastFactorDate)
|
|
{
|
|
Assert.AreNotEqual(rawBar.Price, scaledRawBar.Price,
|
|
$@"Raw price {rawBar.Price} should have been different than scaled raw price {scaledRawBar.Price} at {rawBar.Time} (before and at the last factor date {lastFactorDate})");
|
|
}
|
|
else
|
|
{
|
|
// after the last split/dividend, the factor is 1 because prices are adjusted to the prices after the last factor
|
|
Assert.AreEqual(1m, factors[currentFactorIndex] / lastFactor);
|
|
Assert.AreEqual(rawBar.Price, scaledRawBar.Price,
|
|
$@"Raw price {rawBar.Price} should have been equal to the scaled raw price {scaledRawBar.Price} at {rawBar.Time} (after the last factor date {lastFactorDate})");
|
|
}
|
|
|
|
var expectedScaledRawPrice = rawBar.Price * factors[currentFactorIndex] / lastFactor;
|
|
Assert.Less(Math.Abs(expectedScaledRawPrice - scaledRawBar.Price), 1e-25m, $"Date: {rawBar.Time}");
|
|
}
|
|
}
|
|
|
|
// C#
|
|
[TestCase(Language.CSharp, Resolution.Second, true, 23400, 23400, 23400)]
|
|
[TestCase(Language.CSharp, Resolution.Second, false, 23400, 22884, 16093)]
|
|
[TestCase(Language.CSharp, Resolution.Minute, true, 780, 780, 780)]
|
|
[TestCase(Language.CSharp, Resolution.Minute, false, 780, 390, 390)]
|
|
// Python
|
|
[TestCase(Language.Python, Resolution.Second, true, 23400, 23400, 23400)]
|
|
[TestCase(Language.Python, Resolution.Second, false, 23400, 22884, 16093)]
|
|
[TestCase(Language.Python, Resolution.Minute, true, 780, 780, 780)]
|
|
[TestCase(Language.Python, Resolution.Minute, false, 780, 390, 390)]
|
|
public void HistoryRequestWithFillForward(Language language, Resolution resolution, bool fillForward, int periods,
|
|
int expectedHistoryCount, int expectedTradeBarOnlyHistoryCount)
|
|
{
|
|
// Theres data only for 2013-10-07 to 2013-10-11 for SPY. Data should be fill forwarded till the 15th.
|
|
var start = new DateTime(2013, 10, 11);
|
|
var end = resolution == Resolution.Minute ? new DateTime(2013, 10, 15) : new DateTime(2013, 10, 12);
|
|
var timeSpan = end - start;
|
|
|
|
var algorithm = GetAlgorithm(end);
|
|
var symbol = algorithm.AddEquity("SPY").Symbol;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var noSymbolTimeSpanHistory = algorithm.History(timeSpan, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(noSymbolTimeSpanHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// No symbol, periods
|
|
var noSymbolPeriodBasedHistory = algorithm.History(periods, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(noSymbolPeriodBasedHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// No symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Single symbol, time span
|
|
var singleSymbolTimeSpanHistory = algorithm.History(symbol, timeSpan, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(singleSymbolTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Single symbol, periods
|
|
var singleSymbolPeriodBasedHistory = algorithm.History(symbol, periods, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(singleSymbolPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Single symbol, date range
|
|
var singleSymbolDateRangeHistory = algorithm.History(symbol, start, end,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(singleSymbolDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(new[] { symbol }, timeSpan, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(symbolsTimeSpanHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, periods
|
|
var symbolsPeriodBasedHistory = algorithm.History(new[] { symbol }, periods, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(symbolsPeriodBasedHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, date range
|
|
var symbolsdateRangeHistory = algorithm.History(new[] { symbol }, start, end, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(symbolsdateRangeHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedNoSymbolTimeSpanHistory = algorithm.History<TradeBar>(timeSpan, resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedNoSymbolTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, no symbol, periods
|
|
// TODO: to be implemented
|
|
|
|
// Generic, no symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History<TradeBar>(symbol, timeSpan,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSingleSymbolTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, single symbol, periods
|
|
var typedSingleSymbolPeriodBasedHistory = algorithm.History<TradeBar>(symbol, periods,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History<TradeBar>(symbol, start, end,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSingleSymbolDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History<TradeBar>(new[] { symbol }, timeSpan,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSymbolsTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, periods
|
|
var typedSymbolsPeriodBasedHistory = algorithm.History<TradeBar>(new[] { symbol }, periods,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSymbolsPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History<TradeBar>(new[] { symbol }, start, end,
|
|
resolution, fillForward: fillForward).ToList();
|
|
AssertFillForwardHistoryResults(typedSymbolsDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
|
|
tradeBar = TradeBar
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTradeBarType = testModule.GetAttr("tradeBar");
|
|
|
|
// Single symbol, time span
|
|
var singleSymbolTimeSpanHistory = algorithm.History(pySymbol, timeSpan, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(singleSymbolTimeSpanHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Single symbol, periods
|
|
var singleSymbolPeriodBasedHistory = algorithm.History(pySymbol, periods, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(singleSymbolPeriodBasedHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Single symbol, date range
|
|
var singleSymbolDateRangeHistory = algorithm.History(pySymbol, start, end, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(singleSymbolDateRangeHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(pySymbols, timeSpan, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(symbolsTimeSpanHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, periods
|
|
var symbolsPeriodBasedHistory = algorithm.History(pySymbols, periods, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(symbolsPeriodBasedHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Symbol array, date range
|
|
var symbolsDateRangeHistory = algorithm.History(pySymbols, start, end, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(symbolsDateRangeHistory, expectedHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History(pyTradeBarType, pySymbol, timeSpan,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolTimeSpanHistory = algorithm.History(pyTradeBarType, symbol, timeSpan, resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, single symbol, periods
|
|
var typedSingleSymbolPeriodBasedHistory = algorithm.History(pyTradeBarType, pySymbol, periods,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolPeriodBasedHistory = algorithm.History(pyTradeBarType, symbol, periods,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History(pyTradeBarType, pySymbol, start, end,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolDateRangeHistory = algorithm.History(pyTradeBarType, symbol, start, end,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSingleSymbolDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History(pyTradeBarType, pySymbols, timeSpan,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSymbolsTimeSpanHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, periods
|
|
var typedSymbolsPeriodBasedHistory = algorithm.History(pyTradeBarType, pySymbols, periods,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSymbolsPeriodBasedHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History(pyTradeBarType, pySymbols, start, end,
|
|
resolution, fillForward: fillForward);
|
|
AssertFillForwardHistoryResults(typedSymbolsDateRangeHistory, expectedTradeBarOnlyHistoryCount, resolution, fillForward);
|
|
}
|
|
}
|
|
}
|
|
|
|
// C#
|
|
[TestCase(Language.CSharp, Resolution.Minute, true, 60)]
|
|
[TestCase(Language.CSharp, Resolution.Minute, false, 30)]
|
|
[TestCase(Language.CSharp, Resolution.Second, true, 3600)]
|
|
[TestCase(Language.CSharp, Resolution.Second, false, 1800)]
|
|
// Python
|
|
[TestCase(Language.Python, Resolution.Minute, true, 60)]
|
|
[TestCase(Language.Python, Resolution.Minute, false, 30)]
|
|
[TestCase(Language.Python, Resolution.Second, true, 3600)]
|
|
[TestCase(Language.Python, Resolution.Second, false, 1800)]
|
|
public void HistoryRequestWithExtendedMarketHours(Language language, Resolution resolution, bool extendedMarket, int expectedHistoryCount)
|
|
{
|
|
var end = new DateTime(2013, 10, 08, 16, 30, 0);
|
|
var start = end - TimeSpan.FromHours(1);
|
|
var algorithm = GetAlgorithm(end);
|
|
var symbol = algorithm.AddEquity("SPY").Symbol;
|
|
|
|
var extendedMarketPeriods = expectedHistoryCount;
|
|
var timeSpan = end - start;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var noSymbolTimeSpanHistory = algorithm.History(timeSpan, resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(noSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// No symbol, periods
|
|
var noSymbolPeriodBasedHistory = algorithm.History(extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(noSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
//// No symbol, date range
|
|
//// TODO: to be implemented
|
|
|
|
// Single symbol, time span
|
|
var singleSymbolTimeSpanHistory = algorithm.History(symbol, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(singleSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Single symbol, periods
|
|
var singleSymbolPeriodBasedHistory = algorithm.History(symbol, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(singleSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Single symbol, date range
|
|
var singleSymbolDateRangeHistory = algorithm.History(symbol, start, end,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(singleSymbolDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(new[] { symbol }, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(symbolsTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, periods
|
|
var symbolsPeriodBasedHistory = algorithm.History(new[] { symbol }, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(symbolsPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, date range
|
|
var symbolsdateRangeHistory = algorithm.History(new[] { symbol }, start, end,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(symbolsdateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedNoSymbolTimeSpanHistory = algorithm.History<TradeBar>(timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedNoSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
//// Generic, no symbol, periods
|
|
//// TODO: to be implemented
|
|
|
|
//// Generic, no symbol, date range
|
|
//// TODO: to be implemented
|
|
|
|
// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History<TradeBar>(symbol, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, single symbol, periods
|
|
var typedSingleSymbolPeriodBasedHistory = algorithm.History<TradeBar>(symbol, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History<TradeBar>(symbol, start, end,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History<TradeBar>(new[] { symbol }, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSymbolsTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, periods
|
|
var typedSymbolsPeriodBasedHistory = algorithm.History<TradeBar>(new[] { symbol }, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSymbolsPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History<TradeBar>(new[] { symbol }, start, end,
|
|
resolution, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSymbolsDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
tradeBar = TradeBar
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTradeBarType = testModule.GetAttr("tradeBar");
|
|
|
|
// Single symbol, time span
|
|
var singleSymbolTimeSpanHistory = algorithm.History(pySymbol, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(singleSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Single symbol, periods
|
|
var singleSymbolPeriodBasedHistory = algorithm.History(pySymbol, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(singleSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Single symbol, date range
|
|
var singleSymbolDateRangeHistory = algorithm.History(pySymbol, start, end,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(singleSymbolDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(pySymbols, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(symbolsTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, periods
|
|
var symbolsPeriodBasedHistory = algorithm.History(pySymbols, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(symbolsPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Symbol array, date range
|
|
var symbolsDateRangeHistory = algorithm.History(pySymbols, start, end,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(symbolsDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History(pyTradeBarType, pySymbol, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolTimeSpanHistory = algorithm.History(pyTradeBarType, symbol, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, single symbol, periods
|
|
var typedSingleSymbolPeriodBasedHistory = algorithm.History(pyTradeBarType, pySymbol,
|
|
extendedMarketPeriods, resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolPeriodBasedHistory = algorithm.History(pyTradeBarType, symbol,
|
|
extendedMarketPeriods, resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History(pyTradeBarType, pySymbol, start, end,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolDateRangeHistory = algorithm.History(pyTradeBarType, symbol, start, end,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History(pyTradeBarType, pySymbols, timeSpan,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSymbolsTimeSpanHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, periods
|
|
var typedSymbolsPeriodBasedHistory = algorithm.History(pyTradeBarType, pySymbols, extendedMarketPeriods,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSymbolsPeriodBasedHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History(pyTradeBarType, pySymbols, start, end,
|
|
resolution, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSymbolsDateRangeHistory, expectedHistoryCount, extendedMarket, resolution, symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
// C#
|
|
[TestCase(Language.CSharp, true, 4023, 800)] // 4023, 800
|
|
[TestCase(Language.CSharp, false, 2056, 437)] // 2056, 437
|
|
// Python
|
|
[TestCase(Language.Python, true, 4023)] //, 4023
|
|
[TestCase(Language.Python, false, 2056)] //, 2056
|
|
public void HistoryRequestWithExtendedMarketHoursTickResolution(
|
|
Language language,
|
|
bool extendedMarket,
|
|
int historyExpectedCount,
|
|
// History<T> methods that take multiple symbols still have a bug for Tick type,
|
|
// where slice.Get() returns only the last tick for each symbol, so the expected count is different
|
|
int cSharpTypedMultiSymbolHistoryExpectedCount = 0)
|
|
{
|
|
var start = new DateTime(2013, 10, 07, 15, 59, 55);
|
|
var end = start.AddSeconds(10);
|
|
var algorithm = GetAlgorithm(end);
|
|
var symbol = algorithm.AddEquity("SPY").Symbol;
|
|
|
|
var timeSpan = end - start;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var noSymbolTimeSpanHistory = algorithm.History(timeSpan, Resolution.Tick, extendedMarketHours: extendedMarket)
|
|
.SelectMany(x => x.Ticks[symbol]).ToList();
|
|
AssertExtendedMarketHistoryResults(noSymbolTimeSpanHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// No symbol, periods
|
|
// Not available for tick resolution
|
|
|
|
//// No symbol, date range
|
|
//// TODO: to be implemented
|
|
|
|
// Single symbol, time span
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Single symbol, periods
|
|
// Not available for tick resolution
|
|
|
|
// Single symbol, date range
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(new[] { symbol }, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).SelectMany(x => x.Ticks[symbol]).ToList();
|
|
AssertExtendedMarketHistoryResults(symbolsTimeSpanHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Symbol array, periods
|
|
// Not available for tick resolution
|
|
|
|
//// Symbol array, date range
|
|
var symbolsdateRangeHistory = algorithm.History(new[] { symbol }, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).SelectMany(x => x.Ticks[symbol]).ToList();
|
|
AssertExtendedMarketHistoryResults(symbolsdateRangeHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedNoSymbolTimeSpanHistory = algorithm.History<Tick>(timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedNoSymbolTimeSpanHistory, cSharpTypedMultiSymbolHistoryExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
//// Generic, no symbol, periods
|
|
//// TODO: to be implemented
|
|
|
|
//// Generic, no symbol, date range
|
|
//// TODO: to be implemented
|
|
|
|
//// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History<Tick>(symbol, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Generic, single symbol, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
//// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History<Tick>(symbol, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History<Tick>(new[] { symbol }, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSymbolsTimeSpanHistory, cSharpTypedMultiSymbolHistoryExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Generic, symbol array, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History<Tick>(new[] { symbol }, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket).ToList();
|
|
AssertExtendedMarketHistoryResults(typedSymbolsDateRangeHistory, cSharpTypedMultiSymbolHistoryExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
tick = Tick
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTickType = testModule.GetAttr("tick");
|
|
|
|
// Single symbol, time span
|
|
var singleSymbolTimeSpanHistory = algorithm.History(pySymbol, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(singleSymbolTimeSpanHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Single symbol, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Single symbol, date range
|
|
var singleSymbolDateRangeHistory = algorithm.History(pySymbol, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(singleSymbolDateRangeHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Symbol array, time span
|
|
var symbolsTimeSpanHistory = algorithm.History(pySymbols, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(symbolsTimeSpanHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Symbol array, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Symbol array, date range
|
|
var symbolsDateRangeHistory = algorithm.History(pySymbols, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(symbolsDateRangeHistory, historyExpectedCount, extendedMarket, Resolution.Tick, symbol);
|
|
|
|
// Generic, single symbol, time span
|
|
var typedSingleSymbolTimeSpanHistory = algorithm.History(pyTickType, pySymbol, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolTimeSpanHistory = algorithm.History(pyTickType, symbol, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolTimeSpanHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Generic, single symbol, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Generic, single symbol, date range
|
|
var typedSingleSymbolDateRangeHistory = algorithm.History(pyTickType, pySymbol, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
typedSingleSymbolDateRangeHistory = algorithm.History(pyTickType, symbol, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSingleSymbolDateRangeHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Generic, symbol array, time span
|
|
var typedSymbolsTimeSpanHistory = algorithm.History(pyTickType, pySymbols, timeSpan,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSymbolsTimeSpanHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
|
|
// Generic, symbol array, periods
|
|
// Not available for tick resolution (TradeBar API)
|
|
|
|
// Generic, symbol array, date range
|
|
var typedSymbolsDateRangeHistory = algorithm.History(pyTickType, pySymbols, start, end,
|
|
Resolution.Tick, extendedMarketHours: extendedMarket);
|
|
AssertExtendedMarketHistoryResults(typedSymbolsDateRangeHistory, historyExpectedCount, extendedMarket,
|
|
Resolution.Tick, symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
// C#
|
|
[TestCase(Language.CSharp, Resolution.Daily, 51)]
|
|
[TestCase(Language.CSharp, Resolution.Hour, 400)]
|
|
[TestCase(Language.CSharp, Resolution.Minute, 22395)]
|
|
// Python
|
|
[TestCase(Language.Python, Resolution.Daily, 51)]
|
|
[TestCase(Language.Python, Resolution.Hour, 400)]
|
|
[TestCase(Language.Python, Resolution.Minute, 22395)]
|
|
public void HistoryRequestWithDataMappingMode(Language language, Resolution resolution, int expectedHistoryCount)
|
|
{
|
|
var start = new DateTime(2013, 10, 11);
|
|
var end = new DateTime(2013, 12, 21);
|
|
var algorithm = GetAlgorithm(end);
|
|
var symbol = algorithm.AddFuture(Futures.Indices.SP500EMini, resolution, fillForward: true).Symbol;
|
|
|
|
var dataMappingModes = GetAllDataMappingModes();
|
|
var timeSpan = end - start;
|
|
var periods = expectedHistoryCount;
|
|
|
|
var expectedMappingDates = new List<DateTime>
|
|
{
|
|
// DataMappingMode.LastTradingDay (0)
|
|
new DateTime(2013, 12, 20),
|
|
// DataMappingMode.FirstDayMonth (1)
|
|
new DateTime(2013, 12, 02),
|
|
// DataMappingMode.OpenInterest (2)
|
|
new DateTime(2013, 12, 18),
|
|
// DataMappingMode.OpenInterestAnnual (3)
|
|
new DateTime(2013, 11, 18),
|
|
};
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// No symbol, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(periods, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// No symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Single symbols, time span
|
|
var tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(symbol, timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Single symbols, periods
|
|
tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(symbol, periods, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Single symbols, date range
|
|
tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(symbol, start, end, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Symbol array, time span
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(new[] { symbol }, timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount, resolution, expectedMappingDates);
|
|
|
|
// Symbol array, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(new[] { symbol }, periods, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount, resolution, expectedMappingDates);
|
|
|
|
// Symbol array, date range
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(new[] { symbol }, start, end, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount, resolution, expectedMappingDates);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History<TradeBar>(timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, no symbol, periods
|
|
// TODO: to be implemented
|
|
|
|
// Generic, no symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Generic, single symbol, time span
|
|
tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History<TradeBar>(symbol, timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, single symbol, periods
|
|
tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History<TradeBar>(symbol, periods, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, single symbol, date range
|
|
tradeBarHistoryResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History<TradeBar>(symbol, start, end, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, symbol array, time span
|
|
typedHistoryResults = dataMappingModes
|
|
.Select(mappingMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, timeSpan, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, symbol array, periods
|
|
typedHistoryResults = dataMappingModes
|
|
.Select(mappingMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, periods, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
|
|
// Generic, symbol array, date range
|
|
typedHistoryResults = dataMappingModes
|
|
.Select(mappingMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, start, end, resolution, dataMappingMode: mappingMode).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution,
|
|
expectedMappingDates);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
tradeBar = TradeBar
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTradeBar = testModule.GetAttr("tradeBar");
|
|
|
|
// Single symbols, time span
|
|
var historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbol, timeSpan, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbol, periods, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, date range
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbol, start, end, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, time span
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbols, timeSpan, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbols, periods, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, date range
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pySymbols, start, end, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, time span
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbol, timeSpan, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, symbol, timeSpan, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbol, periods, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, symbol, periods, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, date range
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbol, start, end, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, symbol, start, end, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, time span
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbols, timeSpan, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, periods
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbols, periods, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, date range
|
|
historyResults = dataMappingModes
|
|
.Select(mappingMode => algorithm.History(pyTradeBar, pySymbols, start, end, resolution, dataMappingMode: mappingMode))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, symbol, expectedHistoryCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCaseSource(nameof(GetHistoryWithDataNormalizationModeTestCases))]
|
|
public void HistoryRequestWithDataNormalizationMode(Language language, Symbol symbol, Resolution resolution,
|
|
DateTime start, DateTime end, int expectedHistoryCount)
|
|
{
|
|
var algorithm = GetAlgorithm(end);
|
|
algorithm.AddSecurity(symbol, fillForward: true);
|
|
|
|
var dataNormalizationModes = symbol.SecurityType == SecurityType.Equity
|
|
? new[]
|
|
{
|
|
DataNormalizationMode.Raw,
|
|
DataNormalizationMode.Adjusted,
|
|
DataNormalizationMode.SplitAdjusted
|
|
}
|
|
: new[]
|
|
{
|
|
DataNormalizationMode.Raw,
|
|
DataNormalizationMode.BackwardsRatio,
|
|
DataNormalizationMode.BackwardsPanamaCanal,
|
|
DataNormalizationMode.ForwardPanamaCanal
|
|
};
|
|
var timeSpan = end - start;
|
|
// Request periods is used to get the start and end dates only, the actual number of periods is expectedHistoryCount
|
|
var periods = expectedHistoryCount;
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(timeSpan, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// No symbol, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(periods, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// No symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Single symbols, time span
|
|
var tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(symbol, timeSpan, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Single symbols, periods
|
|
tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(symbol, periods, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Single symbols, date range
|
|
tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(symbol, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, time span
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(new[] { symbol }, timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(new[] { symbol }, periods, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, date range
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(new[] { symbol }, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History<TradeBar>(timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, no symbol, periods
|
|
// TODO: to be implemented
|
|
|
|
// Generic, no symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Generic, single symbol, time span
|
|
tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History<TradeBar>(symbol, timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, single symbol, periods
|
|
tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History<TradeBar>(symbol, periods, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, single symbol, date range
|
|
tradeBarHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History<TradeBar>(symbol, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, time span
|
|
typedHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, timeSpan, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, periods
|
|
typedHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, periods, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, date range
|
|
typedHistoryResults = dataNormalizationModes
|
|
.Select(normalizationMode =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, start, end, resolution, dataNormalizationMode: normalizationMode).ToList())
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
tradeBar = TradeBar
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTradeBar = testModule.GetAttr("tradeBar");
|
|
|
|
// Single symbols, time span
|
|
var historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbol, timeSpan, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbol, periods, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, date range
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbol, start, end, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, time span
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbols, timeSpan, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbols, periods, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, date range
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pySymbols, start, end, resolution, dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, time span
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbol, timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, symbol, timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbol, periods, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, symbol, periods, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, date range
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbol, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, symbol, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, time span
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbols, timeSpan, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, periods
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbols, periods, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, date range
|
|
historyResults = dataNormalizationModes
|
|
.Select(normalizationMode => algorithm.History(pyTradeBar, pySymbols, start, end, resolution,
|
|
dataNormalizationMode: normalizationMode))
|
|
.ToList();
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults, symbol, expectedHistoryCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
// C#
|
|
[TestCase(Language.CSharp, Resolution.Daily, 61)]
|
|
[TestCase(Language.CSharp, Resolution.Hour, 477)]
|
|
[TestCase(Language.CSharp, Resolution.Minute, 26670)]
|
|
// Python
|
|
[TestCase(Language.Python, Resolution.Daily, 61)]
|
|
[TestCase(Language.Python, Resolution.Hour, 477)]
|
|
[TestCase(Language.Python, Resolution.Minute, 26670)]
|
|
public void HistoryRequestWithContracDepthOffsets(Language language, Resolution resolution, int expectedHistoryCount)
|
|
{
|
|
var start = new DateTime(2013, 10, 07);
|
|
var end = new DateTime(2014, 1, 1);
|
|
var algorithm = GetAlgorithm(end);
|
|
var symbol = algorithm.AddFuture(Futures.Indices.SP500EMini, fillForward: true).Symbol;
|
|
|
|
var timeSpan = end - start;
|
|
// We are lacking minute data for the contract used for offset=2 on the request start date computed minute when using periods,
|
|
// So we use a slightly larger number of periods to compensate for that.
|
|
var periods = resolution != Resolution.Minute ? expectedHistoryCount : (int)(expectedHistoryCount * 1.001);
|
|
var offsets = new[] { 0, 1, 2 };
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
// No symbol, time span
|
|
var historyResults = offsets
|
|
.Select(offset => algorithm.History(timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// No symbol, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(periods, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// No symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Single symbols, time span
|
|
var tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History(symbol, timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Single symbols, periods
|
|
tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History(symbol, periods, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Single symbols, date range
|
|
tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History(symbol, start, end, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, time span
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(new[] { symbol }, timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(new[] { symbol }, periods, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Symbol array, date range
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(new[] { symbol }, start, end, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, no symbol, time span
|
|
var typedHistoryResults = offsets
|
|
.Select(offset => algorithm.History<TradeBar>(timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, no symbol, periods
|
|
// TODO: to be implemented
|
|
|
|
// Generic, no symbol, date range
|
|
// TODO: to be implemented
|
|
|
|
// Generic, single symbol, time span
|
|
tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History<TradeBar>(symbol, timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, single symbol, periods
|
|
tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History<TradeBar>(symbol, periods, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, single symbol, date range
|
|
tradeBarHistoryResults = offsets
|
|
.Select(offset => algorithm.History<TradeBar>(symbol, start, end, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(tradeBarHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, time span
|
|
typedHistoryResults = offsets
|
|
.Select(offset =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, timeSpan, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, periods
|
|
typedHistoryResults = offsets
|
|
.Select(offset =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, periods, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
|
|
// Generic, symbol array, date range
|
|
typedHistoryResults = offsets
|
|
.Select(offset =>
|
|
algorithm.History<TradeBar>(new[] { symbol }, start, end, resolution, contractDepthOffset: offset).ToList())
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(typedHistoryResults, symbol, expectedHistoryCount, resolution);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule", @"
|
|
from AlgorithmImports import *
|
|
tradeBar = TradeBar
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
using var pySymbol = symbol.ToPython();
|
|
using var pySymbols = new PyList(new[] { pySymbol });
|
|
using var pyTradeBar = testModule.GetAttr("tradeBar");
|
|
|
|
// Single symbols, time span
|
|
var historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbol, timeSpan, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbol, periods, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Single symbols, date range
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbol, start, end, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, time span
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbols, timeSpan, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbols, periods, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Symbol array, date range
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pySymbols, start, end, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, time span
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbol, timeSpan, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, symbol, timeSpan, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbol, periods, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, symbol, periods, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, single symbol, date range
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbol, start, end, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
// Same as previous but using a Symbol instead of pySymbol
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, symbol, start, end, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, time span
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbols, timeSpan, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, periods
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbols, periods, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
|
|
// Generic, symbol array, date range
|
|
historyResults = offsets
|
|
.Select(offset => algorithm.History(pyTradeBar, pySymbols, start, end, resolution, contractDepthOffset: offset))
|
|
.ToList();
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(historyResults, symbol, expectedHistoryCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
// USA Equity market hours: 4am-9:30am (pre-market), 9:30am-4pm (regular market), 4pm-8pm (post-market)
|
|
// 6.5h (regular market duration) (rounded to 7)
|
|
[TestCase(Resolution.Hour, false, false, 7)]
|
|
// Hour resolution doesn't have extended hours data
|
|
[TestCase(Resolution.Hour, false, true, 7)]
|
|
[TestCase(Resolution.Hour, true, false, 7)]
|
|
[TestCase(Resolution.Hour, true, true, 7)]
|
|
[TestCase(Resolution.Hour, false, null, 7)]
|
|
[TestCase(Resolution.Hour, true, null, 7)]
|
|
// 390 = 6.5h (regular market duration) * 60min/h = 390min (bars)
|
|
[TestCase(Resolution.Minute, false, false, 390)]
|
|
// 960 = [5.5h (pre-market duration) + 6.5h (regular market duration) + 4h (post-market duration)] * 60min/h = 16h * 60min/h = 960min (bars)
|
|
[TestCase(Resolution.Minute, false, true, 960)]
|
|
[TestCase(Resolution.Minute, true, false, 390)]
|
|
[TestCase(Resolution.Minute, true, true, 960)]
|
|
[TestCase(Resolution.Minute, false, null, 390)]
|
|
[TestCase(Resolution.Minute, true, null, 960)]
|
|
[TestCase(Resolution.Second, false, false, 390 * 60)]
|
|
[TestCase(Resolution.Second, false, true, 960 * 60)]
|
|
[TestCase(Resolution.Second, true, false, 390 * 60)]
|
|
[TestCase(Resolution.Second, true, true, 960 * 60)]
|
|
[TestCase(Resolution.Second, false, null, 390 * 60)]
|
|
[TestCase(Resolution.Second, true, null, 960 * 60)]
|
|
public void HistoryRequestFactoryGetsTheRightStartTimeForBarCount(Resolution resolution, bool assetWithExtendedMarket,
|
|
bool? requestWithExtendedMarket, int requestPeriods)
|
|
{
|
|
var start = new DateTime(2014, 06, 09);
|
|
var end = new DateTime(2014, 06, 10);
|
|
var algorithm = GetAlgorithm(end);
|
|
var aapl = algorithm.AddEquity("AAPL", extendedMarketHours: assetWithExtendedMarket);
|
|
var config = algorithm.SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(aapl.Symbol).First();
|
|
var exchangeHours = aapl.Exchange.Hours;
|
|
|
|
var historyRequestFactory = new HistoryRequestFactory(algorithm);
|
|
var extendedMarket = resolution != Resolution.Hour ? requestWithExtendedMarket ?? assetWithExtendedMarket : false;
|
|
var marketOpen = exchangeHours.GetNextMarketOpen(start, extendedMarketHours: extendedMarket);
|
|
if (resolution == Resolution.Hour)
|
|
{
|
|
// Adjust the expected start in case the regular hours segment is not an exact int number of hours
|
|
var marketClose = exchangeHours.GetNextMarketClose(marketOpen, extendedMarketHours: extendedMarket);
|
|
marketOpen += TimeSpan.FromHours((marketClose.TimeOfDay - marketOpen.TimeOfDay).TotalHours - requestPeriods);
|
|
}
|
|
|
|
var requestStart = historyRequestFactory.GetStartTimeAlgoTz(aapl.Symbol, requestPeriods, resolution, exchangeHours,
|
|
config.DataTimeZone, config.Type, extendedMarketHours: requestWithExtendedMarket);
|
|
Assert.AreEqual(marketOpen, requestStart);
|
|
}
|
|
|
|
// This reproduces https://github.com/QuantConnect/Lean/issues/7504
|
|
[TestCase(Language.CSharp)]
|
|
[TestCase(Language.Python)]
|
|
public void DefaultAlwaysOpenMarketHoursForBaseSecurityType(Language language)
|
|
{
|
|
var start = new DateTime(2013, 10, 8);
|
|
var end = start.AddDays(1);
|
|
var algorithm = GetAlgorithm(end);
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var spy = algorithm.AddEquity("SPY").Symbol;
|
|
// We will try to fetch history without a subscription
|
|
var customSymbol = Symbol.CreateBase(typeof(CustomData), spy);
|
|
|
|
List<CustomData> history = null;
|
|
Assert.DoesNotThrow(() => history = algorithm.History<CustomData>(customSymbol, start, end, Resolution.Minute).ToList());
|
|
Console.WriteLine(history.Count);
|
|
Assert.IsNotEmpty(history);
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
dynamic getHistory = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests import *
|
|
|
|
class TestPythonCustomData(PythonData):
|
|
def GetSource(self, config, date, isLiveMode):
|
|
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType)
|
|
source = f'{Globals.DataFolder}equity/usa/minute/spy/{fileName}'
|
|
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
|
|
|
def Reader(self, config, line, date, isLiveMode):
|
|
data = line.split(',')
|
|
|
|
result = TestPythonCustomData()
|
|
result.DataType = MarketDataType.Base
|
|
result.Symbol = config.Symbol
|
|
result.Time = date.date() + timedelta(milliseconds=int(data[0]))
|
|
result.Value = float(data[4])
|
|
result[""Open""] = float(data[1])
|
|
result[""High""] = float(data[2])
|
|
result[""Low""] = float(data[3])
|
|
result[""Close""] = float(data[4])
|
|
|
|
return result
|
|
|
|
def getHistory(algorithm, start, end):
|
|
spy = algorithm.AddEquity(""SPY"").Symbol
|
|
customSymbol = Symbol.CreateBase(TestPythonCustomData, spy, Market.USA)
|
|
|
|
return algorithm.History(TestPythonCustomData, customSymbol, start, end, Resolution.Minute)
|
|
").GetAttr("getHistory");
|
|
|
|
algorithm.SetPandasConverter();
|
|
|
|
dynamic history = null;
|
|
Assert.DoesNotThrow(() => history = getHistory(algorithm, start, end));
|
|
Assert.IsNotNull(history);
|
|
Assert.Greater(history.shape[0].As<int>(), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PythonCustomDataThrowing()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
algorithm.SetHistoryProvider(new ThrowingHistoryProvider());
|
|
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
algorithm.SetPandasConverter();
|
|
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getHistory(algorithm, symbol, period):
|
|
return algorithm.History(symbol, period, Resolution.Minute)
|
|
");
|
|
dynamic getDateRangeHistory = testModule.GetAttr("getHistory");
|
|
|
|
Assert.Throws<ClrBubbledException>(() => getDateRangeHistory(algorithm, Symbols.AAPL, 10));
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PythonUniverseHistoryDataFramesAreFlattened()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 03, 28));
|
|
var universe = algorithm.AddUniverse(x => x.Select(x => x.Symbol));
|
|
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
algorithm.SetPandasConverter();
|
|
|
|
var testModule = PyModule.FromString("PythonHistoryDataFramesAreFlattened",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
def getFlattenedUniverseHistory(algorithm, universe, period):
|
|
return algorithm.history(universe, period, flatten=True)
|
|
|
|
def getUnflattenedUniverseHistory(algorithm, universe, period):
|
|
return algorithm.history(universe, period)
|
|
|
|
def assertFlattenedHistoryDates(df, expected_dates):
|
|
assert df.index.levels[0].to_list() == expected_dates, f'Expected dates: {expected_dates}, actual dates: {df.index.levels[0].to_list()}'
|
|
|
|
def assertUnflattenedHistoryDates(df, expected_dates):
|
|
assert df.index.to_list() == expected_dates, f'Expected dates: {expected_dates}, actual dates: {df.index.levels[0].to_list()}'
|
|
|
|
def assertConstituents(flattened_df, unflattened_df, dates, expected_constituents_per_date):
|
|
for i, date in enumerate(dates):
|
|
unflattened_universe = unflattened_df.loc[date]
|
|
assert isinstance(unflattened_universe, list), f'Unflattened DF: expected a list, found {type(unflattened_universe)}'
|
|
assert len(unflattened_universe) == expected_constituents_per_date[i], f'Unflattened DF: expected {expected_constituents_per_date[i]} constituents for date {date}, got {len(unflattened_universe)}'
|
|
|
|
for constituent in unflattened_universe:
|
|
assert isinstance(constituent, Fundamental), f'Unflattened DF: expected a list of Fundamental, found {type(constituent)}'
|
|
|
|
flattened_sub_df = flattened_df.loc[date]
|
|
assert flattened_sub_df.shape[0] == len(unflattened_universe), f'Flattened DF: expected {len(unflattened_universe)} rows for date {date}, got {flattened_sub_df.shape[0]}'
|
|
|
|
flattened_universe_symbols = flattened_sub_df.index.to_list()
|
|
unflattened_universe_symbols = [constituent.symbol for constituent in unflattened_universe]
|
|
flattened_universe_symbols.sort()
|
|
unflattened_universe_symbols.sort()
|
|
assert flattened_universe_symbols == unflattened_universe_symbols, f'Flattened DF: flattened universe symbols are not equal to unflattened universe symbols for date {date}'
|
|
");
|
|
dynamic getFlattenedUniverseHistory = testModule.GetAttr("getFlattenedUniverseHistory");
|
|
var flattenedDf = getFlattenedUniverseHistory(algorithm, universe, 3);
|
|
|
|
dynamic getUnflattenedUniverseHistory = testModule.GetAttr("getUnflattenedUniverseHistory");
|
|
var unflattenedDf = getUnflattenedUniverseHistory(algorithm, universe, 3);
|
|
// Drop the symbol
|
|
unflattenedDf = unflattenedDf.droplevel(0);
|
|
|
|
var expectedDates = new List<DateTime>
|
|
{
|
|
new DateTime(2014, 03, 26),
|
|
new DateTime(2014, 03, 27),
|
|
new DateTime(2014, 03, 28)
|
|
};
|
|
dynamic assertFlattenedHistoryDates = testModule.GetAttr("assertFlattenedHistoryDates");
|
|
AssertDesNotThrowPythonException(() => assertFlattenedHistoryDates(flattenedDf, expectedDates));
|
|
|
|
dynamic assertUnflattenedHistoryDates = testModule.GetAttr("assertUnflattenedHistoryDates");
|
|
AssertDesNotThrowPythonException(() => assertUnflattenedHistoryDates(unflattenedDf, expectedDates));
|
|
|
|
var expectedConstituentsCounts = new[] { 7068, 7055, 7049 };
|
|
dynamic assertConstituents = testModule.GetAttr("assertConstituents");
|
|
AssertDesNotThrowPythonException(() => assertConstituents(flattenedDf, unflattenedDf, expectedDates, expectedConstituentsCounts));
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void CSharpCustomUniverseHistoryDataFramesHaveExpectedFormat()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2015, 01, 15));
|
|
var universe = algorithm.AddUniverse<CustomUniverseData>("CustomUniverse", Resolution.Daily, (x) => x.Select(y => y.Symbol));
|
|
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
algorithm.SetPandasConverter();
|
|
|
|
using var testModule = PyModule.FromString("PythonCustomUniverseHistoryDataFramesHaveExpectedFormat",
|
|
$@"
|
|
from AlgorithmImports import *
|
|
|
|
def get_universe_history(algorithm, universe, flatten):
|
|
return algorithm.history(universe, 3, flatten=flatten)
|
|
");
|
|
|
|
dynamic getUniverseHistory = testModule.GetAttr("get_universe_history");
|
|
var df = getUniverseHistory(algorithm, universe, false);
|
|
var flattenedDf = getUniverseHistory(algorithm, universe, true);
|
|
|
|
Func<CustomUniverseData, decimal> getWeight = (data) => data.Weight;
|
|
AssertCustomUniverseDataFrames(df, flattenedDf, getWeight);
|
|
|
|
var columns = ((List<PyObject>)flattenedDf.columns.to_list().As<List<PyObject>>())
|
|
.Select(column => column.InvokeMethod("__str__").GetAndDispose<string>());
|
|
CollectionAssert.DoesNotContain(columns, "data");
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PythonCustomUniverseHistoryDataFramesHaveExpectedFormat()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2015, 01, 15));
|
|
|
|
using (Py.GIL())
|
|
{
|
|
PythonInitializer.Initialize();
|
|
algorithm.SetPandasConverter();
|
|
|
|
using var testModule = PyModule.FromString("PythonCustomUniverseHistoryDataFramesHaveExpectedFormat",
|
|
$@"
|
|
from AlgorithmImports import *
|
|
|
|
class CustomUniverseData(PythonData):
|
|
|
|
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
|
|
return SubscriptionDataSource('TestData/portfolio_targets.csv',
|
|
SubscriptionTransportMedium.LOCAL_FILE,
|
|
FileFormat.FOLDING_COLLECTION)
|
|
|
|
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
|
|
# Skip the header row.
|
|
if not line[0].isnumeric():
|
|
return None
|
|
items = line.split(',')
|
|
data = CustomUniverseData()
|
|
data.end_time = datetime.strptime(items[0], '%Y-%m-%d')
|
|
data.time = data.end_time - timedelta(1)
|
|
data.symbol = Symbol.create(items[1], SecurityType.EQUITY, Market.USA)
|
|
data['weight'] = float(items[2])
|
|
return data
|
|
|
|
def get_universe_history(algorithm, flatten):
|
|
universe = algorithm.add_universe(CustomUniverseData, 'CustomUniverse', Resolution.DAILY, lambda alt_coarse: [x.symbol for x in alt_coarse])
|
|
return algorithm.history(universe, 3, flatten=flatten)
|
|
|
|
");
|
|
|
|
dynamic getUniverseHistory = testModule.GetAttr("get_universe_history");
|
|
var df = getUniverseHistory(algorithm, false);
|
|
var flattenedDf = getUniverseHistory(algorithm, true);
|
|
|
|
Func<PythonData, decimal> getWeight = (data) => Convert.ToDecimal(data.GetProperty("weight"));
|
|
AssertCustomUniverseDataFrames(df, flattenedDf, getWeight);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PythonCustomUniverseHistoryCanBeFetchedUsingCSharpApi()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2018, 6, 1));
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("PythonCustomUniverseHistoryCanBeFetchedUsingCSharpApi",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
|
|
class StockDataSource(PythonData):
|
|
|
|
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live: bool) -> SubscriptionDataSource:
|
|
source = ""../../TestData/daily-stock-picker-backtest.csv""
|
|
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
|
|
|
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live: bool) -> BaseData:
|
|
if not (line.strip() and line[0].isdigit()): return None
|
|
|
|
stocks = StockDataSource()
|
|
stocks.symbol = config.symbol
|
|
|
|
try:
|
|
csv = line.split(',')
|
|
stocks.time = datetime.strptime(csv[0], ""%Y%m%d"")
|
|
stocks.end_time = stocks.time + timedelta(days=1)
|
|
stocks[""Symbols""] = csv[1:]
|
|
|
|
except ValueError:
|
|
# Do nothing
|
|
return None
|
|
|
|
return stocks
|
|
|
|
def universe_selector(data):
|
|
return [x.symbol for x in data]
|
|
|
|
def add_universe(algorithm):
|
|
return algorithm.add_universe(StockDataSource, ""universe-stock-data-source"", Resolution.DAILY, universe_selector)
|
|
|
|
def get_history(algorithm, universe):
|
|
return list(algorithm.history[StockDataSource](universe.symbol, datetime(2018, 1, 1), datetime(2018, 6, 1), Resolution.DAILY))
|
|
");
|
|
|
|
dynamic getUniverse = testModule.GetAttr("add_universe");
|
|
dynamic getHistory = testModule.GetAttr("get_history");
|
|
|
|
var universe = getUniverse(algorithm);
|
|
var history = getHistory(algorithm, universe).As<List<PythonData>>() as List<PythonData>;
|
|
Assert.IsNotEmpty(history);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void PythonCustomDataHistoryCanBeFetchedUsingCSharpApi()
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("PythonCustomDataHistoryCanBeFetchedUsingCSharpApi",
|
|
@"
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests import *
|
|
|
|
class MyCustomDataType(PythonData):
|
|
|
|
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live: bool) -> SubscriptionDataSource:
|
|
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, Resolution.MINUTE, config.TickType)
|
|
source = f'{Globals.DataFolder}equity/usa/minute/spy/{fileName}'
|
|
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
|
|
|
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live: bool) -> BaseData:
|
|
data = line.split(',')
|
|
result = MyCustomDataType()
|
|
result.DataType = MarketDataType.Base
|
|
result.Symbol = config.Symbol
|
|
result.Time = date + timedelta(milliseconds=int(data[0]))
|
|
result.Value = 1
|
|
|
|
return result
|
|
|
|
def add_data(algorithm):
|
|
return algorithm.add_data(MyCustomDataType, ""MyCustomDataType"", Resolution.DAILY)
|
|
|
|
def get_history(algorithm, security):
|
|
return list(algorithm.history[MyCustomDataType](security.symbol, datetime(2013, 10, 7), datetime(2013, 10, 8), Resolution.MINUTE))
|
|
");
|
|
|
|
dynamic getCustomSecurity = testModule.GetAttr("add_data");
|
|
dynamic getHistory = testModule.GetAttr("get_history");
|
|
|
|
var security = getCustomSecurity(algorithm);
|
|
var history = getHistory(algorithm, security).As<List<PythonData>>() as List<PythonData>;
|
|
Assert.IsNotEmpty(history);
|
|
}
|
|
}
|
|
|
|
[TestCase(true)]
|
|
[TestCase(false)]
|
|
public void HistoryHandlesSymbolChangedEventsCorrectly(bool useCreateSymbol)
|
|
{
|
|
var start = new DateTime(2021, 1, 1);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2021, 1, 5);
|
|
|
|
Symbol symbol;
|
|
if (useCreateSymbol)
|
|
{
|
|
symbol = Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME);
|
|
}
|
|
else
|
|
{
|
|
var future = _algorithm.AddFuture(
|
|
Futures.Indices.SP500EMini,
|
|
dataMappingMode: DataMappingMode.OpenInterest,
|
|
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
|
|
contractDepthOffset: 0);
|
|
symbol = future.Symbol;
|
|
}
|
|
|
|
// Retrieve historical SymbolChangedEvent data
|
|
var history = _algorithm.History<SymbolChangedEvent>(symbol, TimeSpan.FromDays(365)).ToList();
|
|
|
|
// Ensure the history contains symbol change events
|
|
Assert.IsNotEmpty(history);
|
|
Assert.AreEqual(4, history.Count);
|
|
|
|
// Verify each event has valid old and new symbols, and they are different
|
|
foreach (var symbolChangedEvent in history)
|
|
{
|
|
Assert.IsNotNull(symbolChangedEvent.OldSymbol);
|
|
Assert.IsNotNull(symbolChangedEvent.NewSymbol);
|
|
Assert.AreNotEqual(symbolChangedEvent.OldSymbol, symbolChangedEvent.NewSymbol);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void OpenInterestHistoryOnlyContainsDataDuringRegularTradingHours()
|
|
{
|
|
var start = new DateTime(2013, 12, 01);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2013, 12, 31);
|
|
|
|
// Add ES (E-mini S&P 500)
|
|
var future = _algorithm.AddFuture("ES", Resolution.Daily, Market.CME);
|
|
|
|
var history = _algorithm.History<OpenInterest>(future.Symbol, new DateTime(2013, 10, 10), new DateTime(2013, 11, 01), Resolution.Daily).ToList();
|
|
|
|
/* Expected 16 trading days breakdown:
|
|
October 2013:
|
|
10(Thu), 11(Fri),
|
|
14(Mon), 15(Tue), 16(Wed), 17(Thu), 18(Fri),
|
|
21(Mon), 22(Tue), 23(Wed), 24(Thu), 25(Fri),
|
|
28(Mon), 29(Tue), 30(Wed), 31(Thu)
|
|
*/
|
|
Assert.AreEqual(16, history.Count);
|
|
|
|
// Regular trading hours: Monday-Friday 9:30am-5:00pm ET
|
|
foreach (var data in history)
|
|
{
|
|
var date = data.EndTime;
|
|
var dayOfWeek = date.DayOfWeek;
|
|
Assert.AreNotEqual(DayOfWeek.Saturday, dayOfWeek);
|
|
Assert.AreNotEqual(DayOfWeek.Sunday, dayOfWeek);
|
|
}
|
|
}
|
|
|
|
[TestCase(true, Resolution.Tick, true, false, false)]
|
|
[TestCase(true, Resolution.Tick, false, false, false)]
|
|
[TestCase(true, Resolution.Tick, true, true, false)]
|
|
[TestCase(true, Resolution.Second, true, true, false)]
|
|
[TestCase(true, Resolution.Minute, true, true, false)]
|
|
[TestCase(true, Resolution.Hour, true, true, false)]
|
|
[TestCase(true, Resolution.Daily, true, true, false)]
|
|
[TestCase(true, Resolution.Tick, false, true, false)]
|
|
[TestCase(true, Resolution.Second, false, true, false)]
|
|
[TestCase(true, Resolution.Minute, false, true, false)]
|
|
[TestCase(true, Resolution.Hour, false, true, false)]
|
|
[TestCase(true, Resolution.Daily, false, true, false)]
|
|
[TestCase(false, null, false, true, false)]
|
|
|
|
[TestCase(true, Resolution.Tick, true, false, true)]
|
|
[TestCase(true, Resolution.Tick, false, false, true)]
|
|
[TestCase(true, Resolution.Tick, true, true, true)]
|
|
[TestCase(true, Resolution.Second, true, true, true)]
|
|
[TestCase(true, Resolution.Minute, true, true, true)]
|
|
[TestCase(true, Resolution.Hour, true, true, true)]
|
|
[TestCase(true, Resolution.Daily, true, true, true)]
|
|
[TestCase(true, Resolution.Tick, false, true, true)]
|
|
[TestCase(true, Resolution.Second, false, true, true)]
|
|
[TestCase(true, Resolution.Minute, false, true, true)]
|
|
[TestCase(true, Resolution.Hour, false, true, true)]
|
|
[TestCase(true, Resolution.Daily, false, true, true)]
|
|
[TestCase(false, null, false, true, true)]
|
|
public void OpenInterestReturnsConsistentResultsWithOrWithoutContract(bool addFutureContract, Resolution resolution, bool extendedMarketHours, bool specifyResolution, bool useContinuous)
|
|
{
|
|
var start = new DateTime(2013, 10, 09);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2013, 10, 10);
|
|
|
|
Symbol symbol;
|
|
if (useContinuous)
|
|
{
|
|
symbol = Symbol.Create(Futures.Metals.Gold, SecurityType.Future, Market.COMEX);
|
|
if (addFutureContract)
|
|
{
|
|
_algorithm.AddSecurity(symbol, resolution, extendedMarketHours: extendedMarketHours);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
symbol = Symbol.CreateFuture(Futures.Metals.Gold, Market.COMEX, new DateTime(2013, 10, 29));
|
|
if (addFutureContract)
|
|
{
|
|
_algorithm.AddFutureContract(symbol, resolution, extendedMarketHours: extendedMarketHours);
|
|
}
|
|
}
|
|
|
|
var typedTickHistory = _algorithm.History<OpenInterest>([symbol], TimeSpan.FromDays(1), specifyResolution ? Resolution.Minute : null);
|
|
var typedTicks = typedTickHistory
|
|
.SelectMany(x => x.Values)
|
|
.ToList();
|
|
|
|
var typedQuoteTicks = typedTicks.Where(t => t.TickType == TickType.Quote).ToList();
|
|
var typedTradeTicks = typedTicks.Where(t => t.TickType == TickType.Trade).ToList();
|
|
var typedOpenInterestTicks = typedTicks.Where(t => t.TickType == TickType.OpenInterest).ToList();
|
|
|
|
Assert.AreEqual(0, typedQuoteTicks.Count);
|
|
Assert.AreEqual(typedQuoteTicks.Count, typedTradeTicks.Count);
|
|
Assert.AreNotEqual(0, typedOpenInterestTicks.Count);
|
|
if (extendedMarketHours)
|
|
{
|
|
Assert.AreEqual(1254, typedOpenInterestTicks.Count);
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(452, typedOpenInterestTicks.Count);
|
|
}
|
|
}
|
|
|
|
[TestCase(true, Resolution.Tick, true, false, false)]
|
|
[TestCase(true, Resolution.Tick, false, false, false)]
|
|
[TestCase(true, Resolution.Tick, true, true, false)]
|
|
[TestCase(true, Resolution.Second, true, true, false)]
|
|
[TestCase(true, Resolution.Minute, true, true, false)]
|
|
[TestCase(true, Resolution.Hour, true, true, false)]
|
|
[TestCase(true, Resolution.Daily, true, true, false)]
|
|
[TestCase(true, Resolution.Tick, false, true, false)]
|
|
[TestCase(true, Resolution.Second, false, true, false)]
|
|
[TestCase(true, Resolution.Minute, false, true, false)]
|
|
[TestCase(true, Resolution.Hour, false, true, false)]
|
|
[TestCase(true, Resolution.Daily, false, true, false)]
|
|
[TestCase(false, null, false, true, false)]
|
|
|
|
[TestCase(true, Resolution.Tick, true, false, true)]
|
|
[TestCase(true, Resolution.Tick, false, false, true)]
|
|
[TestCase(true, Resolution.Tick, true, true, true)]
|
|
[TestCase(true, Resolution.Second, true, true, true)]
|
|
[TestCase(true, Resolution.Minute, true, true, true)]
|
|
[TestCase(true, Resolution.Hour, true, true, true)]
|
|
[TestCase(true, Resolution.Daily, true, true, true)]
|
|
[TestCase(true, Resolution.Tick, false, true, true)]
|
|
[TestCase(true, Resolution.Second, false, true, true)]
|
|
[TestCase(true, Resolution.Minute, false, true, true)]
|
|
[TestCase(true, Resolution.Hour, false, true, true)]
|
|
[TestCase(true, Resolution.Daily, false, true, true)]
|
|
[TestCase(false, null, false, true, true)]
|
|
public void TickHistoryReturnsConsistentResultsWithOrWithoutContract(bool addFutureContract, Resolution resolution, bool extendedMarketHours, bool specifyResolution, bool useContinuous)
|
|
{
|
|
var start = new DateTime(2013, 10, 07, 20, 0, 0);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2013, 10, 10);
|
|
|
|
Symbol symbol;
|
|
if (useContinuous)
|
|
{
|
|
symbol = Symbol.Create(Futures.Metals.Gold, SecurityType.Future, Market.COMEX);
|
|
if (addFutureContract)
|
|
{
|
|
_algorithm.AddSecurity(symbol, resolution, extendedMarketHours: extendedMarketHours);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
symbol = Symbol.CreateFuture(Futures.Metals.Gold, Market.COMEX, new DateTime(2013, 10, 29));
|
|
if (addFutureContract)
|
|
{
|
|
_algorithm.AddFutureContract(symbol, resolution, extendedMarketHours: extendedMarketHours);
|
|
}
|
|
}
|
|
|
|
var history = _algorithm.History([symbol], TimeSpan.FromHours(8), specifyResolution ? Resolution.Tick : null);
|
|
var typedTickHistory = _algorithm.History<Tick>([symbol], TimeSpan.FromHours(8), specifyResolution ? Resolution.Tick : null);
|
|
|
|
var extractedTicks = history
|
|
.Select(x => x.Get<Tick>())
|
|
.Where(x => x.Count > 0)
|
|
.SelectMany(x => x.Values)
|
|
.ToList();
|
|
|
|
var typedTicks = typedTickHistory
|
|
.SelectMany(x => x.Values)
|
|
.ToList();
|
|
|
|
var quoteTicks = extractedTicks.Where(t => t.TickType == TickType.Quote).ToList();
|
|
var tradeTicks = extractedTicks.Where(t => t.TickType == TickType.Trade).ToList();
|
|
|
|
var typedQuoteTicks = typedTicks.Where(t => t.TickType == TickType.Quote).ToList();
|
|
var typedTradeTicks = typedTicks.Where(t => t.TickType == TickType.Trade).ToList();
|
|
|
|
Assert.IsTrue(typedTicks.Count > 0);
|
|
Assert.AreEqual(extractedTicks.Count, typedTicks.Count);
|
|
Assert.IsTrue(quoteTicks.Count > 0);
|
|
Assert.IsTrue(tradeTicks.Count > 0);
|
|
Assert.AreEqual(typedQuoteTicks.Count, quoteTicks.Count);
|
|
Assert.AreEqual(typedTradeTicks.Count, tradeTicks.Count);
|
|
if (extendedMarketHours)
|
|
{
|
|
Assert.AreEqual(11950, extractedTicks.Count);
|
|
Assert.AreEqual(11944, typedQuoteTicks.Count);
|
|
Assert.AreEqual(6, typedTradeTicks.Count);
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(10743, extractedTicks.Count);
|
|
Assert.AreEqual(10737, typedQuoteTicks.Count);
|
|
Assert.AreEqual(6, typedTradeTicks.Count);
|
|
}
|
|
}
|
|
|
|
[TestCase(true, Resolution.Tick, true)]
|
|
[TestCase(true, Resolution.Second, true)]
|
|
[TestCase(true, Resolution.Minute, true)]
|
|
[TestCase(true, Resolution.Hour, true)]
|
|
[TestCase(true, Resolution.Daily, true)]
|
|
[TestCase(true, Resolution.Tick, false)]
|
|
[TestCase(true, Resolution.Second, false)]
|
|
[TestCase(true, Resolution.Minute, false)]
|
|
[TestCase(true, Resolution.Hour, false)]
|
|
[TestCase(true, Resolution.Daily, false)]
|
|
[TestCase(false, null, false)]
|
|
public void TickHistoryReturnsConsistentResultsWithOrWithoutEquity(bool addEquity, Resolution resolution, bool extendedMarketHours)
|
|
{
|
|
var start = new DateTime(2013, 10, 07);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2013, 10, 11);
|
|
|
|
var symbol = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
|
if (addEquity)
|
|
{
|
|
_algorithm.AddEquity("SPY", resolution, extendedMarketHours: extendedMarketHours);
|
|
}
|
|
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 07, 16, 00, 3).ConvertToUtc(_algorithm.TimeZone));
|
|
var history = _algorithm.History([symbol], TimeSpan.FromSeconds(6), Resolution.Tick).ToList();
|
|
var typedTickHistory = _algorithm.History<Tick>([symbol], TimeSpan.FromSeconds(6), Resolution.Tick).ToList();
|
|
|
|
var extractedTicks = history
|
|
.Select(x => x.Get<Tick>())
|
|
.Where(x => x.Count > 0)
|
|
.SelectMany(x => x.Values)
|
|
.ToList();
|
|
|
|
var typedTicks = typedTickHistory
|
|
.SelectMany(x => x.Values)
|
|
.ToList();
|
|
|
|
var quoteTicks = extractedTicks.Where(t => t.TickType == TickType.Quote).ToList();
|
|
var tradeTicks = extractedTicks.Where(t => t.TickType == TickType.Trade).ToList();
|
|
|
|
var typedQuoteTicks = typedTicks.Where(t => t.TickType == TickType.Quote).ToList();
|
|
var typedTradeTicks = typedTicks.Where(t => t.TickType == TickType.Trade).ToList();
|
|
|
|
Assert.IsTrue(typedTickHistory.Count > 0);
|
|
Assert.AreEqual(extractedTicks.Count, typedTickHistory.Count);
|
|
Assert.IsTrue(quoteTicks.Count > 0);
|
|
Assert.IsTrue(tradeTicks.Count > 0);
|
|
Assert.AreEqual(typedQuoteTicks.Count, quoteTicks.Count);
|
|
Assert.AreEqual(typedTradeTicks.Count, tradeTicks.Count);
|
|
|
|
if (extendedMarketHours)
|
|
{
|
|
Assert.AreEqual(521, extractedTicks.Count);
|
|
Assert.AreEqual(357, typedQuoteTicks.Count);
|
|
Assert.AreEqual(164, typedTradeTicks.Count);
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(263, extractedTicks.Count);
|
|
Assert.AreEqual(156, typedQuoteTicks.Count);
|
|
Assert.AreEqual(107, typedTradeTicks.Count);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<TestCaseData> GetCustomNonOptionDataHistoryForOptionConfigTestCases()
|
|
{
|
|
foreach (var language in new[] { Language.CSharp, Language.Python })
|
|
{
|
|
foreach (var symbol in new[] { Symbols.SPY, Symbols.SPY_Option_Chain, Symbols.Future_ESZ18_Dec2018 })
|
|
{
|
|
yield return new TestCaseData(language, symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCaseSource(nameof(GetCustomNonOptionDataHistoryForOptionConfigTestCases))]
|
|
public void DoesNotThrowForCustomNonOptionDataHistoryForOptionConfig(Language language, Symbol symbol)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2014, 04, 07));
|
|
|
|
if (language == Language.CSharp)
|
|
{
|
|
var history = (List<DataDictionary<CustomFundamentalTestData>>)null;
|
|
Assert.DoesNotThrow(() =>
|
|
{
|
|
history = algorithm.History<CustomFundamentalTestData>([symbol], 10, Resolution.Daily).ToList();
|
|
});
|
|
Assert.Greater(history.Count, 0);
|
|
}
|
|
else
|
|
{
|
|
using var _ = Py.GIL();
|
|
var module = PyModule.FromString("DoesNotThrowForCustomNonOptionDataHistoryForOptionConfig",
|
|
@"
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Tests.Algorithm import *
|
|
|
|
def get_history(algorithm, symbol):
|
|
return algorithm.history(AlgorithmHistoryTests.CustomFundamentalTestData, [symbol], 10, Resolution.DAILY)
|
|
");
|
|
|
|
algorithm.SetPandasConverter();
|
|
dynamic getHistory = module.GetAttr("get_history");
|
|
dynamic history = null;
|
|
Assert.DoesNotThrow(() => history = getHistory(algorithm, symbol));
|
|
Assert.IsNotNull(history);
|
|
Assert.IsFalse(history.empty.As<bool>());
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<TestCaseData> GetHistoryRequestFromNonTradableDateTestCases()
|
|
{
|
|
foreach (var date in new[]
|
|
{
|
|
// Labor day
|
|
new DateTime(2013, 9, 1),
|
|
// Sunday
|
|
new DateTime(2013, 8, 31),
|
|
// Saturday
|
|
new DateTime(2013, 8, 30)
|
|
})
|
|
{
|
|
foreach (var timeOfDay in new[] { 0, 12, 17 })
|
|
{
|
|
yield return new TestCaseData(date.AddHours(timeOfDay));
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCaseSource(nameof(GetHistoryRequestFromNonTradableDateTestCases))]
|
|
public void GetsRightSliceCountForDailyPeriodHisotryRequestFromNonTradableDate(DateTime dateTime)
|
|
{
|
|
var algorithm = GetAlgorithm(dateTime);
|
|
Assert.AreEqual(dateTime, algorithm.Time);
|
|
Assert.AreEqual(10, algorithm.History(Symbols.SPY, 10, Resolution.Daily).Count());
|
|
}
|
|
|
|
[Test]
|
|
public void DailyFuturesHistoryDoesNotIncludeSundaysAndReturnsCorrectSliceCountForPeriod([Values] bool extendedMarketHours)
|
|
{
|
|
var algorithm = GetAlgorithm(new DateTime(2013, 10, 28));
|
|
var future = algorithm.AddFuture(Futures.Indices.SP500EMini);
|
|
var history = algorithm.History([future.Symbol], 15, Resolution.Daily, fillForward: true, extendedMarketHours: extendedMarketHours).ToList();
|
|
|
|
// Five business days per week, 3 weeks from Monday 2013/10/07 to Friday 2013/10/25, Sundays are only open for extended hours
|
|
Assert.AreEqual(15, history.Count);
|
|
Assert.AreEqual(new DateTime(2013, 10, 07), history[0].Time.Date);
|
|
|
|
foreach (var slice in history)
|
|
{
|
|
foreach (var data in slice.AllData)
|
|
{
|
|
Assert.AreNotEqual(DayOfWeek.Saturday, data.Time.DayOfWeek);
|
|
Assert.AreNotEqual(DayOfWeek.Sunday, data.Time.DayOfWeek);
|
|
}
|
|
}
|
|
}
|
|
|
|
[TestCase(false)]
|
|
[TestCase(true)]
|
|
public void HistoryRequestUsesSecurityConfigOrExplicitValues(bool explicitParameters)
|
|
{
|
|
var start = new DateTime(2013, 10, 28);
|
|
var algorithm = GetAlgorithm(start);
|
|
var future = algorithm.AddFuture(
|
|
Futures.Indices.SP500EMini,
|
|
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
|
|
dataMappingMode: DataMappingMode.LastTradingDay,
|
|
contractDepthOffset: 0,
|
|
extendedMarketHours: true);
|
|
|
|
var customTestHistoryProvider = new CustomTestHistoryProvider();
|
|
algorithm.SetHistoryProvider(customTestHistoryProvider);
|
|
algorithm.HistoryProvider.Initialize(new HistoryProviderInitializeParameters(
|
|
null,
|
|
null,
|
|
_dataProvider,
|
|
_cacheProvider,
|
|
_mapFileProvider,
|
|
_factorFileProvider,
|
|
null,
|
|
false,
|
|
new DataPermissionManager(),
|
|
algorithm.ObjectStore,
|
|
algorithm.Settings));
|
|
|
|
List<SymbolChangedEvent> history;
|
|
|
|
if (!explicitParameters)
|
|
{
|
|
history = algorithm.History<SymbolChangedEvent>(
|
|
future.Symbol,
|
|
new DateTime(2007, 1, 1),
|
|
new DateTime(2012, 1, 1)).ToList();
|
|
}
|
|
else
|
|
{
|
|
history = algorithm.History<SymbolChangedEvent>(
|
|
future.Symbol,
|
|
new DateTime(2007, 1, 1),
|
|
new DateTime(2012, 1, 1),
|
|
dataNormalizationMode: DataNormalizationMode.Raw,
|
|
dataMappingMode: DataMappingMode.OpenInterest,
|
|
contractDepthOffset: 0,
|
|
extendedMarketHours: false).ToList();
|
|
}
|
|
|
|
Assert.AreEqual(1, customTestHistoryProvider.HistoryRequests.Count);
|
|
Assert.Greater(history.Count, 0);
|
|
|
|
var request = customTestHistoryProvider.HistoryRequests[0];
|
|
|
|
if (!explicitParameters)
|
|
{
|
|
// Without explicit parameters: uses values from security configuration
|
|
Assert.AreEqual(DataNormalizationMode.BackwardsRatio, request.DataNormalizationMode);
|
|
Assert.AreEqual(DataMappingMode.LastTradingDay, request.DataMappingMode);
|
|
Assert.AreEqual(true, request.IncludeExtendedMarketHours);
|
|
Assert.AreEqual(0, request.ContractDepthOffset);
|
|
}
|
|
else
|
|
{
|
|
// With explicit parameters: uses values from history request
|
|
Assert.AreEqual(DataNormalizationMode.Raw, request.DataNormalizationMode);
|
|
Assert.AreEqual(DataMappingMode.OpenInterest, request.DataMappingMode);
|
|
Assert.AreEqual(false, request.IncludeExtendedMarketHours);
|
|
Assert.AreEqual(0, request.ContractDepthOffset);
|
|
}
|
|
}
|
|
|
|
[TestCase(Resolution.Tick)]
|
|
[TestCase(Resolution.Second)]
|
|
[TestCase(Resolution.Minute)]
|
|
[TestCase(Resolution.Hour)]
|
|
[TestCase(Resolution.Daily)]
|
|
[TestCase(null)]
|
|
public void TickHistoryRequestsForFuturesShouldReturnSameDataCount(Resolution? resolution)
|
|
{
|
|
var start = new DateTime(2013, 10, 09);
|
|
_algorithm = GetAlgorithm(start);
|
|
_algorithm.SetEndDate(2013, 10, 10);
|
|
|
|
var symbol = Symbol.CreateFuture(Futures.Metals.Gold, Market.COMEX, new DateTime(2013, 10, 29));
|
|
if (resolution == null)
|
|
{
|
|
_algorithm.AddFutureContract(symbol);
|
|
}
|
|
else
|
|
{
|
|
_algorithm.AddFutureContract(symbol, resolution);
|
|
}
|
|
|
|
var startDate = new DateTime(2013, 10, 08, 9, 30, 0);
|
|
var endDate = startDate.AddMinutes(10);
|
|
|
|
var history1 = _algorithm.History<Tick>(symbol, startDate, endDate, Resolution.Tick).ToList();
|
|
var history1Count = history1.Count;
|
|
int history2Count = 0;
|
|
int history3Count = 0;
|
|
using (Py.GIL())
|
|
{
|
|
_algorithm.SetPandasConverter();
|
|
var type = typeof(Tick).ToPython();
|
|
|
|
dynamic history2 = _algorithm.History(symbol.ToPython(), startDate, endDate, Resolution.Tick);
|
|
history2Count = history2.shape[0].As<int>();
|
|
|
|
dynamic history3 = _algorithm.History(type, symbol.ToPython(), startDate, endDate, Resolution.Tick);
|
|
history3Count = history3.shape[0].As<int>();
|
|
}
|
|
|
|
Assert.AreEqual(4923, history1Count);
|
|
Assert.AreEqual(history1Count, history2Count);
|
|
Assert.AreEqual(history1Count, history3Count);
|
|
}
|
|
|
|
private class CustomTestHistoryProvider : SubscriptionDataReaderHistoryProvider
|
|
{
|
|
public List<HistoryRequest> HistoryRequests { get; } = new List<HistoryRequest>();
|
|
|
|
public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> requests, DateTimeZone sliceTimeZone)
|
|
{
|
|
HistoryRequests.AddRange(requests);
|
|
return base.GetHistory(requests, sliceTimeZone);
|
|
}
|
|
}
|
|
|
|
public class CustomFundamentalTestData : BaseData
|
|
{
|
|
private static DateTime _currentDate;
|
|
private static int _currentDateDataPointCount;
|
|
|
|
public override DateTime EndTime => Time.AddDays(1);
|
|
|
|
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
|
{
|
|
var path = Path.Combine(Globals.DataFolder, "equity", "usa", "fundamental", "coarse", $"{date:yyyyMMdd}.csv");
|
|
return new SubscriptionDataSource(path, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
|
|
}
|
|
|
|
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
|
{
|
|
try
|
|
{
|
|
var csv = line.Split(',');
|
|
var sid = SecurityIdentifier.Parse(csv[0]);
|
|
|
|
var data = new CustomFundamentalTestData
|
|
{
|
|
Symbol = new Symbol(sid, csv[1]),
|
|
Time = date,
|
|
Value = Convert.ToDecimal(csv[2], CultureInfo.InvariantCulture),
|
|
};
|
|
|
|
// Let's limit the amount of data we fetch per date to limit tests duration,
|
|
// especially when converting to dataframes
|
|
if (date == _currentDate)
|
|
{
|
|
if (_currentDateDataPointCount >= 10)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
_currentDateDataPointCount++;
|
|
}
|
|
else
|
|
{
|
|
_currentDate = date;
|
|
_currentDateDataPointCount = 0;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public override BaseData Clone()
|
|
{
|
|
return new CustomFundamentalTestData
|
|
{
|
|
Symbol = Symbol,
|
|
Time = Time,
|
|
Value = Value
|
|
};
|
|
}
|
|
}
|
|
|
|
public class CustomUniverseData : BaseDataCollection
|
|
{
|
|
public decimal Weight { get; private set; }
|
|
|
|
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
|
{
|
|
return new SubscriptionDataSource("TestData/portfolio_targets.csv",
|
|
SubscriptionTransportMedium.LocalFile,
|
|
FileFormat.FoldingCollection);
|
|
}
|
|
|
|
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
|
{
|
|
var csv = line.Split(',');
|
|
|
|
try
|
|
{
|
|
var endTime = DateTime.ParseExact(csv[0], "yyyy-MM-dd", CultureInfo.InvariantCulture);
|
|
var symbol = Symbol.Create(csv[1], SecurityType.Equity, Market.USA);
|
|
var weight = Convert.ToDecimal(csv[2], CultureInfo.InvariantCulture);
|
|
|
|
return new CustomUniverseData
|
|
{
|
|
Symbol = symbol,
|
|
Time = endTime - TimeSpan.FromDays(1),
|
|
EndTime = endTime,
|
|
Weight = weight
|
|
};
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void AssertCustomUniverseDataFrames<T>(dynamic df, dynamic flattenedDf, Func<T, decimal> getWeight)
|
|
where T : BaseData
|
|
{
|
|
var expectedDates = new List<DateTime>
|
|
{
|
|
new DateTime(2015, 01, 13),
|
|
new DateTime(2015, 01, 14),
|
|
new DateTime(2015, 01, 15),
|
|
};
|
|
|
|
var flattenedDfDates = ((List<DateTime>)flattenedDf.index.get_level_values(0).to_list().As<List<DateTime>>()).Distinct().ToList();
|
|
CollectionAssert.AreEqual(expectedDates, flattenedDfDates);
|
|
|
|
var dfDates = ((List<DateTime>)df.index.get_level_values(1).to_list().As<List<DateTime>>()).Distinct().ToList();
|
|
CollectionAssert.AreEqual(expectedDates, dfDates);
|
|
|
|
df = df.droplevel(0); // drop symbol just to make access easier
|
|
foreach (var date in expectedDates)
|
|
{
|
|
using var pyDate = date.ToPython();
|
|
var constituents = (List<T>)df.loc[pyDate].As<List<T>>();
|
|
var flattendDfConstituents = flattenedDf.loc[pyDate];
|
|
|
|
CollectionAssert.IsNotEmpty(constituents);
|
|
Assert.AreEqual(flattendDfConstituents.shape[0].As<int>(), constituents.Count);
|
|
|
|
var constituentsSymbols = constituents.Select(x => x.Symbol).ToList();
|
|
var flattendDfConstituentsSymbols = ((List<Symbol>)flattendDfConstituents.index.to_list().As<List<Symbol>>()).ToList();
|
|
CollectionAssert.AreEqual(flattendDfConstituentsSymbols, constituentsSymbols);
|
|
|
|
var constituentsWeights = constituents.Select(x => getWeight(x)).ToList();
|
|
var flattendDfConstituentsWeights = constituentsSymbols
|
|
.Select(symbol => flattendDfConstituents.loc[symbol.ToPython()]["weight"].As<decimal>())
|
|
.Cast<decimal>()
|
|
.ToList();
|
|
CollectionAssert.AreEqual(flattendDfConstituentsWeights, constituentsWeights);
|
|
}
|
|
|
|
Log.Debug((string)df.to_string());
|
|
Log.Debug((string)flattenedDf.to_string());
|
|
}
|
|
|
|
private static void AssertDesNotThrowPythonException(Action action)
|
|
{
|
|
try
|
|
{
|
|
action();
|
|
}
|
|
catch (PythonException ex)
|
|
{
|
|
Assert.Fail(ex.Message);
|
|
}
|
|
}
|
|
|
|
private class ThrowingHistoryProvider : HistoryProviderBase
|
|
{
|
|
public override int DataPointCount => 0;
|
|
|
|
public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> requests, DateTimeZone sliceTimeZone)
|
|
{
|
|
throw new Exception("Expected exception");
|
|
yield return null;
|
|
}
|
|
|
|
public override void Initialize(HistoryProviderInitializeParameters parameters)
|
|
{
|
|
}
|
|
}
|
|
|
|
private static TestCaseData[] GetHistoryWithDataNormalizationModeTestCases()
|
|
{
|
|
var equityStart = new DateTime(2014, 06, 05); // There is an AAPL split on 2014/06/06
|
|
var futureStart = new DateTime(2013, 12, 18); // There is an ES contract mapping on 2013/12/19
|
|
|
|
return new[] { Language.CSharp, Language.Python }.SelectMany(language =>
|
|
{
|
|
return new[]
|
|
{
|
|
// Test cases for equity
|
|
new TestCaseData(language, Symbols.AAPL, Resolution.Daily, equityStart, equityStart.AddDays(100), 70),
|
|
new TestCaseData(language, Symbols.AAPL, Resolution.Hour, equityStart, equityStart.AddDays(20), 98),
|
|
new TestCaseData(language, Symbols.AAPL, Resolution.Minute, equityStart, equityStart.AddDays(1), 390),
|
|
|
|
// Test cases for futures
|
|
new TestCaseData(language, Symbols.ES_Future_Chain, Resolution.Daily, futureStart, futureStart.AddDays(100), 70),
|
|
new TestCaseData(language, Symbols.ES_Future_Chain, Resolution.Hour, futureStart, futureStart.AddDays(20), 93),
|
|
new TestCaseData(language, Symbols.ES_Future_Chain, Resolution.Minute, futureStart, futureStart.AddDays(2), 900),
|
|
};
|
|
}).ToArray();
|
|
}
|
|
|
|
private QCAlgorithm GetAlgorithm(DateTime dateTime)
|
|
{
|
|
var algorithm = new QCAlgorithm();
|
|
|
|
// Initialize the object store for the algorithm
|
|
using var store = new LocalObjectStore();
|
|
store.Initialize(0, 0, "", new Controls() { PersistenceIntervalSeconds = -1 }, AlgorithmMode.Backtesting);
|
|
algorithm.SetObjectStore(store);
|
|
|
|
algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm));
|
|
algorithm.HistoryProvider = new SubscriptionDataReaderHistoryProvider();
|
|
algorithm.SetDateTime(dateTime.ConvertToUtc(algorithm.TimeZone));
|
|
|
|
algorithm.HistoryProvider.Initialize(new HistoryProviderInitializeParameters(
|
|
null,
|
|
null,
|
|
_dataProvider,
|
|
_cacheProvider,
|
|
_mapFileProvider,
|
|
_factorFileProvider,
|
|
null,
|
|
false,
|
|
new DataPermissionManager(),
|
|
algorithm.ObjectStore,
|
|
algorithm.Settings));
|
|
return algorithm;
|
|
}
|
|
|
|
private class TestHistoryProvider : HistoryProviderBase
|
|
{
|
|
public override int DataPointCount { get; }
|
|
public List<HistoryRequest> HistryRequests { get; } = new List<HistoryRequest>();
|
|
|
|
public List<Slice> Slices { get; set; } = new List<Slice>();
|
|
|
|
public override void Initialize(HistoryProviderInitializeParameters parameters)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> requests, DateTimeZone sliceTimeZone)
|
|
{
|
|
foreach (var request in requests)
|
|
{
|
|
HistryRequests.Add(request);
|
|
}
|
|
|
|
if (!requests.Any()) return Enumerable.Empty<Slice>().ToList();
|
|
|
|
var startTime = requests.Min(x => x.StartTimeUtc.ConvertFromUtc(x.DataTimeZone));
|
|
var endTime = requests.Max(x => x.EndTimeUtc.ConvertFromUtc(x.DataTimeZone));
|
|
|
|
return Slices.Where(x => x.Time >= startTime && x.Time <= endTime).ToList();
|
|
}
|
|
}
|
|
|
|
public class CustomData : TradeBar
|
|
{
|
|
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
|
{
|
|
var source = Path.Combine(Globals.DataFolder, "equity", "usa", config.Resolution.ToString().ToLower(),
|
|
Symbols.SPY.Value.ToLowerInvariant(), LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType));
|
|
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
|
|
}
|
|
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
|
{
|
|
var baseData = base.Reader(new SubscriptionDataConfig(config, symbol: Symbols.SPY), line, date, isLiveMode);
|
|
|
|
return new CustomData
|
|
{
|
|
DataType = MarketDataType.Base,
|
|
Symbol = config.Symbol,
|
|
Time = baseData.EndTime,
|
|
Value = baseData.Price
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents custom data with an optional sorting functionality. The <see cref="ExampleCustomDataWithSort"/> class
|
|
/// allows you to specify a static property <seealso cref="CustomDataKey"/>, which defines the name of the custom data source.
|
|
/// Sorting can be enabled or disabled by setting the <seealso cref="Sort"/> property.
|
|
/// This class overrides <see cref="GetSource(SubscriptionDataConfig, DateTime, bool)"/> to initialize the
|
|
/// <seealso cref="SubscriptionDataSource.Sort"/> property based on the value of <see cref="Sort"/>.
|
|
/// </summary>
|
|
public class ExampleCustomDataWithSort : BaseData
|
|
{
|
|
/// <summary>
|
|
/// The name of the custom data source.
|
|
/// </summary>
|
|
public static string CustomDataKey { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specifies whether the data should be sorted. If set to true, the data will be sorted during retrieval.
|
|
/// </summary>
|
|
public static bool Sort { get; set; } = true;
|
|
|
|
public decimal Open { get; set; }
|
|
public decimal High { get; set; }
|
|
public decimal Low { get; set; }
|
|
public decimal Close { get; set; }
|
|
|
|
/// <summary>
|
|
/// Returns the data source for the subscription. It uses the custom data key and sets sorting based on the
|
|
/// <see cref="Sort"/> property.
|
|
/// </summary>
|
|
/// <param name="config">Subscription configuration.</param>
|
|
/// <param name="date">The data date.</param>
|
|
/// <param name="isLiveMode">Specifies whether live mode is enabled.</param>
|
|
/// <returns>The subscription data source with sorting determined by the <see cref="Sort"/> property.</returns>
|
|
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
|
{
|
|
return new SubscriptionDataSource(CustomDataKey, SubscriptionTransportMedium.ObjectStore, FileFormat.Csv)
|
|
{
|
|
Sort = Sort
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a line of CSV data and parses it into an <see cref="ExampleCustomDataWithSort"/> object.
|
|
/// </summary>
|
|
/// <param name="config">Subscription configuration.</param>
|
|
/// <param name="line">The line of data to parse.</param>
|
|
/// <param name="date">The data date.</param>
|
|
/// <param name="isLiveMode">Specifies whether live mode is enabled.</param>
|
|
/// <returns>A populated <see cref="ExampleCustomDataWithSort"/> instance.</returns>
|
|
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
|
{
|
|
var csv = line.Split(",");
|
|
var data = new ExampleCustomDataWithSort
|
|
{
|
|
Symbol = config.Symbol,
|
|
Time = DateTime.ParseExact(csv[0], DateFormat.DB, CultureInfo.InvariantCulture),
|
|
Value = csv[4].ToDecimal(),
|
|
Open = csv[1].ToDecimal(),
|
|
High = csv[2].ToDecimal(),
|
|
Low = csv[3].ToDecimal(),
|
|
Close = csv[4].ToDecimal()
|
|
};
|
|
|
|
return data;
|
|
}
|
|
}
|
|
|
|
private QCAlgorithm GetAlgorithmWithEquity(DateTime dateTime)
|
|
{
|
|
var resolution = Resolution.Minute;
|
|
var algorithm = GetAlgorithm(dateTime);
|
|
algorithm.AddEquity("AAPL", resolution);
|
|
|
|
return algorithm;
|
|
}
|
|
|
|
private QCAlgorithm GetAlgorithmWithFuture(DateTime dateTime)
|
|
{
|
|
var resolution = Resolution.Daily;
|
|
var algorithm = GetAlgorithm(dateTime);
|
|
algorithm.AddFuture(Futures.Indices.SP500EMini, resolution, extendedMarketHours: true);
|
|
|
|
return algorithm;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to check that all history results have the same bar count
|
|
/// </summary>
|
|
private static void CheckThatHistoryResultsHaveEqualBarCount<T>(IEnumerable<IEnumerable<T>> historyResults, int expectedHistoryCount)
|
|
{
|
|
Assert.That(historyResults, Has.All.Not.Empty.And.All.Count.EqualTo(expectedHistoryCount),
|
|
$@"Expected all history results to have {expectedHistoryCount} slices, but counts where {string.Join(", ", historyResults.Select(x => x.Count()))}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to check that all history data frame results have the same bar count
|
|
/// </summary>
|
|
private static void CheckThatHistoryResultsHaveEqualBarCount(List<PyObject> historyResults, int expectedHistoryCount)
|
|
{
|
|
Assert.Greater(expectedHistoryCount, 0);
|
|
var historyCounts = historyResults.Select(x => x.GetAttr("shape")[0].As<int>()).ToList();
|
|
Assert.IsTrue(historyCounts.All(count => count == expectedHistoryCount),
|
|
$"Expected all history results to have {expectedHistoryCount} slices/bars, but counts where {string.Join(", ", historyCounts)}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to check that, for each history result, prices at each time are different
|
|
/// </summary>
|
|
private static void CheckThatHistoryResultsHaveDifferentPrices(List<List<BaseData>> historyResults, string message)
|
|
{
|
|
for (int i = 0; i < historyResults[0].Count; i++)
|
|
{
|
|
var prices = historyResults.Select(hr => hr[i].Price).ToHashSet();
|
|
Assert.AreEqual(historyResults.Count, prices.Count, message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to check that, for each history result, prices at each time are different
|
|
/// </summary>
|
|
private static void CheckThatHistoryResultsHaveDifferentPrices(IEnumerable<IEnumerable<Slice>> historyResults, string message)
|
|
{
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults.Select(hr => hr.Select(x => x.Values.First()).ToList()).ToList(), message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to check that, for each history data frame result, prices at each time are different
|
|
/// </summary>
|
|
private static void CheckThatHistoryResultsHaveDifferentPrices(List<PyObject> historyResults, string message)
|
|
{
|
|
var closesPerResult = historyResults.Select(hr => hr["close"].GetAttr("values").GetAttr("tolist").Invoke().As<List<decimal>>()).ToList();
|
|
|
|
for (int i = 0; i < closesPerResult.First().Count; i++)
|
|
{
|
|
var closePrices = closesPerResult.Select(close => close[i]).ToHashSet();
|
|
Assert.AreEqual(historyResults.Count, closePrices.Count, message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to perform history checks on different data normalization modes
|
|
/// </summary>
|
|
private static void CheckHistoryResultsForDataNormalizationModes(QCAlgorithm algorithm, Symbol symbol, DateTime start,
|
|
DateTime end, Resolution resolution, DataNormalizationMode[] dataNormalizationModes, int expectedHistoryCount)
|
|
{
|
|
var historyResults = dataNormalizationModes
|
|
.Select(x => algorithm.History(new[] { symbol }, start, end, resolution, dataNormalizationMode: x).ToList())
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data normalization mode at each time");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to perform history checks on different data normalization modes
|
|
/// </summary>
|
|
private static void CheckHistoryResultsForDataNormalizationModes(QCAlgorithm algorithm, PyObject symbol, DateTime start,
|
|
DateTime end, Resolution resolution, DataNormalizationMode[] dataNormalizationModes, int expectedHistoryCount)
|
|
{
|
|
var historyResults = dataNormalizationModes
|
|
.Select(x => algorithm.History(symbol, start, end, resolution, dataNormalizationMode: x))
|
|
.ToList();
|
|
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data normalization mode at each time");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to assert that the right custom data history is fetched
|
|
/// </summary>
|
|
private static void AssertCustomDataTypeHistory<T>(List<T> history)
|
|
where T : IBaseData
|
|
{
|
|
Assert.AreEqual(828, history.Count);
|
|
Assert.That(history, Has.All.Property("DataType").EqualTo(MarketDataType.Base));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to assert that the right custom data history is fetched
|
|
/// </summary>
|
|
private static void AssertCustomDataTypeHistory<T>(List<DataDictionary<T>> history)
|
|
where T : IBaseData
|
|
{
|
|
Assert.AreEqual(828, history.Count);
|
|
Assert.That(history.Select(x => x.Single().Value), Has.All.Property("DataType").EqualTo(MarketDataType.Base));
|
|
}
|
|
|
|
private static DataMappingMode[] GetAllDataMappingModes()
|
|
{
|
|
return (DataMappingMode[])Enum.GetValues(typeof(DataMappingMode));
|
|
}
|
|
|
|
private static DataNormalizationMode[] GetAllDataNormalizationModes()
|
|
{
|
|
return (DataNormalizationMode[])Enum.GetValues(typeof(DataNormalizationMode));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts history result has the expected count
|
|
/// </summary>
|
|
private static void AssertHistoryResultCount<T>(List<T> history, int expectedCount)
|
|
{
|
|
Assert.IsNotEmpty(history);
|
|
Assert.AreEqual(expectedCount, history.Count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with fillForward set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/> for Python cases.
|
|
/// </summary>
|
|
private static void AssertHistoryResultCount(PyObject history, int expectedCount)
|
|
{
|
|
var historyCount = history.GetAttr("shape")[0].As<int>();
|
|
Assert.Greater(historyCount, 0);
|
|
Assert.AreEqual(expectedCount, historyCount);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that data from a history request has the expected resolution
|
|
/// </summary>
|
|
private static void AssertHistoryResultResolution(IEnumerable<BaseData> history, Resolution resolution)
|
|
{
|
|
var mhdb = MarketHoursDatabase.FromDataFolder();
|
|
var expectedTimeSpan = resolution.ToTimeSpan();
|
|
Assert.IsTrue(history.All(data =>
|
|
{
|
|
if (resolution == Resolution.Daily)
|
|
{
|
|
var exchange = mhdb.GetExchangeHours(data.Symbol.ID.Market, data.Symbol, data.Symbol.SecurityType);
|
|
var marketHours = exchange.GetMarketHours(data.EndTime);
|
|
expectedTimeSpan = marketHours.MarketDuration;
|
|
}
|
|
return data.EndTime - data.Time == expectedTimeSpan;
|
|
}));
|
|
}
|
|
|
|
private static List<PyObject> GetHistoryDataFrameIndex(PyObject history)
|
|
{
|
|
return history
|
|
.GetAttr("index")
|
|
.GetAttr("to_flat_index").Invoke()
|
|
.GetAttr("tolist").Invoke()
|
|
.As<List<PyObject>>();
|
|
}
|
|
|
|
private static List<Symbol> GetSymbolsFromHistoryDataFrameIndex(List<PyObject> index)
|
|
{
|
|
dynamic builtins = Py.Import("builtins");
|
|
return index.Select(x => x[builtins.len(x) > 2 ? 1 : 0].As<Symbol>()).ToList();
|
|
}
|
|
|
|
private static List<DateTime> GetTimesFromHistoryDataFrameIndex(List<PyObject> index)
|
|
{
|
|
dynamic builtins = Py.Import("builtins");
|
|
return index.Select(x => x[builtins.len(x) > 2 ? 2 : 1].As<DateTime>()).ToList();
|
|
}
|
|
|
|
private static Dictionary<string, T> GetDataFrameDictionary<T>(PyObject dict)
|
|
{
|
|
// Using PyObject because our data frames use our PandasColunm class to wrap strings
|
|
return dict.ConvertToDictionary<PyObject, T>().ToDictionary(
|
|
kvp =>
|
|
{
|
|
var strKey = kvp.Key.ToString();
|
|
kvp.Key.Dispose();
|
|
return strKey;
|
|
},
|
|
kvp => kvp.Value);
|
|
}
|
|
|
|
#region Fill-forwarded history assertions
|
|
|
|
/// <summary>
|
|
/// Asserts that fill forwarded history results has data for every period in the requested time span
|
|
/// </summary>
|
|
private static void AssertFillForwardedHistoryTimes(Symbol symbol, List<DateTime> times, TimeSpan period)
|
|
{
|
|
var hours = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType).ExchangeHours;
|
|
|
|
// We are assuming one regular segment per day for the test security
|
|
var periodMultiplier = 0;
|
|
var baseSegmentTimeIndex = 0;
|
|
for (var i = 0; i < times.Count; i++)
|
|
{
|
|
var currentTime = times[i];
|
|
if (i > 0 && currentTime.DayOfWeek != times[i - 1].DayOfWeek)
|
|
{
|
|
baseSegmentTimeIndex = i;
|
|
periodMultiplier = 0;
|
|
}
|
|
|
|
var expectedCurrentTime = times[baseSegmentTimeIndex] + periodMultiplier++ * period;
|
|
Assert.AreEqual(expectedCurrentTime, currentTime);
|
|
Assert.IsTrue(
|
|
// subtract `period` since the times list has the EndTime
|
|
hours.IsOpen(currentTime - period, extendedMarketHours: false),
|
|
$"Current time {currentTime} is not open");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history data, when called with fillForward set to true, has a period that is equal to the resolution used.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryData(List<TradeBar> history, Resolution resolution, bool fillForward)
|
|
{
|
|
var expectedPeriod = resolution.ToTimeSpan();
|
|
Assert.IsTrue(history.All(bar => bar.Period == expectedPeriod));
|
|
Assert.AreEqual(fillForward, history.Any(x => x.IsFillForward));
|
|
|
|
if (fillForward)
|
|
{
|
|
var symbol = history.First().Symbol;
|
|
var times = history.Select(bar => bar.EndTime).ToList();
|
|
AssertFillForwardedHistoryTimes(symbol, times, expectedPeriod);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history data, when called with fillForward set to true, has a period that is equal to the resolution used.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryData(List<Slice> history, Resolution resolution, bool fillForward)
|
|
{
|
|
AssertFillForwardHistoryData(
|
|
history.Select(slice => slice.Bars.Values.SingleOrDefault((TradeBar)null)).Where(bar => bar != null).ToList(),
|
|
resolution,
|
|
fillForward);
|
|
}
|
|
|
|
///// <summary>
|
|
/// Asserts that history data, when called with fillForward set to true, has a period that is equal to the resolution used.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryData(List<DataDictionary<TradeBar>> history, Resolution resolution, bool fillForward)
|
|
{
|
|
AssertFillForwardHistoryData(
|
|
history.Select(x => x.Values.SingleOrDefault((TradeBar)null)).Where(bar => bar != null).ToList(),
|
|
resolution,
|
|
fillForward);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with fillForward set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryResults(List<TradeBar> history, int expectedCount, Resolution resolution, bool fillForward)
|
|
{
|
|
AssertHistoryResultCount(history, expectedCount);
|
|
AssertFillForwardHistoryData(history, resolution, fillForward);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with fillForward set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryResults(List<Slice> history, int expectedCount, Resolution resolution, bool fillForward)
|
|
{
|
|
AssertHistoryResultCount(history, expectedCount);
|
|
AssertFillForwardHistoryData(history, resolution, fillForward);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with fillForward set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/>.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryResults(List<DataDictionary<TradeBar>> history, int expectedCount,
|
|
Resolution resolution, bool fillForward)
|
|
{
|
|
AssertHistoryResultCount(history, expectedCount);
|
|
AssertFillForwardHistoryData(history, resolution, fillForward);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with fillForward set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithFillForward"/> for Python cases.
|
|
/// </summary>
|
|
private static void AssertFillForwardHistoryResults(PyObject history, int expectedCount, Resolution resolution, bool fillForward)
|
|
{
|
|
AssertHistoryResultCount(history, expectedCount);
|
|
|
|
if (fillForward)
|
|
{
|
|
var index = GetHistoryDataFrameIndex(history);
|
|
var symbols = GetSymbolsFromHistoryDataFrameIndex(index);
|
|
var times = GetTimesFromHistoryDataFrameIndex(index);
|
|
AssertFillForwardedHistoryTimes(symbols[0], times, resolution.ToTimeSpan());
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region History with extended market assertions
|
|
|
|
/// <summary>
|
|
/// Asserts that history with/without extended market results has data for regular hour segments and extended market segments, respectively.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryTimes(Symbol symbol, List<DateTime> times, bool extendedMarket)
|
|
{
|
|
var hours = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType).ExchangeHours;
|
|
|
|
var getTimesCheck = (bool inExtendedHours) => (DateTime time) =>
|
|
{
|
|
var currentDayHours = hours.GetMarketHours(time);
|
|
var segments = currentDayHours.Segments
|
|
.Where(x => inExtendedHours
|
|
? x.State != MarketHoursState.Market
|
|
: x.State == MarketHoursState.Market);
|
|
return segments.Any(segment => time.TimeOfDay > segment.Start && time.TimeOfDay <= segment.End);
|
|
};
|
|
var timeIsInRegularHours = getTimesCheck(false);
|
|
var timeIsInExtendedHours = getTimesCheck(true);
|
|
|
|
if (!extendedMarket)
|
|
{
|
|
Assert.IsTrue(times.All(timeIsInRegularHours));
|
|
}
|
|
else
|
|
{
|
|
Assert.IsTrue(times.Any(timeIsInRegularHours));
|
|
Assert.IsTrue(times.Any(timeIsInExtendedHours));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history with/without extended market results has data for regular hour segments and extended market segments, respectively.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResultsData(List<BaseData> history, bool extendedMarket, Resolution resolution,
|
|
Symbol expectedSymbol)
|
|
{
|
|
Assert.IsTrue(history.All(data => data.Symbol == expectedSymbol));
|
|
var times = history.Select(bar => bar.EndTime).ToList();
|
|
AssertExtendedMarketHistoryTimes(expectedSymbol, times, extendedMarket);
|
|
AssertHistoryResultResolution(history, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history with/without extended market results has data for regular hour segments and extended market segments, respectively.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResultsData(List<Slice> history, bool extendedMarket, Resolution resolution,
|
|
Symbol expectedSymbol)
|
|
{
|
|
var data = history.Select(slice => slice.Bars.Values.Cast<BaseData>().Concat(slice.QuoteBars.Values.Cast<BaseData>())).SelectMany(x => x);
|
|
Assert.IsTrue(data.All(x => x.Symbol == expectedSymbol));
|
|
|
|
var times = data.Select(x => x.EndTime).ToList();
|
|
AssertExtendedMarketHistoryTimes(expectedSymbol, times, extendedMarket);
|
|
AssertHistoryResultResolution(data, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/>.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(List<TradeBar> history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertHistoryResultCount(history, expectedHistoryCount);
|
|
AssertExtendedMarketHistoryResultsData(history.Cast<BaseData>().ToList(), extendedMarket, resolution, expectedSymbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/>.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(List<Slice> history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertHistoryResultCount(history, expectedHistoryCount);
|
|
AssertExtendedMarketHistoryResultsData(history, extendedMarket, resolution, expectedSymbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/>.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(List<DataDictionary<TradeBar>> history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertHistoryResultCount(history, expectedHistoryCount);
|
|
AssertExtendedMarketHistoryResultsData(history.Select(dict => dict.Values.First()).Cast<BaseData>().ToList(), extendedMarket, resolution,
|
|
expectedSymbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/>.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(List<Tick> history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertHistoryResultCount(history, expectedHistoryCount);
|
|
AssertExtendedMarketHistoryResultsData(history.Cast<BaseData>().ToList(), extendedMarket, resolution, expectedSymbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/>.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(List<DataDictionary<Tick>> history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertExtendedMarketHistoryResults(history.SelectMany(dict => dict.Values).ToList(), expectedHistoryCount, extendedMarket, resolution,
|
|
expectedSymbol);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history result has more data when called with extendedMarket set to true.
|
|
/// Used in the test <see cref="HistoryRequestWithExtendedMarket"/> for Python cases.
|
|
/// </summary>
|
|
private static void AssertExtendedMarketHistoryResults(PyObject history, int expectedHistoryCount, bool extendedMarket,
|
|
Resolution resolution, Symbol expectedSymbol)
|
|
{
|
|
AssertHistoryResultCount(history, expectedHistoryCount);
|
|
|
|
var index = GetHistoryDataFrameIndex(history);
|
|
var symbols = GetSymbolsFromHistoryDataFrameIndex(index);
|
|
Assert.IsTrue(symbols.All(x => x == expectedSymbol));
|
|
var times = GetTimesFromHistoryDataFrameIndex(index);
|
|
AssertExtendedMarketHistoryTimes(expectedSymbol, times, extendedMarket);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region History with different data mapping modes assertions
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data mapping mode, each has its expected mapping date, that is,
|
|
/// the date when underlying symbol change due to contract expiration.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentMappingModesResults(List<List<TradeBar>> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount, Resolution resolution, List<DateTime> expectedMappingDates)
|
|
{
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
|
|
// Check that all history results have a mapping date at some point in the history
|
|
for (var i = 0; i < historyResults.Count; i++)
|
|
{
|
|
var history = historyResults[i];
|
|
var prevUnderlying = history[0].Symbol.Underlying;
|
|
var mappingDates = new List<DateTime>();
|
|
|
|
foreach (var bar in history)
|
|
{
|
|
Assert.AreEqual(expectedSymbol, bar.Symbol, $"All bars symbol must have been {expectedSymbol} but found {bar.Symbol}");
|
|
|
|
var currentUnderlying = bar.Symbol.Underlying;
|
|
if (currentUnderlying != prevUnderlying)
|
|
{
|
|
mappingDates.Add(bar.EndTime.Date);
|
|
prevUnderlying = currentUnderlying;
|
|
}
|
|
}
|
|
|
|
Assert.AreEqual(1, mappingDates.Count, "We are expecting only one mapping for this case.");
|
|
var expectedMappingDate = expectedMappingDates[i];
|
|
Assert.AreEqual(expectedMappingDate, mappingDates[0],
|
|
$"Mapping date {mappingDates[0]} for {i}th history result is not the expected one {expectedMappingDate}.");
|
|
|
|
AssertHistoryResultResolution(history, resolution);
|
|
}
|
|
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults.Select(history => history.Cast<BaseData>().ToList()).ToList(),
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data mapping mode, each has its expected mapping date.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentMappingModesResults(List<List<Slice>> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount, Resolution resolution, List<DateTime> expectedMappingDates)
|
|
{
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults.Select(x => x.Select(y => y.Bars.Values.First()).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution, expectedMappingDates);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data mapping mode, each has its expected mapping date.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentMappingModesResults(List<List<DataDictionary<TradeBar>>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution, List<DateTime> expectedMappingDates)
|
|
{
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults.Select(x => x.Select(y => y[expectedSymbol]).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution, expectedMappingDates);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of Python history results, one for a different data mapping mode,
|
|
/// the result counts are the same and contain different prices.
|
|
/// In the data frames we don't have access to the actual mapping dates, so we cannot do the same checks we do for C# in
|
|
/// <see cref="AssertFuturesHistoryWithDifferentMappingModesResults(List{List{TradeBar}}, Symbol, int, Resolution, List{DateTime})"/>.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentMappingModesResults(List<PyObject> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount)
|
|
{
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults,
|
|
"History results prices should have been different for each data mapping mode at each time");
|
|
|
|
foreach (var history in historyResults)
|
|
{
|
|
var index = GetHistoryDataFrameIndex(history);
|
|
var symbols = GetSymbolsFromHistoryDataFrameIndex(index);
|
|
Assert.IsTrue(symbols.All(x => x == expectedSymbol));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region History with different data normalization modes assertions
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data normalization mode, prices are different for each time.
|
|
/// </summary>
|
|
private static void AssertHistoryWithDifferentNormalizationModesResults(List<List<TradeBar>> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults.Select(history => history.Cast<BaseData>().ToList()).ToList(),
|
|
"History results prices should have been different for each data normalization mode at each time");
|
|
|
|
// Assert resolution and symbol
|
|
foreach (var history in historyResults)
|
|
{
|
|
AssertHistoryResultResolution(history, resolution);
|
|
foreach (var bar in history)
|
|
{
|
|
Assert.AreEqual(expectedSymbol, bar.Symbol, $"All bars symbol must have been {expectedSymbol} but found {bar.Symbol}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data normalization mode, prices are different for each time.
|
|
/// </summary>
|
|
private static void AssertHistoryWithDifferentNormalizationModesResults(List<List<Slice>> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults.Select(x => x.Select(y => y.Bars.Values.First()).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data normalization mode, prices are different for each time.
|
|
/// </summary>
|
|
private static void AssertHistoryWithDifferentNormalizationModesResults(List<List<DataDictionary<TradeBar>>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
AssertHistoryWithDifferentNormalizationModesResults(historyResults.Select(x => x.Select(y => y[expectedSymbol]).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that for a list of history results, one for a different data normalization mode, prices are different for each time.
|
|
/// </summary>
|
|
private static void AssertHistoryWithDifferentNormalizationModesResults(List<PyObject> historyResults, Symbol expectedSymbol,
|
|
int expectedHistoryCount)
|
|
{
|
|
// These are the same checks done for mapping modes, even regardless of the security type
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, expectedSymbol, expectedHistoryCount);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region History with different contract depth offsets assertions
|
|
|
|
/// <summary>
|
|
/// Asserts that history request with different contract depth offsets results have the expected mapped symbol depending on the offset used.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List<List<BaseData>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
CheckThatHistoryResultsHaveEqualBarCount(historyResults, expectedHistoryCount);
|
|
|
|
var futureChainProvider = new BacktestingFutureChainProvider();
|
|
futureChainProvider.Initialize(new(TestGlobals.MapFileProvider, TestGlobals.HistoryProvider));
|
|
|
|
var firstDateTime = historyResults[0][0].EndTime;
|
|
var futureChain = futureChainProvider.GetFutureContractList(expectedSymbol, firstDateTime).ToList();
|
|
|
|
// Check that the first mapped contract corresponds to the one in the future chain depending on the depth offset used,
|
|
// where historyResults[i] is the history result for contract depth offset = i
|
|
for (var i = 0; i < historyResults.Count; i++)
|
|
{
|
|
var history = historyResults[i];
|
|
var firstMappedContractSymbol = history[0].Symbol.Underlying;
|
|
|
|
Assert.AreEqual(futureChain[i], firstMappedContractSymbol,
|
|
$@"History[{i}]: Expected the first mapped contract to be the one on index {i} ({futureChain[i]}) in the chain for date {firstDateTime}.");
|
|
|
|
// Finally, assert the resolution and symbol
|
|
AssertHistoryResultResolution(history, resolution);
|
|
Assert.IsTrue(history.All(x => x.Symbol == expectedSymbol));
|
|
}
|
|
|
|
CheckThatHistoryResultsHaveDifferentPrices(historyResults.Select(history => history.Cast<BaseData>().ToList()).ToList(),
|
|
"History results prices should have been different for each available offset at each time");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history request with different contract depth offsets results have the expected mapped symbol depending on the offset used.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List<List<TradeBar>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(
|
|
historyResults.Select(history => history.Cast<BaseData>().ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history request with different contract depth offsets results have the expected mapped symbol depending on the offset used.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List<List<Slice>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(
|
|
historyResults.Select(x => x.Select(y => (BaseData)y.Bars.Values.FirstOrDefault() ?? y.QuoteBars.Values.First()).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history request with different contract depth offsets results have the expected mapped symbol depending on the offset used.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List<List<DataDictionary<TradeBar>>> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount, Resolution resolution)
|
|
{
|
|
AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(
|
|
historyResults.Select(x => x.Select(y => y[expectedSymbol]).ToList()).ToList(),
|
|
expectedSymbol, expectedHistoryCount, resolution);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asserts that history request with different contract depth offsets results have the expected mapped symbol depending on the offset used.
|
|
/// In the data frames we don't have access to the underylings, so we cannot do the same checks we do for C# in
|
|
/// <see cref="AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List{List{BaseData}}, Symbol, int, Resolution)"/>.
|
|
/// </summary>
|
|
private static void AssertFuturesHistoryWithDifferentContractDepthOffsetsResults(List<PyObject> historyResults,
|
|
Symbol expectedSymbol, int expectedHistoryCount)
|
|
{
|
|
// These are the same checks done for mapping modes
|
|
AssertFuturesHistoryWithDifferentMappingModesResults(historyResults, expectedSymbol, expectedHistoryCount);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|