From 4c84156fdfffe6d516e134be4ba8d50f0329a500 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Mon, 28 Jul 2025 16:57:44 -0400 Subject: [PATCH 1/9] Some fixes for new C# enums handling in Pythonnet --- Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py | 2 +- Algorithm.Framework/Alphas/ConstantAlphaModel.py | 4 ++-- Algorithm.Framework/Alphas/EmaCrossAlphaModel.py | 2 +- Algorithm.Framework/Alphas/MacdAlphaModel.py | 8 ++++---- Algorithm.Framework/Alphas/RsiAlphaModel.py | 2 +- ...lackLittermanOptimizationPortfolioConstructionModel.py | 2 +- .../Portfolio/EqualWeightingPortfolioConstructionModel.py | 4 ++-- .../MeanVarianceOptimizationPortfolioConstructionModel.py | 2 +- .../BasicTemplateFuturesFrameworkAlgorithm.py | 2 +- .../BasicTemplateOptionsFrameworkAlgorithm.py | 2 +- .../CustomDataUniverseScheduledRegressionAlgorithm.py | 2 +- Algorithm.Python/UpdateOrderRegressionAlgorithm.py | 8 ++++---- Algorithm/Selection/ManualUniverseSelectionModel.py | 2 +- 13 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py b/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py index 1fc1911c080b..bbdeeef80e1a 100644 --- a/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py +++ b/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py @@ -35,7 +35,7 @@ def __init__(self, lookback = 1, self.pairs = dict() self.securities = set() - resolution_string = Extensions.get_enum_string(resolution, Resolution) + resolution_string = str(resolution) self.name = f'{self.__class__.__name__}({self.lookback},{resolution_string},{Extensions.normalize_to_str(threshold)})' diff --git a/Algorithm.Framework/Alphas/ConstantAlphaModel.py b/Algorithm.Framework/Alphas/ConstantAlphaModel.py index c6ff7ee8b549..7a1f48acd128 100644 --- a/Algorithm.Framework/Alphas/ConstantAlphaModel.py +++ b/Algorithm.Framework/Alphas/ConstantAlphaModel.py @@ -34,8 +34,8 @@ def __init__(self, type, direction, period, magnitude = None, confidence = None, self.securities = [] self.insights_time_by_symbol = {} - type_string = Extensions.GetEnumString(type, InsightType) - direction_string = Extensions.GetEnumString(direction, InsightDirection) + type_string = str(type) + direction_string = str(direction) self.Name = '{}({},{},{}'.format(self.__class__.__name__, type_string, direction_string, strfdelta(period)) if magnitude is not None: diff --git a/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py b/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py index e6005a51a42b..fcdcb41a6815 100644 --- a/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py +++ b/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py @@ -30,7 +30,7 @@ def __init__(self, self.prediction_interval = Time.multiply(Extensions.to_time_span(resolution), fast_period) self.symbol_data_by_symbol = {} - resolution_string = Extensions.get_enum_string(resolution, Resolution) + resolution_string = str(resolution) self.name = '{}({},{},{})'.format(self.__class__.__name__, fast_period, slow_period, resolution_string) diff --git a/Algorithm.Framework/Alphas/MacdAlphaModel.py b/Algorithm.Framework/Alphas/MacdAlphaModel.py index 64869fcb4efc..a223e070d5c9 100644 --- a/Algorithm.Framework/Alphas/MacdAlphaModel.py +++ b/Algorithm.Framework/Alphas/MacdAlphaModel.py @@ -40,8 +40,8 @@ def __init__(self, self.insightCollection = InsightCollection() self.symbolData = {} - resolutionString = Extensions.GetEnumString(resolution, Resolution) - movingAverageTypeString = Extensions.GetEnumString(movingAverageType, MovingAverageType) + resolutionString = str(resolution) + movingAverageTypeString = str(movingAverageType) self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString) @@ -67,7 +67,7 @@ def Update(self, algorithm, data): direction = InsightDirection.Down # ignore signal for same direction as previous signal - if direction == sd.PreviousDirection: + if sd.PreviousDirection is not None and direction == sd.PreviousDirection: continue sd.PreviousDirection = direction @@ -99,7 +99,7 @@ def OnSecuritiesChanged(self, algorithm, changes): if data is not None: # clean up our consolidator algorithm.SubscriptionManager.RemoveConsolidator(symbol, data.Consolidator) - + # remove from insight collection manager self.CancelInsights(algorithm, symbol) diff --git a/Algorithm.Framework/Alphas/RsiAlphaModel.py b/Algorithm.Framework/Alphas/RsiAlphaModel.py index 196a03adbb22..d805ec3999c2 100644 --- a/Algorithm.Framework/Alphas/RsiAlphaModel.py +++ b/Algorithm.Framework/Alphas/RsiAlphaModel.py @@ -30,7 +30,7 @@ def __init__(self, self.insight_period = Time.multiply(Extensions.to_time_span(resolution), period) self.symbol_data_by_symbol ={} - resolution_string = Extensions.get_enum_string(resolution, Resolution) + resolution_string = str(resolution) self.name = '{}({},{})'.format(self.__class__.__name__, period, resolution_string) def update(self, algorithm, data): diff --git a/Algorithm.Framework/Portfolio/BlackLittermanOptimizationPortfolioConstructionModel.py b/Algorithm.Framework/Portfolio/BlackLittermanOptimizationPortfolioConstructionModel.py index 256fca1fd1ca..bbf6c1c54203 100644 --- a/Algorithm.Framework/Portfolio/BlackLittermanOptimizationPortfolioConstructionModel.py +++ b/Algorithm.Framework/Portfolio/BlackLittermanOptimizationPortfolioConstructionModel.py @@ -70,7 +70,7 @@ def __init__(self, # If the argument is an instance of Resolution or Timedelta # Redefine rebalancing_func rebalancing_func = rebalance - if isinstance(rebalance, int): + if isinstance(rebalance, Resolution): rebalance = Extensions.to_time_span(rebalance) if isinstance(rebalance, timedelta): rebalancing_func = lambda dt: dt + rebalance diff --git a/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py b/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py index 57b25f8507a2..518b9eddf74c 100644 --- a/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py +++ b/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py @@ -34,7 +34,7 @@ def __init__(self, rebalance = Resolution.DAILY, portfolio_bias = PortfolioBias. # If the argument is an instance of Resolution or Timedelta # Redefine rebalancing_func rebalancing_func = rebalance - if isinstance(rebalance, int): + if isinstance(rebalance, Resolution): rebalance = Extensions.to_time_span(rebalance) if isinstance(rebalance, timedelta): rebalancing_func = lambda dt: dt + rebalance @@ -59,4 +59,4 @@ def respect_portfolio_bias(self, insight): Args: insight: The insight to create a target for ''' - return self.portfolio_bias == PortfolioBias.LONG_SHORT or insight.direction == self.portfolio_bias + return self.portfolio_bias == PortfolioBias.LONG_SHORT or int(insight.direction) == int(self.portfolio_bias) diff --git a/Algorithm.Framework/Portfolio/MeanVarianceOptimizationPortfolioConstructionModel.py b/Algorithm.Framework/Portfolio/MeanVarianceOptimizationPortfolioConstructionModel.py index 9778357be070..214dba52677a 100644 --- a/Algorithm.Framework/Portfolio/MeanVarianceOptimizationPortfolioConstructionModel.py +++ b/Algorithm.Framework/Portfolio/MeanVarianceOptimizationPortfolioConstructionModel.py @@ -55,7 +55,7 @@ def __init__(self, # If the argument is an instance of Resolution or Timedelta # Redefine rebalancing_func rebalancing_func = rebalance - if isinstance(rebalance, int): + if isinstance(rebalance, Resolution): rebalance = Extensions.to_time_span(rebalance) if isinstance(rebalance, timedelta): rebalancing_func = lambda dt: dt + rebalance diff --git a/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py b/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py index 1abf1d7a3162..0a7af36dfc5e 100644 --- a/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py +++ b/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py @@ -77,5 +77,5 @@ class SingleSharePortfolioConstructionModel(PortfolioConstructionModel): def create_targets(self, algorithm, insights): targets = [] for insight in insights: - targets.append(PortfolioTarget(insight.symbol, insight.direction)) + targets.append(PortfolioTarget(insight.symbol, int(insight.direction))) return targets diff --git a/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py b/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py index 2b490b870f48..63dffe8ba0b1 100644 --- a/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py +++ b/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py @@ -79,5 +79,5 @@ class SingleSharePortfolioConstructionModel(PortfolioConstructionModel): def create_targets(self, algorithm, insights): targets = [] for insight in insights: - targets.append(PortfolioTarget(insight.symbol, insight.direction)) + targets.append(PortfolioTarget(insight.symbol, int(insight.direction))) return targets diff --git a/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py b/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py index f33736a6339d..2b7644e31a4a 100644 --- a/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py +++ b/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py @@ -63,7 +63,7 @@ def on_data(self, data): if len([x for x in custom_data.keys() if x.underlying == symbol]) == 0: raise ValueError(f"Custom data was not found for symbol {symbol}") # equity daily data arrives at 16 pm but custom data is set to arrive at midnight - self.current_underlying_symbols = [symbol for symbol in data.keys() if symbol.security_type is SecurityType.EQUITY] + self.current_underlying_symbols = [symbol for symbol in data.keys() if symbol.security_type == SecurityType.EQUITY] def on_end_of_algorithm(self): if len(self._selection_time) != 0: diff --git a/Algorithm.Python/UpdateOrderRegressionAlgorithm.py b/Algorithm.Python/UpdateOrderRegressionAlgorithm.py index f06c2866fcc4..d540e532db58 100644 --- a/Algorithm.Python/UpdateOrderRegressionAlgorithm.py +++ b/Algorithm.Python/UpdateOrderRegressionAlgorithm.py @@ -69,7 +69,7 @@ def on_data(self, data): limit_price = (1 + self.limit_percentage)*data["SPY"].high if not is_long else (1 - self.limit_percentage)*data["SPY"].low request = SubmitOrderRequest(order_type, self.security.symbol.security_type, "SPY", self.quantity, stop_price, limit_price, 0, 0.01, True, - self.utc_time, str(order_type)) + self.utc_time, str(int(order_type))) ticket = self.transactions.add_order(request) self.tickets.append(ticket) @@ -77,7 +77,7 @@ def on_data(self, data): ticket = self.tickets[-1] if self.time.day > 8 and self.time.day < 14: - if len(ticket.update_requests) == 0 and ticket.status is not OrderStatus.FILLED: + if len(ticket.update_requests) == 0 and ticket.status != OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) update_order_fields = UpdateOrderFields() update_order_fields.quantity = ticket.quantity + copysign(self.delta_quantity, self.quantity) @@ -85,7 +85,7 @@ def on_data(self, data): ticket.update(update_order_fields) elif self.time.day > 13 and self.time.day < 20: - if len(ticket.update_requests) == 1 and ticket.status is not OrderStatus.FILLED: + if len(ticket.update_requests) == 1 and ticket.status != OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) update_order_fields = UpdateOrderFields() update_order_fields.limit_price = self.security.price*(1 - copysign(self.limit_percentage_delta, ticket.quantity)) @@ -93,7 +93,7 @@ def on_data(self, data): update_order_fields.tag = "Change prices: {0}".format(self.time.day) ticket.update(update_order_fields) else: - if len(ticket.update_requests) == 2 and ticket.status is not OrderStatus.FILLED: + if len(ticket.update_requests) == 2 and ticket.status != OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) ticket.cancel("{0} and is still open!".format(self.time.day)) self.log("CANCELLED:: {0}".format(ticket.cancel_request)) diff --git a/Algorithm/Selection/ManualUniverseSelectionModel.py b/Algorithm/Selection/ManualUniverseSelectionModel.py index d4889e74b00e..c5169f81f949 100644 --- a/Algorithm/Selection/ManualUniverseSelectionModel.py +++ b/Algorithm/Selection/ManualUniverseSelectionModel.py @@ -48,7 +48,7 @@ def create_universes(self, algorithm: QCAlgorithm) -> list[Universe]: market = key[0] security_type = key[1] - security_type_str = Extensions.get_enum_string(security_type, SecurityType) + security_type_str = str(security_type) universe_symbol = Symbol.create(f"manual-universe-selection-model-{security_type_str}-{market}", security_type, market) if security_type == SecurityType.BASE: From 6bf1054506b17b1fddae5df5322c8a6b98d67725 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Tue, 29 Jul 2025 09:21:51 -0400 Subject: [PATCH 2/9] Minor changes and cleanup --- .../Alphas/BasePairsTradingAlphaModel.py | 3 +- .../Alphas/ConstantAlphaModel.py | 5 +-- .../Alphas/EmaCrossAlphaModel.py | 3 +- Algorithm.Framework/Alphas/MacdAlphaModel.py | 4 +-- Algorithm.Framework/Alphas/RsiAlphaModel.py | 3 +- .../BasicTemplateFuturesFrameworkAlgorithm.py | 2 +- .../BasicTemplateOptionsFrameworkAlgorithm.py | 2 +- .../Selection/ManualUniverseSelectionModel.py | 3 +- .../Framework/Portfolio/PortfolioTarget.cs | 35 +++++++++++++++++++ .../Messages.Algorithm.Framework.Portfolio.cs | 11 +++++- 10 files changed, 53 insertions(+), 18 deletions(-) diff --git a/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py b/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py index bbdeeef80e1a..e249b65839b5 100644 --- a/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py +++ b/Algorithm.Framework/Alphas/BasePairsTradingAlphaModel.py @@ -35,8 +35,7 @@ def __init__(self, lookback = 1, self.pairs = dict() self.securities = set() - resolution_string = str(resolution) - self.name = f'{self.__class__.__name__}({self.lookback},{resolution_string},{Extensions.normalize_to_str(threshold)})' + self.name = f'{self.__class__.__name__}({self.lookback},{resolution},{Extensions.normalize_to_str(threshold)})' def update(self, algorithm, data): diff --git a/Algorithm.Framework/Alphas/ConstantAlphaModel.py b/Algorithm.Framework/Alphas/ConstantAlphaModel.py index 7a1f48acd128..adb06fb541fc 100644 --- a/Algorithm.Framework/Alphas/ConstantAlphaModel.py +++ b/Algorithm.Framework/Alphas/ConstantAlphaModel.py @@ -34,10 +34,7 @@ def __init__(self, type, direction, period, magnitude = None, confidence = None, self.securities = [] self.insights_time_by_symbol = {} - type_string = str(type) - direction_string = str(direction) - - self.Name = '{}({},{},{}'.format(self.__class__.__name__, type_string, direction_string, strfdelta(period)) + self.Name = '{}({},{},{}'.format(self.__class__.__name__, type, direction, strfdelta(period)) if magnitude is not None: self.Name += ',{}'.format(magnitude) if confidence is not None: diff --git a/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py b/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py index fcdcb41a6815..bc29c6bb6f9f 100644 --- a/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py +++ b/Algorithm.Framework/Alphas/EmaCrossAlphaModel.py @@ -30,8 +30,7 @@ def __init__(self, self.prediction_interval = Time.multiply(Extensions.to_time_span(resolution), fast_period) self.symbol_data_by_symbol = {} - resolution_string = str(resolution) - self.name = '{}({},{},{})'.format(self.__class__.__name__, fast_period, slow_period, resolution_string) + self.name = '{}({},{},{})'.format(self.__class__.__name__, fast_period, slow_period, resolution) def update(self, algorithm, data): diff --git a/Algorithm.Framework/Alphas/MacdAlphaModel.py b/Algorithm.Framework/Alphas/MacdAlphaModel.py index a223e070d5c9..1d0c87cb419e 100644 --- a/Algorithm.Framework/Alphas/MacdAlphaModel.py +++ b/Algorithm.Framework/Alphas/MacdAlphaModel.py @@ -40,9 +40,7 @@ def __init__(self, self.insightCollection = InsightCollection() self.symbolData = {} - resolutionString = str(resolution) - movingAverageTypeString = str(movingAverageType) - self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageTypeString, resolutionString) + self.Name = '{}({},{},{},{},{})'.format(self.__class__.__name__, fastPeriod, slowPeriod, signalPeriod, movingAverageType, resolution) def Update(self, algorithm, data): diff --git a/Algorithm.Framework/Alphas/RsiAlphaModel.py b/Algorithm.Framework/Alphas/RsiAlphaModel.py index d805ec3999c2..0b4aca12698b 100644 --- a/Algorithm.Framework/Alphas/RsiAlphaModel.py +++ b/Algorithm.Framework/Alphas/RsiAlphaModel.py @@ -30,8 +30,7 @@ def __init__(self, self.insight_period = Time.multiply(Extensions.to_time_span(resolution), period) self.symbol_data_by_symbol ={} - resolution_string = str(resolution) - self.name = '{}({},{})'.format(self.__class__.__name__, period, resolution_string) + self.name = '{}({},{})'.format(self.__class__.__name__, period, resolution) def update(self, algorithm, data): '''Updates this alpha model with the latest data from the algorithm. diff --git a/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py b/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py index 0a7af36dfc5e..1abf1d7a3162 100644 --- a/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py +++ b/Algorithm.Python/BasicTemplateFuturesFrameworkAlgorithm.py @@ -77,5 +77,5 @@ class SingleSharePortfolioConstructionModel(PortfolioConstructionModel): def create_targets(self, algorithm, insights): targets = [] for insight in insights: - targets.append(PortfolioTarget(insight.symbol, int(insight.direction))) + targets.append(PortfolioTarget(insight.symbol, insight.direction)) return targets diff --git a/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py b/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py index 63dffe8ba0b1..2b490b870f48 100644 --- a/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py +++ b/Algorithm.Python/BasicTemplateOptionsFrameworkAlgorithm.py @@ -79,5 +79,5 @@ class SingleSharePortfolioConstructionModel(PortfolioConstructionModel): def create_targets(self, algorithm, insights): targets = [] for insight in insights: - targets.append(PortfolioTarget(insight.symbol, int(insight.direction))) + targets.append(PortfolioTarget(insight.symbol, insight.direction)) return targets diff --git a/Algorithm/Selection/ManualUniverseSelectionModel.py b/Algorithm/Selection/ManualUniverseSelectionModel.py index c5169f81f949..a3e3349c81a7 100644 --- a/Algorithm/Selection/ManualUniverseSelectionModel.py +++ b/Algorithm/Selection/ManualUniverseSelectionModel.py @@ -48,8 +48,7 @@ def create_universes(self, algorithm: QCAlgorithm) -> list[Universe]: market = key[0] security_type = key[1] - security_type_str = str(security_type) - universe_symbol = Symbol.create(f"manual-universe-selection-model-{security_type_str}-{market}", security_type, market) + universe_symbol = Symbol.create(f"manual-universe-selection-model-{security_type}-{market}", security_type, market) if security_type == SecurityType.BASE: # add an entry for this custom universe symbol -- we don't really know the time zone for sure, diff --git a/Common/Algorithm/Framework/Portfolio/PortfolioTarget.cs b/Common/Algorithm/Framework/Portfolio/PortfolioTarget.cs index 6e2562338c2d..b9e37f257c43 100644 --- a/Common/Algorithm/Framework/Portfolio/PortfolioTarget.cs +++ b/Common/Algorithm/Framework/Portfolio/PortfolioTarget.cs @@ -18,6 +18,7 @@ using QuantConnect.Securities; using System.Collections.Generic; using QuantConnect.Securities.Positions; +using QuantConnect.Algorithm.Framework.Alphas; namespace QuantConnect.Algorithm.Framework.Portfolio { @@ -62,6 +63,40 @@ public PortfolioTarget(Symbol symbol, decimal quantity, string tag = "") Tag = tag; } + /// + /// Initializes a new instance of the class + /// + /// The symbol this target is for + /// The target quantity + /// The target tag with additional information + public PortfolioTarget(Symbol symbol, int quantity, string tag = "") + : this(symbol, (decimal)quantity, tag) + { + } + + /// + /// Initializes a new instance of the class + /// + /// The symbol this target is for + /// + /// The insight direction, which will be used to calculate the target quantity + /// (1 for Up, 0 for flat, -1 for down) + /// + /// The target tag with additional information + public PortfolioTarget(Symbol symbol, InsightDirection insightDirection, string tag = "") + : this(symbol, + insightDirection switch + { + InsightDirection.Up => 1m, + InsightDirection.Down => -1m, + InsightDirection.Flat => 0m, + _ => throw new ArgumentOutOfRangeException(nameof(insightDirection), insightDirection, + Messages.PortfolioTarget.InvalidInsightDirection(symbol, insightDirection)), + }, + tag) + { + } + /// /// Creates a new target for the specified percent /// diff --git a/Common/Messages/Messages.Algorithm.Framework.Portfolio.cs b/Common/Messages/Messages.Algorithm.Framework.Portfolio.cs index 407521fcc31c..18e71e6036dd 100644 --- a/Common/Messages/Messages.Algorithm.Framework.Portfolio.cs +++ b/Common/Messages/Messages.Algorithm.Framework.Portfolio.cs @@ -14,7 +14,7 @@ */ using System.Runtime.CompilerServices; - +using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Interfaces; using QuantConnect.Securities.Positions; @@ -77,6 +77,15 @@ public static string ToString(Algorithm.Framework.Portfolio.PortfolioTarget port return str; } + + /// + /// Returns a string message saying the insight direction is invalid for the given symbol + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string InvalidInsightDirection(QuantConnect.Symbol symbol, InsightDirection insightDirection) + { + return Invariant($"Invalid insight direction {insightDirection} for symbol: {symbol}."); + } } } } From aef7e74f2a391ce7bd7fb01df4a5f9e4f4e44117 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Tue, 29 Jul 2025 09:23:09 -0400 Subject: [PATCH 3/9] Update Pythonnet version to 2.0.45 --- Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj | 2 +- Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj | 2 +- Algorithm.Python/QuantConnect.Algorithm.Python.csproj | 2 +- Algorithm/QuantConnect.Algorithm.csproj | 2 +- AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj | 2 +- Common/QuantConnect.csproj | 2 +- Engine/QuantConnect.Lean.Engine.csproj | 2 +- Indicators/QuantConnect.Indicators.csproj | 2 +- Report/QuantConnect.Report.csproj | 2 +- Research/QuantConnect.Research.csproj | 2 +- Tests/QuantConnect.Tests.csproj | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj index 33ef01251290..7a18645201ad 100644 --- a/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj +++ b/Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj @@ -32,7 +32,7 @@ portable - + diff --git a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj index 489c19902e38..22450fcbce15 100644 --- a/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj +++ b/Algorithm.Framework/QuantConnect.Algorithm.Framework.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj index e7237a5620a3..aaeb41df4483 100644 --- a/Algorithm.Python/QuantConnect.Algorithm.Python.csproj +++ b/Algorithm.Python/QuantConnect.Algorithm.Python.csproj @@ -37,7 +37,7 @@ - + diff --git a/Algorithm/QuantConnect.Algorithm.csproj b/Algorithm/QuantConnect.Algorithm.csproj index 03881d8892cb..ea50e03fe713 100644 --- a/Algorithm/QuantConnect.Algorithm.csproj +++ b/Algorithm/QuantConnect.Algorithm.csproj @@ -29,7 +29,7 @@ LICENSE - + diff --git a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj index 9dd8b33e2c61..dfb08538fd6d 100644 --- a/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj +++ b/AlgorithmFactory/QuantConnect.AlgorithmFactory.csproj @@ -28,7 +28,7 @@ LICENSE - + diff --git a/Common/QuantConnect.csproj b/Common/QuantConnect.csproj index f58a3eeb3c76..70e6de9e22d1 100644 --- a/Common/QuantConnect.csproj +++ b/Common/QuantConnect.csproj @@ -35,7 +35,7 @@ - + diff --git a/Engine/QuantConnect.Lean.Engine.csproj b/Engine/QuantConnect.Lean.Engine.csproj index b8fdc593b9a2..6b933c8c5cd6 100644 --- a/Engine/QuantConnect.Lean.Engine.csproj +++ b/Engine/QuantConnect.Lean.Engine.csproj @@ -41,7 +41,7 @@ - + diff --git a/Indicators/QuantConnect.Indicators.csproj b/Indicators/QuantConnect.Indicators.csproj index f7d70dc72026..945c8836f0e8 100644 --- a/Indicators/QuantConnect.Indicators.csproj +++ b/Indicators/QuantConnect.Indicators.csproj @@ -31,7 +31,7 @@ - + diff --git a/Report/QuantConnect.Report.csproj b/Report/QuantConnect.Report.csproj index c4151f2ec9aa..3e0330d31fac 100644 --- a/Report/QuantConnect.Report.csproj +++ b/Report/QuantConnect.Report.csproj @@ -39,7 +39,7 @@ LICENSE - + diff --git a/Research/QuantConnect.Research.csproj b/Research/QuantConnect.Research.csproj index 872bbcd1d67b..8eb6639eb92e 100644 --- a/Research/QuantConnect.Research.csproj +++ b/Research/QuantConnect.Research.csproj @@ -34,7 +34,7 @@ - + diff --git a/Tests/QuantConnect.Tests.csproj b/Tests/QuantConnect.Tests.csproj index a4c724960e3e..13808262022d 100644 --- a/Tests/QuantConnect.Tests.csproj +++ b/Tests/QuantConnect.Tests.csproj @@ -31,7 +31,7 @@ - + From 2c9eb6a7b82d0cab349d313b4dbc303cac6451bc Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 30 Jul 2025 09:31:20 -0400 Subject: [PATCH 4/9] Minor changes --- Algorithm.Framework/Alphas/MacdAlphaModel.py | 2 +- .../Portfolio/EqualWeightingPortfolioConstructionModel.py | 2 +- .../CustomDataUniverseScheduledRegressionAlgorithm.py | 2 +- Algorithm.Python/UpdateOrderRegressionAlgorithm.py | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Algorithm.Framework/Alphas/MacdAlphaModel.py b/Algorithm.Framework/Alphas/MacdAlphaModel.py index 1d0c87cb419e..693000f964b6 100644 --- a/Algorithm.Framework/Alphas/MacdAlphaModel.py +++ b/Algorithm.Framework/Alphas/MacdAlphaModel.py @@ -65,7 +65,7 @@ def Update(self, algorithm, data): direction = InsightDirection.Down # ignore signal for same direction as previous signal - if sd.PreviousDirection is not None and direction == sd.PreviousDirection: + if direction == sd.PreviousDirection: continue sd.PreviousDirection = direction diff --git a/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py b/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py index 518b9eddf74c..ff9807631aba 100644 --- a/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py +++ b/Algorithm.Framework/Portfolio/EqualWeightingPortfolioConstructionModel.py @@ -59,4 +59,4 @@ def respect_portfolio_bias(self, insight): Args: insight: The insight to create a target for ''' - return self.portfolio_bias == PortfolioBias.LONG_SHORT or int(insight.direction) == int(self.portfolio_bias) + return self.portfolio_bias == PortfolioBias.LONG_SHORT or insight.direction == self.portfolio_bias diff --git a/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py b/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py index 2b7644e31a4a..f33736a6339d 100644 --- a/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py +++ b/Algorithm.Python/CustomDataUniverseScheduledRegressionAlgorithm.py @@ -63,7 +63,7 @@ def on_data(self, data): if len([x for x in custom_data.keys() if x.underlying == symbol]) == 0: raise ValueError(f"Custom data was not found for symbol {symbol}") # equity daily data arrives at 16 pm but custom data is set to arrive at midnight - self.current_underlying_symbols = [symbol for symbol in data.keys() if symbol.security_type == SecurityType.EQUITY] + self.current_underlying_symbols = [symbol for symbol in data.keys() if symbol.security_type is SecurityType.EQUITY] def on_end_of_algorithm(self): if len(self._selection_time) != 0: diff --git a/Algorithm.Python/UpdateOrderRegressionAlgorithm.py b/Algorithm.Python/UpdateOrderRegressionAlgorithm.py index d540e532db58..0871d857c1eb 100644 --- a/Algorithm.Python/UpdateOrderRegressionAlgorithm.py +++ b/Algorithm.Python/UpdateOrderRegressionAlgorithm.py @@ -77,7 +77,7 @@ def on_data(self, data): ticket = self.tickets[-1] if self.time.day > 8 and self.time.day < 14: - if len(ticket.update_requests) == 0 and ticket.status != OrderStatus.FILLED: + if len(ticket.update_requests) == 0 and ticket.status is not OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) update_order_fields = UpdateOrderFields() update_order_fields.quantity = ticket.quantity + copysign(self.delta_quantity, self.quantity) @@ -85,7 +85,7 @@ def on_data(self, data): ticket.update(update_order_fields) elif self.time.day > 13 and self.time.day < 20: - if len(ticket.update_requests) == 1 and ticket.status != OrderStatus.FILLED: + if len(ticket.update_requests) == 1 and ticket.status is not OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) update_order_fields = UpdateOrderFields() update_order_fields.limit_price = self.security.price*(1 - copysign(self.limit_percentage_delta, ticket.quantity)) @@ -93,7 +93,7 @@ def on_data(self, data): update_order_fields.tag = "Change prices: {0}".format(self.time.day) ticket.update(update_order_fields) else: - if len(ticket.update_requests) == 2 and ticket.status != OrderStatus.FILLED: + if len(ticket.update_requests) == 2 and ticket.status is not OrderStatus.FILLED: self.log("TICKET:: {0}".format(ticket)) ticket.cancel("{0} and is still open!".format(self.time.day)) self.log("CANCELLED:: {0}".format(ticket.cancel_request)) From c4cf8a48c4710e58ffc33f21a1802b3895525905 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 30 Jul 2025 17:34:41 -0400 Subject: [PATCH 5/9] Minor fix --- Algorithm.Framework/Alphas/MacdAlphaModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Algorithm.Framework/Alphas/MacdAlphaModel.py b/Algorithm.Framework/Alphas/MacdAlphaModel.py index 693000f964b6..1d0c87cb419e 100644 --- a/Algorithm.Framework/Alphas/MacdAlphaModel.py +++ b/Algorithm.Framework/Alphas/MacdAlphaModel.py @@ -65,7 +65,7 @@ def Update(self, algorithm, data): direction = InsightDirection.Down # ignore signal for same direction as previous signal - if direction == sd.PreviousDirection: + if sd.PreviousDirection is not None and direction == sd.PreviousDirection: continue sd.PreviousDirection = direction From bd521229f9f4af0db29c04dc9c1937e85373ee52 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 31 Jul 2025 10:16:09 -0400 Subject: [PATCH 6/9] Minor fix --- Algorithm.Framework/Alphas/MacdAlphaModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Algorithm.Framework/Alphas/MacdAlphaModel.py b/Algorithm.Framework/Alphas/MacdAlphaModel.py index 1d0c87cb419e..693000f964b6 100644 --- a/Algorithm.Framework/Alphas/MacdAlphaModel.py +++ b/Algorithm.Framework/Alphas/MacdAlphaModel.py @@ -65,7 +65,7 @@ def Update(self, algorithm, data): direction = InsightDirection.Down # ignore signal for same direction as previous signal - if sd.PreviousDirection is not None and direction == sd.PreviousDirection: + if direction == sd.PreviousDirection: continue sd.PreviousDirection = direction From c03d3cf329ad1711112810837d75e87e97012319 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 31 Jul 2025 11:37:41 -0400 Subject: [PATCH 7/9] Minor change --- Common/Extensions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/Extensions.cs b/Common/Extensions.cs index ac9fc13aee80..250f6b2cafbd 100644 --- a/Common/Extensions.cs +++ b/Common/Extensions.cs @@ -3185,6 +3185,7 @@ public static PyList ToPyListUnSafe(this IEnumerable enumerable) /// Numeric value /// Python object that encapsulated a Enum Type /// String that represents the enumerated object + [Obsolete("Deprecated as of 2025-07-31. Please use `str(enum_value)` in Python instead.")] public static string GetEnumString(this int value, PyObject pyObject) { Type type; From 6b03d4d55983d94bf19660a64795af47564b9c46 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 31 Jul 2025 12:02:16 -0400 Subject: [PATCH 8/9] Minor change --- Common/Extensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/Extensions.cs b/Common/Extensions.cs index 250f6b2cafbd..37b5e2403ce3 100644 --- a/Common/Extensions.cs +++ b/Common/Extensions.cs @@ -3185,7 +3185,7 @@ public static PyList ToPyListUnSafe(this IEnumerable enumerable) /// Numeric value /// Python object that encapsulated a Enum Type /// String that represents the enumerated object - [Obsolete("Deprecated as of 2025-07-31. Please use `str(enum_value)` in Python instead.")] + [Obsolete("Deprecated as of 2025-07. Please use `str()`.")] public static string GetEnumString(this int value, PyObject pyObject) { Type type; From 93ebf30ce74b0b009007376146cc4825af4cbfa8 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 31 Jul 2025 15:59:18 -0400 Subject: [PATCH 9/9] Minor unit test fix --- Tests/Algorithm/AlgorithmIndicatorsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Algorithm/AlgorithmIndicatorsTests.cs b/Tests/Algorithm/AlgorithmIndicatorsTests.cs index f977cd1973cd..d6b58ef4b42a 100644 --- a/Tests/Algorithm/AlgorithmIndicatorsTests.cs +++ b/Tests/Algorithm/AlgorithmIndicatorsTests.cs @@ -700,7 +700,7 @@ from QuantConnect.Indicators import * def create_intraday_vwap_indicator(name): return IntradayVwap(name) def create_consolidator(): - return TradeBarConsolidator(Resolution.HOUR) + return TradeBarConsolidator(timedelta(minutes=1)) "; using (Py.GIL())