← กลับหน้ารายการ

Mean Reversion Mirror

Strategy ผู้เขียน: olegask68 Profit Factor: 1.417

ลิงก์ TradingView

เปิดใน TradingView

Equity Chart

Equity chart

เปิดรูปเต็มขนาด

คำอธิบาย

📌 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.

รูป Preview

Preview

Pine Script Source

//@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)