Library · Pine Script v5 · Library

Strategy Helpers

by ivansoft· code credited to TradingView

A Pine Script v5 library of helper functions for strategy scripts.

View on GitHub

GitHub stars1on the repository
Pine versionv5runs as is
Lines of code378
Settings2inputs
LicenceNone statedfile: MPL-2.0
Last update1 year ago1 Mar 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 library, select all of the template text and paste. Press Save and give it a name.
  4. 4Publish or import itA library draws nothing. Publish it from the editor, then import its functions in your own scripts.

Source Code

378 lines of Pine Script v5 from examples/lib/strategy.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

strategy.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TradingView

//@version=5

// @description This library contains strategy helper functions that assist in various positional calculations such as PnL, percent, ticks, price, or currency.

library("Strategy", true)



// ———————————————————— Library functions {


// @function        Converts a percentage of the supplied price or the average entry price to ticks.
// @param percent   (series int/float) The percentage of supplied price to convert to ticks. 50 is 50% of the entry price.
// @param from      (series int/float) A price that can be used to calculate from. Optional. The default value is `strategy.position_avg_price`.
// @returns         (float) A value in ticks.
export percentToTicks(series float percent, series float from = strategy.position_avg_price) =>
    strategy.position_size != 0 ? percent / 100 * from / syminfo.mintick : float(na)


// @function        Converts a percentage of the supplied price or the average entry price to a price.
// @param percent   (series int/float) The percentage of the supplied price to convert to price. 50 is 50% of the supplied price.
// @param from      (series int/float) A price that can be used to calculate from. Optional. The default value is `strategy.position_avg_price`.
// @returns         (float) A value in the symbol's quote currency (USD for BTCUSD).
export percentToPrice(series float percent, series float from = strategy.position_avg_price) =>
    strategy.position_size != 0 ? (1 + percent / 100) * from : float(na)    


// @function        Converts the percentage of a price to money.
// @param price     (series int/float) The symbol's price.
// @param percent   (series int/float) The percentage of `price` to calculate.
// @returns         (float) A value in the symbol's currency.
export percentToCurrency(series float price, series float percent) =>
    price * percent / 100 * syminfo.pointvalue


// @function        Calculates the profit (as a percentage of the position's `strategy.position_avg_price` entry price) if the trade is closed at `exitPrice`.
// @param exitPrice (series int/float) The potential price to close the position. 
// @returns         (float) Percentage profit for the current position if closed at the `exitPrice`.
export percentProfit(series float exitPrice) =>
    if strategy.position_size != 0
        (exitPrice - strategy.position_avg_price) / strategy.position_avg_price * math.sign(strategy.position_size) * 100
    else
        float(na)


// @function        Converts a price to ticks.
// @param price     (series int/float) Price to convert to ticks.
// @returns         (float) A quantity of ticks.
export priceToTicks(series float price) =>
    price / syminfo.mintick


// @function        Converts ticks to a price offset from the supplied price or the average entry price.
// @param ticks     (series int/float) Ticks to convert to a price.
// @param from      (series int/float) A price that can be used to calculate from. Optional. The default value is `strategy.position_avg_price`.
// @returns         (float) A price level that has a distance from the entry price equal to the specified number of ticks.
export ticksToPrice(series float ticks, series float from = strategy.position_avg_price) =>
    float offset = ticks * syminfo.mintick 
    strategy.position_size != 0 ? from + offset : float(na)
    

// @function        Converts ticks to money.
// @param ticks     (series int/float) Number of ticks.
// @returns         (float) Money amount in the symbol's currency.
export ticksToCurrency(series float ticks) =>
    ticks * syminfo.mintick * syminfo.pointvalue


// @function        Calculates a stop loss level using a distance in ticks from the current `strategy.position_avg_price` entry price. This value can be plotted on the chart, or used as an argument to the `stop` parameter of a `strategy.exit()` call. NOTE: The stop level automatically flips based on whether the position is long or short.
// @param ticks     (series int/float) The distance in ticks from the entry price to the stop loss level.
// @returns         (float) A stop loss level for the current position.
export ticksToStopLevel(series float ticks) =>
    ticksToPrice(-ticks * math.sign(strategy.position_size)) 


// @function        Calculates a take profit level using a distance in ticks from the current `strategy.position_avg_price` entry price.  This value can be plotted on the chart, or used as an argument to the `limit` parameter of a `strategy.exit()` call. NOTE: The take profit level automatically flips based on whether the position is long or short.
// @param ticks     (series int/float) The distance in ticks from the entry price to the take profit level.
// @returns         (float) A take profit level for the current position.
export ticksToTpLevel(series float ticks) =>
    ticksToPrice(ticks * math.sign(strategy.position_size))


// @function            Calculates the position size needed to implement a given stop loss (in ticks) corresponding to `riskPercent` of equity.
// @param stopLossTicks (series int) The stop loss (in ticks) that will be used to protect the position.
// @param riskPercent   (series int/float) The maximum risk level as a percent of current equity (`strategy.equity`).
// @returns             (int) A quantity of contracts.
export calcPositionSizeByStopLossTicks(series int stopLossTicks, series float riskPercent) =>
    float risk = strategy.equity * riskPercent / 100
    math.floor(risk / ticksToCurrency(stopLossTicks))


// @function                Calculates the position size needed to implement a given stop loss (%) corresponding to `riskPercent` of equity.
// @param stopLossPercent   (series int/float) The stop loss in percent that will be used to protect the position.
// @param riskPercent       (series int/float) The maximum risk level as a percent of current equity (`strategy.equity`).
// @param entryPrice        (series int/float) The entry price of the position. 
// @returns                 (int) A quantity of contracts.
export calcPositionSizeByStopLossPercent(series float stopLossPercent, series float riskPercent, series float entryPrice = close) =>
    float risk = strategy.equity * riskPercent / 100
    math.floor(risk / percentToCurrency(entryPrice, stopLossPercent))



// @function             A wrapper of the `strategy.exit()` built-in which adds the possibility to specify loss & profit as a value in percent. NOTE: this function may work incorrectly with pyramiding turned on due to the use of `strategy.position_avg_price` in its calculations of stop loss and take profit offsets.
// @param id             (series string)    The order identifier of the `strategy.exit()` call. 
// @param lossPercent    (series int/float) Stop loss as a percent of the entry price. 
// @param profitPercent  (series int/float) Take profit as a percent of the entry price. 
// @param qty            (series int/float) Number of contracts/shares/lots/units to exit a trade with. Optional. The default value is `na`. 
// @param qtyPercent     (series int/float) The percent of the position's size to exit a trade with.  If `qty` is `na`, the default value of `qty_percent` is 100.
// @param comment        (series string)    Optional. Additional notes on the order.
// @param when           (series bool)      Condition of the order. The order is placed if it is true. 
// @param alertMessage   (series string)    An optional parameter which replaces the {{strategy.order.alert_message}} placeholder when it is used in the "Create Alert" dialog box's "Message" field.
// @returns              (void) 
export exitPercent(series string id               = "Exit", 
                   series float  lossPercent              , 
                   series float  profitPercent            , 
                   series float  qty              = na    , 
                   series float  qtyPercent       = na    , 
                   series string comment          = na    , 
                   series bool   when             = true  , 
                   series string alertMessage     = na    ) =>
    strategy.exit(id, qty = qty, qty_percent = qtyPercent, loss = math.ceil(percentToTicks(lossPercent)), profit = math.round(percentToTicks(profitPercent)), comment = comment, when = when, alert_message = alertMessage)


// @function             A wrapper of the `strategy.exit()` built-in which adds the possibility to specify loss & profit as a value in ticks. 
// @param id             (series string)    The order identifier of the `strategy.exit()` call. 
// @param lossTicks      (series int/float) Loss in ticks.  
// @param profitTicks    (series int/float) Profit in ticks. 
// @param qty            (series int/float) Number of contracts/shares/lots/units to exit a trade with. Optional. The default value is `na`. 
// @param qtyPercent     (series int/float) The percent of the position's size to exit a trade with.  If `qty` is `na`, the default value of `qty_percent` is 100.
// @param comment        (series string)    Optional. Additional notes on the order.
// @param when           (series bool)      Condition of the order. The order is placed if it is true. 
// @param alertMessage   (series string)    An optional parameter which replaces the {{strategy.order.alert_message}} placeholder when it is used in the "Create Alert" dialog box's "Message" field.
// @returns              (void) 
export exitTicks(series string id               = "Exit", 
                 series float  lossTicks                , 
                 series float  profitTicks              , 
                 series float  qty              = na    , 
                 series float  qtyPercent       = na    , 
                 series string comment          = na    , 
                 series bool   when             = true  , 
                 series string alertMessage     = na    ) =>
    strategy.exit(id, qty = qty, qty_percent = qtyPercent, loss = lossTicks * syminfo.mintick, profit = profitTicks * syminfo.mintick, comment = comment, when = when, alert_message = alertMessage)
    

// @function             A wrapper of the `strategy.close_all()` built-in which closes all positions on the close of the last bar of the trading session. 
// @param comment        (series string) Optional. Additional notes on the order.
// @param alertMessage   (series string) An optional parameter which replaces the {{strategy.order.alert_message}} placeholder when it is used in the "Create Alert" dialog box's "Message" field.
// @returns              (void) Closes all positions on the `close` of the last bar of the trading session, instead of the `open` of the next session. 
export closeAllAtEndOfSession(series string comment = na, series string alertMessage = na) =>
    if session.islastbar and barstate.isconfirmed
        strategy.close_all(comment = comment, alert_message = alertMessage, immediately = true)


// @function             A wrapper of the `strategy.close()` built-in which closes the position matching the `entryId` on the close of the last bar of the trading session. 
// @param entryId        (series string) A required parameter. The order identifier. It is possible to close an order by referencing its identifier.
// @param comment        (series string) Optional. Additional notes on the order.
// @param alertMessage   (series string) An optional parameter which replaces the {{strategy.order.alert_message}} placeholder when it is used in the "Create Alert" dialog box's "Message" field.
// @returns              (void) Closes `entryId` on the `close` of the last bar of the trading session, instead of the `open` of the next session. 
export closeAtEndOfSession(series string entryId, series string comment = na, series string alertMessage = na) =>
    if session.islastbar and barstate.isconfirmed
        strategy.close(entryId, comment, alert_message = alertMessage, immediately = true)


days(series int t) =>
    int result = math.floor(t / 86400000.0 + 2440587.5)


months(series int t) =>
    int result = year(t) * 12 + month(t)


numberOfPeriodsInYear(series bool useMonths) =>
    int result = useMonths ? 12 : 365

    
numberOfPeriods(series int prevT, series int curT, series bool useMonths) =>
    int result = useMonths ? (months(curT) - months(prevT)) : (days(curT) - days(prevT))


addPeriodsReturns(series int prevTime, series int xTime, series float profit, series bool useMonths, series float[] periodsReturn) =>
    int periods =  numberOfPeriods(prevTime, xTime, useMonths)
    if periods > 0
        for j = 0 to periods - 1
            array.push(periodsReturn, 0)
    if array.size(periodsReturn) == 0
        array.push(periodsReturn, 0)
    int lastIdx = array.size(periodsReturn) - 1
    float periodProfit = array.get(periodsReturn, lastIdx) + profit
    array.set(periodsReturn, lastIdx, periodProfit)
    int result = xTime

    
interestRatePerPeriod(series float interestRateYearlyPercent, series bool useMonths) =>
    float result = interestRateYearlyPercent / numberOfPeriodsInYear(useMonths) / 100


periodsReturnsPercent(series bool useMonths) =>
    float[] periodsReturn = array.new<float>()
    
    if strategy.closedtrades > 0
        int prevTime = strategy.closedtrades.entry_time(0)
        for i = 0 to strategy.closedtrades - 1
            prevTime := addPeriodsReturns(prevTime, strategy.closedtrades.exit_time(i), strategy.closedtrades.profit(i), useMonths, periodsReturn)
            
    if strategy.opentrades > 0
        for i = 0 to strategy.opentrades - 1
            int prevTime = strategy.opentrades.entry_time(0)
            addPeriodsReturns(prevTime, time, strategy.opentrades.profit(i), useMonths, periodsReturn)
    
    while array.size(periodsReturn) > 0
        if array.get(periodsReturn, array.size(periodsReturn) - 1) == 0
            array.pop(periodsReturn)
        else
            break
    
    float[] periodsReturnPercent = array.new<float>()
    float cum = strategy.initial_capital
    for pR in periodsReturn
        float pp = cum == 0 ? 0 : pR / cum
        cum += pR
        array.push(periodsReturnPercent, pp)
        
    float[] result = periodsReturnPercent


downsideDeviation(float[] returns, series float targetReturn) =>
    int count = 0
    float sum = 0
    for r in returns
        float rr = math.min(0, r - targetReturn)
        sum += rr * rr
        count += 1
    sum /= count
    float result = 0 < sum ? math.sqrt(sum) : na

    
sortinoRatioImpl(series bool useMonths, simple float interestRate) =>
    float[] periodsReturnPcnt = periodsReturnsPercent(useMonths)
    float minRateOfReturn = interestRatePerPeriod(interestRate, useMonths)
    float dd = downsideDeviation(periodsReturnPcnt, minRateOfReturn)
    if not na(dd)
        float avgPP = array.avg(periodsReturnPcnt)
        float result = not na(avgPP) ? (avgPP - minRateOfReturn) / dd : na
    else
        na
  

sharpeRatioImpl(series bool useMonths, simple float interestRate) =>
    float[] periodsReturnPcnt = periodsReturnsPercent(useMonths)
    float minRateOfReturn = interestRatePerPeriod(interestRate, useMonths)
    float stdev = array.stdev(periodsReturnPcnt)
    if not na(stdev)
        float avgPP = array.avg(periodsReturnPcnt)
        float result = not na(avgPP) ? (avgPP - minRateOfReturn) / stdev : na
    else
        na


detectPeriod() =>
    int start = strategy.closedtrades > 0 ? strategy.closedtrades.entry_time(0) : strategy.opentrades > 0 ? strategy.opentrades.entry_time(0) : int(na)
    int end = strategy.opentrades > 0 ? time_close : strategy.closedtrades > 0 ? strategy.closedtrades.exit_time(strategy.closedtrades - 1) : int(na)
    if not(na(start) or na(end))
        int daysDiff = days(end) - days(start)
        int monthsDiff = math.floor(daysDiff/30)
        bool result = monthsDiff > 2 ? true : daysDiff > 2 ? false : bool(na)


// @function            Calculates the Sortino Ratio for the trading period. 
// @param interestRate  (simple int/float) The benchmark "risk-free" rate of return to compare strategy risk against in the ratio. 
// @param forceCalc     (series bool) When `true`, forces a recalculation on each bar. Optional. The default is `false`, whereby calculations are performed only on the last bar, for efficiency.
// @returns             (float) The Sortino Ratio. Useful for measuring the risk-adjusted return of a strategy, taking into account only drawdowns. 
export sortinoRatio(simple float interestRate = 2.0, series bool forceCalc = false) =>
    if barstate.islastconfirmedhistory or barstate.islast or forceCalc
        bool useMonths = detectPeriod()
        float result = not na(useMonths) ? sortinoRatioImpl(useMonths, interestRate) : na
    

// @function            Calculates the Sharpe Ratio for the trading period. 
// @param interestRate  (simple int/float) The benchmark "risk-free" rate of return to compare strategy risk against in the ratio. 
// @param forceCalc     (series bool) When `true`, forces a recalculation on each bar. Optional. The default is `false`, whereby calculations are performed only on the last bar, for efficiency.
// @returns             (float) The Sharpe Ratio. Useful for measuring the risk-adjusted return of a strategy, taking into account both run-ups and drawdowns.
export sharpeRatio(simple float interestRate = 2.0, series bool forceCalc = false) =>
    if barstate.islastconfirmedhistory or barstate.islast or forceCalc
        bool useMonths = detectPeriod()
        float result = not na(useMonths) ? sharpeRatioImpl(useMonths, interestRate) : na
// }



// ———————————————————— Example code {


// —————————— Calculations

// Inputs for stop and limit orders in percent.
float percentStop = input.float(1.0, "Percent Stop",  minval = 0.0, step = 0.25, group = "Exit Orders")
float percentTP   = input.float(2.0, "Percent Limit", minval = 0.0, step = 0.25, group = "Exit Orders")

// Using the input stop/ limit percent, we can convert to ticks and use the ticks to level functions.
// This can be used to calculate the take profit and stop levels.
float sl = ticksToStopLevel (percentToTicks (percentStop))
float tp = ticksToTpLevel   (percentToTicks (percentTP))  

// Conditions used to reference position and determine trade bias
bool long       = strategy.position_size > 0
bool short      = strategy.position_size < 0
bool enterLong  = long  and not long [1]
bool enterShort = short and not short[1]
bool enter      = enterLong or enterShort
bool exit       = strategy.position_size == 0 and not (strategy.position_size == 0)[1]

// Condition to flip labels based on trade bias.
string stopStyle  = short ? label.style_label_up : label.style_label_down
string limitStyle = long  ? label.style_label_up : label.style_label_down

// Used to determine the exit price to draw an arrow for stop and limit exits
// and determine a color for the arrow (fuchsia for stop exits, lime for limits).
// We can use the trade bias as a multiplier to invert the condition for short trades.
exitPrice = strategy.closedtrades.exit_price(strategy.closedtrades-1)
bias      = math.sign(strategy.position_size)
avg       = strategy.position_avg_price

plotCol = exitPrice * bias[1] < avg[1] * bias[1] ? color.fuchsia : color.lime


// —————————— Strategy Calls

// Calculate two moving averages.
float maFast = ta.sma(close, 10)
float maSlow = ta.sma(close, 30)

// Create cross conditions for the averages.
bool longCondition  = ta.crossover(maFast, maSlow)
bool shortCondition = ta.crossunder(maFast, maSlow)

// Create entries based on the cross conditions for both trades biases.
if longCondition
    strategy.entry("My Long Entry Id", strategy.long)
if shortCondition
    strategy.entry("My Short Entry Id", strategy.short)

// Create a hard exit level for a take profit and stop loss.
// Using the `strategy.exit` wrapper function, we can simply specify the percent stop and limit using the input variables.
exitPercent("exit", percentStop, percentTP)
// Another option is to use the "ticksToLevel" functions with the percent to ticks functions to convert percent directly to the stop or limit level:
// strategy.exit("exit", stop = ticksToStopLevel (percentToTicks (percentStop)), limit = ticksToTpLevel (percentToTicks (percentTP)), comment = "exit")


// —————————— Plots

// Plot the entry level.
plot(strategy.position_avg_price, "strategy.position_avg_price", not enter ? color.new(color.orange, 60) : na, 2, plot.style_linebr)

// Plot the exit levels.
plot(sl, "Stop2", not enter ? color.new(color.fuchsia, 60) : na, 6, plot.style_linebr)
plot(tp, "TP2"  , not enter ? color.new(color.green,   60) : na, 6, plot.style_linebr)

// Plot an entry tag for both directions
plotshape(enterLong,  "longCondition",  shape.arrowup,   location.belowbar, color.new(color.green,   30), text = "Long")
plotshape(enterShort, "shortCondition", shape.arrowdown, location.abovebar, color.new(color.fuchsia, 30), text = "Short")

// Plot and arrow for stop or limit exits at the trade exit price. We use the price and the color from above.  
plotchar(exit ? exitPrice : na, "Exit Arrow", "◄", location.absolute, color.new(plotCol, 20))

// Plot labels using the percentProfit() functions to display the distance from entry to the level for the current trade
label labLimit = label.new(bar_index, sl, str.tostring(percentProfit(sl), format.percent), color = color.new(color.red,   70), style = limitStyle, textcolor = color.white)
label labStop  = label.new(bar_index, tp, str.tostring(percentProfit(tp), format.percent), color = color.new(color.green, 70), style = stopStyle,  textcolor = color.white)
// We delete the labels drawn one bar ago, or we end up with many labels drawn 
label.delete(labLimit[1])
label.delete(labStop [1])

// Show ratios in indicator values and the Data Window.
plotchar(sortinoRatio(2.0), "Sortino", "", location.top, size = size.tiny)
plotchar(sharpeRatio(2.0),  "Sharpe", "", location.top, size = size.tiny)
// }

Overview

Strategy Helpers converts a percentage of the entry price into ticks, a price level or a currency amount, converts between prices and ticks, and reports the profit of the open position as a percentage if it were closed at a given price. The functions return na when no position is open. Inputs set a percent stop and a percent limit for the example code. It draws nothing itself.

Details

Published by

ivansoft on GitHub

Code credit

TradingView, as written in the script's header

Draws

Library (no plots)

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Topics

pinescripttradingvew

Settings

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

SettingTypeDefaultVariable
Percent Stopfloat1.0percentStop
Percent Limitfloat2.0percentTP
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

261 stars

AIActivationFunctionsBipolarSigmoid is a Pine Script library that provides mathematical implementations of the bipolar sigmoid activation function and its derivatives for machine learning algorithms. It calculates bipolar sigmoid values bounded between -1 and

Library v5 AI / machine learning
Updated 4 years ago · None stated GitHub repository of AIActivationFunctionsBipolarSigmoid

KernelFunctions

by artificial-intelligence-edge
75 stars

KernelFunctions is a Pine Script v5 library that exports kernel weight functions for Nadaraya-Watson style estimation. It calculates a Rational Quadratic kernel, a Gaussian kernel based on a radial basis function, and a periodic kernel that models repeating be

Library v5
Updated 3 days ago · MIT GitHub repository of KernelFunctions

Ta

by nersent
29 stars

This Pine Script v5 library exports thin wrappers around built-in technical analysis functions. It covers moving averages, momentum measures such as RSI and CCI, volatility tools including ATR and standard deviation, range extremes, cross detection, and runnin

Library v5
Updated 11 months ago · Custom licence GitHub repository of Ta

Bands

by heyphat
26 stars

This library exports a Band type and functions that build a simple-moving-average basis with upper and lower envelopes from standard deviation. It classifies a source as above, below, or inside those envelopes, measures band width, and scales an imported z-sco

Library v6 Volatility & bandsMoving averages
Updated 2 months ago · AGPL-3.0 GitHub repository of Bands

Mylib

by ferranbt
21 stars

This script serves as a minimal utility library written in Pine Script v6. It provides an exported helper function that calculates the sum of two numeric arguments. As a reusable code library, it produces no visual plots, charts, or trading signals, and it con

Library v6
Updated 7 weeks ago · MPL-2.0 GitHub repository of Mylib

HLevel

by stupenkov
14 stars

HLevel is a Pine Script v5 utility library designed to manage horizontal price levels for cryptocurrency charts. It defines custom types for pivots and levels, along with methods to initialize, add, track, and expire price levels based on pivot high and pivot

Library v5
Updated 3 years ago · None stated GitHub repository of HLevel

More libraries

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers