The Complete Market Maker Cycle Tracking System
for TradingView — Pine Script v6
The BTMM State Engine Pro for TradingView is a two-script system that brings institutional-grade Market Maker cycle tracking directly onto TradingView charts. No broker plugins, no external software — just add the indicators and trade.
The main indicator — applied to any single chart.
The scanner — monitors up to 16 pairs from one chart.
request.security()BTMM_State_Engine_Pro_TV.pine — The IndicatorBTMM_Multi_Pair_Scanner_Pro_TV.pine — The Scanner
Adding the scripts to TradingView takes under two minutes.
At the bottom of any TradingView chart, click the Pine Editor tab. If it's collapsed, click the up-arrow or the tab name to expand it.
Open BTMM_State_Engine_Pro_TV.pine in any text editor, select all (Ctrl+A), copy (Ctrl+C), then paste into the Pine Editor (Ctrl+V), replacing any existing code.
Press Ctrl+S to save, give it a name (e.g. "BTMM State Engine Pro"), then click "Add to Chart". The indicator will appear on your chart immediately.
Open a new Pine Editor tab (click the "+" icon), paste BTMM_Multi_Pair_Scanner_Pro_TV.pine, save, and add to chart. The scanner table will appear.
| Setting | Recommendation | Why |
|---|---|---|
| Timeframe | M15 (primary), H1 for structure | Best balance of signal frequency and accuracy |
| Chart Type | Candlestick | Required for Vector Candle filter |
| Session Timezone | Match your broker's server time | Ensures Asian Range aligns correctly |
| Max Bars | At least 5000 | Enough history for 800 EMA and level counting |
The Beat The Market Maker (BTMM) system, pioneered by Steve Mauro, is built on one core insight: Institutional market makers follow a predictable daily cycle to fill orders.
Accumulation — During the Asian session, price trades in a tight range (the "Asian Range"). Market makers are quietly building positions.
Manipulation — At London or NY open, price spikes beyond the Asian Range to trigger retail stop losses. This is the "stop hunt".
Distribution — After sweeping liquidity, price reverses sharply in the opposite direction. This is the high-probability trade.
Each day's Asian Range creates a "room". If today's room clears above yesterday's — that's a Level Up. Rooms stack like a staircase:
L1 = First room (neutral direction). L2+ = Peak Zone (reversal imminent). When the staircase reverses at L2 or higher — that's a Peak Formation (PFH for a top, PFL for a bottom).
Each Asian Range is compared to the previous one. Rooms connect edge-to-edge with no overlap — the previous room's right edge ends exactly where the next room's left edge begins, creating a clean staircase visual. There are three possible outcomes:
| Outcome | Condition | Effect |
|---|---|---|
| Clear Up | Today's low ≥ Yesterday's high | Level increments, room moves UP. New dotted-border rectangle drawn in blue. |
| Clear Down | Today's high ≤ Yesterday's low | Level increments, room moves DOWN. New rectangle drawn in red. |
| Overlap | Ranges intersect | Room expands to contain both ranges. Room box count increases. No new level. |
If a room accumulates too many overlapping Asian Ranges without breaking out, price is consolidating — the cycle has stalled. When the box count reaches the Stale Threshold (default: 5), the room goes stale. Unlike a full reset, stale preserves the current level and direction. A new room opens at the same level with grey colouring, and the label shows "Lx STALE" (e.g. "L3 STALE"). Because the direction is preserved, the next opposite break correctly triggers PFH/PFL.
At L2 or higher, a direction reversal triggers a two-stage confirmation:
PF labels use a simplified format: pending peaks show as PFH? / PFL?, and confirmed peaks as PFH ✓ / PFL ✓ — no level numbers in the labels.
Confirmation uses close-based prices: a PFL? is confirmed when the next room's lowest close exceeds the peak room's highest close, and a PFH? is confirmed when the next room's highest close falls below the peak room's lowest close. On confirmation, all rooms in the old cycle are retroactively recoloured — blue rooms turn red (PFH) or vice versa — giving you a clear visual history of where the cycle peaked.
lastPFHHigh and lastPFLLow to stay alert during shallow pullbacks. Peak resets don't need L2+ — they only require an existing confirmed peak.
The State Engine plots five Exponential Moving Averages, each with a specific role in the BTMM framework:
Three optional filters are applied before any signal fires:
| Filter | Logic | Purpose |
|---|---|---|
| Vector Candle | Current candle body > 1.2× average of last 5 | Ensures momentum behind the signal |
| TDI / RSI | RSI signal line vs. trade line, plus zone check (30/50/70) | Confirms internal momentum alignment |
| Cycle Direction | Only BUY in UP cycles, only SELL in DOWN cycles (unless PF detected) | Prevents counter-trend signals in active trends |
The State Engine classifies every EMA 5/13 crossover into one of four signal types based on price's relationship to the Asian Range and the 50 EMA:
Price has broken below the Asian Range low (for BUY) or above the high (for SELL), then EMA cross occurs. This is the classic BTMM stop-hunt reversal — the highest probability setup.
EMA cross occurs while price remains within the Asian Range zone. No stop hunt detected. Good continuation signal within an established trend.
EMA cross occurs and price is near the 50 EMA (within 15 pips). This is the "Institutional Dynamic" pullback entry. Appears when price respects the ID50 line without having broken the range first.
Price has already broken the Asian Range, then pulls back to the 50 EMA and crosses. This is a re-entry after the initial move — a "safety" second chance trade.
Each valid signal places three elements on the chart:
| Element | Colour | Description |
|---|---|---|
| Label | Green (BUY) / Red (SELL) / Teal (ID50) / Gold (Safety) | Shows "BUY T1", "SELL T2", etc. at the signal bar |
| SL Line | Red | Stop Loss line projected 15 bars forward. Distance = SL Buffer pips from signal bar |
| TP Line | Green | Take Profit projected at 1× Asian Range width from entry |
| Entry Line | White (dashed) | Marks the close price at signal bar |
Show Filtered-Out Signals is enabled, signals that fail the Vector or TDI filter appear as gray "SKIP" labels with the reason (e.g. "(Vec)" or "(TDI)"). Useful for learning why signals were rejected.
The on-chart dashboard gives you a complete state snapshot at a glance. It appears as a compact table in your chosen corner.
| Field | Values | Colour Key |
|---|---|---|
| Level | L1, L2, L3+, or L# STALE | Blue=UP, Red=DOWN, Gray=Neutral/Stale |
| Direction | UP, DOWN, or — | Blue=UP, Red=DOWN |
| Phase | Init, Trend, Peak Zone, Reversal, STALE | Blue=Trend, Gold=Peak, Red=Reversal, Gray=Stale |
| Peak Form | PFH ✓, PFL ✓, PFH?, PFL?, or — | Red=PFH, Blue=PFL |
| Room Boxes | count / max | Orange when near threshold |
| Kill Zone | LONDON, NEW YORK, CLOSED | Green=Active, Red=Closed |
| ADR | Range / ATR (ADR%) | White<60%, Blue 60-80%, Orange 80-100%, Red >100% |
| Mayo (200) | ABOVE / BELOW | Blue=Above (bullish), Red=Below (bearish) |
| Status | READY, ASIAN, WAIT KZ, STALE | Green=Ready, Orange=Waiting |
The scanner runs the full BTMM state engine on up to 16 currency pairs simultaneously, displaying everything in a single dashboard table. No need to flip between charts — see the entire market at a glance.
The scanner uses TradingView's request.security() function to fetch data for each pair and run the BTMM state logic remotely. Bank 1 (pairs 1-8) is always active. Bank 2 (pairs 9-16) can be toggled on when needed. The scanner function is memory-optimised — it returns compact tuples with no internal arrays, keeping it stable in replay mode and on lower timeframes.
request.security() calls per script. Bank 1 uses 16 calls (8 pairs × 2: state + ADR). Bank 2 uses another 16. With both banks enabled, 32 calls are used. This is within limits but leaves little room for additional data feeds.
| PAIR | LVL | PEAK | DIR | SIGNAL | KZ | L3 WARN | PHASE | ADR | RNG | ADR% |
|---|---|---|---|---|---|---|---|---|---|---|
| EURUSD | L2 | — | ▲ | BUY T1 | Active | — | Trend | 78.2 | 42.1 | 54% |
| GBPUSD | L3 | PFH? | ▼ | — | Active | ▼ SHIFT | Peak | 102.5 | 88.3 | 86% |
| USDJPY | L1 | PFL | ▲ | — | Wait | — | Rev | 95.0 | 31.2 | 33% |
| KZ: LDN | 09:45 | BTMM State Engine Pro [G-Labs] | ||||||||||
| Column | Shows | Colours |
|---|---|---|
| PAIR | Symbol name (exchange prefix stripped) | White |
| LVL | Current level (L1, L2, L3+). "!" suffix = stale | Blue=UP, Red=DOWN. Orange bg if stale |
| PEAK | Peak Formation status: PFH, PFL, PFH?, PFL?, PFL↻, PFH↻ | Red=PFH, Blue=PFL, ↻=potential reversal |
| DIR | ▲ (UP), ▼ (DOWN), or — | Blue/Red/Gray |
| SIGNAL | Active signal (BUY T1, SELL Safety, etc.) or — | Green=BUY, Red=SELL |
| KZ | Active or Wait | Green/Red |
| L3 WARN | ▼ SHIFT or ▲ SHIFT at L3+ | Gold when active |
| PHASE | Init, Trend, Peak, Rev, STALE | Blue/Gold/Red/Gray |
| ADR | Average Daily Range in pips | Teal |
| RNG | Today's range in pips | Gold |
| ADR% | Today's range as % of ADR | White→Blue→Orange→Red |
All settings are accessible via the indicator's Settings panel (gear icon or double-click the indicator name).
Must match your broker's server timezone for the Asian Range to align correctly. Common values: Europe/London, UTC, America/New_York, UTC+2.
The 7-hour Asian consolidation window. Adjust if your broker's chart time differs from the timezone setting.
Colour of the Asian Range rectangle drawn on the chart. The border is drawn with less transparency for visibility.
Draws a dashed horizontal line at the midpoint of the Asian Range. Useful for gauging price position within the box.
How many overlapping Asian Ranges are allowed in one room before it's marked STALE. Lower = stricter, Higher = more patient.
Customise the dotted-border room rectangles that wrap each level. They recolour retroactively on PF confirmation.
Shows level labels above each room rectangle at size.normal for maximum visibility. Labels have coloured semi-transparent backgrounds — blue for UP rooms, red for DOWN, grey for neutral/stale. PFH/PFL labels get distinct coloured backgrounds to stand out.
Only fires signals when the trigger candle's body is >1.2× the 5-bar average. Ensures momentum.
Checks RSI signal vs. trade line alignment plus zone thresholds (30/50/70).
Blocks counter-trend signals. BUY only when cycle is UP, SELL only when DOWN. PF detection overrides this.
Distance in pips from signal bar to the projected Stop Loss line. Adjust per your risk tolerance.
The scanner has two banks of 8 pairs each. Bank 1 is always active. Bank 2 is off by default and can be enabled with the Enable Pairs 9-16 toggle.
| Bank | Default Pairs | request.security() Calls |
|---|---|---|
| Bank 1 (Always On) | EURUSD, GBPUSD, USDJPY, AUDUSD, USDCAD, NZDUSD, USDCHF, EURGBP | 16 (8×2: state + ADR) |
| Bank 2 (Optional) | GBPJPY, EURJPY, AUDJPY, CADJPY, GBPAUD, EURAUD, GBPCAD, EURCHF | +16 (32 total) |
Where the scanner table appears on the chart. Options: Bottom Right, Bottom Left, Top Right, Top Left.
0 = fully opaque, 100 = fully transparent. Adjust so the table is readable without obscuring chart data.
Toggles the ADR, Range, and ADR% columns. Disable for a more compact table.
Toggles the Phase column (Init/Trend/Peak/Rev/STALE). Disable for narrower display.
The scanner has its own Stale Threshold setting (default: 5), which works identically to the indicator's room stale detection. Stale preserves the current level and direction — it opens a new room at the same level with grey colouring. Stale pairs show an "!" suffix on their level and an orange background in the scanner table.
Orange dashed lines mark the previous day's high and low. These act as key support/resistance levels and are commonly targeted by market makers during stop hunts.
When enabled, dotted gray lines appear at round-number price levels (every 50 pips for standard pairs, adjusted for JPY). These are institutional order-cluster zones.
The indicator includes optional zone overlays that visualise accumulation and distribution areas within each room. Select a mode via the Zone Mode setting:
| Mode | Name | Description |
|---|---|---|
| ZN0 | Off (default) | Zones disabled — no extra rectangles drawn. |
| ZN1 | AR Union | A dashed rectangle showing the union of all Asian Range boxes within the current room. Highlights the full accumulation zone. |
| ZN2 | Candle Extremes | A dotted rectangle covering all candle wicks (high to low) across the room's full timespan. Shows the complete distribution range. |
ADR is calculated from Daily timeframe data (not the chart timeframe), ensuring accurate values regardless of which timeframe you're viewing. The dashboard shows today's range in pips, the ADR in pips, and the ADR% used. The colour coding tells you at a glance how much room is left:
| ADR% | Meaning | Colour | Action |
|---|---|---|---|
| < 60% | Range barely started | White | Full room for the move — ideal trading conditions |
| 60-80% | Range developing | Blue | Proceed with normal position sizing |
| 80-100% | Range nearly exhausted | Orange | Reduce position size, tighten stops |
| > 100% | Range exceeded | Red | Avoid new trades — the daily move is done |
Both scripts include two types of alerts: Alert Conditions (manual setup via TradingView's alert dialog) and Runtime Alerts (fire automatically when you set an "Any alert() call" alert).
Press Alt+A (or click the alarm clock icon in the right toolbar).
In the "Condition" dropdown, choose "BTMM State Engine Pro [G-Labs]" (or the Scanner).
For specific alerts: select from the dropdown (e.g. "BUY T1", "PF Confirmed").
For all alerts at once: select "Any alert() function call" — this catches every runtime alert the script fires.
Choose your notification method: App notification, Email, Webhook, or SMS. Set expiration and click Create.
| Alert | Fires When |
|---|---|
| PF Pending | L2+ reversal detected — PFH? or PFL? set |
| PF Confirmed | Pending PF confirmed by next clear break |
| Room Stale | Room accumulates max overlapping boxes |
| London / NY KZ Start | Kill zone session begins |
| BUY T1 / T2 / ID50 / Safety | Respective buy signal fires |
| SELL T1 / T2 / ID50 / Safety | Respective sell signal fires |
| Any BUY / Any SELL / Any Entry | Combined conditions |
The scanner fires per-pair runtime alerts with detailed messages including the pair name, signal type, level, direction, and peak formation status:
BTMM Pro: BUY T1 | EURUSD | 1.0842 | L2 UP | —
Open the scanner and identify pairs at L1 (fresh reversal) or early L2 (established trend). These are your primary watchlist. Pairs at L2+ are already in the peak zone — only trade these if you're looking for PF reversals.
Filter out any pairs already above 80% ADR — the daily move is nearly done. Focus on pairs below 60%.
Open the best candidate pair with the State Engine indicator on M15. Check the dashboard: Level, Phase, Mayo status, and Kill Zone.
If KZ is "CLOSED", wait. The best signals occur during London (08:00-11:00) and NY (13:00-16:00) sessions.
When a labelled signal appears (BUY/SELL + type), enter with SL at the indicated red line and TP at the green line. Prefer T1 signals as they have the highest win rate.
| # | Rule |
|---|---|
| 1 | Never trade during the Asian session — that's accumulation, not distribution |
| 2 | T1 (Stop Hunt) entries are the highest probability — prioritise them |
| 3 | If ADR% is red (>100%), the daily move is done — stand aside |
| 4 | STALE rooms mean no clear direction — wait for a breakout |
| 5 | PFH/PFL confirmed = trend change — start looking for signals in the new direction |
| 6 | Always use the scanner to find the best pair, then switch to the indicator for precision |
| 7 | Mayo (200 EMA) alignment increases probability — trade with the macro trend |
| 8 | Risk 1-2% per trade maximum — the system gives you an edge, but no signal is 100% |
Follow this step-by-step walkthrough for your first live session with the State Engine Pro.
Tab 1: Any chart with the Scanner indicator added. This is your "command centre".
Tab 2: EURUSD M15 with the Indicator added. This is your "execution chart".
On both scripts, set Session Timezone to match your broker's server time. If unsure, check TradingView's bottom-right time display vs. your broker's platform.
On the indicator chart, scroll back and confirm the teal Asian Range boxes align with the Asian session hours (roughly 22:00 - 05:00 London time). If they're misaligned, adjust the timezone or session times.
Press Alt+A, select the Indicator, choose "Any alert() function call", enable app notifications, and create. Repeat for the Scanner. You'll now get alerts for every signal, PF, and KZ start.
Watch the scanner. When the KZ column turns green ("Active") for your pairs and the indicator dashboard shows "READY", you're in the trading window.
When a BUY T1 or SELL T1 label appears on the indicator chart, enter the trade with the displayed SL and TP levels. Record the result in your journal.
The most common issue. Your Session Timezone doesn't match your broker's server time.
Fix: Change the timezone setting. Try Europe/London, UTC, or your broker's specific timezone. Compare the box position to when you know the Asian session should be.
Several possible causes:
Fix: (1) Check if Filter Signals by Kill Zones is ON — signals only fire during KZ hours. (2) Check if the room is STALE. (3) Check if all three filters are ON — try disabling one at a time. (4) Ensure you're on M15 timeframe.
Bank 2 is disabled by default. When disabled, the Bank 2 request.security() calls fall back to pair1's data.
Fix: Enable Enable Pairs 9-16 in the scanner settings.
TradingView limits the number of security calls per script.
Fix: If using both banks, ensure you haven't added other indicators that also use request.security() on the same chart. Alternatively, disable Bank 2 and run two scanner instances on separate chart tabs.
TradingView allows max 500 boxes, 500 lines, and 500 labels per indicator.
Fix: Reduce the visible history by zooming in. The indicator draws objects for each session, so very long history windows can hit limits. Alternatively, hide optional elements (Psychological Levels, Yesterday H/L, Show Invalid Signals).
Level counting requires the current bar's timeframe to properly detect session boundaries.
Fix: Use M15 or H1 timeframes. Very high timeframes (D, W) don't have intra-day session detection. Very low timeframes (M1) work but may be slow due to the large number of bars processed.
If you also use the MT5 version of State Engine Pro, here's what to know about the TradingView edition:
| Feature | MT5 Version | TradingView Version |
|---|---|---|
| Platform | MetaTrader 5 desktop | TradingView (web, desktop, mobile) |
| Language | MQL5 | Pine Script v6 |
| Scanner Pairs | Unlimited (comma-separated list) | Up to 16 (2 banks of 8) |
| Push Notifications | MT5 mobile push | TradingView app notification, email, webhook, SMS |
| Dashboard Drag | Click-and-drag to reposition | Fixed position (9 positions available in settings) |
| Broker Suffix | Auto-detected (e.g. EURUSD.m) | Not needed — TradingView uses universal symbols |
| Multi-Timeframe | Full MTF with H1/M15 dual data | Single timeframe per chart (use multiple tabs) |
| Stale Detection | Configurable threshold | Configurable threshold (same logic) |
| Two-Stage PF | Pending → Confirmed | Pending → Confirmed (same logic) |
| Room Recolouring | Instant on PF confirmation | Retroactive recolouring via array tracking |
| Accessibility | Windows only (broker account needed) | Any device, any browser, free TradingView account |
Whether you have a setup question, a feature request, or need help configuring the indicator for your broker — we're here to help.