Tutorial: Multi-Timeframe Strategy¶
This tutorial walks you through building a multi-timeframe momentum strategy from scratch. You'll learn how to:
- Set up bar aggregation for multiple timeframes
- Store bar history in a BarMatrix
- Access bars from different timeframes in your strategy
- Generate trading signals based on MTF analysis
Prerequisites¶
- Basic C++ knowledge
- Flox Engine installed and building
- Completed First Strategy tutorial
What We're Building¶
A momentum strategy that:
- Uses H1 (hourly) bars to determine trend direction
- Uses M5 (5-minute) bars to identify pullbacks
- Uses M1 (1-minute) bars for entry timing
Entry logic: Buy when H1 is bullish, M5 shows a pullback, and M1 prints a reversal bar.
Step 1: Project Setup¶
Create a new file my_mtf_strategy.cpp:
#include "flox/aggregator/bar_aggregator.h"
#include "flox/aggregator/bar_matrix.h"
#include "flox/aggregator/bus/bar_bus.h"
#include "flox/aggregator/multi_timeframe_aggregator.h"
#include "flox/aggregator/timeframe.h"
#include "flox/book/events/trade_event.h"
#include "flox/common.h"
#include "flox/engine/abstract_market_data_subscriber.h"
#include <iostream>
using namespace flox;
Step 2: Define the Strategy Class¶
class MTFMomentumStrategy : public IMarketDataSubscriber
{
public:
explicit MTFMomentumStrategy(SymbolId symbol, BarMatrix<256, 4, 64>* matrix)
: _symbol(symbol), _matrix(matrix)
{
}
SubscriberId id() const override
{
return reinterpret_cast<SubscriberId>(this);
}
void onBar(const BarEvent& ev) override
{
// We'll implement this next
}
private:
SymbolId _symbol;
BarMatrix<256, 4, 64>* _matrix;
};
The strategy:
- Takes a symbol ID and pointer to a BarMatrix
- Implements IMarketDataSubscriber to receive bar events
- Will analyze bars in onBar()
Step 3: Implement the Trading Logic¶
Add the signal generation in onBar():
void onBar(const BarEvent& ev) override
{
// Only process bars for our symbol
if (ev.symbol != _symbol)
{
return;
}
// Only act on M1 bars (fastest timeframe for entries)
if (ev.barType != BarType::Time || ev.barTypeParam != 60)
{
return;
}
// Get bars from different timeframes
const Bar* h1 = _matrix->bar(_symbol, timeframe::H1, 0); // Latest H1
const Bar* h1_prev = _matrix->bar(_symbol, timeframe::H1, 1); // Previous H1
const Bar* m5 = _matrix->bar(_symbol, timeframe::M5, 0); // Latest M5
const Bar* m1 = _matrix->bar(_symbol, timeframe::M1, 0); // Latest M1
// Need all bars to generate signals
if (!h1 || !h1_prev || !m5 || !m1)
{
return; // Not enough data yet
}
// 1. Check H1 trend
bool h1Bullish = h1->close > h1_prev->close;
// 2. Check M5 pullback (bearish bar in uptrend)
bool m5Pullback = m5->close < m5->open;
// 3. Check M1 reversal (bullish bar after pullback)
bool m1Reversal = m1->close > m1->open;
// Generate signal
if (h1Bullish && m5Pullback && m1Reversal)
{
std::cout << "[SIGNAL] BUY @ " << m1->close.toDouble()
<< " | H1 bullish, M5 pullback, M1 reversal" << std::endl;
}
}
Key points:
- We filter for M1 bars to trigger entry logic on the fastest timeframe
bar(symbol, timeframe, index)gives us historical bars (0 = latest, 1 = previous)- We check conditions across all three timeframes
Step 4: Set Up the Infrastructure¶
Now create main() to wire everything together:
int main()
{
constexpr SymbolId SYMBOL = 1;
// 1. Create the bar event bus
BarBus bus;
// 2. Create multi-timeframe aggregator
MultiTimeframeAggregator<4> aggregator(&bus);
aggregator.addTimeInterval(std::chrono::seconds(60)); // M1
aggregator.addTimeInterval(std::chrono::seconds(300)); // M5
aggregator.addTimeInterval(std::chrono::seconds(3600)); // H1
// 3. Create bar matrix for history storage
BarMatrix<256, 4, 64> matrix;
std::array<TimeframeId, 3> timeframes = {
timeframe::M1,
timeframe::M5,
timeframe::H1
};
matrix.configure(timeframes);
// 4. Create strategy
MTFMomentumStrategy strategy(SYMBOL, &matrix);
// 5. Subscribe to bar events
bus.subscribe(&matrix); // Matrix stores bars
bus.subscribe(&strategy); // Strategy receives bars
// 6. Start components
bus.start();
aggregator.start();
// 7. Feed trades (in real system, this comes from connector)
// aggregator.onTrade(tradeEvent);
// 8. Cleanup
aggregator.stop();
bus.stop();
return 0;
}
Step 5: Understanding the Data Flow¶
flowchart TB
TE[TradeEvent] --> MTA[MultiTimeframeAggregator]
MTA --> M1[M1 aggregator]
MTA --> M5[M5 aggregator]
MTA --> H1[H1 aggregator]
M1 --> BE1[BarEvent M1]
M5 --> BE5[BarEvent M5]
H1 --> BEH[BarEvent H1]
BE1 --> BB[BarBus]
BE5 --> BB
BEH --> BB
BB --> BM[BarMatrix<br/>stores bar history]
BB --> STR[MTFMomentumStrategy<br/>generates signals]
- Trades come from your connector
- MultiTimeframeAggregator builds bars for all timeframes simultaneously
- BarBus distributes BarEvents to subscribers
- BarMatrix stores history for lookback
- Strategy accesses BarMatrix for multi-timeframe analysis
Step 6: Adding More Signal Logic¶
Let's enhance the strategy with sell signals and tracking:
class MTFMomentumStrategy : public IMarketDataSubscriber
{
public:
// ... constructor ...
void onBar(const BarEvent& ev) override
{
if (ev.symbol != _symbol) return;
if (ev.barType != BarType::Time || ev.barTypeParam != 60) return;
const Bar* h1 = _matrix->bar(_symbol, timeframe::H1, 0);
const Bar* h1_prev = _matrix->bar(_symbol, timeframe::H1, 1);
const Bar* m5 = _matrix->bar(_symbol, timeframe::M5, 0);
const Bar* m1 = _matrix->bar(_symbol, timeframe::M1, 0);
if (!h1 || !h1_prev || !m5 || !m1) return;
// Trend detection
bool h1Bullish = h1->close > h1_prev->close;
bool h1Bearish = h1->close < h1_prev->close;
// Pullback detection
bool m5Pullback = m5->close < m5->open; // Bearish M5 in uptrend
bool m5Rally = m5->close > m5->open; // Bullish M5 in downtrend
// Entry bar
bool m1BullishReversal = m1->close > m1->open;
bool m1BearishReversal = m1->close < m1->open;
// BUY signal
if (h1Bullish && m5Pullback && m1BullishReversal)
{
std::cout << "[BUY] @ " << m1->close.toDouble() << std::endl;
++_buySignals;
}
// SELL signal
else if (h1Bearish && m5Rally && m1BearishReversal)
{
std::cout << "[SELL] @ " << m1->close.toDouble() << std::endl;
++_sellSignals;
}
}
void printStats() const
{
std::cout << "Buy signals: " << _buySignals << std::endl;
std::cout << "Sell signals: " << _sellSignals << std::endl;
}
private:
SymbolId _symbol;
BarMatrix<256, 4, 64>* _matrix;
int _buySignals = 0;
int _sellSignals = 0;
};
Step 7: Adding Delta Confirmation¶
Use buy/sell volume for confirmation:
// In onBar(), after getting bars:
// Check delta (buy pressure - sell pressure)
Volume buyVol = m1->buyVolume;
Volume sellVol = Volume::fromRaw(m1->volume.raw() - m1->buyVolume.raw());
bool positiveDelta = buyVol.raw() > sellVol.raw();
// Enhanced BUY signal with delta confirmation
if (h1Bullish && m5Pullback && m1BullishReversal && positiveDelta)
{
std::cout << "[BUY] @ " << m1->close.toDouble()
<< " | Delta: +" << (buyVol.raw() - sellVol.raw()) << std::endl;
}
Complete Example¶
See multi_timeframe_demo.cpp for a complete working example.
Next Steps¶
- Add position management and risk controls
- Implement stop-loss and take-profit logic
- Add Volume Profile for key level detection
- Use Footprint for order flow confirmation