📌 Mean Reversion Mirror — Multi‑Level Mean Reversion Strategy
Mean Reversion Mirror is an advanced modular mean‑reversion strategy built around dynamic and static deviation levels from a moving average.
The strategy automatically scales into positions across six levels (LONG and SHORT), using both dynamic MA‑based deviation levels and static levels that lock in after entry.
🔍 Core Concept
Price tends to revert toward its mean.
This strategy exploits that behavior by opening an initial base entry at the first deviation and adding positions as the imbalance grows.
Each additional level improves the average entry price and increases the probability of exiting profitably.
⚙️ Key Features
📈 Six Dynamic Deviation Levels
Levels are calculated as percentage deviations from a selected MA (WMA, SMA, EMA, RMA, HMA).
This makes the strategy adaptive to volatility and market structure.
📉 Six Static Levels
Once triggered, each level becomes fixed and no longer changes.
This creates a stable averaging grid independent of future MA movement.
🎯 Flexible Take‑Profit System
fixed take‑profit
or trailing take‑profit with dynamic offset
🖐 Manual Entry Support
You can manually set a price for LONG or SHORT, and the strategy will execute the base entry automatically.
📊 Rich Visualization
dynamic deviation levels
static levels
dotted “last chance” levels
average position price
take‑profit line
compact mini‑table with key position metrics
📦 Two Operating Modes
STK Mode — fixed unit size
Classic Mode — quantity calculated from USD value
🧠 Entry Logic
The strategy uses sequences B1–B6 (LONG) and S1–S6 (SHORT):
B1/S1 — base entry
B2–B6 / S2–S6 — averaging entries as deviation increases
L6/S6 — “last chance” level with its own size
Each level activates only after the previous one.
📌 Who This Strategy Is For
mean‑reversion traders
grid/averaging system users
pullback‑based investors
traders who want clear visual levels
users who prefer manual control over entry points
⚠️ Disclaimer
This strategy is not financial advice.
Always backtest and adjust parameters according to your risk tolerance before using it in live markets.
![]()
//@version=6
strategy("Mean Reversion Mirror", overlay = true, initial_capital = 5000)
// === Lot Precision ===
lot_precision = input.int(title = "Lot Precision", defval = 3, minval = 0, maxval = 8)
show_base_labels = input.bool(title = "Show Base Order Label", defval = true)
show_small_table = input.bool(title = "Show Mini Table", defval = true)
// === Direction Toggles ===
enable_long = input.bool(title = "Enable LONG", defval = true)
enable_short = input.bool(title = "Enable SHORT", defval = true)
roundQty(q) =>
factor = math.pow(10.0, lot_precision)
raw = math.ceil(q * factor) / factor
math.max(1.0 / factor, raw)
// === Modes ===
STK = input.bool(title = "Stock Mode (units instead of quantity)", defval = false)
PLotD = input.bool(title = "Show Dynamic Levels", defval = false)
// === Manual Levels (manual LONG / SHORT) ===
use_manual_long = input.bool(false, "Enable Manual LONG Entries")
use_manual_short = input.bool(false, "Enable Manual SHORT Entries")
manual_long_price_in = input.float(0.0, "Manual LONG Price", step = 0.1)
manual_short_price_in = input.float(0.0, "Manual SHORT Price", step = 0.1)
reset_manual_levels = input.bool(false, "Reset Manual Levels")
var float manual_long_price = na
var float manual_short_price = na
var line long_line = na
var line short_line = na
var bool reset_done = false
if reset_manual_levels and not reset_done
if not na(long_line)
line.delete(long_line)
if not na(short_line)
line.delete(short_line)
manual_long_price := na
manual_short_price := na
long_line := na
short_line := na
reset_done := true
if not reset_manual_levels
reset_done := false
if use_manual_long and manual_long_price_in > 0 and not reset_manual_levels
if na(manual_long_price) or manual_long_price != manual_long_price_in
if not na(long_line)
line.delete(long_line)
manual_long_price := manual_long_price_in
x1 = bar_index > 5000 ? bar_index - 5000 : 0
long_line := line.new(x1, manual_long_price, bar_index, manual_long_price, extend = extend.right, color = color.new(color.green, 0))
else
if not na(long_line)
line.delete(long_line)
long_line := na
manual_long_price := na
if use_manual_short and manual_short_price_in > 0 and not reset_manual_levels
if na(manual_short_price) or manual_short_price != manual_short_price_in
if not na(short_line)
line.delete(short_line)
manual_short_price := manual_short_price_in
x1s = bar_index > 5000 ? bar_index - 5000 : 0
short_line := line.new(x1s, manual_short_price, bar_index, manual_short_price, extend = extend.right, color = color.new(color.red, 0))
else
if not na(short_line)
line.delete(short_line)
short_line := na
manual_short_price := na
if not na(long_line)
line.set_x2(long_line, bar_index)
if not na(short_line)
line.set_x2(short_line, bar_index)
// === Deviations (common for LONG and SHORT) ===
deviation = input.float(title = "Deviation 1 (%)", defval = 1.3, minval = 0.01, maxval = 100, step = 0.1) / 100
deviation1 = input.float(title = "Deviation 2 (%)", defval = 7.5, minval = 0.01, maxval = 100, step = 0.1) / 100
deviation2 = input.float(title = "Deviation 3 (%)", defval = 13.3, minval = 0.01, maxval = 100, step = 0.1) / 100
deviation3 = input.float(title = "Deviation 4 (%)", defval = 21.1, minval = 0.01, maxval = 100, step = 0.1) / 100
deviation4 = input.float(title = "Deviation 5 (%)", defval = 33.7, minval = 0.01, maxval = 100, step = 0.1) / 100
deviation5 = input.float(title = "Deviation 6 (%) — Last Chance", defval = 54.0, minval = 0.01, maxval = 100, step = 0.1) / 100
// === Buy Levels ($) ===
unitsLevel1 = input.float(title = "Level 1 ($)", defval = 100)
unitsLevel2 = input.float(title = "Level 2 ($)", defval = 200)
unitsLevel3 = input.float(title = "Level 3 ($)", defval = 300)
unitsLevel4 = input.float(title = "Level 4 ($)", defval = 650)
unitsLevel5 = input.float(title = "Level 5 ($)", defval = 850)
unitsLevel6 = input.float(title = "Level 6 ($) — Last Chance", defval = 0.0, minval = 0.0)
// === MA ===
maType = input.string(title = "MA Type", defval = "WMA", options = ["WMA","SMA","RMA","EMA","HMA"])
maLength = input.int(title = "MA Period", defval = 20, minval = 2)
ma = switch maType
"EMA" => ta.ema(close, maLength)
"SMA" => ta.sma(close, maLength)
"RMA" => ta.rma(close, maLength)
"WMA" => ta.wma(close, maLength)
"HMA" => ta.hma(close, maLength)
=> na
// === Dynamic LONG Levels ===
L1 = ma * (1 - deviation)
L2 = ma * (1 - deviation1)
L3 = ma * (1 - deviation2)
L4 = ma * (1 - deviation3)
L5 = ma * (1 - deviation4)
L6 = ma * (1 - deviation5)
// === Dynamic SHORT Levels ===
S1 = ma * (1 + deviation)
S2 = ma * (1 + deviation1)
S3 = ma * (1 + deviation2)
S4 = ma * (1 + deviation3)
S5 = ma * (1 + deviation4)
S6 = ma * (1 + deviation5)
// === Static LONG Levels ===
s2 = 0.0
s2 := na(s2[1]) ? na : s2[1]
s3 = 0.0
s3 := na(s3[1]) ? na : s3[1]
s4 = 0.0
s4 := na(s4[1]) ? na : s4[1]
s5 = 0.0
s5 := na(s5[1]) ? na : s5[1]
s6 = 0.0
s6 := na(s6[1]) ? na : s6[1]
// === Static SHORT Levels ===
s2s = 0.0
s2s := na(s2s[1]) ? na : s2s[1]
s3s = 0.0
s3s := na(s3s[1]) ? na : s3s[1]
s4s = 0.0
s4s := na(s4s[1]) ? na : s4s[1]
s5s = 0.0
s5s := na(s5s[1]) ? na : s5s[1]
s6s = 0.0
s6s := na(s6s[1]) ? na : s6s[1]
// === Take-Profit ===
take_profit_long = input.float(title = "LONG Take-Profit (%)", defval = 2, step = 0.01, minval = 0.0) / 100
take_profit_short = input.float(title = "SHORT Take-Profit (%)", defval = 2, step = 0.01, minval = 0.0) / 100
take_profit_level_long = strategy.position_avg_price * (1 + take_profit_long)
take_profit_level_short = strategy.position_avg_price * (1 - take_profit_short)
// === Trailing ===
takeProfitTrailingEnabled = input.bool(title = "Enable Trailing", defval = true)
trailingTakeProfitDistancePerc = input.float(title = "Trailing Distance (%)", defval = 1.0, minval = 0.01, maxval = 100, step = 0.01) / 100
longTrailingTakeProfitStepTicks = (take_profit_level_long - strategy.position_avg_price) / syminfo.mintick
shortTrailingTakeProfitStepTicks = (strategy.position_avg_price - take_profit_level_short) / syminfo.mintick
// === Exits ===
if enable_long and strategy.position_size > 0
strategy.exit("TP-LONG",
limit = takeProfitTrailingEnabled ? na : take_profit_level_long,
trail_price = takeProfitTrailingEnabled ? take_profit_level_long : na,
trail_offset = takeProfitTrailingEnabled ? longTrailingTakeProfitStepTicks : na)
if enable_short and strategy.position_size < 0
strategy.exit("TP-SHORT",
limit = takeProfitTrailingEnabled ? na : take_profit_level_short,
trail_price = takeProfitTrailingEnabled ? take_profit_level_short : na,
trail_offset = takeProfitTrailingEnabled ? shortTrailingTakeProfitStepTicks : na)
// === TP and Average Price Visualization ===
plot(strategy.position_size > 0 ? take_profit_level_long : na, style = plot.style_linebr, linewidth = 2, color = color.new(color.green, 0), title = "TP LONG")
plot(strategy.position_size < 0 ? take_profit_level_short : na, style = plot.style_linebr, linewidth = 2, color = color.new(color.red, 0), title = "TP SHORT")
plot(strategy.position_avg_price, style = plot.style_linebr, linewidth = 2, color = color.black, title = "Avg Price")
// === Manual Entry Conditions as Base Order ===
long_level_entry = use_manual_long and enable_long and strategy.opentrades == 0 and strategy.position_size == 0 and not na(manual_long_price) and close <= manual_long_price
short_level_entry = use_manual_short and enable_short and strategy.opentrades == 0 and strategy.position_size == 0 and not na(manual_short_price) and close >= manual_short_price
// === LONG Entry Logic B1–B6 ===
// Manual LONG as base entry
if long_level_entry
qtym = STK ? unitsLevel1 : roundQty(unitsLevel1 / close)
strategy.order("MB1", strategy.long, qtym)
s2 := L2
if not na(long_line)
line.delete(long_line)
long_line := na
manual_long_price := na
// B1 LONG
if enable_long and strategy.opentrades == 0 and strategy.position_size == 0 and close < L1
qty1 = STK ? unitsLevel1 : roundQty(unitsLevel1 / close)
strategy.order("B1", strategy.long, qty1)
s2 := L2
if show_base_labels
label.new(bar_index, low, "B1: " + str.tostring(qty1),
style = label.style_label_up, color = color.green)
// B2 LONG
if enable_long and strategy.opentrades == 1 and strategy.position_size > 0 and close < s2
qty2 = STK ? unitsLevel2 : roundQty(unitsLevel2 / close)
strategy.order("B2", strategy.long, qty2)
s3 := L3
// B3 LONG
if enable_long and strategy.opentrades == 2 and strategy.position_size > 0 and close < s3
qty3 = STK ? unitsLevel3 : roundQty(unitsLevel3 / close)
strategy.order("B3", strategy.long, qty3)
s4 := L4
// B4 LONG
if enable_long and strategy.opentrades == 3 and strategy.position_size > 0 and close < s4
qty4 = STK ? unitsLevel4 : roundQty(unitsLevel4 / close)
strategy.order("B4", strategy.long, qty4)
s5 := L5
// B5 LONG
if enable_long and strategy.opentrades == 4 and strategy.position_size > 0 and close < s5
qty5 = STK ? unitsLevel5 : roundQty(unitsLevel5 / close)
strategy.order("B5", strategy.long, qty5)
s6 := L6
// B6 LONG — last chance
if enable_long and unitsLevel6 > 0 and strategy.opentrades == 5 and strategy.position_size > 0 and close < s6
qty6 = STK ? unitsLevel6 : roundQty(unitsLevel6 / close)
strategy.order("B6", strategy.long, qty6)
// === SHORT Entry Logic S1–S6 ===
// Manual SHORT as base entry
if short_level_entry
qtyms = STK ? unitsLevel1 : roundQty(unitsLevel1 / close)
strategy.order("MS1", strategy.short, qtyms)
s2s := S2
if not na(short_line)
line.delete(short_line)
short_line := na
manual_short_price := na
// S1 SHORT
if enable_short and strategy.opentrades == 0 and strategy.position_size == 0 and close > S1
qty1s = STK ? unitsLevel1 : roundQty(unitsLevel1 / close)
strategy.order("S1", strategy.short, qty1s)
s2s := S2
if show_base_labels
label.new(bar_index, high, "S1: " + str.tostring(qty1s),
style = label.style_label_down, color = color.red)
// S2 SHORT
if enable_short and strategy.opentrades == 1 and strategy.position_size < 0 and close > s2s
qty2s = STK ? unitsLevel2 : roundQty(unitsLevel2 / close)
strategy.order("S2", strategy.short, qty2s)
s3s := S3
// S3 SHORT
if enable_short and strategy.opentrades == 2 and strategy.position_size < 0 and close > s3s
qty3s = STK ? unitsLevel3 : roundQty(unitsLevel3 / close)
strategy.order("S3", strategy.short, qty3s)
s4s := S4
// S4 SHORT
if enable_short and strategy.opentrades == 3 and strategy.position_size < 0 and close > s4s
qty4s = STK ? unitsLevel4 : roundQty(unitsLevel4 / close)
strategy.order("S4", strategy.short, qty4s)
s5s := S5
// S5 SHORT
if enable_short and strategy.opentrades == 4 and strategy.position_size < 0 and close > s5s
qty5s = STK ? unitsLevel5 : roundQty(unitsLevel5 / close)
strategy.order("S5", strategy.short, qty5s)
s6s := S6
// S6 SHORT — last chance
if enable_short and unitsLevel6 > 0 and strategy.opentrades == 5 and strategy.position_size < 0 and close > s6s
qty6s = STK ? unitsLevel6 : roundQty(unitsLevel6 / close)
strategy.order("S6", strategy.short, qty6s)
// === Dynamic LONG Levels Graphics ===
l_ma = plot(ma, color = color.new(color.red, 0), linewidth = 3, title = "MA")
l_b1 = plot(PLotD ? L1 : na, color = color.new(color.red, 0), linewidth = 1, title = "L1")
l_b2 = plot(PLotD ? L2 : na, color = color.new(color.black, 0), linewidth = 1, title = "L2")
l_b3 = plot(PLotD ? L3 : na, color = color.new(color.black, 0), linewidth = 1, title = "L3")
l_b4 = plot(PLotD ? L4 : na, color = color.new(color.black, 0), linewidth = 1, title = "L4")
l_b5 = plot(PLotD ? L5 : na, color = color.new(color.black, 0), linewidth = 1, title = "L5")
fill(l_ma, l_b1, color = color.new(color.gray, 50))
fill(l_b1, l_b2, color = color.new(color.orange, 90))
fill(l_b2, l_b3, color = color.new(color.orange, 85))
fill(l_b3, l_b4, color = color.new(color.orange, 80))
fill(l_b4, l_b5, color = color.new(color.orange, 85))
// === Dynamic SHORT Levels Graphics ===
s_b1 = plot(PLotD ? S1 : na, color = color.new(color.maroon, 0), linewidth = 1, title = "S1")
s_b2 = plot(PLotD ? S2 : na, color = color.new(color.maroon, 0), linewidth = 1, title = "S2")
s_b3 = plot(PLotD ? S3 : na, color = color.new(color.maroon, 0), linewidth = 1, title = "S3")
s_b4 = plot(PLotD ? S4 : na, color = color.new(color.maroon, 0), linewidth = 1, title = "S4")
s_b5 = plot(PLotD ? S5 : na, color = color.new(color.maroon, 0), linewidth = 1, title = "S5")
fill(s_b1, s_b2, color = color.new(color.red, 90))
fill(s_b2, s_b3, color = color.new(color.red, 85))
fill(s_b3, s_b4, color = color.new(color.red, 80))
fill(s_b4, s_b5, color = color.new(color.red, 75))
// === L6 — dotted ===
plot_L6 = plot(PLotD ? L6 : na, color = color.new(color.blue, 0), linewidth = 2, title = "L6 — Last Chance")
var line l6_line = na
if barstate.islast and PLotD
line.delete(l6_line)
l6_line := line.new(bar_index - 1, L6, bar_index, L6, extend = extend.right)
line.set_style(l6_line, line.style_dotted)
line.set_color(l6_line, color.new(color.blue, 0))
line.set_width(l6_line, 2)
// === S6 — dotted ===
plot_S6 = plot(PLotD ? S6 : na, color = color.new(color.red, 0), linewidth = 2, title = "S6 — Last Chance")
var line s6_line = na
if barstate.islast and PLotD
line.delete(s6_line)
s6_line := line.new(bar_index - 1, S6, bar_index, S6, extend = extend.right)
line.set_style(s6_line, line.style_dotted)
line.set_color(s6_line, color.new(color.red, 0))
line.set_width(s6_line, 2)
// === Static LONG Levels ===
st2 = plot(strategy.opentrades > 0 and strategy.position_size > 0 ? s2 : na, title = "Static 2 LONG", style = plot.style_linebr)
st3 = plot(strategy.opentrades > 1 and strategy.position_size > 0 ? s3 : na, title = "Static 3 LONG", style = plot.style_linebr)
st4 = plot(strategy.opentrades > 2 and strategy.position_size > 0 ? s4 : na, title = "Static 4 LONG", style = plot.style_linebr)
st5 = plot(strategy.opentrades > 3 and strategy.position_size > 0 ? s5 : na, title = "Static 5 LONG", style = plot.style_linebr)
st6 = plot(strategy.opentrades > 4 and strategy.position_size > 0 ? s6 : na, title = "Static 6 LONG", style = plot.style_linebr)
bot_color = input.color(title = "Static LONG Levels Color", defval = color.blue)
fill(st2, st3, color.new(bot_color, 80))
fill(st3, st4, color.new(bot_color, 75))
fill(st4, st5, color.new(bot_color, 70))
fill(st5, st6, color.new(bot_color, 65))
// === Static SHORT Levels ===
st2s = plot(strategy.opentrades > 0 and strategy.position_size < 0 ? s2s : na, title = "Static 2 SHORT", style = plot.style_linebr, color = color.new(color.red, 0))
st3s = plot(strategy.opentrades > 1 and strategy.position_size < 0 ? s3s : na, title = "Static 3 SHORT", style = plot.style_linebr, color = color.new(color.red, 0))
st4s = plot(strategy.opentrades > 2 and strategy.position_size < 0 ? s4s : na, title = "Static 4 SHORT", style = plot.style_linebr, color = color.new(color.red, 0))
st5s = plot(strategy.opentrades > 3 and strategy.position_size < 0 ? s5s : na, title = "Static 5 SHORT", style = plot.style_linebr, color = color.new(color.red, 0))
st6s = plot(strategy.opentrades > 4 and strategy.position_size < 0 ? s6s : na, title = "Static 6 SHORT", style = plot.style_linebr, color = color.new(color.red, 0))
fill(st2s, st3s, color.new(color.red, 80))
fill(st3s, st4s, color.new(color.red, 75))
fill(st4s, st5s, color.new(color.red, 70))
fill(st5s, st6s, color.new(color.red, 65))
// === Mini Table ===
if show_small_table
sum_qty = strategy.position_size
sum_usd = sum_qty * close
open_trades = strategy.opentrades
open_pnl = strategy.openprofit
eq = strategy.equity
var table mini = table.new(position.top_right, 5, 1, bgcolor = na)
table.cell(mini, 0, 0, "Qty: " + str.tostring(sum_qty), text_color = color.white)
table.cell(mini, 1, 0, "USD: " + str.tostring(sum_usd, format.mintick), text_color = color.white)
table.cell(mini, 2, 0, "Trades: " + str.tostring(open_trades), text_color = color.white)
table.cell(mini, 3, 0, "PnL: " + str.tostring(open_pnl, format.mintick), text_color = open_pnl >= 0 ? color.green : color.red)
table.cell(mini, 4, 0, "Equity: " + str.tostring(eq, format.mintick), text_color = eq >= 0 ? color.green : color.red)