From 631344ee4bf535ee7c7b01ef45778cf872dfc331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:29:16 -0500 Subject: [PATCH] Make `Target.MoveAhead()` case insensitive (#8382) * First draft of the solution * Try a different approach * Nit change * Add unit tests * Add edge cases * Address requests --- Common/Optimizer/Objectives/Constraint.cs | 2 +- Common/Optimizer/Objectives/Target.cs | 27 ++++++++++++++++++++- Tests/Optimizer/Objectives/TargetTests.cs | 29 +++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Common/Optimizer/Objectives/Constraint.cs b/Common/Optimizer/Objectives/Constraint.cs index f62ed78587d7..dad283c02762 100644 --- a/Common/Optimizer/Objectives/Constraint.cs +++ b/Common/Optimizer/Objectives/Constraint.cs @@ -65,7 +65,7 @@ public bool IsMet(string jsonBacktestResult) throw new ArgumentNullException(nameof(jsonBacktestResult), $"Constraint.IsMet(): {Messages.OptimizerObjectivesCommon.NullOrEmptyBacktestResult}"); } - var token = JObject.Parse(jsonBacktestResult).SelectToken(Target); + var token = Objectives.Target.GetTokenInJsonBacktest(jsonBacktestResult, Target); if (token == null) { return false; diff --git a/Common/Optimizer/Objectives/Target.cs b/Common/Optimizer/Objectives/Target.cs index e67d8094ae59..8387094119c4 100644 --- a/Common/Optimizer/Objectives/Target.cs +++ b/Common/Optimizer/Objectives/Target.cs @@ -76,7 +76,7 @@ public bool MoveAhead(string jsonBacktestResult) throw new ArgumentNullException(nameof(jsonBacktestResult), $"Target.MoveAhead(): {Messages.OptimizerObjectivesCommon.NullOrEmptyBacktestResult}"); } - var token = JObject.Parse(jsonBacktestResult).SelectToken(Target); + var token = GetTokenInJsonBacktest(jsonBacktestResult, Target); if (token == null) { return false; @@ -103,6 +103,31 @@ public void CheckCompliance() } } + public static JToken GetTokenInJsonBacktest(string jsonBacktestResult, string target) + { + var jObject = JObject.Parse(jsonBacktestResult); + var path = target.Replace("[", string.Empty, StringComparison.InvariantCultureIgnoreCase) + .Replace("]", string.Empty, StringComparison.InvariantCultureIgnoreCase) + .Replace("\'", string.Empty, StringComparison.InvariantCultureIgnoreCase).Split("."); + JToken token = null; + foreach (var key in path) + { + if (jObject.TryGetValue(key, StringComparison.OrdinalIgnoreCase, out token)) + { + if (token is not JValue) + { + jObject = token.ToObject(); + } + } + else + { + return null; + } + } + + return token; + } + private bool IsComplied() => TargetValue.HasValue && Current.HasValue && (TargetValue.Value == Current.Value || Extremum.Better(TargetValue.Value, Current.Value)); } } diff --git a/Tests/Optimizer/Objectives/TargetTests.cs b/Tests/Optimizer/Objectives/TargetTests.cs index 4f3db8aa368a..ccef9d513929 100644 --- a/Tests/Optimizer/Objectives/TargetTests.cs +++ b/Tests/Optimizer/Objectives/TargetTests.cs @@ -15,6 +15,7 @@ */ using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using NUnit.Framework; using QuantConnect.Optimizer.Objectives; using System; @@ -138,5 +139,33 @@ public void RoundTrip() Assert.AreEqual(origin.Extremum.GetType(), actual.Extremum.GetType()); Assert.AreEqual(origin.TargetValue, actual.TargetValue); } + + [TestCase("['TotalPerformance'].['TradeStatistics'].['ProfitToMaxDrawdownRatio']", "-1")] + [TestCase("['totalPerformance'].['tradeStatistics'].['profitToMaxDrawdownRatio']", "-1")] + [TestCase("['TotalPerformance'].['TradeStatistics'].['lossRate']", "1")] + [TestCase("['totalPerformance'].['tradeStatistics'].['lossRate']", "1")] + [TestCase("['Statistics'].['Start Equity']", "100000")] + [TestCase("['statistics'].['start equity']", "100000")] + [TestCase("['Statistics'].['Sharpe Ratio']", "-5.283")] + [TestCase("['statistics'].['sharpe ratio']", "-5.283")] + [TestCase("['Statistics'].['Sharp Ratio']", null)] + [TestCase("['statistics'].['sharp ratio']", null)] + public void TargetMoveAheadIsCaseInsensitive(string target, string expected) + { + Assert.AreEqual(expected, (Target.GetTokenInJsonBacktest(jsonBacktestResultExample, target))?.Value()); + } + + private string jsonBacktestResultExample = @"{ + ""totalPerformance"": { + ""tradeStatistics"": { + ""lossRate"": ""1"", + ""profitToMaxDrawdownRatio"": ""-1"", + } + }, + ""statistics"": { + ""Start Equity"": ""100000"", + ""Sharpe Ratio"": ""-5.283"", + } +}"; } }