chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -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)
{
}
}
}
+453
View File
@@ -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);
}
}
}
}
+239
View File
@@ -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"; }
}
}
}
+90
View File
@@ -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);
}
}
}
+129
View File
@@ -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); }
}
}
}
+80
View File
@@ -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);
}
}
}
+95
View File
@@ -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";
}
}
+36
View File
@@ -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";
}
}
+316
View File
@@ -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);
}
}
}
}
+173
View File
@@ -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);
};
}
}
}
}
+84
View File
@@ -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);
}
}
}
+54
View File
@@ -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";
}
}
+36
View File
@@ -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);
}
}
}
+383
View File
@@ -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;
}
}
+190
View File
@@ -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);
}
}
}
+32
View File
@@ -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";
}
}
+227
View File
@@ -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;
}
}
}
+291
View File
@@ -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);
}
}
}
+106
View File
@@ -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);
}
}
}
}
+181
View File
@@ -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);
}
}
}
+58
View File
@@ -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);
}
}
}
+77
View File
@@ -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);
}
}
}
}
+36
View File
@@ -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";
}
}
+47
View File
@@ -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);
}
}
}
+168
View File
@@ -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);
}
}
}
+63
View File
@@ -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();
}
}
}
}
+82
View File
@@ -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";
}
}
+45
View File
@@ -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));
}
}
}
}
+190
View File
@@ -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);
}
}
}
+80
View File
@@ -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);
}
}
}
}
+248
View File
@@ -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;
}
}
}
}
+424
View File
@@ -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";
}
}
+103
View File
@@ -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);
}
}
}
+45
View File
@@ -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);
}
}
}
+66
View File
@@ -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);
}
}
}
}
+36
View File
@@ -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";
}
}
+109
View File
@@ -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;
}
}
}
}
+104
View File
@@ -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));
}
}
}
}
+33
View File
@@ -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";
}
}
+40
View File
@@ -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"; }
}
}
}
+90
View File
@@ -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);
}
}
}
+33
View File
@@ -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";
}
}
+67
View File
@@ -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);
}
}
}
+113
View File
@@ -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);
}
}
}
+54
View File
@@ -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";
}
}
+52
View File
@@ -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";
}
}
+279
View File
@@ -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