Instituto Quant · Ejemplo educativo

Ejemplo 1 — Setup Bajista XAUUSD 4h (Pine Script v5)

Copia el código y pégalo en el editor Pine de TradingView. Es un indicador de vigilancia, no una estrategia ejecutable automática.

⚠️ Aviso importante

  • Esto NO es asesoría financiera ni de inversión. Es material educativo para aprender a construir indicadores en Pine Script.
  • Operar instrumentos apalancados (oro, forex, futuros, cripto) puede causar la pérdida total o superior de tu capital. Solo arriesga lo que puedes permitirte perder.
  • Los niveles, parámetros y confluencias mostrados son un ejemplo puntual con fecha de vigencia. No los uses fuera de su contexto temporal sin recalibrar.
  • El rendimiento pasado no garantiza resultados futuros. Tú eres el único responsable de tus decisiones de trading.

Código Pine Script

//@version=5
indicator("Setup Bajista XAUUSD 4h - Profeta v5 SHOT", overlay=true, max_labels_count=20)

// ============================================================
// v5 - VIGILANTE DE UNA SOLA BALA (blindado)
// ============================================================
// Filosofía: vigila UN setup puntual identificado por Profeta.
// - Reloj de vigencia arranca solo en tiempo real
// - No re-dispara al recargar el gráfico
// - Guards contra divisiones por cero y volumen ausente
// - Reset manual disponible
// ============================================================

// ---------- NIVELES PROFETA ----------
grpSetup = "Setup (niveles Profeta)"
entryLow      = input.float(4700.0,   "Entrada baja",  group=grpSetup)
entryHigh     = input.float(4720.0,   "Entrada alta",  group=grpSetup)
slLevel       = input.float(4747.0,   "Stop Loss",     group=grpSetup)
tp1Level      = input.float(4666.68,  "TP1",           group=grpSetup)
tp2Level      = input.float(4314.89,  "TP2",           group=grpSetup)
tp3Level      = input.float(4200.0,   "TP3 runner",    group=grpSetup)
zoneTol       = input.float(3.0,      "Tolerancia zona (USD)", group=grpSetup)

grpFilters = "Confluencias"
useBB     = input.bool(true,  "1) Rechazo banda sup Bollinger",  group=grpFilters)
useRSI    = input.bool(true,  "2) RSI girando a la baja <60",    group=grpFilters)
useMACD   = input.bool(true,  "3) Histograma bajista MACD",      group=grpFilters)
useDiv    = input.bool(true,  "4) Divergencia bajista oculta",   group=grpFilters)
useCandle = input.bool(true,  "5) Vela 4h bajista cuerpo > avg", group=grpFilters)
minConfluences = input.int(3, "Mínimo confluencias para disparar", minval=1, maxval=5, group=grpFilters)

grpVigencia = "Vigencia"
maxBarsToFire = input.int(40, "Velas máx esperando disparo (40 = ~7 días en 4h)", minval=5, group=grpVigencia)
resetSetup    = input.bool(false, "🔄 Resetear setup (toggle para re-armar)", group=grpVigencia)

// ---------- INDICADORES TÉCNICOS ----------
bbBasis = ta.sma(close, 20)
bbDev   = 2.0 * ta.stdev(close, 20)
bbUpper = bbBasis + bbDev
bbLower = bbBasis - bbDev

rsiVal  = ta.rsi(close, 14)
rsiPrev = rsiVal[1]

[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)

// Guard contra volumen ausente (algunos brokers no reportan volume en XAUUSD)
hasVolume = ta.cum(volume) > 0
volAvg    = ta.sma(volume, 20)
volRel    = (hasVolume and volAvg > 0) ? volume / volAvg : 1.0

// Cuerpo promedio de las últimas 14 velas (más estable que bodyRange[1])
avgBody = ta.sma(math.abs(close - open), 14)

// ---------- CONFLUENCIAS (con guards) ----------
upperWick   = high - math.max(open, close)
bodyNow     = math.abs(close - open)

// 1) Rechazo Bollinger superior con mecha clara
bbRejection = high >= bbUpper and close < bbUpper and upperWick > bodyNow * 1.2

// 2) RSI girando bajista desde zona alta-neutral
rsiTurningDown = rsiPrev > rsiVal and rsiVal < 60 and rsiVal > 30

// 3) MACD bajista (histograma decreciendo)
macdBearish = macdLine < signalLine and histLine < nz(histLine[1], histLine)

// 4) Divergencia bajista oculta (reescrita más limpia)
// Precio hace HH en últimas 10 velas pero RSI no acompaña
highestHigh10  = ta.highest(high, 10)
highestHighIdx = ta.barssince(high == highestHigh10)
rsiAtPrevHigh  = ta.valuewhen(high == ta.highest(high, 20) and bar_index < bar_index - 5, rsiVal, 0)
hiddenBearDiv  = high >= highestHigh10 * 0.998 and not na(rsiAtPrevHigh) and rsiVal < rsiAtPrevHigh

// 5) Vela bajista con cuerpo significativo (vs promedio, no vs vela previa)
bearCandle = close < open and bodyNow > avgBody * 0.7

// Zona de entrada
inEntryZone = high >= (entryLow - zoneTol) and low <= (entryHigh + zoneTol)

confluences = (useBB and bbRejection ? 1 : 0) + (useRSI and rsiTurningDown ? 1 : 0) + (useMACD and macdBearish ? 1 : 0) + (useDiv and hiddenBearDiv ? 1 : 0) + (useCandle and bearCandle ? 1 : 0)

totalActive = (useBB?1:0)+(useRSI?1:0)+(useMACD?1:0)+(useDiv?1:0)+(useCandle?1:0)

// Trigger crudo (sin filtrar por tiempo real todavía)
triggerCondition = inEntryZone and confluences >= minConfluences and barstate.isconfirmed

// ---------- MÁQUINA DE ESTADOS BLINDADA ----------
// Estados: 0 = esperando | 1 = posición abierta | 2 = cerrado
var int state = 0
var int barsWaiting = 0
var float entryPrice = na
var string exitReason = ""
var bool clockStarted = false
var int firedAtBar = na

// Reset manual via input
if resetSetup
    state := 0
    barsWaiting := 0
    entryPrice := na
    exitReason := ""
    clockStarted := false
    firedAtBar := na

// El reloj solo arranca cuando llegamos a tiempo real (última vela del gráfico)
// Esto evita que el indicador "viva" toda la historia del gráfico
if barstate.islast and not clockStarted and state == 0
    clockStarted := true
    barsWaiting := 0

// Guardar estado previo ANTES de mutar (para detectar transiciones limpiamente)
prevState = state

// Lógica de estado: esperando disparo
if state == 0 and clockStarted
    if barstate.isconfirmed
        barsWaiting := barsWaiting + 1
    if triggerCondition
        state := 1
        entryPrice := close
        firedAtBar := bar_index
    else if barsWaiting >= maxBarsToFire
        state := 2
        exitReason := "EXPIRADO sin activar"

// Lógica de estado: posición abierta (vigila SL/TP1)
// Nota: si una vela toca SL y TP1 a la vez (gap/mecha larga), SL tiene prioridad (conservador)
if state == 1
    if high >= slLevel
        state := 2
        exitReason := "SL alcanzado"
    else if low <= tp1Level
        state := 2
        exitReason := "TP1 alcanzado"

// Eventos de transición (solo true en la vela exacta donde ocurre el cambio)
justFired  = state == 1 and prevState == 0
justClosed = state == 2 and prevState != 2

// ---------- DIBUJO ----------
plot(entryLow,  "Entrada baja",  color=color.new(color.orange, 0),  linewidth=1)
plot(entryHigh, "Entrada alta",  color=color.new(color.orange, 30), linewidth=1)
plot(slLevel,   "SL",            color=color.new(color.red, 0),     linewidth=2)
plot(tp1Level,  "TP1",           color=color.new(color.green, 0),   linewidth=1)
plot(tp2Level,  "TP2",           color=color.new(color.green, 40),  linewidth=1)
plot(tp3Level,  "TP3 runner",    color=color.new(color.green, 70),  linewidth=1, style=plot.style_circles)

// Fondo rojo tenue mientras posición abierta
bgcolor(state == 1 ? color.new(color.red, 92) : na, title="Posición activa")

// Marcadores de eventos
plotshape(justFired,  title="DISPARO", style=shape.triangledown, location=location.abovebar, color=color.red,    size=size.large,  text="SHORT\nENTRY")
plotshape(justClosed and exitReason == "SL alcanzado",          title="SL",  style=shape.xcross,     location=location.abovebar, color=color.red,    size=size.normal, text="SL")
plotshape(justClosed and exitReason == "TP1 alcanzado",         title="TP1", style=shape.triangleup, location=location.belowbar, color=color.green,  size=size.normal, text="TP1")
plotshape(justClosed and exitReason == "EXPIRADO sin activar",  title="EXP", style=shape.circle,     location=location.abovebar, color=color.gray,   size=size.tiny,   text="exp")

// ---------- PANEL ----------
var table info = table.new(position.top_right, 2, 8, border_width=1)
if barstate.islast
    estadoEmoji = state == 0 ? "🎯" : state == 1 ? "🔫" : "✓"
    estadoText  = state == 0 ? (clockStarted ? "Esperando disparo" : "Iniciando...") : state == 1 ? "Posición ABIERTA" : "Cerrado: " + exitReason
    
    table.cell(info, 0, 0, "XAUUSD 4h - Profeta v5", text_color=color.white, bgcolor=color.new(color.red, 70))
    table.cell(info, 1, 0, "Una sola bala",          text_color=color.white, bgcolor=color.new(color.red, 70))
    
    table.cell(info, 0, 1, "Estado")
    table.cell(info, 1, 1, estadoEmoji + " " + estadoText)
    
    table.cell(info, 0, 2, "Confluencias")
    confText = state == 2 ? "—" : str.tostring(confluences) + " / " + str.tostring(totalActive) + " (min " + str.tostring(minConfluences) + ")"
    table.cell(info, 1, 2, confText)
    
    table.cell(info, 0, 3, "En zona entrada")
    table.cell(info, 1, 3, state == 2 ? "—" : (inEntryZone ? "SÍ" : "no"))
    
    table.cell(info, 0, 4, "RSI / MACD hist")
    rsiMacdText = state == 2 ? "—" : str.tostring(rsiVal, "#.##") + " / " + str.tostring(histLine, "#.##")
    table.cell(info, 1, 4, rsiMacdText)
    
    table.cell(info, 0, 5, "Velas esperando")
    velasText = state == 0 and clockStarted ? str.tostring(barsWaiting) + " / " + str.tostring(maxBarsToFire) : state == 1 ? "—  (en posición)" : "—"
    table.cell(info, 1, 5, velasText)
    
    table.cell(info, 0, 6, "Entry price")
    entryText = na(entryPrice) ? "—" : str.tostring(entryPrice, "#.##")
    table.cell(info, 1, 6, entryText)
    
    table.cell(info, 0, 7, "R:R TP1 / TP2 / TP3")
    table.cell(info, 1, 7, "1:1.17 / 1:10.68 / 1:14.86")

// ---------- ALERTAS ----------
alertcondition(justFired, title="🔫 DISPARO setup bajista XAUUSD",
     message='{"symbol":"XAUUSD","tf":"4h","action":"SHORT_NOW","entry_zone":"4700-4720","sl":4747,"tp1":4666.68,"tp2":4314.89,"tp3":4200,"rr_tp2":"1:10.68","source":"Profeta v5"}')

alertcondition(justClosed and exitReason == "SL alcanzado", title="❌ SL alcanzado XAUUSD",
     message='{"symbol":"XAUUSD","action":"SL_HIT","level":4747,"source":"Profeta v5"}')

alertcondition(justClosed and exitReason == "TP1 alcanzado", title="✅ TP1 alcanzado XAUUSD",
     message='{"symbol":"XAUUSD","action":"TP1_HIT","level":4666.68,"next_action":"move_SL_to_breakeven","source":"Profeta v5"}')

alertcondition(justClosed and exitReason == "EXPIRADO sin activar", title="⏱ Setup XAUUSD expirado sin activar",
     message='{"symbol":"XAUUSD","action":"SETUP_EXPIRED","reason":"no_trigger_in_40_bars","source":"Profeta v5"}')

Descargo de responsabilidad: Instituto Quant y su autor no son asesores financieros registrados. El contenido de esta página es estrictamente educativo e informativo. Ningún elemento aquí publicado constituye una recomendación de compra, venta o mantenimiento de instrumento financiero alguno.

Antes de operar consulta con un profesional autorizado en tu jurisdicción y estudia los riesgos del apalancamiento, slippage, comisiones e impuestos aplicables. El uso de este código es bajo tu propia responsabilidad.

© Instituto Quant · Material educativo