The strategy contained in this post comes courtesy of babypips.com, an excellent resource for all thing forex related. If you are new to trading, the site is definitely worth checking out. Even if you don’t intend to trade Forex!
We are going to take a look at a strategy which has been tested and reviewed on the site since 2017. The strategy in focus is an Inside Bar Momentum strategy which according to the latest reports on the site has had excellent live performance throughout 2018 to date. As such, that makes it an interesting strategy to replicate and backtest.
What is an Inside Bar?
An inside bar is the name of a candle which can fully fit inside the candle before it. The means that the previous candle’s high is higher than the “Inside Bar” candle high. Additionally, the previous bar’s low is lower than the “Inside Bar” candle low.
Inside Bars can be used as technical analysis signal for price action strategies. It is thought that inside bars could indicate a pause or breather in the market before a bigger move happens. If you are familiar with it, this is a similar concept to the Bollinger squeeze. I.e. the calm before the storm!
This particular strategy theorizes that the “Inside Bar” is signalling a pause before momentum continues in the same direction. I should note that it not explicitly stated in the authors’ post. Howeveer, the ruleset certainly appears to confirm this interpretation.
Before we get into the rules though, let’s solidify our understanding of the inside bar with a visual example. This will make it very obvious what we shall be looking for.
Inside Bar Momentum Strategy
For a complete overview of the ruleset, you should check out the strategy article on baby pips. It is only fair to the original author. However, for those who hate to click on links, a summary of the key points is provided below.
Strategy Source: https://www.babypips.com/trading/forex-inside-bar-20170113
The strategy focuses on “setups” rather than signals. This means that we do not enter a trade as soon as we receive a signal. Instead, the signal will highlight a potential setup which may or may not come to fruition. Once we identify the setup, we send a stop
order to attempt to enter an ideal level once the setup is confirmed.
- Long Setup: An Inside bar is detected and the candle before the inside bar is green
- Short Setup: An Inside bar is detected and the candle before the inside bar is red
As mentioned above, once we have identified the setup, we enter a stop
order at a given distance (percentage) away from the high
or low
of the bar before the inside bar. It will be the high
for long setups and the low
for short setups.
- Exit: Simple take profit and stop loss measure from the high or low of the bar before the inside bar. (Not the entry price!)
Special note: If a new Inside bar is detected while we are in a position, we with close it with a market
order. If a new inside bar is detected but we have not triggered out entry level, then we start again. I.e cancel our entry order and create a new one.
The Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
//@version=3 // Inside Bar Momentum Strategy // As defined on Babypips.com // https://www.babypips.com/trading/forex-inside-bar-20170113 strategy("Babypips: Inside Bar Momentum Strategy", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=5) From_Year = input(defval = 2018, title = "From Year") From_Month = input(defval = 1, title = "From Month", minval = 1, maxval = 12) From_Day = input(defval = 1, title = "From Day", minval = 1, maxval = 31) To_Year = input(defval = 9999, title = "To Year") To_Month = input(defval = 1, title = "To Month", minval = 1, maxval = 12) To_Day = input(defval = 1, title = "To Day", minval = 1, maxval = 31) Start = timestamp(From_Year, From_Month, From_Day, 00, 00) // backtest start window Finish = timestamp(To_Year, To_Month, To_Day, 23, 59) // backtest finish window Window = time >= Start and time <= Finish Stop_Buy_Perc = input(10, "Stop Buy Order Percentage From Previous Candle's Range")/100 Stop_Loss_Perc = input(20, "Stop Loss Distance from High/Low of Previous Candle")/100 Take_Prof_Perc = input(80, "Take Profit Distance from High/Low of Previous Candle")/100 Risk = input(2, "Percentage Of EQUITY to risk per trade", step=0.1, minval=0, maxval=100)/100 Inside_Bar = high[1] > high[0] and low[1] < low[0] Prev_Range = high[1] - low[1] Bullish = open[1] < close[1] Bearish = open[1] > close[1] // Get Key Levels Long_Stop_Buy_Level = high[1] + (Prev_Range * Stop_Buy_Perc) Short_Stop_Buy_Level = low[1] - (Prev_Range * Stop_Buy_Perc) Long_Stop_Loss_Level = high[1] - (Prev_Range * Stop_Loss_Perc) Short_Stop_Loss_Level = low[1] + (Prev_Range * Stop_Loss_Perc) Long_Take_Prof_Level = high[1] + (Prev_Range * Take_Prof_Perc) Short_Take_Prof_Level = low[1] - (Prev_Range * Take_Prof_Perc) long_qty = floor((strategy.equity * Risk) / (Long_Stop_Buy_Level - Long_Stop_Loss_Level)) short_qty = floor((strategy.equity * Risk) / (Short_Stop_Loss_Level - Short_Stop_Buy_Level)) // -------------------------- LONG CONDITIONS --------------------------------// // The first candlestick must be bullish (green or white) and if the second // candlestick is completely contained by the first, set a buy stop order at // the first candle’s high plus 10% of its range (high minus low). // Place the stop loss at the first candle’s high minus 20% of its range // and set the target at the first candle’s high plus 80% of its range // If another inside bar pattern forms, the current position should be closed // or the pending buy/sell order must be canceled and entry orders must be // updated to the latest candles. Long_Condition = Window and Inside_Bar and Bullish if (Long_Condition) // Incase we still have a buy stop order in the market strategy.cancel_all() // Close any existing positions according to the rules strategy.close_all() strategy.entry("Bullish IB", strategy.long, stop=Long_Stop_Buy_Level, qty=long_qty) strategy.exit("Bullish Exit","Bullish IB", stop=Long_Stop_Loss_Level, limit=Long_Take_Prof_Level) // -------------------------- SHORT CONDITIONS -------------------------------// // The first candlestick must be bearish (red or black) and if the second // candlestick is completely contained by the first, set a sell stop order at // the first candle’s low minus 10% of its range (high minus low). // Place the stop loss at the first candle’s low plus 20% of its range and // set the target at the first candle’s low minus 80% of its range. // If another inside bar pattern forms, the current position should be closed // or the pending buy/sell order must be canceled and entry orders must be // updated to the latest candles. Short_Condition = Window and Inside_Bar and Bearish if (Short_Condition) // Incase we still have a buy stop order in the market strategy.cancel_all() // Close any existing positions according to the rules strategy.close_all() strategy.entry("Bearish IB", strategy.short, stop=Short_Stop_Buy_Level, qty=short_qty) strategy.exit("Bearish Exit","Bearish IB", stop=Short_Stop_Loss_Level, limit=Short_Take_Prof_Level) // ----------------------------- PLOTTING ------------------------------------// plotshape(Inside_Bar, style=shape.arrowdown, location=location.abovebar, color=purple) |
Commentary
The code attempts to faithfully follow the ruleset provided on babypips with a few additional extras:
- Variable entry, stop and profit levels
- Backtest date ranges
- Position Sizing
Of the three additional extras, it is the position sizing algorithm that could do with a few extra words. Position sizing is not mentioned in the author’s original post. However, it is clear that the strategy has a fixed risk. Only only chances of exiting at a loss are through our stop loss or through the market order (which is triggered before we hit the stop). Therefore, this strategy is an ideal candidate to benefit from some larger position sizing.
In Forex markets it is common for traders to size their position so that if their stoploss is hit, it will result in losing a fixed x% of their overall account. Given that stop losses are not a zero, this results in a much larger quantity than just dividing a percentage of the account by the price. It also means that the position size will change depending on how far your stop loss is from your entry price. If it is far away, your position size will be smaller. Conversely, if it is closer, your position size can be much larger whilst only risking the same amount.
We calculate our position size with the following line:
long_qty = floor((strategy.equity * Risk) / (Long_Stop_Buy_Level - Long_Stop_Loss_Level))
It is worth noting that since we calculate our qty
manually, Order Size
in the strategy properties tab will have no impact on the position size.
Additional Note: The code in this post only works effectively, if your account currency is the same as the counter currency. I.e If you are trading GBPUSD and your account currency is USD. If it is not the base currency, Traders must perform an FX conversion first. The code in this post does not cover that. However, the account currency in this strategy is set to default
which will automatically be the counter currency.
On the Charts
When loading up the strategy for the first time, you will see something that looks like this:
The only eye-candy on display is a simple marker to highlight when inside bars are detected.
Performance
Since this strategy was posted on an FX website, it is only fitting to take a look at some forex pairs first. Following this, we will take a quick look at how it handles some other markets.
Important Note: Some of the results below appear a little too good to be true. In general, if it appears that way, it usually is! So take these results with a healthy dose of caution. There could be something mechanically funky going on like we discovered in: Tradingview: Trailing Stop Mechanics (Beware Version 3).
All backtests below were performed WITHOUT commission (important point because commissions can cause death by 1000 paper cuts) from Jan 2018 to July 2018.
Did a rewrite for ProRealTime. Didn’t even get close to the results I got in TradingView. Have you tried it on any other platform?
Hi Jesper,
Personally, I have had chance to re-write it on another platform. However, I have noted in other articles that Tradingview appears to be a little generous when working out exit levels.
Also, I did note above that I think these results seemed too good to be true. As such, it does not surprise me that you cannot get close.
Out of interest what sort of results are you seeing?
Oh, sorry about not getting back. Didn’t get a notice on you reply.
Can’t remember the results from prorealtime but I did another study on Ninjatrader 8 which is far more accurate as you can set internal resolution on any bar down to ticks.
On an index I trade I got 47% percent profitable and 5.7 profitfactor on 45 closed trades on TradingView.
Same code, same index, same period on Ninjatrader gave 44%, 2.92 on 25 trades.
Have to correct Tradingview numbers as I did a re-run of the strategy (numbers I had up was from another instrument): Sould be 63% and 4.26 on 27 trades.
I would say that TV is wrong on both exits and stop loss.
Have done some more testing after I have revised my code to more advanced order handling in NinjaTrader and the results are just gettting worse.
The 240 Minute on SP500 gives nothing back (Profifactor 1, Winpercent 37,8, ratio win/loss 1,65). The GBPUSD on 240 min is even more awefull.
To bad I can’t post images here so that I could show the actual graphs.
I would not trade this for sure.
Hey thanks for all these blog posts and examples, I love your scripts. I’m really good with TradingView programming but there’s always something new to learn from your articles.
Anyway I just wanted to mention a bug that I found, which may explain the “too good to be true” results. While testing this script against SPY, I noticed that it was buying huge positions, many 1000’s of shares — in a $100,000 simulation that wouldn’t be able to afford it in real life. It was seeing stop-loss risk say like $0.50, and would try to fill up the max risk level of 2% ($2000 risk), and buy 4000 shares, which is over $1M of SPY.
I added the following lines to keep the position sizes to max of available equity:
long_qty := min(long_qty, floor(strategy.equity / Long_Stop_Buy_Level))
short_qty := min(short_qty, floor(strategy.equity / Short_Stop_Buy_Level))
And it seems to be giving more normal results now: 2018 to present, 27 trades, 51.85% profitable, avg win/loss $780/$484, net profit $4634. Seems like a decent strategy, although it doesn’t trigger a lot. Cheers!
Hey, I am looking at this strategy with 1D BTC/USDT and was hoping you could help me understand what has happened on the 31.08.2019.
The day closed with an inside bar. The previous bar on the 29th is bullish. From my understanding the strategy should put an order limit at the previous bars high + 10% of its range. In the strategy tester nothing happens.
On all other occasions everything is clear to me. From 13.09. to 16.09. we also had several consecutive InsideBars and the strategy correctly acted only upon the last one.
I would really appreciate if someone could explain this to me. Thanks!