chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Python.Runtime;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Research;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Data.Market;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Tests.Common.Data.Fundamental;
|
||||
using QuantConnect.Configuration;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture]
|
||||
public class QuantBookFundamentalTests
|
||||
{
|
||||
private dynamic _module;
|
||||
private DateTime _startDate;
|
||||
private DateTime _endDate;
|
||||
private ILogHandler _logHandler;
|
||||
private QuantBook _qb;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// Store initial handler
|
||||
_logHandler = Log.LogHandler;
|
||||
|
||||
SymbolCache.Clear();
|
||||
MarketHoursDatabase.Reset();
|
||||
|
||||
Config.Set("fundamental-data-provider", "NullFundamentalDataProvider");
|
||||
|
||||
// Using a date that we have data for in the repo
|
||||
_startDate = new DateTime(2014, 3, 31);
|
||||
_endDate = new DateTime(2014, 3, 31);
|
||||
|
||||
// Our qb instance to test on
|
||||
_qb = new QuantBook();
|
||||
|
||||
using (Py.GIL())
|
||||
{
|
||||
_module = Py.Import("Test_QuantBookHistory");
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void OneTimeTearDown()
|
||||
{
|
||||
// Reset to initial handler
|
||||
Log.LogHandler = _logHandler;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultEndDate()
|
||||
{
|
||||
var startDate = DateTime.UtcNow.Date.AddDays(-7);
|
||||
|
||||
// Expected end date should be either today if tradable, or last tradable day
|
||||
var aapl = _qb.AddEquity("AAPL");
|
||||
var now = DateTime.UtcNow.Date;
|
||||
var expectedEndDate = aapl.Exchange.Hours.IsDateOpen(now) ? now : aapl.Exchange.Hours.GetPreviousTradingDay(now);
|
||||
expectedEndDate = expectedEndDate.AddDays(1);
|
||||
|
||||
IEnumerable<DataDictionary<dynamic>> data = _qb.GetFundamental("AAPL", "", startDate);
|
||||
|
||||
// Check that the last day in the collection is as expected
|
||||
var lastDay = data.Last();
|
||||
Assert.AreEqual(expectedEndDate, lastDay.Time);
|
||||
Assert.AreEqual(expectedEndDate, lastDay[aapl.Symbol].EndTime);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(DataTestCases))]
|
||||
public void PyFundamentalData(dynamic input)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = _module.FundamentalHistoryTest();
|
||||
FundamentalService.Initialize(TestGlobals.DataProvider, new TestFundamentalDataProvider(), false);
|
||||
var dataFrame = testModule.getFundamentals(input[0], input[1], _startDate, _endDate);
|
||||
|
||||
// Should not be empty
|
||||
Assert.IsFalse(dataFrame.empty.AsManagedObject(typeof(bool)));
|
||||
|
||||
// Get the test row (plus 1 day since data is time-stamped with the base data's end time)
|
||||
var testRow = dataFrame.loc[_startDate.AddDays(1).ToPython()];
|
||||
Assert.IsFalse(testRow.empty.AsManagedObject(typeof(bool)));
|
||||
|
||||
// Check the length
|
||||
var count = testRow.__len__().AsManagedObject(typeof(int));
|
||||
Assert.AreEqual(count, 1);
|
||||
|
||||
// Verify the data value
|
||||
var index = testRow.index[0];
|
||||
if (input.Length == 4)
|
||||
{
|
||||
var fine = testRow.at[index].AsManagedObject(typeof(FineFundamental));
|
||||
Assert.AreEqual(input[2], input[3](fine));
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = testRow.at[index].AsManagedObject(input[2].GetType());
|
||||
Assert.AreEqual(input[2], value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(DataTestCases))]
|
||||
public void CSharpFundamentalData(dynamic input)
|
||||
{
|
||||
FundamentalService.Initialize(TestGlobals.DataProvider, new TestFundamentalDataProvider(), false);
|
||||
var data = _qb.GetFundamental(input[0], input[1], _startDate, _endDate);
|
||||
var currentDate = _startDate;
|
||||
|
||||
foreach (var day in data)
|
||||
{
|
||||
// plus 1 day since data is time-stamped with the base data's end time
|
||||
currentDate = currentDate.AddDays(1);
|
||||
|
||||
foreach (var value in day.Values)
|
||||
{
|
||||
if (input.Length == 4)
|
||||
{
|
||||
Assert.AreEqual(input[2], input[3](value));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(input[2], value);
|
||||
}
|
||||
Assert.AreEqual(currentDate, day.Time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PyReturnNoneTest()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var start = new DateTime(2023, 10, 10);
|
||||
var symbol = Symbol.Create("AIG", SecurityType.Equity, Market.USA);
|
||||
var testModule = _module.FundamentalHistoryTest();
|
||||
var data = testModule.getFundamentals(symbol, "ValuationRatios.PERatio", start, start.AddDays(5));
|
||||
Assert.AreNotEqual(true, (bool)data.empty);
|
||||
|
||||
var subdataframe = data.loc[start.AddDays(1).ToPython()];
|
||||
PyObject result = subdataframe[symbol.ID.ToString()];
|
||||
Assert.IsNull(result.As<object>());
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(NullRequestTestCases))]
|
||||
public void PyReturnNullTest(dynamic input)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = _module.FundamentalHistoryTest();
|
||||
var data = testModule.getFundamentals(input[0], input[1], input[2], input[3]);
|
||||
Assert.AreEqual(true, (bool)data.empty);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(NullRequestTestCases))]
|
||||
public void CSharpReturnNullTest(dynamic input)
|
||||
{
|
||||
var data = _qb.GetFundamental(input[0], input[1], input[2], input[3]);
|
||||
Assert.IsEmpty(data);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(FundamentalEndTimeTestCases))]
|
||||
public void FundamentalDataEndTime(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
var originalQBEndDate = _qb.EndDate;
|
||||
_qb.SetEndDate(endDate);
|
||||
|
||||
var security = _qb.AddEquity("AAPL");
|
||||
|
||||
var history = _qb.History(Symbols.AAPL, startDate, endDate, Resolution.Daily).ToList();
|
||||
Assert.IsNotEmpty(history);
|
||||
|
||||
var fundamental = (_qb.GetFundamental("AAPL", "", startDate, endDate) as IEnumerable<DataDictionary<dynamic>>).ToList();
|
||||
|
||||
var isEndDateOpen = security.Exchange.Hours.IsDateOpen(endDate);
|
||||
var expectedFundamentalCount = 10;
|
||||
var expectedHistoryCount = isEndDateOpen ? expectedFundamentalCount - 1 : expectedFundamentalCount;
|
||||
|
||||
Assert.AreEqual(expectedFundamentalCount, fundamental.Count);
|
||||
Assert.AreEqual(expectedHistoryCount, history.Count);
|
||||
|
||||
var historyTimes = history.Select(x => x.EndTime.AddHours(+8));// shift 4pm to midnight to match fundamental
|
||||
var fundamentalTimes = fundamental.Select(x => x.Time).SkipLast(isEndDateOpen ? 1 : 0);
|
||||
|
||||
CollectionAssert.AreEqual(historyTimes, fundamentalTimes);
|
||||
|
||||
Assert.IsTrue(fundamental.All(x => x.Time == x.Values.Cast<FineFundamental>().Single().EndTime));
|
||||
|
||||
_qb.RemoveSecurity(security.Symbol);
|
||||
_qb.SetEndDate(originalQBEndDate);
|
||||
}
|
||||
|
||||
// Different requests and their expected values
|
||||
private static readonly object[] DataTestCases =
|
||||
{
|
||||
new object[] {new List<string> {"AAPL"}, null, 13.2725m, new Func<FineFundamental, double>(fundamental => fundamental.ValuationRatios.PERatio) },
|
||||
new object[] {new List<string> {"AAPL"}, "ValuationRatios.PERatio", 13.2725m},
|
||||
new object[] {Symbol.Create("IBM", SecurityType.Equity, Market.USA), "ValuationRatios.BookValuePerShare", 22.5177},
|
||||
new object[] {new List<Symbol> {Symbol.Create("AIG", SecurityType.Equity, Market.USA)}, "FinancialStatements.NumberOfShareHolders.Value", 36319}
|
||||
};
|
||||
|
||||
// Different requests that should return null
|
||||
// Nonexistent data; start date after end date;
|
||||
private static readonly object[] NullRequestTestCases =
|
||||
{
|
||||
new object[] {Symbol.Create("AIG", SecurityType.Equity, Market.USA), "ValuationRatios.PERatio", new DateTime(1972, 4, 1), new DateTime(1972, 4, 1)},
|
||||
new object[] {Symbol.Create("IBM", SecurityType.Equity, Market.USA), "ValuationRatios.BookValuePerShare", new DateTime(2014, 4, 1), new DateTime(2014, 3, 31)},
|
||||
};
|
||||
|
||||
private static readonly TestCaseData[] FundamentalEndTimeTestCases =
|
||||
{
|
||||
// monday,friday
|
||||
new TestCaseData(new DateTime(2014, 3, 31), new DateTime(2014, 4, 11)),
|
||||
// monday,saturday
|
||||
new TestCaseData(new DateTime(2014, 3, 31), new DateTime(2014, 4, 12))
|
||||
};
|
||||
|
||||
private class TestFundamentalDataProvider : IFundamentalDataProvider
|
||||
{
|
||||
public T Get<T>(DateTime time, SecurityIdentifier securityIdentifier, FundamentalProperty name)
|
||||
{
|
||||
if (securityIdentifier == SecurityIdentifier.Empty)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
return Get(time, securityIdentifier, name);
|
||||
}
|
||||
|
||||
private dynamic Get(DateTime time, SecurityIdentifier securityIdentifier, FundamentalProperty enumName)
|
||||
{
|
||||
var name = Enum.GetName(enumName);
|
||||
switch (name)
|
||||
{
|
||||
case "ValuationRatios_PERatio":
|
||||
return 13.2725d;
|
||||
case "ValuationRatios_BookValuePerShare":
|
||||
return 22.5177d;
|
||||
case "FinancialStatements_NumberOfShareHolders_TwelveMonths":
|
||||
return 36319;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void Initialize(IDataProvider dataProvider, bool liveMode)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,867 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Research;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using System.Data;
|
||||
using QuantConnect.Securities.Future;
|
||||
using QuantConnect.Data;
|
||||
using NodaTime;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture]
|
||||
public class QuantBookHistoryTests
|
||||
{
|
||||
private ILogHandler _logHandler;
|
||||
dynamic _module;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// Store initial handler
|
||||
_logHandler = Log.LogHandler;
|
||||
|
||||
SymbolCache.Clear();
|
||||
MarketHoursDatabase.Reset();
|
||||
|
||||
using (Py.GIL())
|
||||
{
|
||||
_module = Py.Import("Test_QuantBookHistory");
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void OneTimeTearDown()
|
||||
{
|
||||
// Reset to initial handler
|
||||
Log.LogHandler = _logHandler;
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(2013, 10, 11, SecurityType.Equity, "SPY")]
|
||||
[TestCase(2014, 5, 9, SecurityType.Forex, "EURUSD")]
|
||||
[TestCase(2016, 10, 9, SecurityType.Crypto, "BTCUSD")]
|
||||
public void SecurityQuantBookHistoryTests(int year, int month, int day, SecurityType securityType, string symbol)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var startDate = new DateTime(year, month, day);
|
||||
var securityTestHistory = _module.SecurityHistoryTest(startDate, securityType, symbol);
|
||||
|
||||
// Get the last 10 candles
|
||||
var periodHistory = securityTestHistory.test_period_overload(10);
|
||||
var count = (periodHistory.shape[0] as PyObject).AsManagedObject(typeof(int));
|
||||
Assert.AreEqual(10, count);
|
||||
|
||||
// Get the one day of data
|
||||
var timedeltaHistory = securityTestHistory.test_period_overload(TimeSpan.FromDays(1));
|
||||
var firstIndex = (DateTime)(timedeltaHistory.index.values[0] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-1).Date, firstIndex.Date);
|
||||
|
||||
// Get the one day of data, ending one day before start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate.AddDays(-1));
|
||||
firstIndex = (DateTime)(startEndHistory.index.values[0] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-2).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(2014, 5, 9, "Nifty", "NIFTY")]
|
||||
public void CustomDataQuantBookHistoryTests(int year, int month, int day, string customDataType, string symbol)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var startDate = new DateTime(year, month, day);
|
||||
var securityTestHistory = _module.CustomDataHistoryTest(startDate, customDataType, symbol);
|
||||
|
||||
// Get the last 5 candles
|
||||
var periodHistory = securityTestHistory.test_period_overload(5);
|
||||
var count = (periodHistory.shape[0] as PyObject).AsManagedObject(typeof(int));
|
||||
Assert.AreEqual(5, count);
|
||||
|
||||
// Get the one day of data
|
||||
var timedeltaHistory = securityTestHistory.test_period_overload(TimeSpan.FromDays(8));
|
||||
var firstIndex =
|
||||
(DateTime)(timedeltaHistory.index.values[0] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.AreEqual(startDate.AddDays(-7), firstIndex);
|
||||
|
||||
// Get the one day of data, ending one day before start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate.AddDays(-2));
|
||||
firstIndex = (DateTime)(startEndHistory.index.values[0] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.AreEqual(startDate.AddDays(-2).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleSecuritiesQuantBookHistoryTests()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var startDate = new DateTime(2014, 5, 9);
|
||||
var securityTestHistory = _module.MultipleSecuritiesHistoryTest(startDate, null, null);
|
||||
|
||||
// Get the last 5 candles
|
||||
var periodHistory = securityTestHistory.test_period_overload(5);
|
||||
|
||||
// Note there is no data for BTCUSD at 2014
|
||||
|
||||
//symbol EURUSD SPY
|
||||
//time
|
||||
//2014-05-02 16:00:00 NaN 164.219446
|
||||
//2014-05-04 20:00:00 1.387185 NaN
|
||||
//2014-05-05 16:00:00 NaN 164.551273
|
||||
//2014-05-05 20:00:00 1.387480 NaN
|
||||
//2014-05-06 16:00:00 NaN 163.127909
|
||||
//2014-05-06 20:00:00 1.392925 NaN
|
||||
//2014-05-07 16:00:00 NaN 164.070997
|
||||
//2014-05-07 20:00:00 1.391070 NaN
|
||||
//2014-05-08 16:00:00 NaN 163.905083
|
||||
//2014-05-08 20:00:00 1.384265 NaN
|
||||
Log.Trace(periodHistory.ToString());
|
||||
|
||||
var count = (periodHistory.shape[0] as PyObject).AsManagedObject(typeof(int));
|
||||
Assert.AreEqual(10, count);
|
||||
|
||||
// Get the one day of data
|
||||
var timedeltaHistory = securityTestHistory.test_period_overload(TimeSpan.FromDays(8));
|
||||
var firstIndex = (DateTime)(timedeltaHistory.index.values[0] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
|
||||
// EURUSD exchange time zone is NY but data is UTC so we have a 4 hour difference with algo TZ which is NY
|
||||
Assert.AreEqual(startDate.AddDays(-8).AddHours(16), firstIndex);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanonicalOptionQuantBookHistory()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = "TWX";
|
||||
var startDate = new DateTime(2014, 6, 6);
|
||||
var securityTestHistory = _module.OptionHistoryTest(startDate, SecurityType.Option, symbol);
|
||||
|
||||
// Get the one day of data, ending on start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate);
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
var firstIndex = (DateTime)(startEndHistory.index.values[0][4] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-1).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanonicalOptionIntradayQuantBookHistory()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = "TWX";
|
||||
var currentDate = new DateTime(2014, 6, 6, 18, 0, 0);
|
||||
var securityTestHistory = _module.OptionHistoryTest(new DateTime(2014, 6, 7), SecurityType.Option, symbol);
|
||||
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(currentDate, new DateTime(2014, 6, 6, 10, 0, 0));
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
Assert.IsFalse((bool)startEndHistory.empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestCaseData[] CanonicalOptionIntradayHistoryTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
var twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
var twxOption = Symbol.CreateCanonicalOption(twx);
|
||||
|
||||
var spx = Symbol.Create("SPX", SecurityType.Index, Market.USA);
|
||||
var spxwOption = Symbol.CreateCanonicalOption(spx, Market.USA, null);
|
||||
|
||||
return
|
||||
[
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05), (DateTime?)null, Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05), new DateTime(2014, 06, 05), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05), new DateTime(2014, 06, 06), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05, 0, 0, 0), new DateTime(2014, 06, 05, 15, 0, 0), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05, 10, 0, 0), new DateTime(2014, 06, 05, 15, 0, 0), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05, 10, 0, 0), new DateTime(2014, 06, 06), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05, 10, 0, 0), new DateTime(2014, 06, 06, 10, 0, 0), Resolution.Minute),
|
||||
new TestCaseData(twxOption, new DateTime(2014, 06, 05, 10, 0, 0), new DateTime(2014, 06, 06, 15, 0, 0), Resolution.Minute),
|
||||
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 04), (DateTime?)null, Resolution.Hour),
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 04), new DateTime(2021, 01, 04), Resolution.Hour),
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 04), new DateTime(2021, 01, 05), Resolution.Hour),
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 04, 10, 0, 0), new DateTime(2021, 01, 04, 15, 0, 0), Resolution.Hour),
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 04, 10, 0, 0), new DateTime(2021, 01, 05, 15, 0, 0), Resolution.Hour),
|
||||
new TestCaseData(spxwOption, new DateTime(2021, 01, 14, 10, 0, 0), new DateTime(2021, 01, 14, 15, 0, 0), Resolution.Hour),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(CanonicalOptionIntradayHistoryTestCases))]
|
||||
public void CanonicalOptionIntradayQuantBookHistoryWithIntradayRange(Symbol canonicalOption, DateTime start, DateTime? end, Resolution resolution)
|
||||
{
|
||||
var quantBook = new QuantBook();
|
||||
var historyProvider = new TestHistoryProvider(quantBook.HistoryProvider);
|
||||
quantBook.SetHistoryProvider(historyProvider);
|
||||
quantBook.SetStartDate((end ?? start).Date.AddDays(1));
|
||||
|
||||
var option = quantBook.AddSecurity(canonicalOption);
|
||||
var history = quantBook.OptionHistory(canonicalOption, start, end, resolution);
|
||||
|
||||
Assert.Greater(history.Count, 0);
|
||||
|
||||
var symbolsInHistory = history.SelectMany(slice => slice.AllData.Select(x => x.Symbol)).Distinct().ToList();
|
||||
Assert.Greater(symbolsInHistory.Count, 1);
|
||||
|
||||
var underlying = symbolsInHistory.Where(x => x == canonicalOption.Underlying).ToList();
|
||||
Assert.AreEqual(1, underlying.Count);
|
||||
|
||||
var contractsSymbols = symbolsInHistory.Where(x => x.SecurityType == canonicalOption.SecurityType).ToList();
|
||||
Assert.Greater(contractsSymbols.Count, 1);
|
||||
|
||||
var expectedDates = new HashSet<DateTime> { start.Date };
|
||||
if (end.HasValue && end.Value > end.Value.Date)
|
||||
{
|
||||
expectedDates.Add(end.Value.Date);
|
||||
}
|
||||
|
||||
var dataDates = history.SelectMany(slice => slice.AllData.Where(x => contractsSymbols.Contains(x.Symbol)).Select(x => x.EndTime.Date)).ToHashSet();
|
||||
CollectionAssert.AreEqual(expectedDates, dataDates);
|
||||
|
||||
// OptionUniverse must have been requested for all dates in the range
|
||||
foreach (var date in Time.EachTradeableDay(option, start.Date, (end ?? start).Date))
|
||||
{
|
||||
Assert.AreEqual(1, historyProvider.HistoryRequests.Count(request => request.DataType == typeof(OptionUniverse) && request.EndTimeLocal == date));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OptionContractQuantBookHistory()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = Symbol.CreateOption("TWX", Market.USA, OptionStyle.American, OptionRight.Call, 70, new DateTime(2015, 01, 17));
|
||||
var startDate = new DateTime(2014, 6, 6);
|
||||
var securityTestHistory = _module.OptionContractHistoryTest(startDate, SecurityType.Option, symbol);
|
||||
|
||||
// Get the one day of data, ending on start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate);
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
var firstIndex = (DateTime)(startEndHistory.index.values[0][4] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-1).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OptionIndexWeekly()
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var spxw = qb.AddIndexOption(Symbols.SPX, "SPXW");
|
||||
spxw.SetFilter(u => u.Strikes(0, 1)
|
||||
// single week ahead since there are many SPXW contracts and we want to preserve performance
|
||||
.Expiration(0, 7)
|
||||
.IncludeWeeklys());
|
||||
|
||||
var startTime = new DateTime(2021, 1, 4);
|
||||
|
||||
var historyByOptionSymbol = qb.GetOptionHistory(spxw.Symbol, startTime);
|
||||
var historyByUnderlyingSymbol = qb.GetOptionHistory(Symbols.SPX, "SPXW", startTime);
|
||||
|
||||
List<DateTime> expiry;
|
||||
List<DateTime> byUnderlyingExpiry;
|
||||
|
||||
historyByOptionSymbol.GetExpiryDates().TryConvert(out expiry);
|
||||
historyByUnderlyingSymbol.GetExpiryDates().TryConvert(out byUnderlyingExpiry);
|
||||
|
||||
List<decimal> strikes;
|
||||
List<decimal> byUnderlyingStrikes;
|
||||
|
||||
historyByOptionSymbol.GetStrikes().TryConvert(out strikes);
|
||||
historyByUnderlyingSymbol.GetStrikes().TryConvert(out byUnderlyingStrikes);
|
||||
|
||||
Assert.IsTrue(expiry.Count > 0);
|
||||
Assert.IsTrue(expiry.SequenceEqual(byUnderlyingExpiry));
|
||||
|
||||
Assert.IsTrue(strikes.Count > 0);
|
||||
Assert.IsTrue(strikes.SequenceEqual(byUnderlyingStrikes));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OptionUnderlyingSymbolQuantBookHistory()
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var twx = qb.AddEquity("TWX");
|
||||
var twxOptions = qb.AddOption("TWX");
|
||||
|
||||
var historyByOptionSymbol = qb.GetOptionHistory(twxOptions.Symbol, new DateTime(2014, 6, 5), new DateTime(2014, 6, 6));
|
||||
var historyByEquitySymbol = qb.GetOptionHistory(twx.Symbol, new DateTime(2014, 6, 5), new DateTime(2014, 6, 6));
|
||||
|
||||
List<DateTime> expiry;
|
||||
List<DateTime> byUnderlyingExpiry;
|
||||
|
||||
historyByOptionSymbol.GetExpiryDates().TryConvert(out expiry);
|
||||
historyByEquitySymbol.GetExpiryDates().TryConvert(out byUnderlyingExpiry);
|
||||
|
||||
List<decimal> strikes;
|
||||
List<decimal> byUnderlyingStrikes;
|
||||
|
||||
historyByOptionSymbol.GetStrikes().TryConvert(out strikes);
|
||||
historyByEquitySymbol.GetStrikes().TryConvert(out byUnderlyingStrikes);
|
||||
|
||||
Assert.IsTrue(expiry.Count > 0);
|
||||
Assert.IsTrue(expiry.SequenceEqual(byUnderlyingExpiry));
|
||||
|
||||
Assert.IsTrue(strikes.Count > 0);
|
||||
Assert.IsTrue(strikes.SequenceEqual(byUnderlyingStrikes));
|
||||
}
|
||||
|
||||
[TestCase(182, 2)]
|
||||
[TestCase(120, 1)]
|
||||
public void CanonicalFutureQuantBookHistory(int maxFilter, int numberOfFutureContracts)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = Futures.Indices.SP500EMini;
|
||||
var startDate = new DateTime(2013, 10, 11);
|
||||
var securityTestHistory = _module.FutureHistoryTest(startDate, SecurityType.Future, symbol);
|
||||
|
||||
// Get the one day of data, ending on start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate, startDate.AddDays(-1), maxFilter);
|
||||
|
||||
Log.Trace(startEndHistory.index.levels[1].size.ToString());
|
||||
Assert.AreEqual(numberOfFutureContracts, (int)startEndHistory.index.levels[1].size);
|
||||
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
var firstIndex = (DateTime)(startEndHistory.index.values[0][2] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-1).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(182, 2)]
|
||||
[TestCase(120, 1)]
|
||||
public void CanonicalFutureIntradayQuantBookHistory(int maxFilter, int numberOfFutureContracts)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = Futures.Indices.SP500EMini;
|
||||
var currentDate = new DateTime(2013, 10, 11, 18, 0, 0);
|
||||
var securityTestHistory = _module.FutureHistoryTest(new DateTime(2013, 10, 12), SecurityType.Future, symbol);
|
||||
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(currentDate, new DateTime(2013, 10, 11, 10, 0, 0), maxFilter);
|
||||
|
||||
Log.Trace(startEndHistory.index.levels[1].size.ToString());
|
||||
Assert.AreEqual(numberOfFutureContracts, (int)startEndHistory.index.levels[1].size);
|
||||
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
Assert.IsFalse((bool)startEndHistory.empty);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestCaseData[] CanonicalFutureIntradayHistoryTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
var es = Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME);
|
||||
return
|
||||
[
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10), (DateTime?)null),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10), new DateTime(2013, 10, 10)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10), new DateTime(2013, 10, 11)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10, 0, 0, 0), new DateTime(2013, 10, 10, 15, 0, 0)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10, 10, 0, 0), new DateTime(2013, 10, 10, 15, 0, 0)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10, 10, 0, 0), new DateTime(2013, 10, 11)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10, 10, 0, 0), new DateTime(2013, 10, 11, 10, 0, 0)),
|
||||
new TestCaseData(es, new DateTime(2013, 10, 10, 10, 0, 0), new DateTime(2013, 10, 11, 15, 0, 0))
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(CanonicalFutureIntradayHistoryTestCases))]
|
||||
public void CanonicalFutureIntradayQuantBookHistoryWithIntradayRange(Symbol canonicalFuture, DateTime start, DateTime? end)
|
||||
{
|
||||
var quantBook = new QuantBook();
|
||||
var historyProvider = new TestHistoryProvider(quantBook.HistoryProvider);
|
||||
quantBook.SetHistoryProvider(historyProvider);
|
||||
quantBook.SetStartDate((end ?? start).Date.AddDays(1));
|
||||
var future = quantBook.AddSecurity(canonicalFuture) as Future;
|
||||
future.SetFilter(universe => universe);
|
||||
|
||||
var history = quantBook.FutureHistory(canonicalFuture, start, end, Resolution.Minute);
|
||||
Assert.Greater(history.Count, 0);
|
||||
|
||||
var symbolsInHistory = history.SelectMany(slice => slice.AllData.Select(x => x.Symbol)).Distinct().ToList();
|
||||
Assert.Greater(symbolsInHistory.Count, 1);
|
||||
|
||||
var expectedDates = new HashSet<DateTime> { start.Date };
|
||||
if (end.HasValue && end.Value > end.Value.Date)
|
||||
{
|
||||
expectedDates.Add(end.Value.Date);
|
||||
}
|
||||
|
||||
var dataDates = history.SelectMany(slice => slice.AllData.Select(x => x.EndTime.Date)).ToHashSet();
|
||||
CollectionAssert.AreEqual(expectedDates, dataDates);
|
||||
|
||||
// FutureUniverse must have been requested for all dates in the range
|
||||
foreach (var date in Time.EachTradeableDay(future, start.Date, (end ?? start).Date))
|
||||
{
|
||||
Assert.AreEqual(1, historyProvider.HistoryRequests.Count(request => request.DataType == typeof(FutureUniverse) && request.EndTimeLocal == date));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FutureContractQuantBookHistory()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var symbol = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2014, 12, 19));
|
||||
var startDate = new DateTime(2013, 10, 11);
|
||||
var securityTestHistory = _module.FutureContractHistoryTest(startDate, SecurityType.Future, symbol);
|
||||
|
||||
// Get the one day of data, ending on start date
|
||||
var startEndHistory = securityTestHistory.test_daterange_overload(startDate);
|
||||
Log.Trace(startEndHistory.ToString());
|
||||
var firstIndex = (DateTime)(startEndHistory.index.values[0][2] as PyObject).AsManagedObject(typeof(DateTime));
|
||||
Assert.GreaterOrEqual(startDate.AddDays(-1).Date, firstIndex.Date);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FuturesOptionsWithFutureContract()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var expiry = new DateTime(2020, 3, 20);
|
||||
var future = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, expiry);
|
||||
var start = new DateTime(2020, 1, 5);
|
||||
var end = new DateTime(2020, 1, 6);
|
||||
var history = qb.GetOptionHistory(future, start, end, Resolution.Minute, extendedMarketHours: true);
|
||||
dynamic df = history.GetAllData();
|
||||
|
||||
Assert.IsNotNull(df);
|
||||
Assert.IsFalse((bool)df.empty.AsManagedObject(typeof(bool)));
|
||||
Assert.Greater((int)df.__len__().AsManagedObject(typeof(int)), 360);
|
||||
Assert.AreEqual(5, (int)df.index.levels.__len__().AsManagedObject(typeof(int)));
|
||||
Assert.IsTrue((bool)df.index.levels[0].__contains__(expiry.ToStringInvariant("yyyy-MM-dd")).AsManagedObject(typeof(bool)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FuturesOptionsWithFutureOptionContract()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var expiry = new DateTime(2020, 3, 20);
|
||||
var future = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, expiry);
|
||||
var futureOption = Symbol.CreateOption(
|
||||
future,
|
||||
future.ID.Market,
|
||||
OptionStyle.American,
|
||||
OptionRight.Call,
|
||||
3300m,
|
||||
expiry);
|
||||
|
||||
var start = new DateTime(2020, 1, 5);
|
||||
var end = new DateTime(2020, 1, 6);
|
||||
var history = qb.GetOptionHistory(futureOption, start, end, Resolution.Minute, extendedMarketHours: true);
|
||||
dynamic df = history.GetAllData();
|
||||
|
||||
Assert.IsNotNull(df);
|
||||
Assert.IsFalse((bool)df.empty.AsManagedObject(typeof(bool)));
|
||||
Assert.AreEqual(360, (int)df.__len__().AsManagedObject(typeof(int)));
|
||||
Assert.AreEqual(5, (int)df.index.levels.__len__().AsManagedObject(typeof(int)));
|
||||
Assert.IsTrue((bool)df.index.levels[0].__contains__(expiry.ToStringInvariant("yyyy-MM-dd")).AsManagedObject(typeof(bool)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanoicalFutureCrashesGetOptionHistory()
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var future = Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME);
|
||||
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
qb.GetOptionHistory(future, default(DateTime), DateTime.MaxValue, Resolution.Minute);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(true, true, 1920)]
|
||||
[TestCase(true, false, 780)]
|
||||
[TestCase(false, true, 898)]
|
||||
[TestCase(false, false, 390)]
|
||||
public void OptionHistorySpecifyingFillForwardAndExtendedMarket(bool fillForward, bool extendedMarket, int expectedCount)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var start = new DateTime(2013, 10, 11);
|
||||
var end = new DateTime(2013, 10, 15);
|
||||
|
||||
var spy = qb.AddEquity("SPY");
|
||||
dynamic history = qb.GetOptionHistory(spy.Symbol, start, end, Resolution.Minute, fillForward, extendedMarket).GetAllData();
|
||||
var historyCount = (history.shape[0] as PyObject).As<int>();
|
||||
|
||||
Assert.AreEqual(expectedCount, historyCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(true, true, 8640)]
|
||||
[TestCase(true, false, 2700)]
|
||||
[TestCase(false, true, 8279)]
|
||||
[TestCase(false, false, 2699)]
|
||||
public void FutureHistorySpecifyingFillForwardAndExtendedMarket(bool fillForward, bool extendedMarket, int expectedCount)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var start = new DateTime(2013, 10, 6);
|
||||
var end = new DateTime(2013, 10, 15);
|
||||
|
||||
var future = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2013, 12, 20));
|
||||
dynamic history = qb.GetFutureHistory(future, start, end, Resolution.Minute, fillForward, extendedMarket).GetAllData();
|
||||
var historyCount = (history.shape[0] as PyObject).As<int>();
|
||||
|
||||
Assert.AreEqual(expectedCount, historyCount);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python)]
|
||||
public void OptionHistoryObjectIsIterable(Language language)
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var start = new DateTime(2013, 10, 11);
|
||||
var end = new DateTime(2013, 10, 15);
|
||||
|
||||
var spy = qb.AddEquity("SPY");
|
||||
var history = qb.GetOptionHistory(spy.Symbol, start, end, Resolution.Minute);
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
Assert.AreEqual(780, history.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
def getOptionHistory(qb, symbol, start, end, resolution):
|
||||
return qb.GetOptionHistory(symbol, start, end, resolution)
|
||||
|
||||
def getHistoryCount(history):
|
||||
return len(list(history))
|
||||
");
|
||||
|
||||
dynamic getOptionHistory = testModule.GetAttr("getOptionHistory");
|
||||
dynamic getHistoryCount = testModule.GetAttr("getHistoryCount");
|
||||
var pyHistory = getOptionHistory(qb, spy.Symbol, start, end, Resolution.Minute);
|
||||
Assert.AreEqual(780, getHistoryCount(pyHistory).AsManagedObject(typeof(int)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python)]
|
||||
public void FutureHistoryObjectIsIterable(Language language)
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var start = new DateTime(2013, 10, 6);
|
||||
var end = new DateTime(2013, 10, 15);
|
||||
|
||||
var futureSymbol = Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2013, 12, 20));
|
||||
var history = qb.GetFutureHistory(futureSymbol, start, end, Resolution.Minute);
|
||||
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
Assert.AreEqual(2700, history.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
def getFutureHistory(qb, symbol, start, end, resolution):
|
||||
return qb.GetFutureHistory(symbol, start, end, resolution)
|
||||
|
||||
def getHistoryCount(history):
|
||||
return len(list(history))
|
||||
");
|
||||
|
||||
dynamic getFutureHistory = testModule.GetAttr("getFutureHistory");
|
||||
dynamic getHistoryCount = testModule.GetAttr("getHistoryCount");
|
||||
var pyHistory = getFutureHistory(qb, futureSymbol, start, end, Resolution.Minute);
|
||||
Assert.AreEqual(2700, getHistoryCount(pyHistory).AsManagedObject(typeof(int)));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python)]
|
||||
public void GetOptionContractsWithFrontMonthFilter(Language language)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var start = new DateTime(2015, 12, 24);
|
||||
var end = new DateTime(2015, 12, 24);
|
||||
|
||||
var goog = qb.AddEquity("GOOG");
|
||||
var option = qb.AddOption(goog.Symbol);
|
||||
option.SetFilter(universe => universe.Strikes(-5, 5).FrontMonth());
|
||||
|
||||
var history = qb.GetOptionHistory(goog.Symbol, start, end, Resolution.Minute, fillForward: false, extendedMarketHours: false);
|
||||
dynamic data = history.GetAllData();
|
||||
var labels = data.axes[0].names;
|
||||
Assert.AreEqual("expiry", (labels[0] as PyObject).As<string>());
|
||||
}
|
||||
else
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
def getAllData():
|
||||
qb = QuantBook()
|
||||
underlying_symbol = qb.AddEquity(""GOOG"").Symbol
|
||||
option = qb.AddOption(underlying_symbol)
|
||||
option.SetFilter(lambda option_filter_universe: option_filter_universe.Strikes(-5, 5).FrontMonth())
|
||||
option_history = qb.OptionHistory(underlying_symbol, datetime(2015, 12, 24), datetime(2015, 12, 24), Resolution.Minute, fillForward=False, extendedMarketHours=False)
|
||||
data = option_history.GetAllData()
|
||||
return data.axes[0].names[0]");
|
||||
|
||||
dynamic getAllData = testModule.GetAttr("getAllData");
|
||||
var data = getAllData();
|
||||
Assert.AreEqual("expiry", data.AsManagedObject(typeof(string)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryDataDoesnNotReturnDataLabelWithBaseDataCollectionTypes()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def getHistory():
|
||||
qb = QuantBook()
|
||||
symbol = qb.AddEquity(""AAPL"", Resolution.Daily).symbol
|
||||
dataset_symbol = qb.AddData(FundamentalUniverse, symbol).symbol
|
||||
history = qb.History(dataset_symbol, datetime(2014, 3, 1), datetime(2014, 4, 1), Resolution.Daily)
|
||||
return history
|
||||
");
|
||||
dynamic getHistory = testModule.GetAttr("getHistory");
|
||||
var pyHistory = getHistory() as PyObject;
|
||||
var isHistoryEmpty = pyHistory.GetAttr("empty").GetAndDispose<bool?>();
|
||||
Assert.IsFalse(isHistoryEmpty);
|
||||
Assert.IsFalse(pyHistory.HasAttr("data"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryDataDoesWorksCorrecltyWithoutAddingTheCustomDataInPython()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def getHistory():
|
||||
qb = QuantBook()
|
||||
symbol = qb.AddEquity(""AAPL"", Resolution.Daily).symbol
|
||||
dataset_symbol = Symbol.CreateBase(FundamentalUniverse, symbol, symbol.ID.Market)
|
||||
history = qb.History(dataset_symbol, datetime(2014, 3, 1), datetime(2014, 4, 1), Resolution.Daily)
|
||||
return history
|
||||
");
|
||||
dynamic getHistory = testModule.GetAttr("getHistory");
|
||||
var pyHistory = getHistory() as PyObject;
|
||||
var isHistoryEmpty = pyHistory.GetAttr("empty").GetAndDispose<bool?>();
|
||||
Assert.IsFalse(isHistoryEmpty);
|
||||
Assert.IsFalse(pyHistory.HasAttr("data"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryDataDoesWorksCorrectlyWithCustomDataInPython()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
def getHistory():
|
||||
qb = QuantBook()
|
||||
qb.add_data(
|
||||
type=TestTradeBar,
|
||||
ticker='TEST1',
|
||||
properties=SymbolProperties(
|
||||
description='TEST1',
|
||||
quoteCurrency='USD',
|
||||
contractMultiplier=1,
|
||||
minimumPriceVariation=0.01,
|
||||
lotSize=1,
|
||||
marketTicker='TEST1',
|
||||
),
|
||||
exchange_hours=SecurityExchangeHours.always_open(TimeZones.NEW_YORK),
|
||||
resolution=Resolution.MINUTE,
|
||||
fill_forward=True,
|
||||
leverage=1,
|
||||
)
|
||||
history = qb.history(qb.securities.keys(), datetime(2024, 8, 2), datetime(2024, 8, 3))
|
||||
return history
|
||||
|
||||
class TestTradeBar(TradeBar):
|
||||
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
|
||||
return SubscriptionDataSource(source='../../TestData/test.csv',
|
||||
transportMedium=SubscriptionTransportMedium.LOCAL_FILE,
|
||||
format=FileFormat.CSV)
|
||||
|
||||
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
|
||||
if not line[0].isdigit():
|
||||
return None
|
||||
data = line.split(',')
|
||||
bar_time = datetime.utcfromtimestamp(int(data[0]))
|
||||
|
||||
open = float(data[1])
|
||||
high = float(data[2])
|
||||
low = float(data[3])
|
||||
close = float(data[4])
|
||||
volume = int(float(data[7]))
|
||||
return TradeBar(bar_time, config.symbol, open, high, low, close, volume)
|
||||
");
|
||||
dynamic getHistory = testModule.GetAttr("getHistory");
|
||||
var pyHistory = getHistory() as PyObject;
|
||||
var isHistoryEmpty = pyHistory.GetAttr("empty").GetAndDispose<bool?>();
|
||||
Assert.IsFalse(isHistoryEmpty);
|
||||
Assert.IsFalse(pyHistory.HasAttr("data"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryDataWorksCorrecltyWithoutAddingTheCustomDataInCSharp()
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
var symbol = qb.AddEquity("AAPL", Resolution.Daily).Symbol;
|
||||
var datasetSymbol = Symbol.CreateBase(typeof(FundamentalUniverse), symbol, symbol.ID.Market);
|
||||
MarketHoursDatabase.Reset();
|
||||
Assert.DoesNotThrow(() => qb.History(datasetSymbol, new DateTime(2014, 3, 1), new DateTime(2014, 4, 1), Resolution.Daily).ToList());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryDataDoesnNotReturnDataLabelWithBaseDataCollectionTypesAndPeriods()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def get_history():
|
||||
qb = QuantBook()
|
||||
qb.set_start_date(2014, 4, 8)
|
||||
symbol = qb.add_equity(""AAPL"", Resolution.DAILY).symbol
|
||||
dataset_symbol = qb.add_data(FundamentalUniverse, symbol).symbol
|
||||
history = qb.history(dataset_symbol, 20, Resolution.DAILY)
|
||||
return history
|
||||
");
|
||||
dynamic getHistory = testModule.GetAttr("get_history");
|
||||
var pyHistory = getHistory() as PyObject;
|
||||
var isHistoryEmpty = pyHistory.GetAttr("empty").GetAndDispose<bool?>();
|
||||
Assert.IsFalse(isHistoryEmpty);
|
||||
Assert.IsFalse(pyHistory.HasAttr("data"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndicatorHistoryDoesNotReturnPeriodColumn()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def get_indicator_history():
|
||||
qb = QuantBook()
|
||||
qb.set_start_date(2014, 4, 8)
|
||||
symbol = qb.add_equity(""AAPL"", Resolution.DAILY).symbol
|
||||
history = qb.indicator(ValueAtRisk(252, 0.95), symbol, 365, Resolution.DAILY)
|
||||
return history
|
||||
");
|
||||
dynamic getHistory = testModule.GetAttr("get_indicator_history");
|
||||
var pyHistory = getHistory() as PyObject;
|
||||
var columns = pyHistory.GetAttr("columns")
|
||||
.InvokeMethod("tolist")
|
||||
.AsManagedObject(typeof(List<string>)) as List<string>;
|
||||
Assert.IsFalse(columns.Contains("period"));
|
||||
Assert.AreEqual(1, columns.Count);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestHistoryProvider : HistoryProviderBase
|
||||
{
|
||||
private IHistoryProvider _provider;
|
||||
|
||||
public List<HistoryRequest> HistoryRequests { get; } = new();
|
||||
|
||||
public override int DataPointCount => _provider.DataPointCount;
|
||||
|
||||
public TestHistoryProvider(IHistoryProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public override void Initialize(HistoryProviderInitializeParameters parameters)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<Slice> GetHistory(IEnumerable<HistoryRequest> requests, DateTimeZone sliceTimeZone)
|
||||
{
|
||||
requests = requests.ToList();
|
||||
HistoryRequests.AddRange(requests);
|
||||
return _provider.GetHistory(requests, sliceTimeZone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using System;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture]
|
||||
public class QuantBookIndicatorsTests
|
||||
{
|
||||
private ILogHandler _logHandler;
|
||||
dynamic _module;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
// Store initial handler
|
||||
_logHandler = Log.LogHandler;
|
||||
|
||||
SymbolCache.Clear();
|
||||
MarketHoursDatabase.Reset();
|
||||
|
||||
using (Py.GIL())
|
||||
{
|
||||
_module = Py.Import("Test_QuantBookIndicator");
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void OneTimeTearDown()
|
||||
{
|
||||
// Reset to initial handler
|
||||
Log.LogHandler = _logHandler;
|
||||
}
|
||||
|
||||
[TestCase(2013, 10, 11, SecurityType.Equity, "SPY")]
|
||||
[TestCase(2014, 5, 9, SecurityType.Forex, "EURUSD")]
|
||||
[TestCase(2016, 10, 9, SecurityType.Crypto, "BTCUSD")]
|
||||
public void QuantBookIndicatorTests(int year, int month, int day, SecurityType securityType, string symbol)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var startDate = new DateTime(year, month, day);
|
||||
var indicatorTest = _module.IndicatorTest(startDate, securityType, symbol);
|
||||
|
||||
var endDate = startDate;
|
||||
startDate = endDate.AddYears(-1);
|
||||
|
||||
// Tests a data point indicator
|
||||
var dfBB = indicatorTest.test_bollinger_bands(symbol, startDate, endDate, Resolution.Daily).DataFrame;
|
||||
Assert.IsTrue(GetDataFrameLength(dfBB) > 0);
|
||||
|
||||
// Tests a bar indicator
|
||||
var dfATR = indicatorTest.test_average_true_range(symbol, startDate, endDate, Resolution.Daily).DataFrame;
|
||||
Assert.IsTrue(GetDataFrameLength(dfATR) > 0);
|
||||
|
||||
if (securityType == SecurityType.Forex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Tests a trade bar indicator
|
||||
var dfOBV = indicatorTest.test_on_balance_volume(symbol, startDate, endDate, Resolution.Daily).DataFrame;
|
||||
Assert.IsTrue(GetDataFrameLength(dfOBV) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(2013, 10, 11, SecurityType.Equity, "SPY")]
|
||||
[TestCase(2014, 5, 9, SecurityType.Forex, "EURUSD")]
|
||||
[TestCase(2016, 10, 9, SecurityType.Crypto, "BTCUSD")]
|
||||
public void QuantBookIndicatorTests_BackwardsCompatibility(int year, int month, int day, SecurityType securityType, string symbol)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var startDate = new DateTime(year, month, day);
|
||||
var indicatorTest = _module.IndicatorTest(startDate, securityType, symbol);
|
||||
|
||||
var endDate = startDate;
|
||||
startDate = endDate.AddYears(-1);
|
||||
|
||||
// Tests a data point indicator
|
||||
var dfBB = indicatorTest.test_bollinger_bands_backwards_compatibility(symbol, startDate, endDate, Resolution.Daily);
|
||||
Assert.IsTrue(GetDataFrameLength(dfBB) > 0);
|
||||
|
||||
// Tests a bar indicator
|
||||
var dfATR = indicatorTest.test_average_true_range_backwards_compatibility(symbol, startDate, endDate, Resolution.Daily);
|
||||
Assert.IsTrue(GetDataFrameLength(dfATR) > 0);
|
||||
|
||||
if (securityType == SecurityType.Forex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Tests a trade bar indicator
|
||||
var dfOBV = indicatorTest.test_on_balance_volume_backwards_compatibility(symbol, startDate, endDate, Resolution.Daily);
|
||||
Assert.IsTrue(GetDataFrameLength(dfOBV) > 0);
|
||||
}
|
||||
}
|
||||
|
||||
internal static int GetDataFrameLength(dynamic df) => (int)(df.shape[0] as PyObject).AsManagedObject(typeof(int));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Python.Runtime;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Research;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Scheduling;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture]
|
||||
public class QuantBookSelectionTests
|
||||
{
|
||||
private QuantBook _qb;
|
||||
private DateTime _end;
|
||||
private DateTime _start;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_qb = new QuantBook();
|
||||
_end = new DateTime(2014, 4, 22);
|
||||
_start = new DateTime(2014, 3, 24);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python, true)]
|
||||
[TestCase(Language.Python, false)]
|
||||
public void UniverseSelectionData(Language language, bool flatten = false)
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var universe = _qb.AddUniverse((IEnumerable<Fundamental> fundamentals) => fundamentals.Select(x => x.Symbol));
|
||||
var history = _qb.UniverseHistory(universe, _start, _end).ToList();
|
||||
|
||||
// we asked for 4 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(20, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() > 7000));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def getUniverseHistory(qb, start, end):
|
||||
universe = qb.AddUniverse(lambda fundamentals: [ x.Symbol for x in fundamentals ])
|
||||
" + GetBaseImplementation(expectedCount: 7000, identation: " ", flatten: flatten));
|
||||
|
||||
dynamic getUniverse = testModule.GetAttr("getUniverseHistory");
|
||||
var pyHistory = getUniverse(_qb, _start, _end);
|
||||
|
||||
Console.WriteLine((string)pyHistory.to_string());
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalDataCount = pyHistory.loc[date].shape[0].AsManagedObject(typeof(int));
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 7000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var type = typeof(List<Fundamental>);
|
||||
var fundamental = (List<Fundamental>)pyHistory.loc[index].AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Count, 7000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp, false)]
|
||||
[TestCase(Language.Python, false, true)]
|
||||
[TestCase(Language.Python, false, false)]
|
||||
[TestCase(Language.CSharp, true)]
|
||||
[TestCase(Language.Python, true, true)]
|
||||
[TestCase(Language.Python, true, false)]
|
||||
public void UniverseSelection(Language language, bool useUniverseUnchanged, bool flatten = false)
|
||||
{
|
||||
var selectionState = false;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var universe = _qb.AddUniverse((IEnumerable<Fundamental> fundamentals) =>
|
||||
{
|
||||
if (!useUniverseUnchanged || !selectionState)
|
||||
{
|
||||
selectionState = true;
|
||||
return new[] { Symbols.AAPL };
|
||||
}
|
||||
// after the first call we will return 'unchanged' if 'useUniverseUnchanged' is true
|
||||
return Universe.Unchanged;
|
||||
});
|
||||
var history = _qb.UniverseHistory(universe, _start, _end).ToList();
|
||||
|
||||
// we asked for 4 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(20, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() == 1));
|
||||
Assert.IsTrue(history.All(x => x.Single().Symbol == Symbols.AAPL));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def __init__(self, useUniverseUnchanged):
|
||||
self.useUniverseUnchanged = useUniverseUnchanged
|
||||
self.state = False
|
||||
|
||||
def selection(self, fundamentals):
|
||||
if not self.useUniverseUnchanged or not self.state:
|
||||
self.state = True
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
return Universe.Unchanged
|
||||
|
||||
def getUniverseHistory(self, qb, start, end):
|
||||
universe = qb.add_universe(self.selection)
|
||||
" + GetBaseImplementation(expectedCount: 1, identation: " ", flatten: flatten)).GetAttr("Test");
|
||||
|
||||
var instance = testModule(useUniverseUnchanged);
|
||||
var pyHistory = instance.getUniverseHistory(_qb, _start, _end);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python, true)]
|
||||
[TestCase(Language.Python, false)]
|
||||
public void UniverseSelectionWithDateRule(Language language, bool flatten = false)
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var universe = _qb.AddUniverse((IEnumerable<Fundamental> fundamentals) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
});
|
||||
var history = _qb.UniverseHistory(universe, _start, _end, _qb.DateRules.WeekEnd()).ToList();
|
||||
|
||||
Assert.AreEqual(4, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() == 1));
|
||||
Assert.IsTrue(history.All(x => x.Single().Symbol == Symbols.AAPL));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule", @"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, flatten):
|
||||
universe = qb.add_universe(self.selection)
|
||||
universeDataPerTime = qb.universe_history(universe, start, end, date_rule = qb.date_rules.week_end(), flatten=flatten)
|
||||
|
||||
if flatten:
|
||||
for date in universeDataPerTime.index.levels[0]:
|
||||
dateUniverseData = universeDataPerTime.loc[date]
|
||||
dataPointCount = dateUniverseData.shape[0]
|
||||
if dataPointCount < 1:
|
||||
raise ValueError(f'Unexpected historical Fundamentals data count {dataPointCount}! Expected > 0')
|
||||
else:
|
||||
for universeDataCollection in universeDataPerTime:
|
||||
dataPointCount = 0
|
||||
for fundamental in universeDataCollection:
|
||||
dataPointCount += 1
|
||||
if type(fundamental) is not Fundamental:
|
||||
raise ValueError(f""Unexpected Fundamentals data type {type(fundamental)}! {str(fundamental)}"")
|
||||
if dataPointCount < 1:
|
||||
raise ValueError(f""Unexpected historical Fundamentals data count {dataPointCount}! Expected > expectedCount"")
|
||||
|
||||
return universeDataPerTime
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, _start, _end, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(4, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(4, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python, true)]
|
||||
[TestCase(Language.Python, false)]
|
||||
public void UniverseSelectionEtf(Language language, bool flatten = false)
|
||||
{
|
||||
_start = new DateTime(2020, 12, 1);
|
||||
_end = new DateTime(2021, 1, 31);
|
||||
var selectionState = false;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var spy = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
var universe = _qb.Universe.ETF(spy, _qb.UniverseSettings, (IEnumerable<ETFConstituentUniverse> etfConstituents) =>
|
||||
{
|
||||
if (!selectionState)
|
||||
{
|
||||
selectionState = true;
|
||||
return new[] { Symbols.AAPL };
|
||||
}
|
||||
// after the first call we will return 'unchanged' if 'useUniverseUnchanged' is true
|
||||
return Universe.Unchanged;
|
||||
});
|
||||
var history = _qb.UniverseHistory(universe, _start, _end).ToList();
|
||||
|
||||
// we asked for 2 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(41, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() == 1));
|
||||
Assert.IsTrue(history.All(x => x.Single().Symbol == Symbols.AAPL));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(ETFConstituentUniverse))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def __init__(self):
|
||||
self.state = False
|
||||
|
||||
def selection(self, etfConstituents):
|
||||
if not self.state:
|
||||
self.state = True
|
||||
return [ x.Symbol for x in etfConstituents if x.Symbol.Value == ""AAPL"" ]
|
||||
return Universe.Unchanged
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, flatten):
|
||||
universe = qb.add_universe(qb.universe.etf(""SPY"", Market.USA, qb.universe_settings, self.selection))
|
||||
universeDataPerTime = qb.universe_history(universe, start, end, flatten=flatten)
|
||||
|
||||
if flatten:
|
||||
for date in universeDataPerTime.index.levels[0]:
|
||||
dateUniverseData = universeDataPerTime.loc[date]
|
||||
dataPointCount = dateUniverseData.shape[0]
|
||||
if dataPointCount < 1:
|
||||
raise ValueError(f""Unexpected historical Fundamentals data count {dataPointCount}! Expected > 0"")
|
||||
else:
|
||||
for universeDataCollection in universeDataPerTime:
|
||||
dataPointCount = 0
|
||||
for etfConstituent in universeDataCollection:
|
||||
dataPointCount += 1
|
||||
if type(etfConstituent) is not ETFConstituentUniverse:
|
||||
raise ValueError(f""Unexpected data type {type(etfConstituent)}! {str(ETFConstituentUniverse)}"")
|
||||
if dataPointCount < 1:
|
||||
raise ValueError(f""Unexpected historical Fundamentals data count {dataPointCount}! Expected > expectedCount"")
|
||||
|
||||
return universeDataPerTime
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, _start, _end, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(41, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(41, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 41; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(ETFConstituentUniverse[]);
|
||||
var etfConstituent = (ETFConstituentUniverse[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(etfConstituent.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, etfConstituent[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python, true)]
|
||||
[TestCase(Language.Python, false)]
|
||||
public void UniverseSelectionData_BackwardsCompatibility(Language language, bool flatten = false)
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(_start, _end).ToList();
|
||||
|
||||
// we asked for 4 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(20, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() > 7000));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
def getUniverseHistory(qb, start, end, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, flatten=flatten)
|
||||
");
|
||||
|
||||
dynamic getUniverse = testModule.GetAttr("getUniverseHistory");
|
||||
var pyHistory = getUniverse(_qb, _start, _end, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalDataCount = pyHistory.loc[date].shape[0].AsManagedObject(typeof(int));
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 7000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var type = typeof(List<Fundamental>);
|
||||
var fundamental = (List<Fundamental>)pyHistory.loc[index].AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Count, 7000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp, false)]
|
||||
[TestCase(Language.Python, false, true)]
|
||||
[TestCase(Language.Python, false, false)]
|
||||
[TestCase(Language.CSharp, true)]
|
||||
[TestCase(Language.Python, true, true)]
|
||||
[TestCase(Language.Python, true, false)]
|
||||
public void UniverseSelection_BackwardsCompatibility(Language language, bool useUniverseUnchanged, bool flatten = false)
|
||||
{
|
||||
var selectionState = false;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(_start, _end, (fundamental) =>
|
||||
{
|
||||
if (!useUniverseUnchanged || !selectionState)
|
||||
{
|
||||
selectionState = true;
|
||||
return new[] { Symbols.AAPL };
|
||||
}
|
||||
// after the first call we will return 'unchanged' if 'useUniverseUnchanged' is true
|
||||
return Universe.Unchanged;
|
||||
}).ToList();
|
||||
|
||||
// we asked for 4 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(20, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() == 1));
|
||||
Assert.IsTrue(history.All(x => x.Single().Symbol == Symbols.AAPL));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def __init__(self, useUniverseUnchanged):
|
||||
self.useUniverseUnchanged = useUniverseUnchanged
|
||||
self.state = False
|
||||
|
||||
def selection(self, fundamentals):
|
||||
if not self.useUniverseUnchanged or not self.state:
|
||||
self.state = True
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
return Universe.Unchanged
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection, flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule(useUniverseUnchanged);
|
||||
var pyHistory = instance.getUniverseHistory(_qb, _start, _end, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(20, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python, true)]
|
||||
[TestCase(Language.Python, false)]
|
||||
public void GenericUniverseSelectionIsCompatibleWithDateRule(Language language, bool flatten = false)
|
||||
{
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(_start, _end, (fundamental) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
}, _qb.DateRules.WeekEnd()).ToList();
|
||||
|
||||
// we asked for 4 weeks, 5 work days for each week expected
|
||||
Assert.AreEqual(4, history.Count);
|
||||
Assert.IsTrue(history.All(x => x.Count() == 1));
|
||||
Assert.IsTrue(history.All(x => x.Single().Symbol == Symbols.AAPL));
|
||||
Assert.IsTrue(history.All(x => x.All(fundamental => fundamental.GetType() == typeof(Fundamental))));
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection, date_rule = qb.date_rules.week_end(), flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, _start, _end, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(4, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
|
||||
Assert.AreEqual(4, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(4, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MonthlyEndGenericUniverseSelectionWorksAsExpected()
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(
|
||||
new DateTime(2014, 3, 24),
|
||||
new DateTime(2014, 4, 7),
|
||||
(fundamental) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
},
|
||||
_qb.DateRules.MonthEnd(Symbols.AAPL)).ToList();
|
||||
var lastDayOfMonth = history.Select(x => x.First()).Select(x => x.EndTime).First();
|
||||
Assert.IsNotNull(lastDayOfMonth);
|
||||
Assert.AreEqual(new DateTime(2014, 3, 29), lastDayOfMonth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MonthlyStartGenericUniverseSelectionWorksAsExpected()
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(
|
||||
new DateTime(2014, 3, 24),
|
||||
new DateTime(2014, 4, 7),
|
||||
(fundamental) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
},
|
||||
_qb.DateRules.MonthStart(Symbols.AAPL)).ToList();
|
||||
var firstDayOfMonth = history.Select(x => x.First()).Select(x => x.EndTime).First();
|
||||
Assert.IsNotNull(firstDayOfMonth);
|
||||
Assert.AreEqual(new DateTime(2014, 4, 1), firstDayOfMonth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MonthlyEndSelectionWorksAsExpected()
|
||||
{
|
||||
var universe = _qb.AddUniverse((IEnumerable<Fundamental> fundamentals) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
});
|
||||
var history = _qb.UniverseHistory(universe, new DateTime(2014, 3, 15), new DateTime(2014, 4, 7), _qb.DateRules.MonthEnd(Symbols.AAPL)).ToList();
|
||||
var lastDayOfMonth = history.Select(x => x.First()).Select(x => x.EndTime).First();
|
||||
Assert.IsNotNull(lastDayOfMonth);
|
||||
Assert.AreEqual(new DateTime(2014, 3, 29), lastDayOfMonth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MonthlyStartSelectionWorksAsExpected()
|
||||
{
|
||||
var universe = _qb.AddUniverse((IEnumerable<Fundamental> fundamentals) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
});
|
||||
var history = _qb.UniverseHistory(universe, new DateTime(2014, 3, 15), new DateTime(2014, 4, 7), _qb.DateRules.MonthStart(Symbols.AAPL)).ToList();
|
||||
var firstDayOfMonth = history.Select(x => x.First()).Select(x => x.EndTime).First();
|
||||
Assert.IsNotNull(firstDayOfMonth);
|
||||
Assert.AreEqual(new DateTime(2014, 4, 1), firstDayOfMonth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WeekendGenericUniverseSelectionWorksAsExpected()
|
||||
{
|
||||
var history = _qb.UniverseHistory<Fundamentals, Fundamental>(
|
||||
new DateTime(2014, 3, 24),
|
||||
new DateTime(2014, 4, 7),
|
||||
(fundamental) =>
|
||||
{
|
||||
return new[] { Symbols.AAPL };
|
||||
},
|
||||
_qb.DateRules.Every(DayOfWeek.Wednesday)).ToList();
|
||||
var dates = history.Select(x => x.First()).Select(x => x.EndTime).ToList();
|
||||
Assert.IsNotNull(dates);
|
||||
Assert.IsTrue(dates.All(x => x.DayOfWeek == DayOfWeek.Wednesday));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PythonMonthlyStartGenericUniverseSelectionWorksAsExpected([Values] bool flatten)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, symbol, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection, date_rule = qb.date_rules.month_start(symbol), flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, new DateTime(2014, 3, 24), new DateTime(2014, 4, 7), Symbols.AAPL, flatten);
|
||||
Assert.AreEqual(1, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
var firstDayOfTheMonth = pyHistory.index[0][flatten ? 0 : 1].AsManagedObject(typeof(DateTime));
|
||||
Assert.AreEqual(new DateTime(2014, 4, 1), firstDayOfTheMonth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void PythonMonthlyEndGenericUniverseSelectionWorksAsExpected([Values] bool flatten)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, symbol, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection, date_rule = qb.date_rules.month_end(symbol), flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, new DateTime(2014, 3, 24), new DateTime(2014, 4, 7), Symbols.AAPL, flatten);
|
||||
Assert.AreEqual(1, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
var firstDayOfTheMonth = (pyHistory.index[0][flatten ? 0 : 1]).AsManagedObject(typeof(DateTime));
|
||||
Assert.AreEqual(new DateTime(2014, 3, 29), firstDayOfTheMonth);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PythonDailyGenericUniverseSelectionWorksAsExpected([Values] bool flatten)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, symbol, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection, date_rule = qb.date_rules.every_day(), flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, new DateTime(2014, 3, 24), new DateTime(2014, 4, 7), Symbols.AAPL, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(10, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(10, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PythonWeekendGenericUniverseSelectionWorksAsExpected([Values] bool flatten)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
from datetime import datetime
|
||||
|
||||
class Test():
|
||||
def selection(self, fundamentals):
|
||||
return [ x.Symbol for x in fundamentals if x.Symbol.Value == ""AAPL"" ]
|
||||
|
||||
def getUniverseHistory(self, qb, start, end, symbol, flatten):
|
||||
return qb.universe_history(Fundamentals, start, end, self.selection,
|
||||
date_rule=qb.date_rules.on(datetime(2014, 3, 30), datetime(2014, 3, 31), datetime(2014, 4, 1)),
|
||||
flatten=flatten)
|
||||
").GetAttr("Test");
|
||||
|
||||
var instance = testModule();
|
||||
var pyHistory = instance.getUniverseHistory(_qb, new DateTime(2014, 3, 24), new DateTime(2014, 4, 7), Symbols.AAPL, flatten);
|
||||
|
||||
if (flatten)
|
||||
{
|
||||
Assert.AreEqual(2, pyHistory.index.levels[0].__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
foreach (var date in pyHistory.index.levels[0])
|
||||
{
|
||||
var fundamentalData = pyHistory.loc[date];
|
||||
var fundamentalDataCount = fundamentalData.shape[0].AsManagedObject(typeof(int));
|
||||
|
||||
Assert.GreaterOrEqual(fundamentalDataCount, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamentalData.index[0].AsManagedObject(typeof(Symbol)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(2, pyHistory.__len__().AsManagedObject(typeof(int)));
|
||||
|
||||
for (var i = 0; i < 2; i++)
|
||||
{
|
||||
var index = pyHistory.index[i];
|
||||
var series = pyHistory.loc[index];
|
||||
var type = typeof(Fundamental[]);
|
||||
var fundamental = (Fundamental[])series.AsManagedObject(type);
|
||||
|
||||
Assert.GreaterOrEqual(fundamental.Length, 1);
|
||||
Assert.AreEqual(Symbols.AAPL, fundamental[0].Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterUniverseDataFallsBackToCurrencyPairMatchingWhenSecurityTypesDiffer()
|
||||
{
|
||||
var eurBase = new Symbol(SecurityIdentifier.GenerateBase(typeof(Fundamental), "EUR", Market.USA), "EUR");
|
||||
var gbpBase = new Symbol(SecurityIdentifier.GenerateBase(typeof(Fundamental), "GBP", Market.USA), "GBP");
|
||||
var eurUsd = Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
|
||||
|
||||
// selection returns Forex — security type differs from Base data symbols
|
||||
var filteredSymbols = new HashSet<Symbol> { eurUsd };
|
||||
var data = new List<BaseData> { new Tick { Symbol = eurBase }, new Tick { Symbol = gbpBase } };
|
||||
|
||||
var filterMethod = typeof(QuantBook).GetMethod("FilterUniverseData", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
var result = (List<BaseData>)filterMethod.Invoke(null, new object[] { data, filteredSymbols, (SecurityType?)eurUsd.SecurityType });
|
||||
|
||||
// EUR matches EURUSD base currency, GBP matches neither
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual(eurBase, result[0].Symbol);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PerformSelectionDoesNotSkipDataPointWhenPreviousDataPointIsYielded()
|
||||
{
|
||||
var historyDataPoints = new List<BaseDataCollection>()
|
||||
{
|
||||
new BaseDataCollection(new DateTime(2024, 10, 14), Symbols.AAPL),
|
||||
new BaseDataCollection(new DateTime(2024, 10, 15), Symbols.AAPL),
|
||||
new BaseDataCollection(new DateTime(2024, 10, 17), Symbols.AAPL),
|
||||
new BaseDataCollection(new DateTime(2024, 10, 22), Symbols.AAPL),
|
||||
};
|
||||
|
||||
var dateRule = _qb.DateRules.On(new DateTime(2024, 10, 14), new DateTime(2024, 10, 16), new DateTime(2024, 10, 18));
|
||||
var selectedDates = QuantBookTestClass.PerformSelection(historyDataPoints, new DateTime(2024, 10, 14), new DateTime(2024, 10, 22), dateRule).Select(x => x.EndTime).ToList();
|
||||
|
||||
for (int index = 0; index < 3; index++)
|
||||
{
|
||||
Assert.AreEqual(historyDataPoints[index].EndTime, selectedDates[index]);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetBaseImplementation(int expectedCount, string identation, bool flatten = true)
|
||||
{
|
||||
if (flatten)
|
||||
{
|
||||
return @"
|
||||
{identation}universe_data_df = qb.universe_history(universe, start, end, flatten=True)
|
||||
{identation}for date in universe_data_df.index.levels[0]:
|
||||
{identation} dateUniverseData = universe_data_df.loc[date]
|
||||
{identation} dataPointCount = dateUniverseData.shape[0]
|
||||
{identation} if dataPointCount < expectedCount:
|
||||
{identation} raise ValueError(f""Unexpected historical Fundamentals data count {dataPointCount}! Expected > expectedCount"")
|
||||
{identation}return universe_data_df
|
||||
".Replace("expectedCount", expectedCount.ToStringInvariant(), StringComparison.InvariantCulture)
|
||||
.Replace("{identation}", identation, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
return @"
|
||||
{identation}universeDataPerTime = qb.universe_history(universe, start, end)
|
||||
{identation}for universeDataCollection in universeDataPerTime:
|
||||
{identation} dataPointCount = 0
|
||||
{identation} for fundamental in universeDataCollection:
|
||||
{identation} dataPointCount += 1
|
||||
{identation} if type(fundamental) is not Fundamental:
|
||||
{identation} raise ValueError(f""Unexpected Fundamentals data type {type(fundamental)}! {str(fundamental)}"")
|
||||
{identation} if dataPointCount < expectedCount:
|
||||
{identation} raise ValueError(f""Unexpected historical Fundamentals data count {dataPointCount}! Expected > expectedCount"")
|
||||
{identation}return universeDataPerTime
|
||||
".Replace("expectedCount", expectedCount.ToStringInvariant(), StringComparison.InvariantCulture)
|
||||
.Replace("{identation}", identation, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
private class QuantBookTestClass : QuantBook
|
||||
{
|
||||
public static IEnumerable<BaseDataCollection> PerformSelection(IEnumerable<BaseDataCollection> history, DateTime start, DateTime end, IDateRule dateRule)
|
||||
{
|
||||
Func<BaseDataCollection, BaseDataCollection> processDataPointFunction = dataPoint => dataPoint;
|
||||
Func<BaseDataCollection, DateTime> getTime = dataPoint => dataPoint.EndTime.Date;
|
||||
return PerformSelection<BaseDataCollection, BaseDataCollection>(history, processDataPointFunction, getTime, start, end, dateRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Research;
|
||||
using QuantConnect.Configuration;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture]
|
||||
public class QuantBookTests
|
||||
{
|
||||
[Test]
|
||||
public void AlgorithmModeIsResearch()
|
||||
{
|
||||
var qb = new QuantBook();
|
||||
Assert.AreEqual(AlgorithmMode.Research, qb.AlgorithmMode);
|
||||
}
|
||||
|
||||
[TestCase(DeploymentTarget.CloudPlatform)]
|
||||
[TestCase(DeploymentTarget.LocalPlatform)]
|
||||
[TestCase(null)]
|
||||
public void SetsDeploymentTarget(DeploymentTarget? deploymentTarget)
|
||||
{
|
||||
Config.Reset();
|
||||
if (deploymentTarget.HasValue)
|
||||
{
|
||||
Config.Set("deployment-target", JToken.FromObject(deploymentTarget));
|
||||
Config.Write();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The default value for deploymentTarget = DeploymentTarget.LocalPlatform
|
||||
deploymentTarget = DeploymentTarget.LocalPlatform;
|
||||
}
|
||||
|
||||
var qb = new QuantBook();
|
||||
Assert.AreEqual(deploymentTarget, qb.DeploymentTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
from custom_data import *
|
||||
|
||||
class SecurityHistoryTest():
|
||||
def __init__(self, start_date, security_type, symbol):
|
||||
self.qb = QuantBook()
|
||||
self.qb.SetStartDate(start_date)
|
||||
self.symbol = self.qb.AddSecurity(security_type, symbol).Symbol
|
||||
self.column = 'close'
|
||||
|
||||
def __str__(self):
|
||||
return "{} on {}".format(self.symbol.ID, self.qb.StartDate)
|
||||
|
||||
def test_period_overload(self, period):
|
||||
history = self.qb.History([self.symbol], period)
|
||||
return history[self.column].unstack(level=0)
|
||||
|
||||
def test_daterange_overload(self, end):
|
||||
start = end - timedelta(1)
|
||||
history = self.qb.History([self.symbol], start, end)
|
||||
return history[self.column].unstack(level=0)
|
||||
|
||||
class OptionHistoryTest(SecurityHistoryTest):
|
||||
def test_daterange_overload(self, end, start = None):
|
||||
if start is None:
|
||||
start = end - timedelta(1)
|
||||
history = self.qb.GetOptionHistory(self.symbol, start, end)
|
||||
return history.GetAllData()
|
||||
|
||||
class FutureHistoryTest(SecurityHistoryTest):
|
||||
def test_daterange_overload(self, end, start = None, maxFilter = 182):
|
||||
if start is None:
|
||||
start = end - timedelta(1)
|
||||
self.qb.Securities[self.symbol].SetFilter(0, maxFilter) # default is 35 days
|
||||
history = self.qb.GetFutureHistory(self.symbol, start, end)
|
||||
return history.GetAllData()
|
||||
|
||||
class FutureContractHistoryTest():
|
||||
def __init__(self, start_date, security_type, symbol):
|
||||
self.qb = QuantBook()
|
||||
self.qb.SetStartDate(start_date)
|
||||
self.symbol = symbol
|
||||
self.column = 'close'
|
||||
|
||||
def test_daterange_overload(self, end):
|
||||
start = end - timedelta(1)
|
||||
history = self.qb.GetFutureHistory(self.symbol, start, end)
|
||||
return history.GetAllData()
|
||||
|
||||
class OptionContractHistoryTest(FutureContractHistoryTest):
|
||||
def test_daterange_overload(self, end):
|
||||
start = end - timedelta(1)
|
||||
history = self.qb.GetOptionHistory(self.symbol, start, end)
|
||||
return history.GetAllData()
|
||||
|
||||
class CustomDataHistoryTest(SecurityHistoryTest):
|
||||
def __init__(self, start_date, security_type, symbol):
|
||||
self.qb = QuantBook()
|
||||
self.qb.SetStartDate(start_date)
|
||||
|
||||
if security_type == 'Nifty':
|
||||
type = Nifty
|
||||
self.column = 'close'
|
||||
elif security_type == 'CustomPythonData':
|
||||
type = CustomPythonData
|
||||
self.column = 'close'
|
||||
else:
|
||||
raise
|
||||
|
||||
self.symbol = self.qb.AddData(type, symbol, Resolution.Daily).Symbol
|
||||
|
||||
class MultipleSecuritiesHistoryTest(SecurityHistoryTest):
|
||||
def __init__(self, start_date, security_type, symbol):
|
||||
self.qb = QuantBook()
|
||||
self.qb.SetStartDate(start_date)
|
||||
self.qb.AddEquity('SPY', Resolution.Daily)
|
||||
self.qb.AddForex('EURUSD', Resolution.Daily)
|
||||
self.qb.AddCrypto('BTCUSD', Resolution.Daily)
|
||||
|
||||
def test_period_overload(self, period):
|
||||
history = self.qb.History(self.qb.Securities.Keys, period)
|
||||
return history['close'].unstack(level=0)
|
||||
|
||||
class FundamentalHistoryTest():
|
||||
def __init__(self):
|
||||
self.qb = QuantBook()
|
||||
|
||||
def getFundamentals(self, ticker, selector, start, end):
|
||||
return self.qb.GetFundamental(ticker, selector, start, end)
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
class IndicatorTest():
|
||||
def __init__(self, start_date, security_type, symbol):
|
||||
self.qb = QuantBook()
|
||||
self.qb.SetStartDate(start_date)
|
||||
self.symbol = self.qb.AddSecurity(security_type, symbol).Symbol
|
||||
|
||||
def __str__(self):
|
||||
return "{} on {}".format(self.symbol.ID, self.qb.StartDate)
|
||||
|
||||
def test_bollinger_bands(self, symbol, start, end, resolution):
|
||||
ind = BollingerBands(10, 2)
|
||||
return self.qb.IndicatorHistory(ind, symbol, start, end, resolution)
|
||||
|
||||
def test_average_true_range(self, symbol, start, end, resolution):
|
||||
ind = AverageTrueRange(14)
|
||||
return self.qb.IndicatorHistory(ind, symbol, start, end, resolution)
|
||||
|
||||
def test_on_balance_volume(self, symbol, start, end, resolution):
|
||||
ind = OnBalanceVolume(symbol)
|
||||
return self.qb.IndicatorHistory(ind, symbol, start, end, resolution)
|
||||
|
||||
def test_bollinger_bands_backwards_compatibility(self, symbol, start, end, resolution):
|
||||
ind = BollingerBands(10, 2)
|
||||
return self.qb.Indicator(ind, symbol, start, end, resolution)
|
||||
|
||||
def test_average_true_range_backwards_compatibility(self, symbol, start, end, resolution):
|
||||
ind = AverageTrueRange(14)
|
||||
return self.qb.Indicator(ind, symbol, start, end, resolution)
|
||||
|
||||
def test_on_balance_volume_backwards_compatibility(self, symbol, start, end, resolution):
|
||||
ind = OnBalanceVolume(symbol)
|
||||
return self.qb.Indicator(ind, symbol, start, end, resolution)
|
||||
@@ -0,0 +1,70 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
import decimal
|
||||
|
||||
class CustomPythonData(PythonData):
|
||||
def get_source(self, config, date, is_live):
|
||||
source = Globals.DataFolder + "/equity/usa/daily/ibm.zip"
|
||||
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
||||
|
||||
def reader(self, config, line, date, is_live):
|
||||
if line == None:
|
||||
return None
|
||||
|
||||
customPythonData = CustomPythonData()
|
||||
customPythonData.Symbol = config.Symbol
|
||||
|
||||
scaleFactor = 1 / 10000
|
||||
csv = line.split(",")
|
||||
customPythonData.Time = datetime.strptime(csv[0], '%Y%m%d %H:%M')
|
||||
customPythonData["Open"] = float(csv[1]) * scaleFactor
|
||||
customPythonData["High"] = float(csv[2]) * scaleFactor
|
||||
customPythonData["Low"] = float(csv[3]) * scaleFactor
|
||||
customPythonData["Close"] = float(csv[4]) * scaleFactor
|
||||
customPythonData["Volume"] = float(csv[5])
|
||||
return customPythonData
|
||||
|
||||
class Nifty(PythonData):
|
||||
'''NIFTY Custom Data Class'''
|
||||
def get_source(self, config, date, is_live_mode):
|
||||
return SubscriptionDataSource("https://www.dropbox.com/s/rsmg44jr6wexn2h/CNXNIFTY.csv?dl=1", SubscriptionTransportMedium.REMOTE_FILE)
|
||||
|
||||
|
||||
def reader(self, config, line, date, is_live_mode):
|
||||
if not (line.strip() and line[0].isdigit()): return None
|
||||
|
||||
# New Nifty object
|
||||
index = Nifty()
|
||||
index.symbol = config.symbol
|
||||
|
||||
try:
|
||||
# Example File Format:
|
||||
# Date, Open High Low Close Volume Turnover
|
||||
# 2011-09-13 7792.9 7799.9 7722.65 7748.7 116534670 6107.78
|
||||
data = line.split(',')
|
||||
index.time = datetime.strptime(data[0], "%Y-%m-%d")
|
||||
index.value = decimal.Decimal(data[4])
|
||||
index["Open"] = float(data[1])
|
||||
index["High"] = float(data[2])
|
||||
index["Low"] = float(data[3])
|
||||
index["Close"] = float(data[4])
|
||||
|
||||
|
||||
except ValueError:
|
||||
# Do nothing
|
||||
return None
|
||||
|
||||
return index
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Tests.Research.RegressionTemplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic template framework for regression testing of research notebooks
|
||||
/// </summary>
|
||||
public class BasicTemplateCustomDataTypeHistoryResearchCSharp : IRegressionResearchDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Expected output from the reading the raw notebook file
|
||||
/// </summary>
|
||||
/// <remarks>Requires to be implemented last in the file <see cref="ResearchRegressionTests.UpdateResearchRegressionOutputInSourceFile"/>
|
||||
/// get should start from next line</remarks>
|
||||
public string ExpectedOutput =>
|
||||
"{ \"cells\": [ { \"cell_type\": \"markdown\", \"id\": \"c5f3ed6d\", \"metadata\": { \"papermill\": { \"duration\": 0.003499, \"end_t" +
|
||||
"ime\": \"2023-02-17T23:37:52.736173\", \"exception\": false, \"start_time\": \"2023-02-17T23:37:52.732674\", \"status\": \"completed\" " +
|
||||
"}, \"tags\": [] }, \"source\": [ \"\", \"<hr>\" ] }, { \"cell_type" +
|
||||
"\": \"markdown\", \"id\": \"2025ecbd\", \"metadata\": { \"papermill\": { \"duration\": 0.002997, \"end_time\": \"2023-02-17T23:37:52.74" +
|
||||
"1673\", \"exception\": false, \"start_time\": \"2023-02-17T23:37:52.738676\", \"status\": \"completed\" }, \"tags\": [] }, \"sou" +
|
||||
"rce\": [ \"# Custom data history\" ] }, { \"cell_type\": \"code\", \"execution_count\": 1, \"id\": \"bbc993a0\", \"metadata\": { \"e" +
|
||||
"xecution\": { \"iopub.execute_input\": \"2023-02-17T23:37:52.761679Z\", \"iopub.status.busy\": \"2023-02-17T23:37:52.753179Z\", \"iopub.st" +
|
||||
"atus.idle\": \"2023-02-17T23:38:01.186963Z\", \"shell.execute_reply\": \"2023-02-17T23:38:01.173466Z\" }, \"papermill\": { \"duration\":" +
|
||||
" 8.442283, \"end_time\": \"2023-02-17T23:38:01.187462\", \"exception\": false, \"start_time\": \"2023-02-17T23:37:52.745179\", \"statu" +
|
||||
"s\": \"completed\" }, \"tags\": [], \"vscode\": { \"languageId\": \"csharp\" } }, \"outputs\": [ { \"data\": { \"text/" +
|
||||
"html\": [ \"\", \"<div>\", \" <div id='dotnet-interactive-this-cell-122456.Microsoft.DotNet.Interactive.Http.HttpPort' style='dis" +
|
||||
"play: none'>\", \" The below script needs to be able to find the current output cell; this is an easy method to get it.\", \" </" +
|
||||
"div>\", \" <script type='text/javascript'>\", \"async function probeAddresses(probingAddresses) {\", \" function timeout(ms, p" +
|
||||
"romise) {\", \" return new Promise(function (resolve, reject) {\", \" setTimeout(function () {\", \" " +
|
||||
" reject(new Error('timeout'))\", \" }, ms)\", \" promise.then(resolve, reject)\", \" })\", \" " +
|
||||
" }\", \"\", \" if (Array.isArray(probingAddresses)) {\", \" for (let i = 0; i < probingAddresses.length; i++) {\", \"" +
|
||||
"\", \" let rootUrl = probingAddresses[i];\", \"\", \" if (!rootUrl.endsWith('/')) {\", \" " +
|
||||
" rootUrl = `${rootUrl}/`;\", \" }\", \"\", \" try {\", \" let response = await timeout(10" +
|
||||
"00, fetch(`${rootUrl}discovery`, {\", \" method: 'POST',\", \" cache: 'no-cache',\", \" " +
|
||||
" mode: 'cors',\", \" timeout: 1000,\", \" headers: {\", \" " +
|
||||
"'Content-Type': 'text/plain'\", \" },\", \" body: probingAddresses[i]\", \" }))" +
|
||||
";\", \"\", \" if (response.status == 200) {\", \" return rootUrl;\", \" }\", " +
|
||||
" \" }\", \" catch (e) { }\", \" }\", \" }\", \"}\", \"\", \"function loadDotn" +
|
||||
"etInteractiveApi() {\", \" probeAddresses([\\\"http://172.19.192.1:1000/\\\", \\\"http://192.168.56.1:1000/\\\", \\\"http://192.168.16.104:10" +
|
||||
"00/\\\", \\\"http://127.0.0.1:1000/\\\"])\", \" .then((root) => {\", \" // use probing to find host url and api resources\"," +
|
||||
" \" // load interactive helpers and language services\", \" let dotnetInteractiveRequire = require.config({\", \" " +
|
||||
" context: '122456.Microsoft.DotNet.Interactive.Http.HttpPort',\", \" paths:\", \" {\", \" " +
|
||||
" 'dotnet-interactive': `${root}resources`\", \" }\", \" }) || require;\", \"\", \" window.dot" +
|
||||
"netInteractiveRequire = dotnetInteractiveRequire;\", \"\", \" window.configureRequireFromExtension = function(extensionName, ex" +
|
||||
"tensionCacheBuster) {\", \" let paths = {};\", \" paths[extensionName] = `${root}extensions/${extensionName}" +
|
||||
"/resources/`;\", \" \", \" let internalRequire = require.config({\", \" context: ex" +
|
||||
"tensionCacheBuster,\", \" paths: paths,\", \" urlArgs: `cacheBuster=${extensionCacheBuster}`\", " +
|
||||
" \" }) || require;\", \"\", \" return internalRequire\", \" };\", \" \", " +
|
||||
" \" dotnetInteractiveRequire([\", \" 'dotnet-interactive/dotnet-interactive'\", \" ],\"," +
|
||||
" \" function (dotnet) {\", \" dotnet.init(window);\", \" },\", \" " +
|
||||
" function (error) {\", \" console.log(error);\", \" }\", \" );\", \" })" +
|
||||
"\", \" .catch(error => {console.log(error);});\", \" }\", \"\", \"// ensure `require` is available globally\", " +
|
||||
" \"if ((typeof(require) !== typeof(Function)) || (typeof(require.config) !== typeof(Function))) {\", \" let require_script = document.create" +
|
||||
"Element('script');\", \" require_script.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js');\", " +
|
||||
" \" require_script.setAttribute('type', 'text/javascript');\", \" \", \" \", \" require_script.onload = function() {\"" +
|
||||
", \" loadDotnetInteractiveApi();\", \" };\", \"\", \" document.getElementsByTagName('head')[0].appendChild(requir" +
|
||||
"e_script);\", \"}\", \"else {\", \" loadDotnetInteractiveApi();\", \"}\", \"\", \" </script>\", \"</di" +
|
||||
"v>\" ] }, \"metadata\": {}, \"output_type\": \"display_data\" }, { \"name\": \"stdout\", \"output_type\": \"stream\", " +
|
||||
" \"text\": [ \"Initialize.csx: Loading assemblies from C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] } ], " +
|
||||
" \"source\": [ \"// We need to load assemblies at the start in their own cell\", \"#load \\\"./Initialize.csx\\\"\" ] }, { \"cell_type\":" +
|
||||
" \"code\", \"execution_count\": 2, \"id\": \"0f8ca7c8\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2023-02-17T23:38:01." +
|
||||
"204969Z\", \"iopub.status.busy\": \"2023-02-17T23:38:01.203966Z\", \"iopub.status.idle\": \"2023-02-17T23:38:01.720374Z\", \"shell.execute" +
|
||||
"_reply\": \"2023-02-17T23:38:01.718372Z\" }, \"papermill\": { \"duration\": 0.527908, \"end_time\": \"2023-02-17T23:38:01.720374\", " +
|
||||
"\"exception\": false, \"start_time\": \"2023-02-17T23:38:01.192466\", \"status\": \"completed\" }, \"tags\": [], \"vscode\": { \"" +
|
||||
"languageId\": \"csharp\" } }, \"outputs\": [ { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23" +
|
||||
":38:01.491 TRACE:: Config.GetValue(): debug-mode - Using default value: False\" ] }, { \"name\": \"stdout\", \"output_type\": \"stre" +
|
||||
"am\", \"text\": [ \"20230217 23:38:01.493 TRACE:: Config.Get(): Configuration key not found. Key: results-destination-folder - Using default " +
|
||||
"value: \" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38:01.496 TRACE:: Config.Get(" +
|
||||
"): Configuration key not found. Key: plugin-directory - Using default value: \" ] }, { \"name\": \"stdout\", \"output_type\": \"stre" +
|
||||
"am\", \"text\": [ \"20230217 23:38:01.501 TRACE:: Config.Get(): Configuration key not found. Key: composer-dll-directory - Using default valu" +
|
||||
"e: \" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38:01.502 TRACE:: Composer(): Loa" +
|
||||
"ding Assemblies from C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] }, { \"name\": \"stdout\", \"output_type\":" +
|
||||
" \"stream\", \"text\": [ \"20230217 23:38:01.587 TRAC" +
|
||||
"E:: Config.Get(): Configuration key not found. Key: version-id - Using default value: \" ] }, { \"name\": \"stdout\", \"output_type\"" +
|
||||
": \"stream\", \"text\": [ \"20230217 23:38:01.587 TRACE:: Config.Get(): Configuration key not found. Key: cache-location - Using default valu" +
|
||||
"e: ../../../Data/\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38:01.588 TRACE:: E" +
|
||||
"ngine.Main(): LEAN ALGORITHMIC TRADING ENGINE v2.5.0.0 Mode: DEBUG (64bit) Host: ABREU\" ] }, { \"name\": \"stdout\", \"output_type\"" +
|
||||
": \"stream\", \"text\": [ \"20230217 23:38:01.589 TRACE:: Engine.Main(): Started 7:38 PM\" ] }, { \"name\": \"stdout\", \"o" +
|
||||
"utput_type\": \"stream\", \"text\": [ \"20230217 23:38:01.597 TRACE:: Config.Get(): Configuration key not found. Key: lean-manager-type - Usi" +
|
||||
"ng default value: LocalLeanManager\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38" +
|
||||
":01.619 TRACE:: Config.Get(): Configuration key not found. Key: data-permission-manager - Using default value: DataPermissionManager\" ] }, " +
|
||||
"{ \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38:01.623 TRACE:: Config.Get(): Configuration key not " +
|
||||
"found. Key: results-destination-folder - Using default value: C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] }, {" +
|
||||
" \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20230217 23:38:01.657 TRACE:: Config.Get(): Configuration key not f" +
|
||||
"ound. Key: object-store-root - Using default value: ./storage\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text" +
|
||||
"\": [ \"20230217 23:38:01.668 TRACE:: Config.Get(): Configuration key not found. Key: results-destination-folder - Using default value: C:\\\\Use" +
|
||||
"rs\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] } ], \"source\": [ \"// Initialize Lean Engine.\", \"#load \\\"./Qua" +
|
||||
"ntConnect.csx\\\"\", \"\", \"using System.Globalization;\", \"using System.Linq;\", \"using QuantConnect;\", \"using QuantConnect.Data;" +
|
||||
"\", \"using QuantConnect.Algorithm;\", \"using QuantConnect.Research;\" ] }, { \"cell_type\": \"code\", \"execution_count\": 3, \"id\"" +
|
||||
": \"83870958\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2023-02-17T23:38:01.737374Z\", \"iopub.status.busy\": \"2023-" +
|
||||
"02-17T23:38:01.735874Z\", \"iopub.status.idle\": \"2023-02-17T23:38:02.793265Z\", \"shell.execute_reply\": \"2023-02-17T23:38:02.792263Z\" " +
|
||||
"}, \"papermill\": { \"duration\": 1.067389, \"end_time\": \"2023-02-17T23:38:02.793764\", \"exception\": false, \"start_time\": \"2" +
|
||||
"023-02-17T23:38:01.726375\", \"status\": \"completed\" }, \"tags\": [], \"vscode\": { \"languageId\": \"csharp\" } }, \"output" +
|
||||
"s\": [], \"source\": [ \"class CustomDataType : DynamicData\", \"{\", \" public decimal Open;\", \" public decimal High;\", \" " +
|
||||
" public decimal Low;\", \" public decimal Close;\", \"\", \" public override SubscriptionDataSource GetSource(SubscriptionDataConfig " +
|
||||
"config, DateTime date, bool isLiveMode)\", \" {\", \" var source = \\\"https://www.dl.dropboxusercontent.com/s/d83xvd7mm9fzpk0/path_to" +
|
||||
"_my_csv_data.csv?dl=0\\\";\", \" return new SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile);\", \" }\", \"\"" +
|
||||
", \" public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)\", \" {\", \" i" +
|
||||
"f (string.IsNullOrWhiteSpace(line.Trim()))\", \" {\", \" return null;\", \" }\", \"\", \" try\", \" " +
|
||||
" {\", \" var csv = line.Split(\\\",\\\");\", \" var data = new CustomDataType()\", \" {\", \" " +
|
||||
" Symbol = config.Symbol,\", \" Time = DateTime.ParseExact(csv[0], DateFormat.DB, CultureInfo.InvariantCulture).AddHours(20)" +
|
||||
",\", \" Value = csv[4].ToDecimal(),\", \" Open = csv[1].ToDecimal(),\", \" High = csv[2].ToDecim" +
|
||||
"al(),\", \" Low = csv[3].ToDecimal(),\", \" Close = csv[4].ToDecimal()\", \" };\", \"\", \" " +
|
||||
" return data;\", \" }\", \" catch\", \" {\", \" return null;\", \" }\", \" }\", " +
|
||||
" \"}\" ] }, { \"cell_type\": \"code\", \"execution_count\": 4, \"id\": \"ed9ea0a3\", \"metadata\": { \"execution\": { \"iopub.execu" +
|
||||
"te_input\": \"2023-02-17T23:38:02.815268Z\", \"iopub.status.busy\": \"2023-02-17T23:38:02.814265Z\", \"iopub.status.idle\": \"2023-02-17T23:38" +
|
||||
":10.436355Z\", \"shell.execute_reply\": \"2023-02-17T23:38:10.435324Z\" }, \"papermill\": { \"duration\": 7.633591, \"end_time\": \"" +
|
||||
"2023-02-17T23:38:10.436355\", \"exception\": false, \"start_time\": \"2023-02-17T23:38:02.802764\", \"status\": \"completed\" }, \"t" +
|
||||
"ags\": [], \"vscode\": { \"languageId\": \"csharp\" } }, \"outputs\": [ { \"name\": \"stdout\", \"output_type\": \"stream\", " +
|
||||
" \"text\": [ \"PythonEngine.Initialize(): clr GetManifestResourceStream...\" ] } ], \"source\": [ \"var qb = new QuantBook();\"," +
|
||||
" \"var symbol = qb.AddData<CustomDataType>(\\\"CustomDataType\\\", Resolution.Hour).Symbol;\", \"\", \"var start = new DateTime(2017, 8, 20);" +
|
||||
"\", \"var end = start.AddHours(48);\", \"var history = qb.History<CustomDataType>(symbol, start, end, Resolution.Hour).ToList();\", \"\", " +
|
||||
"\"if (history.Count == 0)\", \"{\", \" throw new Exception(\\\"No history data returned\\\");\", \"}\" ] } ], \"metadata\": { \"kernel" +
|
||||
"spec\": { \"display_name\": \".NET (C#)\", \"language\": \"C#\", \"name\": \".net-csharp\" }, \"language_info\": { \"file_extension\": \".cs" +
|
||||
"\", \"mimetype\": \"text/x-csharp\", \"name\": \"C#\", \"pygments_lexer\": \"csharp\", \"version\": \"10.0\" }, \"papermill\": { \"default" +
|
||||
"_parameters\": {}, \"duration\": 25.341314, \"end_time\": \"2023-02-17T23:38:13.053129\", \"environment_variables\": {}, \"exception\": null, " +
|
||||
" \"input_path\": \"C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\\\\Research\\\\RegressionTemplates\\\\BasicTemplateCustomDat" +
|
||||
"aTypeHistoryResearchCSharp.ipynb\", \"output_path\": \"C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\\\\Research\\\\Regressi" +
|
||||
"onTemplates\\\\BasicTemplateCustomDataTypeHistoryResearchCSharp-output.ipynb\", \"parameters\": {}, \"start_time\": \"2023-02-17T23:37:47.711815\"" +
|
||||
", \"version\": \"2.4.0\" } }, \"nbformat\": 4, \"nbformat_minor\": 5}";
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [ "\n", "<hr>" ]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [ "# Custom data history" ]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": { "vscode": { "languageId": "csharp" } },
|
||||
"outputs": [],
|
||||
"source": [ "// We need to load assemblies at the start in their own cell\n", "#load \"./Initialize.csx\"" ]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": { "vscode": { "languageId": "csharp" } },
|
||||
"outputs": [],
|
||||
"source": [ "// Initialize Lean Engine.\n", "#load \"./QuantConnect.csx\"\n", "\n", "using System.Globalization;\n", "using System.Linq;\n", "using QuantConnect;\n", "using QuantConnect.Data;\n", "using QuantConnect.Algorithm;\n", "using QuantConnect.Research;" ]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": { "vscode": { "languageId": "csharp" } },
|
||||
"outputs": [],
|
||||
"source": [ "class CustomDataType : DynamicData\n", "{\n", " public decimal Open;\n", " public decimal High;\n", " public decimal Low;\n", " public decimal Close;\n", "\n", " public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)\n", " {\n", " var source = \"https://www.dl.dropboxusercontent.com/s/d83xvd7mm9fzpk0/path_to_my_csv_data.csv?dl=0\";\n", " return new SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile);\n", " }\n", "\n", " public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)\n", " {\n", " if (string.IsNullOrWhiteSpace(line.Trim()))\n", " {\n", " return null;\n", " }\n", "\n", " try\n", " {\n", " var csv = line.Split(\",\");\n", " var data = new CustomDataType()\n", " {\n", " Symbol = config.Symbol,\n", " Time = DateTime.ParseExact(csv[0], DateFormat.DB, CultureInfo.InvariantCulture).AddHours(20),\n", " Value = csv[4].ToDecimal(),\n", " Open = csv[1].ToDecimal(),\n", " High = csv[2].ToDecimal(),\n", " Low = csv[3].ToDecimal(),\n", " Close = csv[4].ToDecimal()\n", " };\n", "\n", " return data;\n", " }\n", " catch\n", " {\n", " return null;\n", " }\n", " }\n", "}" ]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": { "vscode": { "languageId": "csharp" } },
|
||||
"outputs": [],
|
||||
"source": [ "var qb = new QuantBook();\n", "var symbol = qb.AddData<CustomDataType>(\"CustomDataType\", Resolution.Hour).Symbol;\n", "\n", "var start = new DateTime(2017, 8, 20);\n", "var end = start.AddHours(48);\n", "var history = qb.History<CustomDataType>(symbol, start, end, Resolution.Hour).ToList();\n", "\n", "if (history.Count == 0)\n", "{\n", " throw new Exception(\"No history data returned\");\n", "}" ]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".NET (C#)",
|
||||
"language": "C#",
|
||||
"name": ".net-csharp"
|
||||
},
|
||||
"language_info": {
|
||||
"file_extension": ".cs",
|
||||
"mimetype": "text/x-csharp",
|
||||
"name": "C#",
|
||||
"pygments_lexer": "csharp",
|
||||
"version": "9.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Tests.Research.RegressionTemplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic template framework for regression testing of research notebooks
|
||||
/// </summary>
|
||||
public class BasicTemplateCustomDataTypeHistoryResearchPython : IRegressionResearchDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Expected output from the reading the raw notebook file
|
||||
/// </summary>
|
||||
/// <remarks>Requires to be implemented last in the file <see cref="ResearchRegressionTests.UpdateResearchRegressionOutputInSourceFile"/>
|
||||
/// get should start from next line</remarks>
|
||||
public string ExpectedOutput =>
|
||||
"{ \"cells\": [ { \"cell_type\": \"markdown\", \"id\": \"d0ea3064\", \"metadata\": { \"papermill\": { \"duration\": 0.003996, \"end_t" +
|
||||
"ime\": \"2023-02-17T21:33:10.586532\", \"exception\": false, \"start_time\": \"2023-02-17T21:33:10.582536\", \"status\": \"completed\" " +
|
||||
"}, \"tags\": [] }, \"source\": [ \"\", \"## Welcome to " +
|
||||
"The QuantConnect Research Page\", \"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\", \"#### Con" +
|
||||
"tribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb\" ] }, { \"cell_type\": \"m" +
|
||||
"arkdown\", \"id\": \"e3e3d0c7\", \"metadata\": { \"papermill\": { \"duration\": 0.003965, \"end_time\": \"2023-02-17T21:33:10.593525\"," +
|
||||
" \"exception\": false, \"start_time\": \"2023-02-17T21:33:10.589560\", \"status\": \"completed\" }, \"tags\": [] }, \"source\": " +
|
||||
"[ \"## QuantBook Basics\", \"\", \"### Start QuantBook\", \"- Add the references and imports\", \"- Create a QuantBook instance\" ] " +
|
||||
"}, { \"cell_type\": \"code\", \"execution_count\": 1, \"id\": \"d0e8fcfc\", \"metadata\": { \"execution\": { \"iopub.execute_input\": " +
|
||||
"\"2023-02-17T21:33:10.602549Z\", \"iopub.status.busy\": \"2023-02-17T21:33:10.601530Z\", \"iopub.status.idle\": \"2023-02-17T21:33:10.616522Z\"" +
|
||||
", \"shell.execute_reply\": \"2023-02-17T21:33:10.614535Z\" }, \"papermill\": { \"duration\": 0.021984, \"end_time\": \"2023-02-17T21" +
|
||||
":33:10.618527\", \"exception\": false, \"start_time\": \"2023-02-17T21:33:10.596543\", \"status\": \"completed\" }, \"tags\": [] }" +
|
||||
", \"outputs\": [], \"source\": [ \"import warnings\", \"warnings.filterwarnings(\\\"ignore\\\")\" ] }, { \"cell_type\": \"code\", \"" +
|
||||
"execution_count\": 2, \"id\": \"a845a7ca\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2023-02-17T21:33:10.627549Z\", " +
|
||||
"\"iopub.status.busy\": \"2023-02-17T21:33:10.627549Z\", \"iopub.status.idle\": \"2023-02-17T21:33:15.142098Z\", \"shell.execute_reply\": \"202" +
|
||||
"3-02-17T21:33:15.140599Z\" }, \"papermill\": { \"duration\": 4.526574, \"end_time\": \"2023-02-17T21:33:15.149104\", \"exception\": " +
|
||||
"false, \"start_time\": \"2023-02-17T21:33:10.622530\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [" +
|
||||
" \"# Load in our startup script, required to set runtime for PythonNet\", \"%run ./start.py\" ] }, { \"cell_type\": \"code\", \"executio" +
|
||||
"n_count\": 3, \"id\": \"9b0cd81c\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2023-02-17T21:33:15.162102Z\", \"iopub." +
|
||||
"status.busy\": \"2023-02-17T21:33:15.160599Z\", \"iopub.status.idle\": \"2023-02-17T21:33:15.266621Z\", \"shell.execute_reply\": \"2023-02-17T" +
|
||||
"21:33:15.263102Z\" }, \"papermill\": { \"duration\": 0.117533, \"end_time\": \"2023-02-17T21:33:15.270138\", \"exception\": false, " +
|
||||
" \"start_time\": \"2023-02-17T21:33:15.152605\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ \"cl" +
|
||||
"ass CustomDataType(PythonData):\", \"\", \" def GetSource(self, config: SubscriptionDataConfig, date: datetime, isLive: bool) -> Subscription" +
|
||||
"DataSource:\", \" source = \\\"https://www.dl.dropboxusercontent.com/s/d83xvd7mm9fzpk0/path_to_my_csv_data.csv?dl=0\\\"\", \" retu" +
|
||||
"rn SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile)\", \"\", \" def Reader(self, config: SubscriptionDataConfig, line: " +
|
||||
"str, date: datetime, isLive: bool) -> BaseData:\", \" if not (line.strip()):\", \" return None\", \"\", \" data =" +
|
||||
" line.split(',')\", \" obj_data = CustomDataType()\", \" obj_data.Symbol = config.Symbol\", \"\", \" try:\", \" " +
|
||||
" obj_data.Time = datetime.strptime(data[0], '%Y-%m-%d %H:%M:%S') + timedelta(hours=20)\", \" obj_data[\\\"open\\\"] = float(data" +
|
||||
"[1])\", \" obj_data[\\\"high\\\"] = float(data[2])\", \" obj_data[\\\"low\\\"] = float(data[3])\", \" obj_da" +
|
||||
"ta[\\\"close\\\"] = float(data[4])\", \" obj_data.Value = obj_data[\\\"close\\\"]\", \"\", \" # property for asserting " +
|
||||
"the correct data is fetched\", \" obj_data[\\\"some_property\\\"] = \\\"some property value\\\"\", \" except ValueError:\", " +
|
||||
" \" return None\", \"\", \" return obj_data\", \"\", \" def __str__ (self):\", \" return f\\\"Time: {self.T" +
|
||||
"ime}, Value: {self.Value}, SomeProperty: {self['some_property']}, Open: {self['open']}, High: {self['high']}, Low: {self['low']}, Close: {self['close'" +
|
||||
"]}\\\"\" ] }, { \"cell_type\": \"code\", \"execution_count\": 4, \"id\": \"e21f2b04\", \"metadata\": { \"execution\": { \"iopub.exe" +
|
||||
"cute_input\": \"2023-02-17T21:33:15.282600Z\", \"iopub.status.busy\": \"2023-02-17T21:33:15.281102Z\", \"iopub.status.idle\": \"2023-02-17T21:" +
|
||||
"33:17.198103Z\", \"shell.execute_reply\": \"2023-02-17T21:33:17.196601Z\" }, \"papermill\": { \"duration\": 1.926991, \"end_time\": " +
|
||||
"\"2023-02-17T21:33:17.200100\", \"exception\": false, \"start_time\": \"2023-02-17T21:33:15.273109\", \"status\": \"completed\" }, \"" +
|
||||
"tags\": [] }, \"outputs\": [], \"source\": [ \"# Create an instance\", \"qb = QuantBook()\", \"symbol = qb.AddData(CustomDataType, \\\"" +
|
||||
"CustomDataType\\\", Resolution.Hour).Symbol\", \"\", \"startDate = datetime(2017, 8, 20)\", \"endDate = startDate + timedelta(hours=48)\", " +
|
||||
" \"history = list(qb.History[CustomDataType](symbol, startDate, endDate, Resolution.Hour))\", \"\", \"if len(history) == 0:\", \" raise Ex" +
|
||||
"ception(\\\"No history data returned\\\")\" ] } ], \"metadata\": { \"kernelspec\": { \"display_name\": \"Python 3 (ipykernel)\", \"language\":" +
|
||||
" \"python\", \"name\": \"python3\" }, \"language_info\": { \"codemirror_mode\": { \"name\": \"ipython\", \"version\": 3 }, \"file_exte" +
|
||||
"nsion\": \".py\", \"mimetype\": \"text/x-python\", \"name\": \"python\", \"nbconvert_exporter\": \"python\", \"pygments_lexer\": \"ipython3\"," +
|
||||
" \"version\": \"3.8.10\" }, \"papermill\": { \"default_parameters\": {}, \"duration\": 13.405899, \"end_time\": \"2023-02-17T21:33:20.058754" +
|
||||
"\", \"environment_variables\": {}, \"exception\": null, \"input_path\": \"C:\\\\Users\\\\jhona\\\\QuantConnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\\\\R" +
|
||||
"esearch\\\\RegressionTemplates\\\\BasicTemplateCustomDataTypeHistoryResearchPython.ipynb\", \"output_path\": \"C:\\\\Users\\\\jhona\\\\QuantConnect\\\\L" +
|
||||
"ean\\\\Tests\\\\bin\\\\Debug\\\\Research\\\\RegressionTemplates\\\\BasicTemplateCustomDataTypeHistoryResearchPython-output.ipynb\", \"parameters\": " +
|
||||
"{}, \"start_time\": \"2023-02-17T21:33:06.652855\", \"version\": \"2.4.0\" }, \"vscode\": { \"interpreter\": { \"hash\": \"9650cb4e16cdd4a8" +
|
||||
"e8e2d128bf38d875813998db22a3c986335f89e0cb4d7bb2\" } } }, \"nbformat\": 4, \"nbformat_minor\": 5}";
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Welcome to The QuantConnect Research Page\n",
|
||||
"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\n",
|
||||
"#### Contribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## QuantBook Basics\n",
|
||||
"\n",
|
||||
"### Start QuantBook\n",
|
||||
"- Add the references and imports\n",
|
||||
"- Create a QuantBook instance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import warnings\n",
|
||||
"warnings.filterwarnings(\"ignore\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load in our startup script, required to set runtime for PythonNet\n",
|
||||
"%run ./start.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class CustomDataType(PythonData):\n",
|
||||
"\n",
|
||||
" def GetSource(self, config: SubscriptionDataConfig, date: datetime, isLive: bool) -> SubscriptionDataSource:\n",
|
||||
" source = \"https://www.dl.dropboxusercontent.com/s/d83xvd7mm9fzpk0/path_to_my_csv_data.csv?dl=0\"\n",
|
||||
" return SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile)\n",
|
||||
"\n",
|
||||
" def Reader(self, config: SubscriptionDataConfig, line: str, date: datetime, isLive: bool) -> BaseData:\n",
|
||||
" if not (line.strip()):\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
" data = line.split(',')\n",
|
||||
" obj_data = CustomDataType()\n",
|
||||
" obj_data.Symbol = config.Symbol\n",
|
||||
"\n",
|
||||
" try:\n",
|
||||
" obj_data.Time = datetime.strptime(data[0], '%Y-%m-%d %H:%M:%S') + timedelta(hours=20)\n",
|
||||
" obj_data[\"open\"] = float(data[1])\n",
|
||||
" obj_data[\"high\"] = float(data[2])\n",
|
||||
" obj_data[\"low\"] = float(data[3])\n",
|
||||
" obj_data[\"close\"] = float(data[4])\n",
|
||||
" obj_data.Value = obj_data[\"close\"]\n",
|
||||
"\n",
|
||||
" # property for asserting the correct data is fetched\n",
|
||||
" obj_data[\"some_property\"] = \"some property value\"\n",
|
||||
" except ValueError:\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
" return obj_data\n",
|
||||
"\n",
|
||||
" def __str__ (self):\n",
|
||||
" return f\"Time: {self.Time}, Value: {self.Value}, SomeProperty: {self['some_property']}, Open: {self['open']}, High: {self['high']}, Low: {self['low']}, Close: {self['close']}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create an instance\n",
|
||||
"qb = QuantBook()\n",
|
||||
"symbol = qb.AddData(CustomDataType, \"CustomDataType\", Resolution.Hour).Symbol\n",
|
||||
"\n",
|
||||
"startDate = datetime(2017, 8, 20)\n",
|
||||
"endDate = startDate + timedelta(hours=48)\n",
|
||||
"history = list(qb.History[CustomDataType](symbol, startDate, endDate, Resolution.Hour))\n",
|
||||
"\n",
|
||||
"if len(history) == 0:\n",
|
||||
" raise Exception(\"No history data returned\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
},
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "9650cb4e16cdd4a8e8e2d128bf38d875813998db22a3c986335f89e0cb4d7bb2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Tests.Research.RegressionTemplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic template framework for regression testing of research notebooks
|
||||
/// </summary>
|
||||
public class BasicTemplateResearchCSharp : IRegressionResearchDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Expected output from the reading the raw notebook file
|
||||
/// </summary>
|
||||
/// <remarks>Requires to be implemented last in the file <see cref="ResearchRegressionTests.UpdateResearchRegressionOutputInSourceFile"/>
|
||||
/// get should start from next line</remarks>
|
||||
public string ExpectedOutput =>
|
||||
"{ \"cells\": [ { \"cell_type\": \"markdown\", \"id\": \"a4652bd4\", \"metadata\": { \"papermill\": { \"duration\": 0.005036, \"end_t" +
|
||||
"ime\": \"2022-03-02T20:44:55.508287\", \"exception\": false, \"start_time\": \"2022-03-02T20:44:55.503251\", \"status\": \"completed\" " +
|
||||
"}, \"tags\": [] }, \"source\": [ \"\", \"## Welcome to " +
|
||||
"The QuantConnect Research Page\", \"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\", \"#### Con" +
|
||||
"tribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb\" ] }, { \"cell_type\": \"m" +
|
||||
"arkdown\", \"id\": \"acb5aec0\", \"metadata\": { \"papermill\": { \"duration\": 0.002024, \"end_time\": \"2022-03-02T20:44:55.513310\"," +
|
||||
" \"exception\": false, \"start_time\": \"2022-03-02T20:44:55.511286\", \"status\": \"completed\" }, \"tags\": [] }, \"source\": " +
|
||||
"[ \"## QuantBook Basics\", \"\", \"### Start QuantBook\", \"- Add the references and imports\", \"- Create a QuantBook instance\" ] " +
|
||||
"}, { \"cell_type\": \"code\", \"execution_count\": 1, \"id\": \"39de525f\", \"metadata\": { \"execution\": { \"iopub.execute_input\": " +
|
||||
"\"2022-03-02T20:44:55.529252Z\", \"iopub.status.busy\": \"2022-03-02T20:44:55.522252Z\", \"iopub.status.idle\": \"2022-03-02T20:44:57.058250Z\"" +
|
||||
", \"shell.execute_reply\": \"2022-03-02T20:44:57.052865Z\" }, \"papermill\": { \"duration\": 1.542006, \"end_time\": \"2022-03-02T20" +
|
||||
":44:57.058250\", \"exception\": false, \"start_time\": \"2022-03-02T20:44:55.516244\", \"status\": \"completed\" }, \"tags\": [] }" +
|
||||
", \"outputs\": [ { \"data\": { \"text/html\": [ \"\", \"<div>\", \" <div id='dotnet-interactive-this-cell-4972.Micr" +
|
||||
"osoft.DotNet.Interactive.Http.HttpPort' style='display: none'>\", \" The below script needs to be able to find the current output cell; t" +
|
||||
"his is an easy method to get it.\", \" </div>\", \" <script type='text/javascript'>\", \"async function probeAddresses(probing" +
|
||||
"Addresses) {\", \" function timeout(ms, promise) {\", \" return new Promise(function (resolve, reject) {\", \" " +
|
||||
"setTimeout(function () {\", \" reject(new Error('timeout'))\", \" }, ms)\", \" promise.then(res" +
|
||||
"olve, reject)\", \" })\", \" }\", \"\", \" if (Array.isArray(probingAddresses)) {\", \" for (let i =" +
|
||||
" 0; i < probingAddresses.length; i++) {\", \"\", \" let rootUrl = probingAddresses[i];\", \"\", \" if (!" +
|
||||
"rootUrl.endsWith('/')) {\", \" rootUrl = `${rootUrl}/`;\", \" }\", \"\", \" try {\", " +
|
||||
" \" let response = await timeout(1000, fetch(`${rootUrl}discovery`, {\", \" method: 'POST',\", \" " +
|
||||
" cache: 'no-cache',\", \" mode: 'cors',\", \" timeout: 1000,\", \" " +
|
||||
" headers: {\", \" 'Content-Type': 'text/plain'\", \" },\", \" body:" +
|
||||
" probingAddresses[i]\", \" }));\", \"\", \" if (response.status == 200) {\", \" " +
|
||||
" return rootUrl;\", \" }\", \" }\", \" catch (e) { }\", \" }\", \" }\"," +
|
||||
" \"}\", \"\", \"function loadDotnetInteractiveApi() {\", \" probeAddresses([\\\"http://192.168.29.151:1000/\\\", \\\"http:/" +
|
||||
"/127.0.0.1:1000/\\\"])\", \" .then((root) => {\", \" // use probing to find host url and api resources\", \" //" +
|
||||
" load interactive helpers and language services\", \" let dotnetInteractiveRequire = require.config({\", \" context: '4972.M" +
|
||||
"icrosoft.DotNet.Interactive.Http.HttpPort',\", \" paths:\", \" {\", \" 'dotnet-interactive'" +
|
||||
": `${root}resources`\", \" }\", \" }) || require;\", \"\", \" window.dotnetInteractiveRequire" +
|
||||
" = dotnetInteractiveRequire;\", \"\", \" window.configureRequireFromExtension = function(extensionName, extensionCacheBuster) {" +
|
||||
"\", \" let paths = {};\", \" paths[extensionName] = `${root}extensions/${extensionName}/resources/`;\", " +
|
||||
" \" \", \" let internalRequire = require.config({\", \" context: extensionCacheBuster,\"" +
|
||||
", \" paths: paths,\", \" urlArgs: `cacheBuster=${extensionCacheBuster}`\", \" " +
|
||||
" }) || require;\", \"\", \" return internalRequire\", \" };\", \" \", \" d" +
|
||||
"otnetInteractiveRequire([\", \" 'dotnet-interactive/dotnet-interactive'\", \" ],\", \" " +
|
||||
" function (dotnet) {\", \" dotnet.init(window);\", \" },\", \" function (error) " +
|
||||
"{\", \" console.log(error);\", \" }\", \" );\", \" })\", \" ." +
|
||||
"catch(error => {console.log(error);});\", \" }\", \"\", \"// ensure `require` is available globally\", \"if ((typeof(requir" +
|
||||
"e) !== typeof(Function)) || (typeof(require.config) !== typeof(Function))) {\", \" let require_script = document.createElement('script');\"," +
|
||||
" \" require_script.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js');\", \" require_scri" +
|
||||
"pt.setAttribute('type', 'text/javascript');\", \" \", \" \", \" require_script.onload = function() {\", \" loa" +
|
||||
"dDotnetInteractiveApi();\", \" };\", \"\", \" document.getElementsByTagName('head')[0].appendChild(require_script);\", \"" +
|
||||
"}\", \"else {\", \" loadDotnetInteractiveApi();\", \"}\", \"\", \" </script>\", \"</div>\" ] }, " +
|
||||
" \"metadata\": {}, \"output_type\": \"display_data\" }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"" +
|
||||
"Initialize.csx: Loading assemblies from D:\\\\quantconnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] } ], \"source\": [ \"// QuantBook C# Res" +
|
||||
"earch Environment\", \"// For more information see https://www.quantconnect.com/docs/research/overview\", \"#load \\\"./Initialize.csx\\\"\" ]" +
|
||||
" }, { \"cell_type\": \"code\", \"execution_count\": 2, \"id\": \"ceb0015a\", \"metadata\": { \"execution\": { \"iopub.execute_input\"" +
|
||||
": \"2022-03-02T20:44:57.066259Z\", \"iopub.status.busy\": \"2022-03-02T20:44:57.066259Z\", \"iopub.status.idle\": \"2022-03-02T20:44:57.188021" +
|
||||
"Z\", \"shell.execute_reply\": \"2022-03-02T20:44:57.187022Z\" }, \"papermill\": { \"duration\": 0.126763, \"end_time\": \"2022-03-02" +
|
||||
"T20:44:57.188021\", \"exception\": false, \"start_time\": \"2022-03-02T20:44:57.061258\", \"status\": \"completed\" }, \"tags\": [] " +
|
||||
" }, \"outputs\": [ { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.140 TRACE:: Config.GetV" +
|
||||
"alue(): debug-mode - Using default value: False\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"2" +
|
||||
"0220302 20:44:57.141 TRACE:: Config.Get(): Configuration key not found. Key: results-destination-folder - Using default value: \" ] }, { " +
|
||||
" \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.141 TRACE:: Config.Get(): Configuration key not found" +
|
||||
". Key: plugin-directory - Using default value: \" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"2" +
|
||||
"0220302 20:44:57.142 TRACE:: Config.Get(): Configuration key not found. Key: composer-dll-directory - Using default value: \" ] }, { \"n" +
|
||||
"ame\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.142 TRACE:: Composer(): Loading Assemblies from D:\\\\qua" +
|
||||
"ntconnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] }, { \"" +
|
||||
"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.168 TRACE:: Config.Get(): Configuration key not found. K" +
|
||||
"ey: version-id - Using default value: \" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 2" +
|
||||
"0:44:57.169 TRACE:: Config.Get(): Configuration key not found. Key: cache-location - Using default value: ../../../Data/\" ] }, { \"name" +
|
||||
"\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.169 TRACE:: Engine.Main(): LEAN ALGORITHMIC TRADING ENGINE v" +
|
||||
"2.5.0.0 Mode: DEBUG (64bit) Host: P3561-70DPBL3\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"2" +
|
||||
"0220302 20:44:57.171 TRACE:: Engine.Main(): Started 2:14 AM\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\"" +
|
||||
": [ \"20220302 20:44:57.173 TRACE:: Config.Get(): Configuration key not found. Key: lean-manager-type - Using default value: LocalLeanManager\" " +
|
||||
" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"20220302 20:44:57.179 TRACE:: Config.Get(): Configur" +
|
||||
"ation key not found. Key: data-permission-manager - Using default value: DataPermissionManager\" ] }, { \"name\": \"stdout\", \"outp" +
|
||||
"ut_type\": \"stream\", \"text\": [ \"20220302 20:44:57.180 TRACE:: Config.Get(): Configuration key not found. Key: results-destination-folder" +
|
||||
" - Using default value: D:\\\\quantconnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream" +
|
||||
"\", \"text\": [ \"20220302 20:44:57.185 TRACE:: Config.Get(): Configuration key not found. Key: object-store-root - Using default value: ./st" +
|
||||
"orage\" ] } ], \"source\": [ \"#load \\\"./QuantConnect.csx\\\"\", \"\", \"using QuantConnect;\", \"using QuantConnect.Data;\"," +
|
||||
" \"using QuantConnect.Algorithm;\", \"using QuantConnect.Research;\" ] }, { \"cell_type\": \"code\", \"execution_count\": 3, \"id\": \"" +
|
||||
"e81ef855\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2022-03-02T20:44:57.200095Z\", \"iopub.status.busy\": \"2022-03-0" +
|
||||
"2T20:44:57.199026Z\", \"iopub.status.idle\": \"2022-03-02T20:44:58.393456Z\", \"shell.execute_reply\": \"2022-03-02T20:44:58.392457Z\" }, " +
|
||||
" \"papermill\": { \"duration\": 1.200436, \"end_time\": \"2022-03-02T20:44:58.393456\", \"exception\": false, \"start_time\": \"2022-" +
|
||||
"03-02T20:44:57.193020\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [ { \"name\": \"stdout\", \"output_type\":" +
|
||||
" \"stream\", \"text\": [ \"PythonEngine.Initialize(): Runtime.Initialize()...\" ] }, { \"name\": \"stdout\", \"output_type\"" +
|
||||
": \"stream\", \"text\": [ \"Runtime.Initialize(): Py_Initialize...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream" +
|
||||
"\", \"text\": [ \"Runtime.Initialize(): PyEval_InitThreads...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", " +
|
||||
" \"text\": [ \"Runtime.Initialize(): Initialize types...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"te" +
|
||||
"xt\": [ \"Runtime.Initialize(): Initialize types end.\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\":" +
|
||||
" [ \"Runtime.Initialize(): AssemblyManager.Initialize()...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"te" +
|
||||
"xt\": [ \"Runtime.Initialize(): AssemblyManager.UpdatePath()...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", " +
|
||||
" \"text\": [ \"PythonEngine.Initialize(): GetCLRModule()...\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"t" +
|
||||
"ext\": [ \"PythonEngine.Initialize(): clr GetManifestResourceStream...\" ] } ], \"source\": [ \"var qb = new QuantBook();\", \"v" +
|
||||
"ar spy = qb.AddEquity(\\\"SPY\\\");\" ] }, { \"cell_type\": \"code\", \"execution_count\": 4, \"id\": \"06cd5f47\", \"metadata\": { \"e" +
|
||||
"xecution\": { \"iopub.execute_input\": \"2022-03-02T20:44:58.408454Z\", \"iopub.status.busy\": \"2022-03-02T20:44:58.408454Z\", \"iopub.st" +
|
||||
"atus.idle\": \"2022-03-02T20:44:58.449457Z\", \"shell.execute_reply\": \"2022-03-02T20:44:58.449457Z\" }, \"papermill\": { \"duration\":" +
|
||||
" 0.048992, \"end_time\": \"2022-03-02T20:44:58.449457\", \"exception\": false, \"start_time\": \"2022-03-02T20:44:58.400465\", \"statu" +
|
||||
"s\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ \"var startDate = new DateTime(2021,1,1);\", \"var endDate = ne" +
|
||||
"w DateTime(2021,12,31);\", \"var history = qb.History(qb.Securities.Keys, startDate, endDate, Resolution.Daily);\" ] }, { \"cell_type\": \"co" +
|
||||
"de\", \"execution_count\": 5, \"id\": \"37b045b2\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2022-03-02T20:44:58.46348" +
|
||||
"5Z\", \"iopub.status.busy\": \"2022-03-02T20:44:58.463485Z\", \"iopub.status.idle\": \"2022-03-02T20:44:58.611572Z\", \"shell.execute_repl" +
|
||||
"y\": \"2022-03-02T20:44:58.611572Z\" }, \"papermill\": { \"duration\": 0.156117, \"end_time\": \"2022-03-02T20:44:58.611572\", \"exc" +
|
||||
"eption\": false, \"start_time\": \"2022-03-02T20:44:58.455455\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [ { " +
|
||||
" \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"SPY: O: 374.075 H: 374.2245 L: 363.6392 C: 367.5863 V: 94685703\" " +
|
||||
"] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"SPY: O: 366.8487 H: 371.2642 L: 366.8487 C: 370.118 V: " +
|
||||
"54790079\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"SPY: O: 368.4634 H: 375.7495 L: 367.9152" +
|
||||
" C: 372.3307 V: 93254510\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"SPY: O: 374.9521 H: 378." +
|
||||
"65 L: 374.693 C: 377.8626 V: 62474792\" ] }, { \"name\": \"stdout\", \"output_type\": \"stream\", \"text\": [ \"SPY: O: 379" +
|
||||
".3876 H: 380.2448 L: 375.8791 C: 380.0156 V: 63370827\" ] } ], \"source\": [ \"foreach(var slice in history.Take(5)) {\", \" Conso" +
|
||||
"le.WriteLine(slice.Bars[spy.Symbol].ToString());\", \"}\" ] }, { \"cell_type\": \"code\", \"execution_count\": null, \"id\": \"bd2ab8d7\"" +
|
||||
", \"metadata\": { \"papermill\": { \"duration\": 0.006983, \"end_time\": \"2022-03-02T20:44:58.626572\", \"exception\": false, \"" +
|
||||
"start_time\": \"2022-03-02T20:44:58.619589\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [] } ], \"met" +
|
||||
"adata\": { \"kernelspec\": { \"display_name\": \".NET (C#)\", \"language\": \"C#\", \"name\": \".net-csharp\" }, \"language_info\": { \"fil" +
|
||||
"e_extension\": \".cs\", \"mimetype\": \"text/x-csharp\", \"name\": \"C#\", \"pygments_lexer\": \"csharp\", \"version\": \"9.0\" }, \"papermi" +
|
||||
"ll\": { \"default_parameters\": {}, \"duration\": 7.291395, \"end_time\": \"2022-03-02T20:45:01.252292\", \"environment_variables\": {}, \"e" +
|
||||
"xception\": null, \"input_path\": \"D:\\\\quantconnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\\\\BasicTemplateResearchCSharp.ipynb\", \"output_path\": \"" +
|
||||
"D:\\\\quantconnect\\\\Lean\\\\Tests\\\\bin\\\\Debug\\\\BasicTemplateResearchCSharp-output.ipynb\", \"parameters\": {}, \"start_time\": \"2022-03-0" +
|
||||
"2T20:44:53.960897\", \"version\": \"2.3.4\" } }, \"nbformat\": 4, \"nbformat_minor\": 5}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Welcome to The QuantConnect Research Page\n",
|
||||
"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\n",
|
||||
"#### Contribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## QuantBook Basics\n",
|
||||
"\n",
|
||||
"### Start QuantBook\n",
|
||||
"- Add the references and imports\n",
|
||||
"- Create a QuantBook instance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"// QuantBook C# Research Environment\n",
|
||||
"// For more information see https://www.quantconnect.com/docs/research/overview\n",
|
||||
"#load \"./Initialize.csx\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#load \"./QuantConnect.csx\"\n",
|
||||
"\n",
|
||||
"using QuantConnect;\n",
|
||||
"using QuantConnect.Data;\n",
|
||||
"using QuantConnect.Algorithm;\n",
|
||||
"using QuantConnect.Research;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var qb = new QuantBook();\n",
|
||||
"var spy = qb.AddEquity(\"SPY\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"var startDate = new DateTime(2021,1,1);\n",
|
||||
"var endDate = new DateTime(2021,12,31);\n",
|
||||
"var history = qb.History(qb.Securities.Keys, startDate, endDate, Resolution.Daily);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"foreach(var slice in history.Take(5)) {\n",
|
||||
" Console.WriteLine(slice.Bars[spy.Symbol].ToString());\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".NET (C#)",
|
||||
"language": "C#",
|
||||
"name": ".net-csharp"
|
||||
},
|
||||
"language_info": {
|
||||
"file_extension": ".cs",
|
||||
"mimetype": "text/x-csharp",
|
||||
"name": "C#",
|
||||
"pygments_lexer": "csharp",
|
||||
"version": "9.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Tests.Research.RegressionTemplates
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic template framework for regression testing of research notebooks
|
||||
/// </summary>
|
||||
public class BasicTemplateResearchPython : IRegressionResearchDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Expected output from the reading the raw notebook file
|
||||
/// </summary>
|
||||
/// <remarks>Requires to be implemented last in the file <see cref="ResearchRegressionTests.UpdateResearchRegressionOutputInSourceFile"/>
|
||||
/// get should start from next line</remarks>
|
||||
public string ExpectedOutput =>
|
||||
"{ \"cells\": [ { \"cell_type\": \"markdown\", \"id\": \"f5416762\", \"metadata\": { \"papermill\": { \"duration\": 0.001898, \"end_t" +
|
||||
"ime\": \"2024-06-06T22:15:49.572477\", \"exception\": false, \"start_time\": \"2024-06-06T22:15:49.570579\", \"status\": \"completed\" " +
|
||||
"}, \"tags\": [] }, \"source\": [ \"\", \"## Welcome to " +
|
||||
"The QuantConnect Research Page\", \"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\", \"#### Con" +
|
||||
"tribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb\" ] }, { \"cell_type\": \"m" +
|
||||
"arkdown\", \"id\": \"44ed65de\", \"metadata\": { \"papermill\": { \"duration\": 0.001, \"end_time\": \"2024-06-06T22:15:49.574475\", " +
|
||||
" \"exception\": false, \"start_time\": \"2024-06-06T22:15:49.573475\", \"status\": \"completed\" }, \"tags\": [] }, \"source\": [ " +
|
||||
" \"## QuantBook Basics\", \"\", \"### Start QuantBook\", \"- Add the references and imports\", \"- Create a QuantBook instance\" ] }, " +
|
||||
" { \"cell_type\": \"code\", \"execution_count\": 1, \"id\": \"677a4f25\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2" +
|
||||
"024-06-06T22:15:49.577476Z\", \"iopub.status.busy\": \"2024-06-06T22:15:49.577476Z\", \"iopub.status.idle\": \"2024-06-06T22:15:49.581464Z\", " +
|
||||
" \"shell.execute_reply\": \"2024-06-06T22:15:49.581464Z\" }, \"papermill\": { \"duration\": 0.006902, \"end_time\": \"2024-06-06T22:1" +
|
||||
"5:49.582478\", \"exception\": false, \"start_time\": \"2024-06-06T22:15:49.575576\", \"status\": \"completed\" }, \"tags\": [] }, " +
|
||||
" \"outputs\": [], \"source\": [ \"import warnings\", \"warnings.filterwarnings(\\\"ignore\\\")\" ] }, { \"cell_type\": \"code\", \"ex" +
|
||||
"ecution_count\": 2, \"id\": \"e752d505\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2024-06-06T22:15:49.584480Z\", \"" +
|
||||
"iopub.status.busy\": \"2024-06-06T22:15:49.584480Z\", \"iopub.status.idle\": \"2024-06-06T22:15:51.028418Z\", \"shell.execute_reply\": \"2024-" +
|
||||
"06-06T22:15:51.028418Z\" }, \"papermill\": { \"duration\": 1.445955, \"end_time\": \"2024-06-06T22:15:51.029433\", \"exception\": fa" +
|
||||
"lse, \"start_time\": \"2024-06-06T22:15:49.583478\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ " +
|
||||
" \"# Load in our startup script, required to set runtime for PythonNet\", \"%run ./start.py\" ] }, { \"cell_type\": \"code\", \"execution_" +
|
||||
"count\": 3, \"id\": \"08d48a2d\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2024-06-06T22:15:51.032433Z\", \"iopub.st" +
|
||||
"atus.busy\": \"2024-06-06T22:15:51.032433Z\", \"iopub.status.idle\": \"2024-06-06T22:15:51.344980Z\", \"shell.execute_reply\": \"2024-06-06T22" +
|
||||
":15:51.344980Z\" }, \"papermill\": { \"duration\": 0.315568, \"end_time\": \"2024-06-06T22:15:51.345999\", \"exception\": false, " +
|
||||
" \"start_time\": \"2024-06-06T22:15:51.030431\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ \"# Cr" +
|
||||
"eate an instance\", \"qb = QuantBook()\", \"\", \"# Select asset data\", \"spy = qb.AddEquity(\\\"SPY\\\")\" ] }, { \"cell_type\": \"" +
|
||||
"markdown\", \"id\": \"07f707ad\", \"metadata\": { \"papermill\": { \"duration\": 0.000998, \"end_time\": \"2024-06-06T22:15:51.348997\"" +
|
||||
", \"exception\": false, \"start_time\": \"2024-06-06T22:15:51.347999\", \"status\": \"completed\" }, \"tags\": [] }, \"source\":" +
|
||||
" [ \"### Historical Data Requests\", \"\", \"We can use the QuantConnect API to make Historical Data Requests. The data will be presented as " +
|
||||
"multi-index pandas.DataFrame where the first index is the Symbol.\", \"\", \"For more information, please follow the [link](https://www.quantcon" +
|
||||
"nect.com/docs#Historical-Data-Historical-Data-Requests).\" ] }, { \"cell_type\": \"code\", \"execution_count\": 4, \"id\": \"ef440f9e\", \"" +
|
||||
"metadata\": { \"execution\": { \"iopub.execute_input\": \"2024-06-06T22:15:51.352999Z\", \"iopub.status.busy\": \"2024-06-06T22:15:51.35299" +
|
||||
"9Z\", \"iopub.status.idle\": \"2024-06-06T22:15:51.356860Z\", \"shell.execute_reply\": \"2024-06-06T22:15:51.356860Z\" }, \"papermill\":" +
|
||||
" { \"duration\": 0.00689, \"end_time\": \"2024-06-06T22:15:51.357885\", \"exception\": false, \"start_time\": \"2024-06-06T22:15:51.35" +
|
||||
"0995\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ \"startDate = DateTime(2021,1,1)\", \"endDat" +
|
||||
"e = DateTime(2021,12,31)\" ] }, { \"cell_type\": \"code\", \"execution_count\": 5, \"id\": \"33a5fd5d\", \"metadata\": { \"execution\":" +
|
||||
" { \"iopub.execute_input\": \"2024-06-06T22:15:51.361875Z\", \"iopub.status.busy\": \"2024-06-06T22:15:51.360883Z\", \"iopub.status.idle\"" +
|
||||
": \"2024-06-06T22:15:51.453871Z\", \"shell.execute_reply\": \"2024-06-06T22:15:51.453871Z\" }, \"papermill\": { \"duration\": 0.095009, " +
|
||||
" \"end_time\": \"2024-06-06T22:15:51.454884\", \"exception\": false, \"start_time\": \"2024-06-06T22:15:51.359875\", \"status\": \"comp" +
|
||||
"leted\" }, \"scrolled\": true, \"tags\": [] }, \"outputs\": [], \"source\": [ \"# Gets historical data from the subscribed assets, t" +
|
||||
"he last 360 datapoints with daily resolution\", \"h1 = qb.History(qb.Securities.Keys, startDate, endDate, Resolution.Daily)\", \"\", \"if h1." +
|
||||
"shape[0] < 1:\", \" raise Exception(\\\"History request resulted in no data\\\")\" ] }, { \"cell_type\": \"markdown\", \"id\": \"e8f9c90" +
|
||||
"d\", \"metadata\": { \"papermill\": { \"duration\": 0.000996, \"end_time\": \"2024-06-06T22:15:51.457883\", \"exception\": false, " +
|
||||
" \"start_time\": \"2024-06-06T22:15:51.456887\", \"status\": \"completed\" }, \"tags\": [] }, \"source\": [ \"### Indicators\", \"" +
|
||||
"\", \"We can easily get the indicator of a given symbol with QuantBook. \", \"\", \"For all indicators, please checkout QuantConnect Indicato" +
|
||||
"rs [Reference Table](https://www.quantconnect.com/docs#Indicators-Reference-Table)\" ] }, { \"cell_type\": \"code\", \"execution_count\": 6, " +
|
||||
" \"id\": \"dcc7e1f0\", \"metadata\": { \"execution\": { \"iopub.execute_input\": \"2024-06-06T22:15:51.461887Z\", \"iopub.status.busy\": " +
|
||||
"\"2024-06-06T22:15:51.461887Z\", \"iopub.status.idle\": \"2024-06-06T22:15:51.502741Z\", \"shell.execute_reply\": \"2024-06-06T22:15:51.502741" +
|
||||
"Z\" }, \"papermill\": { \"duration\": 0.044872, \"end_time\": \"2024-06-06T22:15:51.503757\", \"exception\": false, \"start_time" +
|
||||
"\": \"2024-06-06T22:15:51.458885\", \"status\": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [ \"# Example with BB" +
|
||||
", it is a datapoint indicator\", \"# Define the indicator\", \"bb = BollingerBands(30, 2)\", \"\", \"# Gets historical data of indicator\"" +
|
||||
", \"bbdf = qb.IndicatorHistory(bb, \\\"SPY\\\", startDate, endDate, Resolution.Daily).data_frame\", \"\", \"# drop undesired fields\", \"bbdf = b" +
|
||||
"bdf.drop('standarddeviation', axis=1)\", \"\", \"if bbdf.shape[0] < 1:\", \" raise Exception(\\\"Bollinger Bands resulted in no data\\\")\"" +
|
||||
" ] }, { \"cell_type\": \"code\", \"execution_count\": null, \"id\": \"3095c061\", \"metadata\": { \"papermill\": { \"duration\": 0." +
|
||||
"001003, \"end_time\": \"2024-06-06T22:15:51.506759\", \"exception\": false, \"start_time\": \"2024-06-06T22:15:51.505756\", \"status\"" +
|
||||
": \"completed\" }, \"tags\": [] }, \"outputs\": [], \"source\": [] } ], \"metadata\": { \"kernelspec\": { \"display_name\": \"Python 3" +
|
||||
" (ipykernel)\", \"language\": \"python\", \"name\": \"python3\" }, \"language_info\": { \"codemirror_mode\": { \"name\": \"ipython\", \"" +
|
||||
"version\": 3 }, \"file_extension\": \".py\", \"mimetype\": \"text/x-python\", \"name\": \"python\", \"nbconvert_exporter\": \"python\", \"" +
|
||||
"pygments_lexer\": \"ipython3\", \"version\": \"3.11.7\" }, \"papermill\": { \"default_parameters\": {}, \"duration\": 6.254067, \"end_time\"" +
|
||||
": \"2024-06-06T22:15:54.143637\", \"environment_variables\": {}, \"exception\": null, \"input_path\": \"D:\\\\QuantConnect\\\\MyLean\\\\Lean\\\\T" +
|
||||
"ests\\\\bin\\\\Debug\\\\Research\\\\RegressionTemplates\\\\BasicTemplateResearchPython.ipynb\", \"output_path\": \"D:\\\\QuantConnect\\\\MyLean\\\\L" +
|
||||
"ean\\\\Tests\\\\bin\\\\Debug\\\\Research\\\\RegressionTemplates\\\\BasicTemplateResearchPython-output.ipynb\", \"parameters\": {}, \"start_time\":" +
|
||||
" \"2024-06-06T22:15:47.889570\", \"version\": \"2.4.0\" } }, \"nbformat\": 4, \"nbformat_minor\": 5}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n",
|
||||
"## Welcome to The QuantConnect Research Page\n",
|
||||
"#### Refer to this page for documentation https://www.quantconnect.com/docs/research/overview#\n",
|
||||
"#### Contribute to this template file https://github.com/QuantConnect/Lean/blob/master/Research/BasicQuantBookTemplate.ipynb"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## QuantBook Basics\n",
|
||||
"\n",
|
||||
"### Start QuantBook\n",
|
||||
"- Add the references and imports\n",
|
||||
"- Create a QuantBook instance"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import warnings\n",
|
||||
"warnings.filterwarnings(\"ignore\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load in our startup script, required to set runtime for PythonNet\n",
|
||||
"%run ./start.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create an instance\n",
|
||||
"qb = QuantBook()\n",
|
||||
"\n",
|
||||
"# Select asset data\n",
|
||||
"spy = qb.AddEquity(\"SPY\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Historical Data Requests\n",
|
||||
"\n",
|
||||
"We can use the QuantConnect API to make Historical Data Requests. The data will be presented as multi-index pandas.DataFrame where the first index is the Symbol.\n",
|
||||
"\n",
|
||||
"For more information, please follow the [link](https://www.quantconnect.com/docs#Historical-Data-Historical-Data-Requests)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"startDate = DateTime(2021,1,1)\n",
|
||||
"endDate = DateTime(2021,12,31)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Gets historical data from the subscribed assets, the last 360 datapoints with daily resolution\n",
|
||||
"h1 = qb.History(qb.Securities.Keys, startDate, endDate, Resolution.Daily)\n",
|
||||
"\n",
|
||||
"if h1.shape[0] < 1:\n",
|
||||
" raise Exception(\"History request resulted in no data\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Indicators\n",
|
||||
"\n",
|
||||
"We can easily get the indicator of a given symbol with QuantBook. \n",
|
||||
"\n",
|
||||
"For all indicators, please checkout QuantConnect Indicators [Reference Table](https://www.quantconnect.com/docs#Indicators-Reference-Table)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Example with BB, it is a datapoint indicator\n",
|
||||
"# Define the indicator\n",
|
||||
"bb = BollingerBands(30, 2)\n",
|
||||
"\n",
|
||||
"# Gets historical data of indicator\n",
|
||||
"bbdf = qb.IndicatorHistory(bb, \"SPY\", startDate, endDate, Resolution.Daily).data_frame\n",
|
||||
"\n",
|
||||
"# drop undesired fields\n",
|
||||
"bbdf = bbdf.drop('standarddeviation', axis=1)\n",
|
||||
"\n",
|
||||
"if bbdf.shape[0] < 1:\n",
|
||||
" raise Exception(\"Bollinger Bands resulted in no data\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
|
||||
namespace QuantConnect.Tests.Research
|
||||
{
|
||||
[TestFixture, Category("ResearchRegressionTests")]
|
||||
public class StartTests
|
||||
{
|
||||
[Test]
|
||||
public void RunStartFromPython()
|
||||
{
|
||||
TestProcess.RunPythonProcess("start.py", out var process);
|
||||
|
||||
Assert.AreEqual(0, process.ExitCode);
|
||||
|
||||
process.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user