Split Position Sizing: When a setup triggers, the algorithm automatically divides your total calculated quantity into two separate legs:
Target 1 (TP1): Exactly 50% of your position.
Target 2 (TP2): The remaining 50% of your position.
Dynamic 1:1 RR Scaling: TP1 is locked precisely at a 1:1 Risk-to-Reward ratio relative to your Stop Loss distance. Once hit, half the trade is closed and secured.
Breakeven Migration on TP1: The moment TP1 is achieved, the script instantly shifts the Stop Loss for the remaining 50% (TP2) to Breakeven + Lock-in Pips, turning the rest of the trade completely risk-free while it runs toward the full impulse high/low.
Code: Select all
//@version=5
strategy("Institutional OTE Scalper [Partial TP]", overlay=true, calc_on_every_tick=true, initial_capital=10000, default_qty_type=strategy.cash, commission_type=strategy.commission.cash_per_order, commission_value=3)
// --- 1. RISK & CAPITAL PROTECTION ---
G_RISK = "--- Risk & Capital Protection ---"
lot_type = input.string("Auto Risk %", options=["Auto Risk %", "Fixed Volume"], group=G_RISK)
risk_pct = input.float(1.0, "Risk Per Trade (%)", step=0.1, group=G_RISK)
fixed_qty = input.float(1.0, "Fixed Total Volume (Contracts/Lots)", group=G_RISK)
max_daily_loss = input.float(3.0, "Max Daily Drawdown (%)", step=0.5, group=G_RISK)
max_spread = input.float(2.0, "Max Live Spread (Pips)", group=G_RISK)
// --- 2. TRADE LOGIC & MULTI-TIMEFRAME ---
G_LOGIC = "--- Context & Volatility ---"
htf_res = input.timeframe("15", "Macro Trend Timeframe (HTF)", group=G_LOGIC)
ema_len = input.int(200, "HTF EMA Period", group=G_LOGIC)
lookback = input.int(40, "Swing Lookback", group=G_LOGIC)
mfi_len = input.int(3, "MFI Period", group=G_LOGIC)
atr_mult = input.float(1.0, "ATR Multiplier (Stop Buffer)", group=G_LOGIC)
// --- 3. TRADE MANAGEMENT & PARTIALS ---
G_MGMT = "--- Trade Management & Partials ---"
tp1_ratio = input.float(1.0, "TP1 Risk-to-Reward Ratio", group=G_MGMT, tooltip="Ratio at which to close 50% of the position")
use_be = input.bool(true, "Move SL to BE after TP1", group=G_MGMT)
be_lock = input.float(0.5, "BE Lock-In (Pips)", group=G_MGMT)
use_trail = input.bool(true, "Use Trailing Stop on Remainder", group=G_MGMT)
trail_step = input.float(2.0, "Trail Step (Pips)", group=G_MGMT)
// --- 4. SESSION & TIMING ---
G_SESS = "--- Session & Timing ---"
session_time = input.session("0800-1655", "Active Trading Session", group=G_SESS)
kill_eod = input.bool(true, "Close all trades at Session End", group=G_SESS)
draw_zones = input.bool(true, "Draw OTE Killzones on Chart", group=G_SESS)
// --- System & Risk Variables ---
pip_size = syminfo.type == "forex" ? syminfo.mintick * 10 : syminfo.mintick
in_session = not na(time(timeframe.period, session_time))
session_end = in_session[1] and not in_session
// Max Daily Drawdown Logic
var float day_open_equity = strategy.equity
if dayofweek != dayofweek[1]
day_open_equity := strategy.equity
daily_dd = ((day_open_equity - strategy.equity) / day_open_equity) * 100
hit_max_dd = daily_dd >= max_daily_loss
// --- Indicators ---
htf_ema = request.security(syminfo.tickerid, htf_res, ta.ema(close, ema_len))
mfi = ta.mfi(hlc3, mfi_len)
atr = ta.atr(14)
plot(htf_ema, "HTF 200 EMA", color=color.new(color.white, 30), linewidth=2)
// --- Price Action Swing Logic ---
high_idx = ta.highestbars(high, lookback)
low_idx = ta.lowestbars(low, lookback)
swing_high = ta.highest(high, lookback)
swing_low = ta.lowest(low, lookback)
swing_range = swing_high - swing_low
bars_ago_H = math.abs(high_idx)
bars_ago_L = math.abs(low_idx)
impulse_up = (bars_ago_L > bars_ago_H)
impulse_dn = (bars_ago_H > bars_ago_L)
fib618_L = swing_high - (swing_range * 0.618)
fib786_L = swing_high - (swing_range * 0.786)
fib618_S = swing_low + (swing_range * 0.618)
fib786_S = swing_low + (swing_range * 0.786)
// --- Entry Conditions ---
long_cond = (close > htf_ema) and impulse_up and (close <= fib618_L) and (close >= fib786_L) and (mfi[1] <= 20) and (mfi > 20) and in_session and not hit_max_dd and strategy.opentrades == 0
short_cond = (close < htf_ema) and impulse_dn and (close >= fib618_S) and (close <= fib786_S) and (mfi[1] >= 80) and (mfi < 80) and in_session and not hit_max_dd and strategy.opentrades == 0
// --- Trade State Variables ---
var float sl_level = na
var float tp2_level = na
var float tp1_level = na
var bool tp1_hit = false
if strategy.opentrades == 0
tp1_hit := false // Reset state when out of trades
// --- Execution & Visuals Engine ---
if long_cond
sl_level := swing_low - (atr * atr_mult)
tp2_level := swing_high
// Calculate 1:1 RR Target for TP1
sl_dist = math.abs(close - sl_level)
tp1_level := close + (sl_dist * tp1_ratio)
risk_cash = strategy.equity * (risk_pct / 100)
total_qty = lot_type == "Auto Risk %" ? (risk_cash / (sl_dist * syminfo.pointvalue)) : fixed_qty
half_qty = total_qty / 2.0
// Split into 2 distinct legs for partial scaling
strategy.entry("OTE Long 1", strategy.long, qty=half_qty)
strategy.exit("Exit L1", "OTE Long 1", stop=sl_level, limit=tp1_level)
strategy.entry("OTE Long 2", strategy.long, qty=half_qty)
strategy.exit("Exit L2", "OTE Long 2", stop=sl_level, limit=tp2_level)
if draw_zones
line.new(x1=time[bars_ago_L], y1=swing_low, x2=time[bars_ago_H], y2=swing_high, color=color.new(color.blue, 0), width=2, xloc=xloc.bar_time)
box.new(left=time[bars_ago_L], top=fib618_L, right=time + (time - time[1])*5, bottom=fib786_L, border_color=color.new(color.blue, 20), bgcolor=color.new(color.blue, 80), xloc=xloc.bar_time)
if short_cond
sl_level := swing_high + (atr * atr_mult)
tp2_level := swing_low
sl_dist = math.abs(sl_level - close)
tp1_level := close - (sl_dist * tp1_ratio)
risk_cash = strategy.equity * (risk_pct / 100)
total_qty = lot_type == "Auto Risk %" ? (risk_cash / (sl_dist * syminfo.pointvalue)) : fixed_qty
half_qty = total_qty / 2.0
strategy.entry("OTE Short 1", strategy.short, qty=half_qty)
strategy.exit("Exit S1", "OTE Short 1", stop=sl_level, limit=tp1_level)
strategy.entry("OTE Short 2", strategy.short, qty=half_qty)
strategy.exit("Exit S2", "OTE Short 2", stop=sl_level, limit=tp2_level)
if draw_zones
line.new(x1=time[bars_ago_H], y1=swing_high, x2=time[bars_ago_L], y2=swing_low, color=color.new(color.red, 0), width=2, xloc=xloc.bar_time)
box.new(left=time[bars_ago_H], top=fib618_S, right=time + (time - time[1])*5, bottom=fib786_S, border_color=color.new(color.red, 20), bgcolor=color.new(color.red, 80), xloc=xloc.bar_time)
// --- Dynamic Trailing & Breakeven for Leg 2 (Remainder) ---
// If Leg 1 has closed (meaning we have 1 trade ID remaining instead of 2), trigger BE on Leg 2
if strategy.opentrades == 1 and not tp1_hit
tp1_hit := true
if strategy.position_size > 0 and tp1_hit
if use_be
be_price = strategy.position_avg_price + (be_lock * pip_size)
if sl_level < be_price
sl_level := be_price
if use_trail
trail_price = close - (trail_step * pip_size)
if trail_price > sl_level
sl_level := trail_price
strategy.exit("Exit L2", "OTE Long 2", stop=sl_level, limit=tp2_level)
else if strategy.position_size < 0 and tp1_hit
if use_be
be_price = strategy.position_avg_price - (be_lock * pip_size)
if sl_level > be_price
sl_level := be_price
if use_trail
trail_price = close + (trail_step * pip_size)
if trail_price < sl_level
sl_level := trail_price
strategy.exit("Exit S2", "OTE Short 2", stop=sl_level, limit=tp2_level)
// --- Session Kill Switch ---
if kill_eod and session_end
strategy.close_all(comment="EOD Flat")
// --- Pro-Grade UI Dashboard ---
var table hud = table.new(position.top_left, 1, 5, bgcolor=color.new(color.black, 70), frame_color=color.gray, frame_width=1)
if barstate.islast
table.cell(hud, 0, 0, "■ ELITE OTE SCALPER (PARTIAL TP) ■", text_color=color.silver, text_size=size.small, text_halign=text.align_left)
live_spread = syminfo.ask and syminfo.bid ? (syminfo.ask - syminfo.bid) / pip_size : 0.0
table.cell(hud, 0, 1, "Spread: " + str.tostring(live_spread, "#.#") + " Pips", text_color=(live_spread <= max_spread ? color.lime : color.red), text_size=size.small, text_halign=text.align_left)
table.cell(hud, 0, 2, "Session: " + (in_session ? "ACTIVE" : "CLOSED"), text_color=(in_session ? color.lime : color.orange), text_size=size.small, text_halign=text.align_left)
table.cell(hud, 0, 3, "HTF Trend (" + htf_res + "): " + (close > htf_ema ? "BULLISH" : "BEARISH"), text_color=(close > htf_ema ? color.lime : color.red), text_size=size.small, text_halign=text.align_left)
table.cell(hud, 0, 4, "Daily DD: " + str.tostring(daily_dd, "#.##") + "% / " + str.tostring(max_daily_loss) + "%", text_color=(hit_max_dd ? color.red : color.gray), text_size=size.small, text_halign=text.align_left)