MTF Patterns
Multi-timeframe context and request.security placement — why MTF filters belong in build_signal_frame, not build_trade_frame.
MTF Context Belongs in the Signal Stage#
If a strategy uses a higher-timeframe trend, regime, or confirmation filter, that filter is part of the
strategy logic. It therefore belongs in build_signal_frame(...) next to the local entry conditions, not
inside build_trade_frame(...).
MTF rule: request.security calls are strategy computation. Moving them into build_trade_frame splits
your strategy's decision logic across two stages, making it harder to reason about, test, and maintain.
Why the signal stage owns MTF#
- Higher-timeframe filters determine whether an entry fires at all — that is signal logic, not mapping logic.
build_trade_frameis a thin normalization pass; it must not callrequest.securityor run TA computations.- Keeping MTF context co-located with entries makes the full strategy readable in one place.
Anti-pattern summary#
# Wrong: HTF confirmation is delayed into mapping.
def build_trade_frame(signal_df, params=None, styles=None):
# DO NOT call request.security here
# DO NOT filter rows based on HTF data here
return build_mapped_trade_frame(signal_df)
# Right: request.security is part of signal logic and belongs with entries.
def build_signal_frame(df, params=None):
frame = df.copy().reset_index(drop=True)
htf_data = request.security(symbol, "1D", "close")
frame["htf_trend"] = htf_data["close"] > htf_data["close"].shift(1)
# use frame["htf_trend"] as a filter for entries
return frame