Strategy · Pine Script v6 · On the chart

S50 TFEX Strategy With ATR and Stoploss

by lumduan

This strategy trades S50 TFEX USDTHB contracts on the price chart.

View on GitHub

Repaints

GitHub stars1on the repository
Pine versionv6current
Lines of code218
Settings8inputs
LicenceNone statedfile: MPL-2.0
Last update1 year ago29 Apr 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

218 lines of Pine Script v6 from tfex_s50_atr_stoploss.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

tfex_s50_atr_stoploss.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © lumduan
//@version=6

// For S50 TFEX Strategy Timeframe 30 minutes 

// ————— Constants
float RISK_FREE_RATE = 2.0 // Risk-free rate for calculating Sharpe ratio
HIGH_PRICE_BAR_COLOR = color.green
LOW_PRICE_BAR_COLOR = color.red
int HIGH_PRICE_BAR_WIDTH = 1
int LOW_PRICE_BAR_WIDTH = 1

TABLE_HEADER_COLOR = color.new(color.black, 0)
TABLE_INDEX_COLOR = color.new(color.gray, 0)
TABLE_CELL_COLOR = color.new(color.black, 0)
TABLE_YEAR_CELL_POSITIVE_COLOR = color.new(color.green, 0)
TABLE_YEAR_CELL_NEGATIVE_COLOR = color.new(color.red, 0)

int CONTACT_PER_ORDER = 1
float COMMISSION_PRICE = 23.0 //THB

// Inputs for configuring the pivot point calculation
int leftBars  = input(21) // Number of bars to the left for pivot calculation
int rightBars = input(2) // Number of bars to the right for pivot calculation
int riskPercent = input(1, title="Risk % per Trade") / 100
int atrLength = input(10, title="ATR Length")
int riskRewardRatio = input(5, title="Risk-Reward Ratio")
int stopLossPoint = input(9, title="Stoploss Point")
int prec      = input(2, title = "Return Precision") // Precision of return percentages
bool show_table = input.bool(defval = true, title = "Show Return Rate Table On/Off") // Show or hide the table

strategy(title = "S50 TFEX Strategy With ATR and Stoploss",overlay = true, calc_on_every_tick = true, default_qty_type = strategy.fixed, default_qty_value = CONTACT_PER_ORDER, commission_type = strategy.commission.cash_per_contract, commission_value = COMMISSION_PRICE , risk_free_rate = RISK_FREE_RATE)

// ATR Calculation
atr = ta.atr(atrLength)

// Account Equity & Risk Per Trade
strategy_equity = strategy.equity
riskAmount = strategy_equity * riskPercent

// swing
swingHigh = ta.pivothigh(leftBars,rightBars)
swingLow = ta.pivotlow(leftBars,rightBars)


// Initialize high and low price variables
float highPrice = 0.0
highPrice := not na(swingHigh) ? swingHigh : highPrice[1] // Update high price if a new swing high is detected, else carry forward previous value

float lowPrice = 0.0
lowPrice := not na(swingLow) ? swingLow : lowPrice[1] // Update low price if a new swing low is detected, else carry forward previous value

// Stop-Loss and Take-Profit Calculation
longStop = swingLow - atr
shortStop = swingHigh + atr


longTakeProfit = swingHigh + atr * riskRewardRatio
shortTakeProfit = swingLow - atr * riskRewardRatio

// Position Sizing: Contracts based on risk
longContracts = riskAmount / (swingHigh - longStop)
shortContracts = riskAmount / (shortStop - swingLow)

float max_runup = 0.0
if max_runup < strategy.max_runup
    max_runup := strategy.max_runup

// Long Entry
if not na(swingHigh)
    strategy.entry(id = "Long", direction = strategy.long, qty=longContracts, stop=swingHigh + syminfo.mintick, comment = "Long")
    strategy.exit(id = "Long Exit",from_entry="Long", stop=strategy.position_avg_price - stopLossPoint - atr, limit=longTakeProfit, comment = "Short")

// Short Entry
if not na(swingLow)
    strategy.entry("Short", strategy.short, qty=shortContracts, stop=swingLow - syminfo.mintick, comment = "Short")
    strategy.exit("Short Exit", from_entry="Short", stop=strategy.position_avg_price + stopLossPoint + atr, limit=shortTakeProfit, comment = "Long")

// Plot high and low prices on the chart
plot(highPrice, color = HIGH_PRICE_BAR_COLOR, linewidth = HIGH_PRICE_BAR_WIDTH)
plot(lowPrice, color = LOW_PRICE_BAR_COLOR,   linewidth = LOW_PRICE_BAR_WIDTH)

// Plot Take Profit
plot(highPrice+atr * riskRewardRatio, title="Long Take-Profit", color=color.rgb(0, 240, 164, 50), linewidth=1, style=plot.style_stepline)
plot(lowPrice-atr * riskRewardRatio, title="Short Take-Profit", color=color.rgb(0, 240, 164, 50), linewidth=1, style=plot.style_stepline)


// ======= TABLE ====================================================================================================

// Detect new month and new year
bool newMonth = month(time) != month(time[1]) // True if the current bar is in a new month
bool newYear  = year(time)  != year(time[1])  // True if the current bar is in a new year

// Retrieve current equity and calculate bar profit/loss %
float equity = strategy.equity

// Calculate the profit or loss for the current bar as a percentage change from the previous bar's equity
float profitLossPercent = equity / equity[1] - 1 
float ProfitLoss = equity - equity[1]

// Initialize variables for current month and year P&L %
float currentMonthProfit = 0.0
float currentYearProfit  = 0.0

// Initialize variables for current month and year P&L %
float currentMonthPnL = 0.0
float currentYearPnL  = 0.0

// Calculate current month Profit/Loss
currentMonthProfit := newMonth ? 0.0 : currentMonthProfit[1] + ProfitLoss
// Calculate current year Profit/Loss
currentYearProfit := newYear ? 0.0 : currentYearProfit[1] + ProfitLoss


// Calculate current monthly P&L %
currentMonthPnL := newMonth ? 0.0 : (1 + currentMonthPnL[1]) * (1 + profitLossPercent) - 1
// Calculate current yearly P&L %
currentYearPnL := newYear ? 0.0 : (1 + currentYearPnL[1]) * (1 + profitLossPercent) - 1

// Arrays to store Monthly and Yearly P&Ls
var float[] monthlyProfit = array.new_float(0) // Array to store monthly P&L values
var int[] monthlyProfitCount = array.new_int(0) // Array to store timestamps for monthly P&L

var float[] yearlyProfit = array.new_float(0) // Array to store yearly P&L values
var int[] yearlyProfitCount = array.new_int(0) // Array to store timestamps for yearly P&L

// Arrays to store Monthly and Yearly P&Ls %
var float[] monthlyPnLs = array.new_float(0) // Array to store monthly P&L% values
var int[] monthlyPnLsCount = array.new_int(0) // Array to store timestamps for monthly P&L%

var float[] yearlyPnLs = array.new_float(0) // Array to store yearly P&L% values
var int[] yearlyPnLsCount = array.new_int(0) // Array to store timestamps for yearly P&L%

// Track whether P&L computation was performed
bool pnlComputed = false

// Store monthly P&L when a new month starts or on the last bar
if (not na(currentMonthProfit[1]) and (newMonth or barstate.islast))
    if (pnlComputed[1])
        array.pop(monthlyProfit) // Remove last value if already computed
        array.pop(monthlyProfitCount)
    
    array.push(monthlyProfit, currentMonthProfit[1]) // Add current month P&L
    array.push(monthlyProfitCount, time[1]) // Add corresponding timestamp

// Store monthly P&L% when a new month starts or on the last bar
if (not na(currentMonthPnL[1]) and (newMonth or barstate.islast))
    if (pnlComputed[1])
        array.pop(monthlyPnLs) // Remove last value if already computed
        array.pop(monthlyPnLsCount)
    
    array.push(monthlyPnLs, currentMonthPnL[1]) // Add current month P&L
    array.push(monthlyPnLsCount, time[1]) // Add corresponding timestamp

// Store yearly P&L when a new year starts or on the last bar
if (not na(currentYearProfit[1]) and (newYear or barstate.islast))
    if (pnlComputed[1])
        array.pop(yearlyProfit) // Remove last value if already computed
        array.pop(yearlyProfitCount)
    
    array.push(yearlyProfit, currentYearProfit[1]) // Add current year P&L
    array.push(yearlyProfitCount, time[1]) // Add corresponding timestamp

// Store yearly P&L when a new year starts or on the last bar
if (not na(currentYearPnL[1]) and (newYear or barstate.islast))
    if (pnlComputed[1])
        array.pop(yearlyPnLs) // Remove last value if already computed
        array.pop(yearlyPnLsCount)
    
    array.push(yearlyPnLs, currentYearPnL[1]) // Add current year P&L
    array.push(yearlyPnLsCount, time[1]) // Add corresponding timestamp


// Set to true on the last bar to indicate that P&L% computation has been performed
pnlComputed := barstate.islast ? true : (pnlComputed[1] ? pnlComputed[1] : false)

// Profit/Loss table
var table monthlyProfitTable = table(na)

if show_table

    // Create a table for profit/loss
    if (barstate.islast)
        monthlyProfitTable := table.new(position = position.top_right, columns = 14, rows = array.size(yearlyProfit) +1, border_width = 1)
        table.cell(monthlyProfitTable, 0,  0, "Y|M",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 1,  0, "Jan",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 2,  0, "Feb",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 3,  0, "Mar",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 4,  0, "Apr",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 5,  0, "May",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 6,  0, "Jun",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 7,  0, "Jul",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 8,  0, "Aug",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 9,  0, "Sep",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 10, 0, "Oct",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 11, 0, "Nov",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 12, 0, "Dec",  text_color = color.white, bgcolor = TABLE_HEADER_COLOR)
        table.cell(monthlyProfitTable, 13, 0, "Year", text_color = color.white, bgcolor = TABLE_HEADER_COLOR)

        // Create a row for each year
        for yearIndex = 0 to array.size(yearlyProfit) - 1
            // Create year index cell
            table.cell(monthlyProfitTable, 0, yearIndex + 1, str.tostring(year(array.get(yearlyProfitCount, yearIndex))), text_color = color.white, bgcolor = TABLE_CELL_COLOR)

            // Create Year  P&L cells
            string yearlyReturnValue = str.format("{0,number,integer}", math.round(number = array.get(yearlyProfit, yearIndex), precision = prec))
            string yearlyReturnPercent = str.tostring(math.round(number = array.get(yearlyPnLs,yearIndex)*100, precision = prec)) + " %"
            yearColor = array.get(yearlyProfit, yearIndex) > 0 ? TABLE_YEAR_CELL_POSITIVE_COLOR : TABLE_YEAR_CELL_NEGATIVE_COLOR
            table.cell(monthlyProfitTable, 13, yearIndex + 1, yearlyReturnValue + " \n" + yearlyReturnPercent, text_color = color.white, bgcolor = yearColor)
        
        for monthIndex = 0 to array.size(monthlyProfit) - 1
            string monthlyReturnValue = str.format("{0,number,integer}", math.round(number = array.get(monthlyProfit, monthIndex), precision = prec))
            string monthlyReturnPercent = str.tostring(math.round(number = array.get(monthlyPnLs, monthIndex)*100, precision = prec)) + "%"
            monthRow = year(array.get(monthlyProfitCount, monthIndex)) - year(array.get(yearlyProfitCount, 0)) + 1
            monthColumn = month(array.get(monthlyProfitCount, monthIndex))
            monthColor = array.get(monthlyProfit, monthIndex) > 0 ? TABLE_YEAR_CELL_POSITIVE_COLOR : TABLE_YEAR_CELL_NEGATIVE_COLOR
            table.cell(monthlyProfitTable, monthColumn, monthRow, monthlyReturnValue + "\n" + monthlyReturnPercent, text_color = color.white, bgcolor = monthColor)

Overview

S50 TFEX Strategy With ATR and Stoploss detects swing highs and swing lows with pivot points to generate long entries on swing highs and short entries on swing lows. The ATR indicator calculates dynamic stop-loss distances while the risk-reward ratio sets take-profit levels. Position sizes scale automatically with account equity and a user-set risk percentage per trade. Swing high and low prices are plotted on the chart. Key inputs adjust ATR length, risk-reward ratio, stop-loss points and table display. It repaints signals on the live bar.

Details

Published by

lumduan on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Volatility & bands, Oscillators & momentum, Trend, Support & resistance

Markets

Forex

Timeframes

M15-H1

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
leftBarsint21leftBars
rightBarsint2rightBars
Risk % per Tradeint1riskPercent
ATR Lengthint10atrLength
Risk-Reward Ratioint5riskRewardRatio
Stoploss Pointint9stopLossPoint
Return Precisionint2prec
Show Return Rate Table On/Offbooltrueshow_table
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

Box Scalper v4

by wynnforthework
2 stars

Box Scalper v4 is a strategy that plots buy and sell signals on the price chart using ADX trend filters, QQE momentum oscillator, and BOX channels. It signals entries based on crossovers and zone conditions, with trailing stop loss and take profit targets. Par

Strategy v5 TrendOscillators & momentum
Updated 1 year ago · None stated GitHub repository of Box Scalper v4

Liquidity Sweep Scalper v2

by godlesscherry
2 stars

Liquidity Sweep Scalper v2 is a scalping strategy that detects liquidity sweeps using Supertrend trend filters, Bollinger Bands for volatility, Williams Fractals for pivots, and MACD momentum. It enters on rejection candle breaks after sweeps and exits via sto

Strategy v6 TrendOscillators & momentum
Updated 7 months ago · None stated GitHub repository of Liquidity Sweep Scalper v2
257 stars

This strategy plots VWMA-based Bollinger bands on the 1-minute ES futures price chart for volatility analysis and potential breakout identification. It calculates dynamic long and short stop loss and take profit levels using percentage inputs relative to entry

Strategy v6 Volatility & bandsSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of V1.6 ES - Futures 1M No Repaint

Nasdaq Prop Firm Strategy

by TradersPost
218 stars

This strategy uses multiple VWAP anchors and a custom Lull indicator to generate buy and sell signals on the Nasdaq price chart. It plots the VWAP trend line and three VWAP anchors with customizable colors and mitigation methods. The Lull indicator produces si

Strategy v6 TrendSupport & resistance Repaints
Updated 3 months ago · None stated GitHub repository of Nasdaq Prop Firm Strategy

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers