TradingView MACD Crossover Strategy: Rules and Backtest Checks
This tutorial walks through an existing Pine Script v5 example: 12/26/9 MACD crossovers, an optional 200-period EMA price filter, and percentage profit/stop orders. The source is a starting point for inspection, not a verified profitable system. No reproducible performance result is supplied here; the checklist below shows what a test must record.
Define the crossover before testing it
MACD is the fast average minus the slow average. This example uses EMAs with lengths 12 and 26, then a 9-period EMA signal. The histogram is MACD minus signal.
- A bullish crossover moves MACD from at or below signal to above it.
- A bearish crossunder moves MACD from at or above signal to below it.
- A histogram crossing zero is the same relationship changing sign; it is not an earlier independent signal.
Periods follow the chart interval, not necessarily days. A histogram value is a gap between averages, not the velocity of the underlying price. Crossovers can reverse quickly in ranges.
Sources: TradingView MACD definitions and TradingView strategy order and broker-emulator behavior.
| Component | Description | Role |
|---|---|---|
| MACD line | Fast EMA minus slow EMA | A difference in the source price’s units |
| Signal line | 9-period EMA of MACD by default | Comparison series for crossover rules |
| Histogram | MACD minus signal | Positive means MACD exceeds signal; negative means it is below |
| Price filter | Close above/below a 200-period EMA | Restricts long/short entry eligibility; not a proven improvement |
Pine Script v5 Implementation
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify_Official
//@version=5
strategy("MACD Crossover Pro Strategy - Pineify Optimized",
overlay=true,
initial_capital=10000,
default_qty_type=strategy.percent_of_equity,
default_qty_value=100,
commission_type=strategy.commission.percent,
commission_value=0.1)
// --- Input Parameters ---
// MACD Settings
fast_length = input.int(12, title="MACD Fast Length", minval=1, group="MACD Settings")
slow_length = input.int(26, title="MACD Slow Length", minval=1, group="MACD Settings")
signal_smoothing = input.int(9, title="Signal Line Smoothing", minval=1, group="MACD Settings")
src = input.source(close, title="Source", group="MACD Settings")
// Trend Filter Settings
use_ema_filter = input.bool(true, title="Use 200 EMA Filter?", group="Filters")
ema_filter_len = input.int(200, title="EMA Filter Length", minval=1, group="Filters")
// Risk Management Settings
tp_percent = input.float(5.0, title="Take Profit (%)", minval=0.1, step=0.1, group="Risk Management")
sl_percent = input.float(2.5, title="Stop Loss (%)", minval=0.1, step=0.1, group="Risk Management")
// --- Calculations ---
// MACD Calculation
[macdLine, signalLine, histLine] = ta.macd(src, fast_length, slow_length, signal_smoothing)
// Trend Filter Calculation
ema_filter = ta.ema(close, ema_filter_len)
is_uptrend = close > ema_filter
is_downtrend = close < ema_filter
// --- Entry & Exit Logic ---
// Long Entry: MACD Crossover + (Optional) Price above 200 EMA
longCondition = ta.crossover(macdLine, signalLine) and (not use_ema_filter or is_uptrend)
// Short Entry: MACD Crossunder + (Optional) Price below 200 EMA
shortCondition = ta.crossunder(macdLine, signalLine) and (not use_ema_filter or is_downtrend)
// --- Execution ---
if (longCondition)
strategy.entry("Long", strategy.long, comment="Bullish Crossover")
if (shortCondition)
strategy.entry("Short", strategy.short, comment="Bearish Crossover")
// Exit Logic: Fixed TP/SL
strategy.exit("Exit Long", "Long", limit=strategy.position_avg_price * (1 + tp_percent/100), stop=strategy.position_avg_price * (1 - sl_percent/100))
strategy.exit("Exit Short", "Short", limit=strategy.position_avg_price * (1 - tp_percent/100), stop=strategy.position_avg_price * (1 + sl_percent/100))
// --- Visuals ---
plot(use_ema_filter ? ema_filter : na, color=color.new(color.gray, 50), title="Trend Filter EMA")
plotshape(longCondition, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="Long Signal")
plotshape(shortCondition, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="Short Signal")
// Color the background based on the trend
bgcolor(use_ema_filter and is_uptrend ? color.new(color.green, 90) : use_ema_filter and is_downtrend ? color.new(color.red, 90) : na)What the preserved example actually does
- 1
Entry rules: a MACD/signal crossover permits a long when the optional price filter passes; a crossunder permits a short. Opposite strategy.entry orders can reverse an existing position. Signal markers are conditions, not confirmations of actual fills.
- 2
Price filter: when enabled, long eligibility requires close above the filter EMA and short eligibility requires close below it. This can remove signals, but does not establish fewer losing trades or higher returns.
- 3
Sizing and costs: initial capital is 10,000, default order size is 100% of equity and commission is 0.1% of traded value per filled order. The 2.5% stop is a price distance, not a guarantee that only 2.5% of account equity can be lost. Slippage is not configured in the source.
- 4
Exit basis and timing: targets use strategy.position_avg_price, with default 5% profit and 2.5% stop distances. The script does not enable calc_on_order_fills or process_orders_on_close. From flat, average-price exits become available only after the entry fills and the strategy recalculates; do not assume the entry bar is protected by a bracket immediately.
- 5
Version and settings: this example remains Pine v5. Its title contains “Pro” and “Optimized”, but those labels are not performance evidence. Confirm Properties overrides, margin settings, session, chart type and fill assumptions before comparing runs.
Backtest Assumptions and Metrics
The earlier BTC/USD 2022–2024 performance table had no reproducible export or complete test setup, so its return, win-rate, drawdown and ratio claims have been withdrawn. Use the following record before reporting any result from this example.
| Metric | Value | Interpretation |
|---|---|---|
| Symbol and data | Record before testing | Exchange-qualified ticker, feed, standard candles, bar interval, session and date range. |
| Inputs and properties | Record before testing | MACD lengths, filter toggle/length, TP/SL, equity size, commission, slippage, margin and fill options. |
| Trade sequence | Export and inspect | Check signal time, next available entry fill, bracket creation, reversals and first-bar exposure. |
| Net return and drawdown | Not established here | Calculate on one stated equity basis, including costs and open-trade treatment; compare the same test window. |
| Profit factor and win rate | Not established here | Retain closed-trade count, gross profit/loss and cost convention; no loss denominator means profit factor is undefined. |
| Holdout and sensitivity | Run separately | Keep unseen dates untouched while selecting inputs, then inspect nearby parameters and different cost assumptions. |
A single favorable run cannot identify a durable edge. Filters and exits can trade fewer losses for missed winners. Keep failed variants and test settings, and separate in-sample selection from holdout evaluation. This page has not validated profitability or executed these trades.
Use Pineify to inspect rules and results
Pineify’s coding tools can help draft or revise Pine Script rules. Review the generated source and reproduce the resulting trades in TradingView before treating a change as correct.
The Backtest Deep Report is an analysis workflow for strategy results. It is separate from running a new parameter search in TradingView and cannot repair invalid fill assumptions or establish future profitability. Check the current product workflow and account access before relying on a report.
Strategy Optimization Suggestions
Test one change at a time
Compare the filter on and off using identical dates and costs. Retain trade counts and missed winners as well as drawdown.
Separate higher-timeframe confirmation
If adding higher-timeframe data, specify when that value becomes available and test repainting. The preserved example does not contain this filter.
Make sizing a separate rule
ATR-based sizing is not present here. If added, define stop distance, contract units, rounding and fees rather than assuming constant monetary risk.
Do not add MACD values directly to price stops
MACD is an average difference, not a price level. A proposed trailing exit needs a price-based rule and activation behavior, then a separate test.
Frequently Asked Questions
Review the rules before drawing conclusions
Use Pineify for code assistance and result analysis, then reconcile orders and metrics with the exact TradingView run. Neither a generated script nor a report guarantees live execution or returns.
Disclaimer: Trading involves significant risk. The strategies and code provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.
Related Strategy Tutorials
MACD and Alligator Strategy
Define an Alligator trend filter, MACD timing rule, exits, and controlled Pine Script tests.
Best MACD Settings for Day Trading
Compare documented and faster MACD settings with fixed day-trading rules, costs, and holdout data.
Best MACD Settings for a 5-Minute Chart
Test MACD reaction speed on five-minute charts without hiding session, turnover, or execution costs.
MACD Settings for Scalping
Compare faster MACD settings with explicit timeframe, session, fill, and cost assumptions.
MACD Calculator
Compute Moving Average Convergence Divergence values to identify trend direction and momentum.