Search code examples
pine-scriptalgorithmic-tradingback-testing

Why does pine script enter at the next candle open even when I am using a market order?


I am trying to implement a 2 period RSI based strategy backtest in Pine Script. The idea is simple

Objective

  • 2 Period RSI crosses under 10, when 200 EMA is below the recent close, I go long on the next candle with a market order set to limit 2% less than previous candles close.
  • 2 Period RSI crosses over 90, or its been 10 bars since entry (whichever condition occurs first) I exit the trade.

The problem

So apparently pine script defaults to taking a long/short position on open of the next candle. But despite placing a market order by specifying the limit attribute the long position is entered at opening price of the next candle.

A few more details

Timeframe - 1Day

Ticker link - https://in.tradingview.com/chart/GDSsFCKq/# (Ticker - SBILIFE (NSE INDIA))

I am not sure what I am doing wrong here. Please help.

Code

//@version=4
strategy("2RSI Strategy by Larry Connor", overlay=true)
rsi_length = input(title="RSI Length", defval=2) 
buying_rsi_value = input(title="Buy at RSI Value", defval=5)
selling_rsi_value = input(title="Sell at RSI Value", defval= 40)
price = close
rsi = rsi(price, rsi_length)
buy = crossunder(rsi, buying_rsi_value)
sell = crossover(rsi, selling_rsi_value)

date = tostring(dayofmonth) + '-' + tostring(month) + '-' + tostring(year)
disable_date_ranges = input(title="Disable Date Ranges", defval=true)

start_date = input(title="Start Date", type=input.time, defval=timestamp("19 Oct 2020 
00:00 +0530"))
end_date = input(title="End Date", type=input.time, defval=timestamp("18 Oct 2021 
00:00 +0530"))
in_date_range = time >= start_date and time < end_date
    
ema_len = input(200, minval=1, title="EMA Length")
ema_src = input(close, title="EMA Source")
ema_200 = ema(ema_src, ema_len)

entry_condition= buy and ema_200 < price 

exit_condition = sell or entry_condition[10]

previous_day_close = close[1]
two_percent_of_prev_day_close = previous_day_close * 0.02
entry_price = previous_day_close - two_percent_of_prev_day_close

// plotchar(entry_condition, "debug", "", location.bottom)

capital_invested = input(title="Invested capital", defval=100000)
initial_capital = strategy.initial_capital
capital_to_be_invested = capital_invested
if(na(capital_invested) or capital_invested == 0)
    capital_to_be_invested = initial_capital

if (not na(rsi) and (in_date_range or disable_date_ranges))
    strategy.entry("buy", when=entry_condition and low < entry_price, limit= 
entry_price, long= true, qty = capital_to_be_invested/entry_price, comment="Long")
        
if (exit_condition)
    strategy.close("buy", true)

Solution

  • To confront this issue is to understand the processing of historical data, which is used in back testing. Historical data is 4 data points per candle (OHLC). Calculations for indicators are made using closing price typically, as well as we don’t have enough information about intra-bar price travel to make assumptions where or when an alert took place. Thus, we must rely on the closing condition for a given candle to establish variable states on the historical bar. In real time, we are confronted with similar issues only that we must wait for close to confirm a signal, or we suffer the affects of repainting. As such, the 2 data types (historical and real time) become aligned as one procedure - a candle close is a confirmed and actionable signal. To establish a closing price a candle would exhaust its last tick for the period. This means that our next actionable sale is the next sale available, which occurs in the first ticks of the bar following. This is why open prices are used in backtesting following a state change of a given variable. If a candle is closed how would we execute an order? As mentioned above, we could forgo this in real time, but to do so is to separate 2 differentiated behaviours of a strategy, which effectively makes the strategy unique, and not one we tested on historical data. Just a few of many caveats of strategy building :)

    Cheers!