MACD Strategy + 2 Profit Targets
This lesson demonstrates how I created a 2-target MACD trading strategy script for TradingView's Strategy Tester. I also share some of my thoughts on how I go about the process of vetting viable strategies to test.
Check out my other free lessons!Source Code
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © ZenAndTheArtOfTrading / www.PineScriptMastery.com // // System Rules: // Indicators: MACD indicator (default values), ATR (14), EMA (200) // 1. Price must be trading above/below the 200 EMA // 2. Only 1 bar can close above/below the 200 EMA over the past 5 bars // 3. We take the FIRST MACD cross and ignore subsequent signals until TP/SL is hit // 4. Stop Loss = 0.5 ATR above/below the recent swing high/low (7 bars lookback) // 5. First take profit = 1:1 (25%) // 6. Second take profit = 2:1 (100%) // 7. Move stop loss to break-even after 1st target is hit // // @version=5 strategy("[2022] MACD Cross Strategy", overlay=true, currency="USD", calc_on_order_fills=true, use_bar_magnifier=true, initial_capital=10000, default_qty_type=strategy.percent_of_equity, default_qty_value=100, // 100% of balance invested on each trade commission_type=strategy.commission.cash_per_contract) //commission_value=0.005) // Interactive Brokers rate // Import ZenLibrary import ZenAndTheArtOfTrading/ZenLibrary/5 as zen // Get user input var g_system = "System Entry Settings" i_ema_filter = input.int(title="EMA Filter Length", defval=200, group=g_system) i_ema_filter2 = input.int(title="EMA Max Bars Above/Below", defval=1, group=g_system) i_stop_multi = input.float(title="Stop Loss Multiplier", defval=0.5, step=0.5, group=g_system) i_stop_lookback = input.int(title="Stop Loss Lookback", defval=7, group=g_system) var g_risk = "System Risk Settings" i_rr1 = input.float(title="Risk:Reward Target 1", defval=1.0, group=g_risk) i_rr2 = input.float(title="Risk:Reward Target 2", defval=2.0, group=g_risk) i_target1 = input.float(title="Profit % Target 1", defval=25, group=g_risk) i_riskPerTrade = input.float(title="Forex Risk Per Trade %", defval=1.0) var g_macd = "MACD Settings" i_price_src = input.source(title="Price Source", defval=close, group=g_macd) i_fast_length = input.int(title="Fast Length", defval=12, group=g_macd) i_slow_length = input.int(title="Slow Length", defval=26, group=g_macd) i_signal_length = input.int(title="Signal Smoothing", minval=1, maxval=50, defval=9, group=g_macd) i_sma_source = input.string(title="Oscillator MA Type", defval="EMA", options=["SMA", "EMA"], group=g_macd) i_sma_signal = input.string(title="Signal Line MA Type", defval="EMA", options=["SMA", "EMA"], group=g_macd) //------------- DETERMINE CURRENCY CONVERSION RATE -------------// // Check if our account currency is the same as the base or quote currency or neither (for risk $ conversion purposes) accountSameAsCounterCurrency = strategy.account_currency == syminfo.currency accountSameAsBaseCurrency = strategy.account_currency == syminfo.basecurrency accountNeitherCurrency = not accountSameAsCounterCurrency and not accountSameAsBaseCurrency // Get currency conversion rates if applicable conversionCurrencyPair = accountSameAsCounterCurrency ? syminfo.tickerid : strategy.account_currency + syminfo.currency conversionCurrencyRate = accountSameAsBaseCurrency or accountNeitherCurrency ? request.security(conversionCurrencyPair, "D", close, ignore_invalid_symbol=true) : 1.0 // Display the current conversion currency ticker (for debug purposes) if barstate.islastconfirmedhistory table t = table.new(position.top_right, 1, 2, color.black) table.cell(t, 0, 0, "Conversion: " + conversionCurrencyPair + " (" + str.tostring(conversionCurrencyRate) + ")", text_color=color.white, text_size=size.small) table.cell(t, 0, 1, "Account: $" + str.tostring(zen.truncate(strategy.equity)), text_color=color.white, text_size=size.small) //------------- END CURRENCY CONVERSION RATE CODE -------------// // Calculate MACD [macdLine, signalLine, histLine] = ta.macd(i_price_src, i_fast_length, i_slow_length, i_signal_length) // Get indicator values ema = ta.ema(close, i_ema_filter) atr = ta.atr(14) // Check for zero-point crosses crossUp = ta.crossover(signalLine, macdLine) crossDown = ta.crossunder(signalLine, macdLine) // Check general system filters tradeFilters = not na(ema) and not na(atr) // Check trend conditions upTrend = close > ema downTrend = close < ema // Check trade conditions longConditions = tradeFilters and macdLine[1] < 0 and signalLine[1] < 0 shortConditions = tradeFilters and macdLine[1] > 0 and signalLine[1] > 0 // Confirm long & short setups longSignal = longConditions and upTrend and crossDown shortSignal = shortConditions and downTrend and crossUp // Calculate stop loss longStop = ta.lowest(low, i_stop_lookback) - (atr * i_stop_multi) shortStop = ta.highest(high, i_stop_lookback) + (atr * i_stop_multi) // Save stops & targets var float tradeStop = na var float tradeTarget1 = na var float tradeTarget2 = na var float tradeSize = na // Count bars above/below MA int barsAboveMA = 0 int barsBelowMA = 0 for i = 1 to 5 if close[i] < ema[i] barsBelowMA += 1 if close[i] > ema[i] barsAboveMA += 1 // Combine signal filters longTrade = longSignal and barsBelowMA <= i_ema_filter2 and strategy.position_size == 0 shortTrade = shortSignal and barsAboveMA <= i_ema_filter2 and strategy.position_size == 0 // Handle long trade entry (enter position, reset stops & targets) if longTrade if syminfo.type == "forex" tradeStop := longStop stopDistance = close - tradeStop tradeTarget1 := close + (stopDistance * i_rr1) tradeTarget2 := close + (stopDistance * i_rr2) tradeSize := na positionSize = zen.av_getPositionSize(strategy.equity, i_riskPerTrade, zen.toWhole(stopDistance) * 10, conversionCurrencyRate) strategy.entry(id="Long", direction=strategy.long, qty=positionSize) else strategy.entry(id="Long", direction=strategy.long) tradeStop := na tradeTarget1 := na tradeTarget2 := na // Handle short trade entry (enter position, reset stops & targets) if shortTrade if syminfo.type == "forex" tradeStop := shortStop stopDistance = tradeStop - close tradeTarget1 := close - (stopDistance * i_rr1) tradeTarget2 := close - (stopDistance * i_rr2) tradeSize := na positionSize = zen.av_getPositionSize(strategy.equity, i_riskPerTrade, zen.toWhole(shortStop - close) * 10, conversionCurrencyRate) strategy.entry(id="Short", direction=strategy.short, qty=positionSize) else strategy.entry(id="Short", direction=strategy.short) tradeStop := na tradeTarget1 := na tradeTarget2 := na // Handle forex trade size tracking variable if syminfo.type == "forex" and strategy.position_size != 0 and na(tradeSize) tradeSize := strategy.position_size // Handle long stops & target calculation if strategy.position_size > 0 and na(tradeStop) and syminfo.type != "forex" tradeStop := longStop stopDistance = strategy.position_avg_price - tradeStop tradeTarget1 := strategy.position_avg_price + (stopDistance * i_rr1) tradeTarget2 := strategy.position_avg_price + (stopDistance * i_rr2) tradeSize := strategy.position_size // Handle short stops & target calculation if strategy.position_size < 0 and na(tradeStop) and syminfo.type != "forex" tradeStop := shortStop stopDistance = tradeStop - strategy.position_avg_price tradeTarget1 := strategy.position_avg_price - (stopDistance * i_rr1) tradeTarget2 := strategy.position_avg_price - (stopDistance * i_rr2) tradeSize := strategy.position_size // Handle trade exits strategy.exit(id="Long Exit #1", from_entry="Long", limit=tradeTarget1, stop=tradeStop, qty_percent=i_target1) strategy.exit(id="Long Exit #2", from_entry="Long", limit=tradeTarget2, stop=tradeStop, qty_percent=100) strategy.exit(id="Short Exit #1", from_entry="Short", limit=tradeTarget1, stop=tradeStop, qty_percent=i_target1) strategy.exit(id="Short Exit #2", from_entry="Short", limit=tradeTarget2, stop=tradeStop, qty_percent=100) // Handle both long & short trade break-even stops (do this AFTER first position has exited above ^) if strategy.position_size != tradeSize tradeStop := strategy.position_avg_price tradeTarget1 := na // Draw conditional data plot(ema, color=close > ema ? color.green : color.red, linewidth=2, title="EMA") plotshape(longTrade, style=shape.triangleup, color=color.green, location=location.belowbar, title="Long Setup") plotshape(shortTrade, style=shape.triangledown, color=color.red, location=location.abovebar, title="Short Setup") // Draw stops & targets plot(strategy.position_size != 0 ? tradeStop : na, color=color.red, style=plot.style_linebr, title="Stop Loss") plot(strategy.position_size != 0 ? tradeTarget1 : na, color=color.green, style=plot.style_linebr, title="Profit Target 1") plot(strategy.position_size != 0 ? tradeTarget2 : na, color=color.green, style=plot.style_linebr, title="Profit Target 2")