I asked a similar question before here, but I thought I had figured out the issue, and couldn't provide visual representations of the issue, so posting again here as the issue isn't fixed.
This simple strategy (posted below) makes use of the request.security()
method to access data from a higher timeframe, however I've attempted A LOT of different solutions to get this to prevent repainting, but it still seems to be doing it and I can't figure out why.
When I apply the strategy to a 5-minute SPY chart, and let it run in real-time for a while, I get these trades placed (without extended trading hours turned on):
...but upon refreshing the web page, the results get updated to this:
(Notice the trades later on in the day being updated).
What I would like is to have the strategy behave the same when using bar replay, the backtest tool, AND when performing live trading (without having to refresh the page periodically). It's tricky to test because the bar replay and backtest tools behave the same currently, it's only when applied to live trading for a few days that I spot the issues.
My assumption here, is that when live trading, using the request.security()
method stops working as desired, in that it aggregates the data (in this case, the 2-hour timeframe) from the current bar's time. So if the current time is, say, 14:10, the previous "fully formed candle's" close time of the higher timeframe would be 12:10. This might explain why every 5 minutes, the signal switches because that higher-timeframe bar keeps getting updated. But when using the backtest engine/refreshing the page, it calculates the bars based on the appropriate historical times for backtesting (i.e. 12:00, 14:00, etc.), but I'm not 100% confident in that assumption yet. Need some specific clarification on this, as I've tried using close[1]
in the request.security()
calls, making sure calculate_on_every_tick
is set to false
in the strategy parameters, using different combinations of gaps
and lookahead
's, trying to access the "previous fully closed higher timeframe's bar" (which again, likely changes every 5 minutes), etc. and need someone to point out what I'm still missing? I've read all the web pages that talk about repainting and have tried all their solutions, but the issue persists in live trading.
The only fix I can think of to try would be to create a method that tries to modulus the time, and if it's mod of the higher timeframe == 0, THEN access the previous higher timeframe's bar data. This would prevent the "real time" repainting of the current higher timeframe's candle, but I thought that's what they're describing in the repainting guide by utilizing the barstate.isconfirmed
or is_realtime_candle
(or whatever that option is called), but it doesn't seem to work with any of the configurations I've tried.
Here is the code:
//@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 ===========================//
// Get the upper timeframe's previous, fully closed/formed bar's high, low and close
h = request.security(syminfo.ticker, input_agg_period, high, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)[2]
l = request.security(syminfo.ticker, input_agg_period, low, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)[2]
c = request.security(syminfo.ticker, input_agg_period, close, gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)[2]
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")