Trent
Trent

Reputation: 109

Bracket Order strategy.exit not working when using Variable Calculations for Limit and Stop inputs

I am trying to replicate bracket orders (Market Entry, Limit Take Profit and Limit Stop Loss) in a pine script strategy to enable back testing and strategy execution through pine script. The output variables for Take Profit (Long1_Take_Profit) and Stop Loss (Long1SL) both successfully output the TP & SL exit prices that are transmitted via webhook to my IB_Insync bot and submitted into a TWS bracket order. However, these same variable outputs, when used in strategy.exit to replicate the bracket order, do not work. The Take Profit and Stop Loss are not executed on Tradingview at all.

I can only get strategy.exit to work if i enter in a manual Limit Price and Stop Loss price into the strategy.exit function, or a calculation such as [close * 1.05], and it will work if i manually enter these limit prices into a variable (such as the TP variable below (TP = 17585 + 7). But if I use strategy.position_avg_price or even strategy.position_avg_price + 7 (as 2 examples which both print correct output prices in test labels on the chart) the limit and stop will not work.

My strategy sets the Stop Loss as the lowest low of 3 previous bars [ta.lowest(3)] and the take profit is a ratio of the Stop Loss (e.g 1 to 1). Therefore - the Take Profit price is calculated in reference to the Stop Loss price - and both variable calculations output perfectly fine for use in the TWS IB API bot.

I have spent 2 days reading and trying so many different ways to get this to work. I have tried strategy.order, ensuring the variables correspond to their required output type (float/int etc) and even rounding to mintick. The variable calcuations print correctly to a label and output to webhook/TWS - so i know the variable calculations are working, and i have tried tick calculations and percentage calcs for the Take Profit and Stop = all of which wont work unless input directly into the strategy.exit function.

I have read and re-read over and over tradingcode.net, the TV documentation and Stack Overflow and i seem to be following exactly as prescribed..... so i dont understand why it is not working in V5, when i used to be able to get similar bracket order functionality in V3 and V4. Is Tradingview strategy.exit not able to take 'limit' & 'stop' inputs from variable outputs? (however - i see other code on Stackoverflow using such variable output for limit and stop exit parameters).

working code is below - i know there is a lot of unnecessary code lines in there - that is due to me trying all different attempts to problem solve (such as trying tick and percentage calculations etc) and i have left it in there.

Most likely it is a embarrassingly simple thing i am doing wrong - but i am blind to it at the moment. Please can somebody help guide me in the direction i need to take to resolve this. Thankyou so much :-)

//@version=5
strategy("PPO Strat Entry Test",  overlay=true, default_qty_value=1, calc_on_order_fills=false, calc_on_every_tick=true, pyramiding=0)

//==============================================DATE FILTER===========================================================================================================================================

useDateFilter = input.bool(true, title='Filter Date Range of Backtest', group='Backtest Time Period')
backtestStartDate = input.time(title='Date', defval=timestamp('1 Apr 2024'), title='Start Date', group='Backtest Time Period', tooltip='This start date is in the time zone of the exchange ' + 'where the chart\'s instrument trades. It doesn\'t use the time ' + 'zone of the chart or of your computer.')
backtestEndDate = input.time(title='Date', defval=timestamp('30 Apr 2024'), title='End Date', group='Backtest Time Period', tooltip='This end date is in the time zone of the exchange ' + 'where the chart\'s instrument trades. It doesn\'t use the time ' + 'zone of the chart or of your computer.')

inDateRange = not useDateFilter or time >= backtestStartDate and time < backtestEndDate

//________________________________________________________________________________________________________________________________________________________________________
//              EXPONENTIAL MOVING AVERAGE

EMAlength = input.int(20, minval=1)
EMAexp = input(true, 'exponential')

esma(realClose, EMAlength)=>
    s = ta.sma(realClose, EMAlength)
    e = ta.ema(realClose, EMAlength)
    EMAexp ? e : s


//________________________________________________________________________________________________________________________________________________________________________
//              PPO

//PPO Chart Time
fast_length1 = input(title='Fast Length', defval=7, group = 'PPO')
slow_length1 = input(title='Slow Length', defval=21, group = 'PPO')

signal_length = input.int(title='Signal Smoothing', minval=1, maxval=50, defval=5, group = 'PPO')
sma_source = input(title='Simple MA(Oscillator)', defval=true, group = 'PPO')
sma_signal = input(title='Simple MA(Signal Line)', defval=true, group = 'PPO')
PPOsrc = close

// Calculating
fast_ma = sma_source ? ta.sma(PPOsrc, fast_length1) : ta.ema(PPOsrc, fast_length1)
slow_ma = sma_source ? ta.sma(PPOsrc, slow_length1) : ta.ema(PPOsrc, slow_length1)
PPO = ( ((fast_ma - slow_ma) / slow_ma) * 100 )
PPOsig = (sma_signal ? ta.sma(PPO, signal_length) : ta.ema(PPO, signal_length))

//Price Oscillator Chart Time
OSC_shortlen=input.int(7, "Short Length", minval=1, group = 'Price Osc')
OSC_longlen=input.int(21, "Long Length", minval=1, group = 'Price Osc')
OSC_exp = input(true, "exponential", group = 'Price Osc')

OSCshort = esma(PPOsrc, OSC_shortlen)
OSClong = esma(PPOsrc, OSC_longlen)
PPOosc = ( ((OSCshort - OSClong)/OSClong*100) )


Lowest3 = ta.lowest(3)

long1 = ta.crossunder(PPOsig, PPO)

//BRACKET ORDER PRICE CALCULATIONS
var float Long1_Entry_Price = 0.0
Long1_Entry_Price           := strategy.opentrades.entry_price(strategy.opentrades - 1) // strategy.position_avg_price

//Stop Loss
var float Long1SL           = 0.0
Long1SL                     := if strategy.position_size[1] <= 0 and strategy.position_size >= 1
    math.round_to_mintick(Lowest3)

Long1SLticks                = ((Long1_Entry_Price - Long1SL)/syminfo.mintick) //Attempted the use of ticks to see if that would work
var int Long1SL_Ticks       = 0
Long1SL_Ticks               := int(Long1SLticks)

// TAKE PROFIT
var float Long1_Take_Profit = 0.0
Long1_Take_Profit           := (Long1_Entry_Price - Long1SL) + Long1_Entry_Price           //  math.round_to_mintick(strategy.position_avg_price - Long1SL) + strategy.position_avg_price  // ProfitFactor //math.round_to_mintick(Long1_Entry_Price + (syminfo.mintick * 100))
Long1TP_Ticks               = ((Long1_Take_Profit - Long1_Entry_Price)/syminfo.mintick)
Long1TP_pc                  = 1+ (math.round(((Long1_Take_Profit - Long1_Entry_Price)/Long1_Entry_Price)*100, 2)) // Attempted the use of Percentent Take Profit to see if that would work


//TP =  (17585 + 7)
//SL = (17585 - 6)
TP = math.round_to_mintick(strategy.position_avg_price + 7)
SL = math.round_to_mintick(strategy.position_avg_price - 6)

//TEST LABEL showing variable output prices to check calculations are working properly
var label _l_SL = na
var label _l_TP = na

if strategy.position_size[1] <= 0 and strategy.position_size >= 1
    _l_TP := label.new(bar_index, high, str.tostring(Long1_Take_Profit), yloc=yloc.abovebar)
    label.set_textcolor(id=_l_TP, textcolor=color.white)
    label.set_size(_l_TP, size.large)
    label.set_style(_l_TP, label.style_label_left)

    _l_SL := label.new(bar_index, low, str.tostring(Long1SL), yloc=yloc.belowbar)
    label.set_textcolor(id=_l_SL, textcolor=color.white)
    label.set_size(_l_SL, size.large)
    label.set_style(_l_SL, label.style_label_left)


//ORDER EXECUTION
if inDateRange and long1 and barstate.isconfirmed// and strategy.position_size == 0
    strategy.entry('Long', strategy.long, qty = 1, comment = "Long1\nBrkt Entry", oca_name = "Long1") // oca_type = strategy.oca.cancel,
    strategy.exit('Exit Long', from_entry = 'Long',  qty = strategy.position_size, limit = Long1_Take_Profit, stop = Long1SL, oca_name = "Long1_Exit", comment_profit = "Profit Bkt\nClose Long1", comment_loss = "SL Bkt\nClose Long1") 

    //strategy.exit('Exit Long', from_entry = 'Long',  qty = strategy.position_size, limit = 17900, stop = 17700, comment = "Bkt Close\nLong1")
    //strategy.exit('Exit Long', from_entry = 'Long',  qty = strategy.position_size, profit = Long1TP_Ticks, loss = Long1SLticks, comment = "Bkt Close\nLong1")  
    //strategy.exit('Exit Long', from_entry = 'Long',  qty = strategy.position_size, profit = 10, loss = 10, comment = "Bkt Close\nLong1")

Upvotes: 0

Views: 104

Answers (1)

Trent
Trent

Reputation: 109

After a lot more research and trying different methods - i worked it out. I could have deleted this question given no one had answered - but i decided to post the answer as I could not find a clear and concise answer anywhere on Tradercode.net, the Pine Script Manual, Stack overflow or the web.

The calculations for the Stop Loss variable output needs to be made within the relevant 'If' condition statement for the trade entry, which i did try before and it didnt work. However - the Variable Declarations need to be outside these If statements - whereas my prior attempt had them in. The Stop Loss Variable Declaration is then assigned a new value upon the execution of each 'IF' condition statement, hence the Stop Loss Price is assigned upon and relevant to the entry condition.

I still cannot get strategy.exit to work under the one strategy.entry 'if' statement as documented by Tradingcode.net and Tradingview Docs ... it only works as part of the global scope.

My solution may be a bit of hack as i am still a noob to all this - but at least it now works as i need. I am sure there is a better way - but i certainly couldnt find it documented anywhere.

Cleaned up working code is below

//@version=5
strategy("PPO Strat Entry Test",  overlay=true, default_qty_value=1, calc_on_order_fills=false, calc_on_every_tick=true, pyramiding=0)

//==============================================DATE FILTER===========================================================================================================================================

useDateFilter = input.bool(true, title='Filter Date Range of Backtest', group='Backtest Time Period')
backtestStartDate = input.time(title='Date', defval=timestamp('1 Apr 2024'), title='Start Date', group='Backtest Time Period', tooltip='This start date is in the time zone of the exchange ' + 'where the chart\'s instrument trades. It doesn\'t use the time ' + 'zone of the chart or of your computer.')
backtestEndDate = input.time(title='Date', defval=timestamp('30 Apr 2024'), title='End Date', group='Backtest Time Period', tooltip='This end date is in the time zone of the exchange ' + 'where the chart\'s instrument trades. It doesn\'t use the time ' + 'zone of the chart or of your computer.')

inDateRange = not useDateFilter or time >= backtestStartDate and time < backtestEndDate

//________________________________________________________________________________________________________________________________________________________________________
//              EXPONENTIAL MOVING AVERAGE

EMAlength = input.int(20, minval=1)
EMAexp = input(true, 'exponential')

esma(realClose, EMAlength)=>
    s = ta.sma(realClose, EMAlength)
    e = ta.ema(realClose, EMAlength)
    EMAexp ? e : s


//________________________________________________________________________________________________________________________________________________________________________
//              PPO

//PPO Chart Time
fast_length1 = input(title='Fast Length', defval=7, group = 'PPO')
slow_length1 = input(title='Slow Length', defval=21, group = 'PPO')

signal_length = input.int(title='Signal Smoothing', minval=1, maxval=50, defval=5, group = 'PPO')
sma_source = input(title='Simple MA(Oscillator)', defval=true, group = 'PPO')
sma_signal = input(title='Simple MA(Signal Line)', defval=true, group = 'PPO')
PPOsrc = close

// Calculating
fast_ma = sma_source ? ta.sma(PPOsrc, fast_length1) : ta.ema(PPOsrc, fast_length1)
slow_ma = sma_source ? ta.sma(PPOsrc, slow_length1) : ta.ema(PPOsrc, slow_length1)
PPO = ( ((fast_ma - slow_ma) / slow_ma) * 100 )
PPOsig = (sma_signal ? ta.sma(PPO, signal_length) : ta.ema(PPO, signal_length))

//Price Oscillator Chart Time
OSC_shortlen=input.int(7, "Short Length", minval=1, group = 'Price Osc')
OSC_longlen=input.int(21, "Long Length", minval=1, group = 'Price Osc')
OSC_exp = input(true, "exponential", group = 'Price Osc')

OSCshort = esma(PPOsrc, OSC_shortlen)
OSClong = esma(PPOsrc, OSC_longlen)
PPOosc = ( ((OSCshort - OSClong)/OSClong*100) )


Lowest3 = ta.lowest(low, 3)

long1 = ta.crossunder(PPOsig, PPO)

//BRACKET ORDER PRICE CALCULATIONS
var float Long1_Entry_Price = 0.0
var float Long1SL           = 0.0
var float Long1_Take_Profit = 0.0
Long1_Entry_Price   := strategy.position_avg_price
Long1_Take_Profit   := (strategy.position_avg_price - Long1SL) + strategy.position_avg_price

//ORDER EXECUTION
if inDateRange and long1 and barstate.isconfirmed and strategy.position_size == 0
    strategy.entry('Long', strategy.long, qty = 1, comment = "Long1\nBrkt Entry", oca_name = "Long1") // oca_type = strategy.oca.cancel,
    Long1SL             := Lowest3

strategy.exit('Take Profit Long', from_entry = 'Long',  qty = strategy.position_size, limit = Long1_Take_Profit, stop = Long1SL, oca_name = "Long1_Exit", comment_profit = "Profit Bkt\nClose Long1", comment_loss = "SL Bkt\nClose Long1") 

Upvotes: 0

Related Questions