1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
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
1,330 lines of Pine Script v5 from Andean Scalping.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.
Andean Scalping.pine
//@version=5
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// -----------------------------------------------------------------------------
// Copyright 2022 Iason Nikolas | jason5480
// Template Trailing Strategy script may be freely distributed under the MIT license.
//
// Permission is hereby granted, free of charge,
// to any person obtaining a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------------
//
// Authors: @jason5480
// Revision: v1.1.4
// Date: 16-Aug-2022
//
// Description
// =============================================================================
// This script is designed to be used as a template for building new strategies.
// The framework provide you with a configurable implementation of the entry, exit,
// stop loss and take profit trailing logic. The proposed way to use that script
// is in combination with a customized signal indicator where you implemented your
// own logic of start and end deal conditions. See my "Two MA Signal Indicator"
// and "Template Signal Indicator" in my profile for more. An other way to utilize
// this script (not recomended) is to copy it and replace the startLongDeal,
// startShortDeal, endLongDeal and endShortDeal variables in the STRATEGY 1 module
// according to your needs! However, doing that way you will have to reimplement
// your changes every time I release a new update if you want to recieve the new
// changes I made in the latest release.
//
// -----------------------------------------------------------------------------
// Disclaimer:
// 1. I am not licensed financial advisors or broker dealer. I do not tell you
// when or what to buy or sell. I developed this software which enables you
// execute manual or automated trades using TradingView. The
// software allows you to set the criteria you want for entering and exiting
// trades.
// 2. Do not trade with money you cannot afford to lose.
// 3. I do not guarantee consistent profits or that anyone can make money with no
// effort. And I am not providing you the holy grail.
// 4. Every system can have winning and losing streaks.
// 5. Money management plays a large role in the results of your trading. For
// example: lot size, account size, broker leverage, and broker margin call
// rules all have an effect on results. Also, your Take Profit and Stop Loss
// settings for individual pair trades and for overall account equity have a
// major impact on results. If you are new to trading and do not understand
// these items, then I recommend you seek education materials to further your
// knowledge.
//
// YOU NEED TO FIND AND USE THE TRADING SYSTEM THAT WORKS BEST FOR YOU AND YOUR
// TRADING TOLERANCE.
//
// I HAVE PROVIDED NOTHING MORE THAN A TOOL WITH OPTIONS FOR YOU TO TRADE WITH THIS PROGRAM ON TRADINGVIEW.
//
// I accept suggestions to improve the script! For any change proposal to the existing functionality,
// any additional feture you have in mind, or even reporting a defect (bug) you found, you should write it
// down here docs.google.com/spreadsheets/d/1xwpr2Ut4CmMX6kX9PxflWuvb4mZ6LFKJ1Y6C7LahQ5o/edit?usp=sharing
// and let me know by sending me a private message on TradingView.
// If you encounter any problems I will be happy to share them with me.
// -----------------------------------------------------------------------------
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// SETUP ============================================================================================================
strategy(title = 'Andean Scalping',
shorttitle = 'Andean Scalping',
overlay = true,
pyramiding = 0,
default_qty_type = strategy.percent_of_equity,
default_qty_value = 1,
initial_capital = 10000,
commission_type = strategy.commission.percent,
commission_value = 0.04,
max_lines_count = 300,
max_labels_count = 200)
import jason5480/external_input_utils/3 as exiu
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 📆📈 FILTERS =====================================================================================================
// Description: Module responsible for filtering out long and short open signals that do not meet user defined rules
// Dependencies: NONE
// Results: longFiltersApproval, shortFiltersApproval
import jason5480/time_filters/5 as tif
// INPUT ============================================================================================================
src_timezone = input.string(defval = 'Exchange', title = 'Timezones Src->Dst', options = ['Exchange', 'UTC', 'America/Los_Angeles', 'America/Phoenix', 'America/Vancouver', 'America/El_Salvador', 'America/Bogota', 'America/Chicago', 'America/New_York', 'America/Toronto', 'America/Argentina/Buenos_Aires', 'America/Sao_Paulo', 'Etc/UTC', 'Europe/London', 'Europe/Berlin', 'Europe/Madrid', 'Europe/Paris', 'Europe/Warsaw', 'Europe/Athens', 'Europe/Moscow', 'Asia/Tehran', 'Asia/Dubai', 'Asia/Ashkhabad', 'Asia/Kolkata', 'Asia/Almaty', 'Asia/Bangkok', 'Asia/Hong_Kong', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Taipei', 'Asia/Seoul', 'Asia/Tokyo', 'Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Sydney', 'Pacific/Auckland', 'Pacific/Fakaofo', 'Pacific/Chatham', 'Pacific/Honolulu'], inline = 'Timezone', group = '📆 Filters - Time')
dst_timezone = input.string(defval = 'Exchange', title = '->', options = ['Exchange', 'UTC', 'America/Los_Angeles', 'America/Phoenix', 'America/Vancouver', 'America/El_Salvador', 'America/Bogota', 'America/Chicago', 'America/New_York', 'America/Toronto', 'America/Argentina/Buenos_Aires', 'America/Sao_Paulo', 'Etc/UTC', 'Europe/London', 'Europe/Berlin', 'Europe/Madrid', 'Europe/Paris', 'Europe/Warsaw', 'Europe/Athens', 'Europe/Moscow', 'Asia/Tehran', 'Asia/Dubai', 'Asia/Ashkhabad', 'Asia/Kolkata', 'Asia/Almaty', 'Asia/Bangkok', 'Asia/Hong_Kong', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Taipei', 'Asia/Seoul', 'Asia/Tokyo', 'Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Sydney', 'Pacific/Auckland', 'Pacific/Fakaofo', 'Pacific/Chatham', 'Pacific/Honolulu'], tooltip = 'Source is the timezone to be used as a reference for the time settings. Destination is the timezone to convert into (e.g. the charts\' timezone)', inline = 'Timezone', group = '📆 Filters - Time')
usefromDate = input.bool(defval = true, title = 'From', inline = 'From Date', group = '📆 Filters - Time')
fromDate = input.time(defval = timestamp('01 Jan 2022 00:00'), title = '', inline = 'From Date', group = '📆 Filters - Time')
usetoDate = input.bool(defval = false, title = 'To ', inline = 'To Date', group = '📆 Filters - Time')
toDate = input.time(defval = timestamp('01 Jul 2022 00:00'), title = '', inline = 'To Date', group = '📆 Filters - Time')
useSessionDay = input.bool(defval = false, title = 'Session Days', inline = 'Session Days', group = '📆 Filters - Time')
mon = input.bool(defval = true, title = 'Mon', inline = 'Session Days', group = '📆 Filters - Time')
tue = input.bool(defval = true, title = 'Tue', inline = 'Session Days', group = '📆 Filters - Time')
wed = input.bool(defval = true, title = 'Wed', inline = 'Session Days', group = '📆 Filters - Time')
thu = input.bool(defval = true, title = 'Thu', inline = 'Session Days', group = '📆 Filters - Time')
fri = input.bool(defval = true, title = 'Fri', inline = 'Session Days', group = '📆 Filters - Time')
sat = input.bool(defval = false, title = 'Sat', inline = 'Session Days', group = '📆 Filters - Time')
sun = input.bool(defval = false, title = 'Sun', inline = 'Session Days', group = '📆 Filters - Time')
useSessionStart = input.bool(defval = false, title = 'Session Start', inline = 'Session Start', group = '📆 Filters - Time')
sessionStartHour = input.int(defval = 12, title = '', minval = 0, maxval = 23, step = 1, inline = 'Session Start', group = '📆 Filters - Time')
sessionStartMinute = input.int(defval = 00, title = ':', minval = 0, maxval = 59, step = 1, tooltip = 'Start time of the session.', inline = 'Session Start', group = '📆 Filters - Time')
useSessionEnd = input.bool(defval = false, title = 'Session End', inline = 'Session End', group = '📆 Filters - Time')
sessionEndHour = input.int(defval = 20, title = '', minval = 0, maxval = 23, step = 1, inline = 'Session End', group = '📆 Filters - Time')
sessionEndMinute = input.int(defval = 00, title = ':', minval = 0, maxval = 59, step = 1, tooltip = 'End time of the session.', inline = 'Session End', group = '📆 Filters - Time')
longTradesEnabled = input.bool(defval = true, title = 'Long Trades', inline = 'Trades', group = '📈 Filters - Trend')
shortTradesEnabled = input.bool(defval = true, title = 'Short Trades', tooltip = 'Enable long/short trades.', inline = 'Trades', group = '📈 Filters - Trend')
emaFilterEnabled = input.bool(defval = false, title = 'EMA Filter', tooltip = 'Enable long/short trades based on EMA.', group = '📈 Filters - Trend')
emaTimeframe = input.timeframe(defval = "", title = ' EMA Res/Len/Src', inline = 'EMA Filter', group = '📈 Filters - Trend')
emaLength = input.int(defval = 200, title = '', minval = 1, inline = 'EMA Filter', group = '📈 Filters - Trend')
emaSrc = exiu.str_to_src(input.string(defval = 'close', title = '', options = ['open', 'high', 'low', 'close', 'hl2', 'hlc3', 'ohlc4', 'hlcc4'], tooltip = 'The timeframe, period and source for the EMA calculation.', inline = 'EMA Filter', group = '📈 Filters - Trend'))
emaAtrBandEnabled = input.bool(defval = false, title = 'EMA ATR Band', tooltip = 'Enable ATR band for EMA filter.', group = '📈 Filters - Trend')
filterAtrLength = input.int(defval = 5, title = ' EMA ATR Len/Mul', minval = 1, inline = 'EMA ATR', group = '📈 Filters - Trend')
filterAtrMul = input.float(defval = 1.0, title = '', tooltip = 'ATR length and multiplier to be used for the ATR calculation that will be added on top of the EMA filter.', minval = 0.1, step = 0.1, inline = 'EMA ATR', group = '📈 Filters - Trend')
adxFilterEnabled = input.bool(defval = false, title = 'ADX Filter', tooltip = 'Enable long/short trades based on ADX.', group = '📈 Filters - Trend')
adxSmoothing = input.int(defval = 14, title = ' Smooth/DI Len', minval = 1, inline = 'ADX Filter', group = '📈 Filters - Trend')
diLength = input.int(defval = 14, title = '', minval = 1, tooltip = 'ADX Smoothing length and Direction Length.', inline = 'ADX Filter', group = '📈 Filters - Trend')
adxThreshold = input.float(defval = 39.4, title = ' Threshold', minval = 0.1, maxval = 100.0, step = 0.1, tooltip = 'ADX lower threshold.', group = '📈 Filters - Trend')
// LOGIC ============================================================================================================
bool dateFilterApproval = tif.is_in_date_range(usefromDate, fromDate, usetoDate, toDate, src_timezone, dst_timezone)
bool sessionFilterApproval = tif.is_in_session(useSessionStart, sessionStartHour, sessionStartMinute, useSessionEnd, sessionEndHour, sessionEndMinute, useSessionDay, mon, tue, wed, thu, fri, sat, sun, src_timezone, dst_timezone)
bool timeFilterApproval = dateFilterApproval and sessionFilterApproval
emaLine = request.security(symbol = syminfo.tickerid, timeframe = emaTimeframe, expression = ta.ema(emaSrc, emaLength), gaps = barmerge.gaps_off)
emaAtr = ta.atr(filterAtrLength)
emaUpperBand = emaLine + filterAtrMul * emaAtr
emaLowerBand = emaLine - filterAtrMul * emaAtr
bool emaLongApproval = emaFilterEnabled ? close > (emaAtrBandEnabled ? emaUpperBand : emaLine) and open > (emaAtrBandEnabled ? emaUpperBand : emaLine) : true
bool emaShortApproval = emaFilterEnabled ? close < (emaAtrBandEnabled ? emaLowerBand : emaLine) and open < (emaAtrBandEnabled ? emaLowerBand : emaLine) : true
[_, _, adx] = ta.dmi(diLength, adxSmoothing)
bool adxApproval = adxFilterEnabled ? adx > adxThreshold : true
bool longFiltersApproval = longTradesEnabled and emaLongApproval and adxApproval and timeFilterApproval
bool shortFiltersApproval = shortTradesEnabled and emaShortApproval and adxApproval and timeFilterApproval
// PLOT =============================================================================================================
bgcolor(color = timeFilterApproval ? na : color.new(color.gray, 90), offset = 1, title = 'Time Window')
emaLineColor = emaLongApproval ? color.teal : emaShortApproval ? color.maroon : color.gray
plot(series = emaFilterEnabled ? emaLine : na, color = emaLineColor, style = plot.style_linebr, linewidth = 2, title = 'EMA Line')
emaUpperBandPlot = plot(series = emaUpperBand, color = na, style = plot.style_linebr, linewidth = 1, title = 'EMA Upper Band')
emaLowerBandPlot = plot(series = emaLowerBand, color = na, style = plot.style_linebr, linewidth = 1, title = 'EMA Lower Band')
emaBandFillColor = emaFilterEnabled and emaAtrBandEnabled ? color.new(emaLineColor, 85) : na
fill(plot1 = emaUpperBandPlot, plot2 = emaLowerBandPlot, color = emaBandFillColor, title = 'EMA Band')
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ☭ SHARED VARIABLES 1 =============================================================================================
// Description: Module responsible for general purpose variable that needed for several other modules
// Dependencies: NONE
// Results: currentPositionSize, previousPositionSize, positionSizeIncreased, positionSizeDecreased, longPositionCompleted, shortPositionCompleted
// LOGIC ============================================================================================================
// the current and previous position sizes
float currentPositionSize = strategy.opentrades.size(strategy.opentrades - 1)
float previousPositionSize = strategy.opentrades.size(strategy.opentrades - 1)[1]
bool positionSizeIncreased = currentPositionSize > previousPositionSize
bool positionSizeDecreased = currentPositionSize < previousPositionSize
bool longPositionCompleted = currentPositionSize <= 0 and previousPositionSize > 0
bool shortPositionCompleted = currentPositionSize >= 0 and previousPositionSize < 0
// flags tht are true when already entered a long or short position
bool enteredLongPosition = currentPositionSize > 0 and previousPositionSize <= 0
bool enteredShortPosition = currentPositionSize < 0 and previousPositionSize >= 0
// debug flags
var bool showAlertsEnabled = false
var bool debugModeEnabled = false
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🛠️ STRATEGY 1 ====================================================================================================
// Description: Module responsible for the open position logic. This is implemented based on deal conditions defined internally (in this script) or externaly (based on conditions that take as input other indicator)
// Dependencies: FILTERS, SHARED VARIABLES 1
// Results: openLongPosition, openShortPosition
import HeWhoMustNotBeNamed/enhanced_ta/14 as eta
// INPUT ============================================================================================================
dealMode = input.string(defval = '🔧Internal', title = 'Deal Conditions Mode', options = ['🔧Internal', '🔨External'], tooltip = 'Use the "Internal" MA cross over/under logic to start and end your deals. Or use an "External" indicator to construct your own start and end deal conditions.', group = '🛠️ Strategy')
cooldownEnabled = input.bool(defval = false, title = 'Cooldown # of bars', inline = 'Cooldown', group = '🛠️ Strategy')
cooldownBars = input.int(defval = 7, title = '', minval = 1, tooltip = 'Do NOT emit open position signal if no more than the given number of bars have passed since previous position has been completed. A new start deal signal will be needed after that period to open a new position.', inline = 'Cooldown', group = '🛠️ Strategy')
and_length = input.int(defval=50, title='Andean Length', group='🔧 Strategy - Internal')
sig_length = input.int(defval=9, title='Andean Signal Length', group='🔧 Strategy - Internal')
signal_threshold_length = input.int(defval=1000, title='Signal Threshold Length', group='🔧 Strategy - Internal')
signal_threshold_multiplier = input.float(defval=1.1, step=0.1, title='Signal Threshold Multiplier', group='🔧 Strategy - Internal')
externalInput = input.source(defval = close, title = 'External Input', tooltip = 'Select input from an external indicator. The indicator should be added to the same chart with this strategy and the desired value that will take part in the condition should be ploted in the chart.', group = '🔨 Strategy - External')
startLongDealOperator = input.string(defval = '/10', title = 'Start Long Deal when input', options = ['==', '<', '>', '<=', '>=', '!=', 'crossover', 'crossunder', 'mod2', 'mod3', 'mod10', '/10', 'noop'], inline = 'Start Long Deal', group = '🔨 Strategy - External')
startLongDealValue = input.float(defval = 2.0, title = '', tooltip = 'Conditon to start a Long Deal based on external input.', inline = 'Start Long Deal', group = '🔨 Strategy - External')
endLongDealOperator = input.string(defval = '/10', title = 'End Long Deal when input', options = ['==', '<', '>', '<=', '>=', '!=', 'crossover', 'crossunder', 'mod2', 'mod3', 'mod10', '/10', 'noop'], inline = 'End Long Deal', group = '🔨 Strategy - External')
endLongDealValue = input.float(defval = 1.0, title = '', tooltip = 'Conditon to end a Long Deal based on external input.', inline = 'End Long Deal', group = '🔨 Strategy - External')
startShortDealOperator = input.string(defval = 'mod10', title = 'Start Short Deal when input', options = ['==', '<', '>', '<=', '>=', '!=', 'crossover', 'crossunder', 'mod2', 'mod3', 'mod10', '/10', 'noop'], inline = 'Start Short Deal', group = '🔨 Strategy - External')
startShortDealValue = input.float(defval = 2.0, title = '', tooltip = 'Conditon to start a Short Deal based on external input.', inline = 'Start Short Deal', group = '🔨 Strategy - External')
endShortDealOperator = input.string(defval = 'mod10', title = 'End Short Deal when input', options = ['==', '<', '>', '<=', '>=', '!=', 'crossover', 'crossunder', 'mod2', 'mod3', 'mod10', '/10', 'noop'], inline = 'End Short Deal', group = '🔨 Strategy - External')
endShortDealValue = input.float(defval = 1.0, title = '', tooltip = 'Conditon to end a Short Deal based on external input.', inline = 'End Short Deal', group = '🔨 Strategy - External')
// ANDEAN OSCILLATOR ============================================================================================================
var alpha = 2/(and_length+1)
var up1 = 0.,var up2 = 0.
var dn1 = 0.,var dn2 = 0.
C = close
O = open
up1 := nz(math.max(C, O, up1[1] - (up1[1] - C) * alpha), C)
up2 := nz(math.max(C * C, O * O, up2[1] - (up2[1] - C * C) * alpha), C * C)
dn1 := nz(math.min(C, O, dn1[1] + (C - dn1[1]) * alpha), C)
dn2 := nz(math.min(C * C, O * O, dn2[1] + (C * C - dn2[1]) * alpha), C * C)
bull = math.sqrt(dn2 - dn1 * dn1)
bear = math.sqrt(up2 - up1 * up1)
signal = ta.ema(math.max(bull, bear), sig_length)
average_threshold = ta.sma(signal, signal_threshold_length) * signal_threshold_multiplier
plotchar(average_threshold, "Avg Threshold", "")
and_startLongDeal = bull > bear and bull > signal and signal > average_threshold
and_endLongDeal = bull < signal
and_startShortDeal = bear > bull and bear > signal and signal > average_threshold
and_endShortDeal = bear < signal
//-----------------------------------------------------------------------------}
bool startLongDeal = dealMode == '🔧Internal' ? and_startLongDeal : exiu.eval_cond(externalInput, startLongDealOperator, startLongDealValue, false)
bool endLongDeal = dealMode == '🔧Internal' ? and_endLongDeal : exiu.eval_cond(externalInput, endLongDealOperator, endLongDealValue, true)
bool startShortDeal = dealMode == '🔧Internal' ? and_startShortDeal : exiu.eval_cond(externalInput, startShortDealOperator, startShortDealValue, false)
bool endShortDeal = dealMode == '🔧Internal' ? and_endShortDeal : exiu.eval_cond(externalInput, endShortDealOperator, endShortDealValue, true)
int barsSinceComplete = nz(ta.barssince(currentPositionSize != 0), 999999)
bool cooldownApproval = cooldownEnabled ? barsSinceComplete > cooldownBars : true
bool openLongPosition = longFiltersApproval and startLongDeal and cooldownApproval
bool openShortPosition = shortFiltersApproval and startShortDeal and cooldownApproval
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ☭ SHARED VARIABLES 2 =============================================================================================
// Description: Module responsible for general purpose variable that needed for several other modules
// Dependencies: SHARED VARIABLES 1, STRATEGY 1
// Results: validOpenLongPosition, validOpenShortPosition, validOpenClose, barsSinceOpenLong, barsSinceOpenShort,
// barsSinceValidOpenLong, barsSinceValidOpenShort, barsSinceCloseLong, barsSinceCloseShort, longAtr, shortAtr
import HeWhoMustNotBeNamed/arrayutils/21 as pa
// INPUT ============================================================================================================
atrMethod = input.string(defval = 'STATIC', title = 'ATR Method', options = ['STATIC', 'DYNAMIC', 'LADDER'], tooltip = 'The method to calculate the ATR used for the trailing.', group = '☭ Shared Variables')
atrMaType = str.lower(input.string(defval = 'RMA', title = ' ATR Smooth Type/Len', options = ['SMA', 'EMA', 'HMA', 'RMA', 'WMA'], inline = 'ATR', group = '☭ Shared Variables'))
atrLength = input.int(defval = 14, title = '', minval = 1, tooltip = 'The smoothing type and the length to be used for the ATR calculation.', inline = 'ATR', group = '☭ Shared Variables')
// LOGIC ============================================================================================================
// the open signals when not already into a position
bool validOpenLongPosition = openLongPosition and not (currentPositionSize > 0)
bool validOpenShortPosition = openShortPosition and not (currentPositionSize < 0)
// The close price when last valid open was triggered
float validOpenClose = ta.valuewhen(validOpenLongPosition or validOpenShortPosition, close, 0)
// count how far are the last open signals (valid or not)
int barsSinceOpenLong = nz(ta.barssince(openLongPosition), 999999)
int barsSinceOpenShort = nz(ta.barssince(openShortPosition), 999999)
int barsSinceValidOpenLong = nz(ta.barssince(validOpenLongPosition), 999999)
int barsSinceValidOpenShort = nz(ta.barssince(validOpenShortPosition), 999999)
// count how far are the last valid close signals
var bool closeLongPosition = false
var bool closeShortPosition = false
int barsSinceCloseLong = nz(ta.barssince(closeLongPosition and timeFilterApproval), 999999)
int barsSinceCloseShort = nz(ta.barssince(closeShortPosition and timeFilterApproval), 999999)
// calculate atr based on method selected, static atr when last open signal was triggered, dynamic atr is the current atr that change over time, ladder atr for positive and negative bars
float dynamicAtr = eta.ma(ta.tr(true), atrMaType, atrLength)
float staticAtr = ta.valuewhen(validOpenLongPosition or validOpenShortPosition, dynamicAtr, 0)
var negativeTrs = array.new<float>()
var positiveTrs = array.new<float>()
if(open > close)
pa.push(negativeTrs, ta.tr(true), atrLength)
else
pa.push(positiveTrs, ta.tr(true), atrLength)
float ladderLongAtr = pa.ma(negativeTrs, atrMaType, atrLength)
float ladderShortAtr = pa.ma(positiveTrs, atrMaType, atrLength)
float longAtr = switch atrMethod
'STATIC' => staticAtr
'DYNAMIC' => dynamicAtr
'LADDER' => ladderLongAtr
=> na
float shortAtr = switch atrMethod
'STATIC' => staticAtr
'DYNAMIC' => dynamicAtr
'LADDER' => ladderShortAtr
=> na
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ⇩ ENTRY ==========================================================================================================
// Description: Module responsible for the trailing entry logic implementation based on the deviation
// Dependencies: SHARED VARIABLES 1, STRATEGY 1, SHARED VARIABLES 2
// Results: longPositionEntry, longIsActive, longTrEntryIsActive, shortPositionEntry, shortIsActive, shortTrEntryIsActive, entryBarId, entryPrice
// INPUT ============================================================================================================
entryTrailingEnabled = input.bool(defval = false, title = 'Enable Trailing', tooltip = 'Enable or disable the trailing for entry position.', group = '⇩ Entry')
devEntryMethod = input.string(defval = 'PERC', title = 'Deviation Method', options = ['PERC', 'ATR', 'LOC'], tooltip = 'The method to calculate the deviation for the trailing entry.', group = '⇩ Entry')
devEntryPerc = input.float(defval = 3.0, title = ' Deviation %', minval = 0.01, maxval = 100.0, step = 0.05, tooltip = 'The step to follow the price when the open position condition is met.', group = '⇩ Entry') / 100.0
devEntryAtrMul = input.float(defval = 0.5, title = ' Deviation ATR Mul', minval = 0.01, step = 0.05, tooltip = 'Multiplier to be used on the ATR to calculate the step for following the price, until the entry target is reached.', group = '⇩ Entry')
devEntryLen = input.int(defval = 3, title = ' Dev Loc Extrema Len/Ticks', minval = 1, inline = 'Deviation Entry Local Extrema', group = '⇩ Entry')
devEntryTicks = input.int(defval = 2, title = '', minval = 0, tooltip = 'Local extrema (minimum/maximum) within a window of length minus/plus some ticks to be used for the entry target.', inline = 'Deviation Entry Local Extrema', group = '⇩ Entry')
// LOGIC ============================================================================================================
bool longPositionEntry = entryTrailingEnabled ? enteredLongPosition : validOpenLongPosition
// Mark when long position is active
bool longIsActive = longPositionEntry or currentPositionSize > 0
// count how far is the last long entry signal
int barsSinceEnterLong = nz(ta.barssince(longPositionEntry), 999999)
bool openLongIsActive = barsSinceCloseLong >= barsSinceOpenLong // close -> open
bool enterLongIsPending = barsSinceEnterLong > barsSinceValidOpenLong // enter -> validOpen
bool tryEnterLongPosition = entryTrailingEnabled and longFiltersApproval and openLongIsActive and enterLongIsPending and currentPositionSize <= 0
float entryHighestHigh = ta.highest(high, devEntryLen)
float openEntryHighestHigh = ta.valuewhen(validOpenLongPosition, entryHighestHigh, 0)
getLongEntryOpenBaseScr() =>
switch devEntryMethod
'PERC' => validOpenClose
'ATR' => validOpenClose
'LOC' => openEntryHighestHigh
=> na
getLongEntryTrailingBaseScr() =>
switch devEntryMethod
'PERC' => low
'ATR' => low
'LOC' => entryHighestHigh
=> na
getLongEntryPrice(baseSrc) =>
switch devEntryMethod
'PERC' => baseSrc * (1 + devEntryPerc)
'ATR' => baseSrc + devEntryAtrMul * longAtr
'LOC' => baseSrc + devEntryTicks * syminfo.mintick
=> na
var float longTrEntryPrice = na
bool isFirstValidOpenLongPosition = entryTrailingEnabled and validOpenLongPosition and na(longTrEntryPrice[1])
longTrEntryPrice := if (isFirstValidOpenLongPosition)
getLongEntryPrice(getLongEntryOpenBaseScr())
else if (tryEnterLongPosition)
math.min(getLongEntryPrice(getLongEntryTrailingBaseScr()), nz(longTrEntryPrice[1], 999999))
else
na
bool longTrEntryIsActive = not na(longTrEntryPrice)
bool shortPositionEntry = entryTrailingEnabled ? enteredShortPosition : validOpenShortPosition
// Mark when short position is active
bool shortIsActive = shortPositionEntry or currentPositionSize < 0
// count how far is the last short entry signal
int barsSinceEnterShort = nz(ta.barssince(shortPositionEntry), 999999)
bool openShortIsActive = barsSinceCloseShort >= barsSinceOpenShort // close -> open
bool enterShortIsPending = barsSinceEnterShort > barsSinceValidOpenShort // enter -> validOpen
bool tryEnterShortPosition = entryTrailingEnabled and shortFiltersApproval and openShortIsActive and enterShortIsPending and currentPositionSize >= 0
float entryLowestLow = ta.lowest(low, devEntryLen)
float openEntryLowestLow = ta.valuewhen(validOpenShortPosition, entryLowestLow, 0)
getShortEntryOpenBaseScr() =>
switch devEntryMethod
'PERC' => validOpenClose
'ATR' => validOpenClose
'LOC' => openEntryLowestLow
=> na
getShortEntryTrailingBaseScr() =>
switch devEntryMethod
'PERC' => high
'ATR' => high
'LOC' => entryLowestLow
=> na
getShortEntryPrice(baseSrc) =>
switch devEntryMethod
'PERC' => baseSrc * (1 - devEntryPerc)
'ATR' => baseSrc - devEntryAtrMul * shortAtr
'LOC' => baseSrc - devEntryTicks * syminfo.mintick
=> na
var float shortTrEntryPrice = na
bool isFirstValidOpenShortPosition = entryTrailingEnabled and validOpenShortPosition and na(shortTrEntryPrice[1])
shortTrEntryPrice := if (isFirstValidOpenShortPosition)
getShortEntryPrice(getShortEntryOpenBaseScr())
else if (tryEnterShortPosition)
math.max(getShortEntryPrice(getShortEntryTrailingBaseScr()), nz(shortTrEntryPrice[1]))
else
na
bool shortTrEntryIsActive = not na(shortTrEntryPrice)
// bar index when the enter signal was triggered
int entryBarId = ta.valuewhen(longPositionEntry or shortPositionEntry, bar_index, 0)
// current entry position price
float posPrice = strategy.opentrades.entry_price(strategy.opentrades - 1)
// price when entered a position or potential entry price in case of trailing
float entryPrice = (validOpenLongPosition or validOpenShortPosition) and not entryTrailingEnabled ? close
: longTrEntryIsActive ? longTrEntryPrice
: shortTrEntryIsActive ? shortTrEntryPrice
: posPrice
// PLOT =============================================================================================================
var trailingEntryColor = color.new(color.blue, 0)
plot(series = longTrEntryPrice, title = 'Long Tr Entry Price', color = trailingEntryColor, linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = shortTrEntryPrice, title = 'Short Tr Entry Price', color = trailingEntryColor, linewidth = 1, style = plot.style_linebr, offset = 1)
var posColor = color.new(color.white, 0)
plot(series = posPrice, title = 'Position', color = posColor, linewidth = 1, style = plot.style_linebr, offset = 1)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🟢 TAKE PROFIT 1 =================================================================================================
// Description: Module responsible for the take profit logic implementation based on the method and the number of step take profit targets and the trailing deviation
// Dependencies: SHARED VARIABLES 1
// Results: longTrailingTakeProfitExecuted, shortTrailingTakeProfitExecuted, longInitTrailingTakeProfitExecuted, shortInitTrailingTakeProfitExecuted
// INPUT ============================================================================================================
numOfTakeProfitTargets = input.int(defval = 1, title = 'Take Profit Targets', minval = 0, tooltip = 'The number of take profit targets to be set for each entry. The first target is the initial target and every additional target is a step target.', group = '🟢 Take Profit')
// LOGIC ============================================================================================================
// arrays with the updated take Profit Prices
var longTakeProfitPrices = array.new<float>(numOfTakeProfitTargets, na)
var shortTakeProfitPrices = array.new<float>(numOfTakeProfitTargets, na)
// take profit has to communicate the execution of take profit targets with the stop loss logic when 'TP' mode is selected
var longTrailingTakeProfitExecuted = array.new<bool>(numOfTakeProfitTargets, false)
var shortTrailingTakeProfitExecuted = array.new<bool>(numOfTakeProfitTargets, false)
// count the Take Profit targets that executed
int longTrailingTakeProfitExecutedCount = 0
int shortTrailingTakeProfitExecutedCount = 0
// calculate the highest high since the long entry
var float highestHighSinceLongEntry = na
highestHighSinceLongEntry := if (longIsActive)
math.max(high, nz(highestHighSinceLongEntry[1], high))
else
na
// calculate the lowest low since the short entry
var float lowestLowSinceShortEntry = na
lowestLowSinceShortEntry := if (shortIsActive)
math.min(low, nz(lowestLowSinceShortEntry[1], low))
else
na
for [i, takeProfitPrice] in longTakeProfitPrices
executed = currentPositionSize > 0 and (array.get(longTrailingTakeProfitExecuted, i) or (((positionSizeDecreased or previousPositionSize == 0)) and highestHighSinceLongEntry >= takeProfitPrice))
array.set(longTrailingTakeProfitExecuted, i, executed)
if (executed)
longTrailingTakeProfitExecutedCount := longTrailingTakeProfitExecutedCount + 1
for [i, takeProfitPrice] in shortTakeProfitPrices
executed = currentPositionSize < 0 and (array.get(shortTrailingTakeProfitExecuted, i) or (((positionSizeIncreased or previousPositionSize == 0)) and lowestLowSinceShortEntry <= takeProfitPrice))
array.set(shortTrailingTakeProfitExecuted, i, executed)
if (executed)
shortTrailingTakeProfitExecutedCount := shortTrailingTakeProfitExecutedCount + 1
// check if the i-th Take Profit target was executed
longIthTrailingTakeProfitExecuted(i) =>
array.size(longTrailingTakeProfitExecuted) > i ? array.get(longTrailingTakeProfitExecuted, i) : false
shortIthTrailingTakeProfitExecuted(i) =>
array.size(shortTrailingTakeProfitExecuted) > i ? array.get(shortTrailingTakeProfitExecuted, i) : false
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🛠️ STRATEGY 2 ====================================================================================================
// Description: Module responsible for the close position logic. This is implemented based on deal conditions defined internally (in this script) or externaly (based on conditions that take as input other indicator)
// Dependencies: FILTERS, STRATEGY 1, ENTRY
// Results: closeLongPosition, closeShortPosition
// INPUT ============================================================================================================
ignoreEndDeals = input.bool(defval = true, title = 'Ignore End Deals', tooltip = 'A close signal will not be emited when the end deal condition is met, thus you will not exit your existing position based on the strategy logic. If this option is checked, you will exit only when the stop loss or the take profit targets are triggered.', group = '🛠️ Strategy')
closeEarlyEnabled = input.bool(defval = false, title = 'Close Early # of bars', inline = 'Close Early', group = '🛠️ Strategy')
closeEarlyBars = input.int(defval = 7, title = '', minval = 1, tooltip = 'Emit close position signal if the given number of bars have passed since the entry and the first take profit target has not been reached yet.', inline = 'Close Early', group = '🛠️ Strategy')
closeAtSessionEnd = input.bool(defval = false, title = 'Close at Session End', tooltip = 'Close all positions at the market price at the end of each session or the end of time window.', group = '🛠️ Strategy')
// LOGIC ============================================================================================================
closeLongPosition := (ignoreEndDeals ? false : longTradesEnabled and endLongDeal) or (closeEarlyEnabled ? bar_index - entryBarId == closeEarlyBars and not longIthTrailingTakeProfitExecuted(0) : false)
closeShortPosition := (ignoreEndDeals ? false : shortTradesEnabled and endShortDeal) or (closeEarlyEnabled ? bar_index - entryBarId == closeEarlyBars and not shortIthTrailingTakeProfitExecuted(0) : false)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ☭ SHARED VARIABLES 3 =============================================================================================
// Description: Module responsible for general purpose variable that needed for several other modules
// Dependencies: SHARED VARIABLES 1, STRATEGY 2
// Results: validCloseLongPosition, validCloseShortPosition, closeClose
// LOGIC ============================================================================================================
// the close signals when already into a position to that direction
bool validCloseLongPosition = closeLongPosition and currentPositionSize > 0
bool validCloseShortPosition = closeShortPosition and currentPositionSize < 0
// The close price when last valid close was triggered
float closeClose = ta.valuewhen(validCloseLongPosition or validCloseShortPosition, close, 0)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ⇧ EXIT ===========================================================================================================
// Description: Module responsible for the trailing exit logic implementation based on the deviation
// Dependencies: SHARED VARIABLES 1, SHARED VARIABLES 2, STRATEGY 2, SHARED VARIABLES 3
// Results: longTrExitIsActive, shortTrExitIsActive
// INPUT ============================================================================================================
exitTrailingEnabled = input.bool(defval = false, title = 'Enable Trailing', tooltip = 'Enable or disable the trailing for exit position.', group = '⇧ Exit')
devExitMethod = input.string(defval = 'PERC', title = 'Deviation Method', options = ['PERC', 'ATR', 'LOC'], tooltip = 'The method to calculate the deviation for the trailing exit.', group = '⇧ Exit')
devExitPerc = input.float(defval = 3.0, title = ' Deviation %', minval = 0.01, maxval = 100.0, step = 0.05, tooltip = 'The step to follow the price when the close position condition is met.', group = '⇧ Exit') / 100.0
devExitAtrMul = input.float(defval = 0.5, title = ' Deviation ATR Mul', minval = 0.01, step = 0.05, tooltip = 'Multiplier to be used on the ATR to calculate the step for following the price, until the exit target is reached.', group = '⇧ Exit')
devExitLen = input.int(defval = 3, title = ' Dev Loc Extrema Len/Ticks', minval = 1, inline = 'Deviation Exit Local Extrema', group = '⇧ Exit')
devExitTicks = input.int(defval = 2, title = '', minval = 0, tooltip = 'Local extrema (minimum/maximum) within a window of length minus/plus some ticks to be used for the entry target.', inline = 'Deviation Exit Local Extrema', group = '⇧ Exit')
// LOGIC ============================================================================================================
bool exitLongIsPending = barsSinceOpenLong > barsSinceCloseLong and currentPositionSize > 0 // open -> close
bool tryExitLongPosition = exitTrailingEnabled and exitLongIsPending
float exitLowestLow = ta.lowest(low, devExitLen)
float closeExitLowestLow = ta.valuewhen(validCloseLongPosition, exitLowestLow, 0)
getLongExitCloseBaseScr() =>
switch devExitMethod
'PERC' => closeClose
'ATR' => closeClose
'LOC' => closeExitLowestLow
=> na
getLongTrExitBaseScr() =>
switch devExitMethod
'PERC' => high
'ATR' => high
'LOC' => exitLowestLow
=> na
getLongTrExitPrice(baseSrc) =>
switch devExitMethod
'PERC' => baseSrc * (1 - devExitPerc)
'ATR' => baseSrc - devExitAtrMul * longAtr
'LOC' => baseSrc - devExitTicks * syminfo.mintick
=> na
var float longTrExitPrice = na
bool isFirstValidCloseLongPosition = exitTrailingEnabled and validCloseLongPosition and na(longTrExitPrice[1])
longTrExitPrice := if (isFirstValidCloseLongPosition)
getLongTrExitPrice(getLongExitCloseBaseScr())
else if (tryExitLongPosition)
math.max(getLongTrExitPrice(getLongTrExitBaseScr()), nz(longTrExitPrice[1], 999999.9))
else
na
bool longTrExitIsActive = not na(longTrExitPrice)
bool exitShortIsPending = barsSinceOpenShort > barsSinceCloseShort and currentPositionSize < 0 // open -> close
bool tryExitShortPosition = exitTrailingEnabled and exitShortIsPending
float exitHighestHigh = ta.highest(high, devExitLen)
float closeExitHighestHigh = ta.valuewhen(validCloseShortPosition, exitHighestHigh, 0)
getShortExitCloseBaseScr() =>
switch devExitMethod
'PERC' => closeClose
'ATR' => closeClose
'LOC' => closeExitHighestHigh
=> na
getShortTrExitBaseScr() =>
switch devExitMethod
'PERC' => low
'ATR' => low
'LOC' => exitHighestHigh
=> na
getShortTrExitPrice(baseSrc) =>
switch devExitMethod
'PERC' => baseSrc * (1 + devExitPerc)
'ATR' => baseSrc + devExitAtrMul * shortAtr
'LOC' => baseSrc + devExitTicks * syminfo.mintick
=> na
var float shortTrExitPrice = na
bool isFirstValidCloseShortPosition = exitTrailingEnabled and validCloseShortPosition and na(shortTrExitPrice[1])
shortTrExitPrice := if (isFirstValidCloseShortPosition)
getShortTrExitPrice(getShortExitCloseBaseScr())
else if (tryExitShortPosition)
math.min(getShortTrExitPrice(getShortTrExitBaseScr()), nz(shortTrExitPrice[1], 999999.9))
else
na
bool shortTrExitIsActive = not na(shortTrExitPrice)
// price when exited a position or potential exit price in case of trailing
float exitPrice = longTrExitIsActive ? longTrExitPrice
: shortTrExitIsActive ? shortTrExitPrice
: close
// PLOT =============================================================================================================
var trailingExitColor = color.new(color.purple, 0)
plot(series = longTrExitPrice, title = 'Long Sell Price', color = trailingExitColor, linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = shortTrExitPrice, title = 'Short Sell Price', color = trailingExitColor, linewidth = 1, style = plot.style_linebr, offset = 1)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🔴 STOP LOSS =====================================================================================================
// Description: Module responsible for the stop loss logic implementation based on the method and the trailing mode
// Dependencies: SHARED VARIABLES 2, ENTRY, TAKE PROFIT 1
// Results: longStopLossPrice, shortStopLossPrice
// INPUT ============================================================================================================
stopLossMethod = input.string(defval = 'PERC', title = 'Stop Loss Method', options = ['PERC', 'ATR', 'LOC'], tooltip = 'The method to calculate the stop loss (percentagewise, ATR based or based on local extrema).', group = '🔴 Stop Loss')
longTrailingStopLossPerc = input.float(defval = 7.5, title = ' Long/Short Stop Loss %', minval = 0.05, maxval = 100.0, step = 0.05, inline = 'Stop Loss Perc', group = '🔴 Stop Loss') / 100.0
shortTrailingStopLossPerc = input.float(defval = 7.5, title = '', minval = 0.05, maxval = 100.0, step = 0.05, tooltip = 'The percentage of the price decrease/increase to set the stop loss price target for long/short positions.', inline = 'Stop Loss Perc', group = '🔴 Stop Loss') / 100.0
longStopLossAtrMul = input.float(defval = 3.0, title = ' ATR Long/Short Mul ', minval = 0.1, step = 0.1, inline = 'Stop Loss ATR Multiplier', group = '🔴 Stop Loss')
shortStopLossAtrMul = input.float(defval = 3.0, title = '', minval = 0.1, step = 0.1, tooltip = 'ATR multiplier to be used for the long/short stop loss.', inline = 'Stop Loss ATR Multiplier', group = '🔴 Stop Loss')
stopLossLocLen = input.int(defval = 5, title = ' Loc Extrema Len/Ticks ', minval = 1, inline = 'Stop Loss Local Extrema', group = '🔴 Stop Loss')
stopLossLocTicks = input.int(defval = 5, title = '', minval = 0, tooltip = 'Local extrema (minimum/maximum) within a window of length minus/plus some ticks to be used for the long/short stop loss.', inline = 'Stop Loss Local Extrema', group = '🔴 Stop Loss')
stopLossMoveActMode = input.string(defval = 'MULTI', title = 'Move Activation Mode', options = ['OFF', 'ONCE', 'MULTI'], tooltip = 'Move the stop loss to the entry price (a.k.a breakeven) when first take profit price target is reached (ONCE), or move the stop loss to the previous take profit target price every time a new take profit is reached (MULTI). Thus, more than one take profit targets are needed to see a difference between the two modes! If both move and trailing are activated the stop loss is defined by the more strict price at any given time.', group = '🔴 Stop Loss')
stopLossTrailingActMode = input.string(defval = 'TP', title = 'Trailing Activation Mode', options = ['OFF', 'START', 'TP'], tooltip = 'Enables the trailing for the stop loss from the very beginning of the entry order (START), or when the i-th take profit order is executed (TP) or not at all (OFF).', group = '🔴 Stop Loss')
stopLossTrailingTPActNum = input.int(defval = 3, title = ' Act Take Profit #', minval = 1, tooltip = 'The Take Profit number (from 1 up to the "Take Profit Targets" number set in TAKE PROFIT section) that will activate the trailing of the stop loss after it is reached. The Trailing Activation should be set to "TP".', group = '🔴 Stop Loss')
stopLossTrailingDistMode = input.string(defval = 'FIXED', title = 'Trailing Distance Mode', options = ['FIXED', 'STEP'], tooltip = 'When stop loss trailining is activated the distance will trail the price according to the stop loss method so it maintains its maximum value from the current high/low (FIXED), or the distance will change every time a new take profit is reached and will add the correspondig step value to the previous. Thus, more than one take profit targets are needed to see a difference between two modes! If both move and trailing are activated the stop loss is defined by the more strict price at any given time.', group = '🔴 Stop Loss')
longStepTrailingStopLossPerc = input.float(defval = 2.5, title = ' Step Long/Short Stop Loss %', minval = -100.0, maxval = 100.0, step = 0.05, inline = 'Stop Loss Perc Step', group = '🔴 Stop Loss') / 100.0
shortStepTrailingStopLossPerc = input.float(defval = 2.5, title = '', minval = -100.0, maxval = 100.0, step = 0.05, tooltip = 'The Step percentage of the price decrease/increase to set the stop loss price target for long/short positions after the first take profit target is reached.', inline = 'Stop Loss Perc Step', group = '🔴 Stop Loss') / 100.0
longStepStopLossAtrMul = input.float(defval = 1.0, title = ' Step ATR Long/Short Mul ', step = 0.1, inline = 'Stop Loss ATR Multiplier Step', group = '🔴 Stop Loss')
shortStepStopLossAtrMul = input.float(defval = 1.0, title = '', step = 0.1, tooltip = 'Step ATR multiplier to be used for the long/short Stop Loss after the first take profit target is reached.', inline = 'Stop Loss ATR Multiplier Step', group = '🔴 Stop Loss')
stepStopLossLocLen = input.int(defval = 4, title = ' Step Loc Extrema Len/Ticks', inline = 'Stop Loss Local Extrema Step', group = '🔴 Stop Loss')
stepStopLossLocTicks = input.int(defval = 0, title = '', tooltip = 'Step Local extrema (minimum/maximum) within a window of length minus/plus some ticks to be used for the long/short stop loss after the first take profit target is reached.', inline = 'Stop Loss Local Extrema Step', group = '🔴 Stop Loss')
// LOGIC ============================================================================================================
float stopLossLowestLow = ta.lowest(low, (stopLossTrailingDistMode == 'STEP' ? stopLossLocLen + longTrailingTakeProfitExecutedCount * stepStopLossLocLen : stopLossLocLen))
getLongStopLossEntryBaseScr() =>
switch stopLossMethod
'PERC' => entryPrice
'ATR' => entryPrice
'LOC' => stopLossLowestLow
=> na
getLongStopLossTrailingBaseScr() =>
switch stopLossMethod
'PERC' => high
'ATR' => high
'LOC' => stopLossLowestLow
=> na
getLongStopLossPrice(baseSrc) =>
switch stopLossMethod
'PERC' => baseSrc * (1 - (stopLossTrailingDistMode == 'STEP' ? longTrailingStopLossPerc + longTrailingTakeProfitExecutedCount * longStepTrailingStopLossPerc : longTrailingStopLossPerc))
'ATR' => baseSrc - (stopLossTrailingDistMode == 'STEP' ? longStopLossAtrMul + longTrailingTakeProfitExecutedCount * longStepStopLossAtrMul : longStopLossAtrMul) * longAtr
'LOC' => baseSrc - (stopLossTrailingDistMode == 'STEP' ? stopLossLocTicks + longTrailingTakeProfitExecutedCount * stepStopLossLocTicks : stopLossLocTicks) * syminfo.mintick
=> na
// trailing starts when the selected take profit price is reached if 'TP' mode is set or from the very begining if 'START' mode is selected
bool longStopLossTrailingEnabled = stopLossTrailingActMode == 'START' or (stopLossTrailingActMode == 'TP' and longIthTrailingTakeProfitExecuted(stopLossTrailingTPActNum - 1))
// calculate the stop loss price when enter long position and peserve its value until the position closes
var float longStopLossPrice = na
longStopLossPrice := if (longTrEntryIsActive or validOpenLongPosition or enteredLongPosition)
getLongStopLossPrice(getLongStopLossEntryBaseScr())
else if (longIsActive)
stopPrice = longStopLossTrailingEnabled ? getLongStopLossPrice(getLongStopLossTrailingBaseScr()) : longStopLossPrice[1]
stopPrice := (stopLossMoveActMode == 'ONCE' and longTrailingTakeProfitExecutedCount >= 1) or (stopLossMoveActMode == 'MULTI' and longTrailingTakeProfitExecutedCount == 1) ? math.max(stopPrice, entryPrice)
: stopLossMoveActMode == 'MULTI' and longTrailingTakeProfitExecutedCount > 1 ? math.max(stopPrice, array.get(longTakeProfitPrices, longTrailingTakeProfitExecutedCount - 2))
: stopPrice
math.max(stopPrice, nz(longStopLossPrice[1]))
else
na
float longStopLossPerc = (entryPrice - longStopLossPrice) / entryPrice
float stopLossHighestHigh = ta.highest(high, (stopLossTrailingDistMode == 'STEP' ? stopLossLocLen + shortTrailingTakeProfitExecutedCount * stepStopLossLocLen : stopLossLocLen))
getShortStopLossEntryBaseScr() =>
switch stopLossMethod
'PERC' => entryPrice
'ATR' => entryPrice
'LOC' => stopLossHighestHigh
getShortStopLossTrailingBaseScr() =>
switch stopLossMethod
'PERC' => low
'ATR' => low
'LOC' => stopLossHighestHigh
getShortStopLossPrice(baseSrc) =>
switch stopLossMethod
'PERC' => baseSrc * (1 + (stopLossTrailingDistMode == 'STEP' ? shortTrailingStopLossPerc + shortTrailingTakeProfitExecutedCount * shortStepTrailingStopLossPerc : shortTrailingStopLossPerc))
'ATR' => baseSrc + (stopLossTrailingDistMode == 'STEP' ? shortStopLossAtrMul + shortTrailingTakeProfitExecutedCount * shortStepStopLossAtrMul : shortStopLossAtrMul) * shortAtr
'LOC' => baseSrc + (stopLossTrailingDistMode == 'STEP' ? stopLossLocTicks + shortTrailingTakeProfitExecutedCount * stepStopLossLocTicks : stopLossLocTicks) * syminfo.mintick
=> na
// trailing starts when the selected take profit price is reached if 'TP' mode is set or from the very begining if 'START' mode is selected
bool shortStopLossTrailingEnabled = stopLossTrailingActMode == 'START' or (stopLossTrailingActMode == 'TP' and shortIthTrailingTakeProfitExecuted(stopLossTrailingTPActNum - 1))
// calculate the stop loss price when enter short position and peserve its value until the position closes
var float shortStopLossPrice = na
shortStopLossPrice := if (shortTrEntryIsActive or validOpenShortPosition or enteredShortPosition)
getShortStopLossPrice(getShortStopLossEntryBaseScr())
else if (shortIsActive)
stopPrice = shortStopLossTrailingEnabled ? getShortStopLossPrice(getShortStopLossTrailingBaseScr()) : shortStopLossPrice[1]
stopPrice := (stopLossMoveActMode == 'ONCE' and shortTrailingTakeProfitExecutedCount >= 1) or (stopLossMoveActMode == 'MULTI' and shortTrailingTakeProfitExecutedCount == 1) ? math.min(stopPrice, entryPrice)
: stopLossMoveActMode == 'MULTI' and shortTrailingTakeProfitExecutedCount > 1 ? math.min(stopPrice, array.get(shortTakeProfitPrices, shortTrailingTakeProfitExecutedCount - 2))
: stopPrice
math.min(stopPrice, nz(shortStopLossPrice[1], 999999.9))
else
na
float shortStopLossPerc = (shortStopLossPrice - entryPrice) / entryPrice
// PLOT =============================================================================================================
var stopLossColor = color.new(#e25141, 0)
plot(series = debugModeEnabled or na(longTrEntryPrice) ? longStopLossPrice : na, title = 'Long Stop Loss', color = stopLossColor, linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled or na(shortTrEntryPrice) ? shortStopLossPrice : na, title = 'Short Stop Loss', color = stopLossColor, linewidth = 1, style = plot.style_linebr, offset = 1)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🟢 TAKE PROFIT 2 =================================================================================================
// Description: Module responsible for the take profit logic implementation based on the method and the number of step take profit targets and the trailing deviation
// Dependencies: SHARED VARIABLES 2, ENTRY, TAKE PROFIT 1
// Results: longTakeProfitPrices, shortTakeProfitPrices
// INPUT ============================================================================================================
initTakeProfitMethod = input.string(defval = 'PERC', title = 'Init Take Profit Method', options = ['PERC', 'ATR', 'RR', 'LOC'], tooltip = 'The method to calculate the first take profit price target.', group = '🟢 Take Profit')
longInitTakeProfitPerc = input.float(defval = 10.0, title = ' Long/Short Init Take Profit %', minval = 0.05, step = 0.05, inline = 'Init Take Profit Perc', group = '🟢 Take Profit') / 100.0
shortInitTakeProfitPerc = input.float(defval = 10.0, title = '', minval = 0.05, step = 0.05, tooltip = 'The percentage of the price increase/decrease from the open price to set the first take profit price target for long/short positions.', inline = 'Init Take Profit Perc', group = '🟢 Take Profit') / 100.0
longInitTakeProfitAtrMul = input.float(defval = 6.0, title = ' Init ATR Long/Short Mul ', minval = 0.1, step = 0.1, inline = 'Init Take Profit ATR Multiplier', group = '🟢 Take Profit')
shortInitTakeProfitAtrMul = input.float(defval = 6.0, title = '', minval = 0.1, step = 0.1, tooltip = 'ATR multiplier to be used for the long/short first take profit target.', inline = 'Init Take Profit ATR Multiplier', group = '🟢 Take Profit')
longInitRiskRewardRatio = input.float(defval = 1.5, title = ' Init Long/Short RR Ratio ', minval = 0.1, step = 0.1, inline = 'Init Risk Reward Ratio', group = '🟢 Take Profit')
shortInitRiskRewardRatio = input.float(defval = 1.5, title = '', minval = 0.1, step = 0.1, tooltip = 'The Risk/Reward Ratio to be used for the long/short first take profit based on the stop loss price target.', inline = 'Init Risk Reward Ratio', group = '🟢 Take Profit')
takeProfitLocLen = input.int(defval = 14, title = ' Loc Extrema Len/Ticks ', minval = 1, inline = 'Take Profit Local Extrema', group = '🟢 Take Profit')
takeProfitLocTicks = input.int(defval = 5, title = '', minval = 0, tooltip = 'Local extrema (minimum/maximum) within a window of length minus/plus some ticks to be used for the long/short take profit.', inline = 'Take Profit Local Extrema', group = '🟢 Take Profit')
stepTakeProfitMethod = input.string(defval = 'PERC', title = 'Step Take Profit Method', options = ['PERC', 'ATR', 'RR'], tooltip = 'The method to calculate the additional step take profit price targets.', group = '🟢 Take Profit')
longStepTakeProfitPerc = input.float(defval = 5.0, title = ' Long/Short Step Take Profit %', minval = 0.05, step = 0.05, inline = 'Step Take Profit Perc', group = '🟢 Take Profit') / 100.0
shortStepTakeProfitPerc = input.float(defval = 5.0, title = '', minval = 0.05, step = 0.05, tooltip = 'The percentage of the price to be added/subtracted on top of the first take profit target for each additional step take profit price target for long/short positions.', inline = 'Step Take Profit Perc', group = '🟢 Take Profit') / 100.0
longStepTakeProfitAtrMul = input.float(defval = 3.0, title = ' Step ATR Long/Short Mul ', minval = 0.1, step = 0.1, inline = 'Step Take Profit ATR Multiplier', group = '🟢 Take Profit')
shortStepTakeProfitAtrMul = input.float(defval = 3.0, title = '', minval = 0.1, step = 0.1, tooltip = 'ATR multiplier to be added/subtracted on top of the first take profit target for each additional step take profit price target for the long/short positions.', inline = 'Step Take Profit ATR Multiplier', group = '🟢 Take Profit')
longStepRiskRewardRatio = input.float(defval = 1.0, title = ' Step Long/Short RR Ratio ', minval = 0.1, step = 0.1, inline = 'Step Risk Reward Ratio', group = '🟢 Take Profit')
shortStepRiskRewardRatio = input.float(defval = 1.0, title = '', minval = 0.1, step = 0.1, tooltip = 'The Risk/Reward Ratio to be added/subtracted to the first take profit target for each additional step take profit price target for the long/short positions.', inline = 'Step Risk Reward Ratio', group = '🟢 Take Profit')
takeProfitTrailingEnabled = input.bool(defval = true, title = 'Enable Trailing⚠️🎨️', tooltip = 'Enable or disable the trailing for take profit. WARNING! This feature will repaint. Make sure you use it along with "Bar Magnifier" and "Deep Backtesting" for realistic backtest results.', group = '🟢 Take Profit')
devTakeProfitMethod = input.string(defval = 'PERC', title = ' Deviation Method', options = ['PERC', 'ATR'], tooltip = 'The method to calculate the deviation for the trailing take profit.', group = '🟢 Take Profit')
devTakeProfitPerc = input.float(defval = 1.0, title = ' Deviation %', minval = 0.01, maxval = 100.0, step = 0.05, tooltip = 'The percentage wise step to be used for following the price, when the take profit target is reached.', group = '🟢 Take Profit') / 100.0
devTakeProfitAtrMul = input.float(defval = 0.01, title = ' Deviation ATR Mul', minval = 0.01, step = 0.05, tooltip = 'Multiplier to be used on the ATR to calculate the step for following the price, when the take profit target is reached.', group = '🟢 Take Profit')
// LOGIC ============================================================================================================
getTrailingOffsetTicks(takeProfitPrice, atr) =>
switch devTakeProfitMethod
'PERC' => takeProfitPrice * devTakeProfitPerc / syminfo.mintick
'ATR' => devTakeProfitAtrMul * atr / syminfo.mintick
=> na
float takeProfitHighestHigh = ta.highest(high, takeProfitLocLen)
getLongInitTakeProfitBaseScr() =>
switch initTakeProfitMethod
'PERC' => entryPrice
'ATR' => entryPrice
'RR' => entryPrice
'LOC' => takeProfitHighestHigh
=> na
getLongTakeProfitPrice(baseSrc, takeProfitMethod, takeProfitPerc, takeProfitAtrMul, riskRewardRatio) =>
switch takeProfitMethod
'PERC' => baseSrc * (1 + takeProfitPerc)
'ATR' => baseSrc + takeProfitAtrMul * longAtr
'RR' => baseSrc + riskRewardRatio * (entryPrice - longStopLossPrice)
'LOC' => baseSrc - takeProfitLocTicks * syminfo.mintick
=> na
// calculate take profit prices when enter long position and peserve their values until the entire position closes
for [i, takeProfitPrice] in longTakeProfitPrices
longTakeProfitPerc = i * longStepTakeProfitPerc
longTakeProfitAtrMul = i * longStepTakeProfitAtrMul
longRiskRewardRatio = i * longStepRiskRewardRatio
if (longTrEntryIsActive or validOpenLongPosition or enteredLongPosition)
array.set(longTakeProfitPrices, i, getLongTakeProfitPrice(getLongTakeProfitPrice(getLongInitTakeProfitBaseScr(), initTakeProfitMethod, longInitTakeProfitPerc, longInitTakeProfitAtrMul, longInitRiskRewardRatio), stepTakeProfitMethod, longTakeProfitPerc, longTakeProfitAtrMul, longRiskRewardRatio))
else if (longIsActive)
array.set(longTakeProfitPrices, i, nz(takeProfitPrice, getLongTakeProfitPrice(getLongTakeProfitPrice(getLongInitTakeProfitBaseScr(), initTakeProfitMethod, longInitTakeProfitPerc, longInitTakeProfitAtrMul, longInitRiskRewardRatio), stepTakeProfitMethod, longTakeProfitPerc, longTakeProfitAtrMul, longRiskRewardRatio)))
else
array.set(longTakeProfitPrices, i, na)
var longTrailingTakeProfitOffsetTicks = array.new<float>(numOfTakeProfitTargets, na)
if (longPositionEntry)
for [i, takeProfitPrice] in longTakeProfitPrices
array.set(longTrailingTakeProfitOffsetTicks, i, getTrailingOffsetTicks(takeProfitPrice, longAtr))
float takeProfitLowestLow = ta.lowest(low, takeProfitLocLen)
getShortInitTakeProfitBaseScr() =>
switch initTakeProfitMethod
'PERC' => entryPrice
'ATR' => entryPrice
'RR' => entryPrice
'LOC' => takeProfitLowestLow
=> na
getShortTakeProfitPrice(baseSrc, takeProfitMethod, takeProfitPerc, takeProfitAtrMul, riskRewardRatio) =>
switch takeProfitMethod
'PERC' => baseSrc * (1 - takeProfitPerc)
'ATR' => baseSrc - takeProfitAtrMul * shortAtr
'RR' => baseSrc - riskRewardRatio * (shortStopLossPrice - entryPrice)
'LOC' => baseSrc + takeProfitLocTicks * syminfo.mintick
=> na
// calculate take profit prices when enter short position and peserve their values until the entire position closes
for [i, takeProfitPrice] in shortTakeProfitPrices
shortTakeProfitPerc = i * shortStepTakeProfitPerc
shortTakeProfitAtrMul = i * shortStepTakeProfitAtrMul
shortRiskRewardRatio = i * shortStepRiskRewardRatio
if (shortTrEntryIsActive or validOpenShortPosition or enteredShortPosition)
array.set(shortTakeProfitPrices, i, getShortTakeProfitPrice(getShortTakeProfitPrice(getShortInitTakeProfitBaseScr(), initTakeProfitMethod, shortInitTakeProfitPerc, shortInitTakeProfitAtrMul, shortInitRiskRewardRatio), stepTakeProfitMethod, shortTakeProfitPerc, shortTakeProfitAtrMul, shortRiskRewardRatio))
else if (shortIsActive)
array.set(shortTakeProfitPrices, i, nz(takeProfitPrice, getShortTakeProfitPrice(getShortTakeProfitPrice(getShortInitTakeProfitBaseScr(), initTakeProfitMethod, shortInitTakeProfitPerc, shortInitTakeProfitAtrMul, shortInitRiskRewardRatio), stepTakeProfitMethod, shortTakeProfitPerc, shortTakeProfitAtrMul, shortRiskRewardRatio)))
else
array.set(shortTakeProfitPrices, i, na)
var shortTrailingTakeProfitOffsetTicks = array.new<float>(numOfTakeProfitTargets, na)
if (shortPositionEntry)
for [i, takeProfitPrice] in shortTakeProfitPrices
array.set(shortTrailingTakeProfitOffsetTicks, i, getTrailingOffsetTicks(takeProfitPrice, shortAtr))
// PLOT =============================================================================================================
var takeProfitColor = color.new(#419388, 0)
var winColor = color.new(color.lime, 0)
updateLines(lines, prices, trasp) =>
for [i, ln] in lines
price = array.get(prices, i)
if (not na(price) and not array.get(trasp, i))
line.set_y1(ln, price)
line.set_y2(ln, price)
line.set_x2(ln, bar_index + 1)
moveAllElements(fromArr, toArr) =>
len = array.size(fromArr) - 1
for i = 0 to len >=0 ? len : na
array.push(toArr, array.pop(fromArr))
var allLongTakeProfitLines = array.new<line>()
var currentLongTakeProfitLines = array.new<line>()
if (longIsActive)
if (longPositionEntry)
moveAllElements(currentLongTakeProfitLines, allLongTakeProfitLines)
for [i, takeProfitPrice] in longTakeProfitPrices
pa.push(currentLongTakeProfitLines, line.new(x1 = entryBarId + 1, y1 = takeProfitPrice, x2 = bar_index + 1, y2 = takeProfitPrice, xloc = xloc.bar_index, extend = extend.none, color = color.from_gradient(i, 0, numOfTakeProfitTargets, takeProfitColor, winColor), style = line.style_solid, width = 1), numOfTakeProfitTargets)
updateLines(currentLongTakeProfitLines, longTakeProfitPrices, longTrailingTakeProfitExecuted)
var allShortTakeProfitLines = array.new<line>()
var currentShortTakeProfitLines = array.new<line>()
if (shortIsActive)
if (shortPositionEntry)
moveAllElements(currentShortTakeProfitLines, allShortTakeProfitLines)
for [i, takeProfitPrice] in shortTakeProfitPrices
pa.push(currentShortTakeProfitLines, line.new(x1 = entryBarId + 1, y1 = takeProfitPrice, x2 = bar_index + 1, y2 = takeProfitPrice, xloc = xloc.bar_index, extend = extend.none, color = color.from_gradient(i, 0, numOfTakeProfitTargets, takeProfitColor, winColor), style = line.style_solid, width = 1), numOfTakeProfitTargets)
updateLines(currentShortTakeProfitLines, shortTakeProfitPrices, shortTrailingTakeProfitExecuted)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 💰 QUANTITY/RISK MANAGEMENT ======================================================================================
// Description: Module responsible for the calculation of the quantity percentage that will be used on each entry
// Dependencies: SHARED VARIABLES 2, ENTRY, SHARED VARIABLES 3, EXIT
// Results: longEntryBaseQuantity, shortEntryBaseQuantity, longRemainingQuantityPerc, shortRemainingQuantityPerc
import jason5480/math_utils/3 as mu
// INPUT ============================================================================================================
allTakeProfitsQuantityPerc = input.float(defval = 100.0, title = 'Take Profit Quantity %', minval = 0.0, maxval = 100.0, step = 0.5, tooltip = 'The percentage of the position that will be withdrawn when ALL the take profit price targets are reached. If more than one take profit target is set, then ALL targets will share equally this amount and exit accordingly.', group = '💰 Quantity/Risk Management') / 100.0
quantityMethod = input.string(defval = 'RISK', title = 'Quantity Method', options = ['RISK', 'EQUITY'], tooltip = 'The method to calculate the quantity to enter each new position.', group = '💰 Quantity/Risk Management')
riskPerc = input.float(defval = 2.0, title = ' Capital at Risk %', minval = 1.0, maxval = 100.0, tooltip = 'The maximum percentage of the equity to risk in every trade when no leverage is used.', group = '💰 Quantity/Risk Management') / 100.0
equityPerc = input.float(defval = 20.0, title = ' Equity % ', minval = 1.0, maxval = 100.0, tooltip = 'The percentage of the equity to enter in every trade when no leverage is used.', group = '💰 Quantity/Risk Management') / 100.0
minTrade = input.int(defval = 10, title = 'Minimum Trade Price', minval = 1, tooltip = 'The minimum trade price in quote currency that is allowed in the exchange for a valid new position.', group = '💰 Quantity/Risk Management')
longLeverage = input.int(defval = 1, title = 'Leverage Long/Short ', minval = 1, inline = 'Leverage', group = '💰 Quantity/Risk Management')
shortLeverage = input.int(defval = 1, title = '', minval = 1, tooltip = 'Leverage factor used to multiply the initial risk quantity of each trade (by borrowing the remaining amount). Thus, the profits and losses are multiplied respectivelly.', inline = 'Leverage', group = '💰 Quantity/Risk Management')
// LOGIC ============================================================================================================
var float epsilon = 0.0001
var float takeProfitQuantityPerc = numOfTakeProfitTargets == 0 ? 0 : mu.clamp_up((allTakeProfitsQuantityPerc / numOfTakeProfitTargets) + epsilon, 0.0, 1.0, 4)
var int quoteDecimalDigits = mu.num_of_decimal_digits(syminfo.mintick * syminfo.pointvalue)
getLongRiskQuoteQuantity() =>
mu.clamp_down(strategy.equity * riskPerc * longLeverage / longStopLossPerc, minTrade, strategy.equity * longLeverage, quoteDecimalDigits)
getLongEquityQuoteQuantity() =>
mu.clamp_down(strategy.equity * equityPerc * longLeverage, minTrade, strategy.equity * longLeverage, quoteDecimalDigits)
float longQuoteQuantity = switch quantityMethod
'RISK' => getLongRiskQuoteQuantity()
'EQUITY' => getLongEquityQuoteQuantity()
=> na
getLongQuoteQuantityPerc() =>
longQuoteQuantity / strategy.equity
float longEntryQuoteQuantityPerc = na
longEntryQuoteQuantityPerc := if (longTrEntryIsActive or validOpenLongPosition)
getLongQuoteQuantityPerc()
else if (longIsActive)
nz(longEntryQuoteQuantityPerc[1], getLongQuoteQuantityPerc())
else
na
getLongBaseQuantity() =>
longQuoteQuantity / entryPrice
var float longEntryBaseQuantity = na
longEntryBaseQuantity := if (longTrEntryIsActive or validOpenLongPosition)
getLongBaseQuantity()
else if (longIsActive)
nz(longEntryBaseQuantity[1], getLongBaseQuantity())
else
na
getLongRisk() =>
switch quantityMethod
'RISK' => riskPerc
'EQUITY' => equityPerc * longStopLossPerc
=>na
var float longRisk = na
longRisk := if (longTrEntryIsActive or validOpenLongPosition)
getLongRisk()
else if (longIsActive)
nz(longRisk[1], getLongRisk())
else
na
float longRemainingQuantityPerc = 1 - longTrailingTakeProfitExecutedCount * takeProfitQuantityPerc
getShortRiskQuoteQuantity() =>
mu.clamp_down(strategy.equity * riskPerc * shortLeverage / shortStopLossPerc, minTrade, strategy.equity * shortLeverage, quoteDecimalDigits)
getShortEquityQuoteQuantity() =>
mu.clamp_down(strategy.equity * equityPerc * shortLeverage, minTrade, strategy.equity * shortLeverage, quoteDecimalDigits)
float shortQuoteQuantity = switch quantityMethod
'RISK' => getShortRiskQuoteQuantity()
'EQUITY' => getShortEquityQuoteQuantity()
=> na
getShortQuoteQuantityPerc() =>
shortQuoteQuantity / strategy.equity
float shortEntryQuoteQuantityPerc = na
shortEntryQuoteQuantityPerc := if (shortTrEntryIsActive or validOpenShortPosition)
getShortQuoteQuantityPerc()
else if (shortIsActive)
nz(shortEntryQuoteQuantityPerc[1], getShortQuoteQuantityPerc())
else
na
getShortBaseQuantity() =>
shortQuoteQuantity / entryPrice
var float shortEntryBaseQuantity = na
shortEntryBaseQuantity := if (shortTrEntryIsActive or validOpenShortPosition)
getShortBaseQuantity()
else if (shortIsActive)
nz(shortEntryBaseQuantity[1], getShortBaseQuantity())
else
na
getShortRisk() =>
switch quantityMethod
'RISK' => riskPerc
'EQUITY' => equityPerc * shortStopLossPerc
=>na
var float shortRisk = na
shortRisk := if (shortTrEntryIsActive or validOpenShortPosition)
getShortRisk()
else if (shortIsActive)
nz(shortRisk[1], getShortRisk())
else
na
float shortRemainingQuantityPerc = 1 - shortTrailingTakeProfitExecutedCount * takeProfitQuantityPerc
// PLOT =============================================================================================================
var buyColor = color.new(color.green, 0)
var sellColor = color.new(color.red, 0)
var textColor = color.new(color.white, 0)
if (validOpenLongPosition)
label.new(x = bar_index, y = na, text = 'Buy', yloc = yloc.belowbar, color = buyColor, style = label.style_label_up, textcolor = textColor)
if (validOpenShortPosition)
label.new(x = bar_index, y = na, text = 'Sell', yloc = yloc.abovebar, color = sellColor, style = label.style_label_down, textcolor = textColor)
if (validCloseShortPosition)
label.new(x = bar_index, y = na, text = 'Buy', yloc = yloc.belowbar, color = buyColor, style = label.style_label_up, textcolor = textColor)
if (validCloseLongPosition)
label.new(x = bar_index, y = na, text = 'Sell', yloc = yloc.abovebar, color = sellColor, style = label.style_label_down, textcolor = textColor)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 📊 STATS =========================================================================================================
// Description: Module responsible for plotting additional information for statistics and backtesting purposes
// Dependencies: SHARED VARIABLES 1, ENTRY
// Results: NONE
// INPUT ============================================================================================================
showStatsTable = input.bool(defval = false, title = 'Show Stats Table⚠️🐌', inline = 'Stats Table', group = '📊 Stats')
statsTablePos = input.string(defval = 'TR', title = '', options = ['TR', 'TL', 'BR', 'BL'], tooltip = 'Show the table with the overall trade statistics on the selected corner. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', inline = 'Stats Table', group = '📊 Stats')
showTradeStats = input.bool(defval = false, title = 'Show Trade Stats⚠️🐌', tooltip = 'Show the winning/losing trade stats when complete. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '📊 Stats')
// LOGIC ============================================================================================================
getTablePosition(pos) =>
switch pos
'TR' => position.top_right
'TL' => position.top_left
'BR' => position.bottom_right
'BL' => position.bottom_left
=> position.middle_center
float currentLongProfitPerc = if ((longIsActive or longPositionCompleted) and (showStatsTable or showTradeStats))
float profit = 0.0
for [i, takeProfitPrice] in longTakeProfitPrices
if (array.get(longTrailingTakeProfitExecuted, i))
float longTakeProfitPerc = (takeProfitPrice - entryPrice) / entryPrice
profit += longTakeProfitPerc * takeProfitQuantityPerc
profit -= (validCloseLongPosition ? (entryPrice - close) / entryPrice : longStopLossPerc) * longRemainingQuantityPerc
else
na
float currentShortProfitPerc = if ((shortIsActive or shortPositionCompleted) and (showStatsTable or showTradeStats))
float profit = 0.0
for [i, takeProfitPrice] in shortTakeProfitPrices
if (array.get(shortTrailingTakeProfitExecuted, i))
float shortTakeProfitPerc = (entryPrice - takeProfitPrice) / entryPrice
profit += shortTakeProfitPerc * takeProfitQuantityPerc
profit -= (validCloseShortPosition ? (close - entryPrice) / entryPrice : shortStopLossPerc) * shortRemainingQuantityPerc
else
na
// Store the winning/losing profits
var winTradeProfits = array.new<float>()
var lossTradeProfits = array.new<float>()
var winTradeWProfits = array.new<float>()
var lossTradeWProfits = array.new<float>()
var float winQuantityPercSum = 0.0
var float lossQuantityPercSum = 0.0
// Count winning/losing streaks
var int maxWinningStreak = 0
var int maxLosingStreak = 0
var int currentWinningStreak = 0
var int currentLosingStreak = 0
if (showStatsTable and longPositionCompleted)
float longProfit = 100.0 * currentLongProfitPerc[1]
float longQuantityPerc = longEntryQuoteQuantityPerc[1]
float longWProfit = longProfit * longQuantityPerc
bool isWin = longProfit > 0
if (isWin)
array.push(winTradeProfits, longProfit)
array.push(winTradeWProfits, longWProfit)
winQuantityPercSum := winQuantityPercSum + longQuantityPerc
currentWinningStreak := currentWinningStreak + 1
currentLosingStreak := 0
else
array.push(lossTradeProfits, longProfit)
array.push(lossTradeWProfits, longWProfit)
lossQuantityPercSum := lossQuantityPercSum + longQuantityPerc
currentLosingStreak := currentLosingStreak + 1
currentWinningStreak := 0
maxWinningStreak := math.max(currentWinningStreak, maxWinningStreak)
maxLosingStreak := math.max(currentLosingStreak, maxLosingStreak)
if (showStatsTable and shortPositionCompleted)
float shortProfit = 100.0 * currentShortProfitPerc[1]
float shortQuantityPerc = shortEntryQuoteQuantityPerc[1]
float shortWProfit = shortProfit * shortQuantityPerc
bool isWin = shortProfit > 0
if (isWin)
array.push(winTradeProfits, shortProfit)
array.push(winTradeWProfits, shortWProfit)
winQuantityPercSum := winQuantityPercSum + shortQuantityPerc
currentWinningStreak := currentWinningStreak + 1
currentLosingStreak := 0
else
array.push(lossTradeProfits, shortProfit)
array.push(lossTradeWProfits, shortWProfit)
lossQuantityPercSum := lossQuantityPercSum + shortQuantityPerc
currentLosingStreak := currentLosingStreak + 1
currentWinningStreak := 0
maxWinningStreak := math.max(currentWinningStreak, maxWinningStreak)
maxLosingStreak := math.max(currentLosingStreak, maxLosingStreak)
// PLOT =============================================================================================================
if (showStatsTable and (barstate.islastconfirmedhistory or barstate.islast))
var txtColor = color.new(color.gray, 0)
var bgColor = color.new(color.black, 95)
var winBgColor = color.new(color.green, 95)
var lossBgColor = color.new(color.red, 95)
var statsTable = table.new(position = getTablePosition(statsTablePos), columns = 4, rows = 6, frame_color = color.new(color.black, 0), frame_width = 2, border_color = color.new(color.black, 0), border_width = 1)
// horizontal headers
table.cell(table_id = statsTable, column = 0, row = 0, bgcolor = color.new(color.black, 95))
table.cell(table_id = statsTable, column = 1, row = 0, text = 'All', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'In contrast to the TV backtest engine, a trade is considered to be complete when the entire entry position was exited and not when a move is done (e.g. a take profit target is reached).')
table.cell(table_id = statsTable, column = 2, row = 0, text = 'Wins', text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor, tooltip = 'The winning trades.')
table.cell(table_id = statsTable, column = 3, row = 0, text = 'Losses', text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor, tooltip = 'The losing trades.')
// vertical headers
table.cell(table_id = statsTable, column = 0, row = 1, text = 'Trades', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'The total number of trades and those that had a winning/losing outcome.')
table.cell(table_id = statsTable, column = 0, row = 2, text = 'Streak', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'The maximum consecutive number of wins/loses in a row.')
table.cell(table_id = statsTable, column = 0, row = 3, text = 'Rate', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'The percent of the wins/losses.')
table.cell(table_id = statsTable, column = 0, row = 4, text = 'Avg Profit', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'The average profit (percentagewise) per trade of all/winning/losing trades.')
table.cell(table_id = statsTable, column = 0, row = 5, text = 'WAvg Profit', text_color = txtColor, text_size = size.normal, bgcolor = bgColor, tooltip = 'The weighted average profit (percentagewise) per trade of all/winning/losing trades normalized by the entry quantity percentage. Thus the result is non-compound.')
// Trades
int numOfWins = array.size(winTradeProfits)
int numOfLosses = array.size(lossTradeProfits)
int numOfTrades = numOfWins + numOfLosses
table.cell(table_id = statsTable, column = 1, row = 1, text = str.tostring(numOfTrades), text_color = txtColor, text_size = size.normal, bgcolor = bgColor)
table.cell(table_id = statsTable, column = 2, row = 1, text = str.tostring(numOfWins), text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor)
table.cell(table_id = statsTable, column = 3, row = 1, text = str.tostring(numOfLosses), text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor)
// Streak
table.cell(table_id = statsTable, column = 1, row = 2, bgcolor = bgColor)
table.cell(table_id = statsTable, column = 2, row = 2, text = str.tostring(maxWinningStreak), text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor)
table.cell(table_id = statsTable, column = 3, row = 2, text = str.tostring(maxLosingStreak), text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor)
// Rate
float winRate = 100.0 * numOfWins / numOfTrades
float loseRate = 100.0 * numOfLosses / numOfTrades
table.cell(table_id = statsTable, column = 1, row = 3, text = str.tostring(winRate + loseRate, format.percent), text_color = txtColor, text_size = size.normal, bgcolor = bgColor)
table.cell(table_id = statsTable, column = 2, row = 3, text = str.tostring(winRate, format.percent), text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor)
table.cell(table_id = statsTable, column = 3, row = 3, text = str.tostring(loseRate, format.percent), text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor)
// Avg Profit
float winAvgProfit = array.avg(winTradeProfits)
float loseAvgProfit = array.avg(lossTradeProfits)
float avgProfit = (winAvgProfit * numOfWins + loseAvgProfit * numOfLosses) / numOfTrades
table.cell(table_id = statsTable, column = 1, row = 4, text = str.tostring(avgProfit, format.percent), text_color = avgProfit > 0 ? takeProfitColor : stopLossColor, text_size = size.normal, bgcolor = bgColor)
table.cell(table_id = statsTable, column = 2, row = 4, text = str.tostring(winAvgProfit, format.percent), text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor)
table.cell(table_id = statsTable, column = 3, row = 4, text = str.tostring(loseAvgProfit, format.percent), text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor)
// WAvg Profit
float winWAvgProfit = array.sum(winTradeWProfits) / winQuantityPercSum
float lossWAvgProfit = array.sum(lossTradeWProfits) / lossQuantityPercSum
float wavgProfit = (winWAvgProfit * numOfWins + lossWAvgProfit * numOfLosses) / numOfTrades
table.cell(table_id = statsTable, column = 1, row = 5, text = str.tostring(wavgProfit, format.percent), text_color = wavgProfit > 0 ? takeProfitColor : stopLossColor, text_size = size.normal, bgcolor = bgColor)
table.cell(table_id = statsTable, column = 2, row = 5, text = str.tostring(winWAvgProfit, format.percent), text_color = takeProfitColor, text_size = size.normal, bgcolor = winBgColor)
table.cell(table_id = statsTable, column = 3, row = 5, text = str.tostring(lossWAvgProfit, format.percent), text_color = stopLossColor, text_size = size.normal, bgcolor = lossBgColor)
if (showTradeStats and longPositionCompleted)
float longProfit = 100.0 * currentLongProfitPerc[1]
bool isWin = longProfit > 0
string longProfitStr = str.tostring(longProfit, format.percent)
int takeProfitsExecuted = longTrailingTakeProfitExecutedCount[1]
float stopLossPrice = longStopLossPrice[1]
float tradePrice = isWin and longTrailingTakeProfitExecutedCount[1] > 0 ? math.max(array.get(longTakeProfitPrices[1], takeProfitsExecuted - 1), stopLossPrice) : validCloseLongPosition ? close : stopLossPrice
line.new(x1 = entryBarId + 1, y1 = tradePrice, x2 = bar_index, y2 = tradePrice, xloc = xloc.bar_index, extend = extend.none, color = isWin ? winColor : stopLossColor, style = line.style_arrow_both, width = 1)
label.new(x = int((entryBarId + bar_index + 1) / 2), y = tradePrice, text = isWin ? 'Win\n+' + longProfitStr : 'Loss\n' + longProfitStr, yloc = yloc.price, color = isWin ? winColor : stopLossColor, style = isWin ? label.style_label_down : label.style_label_up, textcolor = textColor, size = size.large, tooltip = 'Number of Take Profit targets executed: ' + str.tostring(takeProfitsExecuted))
if (showTradeStats and shortPositionCompleted)
float shortProfit = 100.0 * currentShortProfitPerc[1]
bool isWin = shortProfit > 0
string shortProfitStr = str.tostring(shortProfit, format.percent)
int takeProfitsExecuted = shortTrailingTakeProfitExecutedCount[1]
float stopLossPrice = shortStopLossPrice[1]
float tradePrice = isWin and shortTrailingTakeProfitExecutedCount[1] > 0 ? math.min(array.get(shortTakeProfitPrices[1], takeProfitsExecuted - 1), stopLossPrice) : validCloseShortPosition ? close : stopLossPrice
line.new(x1 = entryBarId + 1, y1 = tradePrice, x2 = bar_index, y2 = tradePrice, xloc = xloc.bar_index, extend = extend.none, color = isWin ? winColor : stopLossColor, style = line.style_arrow_both, width = 1)
label.new(x = int((entryBarId + bar_index + 1) / 2), y = tradePrice, text = isWin ? 'Win\n+' + shortProfitStr : 'Loss\n' + shortProfitStr, yloc = yloc.price, color = isWin ? winColor : stopLossColor, style = isWin ? label.style_label_up : label.style_label_down, textcolor = textColor, size = size.large, tooltip = 'Number of Take Profit targets executed: ' + str.tostring(takeProfitsExecuted))
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ☭ SHARED VARIABLES 4 =============================================================================================
// Description: Module responsible for conditional variables to enter and exit orders
// Dependencies: SHARED VARIABLES 2, EXIT
// Results: ALL
// LOGIC ============================================================================================================
bool doCloseLong = not exitTrailingEnabled and closeLongPosition
bool doCloseShort = not exitTrailingEnabled and closeShortPosition
bool doSLorExitTrLong = longTrExitIsActive or longIsActive
bool doSLorExitTrShort = shortTrExitIsActive or shortIsActive
bool doCloseAll = closeAtSessionEnd and not timeFilterApproval
bool doCnlTrEntryLong = entryTrailingEnabled and longTrEntryIsActive and closeLongPosition
bool doCnlTrEntryShort = entryTrailingEnabled and shortTrEntryIsActive and closeShortPosition
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🔔 ALERT MESSAGES ================================================================================================
// Description: Module responsible for the message outputs when strategy orders are being executed
// Dependencies: ENTRY, TAKE PROFIT 1, EXIT, STOP LOSS, TAKE PROFIT 2, QUANTITY/RISK MANAGEMENT, SHARED VARIABLES 4
// Results: ALL
import jason5480/string_variables/8 as sv
// INPUT ============================================================================================================
tickerPrefix = input.string(defval = '', title = 'Ticker Prefix/Postfix', inline = 'Ticker', group = '🔔 Alert Messages')
tickerPostfix = input.string(defval = '', title = '', tooltip = 'Prepend/Append a custom string to the TVs ticker when you use {@{TICKER}} as variable in the alert messages.', inline = 'Ticker', group = '🔔 Alert Messages')
usePercRange100 = input.bool(defval = true, title = 'Percentage Range [0, 100]', tooltip = 'Enable to multiply all the percentatges with 100 so that the values range from [0, 100] instead of [0, 1].', group = '🔔 Alert Messages')
marginTicks = input.int(defval = 5, title = 'Margin Ticks', minval = 1, tooltip = 'The ticks that will be added/substracted from entry/exit price to calculate the {${ENTRY+}}, {${ENTRY-}}, {${EXIT+}}, {${EXIT-}} variables. Those values could be used if you want to send a limit order to your exchange and the price might go to the "wrong" side due to high volatility of the market and delay between the time of computation and execution.', group = '🔔 Alert Messages')
msgEventsEnabled = input.bool(defval = false, title = 'Enable All Event Alerts⚠️🐌', tooltip = 'Enable the alert messages that correspond to "order fills" of the strategy (e.g. entry, exit, close, take profit and stop loss). WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '🔔 Alert Messages')
rawMsgEnteredLong = input.text_area(defval = 'Long Entry({@{TICKER}}): Bought at market price of {${ENTRY}} {@{QUOTE}} an amount equal to {#{LBQ}} {@{BASE}} (for {${LQQ}} {@{QUOTE}}) and a risk of {#{LRISK}}%. The stop loss was placed at {${LSL}} {@{QUOTE}} and take profit targets at [{${LTP1}}, {${LTP2}}, {${LTP3}}, {${LTP4}}, {${LTP5}}] {@{QUOTE}}', title = 'Enter Long', tooltip = 'Alert message emited when entered long position. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgEnteredShort = input.text_area(defval = 'Short Entry({@{TICKER}}): Sold at market price of {${ENTRY}} {@{QUOTE}} an amount equal to {#{SBQ}} {@{BASE}} (for {${SQQ}} {@{QUOTE}}) and a risk of {#{SRISK}}%. The stop loss was placed at {${SSL}} {@{QUOTE}} and take profit targets at [{${STP1}}, {${STP2}}, {${STP3}}, {${STP4}}, {${STP5}}] {@{QUOTE}}', title = 'Enter Short', tooltip = 'Alert message emited when entered short position. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgEnteredTrLong = input.text_area(defval = 'Long Entry({@{TICKER}}): Bought with trailing stop-limit entry order at price of {${ENTRY}} {@{QUOTE}} an amount equal to {#{LBQ}} {@{BASE}} and a risk of {#{LRISK}}%. The stop loss was placed at {${LSL}} {@{QUOTE}} and take profit targets at [{${LTP1}}, {${LTP2}}, {${LTP3}}, {${LTP4}}, {${LTP5}}] {@{QUOTE}}', title = 'Enter Tr Long', tooltip = 'Alert message emited when entered long position with trailing stop order. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgEnteredTrShort = input.text_area(defval = 'Short Entry({@{TICKER}}): Sold with trailing stop-limit entry order at price of {${ENTRY}} {@{QUOTE}} an amount equal to {#{SBQ}} {@{BASE}} and a risk of {#{SRISK}}%. The stop loss was placed at {${SSL}} {@{QUOTE}} and take profit targets at [{${STP1}}, {${STP2}}, {${STP3}}, {${STP4}}, {${STP5}}] {@{QUOTE}}', title = 'Enter Tr Short', tooltip = 'Alert message emited when entered short position with trailing stop order. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgClosedLong = input.text_area(defval = 'Long Close({@{TICKER}}): Sold at market price of {${EXIT}} {@{QUOTE}} {#{LRQP}}% of the initial position', title = 'Close Long', tooltip = 'Alert message emited when closed long position. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgClosedShort = input.text_area(defval = 'Short Close({@{TICKER}}): Bought at market price of {${EXIT}} {@{QUOTE}} {#{SRQP}}% of the initial position', title = 'Close Short', tooltip = 'Alert message emited when closed short position. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgExitedTrLong = input.text_area(defval = 'Long Exit({@{TICKER}}): Sold with trailing stop-limit exit order at price of {${EXIT}} {@{QUOTE}} {#{LRQP}}% of the initial position', title = 'Exit Tr Long', tooltip = 'Alert message emited when exited long position with trailing stop order. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgExitedTrShort = input.text_area(defval = 'Short Exit({@{TICKER}}): Bought with trailing stop-limit exit order at price of {${EXIT}} {@{QUOTE}} {#{SRQP}}% of the initial position', title = 'Exit Tr Short', tooltip = 'Alert message emited when exited short position with trailing stop order. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
var rawMsgRchTPLong = array.new<string>(5, na)
var rawMsgRchTPShort = array.new<string>(5, na)
array.set(rawMsgRchTPLong, 0, input.text_area(defval = 'Long Exit({@{TICKER}}): Take Profit 1 executed at price of {${LTP1}} {@{QUOTE}} and sold {#{TPQP}}% of the initial position, that equals to an amount of {#{LTPBQ}} {@{BASE}}', title = 'TP1 Long', tooltip = 'Alert message emited when the first quantity take profit target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPShort, 0, input.text_area(defval = 'Short Exit({@{TICKER}}): Take Profit 1 executed at price of {${STP1}} {@{QUOTE}} and bought {#{TPQP}}% of the initial position, that equals to an amount of {#{STPBQ}} {@{BASE}}', title = 'TP1 Short', tooltip = 'Alert message emited when the first quantity take profit target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPLong, 1, input.text_area(defval = 'Long Exit({@{TICKER}}): Take Profit 2 executed at price of {${LTP2}} {@{QUOTE}} and sold {#{TPQP}}% of the initial position, that equals to an amount of {#{LTPBQ}} {@{BASE}}', title = 'TP2 Long', tooltip = 'Alert message emited when the second quantity take profit target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPShort, 1, input.text_area(defval = 'Short Exit({@{TICKER}}): Take Profit 2 executed at price of {${STP2}} {@{QUOTE}} and bought {#{TPQP}}% of the initial position, that equals to an amount of {#{STPBQ}} {@{BASE}}', title = 'TP2 Short', tooltip = 'Alert message emited when the second quantity take profit target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPLong, 2, input.text_area(defval = 'Long Exit({@{TICKER}}): Take Profit 3 executed at price of {${LTP3}} {@{QUOTE}} and sold {#{TPQP}}% of the initial position, that equals to an amount of {#{LTPBQ}} {@{BASE}}', title = 'TP3 Long', tooltip = 'Alert message emited when the third quantity take profit target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPShort, 2, input.text_area(defval = 'Short Exit({@{TICKER}}): Take Profit 3 executed at price of {${STP3}} {@{QUOTE}} and bought {#{TPQP}}% of the initial position, that equals to an amount of {#{STPBQ}} {@{BASE}}', title = 'TP3 Short', tooltip = 'Alert message emited when the third quantity take profit target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPLong, 3, input.text_area(defval = 'Long Exit({@{TICKER}}): Take Profit 4 executed at price of {${LTP4}} {@{QUOTE}} and sold {#{TPQP}}% of the initial position, that equals to an amount of {#{LTPBQ}} {@{BASE}}', title = 'TP4 Long', tooltip = 'Alert message emited when the forth quantity take profit target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPShort, 3, input.text_area(defval = 'Short Exit({@{TICKER}}): Take Profit 4 executed at price of {${STP4}} {@{QUOTE}} and bought {#{TPQP}}% of the initial position, that equals to an amount of {#{STPBQ}} {@{BASE}}', title = 'TP4 Short', tooltip = 'Alert message emited when the forth quantity take profit target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPLong, 4, input.text_area(defval = 'Long Exit({@{TICKER}}): Take Profit 5 executed at price of {${LTP5}} {@{QUOTE}} and sold {#{TPQP}}% of the initial position, that equals to an amount of {#{LTPBQ}} {@{BASE}}', title = 'TP5 Long', tooltip = 'Alert message emited when the fifth quantity take profit target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
array.set(rawMsgRchTPShort, 4, input.text_area(defval = 'Short Exit({@{TICKER}}): Take Profit 5 executed at price of {${STP5}} {@{QUOTE}} and bought {#{TPQP}}% of the initial position, that equals to an amount of {#{STPBQ}} {@{BASE}}', title = 'TP5 Short', tooltip = 'Alert message emited when the fifth quantity take profit target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages'))
rawMsgRchSLLong = input.text_area(defval = 'Long Exit({@{TICKER}}): Stop Loss executed at price of {${LSL}} {@{QUOTE}} and sold {#{LRQP}}% of the initial position, that equals to an amount of {#{LRBQ}} {@{BASE}}', title = 'SL Long', tooltip = 'Alert message emited when the last quantity stop loss target for long position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgRchSLShort = input.text_area(defval = 'Short Exit({@{TICKER}}): Stop Loss executed at price of {${SSL}} {@{QUOTE}} and bought {#{SRQP}}% of the initial position, that equals to an amount of {#{SRBQ}} {@{BASE}}', title = 'SL Short', tooltip = 'Alert message emited when the last quantity stop loss target for short position is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
rawMsgClosedAll = input.text_area(defval = 'Close All({@{TICKER}}): Closed all positions at market price of {${CLOSE}} {@{QUOTE}}', title = 'Close All', tooltip = 'Alert message emited when closed all positions. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages')
msgChgTrEntryEnabled = input.bool(defval = false, title = 'Enable Tr Entry Change Alerts⚠️🐌', tooltip = 'Enable the alert messages that correspond to "alert() function calls" when the trailing entry price changes. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '🔔 Alert Messages')
rawMsgChgTrEntryLong = input.text_area(defval = 'Long Trailing Entry Update({@{TICKER}}): Long trailing stop-limit entry order changed and set at new price of {${ENTRY}} {@{QUOTE}}', title = 'Chg Tr Entry Long', tooltip = 'Alert message emited when long trailing stop-limit entry order changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
rawMsgChgTrEntryShort = input.text_area(defval = 'Short Trailing Entry Update({@{TICKER}}): Short trailing stop-limit entry order changed and set at new price of {${ENTRY}} {@{QUOTE}}', title = 'Chg Tr Entry Short', tooltip = 'Alert message emited when short trailing stop-limit entry order changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
msgChgTrExitEnabled = input.bool(defval = false, title = 'Enable Tr Exit Change Alerts⚠️🐌', tooltip = 'Enable the alert messages that correspond to "alert() function calls" when the trailing exit price changes. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '🔔 Alert Messages')
rawMsgChgTrExitLong = input.text_area(defval = 'Long Trailing Exit Update({@{TICKER}}): Long trailing stop-limit exit order changed and set at new price of {${EXIT}} {@{QUOTE}}', title = 'Chg Tr Exit Long', tooltip = 'Alert message emited when long trailing stop-limit exit order changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
rawMsgChgTrExitShort = input.text_area(defval = 'Short Trailing Exit Update({@{TICKER}}): Short trailing stop-limit exit order changed and set at new price of {${EXIT}} {@{QUOTE}}', title = 'Chg Tr Exit Short', tooltip = 'Alert message emited when short trailing stop-limit exit order changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
msgChgSLEnabled = input.bool(defval = false, title = 'Enable Stop Loss Change Alerts⚠️🐌', tooltip = 'Enable the alert messages that correspond to "alert() function calls" when the stop loss price changes. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '🔔 Alert Messages')
rawMsgChgSLLong = input.text_area(defval = 'Long Stop Loss Update({@{TICKER}}): Stop Loss value changed and set at new price of {${LSL}} {@{QUOTE}}', title = 'Chg SL Long', tooltip = 'Alert message emited when the stop loss target for long position changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
rawMsgChgSLShort = input.text_area(defval = 'Short Stop Loss Update({@{TICKER}}): Stop Loss value changed and set at new price of {${SSL}} {@{QUOTE}}', title = 'Chg SL Short', tooltip = 'Alert message emited when the stop loss target for short position changed. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
msgCnlTrEntryEnabled = input.bool(defval = false, title = 'Enable Tr Entry Cancel Alerts⚠️🐌', tooltip = 'Enable the alert messages that correspond to "alert() function calls" when trailing for entry and it cancels. WARNING! Computational heavy operation. If you get a timeout error adjust the time window filter or disable other computational heavy operations.', group = '🔔 Alert Messages')
rawMsgCnlTrEntryLong = input.text_area(defval = 'Long Trailing Entry Cancel({@{TICKER}}): Long trailing stop-limit entry order canceled', title = 'Cnl Tr Entry Long', tooltip = 'Alert message emited when long trailing stop-limit entry order canceled. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
rawMsgCnlTrEntryShort = input.text_area(defval = 'Short Trailing Entry Cancel({@{TICKER}}): Short trailing stop-limit entry order canceled', title = 'Cnl Tr Entry Short', tooltip = 'Alert message emited when short trailing stop-limit entry order canceled. In the "Condition" combobox of the "Create Alert" dialog the "Order fills and alert() function calls" or the "alert() function calls only" options should be selected to be able to receive this alert message.', group = '🔔 Alert Messages')
msgMaxDrawdown = msgEventsEnabled ? input.text_area(defval = 'Max drawdown was reached: All pending orders are cancelled, all open positions are closed and no new orders can be placed! RIP⚰️', title = 'Max Drawdown', tooltip = 'Alert message emited when the max drawdown limit is reached. Text that will replace the \'{{strategy.order.alert_message}}\' placeholder when one is used in the "Message" field of the "Create Alert" dialog.', group = '🔔 Alert Messages') : na
// LOGIC ============================================================================================================
var variableNames = array.new<string>(37, na)
var variableValues = array.new<float>(array.size(variableNames), na)
var stringVariableNames = array.new<string>(3, na)
var stringVariableValues = array.new<string>(array.size(stringVariableNames), na)
var bool variableNamesInit = false
if ((msgEventsEnabled or msgChgTrEntryEnabled or msgChgTrExitEnabled or msgChgSLEnabled or msgCnlTrEntryEnabled) and not variableNamesInit)
array.set(variableNames, 0, 'OPEN') // Open price
array.set(variableNames, 1, 'HIGH') // High price
array.set(variableNames, 2, 'LOW') // Low price
array.set(variableNames, 3, 'CLOSE') // Close price
array.set(variableNames, 4, 'LBQ') // Long base currency entry quantity
array.set(variableNames, 5, 'SBQ') // Short base currency entry quantity
array.set(variableNames, 6, 'LQQ') // Long quote currency entry quantity
array.set(variableNames, 7, 'SQQ') // Short quote currency entry quantity
array.set(variableNames, 8, 'LQQP') // Long quote currency entry quantity percentage
array.set(variableNames, 9, 'SQQP') // Short quote currency entry quantity percentage
array.set(variableNames, 10, 'TPQP') // Take Profit quantity percentage
array.set(variableNames, 11, 'LRQP') // Long remaining quantity percentage
array.set(variableNames, 12, 'SRQP') // Short remaining quantity percentage
array.set(variableNames, 13, 'LTPBQ') // Long Take Profit base quantity
array.set(variableNames, 14, 'STPBQ') // Short Take Profit base quantity
array.set(variableNames, 15, 'LRBQ') // Long remaining base quantity
array.set(variableNames, 16, 'SRBQ') // Short remaining base quantity
array.set(variableNames, 17, 'LRISK') // Long risk percentage of the entry quantity
array.set(variableNames, 18, 'SRISK') // Short risk percentage of the entry quantity
array.set(variableNames, 19, 'ENTRY') // Entry price
array.set(variableNames, 20, 'ENTRY+') // Entry price plus the margin ticks
array.set(variableNames, 21, 'ENTRY-') // Entry price minus the margin ticks
array.set(variableNames, 22, 'EXIT') // Exit price
array.set(variableNames, 23, 'EXIT+') // Exit price plus the margin ticks
array.set(variableNames, 24, 'EXIT-') // Exit price minus the margin ticks
array.set(variableNames, 25, 'LSL') // Long Stop Loss price
array.set(variableNames, 26, 'SSL') // Short Stop Loss price
array.set(variableNames, 27, 'LTP1') // Long Take Profit target 1 price
array.set(variableNames, 28, 'STP1') // Short Take Profit target 1 price
array.set(variableNames, 29, 'LTP2') // Long Take Profit target 2 price
array.set(variableNames, 30, 'STP2') // Short Take Profit target 2 price
array.set(variableNames, 31, 'LTP3') // Long Take Profit target 3 price
array.set(variableNames, 32, 'STP3') // Short Take Profit target 3 price
array.set(variableNames, 33, 'LTP4') // Long Take Profit target 4 price
array.set(variableNames, 34, 'STP4') // Short Take Profit target 4 price
array.set(variableNames, 35, 'LTP5') // Long Take Profit target 5 price
array.set(variableNames, 36, 'STP5') // Short Take Profit target 5 price
variableNamesInit := true
if (msgEventsEnabled or msgChgTrEntryEnabled or msgChgTrExitEnabled or msgChgSLEnabled or msgCnlTrEntryEnabled)
var float percMul = usePercRange100 ? 100.0 : 1.0
array.set(variableValues, 0, open)
array.set(variableValues, 1, high)
array.set(variableValues, 2, low)
array.set(variableValues, 3, close)
array.set(variableValues, 4, longEntryBaseQuantity)
array.set(variableValues, 5, shortEntryBaseQuantity)
array.set(variableValues, 6, longQuoteQuantity)
array.set(variableValues, 7, shortQuoteQuantity)
array.set(variableValues, 8, longEntryQuoteQuantityPerc * percMul)
array.set(variableValues, 9, shortEntryQuoteQuantityPerc * percMul)
array.set(variableValues, 10, takeProfitQuantityPerc * percMul)
array.set(variableValues, 11, longRemainingQuantityPerc * percMul)
array.set(variableValues, 12, shortRemainingQuantityPerc * percMul)
array.set(variableValues, 13, longEntryBaseQuantity * takeProfitQuantityPerc)
array.set(variableValues, 14, shortEntryBaseQuantity * takeProfitQuantityPerc)
array.set(variableValues, 15, longEntryBaseQuantity * longRemainingQuantityPerc)
array.set(variableValues, 16, shortEntryBaseQuantity * shortRemainingQuantityPerc)
array.set(variableValues, 17, longRisk * percMul)
array.set(variableValues, 18, shortRisk * percMul)
array.set(variableValues, 19, entryPrice)
array.set(variableValues, 20, entryPrice + marginTicks * syminfo.mintick)
array.set(variableValues, 21, entryPrice - marginTicks * syminfo.mintick)
array.set(variableValues, 22, exitPrice)
array.set(variableValues, 23, exitPrice + marginTicks * syminfo.mintick)
array.set(variableValues, 24, exitPrice - marginTicks * syminfo.mintick)
array.set(variableValues, 25, longStopLossPrice)
array.set(variableValues, 26, shortStopLossPrice)
if (array.size(longTakeProfitPrices) > 0)
array.set(variableValues, 27, array.get(longTakeProfitPrices, 0))
if (array.size(shortTakeProfitPrices) > 0)
array.set(variableValues, 28, array.get(shortTakeProfitPrices, 0))
if (array.size(longTakeProfitPrices) > 1)
array.set(variableValues, 29, array.get(longTakeProfitPrices, 1))
if (array.size(shortTakeProfitPrices) > 1)
array.set(variableValues, 30, array.get(shortTakeProfitPrices, 1))
if (array.size(longTakeProfitPrices) > 2)
array.set(variableValues, 31, array.get(longTakeProfitPrices, 2))
if (array.size(shortTakeProfitPrices) > 2)
array.set(variableValues, 32, array.get(shortTakeProfitPrices, 2))
if (array.size(longTakeProfitPrices) > 3)
array.set(variableValues, 33, array.get(longTakeProfitPrices, 3))
if (array.size(shortTakeProfitPrices) > 3)
array.set(variableValues, 34, array.get(shortTakeProfitPrices, 3))
if (array.size(longTakeProfitPrices) > 4)
array.set(variableValues, 35, array.get(longTakeProfitPrices, 4))
if (array.size(shortTakeProfitPrices) > 4)
array.set(variableValues, 36, array.get(shortTakeProfitPrices, 4))
var bool stringVariablesInit = false
if ((msgEventsEnabled or msgChgTrEntryEnabled or msgChgTrExitEnabled or msgChgSLEnabled or msgCnlTrEntryEnabled) and not stringVariablesInit)
array.set(stringVariableNames, 0, 'TICKER')
array.set(stringVariableNames, 1, 'BASE')
array.set(stringVariableNames, 2, 'QUOTE')
array.set(stringVariableValues, 0, tickerPrefix + syminfo.ticker + tickerPostfix)
array.set(stringVariableValues, 1, syminfo.basecurrency)
array.set(stringVariableValues, 2, syminfo.currency)
stringVariablesInit := true
// create the actual messages
msgEnteredLong = msgEventsEnabled and validOpenLongPosition ? sv.eval_vars(rawMsgEnteredLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgEnteredShort = msgEventsEnabled and validOpenShortPosition ? sv.eval_vars(rawMsgEnteredShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgEnteredTrLong = msgEventsEnabled and longTrEntryIsActive ? sv.eval_vars(rawMsgEnteredTrLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgEnteredTrShort = msgEventsEnabled and shortTrEntryIsActive ? sv.eval_vars(rawMsgEnteredTrShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgClosedLong = msgEventsEnabled and doCloseLong ? sv.eval_vars(rawMsgClosedLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgClosedShort = msgEventsEnabled and doCloseShort ? sv.eval_vars(rawMsgClosedShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgExitedTrLong = msgEventsEnabled and doSLorExitTrLong ? sv.eval_vars(rawMsgExitedTrLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgExitedTrShort = msgEventsEnabled and doSLorExitTrShort ? sv.eval_vars(rawMsgExitedTrShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
var msgRchTPLong = array.new<string>(array.size(rawMsgRchTPLong), na)
var msgRchTPShort = array.new<string>(array.size(rawMsgRchTPShort), na)
if (msgEventsEnabled)
if (doSLorExitTrLong)
for [i, rawMsg] in rawMsgRchTPLong
array.set(msgRchTPLong, i, sv.eval_vars(rawMsg, variableNames, variableValues, stringVariableNames, stringVariableValues))
if (doSLorExitTrShort)
for [i, rawMsg] in rawMsgRchTPShort
array.set(msgRchTPShort, i, sv.eval_vars(rawMsg, variableNames, variableValues, stringVariableNames, stringVariableValues))
msgRchSLLong = msgEventsEnabled and doSLorExitTrLong ? sv.eval_vars(rawMsgRchSLLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgRchSLShort = msgEventsEnabled and doSLorExitTrShort ? sv.eval_vars(rawMsgRchSLShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgClosedAll = msgEventsEnabled and doCloseAll ? sv.eval_vars(rawMsgClosedAll, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgTrEntryLong = msgChgTrEntryEnabled ? sv.eval_vars(rawMsgChgTrEntryLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgTrEntryShort = msgChgTrEntryEnabled ? sv.eval_vars(rawMsgChgTrEntryShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgTrExitLong = msgChgTrExitEnabled ? sv.eval_vars(rawMsgChgTrExitLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgTrExitShort = msgChgTrExitEnabled ? sv.eval_vars(rawMsgChgTrExitShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgSLLong = msgChgSLEnabled ? sv.eval_vars(rawMsgChgSLLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgChgSLShort = msgChgSLEnabled ? sv.eval_vars(rawMsgChgSLShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgCnlTrEntryLong = msgCnlTrEntryEnabled and doCnlTrEntryLong ? sv.eval_vars(rawMsgCnlTrEntryLong, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
msgCnlTrEntryShort = msgCnlTrEntryEnabled and doCnlTrEntryShort ? sv.eval_vars(rawMsgCnlTrEntryShort, variableNames, variableValues, stringVariableNames, stringVariableValues) : na
// signal change alerts
if (msgChgTrEntryEnabled and ta.change(longTrEntryPrice))
alert(message = msgChgTrEntryLong)
if (msgChgTrEntryEnabled and ta.change(shortTrEntryPrice))
alert(message = msgChgTrEntryShort)
if (msgChgTrExitEnabled and ta.change(longTrExitPrice))
alert(message = msgChgTrExitLong)
if (msgChgTrExitEnabled and ta.change(shortTrExitPrice))
alert(message = msgChgTrExitShort)
if (msgChgSLEnabled and na(longTrEntryPrice) and ta.change(longStopLossPrice))
alert(message = msgChgSLLong)
if (msgChgSLEnabled and na(shortTrEntryPrice) and ta.change(shortStopLossPrice))
alert(message = msgChgSLShort)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🗲 POSITION ORDERS ===============================================================================================
// Description: Module responsible for the actual execution of the strategy orders
// Dependencies: STRATEGY 1, ENTRY, STRATEGY 2, EXIT, STOP LOSS, TAKE PROFIT 2, QUANTITY MANAGEMENT, SHARED VARIABLES 4, ALERT MESSAGES
// Results: NONE
// INPUT ============================================================================================================
maxDrawdown = input.int(defval = 25, title = 'Max Drawdown %', minval = 1, maxval = 100, tooltip = 'The maximum drawdown to stop trading.', group = '💰 Quantity/Risk Management')
// LOGIC ============================================================================================================
// close all positions at the end of the session
if (doCloseAll)
strategy.close_all(comment = showAlertsEnabled ? msgClosedAll : 'AC', alert_message = msgClosedAll)
// close on trend reversal when not exit trailing
if (doCloseLong)
strategy.close(id = 'Long Entry', comment = showAlertsEnabled ? msgClosedLong : 'LC', alert_message = msgClosedLong)
// close on trend reversal when not exit trailing
if (doCloseShort)
strategy.close(id = 'Short Entry', comment = showAlertsEnabled ? msgClosedShort : 'SC', alert_message = msgClosedShort)
// getting into LONG position
if (entryTrailingEnabled ? longTrEntryIsActive : validOpenLongPosition)
strategy.entry(id = 'Long Entry', direction = strategy.long, stop = longTrEntryPrice, qty = longEntryBaseQuantity, comment = showAlertsEnabled ? entryTrailingEnabled ? msgEnteredTrLong : msgEnteredLong : 'LEn', alert_message = entryTrailingEnabled ? msgEnteredTrLong : msgEnteredLong)
// cancel existing entry trailing stop orders (if any)
if (doCnlTrEntryLong)
strategy.cancel(id = 'Long Entry')
if (msgCnlTrEntryEnabled)
alert(message = msgCnlTrEntryLong)
// calculate the tightest stop order by combining the stop loss and trailing exit (if any)
float longTightestStop = na(longTrExitPrice) ? longStopLossPrice : math.max(nz(longTrExitPrice), nz(longStopLossPrice))
bool isLongTrailingExitTighter = longTrExitIsActive and longTrExitPrice > longStopLossPrice
// submit exit order for trailing take profit price also set the stop loss for the take profit percentage in case that stop loss is reached first
// submit exit stop order on trend reversal when exit trailing
for [i, longTakeProfitPrice] in longTakeProfitPrices
if (longTrExitIsActive or (longIsActive and not array.get(longTrailingTakeProfitExecuted, i)))
alertMessage = array.get(msgRchTPLong, i % array.size(msgRchTPLong))
comment = 'LTP' + str.tostring(i + 1)
strategy.exit(id = 'Long Take Profit ' + str.tostring(i + 1) + ' / Stop Loss / Trailing Exit', from_entry = 'Long Entry', qty_percent = 100.0 * takeProfitQuantityPerc,
limit = takeProfitTrailingEnabled ? na : longTakeProfitPrice, stop = longTightestStop,
trail_price = takeProfitTrailingEnabled ? longTakeProfitPrice : na, trail_offset = takeProfitTrailingEnabled ? array.get(longTrailingTakeProfitOffsetTicks, i) : na,
comment_profit = showAlertsEnabled ? alertMessage : comment, comment_loss = allTakeProfitsQuantityPerc == 1.0 and i == numOfTakeProfitTargets - 1 ? isLongTrailingExitTighter ? msgExitedTrLong : msgRchSLLong : isLongTrailingExitTighter ? 'LTrEx' + str.tostring(i + 1) : 'LSL' + str.tostring(i + 1), comment_trailing = showAlertsEnabled ? alertMessage : comment,
alert_profit = alertMessage, alert_loss = allTakeProfitsQuantityPerc == 1.0 and i == numOfTakeProfitTargets - 1 ? isLongTrailingExitTighter ? msgExitedTrLong : msgRchSLLong : na, alert_trailing = alertMessage)
// submit exit order for trailing stop loss price for the remaining percent of the quantity not reserved by the take profit order
// submit exit stop order on trend reversal when exit trailing
if (doSLorExitTrLong)
strategy.exit(id = 'Long Stop Loss / Trailing Exit', from_entry = 'Long Entry', stop = longTightestStop,
comment_loss = showAlertsEnabled ? isLongTrailingExitTighter ? msgExitedTrLong : msgRchSLLong : isLongTrailingExitTighter ? 'LTrEx' : 'LSL', alert_loss = isLongTrailingExitTighter ? msgExitedTrLong : msgRchSLLong)
// getting into SHORT position
if (entryTrailingEnabled ? shortTrEntryIsActive : validOpenShortPosition)
strategy.entry(id = 'Short Entry', direction = strategy.short, stop = shortTrEntryPrice, qty = shortEntryBaseQuantity, comment = showAlertsEnabled ? entryTrailingEnabled ? msgEnteredTrShort : msgEnteredShort : 'SEn', alert_message = entryTrailingEnabled ? msgEnteredTrShort : msgEnteredShort)
// cancel existing entry trailing stop orders (if any)
if (doCnlTrEntryShort)
strategy.cancel(id = 'Short Entry')
if (msgCnlTrEntryEnabled)
alert(message = msgCnlTrEntryShort)
// calculate the tightest stop order by combining the stop loss and trailing exit (if any)
float shortTightestStop = na(shortTrExitPrice) ? shortStopLossPrice : math.min(nz(shortTrExitPrice, 999999.9), nz(shortStopLossPrice, 999999.9))
bool isShortTrailingExitTighter = shortTrExitIsActive and shortTrExitPrice < shortStopLossPrice
// submit exit order for trailing take profit price also set the stop loss for the take profit percentage in case that stop loss is reached first
// submit exit stop order on trend reversal when exit trailing
for [i, shortTakeProfitPrice] in shortTakeProfitPrices
if (shortTrExitIsActive or (shortIsActive and not array.get(shortTrailingTakeProfitExecuted, i)))
alertMessage = array.get(msgRchTPShort, i % array.size(msgRchTPShort))
comment = 'STP' + str.tostring(i + 1)
strategy.exit(id = 'Short Take Profit ' + str.tostring(i + 1) + ' / Stop Loss / Trailing Exit', from_entry = 'Short Entry', qty_percent = 100.0 * takeProfitQuantityPerc,
limit = takeProfitTrailingEnabled ? na : shortTakeProfitPrice, stop = shortTightestStop,
trail_price = takeProfitTrailingEnabled ? shortTakeProfitPrice : na, trail_offset = takeProfitTrailingEnabled ? array.get(shortTrailingTakeProfitOffsetTicks, i) : na,
comment_profit = showAlertsEnabled ? alertMessage : comment, comment_loss = allTakeProfitsQuantityPerc == 1.0 and i == numOfTakeProfitTargets - 1 ? isShortTrailingExitTighter ? msgExitedTrShort : msgRchSLShort : isShortTrailingExitTighter ? 'STrEx' + str.tostring(i + 1) : 'SSL' + str.tostring(i + 1), comment_trailing = showAlertsEnabled ? alertMessage : comment,
alert_profit = alertMessage, alert_loss = allTakeProfitsQuantityPerc == 1.0 and i == numOfTakeProfitTargets - 1 ? isShortTrailingExitTighter ? msgExitedTrShort : msgRchSLShort : na, alert_trailing = alertMessage)
// submit exit order for trailing stop loss price for the remaining percent of the quantity not reserved by the take profit order
// submit exit stop order on trend reversal when exit trailing
if (doSLorExitTrShort)
strategy.exit(id = 'Short Stop Loss / Trailing Exit', from_entry = 'Short Entry', stop = shortTightestStop,
comment_loss = showAlertsEnabled ? isShortTrailingExitTighter ? msgExitedTrShort : msgRchSLShort : isShortTrailingExitTighter ? 'STrEx' : 'SSL', alert_loss = isShortTrailingExitTighter ? msgExitedTrShort : msgRchSLShort)
// limit the maximum drawdown
strategy.risk.max_drawdown(value = maxDrawdown, type = strategy.percent_of_equity, alert_message = msgMaxDrawdown)
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// 🐞 DEBUG =========================================================================================================
// Description: Module responsible for plotting additional information for debuging purposes
// Dependencies: ALL
// Results: showAlertsEnabled, debugModeEnabled
// INPUT ============================================================================================================
showAlertsEnabled := input.bool(defval = false, title = 'Show Alerts', tooltip = 'Show alert messages that correspond to events in the chart.', group = '🐞 Debug')
debugModeEnabled := input.bool(defval = false, title = 'Debug Mode', tooltip = 'Enable debug mode.', group = '🐞 Debug')
// PLOT =============================================================================================================
plotshape(series = debugModeEnabled ? tryEnterLongPosition : na, title = 'Dbg Try Enter Long', style = shape.arrowup, location = location.belowbar, color = color.new(buyColor, 70), size = size.tiny)
plotshape(series = debugModeEnabled ? tryEnterShortPosition : na, title = 'Dbg Try Enter Short', style = shape.arrowdown, location = location.abovebar, color = color.new(sellColor, 70), size = size.tiny)
plotshape(series = debugModeEnabled ? longPositionEntry : na, title = 'Dbg Long Entry', style = shape.flag, location = location.belowbar, color = color.new(buyColor, 70), size = size.small)
plotshape(series = debugModeEnabled ? shortPositionEntry : na, title = 'Dbg Short Entry', style = shape.flag, location = location.abovebar, color = color.new(sellColor, 70), size = size.small)
plotshape(series = debugModeEnabled ? longPositionCompleted : na, title = 'Dbg Long Completed', style = shape.square, location = location.belowbar, color = color.new(buyColor, 70), size = size.small)
plotshape(series = debugModeEnabled ? shortPositionCompleted : na, title = 'Dbg Short Completed', style = shape.square, location = location.abovebar, color = color.new(sellColor, 70), size = size.small)
plotshape(series = debugModeEnabled ? longIsActive : na, title = 'Dbg Active Long', style = shape.labelup, location = location.belowbar, color = color.new(buyColor, 70), size = size.tiny)
plotshape(series = debugModeEnabled ? shortIsActive : na, title = 'Dbg Active Short', style = shape.labeldown, location = location.abovebar, color = color.new(sellColor, 70), size = size.tiny)
plot(series = debugModeEnabled ? entryPrice : na, title = 'Dbg Entry Price', color = color.new(posColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 0 ? array.get(longTakeProfitPrices, 0) : na, title = 'Dbg Long Take Profit 1', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 0 ? array.get(shortTakeProfitPrices, 0) : na, title = 'Dbg Short Take Profit 1', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 1 ? array.get(longTakeProfitPrices, 1) : na, title = 'Dbg Long Take Profit 2', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 1 ? array.get(shortTakeProfitPrices, 1) : na, title = 'Dbg Short Take Profit 2', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 2 ? array.get(longTakeProfitPrices, 2) : na, title = 'Dbg Long Take Profit 3', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 2 ? array.get(shortTakeProfitPrices, 2) : na, title = 'Dbg Short Take Profit 3', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 3 ? array.get(longTakeProfitPrices, 3) : na, title = 'Dbg Long Take Profit 4', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 3 ? array.get(shortTakeProfitPrices, 3) : na, title = 'Dbg Short Take Profit 4', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 4 ? array.get(longTakeProfitPrices, 4) : na, title = 'Dbg Long Take Profit 5', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
plot(series = debugModeEnabled and array.size(shortTakeProfitPrices) > 4 ? array.get(shortTakeProfitPrices, 4) : na, title = 'Dbg Short Take Profit 5', color = color.new(takeProfitColor, 70), linewidth = 1, style = plot.style_linebr, offset = 1)
// ==================================================================================================================
Expand (1566 lines)
Overview
Andean Scalping features session and date-range filtering, directional trade toggles for long and short entries, and optional trend filtering tools including higher-timeframe EMA filters, EMA ATR bands, and ADX filters. Trade entries and exits are drawn directly on the main price chart. The strategy provides a flexible framework for testing signal logic across custom session times and specific calendar dates.
Date range and session filtering
Directional trade toggles for long and short trades
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Timezones Src->Dst
string
Exchange
src_timezone
->
string
Exchange
dst_timezone
From
bool
true
usefromDate
fromDate
time
timestamp('01 Jan 2022 00:00')
fromDate
To
bool
false
usetoDate
toDate
time
timestamp('01 Jul 2022 00:00')
toDate
Session Days
bool
false
useSessionDay
Mon
bool
true
mon
Tue
bool
true
tue
Wed
bool
true
wed
Thu
bool
true
thu
Fri
bool
true
fri
Show all 40 settings
Setting
Type
Default
Variable
Sat
bool
false
sat
Sun
bool
false
sun
Session Start
bool
false
useSessionStart
sessionStartHour
int
12
sessionStartHour
:
int
00
sessionStartMinute
Session End
bool
false
useSessionEnd
sessionEndHour
int
20
sessionEndHour
:
int
00
sessionEndMinute
Long Trades
bool
true
longTradesEnabled
Short Trades
bool
true
shortTradesEnabled
EMA Filter
bool
false
emaFilterEnabled
EMA Res/Len/Src
timeframe
emaTimeframe
emaLength
int
200
emaLength
string
close
EMA ATR Band
bool
false
emaAtrBandEnabled
EMA ATR Len/Mul
int
5
filterAtrLength
filterAtrMul
float
1.0
filterAtrMul
ADX Filter
bool
false
adxFilterEnabled
Smooth/DI Len
int
14
adxSmoothing
diLength
int
14
diLength
Threshold
float
39.4
adxThreshold
Deal Conditions Mode
string
🔧Internal
dealMode
Cooldown # of bars
bool
false
cooldownEnabled
cooldownBars
int
7
cooldownBars
Andean Length
int
50
and_length
Andean Signal Length
int
9
sig_length
Signal Threshold Length
int
1000
signal_threshold_length
Signal Threshold Multiplier
float
1.1
signal_threshold_multiplier
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.
Donald - Test Logic Futures is an overlay strategy designed for intraday futures trading. It combines kernel regression functions with exponential moving averages to evaluate price retracements and entry timing. Trades are filtered by session execution windows
This strategy trades EUR/USD on the price chart using a fast and slow exponential moving average crossover filtered by RSI. It enters long when the fast EMA crosses above the slow EMA while RSI is above 50 and below the overbought level, and enters short on th
Gold Scalping BOS & CHoCH with MA Filter is a strategy that identifies market structure shifts using Break of Structure (BOS) and Change of Character (CHoCH) signals alongside exponential moving average trend filters. Long trades enter when bullish BOS, bullis
This overlay strategy targets one-minute ATOM scalps. It tracks pivot highs and lows as resistance and support, compares a three-period EMA with a multi-stage smoothed close, and flags long or short when price crosses that curve in the matching direction. Dail
This strategy trades Nikkei 225 futures during a configurable overnight session, defaulting to 19:00–03:00 ET. It builds long and short trend conditions from an 8/21 EMA pair and a 200-period SMA, with RSI, momentum, an ATR-scaled breakout range, and a volume
This strategy plots on the price chart and enters from a moving-average filter, ADX and Gann colours. Price above a 200-period EMA allows only longs; price below allows only shorts. A Gann aqua colour with ADX above a key level opens a long, and a Gann brown c