/* * 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.Optimizer; using QuantConnect.Optimizer.Objectives; using QuantConnect.Optimizer.Parameters; using QuantConnect.Optimizer.Strategies; using System; using System.Collections.Generic; namespace QuantConnect.Tests.Optimizer.Strategies { [TestFixture, Parallelizable(ParallelScope.Fixtures)] public class EulerSearchOptimizationStrategyTests : OptimizationStrategyTests { [TestFixture] public class EulerSearchTests { private EulerSearchOptimizationStrategy _strategy; [SetUp] public void Init() { this._strategy = new EulerSearchOptimizationStrategy(); } [TestCase(10)] [TestCase(5)] [TestCase(2)] public void Depth(int _defaultSegmentAmount) { var param = new OptimizationStepParameter("ema-fast", 10, 100, 10, 0.1m); var set = new HashSet { param, new StaticOptimizationParameter("pepe", "pipi") }; _strategy.Initialize( new Target("Profit", new Maximization(), null), new List(), set, new StepBaseOptimizationStrategySettings { DefaultSegmentAmount = _defaultSegmentAmount }); Queue _pendingOptimizationResults = new Queue(); int depth = -1; _strategy.NewParameterSet += (s, parameterSet) => { if (_pendingOptimizationResults.Count == 0) { depth++; } _pendingOptimizationResults.Enqueue(new OptimizationResult(_stringify(_profit(parameterSet), _drawdown(parameterSet)), parameterSet, "")); }; _strategy.PushNewResults(OptimizationResult.Initial); while (_pendingOptimizationResults.TryDequeue(out var item)) { _strategy.PushNewResults(item); } Assert.AreEqual(Math.Ceiling(Math.Log((double)(param.Step / param.MinStep), _defaultSegmentAmount)), depth); } [Test] public void ThrowIfNoSettingsPassed() { Assert.Throws(() => { _strategy.Initialize( new Target("Profit", new Maximization(), null), new List(), new HashSet { new OptimizationStepParameter("ema-fast", 10, 100, 10, 0.1m), new StaticOptimizationParameter("pepe", "pipi") }, null); }); } [TestCase(5)] [TestCase(10)] public void Reduce(int amountOfSegments) { var param = new OptimizationStepParameter("ema-fast", 10, 100, 10, 0.1m); var set = new HashSet { param, new StaticOptimizationParameter("pepe", "pipi") }; _strategy.Initialize( new Target("Profit", new Maximization(), null), new List(), set, new StepBaseOptimizationStrategySettings { DefaultSegmentAmount = amountOfSegments }); Queue pendingOptimizationResults = new Queue(); int depth = -1; _strategy.NewParameterSet += (s, parameterSet) => { if (pendingOptimizationResults.Count == 0) { depth++; } pendingOptimizationResults.Enqueue(new OptimizationResult(_stringify(_profit(parameterSet), _drawdown(parameterSet)), parameterSet, "")); }; _strategy.PushNewResults(OptimizationResult.Initial); var step = param.Step ?? 1; var datapoint = param.MinValue; while (pendingOptimizationResults.Count > 0) { var count = pendingOptimizationResults.Count; for (int i = 0; i < count; i++) { var optimizationResult = pendingOptimizationResults.Dequeue(); Assert.AreEqual(datapoint + step * i, optimizationResult.ParameterSet.Value["ema-fast"].ToDecimal()); _strategy.PushNewResults(optimizationResult); } step = Math.Max(param.MinStep.Value, step / amountOfSegments); datapoint = param.MaxValue - step * ((decimal)amountOfSegments / 2); } } } protected override IOptimizationStrategy CreateStrategy() { return new EulerSearchOptimizationStrategy(); } protected override OptimizationStrategySettings CreateSettings() { return new StepBaseOptimizationStrategySettings(){DefaultSegmentAmount = 10}; } private static TestCaseData[] StrategySettings => new[] { new TestCaseData(new Maximization(), OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "5.0"}, { "ema-fast" , "6.0"} })), new TestCaseData(new Minimization(), OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3"} })), new TestCaseData(new Maximization(), OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "5"}, { "ema-fast" , "6.0" }, { "skipFromResultSum", "SPY" } })), new TestCaseData(new Minimization(), OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3"}, { "skipFromResultSum", "SPY" } })) }; [Test, TestCaseSource(nameof(StrategySettings))] public override void StepInsideNoTargetNoConstraints(Extremum extremum, HashSet optimizationParameters, ParameterSet solution) { base.StepInsideNoTargetNoConstraints(extremum, optimizationParameters, solution); } private static TestCaseData[] OptimizeWithConstraint => new[] { new TestCaseData(0.05m, OptimizationStepParameters,new ParameterSet(-1, new Dictionary{{"ema-slow", "1.1"}, { "ema-fast" , "3.8"} })), new TestCaseData(0.06m, OptimizationStepParameters,new ParameterSet(-1, new Dictionary{{"ema-slow", "1.9"}, { "ema-fast" , "4.0"} })), new TestCaseData(0.05m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3.9" }, { "skipFromResultSum", "SPY" } })), new TestCaseData(0.06m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "2"}, { "ema-fast" , "3.9" }, { "skipFromResultSum", "SPY" } })) }; [Test, TestCaseSource(nameof(OptimizeWithConstraint))] public override void StepInsideWithConstraints(decimal drawdown, HashSet optimizationParameters, ParameterSet solution) { base.StepInsideWithConstraints(drawdown, optimizationParameters, solution); } private static TestCaseData[] OptimizeWithTarget => new[] { new TestCaseData(0m, OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3"} })), new TestCaseData(4m, OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3"} })), new TestCaseData(5m, OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "5"} })), new TestCaseData(8m, OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "3"}, { "ema-fast" , "5"} })), new TestCaseData(0m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "3"}, { "skipFromResultSum", "SPY" } })), new TestCaseData(5m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "1"}, { "ema-fast" , "5"}, { "skipFromResultSum", "SPY" } })), new TestCaseData(8m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "3"}, { "ema-fast" , "5"}, { "skipFromResultSum", "SPY" } })) }; [Test, TestCaseSource(nameof(OptimizeWithTarget))] public override void StepInsideWithTarget(decimal targetValue, HashSet optimizationParameters, ParameterSet solution) { base.StepInsideWithTarget(targetValue, optimizationParameters, solution); } private static TestCaseData[] OptimizeWithTargetNotReached => new[] { new TestCaseData(15m, OptimizationStepParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "5.0"}, { "ema-fast" , "6.0" } })), new TestCaseData(155m, OptimizationMixedParameters, new ParameterSet(-1, new Dictionary{{"ema-slow", "5"}, { "ema-fast" , "6.0"}, { "skipFromResultSum", "SPY" } })) }; [Test, TestCaseSource(nameof(OptimizeWithTargetNotReached))] public override void TargetNotReached(decimal targetValue, HashSet optimizationParameters, ParameterSet solution) { base.TargetNotReached(targetValue, optimizationParameters, solution); } } }