@@ -587,7 +587,7 @@ def visit(value):
587587
588588 result = []
589589 for attr_name , attr_value in attrs .items ():
590- if attr_name . startswith ( "_" ) :
590+ if attr_name in { "_strategy_lineactions_cache" , "_strategy_next_lineactions_cache" } :
591591 continue
592592 result .extend (visit (attr_value ))
593593
@@ -904,52 +904,43 @@ def _feed_of(node, _seen=None):
904904 # Indicator minimum periods
905905 minperiods = [x ._minperiod for x in all_indicators ]
906906
907- # CRITICAL FIX: Also scan strategy attributes for LineActions objects
908- # (like LinesOperation from sma - sma(-10)) that aren't registered as indicators
909- # but still need their minperiod considered
910- from .linebuffer import LineActions
911-
912- for attr_name in dir (self ):
913- if attr_name .startswith ("_" ):
914- continue
915- try :
916- attr = getattr (self , attr_name )
917- # Check if it's a LineActions but not already in _lineiterators
918- if isinstance (attr , LineActions ) and hasattr (attr , "_minperiod" ):
919- if attr not in self ._lineiterators [LineIterator .IndType ]:
920- minperiods .append (attr ._minperiod )
921- except (AttributeError , TypeError ):
922- # Attribute access/typecheck failed; skip this attribute.
923- pass
907+ # Strategy-owned LineActions are not necessarily registered indicators.
908+ # Original backtrader's metaclass machinery advanced them regardless of
909+ # whether users kept them in public or private attributes. Reuse the
910+ # recursive strategy-attribute scan here so private containers such as
911+ # self._Type / self._OptionType participate in minperiod and execution.
912+ strategy_lineactions = tuple (self ._get_strategy_lineactions ())
913+ for attr in strategy_lineactions :
914+ if (
915+ hasattr (attr , "_minperiod" )
916+ and attr not in self ._lineiterators [LineIterator .IndType ]
917+ ):
918+ minperiods .append (attr ._minperiod )
924919
925920 # Set strategy minimum period to max of indicator and data minperiods
926921 self ._minperiod = max (minperiods or [self ._minperiod ])
927922
928- # CRITICAL FIX: Update _minperiods for LineActions, but only for their associated data
929- # For single-data strategies, apply LineActions minperiod to data[0]
930- # For multi-data strategies, LineActions minperiod should only affect its source data
931- from .linebuffer import LineActions
932-
923+ # Update _minperiods for strategy-owned LineActions, but only for their
924+ # associated data. For multi-data strategies, LineActions minperiod
925+ # should only affect the source data that clocks the expression.
933926 if self ._minperiods :
934- for attr_name in dir (self ):
935- if attr_name .startswith ("_" ):
936- continue
927+ for attr in strategy_lineactions :
937928 try :
938- attr = getattr ( self , attr_name )
939- if isinstance ( attr , LineActions ) and hasattr ( attr , "_minperiod" ):
940- # Try to determine which data this LineActions is associated with
941- # by checking its _clock or data sources
942- data_idx = 0 # Default to data[0]
943- if hasattr (attr , "_clock" ) and attr ._clock is not None :
944- for i , d in enumerate (self .datas ):
945- if attr ._clock is d or attr ._clock in d .lines :
946- data_idx = i
947- break
948- # Only update minperiod for the specific data
949- if data_idx < len (self ._minperiods ):
950- self ._minperiods [data_idx ] = max (
951- self ._minperiods [data_idx ], attr ._minperiod
952- )
929+ if not hasattr ( attr , "_minperiod" ):
930+ continue
931+ # Try to determine which data this LineActions is associated
932+ # with by checking its _clock or data sources.
933+ data_idx = 0 # Default to data[0]
934+ if hasattr (attr , "_clock" ) and attr ._clock is not None :
935+ for i , d in enumerate (self .datas ):
936+ if attr ._clock is d or attr ._clock in d .lines :
937+ data_idx = i
938+ break
939+ # Only update minperiod for the specific data.
940+ if data_idx < len (self ._minperiods ):
941+ self ._minperiods [data_idx ] = max (
942+ self ._minperiods [data_idx ], attr ._minperiod
943+ )
953944 except (AttributeError , TypeError ):
954945 # Attribute access/typecheck failed; skip this attribute.
955946 pass
@@ -1502,7 +1493,9 @@ def _next(self):
15021493 forward_line ._idx += 1
15031494 forward_line .lencount += 1
15041495 forward_line .array .append (
1505- dt_value if valid_dt and dt_value >= 1.0 else forward_line ._default_value
1496+ dt_value
1497+ if valid_dt and dt_value >= 1.0
1498+ else forward_line ._default_value
15061499 )
15071500 elif valid_dt :
15081501 idx = datetime_line ._idx
@@ -1726,8 +1719,7 @@ def _start(self):
17261719 and type (self ).notify_cashvalue is Strategy .notify_cashvalue
17271720 )
17281721 self ._notify_fund_default = (
1729- "notify_fund" not in self .__dict__
1730- and type (self ).notify_fund is Strategy .notify_fund
1722+ "notify_fund" not in self .__dict__ and type (self ).notify_fund is Strategy .notify_fund
17311723 )
17321724 self ._skip_empty_notify = (
17331725 not self ._quicknotify
@@ -1736,7 +1728,9 @@ def _start(self):
17361728 and self ._notify_fund_default
17371729 )
17381730 self ._fast_simple_next = (
1739- not self ._lineiterators [LineIterator .IndType ]
1731+ len (self .datas ) == 1
1732+ and len (self ._minperiods ) == 1
1733+ and not self ._lineiterators [LineIterator .IndType ]
17401734 and not self ._lineaction_datas
17411735 and not self ._has_strategy_next_lineactions
17421736 and self ._skip_empty_notify
0 commit comments