850 lines
37 KiB
C#
850 lines
37 KiB
C#
/*
|
|
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
|
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Moq;
|
|
using NUnit.Framework;
|
|
using Python.Runtime;
|
|
using QuantConnect.Algorithm;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Data.Market;
|
|
using QuantConnect.Indicators;
|
|
using QuantConnect.Lean.Engine.DataFeeds;
|
|
using QuantConnect.Lean.Engine.HistoricalData;
|
|
using QuantConnect.Python;
|
|
using QuantConnect.Tests.Engine.DataFeeds;
|
|
using QuantConnect.Tests.Research;
|
|
|
|
namespace QuantConnect.Tests.Algorithm
|
|
{
|
|
[TestFixture]
|
|
public class AlgorithmIndicatorsTests
|
|
{
|
|
private QCAlgorithm _algorithm;
|
|
private Symbol _equity;
|
|
private Symbol _option;
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
_algorithm = new AlgorithmStub();
|
|
var historyProvider = new SubscriptionDataReaderHistoryProvider();
|
|
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null,
|
|
TestGlobals.DataProvider, TestGlobals.DataCacheProvider, TestGlobals.MapFileProvider, TestGlobals.FactorFileProvider,
|
|
null, true, new DataPermissionManager(), _algorithm.ObjectStore, _algorithm.Settings));
|
|
_algorithm.SetHistoryProvider(historyProvider);
|
|
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11, 15, 0, 0));
|
|
_equity = _algorithm.AddEquity("SPY").Symbol;
|
|
_option = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, OptionRight.Call, 450m, new DateTime(2023, 9, 1));
|
|
_algorithm.AddOptionContract(_option);
|
|
_algorithm.Settings.AutomaticIndicatorWarmUp = true;
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorsPassSelectorToWarmUp()
|
|
{
|
|
var mockSelector = new Mock<Func<IBaseData, TradeBar>>();
|
|
mockSelector.Setup(_ => _(It.IsAny<IBaseData>())).Returns<TradeBar>(_ => (TradeBar)_);
|
|
|
|
var indicator = _algorithm.ABANDS(Symbols.SPY, 20, selector: mockSelector.Object);
|
|
|
|
Assert.IsTrue(indicator.IsReady);
|
|
mockSelector.Verify(_ => _(It.IsAny<IBaseData>()), Times.Exactly(indicator.WarmUpPeriod));
|
|
}
|
|
|
|
[Test]
|
|
public void SharpeRatioIndicatorUsesAlgorithmsRiskFreeRateModelSetAfterIndicatorRegistration()
|
|
{
|
|
// Register indicator
|
|
var sharpeRatio = _algorithm.SR(Symbols.SPY, 10);
|
|
|
|
// Setup risk free rate model
|
|
var interestRateProviderMock = new Mock<IRiskFreeInterestRateModel>();
|
|
var reference = new DateTime(2023, 11, 21, 10, 0, 0);
|
|
interestRateProviderMock.Setup(x => x.GetInterestRate(reference)).Verifiable();
|
|
|
|
// Update indicator
|
|
sharpeRatio.Update(new IndicatorDataPoint(Symbols.SPY, reference, 300m));
|
|
|
|
// Our interest rate provider shouldn't have been called yet since it's hasn't been set to the algorithm
|
|
interestRateProviderMock.Verify(x => x.GetInterestRate(reference), Times.Never);
|
|
|
|
// Set the interest rate provider to the algorithm
|
|
_algorithm.SetRiskFreeInterestRateModel(interestRateProviderMock.Object);
|
|
|
|
// Update indicator
|
|
sharpeRatio.Update(new IndicatorDataPoint(Symbols.SPY, reference, 300m));
|
|
|
|
// Our interest rate provider should have been called once
|
|
interestRateProviderMock.Verify(x => x.GetInterestRate(reference), Times.Once);
|
|
}
|
|
|
|
[TestCase("Span", Language.CSharp)]
|
|
[TestCase("Count", Language.CSharp)]
|
|
[TestCase("StartAndEndDate", Language.CSharp)]
|
|
[TestCase("Span", Language.Python)]
|
|
[TestCase("Count", Language.Python)]
|
|
[TestCase("StartAndEndDate", Language.Python)]
|
|
public void IndicatorsDataPoint(string testCase, Language language)
|
|
{
|
|
var period = 10;
|
|
var indicator = new BollingerBands(period, 2);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
int dataCount;
|
|
|
|
IndicatorHistory indicatorValues;
|
|
if (language == Language.CSharp)
|
|
{
|
|
if (testCase == "StartAndEndDate")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, new DateTime(2013, 10, 07), new DateTime(2013, 10, 11), Resolution.Minute);
|
|
}
|
|
else if (testCase == "Span")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, TimeSpan.FromDays(5), Resolution.Minute);
|
|
}
|
|
else
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, (int)(4 * 60 * 6.5), Resolution.Minute);
|
|
}
|
|
// BollingerBands, upper, lower, mid bands, std, band width, percentB, price
|
|
Assert.AreEqual(8, indicatorValues.First().GetStorageDictionary().Count);
|
|
dataCount = indicatorValues.ToList().Count;
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
if (testCase == "StartAndEndDate")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), new DateTime(2013, 10, 07), new DateTime(2013, 10, 11), Resolution.Minute);
|
|
}
|
|
else if (testCase == "Span")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), TimeSpan.FromDays(5), Resolution.Minute);
|
|
}
|
|
else
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), (int)(4 * 60 * 6.5), Resolution.Minute);
|
|
}
|
|
dataCount = QuantBookIndicatorsTests.GetDataFrameLength(indicatorValues.DataFrame);
|
|
}
|
|
}
|
|
|
|
// the historical indicator current values
|
|
Assert.AreEqual(1550 + period, indicatorValues.Current.Count);
|
|
Assert.AreEqual(1550 + period, indicatorValues["current"].Count);
|
|
Assert.AreEqual(indicatorValues.Current, indicatorValues["current"]);
|
|
Assert.IsNull(indicatorValues["NonExisting"]);
|
|
|
|
Assert.IsTrue(indicator.IsReady);
|
|
Assert.AreEqual(1550 + period, dataCount);
|
|
|
|
var lastData = indicatorValues.Current.Last();
|
|
Assert.AreEqual(new DateTime(2013, 10, 10, 16, 0, 0), lastData.EndTime);
|
|
Assert.AreEqual(lastData.EndTime, indicatorValues.Last().EndTime);
|
|
}
|
|
|
|
[TestCase("Span", Language.CSharp)]
|
|
[TestCase("Count", Language.CSharp)]
|
|
[TestCase("StartAndEndDate", Language.CSharp)]
|
|
[TestCase("Span", Language.Python)]
|
|
[TestCase("Count", Language.Python)]
|
|
[TestCase("StartAndEndDate", Language.Python)]
|
|
public void IndicatorsBar(string testCase, Language language)
|
|
{
|
|
var period = 10;
|
|
var indicator = new AverageTrueRange(period);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
|
|
IndicatorHistory indicatorValues;
|
|
int dataCount;
|
|
if (language == Language.CSharp)
|
|
{
|
|
if (testCase == "StartAndEndDate")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, new DateTime(2013, 10, 07), new DateTime(2013, 10, 11), Resolution.Minute);
|
|
}
|
|
else if (testCase == "Span")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, TimeSpan.FromDays(5), Resolution.Minute);
|
|
}
|
|
else
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, _equity, (int)(4 * 60 * 6.5), Resolution.Minute);
|
|
}
|
|
// the TrueRange & the AVGTrueRange
|
|
Assert.AreEqual(2, indicatorValues.First().GetStorageDictionary().Count);
|
|
dataCount = indicatorValues.ToList().Count;
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
if (testCase == "StartAndEndDate")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), new DateTime(2013, 10, 07), new DateTime(2013, 10, 11), Resolution.Minute);
|
|
}
|
|
else if (testCase == "Span")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), TimeSpan.FromDays(5), Resolution.Minute);
|
|
}
|
|
else
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), _equity.ToPython(), (int)(4 * 60 * 6.5), Resolution.Minute);
|
|
}
|
|
dataCount = QuantBookIndicatorsTests.GetDataFrameLength(indicatorValues.DataFrame);
|
|
}
|
|
}
|
|
|
|
// the historical indicator current values
|
|
Assert.AreEqual(1550 + period, indicatorValues.Current.Count);
|
|
Assert.AreEqual(1550 + period, indicatorValues["current"].Count);
|
|
Assert.AreEqual(indicatorValues.Current, indicatorValues["current"]);
|
|
Assert.IsNull(indicatorValues["NonExisting"]);
|
|
|
|
Assert.IsTrue(indicator.IsReady);
|
|
Assert.AreEqual(1550 + period, dataCount);
|
|
|
|
var lastData = indicatorValues.Current.Last();
|
|
Assert.AreEqual(new DateTime(2013, 10, 10, 16, 0, 0), lastData.EndTime);
|
|
Assert.AreEqual(lastData.EndTime, indicatorValues.Last().EndTime);
|
|
}
|
|
|
|
[TestCase(Language.Python)]
|
|
[TestCase(Language.CSharp)]
|
|
public void IndicatorMultiSymbol(Language language)
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new Beta(_equity, referenceSymbol, 10);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
|
|
int dataCount;
|
|
IndicatorHistory indicatorValues;
|
|
if (language == Language.CSharp)
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, new[] { _equity, referenceSymbol }, TimeSpan.FromDays(5));
|
|
Assert.AreEqual(1, indicatorValues.First().GetStorageDictionary().Count);
|
|
dataCount = indicatorValues.ToList().Count;
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator.ToPython(), (new[] { _equity, referenceSymbol }).ToPython(), TimeSpan.FromDays(5));
|
|
dataCount = QuantBookIndicatorsTests.GetDataFrameLength(indicatorValues.DataFrame);
|
|
}
|
|
}
|
|
|
|
// the historical indicator current values
|
|
Assert.AreEqual(1560, indicatorValues.Current.Count);
|
|
Assert.AreEqual(1560, indicatorValues["current"].Count);
|
|
Assert.AreEqual(indicatorValues.Current, indicatorValues["current"]);
|
|
Assert.IsNull(indicatorValues["NonExisting"]);
|
|
|
|
Assert.AreEqual(1560, dataCount);
|
|
Assert.IsTrue(indicator.IsReady);
|
|
}
|
|
|
|
[Test]
|
|
public void BetaCalculation()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new Beta(_equity, referenceSymbol, 10);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
|
|
var indicatorValues = _algorithm.IndicatorHistory(indicator, new[] { _equity, referenceSymbol }, TimeSpan.FromDays(50), Resolution.Daily);
|
|
var lastPoint = indicatorValues.Last();
|
|
Assert.AreEqual(0.477585951081753m, lastPoint.Price);
|
|
Assert.AreEqual(0.477585951081753m, lastPoint.Current.Value);
|
|
Assert.AreEqual(new DateTime(2013, 10, 10, 16, 0, 0), lastPoint.Current.EndTime);
|
|
}
|
|
|
|
[TestCase(Language.Python)]
|
|
[TestCase(Language.CSharp)]
|
|
public void IndicatorsPassingHistory(Language language)
|
|
{
|
|
var period = 10;
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new Beta(_equity, referenceSymbol, period);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
|
|
var history = _algorithm.History(new[] { _equity, referenceSymbol }, TimeSpan.FromDays(5), Resolution.Minute);
|
|
int dataCount;
|
|
if (language == Language.CSharp)
|
|
{
|
|
var indicatorValues = _algorithm.IndicatorHistory(indicator, history);
|
|
Assert.AreEqual(1, indicatorValues.First().GetStorageDictionary().Count);
|
|
dataCount = indicatorValues.Count;
|
|
}
|
|
else
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
var pandasFrame = _algorithm.IndicatorHistory(indicator.ToPython(), history);
|
|
dataCount = QuantBookIndicatorsTests.GetDataFrameLength(pandasFrame.DataFrame);
|
|
}
|
|
}
|
|
Assert.AreEqual((int)(4 * 60 * 6.5) - period, dataCount);
|
|
Assert.IsTrue(indicator.IsReady);
|
|
}
|
|
|
|
[Test]
|
|
public void PythonIndicatorCanBeWarmedUpWithTimespan()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new SimpleMovingAverage("SMA", 100);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
_algorithm.AddEquity(referenceSymbol);
|
|
using (Py.GIL())
|
|
{
|
|
var pythonIndicator = indicator.ToPython();
|
|
_algorithm.WarmUpIndicator(referenceSymbol, pythonIndicator, TimeSpan.FromMinutes(60));
|
|
Assert.IsTrue(pythonIndicator.GetAttr("is_ready").GetAndDispose<bool>());
|
|
Assert.IsTrue(pythonIndicator.GetAttr("samples").GetAndDispose<int>() >= 100);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorCanBeWarmedUpWithTimespan()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
_algorithm.AddEquity(referenceSymbol);
|
|
var indicator = new SimpleMovingAverage("SMA", 100);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
_algorithm.WarmUpIndicator(referenceSymbol, indicator, TimeSpan.FromMinutes(60));
|
|
Assert.IsTrue(indicator.IsReady);
|
|
Assert.IsTrue(indicator.Samples >= 100);
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorCanBeWarmedUpWithoutSymbolInSecurities()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new SimpleMovingAverage("SMA", 100);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
Assert.DoesNotThrow(() => _algorithm.WarmUpIndicator(referenceSymbol, indicator, TimeSpan.FromMinutes(60)));
|
|
Assert.IsTrue(indicator.IsReady);
|
|
Assert.IsTrue(indicator.Samples >= 100);
|
|
}
|
|
|
|
[Test]
|
|
public void PythonCustomIndicatorCanBeWarmedUpWithTimespan()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
_algorithm.AddEquity(referenceSymbol);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
using (Py.GIL())
|
|
{
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
from collections import deque
|
|
|
|
class CustomSimpleMovingAverage(PythonIndicator):
|
|
def __init__(self, name, period):
|
|
super().__init__()
|
|
self.warm_up_period = period
|
|
self.name = name
|
|
self.value = 0
|
|
self.queue = deque(maxlen=period)
|
|
|
|
# Update method is mandatory
|
|
def update(self, input):
|
|
self.queue.appendleft(input.value)
|
|
count = len(self.queue)
|
|
self.value = np.sum(self.queue) / count
|
|
return count == self.queue.maxlen");
|
|
|
|
var customIndicator = testModule.GetAttr("CustomSimpleMovingAverage").Invoke("custom".ToPython(), 100.ToPython());
|
|
_algorithm.WarmUpIndicator(referenceSymbol, customIndicator, TimeSpan.FromMinutes(60));
|
|
Assert.IsTrue(customIndicator.GetAttr("is_ready").GetAndDispose<bool>());
|
|
Assert.IsTrue(customIndicator.GetAttr("samples").GetAndDispose<int>() >= 100);
|
|
}
|
|
}
|
|
|
|
[TestCase("count")]
|
|
[TestCase("StartAndEndDate")]
|
|
public void IndicatorUpdatedWithSymbol(string testCase)
|
|
{
|
|
var time = new DateTime(2014, 06, 07);
|
|
|
|
var put = Symbols.CreateOptionSymbol("AAPL", OptionRight.Call, 250m, new DateTime(2016, 01, 15));
|
|
var call = Symbols.CreateOptionSymbol("AAPL", OptionRight.Put, 250m, new DateTime(2016, 01, 15));
|
|
var indicator = new Delta(option: put, mirrorOption: call, optionModel: OptionPricingModelType.BlackScholes, ivModel: OptionPricingModelType.BlackScholes);
|
|
_algorithm.SetDateTime(time);
|
|
|
|
IndicatorHistory indicatorValues;
|
|
if (testCase == "count")
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, new[] { put, call, put.Underlying }, 60 * 10, resolution: Resolution.Minute);
|
|
}
|
|
else
|
|
{
|
|
indicatorValues = _algorithm.IndicatorHistory(indicator, new[] { put, call, put.Underlying }, TimeSpan.FromMinutes(60 * (10 + 2)), resolution: Resolution.Minute);
|
|
}
|
|
|
|
Assert.IsTrue(indicator.IsReady);
|
|
Assert.AreEqual(0.9942989m, indicator.Current.Value);
|
|
Assert.AreEqual(0.3514844m, indicator.ImpliedVolatility.Current.Value);
|
|
Assert.AreEqual(390, indicatorValues.Count);
|
|
|
|
var lastData = indicatorValues.Current.Last();
|
|
Assert.AreEqual(new DateTime(2014, 6, 6, 16, 0, 0), lastData.EndTime);
|
|
Assert.AreEqual(lastData.EndTime, indicatorValues.Last().EndTime);
|
|
}
|
|
|
|
[TestCase(1)]
|
|
[TestCase(2)]
|
|
public void PythonCustomIndicator(int testCases)
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
using (Py.GIL())
|
|
{
|
|
PyModule module;
|
|
if (testCases == 1)
|
|
{
|
|
module = PyModule.FromString("PythonCustomIndicator",
|
|
@"
|
|
from AlgorithmImports import *
|
|
class GoodCustomIndicator(PythonIndicator):
|
|
def __init__(self):
|
|
self.Value = 0
|
|
def Update(self, input):
|
|
self.Value = input.Value
|
|
return True");
|
|
}
|
|
else
|
|
{
|
|
module = PyModule.FromString("PythonCustomIndicator",
|
|
@"
|
|
from AlgorithmImports import *
|
|
class GoodCustomIndicator:
|
|
def __init__(self):
|
|
self.IsReady = True
|
|
self.Value = 0
|
|
def Update(self, input):
|
|
self.Value = input.Value
|
|
return True");
|
|
}
|
|
|
|
var goodIndicator = module.GetAttr("GoodCustomIndicator").Invoke();
|
|
var pandasFrame = _algorithm.IndicatorHistory(goodIndicator, _equity.ToPython(), TimeSpan.FromDays(5), Resolution.Minute);
|
|
var dataCount = QuantBookIndicatorsTests.GetDataFrameLength(pandasFrame.DataFrame);
|
|
|
|
Assert.IsTrue((bool)((dynamic)goodIndicator).IsReady);
|
|
Assert.AreEqual((int)(4 * 60 * 6.5), dataCount);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SpecificTTypeIndicator()
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new CustomIndicator();
|
|
var result = _algorithm.IndicatorHistory(indicator, referenceSymbol, TimeSpan.FromDays(1), Resolution.Minute).ToList();
|
|
Assert.AreEqual(390, result.Count);
|
|
Assert.IsTrue(indicator.IsReady);
|
|
}
|
|
|
|
[TestCase("span", 1)]
|
|
[TestCase("count", 1)]
|
|
[TestCase("span", 2)]
|
|
[TestCase("count", 2)]
|
|
public void SMAAssertDataCount(string testCase, int requestCount)
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new SimpleMovingAverage(10);
|
|
IndicatorHistory result;
|
|
if (testCase == "span")
|
|
{
|
|
result = _algorithm.IndicatorHistory(indicator, referenceSymbol, TimeSpan.FromDays(requestCount), Resolution.Daily);
|
|
}
|
|
else
|
|
{
|
|
result = _algorithm.IndicatorHistory(indicator, referenceSymbol, requestCount, Resolution.Daily);
|
|
}
|
|
Assert.AreEqual(requestCount, result.Count);
|
|
Assert.AreEqual(10 + requestCount - 1, indicator.Samples);
|
|
Assert.IsTrue(indicator.IsReady);
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorHistoryIsSupportedInPythonForOptionsIndicators([Range(1, 4)] int overload, [Values] bool useMirrorContract)
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2014, 06, 07));
|
|
|
|
var contract = Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Call, 505, new DateTime(2014, 6, 27));
|
|
var mirrorContract = useMirrorContract
|
|
? Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Put, 505, new DateTime(2014, 6, 27))
|
|
: null;
|
|
var underlying = contract.Underlying;
|
|
|
|
var indicator = new ImpliedVolatility(contract, optionModel: OptionPricingModelType.BlackScholes, mirrorOption: mirrorContract);
|
|
|
|
using var _ = Py.GIL();
|
|
|
|
using var pyIndicator = indicator.ToPython();
|
|
var symbols = useMirrorContract ? new[] { contract, mirrorContract, underlying } : new[] { contract, underlying };
|
|
using var pySymbols = symbols.ToPyListUnSafe();
|
|
|
|
var symbolsHistory = overload != 4
|
|
? null
|
|
: _algorithm.History(symbols, TimeSpan.FromDays(2), Resolution.Minute);
|
|
|
|
var indicatorHistory = overload switch
|
|
{
|
|
1 => _algorithm.IndicatorHistory(pyIndicator, pySymbols, TimeSpan.FromDays(2), Resolution.Minute),
|
|
2 => _algorithm.IndicatorHistory(pyIndicator, pySymbols, 60 * 24 * 2, Resolution.Minute),
|
|
3 => _algorithm.IndicatorHistory(pyIndicator, pySymbols, new DateTime(2014, 6, 6), new DateTime(2014, 6, 7), Resolution.Minute),
|
|
4 => _algorithm.IndicatorHistory(pyIndicator, symbolsHistory),
|
|
_ => throw new ArgumentOutOfRangeException(nameof(overload), "Invalid overload")
|
|
};
|
|
|
|
Assert.AreEqual(390, indicatorHistory.Count);
|
|
|
|
using var dataFrame = indicatorHistory.DataFrame;
|
|
Assert.AreEqual(390, dataFrame.GetAttr("shape")[0].GetAndDispose<int>());
|
|
// Assert dataframe column names are current, price, oppositeprice and underlyingprice
|
|
var columns = dataFrame.GetAttr("columns").InvokeMethod<List<string>>("tolist");
|
|
var expectedColumns = new[] { "current", "price", "oppositeprice", "underlyingprice", "theoreticalprice" };
|
|
CollectionAssert.AreEquivalent(expectedColumns, columns);
|
|
}
|
|
|
|
[Test]
|
|
public void WarmUpIndicatorIsSupportedInPythonForOptionsIndicators([Values(1, 2)] int overload, [Values] bool useMirrorContract)
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2014, 06, 07));
|
|
|
|
var contract = Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Call, 505, new DateTime(2014, 07, 19));
|
|
var mirrorContract = useMirrorContract
|
|
? Symbol.CreateOption("AAPL", Market.USA, OptionStyle.American, OptionRight.Put, 505, new DateTime(2014, 07, 19))
|
|
: null;
|
|
var underlying = contract.Underlying;
|
|
|
|
var indicator = new ImpliedVolatility(contract, optionModel: OptionPricingModelType.BlackScholes, mirrorOption: mirrorContract);
|
|
|
|
using var _ = Py.GIL();
|
|
|
|
using var pyIndicator = indicator.ToPython();
|
|
var symbols = useMirrorContract ? new[] { contract, mirrorContract, underlying } : new[] { contract, underlying };
|
|
using var pySymbols = symbols.ToPyListUnSafe();
|
|
|
|
switch (overload)
|
|
{
|
|
case 1:
|
|
_algorithm.WarmUpIndicator(pySymbols, pyIndicator, TimeSpan.FromDays(1));
|
|
break;
|
|
|
|
case 2:
|
|
_algorithm.WarmUpIndicator(pySymbols, pyIndicator, Resolution.Daily);
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(overload), "Invalid overload");
|
|
}
|
|
|
|
Assert.IsTrue(indicator.IsReady);
|
|
|
|
if (useMirrorContract)
|
|
{
|
|
Assert.IsNotNull(indicator.OppositePrice);
|
|
}
|
|
else
|
|
{
|
|
Assert.IsNull(indicator.OppositePrice);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorHistoryDataFrameDoesNotCointainDefaultDateTimeIndex()
|
|
{
|
|
var pandasConverter = new PandasConverter();
|
|
var indicatorsDataPointPerProperty = new List<InternalIndicatorValues>
|
|
{
|
|
new InternalIndicatorValues(null, "current")
|
|
};
|
|
var lazyDataFrame = new Lazy<PyObject>(
|
|
() => pandasConverter.GetIndicatorDataFrame(indicatorsDataPointPerProperty.Select(x => new KeyValuePair<string, List<IndicatorDataPoint>>(x.Name, x.Values))),
|
|
isThreadSafe: false);
|
|
var indicatorHistory = new IndicatorHistory(new List<IndicatorDataPoints>(), indicatorsDataPointPerProperty, lazyDataFrame);
|
|
indicatorHistory.Current.Add(new IndicatorDataPoint(Symbols.SPY, new DateTime(2018, 1, 1), 100));
|
|
indicatorHistory.Current.Add(new IndicatorDataPoint(Symbols.SPY, new DateTime(2018, 1, 2), 100));
|
|
// Force insertion of a default(DateTime) timestamp to ensure it's excluded from the DataFrame
|
|
indicatorHistory.Current.Add(new IndicatorDataPoint(Symbols.SPY, default, 100));
|
|
|
|
dynamic dataframe = indicatorHistory.DataFrame;
|
|
using (Py.GIL())
|
|
{
|
|
var index = dataframe.index;
|
|
foreach (dynamic time in index)
|
|
{
|
|
DateTime timestamp = (DateTime)time.AsManagedObject(typeof(DateTime));
|
|
// Ensure that no timestamp in the DataFrame index is equal to default(DateTime)
|
|
Assert.AreNotEqual(default(DateTime), timestamp);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void IndicatorHistoryShouldIncludeValidIndicatorsAndExplicitlyIncludedProperties()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
var indicator = new TestIndicator();
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
var history = _algorithm.History(new[] { referenceSymbol }, TimeSpan.FromDays(5), Resolution.Minute);
|
|
var indicatorValues = _algorithm.IndicatorHistory(indicator, history);
|
|
|
|
dynamic dataframe = indicatorValues.DataFrame;
|
|
using (Py.GIL())
|
|
{
|
|
var index = dataframe.index;
|
|
var columns = dataframe.columns;
|
|
var expectedColumns = new List<string> { "smaprop", "genericprop", "current", "nongenericprop", "counter", "indicatortype", "description" };
|
|
var columnsCount = 0;
|
|
foreach (dynamic col in columns)
|
|
{
|
|
columnsCount++;
|
|
var columnName = (string)col.AsManagedObject(typeof(string));
|
|
Assert.IsTrue(expectedColumns.Contains(columnName));
|
|
}
|
|
Assert.AreEqual(expectedColumns.Count, columnsCount);
|
|
|
|
// Validate that the number of rows in the "current" column
|
|
// matches the row count in "counter", "indicatortype", and "description" columns
|
|
// Get the number of rows in each relevant column using __len__()
|
|
int currentLen = dataframe["current"].InvokeMethod("__len__").As<int>();
|
|
int counterLen = dataframe["counter"].InvokeMethod("__len__").As<int>();
|
|
int indicatorTypeLen = dataframe["indicatortype"].InvokeMethod("__len__").As<int>();
|
|
int descriptionLen = dataframe["description"].InvokeMethod("__len__").As<int>();
|
|
|
|
// Assert that all lengths match the length of "current"
|
|
Assert.AreEqual(currentLen, counterLen);
|
|
Assert.AreEqual(currentLen, indicatorTypeLen);
|
|
Assert.AreEqual(currentLen, descriptionLen);
|
|
}
|
|
}
|
|
|
|
private enum TestIndicatorType
|
|
{
|
|
TypeA,
|
|
TypeB
|
|
}
|
|
|
|
private class TestIndicator : IndicatorBase<QuoteBar>, IIndicatorWarmUpPeriodProvider
|
|
{
|
|
[PandasIgnore]
|
|
public Identity IgnoredProp { get; }
|
|
public SimpleMovingAverage SmaProp { get; }
|
|
public IndicatorBase<IndicatorDataPoint> GenericProp { get; }
|
|
public IndicatorBase NonGenericProp { get; }
|
|
public int Counter { get; set; }
|
|
public TestIndicatorType IndicatorType { get; set; }
|
|
public string Description { get; set; }
|
|
private bool _isReady;
|
|
public int WarmUpPeriod => 1;
|
|
public override bool IsReady => _isReady;
|
|
public TestIndicator() : base("Pepe")
|
|
{
|
|
SmaProp = new SimpleMovingAverage("SMA", 5);
|
|
GenericProp = new Identity("Generic");
|
|
IgnoredProp = new Identity("Ignored");
|
|
NonGenericProp = new Identity("NoGeneric");
|
|
}
|
|
protected override decimal ComputeNextValue(QuoteBar input)
|
|
{
|
|
Counter++;
|
|
_isReady = true;
|
|
return input.Ask.High;
|
|
}
|
|
}
|
|
|
|
private class CustomIndicator : IndicatorBase<QuoteBar>, IIndicatorWarmUpPeriodProvider
|
|
{
|
|
private bool _isReady;
|
|
public int WarmUpPeriod => 1;
|
|
public override bool IsReady => _isReady;
|
|
public CustomIndicator() : base("Pepe")
|
|
{ }
|
|
protected override decimal ComputeNextValue(QuoteBar input)
|
|
{
|
|
_isReady = true;
|
|
return input.Ask.High;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void SupportsConversionToIndicatorBaseBaseDataCorrectly([Range(1, 6)] int scenario)
|
|
{
|
|
const string code = @"
|
|
from AlgorithmImports import *
|
|
from QuantConnect.Indicators import *
|
|
|
|
def create_intraday_vwap_indicator(name):
|
|
return IntradayVwap(name)
|
|
def create_consolidator():
|
|
return TradeBarConsolidator(timedelta(minutes=1))
|
|
";
|
|
|
|
using (Py.GIL())
|
|
{
|
|
var module = PyModule.FromString(Guid.NewGuid().ToString(), code);
|
|
string name = "test";
|
|
|
|
// Creates the IntradayVWAP (IndicatorBase<BaseData>)
|
|
var indicator = module.GetAttr("create_intraday_vwap_indicator").Invoke(name.ToPython());
|
|
var consolidator = module.GetAttr("create_consolidator").Invoke();
|
|
var SymbolList = new List<Symbol>
|
|
{
|
|
Symbols.SPY,
|
|
Symbols.IBM,
|
|
};
|
|
|
|
// Tests different scenarios based on the "scenario" parameter
|
|
switch (scenario)
|
|
{
|
|
case 1:
|
|
Assert.DoesNotThrow(() => _algorithm.RegisterIndicator(Symbols.SPY, indicator, consolidator));
|
|
break;
|
|
case 2:
|
|
Assert.DoesNotThrow(() => _algorithm.WarmUpIndicator(SymbolList.ToPyList(), indicator));
|
|
break;
|
|
case 3:
|
|
Assert.DoesNotThrow(() => _algorithm.WarmUpIndicator(SymbolList.ToPyList(), indicator, TimeSpan.FromDays(1)));
|
|
break;
|
|
case 4:
|
|
Assert.DoesNotThrow(() => _algorithm.IndicatorHistory(indicator, SymbolList.ToPyList(), 10));
|
|
break;
|
|
case 5:
|
|
Assert.DoesNotThrow(() => _algorithm.IndicatorHistory(indicator, SymbolList.ToPyList(), new DateTime(2014, 6, 6), new DateTime(2014, 6, 7)));
|
|
break;
|
|
case 6:
|
|
var symbolsHistory = _algorithm.History(SymbolList, TimeSpan.FromDays(2), Resolution.Minute);
|
|
Assert.DoesNotThrow(() => _algorithm.IndicatorHistory(indicator, symbolsHistory));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void IchimokuIndicatorHistoryDataFrameDoesNotContainNaNInCurrentColumn()
|
|
{
|
|
var referenceSymbol = Symbol.Create("IBM", SecurityType.Equity, Market.USA);
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
var history = _algorithm.History(new[] { referenceSymbol }, TimeSpan.FromDays(5), Resolution.Minute);
|
|
var indicator = new IchimokuKinkoHyo(9, 26, 17, 52, 26, 26);
|
|
var indicatorValues = _algorithm.IndicatorHistory(indicator, history);
|
|
indicatorValues.Current.Add(new IndicatorDataPoint(referenceSymbol, default(DateTime), 1));
|
|
|
|
dynamic dataframe = indicatorValues.DataFrame;
|
|
using (Py.GIL())
|
|
{
|
|
var currentColumn = dataframe["current"];
|
|
foreach (PyObject value in currentColumn)
|
|
{
|
|
double doubleValue = value.As<double>();
|
|
Assert.IsFalse(double.IsNaN(doubleValue));
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void CanRegisterIndicatorsWithPythonSelector()
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
_algorithm.Settings.AutomaticIndicatorWarmUp = true;
|
|
var symbol = _algorithm.AddEquity("SPY").Symbol;
|
|
|
|
using var _ = Py.GIL();
|
|
var testModule = PyModule.FromString("testModule",
|
|
@"
|
|
from AlgorithmImports import *
|
|
|
|
class LastInputTracker:
|
|
last_input = None
|
|
|
|
def selector(bar):
|
|
LastInputTracker.last_input = bar
|
|
return bar.close
|
|
|
|
def get_indicator(algo, symbol):
|
|
indicator = SimpleMovingAverage(10)
|
|
algo.register_indicator(symbol, indicator, Resolution.MINUTE, selector=selector)
|
|
algo.warm_up_indicator(symbol, indicator, selector=selector)
|
|
return indicator
|
|
");
|
|
|
|
using var pyAlgo = _algorithm.ToPython();
|
|
using var pySymbol = symbol.ToPython();
|
|
var indicator = testModule.GetAttr("get_indicator").Invoke(pyAlgo, pySymbol).GetAndDispose<IndicatorBase>();
|
|
|
|
// The indicator should have been updated during the warm-up period
|
|
var lastInput = testModule.GetAttr("LastInputTracker").GetAttr("last_input").GetAndDispose<TradeBar>();
|
|
Assert.IsNotNull(lastInput);
|
|
}
|
|
|
|
// Some specific indicator helper methods tests
|
|
[TestCase("abands", "symbol, 2", false)]
|
|
[TestCase("ad", "symbol", false)]
|
|
[TestCase("adosc", "symbol, 2, 3", false)]
|
|
[TestCase("sma", "symbol, 3", true)]
|
|
[TestCase("ema", "symbol, 3", true)]
|
|
[TestCase("arima", "symbol, 1, 1, 1, 10", true)]
|
|
public void IndicatorHelperMethodsWorkWithPythonSelectors(string indicatorName, string indicatorArgs, bool decimalSelector)
|
|
{
|
|
_algorithm.SetDateTime(new DateTime(2013, 10, 11));
|
|
_algorithm.Settings.AutomaticIndicatorWarmUp = true;
|
|
|
|
var symbol = _algorithm.AddEquity("SPY").Symbol;
|
|
var selector = decimalSelector ? "decimal_selector" : "selector";
|
|
|
|
using var _ = Py.GIL();
|
|
var testModule = PyModule.FromString("testModule",
|
|
@$"
|
|
from AlgorithmImports import *
|
|
|
|
class LastInputTracker:
|
|
last_input = None
|
|
|
|
def selector(bar):
|
|
LastInputTracker.last_input = bar
|
|
return bar
|
|
|
|
def decimal_selector(bar):
|
|
LastInputTracker.last_input = bar
|
|
return bar.close
|
|
|
|
def get_indicator(algo, symbol):
|
|
return algo.{indicatorName}({indicatorArgs}, selector={selector})
|
|
");
|
|
|
|
using var pyAlgo = _algorithm.ToPython();
|
|
using var pySymbol = symbol.ToPython();
|
|
var indicator = testModule.GetAttr("get_indicator").Invoke(pyAlgo, pySymbol).GetAndDispose<IndicatorBase>();
|
|
|
|
// The indicator should have been updated during the warm-up period
|
|
var lastInput = testModule.GetAttr("LastInputTracker").GetAttr("last_input").GetAndDispose<TradeBar>();
|
|
Assert.IsNotNull(lastInput);
|
|
}
|
|
}
|
|
}
|