chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AbsolutePriceOscillatorTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new AbsolutePriceOscillator(5, 10);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_apo.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "APO_5_10"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AccelerationBandsTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AccelerationBands(period: 20, width: 4m);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_acceleration_bands_20_4.txt";
|
||||
|
||||
protected override string TestColumnName => "MiddleBand";
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataLowerBand()
|
||||
{
|
||||
var abands = CreateIndicator();
|
||||
TestHelper.TestIndicator(
|
||||
abands,
|
||||
"spy_acceleration_bands_20_4.txt",
|
||||
"LowerBand",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ((AccelerationBands) ind).LowerBand.Current.Value,
|
||||
delta: 1e-4, message: "Lower band test fail.")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataUpperBand()
|
||||
{
|
||||
var abands = CreateIndicator();
|
||||
TestHelper.TestIndicator(
|
||||
abands,
|
||||
"spy_acceleration_bands_20_4.txt",
|
||||
"UpperBand",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ((AccelerationBands) ind).UpperBand.Current.Value,
|
||||
delta: 1e-4, message: "Upper band test fail.")
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WorksWithLowValues()
|
||||
{
|
||||
var abands = CreateIndicator();
|
||||
var random = new Random();
|
||||
var time = DateTime.UtcNow;
|
||||
for(int i = 0; i < 40; i++)
|
||||
{
|
||||
var value = random.NextDouble() * 0.000000000000000000000000000001;
|
||||
Assert.DoesNotThrow(() => abands.Update(new TradeBar { High = (decimal)value, Low = (decimal)value, Time = time.AddDays(i)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class AccumulationDistributionOscillatorTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
return new AccumulationDistributionOscillator(3, 10);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_ad_osc.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "AdOsc_3_10"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AccumulationDistributionTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
return new AccumulationDistribution("AD");
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_ad.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "AD"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AdvanceDeclineDifferenceTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var adDifference = new AdvanceDeclineDifference("test_name");
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(adDifference.AddStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
adDifference.AddStock(Symbols.AAPL);
|
||||
adDifference.AddStock(Symbols.IBM);
|
||||
adDifference.AddStock(Symbols.GOOG);
|
||||
RenkoBarSize = 5000000;
|
||||
}
|
||||
return adDifference;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL, Symbols.IBM];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var adDifference = (AdvanceDeclineDifference)CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(1m, adDifference.Current.Value);
|
||||
adDifference.Reset();
|
||||
adDifference.RemoveStock(Symbols.GOOG);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(0m, adDifference.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var adDifference = (AdvanceDeclineDifference)CreateIndicator();
|
||||
adDifference.AddStock(Symbols.MSFT);
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Time = reference.AddMinutes(2) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(0m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 3, Time = reference.AddMinutes(3) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Time = reference.AddMinutes(3) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 2, Time = reference.AddMinutes(3) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(1m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Time = reference.AddMinutes(4) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 2, Time = reference.AddMinutes(4) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Time = reference.AddMinutes(4) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 2, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(2m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(5) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 3, Time = reference.AddMinutes(5) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(2m, adDifference.Current.Value);
|
||||
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(6) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 4, Time = reference.AddMinutes(6) });
|
||||
adDifference.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 5, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(2m, adDifference.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(1m, indicator.Current.Value);
|
||||
Assert.AreEqual(6, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(1m, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<TradeBar>)
|
||||
{
|
||||
var aaplRenkoConsolidator = new RenkoConsolidator(10000m);
|
||||
aaplRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var googRenkoConsolidator = new RenkoConsolidator(100000m);
|
||||
googRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var ibmRenkoConsolidator = new RenkoConsolidator(10000m);
|
||||
ibmRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AAPL")
|
||||
{
|
||||
aaplRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else if (tradebar.Symbol.Value == "GOOG")
|
||||
{
|
||||
googRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ibmRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveRenkoBars(indicator);
|
||||
aaplRenkoConsolidator.Dispose();
|
||||
googRenkoConsolidator.Dispose();
|
||||
ibmRenkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<TradeBar>)
|
||||
{
|
||||
var aaplRenkoConsolidator = new VolumeRenkoConsolidator(10000000m);
|
||||
aaplRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var googRenkoConsolidator = new VolumeRenkoConsolidator(500000m);
|
||||
googRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var ibmRenkoConsolidator = new VolumeRenkoConsolidator(500000m);
|
||||
ibmRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AAPL")
|
||||
{
|
||||
aaplRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else if (tradebar.Symbol.Value == "GOOG")
|
||||
{
|
||||
googRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ibmRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(indicator);
|
||||
aaplRenkoConsolidator.Dispose();
|
||||
googRenkoConsolidator.Dispose();
|
||||
ibmRenkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// indicator is not ready yet
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is ready
|
||||
// The last update used Symbol.GOOG, so the indicator's current Symbol should be GOOG
|
||||
Assert.AreEqual(Symbols.GOOG, indicator.Current.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected override string TestFileName => "arms_data.txt";
|
||||
|
||||
protected override string TestColumnName => "A/D Difference";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AdvanceDeclineRatioTests : AdvanceDeclineDifferenceTests
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var adr = new AdvanceDeclineRatio("test_name");
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(adr.AddStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
adr.Add(Symbols.AAPL);
|
||||
adr.Add(Symbols.IBM);
|
||||
adr.Add(Symbols.GOOG);
|
||||
RenkoBarSize = 5000000;
|
||||
}
|
||||
return adr;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var adr = (AdvanceDeclineRatio)CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(2m, adr.Current.Value);
|
||||
adr.Reset();
|
||||
adr.Remove(Symbols.GOOG);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(1m, adr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var adr = (AdvanceDeclineRatio)CreateIndicator();
|
||||
adr.Add(Symbols.MSFT);
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 3, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 2, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(2m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 2, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 2, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(1m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 3, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(1m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 4, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 5, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(1m, adr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(2m, indicator.Current.Value);
|
||||
Assert.AreEqual(6, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(2m, indicator.Current.Value);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "arms_data.txt";
|
||||
|
||||
protected override string TestColumnName => "A/D Ratio";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AdvanceDeclineVolumeRatioTests : AdvanceDeclineDifferenceTests
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var advr = new AdvanceDeclineVolumeRatio("test_name");
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(advr.AddStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
advr.Add(Symbols.AAPL);
|
||||
advr.Add(Symbols.IBM);
|
||||
advr.Add(Symbols.GOOG);
|
||||
RenkoBarSize = 5000000;
|
||||
}
|
||||
return advr;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var advr = (AdvanceDeclineVolumeRatio)CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, advr.Current.Value);
|
||||
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(2m, advr.Current.Value);
|
||||
advr.Reset();
|
||||
advr.Remove(Symbols.AAPL);
|
||||
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, advr.Current.Value);
|
||||
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
advr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 150, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(1.5m, advr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var adr = (AdvanceDeclineVolumeRatio)CreateIndicator();
|
||||
adr.Add(Symbols.MSFT);
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 3, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 200, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(1m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 2, Volume = 250, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 2, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 2, Volume = 150, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(5m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 3, Volume = 220, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 2, Volume = 120, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 100, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(5m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 70, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 3, Volume = 200, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 10, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(5m, adr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 20, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 5, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 10, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(6m, indicator.Current.Value);
|
||||
Assert.AreEqual(6, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 20, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 5, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 10, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(6m, indicator.Current.Value);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "arms_data.txt";
|
||||
|
||||
protected override string TestColumnName => "A/D Volume Ratio";
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AlphaIndicatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override string TestFileName => "alpha_indicator_datatest.csv";
|
||||
|
||||
protected override string TestColumnName => "Alpha";
|
||||
|
||||
private DateTime _reference = new DateTime(2020, 1, 1);
|
||||
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
Symbol symbolA = "AMZN 2T";
|
||||
Symbol symbolB = "SPX 2T";
|
||||
if (SymbolList.Count > 1)
|
||||
{
|
||||
symbolA = SymbolList[0];
|
||||
symbolB = SymbolList[1];
|
||||
}
|
||||
#pragma warning disable CS0618
|
||||
var indicator = new Alpha("testAlphaIndicator", symbolA, symbolB, 5);
|
||||
#pragma warning restore CS0618
|
||||
return indicator;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.IBM, Symbols.SPY, period);
|
||||
var warmUpPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!warmUpPeriod.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
// warmup period is 5 + 1
|
||||
for (var i = 1; i <= warmUpPeriod.Value; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
|
||||
if (i < warmUpPeriod.Value)
|
||||
{
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * warmUpPeriod.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WorksWithLowValues()
|
||||
{
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.IBM, Symbols.SPY, 10);
|
||||
var warmUpPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
var random = new Random();
|
||||
var time = DateTime.UtcNow;
|
||||
for (int i = 0; i < 2 * warmUpPeriod; i++)
|
||||
{
|
||||
var value = (decimal)(random.NextDouble() * 0.000000000000000000000000000001);
|
||||
Assert.DoesNotThrow(() => indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = value, High = value, Volume = 100, Close = value, Time = _reference.AddDays(1 + i) }));
|
||||
Assert.DoesNotThrow(() => indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = value, High = value, Volume = 100, Close = value, Time = _reference.AddDays(1 + i) }));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var firstRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
var secondRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
firstRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AMZN")
|
||||
{
|
||||
firstRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstRenkoConsolidator.Dispose();
|
||||
secondRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var firstVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000);
|
||||
var secondVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000000);
|
||||
firstVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AMZN")
|
||||
{
|
||||
firstVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstVolumeRenkoConsolidator.Dispose();
|
||||
secondVolumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AcceptsQuoteBarsAsInput()
|
||||
{
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.IBM, Ask = new Bar(1, 2, 1, 500), Bid = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.SPY, Ask = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EqualAlphaValue()
|
||||
{
|
||||
int period = 5;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, period);
|
||||
|
||||
for (int i = 0; i <= period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < period)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0.0032053150, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RiskFreeRate()
|
||||
{
|
||||
decimal riskFreeRate = 0.0002m;
|
||||
int period = 5;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, period, riskFreeRate);
|
||||
|
||||
for (int i = 0; i <= period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < period)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0.0030959108, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RiskFreeRate252()
|
||||
{
|
||||
int alphaPeriod = 1;
|
||||
int betaPeriod = 252;
|
||||
decimal riskFreeRate = 0.0025m;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, alphaPeriod, betaPeriod, riskFreeRate);
|
||||
|
||||
for (int i = 0; i <= betaPeriod; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < betaPeriod)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(-0.0000620852, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoRiskFreeRate252()
|
||||
{
|
||||
int alphaPeriod = 1;
|
||||
int betaPeriod = 252;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, alphaPeriod, betaPeriod);
|
||||
|
||||
for (int i = 0; i <= betaPeriod; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < betaPeriod)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0.0008518139, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstantZeroRiskFreeRateModel()
|
||||
{
|
||||
int alphaPeriod = 1;
|
||||
int betaPeriod = 252;
|
||||
IRiskFreeInterestRateModel riskFreeRateModel = new ConstantRiskFreeRateInterestRateModel(0.0m);
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, alphaPeriod, betaPeriod, riskFreeRateModel);
|
||||
|
||||
for (int i = 0; i <= betaPeriod; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < betaPeriod)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0.0008518139, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConstantRiskFreeRateModel()
|
||||
{
|
||||
int alphaPeriod = 1;
|
||||
int betaPeriod = 252;
|
||||
IRiskFreeInterestRateModel riskFreeRateModel = new ConstantRiskFreeRateInterestRateModel(0.0025m);
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, alphaPeriod, betaPeriod, riskFreeRateModel);
|
||||
|
||||
for (int i = 0; i <= betaPeriod; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < betaPeriod)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(-0.0000620852, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NullRiskFreeRate()
|
||||
{
|
||||
int alphaPeriod = 1;
|
||||
int betaPeriod = 252;
|
||||
var indicator = new Alpha("testAlphaIndicator", Symbols.AAPL, Symbols.SPX, alphaPeriod, betaPeriod, riskFreeRate: null);
|
||||
|
||||
for (int i = 0; i <= betaPeriod; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 100 + i, Time = startTime, EndTime = endTime });
|
||||
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 200 + i * 3, Time = startTime, EndTime = endTime });
|
||||
|
||||
if (i < betaPeriod)
|
||||
{
|
||||
Assert.AreEqual(0.0, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(0.0008518139, (double)indicator.Current.Value, 0.0000000001);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TracksPreviousState()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Alpha(Symbols.AAPL, Symbols.SPX, period);
|
||||
var previousValue = indicator.Current.Value;
|
||||
|
||||
// Update the indicator and verify the previous values
|
||||
for (var i = 1; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
// Verify the previous value matches the indicator's previous value
|
||||
Assert.AreEqual(previousValue, indicator.Previous.Value);
|
||||
|
||||
// Update previousValue to the current value for the next iteration
|
||||
previousValue = indicator.Current.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Beta(Symbols.AAPL, Symbols.SPX, period);
|
||||
|
||||
for (var i = 0; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
// Update with the first symbol (AAPL) — indicator.Current.Symbol should reflect this update
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.AAPL, indicator.Current.Symbol);
|
||||
|
||||
// Update with the second symbol (SPX) — indicator.Current.Symbol should now be SPX
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.SPX, indicator.Current.Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ArmsIndexTests : AdvanceDeclineDifferenceTests
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var indicator = new ArmsIndex("test_name");
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(indicator.AddStock);
|
||||
}
|
||||
else
|
||||
{
|
||||
indicator.Add(Symbols.AAPL);
|
||||
indicator.Add(Symbols.IBM);
|
||||
indicator.Add(Symbols.GOOG);
|
||||
RenkoBarSize = 5000000;
|
||||
}
|
||||
return indicator;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var trin = (ArmsIndex)CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, trin.Current.Value);
|
||||
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(1m, trin.Current.Value);
|
||||
trin.Reset();
|
||||
trin.Remove(Symbols.IBM);
|
||||
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, trin.Current.Value);
|
||||
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
trin.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreNotEqual(1m, trin.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var adr = (ArmsIndex)CreateIndicator();
|
||||
adr.Add(Symbols.MSFT);
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.AreEqual(0m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 3, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 200, Time = reference.AddMinutes(3) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(2m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 2, Volume = 250, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 2, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 2, Volume = 150, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(3m / 5m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 140, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 5, Volume = 110, Time = reference.AddMinutes(5) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = 1, Volume = 150, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(3m / 5m, adr.Current.Value);
|
||||
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 120, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 3, Volume = 350, Time = reference.AddMinutes(6) });
|
||||
adr.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 4, Volume = 200, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(3m / 5m, adr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 60, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 10, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 40, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(0.2m, indicator.Current.Value);
|
||||
Assert.AreEqual(6, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = 2, Volume = 60, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Close = 0.5m, Volume = 10, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Volume = 40, Time = reference.AddMinutes(2) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(0.2m, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
var startDate = new DateTime(2019, 1, 1);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
var input = GetInput(Symbols.AAPL, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
var input = GetInput(Symbols.IBM, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
var input = GetInput(Symbols.GOOG, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
Assert.AreEqual(3, indicator.Samples);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var input = GetInput(Symbols.AAPL, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var input = GetInput(Symbols.IBM, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var input = GetInput(Symbols.GOOG, startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
Assert.AreEqual(30, indicator.Samples);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "arms_data.txt";
|
||||
|
||||
protected override string TestColumnName => "TRIN";
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ArnaudLegouxMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new ArnaudLegouxMovingAverage(9, 6);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_alma.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "ALMA"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AroonOscillatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AroonOscillator(14, 14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_aroon_oscillator.txt";
|
||||
|
||||
protected override string TestColumnName => "Aroon Oscillator 14";
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var aroon = new AroonOscillator(3, 3);
|
||||
aroon.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today,
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
aroon.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today.AddSeconds(1),
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
aroon.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today.AddSeconds(2),
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
Assert.IsFalse(aroon.IsReady);
|
||||
aroon.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today.AddSeconds(3),
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
Assert.IsTrue(aroon.IsReady);
|
||||
|
||||
aroon.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(aroon);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(aroon.AroonUp);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(aroon.AroonDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AugenPriceSpikeTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new AugenPriceSpike(20);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_aps.txt";
|
||||
|
||||
protected override string TestColumnName => "APS";
|
||||
|
||||
[Test]
|
||||
public void TestWithStream()
|
||||
{
|
||||
var aps = new AugenPriceSpike(22);
|
||||
foreach (var data in TestHelper.GetDataStream(50))
|
||||
{
|
||||
aps.Update(data.Time, data.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestForPeriod()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new AugenPriceSpike(1));
|
||||
Assert.Throws<ArgumentException>(() => new AugenPriceSpike(2));
|
||||
}
|
||||
|
||||
protected override Action<IndicatorBase<IndicatorDataPoint>, double> Assertion
|
||||
{
|
||||
get { return (indicator, expected) => Assert.AreEqual(expected, (double)indicator.Current.Value, 1); }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PeriodSet()
|
||||
{
|
||||
var aps = new AugenPriceSpike(period: 20);
|
||||
var reference = DateTime.Today;
|
||||
|
||||
double correctValue = 0.31192350881956543;
|
||||
decimal finalTestValue = 22;
|
||||
|
||||
int count = 0;
|
||||
List<double> testValues = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 };
|
||||
|
||||
foreach (decimal i in testValues)
|
||||
{
|
||||
count += 1;
|
||||
aps.Update(reference.AddMinutes(count), i);
|
||||
Assert.IsFalse(aps.IsReady);
|
||||
Assert.AreEqual(0, aps.Current.Value);
|
||||
}
|
||||
aps.Update(reference.AddMinutes(count + 1), finalTestValue);
|
||||
Assert.IsTrue(aps.IsReady);
|
||||
Assert.AreEqual(correctValue, (double)aps.Current.Value, 0.00001);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var aps = new AugenPriceSpike(10);
|
||||
var reference = DateTime.Today;
|
||||
|
||||
aps.Update(reference.AddMinutes(1), 5);
|
||||
aps.Update(reference.AddMinutes(2), 10);
|
||||
aps.Update(reference.AddMinutes(3), 8);
|
||||
aps.Update(reference.AddMinutes(4), 12);
|
||||
aps.Update(reference.AddMinutes(5), 103);
|
||||
aps.Update(reference.AddMinutes(6), 82);
|
||||
aps.Update(reference.AddMinutes(7), 55);
|
||||
aps.Update(reference.AddMinutes(8), 10);
|
||||
aps.Update(reference.AddMinutes(9), 878);
|
||||
aps.Update(reference.AddMinutes(10), 84);
|
||||
aps.Update(reference.AddMinutes(11), 832);
|
||||
aps.Update(reference.AddMinutes(12), 81);
|
||||
aps.Update(reference.AddMinutes(13), 867);
|
||||
aps.Update(reference.AddMinutes(14), 89);
|
||||
Assert.IsTrue(aps.IsReady);
|
||||
Assert.AreNotEqual(0m, aps.Current.Value);
|
||||
|
||||
aps.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(aps);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowOverflowException()
|
||||
{
|
||||
var aps = new AugenPriceSpike(5);
|
||||
var values = new List<decimal>
|
||||
{
|
||||
decimal.MaxValue,
|
||||
0,
|
||||
1e-18m,
|
||||
decimal.MaxValue,
|
||||
1m
|
||||
};
|
||||
|
||||
var date = new DateTime(2024, 12, 2, 12, 0, 0);
|
||||
|
||||
for (var i = 0; i < values.Count; i++)
|
||||
{
|
||||
Assert.DoesNotThrow(() => aps.Update(date, values[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.Globalization;
|
||||
using System.Linq;
|
||||
using Accord.Statistics;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AutoregressiveIntegratedMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
private static List<decimal> betweenMethods;
|
||||
private double _ssIndicator;
|
||||
private double _ssTest;
|
||||
|
||||
protected override string TestFileName => "spy_arima.csv";
|
||||
protected override string TestColumnName => "ARIMA";
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
var ARIMA = CreateIndicator();
|
||||
TestHelper.TestIndicator(ARIMA, TestFileName, TestColumnName,
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ARIMA.Current.Value, 10d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
var ARIMA = CreateIndicator();
|
||||
TestHelper.TestIndicator(ARIMA, TestFileName, TestColumnName,
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ARIMA.Current.Value, 10d));
|
||||
ARIMA.Reset();
|
||||
TestHelper.TestIndicator(ARIMA, TestFileName, TestColumnName,
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ARIMA.Current.Value, 10d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PredictionErrorAgainstExternalData()
|
||||
{
|
||||
if (betweenMethods == null)
|
||||
{
|
||||
betweenMethods = FillDataPerMethod();
|
||||
}
|
||||
|
||||
// Testing predictive performance vs. external.
|
||||
Assert.LessOrEqual(_ssIndicator, _ssTest);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly() // Overridden in order to ensure matrix inversion during ARIMA fitting.
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
var startDate = new DateTime(2019, 1, 1);
|
||||
for (decimal i = 0; i < period.Value; i++)
|
||||
{
|
||||
indicator.Update(startDate, 100m * (1m + 0.05m * i)); // Values should be sufficiently different, now.
|
||||
Assert.AreEqual(i == period.Value - 1, indicator.IsReady);
|
||||
}
|
||||
|
||||
Assert.AreEqual(period.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpectedDifferenceFromExternal()
|
||||
{
|
||||
if (betweenMethods == null)
|
||||
{
|
||||
betweenMethods = FillDataPerMethod();
|
||||
}
|
||||
|
||||
Assert.LessOrEqual(1.39080827453985, betweenMethods.Average()); // Mean difference
|
||||
Assert.LessOrEqual(1.19542348709062, betweenMethods.ToDoubleArray().StandardDeviation()); // Std. Dev
|
||||
}
|
||||
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
var ARIMA = new AutoRegressiveIntegratedMovingAverage("ARIMA", 1, 0, 1, 50);
|
||||
return ARIMA;
|
||||
}
|
||||
|
||||
private List<decimal> FillDataPerMethod()
|
||||
{
|
||||
var ARIMA = CreateIndicator();
|
||||
var realValues = new List<decimal>();
|
||||
var testValues = new List<decimal[]>();
|
||||
var betweenMethods = new List<decimal>();
|
||||
var data = TestHelper.GetCsvFileStream(TestFileName);
|
||||
foreach (var val in data)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(val["Close"]))
|
||||
{
|
||||
var close = val["Close"];
|
||||
realValues.Add(decimal.Parse(val["Close"], new NumberFormatInfo()));
|
||||
ARIMA.Update(new IndicatorDataPoint(Convert.ToDateTime(val["Date"], new DateTimeFormatInfo()),
|
||||
Convert.ToDecimal(close, new NumberFormatInfo())));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(val[TestColumnName]))
|
||||
{
|
||||
var fromTest = decimal.Parse(val[TestColumnName], new NumberFormatInfo());
|
||||
testValues.Add(new[] {ARIMA.Current.Value, fromTest});
|
||||
}
|
||||
}
|
||||
|
||||
_ssIndicator = 0d;
|
||||
_ssTest = 0d;
|
||||
for (var i = 51; i < realValues.Count; i++)
|
||||
{
|
||||
var test = realValues[i];
|
||||
var arimas = testValues[i - 50];
|
||||
_ssIndicator += Math.Pow((double) (arimas[0] - test), 2);
|
||||
_ssTest += Math.Pow((double) (arimas[1] - test), 2);
|
||||
betweenMethods.Add(Math.Abs(arimas[0] - arimas[1]));
|
||||
}
|
||||
|
||||
return betweenMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AverageDirectionalIndexTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AverageDirectionalIndex(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_adx.txt";
|
||||
|
||||
protected override string TestColumnName => "ADX 14";
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
const double epsilon = .0001;
|
||||
var adx = CreateIndicator();
|
||||
|
||||
TestHelper.TestIndicator(adx, TestFileName, "+DI14",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((AverageDirectionalIndex)ind).PositiveDirectionalIndex.Current.Value, epsilon)
|
||||
);
|
||||
adx.Reset();
|
||||
|
||||
TestHelper.TestIndicator(adx, TestFileName, "-DI14",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((AverageDirectionalIndex)ind).NegativeDirectionalIndex.Current.Value, epsilon)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AverageDirectionalMovementIndexRatingTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AverageDirectionalMovementIndexRating(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_adx.txt";
|
||||
|
||||
protected override string TestColumnName => "ADXR 14";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion
|
||||
{
|
||||
get { return (indicator, expected) => Assert.AreEqual(expected, (double)indicator.Current.Value, 1.0); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AverageRangeTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AverageRange(20);
|
||||
}
|
||||
protected override string TestFileName => "spy_adr.csv";
|
||||
|
||||
protected override string TestColumnName => "adr";
|
||||
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
var period = 20;
|
||||
var adr = new AverageRange(period);
|
||||
var values = new List<TradeBar>();
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
var value = new TradeBar
|
||||
{
|
||||
Symbol = Symbol.Empty,
|
||||
Time = DateTime.Now.AddSeconds(i),
|
||||
High = 2 * i,
|
||||
Low = i
|
||||
};
|
||||
adr.Update(value);
|
||||
values.Add(value);
|
||||
}
|
||||
var expected = values.Average(x => x.High - x.Low);
|
||||
Assert.AreEqual(expected, adr.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadyAfterPeriodUpdates()
|
||||
{
|
||||
var period = 5;
|
||||
var adr = new AverageRange(period);
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
Assert.IsFalse(adr.IsReady);
|
||||
var value = new TradeBar
|
||||
{
|
||||
Symbol = Symbol.Empty,
|
||||
Time = DateTime.Now.AddSeconds(i),
|
||||
High = 2 * i,
|
||||
Low = i
|
||||
};
|
||||
adr.Update(value);
|
||||
}
|
||||
Assert.IsTrue(adr.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AverageTrueRangeTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new AverageTrueRange(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_atr_wilder.txt";
|
||||
|
||||
protected override string TestColumnName => "Average True Range 14";
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
var atrSimple = new AverageTrueRange(14, MovingAverageType.Simple);
|
||||
TestHelper.TestIndicator(atrSimple, "spy_atr.txt", "Average True Range 14");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var atr = new AverageTrueRange(14, MovingAverageType.Simple);
|
||||
atr.Update(new TradeBar
|
||||
{
|
||||
Time = DateTime.Today,
|
||||
Open = 1m,
|
||||
High = 3m,
|
||||
Low = .5m,
|
||||
Close = 2.75m,
|
||||
Volume = 1234567890
|
||||
});
|
||||
|
||||
atr.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(atr);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(atr.TrueRange);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TrueRangePropertyIsReadyAfterTwoUpdates()
|
||||
{
|
||||
var atr = new AverageTrueRange(14, MovingAverageType.Simple);
|
||||
Assert.IsFalse(atr.TrueRange.IsReady);
|
||||
|
||||
atr.Update(new TradeBar
|
||||
{
|
||||
Time = DateTime.Today,
|
||||
Open = 1m,
|
||||
High = 3m,
|
||||
Low = .5m,
|
||||
Close = 2.75m,
|
||||
Volume = 1234567890
|
||||
});
|
||||
|
||||
Assert.IsFalse(atr.TrueRange.IsReady);
|
||||
|
||||
atr.Update(new TradeBar
|
||||
{
|
||||
Time = DateTime.Today,
|
||||
Open = 1m,
|
||||
High = 3m,
|
||||
Low = .5m,
|
||||
Close = 2.75m,
|
||||
Volume = 1234567890
|
||||
});
|
||||
|
||||
Assert.IsTrue(atr.TrueRange.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class AwesomeOscillatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 100000000m;
|
||||
return new AwesomeOscillator(5, 34);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_ao.txt";
|
||||
|
||||
protected override string TestColumnName => "AO";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class BalanceOfPowerTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 100000000m;
|
||||
return new BalanceOfPower();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_bop.txt";
|
||||
|
||||
protected override string TestColumnName => "BOP";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MathNet.Numerics.Statistics;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class BetaIndicatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override string TestFileName => "bi_datatest.csv";
|
||||
|
||||
protected override string TestColumnName => "Beta";
|
||||
|
||||
private DateTime _reference = new DateTime(2020, 1, 1);
|
||||
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
Symbol symbolA = "AMZN 2T";
|
||||
Symbol symbolB = "SPX 2T";
|
||||
if (SymbolList.Count > 1)
|
||||
{
|
||||
symbolA = SymbolList[0];
|
||||
symbolB = SymbolList[1];
|
||||
}
|
||||
#pragma warning disable CS0618
|
||||
var indicator = new Beta("testBetaIndicator", symbolA, symbolB, 5);
|
||||
#pragma warning restore CS0618
|
||||
return indicator;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = new Beta("testBetaIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = new Beta("testBetaIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < period.Value; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * period.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WorksWithLowValues()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
Symbol = "SPX 2T";
|
||||
#pragma warning restore CS0618
|
||||
base.WorksWithLowValues();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var firstRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
var secondRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
firstRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AMZN")
|
||||
{
|
||||
firstRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstRenkoConsolidator.Dispose();
|
||||
secondRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var firstVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000);
|
||||
var secondVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000000);
|
||||
firstVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AMZN")
|
||||
{
|
||||
firstVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstVolumeRenkoConsolidator.Dispose();
|
||||
secondVolumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void AcceptsQuoteBarsAsInput()
|
||||
{
|
||||
var indicator = new Beta("testBetaIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.IBM, Ask = new Bar(1, 2, 1, 500), Bid = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.SPY, Ask = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EqualBetaValue()
|
||||
{
|
||||
var indicator = new Beta("testBetaIndicator", Symbols.AAPL, Symbols.SPX, 5);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, (double)indicator.Current.Value, 0.0001);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotEqualBetaValue()
|
||||
{
|
||||
var indicator = new Beta("testBetaIndicator", Symbols.AAPL, Symbols.SPX, 5);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = i + 2, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreNotEqual(1, (double)indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateBetaCalculation()
|
||||
{
|
||||
var beta = new Beta(Symbols.AAPL, Symbols.SPX, 3);
|
||||
|
||||
var values = new List<TradeBar>()
|
||||
{
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 10, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 35, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2),EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2), EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 15, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 80, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 37, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 90, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 105, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 302, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
};
|
||||
|
||||
// Calculating beta manually using the formula: Beta = Covariance(AAPL, SPX) / Variance(SPX)
|
||||
var closeAAPL = new List<double>() { 10, 15, 90, 105 };
|
||||
var closeSPX = new List<double>() { 35, 80, 37, 302 };
|
||||
var priceChangesAAPL = new List<double>();
|
||||
var priceChangesSPX = new List<double>();
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
priceChangesAAPL.Add((closeAAPL[i] - closeAAPL[i - 1]) / closeAAPL[i - 1]);
|
||||
priceChangesSPX.Add((closeSPX[i] - closeSPX[i - 1]) / closeSPX[i - 1]);
|
||||
}
|
||||
var variance = priceChangesSPX.Variance();
|
||||
var covariance = priceChangesAAPL.Covariance(priceChangesSPX);
|
||||
var expectedBeta = (decimal)(covariance / variance);
|
||||
|
||||
// Calculating beta using the indicator
|
||||
for (int i = 0; i < values.Count; i++)
|
||||
{
|
||||
beta.Update(values[i]);
|
||||
}
|
||||
|
||||
Assert.AreEqual(expectedBeta, beta.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BetaWithDifferentTimeZones()
|
||||
{
|
||||
var indicator = new Beta(Symbols.SPY, Symbols.BTCUSD, 5);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.BTCUSD, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
Assert.AreEqual(1, (double)indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TracksPreviousState()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Beta(Symbols.SPY, Symbols.AAPL, period);
|
||||
var previousValue = indicator.Current.Value;
|
||||
|
||||
// Update the indicator and verify the previous values
|
||||
for (var i = 1; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
// Verify the previous value matches the indicator's previous value
|
||||
Assert.AreEqual(previousValue, indicator.Previous.Value);
|
||||
|
||||
// Update previousValue to the current value for the next iteration
|
||||
previousValue = indicator.Current.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Beta(Symbols.SPY, Symbols.AAPL, period);
|
||||
|
||||
for (var i = 0; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
// Update with the first symbol (SPY) — indicator.Current.Symbol should reflect this update
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.SPY, indicator.Current.Symbol);
|
||||
|
||||
// Update with the first symbol (AAPL) — indicator.Current.Symbol should reflect this update
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.AAPL, indicator.Current.Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class BollingerBandsTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new BollingerBands(20, 2.0m);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_bollinger_bands.txt";
|
||||
|
||||
protected override string TestColumnName => "Bollinger Bands® 20 2 Bottom";
|
||||
|
||||
protected override Action<IndicatorBase<IndicatorDataPoint>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double) ((BollingerBands) indicator).LowerBand.Current.Value, 1e-3);
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataMiddleBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator() as BollingerBands,
|
||||
TestFileName,
|
||||
"Moving Average 20",
|
||||
ind => (double) ind.MiddleBand.Current.Value
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataUpperBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator() as BollingerBands,
|
||||
TestFileName,
|
||||
"Bollinger Bands® 20 2 Top",
|
||||
ind => (double) ind.UpperBand.Current.Value
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataBandWidth()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator() as BollingerBands,
|
||||
TestFileName,
|
||||
"BandWidth",
|
||||
ind => (double)ind.BandWidth.Current.Value
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataPercentB()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator() as BollingerBands,
|
||||
TestFileName,
|
||||
"%B",
|
||||
ind => (double)ind.PercentB.Current.Value
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var bb = new BollingerBands(2, 2m);
|
||||
bb.Update(DateTime.Today, 1m);
|
||||
|
||||
Assert.IsFalse(bb.IsReady);
|
||||
bb.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
Assert.IsTrue(bb.IsReady);
|
||||
Assert.IsTrue(bb.StandardDeviation.IsReady);
|
||||
Assert.IsTrue(bb.LowerBand.IsReady);
|
||||
Assert.IsTrue(bb.MiddleBand.IsReady);
|
||||
Assert.IsTrue(bb.UpperBand.IsReady);
|
||||
Assert.IsTrue(bb.BandWidth.IsReady);
|
||||
Assert.IsTrue(bb.PercentB.IsReady);
|
||||
|
||||
bb.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.StandardDeviation);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.LowerBand);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.MiddleBand);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.UpperBand);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.BandWidth);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(bb.PercentB);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LowerUpperBandUpdateOnce()
|
||||
{
|
||||
var bb = new BollingerBands(2, 2m);
|
||||
var lowerBandUpdateCount = 0;
|
||||
var upperBandUpdateCount = 0;
|
||||
bb.LowerBand.Updated += (sender, updated) =>
|
||||
{
|
||||
lowerBandUpdateCount++;
|
||||
};
|
||||
bb.UpperBand.Updated += (sender, updated) =>
|
||||
{
|
||||
upperBandUpdateCount++;
|
||||
};
|
||||
|
||||
Assert.AreEqual(0, lowerBandUpdateCount);
|
||||
Assert.AreEqual(0, upperBandUpdateCount);
|
||||
bb.Update(DateTime.Today, 1m);
|
||||
|
||||
Assert.AreEqual(1, lowerBandUpdateCount);
|
||||
Assert.AreEqual(1, upperBandUpdateCount);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotUpdateWhenStale()
|
||||
{
|
||||
// Unit test for GH Issue #4927
|
||||
var period = 5;
|
||||
var bb = new BollingerBands(period, 2m);
|
||||
|
||||
var lastPercentB = new IndicatorDataPoint();
|
||||
var lastUpdateTime = DateTime.MinValue;
|
||||
bb.Updated += (s, e) =>
|
||||
{
|
||||
if (bb.IsReady && lastPercentB == bb.PercentB.Current)
|
||||
{
|
||||
throw new ArgumentException("BollingerBand is stale and should not be updating");
|
||||
}
|
||||
|
||||
lastUpdateTime = e.Time;
|
||||
lastPercentB = bb.PercentB.Current;
|
||||
};
|
||||
|
||||
// Push in identical value points for the entire period.
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
bb.Update(DateTime.UtcNow, 1);
|
||||
}
|
||||
|
||||
// Push in another identical value point, this should not update!
|
||||
var time = DateTime.UtcNow;
|
||||
bb.Update(time, 1);
|
||||
|
||||
// Assert this was not updated
|
||||
Assert.AreNotEqual(time, lastUpdateTime);
|
||||
|
||||
// Push in a new value
|
||||
time = DateTime.UtcNow;
|
||||
bb.Update(time, 2);
|
||||
|
||||
// Assert this did update
|
||||
Assert.AreEqual(time, lastUpdateTime);
|
||||
Assert.AreEqual(lastPercentB, bb.PercentB.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Indicators.CandlestickPatterns;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators.CandlestickPatterns
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.All)]
|
||||
public class CandlestickPatternTests
|
||||
{
|
||||
private static readonly string[] _testFileNames =
|
||||
{
|
||||
"spy_candle_patterns.txt", "ewz_candle_patterns.txt", "eurusd_candle_patterns.txt"
|
||||
};
|
||||
|
||||
private static TestCaseData[] PatternTestParameters
|
||||
{
|
||||
get
|
||||
{
|
||||
var rows = new List<TestCaseData>();
|
||||
|
||||
foreach (var testFileName in _testFileNames)
|
||||
{
|
||||
rows.Add(new TestCaseData(new TwoCrows(), "CDL2CROWS", testFileName).SetName("TwoCrows-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeBlackCrows(), "CDL3BLACKCROWS", testFileName).SetName("ThreeBlackCrows-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeInside(), "CDL3INSIDE", testFileName).SetName("ThreeInside-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeLineStrike(), "CDL3LINESTRIKE", testFileName).SetName("ThreeLineStrike-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeOutside(), "CDL3OUTSIDE", testFileName).SetName("ThreeOutside-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeStarsInSouth(), "CDL3STARSINSOUTH", testFileName).SetName("ThreeStarsInSouth-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ThreeWhiteSoldiers(), "CDL3WHITESOLDIERS", testFileName).SetName("ThreeWhiteSoldiers-" + testFileName));
|
||||
rows.Add(new TestCaseData(new AbandonedBaby(), "CDLABANDONEDBABY", testFileName).SetName("AbandonedBaby-" + testFileName));
|
||||
rows.Add(new TestCaseData(new AdvanceBlock(), "CDLADVANCEBLOCK", testFileName).SetName("AdvanceBlock-" + testFileName));
|
||||
rows.Add(new TestCaseData(new BeltHold(), "CDLBELTHOLD", testFileName).SetName("BeltHold-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Breakaway(), "CDLBREAKAWAY", testFileName).SetName("Breakaway-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ClosingMarubozu(), "CDLCLOSINGMARUBOZU", testFileName).SetName("ClosingMarubozu-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ConcealedBabySwallow(), "CDLCONCEALBABYSWALL", testFileName).SetName("ConcealedBabySwallow-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Counterattack(), "CDLCOUNTERATTACK", testFileName).SetName("Counterattack-" + testFileName));
|
||||
rows.Add(new TestCaseData(new DarkCloudCover(), "CDLDARKCLOUDCOVER", testFileName).SetName("DarkCloudCover-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Doji(), "CDLDOJI", testFileName).SetName("Doji-" + testFileName));
|
||||
rows.Add(new TestCaseData(new DojiStar(), "CDLDOJISTAR", testFileName).SetName("DojiStar-" + testFileName));
|
||||
rows.Add(new TestCaseData(new DragonflyDoji(), "CDLDRAGONFLYDOJI", testFileName).SetName("DragonflyDoji-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Engulfing(), "CDLENGULFING", testFileName).SetName("Engulfing-" + testFileName));
|
||||
rows.Add(new TestCaseData(new EveningDojiStar(), "CDLEVENINGDOJISTAR", testFileName).SetName("EveningDojiStar-" + testFileName));
|
||||
rows.Add(new TestCaseData(new EveningStar(), "CDLEVENINGSTAR", testFileName).SetName("EveningStar-" + testFileName));
|
||||
rows.Add(new TestCaseData(new GapSideBySideWhite(), "CDLGAPSIDESIDEWHITE", testFileName).SetName("GapSideBySideWhite-" + testFileName));
|
||||
rows.Add(new TestCaseData(new GravestoneDoji(), "CDLGRAVESTONEDOJI", testFileName).SetName("GravestoneDoji-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Hammer(), "CDLHAMMER", testFileName).SetName("Hammer-" + testFileName));
|
||||
rows.Add(new TestCaseData(new HangingMan(), "CDLHANGINGMAN", testFileName).SetName("HangingMan-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Harami(), "CDLHARAMI", testFileName).SetName("Harami-" + testFileName));
|
||||
rows.Add(new TestCaseData(new HaramiCross(), "CDLHARAMICROSS", testFileName).SetName("HaramiCross-" + testFileName));
|
||||
if (testFileName.Contains("ewz"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test only passes with the ewz test file
|
||||
rows.Add(new TestCaseData(new HighWaveCandle(), "CDLHIGHWAVE", testFileName).SetName("HighWaveCandle-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new Hikkake(), "CDLHIKKAKE", testFileName).SetName("Hikkake-" + testFileName));
|
||||
if (testFileName.Contains("spy"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test only passes with the spy test file
|
||||
rows.Add(new TestCaseData(new HikkakeModified(), "CDLHIKKAKEMOD", testFileName).SetName("HikkakeModified-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new HomingPigeon(), "CDLHOMINGPIGEON", testFileName).SetName("HomingPigeon-" + testFileName));
|
||||
rows.Add(new TestCaseData(new IdenticalThreeCrows(), "CDLIDENTICAL3CROWS", testFileName).SetName("IdenticalThreeCrows-" + testFileName));
|
||||
rows.Add(new TestCaseData(new InNeck(), "CDLINNECK", testFileName).SetName("InNeck-" + testFileName));
|
||||
rows.Add(new TestCaseData(new InvertedHammer(), "CDLINVERTEDHAMMER", testFileName).SetName("InvertedHammer-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Kicking(), "CDLKICKING", testFileName).SetName("Kicking-" + testFileName));
|
||||
rows.Add(new TestCaseData(new KickingByLength(), "CDLKICKINGBYLENGTH", testFileName).SetName("KickingByLength-" + testFileName));
|
||||
rows.Add(new TestCaseData(new LadderBottom(), "CDLLADDERBOTTOM", testFileName).SetName("LadderBottom-" + testFileName));
|
||||
rows.Add(new TestCaseData(new LongLeggedDoji(), "CDLLONGLEGGEDDOJI", testFileName).SetName("LongLeggedDoji-" + testFileName));
|
||||
rows.Add(new TestCaseData(new LongLineCandle(), "CDLLONGLINE", testFileName).SetName("LongLineCandle-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Marubozu(), "CDLMARUBOZU", testFileName).SetName("Marubozu-" + testFileName));
|
||||
rows.Add(new TestCaseData(new MatchingLow(), "CDLMATCHINGLOW", testFileName).SetName("MatchingLow-" + testFileName));
|
||||
rows.Add(new TestCaseData(new MatHold(), "CDLMATHOLD", testFileName).SetName("MatHold-" + testFileName));
|
||||
rows.Add(new TestCaseData(new MorningDojiStar(), "CDLMORNINGDOJISTAR", testFileName).SetName("MorningDojiStar-" + testFileName));
|
||||
if (!testFileName.Contains("eurusd"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test does not pass with the eurusd test file
|
||||
rows.Add(new TestCaseData(new MorningStar(), "CDLMORNINGSTAR", testFileName).SetName("MorningStar-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new OnNeck(), "CDLONNECK", testFileName).SetName("OnNeck-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Piercing(), "CDLPIERCING", testFileName).SetName("Piercing-" + testFileName));
|
||||
if (!testFileName.Contains("spy"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test does not pass with the spy test file
|
||||
rows.Add(new TestCaseData(new RickshawMan(), "CDLRICKSHAWMAN", testFileName).SetName("RickshawMan-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new RiseFallThreeMethods(), "CDLRISEFALL3METHODS", testFileName).SetName("RiseFallThreeMethods-" + testFileName));
|
||||
rows.Add(new TestCaseData(new SeparatingLines(), "CDLSEPARATINGLINES", testFileName).SetName("SeparatingLines-" + testFileName));
|
||||
rows.Add(new TestCaseData(new ShootingStar(), "CDLSHOOTINGSTAR", testFileName).SetName("ShootingStar-" + testFileName));
|
||||
if (!testFileName.Contains("spy"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test does not pass with the spy test file
|
||||
rows.Add(new TestCaseData(new ShortLineCandle(), "CDLSHORTLINE", testFileName).SetName("ShortLineCandle-" + testFileName));
|
||||
}
|
||||
if (!testFileName.Contains("spy"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test does not pass with the spy test file
|
||||
rows.Add(new TestCaseData(new SpinningTop(), "CDLSPINNINGTOP", testFileName).SetName("SpinningTop-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new StalledPattern(), "CDLSTALLEDPATTERN", testFileName).SetName("StalledPattern-" + testFileName));
|
||||
if (testFileName.Contains("ewz"))
|
||||
{
|
||||
// Lean uses decimals while TA-lib uses doubles, so this test only passes with the ewz test file
|
||||
rows.Add(new TestCaseData(new StickSandwich(), "CDLSTICKSANDWICH", testFileName).SetName("StickSandwich-" + testFileName));
|
||||
}
|
||||
rows.Add(new TestCaseData(new Takuri(), "CDLTAKURI", testFileName).SetName("Takuri-" + testFileName));
|
||||
rows.Add(new TestCaseData(new TasukiGap(), "CDLTASUKIGAP", testFileName).SetName("TasukiGap-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Thrusting(), "CDLTHRUSTING", testFileName).SetName("Thrusting-" + testFileName));
|
||||
rows.Add(new TestCaseData(new Tristar(), "CDLTRISTAR", testFileName).SetName("Tristar-" + testFileName));
|
||||
rows.Add(new TestCaseData(new UniqueThreeRiver(), "CDLUNIQUE3RIVER", testFileName).SetName("UniqueThreeRiver-" + testFileName));
|
||||
rows.Add(new TestCaseData(new UpsideGapTwoCrows(), "CDLUPSIDEGAP2CROWS", testFileName).SetName("UpsideGapTwoCrows-" + testFileName));
|
||||
rows.Add(new TestCaseData(new UpDownGapThreeMethods(), "CDLXSIDEGAP3METHODS", testFileName).SetName("UpDownGapThreeMethods-" + testFileName));
|
||||
}
|
||||
|
||||
return rows.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(PatternTestParameters))]
|
||||
public void ComparesAgainstExternalData(IndicatorBase<IBaseDataBar> indicator, string columnName, string testFileName)
|
||||
{
|
||||
TestHelper.TestIndicator(indicator, testFileName, columnName, Assertion);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(PatternTestParameters))]
|
||||
public void ComparesAgainstExternalDataAfterReset(CandlestickPattern indicator, string columnName, string testFileName)
|
||||
{
|
||||
TestHelper.TestIndicator(indicator, testFileName, columnName, Assertion);
|
||||
indicator.Reset();
|
||||
TestHelper.TestIndicator(indicator, testFileName, columnName, Assertion);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(PatternTestParameters))]
|
||||
public void ResetsProperly(CandlestickPattern indicator, string columnName, string testFileName)
|
||||
{
|
||||
TestHelper.TestIndicatorReset(indicator, testFileName);
|
||||
}
|
||||
|
||||
private static Action<IndicatorBase<IBaseDataBar>, double> Assertion
|
||||
{
|
||||
get
|
||||
{
|
||||
return (indicator, expected) =>
|
||||
{
|
||||
// Trace line for debugging
|
||||
// Console.WriteLine(indicator.Current.EndTime + "\t" + expected + "\t" + indicator.Current.Value * 100);
|
||||
|
||||
Assert.AreEqual(expected, (double) indicator.Current.Value * 100);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ChaikinMoneyFlowTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
return new ChaikinMoneyFlow("CMF", 20);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_cmf.txt";
|
||||
|
||||
protected override string TestColumnName => "CMF_20";
|
||||
|
||||
[Test]
|
||||
public void TestTradeBarsWithNoVolume()
|
||||
{
|
||||
// As volume is a multiplier in numerator, should return default value 0m.
|
||||
var cmf = new ChaikinMoneyFlow("CMF", 3);
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = data.Value,
|
||||
Close = data.Value,
|
||||
High = data.Value,
|
||||
Low = data.Value,
|
||||
Volume = 0
|
||||
};
|
||||
cmf.Update(tradeBar);
|
||||
}
|
||||
Assert.AreEqual(cmf.Current.Value, 0m);
|
||||
}
|
||||
[Test]
|
||||
public void TestDivByZero()
|
||||
{
|
||||
var cmf = new ChaikinMoneyFlow("CMF", 3);
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
// Should handle High = Low case by returning 0m.
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = data.Value,
|
||||
Close = data.Value,
|
||||
High = 1,
|
||||
Low = 1,
|
||||
Volume = 1
|
||||
};
|
||||
cmf.Update(tradeBar);
|
||||
}
|
||||
Assert.AreEqual(cmf.Current.Value, 0m);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
public class ChaikinOscillatorTests : AccumulationDistributionOscillatorTests
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
return new ChaikinOscillator(3, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ChandeKrollStopTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
return new ChandeKrollStop(5, 2.0m, 3);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_ChandeKrollStop.csv";
|
||||
|
||||
protected override string TestColumnName => "short_stop";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double)((ChandeKrollStop)indicator).ShortStop.Current.Value, 1e-6);
|
||||
|
||||
|
||||
[Test]
|
||||
public void CompareAgainstExternalDataForLongStop()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"long_stop",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ((ChandeKrollStop) ind).LongStop.Current.Value, 1e-6)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ChandeMomentumOscillatorTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new ChandeMomentumOscillator(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_cmo.txt";
|
||||
|
||||
protected override string TestColumnName => "CMO_5";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ChoppinessIndexTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
//VolumeRenkoBarSize = 0.5m; // AcceptsVolumeRenkoBarsAsInput is hanging when uncommented
|
||||
return new ChoppinessIndex(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_chop.csv";
|
||||
|
||||
protected override string TestColumnName => "CHOP14";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class CommodityChannelIndexTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new CommodityChannelIndex(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_cci.txt";
|
||||
|
||||
protected override string TestColumnName => "Commodity Channel Index (CCI) 14";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double) indicator.Current.Value, 1e-2);
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var cci = new CommodityChannelIndex(2);
|
||||
cci.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today,
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
Assert.IsFalse(cci.IsReady);
|
||||
cci.Update(new TradeBar
|
||||
{
|
||||
Symbol = Symbols.SPY,
|
||||
Time = DateTime.Today.AddSeconds(1),
|
||||
Open = 3m,
|
||||
High = 7m,
|
||||
Low = 2m,
|
||||
Close = 5m,
|
||||
Volume = 10
|
||||
});
|
||||
Assert.IsTrue(cci.IsReady);
|
||||
|
||||
cci.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(cci);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(cci.TypicalPriceAverage);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(cci.TypicalPriceMeanDeviation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
public abstract class CommonIndicatorTests<T>
|
||||
where T : class, IBaseData
|
||||
{
|
||||
protected Symbol Symbol { get; set; } = Symbols.SPY;
|
||||
protected List<Symbol> SymbolList = new List<Symbol>();
|
||||
protected bool ValueCanBeZero { get; set; } = false;
|
||||
|
||||
[Test]
|
||||
public virtual void ComparesAgainstExternalData()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
RunTestIndicator(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
RunTestIndicator(indicator);
|
||||
indicator.Reset();
|
||||
RunTestIndicator(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void ResetsProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<IndicatorDataPoint>)
|
||||
TestHelper.TestIndicatorReset(indicator as IndicatorBase<IndicatorDataPoint>, TestFileName);
|
||||
else if (indicator is IndicatorBase<IBaseDataBar>)
|
||||
TestHelper.TestIndicatorReset(indicator as IndicatorBase<IBaseDataBar>, TestFileName);
|
||||
else if (indicator is IndicatorBase<TradeBar>)
|
||||
TestHelper.TestIndicatorReset(indicator as IndicatorBase<TradeBar>, TestFileName);
|
||||
else
|
||||
throw new NotSupportedException("ResetsProperly: Unsupported indicator data type: " + typeof(T));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
var startDate = new DateTime(2019, 1, 1);
|
||||
|
||||
for (var i = 0; i < period.Value; i++)
|
||||
{
|
||||
var input = GetInput(startDate, i);
|
||||
indicator.Update(input);
|
||||
Assert.AreEqual(i == period.Value - 1, indicator.IsReady);
|
||||
}
|
||||
|
||||
Assert.AreEqual(period.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
protected QCAlgorithm CreateAlgorithm()
|
||||
{
|
||||
var algo = new QCAlgorithm();
|
||||
algo.SetHistoryProvider(TestGlobals.HistoryProvider);
|
||||
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
|
||||
return algo;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void WarmUpIndicatorProducesConsistentResults()
|
||||
{
|
||||
var algo = CreateAlgorithm();
|
||||
algo.SetStartDate(2020, 1, 1);
|
||||
algo.SetEndDate(2021, 2, 1);
|
||||
|
||||
SymbolList = GetSymbols();
|
||||
|
||||
var firstIndicator = CreateIndicator();
|
||||
var period = (firstIndicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
if (period == null || period == 0)
|
||||
{
|
||||
Assert.Ignore($"{firstIndicator.Name}, Skipping this test because it's not applicable.");
|
||||
}
|
||||
// Warm up the first indicator
|
||||
algo.WarmUpIndicator(SymbolList, firstIndicator, Resolution.Daily);
|
||||
|
||||
// Warm up the second indicator manually
|
||||
var secondIndicator = CreateIndicator();
|
||||
var history = algo.History(SymbolList, period.Value, Resolution.Daily).ToList();
|
||||
foreach (var slice in history)
|
||||
{
|
||||
foreach (var symbol in SymbolList)
|
||||
{
|
||||
secondIndicator.Update(slice[symbol]);
|
||||
}
|
||||
}
|
||||
SymbolList.Clear();
|
||||
|
||||
// Assert that the indicators are ready
|
||||
Assert.IsTrue(firstIndicator.IsReady);
|
||||
Assert.IsTrue(secondIndicator.IsReady);
|
||||
if (!ValueCanBeZero)
|
||||
{
|
||||
Assert.AreNotEqual(firstIndicator.Current.Value, 0);
|
||||
}
|
||||
|
||||
// Ensure that the first indicator has processed some data
|
||||
Assert.AreNotEqual(firstIndicator.Samples, 0);
|
||||
|
||||
// Validate that both indicators have the same number of processed samples
|
||||
Assert.AreEqual(firstIndicator.Samples, secondIndicator.Samples);
|
||||
|
||||
// Validate that both indicators produce the same final computed value
|
||||
Assert.AreEqual(firstIndicator.Current.Value, secondIndicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void TimeMovesForward()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var startDate = new DateTime(2019, 1, 1);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
var input = GetInput(startDate, i);
|
||||
indicator.Update(input);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<TradeBar> ||
|
||||
indicator is IndicatorBase<IBaseData> ||
|
||||
indicator is BarIndicator ||
|
||||
indicator is IndicatorBase<IBaseDataBar>)
|
||||
{
|
||||
var renkoConsolidator = new RenkoConsolidator(RenkoBarSize);
|
||||
renkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
TestHelper.UpdateRenkoConsolidator(renkoConsolidator, TestFileName);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveRenkoBars(indicator);
|
||||
renkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<TradeBar> ||
|
||||
indicator is IndicatorBase<IBaseData> ||
|
||||
indicator is BarIndicator ||
|
||||
indicator is IndicatorBase<IBaseDataBar>)
|
||||
{
|
||||
var volumeRenkoConsolidator = new VolumeRenkoConsolidator(VolumeRenkoBarSize);
|
||||
volumeRenkoConsolidator.DataConsolidated += (sender, volumeRenkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(volumeRenkoBar));
|
||||
};
|
||||
|
||||
TestHelper.UpdateRenkoConsolidator(volumeRenkoConsolidator, TestFileName);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(indicator);
|
||||
volumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void TracksPreviousState()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
var startDate = new DateTime(2024, 1, 1);
|
||||
var previousValue = indicator.Current.Value;
|
||||
|
||||
// Update the indicator and verify the previous values
|
||||
for (var i = 0; i < 2 * period; i++)
|
||||
{
|
||||
indicator.Update(GetInput(startDate, i));
|
||||
|
||||
// Verify the previous value matches the indicator's previous value
|
||||
Assert.AreEqual(previousValue, indicator.Previous.Value);
|
||||
|
||||
// Update previousValue to the current value for the next iteration
|
||||
previousValue = indicator.Current.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void WorksWithLowValues()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
var random = new Random();
|
||||
var time = new DateTime(2023, 5, 28);
|
||||
for (int i = 0; i < 2 * period; i++)
|
||||
{
|
||||
var value = (decimal)(random.NextDouble() * 0.000000000000000000000000000001);
|
||||
Assert.DoesNotThrow(() => indicator.Update(GetInput(Symbol, time, i, value, value, value, value)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
var startDate = new DateTime(2024, 1, 1);
|
||||
|
||||
for (var i = 0; i < 2 * period; i++)
|
||||
{
|
||||
// Feed input data to the indicator, each input uses Symbol.SPY
|
||||
indicator.Update(GetInput(startDate, i));
|
||||
|
||||
// The indicator should retain the symbol from the input (SPY)
|
||||
Assert.AreEqual(Symbols.SPY, indicator.Current.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
Assert.AreNotEqual(0, indicator.Current.Value);
|
||||
}
|
||||
|
||||
protected virtual void IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
Assert.AreNotEqual(0, indicator.Current.Value);
|
||||
}
|
||||
|
||||
protected static IBaseData GetInput(DateTime startDate, int days) => GetInput(Symbols.SPY, startDate, days);
|
||||
|
||||
protected static IBaseData GetInput(Symbol symbol, DateTime startDate, int days) => GetInput(symbol, startDate, days, 100m + days, 105m + days, 95m + days, 100 + days);
|
||||
|
||||
protected static IBaseData GetInput(Symbol symbol, DateTime startDate, int days, decimal open, decimal high, decimal low, decimal close)
|
||||
{
|
||||
if (typeof(T) == typeof(IndicatorDataPoint))
|
||||
{
|
||||
return new IndicatorDataPoint(symbol, startDate.AddDays(days), close);
|
||||
}
|
||||
|
||||
return new TradeBar(
|
||||
startDate.AddDays(days),
|
||||
symbol,
|
||||
open,
|
||||
high,
|
||||
low,
|
||||
close,
|
||||
100m,
|
||||
Time.OneDay
|
||||
);
|
||||
}
|
||||
|
||||
public PyObject GetIndicatorAsPyObject()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
return Indicator.ToPython();
|
||||
}
|
||||
}
|
||||
|
||||
public IndicatorBase<T> Indicator => CreateIndicator();
|
||||
|
||||
/// <summary>
|
||||
/// Executes a test of the specified indicator
|
||||
/// </summary>
|
||||
protected virtual void RunTestIndicator(IndicatorBase<T> indicator)
|
||||
{
|
||||
if (indicator is IndicatorBase<IndicatorDataPoint>)
|
||||
TestHelper.TestIndicator(
|
||||
indicator as IndicatorBase<IndicatorDataPoint>,
|
||||
TestFileName,
|
||||
TestColumnName,
|
||||
Assertion as Action<IndicatorBase<IndicatorDataPoint>, double>
|
||||
);
|
||||
else if (indicator is IndicatorBase<IBaseDataBar>)
|
||||
TestHelper.TestIndicator(
|
||||
indicator as IndicatorBase<IBaseDataBar>,
|
||||
TestFileName,
|
||||
TestColumnName,
|
||||
Assertion as Action<IndicatorBase<IBaseDataBar>, double>
|
||||
);
|
||||
else if (indicator is IndicatorBase<TradeBar>)
|
||||
TestHelper.TestIndicator(
|
||||
indicator as IndicatorBase<TradeBar>,
|
||||
TestFileName,
|
||||
TestColumnName,
|
||||
Assertion as Action<IndicatorBase<TradeBar>, double>);
|
||||
else
|
||||
throw new NotSupportedException("RunTestIndicator: Unsupported indicator data type: " + typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a custom assertion function, parameters are the indicator and the expected value from the file
|
||||
/// </summary>
|
||||
protected virtual Action<IndicatorBase<T>, double> Assertion
|
||||
{
|
||||
get
|
||||
{
|
||||
return (indicator, expected) =>
|
||||
{
|
||||
Assert.AreEqual(expected, (double)indicator.Current.Value, 1e-3);
|
||||
|
||||
var relativeDifference = Math.Abs(((double)indicator.Current.Value - expected) / expected);
|
||||
Assert.LessOrEqual(relativeDifference, 1); // less than 1% error rate
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new instance of the indicator to test
|
||||
/// </summary>
|
||||
protected abstract IndicatorBase<T> CreateIndicator();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CSV file name containing test data for the indicator
|
||||
/// </summary>
|
||||
protected abstract string TestFileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the column of the CSV file corresponding to the pre-calculated data for the indicator
|
||||
/// </summary>
|
||||
protected abstract string TestColumnName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of symbols used for testing, defaulting to SPY.
|
||||
/// </summary>
|
||||
protected virtual List<Symbol> GetSymbols() => [Symbols.SPY];
|
||||
|
||||
/// <summary>
|
||||
/// Returns the BarSize for the RenkoBar test, namely, AcceptsRenkoBarsAsInput()
|
||||
/// </summary>
|
||||
protected decimal RenkoBarSize { get; set; } = 10m;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the BarSize for the VolumeRenkoBar test, namely, AcceptsVolumeRenkoBarsAsInput()
|
||||
/// </summary>
|
||||
protected decimal VolumeRenkoBarSize { get; set; } = 500000m;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class CompositeIndicatorTests
|
||||
{
|
||||
[Test]
|
||||
public void CompositeIsReadyWhenBothAre()
|
||||
{
|
||||
var left = new Delay(1);
|
||||
var right = new Delay(2);
|
||||
var composite = CreateCompositeIndicator(left, right, (l, r) => l.Current.Value + r.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today.AddSeconds(0), 1m);
|
||||
right.Update(DateTime.Today.AddSeconds(0), 1m);
|
||||
Assert.IsFalse(composite.IsReady);
|
||||
Assert.IsFalse(composite.Left.IsReady);
|
||||
Assert.IsFalse(composite.Right.IsReady);
|
||||
|
||||
left.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
right.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
Assert.IsFalse(composite.IsReady);
|
||||
Assert.IsTrue(composite.Left.IsReady);
|
||||
Assert.IsFalse(composite.Right.IsReady);
|
||||
|
||||
left.Update(DateTime.Today.AddSeconds(2), 3m);
|
||||
right.Update(DateTime.Today.AddSeconds(2), 3m);
|
||||
Assert.IsTrue(composite.IsReady);
|
||||
Assert.IsTrue(composite.Left.IsReady);
|
||||
Assert.IsTrue(composite.Right.IsReady);
|
||||
|
||||
left.Update(DateTime.Today.AddSeconds(3), 4m);
|
||||
right.Update(DateTime.Today.AddSeconds(3), 4m);
|
||||
Assert.IsTrue(composite.IsReady);
|
||||
Assert.IsTrue(composite.Left.IsReady);
|
||||
Assert.IsTrue(composite.Right.IsReady);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CallsDelegateCorrectly()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = CreateCompositeIndicator(left, right, (l, r) =>
|
||||
{
|
||||
Assert.AreEqual(left, l);
|
||||
Assert.AreEqual(right, r);
|
||||
return l.Current.Value + r.Current.Value;
|
||||
});
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(2m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void ResetsProperly()
|
||||
{
|
||||
var left = new Maximum("left", 2);
|
||||
var right = new Minimum("right", 2);
|
||||
var composite = CreateCompositeIndicator(left, right, (l, r) => l.Current.Value + r.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, -1m);
|
||||
|
||||
left.Update(DateTime.Today.AddDays(1), -1m);
|
||||
right.Update(DateTime.Today.AddDays(1), 1m);
|
||||
|
||||
Assert.AreEqual(left.PeriodsSinceMaximum, 1);
|
||||
Assert.AreEqual(right.PeriodsSinceMinimum, 1);
|
||||
|
||||
composite.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(composite);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(left);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(right);
|
||||
Assert.AreEqual(left.PeriodsSinceMaximum, 0);
|
||||
Assert.AreEqual(right.PeriodsSinceMinimum, 0);
|
||||
}
|
||||
|
||||
[TestCase("sum", 5, 10, 15, false)]
|
||||
[TestCase("min", -12, 52, -12, false)]
|
||||
[TestCase("sum", 5, 10, 15, true)]
|
||||
[TestCase("min", -12, 52, -12, true)]
|
||||
public virtual void PythonCompositeIndicatorConstructorValidatesBehavior(string operation, decimal leftValue, decimal rightValue, decimal expectedValue, bool usePythonIndicator)
|
||||
{
|
||||
var left = new SimpleMovingAverage("SMA", 10);
|
||||
var right = new SimpleMovingAverage("SMA", 10);
|
||||
using (Py.GIL())
|
||||
{
|
||||
var testModule = PyModule.FromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
from QuantConnect.Indicators import *
|
||||
|
||||
def create_composite_indicator(left, right, operation):
|
||||
if operation == 'sum':
|
||||
def composer(l, r):
|
||||
return IndicatorResult(l.current.value + r.current.value)
|
||||
elif operation == 'min':
|
||||
def composer(l, r):
|
||||
return IndicatorResult(min(l.current.value, r.current.value))
|
||||
return CompositeIndicator(left, right, composer)
|
||||
|
||||
def update_indicators(left, right, value_left, value_right):
|
||||
left.update(IndicatorDataPoint(DateTime.Now, value_left))
|
||||
right.update(IndicatorDataPoint(DateTime.Now, value_right))
|
||||
");
|
||||
|
||||
using var createCompositeIndicator = testModule.GetAttr("create_composite_indicator");
|
||||
using var updateIndicators = testModule.GetAttr("update_indicators");
|
||||
|
||||
using var leftPy = usePythonIndicator ? CreatePyObjectIndicator(10) : left.ToPython();
|
||||
using var rightPy = usePythonIndicator ? CreatePyObjectIndicator(10) : right.ToPython();
|
||||
|
||||
// Create composite indicator using Python logic
|
||||
using var composite = createCompositeIndicator.Invoke(leftPy, rightPy, operation.ToPython());
|
||||
|
||||
// Update the indicator with sample values (left, right)
|
||||
updateIndicators.Invoke(leftPy, rightPy, leftValue.ToPython(), rightValue.ToPython());
|
||||
|
||||
// Verify composite indicator name and properties
|
||||
using var name = composite.GetAttr("Name");
|
||||
Assert.AreEqual($"COMPOSE({left.Name},{right.Name})", name.ToString());
|
||||
|
||||
// Validate the composite indicator's computed value
|
||||
using var value = composite.GetAttr("Current").GetAttr("Value");
|
||||
Assert.AreEqual(expectedValue, value.As<decimal>());
|
||||
}
|
||||
}
|
||||
|
||||
private static PyObject CreatePyObjectIndicator(int period)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var module = PyModule.FromString(
|
||||
"custom_indicator",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
from collections import deque
|
||||
|
||||
class CustomSimpleMovingAverage(PythonIndicator):
|
||||
def __init__(self, period):
|
||||
self.name = 'SMA'
|
||||
self.value = 0
|
||||
self.period = period
|
||||
self.warm_up_period = period
|
||||
self.queue = deque(maxlen=period)
|
||||
self.current = IndicatorDataPoint(DateTime.Now, self.value)
|
||||
|
||||
def update(self, input):
|
||||
self.queue.appendleft(input.value)
|
||||
count = len(self.queue)
|
||||
self.value = sum(self.queue) / count
|
||||
self.current = IndicatorDataPoint(input.time, self.value)
|
||||
self.on_updated(IndicatorDataPoint(DateTime.Now, input.value))
|
||||
"
|
||||
);
|
||||
|
||||
var indicator = module.GetAttr("CustomSimpleMovingAverage")
|
||||
.Invoke(period.ToPython());
|
||||
|
||||
return indicator;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual CompositeIndicator CreateCompositeIndicator(IndicatorBase left, IndicatorBase right, QuantConnect.Indicators.CompositeIndicator.IndicatorComposer composer)
|
||||
{
|
||||
return new CompositeIndicator(left, right, composer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ConnorsRelativeStrengthIndexTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new ConnorsRelativeStrengthIndex(3, 2, 100);
|
||||
}
|
||||
protected override string TestFileName => "spy_crsi.csv";
|
||||
|
||||
protected override string TestColumnName => "crsi";
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowDivisionByZero()
|
||||
{
|
||||
var crsi = new ConnorsRelativeStrengthIndex(2, 2, 2);
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
Assert.DoesNotThrow(() => crsi.Update(DateTime.UtcNow, 0m));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadyAfterPeriodUpdates()
|
||||
{
|
||||
var rsiPeriod = 2;
|
||||
var rsiPeriodStreak = 3;
|
||||
var lookBackPeriod = 4;
|
||||
var crsi = new ConnorsRelativeStrengthIndex(rsiPeriod, rsiPeriodStreak, lookBackPeriod);
|
||||
int minInputValues = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, lookBackPeriod));
|
||||
for (int i = 0; i < minInputValues; i++)
|
||||
{
|
||||
Assert.IsFalse(crsi.IsReady);
|
||||
crsi.Update(DateTime.Now, i + 1);
|
||||
}
|
||||
Assert.IsTrue(crsi.IsReady);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ConstantIndicatorTests
|
||||
{
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
var cons = new ConstantIndicator<IndicatorDataPoint>("c", 1m);
|
||||
Assert.AreEqual(1m, cons.Current.Value);
|
||||
Assert.IsTrue(cons.IsReady);
|
||||
|
||||
cons.Update(DateTime.Today, 3m);
|
||||
Assert.AreEqual(1m, cons.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
// constant reset should reset samples but the value should still be the same
|
||||
var cons = new ConstantIndicator<IndicatorDataPoint>("c", 1m);
|
||||
cons.Update(DateTime.Today, 3m);
|
||||
cons.Update(DateTime.Today.AddDays(1), 10m);
|
||||
|
||||
cons.Reset();
|
||||
Assert.AreEqual(1m, cons.Current.Value);
|
||||
Assert.AreEqual(DateTime.MinValue, cons.Current.Time);
|
||||
Assert.AreEqual(0, cons.Samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class CoppockCurveTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new CoppockCurve();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_coppock_curve.csv";
|
||||
protected override string TestColumnName => "CoppockCurve";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class CorrelationPearsonTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override string TestFileName => "spy_qqq_corr.csv";
|
||||
|
||||
private DateTime _reference = new DateTime(2020, 1, 1);
|
||||
|
||||
protected CorrelationType _correlationType { get; set; } = CorrelationType.Pearson;
|
||||
protected override string TestColumnName => (_correlationType == CorrelationType.Pearson) ? "Correlation_Pearson" : "Correlation_Spearman";
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
Symbol symbolA = Symbols.SPY;
|
||||
Symbol symbolB = "QQQ RIWIV7K5Z9LX";
|
||||
if (SymbolList.Count > 1)
|
||||
{
|
||||
symbolA = SymbolList[0];
|
||||
symbolB = SymbolList[1];
|
||||
}
|
||||
#pragma warning disable CS0618
|
||||
var indicator = new Correlation("testCorrelationIndicator", symbolA, symbolB, 252, _correlationType);
|
||||
#pragma warning restore CS0618
|
||||
return indicator;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = new Correlation("testCorrelationIndicator", Symbols.IBM, Symbols.SPY, 5, _correlationType);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = new Correlation("testCorrelationIndicator", Symbols.IBM, Symbols.SPY, 5, _correlationType);
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < period.Value; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * period.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new Correlation(Symbols.SPY, "QQQ RIWIV7K5Z9LX", 70, _correlationType);
|
||||
var firstRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
var secondRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
firstRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
int counter = 0;
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "SPY")
|
||||
{
|
||||
firstRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondRenkoConsolidator.Update(tradebar);
|
||||
counter++;
|
||||
}
|
||||
if (counter >= 100)
|
||||
break;
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstRenkoConsolidator.Dispose();
|
||||
secondRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var firstVolumeRenkoConsolidator = new VolumeRenkoConsolidator(100000);
|
||||
var secondVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000);
|
||||
firstVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
int counter = 0;
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "SPY")
|
||||
{
|
||||
firstVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondVolumeRenkoConsolidator.Update(tradebar);
|
||||
counter++;
|
||||
}
|
||||
if (counter >= 500)
|
||||
break;
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstVolumeRenkoConsolidator.Dispose();
|
||||
secondVolumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
[Test]
|
||||
public void AcceptsQuoteBarsAsInput()
|
||||
{
|
||||
var indicator = new Correlation("testCorrelationIndicator", Symbols.IBM, Symbols.SPY, 5, _correlationType);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.IBM, Ask = new Bar(1, 2, 1, 500), Bid = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.SPY, Ask = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EqualCorrelationValue()
|
||||
{
|
||||
var indicator = new Correlation("testCorrelationIndicator", Symbols.AAPL, Symbols.SPX, 3, _correlationType);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, (double)indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotEqualCorrelationValue()
|
||||
{
|
||||
var indicator = new Correlation("testCorrelationIndicator", Symbols.AAPL, Symbols.SPX, 3, _correlationType);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = i + 2, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
|
||||
Assert.AreNotEqual(0, (double)indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CorrelationWithDifferentTimeZones()
|
||||
{
|
||||
var indicator = new Correlation(Symbols.SPY, Symbols.BTCUSD, 3);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.BTCUSD, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
Assert.AreEqual(1, (double)indicator.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.Algorithm.CSharp;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class CorrelationSpearmanTests : CorrelationPearsonTests
|
||||
{
|
||||
public CorrelationSpearmanTests()
|
||||
{
|
||||
_correlationType = CorrelationType.Spearman;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MathNet.Numerics.Statistics;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class CovarianceTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override string TestFileName => "spy_qqq_cov.csv";
|
||||
|
||||
protected override string TestColumnName => "Covariance";
|
||||
|
||||
private DateTime _reference = new DateTime(2020, 1, 1);
|
||||
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
Symbol symbolA;
|
||||
Symbol symbolB;
|
||||
if (SymbolList.Count > 1)
|
||||
{
|
||||
symbolA = SymbolList[0];
|
||||
symbolB = SymbolList[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
symbolA = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
symbolB = Symbol.Create("QQQ", SecurityType.Equity, Market.USA);
|
||||
}
|
||||
var indicator = new Covariance("testCovarianceIndicator", symbolA, symbolB, 252);
|
||||
return indicator;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
var QQQ = Symbol.Create("QQQ", SecurityType.Equity, Market.USA);
|
||||
return [Symbols.SPY, QQQ];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = new Covariance("testCovarianceIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = new Covariance("testCovarianceIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < period.Value; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * period.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WorksWithLowValues()
|
||||
{
|
||||
SymbolList = GetSymbols();
|
||||
Symbol = SymbolList[1];
|
||||
base.WorksWithLowValues();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new Covariance("testCovarianceIndicator", Symbols.SPY, Symbol.Create("QQQ", SecurityType.Equity, Market.USA), 5);
|
||||
var firstRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
var secondRenkoConsolidator = new RenkoConsolidator(10m);
|
||||
firstRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName).Take(50))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "SPY")
|
||||
{
|
||||
firstRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstRenkoConsolidator.Dispose();
|
||||
secondRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new Covariance("testCovarianceIndicator", Symbols.SPY, Symbol.Create("QQQ", SecurityType.Equity, Market.USA), 5);
|
||||
var firstVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000);
|
||||
var secondVolumeRenkoConsolidator = new VolumeRenkoConsolidator(1000000000);
|
||||
firstVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
secondVolumeRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName).Take(50))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "SPY")
|
||||
{
|
||||
firstVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
secondVolumeRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
// With VolumeRenkoConsolidator(1000000000), limited data won't produce enough bars
|
||||
// The test verifies the indicator accepts the input, not that it becomes ready
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
firstVolumeRenkoConsolidator.Dispose();
|
||||
secondVolumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void AcceptsQuoteBarsAsInput()
|
||||
{
|
||||
var indicator = new Covariance("testCovarianceIndicator", Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.IBM, Ask = new Bar(1, 2, 1, 500), Bid = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new QuoteBar { Symbol = Symbols.SPY, Ask = new Bar(1, 2, 1, 500), Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateCovarianceCalculation()
|
||||
{
|
||||
var cov = new Covariance(Symbols.AAPL, Symbols.SPX, 3);
|
||||
|
||||
var values = new List<TradeBar>()
|
||||
{
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 10, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 35, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2),EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2), EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 15, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 80, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 37, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 90, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 105, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 302, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
};
|
||||
|
||||
// Calculating covariance manually
|
||||
var closeAAPL = new List<double>() { 10, 15, 90, 105 };
|
||||
var closeSPX = new List<double>() { 35, 80, 37, 302 };
|
||||
var priceChangesAAPL = new List<double>();
|
||||
var priceChangesSPX = new List<double>();
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
priceChangesAAPL.Add((closeAAPL[i] - closeAAPL[i - 1]) / closeAAPL[i - 1]);
|
||||
priceChangesSPX.Add((closeSPX[i] - closeSPX[i - 1]) / closeSPX[i - 1]);
|
||||
}
|
||||
var expectedCovariance = priceChangesAAPL.Covariance(priceChangesSPX);
|
||||
|
||||
// Calculating covariance using the indicator
|
||||
for (int i = 0; i < values.Count; i++)
|
||||
{
|
||||
cov.Update(values[i]);
|
||||
}
|
||||
|
||||
Assert.AreEqual((decimal)expectedCovariance, cov.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CovarianceWithDifferentTimeZones()
|
||||
{
|
||||
var indicator = new Covariance(Symbols.SPY, Symbols.BTCUSD, 5);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.BTCUSD, Low = 1, High = 2, Volume = 100, Close = i + 1, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
// All close prices are increasing by constant amount, so returns are decreasing but positive.
|
||||
// Both assets have same prices, so covariance should be equal to variance of either.
|
||||
Assert.IsTrue(indicator.Current.Value > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TracksPreviousState()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Covariance(Symbols.SPY, Symbols.AAPL, period);
|
||||
var previousValue = indicator.Current.Value;
|
||||
|
||||
// Update the indicator and verify the previous values
|
||||
for (var i = 1; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
// Verify the previous value matches the indicator's previous value
|
||||
Assert.AreEqual(previousValue, indicator.Previous.Value);
|
||||
|
||||
// Update previousValue to the current value for the next iteration
|
||||
previousValue = indicator.Current.Value;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new Covariance(Symbols.SPY, Symbols.AAPL, period);
|
||||
|
||||
for (var i = 0; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
// Update with the first symbol (SPY) — indicator.Current.Symbol should reflect this update
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.SPY, indicator.Current.Symbol);
|
||||
|
||||
// Update with the first symbol (AAPL) — indicator.Current.Symbol should reflect this update
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
Assert.AreEqual(Symbols.AAPL, indicator.Current.Symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DeMarkerIndicatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 0.001m;
|
||||
VolumeRenkoBarSize = 1000m;
|
||||
return new DeMarkerIndicator("DEM", 14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "eurusd60_dem.txt";
|
||||
|
||||
protected override string TestColumnName => "dem";
|
||||
|
||||
[Test]
|
||||
public void TestDivByZero()
|
||||
{
|
||||
var dem = new DeMarkerIndicator("DEM", 3);
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
// Should handle High = Low case by returning 0m.
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = data.Value,
|
||||
Close = data.Value,
|
||||
High = 1,
|
||||
Low = 1,
|
||||
Volume = 1
|
||||
};
|
||||
dem.Update(tradeBar);
|
||||
}
|
||||
Assert.AreEqual(dem.Current.Value, 0m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DelayTests
|
||||
{
|
||||
[Test]
|
||||
public void DelayZeroThrowsArgumentException()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
{
|
||||
new Delay(0);
|
||||
}, "size of at least 1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DelayOneRepeatsFirstInputValue()
|
||||
{
|
||||
var delay = new Delay(1);
|
||||
|
||||
var data = new IndicatorDataPoint(DateTime.UtcNow, 1m);
|
||||
delay.Update(data);
|
||||
Assert.AreEqual(1m, delay.Current.Value);
|
||||
|
||||
data = new IndicatorDataPoint(DateTime.UtcNow.AddSeconds(1), 2m);
|
||||
delay.Update(data);
|
||||
Assert.AreEqual(1m, delay.Current.Value);
|
||||
|
||||
data = new IndicatorDataPoint(DateTime.UtcNow.AddSeconds(1), 2m);
|
||||
delay.Update(data);
|
||||
Assert.AreEqual(2m, delay.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DelayTakesPeriodPlus2UpdatesToEmitNonInitialPoint()
|
||||
{
|
||||
const int start = 1;
|
||||
const int count = 10;
|
||||
for (var i = start; i < count+start; i++)
|
||||
{
|
||||
TestDelayTakesPeriodPlus2UpdatesToEmitNonInitialPoint(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void TestDelayTakesPeriodPlus2UpdatesToEmitNonInitialPoint(int period)
|
||||
{
|
||||
var delay = new Delay(period);
|
||||
for (var i = 0; i < period + 2; i++)
|
||||
{
|
||||
Assert.AreEqual(0m, delay.Current.Value);
|
||||
delay.Update(new IndicatorDataPoint(DateTime.Today.AddSeconds(i), i));
|
||||
}
|
||||
Assert.AreEqual(1m, delay.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
var delay = new Delay(2);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(3))
|
||||
{
|
||||
delay.Update(data);
|
||||
}
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.AreEqual(3, delay.Samples);
|
||||
|
||||
delay.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(delay);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpProperly()
|
||||
{
|
||||
var delay = new Delay(20);
|
||||
var count = ((IIndicatorWarmUpPeriodProvider) delay).WarmUpPeriod;
|
||||
var dataArray = TestHelper.GetDataStream(count).ToArray();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
delay.Update(dataArray[i]);
|
||||
Assert.AreEqual(i == count - 1, delay.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.Algorithm;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class DeltaTests : OptionBaseIndicatorTests<Delta>
|
||||
{
|
||||
protected override IndicatorBase<IBaseData> CreateIndicator()
|
||||
=> new Delta("testDeltaIndicator", _symbol, 0.0403m, 0.0m);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel)
|
||||
=> new Delta("testDeltaIndicator", _symbol, riskFreeRateModel);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel)
|
||||
{
|
||||
var symbol = (SymbolList.Count > 0) ? SymbolList[0] : _symbol;
|
||||
return new Delta("testDeltaIndicator", symbol, riskFreeRateModel, dividendYieldModel);
|
||||
}
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(QCAlgorithm algorithm)
|
||||
=> algorithm.D(_symbol);
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
// 2 updates per iteration, 1 for greek, 1 for IV
|
||||
RiskFreeRateUpdatesPerIteration = 2;
|
||||
DividendYieldUpdatesPerIteration = 2;
|
||||
}
|
||||
|
||||
[TestCase("american/third_party_1_greeks.csv", true, false, 0.03)]
|
||||
[TestCase("american/third_party_1_greeks.csv", false, false, 0.03)]
|
||||
// Just placing the test and data here, we are unsure about the smoothing function and not going to reverse engineer
|
||||
[TestCase("american/third_party_2_greeks.csv", false, true, 10000)]
|
||||
public void ComparesAgainstExternalData(string subPath, bool reset, bool singleContract, double errorRate, double errorMargin = 1e-4,
|
||||
int callColumn = 9, int putColumn = 8)
|
||||
{
|
||||
var path = Path.Combine("TestData", "greeksindicator", subPath);
|
||||
// skip last entry since for deep ITM, IV will not affect much on price. Thus root finding will not be optimizing a non-convex function.
|
||||
foreach (var line in File.ReadAllLines(path).Skip(3).SkipLast(1))
|
||||
{
|
||||
var items = line.Split(',');
|
||||
|
||||
var interestRate = Parse.Decimal(items[^2]);
|
||||
var dividendYield = Parse.Decimal(items[^1]);
|
||||
|
||||
var model = ParseSymbols(items, path.Contains("american"), out var call, out var put);
|
||||
|
||||
Delta callIndicator;
|
||||
Delta putIndicator;
|
||||
if (singleContract)
|
||||
{
|
||||
callIndicator = new Delta(call, interestRate, dividendYield, optionModel: model);
|
||||
putIndicator = new Delta(put, interestRate, dividendYield, optionModel: model);
|
||||
}
|
||||
else
|
||||
{
|
||||
callIndicator = new Delta(call, interestRate, dividendYield, put, model);
|
||||
putIndicator = new Delta(put, interestRate, dividendYield, call, model);
|
||||
}
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
|
||||
if (reset == true)
|
||||
{
|
||||
callIndicator.Reset();
|
||||
putIndicator.Reset();
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reference values from QuantLib
|
||||
[TestCase(23.753, 450.0, OptionRight.Call, 60, 0.546, OptionStyle.European)]
|
||||
[TestCase(35.830, 450.0, OptionRight.Put, 60, -0.446, OptionStyle.European)]
|
||||
[TestCase(33.928, 470.0, OptionRight.Call, 60, 0.693, OptionStyle.European)]
|
||||
[TestCase(6.428, 470.0, OptionRight.Put, 60, -0.260, OptionStyle.European)]
|
||||
[TestCase(3.219, 430.0, OptionRight.Call, 60, 0.243, OptionStyle.European)]
|
||||
[TestCase(47.701, 430.0, OptionRight.Put, 60, -0.526, OptionStyle.European)]
|
||||
[TestCase(16.528, 450.0, OptionRight.Call, 180, 0.632, OptionStyle.European)]
|
||||
[TestCase(21.784, 450.0, OptionRight.Put, 180, -0.417, OptionStyle.European)]
|
||||
[TestCase(35.207, 470.0, OptionRight.Call, 180, 0.765, OptionStyle.European)]
|
||||
[TestCase(0.409, 470.0, OptionRight.Put, 180, -0.052, OptionStyle.European)]
|
||||
[TestCase(2.642, 430.0, OptionRight.Call, 180, 0.263, OptionStyle.European)]
|
||||
[TestCase(27.772, 430.0, OptionRight.Put, 180, -0.556, OptionStyle.European)]
|
||||
[TestCase(23.753, 450.0, OptionRight.Call, 60, 0.546, OptionStyle.American)]
|
||||
[TestCase(35.830, 450.0, OptionRight.Put, 60, -0.446, OptionStyle.American)]
|
||||
[TestCase(33.928, 470.0, OptionRight.Call, 60, 0.693, OptionStyle.American)]
|
||||
[TestCase(6.428, 470.0, OptionRight.Put, 60, -0.260, OptionStyle.American)]
|
||||
[TestCase(3.219, 430.0, OptionRight.Call, 60, 0.243, OptionStyle.American)]
|
||||
[TestCase(47.701, 430.0, OptionRight.Put, 60, -0.526, OptionStyle.American)]
|
||||
[TestCase(16.528, 450.0, OptionRight.Call, 180, 0.632, OptionStyle.American)]
|
||||
[TestCase(21.784, 450.0, OptionRight.Put, 180, -0.417, OptionStyle.American)]
|
||||
[TestCase(35.207, 470.0, OptionRight.Call, 180, 0.765, OptionStyle.American)]
|
||||
[TestCase(0.409, 470.0, OptionRight.Put, 180, -0.052, OptionStyle.American)]
|
||||
[TestCase(2.642, 430.0, OptionRight.Call, 180, 0.264, OptionStyle.American)]
|
||||
[TestCase(27.772, 430.0, OptionRight.Put, 180, -0.556, OptionStyle.American)]
|
||||
public void ComparesAgainstExternalData2(decimal price, decimal spotPrice, OptionRight right, int expiry, double refDelta, OptionStyle style)
|
||||
{
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, style, right, 450m, _reference.AddDays(expiry));
|
||||
var model = style == OptionStyle.European ? OptionPricingModelType.BlackScholes : OptionPricingModelType.BinomialCoxRossRubinstein;
|
||||
var indicator = new Delta(symbol, 0.0403m, 0.0m, optionModel: model, ivModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, _reference, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference, spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refDelta, (double)indicator.Current.Value, 0.0017d);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IVAndDeltaAreNonZeroForPMSettledIndexOptionOnExpirationDay()
|
||||
{
|
||||
// SPXW expiring on the 3rd Friday is PM-settled (15:15 CT),
|
||||
// so at 10 AM the contract still has time value — IV and Delta must be non-zero.
|
||||
var thirdFriday = new DateTime(2016, 02, 19);
|
||||
var spxwSymbol = Symbol.CreateOption(Symbols.SPX, "SPXW", Market.USA, OptionStyle.European,
|
||||
OptionRight.Call, 1900m, thirdFriday);
|
||||
|
||||
var delta = new Delta(spxwSymbol, 0.005m, 0.02m, optionModel: OptionPricingModelType.BlackScholes,
|
||||
ivModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var currentTime = new DateTime(2016, 02, 19, 10, 0, 0);
|
||||
delta.Update(new IndicatorDataPoint(spxwSymbol, currentTime, 20m));
|
||||
delta.Update(new IndicatorDataPoint(spxwSymbol.Underlying, currentTime, 1900m));
|
||||
|
||||
Assert.IsTrue(delta.IsReady);
|
||||
Assert.IsTrue(delta.ImpliedVolatility.Current.Value > 0);
|
||||
Assert.IsTrue(delta.Current.Value > 0);
|
||||
}
|
||||
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 0)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 5)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 10)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 15)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 0)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 5)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 10)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, 15)]
|
||||
public void CanComputeOnExpirationDate(decimal price, decimal spotPrice, OptionRight right, int hoursAfterExpiryDate)
|
||||
{
|
||||
var expiration = new DateTime(2024, 12, 6);
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right, 450m, expiration);
|
||||
var indicator = new Delta(symbol, 0.0403m, 0.0m,
|
||||
optionModel: OptionPricingModelType.BinomialCoxRossRubinstein, ivModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var currentTime = expiration.AddHours(hoursAfterExpiryDate);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, currentTime, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, currentTime, spotPrice);
|
||||
|
||||
Assert.IsFalse(indicator.Update(optionDataPoint));
|
||||
Assert.IsTrue(indicator.Update(spotDataPoint));
|
||||
|
||||
Assert.AreNotEqual(0, indicator.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators;
|
||||
|
||||
[TestFixture]
|
||||
public class DerivativeOscillatorIndicatorTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new DerivativeOscillator("D014", 14, 5, 3, 9);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_do.csv";
|
||||
|
||||
protected override string TestColumnName => "DO";
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var derivativeOscillator = new DerivativeOscillator("D014", 14, 5, 3, 9);
|
||||
var period = derivativeOscillator.WarmUpPeriod;
|
||||
|
||||
// Generate a stream of random data and calculate the derivative oscillator
|
||||
var seed = 14;
|
||||
Random rand = new Random(seed);
|
||||
var reference = DateTime.Today;
|
||||
for(int i = 0; i < period; i++)
|
||||
{
|
||||
var data = new IndicatorDataPoint(reference.AddSeconds(i), rand.Next());
|
||||
derivativeOscillator.Update(data);
|
||||
}
|
||||
|
||||
var expected = derivativeOscillator.Current.Value;
|
||||
|
||||
Assert.IsTrue(derivativeOscillator.IsReady);
|
||||
Assert.AreNotEqual(0m, derivativeOscillator.Current.Value);
|
||||
Assert.AreNotEqual(0, derivativeOscillator.Samples);
|
||||
|
||||
// Now do some partial updates
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
var data = new IndicatorDataPoint(reference.AddSeconds(i), rand.Next());
|
||||
derivativeOscillator.Update(data);
|
||||
}
|
||||
|
||||
// Check if reset functionality works
|
||||
derivativeOscillator.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(derivativeOscillator);
|
||||
|
||||
// Update using the exact same random data stream again to verify if internals were reset properly
|
||||
// But now insert data until it is ready.
|
||||
rand = new Random(seed);
|
||||
reference = DateTime.Today;
|
||||
int j = 0;
|
||||
while (!derivativeOscillator.IsReady)
|
||||
{
|
||||
var data = new IndicatorDataPoint(reference.AddSeconds(j), rand.Next());
|
||||
derivativeOscillator.Update(data);
|
||||
j++;
|
||||
}
|
||||
|
||||
Assert.IsTrue(derivativeOscillator.IsReady);
|
||||
Assert.AreNotEqual(0m, derivativeOscillator.Current.Value);
|
||||
Assert.AreNotEqual(0, derivativeOscillator.Samples);
|
||||
|
||||
// If they are not equal, the internal indicators were not reset properly
|
||||
Assert.AreEqual(expected, derivativeOscillator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalData()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
TestColumnName,
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((DerivativeOscillator)ind).Current.Value, 1e-9)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DetrendedPriceOscillatorTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override string TestColumnName => "DPO";
|
||||
|
||||
protected override string TestFileName => "spy_dpo.csv";
|
||||
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new DetrendedPriceOscillator(period: 21);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DonchianChannelTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new DonchianChannel(50);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_don50.txt";
|
||||
|
||||
protected override string TestColumnName => "Donchian Channels 50 Mean";
|
||||
|
||||
[Test]
|
||||
public void CompareAgainstExternalDataForUpperBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Donchian Channels 50 Top",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ((DonchianChannel) ind).UpperBand.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CompareAgainstExternalDataForLowerBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Donchian Channels 50 Bottom",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double) ((DonchianChannel) ind).LowerBand.Current.Value)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DoubleExponentialMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new DoubleExponentialMovingAverage(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_dema.txt";
|
||||
|
||||
protected override string TestColumnName => "DEMA_5";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class DualSymbolIndicatorTests
|
||||
{
|
||||
private DateTime _reference = new DateTime(2020, 1, 1);
|
||||
|
||||
[Test]
|
||||
public void TimeMovesForward()
|
||||
{
|
||||
var indicator = new TestAverageIndicator(Symbols.IBM, Symbols.SPY, 5);
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 500, Time = _reference.AddDays(1 + i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateCalculation()
|
||||
{
|
||||
var indicator = new TestAverageIndicator(Symbols.AAPL, Symbols.SPX, 3);
|
||||
|
||||
var bars = new List<TradeBar>()
|
||||
{
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 10, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 35, Time = _reference.AddDays(1), EndTime = _reference.AddDays(2) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2),EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 2, Time = _reference.AddDays(2), EndTime = _reference.AddDays(3) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 15, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 80, Time = _reference.AddDays(3), EndTime = _reference.AddDays(4) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 4, Time = _reference.AddDays(4), EndTime = _reference.AddDays(5) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 37, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 90, Time = _reference.AddDays(5), EndTime = _reference.AddDays(6) },
|
||||
new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 105, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
new TradeBar() { Symbol = Symbols.SPX, Low = 1, High = 2, Volume = 100, Close = 302, Time = _reference.AddDays(6), EndTime = _reference.AddDays(7) },
|
||||
};
|
||||
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
indicator.Update(bar);
|
||||
}
|
||||
|
||||
var closeAAPL = new List<decimal>() { 10, 15, 90, 105 };
|
||||
var closeSPX = new List<decimal>() { 35, 80, 37, 302 };
|
||||
var expectedValue = 0m;
|
||||
for (var i = 0; i < closeAAPL.Count; i++)
|
||||
{
|
||||
expectedValue += (closeAAPL[i] + closeSPX[i]) / 2;
|
||||
}
|
||||
|
||||
Assert.AreEqual(expectedValue, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WorksWithDifferentTimeZones()
|
||||
{
|
||||
var indicator = new TestAverageIndicator(Symbols.SPY, Symbols.BTCUSD, 5);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 100, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.BTCUSD, Low = 1, High = 2, Volume = 100, Close = 100, Time = startTime, EndTime = endTime });
|
||||
}
|
||||
Assert.AreEqual(100 * 10, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TracksPreviousState()
|
||||
{
|
||||
var period = 5;
|
||||
var indicator = new TestAverageIndicator(Symbols.SPY, Symbols.AAPL, period);
|
||||
var previousValue = indicator.Current.Value;
|
||||
|
||||
// Update the indicator and verify the previous values
|
||||
for (var i = 1; i < 2 * period; i++)
|
||||
{
|
||||
var startTime = _reference.AddDays(1 + i);
|
||||
var endTime = startTime.AddDays(1);
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.SPY, Low = 1, High = 2, Volume = 100, Close = 1000 + i * 10, Time = startTime, EndTime = endTime });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Close = 1000 + (i * 15), Time = startTime, EndTime = endTime });
|
||||
// Verify the previous value matches the indicator's previous value
|
||||
Assert.AreEqual(previousValue, indicator.Previous.Value);
|
||||
|
||||
// Update previousValue to the current value for the next iteration
|
||||
previousValue = indicator.Current.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestAverageIndicator : DualSymbolIndicator<IBaseDataBar>
|
||||
{
|
||||
public TestAverageIndicator(Symbol targetSymbol, Symbol referenceSymbol, int period)
|
||||
: base("TestIndicator", targetSymbol, referenceSymbol, period)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsReady => TargetDataPoints.IsReady && ReferenceDataPoints.IsReady;
|
||||
|
||||
protected override decimal ComputeIndicator()
|
||||
{
|
||||
var prevValue = IndicatorValue;
|
||||
var result = IndicatorValue += (TargetDataPoints[0].Close + ReferenceDataPoints[0].Close) / 2;
|
||||
Console.WriteLine($"Previous Value: {prevValue}, Current Value: {IndicatorValue} (Inputs: {TargetDataPoints[^1]} and {ReferenceDataPoints[^1]})");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class EaseOfMovementValueTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
// Even if the indicator is ready, there may be zero values
|
||||
ValueCanBeZero = true;
|
||||
RenkoBarSize = 0.5m;
|
||||
return new EaseOfMovementValue();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_emv.txt";
|
||||
|
||||
protected override string TestColumnName => "EMV";
|
||||
|
||||
[Test]
|
||||
public void TestTradeBarsWithVolume()
|
||||
{
|
||||
var emv = new EaseOfMovementValue();
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = data.Value,
|
||||
Close = data.Value,
|
||||
High = data.Value,
|
||||
Low = data.Value,
|
||||
Volume = data.Value
|
||||
};
|
||||
emv.Update(tradeBar);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Action<IndicatorBase<TradeBar>, double> Assertion
|
||||
{
|
||||
get { return (indicator, expected) => Assert.AreEqual(expected, (double)indicator.Current.Value, 1); }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void PeriodSet()
|
||||
{
|
||||
var emv = new EaseOfMovementValue(period: 3, scale: 1);
|
||||
var reference = System.DateTime.Today;
|
||||
|
||||
emv.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 1, High = 2, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
Assert.AreEqual(0, emv.Current.Value);
|
||||
Assert.IsFalse(emv.IsReady);
|
||||
|
||||
emv.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 3, High = 4, Volume = 200, Time = reference.AddMinutes(2) });
|
||||
Assert.AreEqual(0.005, (double)emv.Current.Value, 0.00001);
|
||||
Assert.IsFalse(emv.IsReady);
|
||||
|
||||
emv.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 5, High = 6, Volume = 300, Time = reference.AddMinutes(3) });
|
||||
Assert.AreEqual(0.00556, (double)emv.Current.Value, 0.00001);
|
||||
Assert.IsTrue(emv.IsReady);
|
||||
|
||||
emv.Update(new TradeBar() { Symbol = Symbols.AAPL, Low = 6, High = 7, Volume = 400, Time = reference.AddMinutes(4) });
|
||||
Assert.AreEqual(0.00639, (double)emv.Current.Value, 0.00001);
|
||||
Assert.IsTrue(emv.IsReady);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because the bars it's receiving are the same.
|
||||
/// Therefore we skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class ExponentialMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new ExponentialMovingAverage(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_ema.csv";
|
||||
|
||||
protected override string TestColumnName => "EMA14";
|
||||
|
||||
[Test]
|
||||
public void EmaComputesCorrectly()
|
||||
{
|
||||
const int period = 4;
|
||||
decimal[] values = { 1m, 10m, 100m, 1000m, 2000m, 3000m, 4000m, 5000m, 6000m, 7000m, 8000m, 9000m, 10000m };
|
||||
const decimal expFactor = 2m/(1m + period);
|
||||
|
||||
var ema4 = new ExponentialMovingAverage(period);
|
||||
|
||||
decimal expectedCurrent = 0m;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
ema4.Update(new IndicatorDataPoint(DateTime.UtcNow.AddSeconds(i), values[i]));
|
||||
if (i == period - 1)
|
||||
{
|
||||
// The indicator is ready after the first full period, the first value should be a SMA of the first period
|
||||
expectedCurrent = values.Take(period).Sum() / period;
|
||||
}
|
||||
if (i >= period)
|
||||
{
|
||||
expectedCurrent = values[i] * expFactor + (1 - expFactor) * expectedCurrent;
|
||||
}
|
||||
Assert.AreEqual(expectedCurrent, ema4.Current.Value, $"Index: {i}");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
// ema reset is just setting the value and samples back to 0
|
||||
var ema = new ExponentialMovingAverage(3);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(5))
|
||||
{
|
||||
ema.Update(data);
|
||||
}
|
||||
Assert.IsTrue(ema.IsReady);
|
||||
Assert.AreNotEqual(0m, ema.Current.Value);
|
||||
Assert.AreNotEqual(0, ema.Samples);
|
||||
|
||||
ema.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(ema);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
/// <summary>
|
||||
/// Test class for QuantConnect.Indicators.FilteredIdentity
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class FilteredIdentityTests
|
||||
{
|
||||
[TestCase("lambda")]
|
||||
[TestCase("function")]
|
||||
public void FilteredIdentityWorksWithPythonFilter(string filterType)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
string filterCode = filterType == "lambda"
|
||||
? "filter = lambda x: x.Close > x.Open"
|
||||
: "filter = filter";
|
||||
|
||||
string functionCode = filterType == "function"
|
||||
? @"
|
||||
def filter(data):
|
||||
return data.Close > data.Open
|
||||
"
|
||||
: "";
|
||||
|
||||
var testModule = PyModule.FromString("TestFilteredIdentity",
|
||||
$@"
|
||||
from AlgorithmImports import *
|
||||
from QuantConnect.Tests import *
|
||||
|
||||
{functionCode}
|
||||
|
||||
def test_filtered_identity():
|
||||
test = FilteredIdentity(Symbols.SPY, {filterCode})
|
||||
tradeBar1 = TradeBar()
|
||||
tradeBar1.Close = 100
|
||||
tradeBar1.Open = 50
|
||||
tradeBar2 = TradeBar()
|
||||
tradeBar2.Close = 20
|
||||
tradeBar2.Open = 50
|
||||
tradeBar3 = TradeBar()
|
||||
tradeBar3.Close = 300
|
||||
tradeBar3.Open = 50
|
||||
test.Update(tradeBar1)
|
||||
test.Update(tradeBar2)
|
||||
test.Update(tradeBar3)
|
||||
return test
|
||||
");
|
||||
|
||||
var test = testModule.GetAttr("test_filtered_identity").Invoke();
|
||||
var filteredIdentity = test.As<FilteredIdentity>();
|
||||
Assert.AreEqual(3, filteredIdentity.Samples);
|
||||
Assert.AreEqual(300, filteredIdentity.Current.Value);
|
||||
Assert.AreEqual(100, filteredIdentity.Previous.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class FisherTransformTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new FisherTransform(10);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_fisher.txt";
|
||||
|
||||
protected override string TestColumnName => "Fisher Transform 10";
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ForceIndexTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
// VolumeRenkoBarSize = 0.5m; // when uncommented test AcceptsVolumeRenkoBarsAsInput in hanging
|
||||
return new ForceIndex(20);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_ForceIndex.csv";
|
||||
|
||||
protected override string TestColumnName => "ForceIndex20";
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class FractalAdaptiveMovingAverageTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new FractalAdaptiveMovingAverage(16);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "frama.txt";
|
||||
|
||||
protected override string TestColumnName => "Filt";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double) indicator.Current.Value, 0.006);
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var frama = new FractalAdaptiveMovingAverage(6);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(7))
|
||||
{
|
||||
frama.Update(new TradeBar { High = data.Value, Low = data.Value });
|
||||
}
|
||||
Assert.IsTrue(frama.IsReady);
|
||||
Assert.AreNotEqual(0m, frama.Current.Value);
|
||||
Assert.AreNotEqual(0, frama.Samples);
|
||||
|
||||
frama.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(frama);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class FunctionalIndicatorTests
|
||||
{
|
||||
[Test]
|
||||
public void ComputesDelegateCorrectly()
|
||||
{
|
||||
var func = new FunctionalIndicator<IndicatorDataPoint>("f", data => data.Value, @this => @this.Samples > 1, () => {/*no reset action required*/});
|
||||
func.Update(DateTime.Today, 1m);
|
||||
Assert.IsFalse(func.IsReady);
|
||||
Assert.AreEqual(1m, func.Current.Value);
|
||||
|
||||
func.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
Assert.IsTrue(func.IsReady);
|
||||
Assert.AreEqual(2m, func.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
var inner = new SimpleMovingAverage(2);
|
||||
var func = new FunctionalIndicator<IndicatorDataPoint>("f", data =>
|
||||
{
|
||||
inner.Update(data);
|
||||
return inner.Current.Value*2;
|
||||
},
|
||||
@this => inner.IsReady,
|
||||
() => inner.Reset()
|
||||
);
|
||||
|
||||
func.Update(DateTime.Today, 1m);
|
||||
func.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
Assert.IsTrue(func.IsReady);
|
||||
|
||||
func.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(inner);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.Algorithm;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class GammaTests : OptionBaseIndicatorTests<Gamma>
|
||||
{
|
||||
protected override IndicatorBase<IBaseData> CreateIndicator()
|
||||
=> new Gamma("testGammaIndicator", _symbol, 0.0403m, 0.0m);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel)
|
||||
=> new Gamma("testGammaIndicator", _symbol, riskFreeRateModel);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel)
|
||||
{
|
||||
var symbol = (SymbolList.Count > 0) ? SymbolList[0] : _symbol;
|
||||
return new Gamma("testGammaIndicator", symbol, riskFreeRateModel, dividendYieldModel);
|
||||
}
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(QCAlgorithm algorithm)
|
||||
=> algorithm.G(_symbol);
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
// 2 updates per iteration, 1 for greek, 1 for IV
|
||||
RiskFreeRateUpdatesPerIteration = 2;
|
||||
DividendYieldUpdatesPerIteration = 2;
|
||||
}
|
||||
|
||||
[TestCase("american/third_party_1_greeks.csv", true, false, 0.12)]
|
||||
[TestCase("american/third_party_1_greeks.csv", false, false, 0.12)]
|
||||
// Just placing the test and data here, we are unsure about the smoothing function and not going to reverse engineer
|
||||
[TestCase("american/third_party_2_greeks.csv", false, true, 10000, 0.002)]
|
||||
public void ComparesAgainstExternalData(string subPath, bool reset, bool singleContract, double errorRate, double errorMargin = 1e-4,
|
||||
int callColumn = 11, int putColumn = 10)
|
||||
{
|
||||
var path = Path.Combine("TestData", "greeksindicator", subPath);
|
||||
// skip last entry since for deep ITM, IV will not affect much on price. Thus root finding will not be optimizing a non-convex function.
|
||||
foreach (var line in File.ReadAllLines(path).Skip(3).SkipLast(1))
|
||||
{
|
||||
var items = line.Split(',');
|
||||
|
||||
var interestRate = Parse.Decimal(items[^2]);
|
||||
var dividendYield = Parse.Decimal(items[^1]);
|
||||
|
||||
var model = ParseSymbols(items, path.Contains("american"), out var call, out var put);
|
||||
|
||||
Gamma callIndicator;
|
||||
Gamma putIndicator;
|
||||
if (singleContract)
|
||||
{
|
||||
callIndicator = new Gamma(call, interestRate, dividendYield, optionModel: model);
|
||||
putIndicator = new Gamma(put, interestRate, dividendYield, optionModel: model);
|
||||
}
|
||||
else
|
||||
{
|
||||
callIndicator = new Gamma(call, interestRate, dividendYield, put, model);
|
||||
putIndicator = new Gamma(put, interestRate, dividendYield, call, model);
|
||||
}
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
|
||||
if (reset == true)
|
||||
{
|
||||
callIndicator.Reset();
|
||||
putIndicator.Reset();
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reference values from QuantLib
|
||||
[TestCase(23.753, 450.0, OptionRight.Call, 60, 0.0071, OptionStyle.European)]
|
||||
[TestCase(35.830, 450.0, OptionRight.Put, 60, 0.0042, OptionStyle.European)]
|
||||
[TestCase(33.928, 470.0, OptionRight.Call, 60, 0.0067, OptionStyle.European)]
|
||||
[TestCase(6.428, 470.0, OptionRight.Put, 60, 0.0083, OptionStyle.European)]
|
||||
[TestCase(3.219, 430.0, OptionRight.Call, 60, 0.0136, OptionStyle.European)]
|
||||
[TestCase(47.701, 430.0, OptionRight.Put, 60, 0.0042, OptionStyle.European)]
|
||||
[TestCase(16.528, 450.0, OptionRight.Call, 180, 0.0128, OptionStyle.European)]
|
||||
[TestCase(21.784, 450.0, OptionRight.Put, 180, 0.0059, OptionStyle.European)]
|
||||
[TestCase(35.207, 470.0, OptionRight.Call, 180, 0.0070, OptionStyle.European)]
|
||||
[TestCase(0.409, 470.0, OptionRight.Put, 180, 0.0057, OptionStyle.European)]
|
||||
[TestCase(2.642, 430.0, OptionRight.Call, 180, 0.0193, OptionStyle.European)]
|
||||
[TestCase(27.772, 430.0, OptionRight.Put, 180, 0.0073, OptionStyle.European)]
|
||||
[TestCase(23.753, 450.0, OptionRight.Call, 60, 0.0071, OptionStyle.American)]
|
||||
[TestCase(35.830, 450.0, OptionRight.Put, 60, 0.0042, OptionStyle.American)]
|
||||
[TestCase(33.928, 470.0, OptionRight.Call, 60, 0.0067, OptionStyle.American)]
|
||||
[TestCase(6.428, 470.0, OptionRight.Put, 60, 0.0083, OptionStyle.American)]
|
||||
[TestCase(3.219, 430.0, OptionRight.Call, 60, 0.0136, OptionStyle.American)]
|
||||
[TestCase(47.701, 430.0, OptionRight.Put, 60, 0.0042, OptionStyle.American)]
|
||||
[TestCase(16.528, 450.0, OptionRight.Call, 180, 0.0129, OptionStyle.American)]
|
||||
[TestCase(21.784, 450.0, OptionRight.Put, 180, 0.0059, OptionStyle.American)]
|
||||
[TestCase(35.207, 470.0, OptionRight.Call, 180, 0.0070, OptionStyle.American)]
|
||||
[TestCase(0.409, 470.0, OptionRight.Put, 180, 0.0058, OptionStyle.American)]
|
||||
[TestCase(2.642, 430.0, OptionRight.Call, 180, 0.0193, OptionStyle.American)]
|
||||
[TestCase(27.772, 430.0, OptionRight.Put, 180, 0.0073, OptionStyle.American)]
|
||||
public void ComparesAgainstExternalData2(decimal price, decimal spotPrice, OptionRight right, int expiry, double refGamma, OptionStyle style)
|
||||
{
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, style, right, 450m, _reference.AddDays(expiry));
|
||||
var model = style == OptionStyle.European ? OptionPricingModelType.BlackScholes : OptionPricingModelType.BinomialCoxRossRubinstein;
|
||||
var indicator = new Gamma(symbol, 0.0403m, 0.0m, optionModel: model, ivModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, _reference, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference, spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refGamma, (double)indicator.Current.Value, 0.0005d);
|
||||
}
|
||||
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.American, 0)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.American, 5)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.American, 10)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.American, 15)] // Expires at 16:00
|
||||
[TestCase(15.0, 450.0, OptionRight.Call, OptionStyle.American, 0)]
|
||||
[TestCase(15.0, 450.0, OptionRight.Call, OptionStyle.American, 5)]
|
||||
[TestCase(15.0, 450.0, OptionRight.Call, OptionStyle.American, 10)]
|
||||
[TestCase(15.0, 450.0, OptionRight.Call, OptionStyle.American, 15)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.European, 0)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.European, 5)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.European, 10)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, OptionStyle.European, 15)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, OptionStyle.European, 0)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, OptionStyle.European, 5)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, OptionStyle.European, 10)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, OptionStyle.European, 15)]
|
||||
public void CanComputeOnExpirationDate(decimal price, decimal spotPrice, OptionRight right, OptionStyle style, int hoursAfterExpiryDate)
|
||||
{
|
||||
var expiration = new DateTime(2024, 12, 6);
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, style, right, 450m, expiration);
|
||||
var model = style == OptionStyle.European ? OptionPricingModelType.BlackScholes : OptionPricingModelType.BinomialCoxRossRubinstein;
|
||||
var indicator = new Gamma(symbol, 0.0403m, 0.0m, optionModel: model, ivModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var currentTime = expiration.AddHours(hoursAfterExpiryDate);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, currentTime, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, currentTime, spotPrice);
|
||||
|
||||
Assert.IsFalse(indicator.Update(optionDataPoint));
|
||||
Assert.IsTrue(indicator.Update(spotDataPoint));
|
||||
|
||||
Assert.AreNotEqual(0, indicator.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class HeikinAshiTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
return new HeikinAshi();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_heikin_ashi.txt";
|
||||
|
||||
protected override string TestColumnName => "";
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
TestHelper.TestIndicator(new HeikinAshi(), TestFileName, "HA_Open", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Open.Current.Value, 1e-3));
|
||||
TestHelper.TestIndicator(new HeikinAshi(), TestFileName, "HA_High", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).High.Current.Value, 1e-3));
|
||||
TestHelper.TestIndicator(new HeikinAshi(), TestFileName, "HA_Low", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Low.Current.Value, 1e-3));
|
||||
TestHelper.TestIndicator(new HeikinAshi(), TestFileName, "HA_Close", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Close.Current.Value, 1e-3));
|
||||
TestHelper.TestIndicator(new HeikinAshi(), TestFileName, "Volume", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Volume.Current.Value, 1e-3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
for (var i = 1; i <= 2; i++)
|
||||
{
|
||||
TestHelper.TestIndicator(indicator, TestFileName, "HA_Open", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Open.Current.Value, 1e-3));
|
||||
indicator.Reset();
|
||||
TestHelper.TestIndicator(indicator, TestFileName, "HA_High", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).High.Current.Value, 1e-3));
|
||||
indicator.Reset();
|
||||
TestHelper.TestIndicator(indicator, TestFileName, "HA_Low", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Low.Current.Value, 1e-3));
|
||||
indicator.Reset();
|
||||
TestHelper.TestIndicator(indicator, TestFileName, "HA_Close", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Close.Current.Value, 1e-3));
|
||||
indicator.Reset();
|
||||
TestHelper.TestIndicator(indicator, TestFileName, "Volume", (ind, expected) => Assert.AreEqual(expected, (double)((HeikinAshi)ind).Volume.Current.Value, 1e-3));
|
||||
indicator.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators;
|
||||
|
||||
[TestFixture]
|
||||
public class HilbertTransformTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new HilbertTransform();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_hilbert.csv";
|
||||
|
||||
protected override string TestColumnName => "Close";
|
||||
|
||||
[Test]
|
||||
public void ComparesAgainstExternalDataInPhase()
|
||||
{
|
||||
var hilbertTransform = new HilbertTransform();
|
||||
TestHelper.TestIndicator(
|
||||
hilbertTransform,
|
||||
TestFileName,
|
||||
"InPhase",
|
||||
(_, expected) =>
|
||||
Assert.AreEqual(expected, (double)hilbertTransform.InPhase.Current.Value, 1e-3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesAgainstExternalDataQuadrature()
|
||||
{
|
||||
var hilbertTransform = new HilbertTransform();
|
||||
TestHelper.TestIndicator(
|
||||
hilbertTransform,
|
||||
TestFileName,
|
||||
"Quadrature",
|
||||
(actual, expected) =>
|
||||
Assert.AreEqual(expected, (double)hilbertTransform.Quadrature.Current.Value, 1e-3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var hti = new HilbertTransform(length: 2);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti.Quadrature);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti.InPhase);
|
||||
|
||||
hti.Update(DateTime.Today, 1m);
|
||||
hti.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
hti.Update(DateTime.Today.AddSeconds(2), 3m);
|
||||
hti.Update(DateTime.Today.AddSeconds(3), 1m);
|
||||
hti.Update(DateTime.Today.AddSeconds(4), 2m);
|
||||
hti.Update(DateTime.Today.AddSeconds(5), 3m);
|
||||
hti.Update(DateTime.Today.AddSeconds(6), 1m);
|
||||
hti.Update(DateTime.Today.AddSeconds(7), 2m);
|
||||
hti.Update(DateTime.Today.AddSeconds(8), 3m);
|
||||
Assert.IsTrue(hti.IsReady);
|
||||
|
||||
hti.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti.Quadrature);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(hti.InPhase);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class HullMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new HullMovingAverage(16);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_hma.txt";
|
||||
|
||||
protected override string TestColumnName => "HMA_16";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class HurstExponentTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new HurstExponent("HE", 252, 20);
|
||||
}
|
||||
protected override string TestFileName => "spy_hurst_exponent.csv";
|
||||
|
||||
protected override string TestColumnName => "hurst_exponent";
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowDivisionByZero()
|
||||
{
|
||||
var he = new HurstExponent(2);
|
||||
var date = new DateTime(2024, 12, 2, 12, 0, 0);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
Assert.DoesNotThrow(() => he.Update(date, 0m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class IchimokuKinkoHyoTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 0.1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new IchimokuKinkoHyo();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_ichimoku.csv";
|
||||
|
||||
protected override string TestColumnName => "Tenkan";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)indicator).Tenkan.Current.Value, 1e-3);
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataTenkanMaximum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"TenkanMaximum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).TenkanMaximum.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataTenkanMinimum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"TenkanMinimum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).TenkanMinimum.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataKijunMaximum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"KijunMaximum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).KijunMaximum.Current.Value)
|
||||
);
|
||||
}
|
||||
[Test]
|
||||
public void ComparesWithExternalDataKijunMinimum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"KijunMinimum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).KijunMinimum.Current.Value)
|
||||
);
|
||||
}
|
||||
[Test]
|
||||
public void ComparesWithExternalDataKijun()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Kijun",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).Kijun.Current.Value)
|
||||
);
|
||||
}
|
||||
[Test]
|
||||
public void ComparesWithExternalDataDelayedTenkanSenkouA()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"DelayedTenkanSenkouA",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).DelayedTenkanSenkouA.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataDelayedKijunSenkouA()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"DelayedKijunSenkouA",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).DelayedKijunSenkouA.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataSenkouA()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Senkou A",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).SenkouA.Current.Value)
|
||||
);
|
||||
}
|
||||
[Test]
|
||||
public void ComparesWithExternalDataSenkouBMaximum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"SenkouBMaximum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).SenkouBMaximum.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataSenkouBMinimum()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"SenkouBMinimum",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).SenkouBMinimum.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataDelayedMaximumSenkouB()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"DelayedMaximumSenkouB",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).DelayedMaximumSenkouB.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataDelayedMinimumSenkouB()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"DelayedMinimumSenkouB",
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)((IchimokuKinkoHyo)ind).DelayedMinimumSenkouB.Current.Value)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComponentsAreNonZeroWhenIndicatorIsReady()
|
||||
{
|
||||
var indicator = new IchimokuKinkoHyo(2, 3, 2, 4, 2, 2);
|
||||
var date = new DateTime(2017, 1, 1);
|
||||
|
||||
for (int i = 1; i <= indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
var tradeBar = new TradeBar(date + TimeSpan.FromDays(i), Symbols.SPY,
|
||||
100 * i, 200 * i, 100 * i, 200 * i, 500 * i);
|
||||
indicator.Update(tradeBar);
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0m, indicator.Tenkan.Current.Value);
|
||||
Assert.AreNotEqual(0m, indicator.Kijun.Current.Value);
|
||||
Assert.AreNotEqual(0m, indicator.SenkouA.Current.Value);
|
||||
Assert.AreNotEqual(0m, indicator.SenkouB.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
/// <summary>
|
||||
/// Test class for QuantConnect.Indicators.Identity
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class IdentityTests
|
||||
{
|
||||
[Test]
|
||||
public void TestIdentityInvariants()
|
||||
{
|
||||
// the invariants of the identity indicator is to be ready after
|
||||
// a single sample has been added, and always produce the same value
|
||||
// as the last ingested value
|
||||
|
||||
var identity = new Identity("test");
|
||||
Assert.IsFalse(identity.IsReady);
|
||||
|
||||
const decimal value = 1m;
|
||||
identity.Update(new IndicatorDataPoint(DateTime.UtcNow, value));
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.AreEqual(value, identity.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
var identity = new Identity("test");
|
||||
Assert.IsFalse(identity.IsReady);
|
||||
Assert.AreEqual(0m, identity.Current.Value);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(2))
|
||||
{
|
||||
identity.Update(data);
|
||||
}
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.AreEqual(2, identity.Samples);
|
||||
|
||||
identity.Reset();
|
||||
|
||||
Assert.IsFalse(identity.IsReady);
|
||||
Assert.AreEqual(0, identity.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpProperly()
|
||||
{
|
||||
var identityIndicator = new Identity("Example");
|
||||
var time = new DateTime(2020, 8, 1);
|
||||
var period = ((IIndicatorWarmUpPeriodProvider)identityIndicator).WarmUpPeriod;
|
||||
|
||||
Assert.IsFalse(identityIndicator.IsReady);
|
||||
|
||||
for (var i = 0; i < period; i++)
|
||||
{
|
||||
identityIndicator.Update(time.AddDays(i), i);
|
||||
Assert.AreEqual(i == period - 1, identityIndicator.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class ImpliedVolatilityTests : OptionBaseIndicatorTests<ImpliedVolatility>
|
||||
{
|
||||
protected override IndicatorBase<IBaseData> CreateIndicator()
|
||||
=> new ImpliedVolatility("testImpliedVolatilityIndicator", _symbol, 0.053m, 0.0153m);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel)
|
||||
=> new ImpliedVolatility("testImpliedVolatilityIndicator", _symbol, riskFreeRateModel);
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel)
|
||||
{
|
||||
var symbol = (SymbolList.Count > 0) ? SymbolList[0] : _symbol;
|
||||
return new ImpliedVolatility("testImpliedVolatilityIndicator", symbol, riskFreeRateModel, dividendYieldModel);
|
||||
}
|
||||
|
||||
protected override OptionIndicatorBase CreateIndicator(QCAlgorithm algorithm)
|
||||
=> algorithm.IV(_symbol);
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
RiskFreeRateUpdatesPerIteration = 1;
|
||||
DividendYieldUpdatesPerIteration = 1;
|
||||
}
|
||||
|
||||
[TestCase("american/third_party_1_greeks.csv", true, false, 0.08)]
|
||||
[TestCase("american/third_party_1_greeks.csv", false, false, 0.08)]
|
||||
// Just placing the test and data here, we are unsure about the smoothing function and not going to reverse engineer
|
||||
[TestCase("american/third_party_2_greeks.csv", false, true, 10000)]
|
||||
public void ComparesAgainstExternalData(string subPath, bool reset, bool singleContract, double errorRate, double errorMargin = 1e-4,
|
||||
int callColumn = 7, int putColumn = 6)
|
||||
{
|
||||
var path = Path.Combine("TestData", "greeksindicator", subPath);
|
||||
// skip last entry since for deep ITM, IV will not affect much on price. Thus root finding will not be optimizing a non-convex function.
|
||||
foreach (var line in File.ReadAllLines(path).Skip(3).SkipLast(1))
|
||||
{
|
||||
var items = line.Split(',');
|
||||
|
||||
var interestRate = Parse.Decimal(items[^2]);
|
||||
var dividendYield = Parse.Decimal(items[^1]);
|
||||
|
||||
var model = ParseSymbols(items, path.Contains("american"), out var call, out var put);
|
||||
|
||||
ImpliedVolatility callIndicator;
|
||||
ImpliedVolatility putIndicator;
|
||||
if (singleContract)
|
||||
{
|
||||
callIndicator = new ImpliedVolatility(call, interestRate, dividendYield, optionModel: model);
|
||||
putIndicator = new ImpliedVolatility(put, interestRate, dividendYield, optionModel: model);
|
||||
}
|
||||
else
|
||||
{
|
||||
callIndicator = new ImpliedVolatility(call, interestRate, dividendYield, put, model);
|
||||
putIndicator = new ImpliedVolatility(put, interestRate, dividendYield, call, model);
|
||||
}
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
|
||||
if (reset == true)
|
||||
{
|
||||
callIndicator.Reset();
|
||||
putIndicator.Reset();
|
||||
|
||||
RunTestIndicator(call, put, callIndicator, putIndicator, items, callColumn, putColumn, errorRate, errorMargin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(23.753, 27.651, 450.0, OptionRight.Call, 60, 0.309, 0.309)]
|
||||
[TestCase(33.928, 5.564, 470.0, OptionRight.Call, 60, 0.191, 0.279)]
|
||||
[TestCase(47.701, 10.213, 430.0, OptionRight.Put, 60, 0.247, 0.545)]
|
||||
public void SetSmoothingFunction(decimal price, decimal mirrorPrice, decimal spotPrice, OptionRight right, int expiry, double refIV1, double refIV2)
|
||||
{
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right, 450m, _reference.AddDays(expiry));
|
||||
var mirrorSymbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right == OptionRight.Call ? OptionRight.Put : OptionRight.Call,
|
||||
450m, _reference.AddDays(expiry));
|
||||
var indicator = new ImpliedVolatility(symbol, 0.0530m, 0.0153m, mirrorSymbol, OptionPricingModelType.BlackScholes);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, _reference, price);
|
||||
var mirrorOptionDataPoint = new IndicatorDataPoint(mirrorSymbol, _reference, mirrorPrice);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference, spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(mirrorOptionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refIV1, (double)indicator.Current.Value, 0.0025d);
|
||||
|
||||
indicator.SetSmoothingFunction((iv, mirrorIv) => iv);
|
||||
|
||||
optionDataPoint = new IndicatorDataPoint(symbol, _reference.AddMilliseconds(1), price);
|
||||
mirrorOptionDataPoint = new IndicatorDataPoint(mirrorSymbol, _reference.AddMilliseconds(1), mirrorPrice);
|
||||
spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference.AddMilliseconds(1), spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(mirrorOptionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refIV2, (double)indicator.Current.Value, 0.0035d);
|
||||
}
|
||||
|
||||
[TestCase(23.753, 27.651, 450.0, OptionRight.Call, 60, 0.309, 0.309)]
|
||||
[TestCase(33.928, 5.564, 470.0, OptionRight.Call, 60, 0.191, 0.279)]
|
||||
[TestCase(47.701, 10.213, 430.0, OptionRight.Put, 60, 0.247, 0.545)]
|
||||
public void SetPythonSmoothingFunction(decimal price, decimal mirrorPrice, decimal spotPrice, OptionRight right, int expiry, double refIV1, double refIV2)
|
||||
{
|
||||
using var _ = Py.GIL();
|
||||
var module = PyModule.FromString(Guid.NewGuid().ToString(), $@"
|
||||
def TestSmoothingFunction(iv: float, mirror_iv: float) -> float:
|
||||
return iv");
|
||||
var pythonSmoothingFunction = module.GetAttr("TestSmoothingFunction");
|
||||
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right, 450m, _reference.AddDays(expiry));
|
||||
var mirrorSymbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right == OptionRight.Call ? OptionRight.Put : OptionRight.Call,
|
||||
450m, _reference.AddDays(expiry));
|
||||
var indicator = new ImpliedVolatility(symbol, 0.0530m, 0.0153m, mirrorSymbol, OptionPricingModelType.BlackScholes);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, _reference, price);
|
||||
var mirrorOptionDataPoint = new IndicatorDataPoint(mirrorSymbol, _reference, mirrorPrice);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference, spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(mirrorOptionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refIV1, (double)indicator.Current.Value, 0.0025d);
|
||||
|
||||
indicator.SetSmoothingFunction(pythonSmoothingFunction);
|
||||
|
||||
optionDataPoint = new IndicatorDataPoint(symbol, _reference.AddMilliseconds(1), price);
|
||||
mirrorOptionDataPoint = new IndicatorDataPoint(mirrorSymbol, _reference.AddMilliseconds(1), mirrorPrice);
|
||||
spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference.AddMilliseconds(1), spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(mirrorOptionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refIV2, (double)indicator.Current.Value, 0.0035d);
|
||||
}
|
||||
|
||||
// Reference values from QuantLib
|
||||
[TestCase(23.753, 450.0, OptionRight.Call, 60, 0.309)]
|
||||
[TestCase(35.830, 450.0, OptionRight.Put, 60, 0.515)]
|
||||
[TestCase(33.928, 470.0, OptionRight.Call, 60, 0.279)]
|
||||
[TestCase(6.428, 470.0, OptionRight.Put, 60, 0.205)]
|
||||
[TestCase(3.219, 430.0, OptionRight.Call, 60, 0.133)]
|
||||
[TestCase(47.701, 430.0, OptionRight.Put, 60, 0.545)]
|
||||
[TestCase(16.528, 450.0, OptionRight.Call, 180, 0.097)]
|
||||
[TestCase(21.784, 450.0, OptionRight.Put, 180, 0.207)]
|
||||
[TestCase(35.207, 470.0, OptionRight.Call, 180, 0.140)]
|
||||
[TestCase(0.409, 470.0, OptionRight.Put, 180, 0.055)]
|
||||
[TestCase(2.642, 430.0, OptionRight.Call, 180, 0.057)]
|
||||
[TestCase(27.772, 430.0, OptionRight.Put, 180, 0.177)]
|
||||
public void ComparesAgainstExternalData2(decimal price, decimal spotPrice, OptionRight right, int expiry, double refIV)
|
||||
{
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right, 450m, _reference.AddDays(expiry));
|
||||
var indicator = new ImpliedVolatility(symbol, 0.0530m, 0.0153m, optionModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, _reference, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, _reference, spotPrice);
|
||||
indicator.Update(optionDataPoint);
|
||||
indicator.Update(spotDataPoint);
|
||||
|
||||
Assert.AreEqual(refIV, (double)indicator.Current.Value, 0.0036d);
|
||||
}
|
||||
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 0)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 5)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 10)]
|
||||
[TestCase(0.5, 470.0, OptionRight.Put, 15)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 0)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 5)]
|
||||
[TestCase(15, 450.0, OptionRight.Call, 10)]
|
||||
[TestCase(0.5, 450.0, OptionRight.Call, 15)]
|
||||
public void CanComputeOnExpirationDate(decimal price, decimal spotPrice, OptionRight right, int hoursAfterExpiryDate)
|
||||
{
|
||||
var expiration = new DateTime(2024, 12, 6);
|
||||
var symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, right, 450m, expiration);
|
||||
var indicator = new ImpliedVolatility(symbol, 0.0530m, 0.0153m, optionModel: OptionPricingModelType.BlackScholes);
|
||||
|
||||
var currentTime = expiration.AddHours(hoursAfterExpiryDate);
|
||||
|
||||
var optionDataPoint = new IndicatorDataPoint(symbol, currentTime, price);
|
||||
var spotDataPoint = new IndicatorDataPoint(symbol.Underlying, currentTime, spotPrice);
|
||||
|
||||
Assert.IsFalse(indicator.Update(optionDataPoint));
|
||||
Assert.IsTrue(indicator.Update(spotDataPoint));
|
||||
|
||||
Assert.AreNotEqual(0, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = new ImpliedVolatility("testImpliedVolatilityIndicator", _symbol, 0.053m, 0.0153m);
|
||||
var warmUpPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!warmUpPeriod.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
// warmup period is 5 + 1
|
||||
for (var i = 1; i <= warmUpPeriod.Value; i++)
|
||||
{
|
||||
var time = _reference.AddDays(i);
|
||||
var price = 500m;
|
||||
var optionPrice = Math.Max(price - 450, 0) * 1.1m;
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, time, optionPrice));
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
}
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, time, price));
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * warmUpPeriod.Value, indicator.Samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Tests.Common;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class IndicatorBasedOptionPriceModelTests
|
||||
{
|
||||
[TestCase(true, 6.05392693521696, 0.3559978, 0.7560627, 0.0430897, 0.0663327, -1599.430292, 0.0000904)]
|
||||
[TestCase(false, 5.05414551764534, 0.1427122, 0.957485, 0.0311303, 0.020584, -163.902082, 0.0000057)]
|
||||
public void WorksWithAndWithoutMirrorContract([Values] bool withMirrorContract, decimal expectedTheoreticalPrice,
|
||||
decimal expectedIv, decimal expectedDelta, decimal expectedGamma, decimal expectedVega,
|
||||
decimal expectedTheta, decimal expectedRho)
|
||||
{
|
||||
GetTestData(true, true, withMirrorContract, out var option, out var contract, out var securities);
|
||||
|
||||
var model = new IndicatorBasedOptionPriceModel(securityProvider: securities);
|
||||
|
||||
var result = model.Evaluate(new OptionPriceModelParameters(option, null, contract));
|
||||
var theoreticalPrice = result.TheoreticalPrice;
|
||||
var iv = result.ImpliedVolatility;
|
||||
var greeks = result.Greeks;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreEqual(expectedTheoreticalPrice, theoreticalPrice);
|
||||
Assert.AreEqual(expectedIv, iv);
|
||||
Assert.AreEqual(expectedDelta, greeks.Delta);
|
||||
Assert.AreEqual(expectedGamma, greeks.Gamma);
|
||||
Assert.AreEqual(expectedVega, greeks.Vega);
|
||||
Assert.AreEqual(expectedTheta, greeks.Theta);
|
||||
Assert.AreEqual(expectedRho, greeks.Rho);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(false, false)]
|
||||
[TestCase(true, false)]
|
||||
[TestCase(false, true)]
|
||||
public void WontCalculateIfMissindData(bool withUnderlyingData, bool withOptionData)
|
||||
{
|
||||
GetTestData(withUnderlyingData, withOptionData, true, out var option, out var contract, out var securities);
|
||||
|
||||
var model = new IndicatorBasedOptionPriceModel(securityProvider: securities);
|
||||
var result = model.Evaluate(new OptionPriceModelParameters(option, null, contract));
|
||||
|
||||
Assert.AreEqual(OptionPriceModelResult.None, result);
|
||||
}
|
||||
|
||||
private static void GetTestData(bool withUnderlying, bool withOption, bool withMirrorOption,
|
||||
out Option option, out OptionContract contract, out SecurityManager securities)
|
||||
{
|
||||
var underlyingSymbol = Symbols.GOOG;
|
||||
var date = new DateTime(2015, 11, 24);
|
||||
var contractSymbol = Symbols.CreateOptionSymbol(underlyingSymbol.Value, OptionRight.Call, 745m, date);
|
||||
|
||||
var tz = TimeZones.NewYork;
|
||||
var underlyingPrice = 750m;
|
||||
var underlyingVolume = 10000;
|
||||
var contractPrice = 5.05m;
|
||||
var mirrorContractPrice = 1.05m;
|
||||
var underlying = OptionPriceModelTests.GetEquity(underlyingSymbol, 0m, underlyingVolume, tz);
|
||||
option = OptionPriceModelTests.GetOption(contractSymbol, underlying, tz);
|
||||
contract = OptionPriceModelTests.GetOptionContract(contractSymbol, underlyingSymbol, date);
|
||||
|
||||
var time = date.Add(new TimeSpan(9, 31, 0));
|
||||
var timeKeeper = new TimeKeeper(time.ConvertToUtc(tz));
|
||||
securities = new SecurityManager(timeKeeper);
|
||||
|
||||
if (withUnderlying)
|
||||
{
|
||||
var underlyingData = new Tick { Symbol = underlying.Symbol, Time = time, Value = underlyingPrice, Quantity = underlyingVolume, TickType = TickType.Trade };
|
||||
underlying.SetMarketPrice(underlyingData);
|
||||
securities.Add(underlying);
|
||||
}
|
||||
|
||||
if (withOption)
|
||||
{
|
||||
var contractData = new Tick { Symbol = contractSymbol, Time = time, Value = contractPrice, Quantity = 10, TickType = TickType.Trade };
|
||||
option.SetMarketPrice(contractData);
|
||||
securities.Add(option);
|
||||
}
|
||||
|
||||
if (withMirrorOption)
|
||||
{
|
||||
var mirrorContractSymbol = Symbol.CreateOption(contractSymbol.Underlying,
|
||||
contractSymbol.ID.Symbol,
|
||||
contractSymbol.ID.Market,
|
||||
contractSymbol.ID.OptionStyle,
|
||||
contractSymbol.ID.OptionRight == OptionRight.Call ? OptionRight.Put : OptionRight.Call,
|
||||
contractSymbol.ID.StrikePrice,
|
||||
contractSymbol.ID.Date);
|
||||
var mirrorContractData = new Tick { Symbol = mirrorContractSymbol, Time = time, Value = mirrorContractPrice, Quantity = 10, TickType = TickType.Trade };
|
||||
var mirrorOption = OptionPriceModelTests.GetOption(mirrorContractSymbol, underlying, tz);
|
||||
mirrorOption.SetMarketPrice(mirrorContractData);
|
||||
securities.Add(mirrorOption);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
using System.Linq;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Statistics;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class IndicatorExtensionsTests
|
||||
{
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void PipesDataUsingOfFromFirstToSecond()
|
||||
{
|
||||
var first = new SimpleMovingAverage(2);
|
||||
var second = new Delay(1);
|
||||
|
||||
// this is a configuration step, but returns the reference to the second for method chaining
|
||||
second.Of(first);
|
||||
|
||||
var data1 = new IndicatorDataPoint(DateTime.UtcNow, 1m);
|
||||
var data2 = new IndicatorDataPoint(DateTime.UtcNow, 2m);
|
||||
var data3 = new IndicatorDataPoint(DateTime.UtcNow, 3m);
|
||||
var data4 = new IndicatorDataPoint(DateTime.UtcNow, 4m);
|
||||
|
||||
// sma has one item
|
||||
first.Update(data1);
|
||||
Assert.IsFalse(first.IsReady);
|
||||
Assert.AreEqual(0m, second.Current.Value);
|
||||
|
||||
// sma is ready, delay will repeat this value
|
||||
first.Update(data2);
|
||||
Assert.IsTrue(first.IsReady);
|
||||
Assert.IsFalse(second.IsReady);
|
||||
Assert.AreEqual(1.5m, second.Current.Value);
|
||||
|
||||
// delay is ready, and repeats its first input
|
||||
first.Update(data3);
|
||||
Assert.IsTrue(second.IsReady);
|
||||
Assert.AreEqual(1.5m, second.Current.Value);
|
||||
|
||||
// now getting the delayed data
|
||||
first.Update(data4);
|
||||
Assert.AreEqual(2.5m, second.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void PipesDataFirstWeightedBySecond()
|
||||
{
|
||||
const int period = 4;
|
||||
var value = new Identity("Value");
|
||||
var weight = new Identity("Weight");
|
||||
|
||||
var third = value.WeightedBy(weight, period);
|
||||
|
||||
var data = Enumerable.Range(1, 10).ToList();
|
||||
var window = Enumerable.Reverse(data).Take(period);
|
||||
var current = window.Sum(x => 2 * x * x) / (decimal)window.Sum(x => x);
|
||||
|
||||
foreach (var item in data)
|
||||
{
|
||||
value.Update(new IndicatorDataPoint(DateTime.UtcNow, Convert.ToDecimal(2 * item)));
|
||||
weight.Update(new IndicatorDataPoint(DateTime.UtcNow, Convert.ToDecimal(item)));
|
||||
}
|
||||
|
||||
Assert.AreEqual(current, third.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void NewDataPushesToDerivedIndicators()
|
||||
{
|
||||
var identity = new Identity("identity");
|
||||
var sma = new SimpleMovingAverage(3);
|
||||
|
||||
identity.Updated += (sender, consolidated) =>
|
||||
{
|
||||
sma.Update(consolidated);
|
||||
};
|
||||
|
||||
identity.Update(DateTime.UtcNow, 1m);
|
||||
identity.Update(DateTime.UtcNow, 2m);
|
||||
Assert.IsFalse(sma.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 3m);
|
||||
Assert.IsTrue(sma.IsReady);
|
||||
Assert.AreEqual(2m, sma.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MultiChainSMA()
|
||||
{
|
||||
var identity = new Identity("identity");
|
||||
var delay = new Delay(2);
|
||||
|
||||
// create the SMA of the delay of the identity
|
||||
var sma = delay.Of(identity).SMA(2);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 1m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(sma.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 2m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(sma.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 3m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsFalse(sma.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 4m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsTrue(sma.IsReady);
|
||||
|
||||
Assert.AreEqual(1.5m, sma.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MultiChainEMA()
|
||||
{
|
||||
var identity = new Identity("identity");
|
||||
var delay = new Delay(2);
|
||||
|
||||
// create the EMA of chained methods
|
||||
var ema = delay.Of(identity).EMA(2, 1);
|
||||
|
||||
// Assert.IsTrue(ema. == 1);
|
||||
identity.Update(DateTime.UtcNow, 1m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(ema.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 2m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(ema.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 3m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsFalse(ema.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 4m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsTrue(ema.IsReady);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MultiChainMAX()
|
||||
{
|
||||
var identity = new Identity("identity");
|
||||
var delay = new Delay(2);
|
||||
|
||||
// create the MAX of the delay of the identity
|
||||
var max = delay.Of(identity).MAX(2);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 1m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(max.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 2m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(max.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 3m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsFalse(max.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 4m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsTrue(max.IsReady);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MultiChainMIN()
|
||||
{
|
||||
var identity = new Identity("identity");
|
||||
var delay = new Delay(2);
|
||||
|
||||
// create the MIN of the delay of the identity
|
||||
var min = delay.Of(identity).MIN(2);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 1m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(min.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 2m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsFalse(delay.IsReady);
|
||||
Assert.IsFalse(min.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 3m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsFalse(min.IsReady);
|
||||
|
||||
identity.Update(DateTime.UtcNow, 4m);
|
||||
Assert.IsTrue(identity.IsReady);
|
||||
Assert.IsTrue(delay.IsReady);
|
||||
Assert.IsTrue(min.IsReady);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void PlusAddsLeftAndRightAfterBothUpdated()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = left.Plus(right);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(2m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(2m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 3m);
|
||||
Assert.AreEqual(2m, composite.Current.Value);
|
||||
|
||||
right.Update(DateTime.Today, 4m);
|
||||
Assert.AreEqual(7m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void PlusAddsLeftAndConstant()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = left.Plus(5);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(6m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(7m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MinusSubtractsLeftAndRightAfterBothUpdated()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = left.Minus(right);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(0m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(0m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 3m);
|
||||
Assert.AreEqual(0m, composite.Current.Value);
|
||||
|
||||
right.Update(DateTime.Today, 4m);
|
||||
Assert.AreEqual(-1m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void MinusSubtractsLeftAndConstant()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = left.Minus(1);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(0m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void OverDividesLeftAndRightAfterBothUpdated()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = left.Over(right);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 3m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
right.Update(DateTime.Today, 4m);
|
||||
Assert.AreEqual(3m / 4m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void OverDividesLeftAndConstant()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = left.Over(2);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 4m);
|
||||
Assert.AreEqual(2m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void OverHandlesDivideByZero()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = left.Over(right);
|
||||
var updatedEventFired = false;
|
||||
composite.Updated += delegate { updatedEventFired = true; };
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
Assert.IsFalse(updatedEventFired);
|
||||
right.Update(DateTime.Today, 0m);
|
||||
Assert.IsFalse(updatedEventFired);
|
||||
|
||||
// submitting another update to right won't cause an update without corresponding update to left
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.IsFalse(updatedEventFired);
|
||||
left.Update(DateTime.Today, 1m);
|
||||
Assert.IsTrue(updatedEventFired);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void TimesMultipliesLeftAndRightAfterBothUpdated()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = left.Times(right);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
right.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 3m);
|
||||
Assert.AreEqual(1m, composite.Current.Value);
|
||||
|
||||
right.Update(DateTime.Today, 4m);
|
||||
Assert.AreEqual(12m, composite.Current.Value);
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void TimesMultipliesLeftAndConstant()
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = left.Times(10);
|
||||
|
||||
left.Update(DateTime.Today, 1m);
|
||||
Assert.AreEqual(10m, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2m);
|
||||
Assert.AreEqual(20m, composite.Current.Value);
|
||||
|
||||
}
|
||||
|
||||
[Test, Parallelizable(ParallelScope.Self)]
|
||||
public void WorksForIndicatorsOfDifferentTypes()
|
||||
{
|
||||
var indicatorA1 = new TestIndicatorA("1");
|
||||
var indicatorA2 = new TestIndicatorA("2");
|
||||
|
||||
indicatorA1.Over(indicatorA2);
|
||||
indicatorA1.Minus(indicatorA2);
|
||||
indicatorA1.Times(indicatorA2);
|
||||
indicatorA1.Plus(indicatorA2);
|
||||
indicatorA1.Of(indicatorA2);
|
||||
|
||||
var indicatorB1 = new TestIndicatorB("1");
|
||||
var indicatorB2 = new TestIndicatorB("2");
|
||||
indicatorB1.Over(indicatorB2);
|
||||
indicatorB1.Minus(indicatorB2);
|
||||
indicatorB1.Times(indicatorB2);
|
||||
indicatorB1.Plus(indicatorB2);
|
||||
indicatorB1.Of(indicatorB2);
|
||||
}
|
||||
|
||||
protected static TestCaseData[] IndicatorOfDifferentBaseCases()
|
||||
{
|
||||
// Helper for getting all permutations of the indicators listed below
|
||||
static IEnumerable<IEnumerable<T>>
|
||||
GetPermutations<T>(IEnumerable<T> list, int length)
|
||||
{
|
||||
if (length == 1) return list.Select(t => new T[] { t });
|
||||
return GetPermutations(list, length - 1)
|
||||
.SelectMany(t => list.Where(o => !t.Contains(o)),
|
||||
(t1, t2) => t1.Concat(new T[] { t2 }));
|
||||
}
|
||||
|
||||
// Define our indicators to test on
|
||||
var testIndicators = new IIndicator[]
|
||||
{
|
||||
new TestIndicator<BaseData>("BD"),
|
||||
new TestIndicator<QuoteBar>("QB"),
|
||||
new TestIndicator<TradeBar>("TB"),
|
||||
new TestIndicator<IndicatorDataPoint>("IDP")
|
||||
};
|
||||
|
||||
// Methods defined in CompositeTestRunner
|
||||
var methods = new string[]
|
||||
{
|
||||
"minus", "plus", "over", "times"
|
||||
};
|
||||
|
||||
// Create every combination of indicators
|
||||
var combinations = GetPermutations(testIndicators, 2);
|
||||
|
||||
// Create a case for each method with each combination of indicators
|
||||
var cases = new List<TestCaseData>();
|
||||
foreach (var combo in combinations)
|
||||
{
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var newCase = new TestCaseData(combo, method);
|
||||
cases.Add(newCase);
|
||||
}
|
||||
}
|
||||
|
||||
return cases.ToArray();
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(IndicatorOfDifferentBaseCases))]
|
||||
public void DifferentBaseIndicators(IEnumerable<IIndicator> indicators, string method)
|
||||
{
|
||||
CompositeTestRunner(indicators.ElementAt(0), indicators.ElementAt(1), method);
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(IndicatorOfDifferentBaseCases))]
|
||||
public void DifferentBaseIndicatorsPy(IEnumerable<IIndicator> indicators, string method)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
CompositeTestRunner(indicators.ElementAt(0).ToPython(), indicators.ElementAt(1).ToPython(), method);
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompositeTestRunner(dynamic left, dynamic right, string method)
|
||||
{
|
||||
// Reset before every test; the permutation setup in test cases uses the same instance for each permutation
|
||||
left.Reset();
|
||||
right.Reset();
|
||||
|
||||
double expected;
|
||||
CompositeIndicator compositeIndicator;
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case "minus":
|
||||
expected = -5; // 5 - 10
|
||||
compositeIndicator = IndicatorExtensions.Minus(left, right);
|
||||
break;
|
||||
case "plus":
|
||||
expected = 15; // 5 + 10
|
||||
compositeIndicator = IndicatorExtensions.Plus(left, right);
|
||||
break;
|
||||
case "over":
|
||||
expected = .5; // 5 / 10
|
||||
compositeIndicator = IndicatorExtensions.Over(left, right);
|
||||
break;
|
||||
case "times":
|
||||
expected = 50; // 5 * 10
|
||||
compositeIndicator = IndicatorExtensions.Times(left, right);
|
||||
break;
|
||||
default:
|
||||
Assert.Fail($"Method '{method}' not handled by this test, please implement");
|
||||
throw new ArgumentException($"Cannot proceed with test using method {method}");
|
||||
}
|
||||
|
||||
// Check our values are all zero
|
||||
Assert.AreEqual(0, (int)right.Current.Value);
|
||||
Assert.AreEqual(0, (int)left.Current.Value);
|
||||
Assert.AreEqual(0, compositeIndicator.Current.Value);
|
||||
|
||||
// Use our test indicator method to update left and right
|
||||
left.UpdateValue(5);
|
||||
right.UpdateValue(10);
|
||||
|
||||
// Check final expected values, this ensures that composites are updating correctly
|
||||
Assert.AreEqual(5, (int)left.Current.Value);
|
||||
Assert.AreEqual(10, (int)right.Current.Value);
|
||||
Assert.AreEqual(expected, compositeIndicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinusSubtractsLeftAndConstant_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = (IIndicator) IndicatorExtensions.Minus(left.ToPython(), 10);
|
||||
|
||||
left.Update(DateTime.Today, 1);
|
||||
Assert.AreEqual(-9, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2);
|
||||
Assert.AreEqual(-8, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PlusAddsLeftAndConstant_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = (IIndicator)IndicatorExtensions.Plus(left.ToPython(), 10);
|
||||
|
||||
left.Update(DateTime.Today, 1);
|
||||
Assert.AreEqual(11, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 2);
|
||||
Assert.AreEqual(12, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OverDivdesLeftAndConstant_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = (IIndicator)IndicatorExtensions.Over(left.ToPython(), 5);
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(2, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(4, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TimesMultipliesLeftAndConstant_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var composite = (IIndicator)IndicatorExtensions.Times(left.ToPython(), 5);
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(50, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(100, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TimesMultipliesLeftAndRight_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = (IIndicator)IndicatorExtensions.Times(left.ToPython(), right.ToPython());
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
right.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(100, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(100, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OverDividesLeftAndRight_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = (IIndicator)IndicatorExtensions.Over(left.ToPython(), right.ToPython());
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
right.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(1, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(1, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PlusAddsLeftAndRight_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = (IIndicator)IndicatorExtensions.Plus(left.ToPython(), right.ToPython());
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
right.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(20, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(20, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinusSubstractsLeftAndRight_Py()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var left = new Identity("left");
|
||||
var right = new Identity("right");
|
||||
var composite = (IIndicator)IndicatorExtensions.Minus(left.ToPython(), right.ToPython());
|
||||
|
||||
left.Update(DateTime.Today, 10);
|
||||
right.Update(DateTime.Today, 10);
|
||||
Assert.AreEqual(0, composite.Current.Value);
|
||||
|
||||
left.Update(DateTime.Today, 20);
|
||||
Assert.AreEqual(0, composite.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RunPythonRegressionAlgorithmWithIndicatorExtensions()
|
||||
{
|
||||
var parameter = new RegressionTests.AlgorithmStatisticsTestParameters("IndicatorExtensionsSMAWithCustomIndicatorsRegressionAlgorithm",
|
||||
new Dictionary<string, string> {
|
||||
{PerformanceMetrics.TotalOrders, "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0.717"},
|
||||
{"Tracking Error", "0.593"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
},
|
||||
Language.Python,
|
||||
AlgorithmStatus.Completed);
|
||||
|
||||
AlgorithmRunner.RunLocalBacktest(parameter.Algorithm,
|
||||
parameter.Statistics,
|
||||
parameter.Language,
|
||||
parameter.ExpectedFinalStatus,
|
||||
initialCash: 100000);
|
||||
}
|
||||
|
||||
private class TestIndicatorA : IndicatorBase<IBaseData>
|
||||
{
|
||||
public TestIndicatorA(string name) : base(name)
|
||||
{
|
||||
}
|
||||
public override bool IsReady { get; }
|
||||
protected override decimal ComputeNextValue(IBaseData input)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestIndicatorB : IndicatorBase<IndicatorDataPoint>
|
||||
{
|
||||
public TestIndicatorB(string name) : base(name)
|
||||
{
|
||||
}
|
||||
public override bool IsReady
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
protected override decimal ComputeNextValue(IndicatorDataPoint input)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestIndicator<T> : IndicatorBase<T>
|
||||
where T : IBaseData
|
||||
{
|
||||
public TestIndicator(string name)
|
||||
: base(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool IsReady
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateValue(int value)
|
||||
{
|
||||
Current = new IndicatorDataPoint(DateTime.MinValue, value);
|
||||
OnUpdated(Current);
|
||||
}
|
||||
|
||||
protected override decimal ComputeNextValue(T input)
|
||||
{
|
||||
return input.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* 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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
/// <summary>
|
||||
/// Test class for QuantConnect.Indicators.Indicator
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class IndicatorTests
|
||||
{
|
||||
[Test]
|
||||
public void NameSaves()
|
||||
{
|
||||
// just testing that we get the right name out
|
||||
const string name = "name";
|
||||
var target = new TestIndicator(name);
|
||||
Assert.AreEqual(name, target.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UpdatesProperly()
|
||||
{
|
||||
// we want to make sure the initialized value is the default value
|
||||
// for a datapoint, and also verify the our indicator updates as we
|
||||
// expect it to, in this case, it should return identity
|
||||
var target = new TestIndicator();
|
||||
|
||||
Assert.AreEqual(DateTime.MinValue, target.Current.Time);
|
||||
Assert.AreEqual(0m, target.Current.Value);
|
||||
|
||||
var time = DateTime.UtcNow;
|
||||
var data = new IndicatorDataPoint(time, 1m);
|
||||
|
||||
target.Update(data);
|
||||
Assert.AreEqual(1m, target.Current.Value);
|
||||
|
||||
target.Update(new IndicatorDataPoint(time.AddMilliseconds(1), 2m));
|
||||
Assert.AreEqual(2m, target.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldNotThrowOnDifferentDataType()
|
||||
{
|
||||
var target = new TestIndicator();
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
target.Update(new Tick());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PassesOnDuplicateTimes()
|
||||
{
|
||||
var target = new TestIndicator();
|
||||
|
||||
var time = DateTime.UtcNow;
|
||||
|
||||
const decimal value1 = 1m;
|
||||
var data = new IndicatorDataPoint(time, value1);
|
||||
target.Update(data);
|
||||
Assert.AreEqual(value1, target.Current.Value);
|
||||
|
||||
// this won't update because we told it to ignore duplicate
|
||||
// data based on time
|
||||
target.Update(data);
|
||||
Assert.AreEqual(value1, target.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SortsTheSameAsDecimalDescending()
|
||||
{
|
||||
int count = 100;
|
||||
var targets = Enumerable.Range(0, count)
|
||||
.Select(x => new TestIndicator(x.ToString(CultureInfo.InvariantCulture)))
|
||||
.ToList();
|
||||
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
targets[i].Update(DateTime.Today, i);
|
||||
}
|
||||
|
||||
var expected = Enumerable.Range(0, count)
|
||||
.Select(x => (decimal)x)
|
||||
.OrderByDescending(x => x)
|
||||
.ToList();
|
||||
|
||||
var actual = targets.OrderByDescending(x => x).ToList();
|
||||
foreach (var pair in expected.Zip<decimal, TestIndicator, Tuple<decimal, TestIndicator>>(actual, Tuple.Create))
|
||||
{
|
||||
Assert.AreEqual(pair.Item1, pair.Item2.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SortsTheSameAsDecimalAsecending()
|
||||
{
|
||||
int count = 100;
|
||||
var targets = Enumerable.Range(0, count).Select(x => new TestIndicator(x.ToString(CultureInfo.InvariantCulture))).ToList();
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
targets[i].Update(DateTime.Today, i);
|
||||
}
|
||||
|
||||
var expected = Enumerable.Range(0, count).Select(x => (decimal)x).OrderBy(x => x).ToList();
|
||||
var actual = targets.OrderBy(x => x).ToList();
|
||||
foreach (var pair in expected.Zip<decimal, TestIndicator, Tuple<decimal, TestIndicator>>(actual, Tuple.Create))
|
||||
{
|
||||
Assert.AreEqual(pair.Item1, pair.Item2.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparisonFunctions()
|
||||
{
|
||||
TestComparisonOperators<int>();
|
||||
TestComparisonOperators<long>();
|
||||
TestComparisonOperators<float>();
|
||||
TestComparisonOperators<double>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EqualsMethodShouldNotThrowExceptions()
|
||||
{
|
||||
var indicator = new TestIndicator();
|
||||
var res = true;
|
||||
try
|
||||
{
|
||||
res = indicator.Equals(new Exception(""));
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
Assert.Fail();
|
||||
}
|
||||
Assert.IsFalse(res);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndicatorMustBeEqualToItself()
|
||||
{
|
||||
var indicators = typeof(Indicator).Assembly.GetTypes()
|
||||
.Where(t => t.BaseType.Name != "CandlestickPattern" && !t.Name.StartsWith("<"))
|
||||
.OrderBy(t => t.Name)
|
||||
.ToList();
|
||||
|
||||
var counter = 0;
|
||||
object instantiatedIndicator;
|
||||
foreach (var indicator in indicators)
|
||||
{
|
||||
try
|
||||
{
|
||||
instantiatedIndicator = Activator.CreateInstance(indicator, new object[] { 10 });
|
||||
counter++;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Some indicators will fail because they don't have a single-parameter constructor.
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.IsTrue(instantiatedIndicator.Equals(instantiatedIndicator));
|
||||
var anotherInstantiatedIndicator = Activator.CreateInstance(indicator, new object[] { 10 });
|
||||
Assert.IsFalse(instantiatedIndicator.Equals(anotherInstantiatedIndicator));
|
||||
}
|
||||
Log.Trace($"{counter} indicators out of {indicators.Count} were tested.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndicatorsOfDifferentTypeDiplaySameCurrentTime()
|
||||
{
|
||||
var algorithm = new QCAlgorithm();
|
||||
algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm));
|
||||
var spy = algorithm.AddEquity("SPY");
|
||||
|
||||
var indicatorTimeList = new List<DateTime>();
|
||||
// RSI is a DataPointIndicator
|
||||
algorithm.RSI(spy.Symbol, 14).Updated += (_, e) => indicatorTimeList.Add(e.EndTime);
|
||||
// STO is a BarIndicator
|
||||
algorithm.STO(spy.Symbol, 14, 2, 2).Updated += (_, e) => indicatorTimeList.Add(e.EndTime);
|
||||
// MFI is a TradeBarIndicator
|
||||
algorithm.MFI(spy.Symbol, 14).Updated += (_, e) => indicatorTimeList.Add(e.EndTime);
|
||||
|
||||
var consolidators = spy.Subscriptions.SelectMany(x => x.Consolidators).ToList();
|
||||
Assert.AreEqual(3, consolidators.Count); // One consolidator for each indicator
|
||||
|
||||
var bars = new[] { 30, 31 }.Select(d =>
|
||||
new TradeBar(new DateTime(2020, 03, 04, 9, d, 0),
|
||||
spy.Symbol, 100, 100, 100, 100, 1000));
|
||||
|
||||
foreach (var bar in bars)
|
||||
{
|
||||
foreach (var consolidator in consolidators)
|
||||
{
|
||||
consolidator.Update(bar);
|
||||
}
|
||||
}
|
||||
|
||||
// All indicators should have the same EndTime, with xx:31:00 & xx:32:00
|
||||
Assert.AreEqual(6, indicatorTimeList.Count);
|
||||
Assert.AreEqual(2, indicatorTimeList.Distinct().Count());
|
||||
Assert.AreEqual(3, indicatorTimeList.Count(x => x.Minute == 31));
|
||||
Assert.AreEqual(3, indicatorTimeList.Count(x => x.Minute == 32));
|
||||
}
|
||||
|
||||
[TestCase(2)]
|
||||
[TestCase(5)]
|
||||
[TestCase(10)]
|
||||
public void IndicatorKeepsHistory(int historyWindow)
|
||||
{
|
||||
var indicator = new TestIndicator("Test indicator");
|
||||
indicator.Window.Size = historyWindow;
|
||||
|
||||
var points = new List<IndicatorDataPoint>(100);
|
||||
var referenceDate = new DateTime(2023, 06, 12, 9, 0, 0);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
// The first iteration will not update the indicator. By default, first value is IndicatorDataPoint(DateTime.MinValue, 0)
|
||||
if (i == 0)
|
||||
{
|
||||
var defaultValue = new IndicatorDataPoint(DateTime.MinValue, 0);
|
||||
Assert.AreEqual(defaultValue, indicator.Current);
|
||||
Assert.AreEqual(defaultValue, indicator[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dateTime = referenceDate.AddMinutes(i);
|
||||
indicator.Update(dateTime, i);
|
||||
var expected = new IndicatorDataPoint(dateTime, i);
|
||||
|
||||
Assert.AreEqual(expected, indicator.Current);
|
||||
Assert.AreEqual(expected, indicator[0]);
|
||||
}
|
||||
|
||||
points.Insert(0, indicator[0]);
|
||||
var startIndex = Math.Max(0, i - historyWindow + 1);
|
||||
for (int j = startIndex; j <= i; j++)
|
||||
{
|
||||
Assert.AreEqual(points[i - j], indicator[i - j]);
|
||||
}
|
||||
|
||||
// Check the enumerator
|
||||
var windowPoints = indicator.ToList();
|
||||
var count = i - startIndex < historyWindow ? i - startIndex + 1 : historyWindow;
|
||||
CollectionAssert.AreEqual(points.GetRange(0, count), windowPoints);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HistoryWindowIsCorrectlyReset()
|
||||
{
|
||||
var indicator = new TestIndicator("Test indicator");
|
||||
indicator.Window.Size = 20;
|
||||
|
||||
// Update the indicator a few times
|
||||
var referenceDate = new DateTime(2023, 06, 12, 9, 0, 0);
|
||||
for (var i = 1; i < indicator.Window.Size; i++)
|
||||
{
|
||||
indicator.Update(referenceDate.AddMinutes(i - 1), i);
|
||||
}
|
||||
|
||||
Assert.AreEqual(indicator.Window.Size, indicator.Window.Count);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
// Window size is kept
|
||||
Assert.AreEqual(20, indicator.Window.Size);
|
||||
|
||||
// Window values are removed
|
||||
Assert.AreEqual(1, indicator.Window.Count);
|
||||
Assert.AreEqual(new IndicatorDataPoint(DateTime.MinValue, 0), indicator[0]);
|
||||
Assert.IsNull(indicator[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanAccessCurrentAndPreviousState()
|
||||
{
|
||||
var indicator = new TestIndicator("Test indicator");
|
||||
indicator.Window.Size = 10;
|
||||
|
||||
// Update the indicator a few times
|
||||
var referenceDate = new DateTime(2023, 06, 12, 9, 0, 0);
|
||||
var dataPoints = new List<IndicatorDataPoint>(indicator.Window.Size);
|
||||
for (var i = 0; i < indicator.Window.Size; i++)
|
||||
{
|
||||
var dateTime = referenceDate.AddMinutes(i);
|
||||
indicator.Update(dateTime, i);
|
||||
dataPoints.Add(new IndicatorDataPoint(dateTime, i));
|
||||
}
|
||||
|
||||
Assert.AreEqual(dataPoints[^1], indicator.Current);
|
||||
Assert.AreEqual(dataPoints[^1], indicator[0]);
|
||||
|
||||
Assert.AreEqual(dataPoints[^2], indicator.Previous);
|
||||
Assert.AreEqual(dataPoints[^2], indicator[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PreviousValueIsNotNullAtStart()
|
||||
{
|
||||
var indicator = new TestIndicator("Test indicator");
|
||||
|
||||
// Access current and previous without warming the indicator up
|
||||
var defaultValue = new IndicatorDataPoint(DateTime.MinValue, 0);
|
||||
Assert.IsNotNull(indicator.Current);
|
||||
Assert.AreEqual(defaultValue, indicator.Current);
|
||||
Assert.IsNotNull(indicator.Previous);
|
||||
Assert.AreEqual(defaultValue, indicator.Previous);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndicatorShouldRetainSymbolWhenUpdatedWithDifferentDataType()
|
||||
{
|
||||
var target = new TestIndicator();
|
||||
var date = new DateTime(2020, 1, 1);
|
||||
target.Update(new Tick(date, Symbols.SPY, 1, 1));
|
||||
Assert.AreEqual(Symbols.SPY, target.Current.Symbol);
|
||||
}
|
||||
|
||||
private static void TestComparisonOperators<TValue>()
|
||||
{
|
||||
var indicator = new TestIndicator();
|
||||
TestOperator(indicator, default(TValue), "GreaterThan", true, false);
|
||||
TestOperator(indicator, default(TValue), "GreaterThan", false, false);
|
||||
TestOperator(indicator, default(TValue), "GreaterThanOrEqual", true, true);
|
||||
TestOperator(indicator, default(TValue), "GreaterThanOrEqual", false, true);
|
||||
TestOperator(indicator, default(TValue), "LessThan", true, false);
|
||||
TestOperator(indicator, default(TValue), "LessThan", false, false);
|
||||
TestOperator(indicator, default(TValue), "LessThanOrEqual", true, true);
|
||||
TestOperator(indicator, default(TValue), "LessThanOrEqual", false, true);
|
||||
TestOperator(indicator, default(TValue), "Equality", true, true);
|
||||
TestOperator(indicator, default(TValue), "Equality", false, true);
|
||||
TestOperator(indicator, default(TValue), "Inequality", true, false);
|
||||
TestOperator(indicator, default(TValue), "Inequality", false, false);
|
||||
}
|
||||
|
||||
private static void TestOperator<TIndicator, TValue>(TIndicator indicator, TValue value, string opName, bool tvalueIsFirstParm, bool expected)
|
||||
{
|
||||
var method = GetOperatorMethodInfo<TValue>(opName, tvalueIsFirstParm ? 0 : 1);
|
||||
var ctIndicator = Expression.Constant(indicator);
|
||||
var ctValue = Expression.Constant(value);
|
||||
var call = tvalueIsFirstParm ? Expression.Call(method, ctValue, ctIndicator) : Expression.Call(method, ctIndicator, ctValue);
|
||||
var lamda = Expression.Lambda<Func<bool>>(call);
|
||||
var func = lamda.Compile();
|
||||
Assert.AreEqual(expected, func());
|
||||
}
|
||||
|
||||
private static MethodInfo GetOperatorMethodInfo<T>(string @operator, int argIndex)
|
||||
{
|
||||
var methodName = "op_" + @operator;
|
||||
var method =
|
||||
typeof(IndicatorBase).GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.SingleOrDefault(x => x.Name == methodName && x.GetParameters()[argIndex].ParameterType == typeof(T));
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
Assert.Fail("Failed to find method for " + @operator + " of type " + typeof(T).Name + " at index: " + argIndex);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private class TestIndicator : Indicator
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Indicator class using the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of this indicator</param>
|
||||
public TestIndicator(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Indicator class using the name "test"
|
||||
/// </summary>
|
||||
public TestIndicator()
|
||||
: base("test")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag indicating when this indicator is ready and fully initialized
|
||||
/// </summary>
|
||||
public override bool IsReady
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the next value of this indicator from the given state
|
||||
/// </summary>
|
||||
/// <param name="input">The input given to the indicator</param>
|
||||
/// <returns>A new value for this indicator</returns>
|
||||
protected override decimal ComputeNextValue(IndicatorDataPoint input)
|
||||
{
|
||||
return input.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class InternalBarStrengthTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
return new InternalBarStrength("IBS");
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_ibs.csv";
|
||||
|
||||
protected override string TestColumnName => "IBS";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class KaufmanAdaptiveMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new KaufmanAdaptiveMovingAverage(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_kama.txt";
|
||||
|
||||
protected override string TestColumnName => "KAMA_5";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class KaufmanEfficiencyRatioTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new KaufmanEfficiencyRatio(10);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_ker.txt";
|
||||
|
||||
protected override string TestColumnName => "KER";
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class KeltnerChannelsTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new KeltnerChannels(20, 1.5m);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_keltner.csv";
|
||||
|
||||
protected override string TestColumnName => "Middle Band";
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataUpperBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Keltner Channels 20 Top",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
expected,
|
||||
(double) ((KeltnerChannels) ind).UpperBand.Current.Value,
|
||||
1e-3
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataLowerBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Keltner Channels 20 Bottom",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
expected,
|
||||
(double) ((KeltnerChannels) ind).LowerBand.Current.Value,
|
||||
1e-3
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesTimeStampBetweenKeltnerChannelAndMiddleBand()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator(),
|
||||
TestFileName,
|
||||
"Middle Band",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
((KeltnerChannels)ind).Current.EndTime,
|
||||
((KeltnerChannels)ind).MiddleBand.Current.EndTime
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var kch = CreateIndicator() as KeltnerChannels;
|
||||
foreach (var data in TestHelper.GetTradeBarStream(TestFileName, false))
|
||||
{
|
||||
kch.Update(data);
|
||||
}
|
||||
|
||||
Assert.IsTrue(kch.IsReady);
|
||||
Assert.IsTrue(kch.UpperBand.IsReady);
|
||||
Assert.IsTrue(kch.LowerBand.IsReady);
|
||||
Assert.IsTrue(kch.MiddleBand.IsReady);
|
||||
Assert.IsTrue(kch.AverageTrueRange.IsReady);
|
||||
|
||||
kch.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(kch);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(kch.UpperBand);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(kch.LowerBand);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(kch.AverageTrueRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the Klinger Volume Oscillator (KVO) indicator
|
||||
/// </summary>
|
||||
public class KlingerVolumeOscillatorTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
/// <summary>
|
||||
/// Generated Klinger Volume Oscillator test data from talipp
|
||||
/// </summary>
|
||||
protected override string TestFileName => "spy_with_kvo.csv";
|
||||
|
||||
/// <summary>
|
||||
/// Generated column for KVO(5,10) from talipp
|
||||
/// </summary>
|
||||
protected override string TestColumnName => "KVO5_10";
|
||||
|
||||
/// <summary>
|
||||
/// Required by CommonIndicatorTests: return a fresh instance of your indicator.
|
||||
/// </summary>
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
|
||||
// match generated data from talipp
|
||||
return new KlingerVolumeOscillator(fastPeriod: 5, slowPeriod: 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This indicator doesn't accept Renko Bars as input. Skip this test.
|
||||
/// </summary>
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SignalLineIsReadyAfterWarmUpPeriod()
|
||||
{
|
||||
var indicator = CreateIndicator() as KlingerVolumeOscillator;
|
||||
Assert.IsFalse(indicator.Signal.IsReady);
|
||||
// Warm up the indicator
|
||||
for (int i = 0; i < indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar { Time = DateTime.UtcNow.AddDays(i), Close = 100 + i, Volume = 1000 });
|
||||
}
|
||||
Assert.IsTrue(indicator.Signal.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class KnowSureThingTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new KnowSureThing(5, 5, 10, 5, 15, 5, 25, 10, 9, MovingAverageType.Simple);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_kst.csv";
|
||||
|
||||
protected override string TestColumnName => "kst";
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataSignal()
|
||||
{
|
||||
TestHelper.TestIndicator(
|
||||
CreateIndicator() as KnowSureThing,
|
||||
TestFileName,
|
||||
"signal",
|
||||
ind => (double)ind.SignalLine.Current.Value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
/// <summary>
|
||||
/// Result tested vs. Python available at: http://tinyurl.com/o7redso
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class LeastSquaresMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new LeastSquaresMovingAverage(20);
|
||||
}
|
||||
|
||||
protected override string TestFileName => string.Empty;
|
||||
|
||||
protected override string TestColumnName => string.Empty;
|
||||
|
||||
#region Array input
|
||||
// Real AAPL minute data rounded to 2 decimals.
|
||||
public static decimal[] Prices =
|
||||
{
|
||||
125.99m, 125.91m, 125.75m, 125.62m, 125.54m, 125.45m, 125.47m,
|
||||
125.4m , 125.43m, 125.45m, 125.42m, 125.36m, 125.23m, 125.32m,
|
||||
125.26m, 125.31m, 125.41m, 125.5m , 125.51m, 125.41m, 125.54m,
|
||||
125.51m, 125.61m, 125.43m, 125.42m, 125.42m, 125.46m, 125.43m,
|
||||
125.4m , 125.35m, 125.3m , 125.28m, 125.21m, 125.37m, 125.32m,
|
||||
125.34m, 125.37m, 125.26m, 125.28m, 125.16m
|
||||
};
|
||||
#endregion Array input
|
||||
|
||||
#region Array expected
|
||||
public static decimal[] Expected =
|
||||
{
|
||||
125.99m , 125.91m , 125.75m , 125.62m , 125.54m , 125.45m ,
|
||||
125.47m , 125.4m , 125.43m , 125.45m , 125.42m , 125.36m ,
|
||||
125.23m , 125.32m , 125.26m , 125.31m , 125.41m , 125.5m ,
|
||||
125.51m , 125.2679m , 125.328m , 125.381m , 125.4423m, 125.4591m,
|
||||
125.4689m, 125.4713m, 125.4836m, 125.4834m, 125.4803m, 125.4703m,
|
||||
125.4494m, 125.4206m, 125.3669m, 125.3521m, 125.3214m, 125.2986m,
|
||||
125.2909m, 125.2723m, 125.2619m, 125.2224m,
|
||||
};
|
||||
#endregion Array input
|
||||
|
||||
protected override void RunTestIndicator(IndicatorBase<IndicatorDataPoint> indicator)
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
|
||||
for (var i = 0; i < Prices.Length; i++)
|
||||
{
|
||||
indicator.Update(time.AddMinutes(i), Prices[i]);
|
||||
Assert.AreEqual(Expected[i], Math.Round(indicator.Current.Value, 4));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var time = DateTime.Now;
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
indicator.Update(time.AddMinutes(i), Prices[i]);
|
||||
Assert.AreEqual(Expected[i], Math.Round(indicator.Current.Value, 4));
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady, "LeastSquaresMovingAverage Ready");
|
||||
indicator.Reset();
|
||||
TestHelper.AssertIndicatorIsInDefaultState(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var period = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!period.HasValue) return;
|
||||
|
||||
var time = DateTime.Now;
|
||||
|
||||
for (var i = 1; i < period.Value; i++)
|
||||
{
|
||||
indicator.Update(time.AddMinutes(i - 1), Prices[i - 1]);
|
||||
Assert.AreEqual(Expected[i - 1], Math.Round(indicator.Current.Value, 4));
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
}
|
||||
|
||||
indicator.Update(time.AddMinutes(period.Value - 1), Prices[period.Value - 1]);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class LinearWeightedMovingAverageTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override string TestFileName => "spx_lwma.csv";
|
||||
|
||||
protected override string TestColumnName => "LWMA";
|
||||
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new LinearWeightedMovingAverage(6);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
// See http://en.wikipedia.org/wiki/Moving_average
|
||||
// for the formula and the numbers in this test.
|
||||
public void ComputesCorrectly(int period)
|
||||
{
|
||||
var values = new[] {77m, 79m, 79m, 81m, 83m};
|
||||
var weights = Enumerable.Range(1, period).ToArray();
|
||||
var current = weights.Sum(i => i * values[i - 1]) / weights.Sum();
|
||||
|
||||
var lwma = new LinearWeightedMovingAverage(period);
|
||||
var time = DateTime.UtcNow;
|
||||
|
||||
for (var i = 0; i < period; i++)
|
||||
{
|
||||
lwma.Update(time.AddSeconds(i), values[i]);
|
||||
}
|
||||
Assert.AreEqual(current, lwma.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var lwma = new LinearWeightedMovingAverage(6);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(8))
|
||||
{
|
||||
lwma.Update(data);
|
||||
}
|
||||
Assert.IsTrue(lwma.IsReady);
|
||||
Assert.AreNotEqual(0m, lwma.Current.Value);
|
||||
Assert.AreNotEqual(0, lwma.Samples);
|
||||
|
||||
lwma.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(lwma);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
// See http://en.wikipedia.org/wiki/Moving_average
|
||||
// for the formula and the numbers in this test.
|
||||
public void WarmsUpProperly(int period)
|
||||
{
|
||||
var values = new[] { 77m, 79m, 79m, 81m, 83m };
|
||||
var weights = Enumerable.Range(1, period).ToArray();
|
||||
var current = weights.Sum(i => i * values[i - 1]) / weights.Sum();
|
||||
|
||||
var lwma = new LinearWeightedMovingAverage(period);
|
||||
var time = DateTime.UtcNow;
|
||||
|
||||
var warmUpPeriod = (lwma as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
for (var i = 0; i < warmUpPeriod; i++)
|
||||
{
|
||||
lwma.Update(time.AddSeconds(i), values[i]);
|
||||
Assert.AreEqual(i == warmUpPeriod - 1, lwma.IsReady);
|
||||
}
|
||||
Assert.AreEqual(current, lwma.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class LogReturnTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new LogReturn(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_logr14.txt";
|
||||
|
||||
protected override string TestColumnName => "LOGR14";
|
||||
|
||||
[Test]
|
||||
public void LOGRComputesCorrectly()
|
||||
{
|
||||
var period = 4;
|
||||
var logr = new LogReturn(period);
|
||||
var data = new[] { 1, 10, 100, 1000, 10000, 1234, 56789 };
|
||||
var seen = new List<int>();
|
||||
var time = DateTime.Now;
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var datum = data[i];
|
||||
var value0 = 0.0;
|
||||
|
||||
if (seen.Count >= 0 && seen.Count < period)
|
||||
{
|
||||
value0 = data[0];
|
||||
}
|
||||
else if (seen.Count >= period)
|
||||
{
|
||||
value0 = data[i - period];
|
||||
}
|
||||
|
||||
var expected = (decimal)Math.Log(datum / value0);
|
||||
|
||||
seen.Add(datum);
|
||||
logr.Update(time.AddSeconds(i), datum);
|
||||
Assert.AreEqual(expected, logr.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MassIndexTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 0.1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new MassIndex();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_mass_index_25.txt";
|
||||
|
||||
protected override string TestColumnName => "MassIndex";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MaximumTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new Maximum(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_max.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "MAX_5"; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
var max = new Maximum(3);
|
||||
|
||||
var reference = DateTime.MinValue;
|
||||
|
||||
max.Update(reference.AddDays(1), 1m);
|
||||
Assert.AreEqual(1m, max.Current.Value);
|
||||
Assert.AreEqual(0, max.PeriodsSinceMaximum);
|
||||
|
||||
max.Update(reference.AddDays(2), -1m);
|
||||
Assert.AreEqual(1m, max.Current.Value);
|
||||
Assert.AreEqual(1, max.PeriodsSinceMaximum);
|
||||
|
||||
max.Update(reference.AddDays(3), 0m);
|
||||
Assert.AreEqual(1m, max.Current.Value);
|
||||
Assert.AreEqual(2, max.PeriodsSinceMaximum);
|
||||
|
||||
max.Update(reference.AddDays(4), -2m);
|
||||
Assert.AreEqual(0m, max.Current.Value);
|
||||
Assert.AreEqual(1, max.PeriodsSinceMaximum);
|
||||
|
||||
max.Update(reference.AddDays(5), -2m);
|
||||
Assert.AreEqual(0m, max.Current.Value);
|
||||
Assert.AreEqual(2, max.PeriodsSinceMaximum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComputesCorrectlyMaximum()
|
||||
{
|
||||
const int period = 5;
|
||||
var max = new Maximum(period);
|
||||
|
||||
Assert.AreEqual(0m, max.Current.Value);
|
||||
|
||||
// test an increasing stream of data
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
max.Update(DateTime.Now.AddDays(i), i);
|
||||
Assert.AreEqual(i, max.Current.Value);
|
||||
Assert.AreEqual(0, max.PeriodsSinceMaximum);
|
||||
}
|
||||
|
||||
// test a decreasing stream of data
|
||||
for (int i = 0; i < period; i++)
|
||||
{
|
||||
max.Update(DateTime.Now.AddDays(period + i), period - i - 1);
|
||||
Assert.AreEqual(period - 1, max.Current.Value);
|
||||
Assert.AreEqual(i, max.PeriodsSinceMaximum);
|
||||
}
|
||||
|
||||
Assert.AreEqual(max.Period, max.PeriodsSinceMaximum + 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperlyMaximum()
|
||||
{
|
||||
var max = new Maximum(3);
|
||||
max.Update(DateTime.Today, 1m);
|
||||
max.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
max.Update(DateTime.Today.AddSeconds(2), 1m);
|
||||
Assert.IsTrue(max.IsReady);
|
||||
|
||||
max.Reset();
|
||||
Assert.AreEqual(0, max.PeriodsSinceMaximum);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(max);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
public interface ITestMcClellanOscillator
|
||||
{
|
||||
public void TestUpdate(IndicatorDataPoint input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Miscellaneous tool for McClellan Indicator test
|
||||
/// </summary>
|
||||
public class McClellanIndicatorTestHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Run test for McClellan Indicator
|
||||
/// </summary>
|
||||
/// <param name="indicator">McClellan Indicator instance</param>
|
||||
/// <param name="fileName">External source file name</param>
|
||||
/// <param name="columnName">External source reference column name</param>
|
||||
public static void RunTestIndicator<T>(T indicator, string fileName, string columnName)
|
||||
where T : TradeBarIndicator, ITestMcClellanOscillator
|
||||
{
|
||||
foreach (var parts in TestHelper.GetCsvFileStream(fileName))
|
||||
{
|
||||
parts.TryGetValue("a/d difference", out var adDifference);
|
||||
parts.TryGetValue("date", out var date);
|
||||
|
||||
var data = new IndicatorDataPoint(Parse.DateTimeExact(date, "yyyyMMdd"), adDifference.ToDecimal());
|
||||
indicator.TestUpdate(data);
|
||||
|
||||
if (!indicator.IsReady || !parts.TryGetValue(columnName, out var expected))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Source data has only 2 decimal places
|
||||
Assert.AreEqual(Parse.Double(expected), (double)indicator.Current.Value, 0.02d);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the given consolidator with the entries from the given external CSV file
|
||||
/// </summary>
|
||||
/// <param name="renkoConsolidator">RenkoConsoliadtor instance</param>
|
||||
/// <param name="fileName">External source file name</param>
|
||||
public static void UpdateRenkoConsolidator(IDataConsolidator renkoConsolidator, string fileName)
|
||||
{
|
||||
var closeValue = 1m;
|
||||
foreach (var parts in TestHelper.GetCsvFileStream(fileName))
|
||||
{
|
||||
parts.TryGetValue("a/d difference", out var adDifference);
|
||||
parts.TryGetValue("date", out var date);
|
||||
|
||||
var data = new TradeBar() { Symbol = Symbols.SPY, Close = closeValue, Open = closeValue - 1, Volume = 1, Time = Parse.DateTimeExact(date, "yyyyMMdd") };
|
||||
closeValue++;
|
||||
renkoConsolidator.Update(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the simulated number of advance and decline asset
|
||||
/// </summary>
|
||||
/// <param name="adDifference">Number of advancing asset minus that of declining ones</param>
|
||||
/// <param name="advance">Simulated number of advancing asset</param>
|
||||
/// <param name="decline">Simulated number of declining asset</param>
|
||||
public static bool GetAdvanceDeclineNumber(decimal adDifference, out int advance, out int decline)
|
||||
{
|
||||
// x + (3000 - x) = adDifference
|
||||
var simulatedAdvance = (adDifference + 2530m) / 2m;
|
||||
|
||||
// Both -0.5 if `simulatedAdvance` is not divisible by 2
|
||||
if (simulatedAdvance % 1 != 0)
|
||||
{
|
||||
advance = (int)Math.Floor(simulatedAdvance);
|
||||
decline = 2530 - advance - 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
advance = (int)simulatedAdvance;
|
||||
decline = 2530 - advance;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class McClellanOscillatorTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var mcClellanOscillator = new McClellanOscillator(19, 39);
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(mcClellanOscillator.Add);
|
||||
}
|
||||
else
|
||||
{
|
||||
mcClellanOscillator.Add(Symbols.MSFT);
|
||||
mcClellanOscillator.Add(Symbols.GOOG);
|
||||
mcClellanOscillator.Add(Symbols.AAPL);
|
||||
}
|
||||
return mcClellanOscillator;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL, Symbols.IBM];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = (McClellanOscillator)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 1; i <= indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(0m, indicator.Current.Value);
|
||||
Assert.AreEqual(indicator.WarmUpPeriod * 3, indicator.Samples);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var indicator = (McClellanOscillator)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 1; i <= indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
var indicator = new TestMcClellanOscillator();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
var indicator = new TestMcClellanOscillator();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
indicator.Reset();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new TestMcClellanOscillator();
|
||||
var renkoConsolidator = new RenkoConsolidator(0.5m);
|
||||
renkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
McClellanIndicatorTestHelper.UpdateRenkoConsolidator(renkoConsolidator, TestFileName);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
renkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new TestMcClellanOscillator();
|
||||
var volumeRenkoConsolidator = new VolumeRenkoConsolidator(0.5m);
|
||||
volumeRenkoConsolidator.DataConsolidated += (sender, volumeRenkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(volumeRenkoBar));
|
||||
};
|
||||
|
||||
McClellanIndicatorTestHelper.UpdateRenkoConsolidator(volumeRenkoConsolidator, TestFileName);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
volumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "mcclellan_data.csv";
|
||||
|
||||
protected override string TestColumnName => "MO";
|
||||
}
|
||||
|
||||
public class TestMcClellanOscillator : McClellanOscillator, ITestMcClellanOscillator
|
||||
{
|
||||
private Dictionary<Symbol, decimal> _symbols = new();
|
||||
private int _dateCount = 1;
|
||||
|
||||
public TestMcClellanOscillator() : base()
|
||||
{
|
||||
// Maximum A/D difference from the test set is 2527
|
||||
for (int i = 1; i <= 2530; i++)
|
||||
{
|
||||
var symbol = Symbol.Create($"TestSymbol{i}", SecurityType.Equity, Market.USA);
|
||||
_symbols.Add(symbol, 0m);
|
||||
Add(symbol);
|
||||
}
|
||||
|
||||
// Set to the first EMA values to account for past A/D Difference values that we don't have access
|
||||
Reset();
|
||||
EMAFast.Update(new DateTime(2022, 6, 30), -209.85m);
|
||||
EMASlow.Update(new DateTime(2022, 6, 30), -186.41m);
|
||||
}
|
||||
|
||||
public void TestUpdate(IndicatorDataPoint input)
|
||||
{
|
||||
var isTotal2530 = McClellanIndicatorTestHelper.GetAdvanceDeclineNumber(input.Value, out var advance, out var decline);
|
||||
var symbols = _symbols.Keys.ToList();
|
||||
|
||||
for (int i = 0; i < advance; i++)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[i], Close = _dateCount, Volume = 1, Time = input.Time });
|
||||
_symbols[symbols[i]] = _dateCount;
|
||||
}
|
||||
for (int j = 1; j <= decline; j++)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[^j], Close = -_dateCount, Volume = 1, Time = input.Time });
|
||||
_symbols[symbols[^j]] = -_dateCount;
|
||||
}
|
||||
if (!isTotal2530)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[advance], Close = _symbols[symbols[advance]], Volume = 1, Time = input.Time });
|
||||
}
|
||||
|
||||
_dateCount += 1;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_dateCount = 1;
|
||||
|
||||
foreach (var symbol in _symbols.Keys)
|
||||
{
|
||||
_symbols[symbol] = 0m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class McClellanSummationIndexTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
var mcClellanOscillator = new McClellanSummationIndex(19, 39);
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(mcClellanOscillator.Add);
|
||||
}
|
||||
else
|
||||
{
|
||||
mcClellanOscillator.Add(Symbols.MSFT);
|
||||
mcClellanOscillator.Add(Symbols.GOOG);
|
||||
mcClellanOscillator.Add(Symbols.AAPL);
|
||||
}
|
||||
return mcClellanOscillator;
|
||||
}
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL, Symbols.IBM];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = (McClellanSummationIndex)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 1; i <= indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
}
|
||||
|
||||
Assert.AreEqual(60m, indicator.Current.Value);
|
||||
Assert.AreEqual(indicator.WarmUpPeriod * 3, indicator.Samples);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var indicator = (McClellanSummationIndex)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 1; i <= indicator.WarmUpPeriod; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = i, Volume = 1, Time = reference.AddMinutes(i) });
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
var indicator = new TestMcClellanSummationIndex();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
var indicator = new TestMcClellanSummationIndex();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
indicator.Reset();
|
||||
McClellanIndicatorTestHelper.RunTestIndicator(indicator, TestFileName, TestColumnName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new TestMcClellanSummationIndex();
|
||||
var renkoConsolidator = new RenkoConsolidator(0.5m);
|
||||
renkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
McClellanIndicatorTestHelper.UpdateRenkoConsolidator(renkoConsolidator, TestFileName);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
renkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = new TestMcClellanSummationIndex();
|
||||
var volumeRenkoConsolidator = new VolumeRenkoConsolidator(0.5m);
|
||||
volumeRenkoConsolidator.DataConsolidated += (sender, volumeRenkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(volumeRenkoBar));
|
||||
};
|
||||
|
||||
McClellanIndicatorTestHelper.UpdateRenkoConsolidator(volumeRenkoConsolidator, TestFileName);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
volumeRenkoConsolidator.Dispose();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "mcclellan_data.csv";
|
||||
|
||||
protected override string TestColumnName => "MSI";
|
||||
}
|
||||
|
||||
public class TestMcClellanSummationIndex : McClellanSummationIndex, ITestMcClellanOscillator
|
||||
{
|
||||
private Dictionary<Symbol, decimal> _symbols = new();
|
||||
private int _dateCount = 1;
|
||||
|
||||
public TestMcClellanSummationIndex() : base()
|
||||
{
|
||||
// Maximum A/D difference from the test set is 2527
|
||||
for (int i = 1; i <= 2530; i++)
|
||||
{
|
||||
var symbol = Symbol.Create($"TestSymbol{i}", SecurityType.Equity, Market.USA);
|
||||
_symbols.Add(symbol, 0m);
|
||||
Add(symbol);
|
||||
}
|
||||
|
||||
// Set to the first EMA values to account for past A/D Difference values that we don't have access
|
||||
Reset();
|
||||
Summation.Time = new DateTime(2022, 6, 30);
|
||||
Summation.Value = -606.25m;
|
||||
McClellanOscillator.EMAFast.Update(new DateTime(2022, 6, 30), -209.85m);
|
||||
McClellanOscillator.EMASlow.Update(new DateTime(2022, 6, 30), -186.41m);
|
||||
}
|
||||
|
||||
public void TestUpdate(IndicatorDataPoint input)
|
||||
{
|
||||
var isTotal2530 = McClellanIndicatorTestHelper.GetAdvanceDeclineNumber(input.Value, out var advance, out var decline);
|
||||
var symbols = _symbols.Keys.ToList();
|
||||
|
||||
for (int i = 0; i < advance; i++)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[i], Close = _dateCount, Volume = 1, Time = input.Time });
|
||||
_symbols[symbols[i]] = _dateCount;
|
||||
}
|
||||
for (int j = 1; j <= decline; j++)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[^j], Close = -_dateCount, Volume = 1, Time = input.Time });
|
||||
_symbols[symbols[^j]] = -_dateCount;
|
||||
}
|
||||
if (!isTotal2530)
|
||||
{
|
||||
Update(new TradeBar() { Symbol = symbols[advance], Close = _symbols[symbols[advance]], Volume = 1, Time = input.Time });
|
||||
}
|
||||
|
||||
_dateCount += 1;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_dateCount = 1;
|
||||
|
||||
foreach (var symbol in _symbols.Keys)
|
||||
{
|
||||
_symbols[symbol] = 0m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class McGinleyDynamicTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new McGinleyDynamic(14);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_McGinleyDynamic.csv";
|
||||
protected override string TestColumnName => "McGinleyDynamic14";
|
||||
|
||||
|
||||
[Test]
|
||||
public void IsReadyAfterPeriodUpdates()
|
||||
{
|
||||
var indicator = new McGinleyDynamic(3);
|
||||
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 1, 0), 1m);
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 2, 0), 1m);
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 3, 0), 1m);
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var indicator = new McGinleyDynamic(3);
|
||||
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
indicator.Update(data);
|
||||
}
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(indicator);
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 1, 0), 2.0m);
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 2, 0), 2.0m);
|
||||
indicator.Update(new DateTime(2024, 7, 9, 0, 3, 0), 2.0m);
|
||||
Assert.AreEqual(indicator.Current.Value, 2.0m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WorksWithLowValues()
|
||||
{
|
||||
var indicator = new McGinleyDynamic("test", 10);
|
||||
|
||||
var startDate = new DateTime(2020, 6, 4);
|
||||
var dataPoints = new List<IndicatorDataPoint>()
|
||||
{
|
||||
new IndicatorDataPoint(startDate, 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(1), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(2), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(3), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(4), 3.27743794800m),
|
||||
new IndicatorDataPoint(startDate.AddDays(5), 7.46527532600m),
|
||||
new IndicatorDataPoint(startDate.AddDays(6), 2.54419732600m),
|
||||
new IndicatorDataPoint(startDate.AddDays(7), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(8), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(9), 0.71847738800m),
|
||||
new IndicatorDataPoint(startDate.AddDays(10), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(11), 1.86016748400m),
|
||||
new IndicatorDataPoint(startDate.AddDays(12), 0.45273917600m),
|
||||
new IndicatorDataPoint(startDate.AddDays(13), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(14), 1.80111454800m),
|
||||
new IndicatorDataPoint(startDate.AddDays(15), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(16), 2.74596152400m),
|
||||
new IndicatorDataPoint(startDate.AddDays(17), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(18), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(19), 0m),
|
||||
new IndicatorDataPoint(startDate.AddDays(20), 0.84642541600m),
|
||||
};
|
||||
|
||||
for (int i=0; i < 21; i++)
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(dataPoints[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MeanAbsoluteDeviationTests
|
||||
{
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
// Indicator output was compared against the octave code:
|
||||
// mad = @(v) mean(abs(v - mean(v)));
|
||||
var mad = new MeanAbsoluteDeviation(3);
|
||||
var reference = DateTime.MinValue;
|
||||
|
||||
mad.Update(reference.AddDays(1), 1m);
|
||||
Assert.AreEqual(0m, mad.Current.Value);
|
||||
|
||||
mad.Update(reference.AddDays(2), -1m);
|
||||
Assert.AreEqual(1m, mad.Current.Value);
|
||||
|
||||
mad.Update(reference.AddDays(3), 1m);
|
||||
Assert.AreEqual(0.888888888888889m, decimal.Round(mad.Current.Value, 15));
|
||||
|
||||
mad.Update(reference.AddDays(4), -2m);
|
||||
Assert.AreEqual(1.111111111111111m, decimal.Round(mad.Current.Value, 15));
|
||||
|
||||
mad.Update(reference.AddDays(5), 3m);
|
||||
Assert.AreEqual(1.777777777777778m, decimal.Round(mad.Current.Value, 15));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
var mad = new MeanAbsoluteDeviation(3);
|
||||
mad.Update(DateTime.Today, 1m);
|
||||
mad.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
mad.Update(DateTime.Today.AddSeconds(1), 1m);
|
||||
Assert.IsTrue(mad.IsReady);
|
||||
|
||||
mad.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(mad);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(mad.Mean);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpProperly()
|
||||
{
|
||||
var mad = new MeanAbsoluteDeviation(20);
|
||||
var time = DateTime.Today;
|
||||
var period = ((IIndicatorWarmUpPeriodProvider)mad).WarmUpPeriod;
|
||||
|
||||
for (var i = 0; i < period; i++)
|
||||
{
|
||||
mad.Update(time.AddDays(i), i);
|
||||
Assert.AreEqual(i == period - 1, mad.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MesaAdaptiveMovingAverageTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new MesaAdaptiveMovingAverage("MAMA");
|
||||
}
|
||||
protected override string TestFileName => "spy_mama.csv";
|
||||
|
||||
protected override string TestColumnName => "mama";
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowDivisionByZero()
|
||||
{
|
||||
var mama = new MesaAdaptiveMovingAverage("MAMA");
|
||||
|
||||
for (var i = 0; i < 500; i++)
|
||||
{
|
||||
var data = new TradeBar
|
||||
{
|
||||
Symbol = Symbol.Empty,
|
||||
Time = DateTime.Now.AddSeconds(i),
|
||||
Open = 0,
|
||||
Low = 0,
|
||||
High = 0,
|
||||
Close = 0
|
||||
};
|
||||
Assert.DoesNotThrow(() => mama.Update(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MidPointTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new MidPoint(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_midpoint.txt";
|
||||
|
||||
protected override string TestColumnName => "MIDPOINT_5";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class MidPriceTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
return new MidPrice(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_midprice.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "MIDPRICE_5"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MinimumTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new Minimum(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName
|
||||
{
|
||||
get { return "spy_min.txt"; }
|
||||
}
|
||||
|
||||
protected override string TestColumnName
|
||||
{
|
||||
get { return "MIN_5"; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
var min = new Minimum(3);
|
||||
|
||||
var reference = DateTime.UtcNow;
|
||||
|
||||
min.Update(reference, 1m);
|
||||
Assert.AreEqual(1m, min.Current.Value);
|
||||
Assert.AreEqual(0, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(1), 2m);
|
||||
Assert.AreEqual(1m, min.Current.Value);
|
||||
Assert.AreEqual(1, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(2), -1m);
|
||||
Assert.AreEqual(-1m, min.Current.Value);
|
||||
Assert.AreEqual(0, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(3), 2m);
|
||||
Assert.AreEqual(-1m, min.Current.Value);
|
||||
Assert.AreEqual(1, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(4), 0m);
|
||||
Assert.AreEqual(-1m, min.Current.Value);
|
||||
Assert.AreEqual(2, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(5), 3m);
|
||||
Assert.AreEqual(0m, min.Current.Value);
|
||||
Assert.AreEqual(1, min.PeriodsSinceMinimum);
|
||||
|
||||
min.Update(reference.AddDays(6), 2m);
|
||||
Assert.AreEqual(0m, min.Current.Value);
|
||||
Assert.AreEqual(2, min.PeriodsSinceMinimum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperlyMinimum()
|
||||
{
|
||||
var min = new Minimum(3);
|
||||
min.Update(DateTime.Today, 1m);
|
||||
min.Update(DateTime.Today.AddSeconds(1), 2m);
|
||||
min.Update(DateTime.Today.AddSeconds(2), 1m);
|
||||
Assert.IsTrue(min.IsReady);
|
||||
|
||||
min.Reset();
|
||||
Assert.AreEqual(0, min.PeriodsSinceMinimum);
|
||||
TestHelper.AssertIndicatorIsInDefaultState(min);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MomentumPercentTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new MomentumPercent(50);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_rocp50.txt";
|
||||
|
||||
protected override string TestColumnName => "Rate of Change % 50";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MomentumTests
|
||||
{
|
||||
[Test]
|
||||
public void ComputesCorrectly()
|
||||
{
|
||||
var mom = new Momentum(5);
|
||||
foreach (var data in TestHelper.GetDataStream(5))
|
||||
{
|
||||
mom.Update(data);
|
||||
Assert.AreEqual(data.Value, mom.Current.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResetsProperly()
|
||||
{
|
||||
var mom = new Momentum(5);
|
||||
foreach (var data in TestHelper.GetDataStream(6))
|
||||
{
|
||||
mom.Update(data);
|
||||
}
|
||||
Assert.IsTrue(mom.IsReady);
|
||||
|
||||
mom.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(mom);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpProperly()
|
||||
{
|
||||
var mom = new Momentum(5);
|
||||
var period = ((IIndicatorWarmUpPeriodProvider)mom).WarmUpPeriod;
|
||||
var dataStream = TestHelper.GetDataStream(period).ToArray();
|
||||
|
||||
for (var i = 0; i < period; i++)
|
||||
{
|
||||
mom.Update(dataStream[i]);
|
||||
Assert.AreEqual(i == period - 1, mom.IsReady);
|
||||
}
|
||||
Assert.IsTrue(mom.IsReady);
|
||||
Assert.IsTrue(mom.Samples > mom.Period);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
using QuantConnect.Logging;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
/// <summary>
|
||||
/// Result tested vs. Python and Excel available in http://tinyurl.com/ob5tslj
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class MomersionTests
|
||||
{
|
||||
#region Array input
|
||||
|
||||
// Real AAPL minute data rounded to 2 decimals.
|
||||
private readonly decimal[] _prices = {
|
||||
125.99m, 125.91m, 125.75m, 125.62m, 125.54m, 125.45m, 125.47m,
|
||||
125.4m , 125.43m, 125.45m, 125.42m, 125.36m, 125.23m, 125.32m,
|
||||
125.26m, 125.31m, 125.41m, 125.5m , 125.51m, 125.41m, 125.54m,
|
||||
125.51m, 125.61m, 125.43m, 125.42m, 125.42m, 125.46m, 125.43m,
|
||||
125.4m , 125.35m
|
||||
};
|
||||
|
||||
private readonly decimal[] _expectedMinPeriod = {
|
||||
50.00m, 50.00m, 50.00m, 50.00m, 50.00m, 50.00m, 50.00m, 50.00m, 57.14m, 62.50m,
|
||||
55.56m, 60.00m, 63.64m, 58.33m, 53.85m, 50.00m, 53.33m, 56.25m, 58.82m, 55.56m,
|
||||
52.63m, 50.00m, 45.00m, 40.00m, 40.00m, 36.84m, 38.89m, 38.89m, 44.44m, 44.44m
|
||||
};
|
||||
|
||||
private readonly decimal[] _expectedFullPeriod = {
|
||||
50m, 50m , 50m , 50m, 50m, 50m , 50m, 50m,
|
||||
50m, 50m , 50m , 50m, 60m, 50m , 40m, 30m,
|
||||
40m, 50m , 60m , 50m, 50m, 40m , 30m, 30m,
|
||||
40m, 44.44m, 37.5m, 25m, 25m, 37.5m,
|
||||
};
|
||||
|
||||
#endregion Array input
|
||||
|
||||
[TestCase(7, 20)]
|
||||
[TestCase(null, 10)]
|
||||
public void ComputesCorrectly(int? minPeriod, int fullPeriod)
|
||||
{
|
||||
var momersion = new Momersion(minPeriod, fullPeriod);
|
||||
var expected = minPeriod.HasValue ? _expectedMinPeriod : _expectedFullPeriod;
|
||||
|
||||
RunTestIndicator(momersion, expected);
|
||||
}
|
||||
|
||||
[TestCase(7, 20)]
|
||||
[TestCase(null, 10)]
|
||||
public void ResetsProperly(int? minPeriod, int fullPeriod)
|
||||
{
|
||||
var momersion = new Momersion(minPeriod, fullPeriod);
|
||||
var expected = minPeriod.HasValue ? _expectedMinPeriod : _expectedFullPeriod;
|
||||
|
||||
RunTestIndicator(momersion, expected);
|
||||
|
||||
Assert.IsTrue(momersion.IsReady);
|
||||
|
||||
momersion.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(momersion);
|
||||
}
|
||||
|
||||
[TestCase(7, 20)]
|
||||
[TestCase(null, 10)]
|
||||
public void WarmsUpProperly(int? minPeriod, int fullPeriod)
|
||||
{
|
||||
var momersion = new Momersion(minPeriod, fullPeriod);
|
||||
var period = ((IIndicatorWarmUpPeriodProvider)momersion).WarmUpPeriod;
|
||||
var dataStream = TestHelper.GetDataStream(period).ToArray();
|
||||
|
||||
for (var i = 0; i < period; i++)
|
||||
{
|
||||
momersion.Update(dataStream[i]);
|
||||
Assert.AreEqual(i == period - 1, momersion.IsReady);
|
||||
}
|
||||
}
|
||||
|
||||
private void RunTestIndicator(Momersion momersion, IEnumerable expected)
|
||||
{
|
||||
var time = DateTime.Now;
|
||||
var actual = new decimal[_prices.Length];
|
||||
|
||||
for (var i = 0; i < _prices.Length; i++)
|
||||
{
|
||||
momersion.Update(time.AddMinutes(i), _prices[i]);
|
||||
actual[i] = Math.Round(momersion.Current.Value, 2);
|
||||
|
||||
Log.Trace($"Bar : {i} | {momersion}, Is ready? {momersion.IsReady}");
|
||||
}
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class MoneyFlowIndexTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 0.1m;
|
||||
return new MoneyFlowIndex(20);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_mfi.txt";
|
||||
|
||||
protected override string TestColumnName => "Money Flow Index 20";
|
||||
|
||||
[Test]
|
||||
public void TestTradeBarsWithNoVolume()
|
||||
{
|
||||
var mfi = new MoneyFlowIndex(3);
|
||||
foreach (var data in TestHelper.GetDataStream(4))
|
||||
{
|
||||
var tradeBar = new TradeBar
|
||||
{
|
||||
Open = data.Value,
|
||||
Close = data.Value,
|
||||
High = data.Value,
|
||||
Low = data.Value,
|
||||
Volume = 0
|
||||
};
|
||||
mfi.Update(tradeBar);
|
||||
}
|
||||
Assert.AreEqual(mfi.Current.Value, 100.0m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MovingAverageConvergenceDivergenceTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new MovingAverageConvergenceDivergence(fastPeriod: 12, slowPeriod: 26, signalPeriod: 9);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_macd.txt";
|
||||
|
||||
protected override string TestColumnName => "MACD";
|
||||
|
||||
[Test]
|
||||
public void FastPeriodLessThanSlowPeriod()
|
||||
{
|
||||
var a = new MovingAverageConvergenceDivergence(fastPeriod: 2, slowPeriod: 3, signalPeriod: 2);
|
||||
Assert.Throws<ArgumentException>(() => new MovingAverageConvergenceDivergence(fastPeriod: 3, slowPeriod: 3, signalPeriod: 2));
|
||||
Assert.Throws<ArgumentException>(() => new MovingAverageConvergenceDivergence(fastPeriod: 4, slowPeriod: 3, signalPeriod: 2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataMacdHistogram()
|
||||
{
|
||||
var macd = CreateIndicator();
|
||||
TestHelper.TestIndicator(
|
||||
macd,
|
||||
TestFileName,
|
||||
"Histogram",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
expected,
|
||||
(double) ((MovingAverageConvergenceDivergence) ind).Histogram.Current.Value,
|
||||
delta: 1e-4
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataMacdSignal()
|
||||
{
|
||||
var macd = CreateIndicator();
|
||||
TestHelper.TestIndicator(
|
||||
macd,
|
||||
TestFileName,
|
||||
"Signal",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
expected,
|
||||
(double) ((MovingAverageConvergenceDivergence) ind).Signal.Current.Value,
|
||||
delta: 1e-4
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ComparesWithExternalDataMacdValue()
|
||||
{
|
||||
var macd = CreateIndicator();
|
||||
TestHelper.TestIndicator(
|
||||
macd,
|
||||
TestFileName,
|
||||
"MACD",
|
||||
(ind, expected) => Assert.AreEqual(
|
||||
expected,
|
||||
(double)((MovingAverageConvergenceDivergence)ind).Current.Value,
|
||||
delta: 1e-4
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
int fastPeriod = 3,
|
||||
slowPeriod = 4,
|
||||
signalPeriod = 2;
|
||||
var macd = new MovingAverageConvergenceDivergence(fastPeriod: fastPeriod, slowPeriod: slowPeriod, signalPeriod: signalPeriod);
|
||||
|
||||
Assert.IsFalse(macd.Signal.IsReady);
|
||||
Assert.IsFalse(macd.Histogram.IsReady);
|
||||
Assert.IsFalse(macd.IsReady);
|
||||
|
||||
for (var i = 0; i < fastPeriod; i++)
|
||||
{
|
||||
Assert.IsFalse(macd.Fast.IsReady);
|
||||
macd.Update(new IndicatorDataPoint(DateTime.Today.AddSeconds(i), i));
|
||||
}
|
||||
Assert.IsTrue(macd.Fast.IsReady);
|
||||
|
||||
|
||||
for (var i = fastPeriod; i < slowPeriod; i++)
|
||||
{
|
||||
Assert.IsFalse(macd.Slow.IsReady);
|
||||
macd.Update(new IndicatorDataPoint(DateTime.Today.AddSeconds(i), i));
|
||||
}
|
||||
Assert.IsTrue(macd.Slow.IsReady);
|
||||
|
||||
|
||||
for (var i = slowPeriod; i < macd.WarmUpPeriod; i++)
|
||||
{
|
||||
Assert.IsFalse(macd.Signal.IsReady);
|
||||
Assert.IsFalse(macd.Histogram.IsReady);
|
||||
Assert.IsFalse(macd.IsReady);
|
||||
macd.Update(new IndicatorDataPoint(DateTime.Today.AddSeconds(i), i));
|
||||
}
|
||||
Assert.IsTrue(macd.Signal.IsReady);
|
||||
Assert.IsTrue(macd.Histogram.IsReady);
|
||||
Assert.IsTrue(macd.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class MovingAverageTypeExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void CreatesCorrectAveragingIndicator()
|
||||
{
|
||||
var indicator = MovingAverageType.Simple.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(SimpleMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Exponential.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(ExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Wilders.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(WilderMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.LinearWeightedMovingAverage.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(LinearWeightedMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.DoubleExponential.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(DoubleExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.TripleExponential.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(TripleExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Triangular.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(TriangularMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.T3.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(T3MovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Kama.AsIndicator(1);
|
||||
Assert.IsInstanceOf(typeof(KaufmanAdaptiveMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Hull.AsIndicator(4);
|
||||
Assert.IsInstanceOf(typeof(HullMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Alma.AsIndicator(9);
|
||||
Assert.IsInstanceOf(typeof(ArnaudLegouxMovingAverage), indicator);
|
||||
|
||||
string name = string.Empty;
|
||||
indicator = MovingAverageType.Simple.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(SimpleMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Exponential.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(ExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Wilders.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(WilderMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.LinearWeightedMovingAverage.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(LinearWeightedMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.DoubleExponential.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(DoubleExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.TripleExponential.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(TripleExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Triangular.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(TriangularMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.T3.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(T3MovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Kama.AsIndicator(name, 1);
|
||||
Assert.IsInstanceOf(typeof(KaufmanAdaptiveMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Hull.AsIndicator(name, 4);
|
||||
Assert.IsInstanceOf(typeof(HullMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Alma.AsIndicator(name, 9);
|
||||
Assert.IsInstanceOf(typeof(ArnaudLegouxMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.Zlema.AsIndicator(name, 9);
|
||||
Assert.IsInstanceOf(typeof(ZeroLagExponentialMovingAverage), indicator);
|
||||
|
||||
indicator = MovingAverageType.MGD.AsIndicator(name, 9);
|
||||
Assert.IsInstanceOf(typeof(McGinleyDynamic), indicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class NewHighsNewLowsDifferenceTests : NewHighsNewLowsTestsBase<IBaseDataBar>
|
||||
{
|
||||
protected override NewHighsNewLows<IBaseDataBar> CreateNewHighsNewLowsIndicator()
|
||||
{
|
||||
// For test purposes we use period of two
|
||||
return new NewHighsNewLows("test_name", 2);
|
||||
}
|
||||
|
||||
protected override IndicatorBase<IBaseDataBar> GetSubIndicator(IndicatorBase<IBaseDataBar> mainIndicator)
|
||||
{
|
||||
return (mainIndicator as NewHighsNewLows).Difference;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Difference.Current.Value);
|
||||
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.5m, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(-3m, indicator.Difference.Current.Value);
|
||||
|
||||
indicator.Reset();
|
||||
indicator.Remove(Symbols.GOOG);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Difference.Current.Value);
|
||||
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.5m, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Time = reference.AddMinutes(3) });
|
||||
// new low (ignored)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(-2m, indicator.Difference.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
indicator.Add(Symbols.MSFT);
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Difference.Current.Value);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.5m, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(0m, indicator.Difference.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 4, Low = 1, Time = reference.AddMinutes(4) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.3m, Time = reference.AddMinutes(4) });
|
||||
// no change
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(4) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 4, Low = 1, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(1m, indicator.Difference.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 5, Low = 1, Time = reference.AddMinutes(5) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.2m, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 5, Low = 1, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.Difference.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 6, Low = 1, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 3, Low = 1, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 6, Low = 1, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.Difference.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 7, Low = 1, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 7, Low = 1, Time = reference.AddMinutes(7) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.Difference.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (unordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 0.2m, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.75m, Low = 0.1m, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 2, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(1m, indicator.Difference.Current.Value);
|
||||
Assert.AreEqual(9, indicator.Samples);
|
||||
Assert.AreEqual(1, indicator.Difference.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public virtual void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (ordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 4, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(3m, indicator.Difference.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
// indicator is ready
|
||||
|
||||
// The last update used Symbol.GOOG, so the indicator's current Symbol should be GOOG
|
||||
Assert.AreEqual(Symbols.GOOG, indicator.Difference.Current.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected override string TestFileName => "nhnl_data.csv";
|
||||
|
||||
protected override string TestColumnName => "NH/NL Difference";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class NewHighsNewLowsRatioTests : NewHighsNewLowsTestsBase<IBaseDataBar>
|
||||
{
|
||||
protected override NewHighsNewLows<IBaseDataBar> CreateNewHighsNewLowsIndicator()
|
||||
{
|
||||
// For test purposes we use period of two
|
||||
return new NewHighsNewLows("test_name", 2);
|
||||
}
|
||||
|
||||
protected override IndicatorBase<IBaseDataBar> GetSubIndicator(IndicatorBase<IBaseDataBar> mainIndicator)
|
||||
{
|
||||
// we need to use the Ratio sub-indicator
|
||||
return (mainIndicator as NewHighsNewLows).Ratio;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var indicator = (NewHighsNewLows)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 0.9m, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(0.5m, indicator.Ratio.Current.Value);
|
||||
|
||||
indicator.Reset();
|
||||
indicator.Remove(Symbols.GOOG);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 0.9m, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.9m, Time = reference.AddMinutes(3) });
|
||||
// new low (ignored)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.Ratio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var indicator = (NewHighsNewLows)CreateIndicator();
|
||||
indicator.Add(Symbols.MSFT);
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.Ratio.Current.Value);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.5m, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(0m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 4, Low = 1, Time = reference.AddMinutes(4) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.3m, Time = reference.AddMinutes(4) });
|
||||
// no change
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(4) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 4, Low = 1, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 5, Low = 1, Time = reference.AddMinutes(5) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.2m, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 5, Low = 1, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 6, Low = 1, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 3, Low = 1, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 6, Low = 1, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.Ratio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 7, Low = 1, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 7, Low = 1, Time = reference.AddMinutes(7) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.Ratio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = (NewHighsNewLows)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (unordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 0.2m, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.75m, Low = 0.1m, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 2, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.Ratio.IsReady);
|
||||
Assert.AreEqual(2m, indicator.Ratio.Current.Value);
|
||||
Assert.AreEqual(9, indicator.Samples);
|
||||
Assert.AreEqual(1, indicator.Ratio.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = (NewHighsNewLows)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (ordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.Ratio.IsReady);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 4, Low = 1, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.Ratio.IsReady);
|
||||
Assert.AreEqual(3m, indicator.Ratio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLows;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
// indicator is ready
|
||||
|
||||
// The last update used Symbol.GOOG, so the indicator's current Symbol should be GOOG
|
||||
Assert.AreEqual(Symbols.GOOG, indicator.Ratio.Current.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected override string TestFileName => "nhnl_data.csv";
|
||||
|
||||
protected override string TestColumnName => "NH/NL Ratio";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static QuantConnect.Tests.Indicators.TestHelper;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
public abstract class NewHighsNewLowsTestsBase<T> : CommonIndicatorTests<T>
|
||||
where T : class, IBaseDataBar
|
||||
{
|
||||
protected override IndicatorBase<T> CreateIndicator()
|
||||
{
|
||||
var nhnlRatio = CreateNewHighsNewLowsIndicator();
|
||||
if (SymbolList.Count > 2)
|
||||
{
|
||||
SymbolList.Take(3).ToList().ForEach(nhnlRatio.Add);
|
||||
}
|
||||
else
|
||||
{
|
||||
nhnlRatio.Add(Symbols.AAPL);
|
||||
nhnlRatio.Add(Symbols.IBM);
|
||||
nhnlRatio.Add(Symbols.GOOG);
|
||||
RenkoBarSize = 5000000;
|
||||
}
|
||||
|
||||
// Even if the indicator is ready, there may be zero values
|
||||
ValueCanBeZero = true;
|
||||
|
||||
return nhnlRatio;
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.SPY, Symbols.AAPL, Symbols.IBM];
|
||||
}
|
||||
|
||||
protected override Action<IndicatorBase<T>, double> Assertion => (indicator, expected) =>
|
||||
{
|
||||
// we need to use the Ratio sub-indicator
|
||||
base.Assertion(GetSubIndicator(indicator), expected);
|
||||
};
|
||||
|
||||
protected abstract NewHighsNewLows<T> CreateNewHighsNewLowsIndicator();
|
||||
|
||||
protected abstract IndicatorBase<T> GetSubIndicator(IndicatorBase<T> mainIndicator);
|
||||
|
||||
[Test]
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<IBaseDataBar>)
|
||||
{
|
||||
var aaplRenkoConsolidator = new RenkoConsolidator(10000m);
|
||||
aaplRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var googRenkoConsolidator = new RenkoConsolidator(100000m);
|
||||
googRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var ibmRenkoConsolidator = new RenkoConsolidator(10000m);
|
||||
ibmRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AAPL")
|
||||
{
|
||||
aaplRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else if (tradebar.Symbol.Value == "GOOG")
|
||||
{
|
||||
googRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ibmRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveRenkoBars(indicator);
|
||||
aaplRenkoConsolidator.Dispose();
|
||||
googRenkoConsolidator.Dispose();
|
||||
ibmRenkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
if (indicator is IndicatorBase<IBaseDataBar>)
|
||||
{
|
||||
var aaplRenkoConsolidator = new VolumeRenkoConsolidator(10000000m);
|
||||
aaplRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var googRenkoConsolidator = new VolumeRenkoConsolidator(500000m);
|
||||
googRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
var ibmRenkoConsolidator = new VolumeRenkoConsolidator(500000m);
|
||||
ibmRenkoConsolidator.DataConsolidated += (sender, renkoBar) =>
|
||||
{
|
||||
Assert.DoesNotThrow(() => indicator.Update(renkoBar));
|
||||
};
|
||||
|
||||
foreach (var parts in GetCsvFileStream(TestFileName))
|
||||
{
|
||||
var tradebar = parts.GetTradeBar();
|
||||
if (tradebar.Symbol.Value == "AAPL")
|
||||
{
|
||||
aaplRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else if (tradebar.Symbol.Value == "GOOG")
|
||||
{
|
||||
googRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ibmRenkoConsolidator.Update(tradebar);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreNotEqual(0, indicator.Samples);
|
||||
IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(indicator);
|
||||
aaplRenkoConsolidator.Dispose();
|
||||
googRenkoConsolidator.Dispose();
|
||||
ibmRenkoConsolidator.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
internal class NewHighsNewLowsVolumeRatioTests : NewHighsNewLowsTestsBase<TradeBar>
|
||||
{
|
||||
protected override NewHighsNewLows<TradeBar> CreateNewHighsNewLowsIndicator()
|
||||
{
|
||||
// For test purposes we use period of two
|
||||
return new NewHighsNewLowsVolume("test_name", 2);
|
||||
}
|
||||
|
||||
protected override IndicatorBase<TradeBar> GetSubIndicator(IndicatorBase<TradeBar> mainIndicator)
|
||||
{
|
||||
return (mainIndicator as NewHighsNewLowsVolume).VolumeRatio;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShouldIgnoreRemovedStocks()
|
||||
{
|
||||
var indicator = (NewHighsNewLowsVolume)CreateIndicator();
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(0.5m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
indicator.Reset();
|
||||
indicator.Remove(Symbols.GOOG);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.9m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new low (ignored)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 0.2m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(200m, indicator.VolumeRatio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IgnorePeriodIfAnyStockMissed()
|
||||
{
|
||||
var indicator = (NewHighsNewLowsVolume)CreateIndicator();
|
||||
indicator.Add(Symbols.MSFT);
|
||||
var reference = DateTime.Today;
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
// value is not ready yet
|
||||
Assert.AreEqual(0m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.5m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, Close = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.AreEqual(0m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 4, Low = 1, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.3m, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
// no change
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 4, Low = 1, Volume = 100, Time = reference.AddMinutes(4) });
|
||||
|
||||
Assert.AreEqual(2m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 5, Low = 1, Volume = 100, Time = reference.AddMinutes(5) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 2, Low = 0.2m, Volume = 100, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Volume = 100, Time = reference.AddMinutes(5) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 5, Low = 1, Volume = 100, Time = reference.AddMinutes(5) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 6, Low = 1, Volume = 100, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(6) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 6, Low = 1, Volume = 100, Time = reference.AddMinutes(6) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.VolumeRatio.Current.Value);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 7, Low = 1, Volume = 100, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Volume = 100, Time = reference.AddMinutes(7) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.MSFT, High = 7, Low = 1, Volume = 100, Time = reference.AddMinutes(7) });
|
||||
|
||||
Assert.AreEqual(3m, indicator.VolumeRatio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLowsVolume;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (unordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 0.2m, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new low
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.75m, Low = 0.1m, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 2, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(2m, indicator.VolumeRatio.Current.Value);
|
||||
Assert.AreEqual(9, indicator.Samples);
|
||||
Assert.AreEqual(1, indicator.VolumeRatio.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WarmsUpOrdered()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLowsVolume;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
// setup period (ordered)
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 100, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 0.5m, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 4, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
// new high
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 5, Low = 1, Volume = 100, Time = reference.AddMinutes(3) });
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
Assert.AreEqual(300m, indicator.VolumeRatio.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void IndicatorShouldHaveSymbolAfterUpdates()
|
||||
{
|
||||
var indicator = CreateIndicator() as NewHighsNewLowsVolume;
|
||||
var reference = DateTime.Today;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 1, Low = 1, Volume = 1, Time = reference.AddMinutes(1) });
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 2, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.5m, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(2) });
|
||||
|
||||
// indicator is not ready yet
|
||||
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.AAPL, High = 3, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.IBM, High = 1, Low = 0.3m, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
indicator.Update(new TradeBar() { Symbol = Symbols.GOOG, High = 4, Low = 1, Volume = 1, Time = reference.AddMinutes(3) });
|
||||
|
||||
// indicator is ready
|
||||
|
||||
// The last update used Symbol.GOOG, so the indicator's current Symbol should be GOOG
|
||||
Assert.AreEqual(Symbols.GOOG, indicator.VolumeRatio.Current.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
protected override string TestFileName => "nhnl_data.csv";
|
||||
|
||||
protected override string TestColumnName => "NH/NL Volume Ratio";
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class NormalizedAverageTrueRangeTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
return new NormalizedAverageTrueRange(5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_natr.txt";
|
||||
|
||||
protected override string TestColumnName => "NATR_5";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
|
||||
public class OnBalanceVolumeTests : CommonIndicatorTests<TradeBar>
|
||||
{
|
||||
protected override IndicatorBase<TradeBar> CreateIndicator()
|
||||
{
|
||||
return new OnBalanceVolume();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_with_obv.txt";
|
||||
|
||||
protected override string TestColumnName => "OBV";
|
||||
|
||||
protected override Action<IndicatorBase<TradeBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(
|
||||
expected.ToStringInvariant("0.##E-00"),
|
||||
indicator.Current.Value.ToStringInvariant("0.##E-00")
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// The final value of this indicator is zero because it uses the Volume of the bars it receives.
|
||||
/// Since RenkoBar's don't always have Volume, the final current value is zero. Therefore we
|
||||
/// skip this test
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* 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.Globalization;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Lean.Engine.DataFeeds;
|
||||
using QuantConnect.Lean.Engine.HistoricalData;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
public abstract class OptionBaseIndicatorTests<T> : CommonIndicatorTests<IBaseData>
|
||||
where T : OptionIndicatorBase
|
||||
{
|
||||
// count of risk free rate calls per each update on opiton indicator
|
||||
protected int RiskFreeRateUpdatesPerIteration { get; set; }
|
||||
|
||||
// count of dividend yield calls per each update on option indicator
|
||||
protected int DividendYieldUpdatesPerIteration { get; set; }
|
||||
|
||||
protected static DateTime _reference = new DateTime(2023, 8, 1, 10, 0, 0);
|
||||
protected static Symbol _symbol = Symbol.CreateOption("SPY", Market.USA, OptionStyle.American, OptionRight.Call, 450m, new DateTime(2023, 9, 1));
|
||||
protected Symbol _underlying => _symbol.Underlying;
|
||||
|
||||
protected override IndicatorBase<IBaseData> CreateIndicator()
|
||||
{
|
||||
throw new NotImplementedException("method `CreateIndicator()` is required to be set up");
|
||||
}
|
||||
|
||||
protected virtual OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel)
|
||||
{
|
||||
throw new NotImplementedException("method `CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel)` is required to be set up");
|
||||
}
|
||||
|
||||
protected virtual OptionIndicatorBase CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel)
|
||||
{
|
||||
throw new NotImplementedException("method `CreateIndicator(IRiskFreeInterestRateModel riskFreeRateModel, IDividendYieldModel dividendYieldModel)` is required to be set up");
|
||||
}
|
||||
|
||||
protected virtual OptionIndicatorBase CreateIndicator(QCAlgorithm algorithm)
|
||||
{
|
||||
throw new NotImplementedException("method `CreateIndicator(QCAlgorithm algorithm)` is required to be set up");
|
||||
}
|
||||
|
||||
protected OptionPricingModelType ParseSymbols(string[] items, bool american, out Symbol call, out Symbol put)
|
||||
{
|
||||
var ticker = items[0];
|
||||
var expiry = DateTime.ParseExact(items[1], "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
var strike = Parse.Decimal(items[2]);
|
||||
var style = american ? OptionStyle.American : OptionStyle.European;
|
||||
|
||||
call = Symbol.CreateOption(ticker, Market.USA, style, OptionRight.Call, strike, expiry);
|
||||
put = Symbol.CreateOption(ticker, Market.USA, style, OptionRight.Put, strike, expiry);
|
||||
|
||||
return american ? OptionPricingModelType.ForwardTree : OptionPricingModelType.BlackScholes;
|
||||
}
|
||||
|
||||
protected void RunTestIndicator(Symbol call, Symbol put, OptionIndicatorBase callIndicator, OptionIndicatorBase putIndicator,
|
||||
string[] items, int callColumn, int putColumn, double errorRate, double errorMargin = 1e-4)
|
||||
{
|
||||
var time = DateTime.ParseExact(items[3], "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
|
||||
var callDataPoint = new IndicatorDataPoint(call, time, decimal.Parse(items[5], NumberStyles.Any, CultureInfo.InvariantCulture));
|
||||
var putDataPoint = new IndicatorDataPoint(put, time, decimal.Parse(items[4], NumberStyles.Any, CultureInfo.InvariantCulture));
|
||||
var underlyingDataPoint = new IndicatorDataPoint(call.Underlying, time, decimal.Parse(items[^4], NumberStyles.Any, CultureInfo.InvariantCulture));
|
||||
|
||||
callIndicator.Update(callDataPoint);
|
||||
callIndicator.Update(underlyingDataPoint);
|
||||
if (callIndicator.UseMirrorContract)
|
||||
{
|
||||
callIndicator.Update(putDataPoint);
|
||||
}
|
||||
|
||||
var expected = double.Parse(items[callColumn], NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
var acceptance = Math.Max(errorRate * Math.Abs(expected), errorMargin); // percentage error
|
||||
Assert.AreEqual(expected, (double)callIndicator.Current.Value, acceptance);
|
||||
|
||||
putIndicator.Update(putDataPoint);
|
||||
putIndicator.Update(underlyingDataPoint);
|
||||
if (putIndicator.UseMirrorContract)
|
||||
{
|
||||
putIndicator.Update(callDataPoint);
|
||||
}
|
||||
|
||||
expected = double.Parse(items[putColumn], NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
acceptance = Math.Max(errorRate * Math.Abs(expected), errorMargin); // percentage error
|
||||
Assert.AreEqual(expected, (double)putIndicator.Current.Value, acceptance);
|
||||
}
|
||||
|
||||
protected override List<Symbol> GetSymbols()
|
||||
{
|
||||
return [Symbols.GOOG];
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ZeroGreeksIfExpired()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var date = new DateTime(2099, 1, 1); // date that the option must be expired already
|
||||
var price = 500m;
|
||||
var optionPrice = 10m;
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, date, optionPrice));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, date, price));
|
||||
|
||||
Assert.AreEqual(0m, indicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ResetsProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var price = 500m;
|
||||
var optionPrice = Math.Max(price - 450, 0) * 1.1m;
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, _reference.AddDays(1 + i), optionPrice));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, _reference.AddDays(1 + i), price));
|
||||
}
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
|
||||
indicator.Reset();
|
||||
|
||||
TestHelper.AssertIndicatorIsInDefaultState(indicator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void TimeMovesForward()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
|
||||
for (var i = 10; i > 0; i--)
|
||||
{
|
||||
var price = 500m;
|
||||
var optionPrice = Math.Max(price - 450, 0) * 1.1m;
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, _reference.AddDays(1 + i), optionPrice));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, _reference.AddDays(1 + i), price));
|
||||
}
|
||||
|
||||
Assert.AreEqual(2, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmsUpProperly()
|
||||
{
|
||||
var indicator = CreateIndicator();
|
||||
var warmUpPeriod = (indicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
|
||||
if (!warmUpPeriod.HasValue)
|
||||
{
|
||||
Assert.Ignore($"{indicator.Name} is not IIndicatorWarmUpPeriodProvider");
|
||||
return;
|
||||
}
|
||||
|
||||
// warmup period is 5 + 1
|
||||
for (var i = 1; i <= warmUpPeriod.Value; i++)
|
||||
{
|
||||
var time = _reference.AddDays(i);
|
||||
var price = 500m;
|
||||
var optionPrice = Math.Max(price - 450, 0) * 1.1m;
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, time, optionPrice));
|
||||
|
||||
Assert.IsFalse(indicator.IsReady);
|
||||
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, time, price));
|
||||
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
}
|
||||
|
||||
Assert.AreEqual(2 * warmUpPeriod.Value, indicator.Samples);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WarmUpIndicatorProducesConsistentResults()
|
||||
{
|
||||
var algo = CreateAlgorithm();
|
||||
|
||||
algo.SetStartDate(2015, 12, 24);
|
||||
algo.SetEndDate(2015, 12, 24);
|
||||
|
||||
var underlying = Symbols.GOOG;
|
||||
|
||||
var expiration = new DateTime(2015, 12, 24);
|
||||
var strike = 650m;
|
||||
|
||||
var option = Symbol.CreateOption(underlying, Market.USA, OptionStyle.American, OptionRight.Put, strike, expiration);
|
||||
SymbolList = [option];
|
||||
|
||||
var symbolsForWarmUp = new List<Symbol> { option, option.Underlying };
|
||||
// Define the risk-free rate and dividend yield models
|
||||
var risk = new ConstantRiskFreeRateInterestRateModel(12);
|
||||
var dividend = new ConstantDividendYieldModel(12);
|
||||
|
||||
// Create the first indicator using the risk and dividend models
|
||||
var firstIndicator = CreateIndicator(risk, dividend);
|
||||
var period = (firstIndicator as IIndicatorWarmUpPeriodProvider)?.WarmUpPeriod;
|
||||
if (period == null || period == 0)
|
||||
{
|
||||
Assert.Ignore($"{firstIndicator.Name}, Skipping this test because it's not applicable.");
|
||||
}
|
||||
|
||||
// Warm up the first indicator
|
||||
algo.WarmUpIndicator(symbolsForWarmUp, firstIndicator, Resolution.Daily);
|
||||
|
||||
// Warm up the second indicator manually
|
||||
var secondIndicator = CreateIndicator(risk, dividend);
|
||||
var history = algo.History(symbolsForWarmUp, period.Value, Resolution.Daily).ToList();
|
||||
foreach (var slice in history)
|
||||
{
|
||||
foreach (var symbol in symbolsForWarmUp)
|
||||
{
|
||||
secondIndicator.Update(slice[symbol]);
|
||||
}
|
||||
}
|
||||
SymbolList.Clear();
|
||||
|
||||
// Assert that the indicators are ready
|
||||
Assert.IsTrue(firstIndicator.IsReady);
|
||||
Assert.IsTrue(secondIndicator.IsReady);
|
||||
if (!ValueCanBeZero)
|
||||
{
|
||||
Assert.AreNotEqual(firstIndicator.Current.Value, 0);
|
||||
}
|
||||
|
||||
// Ensure that the first indicator has processed some data
|
||||
Assert.AreNotEqual(firstIndicator.Samples, 0);
|
||||
|
||||
// Validate that both indicators have the same number of processed samples
|
||||
Assert.AreEqual(firstIndicator.Samples, secondIndicator.Samples);
|
||||
|
||||
// Validate that both indicators produce the same final computed value
|
||||
Assert.AreEqual(firstIndicator.Current.Value, secondIndicator.Current.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void WorksWithLowValues()
|
||||
{
|
||||
Symbol = _symbol;
|
||||
base.WorksWithLowValues();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UsesRiskFreeInterestRateModel()
|
||||
{
|
||||
const int count = 20;
|
||||
var dates = Enumerable.Range(0, count).Select(i => new DateTime(2022, 11, 21, 10, 0, 0) + TimeSpan.FromDays(i)).ToList();
|
||||
var interestRateValues = Enumerable.Range(0, count).Select(i => 0m + (10 - 0m) * (i / (count - 1m))).ToList();
|
||||
|
||||
var interestRateProviderMock = new Mock<IRiskFreeInterestRateModel>();
|
||||
|
||||
// Set up
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
interestRateProviderMock.Setup(x => x.GetInterestRate(dates[i])).Returns(interestRateValues[i]).Verifiable();
|
||||
}
|
||||
|
||||
var indicator = CreateIndicator(interestRateProviderMock.Object);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, dates[i], 80m + i));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, dates[i], 500m + i));
|
||||
Assert.AreEqual(interestRateValues[i], indicator.RiskFreeRate.Current.Value);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
interestRateProviderMock.Verify(x => x.GetInterestRate(It.IsAny<DateTime>()), Times.Exactly(dates.Count * RiskFreeRateUpdatesPerIteration));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
interestRateProviderMock.Verify(x => x.GetInterestRate(dates[i]), Times.Exactly(RiskFreeRateUpdatesPerIteration));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UsesPythonDefinedRiskFreeInterestRateModel()
|
||||
{
|
||||
using var _ = Py.GIL();
|
||||
|
||||
var module = PyModule.FromString(Guid.NewGuid().ToString(), $@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class TestRiskFreeInterestRateModel:
|
||||
CallCount = 0
|
||||
|
||||
def GetInterestRate(self, date: datetime) -> float:
|
||||
TestRiskFreeInterestRateModel.CallCount += 1
|
||||
return 0.5
|
||||
|
||||
def getOptionIndicatorBaseIndicator(symbol: Symbol) -> OptionIndicatorBase:
|
||||
return {typeof(T).Name}(symbol, TestRiskFreeInterestRateModel())
|
||||
");
|
||||
|
||||
var iv = module.GetAttr("getOptionIndicatorBaseIndicator").Invoke(_symbol.ToPython()).GetAndDispose<T>();
|
||||
var modelClass = module.GetAttr("TestRiskFreeInterestRateModel");
|
||||
|
||||
var reference = new DateTime(2022, 11, 21, 10, 0, 0);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
iv.Update(new IndicatorDataPoint(_symbol, reference + TimeSpan.FromMinutes(i), 10m + i));
|
||||
iv.Update(new IndicatorDataPoint(_underlying, reference + TimeSpan.FromMinutes(i), 1000m + i));
|
||||
Assert.AreEqual((i + 1) * RiskFreeRateUpdatesPerIteration, modelClass.GetAttr("CallCount").GetAndDispose<int>());
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OptionIndicatorUsesAlgorithmsRiskFreeRateModelSetAfterIndicatorRegistration()
|
||||
{
|
||||
var algorithm = new QCAlgorithm();
|
||||
algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm));
|
||||
|
||||
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(_reference);
|
||||
algorithm.AddEquity(_underlying.Value);
|
||||
algorithm.AddOptionContract(_symbol);
|
||||
algorithm.Settings.AutomaticIndicatorWarmUp = true;
|
||||
|
||||
// Register indicator
|
||||
var indicator = CreateIndicator(algorithm);
|
||||
|
||||
// Setup risk free rate model
|
||||
var interestRateProviderMock = new Mock<IRiskFreeInterestRateModel>();
|
||||
interestRateProviderMock.Setup(x => x.GetInterestRate(_reference)).Verifiable();
|
||||
|
||||
// Update indicator
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, _reference, 30m));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, _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
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, _reference.AddDays(1), 30m));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, _reference.AddDays(1), 300m));
|
||||
|
||||
// Our interest rate provider should have been called once by each update
|
||||
interestRateProviderMock.Verify(x => x.GetInterestRate(_reference.AddDays(1)), Times.Exactly(RiskFreeRateUpdatesPerIteration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UsesDividendYieldModel()
|
||||
{
|
||||
const int count = 20;
|
||||
var dates = Enumerable.Range(0, count).Select(i => new DateTime(2022, 11, 21, 10, 0, 0) + TimeSpan.FromDays(i)).ToList();
|
||||
var dividends = Enumerable.Range(0, count).Select(i => 0m + (10 - 0m) * (i / (count - 1m))).ToList();
|
||||
|
||||
var dividendYieldProviderMock = new Mock<IDividendYieldModel>();
|
||||
|
||||
// Set up
|
||||
var underlyingBasePrice = 500m;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dividendYieldProviderMock.Setup(x => x.GetDividendYield(dates[i], underlyingBasePrice + i)).Returns(dividends[i]).Verifiable();
|
||||
}
|
||||
|
||||
var indicator = CreateIndicator(new ConstantRiskFreeRateInterestRateModel(0.05m), dividendYieldProviderMock.Object);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, dates[i], 80m + i));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, dates[i], underlyingBasePrice + i));
|
||||
Assert.AreEqual(dividends[i], indicator.DividendYield.Current.Value);
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(indicator.IsReady);
|
||||
dividendYieldProviderMock.Verify(x => x.GetDividendYield(It.IsAny<DateTime>(), It.IsAny<decimal>()), Times.Exactly(dates.Count * DividendYieldUpdatesPerIteration));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dividendYieldProviderMock.Verify(x => x.GetDividendYield(dates[i], underlyingBasePrice + i), Times.Exactly(DividendYieldUpdatesPerIteration));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UsesPythonDefinedDividendYieldModel()
|
||||
{
|
||||
using var _ = Py.GIL();
|
||||
|
||||
var module = PyModule.FromString(Guid.NewGuid().ToString(), $@"
|
||||
from AlgorithmImports import *
|
||||
|
||||
class TestDividendYieldModel:
|
||||
call_count = 0
|
||||
|
||||
def get_dividend_yield(self, date: datetime, price: float) -> float:
|
||||
TestDividendYieldModel.call_count += 1
|
||||
return 0.5
|
||||
|
||||
def get_option_indicator_base_indicator(symbol: Symbol) -> OptionIndicatorBase:
|
||||
return {typeof(T).Name}(symbol, InterestRateProvider(), TestDividendYieldModel())
|
||||
");
|
||||
|
||||
var indicator = module.GetAttr("get_option_indicator_base_indicator").Invoke(_symbol.ToPython()).GetAndDispose<T>();
|
||||
var modelClass = module.GetAttr("TestDividendYieldModel");
|
||||
|
||||
var reference = new DateTime(2022, 11, 21, 10, 0, 0);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
indicator.Update(new IndicatorDataPoint(_symbol, reference + TimeSpan.FromMinutes(i), 10m + i));
|
||||
indicator.Update(new IndicatorDataPoint(_underlying, reference + TimeSpan.FromMinutes(i), 1000m + i));
|
||||
Assert.AreEqual((i + 1) * DividendYieldUpdatesPerIteration, modelClass.GetAttr("call_count").GetAndDispose<int>());
|
||||
}
|
||||
}
|
||||
|
||||
// Not used
|
||||
protected override string TestFileName => null;
|
||||
protected override string TestColumnName => null;
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalDataAfterReset()
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
|
||||
public override void AcceptsRenkoBarsAsInput()
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
|
||||
public override void AcceptsVolumeRenkoBarsAsInput()
|
||||
{
|
||||
// Not used
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ParabolicStopAndReverseExtendedTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new ParabolicStopAndReverseExtended();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_sarext.txt";
|
||||
|
||||
protected override string TestColumnName => "SAREXT";
|
||||
|
||||
[TestCase("SAREXT_PARAM1", 0.0, 0.0, 0.03, 0.02, 0.4, 0.02, 0.01, 0.3)]
|
||||
[TestCase("SAREXT_PARAM2", 100, 0.0, 0.03, 0.02, 0.4, 0.02, 0.01, 0.3)]
|
||||
[TestCase("SAREXT_PARAM3", -95, 0.0, 0.03, 0.02, 0.4, 0.02, 0.01, 0.3)]
|
||||
[TestCase("SAREXT_PARAM4", 100, 0.02, 0.03, 0.02, 0.4, 0.02, 0.01, 0.3)]
|
||||
public void ComparesWithExternalDataWithParams(string colName, decimal ss, decimal offset,
|
||||
decimal afss, decimal afis, decimal afms, decimal afsl, decimal afil, decimal afml)
|
||||
{
|
||||
var sarext = new ParabolicStopAndReverseExtended(sarStart : ss, offsetOnReverse : offset,
|
||||
afStartShort : afss, afIncrementShort : afis, afMaxShort : afms,
|
||||
afStartLong : afsl, afIncrementLong : afil, afMaxLong : afml);
|
||||
TestHelper.TestIndicator(
|
||||
sarext,
|
||||
TestFileName,
|
||||
colName,
|
||||
(ind, expected) => Assert.AreEqual(expected, (double)sarext.Current.Value, delta: 1e-4)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class ParabolicStopAndReverseTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new ParabolicStopAndReverse();
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_sarext.txt";
|
||||
|
||||
protected override string TestColumnName => "SAR";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class PercentagePriceOscillatorTests : CommonIndicatorTests<IndicatorDataPoint>
|
||||
{
|
||||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
|
||||
{
|
||||
return new PercentagePriceOscillator(5, 10);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_ppo.txt";
|
||||
|
||||
protected override string TestColumnName => "PPO_5_10";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class PivotPointsHighLowTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
// Even if the indicator is ready, there may be zero values
|
||||
ValueCanBeZero = true;
|
||||
return new PivotPointsHighLow(10);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_pivot_pnt_hl.txt";
|
||||
|
||||
protected override string TestColumnName => "PPHL";
|
||||
|
||||
[Test]
|
||||
public override void ComparesAgainstExternalData()
|
||||
{
|
||||
var indicator = (PivotPointsHighLow)CreateIndicator();
|
||||
RunTestIndicator(indicator);
|
||||
|
||||
var highPivotPoints = indicator.GetHighPivotPointsArray();
|
||||
var lowPivotPoints = indicator.GetLowPivotPointsArray();
|
||||
var pivotPoints = indicator.GetAllPivotPointsArray();
|
||||
|
||||
Assert.True(highPivotPoints.Length > 0);
|
||||
Assert.True(lowPivotPoints.Length > 0);
|
||||
Assert.True(pivotPoints.Length > 0);
|
||||
Assert.AreEqual(pivotPoints.Length, highPivotPoints.Length + lowPivotPoints.Length);
|
||||
|
||||
Assert.That(pivotPoints, Is.Ordered.Descending.By("Time"));
|
||||
}
|
||||
|
||||
[TestCase(PivotPointType.Low)]
|
||||
[TestCase(PivotPointType.High)]
|
||||
[TestCase(PivotPointType.Both)]
|
||||
[TestCase(PivotPointType.None)]
|
||||
public void PivotPointPerType(PivotPointType pointType)
|
||||
{
|
||||
var pointsHighLow = new PivotPointsHighLow(10, 20);
|
||||
|
||||
for (var i = 0; i < pointsHighLow.WarmUpPeriod; i++)
|
||||
{
|
||||
Assert.IsFalse(pointsHighLow.IsReady);
|
||||
|
||||
var low = 1;
|
||||
var high = 1;
|
||||
if (i == 10)
|
||||
{
|
||||
if (pointType == PivotPointType.Low || pointType == PivotPointType.Both)
|
||||
{
|
||||
low = 0;
|
||||
}
|
||||
if (pointType == PivotPointType.High || pointType == PivotPointType.Both)
|
||||
{
|
||||
high = 2;
|
||||
}
|
||||
}
|
||||
|
||||
var bar = new TradeBar(DateTime.UtcNow.AddSeconds(i), Symbols.AAPL, i, high, low, i, i);
|
||||
pointsHighLow.Update(bar);
|
||||
}
|
||||
|
||||
Assert.IsTrue(pointsHighLow.IsReady);
|
||||
|
||||
var bothPivotPoint = pointsHighLow.GetAllPivotPointsArray();
|
||||
var lowPivotPoint = pointsHighLow.GetLowPivotPointsArray();
|
||||
var highPivotPoint = pointsHighLow.GetHighPivotPointsArray();
|
||||
|
||||
if (pointType == PivotPointType.None)
|
||||
{
|
||||
Assert.AreEqual(0, bothPivotPoint.Length);
|
||||
}
|
||||
if (pointType == PivotPointType.Both)
|
||||
{
|
||||
Assert.AreEqual(2, bothPivotPoint.Length);
|
||||
Assert.AreEqual(1, lowPivotPoint.Length);
|
||||
Assert.AreEqual(1, highPivotPoint.Length);
|
||||
Assert.IsTrue(lowPivotPoint.Any(point => point.Value == 0));
|
||||
Assert.IsTrue(highPivotPoint.Any(point => point.Value == 2));
|
||||
}
|
||||
if (pointType == PivotPointType.High)
|
||||
{
|
||||
Assert.AreEqual(1, bothPivotPoint.Length);
|
||||
Assert.AreEqual(0, lowPivotPoint.Length);
|
||||
Assert.AreEqual(1, highPivotPoint.Length);
|
||||
Assert.IsTrue(highPivotPoint.Any(point => point.Value == 2));
|
||||
}
|
||||
if (pointType == PivotPointType.Low)
|
||||
{
|
||||
Assert.AreEqual(1, bothPivotPoint.Length);
|
||||
Assert.AreEqual(1, lowPivotPoint.Length);
|
||||
Assert.AreEqual(0, highPivotPoint.Length);
|
||||
Assert.IsTrue(lowPivotPoint.Any(point => point.Value == 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The expected value for this indicator is always zero
|
||||
/// </summary>
|
||||
/// <param name="indicator"></param>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The expected value for this indicator is always zero
|
||||
/// </summary>
|
||||
protected override void IndicatorValueIsNotZeroAfterReceiveVolumeRenkoBars(IndicatorBase indicator)
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void StrictVsRelaxedHighPivotDetection(bool strict)
|
||||
{
|
||||
var indicator = new PivotPointsHighLow(2, 2, strict: strict);
|
||||
var referenceTime = new DateTime(2020, 1, 1);
|
||||
|
||||
// Create 5 bars where middle bar high EQUALS neighbors
|
||||
// All bars have high = 100, which should only detect pivot in relaxed mode
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var bar = new TradeBar(referenceTime.AddSeconds(i), Symbols.AAPL, 100, 100, 90, 95, 1000);
|
||||
indicator.Update(bar);
|
||||
}
|
||||
|
||||
var highPivots = indicator.GetHighPivotPointsArray();
|
||||
|
||||
if (strict)
|
||||
{
|
||||
// Strict mode: middle bar high (100) is NOT > neighbors (100), so NO pivot
|
||||
Assert.AreEqual(0, highPivots.Length, "Strict mode should reject equal high values");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Relaxed mode: middle bar high (100) is >= neighbors (100), so YES pivot
|
||||
Assert.AreEqual(1, highPivots.Length, "Relaxed mode should accept equal high values");
|
||||
Assert.AreEqual(100, highPivots[0].Value);
|
||||
}
|
||||
|
||||
var lowPivots = indicator.GetLowPivotPointsArray();
|
||||
|
||||
if (strict)
|
||||
{
|
||||
// Strict mode: middle bar low (50) is NOT < neighbors (50), so NO pivot
|
||||
Assert.AreEqual(0, lowPivots.Length, "Strict mode should reject equal low values");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Relaxed mode: middle bar low (50) is <= neighbors (50), so YES pivot
|
||||
Assert.AreEqual(1, lowPivots.Length, "Relaxed mode should accept equal low values");
|
||||
Assert.AreEqual(90, lowPivots[0].Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultBehaviorIsStrict()
|
||||
{
|
||||
// Create indicator without specifying strict parameter (should default to true)
|
||||
var indicator = new PivotPointsHighLow(2, 2);
|
||||
var referenceTime = new DateTime(2020, 1, 1);
|
||||
|
||||
// Create bars with equal high values
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var bar = new TradeBar(referenceTime.AddSeconds(i), Symbols.AAPL, 100, 100, 90, 95, 1000);
|
||||
indicator.Update(bar);
|
||||
}
|
||||
|
||||
var highPivots = indicator.GetHighPivotPointsArray();
|
||||
|
||||
// Default behavior should be strict, so NO pivot detected
|
||||
Assert.AreEqual(0, highPivots.Length, "Default behavior should be strict mode");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QCAlgorithmHelperMethodOverloadResolution()
|
||||
{
|
||||
// This test verifies that calling PPHL with minimal arguments compiles without ambiguity
|
||||
// and uses the correct default behavior (strict mode)
|
||||
|
||||
// Instead of using QCAlgorithm, directly test the indicator
|
||||
// The key point is that the method signature compiles without ambiguity
|
||||
var indicator = new PivotPointsHighLow(2, 2);
|
||||
var referenceTime = new DateTime(2020, 1, 1);
|
||||
|
||||
// Create bars with equal high values
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
var bar = new TradeBar(referenceTime.AddSeconds(i), Symbols.AAPL, 100, 100, 90, 95, 1000);
|
||||
indicator.Update(bar);
|
||||
}
|
||||
|
||||
var highPivots = indicator.GetHighPivotPointsArray();
|
||||
|
||||
// Should default to strict mode, so NO pivot detected with equal values
|
||||
Assert.AreEqual(0, highPivots.Length, "Default constructor should use strict mode");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QCAlgorithmHelperOverloadResolution()
|
||||
{
|
||||
// This test verifies that all valid PPHL helper method call patterns compile without ambiguity
|
||||
// and maintain backward compatibility with the original API.
|
||||
// If this test compiles and passes, the overload resolution is working correctly.
|
||||
|
||||
var algorithm = new QCAlgorithm();
|
||||
algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm));
|
||||
|
||||
var spy = algorithm.AddEquity("SPY");
|
||||
var symbol = spy.Symbol;
|
||||
|
||||
// Backward-compatible patterns that existed before adding the strict parameter:
|
||||
|
||||
// Pattern 1: Minimal call with just symbol and lengths
|
||||
var pphl1 = algorithm.PPHL(symbol, 3, 3);
|
||||
Assert.IsNotNull(pphl1, "Minimal call pattern should work");
|
||||
|
||||
// Pattern 2: With lastStoredValues
|
||||
var pphl2 = algorithm.PPHL(symbol, 3, 3, 100);
|
||||
Assert.IsNotNull(pphl2, "Call with lastStoredValues should work");
|
||||
|
||||
// Pattern 3: With lastStoredValues and resolution (CRITICAL backward compatibility test)
|
||||
var pphl3 = algorithm.PPHL(symbol, 3, 3, 100, Resolution.Minute);
|
||||
Assert.IsNotNull(pphl3, "Call with lastStoredValues and resolution should work for backward compatibility");
|
||||
|
||||
// Pattern 4: With lastStoredValues, resolution, and selector (CRITICAL backward compatibility test)
|
||||
var pphl4 = algorithm.PPHL(symbol, 3, 3, 100, Resolution.Minute, (x) => x as IBaseDataBar);
|
||||
Assert.IsNotNull(pphl4, "Full original signature should work for backward compatibility");
|
||||
|
||||
// New patterns with strict parameter:
|
||||
|
||||
// Pattern 5: With named strict parameter only
|
||||
var pphl5 = algorithm.PPHL(symbol, 3, 3, strict: false);
|
||||
Assert.IsNotNull(pphl5, "Call with named strict parameter should work");
|
||||
|
||||
// Pattern 6: With lastStoredValues and strict
|
||||
var pphl6 = algorithm.PPHL(symbol, 3, 3, 100, false);
|
||||
Assert.IsNotNull(pphl6, "Call with lastStoredValues and strict should work");
|
||||
|
||||
// Pattern 7: With lastStoredValues, strict, and resolution
|
||||
var pphl7 = algorithm.PPHL(symbol, 3, 3, 100, false, Resolution.Minute);
|
||||
Assert.IsNotNull(pphl7, "Call with lastStoredValues, strict, and resolution should work");
|
||||
|
||||
// Pattern 8: Full new signature
|
||||
var pphl8 = algorithm.PPHL(symbol, 3, 3, 100, true, Resolution.Minute, (x) => x as IBaseDataBar);
|
||||
Assert.IsNotNull(pphl8, "Full new signature should work");
|
||||
|
||||
// Pattern 9: With named parameters for clarity
|
||||
var pphl9 = algorithm.PPHL(symbol, 3, 3, lastStoredValues: 50, strict: false, resolution: Resolution.Daily);
|
||||
Assert.IsNotNull(pphl9, "Call with named parameters should work");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Tests.Indicators
|
||||
{
|
||||
[TestFixture]
|
||||
public class PremierStochasticOscillatorTests : CommonIndicatorTests<IBaseDataBar>
|
||||
{
|
||||
protected override IndicatorBase<IBaseDataBar> CreateIndicator()
|
||||
{
|
||||
RenkoBarSize = 1m;
|
||||
VolumeRenkoBarSize = 0.5m;
|
||||
return new PremierStochasticOscillator("PSO", 8, 5);
|
||||
}
|
||||
|
||||
protected override string TestFileName => "spy_pso.csv";
|
||||
|
||||
protected override string TestColumnName => "pso";
|
||||
|
||||
protected override Action<IndicatorBase<IBaseDataBar>, double> Assertion =>
|
||||
(indicator, expected) =>
|
||||
Assert.AreEqual(expected, (double)((PremierStochasticOscillator)indicator).Current.Value, 1e-3);
|
||||
|
||||
[Test]
|
||||
public void IsReadyAfterPeriodUpdates()
|
||||
{
|
||||
int period = 3;
|
||||
int emaPeriod = 2;
|
||||
var pso = new PremierStochasticOscillator(period, emaPeriod);
|
||||
int minInputValues = period + 2 * (emaPeriod - 1);
|
||||
for (int i = 0; i < minInputValues; i++)
|
||||
{
|
||||
var data = new TradeBar
|
||||
{
|
||||
Symbol = Symbol.Empty,
|
||||
Time = DateTime.Now.AddSeconds(i),
|
||||
Close = i
|
||||
};
|
||||
pso.Update(data);
|
||||
}
|
||||
Assert.IsTrue(pso.IsReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user