// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © johnfalcone Owned by Falcone Trading Solutions LLC //──────────────────────────────────────────────────────────── // PINE SCRIPT DISCLAIMER // The STI Pine Script provided here is for EDUCATIONAL USE ONLY. // It is included solely to demonstrate the logic and structure of // the trading system described by the author. // // This script is NOT guaranteed to be free of errors and is NOT a // complete trading strategy. Nothing in this code should be taken // as financial advice or a recommendation to place live trades. // // Use of this script in TradingView or any third-party automation // platform (including but not limited to TradersPost, brokers, // or API services) is entirely at YOUR OWN RISK. // // The author and publisher assume ZERO responsibility for any // losses, errors, or damages resulting from using, modifying, // or applying this script in any form. // // ALWAYS test thoroughly in a simulated/paper environment before // risking real capital. //──────────────────────────────────────────────────────────── //@version=6 indicator("STI Buy/Sell TP1/TP2/SL/BE", overlay=true) // === Inputs === a = input.int(3, title='Key Value (Sensitivity)') atrPeriod = input.int(10, title='ATR Period') useHeikinAshi = input.bool(false, title='Use Heikin Ashi') manualBuyTrigger = input.bool(false, title="Force Buy Signal for Testing") manualSellTrigger = input.bool(false, title="Force Sell Signal for Testing") enableTP = input.bool(true, "Enable TradersPost Alerts?") enableDiscord = input.bool(true, "Enable Discord Alerts?") tradeSide = input.string("Both", "Trade Side", options = ["Both", "Calls Only", "Puts Only"]) // ========================= // === CONTRACT SETTINGS === // ========================= contractsPerEntry = input.int(3, "Contracts per Entry", minval = 1) tp1Qty = input.int(2, "TP1 Exit Qty", minval = 1, maxval = 100) tp2Qty = input.int(1, "TP2 Exit Qty", minval = 1, maxval = 100) // ============================= // === LONG ATR MULTIPLIERS === // ============================= longTP1Mult = input.float(2.0, "Long TP1 ATR Mult", step = 0.1) longTP2Mult = input.float(3.5, "Long TP2 ATR Mult", step = 0.1) longSLMult = input.float(1.5, "Long SL ATR Mult", step = 0.1) // ============================= // === SHORT ATR MULTIPLIERS === // ============================= shortTP1Mult = input.float(1.5, "Short TP1 ATR Mult", step = 0.1) shortTP2Mult = input.float(2.5, "Short TP2 ATR Mult", step = 0.1) shortSLMult = input.float(1.0, "Short SL ATR Mult", step = 0.1) // === Source and ATR === src = useHeikinAshi ? request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, close, lookahead=barmerge.lookahead_off) : close xATR = ta.atr(atrPeriod) nLoss = a * xATR // === Trailing Stop Calculation === var float xATRTrailingStop = 0.0 iff_1 = src > nz(xATRTrailingStop[1], 0) ? src - nLoss : src + nLoss iff_2 = src < nz(xATRTrailingStop[1], 0) and src[1] < nz(xATRTrailingStop[1], 0) ? math.min(nz(xATRTrailingStop[1]), src + nLoss) : iff_1 xATRTrailingStop := src > nz(xATRTrailingStop[1], 0) and src[1] > nz(xATRTrailingStop[1], 0) ? math.max(nz(xATRTrailingStop[1]), src - nLoss) : iff_2 // === EMA & Signals === ema = ta.ema(src, 1) above = ta.crossover(ema, xATRTrailingStop) below = ta.crossover(xATRTrailingStop, ema) // Trigger on the most recent bar for testing isLastBar = bar_index == ta.highest(bar_index, 1) buySignal = (src > xATRTrailingStop and above) or (manualBuyTrigger and isLastBar) sellSignal = (src < xATRTrailingStop and below) or (manualSellTrigger and isLastBar) // === Position Tracking === var bool inLong = false var bool inShort = false var int entryBar = na var float entry = na var float tp1 = na var float tp2 = na var float sl = na var bool tp1Hit = false var bool breakevenSet = false // === Prevent same-bar reversals and dual triggers === buySignal := buySignal and bar_index > nz(entryBar, -1) sellSignal := sellSignal and bar_index > nz(entryBar, -1) // If both signals somehow trigger on same candle, ignore both if buySignal and sellSignal buySignal := false sellSignal := false // === Counters for daily P&L === var int dailyWins = 0 var int dailyLosses = 0 var float total_pnl = 0.0 var int currentDay = na // Reset daily counters at the start of a new day if na(currentDay) or currentDay != dayofmonth(time) currentDay := dayofmonth(time) dailyWins := 0 dailyLosses := 0 total_pnl := 0.0 // === Buy Logic === if buySignal and not inLong inShort := false inLong := true entry := close tp1 := entry + (xATR * longTP1Mult) tp2 := entry + (xATR * longTP2Mult) sl := entry - (xATR * longSLMult) entryBar := bar_index tp1Hit := false breakevenSet := false // === Sell Logic === if sellSignal and not inShort inLong := false inShort := true entry := close tp1 := entry - (xATR * shortTP1Mult) tp2 := entry - (xATR * shortTP2Mult) sl := entry + (xATR * shortSLMult) entryBar := bar_index tp1Hit := false breakevenSet := false // === TP and SL Checks === longTP1Hit = inLong and not tp1Hit and high >= tp1 longTP2Hit = inLong and tp1Hit and high >= tp2 longStopHit = inLong and low <= sl shortTP1Hit = inShort and not tp1Hit and low <= tp1 shortTP2Hit = inShort and tp1Hit and low <= tp2 shortStopHit = inShort and high >= sl // === Breakeven Checks === longBEHit = inLong and tp1Hit and breakevenSet and low <= sl shortBEHit = inShort and tp1Hit and breakevenSet and high >= sl if longTP1Hit or shortTP1Hit tp1Hit := true // === Breakeven Logic === if inLong and tp1Hit and not breakevenSet sl := entry breakevenSet := true if inShort and tp1Hit and not breakevenSet sl := entry breakevenSet := true // === Recompute side-specific targets from current entry/xATR === long_tp1 = entry + 1.5 * xATR long_tp2 = entry + 3.0 * xATR long_sl = breakevenSet and inLong ? entry : entry - 2.0 * xATR short_tp1 = entry - 1.5 * xATR short_tp2 = entry - 3.0 * xATR short_sl = breakevenSet and inShort ? entry : entry + 2.0 * xATR // === Hidden placeholders for Discord (after TP/SL & BE, before alerts) === longActive = inLong or buySignal shortActive = inShort or sellSignal // Long → {{plot_0}}..{{plot_2}} plot(longActive ? tp1 : na, "Discord Long TP1", display=display.none) // {{plot_0}} plot(longActive ? tp2 : na, "Discord Long TP2", display=display.none) // {{plot_1}} plot(longActive ? sl : na, "Discord Long SL", display=display.none) // {{plot_2}} // Short → {{plot_3}}..{{plot_5}} plot(shortActive ? tp1 : na, "Discord Short TP1", display=display.none) // {{plot_3}} plot(shortActive ? tp2 : na, "Discord Short TP2", display=display.none) // {{plot_4}} plot(shortActive ? sl : na, "Discord Short SL", display=display.none) // {{plot_5}} // === Exit Cleanup & Daily Counters === var bool showLongLevels = false var bool showShortLevels = false var float long_quantity_tp1 = 2.0 var float long_quantity_tp2 = 1.0 var float short_quantity_tp1 = 2.0 var float short_quantity_tp2 = 1.0 var float stop_quantity = 3.0 if inLong showLongLevels := true if sellSignal and bar_index > entryBar showLongLevels := false inLong := false entryBar := na float trade_pnl = close - entry total_pnl := total_pnl + (trade_pnl * stop_quantity) if trade_pnl > 0 dailyWins += 1 else dailyLosses += 1 else if longTP1Hit total_pnl := total_pnl + (tp1 - entry) * long_quantity_tp1 else if longTP2Hit total_pnl := total_pnl + (tp2 - entry) * long_quantity_tp2 else if longStopHit or longBEHit total_pnl := total_pnl + (sl - entry) * stop_quantity if inShort showShortLevels := true if buySignal and bar_index > entryBar showShortLevels := false inShort := false entryBar := na float trade_pnl = entry - close total_pnl := total_pnl + (trade_pnl * stop_quantity) if trade_pnl > 0 dailyWins += 1 else dailyLosses += 1 else if shortTP1Hit total_pnl := total_pnl + (entry - tp1) * short_quantity_tp1 else if shortTP2Hit total_pnl := total_pnl + (entry - tp2) * short_quantity_tp2 else if shortStopHit or shortBEHit total_pnl := total_pnl + (entry - sl) * stop_quantity // === Plot Buy/Sell Arrows === plotshape(buySignal, title="Buy Entry", location=location.belowbar, color=color.green, style=shape.triangleup, size=size.small) plotshape(sellSignal, title="Sell Entry", location=location.abovebar, color=color.red, style=shape.triangledown, size=size.small) // === Entry Lines === var line buyLine = na var line sellLine = na if buySignal line.delete(buyLine) buyLine := line.new(bar_index, low, bar_index, high, color=color.green, width=1) if sellSignal line.delete(sellLine) sellLine := line.new(bar_index, low, bar_index, high, color=color.red, width=1) // === TP/SL Plots === plot(showLongLevels ? tp1 : na, title="Long TP1", color=color.green, linewidth=2) plot(showLongLevels ? tp2 : na, title="Long TP2", color=color.green, linewidth=2) plot(showLongLevels ? sl : na, title="Long SL", color=color.red, linewidth=2) plot(showShortLevels ? tp1 : na, title="Short TP1", color=color.green, linewidth=2) plot(showShortLevels ? tp2 : na, title="Short TP2", color=color.green, linewidth=2) plot(showShortLevels ? sl : na, title="Short SL", color=color.red, linewidth=2) // === Hidden plots for Discord Alerts === // Long placeholders plot(showLongLevels ? tp1 : na, "Long TP1", display=display.none) // {{plot_0}} plot(showLongLevels ? tp2 : na, "Long TP2", display=display.none) // {{plot_1}} plot(showLongLevels ? sl : na, "Long SL", display=display.none) // {{plot_2}} // Short placeholders plot(showShortLevels ? tp1 : na, "Short TP1", display=display.none) // {{plot_3}} plot(showShortLevels ? tp2 : na, "Short TP2", display=display.none) // {{plot_4}} plot(showShortLevels ? sl : na, "Short SL", display=display.none) // {{plot_5}} // === TradersPost Alerts === alertcondition(buySignal, title="Buy Alert (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"buy","price":"{{close}}","quantity":3,"orderType":"market"}') alertcondition(sellSignal, title="Sell Alert (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"sell","price":"{{close}}","quantity":3,"orderType":"market"}') alertcondition(longTP1Hit, title="TP1 Long Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":2,"orderType":"market"}') alertcondition(longTP2Hit, title="TP2 Long Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":1,"orderType":"market"}') alertcondition(shortTP1Hit, title="TP1 Short Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":2,"orderType":"market"}') alertcondition(shortTP2Hit, title="TP2 Short Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":1,"orderType":"market"}') alertcondition(longStopHit, title="Stop Loss Long Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":"100","quantityType":"percent_of_position","orderType":"market"}') alertcondition(shortStopHit, title="Stop Loss Short Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":"100","quantityType":"percent_of_position","orderType":"market"}') alertcondition(longBEHit, title="Breakeven Long Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":"100","quantityType":"percent_of_position","orderType":"market"}') alertcondition(shortBEHit, title="Breakeven Short Hit (TP)", message='{"strategy_id":"STI_STRATEGY","ticker":"{{ticker}}","action":"exit","price":"{{close}}","quantity":"100","quantityType":"percent_of_position","orderType":"market"}') // === Discord Alerts === // === Entry Alerts === alertcondition(buySignal, title="Buy Alert (Discord)", message='{ "content": "📈 LONG {{ticker}} @ {{close}} | TP1: {{plot_0}} | TP2: {{plot_1}} | SL: {{plot_2}} | Interval: {{interval}}" }') alertcondition(sellSignal, title="Sell Alert (Discord)", message='{ "content": "📉 SHORT {{ticker}} @ {{close}} | TP1: {{plot_3}} | TP2: {{plot_4}} | SL: {{plot_5}} | Interval: {{interval}}" }') // === Long Side TP/SL/BE === alertcondition(longTP1Hit, title="TP1 Long Hit (Discord)", message='{ "content": "✅ Long TP1 Hit {{ticker}} @ {{plot_0}}" }') alertcondition(longTP2Hit, title="TP2 Long Hit (Discord)", message='{ "content": "✅ Long TP2 Hit {{ticker}} @ {{plot_1}}" }') alertcondition(longStopHit, title="Stop Loss Long (Discord)", message='{ "content": "❌ Long Stop Loss {{ticker}} @ {{plot_2}}" }') alertcondition(longBEHit, title="Long Breakeven (Discord)", message='{ "content": "⚖️ Long Breakeven {{ticker}} @ {{close}}" }') // === Short Side TP/SL/BE === alertcondition(shortTP1Hit, title="TP1 Short Hit (Discord)", message='{ "content": "✅ Short TP1 Hit {{ticker}} @ {{plot_3}}" }') alertcondition(shortTP2Hit, title="TP2 Short Hit (Discord)", message='{ "content": "✅ Short TP2 Hit {{ticker}} @ {{plot_4}}" }') alertcondition(shortStopHit, title="Stop Loss Short (Discord)", message='{ "content": "❌ Short Stop Loss {{ticker}} @ {{plot_5}}" }') alertcondition(shortBEHit, title="Short Breakeven (Discord)", message='{ "content": "⚖️ Short Breakeven {{ticker}} @ {{close}}" }') //────────────────────────── ANY ALERTS (TradersPost + Discord) ────────────────────────── // =============== 1) EXIT LOCKS — FIRE ONLY ONCE ================= var bool firedTP1_long = false var bool firedTP2_long = false var bool firedBE_long = false var bool firedSL_long = false var bool firedTP1_short = false var bool firedTP2_short = false var bool firedBE_short = false var bool firedSL_short = false // Reset locks on NEW entry if buySignal firedTP1_long := false firedTP2_long := false firedBE_long := false firedSL_long := false if sellSignal firedTP1_short := false firedTP2_short := false firedBE_short := false firedSL_short := false //==================== 2) DETERMINE EVENT ======================== var string event = "none" event := "none" // ENTRY EVENTS if buySignal and not inShort event := "buy" if sellSignal and not inLong event := "sell" // LONG EXIT EVENTS (lock protected) if inLong and event == "none" if longTP1Hit and not firedTP1_long event := "tp1_long" if longTP2Hit and not firedTP2_long event := "tp2_long" if longBEHit and not firedBE_long event := "be_long" if longStopHit and not firedSL_long event := "stop_long" // SHORT EXIT EVENTS (lock protected) if inShort and event == "none" if shortTP1Hit and not firedTP1_short event := "tp1_short" if shortTP2Hit and not firedTP2_short event := "tp2_short" if shortBEHit and not firedBE_short event := "be_short" if shortStopHit and not firedSL_short event := "stop_short" //=============== 3) TRADE SIDE FILTER ======================= if tradeSide == "Calls Only" and (event == "sell" or str.contains(event,"short")) event := "none" if tradeSide == "Puts Only" and (event == "buy" or str.contains(event,"long")) event := "none" //=============== 4) MAP EVENT → ACTION ====================== string act = (event == "buy" or event == "sell") ? event : "exit" //=============== 5) MAP EVENT → QTY ========================= float qty = contractsPerEntry qty := str.contains(event,"tp1") ? tp1Qty : qty qty := str.contains(event,"tp2") ? tp2Qty : qty qty := (str.contains(event,"stop_") or str.contains(event,"be_")) ? 100 : qty //=============== 6) QTY TYPE ================================ string qtyJSON = (str.contains(event,"stop_") or str.contains(event,"be_")) ? str.tostring(qty) + ",\"quantityType\":\"percent_of_position\"" : str.tostring(qty) //=============== 7) PRICE TO SEND =========================== float px = close //=============== 8) TRADERSPOST JSON (ONE LINE) ============= string tpJSON = "{\"strategy_id\":\"STI_STRATEGY\",\"ticker\":\"" + syminfo.ticker + "\",\"action\":\"" + act + "\",\"price\":\"" + str.tostring(px,"#.########") + "\",\"quantity\":" + qtyJSON + ",\"orderType\":\"market\"}" //=============== 9) DISCORD JSON (ONE LINE) ================= // Emoji selection string emoji = event == "buy" ? "📈 BUY " : event == "sell" ? "📉 SELL " : event == "tp1_long" ? "💰 TP1 LONG " : event == "tp1_short" ? "💰 TP1 SHORT " : event == "tp2_long" ? "💎 TP2 LONG " : event == "tp2_short" ? "💎 TP2 SHORT " : event == "stop_long" ? "🛑 STOP LONG " : event == "stop_short" ? "🛑 STOP SHORT " : event == "be_long" ? "⚖️ BE LONG " : event == "be_short" ? "⚖️ BE SHORT " : "" // FULL Discord message with TF and TP/SL values string dcJSON = "{\"content\":\"" + emoji + syminfo.ticker + " @ " + str.tostring(px,"#.########") + " | TF: " + timeframe.period + " | TP1: " + str.tostring(tp1,"#.########") + " | TP2: " + str.tostring(tp2,"#.########") + " | SL: " + str.tostring(sl,"#.########") + "\"}" //=================== 10) FIRE ALERT (NO ERRORS) ========================== // ENTRY → once per bar CLOSE if (event == "buy" or event == "sell") if enableTP alert(tpJSON, alert.freq_once_per_bar_close) if enableDiscord alert(dcJSON, alert.freq_once_per_bar_close) // EXITS → once per bar (reactive) if (event != "none") and not (event == "buy" or event == "sell") if enableTP alert(tpJSON, alert.freq_once_per_bar) if enableDiscord alert(dcJSON, alert.freq_once_per_bar) //=================== 11) SET LOCKS AFTER FIRING ============== if event == "tp1_long" firedTP1_long := true if event == "tp2_long" firedTP2_long := true if event == "stop_long" firedSL_long := true if event == "be_long" firedBE_long := true if event == "tp1_short" firedTP1_short := true if event == "tp2_short" firedTP2_short := true if event == "stop_short" firedSL_short := true if event == "be_short" firedBE_short := true