1

I know this is a common issue, however (I believe) I've researched the issue enough and have followed the proper tricks to mitigate repainting as much as possible (i.e. using [n] indices when using request.security(), not using calc_on_order_fills=true/calc_on_every_tick=true, toggling lookahead_on/lookahead_off`, etc.), however, I'm stuck as to why when I run the bar replay with this below, simplified strategy, the trades are different when the replay is done and the backtest engine shows its results on the chart. There's some repainting of past trades going on and I'm not sure which one to trust in real time. What I want is to be able to have the strategy reflect on the backtest, the same trades that it does on the bar replay.

This strategy uses high, low and close from a higher timeframe (in this case 2-hour), performs and performs a simple trend calculation where, if the value is > 0.5, it signals an uptrend, and < 0.5 means downtrend. As you'll see, I'm using high[1], low[1], and close[1] in the request.security() method calls to access the previous fully closed bar of the higher TF, I have the calc_on_order_fills/calc_on_every_tick set to false, and using lookahead=barmerge.lookahead_off, however when run on bar replay it opens trades that don't appear on the backtest chart. Why? And how to fix? Preferably without just posting a link to the backtesting references of TradingView as I've read them and still don't get why it's happening.

//@version=5
global_strategy_name = "Mobius_Useful_Candles2_Strat_Matts"
//=========================== IMPORTS AND STRATEGY ===========================//
strategy(global_strategy_name, shorttitle=global_strategy_name, 
     overlay=true, 
     initial_capital=1000, 
     pyramiding=1, 
     precision=2,
     calc_on_order_fills=false, 
     calc_on_every_tick=false, 
     //backtest_fill_limits_assumption=30, 
     default_qty_type=strategy.fixed,//strategy.percent_of_equity, 
     default_qty_value=1,//100, 
     currency=currency.NONE, 
     slippage=30, 
     commission_type=strategy.commission.cash_per_contract, 
     commission_value=0.01)

//=========================== INPUTS ===========================//
//========= STRATEGY
input_agg_period = input.timeframe(
     title = "Higher TimeFrame Aggregation Period", 
     defval = "120", //"240", // 120
     group = 'Strategy',
     tooltip = 'The higher timeframe to use with strategy')

input_trend_periods = input.int(
     title = "Trend Periods", 
     minval = 1, 
     defval = 358,
     group = 'Strategy',
     tooltip = 'The number of bars/periods to use for calculating trend')


//=========================== STRATEGY ===========================//
h = request.security(syminfo.ticker, input_agg_period, high[1])
l = request.security(syminfo.ticker, input_agg_period, low[1])
c = request.security(syminfo.ticker, input_agg_period, close[1])

hh = ta.highest(h, input_trend_periods)
ll = ta.lowest(l, input_trend_periods)

// If "trend" > 0.5, then true/uptrend, else false/downtrend
trendSignal = (c - ll) / (hh - ll) > 0.5 ? 1 : 0

// Update the bar colours for visual inspection
barcolor(trendSignal == 1 ? color.rgb(0, 252, 0) : color.red)

// Define entry conditions
longCondition = trendSignal == 1
shortCondition = trendSignal == 0

// Check for currently held positions
bool longIsActive = strategy.position_size > 0
bool shortIsActive = strategy.position_size < 0

// Define exit conditions
bool validExitLongPosition = shortCondition and longIsActive
bool validExitShortPosition = longCondition and shortIsActive

// Enter/Exit trades
if longCondition
    strategy.entry("Enter Long", strategy.long, alert_message = "MOBIUS - Enter Long")
else if shortCondition
    strategy.entry("Enter Short", strategy.short, alert_message = "MOBIUS - Enter Short")

if validExitLongPosition or validExitShortPosition
    strategy.close_all("x", alert_message = global_strategy_name + " - Exit Position")
windowshopr
  • 138
  • 7

1 Answers1

1

This seems to be working:

h = request.security(syminfo.ticker, input_agg_period, high, lookahead = barmerge.lookahead_on)[1]
l = request.security(syminfo.ticker, input_agg_period, low, lookahead = barmerge.lookahead_on)[1]
c = request.security(syminfo.ticker, input_agg_period, close, lookahead = barmerge.lookahead_on)[1]

...

// Define entry conditions
longCondition = trendSignal == 1 and barstate.isconfirmed
shortCondition = trendSignal == 0 and barstate.isconfirmed

// Check for currently held positions
bool longIsActive = strategy.position_size > 0
bool shortIsActive = strategy.position_size < 0

// Define exit conditions
bool validExitLongPosition = shortCondition and longIsActive and barstate.isconfirmed
bool validExitShortPosition = longCondition and shortIsActive and barstate.isconfirmed

Indexing the request.security() call by [1], AND setting barmerge.lookahead_on in the lookahead parameter works, don't know why, just does.

Also I added some barstate.isconfirmed's to the open and close conditions to ensure it's checking for conditions on bar close.

Hopefully this helps someone else.

windowshopr
  • 138
  • 7