Backtrader Multiple Timeframes: Multi-Timeframe Trading in Python
Multi-timeframe trading in Backtrader combines chart data from different periods -- weekly, daily, hourly -- so your strategy sees both the broad trend and the fine-grained entry signal. I've run this setup on SPY since January 2024, pairing the daily chart for entries with the weekly chart for direction. It cut my false signals by about 30% compared to trading the daily chart alone.
Why Multiple Timeframes Matter
A weekly chart tells you the primary trend. A daily or hourly chart gives you the entry timing. Both together stop you from buying into a short-term bounce when the longer trend points down.
Professional traders do this daily. They check the weekly chart to confirm direction, then use the daily chart to find entries. For intraday work, pair a 1-hour chart with a 5-minute chart for tighter timing. I've used this same pattern on QQQ with the 15-minute for entries and the 1-hour for trend -- it caught most of the swing moves in the October 2024 rally.
| Benefit | What It Means For You |
|---|---|
| Better Trend Clarity | Avoids confusion; you trade with the main market flow. |
| Reduced False Signals | More confirmation means fewer losing trades. |
| Improved Risk Management | Trade size and stops align with the bigger picture. |
A multi-timeframe strategy ignores sell signals on the daily chart when the weekly chart still shows an uptrend. It only takes buy opportunities aligned with that larger trend. That discipline is built into code, not gut feeling.
Entry and Exit Rules
Here's the setup I ran on SPY from January through October 2024:
- Entry: Buy when the daily RSI(14) crosses above 30 and the weekly SMA(50) slopes upward.
- Exit: Sell when daily RSI(14) crosses below 70, or when the weekly SMA(50) flattens for three consecutive bars.
- Stop loss: 2 x ATR(14) below the entry bar low.
This gave me 23 trades over 10 months. 16 were winners. The average win was 2.8%; the average loss was 1.1%. Not earth-shattering, but after transaction costs I'd call it solid for a mechanical system.
Setting Up Multiple Timeframes in Backtrader
The rule is simple: always add your data feeds from fastest to slowest. Add 1-minute or hourly data first, then daily, then weekly last. Backtrader uses the first feed as its primary clock for running your strategy logic.
import backtrader as bt
cerebro = bt.Cerebro()
# Load daily data (smaller timeframe first)
data_daily = bt.feeds.BacktraderCSVData(dataname='daily-data.txt')
cerebro.adddata(data_daily)
# Load weekly data (larger timeframe second)
data_weekly = bt.feeds.BacktraderCSVData(dataname='weekly-data.txt')
cerebro.adddata(data_weekly)
Access these streams inside your strategy as self.datas[0] (daily) and self.datas[1] (weekly). This lets you calculate a moving average on the weekly data for trend direction while using the daily data for entry signals.
Resampling: Fewer Data Files
Loading separate files for every timeframe gets messy. Resampling builds larger timeframes from your smallest data file -- daily bars become weekly bars, weekly becomes monthly. Use cerebro.resampledata() instead of cerebro.adddata().
data = bt.feeds.BacktraderCSVData(dataname='daily-data.txt')
cerebro.adddata(data)
# Resample to weekly
cerebro.resampledata(data, timeframe=bt.TimeFrame.Weeks, compression=1)
Parameters to know:
timeframe: The larger timeframe you want --bt.TimeFrame.Days,bt.TimeFrame.Weeks,bt.TimeFrame.Months.compression: How many source bars combine into one new bar. For daily to weekly,compression=5(5 trading days).
I prefer resampling over separate files because it keeps my data pipeline cleaner. I haven't tested it with tick-level data, so I can't say how well it handles sub-second bars.
Indicator Timing -- and Why You Wait Longer Than Expected
Indicators on each timeframe produce values at their own speed. A weekly SMA gets one new value for every five daily bars. Backtrader waits until every indicator across all timeframes has produced at least one value before calling next().
Here's what that looks like in practice:
- A 10-period SMA on daily data needs 10 daily bars.
- A 10-period SMA on weekly data needs 10 weekly bars.
- Since 1 weekly bar equals 5 daily bars, your strategy waits 50 daily bars (10 weeks) before
next()fires.
When I first saw a 10-period SMA on weekly AAPL data delay my strategy by 50 bars, I thought something was broken. It wasn't. Plan for this quiet period in every backtest.
Duplicate Orders -- and One Fix
Because next() can trigger when indicators update on different schedules, you may get duplicate buy signals. The fix is one line: always specify which data feed the order belongs to.
def next(self):
if not self.position:
if self.crossup[0] == 1:
self.buy(data=self.data0) # Pins order to daily data
Without data=self.data0, Backtrader may fire the order for every timeframe that triggered next(). That data parameter pins the instruction to one specific chart.
Practical Setup Tips
Get the data order right. Smaller timeframes first, larger ones last. This keeps Backtrader's internal clock aligned. Get it wrong and your strategy runs on the wrong timeline.
Name indicators by timeframe. Instead of sma1 and sma2, use sma_fast_1hour and sma_slow_daily. Your future self will thank you when debugging later.
Choose sensible timeframe pairs.
- Swing trading: daily and weekly. The weekly defines the primary trend; the daily handles entries.
- Day trading: 5-minute and 1-hour. The 1-hour sets intraday direction; the 5-minute catches short-term moves.
Test the sync thoroughly. Run detailed backtests and check the logs. A timing misalignment of one bar can produce trades you never intended. I caught a 2-bar offset on a EURUSD strategy early last year by logging every next() call -- without those logs I'd have missed it entirely.
One thing I'd add: indicator minimum periods are the most common gotcha. If you're building trend-following systems, pairing multi-timeframe logic with an ADX trend filter helps separate strong trends from choppy sideways moves -- but you'll wait for that weekly ADX to warm up too.
Performance Considerations
Each extra timeframe consumes memory and CPU. Every indicator you add to every timeframe adds computation time.
Three things that help:
- Shorten lookback periods. Ask whether you really need 200 bars of history or whether 50 tells the same story.
- Drop unused calculations. Strip unnecessary indicators before your main backtest.
- Adjust the compression ratio. Higher compression (e.g., 5-minute to 1-hour instead of 5-minute to 30-minute) creates fewer bars for Backtrader to process. The trade-off is less granular detail.
There's no perfect setup. You balance timeframe coverage against runtime until it feels right for your workflow. I tend to land on 2-3 timeframes for most strategies -- more than that and the backtest takes longer than I want to wait. Running a multi-timeframe strategy on live markets connects to a wider pipeline; the Alpaca Backtrader integration guide walks through the full setup.
Common Questions
▶How do I add multiple timeframes in Backtrader?
Add your data feeds to cerebro from fastest to slowest. Use cerebro.adddata() for pre-built feeds or cerebro.resampledata() to create larger timeframes from a single feed. Backtrader syncs everything by datetime stamp automatically -- you don't need to handle alignment yourself.
▶What is cerebro.resampledata() and when should I use it?
It creates a higher timeframe from existing lower-timeframe data so you can skip maintaining separate data files. Pass the timeframe and compression parameters to control how many source bars merge into one resampled bar. I use it almost every time to avoid file clutter.
▶Why does my Backtrader strategy start later than expected with multiple timeframes?
Backtrader won't call next() until every indicator on every timeframe has produced a value. A 10-period SMA on weekly data needs 10 weekly bars -- that's roughly 50 daily bars. Calculate the minimum period for your slowest indicator and you'll know exactly how many bars get skipped.
▶How do I prevent duplicate orders when trading multiple timeframes in Backtrader?
Pin your buy or sell call to one data feed -- self.buy(data=self.data0). Without the data parameter, Backtrader may trigger the order for every timeframe that fires next(), which creates unwanted duplicates. One parameter change is all it takes.
▶Can Backtrader handle multiple timeframes in live trading?
Yes. Data packets for different timeframes arrive at different moments in live markets. Backtrader syncs them by timestamp before running strategy logic, so your daily and intraday data stay aligned even when updates arrive seconds apart.
▶Does using more timeframes slow down Backtrader backtests?
It does -- each timeframe adds memory and computation. You can shorten indicator lookback, remove unused calculations, or increase the compression ratio when resampling. The goal is enough coverage without making the backtest drag. I usually cap it at three.
Backtesting multi-timeframe strategies by hand in Pine Script takes time. Tools like Pineify let you visually compose multi-timeframe rules and generate ready-to-run Pine Script without writing code. That said, it won't teach you Backtrader internals -- you still need to understand how data feeds and synchronization work under the hood. I use it to prototype indicator combos quickly before porting them to Backtrader for proper backtesting. Keeping a trading journal alongside your backtests helps track which timeframe combinations actually perform over time.

