Code for backtest using Bollinger bands and a MA

I am trying to do a backtest at a strategy using bollinger bands and a 40 period simple Moving Average.
The purposed goal is to short at the exact price of a bbandtop and cover at the exact price of a 40 period MA.
It should take only one short position until covered.
The Long part of the opperation would happen when the price hits the bbandbot entering long at the exact price of the lower band and sell that potion at the exact price of a 40 period MA.
It should take only one long position until a sell at the average happens.
I have looked at the forums and google for it and was able to do some things but I can not make it work properly.
I think that the buy and short signals at the bands are working but I can not make they exit at the 40 MA.

Here is the code I am using:

shortenter = BBandTop(C, 40, 2);
longenter = BBandBot(C, 40, 2);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Buy =  C <= longenter;
Sell = C >= media;
Short = C >= shortenter;
Cover = C <= media;

Buy = ExRem(Buy,Short);
Sell = ExRem(Sell, Cover);
Short = ExRem(Short, Buy);
Cover = ExRem(Cover, Sell);

Plot(longenter, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(shortenter, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

PlotShapes(Buy*shapeUpArrow, colorGreen, 0, longenter);
PlotShapes(Short*shapeDownArrow, colorBlue, 0, shortenter); 

PlotShapes(Cover*shapeStar, colorRed, 0, media);
PlotShapes(sell*shapeStar, colorPink, 0, media);

Any help is much appreciated.Thanks in Advance

Try this:

shortenter = BBandTop(C, 40, 2);
longenter = BBandBot(C, 40, 2);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Buy =  C <= longenter;
Sell = C >= media;
Short = C >= shortenter;
Cover = C <= media;

/*** Change ***/
/*
Buy = ExRem(Buy,Short);
Sell = ExRem(Sell, Cover);
Short = ExRem(Short, Buy);
Cover = ExRem(Cover, Sell);
*/

Buy = ExRem(Buy, Sell);
Sell = ExRem(Sell, Buy);
Short = ExRem(Short, Cover);
Cover = ExRem(Cover, Short);

Plot(longenter, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(shortenter, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

PlotShapes(Buy*shapeUpArrow, colorGreen, 0, Low);
PlotShapes(Short*shapeDownArrow, colorBlue, 0, High); 

PlotShapes(Cover*shapeStar, colorRed, 0, Low);
PlotShapes(Sell*shapeStar, colorPink, 0, High);
1 Like

Thank you for your help. I got some Ideas from what you sent me and was able to make it work. But there are still some bugs happening.
I am uploading an image showing that in the candle marked by the drawn red arrow, since the low of the candle crossed the bbandbot a buy position should have been taken but I do not know why it has not.
Here is the current code:

_SECTION_BEGIN("bollinges bands sell");
Perna1short = BBandTop(C, 40, 3);
Perna1long = BBandBot(C, 40, 3);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Short = h >= Perna1short;
ShortPrice = Perna1short;
Cover = l <= media;
CoverPrice = media;


Cover = ExRem(Cover,Short);
Short = ExRem(Short, Cover);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 
PlotShapes(Short*shapeDownArrow, colorBlue, 0, Perna1short); 
PlotShapes(Cover*shapeStar, colorRed, 0, media);

_SECTION_END();

_SECTION_BEGIN("bollinges bands buy");
Perna1short = BBandTop(C, 40, 3);
Perna1long = BBandBot(C, 40, 3);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Buy =  l <= Perna1long;
BuyPrice = Perna1long;
Sell = h >= media;
SellPrice = media;

Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell, Buy);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

PlotShapes(Buy*shapeUpArrow, colorGreen, 0, Perna1long);
PlotShapes(sell*shapeStar, colorPink, 0, media);

_SECTION_END();

And here is the image.
Capturar

Again, any help is much appreciated.

Are you sure the Low was less than or equal to bbandbot?

1 Like

You are correct the low is not lower or equal to the bbandbot. The close of the candle is lower then the bbandbot as we can see in a new image I am sending attached. This seems wrong or showing a database issue, but the cause for that problem in the database is that what I am trying to do is to perform a backtest in a pairs trading strategy.
All the prices in the chart are a ratio between the prices of 2 different symbols: open of one symbol / open of a other symbol; close of one symbol / close of a other symbol; low of one symbol / low of a other symbol.... etc I created those ratios in excel and imported to amibroker. Since not always the ratio between the 2 lows of the price of the symbols is the lower value of the ratio of that current day I am having those kind of problems.
It would be really difficult to reorder the ratios in excel to place the correct positions of the lower ratio in the lower of the candle, the higher ratio in the higher of the candle etc.
I wonder if there is any way I could make in my formula so amibroker understends that if any of the Lower value or the higher value or the close value or the open value is lower or equal then the bbandbot it is supposed to buy.
I tryed to change the code for that but it has not worked placing buy and sells in every candle.
Here is the formula I tested:

_SECTION_BEGIN("bollinges bands sell");
Perna1short = BBandTop(C, 40, 3);
Perna1long = BBandBot(C, 40, 3);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Short = L OR H OR C OR O >= Perna1short;
ShortPrice = Perna1short;
Cover = L OR H OR C OR O <= media;
CoverPrice = media;


Cover = ExRem(Cover,Short);
Short = ExRem(Short, Cover);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 
PlotShapes(Short*shapeDownArrow, colorBlue, 0, Perna1short); 
PlotShapes(Cover*shapeStar, colorRed, 0, media);

_SECTION_END();

_SECTION_BEGIN("bollinges bands buy");
Perna1short = BBandTop(C, 40, 3);
Perna1long = BBandBot(C, 40, 3);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Buy =  L or h or c or O <= Perna1long;
BuyPrice = Perna1long;
Sell = L OR H OR C OR O >= media;
SellPrice = media;

Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell, Buy);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

PlotShapes(Buy*shapeUpArrow, colorGreen, 0, Perna1long);
PlotShapes(sell*shapeStar, colorPink, 0, media);

_SECTION_END();

And here is an attached image showing the error.
Sem%20t%C3%ADtulo

How is it possible for the Close to be lower than the Low?

1 Like

Yes. It was a problem with the database I was using. Now I got it fixed and it is working as I wanted. Thank you alot for all your help!
May I ask for just one more tip?
I would like to add a stop by time in my trading strategy. After 10 days of an open positon, if not ended in the other parameters that are already in the formula the trade should be closed.
Is that possible? I have found time restrictions but only within one day.
Thank you again!!

Use BarsSince() on the open position array,
Ie. BarsSince Buy for eg.
If you are in Daily TF, it will return number of days and that should be less than 11 in your close position code.

If its a lower than daily TF then divide number of bars by Interval()

1 Like

Don't use BarSince() but use Applystop() with n-bar stop mode.

ApplyStop(stopTypeNBar, stopModeBars, bars = 10, nbar_stop_priority = 1);

BTW, @marceloutn
Your upper signal code is pretty much wrong.

2 Likes

Thank you for all the help.
Actualy you are right it was not working like that and I changed it but forgot to post.
The code now is like this adding the stop. It seems to be working fine.

_SECTION_BEGIN("bollinges bands sell");
Perna1short = BBandTop(C, 40, 2);
Perna1long = BBandBot(C, 40, 2);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Short = L  >= Perna1short OR Short = H >= Perna1short OR Short = C  >= Perna1short OR Short = O  >= Perna1short;
ShortPrice = Perna1short;
Cover = H  <= media OR Cover = L <= media OR Cover = C  <= media OR Cover = O  <= media;
CoverPrice = media;


Cover = ExRem(Cover,Short);
Short = ExRem(Short, Cover);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 
PlotShapes(Short*shapeDownArrow, colorBlue, 0, Perna1short); 
PlotShapes(Cover*shapeStar, colorRed, 0, media);
ApplyStop(stopTypeNBar, stopModeBars, bars = 10, nbar_stop_priority = 1);

_SECTION_END();

_SECTION_BEGIN("bollinges bands buy");
Perna1short = BBandTop(C, 40, 2);
Perna1long = BBandBot(C, 40, 2);

media = MA(C,40);
Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

Buy =  L <= Perna1long OR Buy =  H <= Perna1long OR Buy =  C <= Perna1long OR Buy =  O <= Perna1long;
BuyPrice = Perna1long;
Sell = H >= media OR Sell = L >= media OR Sell = C >= media OR Sell = O >= media;
SellPrice = media;

Buy = ExRem(Buy,Sell);
Sell = ExRem(Sell, Buy);


Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

PlotShapes(Buy*shapeUpArrow, colorGreen, 0, Perna1long);
PlotShapes(sell*shapeStar, colorPink, 0, media);
ApplyStop(stopTypeNBar, stopModeBars, bars = 10, nbar_stop_priority = 1);

_SECTION_END();

Again thx for all the Help!

Sorry, but it is still wrong. Several logical mistakes and coding mistakes.

One of those mistakes... You have a future leak there. BBand and MA values are known at close of bar! And you check that one against O,L,H of same bar. It is wrong.

etc.

Also why so many repetitions?
E.g. ApplyStop is a stop. It is called once per stop mode as it applies to Long and Short.

Here is possible fix (orange square is n-bar stop):

_SECTION_BEGIN("bollinges bands Long_Short");
/// @link https://forum.amibroker.com/t/code-for-backtest-using-bollinger-bands-and-a-ma/9018/10
/// fix at:
/// @link https://forum.amibroker.com/t/code-for-backtest-using-bollinger-bands-and-a-ma/9018/11
Perna1short = BBandTop(C, 40, 2);
Perna1long = BBandBot(C, 40, 2);
media = MA(C,40);

prev_BBbot = Ref(Perna1long,-1);
prev_BBtop = Ref(Perna1short,-1);
prev_MA = Ref(media,-1);

Buy = L <= prev_BBbot;
BuyPrice = Min(Open,prev_BBbot);
Sell = H >= prev_MA;
SellPrice = Max(Open, prev_MA);

Short = H >= prev_BBtop;
ShortPrice = Max(Open, prev_BBtop);
Cover = L <= prev_MA;
CoverPrice = Min(Open,prev_MA);

ApplyStop(stopTypeNBar, stopModeBars, bars = 10, nbar_stop_priority = 1);

if ( Status( "action" ) == actionIndicator ) {
	eq = Equity(1,0);

	Plot( C, "Price", colorDefault, styleBar );

	Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

	Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
	Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

	PlotShapes(Short*shapeDownArrow, colorBlue, 0, ShortPrice); 
	PlotShapes((Cover AND Cover != 5)*shapeStar, colorRed, 0, CoverPrice, 0);
	PlotShapes(Buy*shapeUpArrow, colorGreen, 0, BuyPrice);
	PlotShapes((Sell AND Sell != 5)*shapeStar, colorPink, 0, SellPrice, 0);
	PlotShapes( (Sell == 5 OR Cover == 5)*shapeSmallSquare, colorOrange, 0, L );
}
_SECTION_END();

900

4 Likes

Hi @fxshrat,

Are you sure that is a Future Leak?

Future Leak (@howardbandy) - "A future leak occurs when a trading system looks ahead to data that will not be available in real-time to make its buy and sell decisions"

I've seen Bollinger Bands coded many times with all the calculations done off the completion of the same bar. Such as in Howard Bandy's books. So I'm curious on your thoughts and what others feel is their preferred method with this?

Thanks.

1 Like

@TrendSurfer there is definitely a leak in the code from @marceloutn, for exactly the reason that @fxshrat stated. The original code is checking the current bar's high and low against the current bar's BBands. And the current bar's BBands cannot be calculated until the current bar's close.

If @marceloutn was entering at the close (and therefore looking back at the current bar's high and low), then this would all be OK. However, he's trying to enter in the middle of the bar at a price that won't be known until the close of the bar. Similar problem with the exits.

3 Likes

Yep, that's what I was missing. Makes sense now, thanks @mradtke.

1 Like

Thank you for the help. You are absolutely right. Sorry I am a nb here, trying to learn as I go! Yor code seem to be running really good.
One question on one of the items though, I was looking at the backtest results and comparing with the chart values to check everithing is ok and I saw that the stopnbar exits at the high of the exit bar on long trades and on the low of the exit bar on short trades. And like that if I am not mistaken it will produce better results then actually should be. Is there any way I can say that the exit value of the stopnbar should be at the opening of the next bar or at the close of the current one?
Thank you for all the help!

Here is changed code to exit at different price when there is n-bar stop.
Also added re-entry delay of one bar after nbar stop.
(If it is solved then you may mark post as solution)

_SECTION_BEGIN("bollinges bands Long_Short");
/// @link https://forum.amibroker.com/t/code-for-backtest-using-bollinger-bands-and-a-ma/9018/10
/// fix by fxshrat@gmail.com at:
/// @link https://forum.amibroker.com/t/code-for-backtest-using-bollinger-bands-and-a-ma/9018/15
Perna1short = BBandTop(C, 40, 2);
Perna1long = BBandBot(C, 40, 2);
media = MA(C,40);

prev_BBbot = Ref(Perna1long,-1);
prev_BBtop = Ref(Perna1short,-1);
prev_MA = Ref(media,-1);

Buy = L <= prev_BBbot;
BuyPrice = Min(Open,prev_BBbot);
Sell = H >= prev_MA;
SellPrice = IIf( /*regular exit*/ Sell == 1, Max(Open, prev_MA), /*else if nbar stop then*/ Close);

Short = H >= prev_BBtop;
ShortPrice = Max(Open, prev_BBtop);
Cover = L <= prev_MA;
CoverPrice = IIf( /*regular exit*/ Cover == 1, Min(Open,prev_MA), /*else if nbar stop then*/ Close);

ApplyStop(stopTypeNBar, stopModeBars, bars = 10, nbar_stop_priority = 1, False, re_entry_delay = 1);

if ( Status( "action" ) == actionIndicator ) {
	eq = Equity(1,0);

	Plot( C, "", colorDefault, styleBar );

	Plot(media, "mm", colorRed, styleLine, 0, 0, 0, 0);

	Plot(Perna1long, "buy1", colorBlue, styleLine, 0 ,0 ,0 ,0);
	Plot(Perna1short, "short1", colorBlue, styleLine, 0, 0, 0, 0); 

	PlotShapes(Short*shapeDownArrow, colorBlue, 0, ShortPrice); 
	PlotShapes((Cover AND Cover != 5)*shapeStar, colorRed, 0, CoverPrice, 0);
	PlotShapes(Buy*shapeUpArrow, colorGreen, 0, BuyPrice);
	PlotShapes((Sell AND Sell != 5)*shapeStar, colorPink, 0, SellPrice, 0);
	PlotShapes( (Sell == 5 OR Cover == 5)*shapeSmallSquare, colorOrange, 0, L );
	
	_N( Title = StrFormat( "{{NAME}} - {{INTERVAL}} - {{DATE}} Open %g, Hi %g, Lo %g, Close %g (%.1f%%), Vol %g {{VALUES}}",
                           O, H, L, C, SelectedValue( ROC( C, 1 ) ), V ) );
}
_SECTION_END();

842


Yes, as long as I do not add "I am not sure but.." then I am always sure.
I don't read H.Bandy books so I can't tell what calculation he does.
But if he does add such leaks then it is wrong (to me).

2 Likes

Although no doubt real close is known when market really closes, still some people want to avoid overnight gaps and delay and run the code in the last minute of the trading session and place order on close. This gives them not perfect but "good enough" approximation of given bar OHLC. It all depends on how one actually trades and in practice it is not always "wrong".

5 Likes